123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997 |
- /**************************************************************************
- * Initio 9100 device driver for Linux.
- *
- * Copyright (c) 1994-1998 Initio Corporation
- * Copyright (c) 1998 Bas Vermeulen <bvermeul@blackstar.xs4all.nl>
- * Copyright (c) 2004 Christoph Hellwig <hch@lst.de>
- * Copyright (c) 2007 Red Hat
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *
- *************************************************************************
- *
- * DESCRIPTION:
- *
- * This is the Linux low-level SCSI driver for Initio INI-9X00U/UW SCSI host
- * adapters
- *
- * 08/06/97 hc - v1.01h
- * - Support inic-940 and inic-935
- * 09/26/97 hc - v1.01i
- * - Make correction from J.W. Schultz suggestion
- * 10/13/97 hc - Support reset function
- * 10/21/97 hc - v1.01j
- * - Support 32 LUN (SCSI 3)
- * 01/14/98 hc - v1.01k
- * - Fix memory allocation problem
- * 03/04/98 hc - v1.01l
- * - Fix tape rewind which will hang the system problem
- * - Set can_queue to initio_num_scb
- * 06/25/98 hc - v1.01m
- * - Get it work for kernel version >= 2.1.75
- * - Dynamic assign SCSI bus reset holding time in initio_init()
- * 07/02/98 hc - v1.01n
- * - Support 0002134A
- * 08/07/98 hc - v1.01o
- * - Change the initio_abort_srb routine to use scsi_done. <01>
- * 09/07/98 hl - v1.02
- * - Change the INI9100U define and proc_dir_entry to
- * reflect the newer Kernel 2.1.118, but the v1.o1o
- * should work with Kernel 2.1.118.
- * 09/20/98 wh - v1.02a
- * - Support Abort command.
- * - Handle reset routine.
- * 09/21/98 hl - v1.03
- * - remove comments.
- * 12/09/98 bv - v1.03a
- * - Removed unused code
- * 12/13/98 bv - v1.03b
- * - Remove cli() locking for kernels >= 2.1.95. This uses
- * spinlocks to serialize access to the pSRB_head and
- * pSRB_tail members of the HCS structure.
- * 09/01/99 bv - v1.03d
- * - Fixed a deadlock problem in SMP.
- * 21/01/99 bv - v1.03e
- * - Add support for the Domex 3192U PCI SCSI
- * This is a slightly modified patch by
- * Brian Macy <bmacy@sunshinecomputing.com>
- * 22/02/99 bv - v1.03f
- * - Didn't detect the INIC-950 in 2.0.x correctly.
- * Now fixed.
- * 05/07/99 bv - v1.03g
- * - Changed the assumption that HZ = 100
- * 10/17/03 mc - v1.04
- * - added new DMA API support
- * 06/01/04 jmd - v1.04a
- * - Re-add reset_bus support
- **************************************************************************/
- #include <linux/module.h>
- #include <linux/errno.h>
- #include <linux/delay.h>
- #include <linux/pci.h>
- #include <linux/init.h>
- #include <linux/blkdev.h>
- #include <linux/spinlock.h>
- #include <linux/stat.h>
- #include <linux/kernel.h>
- #include <linux/proc_fs.h>
- #include <linux/string.h>
- #include <linux/interrupt.h>
- #include <linux/ioport.h>
- #include <linux/slab.h>
- #include <linux/jiffies.h>
- #include <linux/dma-mapping.h>
- #include <asm/io.h>
- #include <scsi/scsi.h>
- #include <scsi/scsi_cmnd.h>
- #include <scsi/scsi_device.h>
- #include <scsi/scsi_host.h>
- #include <scsi/scsi_tcq.h>
- #include "initio.h"
- #define SENSE_SIZE 14
- #define i91u_MAXQUEUE 2
- #define i91u_REVID "Initio INI-9X00U/UW SCSI device driver; Revision: 1.04a"
- #ifdef DEBUG_i91u
- static unsigned int i91u_debug = DEBUG_DEFAULT;
- #endif
- static int initio_tag_enable = 1;
- #ifdef DEBUG_i91u
- static int setup_debug = 0;
- #endif
- static void i91uSCBPost(u8 * pHcb, u8 * pScb);
- #define DEBUG_INTERRUPT 0
- #define DEBUG_QUEUE 0
- #define DEBUG_STATE 0
- #define INT_DISC 0
- /*--- forward references ---*/
- static struct scsi_ctrl_blk *initio_find_busy_scb(struct initio_host * host, u16 tarlun);
- static struct scsi_ctrl_blk *initio_find_done_scb(struct initio_host * host);
- static int tulip_main(struct initio_host * host);
- static int initio_next_state(struct initio_host * host);
- static int initio_state_1(struct initio_host * host);
- static int initio_state_2(struct initio_host * host);
- static int initio_state_3(struct initio_host * host);
- static int initio_state_4(struct initio_host * host);
- static int initio_state_5(struct initio_host * host);
- static int initio_state_6(struct initio_host * host);
- static int initio_state_7(struct initio_host * host);
- static int initio_xfer_data_in(struct initio_host * host);
- static int initio_xfer_data_out(struct initio_host * host);
- static int initio_xpad_in(struct initio_host * host);
- static int initio_xpad_out(struct initio_host * host);
- static int initio_status_msg(struct initio_host * host);
- static int initio_msgin(struct initio_host * host);
- static int initio_msgin_sync(struct initio_host * host);
- static int initio_msgin_accept(struct initio_host * host);
- static int initio_msgout_reject(struct initio_host * host);
- static int initio_msgin_extend(struct initio_host * host);
- static int initio_msgout_ide(struct initio_host * host);
- static int initio_msgout_abort_targ(struct initio_host * host);
- static int initio_msgout_abort_tag(struct initio_host * host);
- static int initio_bus_device_reset(struct initio_host * host);
- static void initio_select_atn(struct initio_host * host, struct scsi_ctrl_blk * scb);
- static void initio_select_atn3(struct initio_host * host, struct scsi_ctrl_blk * scb);
- static void initio_select_atn_stop(struct initio_host * host, struct scsi_ctrl_blk * scb);
- static int int_initio_busfree(struct initio_host * host);
- static int int_initio_scsi_rst(struct initio_host * host);
- static int int_initio_bad_seq(struct initio_host * host);
- static int int_initio_resel(struct initio_host * host);
- static int initio_sync_done(struct initio_host * host);
- static int wdtr_done(struct initio_host * host);
- static int wait_tulip(struct initio_host * host);
- static int initio_wait_done_disc(struct initio_host * host);
- static int initio_wait_disc(struct initio_host * host);
- static void tulip_scsi(struct initio_host * host);
- static int initio_post_scsi_rst(struct initio_host * host);
- static void initio_se2_ew_en(unsigned long base);
- static void initio_se2_ew_ds(unsigned long base);
- static int initio_se2_rd_all(unsigned long base);
- static void initio_se2_update_all(unsigned long base); /* setup default pattern */
- static void initio_read_eeprom(unsigned long base);
- /* ---- INTERNAL VARIABLES ---- */
- static NVRAM i91unvram;
- static NVRAM *i91unvramp;
- static u8 i91udftNvRam[64] =
- {
- /*----------- header -----------*/
- 0x25, 0xc9, /* Signature */
- 0x40, /* Size */
- 0x01, /* Revision */
- /* -- Host Adapter Structure -- */
- 0x95, /* ModelByte0 */
- 0x00, /* ModelByte1 */
- 0x00, /* ModelInfo */
- 0x01, /* NumOfCh */
- NBC1_DEFAULT, /* BIOSConfig1 */
- 0, /* BIOSConfig2 */
- 0, /* HAConfig1 */
- 0, /* HAConfig2 */
- /* SCSI channel 0 and target Structure */
- 7, /* SCSIid */
- NCC1_DEFAULT, /* SCSIconfig1 */
- 0, /* SCSIconfig2 */
- 0x10, /* NumSCSItarget */
- NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
- NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
- NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
- NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
- /* SCSI channel 1 and target Structure */
- 7, /* SCSIid */
- NCC1_DEFAULT, /* SCSIconfig1 */
- 0, /* SCSIconfig2 */
- 0x10, /* NumSCSItarget */
- NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
- NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
- NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
- NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0}; /* - CheckSum - */
- static u8 initio_rate_tbl[8] = /* fast 20 */
- {
- /* nanosecond divide by 4 */
- 12, /* 50ns, 20M */
- 18, /* 75ns, 13.3M */
- 25, /* 100ns, 10M */
- 31, /* 125ns, 8M */
- 37, /* 150ns, 6.6M */
- 43, /* 175ns, 5.7M */
- 50, /* 200ns, 5M */
- 62 /* 250ns, 4M */
- };
- static void initio_do_pause(unsigned amount)
- {
- /* Pause for amount jiffies */
- unsigned long the_time = jiffies + amount;
- while (time_before_eq(jiffies, the_time))
- cpu_relax();
- }
- /*-- forward reference --*/
- /******************************************************************
- Input: instruction for Serial E2PROM
- EX: se2_rd(0 call se2_instr() to send address and read command
- StartBit OP_Code Address Data
- --------- -------- ------------------ -------
- 1 1 , 0 A5,A4,A3,A2,A1,A0 D15-D0
- +-----------------------------------------------------
- |
- CS -----+
- +--+ +--+ +--+ +--+ +--+
- ^ | ^ | ^ | ^ | ^ |
- | | | | | | | | | |
- CLK -------+ +--+ +--+ +--+ +--+ +--
- (leading edge trigger)
- +--1-----1--+
- | SB OP | OP A5 A4
- DI ----+ +--0------------------
- (address and cmd sent to nvram)
- -------------------------------------------+
- |
- DO +---
- (data sent from nvram)
- ******************************************************************/
- /**
- * initio_se2_instr - bitbang an instruction
- * @base: Base of InitIO controller
- * @instr: Instruction for serial E2PROM
- *
- * Bitbang an instruction out to the serial E2Prom
- */
- static void initio_se2_instr(unsigned long base, u8 instr)
- {
- int i;
- u8 b;
- outb(SE2CS | SE2DO, base + TUL_NVRAM); /* cs+start bit */
- udelay(30);
- outb(SE2CS | SE2CLK | SE2DO, base + TUL_NVRAM); /* +CLK */
- udelay(30);
- for (i = 0; i < 8; i++) {
- if (instr & 0x80)
- b = SE2CS | SE2DO; /* -CLK+dataBit */
- else
- b = SE2CS; /* -CLK */
- outb(b, base + TUL_NVRAM);
- udelay(30);
- outb(b | SE2CLK, base + TUL_NVRAM); /* +CLK */
- udelay(30);
- instr <<= 1;
- }
- outb(SE2CS, base + TUL_NVRAM); /* -CLK */
- udelay(30);
- }
- /**
- * initio_se2_ew_en - Enable erase/write
- * @base: Base address of InitIO controller
- *
- * Enable erase/write state of serial EEPROM
- */
- void initio_se2_ew_en(unsigned long base)
- {
- initio_se2_instr(base, 0x30); /* EWEN */
- outb(0, base + TUL_NVRAM); /* -CS */
- udelay(30);
- }
- /**
- * initio_se2_ew_ds - Disable erase/write
- * @base: Base address of InitIO controller
- *
- * Disable erase/write state of serial EEPROM
- */
- void initio_se2_ew_ds(unsigned long base)
- {
- initio_se2_instr(base, 0); /* EWDS */
- outb(0, base + TUL_NVRAM); /* -CS */
- udelay(30);
- }
- /**
- * initio_se2_rd - read E2PROM word
- * @base: Base of InitIO controller
- * @addr: Address of word in E2PROM
- *
- * Read a word from the NV E2PROM device
- */
- static u16 initio_se2_rd(unsigned long base, u8 addr)
- {
- u8 instr, rb;
- u16 val = 0;
- int i;
- instr = (u8) (addr | 0x80);
- initio_se2_instr(base, instr); /* READ INSTR */
- for (i = 15; i >= 0; i--) {
- outb(SE2CS | SE2CLK, base + TUL_NVRAM); /* +CLK */
- udelay(30);
- outb(SE2CS, base + TUL_NVRAM); /* -CLK */
- /* sample data after the following edge of clock */
- rb = inb(base + TUL_NVRAM);
- rb &= SE2DI;
- val += (rb << i);
- udelay(30); /* 6/20/95 */
- }
- outb(0, base + TUL_NVRAM); /* no chip select */
- udelay(30);
- return val;
- }
- /**
- * initio_se2_wr - read E2PROM word
- * @base: Base of InitIO controller
- * @addr: Address of word in E2PROM
- * @val: Value to write
- *
- * Write a word to the NV E2PROM device. Used when recovering from
- * a problem with the NV.
- */
- static void initio_se2_wr(unsigned long base, u8 addr, u16 val)
- {
- u8 rb;
- u8 instr;
- int i;
- instr = (u8) (addr | 0x40);
- initio_se2_instr(base, instr); /* WRITE INSTR */
- for (i = 15; i >= 0; i--) {
- if (val & 0x8000)
- outb(SE2CS | SE2DO, base + TUL_NVRAM); /* -CLK+dataBit 1 */
- else
- outb(SE2CS, base + TUL_NVRAM); /* -CLK+dataBit 0 */
- udelay(30);
- outb(SE2CS | SE2CLK, base + TUL_NVRAM); /* +CLK */
- udelay(30);
- val <<= 1;
- }
- outb(SE2CS, base + TUL_NVRAM); /* -CLK */
- udelay(30);
- outb(0, base + TUL_NVRAM); /* -CS */
- udelay(30);
- outb(SE2CS, base + TUL_NVRAM); /* +CS */
- udelay(30);
- for (;;) {
- outb(SE2CS | SE2CLK, base + TUL_NVRAM); /* +CLK */
- udelay(30);
- outb(SE2CS, base + TUL_NVRAM); /* -CLK */
- udelay(30);
- if ((rb = inb(base + TUL_NVRAM)) & SE2DI)
- break; /* write complete */
- }
- outb(0, base + TUL_NVRAM); /* -CS */
- }
- /**
- * initio_se2_rd_all - read hostadapter NV configuration
- * @base: Base address of InitIO controller
- *
- * Reads the E2PROM data into main memory. Ensures that the checksum
- * and header marker are valid. Returns 1 on success -1 on error.
- */
- static int initio_se2_rd_all(unsigned long base)
- {
- int i;
- u16 chksum = 0;
- u16 *np;
- i91unvramp = &i91unvram;
- np = (u16 *) i91unvramp;
- for (i = 0; i < 32; i++)
- *np++ = initio_se2_rd(base, i);
- /* Is signature "ini" ok ? */
- if (i91unvramp->NVM_Signature != INI_SIGNATURE)
- return -1;
- /* Is ckecksum ok ? */
- np = (u16 *) i91unvramp;
- for (i = 0; i < 31; i++)
- chksum += *np++;
- if (i91unvramp->NVM_CheckSum != chksum)
- return -1;
- return 1;
- }
- /**
- * initio_se2_update_all - Update E2PROM
- * @base: Base of InitIO controller
- *
- * Update the E2PROM by wrting any changes into the E2PROM
- * chip, rewriting the checksum.
- */
- static void initio_se2_update_all(unsigned long base)
- { /* setup default pattern */
- int i;
- u16 chksum = 0;
- u16 *np, *np1;
- i91unvramp = &i91unvram;
- /* Calculate checksum first */
- np = (u16 *) i91udftNvRam;
- for (i = 0; i < 31; i++)
- chksum += *np++;
- *np = chksum;
- initio_se2_ew_en(base); /* Enable write */
- np = (u16 *) i91udftNvRam;
- np1 = (u16 *) i91unvramp;
- for (i = 0; i < 32; i++, np++, np1++) {
- if (*np != *np1)
- initio_se2_wr(base, i, *np);
- }
- initio_se2_ew_ds(base); /* Disable write */
- }
- /**
- * initio_read_eeprom - Retrieve configuration
- * @base: Base of InitIO Host Adapter
- *
- * Retrieve the host adapter configuration data from E2Prom. If the
- * data is invalid then the defaults are used and are also restored
- * into the E2PROM. This forms the access point for the SCSI driver
- * into the E2PROM layer, the other functions for the E2PROM are all
- * internal use.
- *
- * Must be called single threaded, uses a shared global area.
- */
- static void initio_read_eeprom(unsigned long base)
- {
- u8 gctrl;
- i91unvramp = &i91unvram;
- /* Enable EEProm programming */
- gctrl = inb(base + TUL_GCTRL);
- outb(gctrl | TUL_GCTRL_EEPROM_BIT, base + TUL_GCTRL);
- if (initio_se2_rd_all(base) != 1) {
- initio_se2_update_all(base); /* setup default pattern */
- initio_se2_rd_all(base); /* load again */
- }
- /* Disable EEProm programming */
- gctrl = inb(base + TUL_GCTRL);
- outb(gctrl & ~TUL_GCTRL_EEPROM_BIT, base + TUL_GCTRL);
- }
- /**
- * initio_stop_bm - stop bus master
- * @host: InitIO we are stopping
- *
- * Stop any pending DMA operation, aborting the DMA if necessary
- */
- static void initio_stop_bm(struct initio_host * host)
- {
- if (inb(host->addr + TUL_XStatus) & XPEND) { /* if DMA xfer is pending, abort DMA xfer */
- outb(TAX_X_ABT | TAX_X_CLR_FIFO, host->addr + TUL_XCmd);
- /* wait Abort DMA xfer done */
- while ((inb(host->addr + TUL_Int) & XABT) == 0)
- cpu_relax();
- }
- outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0);
- }
- /**
- * initio_reset_scsi - Reset SCSI host controller
- * @host: InitIO host to reset
- * @seconds: Recovery time
- *
- * Perform a full reset of the SCSI subsystem.
- */
- static int initio_reset_scsi(struct initio_host * host, int seconds)
- {
- outb(TSC_RST_BUS, host->addr + TUL_SCtrl0);
- while (!((host->jsint = inb(host->addr + TUL_SInt)) & TSS_SCSIRST_INT))
- cpu_relax();
- /* reset tulip chip */
- outb(0, host->addr + TUL_SSignal);
- /* Stall for a while, wait for target's firmware ready,make it 2 sec ! */
- /* SONY 5200 tape drive won't work if only stall for 1 sec */
- /* FIXME: this is a very long busy wait right now */
- initio_do_pause(seconds * HZ);
- inb(host->addr + TUL_SInt);
- return SCSI_RESET_SUCCESS;
- }
- /**
- * initio_init - set up an InitIO host adapter
- * @host: InitIO host adapter
- * @num_scbs: Number of SCBS
- * @bios_addr: BIOS address
- *
- * Set up the host adapter and devices according to the configuration
- * retrieved from the E2PROM.
- *
- * Locking: Calls E2PROM layer code which is not re-enterable so must
- * run single threaded for now.
- */
- static void initio_init(struct initio_host * host, u8 *bios_addr)
- {
- int i;
- u8 *flags;
- u8 *heads;
- /* Get E2Prom configuration */
- initio_read_eeprom(host->addr);
- if (i91unvramp->NVM_SCSIInfo[0].NVM_NumOfTarg == 8)
- host->max_tar = 8;
- else
- host->max_tar = 16;
- host->config = i91unvramp->NVM_SCSIInfo[0].NVM_ChConfig1;
- host->scsi_id = i91unvramp->NVM_SCSIInfo[0].NVM_ChSCSIID;
- host->idmask = ~(1 << host->scsi_id);
- #ifdef CHK_PARITY
- /* Enable parity error response */
- outb(inb(host->addr + TUL_PCMD) | 0x40, host->addr + TUL_PCMD);
- #endif
- /* Mask all the interrupt */
- outb(0x1F, host->addr + TUL_Mask);
- initio_stop_bm(host);
- /* --- Initialize the tulip --- */
- outb(TSC_RST_CHIP, host->addr + TUL_SCtrl0);
- /* program HBA's SCSI ID */
- outb(host->scsi_id << 4, host->addr + TUL_SScsiId);
- /* Enable Initiator Mode ,phase latch,alternate sync period mode,
- disable SCSI reset */
- if (host->config & HCC_EN_PAR)
- host->sconf1 = (TSC_INITDEFAULT | TSC_EN_SCSI_PAR);
- else
- host->sconf1 = (TSC_INITDEFAULT);
- outb(host->sconf1, host->addr + TUL_SConfig);
- /* Enable HW reselect */
- outb(TSC_HW_RESELECT, host->addr + TUL_SCtrl1);
- outb(0, host->addr + TUL_SPeriod);
- /* selection time out = 250 ms */
- outb(153, host->addr + TUL_STimeOut);
- /* Enable SCSI terminator */
- outb((host->config & (HCC_ACT_TERM1 | HCC_ACT_TERM2)),
- host->addr + TUL_XCtrl);
- outb(((host->config & HCC_AUTO_TERM) >> 4) |
- (inb(host->addr + TUL_GCTRL1) & 0xFE),
- host->addr + TUL_GCTRL1);
- for (i = 0,
- flags = & (i91unvramp->NVM_SCSIInfo[0].NVM_Targ0Config),
- heads = bios_addr + 0x180;
- i < host->max_tar;
- i++, flags++) {
- host->targets[i].flags = *flags & ~(TCF_SYNC_DONE | TCF_WDTR_DONE);
- if (host->targets[i].flags & TCF_EN_255)
- host->targets[i].drv_flags = TCF_DRV_255_63;
- else
- host->targets[i].drv_flags = 0;
- host->targets[i].js_period = 0;
- host->targets[i].sconfig0 = host->sconf1;
- host->targets[i].heads = *heads++;
- if (host->targets[i].heads == 255)
- host->targets[i].drv_flags = TCF_DRV_255_63;
- else
- host->targets[i].drv_flags = 0;
- host->targets[i].sectors = *heads++;
- host->targets[i].flags &= ~TCF_BUSY;
- host->act_tags[i] = 0;
- host->max_tags[i] = 0xFF;
- } /* for */
- printk("i91u: PCI Base=0x%04X, IRQ=%d, BIOS=0x%04X0, SCSI ID=%d\n",
- host->addr, host->pci_dev->irq,
- host->bios_addr, host->scsi_id);
- /* Reset SCSI Bus */
- if (host->config & HCC_SCSI_RESET) {
- printk(KERN_INFO "i91u: Reset SCSI Bus ... \n");
- initio_reset_scsi(host, 10);
- }
- outb(0x17, host->addr + TUL_SCFG1);
- outb(0xE9, host->addr + TUL_SIntEnable);
- }
- /**
- * initio_alloc_scb - Allocate an SCB
- * @host: InitIO host we are allocating for
- *
- * Walk the SCB list for the controller and allocate a free SCB if
- * one exists.
- */
- static struct scsi_ctrl_blk *initio_alloc_scb(struct initio_host *host)
- {
- struct scsi_ctrl_blk *scb;
- unsigned long flags;
- spin_lock_irqsave(&host->avail_lock, flags);
- if ((scb = host->first_avail) != NULL) {
- #if DEBUG_QUEUE
- printk("find scb at %p\n", scb);
- #endif
- if ((host->first_avail = scb->next) == NULL)
- host->last_avail = NULL;
- scb->next = NULL;
- scb->status = SCB_RENT;
- }
- spin_unlock_irqrestore(&host->avail_lock, flags);
- return scb;
- }
- /**
- * initio_release_scb - Release an SCB
- * @host: InitIO host that owns the SCB
- * @cmnd: SCB command block being returned
- *
- * Return an allocated SCB to the host free list
- */
- static void initio_release_scb(struct initio_host * host, struct scsi_ctrl_blk * cmnd)
- {
- unsigned long flags;
- #if DEBUG_QUEUE
- printk("Release SCB %p; ", cmnd);
- #endif
- spin_lock_irqsave(&(host->avail_lock), flags);
- cmnd->srb = NULL;
- cmnd->status = 0;
- cmnd->next = NULL;
- if (host->last_avail != NULL) {
- host->last_avail->next = cmnd;
- host->last_avail = cmnd;
- } else {
- host->first_avail = cmnd;
- host->last_avail = cmnd;
- }
- spin_unlock_irqrestore(&(host->avail_lock), flags);
- }
- /***************************************************************************/
- static void initio_append_pend_scb(struct initio_host * host, struct scsi_ctrl_blk * scbp)
- {
- #if DEBUG_QUEUE
- printk("Append pend SCB %p; ", scbp);
- #endif
- scbp->status = SCB_PEND;
- scbp->next = NULL;
- if (host->last_pending != NULL) {
- host->last_pending->next = scbp;
- host->last_pending = scbp;
- } else {
- host->first_pending = scbp;
- host->last_pending = scbp;
- }
- }
- /***************************************************************************/
- static void initio_push_pend_scb(struct initio_host * host, struct scsi_ctrl_blk * scbp)
- {
- #if DEBUG_QUEUE
- printk("Push pend SCB %p; ", scbp);
- #endif
- scbp->status = SCB_PEND;
- if ((scbp->next = host->first_pending) != NULL) {
- host->first_pending = scbp;
- } else {
- host->first_pending = scbp;
- host->last_pending = scbp;
- }
- }
- static struct scsi_ctrl_blk *initio_find_first_pend_scb(struct initio_host * host)
- {
- struct scsi_ctrl_blk *first;
- first = host->first_pending;
- while (first != NULL) {
- if (first->opcode != ExecSCSI)
- return first;
- if (first->tagmsg == 0) {
- if ((host->act_tags[first->target] == 0) &&
- !(host->targets[first->target].flags & TCF_BUSY))
- return first;
- } else {
- if ((host->act_tags[first->target] >=
- host->max_tags[first->target]) |
- (host->targets[first->target].flags & TCF_BUSY)) {
- first = first->next;
- continue;
- }
- return first;
- }
- first = first->next;
- }
- return first;
- }
- static void initio_unlink_pend_scb(struct initio_host * host, struct scsi_ctrl_blk * scb)
- {
- struct scsi_ctrl_blk *tmp, *prev;
- #if DEBUG_QUEUE
- printk("unlink pend SCB %p; ", scb);
- #endif
- prev = tmp = host->first_pending;
- while (tmp != NULL) {
- if (scb == tmp) { /* Unlink this SCB */
- if (tmp == host->first_pending) {
- if ((host->first_pending = tmp->next) == NULL)
- host->last_pending = NULL;
- } else {
- prev->next = tmp->next;
- if (tmp == host->last_pending)
- host->last_pending = prev;
- }
- tmp->next = NULL;
- break;
- }
- prev = tmp;
- tmp = tmp->next;
- }
- }
- static void initio_append_busy_scb(struct initio_host * host, struct scsi_ctrl_blk * scbp)
- {
- #if DEBUG_QUEUE
- printk("append busy SCB %p; ", scbp);
- #endif
- if (scbp->tagmsg)
- host->act_tags[scbp->target]++;
- else
- host->targets[scbp->target].flags |= TCF_BUSY;
- scbp->status = SCB_BUSY;
- scbp->next = NULL;
- if (host->last_busy != NULL) {
- host->last_busy->next = scbp;
- host->last_busy = scbp;
- } else {
- host->first_busy = scbp;
- host->last_busy = scbp;
- }
- }
- /***************************************************************************/
- static struct scsi_ctrl_blk *initio_pop_busy_scb(struct initio_host * host)
- {
- struct scsi_ctrl_blk *tmp;
- if ((tmp = host->first_busy) != NULL) {
- if ((host->first_busy = tmp->next) == NULL)
- host->last_busy = NULL;
- tmp->next = NULL;
- if (tmp->tagmsg)
- host->act_tags[tmp->target]--;
- else
- host->targets[tmp->target].flags &= ~TCF_BUSY;
- }
- #if DEBUG_QUEUE
- printk("Pop busy SCB %p; ", tmp);
- #endif
- return tmp;
- }
- /***************************************************************************/
- static void initio_unlink_busy_scb(struct initio_host * host, struct scsi_ctrl_blk * scb)
- {
- struct scsi_ctrl_blk *tmp, *prev;
- #if DEBUG_QUEUE
- printk("unlink busy SCB %p; ", scb);
- #endif
- prev = tmp = host->first_busy;
- while (tmp != NULL) {
- if (scb == tmp) { /* Unlink this SCB */
- if (tmp == host->first_busy) {
- if ((host->first_busy = tmp->next) == NULL)
- host->last_busy = NULL;
- } else {
- prev->next = tmp->next;
- if (tmp == host->last_busy)
- host->last_busy = prev;
- }
- tmp->next = NULL;
- if (tmp->tagmsg)
- host->act_tags[tmp->target]--;
- else
- host->targets[tmp->target].flags &= ~TCF_BUSY;
- break;
- }
- prev = tmp;
- tmp = tmp->next;
- }
- return;
- }
- struct scsi_ctrl_blk *initio_find_busy_scb(struct initio_host * host, u16 tarlun)
- {
- struct scsi_ctrl_blk *tmp, *prev;
- u16 scbp_tarlun;
- prev = tmp = host->first_busy;
- while (tmp != NULL) {
- scbp_tarlun = (tmp->lun << 8) | (tmp->target);
- if (scbp_tarlun == tarlun) { /* Unlink this SCB */
- break;
- }
- prev = tmp;
- tmp = tmp->next;
- }
- #if DEBUG_QUEUE
- printk("find busy SCB %p; ", tmp);
- #endif
- return tmp;
- }
- static void initio_append_done_scb(struct initio_host * host, struct scsi_ctrl_blk * scbp)
- {
- #if DEBUG_QUEUE
- printk("append done SCB %p; ", scbp);
- #endif
- scbp->status = SCB_DONE;
- scbp->next = NULL;
- if (host->last_done != NULL) {
- host->last_done->next = scbp;
- host->last_done = scbp;
- } else {
- host->first_done = scbp;
- host->last_done = scbp;
- }
- }
- struct scsi_ctrl_blk *initio_find_done_scb(struct initio_host * host)
- {
- struct scsi_ctrl_blk *tmp;
- if ((tmp = host->first_done) != NULL) {
- if ((host->first_done = tmp->next) == NULL)
- host->last_done = NULL;
- tmp->next = NULL;
- }
- #if DEBUG_QUEUE
- printk("find done SCB %p; ",tmp);
- #endif
- return tmp;
- }
- static int initio_abort_srb(struct initio_host * host, struct scsi_cmnd *srbp)
- {
- unsigned long flags;
- struct scsi_ctrl_blk *tmp, *prev;
- spin_lock_irqsave(&host->semaph_lock, flags);
- if ((host->semaph == 0) && (host->active == NULL)) {
- /* disable Jasmin SCSI Int */
- outb(0x1F, host->addr + TUL_Mask);
- spin_unlock_irqrestore(&host->semaph_lock, flags);
- /* FIXME: synchronize_irq needed ? */
- tulip_main(host);
- spin_lock_irqsave(&host->semaph_lock, flags);
- host->semaph = 1;
- outb(0x0F, host->addr + TUL_Mask);
- spin_unlock_irqrestore(&host->semaph_lock, flags);
- return SCSI_ABORT_SNOOZE;
- }
- prev = tmp = host->first_pending; /* Check Pend queue */
- while (tmp != NULL) {
- /* 07/27/98 */
- if (tmp->srb == srbp) {
- if (tmp == host->active) {
- spin_unlock_irqrestore(&host->semaph_lock, flags);
- return SCSI_ABORT_BUSY;
- } else if (tmp == host->first_pending) {
- if ((host->first_pending = tmp->next) == NULL)
- host->last_pending = NULL;
- } else {
- prev->next = tmp->next;
- if (tmp == host->last_pending)
- host->last_pending = prev;
- }
- tmp->hastat = HOST_ABORTED;
- tmp->flags |= SCF_DONE;
- if (tmp->flags & SCF_POST)
- (*tmp->post) ((u8 *) host, (u8 *) tmp);
- spin_unlock_irqrestore(&host->semaph_lock, flags);
- return SCSI_ABORT_SUCCESS;
- }
- prev = tmp;
- tmp = tmp->next;
- }
- prev = tmp = host->first_busy; /* Check Busy queue */
- while (tmp != NULL) {
- if (tmp->srb == srbp) {
- if (tmp == host->active) {
- spin_unlock_irqrestore(&host->semaph_lock, flags);
- return SCSI_ABORT_BUSY;
- } else if (tmp->tagmsg == 0) {
- spin_unlock_irqrestore(&host->semaph_lock, flags);
- return SCSI_ABORT_BUSY;
- } else {
- host->act_tags[tmp->target]--;
- if (tmp == host->first_busy) {
- if ((host->first_busy = tmp->next) == NULL)
- host->last_busy = NULL;
- } else {
- prev->next = tmp->next;
- if (tmp == host->last_busy)
- host->last_busy = prev;
- }
- tmp->next = NULL;
- tmp->hastat = HOST_ABORTED;
- tmp->flags |= SCF_DONE;
- if (tmp->flags & SCF_POST)
- (*tmp->post) ((u8 *) host, (u8 *) tmp);
- spin_unlock_irqrestore(&host->semaph_lock, flags);
- return SCSI_ABORT_SUCCESS;
- }
- }
- prev = tmp;
- tmp = tmp->next;
- }
- spin_unlock_irqrestore(&host->semaph_lock, flags);
- return SCSI_ABORT_NOT_RUNNING;
- }
- /***************************************************************************/
- static int initio_bad_seq(struct initio_host * host)
- {
- struct scsi_ctrl_blk *scb;
- printk("initio_bad_seg c=%d\n", host->index);
- if ((scb = host->active) != NULL) {
- initio_unlink_busy_scb(host, scb);
- scb->hastat = HOST_BAD_PHAS;
- scb->tastat = 0;
- initio_append_done_scb(host, scb);
- }
- initio_stop_bm(host);
- initio_reset_scsi(host, 8); /* 7/29/98 */
- return initio_post_scsi_rst(host);
- }
- /************************************************************************/
- static void initio_exec_scb(struct initio_host * host, struct scsi_ctrl_blk * scb)
- {
- unsigned long flags;
- scb->mode = 0;
- scb->sgidx = 0;
- scb->sgmax = scb->sglen;
- spin_lock_irqsave(&host->semaph_lock, flags);
- initio_append_pend_scb(host, scb); /* Append this SCB to Pending queue */
- /* VVVVV 07/21/98 */
- if (host->semaph == 1) {
- /* Disable Jasmin SCSI Int */
- outb(0x1F, host->addr + TUL_Mask);
- host->semaph = 0;
- spin_unlock_irqrestore(&host->semaph_lock, flags);
- tulip_main(host);
- spin_lock_irqsave(&host->semaph_lock, flags);
- host->semaph = 1;
- outb(0x0F, host->addr + TUL_Mask);
- }
- spin_unlock_irqrestore(&host->semaph_lock, flags);
- return;
- }
- /***************************************************************************/
- static int initio_isr(struct initio_host * host)
- {
- if (inb(host->addr + TUL_Int) & TSS_INT_PENDING) {
- if (host->semaph == 1) {
- outb(0x1F, host->addr + TUL_Mask);
- /* Disable Tulip SCSI Int */
- host->semaph = 0;
- tulip_main(host);
- host->semaph = 1;
- outb(0x0F, host->addr + TUL_Mask);
- return 1;
- }
- }
- return 0;
- }
- static int tulip_main(struct initio_host * host)
- {
- struct scsi_ctrl_blk *scb;
- for (;;) {
- tulip_scsi(host); /* Call tulip_scsi */
- /* Walk the list of completed SCBs */
- while ((scb = initio_find_done_scb(host)) != NULL) { /* find done entry */
- if (scb->tastat == INI_QUEUE_FULL) {
- host->max_tags[scb->target] =
- host->act_tags[scb->target] - 1;
- scb->tastat = 0;
- initio_append_pend_scb(host, scb);
- continue;
- }
- if (!(scb->mode & SCM_RSENS)) { /* not in auto req. sense mode */
- if (scb->tastat == 2) {
- /* clr sync. nego flag */
- if (scb->flags & SCF_SENSE) {
- u8 len;
- len = scb->senselen;
- if (len == 0)
- len = 1;
- scb->buflen = scb->senselen;
- scb->bufptr = scb->senseptr;
- scb->flags &= ~(SCF_SG | SCF_DIR); /* for xfer_data_in */
- /* so, we won't report wrong direction in xfer_data_in,
- and won't report HOST_DO_DU in state_6 */
- scb->mode = SCM_RSENS;
- scb->ident &= 0xBF; /* Disable Disconnect */
- scb->tagmsg = 0;
- scb->tastat = 0;
- scb->cdblen = 6;
- scb->cdb[0] = SCSICMD_RequestSense;
- scb->cdb[1] = 0;
- scb->cdb[2] = 0;
- scb->cdb[3] = 0;
- scb->cdb[4] = len;
- scb->cdb[5] = 0;
- initio_push_pend_scb(host, scb);
- break;
- }
- }
- } else { /* in request sense mode */
- if (scb->tastat == 2) { /* check contition status again after sending
- requset sense cmd 0x3 */
- scb->hastat = HOST_BAD_PHAS;
- }
- scb->tastat = 2;
- }
- scb->flags |= SCF_DONE;
- if (scb->flags & SCF_POST) {
- /* FIXME: only one post method and lose casts */
- (*scb->post) ((u8 *) host, (u8 *) scb);
- }
- } /* while */
- /* find_active: */
- if (inb(host->addr + TUL_SStatus0) & TSS_INT_PENDING)
- continue;
- if (host->active) /* return to OS and wait for xfer_done_ISR/Selected_ISR */
- return 1; /* return to OS, enable interrupt */
- /* Check pending SCB */
- if (initio_find_first_pend_scb(host) == NULL)
- return 1; /* return to OS, enable interrupt */
- } /* End of for loop */
- /* statement won't reach here */
- }
- static void tulip_scsi(struct initio_host * host)
- {
- struct scsi_ctrl_blk *scb;
- struct target_control *active_tc;
- /* make sure to service interrupt asap */
- if ((host->jsstatus0 = inb(host->addr + TUL_SStatus0)) & TSS_INT_PENDING) {
- host->phase = host->jsstatus0 & TSS_PH_MASK;
- host->jsstatus1 = inb(host->addr + TUL_SStatus1);
- host->jsint = inb(host->addr + TUL_SInt);
- if (host->jsint & TSS_SCSIRST_INT) { /* SCSI bus reset detected */
- int_initio_scsi_rst(host);
- return;
- }
- if (host->jsint & TSS_RESEL_INT) { /* if selected/reselected interrupt */
- if (int_initio_resel(host) == 0)
- initio_next_state(host);
- return;
- }
- if (host->jsint & TSS_SEL_TIMEOUT) {
- int_initio_busfree(host);
- return;
- }
- if (host->jsint & TSS_DISC_INT) { /* BUS disconnection */
- int_initio_busfree(host); /* unexpected bus free or sel timeout */
- return;
- }
- if (host->jsint & (TSS_FUNC_COMP | TSS_BUS_SERV)) { /* func complete or Bus service */
- if ((scb = host->active) != NULL)
- initio_next_state(host);
- return;
- }
- }
- if (host->active != NULL)
- return;
- if ((scb = initio_find_first_pend_scb(host)) == NULL)
- return;
- /* program HBA's SCSI ID & target SCSI ID */
- outb((host->scsi_id << 4) | (scb->target & 0x0F),
- host->addr + TUL_SScsiId);
- if (scb->opcode == ExecSCSI) {
- active_tc = &host->targets[scb->target];
- if (scb->tagmsg)
- active_tc->drv_flags |= TCF_DRV_EN_TAG;
- else
- active_tc->drv_flags &= ~TCF_DRV_EN_TAG;
- outb(active_tc->js_period, host->addr + TUL_SPeriod);
- if ((active_tc->flags & (TCF_WDTR_DONE | TCF_NO_WDTR)) == 0) { /* do wdtr negotiation */
- initio_select_atn_stop(host, scb);
- } else {
- if ((active_tc->flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0) { /* do sync negotiation */
- initio_select_atn_stop(host, scb);
- } else {
- if (scb->tagmsg)
- initio_select_atn3(host, scb);
- else
- initio_select_atn(host, scb);
- }
- }
- if (scb->flags & SCF_POLL) {
- while (wait_tulip(host) != -1) {
- if (initio_next_state(host) == -1)
- break;
- }
- }
- } else if (scb->opcode == BusDevRst) {
- initio_select_atn_stop(host, scb);
- scb->next_state = 8;
- if (scb->flags & SCF_POLL) {
- while (wait_tulip(host) != -1) {
- if (initio_next_state(host) == -1)
- break;
- }
- }
- } else if (scb->opcode == AbortCmd) {
- if (initio_abort_srb(host, scb->srb) != 0) {
- initio_unlink_pend_scb(host, scb);
- initio_release_scb(host, scb);
- } else {
- scb->opcode = BusDevRst;
- initio_select_atn_stop(host, scb);
- scb->next_state = 8;
- }
- } else {
- initio_unlink_pend_scb(host, scb);
- scb->hastat = 0x16; /* bad command */
- initio_append_done_scb(host, scb);
- }
- return;
- }
- /**
- * initio_next_state - Next SCSI state
- * @host: InitIO host we are processing
- *
- * Progress the active command block along the state machine
- * until we hit a state which we must wait for activity to occur.
- *
- * Returns zero or a negative code.
- */
- static int initio_next_state(struct initio_host * host)
- {
- int next;
- next = host->active->next_state;
- for (;;) {
- switch (next) {
- case 1:
- next = initio_state_1(host);
- break;
- case 2:
- next = initio_state_2(host);
- break;
- case 3:
- next = initio_state_3(host);
- break;
- case 4:
- next = initio_state_4(host);
- break;
- case 5:
- next = initio_state_5(host);
- break;
- case 6:
- next = initio_state_6(host);
- break;
- case 7:
- next = initio_state_7(host);
- break;
- case 8:
- return initio_bus_device_reset(host);
- default:
- return initio_bad_seq(host);
- }
- if (next <= 0)
- return next;
- }
- }
- /**
- * initio_state_1 - SCSI state machine
- * @host: InitIO host we are controlling
- *
- * Perform SCSI state processing for Select/Attention/Stop
- */
- static int initio_state_1(struct initio_host * host)
- {
- struct scsi_ctrl_blk *scb = host->active;
- struct target_control *active_tc = host->active_tc;
- #if DEBUG_STATE
- printk("-s1-");
- #endif
- /* Move the SCB from pending to busy */
- initio_unlink_pend_scb(host, scb);
- initio_append_busy_scb(host, scb);
- outb(active_tc->sconfig0, host->addr + TUL_SConfig );
- /* ATN on */
- if (host->phase == MSG_OUT) {
- outb(TSC_EN_BUS_IN | TSC_HW_RESELECT, host->addr + TUL_SCtrl1);
- outb(scb->ident, host->addr + TUL_SFifo);
- if (scb->tagmsg) {
- outb(scb->tagmsg, host->addr + TUL_SFifo);
- outb(scb->tagid, host->addr + TUL_SFifo);
- }
- if ((active_tc->flags & (TCF_WDTR_DONE | TCF_NO_WDTR)) == 0) {
- active_tc->flags |= TCF_WDTR_DONE;
- outb(MSG_EXTEND, host->addr + TUL_SFifo);
- outb(2, host->addr + TUL_SFifo); /* Extended msg length */
- outb(3, host->addr + TUL_SFifo); /* Sync request */
- outb(1, host->addr + TUL_SFifo); /* Start from 16 bits */
- } else if ((active_tc->flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0) {
- active_tc->flags |= TCF_SYNC_DONE;
- outb(MSG_EXTEND, host->addr + TUL_SFifo);
- outb(3, host->addr + TUL_SFifo); /* extended msg length */
- outb(1, host->addr + TUL_SFifo); /* sync request */
- outb(initio_rate_tbl[active_tc->flags & TCF_SCSI_RATE], host->addr + TUL_SFifo);
- outb(MAX_OFFSET, host->addr + TUL_SFifo); /* REQ/ACK offset */
- }
- outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
- if (wait_tulip(host) == -1)
- return -1;
- }
- outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0);
- outb((inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7)), host->addr + TUL_SSignal);
- /* Into before CDB xfer */
- return 3;
- }
- /**
- * initio_state_2 - SCSI state machine
- * @host: InitIO host we are controlling
- *
- * state after selection with attention
- * state after selection with attention3
- */
- static int initio_state_2(struct initio_host * host)
- {
- struct scsi_ctrl_blk *scb = host->active;
- struct target_control *active_tc = host->active_tc;
- #if DEBUG_STATE
- printk("-s2-");
- #endif
- initio_unlink_pend_scb(host, scb);
- initio_append_busy_scb(host, scb);
- outb(active_tc->sconfig0, host->addr + TUL_SConfig);
- if (host->jsstatus1 & TSS_CMD_PH_CMP)
- return 4;
- outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0);
- outb((inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7)), host->addr + TUL_SSignal);
- /* Into before CDB xfer */
- return 3;
- }
- /**
- * initio_state_3 - SCSI state machine
- * @host: InitIO host we are controlling
- *
- * state before CDB xfer is done
- */
- static int initio_state_3(struct initio_host * host)
- {
- struct scsi_ctrl_blk *scb = host->active;
- struct target_control *active_tc = host->active_tc;
- int i;
- #if DEBUG_STATE
- printk("-s3-");
- #endif
- for (;;) {
- switch (host->phase) {
- case CMD_OUT: /* Command out phase */
- for (i = 0; i < (int) scb->cdblen; i++)
- outb(scb->cdb[i], host->addr + TUL_SFifo);
- outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
- if (wait_tulip(host) == -1)
- return -1;
- if (host->phase == CMD_OUT)
- return initio_bad_seq(host);
- return 4;
- case MSG_IN: /* Message in phase */
- scb->next_state = 3;
- if (initio_msgin(host) == -1)
- return -1;
- break;
- case STATUS_IN: /* Status phase */
- if (initio_status_msg(host) == -1)
- return -1;
- break;
- case MSG_OUT: /* Message out phase */
- if (active_tc->flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) {
- outb(MSG_NOP, host->addr + TUL_SFifo); /* msg nop */
- outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
- if (wait_tulip(host) == -1)
- return -1;
- } else {
- active_tc->flags |= TCF_SYNC_DONE;
- outb(MSG_EXTEND, host->addr + TUL_SFifo);
- outb(3, host->addr + TUL_SFifo); /* ext. msg len */
- outb(1, host->addr + TUL_SFifo); /* sync request */
- outb(initio_rate_tbl[active_tc->flags & TCF_SCSI_RATE], host->addr + TUL_SFifo);
- outb(MAX_OFFSET, host->addr + TUL_SFifo); /* REQ/ACK offset */
- outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
- if (wait_tulip(host) == -1)
- return -1;
- outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0);
- outb(inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7), host->addr + TUL_SSignal);
- }
- break;
- default:
- return initio_bad_seq(host);
- }
- }
- }
- /**
- * initio_state_4 - SCSI state machine
- * @host: InitIO host we are controlling
- *
- * SCSI state machine. State 4
- */
- static int initio_state_4(struct initio_host * host)
- {
- struct scsi_ctrl_blk *scb = host->active;
- #if DEBUG_STATE
- printk("-s4-");
- #endif
- if ((scb->flags & SCF_DIR) == SCF_NO_XF) {
- return 6; /* Go to state 6 (After data) */
- }
- for (;;) {
- if (scb->buflen == 0)
- return 6;
- switch (host->phase) {
- case STATUS_IN: /* Status phase */
- if ((scb->flags & SCF_DIR) != 0) /* if direction bit set then report data underrun */
- scb->hastat = HOST_DO_DU;
- if ((initio_status_msg(host)) == -1)
- return -1;
- break;
- case MSG_IN: /* Message in phase */
- scb->next_state = 0x4;
- if (initio_msgin(host) == -1)
- return -1;
- break;
- case MSG_OUT: /* Message out phase */
- if (host->jsstatus0 & TSS_PAR_ERROR) {
- scb->buflen = 0;
- scb->hastat = HOST_DO_DU;
- if (initio_msgout_ide(host) == -1)
- return -1;
- return 6;
- } else {
- outb(MSG_NOP, host->addr + TUL_SFifo); /* msg nop */
- outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
- if (wait_tulip(host) == -1)
- return -1;
- }
- break;
- case DATA_IN: /* Data in phase */
- return initio_xfer_data_in(host);
- case DATA_OUT: /* Data out phase */
- return initio_xfer_data_out(host);
- default:
- return initio_bad_seq(host);
- }
- }
- }
- /**
- * initio_state_5 - SCSI state machine
- * @host: InitIO host we are controlling
- *
- * State after dma xfer done or phase change before xfer done
- */
- static int initio_state_5(struct initio_host * host)
- {
- struct scsi_ctrl_blk *scb = host->active;
- long cnt, xcnt; /* cannot use unsigned !! code: if (xcnt < 0) */
- #if DEBUG_STATE
- printk("-s5-");
- #endif
- /*------ get remaining count -------*/
- cnt = inl(host->addr + TUL_SCnt0) & 0x0FFFFFF;
- if (inb(host->addr + TUL_XCmd) & 0x20) {
- /* ----------------------- DATA_IN ----------------------------- */
- /* check scsi parity error */
- if (host->jsstatus0 & TSS_PAR_ERROR)
- scb->hastat = HOST_DO_DU;
- if (inb(host->addr + TUL_XStatus) & XPEND) { /* DMA xfer pending, Send STOP */
- /* tell Hardware scsi xfer has been terminated */
- outb(inb(host->addr + TUL_XCtrl) | 0x80, host->addr + TUL_XCtrl);
- /* wait until DMA xfer not pending */
- while (inb(host->addr + TUL_XStatus) & XPEND)
- cpu_relax();
- }
- } else {
- /*-------- DATA OUT -----------*/
- if ((inb(host->addr + TUL_SStatus1) & TSS_XFER_CMP) == 0) {
- if (host->active_tc->js_period & TSC_WIDE_SCSI)
- cnt += (inb(host->addr + TUL_SFifoCnt) & 0x1F) << 1;
- else
- cnt += (inb(host->addr + TUL_SFifoCnt) & 0x1F);
- }
- if (inb(host->addr + TUL_XStatus) & XPEND) { /* if DMA xfer is pending, abort DMA xfer */
- outb(TAX_X_ABT, host->addr + TUL_XCmd);
- /* wait Abort DMA xfer done */
- while ((inb(host->addr + TUL_Int) & XABT) == 0)
- cpu_relax();
- }
- if ((cnt == 1) && (host->phase == DATA_OUT)) {
- outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
- if (wait_tulip(host) == -1)
- return -1;
- cnt = 0;
- } else {
- if ((inb(host->addr + TUL_SStatus1) & TSS_XFER_CMP) == 0)
- outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0);
- }
- }
- if (cnt == 0) {
- scb->buflen = 0;
- return 6; /* After Data */
- }
- /* Update active data pointer */
- xcnt = (long) scb->buflen - cnt; /* xcnt== bytes already xferred */
- scb->buflen = (u32) cnt; /* cnt == bytes left to be xferred */
- if (scb->flags & SCF_SG) {
- struct sg_entry *sgp;
- unsigned long i;
- sgp = &scb->sglist[scb->sgidx];
- for (i = scb->sgidx; i < scb->sgmax; sgp++, i++) {
- xcnt -= (long) sgp->len;
- if (xcnt < 0) { /* this sgp xfer half done */
- xcnt += (long) sgp->len; /* xcnt == bytes xferred in this sgp */
- sgp->data += (u32) xcnt; /* new ptr to be xfer */
- sgp->len -= (u32) xcnt; /* new len to be xfer */
- scb->bufptr += ((u32) (i - scb->sgidx) << 3);
- /* new SG table ptr */
- scb->sglen = (u8) (scb->sgmax - i);
- /* new SG table len */
- scb->sgidx = (u16) i;
- /* for next disc and come in this loop */
- return 4; /* Go to state 4 */
- }
- /* else (xcnt >= 0 , i.e. this sgp already xferred */
- } /* for */
- return 6; /* Go to state 6 */
- } else {
- scb->bufptr += (u32) xcnt;
- }
- return 4; /* Go to state 4 */
- }
- /**
- * initio_state_6 - SCSI state machine
- * @host: InitIO host we are controlling
- *
- * State after Data phase
- */
- static int initio_state_6(struct initio_host * host)
- {
- struct scsi_ctrl_blk *scb = host->active;
- #if DEBUG_STATE
- printk("-s6-");
- #endif
- for (;;) {
- switch (host->phase) {
- case STATUS_IN: /* Status phase */
- if ((initio_status_msg(host)) == -1)
- return -1;
- break;
- case MSG_IN: /* Message in phase */
- scb->next_state = 6;
- if ((initio_msgin(host)) == -1)
- return -1;
- break;
- case MSG_OUT: /* Message out phase */
- outb(MSG_NOP, host->addr + TUL_SFifo); /* msg nop */
- outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
- if (wait_tulip(host) == -1)
- return -1;
- break;
- case DATA_IN: /* Data in phase */
- return initio_xpad_in(host);
- case DATA_OUT: /* Data out phase */
- return initio_xpad_out(host);
- default:
- return initio_bad_seq(host);
- }
- }
- }
- /**
- * initio_state_7 - SCSI state machine
- * @host: InitIO host we are controlling
- *
- */
- int initio_state_7(struct initio_host * host)
- {
- int cnt, i;
- #if DEBUG_STATE
- printk("-s7-");
- #endif
- /* flush SCSI FIFO */
- cnt = inb(host->addr + TUL_SFifoCnt) & 0x1F;
- if (cnt) {
- for (i = 0; i < cnt; i++)
- inb(host->addr + TUL_SFifo);
- }
- switch (host->phase) {
- case DATA_IN: /* Data in phase */
- case DATA_OUT: /* Data out phase */
- return initio_bad_seq(host);
- default:
- return 6; /* Go to state 6 */
- }
- }
- /**
- * initio_xfer_data_in - Commence data input
- * @host: InitIO host in use
- *
- * Commence a block of data transfer. The transfer itself will
- * be managed by the controller and we will get a completion (or
- * failure) interrupt.
- */
- static int initio_xfer_data_in(struct initio_host * host)
- {
- struct scsi_ctrl_blk *scb = host->active;
- if ((scb->flags & SCF_DIR) == SCF_DOUT)
- return 6; /* wrong direction */
- outl(scb->buflen, host->addr + TUL_SCnt0);
- outb(TSC_XF_DMA_IN, host->addr + TUL_SCmd); /* 7/25/95 */
- if (scb->flags & SCF_SG) { /* S/G xfer */
- outl(((u32) scb->sglen) << 3, host->addr + TUL_XCntH);
- outl(scb->bufptr, host->addr + TUL_XAddH);
- outb(TAX_SG_IN, host->addr + TUL_XCmd);
- } else {
- outl(scb->buflen, host->addr + TUL_XCntH);
- outl(scb->bufptr, host->addr + TUL_XAddH);
- outb(TAX_X_IN, host->addr + TUL_XCmd);
- }
- scb->next_state = 0x5;
- return 0; /* return to OS, wait xfer done , let jas_isr come in */
- }
- /**
- * initio_xfer_data_out - Commence data output
- * @host: InitIO host in use
- *
- * Commence a block of data transfer. The transfer itself will
- * be managed by the controller and we will get a completion (or
- * failure) interrupt.
- */
- static int initio_xfer_data_out(struct initio_host * host)
- {
- struct scsi_ctrl_blk *scb = host->active;
- if ((scb->flags & SCF_DIR) == SCF_DIN)
- return 6; /* wrong direction */
- outl(scb->buflen, host->addr + TUL_SCnt0);
- outb(TSC_XF_DMA_OUT, host->addr + TUL_SCmd);
- if (scb->flags & SCF_SG) { /* S/G xfer */
- outl(((u32) scb->sglen) << 3, host->addr + TUL_XCntH);
- outl(scb->bufptr, host->addr + TUL_XAddH);
- outb(TAX_SG_OUT, host->addr + TUL_XCmd);
- } else {
- outl(scb->buflen, host->addr + TUL_XCntH);
- outl(scb->bufptr, host->addr + TUL_XAddH);
- outb(TAX_X_OUT, host->addr + TUL_XCmd);
- }
- scb->next_state = 0x5;
- return 0; /* return to OS, wait xfer done , let jas_isr come in */
- }
- int initio_xpad_in(struct initio_host * host)
- {
- struct scsi_ctrl_blk *scb = host->active;
- struct target_control *active_tc = host->active_tc;
- if ((scb->flags & SCF_DIR) != SCF_NO_DCHK)
- scb->hastat = HOST_DO_DU; /* over run */
- for (;;) {
- if (active_tc->js_period & TSC_WIDE_SCSI)
- outl(2, host->addr + TUL_SCnt0);
- else
- outl(1, host->addr + TUL_SCnt0);
- outb(TSC_XF_FIFO_IN, host->addr + TUL_SCmd);
- if (wait_tulip(host) == -1)
- return -1;
- if (host->phase != DATA_IN) {
- outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0);
- return 6;
- }
- inb(host->addr + TUL_SFifo);
- }
- }
- int initio_xpad_out(struct initio_host * host)
- {
- struct scsi_ctrl_blk *scb = host->active;
- struct target_control *active_tc = host->active_tc;
- if ((scb->flags & SCF_DIR) != SCF_NO_DCHK)
- scb->hastat = HOST_DO_DU; /* over run */
- for (;;) {
- if (active_tc->js_period & TSC_WIDE_SCSI)
- outl(2, host->addr + TUL_SCnt0);
- else
- outl(1, host->addr + TUL_SCnt0);
- outb(0, host->addr + TUL_SFifo);
- outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
- if ((wait_tulip(host)) == -1)
- return -1;
- if (host->phase != DATA_OUT) { /* Disable wide CPU to allow read 16 bits */
- outb(TSC_HW_RESELECT, host->addr + TUL_SCtrl1);
- outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0);
- return 6;
- }
- }
- }
- int initio_status_msg(struct initio_host * host)
- { /* status & MSG_IN */
- struct scsi_ctrl_blk *scb = host->active;
- u8 msg;
- outb(TSC_CMD_COMP, host->addr + TUL_SCmd);
- if (wait_tulip(host) == -1)
- return -1;
- /* get status */
- scb->tastat = inb(host->addr + TUL_SFifo);
- if (host->phase == MSG_OUT) {
- if (host->jsstatus0 & TSS_PAR_ERROR)
- outb(MSG_PARITY, host->addr + TUL_SFifo);
- else
- outb(MSG_NOP, host->addr + TUL_SFifo);
- outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
- return wait_tulip(host);
- }
- if (host->phase == MSG_IN) {
- msg = inb(host->addr + TUL_SFifo);
- if (host->jsstatus0 & TSS_PAR_ERROR) { /* Parity error */
- if ((initio_msgin_accept(host)) == -1)
- return -1;
- if (host->phase != MSG_OUT)
- return initio_bad_seq(host);
- outb(MSG_PARITY, host->addr + TUL_SFifo);
- outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
- return wait_tulip(host);
- }
- if (msg == 0) { /* Command complete */
- if ((scb->tastat & 0x18) == 0x10) /* No link support */
- return initio_bad_seq(host);
- outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0);
- outb(TSC_MSG_ACCEPT, host->addr + TUL_SCmd);
- return initio_wait_done_disc(host);
- }
- if (msg == MSG_LINK_COMP || msg == MSG_LINK_FLAG) {
- if ((scb->tastat & 0x18) == 0x10)
- return initio_msgin_accept(host);
- }
- }
- return initio_bad_seq(host);
- }
- /* scsi bus free */
- int int_initio_busfree(struct initio_host * host)
- {
- struct scsi_ctrl_blk *scb = host->active;
- if (scb != NULL) {
- if (scb->status & SCB_SELECT) { /* selection timeout */
- initio_unlink_pend_scb(host, scb);
- scb->hastat = HOST_SEL_TOUT;
- initio_append_done_scb(host, scb);
- } else { /* Unexpected bus free */
- initio_unlink_busy_scb(host, scb);
- scb->hastat = HOST_BUS_FREE;
- initio_append_done_scb(host, scb);
- }
- host->active = NULL;
- host->active_tc = NULL;
- }
- outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0); /* Flush SCSI FIFO */
- outb(TSC_INITDEFAULT, host->addr + TUL_SConfig);
- outb(TSC_HW_RESELECT, host->addr + TUL_SCtrl1); /* Enable HW reselect */
- return -1;
- }
- /**
- * int_initio_scsi_rst - SCSI reset occurred
- * @host: Host seeing the reset
- *
- * A SCSI bus reset has occurred. Clean up any pending transfer
- * the hardware is doing by DMA and then abort all active and
- * disconnected commands. The mid layer should sort the rest out
- * for us
- */
- static int int_initio_scsi_rst(struct initio_host * host)
- {
- struct scsi_ctrl_blk *scb;
- int i;
- /* if DMA xfer is pending, abort DMA xfer */
- if (inb(host->addr + TUL_XStatus) & 0x01) {
- outb(TAX_X_ABT | TAX_X_CLR_FIFO, host->addr + TUL_XCmd);
- /* wait Abort DMA xfer done */
- while ((inb(host->addr + TUL_Int) & 0x04) == 0)
- cpu_relax();
- outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0);
- }
- /* Abort all active & disconnected scb */
- while ((scb = initio_pop_busy_scb(host)) != NULL) {
- scb->hastat = HOST_BAD_PHAS;
- initio_append_done_scb(host, scb);
- }
- host->active = NULL;
- host->active_tc = NULL;
- /* clr sync nego. done flag */
- for (i = 0; i < host->max_tar; i++)
- host->targets[i].flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE);
- return -1;
- }
- /**
- * int_initio_scsi_resel - Reselection occurred
- * @host: InitIO host adapter
- *
- * A SCSI reselection event has been signalled and the interrupt
- * is now being processed. Work out which command block needs attention
- * and continue processing that command.
- */
- int int_initio_resel(struct initio_host * host)
- {
- struct scsi_ctrl_blk *scb;
- struct target_control *active_tc;
- u8 tag, msg = 0;
- u8 tar, lun;
- if ((scb = host->active) != NULL) {
- /* FIXME: Why check and not just clear ? */
- if (scb->status & SCB_SELECT) /* if waiting for selection complete */
- scb->status &= ~SCB_SELECT;
- host->active = NULL;
- }
- /* --------- get target id---------------------- */
- tar = inb(host->addr + TUL_SBusId);
- /* ------ get LUN from Identify message----------- */
- lun = inb(host->addr + TUL_SIdent) & 0x0F;
- /* 07/22/98 from 0x1F -> 0x0F */
- active_tc = &host->targets[tar];
- host->active_tc = active_tc;
- outb(active_tc->sconfig0, host->addr + TUL_SConfig);
- outb(active_tc->js_period, host->addr + TUL_SPeriod);
- /* ------------- tag queueing ? ------------------- */
- if (active_tc->drv_flags & TCF_DRV_EN_TAG) {
- if ((initio_msgin_accept(host)) == -1)
- return -1;
- if (host->phase != MSG_IN)
- goto no_tag;
- outl(1, host->addr + TUL_SCnt0);
- outb(TSC_XF_FIFO_IN, host->addr + TUL_SCmd);
- if (wait_tulip(host) == -1)
- return -1;
- msg = inb(host->addr + TUL_SFifo); /* Read Tag Message */
- if (msg < MSG_STAG || msg > MSG_OTAG) /* Is simple Tag */
- goto no_tag;
- if (initio_msgin_accept(host) == -1)
- return -1;
- if (host->phase != MSG_IN)
- goto no_tag;
- outl(1, host->addr + TUL_SCnt0);
- outb(TSC_XF_FIFO_IN, host->addr + TUL_SCmd);
- if (wait_tulip(host) == -1)
- return -1;
- tag = inb(host->addr + TUL_SFifo); /* Read Tag ID */
- scb = host->scb + tag;
- if (scb->target != tar || scb->lun != lun) {
- return initio_msgout_abort_tag(host);
- }
- if (scb->status != SCB_BUSY) { /* 03/24/95 */
- return initio_msgout_abort_tag(host);
- }
- host->active = scb;
- if ((initio_msgin_accept(host)) == -1)
- return -1;
- } else { /* No tag */
- no_tag:
- if ((scb = initio_find_busy_scb(host, tar | (lun << 8))) == NULL) {
- return initio_msgout_abort_targ(host);
- }
- host->active = scb;
- if (!(active_tc->drv_flags & TCF_DRV_EN_TAG)) {
- if ((initio_msgin_accept(host)) == -1)
- return -1;
- }
- }
- return 0;
- }
- /**
- * int_initio_bad_seq - out of phase
- * @host: InitIO host flagging event
- *
- * We have ended up out of phase somehow. Reset the host controller
- * and throw all our toys out of the pram. Let the midlayer clean up
- */
- static int int_initio_bad_seq(struct initio_host * host)
- { /* target wrong phase */
- struct scsi_ctrl_blk *scb;
- int i;
- initio_reset_scsi(host, 10);
- while ((scb = initio_pop_busy_scb(host)) != NULL) {
- scb->hastat = HOST_BAD_PHAS;
- initio_append_done_scb(host, scb);
- }
- for (i = 0; i < host->max_tar; i++)
- host->targets[i].flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE);
- return -1;
- }
- /**
- * initio_msgout_abort_targ - abort a tag
- * @host: InitIO host
- *
- * Abort when the target/lun does not match or when our SCB is not
- * busy. Used by untagged commands.
- */
- static int initio_msgout_abort_targ(struct initio_host * host)
- {
- outb(((inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN), host->addr + TUL_SSignal);
- if (initio_msgin_accept(host) == -1)
- return -1;
- if (host->phase != MSG_OUT)
- return initio_bad_seq(host);
- outb(MSG_ABORT, host->addr + TUL_SFifo);
- outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
- return initio_wait_disc(host);
- }
- /**
- * initio_msgout_abort_tag - abort a tag
- * @host: InitIO host
- *
- * Abort when the target/lun does not match or when our SCB is not
- * busy. Used for tagged commands.
- */
- static int initio_msgout_abort_tag(struct initio_host * host)
- {
- outb(((inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN), host->addr + TUL_SSignal);
- if (initio_msgin_accept(host) == -1)
- return -1;
- if (host->phase != MSG_OUT)
- return initio_bad_seq(host);
- outb(MSG_ABORT_TAG, host->addr + TUL_SFifo);
- outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
- return initio_wait_disc(host);
- }
- /**
- * initio_msgin - Message in
- * @host: InitIO Host
- *
- * Process incoming message
- */
- static int initio_msgin(struct initio_host * host)
- {
- struct target_control *active_tc;
- for (;;) {
- outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0);
- outl(1, host->addr + TUL_SCnt0);
- outb(TSC_XF_FIFO_IN, host->addr + TUL_SCmd);
- if (wait_tulip(host) == -1)
- return -1;
- switch (inb(host->addr + TUL_SFifo)) {
- case MSG_DISC: /* Disconnect msg */
- outb(TSC_MSG_ACCEPT, host->addr + TUL_SCmd);
- return initio_wait_disc(host);
- case MSG_SDP:
- case MSG_RESTORE:
- case MSG_NOP:
- initio_msgin_accept(host);
- break;
- case MSG_REJ: /* Clear ATN first */
- outb((inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7)),
- host->addr + TUL_SSignal);
- active_tc = host->active_tc;
- if ((active_tc->flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0) /* do sync nego */
- outb(((inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN),
- host->addr + TUL_SSignal);
- initio_msgin_accept(host);
- break;
- case MSG_EXTEND: /* extended msg */
- initio_msgin_extend(host);
- break;
- case MSG_IGNOREWIDE:
- initio_msgin_accept(host);
- break;
- case MSG_COMP:
- outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0);
- outb(TSC_MSG_ACCEPT, host->addr + TUL_SCmd);
- return initio_wait_done_disc(host);
- default:
- initio_msgout_reject(host);
- break;
- }
- if (host->phase != MSG_IN)
- return host->phase;
- }
- /* statement won't reach here */
- }
- static int initio_msgout_reject(struct initio_host * host)
- {
- outb(((inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN), host->addr + TUL_SSignal);
- if (initio_msgin_accept(host) == -1)
- return -1;
- if (host->phase == MSG_OUT) {
- outb(MSG_REJ, host->addr + TUL_SFifo); /* Msg reject */
- outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
- return wait_tulip(host);
- }
- return host->phase;
- }
- static int initio_msgout_ide(struct initio_host * host)
- {
- outb(MSG_IDE, host->addr + TUL_SFifo); /* Initiator Detected Error */
- outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
- return wait_tulip(host);
- }
- static int initio_msgin_extend(struct initio_host * host)
- {
- u8 len, idx;
- if (initio_msgin_accept(host) != MSG_IN)
- return host->phase;
- /* Get extended msg length */
- outl(1, host->addr + TUL_SCnt0);
- outb(TSC_XF_FIFO_IN, host->addr + TUL_SCmd);
- if (wait_tulip(host) == -1)
- return -1;
- len = inb(host->addr + TUL_SFifo);
- host->msg[0] = len;
- for (idx = 1; len != 0; len--) {
- if ((initio_msgin_accept(host)) != MSG_IN)
- return host->phase;
- outl(1, host->addr + TUL_SCnt0);
- outb(TSC_XF_FIFO_IN, host->addr + TUL_SCmd);
- if (wait_tulip(host) == -1)
- return -1;
- host->msg[idx++] = inb(host->addr + TUL_SFifo);
- }
- if (host->msg[1] == 1) { /* if it's synchronous data transfer request */
- u8 r;
- if (host->msg[0] != 3) /* if length is not right */
- return initio_msgout_reject(host);
- if (host->active_tc->flags & TCF_NO_SYNC_NEGO) { /* Set OFFSET=0 to do async, nego back */
- host->msg[3] = 0;
- } else {
- if (initio_msgin_sync(host) == 0 &&
- (host->active_tc->flags & TCF_SYNC_DONE)) {
- initio_sync_done(host);
- return initio_msgin_accept(host);
- }
- }
- r = inb(host->addr + TUL_SSignal);
- outb((r & (TSC_SET_ACK | 7)) | TSC_SET_ATN,
- host->addr + TUL_SSignal);
- if (initio_msgin_accept(host) != MSG_OUT)
- return host->phase;
- /* sync msg out */
- outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0);
- initio_sync_done(host);
- outb(MSG_EXTEND, host->addr + TUL_SFifo);
- outb(3, host->addr + TUL_SFifo);
- outb(1, host->addr + TUL_SFifo);
- outb(host->msg[2], host->addr + TUL_SFifo);
- outb(host->msg[3], host->addr + TUL_SFifo);
- outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
- return wait_tulip(host);
- }
- if (host->msg[0] != 2 || host->msg[1] != 3)
- return initio_msgout_reject(host);
- /* if it's WIDE DATA XFER REQ */
- if (host->active_tc->flags & TCF_NO_WDTR) {
- host->msg[2] = 0;
- } else {
- if (host->msg[2] > 2) /* > 32 bits */
- return initio_msgout_reject(host);
- if (host->msg[2] == 2) { /* == 32 */
- host->msg[2] = 1;
- } else {
- if ((host->active_tc->flags & TCF_NO_WDTR) == 0) {
- wdtr_done(host);
- if ((host->active_tc->flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0)
- outb(((inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN), host->addr + TUL_SSignal);
- return initio_msgin_accept(host);
- }
- }
- }
- outb(((inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN), host->addr + TUL_SSignal);
- if (initio_msgin_accept(host) != MSG_OUT)
- return host->phase;
- /* WDTR msg out */
- outb(MSG_EXTEND, host->addr + TUL_SFifo);
- outb(2, host->addr + TUL_SFifo);
- outb(3, host->addr + TUL_SFifo);
- outb(host->msg[2], host->addr + TUL_SFifo);
- outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
- return wait_tulip(host);
- }
- static int initio_msgin_sync(struct initio_host * host)
- {
- char default_period;
- default_period = initio_rate_tbl[host->active_tc->flags & TCF_SCSI_RATE];
- if (host->msg[3] > MAX_OFFSET) {
- host->msg[3] = MAX_OFFSET;
- if (host->msg[2] < default_period) {
- host->msg[2] = default_period;
- return 1;
- }
- if (host->msg[2] >= 59) /* Change to async */
- host->msg[3] = 0;
- return 1;
- }
- /* offset requests asynchronous transfers ? */
- if (host->msg[3] == 0) {
- return 0;
- }
- if (host->msg[2] < default_period) {
- host->msg[2] = default_period;
- return 1;
- }
- if (host->msg[2] >= 59) {
- host->msg[3] = 0;
- return 1;
- }
- return 0;
- }
- static int wdtr_done(struct initio_host * host)
- {
- host->active_tc->flags &= ~TCF_SYNC_DONE;
- host->active_tc->flags |= TCF_WDTR_DONE;
- host->active_tc->js_period = 0;
- if (host->msg[2]) /* if 16 bit */
- host->active_tc->js_period |= TSC_WIDE_SCSI;
- host->active_tc->sconfig0 &= ~TSC_ALT_PERIOD;
- outb(host->active_tc->sconfig0, host->addr + TUL_SConfig);
- outb(host->active_tc->js_period, host->addr + TUL_SPeriod);
- return 1;
- }
- static int initio_sync_done(struct initio_host * host)
- {
- int i;
- host->active_tc->flags |= TCF_SYNC_DONE;
- if (host->msg[3]) {
- host->active_tc->js_period |= host->msg[3];
- for (i = 0; i < 8; i++) {
- if (initio_rate_tbl[i] >= host->msg[2]) /* pick the big one */
- break;
- }
- host->active_tc->js_period |= (i << 4);
- host->active_tc->sconfig0 |= TSC_ALT_PERIOD;
- }
- outb(host->active_tc->sconfig0, host->addr + TUL_SConfig);
- outb(host->active_tc->js_period, host->addr + TUL_SPeriod);
- return -1;
- }
- static int initio_post_scsi_rst(struct initio_host * host)
- {
- struct scsi_ctrl_blk *scb;
- struct target_control *active_tc;
- int i;
- host->active = NULL;
- host->active_tc = NULL;
- host->flags = 0;
- while ((scb = initio_pop_busy_scb(host)) != NULL) {
- scb->hastat = HOST_BAD_PHAS;
- initio_append_done_scb(host, scb);
- }
- /* clear sync done flag */
- active_tc = &host->targets[0];
- for (i = 0; i < host->max_tar; active_tc++, i++) {
- active_tc->flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE);
- /* Initialize the sync. xfer register values to an asyn xfer */
- active_tc->js_period = 0;
- active_tc->sconfig0 = host->sconf1;
- host->act_tags[0] = 0; /* 07/22/98 */
- host->targets[i].flags &= ~TCF_BUSY; /* 07/22/98 */
- } /* for */
- return -1;
- }
- static void initio_select_atn_stop(struct initio_host * host, struct scsi_ctrl_blk * scb)
- {
- scb->status |= SCB_SELECT;
- scb->next_state = 0x1;
- host->active = scb;
- host->active_tc = &host->targets[scb->target];
- outb(TSC_SELATNSTOP, host->addr + TUL_SCmd);
- }
- static void initio_select_atn(struct initio_host * host, struct scsi_ctrl_blk * scb)
- {
- int i;
- scb->status |= SCB_SELECT;
- scb->next_state = 0x2;
- outb(scb->ident, host->addr + TUL_SFifo);
- for (i = 0; i < (int) scb->cdblen; i++)
- outb(scb->cdb[i], host->addr + TUL_SFifo);
- host->active_tc = &host->targets[scb->target];
- host->active = scb;
- outb(TSC_SEL_ATN, host->addr + TUL_SCmd);
- }
- static void initio_select_atn3(struct initio_host * host, struct scsi_ctrl_blk * scb)
- {
- int i;
- scb->status |= SCB_SELECT;
- scb->next_state = 0x2;
- outb(scb->ident, host->addr + TUL_SFifo);
- outb(scb->tagmsg, host->addr + TUL_SFifo);
- outb(scb->tagid, host->addr + TUL_SFifo);
- for (i = 0; i < scb->cdblen; i++)
- outb(scb->cdb[i], host->addr + TUL_SFifo);
- host->active_tc = &host->targets[scb->target];
- host->active = scb;
- outb(TSC_SEL_ATN3, host->addr + TUL_SCmd);
- }
- /**
- * initio_bus_device_reset - SCSI Bus Device Reset
- * @host: InitIO host to reset
- *
- * Perform a device reset and abort all pending SCBs for the
- * victim device
- */
- int initio_bus_device_reset(struct initio_host * host)
- {
- struct scsi_ctrl_blk *scb = host->active;
- struct target_control *active_tc = host->active_tc;
- struct scsi_ctrl_blk *tmp, *prev;
- u8 tar;
- if (host->phase != MSG_OUT)
- return int_initio_bad_seq(host); /* Unexpected phase */
- initio_unlink_pend_scb(host, scb);
- initio_release_scb(host, scb);
- tar = scb->target; /* target */
- active_tc->flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE | TCF_BUSY);
- /* clr sync. nego & WDTR flags 07/22/98 */
- /* abort all SCB with same target */
- prev = tmp = host->first_busy; /* Check Busy queue */
- while (tmp != NULL) {
- if (tmp->target == tar) {
- /* unlink it */
- if (tmp == host->first_busy) {
- if ((host->first_busy = tmp->next) == NULL)
- host->last_busy = NULL;
- } else {
- prev->next = tmp->next;
- if (tmp == host->last_busy)
- host->last_busy = prev;
- }
- tmp->hastat = HOST_ABORTED;
- initio_append_done_scb(host, tmp);
- }
- /* Previous haven't change */
- else {
- prev = tmp;
- }
- tmp = tmp->next;
- }
- outb(MSG_DEVRST, host->addr + TUL_SFifo);
- outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd);
- return initio_wait_disc(host);
- }
- static int initio_msgin_accept(struct initio_host * host)
- {
- outb(TSC_MSG_ACCEPT, host->addr + TUL_SCmd);
- return wait_tulip(host);
- }
- static int wait_tulip(struct initio_host * host)
- {
- while (!((host->jsstatus0 = inb(host->addr + TUL_SStatus0))
- & TSS_INT_PENDING))
- cpu_relax();
- host->jsint = inb(host->addr + TUL_SInt);
- host->phase = host->jsstatus0 & TSS_PH_MASK;
- host->jsstatus1 = inb(host->addr + TUL_SStatus1);
- if (host->jsint & TSS_RESEL_INT) /* if SCSI bus reset detected */
- return int_initio_resel(host);
- if (host->jsint & TSS_SEL_TIMEOUT) /* if selected/reselected timeout interrupt */
- return int_initio_busfree(host);
- if (host->jsint & TSS_SCSIRST_INT) /* if SCSI bus reset detected */
- return int_initio_scsi_rst(host);
- if (host->jsint & TSS_DISC_INT) { /* BUS disconnection */
- if (host->flags & HCF_EXPECT_DONE_DISC) {
- outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0); /* Flush SCSI FIFO */
- initio_unlink_busy_scb(host, host->active);
- host->active->hastat = 0;
- initio_append_done_scb(host, host->active);
- host->active = NULL;
- host->active_tc = NULL;
- host->flags &= ~HCF_EXPECT_DONE_DISC;
- outb(TSC_INITDEFAULT, host->addr + TUL_SConfig);
- outb(TSC_HW_RESELECT, host->addr + TUL_SCtrl1); /* Enable HW reselect */
- return -1;
- }
- if (host->flags & HCF_EXPECT_DISC) {
- outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0); /* Flush SCSI FIFO */
- host->active = NULL;
- host->active_tc = NULL;
- host->flags &= ~HCF_EXPECT_DISC;
- outb(TSC_INITDEFAULT, host->addr + TUL_SConfig);
- outb(TSC_HW_RESELECT, host->addr + TUL_SCtrl1); /* Enable HW reselect */
- return -1;
- }
- return int_initio_busfree(host);
- }
- /* The old code really does the below. Can probably be removed */
- if (host->jsint & (TSS_FUNC_COMP | TSS_BUS_SERV))
- return host->phase;
- return host->phase;
- }
- static int initio_wait_disc(struct initio_host * host)
- {
- while (!((host->jsstatus0 = inb(host->addr + TUL_SStatus0)) & TSS_INT_PENDING))
- cpu_relax();
- host->jsint = inb(host->addr + TUL_SInt);
- if (host->jsint & TSS_SCSIRST_INT) /* if SCSI bus reset detected */
- return int_initio_scsi_rst(host);
- if (host->jsint & TSS_DISC_INT) { /* BUS disconnection */
- outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0); /* Flush SCSI FIFO */
- outb(TSC_INITDEFAULT, host->addr + TUL_SConfig);
- outb(TSC_HW_RESELECT, host->addr + TUL_SCtrl1); /* Enable HW reselect */
- host->active = NULL;
- return -1;
- }
- return initio_bad_seq(host);
- }
- static int initio_wait_done_disc(struct initio_host * host)
- {
- while (!((host->jsstatus0 = inb(host->addr + TUL_SStatus0))
- & TSS_INT_PENDING))
- cpu_relax();
- host->jsint = inb(host->addr + TUL_SInt);
- if (host->jsint & TSS_SCSIRST_INT) /* if SCSI bus reset detected */
- return int_initio_scsi_rst(host);
- if (host->jsint & TSS_DISC_INT) { /* BUS disconnection */
- outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0); /* Flush SCSI FIFO */
- outb(TSC_INITDEFAULT, host->addr + TUL_SConfig);
- outb(TSC_HW_RESELECT, host->addr + TUL_SCtrl1); /* Enable HW reselect */
- initio_unlink_busy_scb(host, host->active);
- initio_append_done_scb(host, host->active);
- host->active = NULL;
- return -1;
- }
- return initio_bad_seq(host);
- }
- /**
- * i91u_intr - IRQ handler
- * @irqno: IRQ number
- * @dev_id: IRQ identifier
- *
- * Take the relevant locks and then invoke the actual isr processing
- * code under the lock.
- */
- static irqreturn_t i91u_intr(int irqno, void *dev_id)
- {
- struct Scsi_Host *dev = dev_id;
- unsigned long flags;
- int r;
-
- spin_lock_irqsave(dev->host_lock, flags);
- r = initio_isr((struct initio_host *)dev->hostdata);
- spin_unlock_irqrestore(dev->host_lock, flags);
- if (r)
- return IRQ_HANDLED;
- else
- return IRQ_NONE;
- }
- /**
- * initio_build_scb - Build the mappings and SCB
- * @host: InitIO host taking the command
- * @cblk: Firmware command block
- * @cmnd: SCSI midlayer command block
- *
- * Translate the abstract SCSI command into a firmware command block
- * suitable for feeding to the InitIO host controller. This also requires
- * we build the scatter gather lists and ensure they are mapped properly.
- */
- static void initio_build_scb(struct initio_host * host, struct scsi_ctrl_blk * cblk, struct scsi_cmnd * cmnd)
- { /* Create corresponding SCB */
- struct scatterlist *sglist;
- struct sg_entry *sg; /* Pointer to SG list */
- int i, nseg;
- long total_len;
- dma_addr_t dma_addr;
- /* Fill in the command headers */
- cblk->post = i91uSCBPost; /* i91u's callback routine */
- cblk->srb = cmnd;
- cblk->opcode = ExecSCSI;
- cblk->flags = SCF_POST; /* After SCSI done, call post routine */
- cblk->target = cmnd->device->id;
- cblk->lun = cmnd->device->lun;
- cblk->ident = cmnd->device->lun | DISC_ALLOW;
- cblk->flags |= SCF_SENSE; /* Turn on auto request sense */
- /* Map the sense buffer into bus memory */
- dma_addr = dma_map_single(&host->pci_dev->dev, cmnd->sense_buffer,
- SENSE_SIZE, DMA_FROM_DEVICE);
- cblk->senseptr = (u32)dma_addr;
- cblk->senselen = SENSE_SIZE;
- cmnd->SCp.ptr = (char *)(unsigned long)dma_addr;
- cblk->cdblen = cmnd->cmd_len;
- /* Clear the returned status */
- cblk->hastat = 0;
- cblk->tastat = 0;
- /* Command the command */
- memcpy(cblk->cdb, cmnd->cmnd, cmnd->cmd_len);
- /* Set up tags */
- if (cmnd->device->tagged_supported) { /* Tag Support */
- cblk->tagmsg = SIMPLE_QUEUE_TAG; /* Do simple tag only */
- } else {
- cblk->tagmsg = 0; /* No tag support */
- }
- /* todo handle map_sg error */
- nseg = scsi_dma_map(cmnd);
- BUG_ON(nseg < 0);
- if (nseg) {
- dma_addr = dma_map_single(&host->pci_dev->dev, &cblk->sglist[0],
- sizeof(struct sg_entry) * TOTAL_SG_ENTRY,
- DMA_BIDIRECTIONAL);
- cblk->bufptr = (u32)dma_addr;
- cmnd->SCp.dma_handle = dma_addr;
- cblk->sglen = nseg;
- cblk->flags |= SCF_SG; /* Turn on SG list flag */
- total_len = 0;
- sg = &cblk->sglist[0];
- scsi_for_each_sg(cmnd, sglist, cblk->sglen, i) {
- sg->data = cpu_to_le32((u32)sg_dma_address(sglist));
- sg->len = cpu_to_le32((u32)sg_dma_len(sglist));
- total_len += sg_dma_len(sglist);
- ++sg;
- }
- cblk->buflen = (scsi_bufflen(cmnd) > total_len) ?
- total_len : scsi_bufflen(cmnd);
- } else { /* No data transfer required */
- cblk->buflen = 0;
- cblk->sglen = 0;
- }
- }
- /**
- * i91u_queuecommand - Queue a new command if possible
- * @cmd: SCSI command block from the mid layer
- * @done: Completion handler
- *
- * Attempts to queue a new command with the host adapter. Will return
- * zero if successful or indicate a host busy condition if not (which
- * will cause the mid layer to call us again later with the command)
- */
- static int i91u_queuecommand_lck(struct scsi_cmnd *cmd,
- void (*done)(struct scsi_cmnd *))
- {
- struct initio_host *host = (struct initio_host *) cmd->device->host->hostdata;
- struct scsi_ctrl_blk *cmnd;
- cmd->scsi_done = done;
- cmnd = initio_alloc_scb(host);
- if (!cmnd)
- return SCSI_MLQUEUE_HOST_BUSY;
- initio_build_scb(host, cmnd, cmd);
- initio_exec_scb(host, cmnd);
- return 0;
- }
- static DEF_SCSI_QCMD(i91u_queuecommand)
- /**
- * i91u_bus_reset - reset the SCSI bus
- * @cmnd: Command block we want to trigger the reset for
- *
- * Initiate a SCSI bus reset sequence
- */
- static int i91u_bus_reset(struct scsi_cmnd * cmnd)
- {
- struct initio_host *host;
- host = (struct initio_host *) cmnd->device->host->hostdata;
- spin_lock_irq(cmnd->device->host->host_lock);
- initio_reset_scsi(host, 0);
- spin_unlock_irq(cmnd->device->host->host_lock);
- return SUCCESS;
- }
- /**
- * i91u_biospararm - return the "logical geometry
- * @sdev: SCSI device
- * @dev; Matching block device
- * @capacity: Sector size of drive
- * @info_array: Return space for BIOS geometry
- *
- * Map the device geometry in a manner compatible with the host
- * controller BIOS behaviour.
- *
- * FIXME: limited to 2^32 sector devices.
- */
- static int i91u_biosparam(struct scsi_device *sdev, struct block_device *dev,
- sector_t capacity, int *info_array)
- {
- struct initio_host *host; /* Point to Host adapter control block */
- struct target_control *tc;
- host = (struct initio_host *) sdev->host->hostdata;
- tc = &host->targets[sdev->id];
- if (tc->heads) {
- info_array[0] = tc->heads;
- info_array[1] = tc->sectors;
- info_array[2] = (unsigned long)capacity / tc->heads / tc->sectors;
- } else {
- if (tc->drv_flags & TCF_DRV_255_63) {
- info_array[0] = 255;
- info_array[1] = 63;
- info_array[2] = (unsigned long)capacity / 255 / 63;
- } else {
- info_array[0] = 64;
- info_array[1] = 32;
- info_array[2] = (unsigned long)capacity >> 11;
- }
- }
- #if defined(DEBUG_BIOSPARAM)
- if (i91u_debug & debug_biosparam) {
- printk("bios geometry: head=%d, sec=%d, cyl=%d\n",
- info_array[0], info_array[1], info_array[2]);
- printk("WARNING: check, if the bios geometry is correct.\n");
- }
- #endif
- return 0;
- }
- /**
- * i91u_unmap_scb - Unmap a command
- * @pci_dev: PCI device the command is for
- * @cmnd: The command itself
- *
- * Unmap any PCI mapping/IOMMU resources allocated when the command
- * was mapped originally as part of initio_build_scb
- */
- static void i91u_unmap_scb(struct pci_dev *pci_dev, struct scsi_cmnd *cmnd)
- {
- /* auto sense buffer */
- if (cmnd->SCp.ptr) {
- dma_unmap_single(&pci_dev->dev,
- (dma_addr_t)((unsigned long)cmnd->SCp.ptr),
- SENSE_SIZE, DMA_FROM_DEVICE);
- cmnd->SCp.ptr = NULL;
- }
- /* request buffer */
- if (scsi_sg_count(cmnd)) {
- dma_unmap_single(&pci_dev->dev, cmnd->SCp.dma_handle,
- sizeof(struct sg_entry) * TOTAL_SG_ENTRY,
- DMA_BIDIRECTIONAL);
- scsi_dma_unmap(cmnd);
- }
- }
- /**
- * i91uSCBPost - SCSI callback
- * @host: Pointer to host adapter control block.
- * @cmnd: Pointer to SCSI control block.
- *
- * This is callback routine be called when tulip finish one
- * SCSI command.
- */
- static void i91uSCBPost(u8 * host_mem, u8 * cblk_mem)
- {
- struct scsi_cmnd *cmnd; /* Pointer to SCSI request block */
- struct initio_host *host;
- struct scsi_ctrl_blk *cblk;
- host = (struct initio_host *) host_mem;
- cblk = (struct scsi_ctrl_blk *) cblk_mem;
- if ((cmnd = cblk->srb) == NULL) {
- printk(KERN_ERR "i91uSCBPost: SRB pointer is empty\n");
- WARN_ON(1);
- initio_release_scb(host, cblk); /* Release SCB for current channel */
- return;
- }
- /*
- * Remap the firmware error status into a mid layer one
- */
- switch (cblk->hastat) {
- case 0x0:
- case 0xa: /* Linked command complete without error and linked normally */
- case 0xb: /* Linked command complete without error interrupt generated */
- cblk->hastat = 0;
- break;
- case 0x11: /* Selection time out-The initiator selection or target
- reselection was not complete within the SCSI Time out period */
- cblk->hastat = DID_TIME_OUT;
- break;
- case 0x14: /* Target bus phase sequence failure-An invalid bus phase or bus
- phase sequence was requested by the target. The host adapter
- will generate a SCSI Reset Condition, notifying the host with
- a SCRD interrupt */
- cblk->hastat = DID_RESET;
- break;
- case 0x1a: /* SCB Aborted. 07/21/98 */
- cblk->hastat = DID_ABORT;
- break;
- case 0x12: /* Data overrun/underrun-The target attempted to transfer more data
- than was allocated by the Data Length field or the sum of the
- Scatter / Gather Data Length fields. */
- case 0x13: /* Unexpected bus free-The target dropped the SCSI BSY at an unexpected time. */
- case 0x16: /* Invalid SCB Operation Code. */
- default:
- printk("ini9100u: %x %x\n", cblk->hastat, cblk->tastat);
- cblk->hastat = DID_ERROR; /* Couldn't find any better */
- break;
- }
- cmnd->result = cblk->tastat | (cblk->hastat << 16);
- i91u_unmap_scb(host->pci_dev, cmnd);
- cmnd->scsi_done(cmnd); /* Notify system DONE */
- initio_release_scb(host, cblk); /* Release SCB for current channel */
- }
- static struct scsi_host_template initio_template = {
- .proc_name = "INI9100U",
- .name = "Initio INI-9X00U/UW SCSI device driver",
- .queuecommand = i91u_queuecommand,
- .eh_bus_reset_handler = i91u_bus_reset,
- .bios_param = i91u_biosparam,
- .can_queue = MAX_TARGETS * i91u_MAXQUEUE,
- .this_id = 1,
- .sg_tablesize = SG_ALL,
- .use_clustering = ENABLE_CLUSTERING,
- };
- static int initio_probe_one(struct pci_dev *pdev,
- const struct pci_device_id *id)
- {
- struct Scsi_Host *shost;
- struct initio_host *host;
- u32 reg;
- u16 bios_seg;
- struct scsi_ctrl_blk *scb, *tmp, *prev = NULL /* silence gcc */;
- int num_scb, i, error;
- error = pci_enable_device(pdev);
- if (error)
- return error;
- pci_read_config_dword(pdev, 0x44, (u32 *) & reg);
- bios_seg = (u16) (reg & 0xFF);
- if (((reg & 0xFF00) >> 8) == 0xFF)
- reg = 0;
- bios_seg = (bios_seg << 8) + ((u16) ((reg & 0xFF00) >> 8));
- if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
- printk(KERN_WARNING "i91u: Could not set 32 bit DMA mask\n");
- error = -ENODEV;
- goto out_disable_device;
- }
- shost = scsi_host_alloc(&initio_template, sizeof(struct initio_host));
- if (!shost) {
- printk(KERN_WARNING "initio: Could not allocate host structure.\n");
- error = -ENOMEM;
- goto out_disable_device;
- }
- host = (struct initio_host *)shost->hostdata;
- memset(host, 0, sizeof(struct initio_host));
- host->addr = pci_resource_start(pdev, 0);
- host->bios_addr = bios_seg;
- if (!request_region(host->addr, 256, "i91u")) {
- printk(KERN_WARNING "initio: I/O port range 0x%x is busy.\n", host->addr);
- error = -ENODEV;
- goto out_host_put;
- }
- if (initio_tag_enable) /* 1.01i */
- num_scb = MAX_TARGETS * i91u_MAXQUEUE;
- else
- num_scb = MAX_TARGETS + 3; /* 1-tape, 1-CD_ROM, 1- extra */
- for (; num_scb >= MAX_TARGETS + 3; num_scb--) {
- i = num_scb * sizeof(struct scsi_ctrl_blk);
- if ((scb = kzalloc(i, GFP_DMA)) != NULL)
- break;
- }
- if (!scb) {
- printk(KERN_WARNING "initio: Cannot allocate SCB array.\n");
- error = -ENOMEM;
- goto out_release_region;
- }
- host->pci_dev = pdev;
- host->semaph = 1;
- spin_lock_init(&host->semaph_lock);
- host->num_scbs = num_scb;
- host->scb = scb;
- host->next_pending = scb;
- host->next_avail = scb;
- for (i = 0, tmp = scb; i < num_scb; i++, tmp++) {
- tmp->tagid = i;
- if (i != 0)
- prev->next = tmp;
- prev = tmp;
- }
- prev->next = NULL;
- host->scb_end = tmp;
- host->first_avail = scb;
- host->last_avail = prev;
- spin_lock_init(&host->avail_lock);
- initio_init(host, phys_to_virt(((u32)bios_seg << 4)));
- host->jsstatus0 = 0;
- shost->io_port = host->addr;
- shost->n_io_port = 0xff;
- shost->can_queue = num_scb; /* 03/05/98 */
- shost->unique_id = host->addr;
- shost->max_id = host->max_tar;
- shost->max_lun = 32; /* 10/21/97 */
- shost->irq = pdev->irq;
- shost->this_id = host->scsi_id; /* Assign HCS index */
- shost->base = host->addr;
- shost->sg_tablesize = TOTAL_SG_ENTRY;
- error = request_irq(pdev->irq, i91u_intr, IRQF_SHARED, "i91u", shost);
- if (error < 0) {
- printk(KERN_WARNING "initio: Unable to request IRQ %d\n", pdev->irq);
- goto out_free_scbs;
- }
- pci_set_drvdata(pdev, shost);
- error = scsi_add_host(shost, &pdev->dev);
- if (error)
- goto out_free_irq;
- scsi_scan_host(shost);
- return 0;
- out_free_irq:
- free_irq(pdev->irq, shost);
- out_free_scbs:
- kfree(host->scb);
- out_release_region:
- release_region(host->addr, 256);
- out_host_put:
- scsi_host_put(shost);
- out_disable_device:
- pci_disable_device(pdev);
- return error;
- }
- /**
- * initio_remove_one - control shutdown
- * @pdev: PCI device being released
- *
- * Release the resources assigned to this adapter after it has
- * finished being used.
- */
- static void initio_remove_one(struct pci_dev *pdev)
- {
- struct Scsi_Host *host = pci_get_drvdata(pdev);
- struct initio_host *s = (struct initio_host *)host->hostdata;
- scsi_remove_host(host);
- free_irq(pdev->irq, host);
- release_region(s->addr, 256);
- scsi_host_put(host);
- pci_disable_device(pdev);
- }
- MODULE_LICENSE("GPL");
- static struct pci_device_id initio_pci_tbl[] = {
- {PCI_VENDOR_ID_INIT, 0x9500, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_INIT, 0x9400, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_INIT, 0x9401, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_INIT, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_DOMEX, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {0,}
- };
- MODULE_DEVICE_TABLE(pci, initio_pci_tbl);
- static struct pci_driver initio_pci_driver = {
- .name = "initio",
- .id_table = initio_pci_tbl,
- .probe = initio_probe_one,
- .remove = initio_remove_one,
- };
- static int __init initio_init_driver(void)
- {
- return pci_register_driver(&initio_pci_driver);
- }
- static void __exit initio_exit_driver(void)
- {
- pci_unregister_driver(&initio_pci_driver);
- }
- MODULE_DESCRIPTION("Initio INI-9X00U/UW SCSI device driver");
- MODULE_AUTHOR("Initio Corporation");
- MODULE_LICENSE("GPL");
- module_init(initio_init_driver);
- module_exit(initio_exit_driver);
|