12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862 |
- From d434f75bc6411d2964fce7fee50fe0ce49dd02eb Mon Sep 17 00:00:00 2001
- From: P33M <P33M@github.com>
- Date: Wed, 19 Mar 2014 12:58:23 +0000
- Subject: [PATCH 026/114] dwc_otg: fiq_fsm: Base commit for driver rewrite
- This commit removes the previous FIQ fixes entirely and adds fiq_fsm.
- This rewrite features much more complete support for split transactions
- and takes into account several OTG hardware bugs. High-speed
- isochronous transactions are also capable of being performed by fiq_fsm.
- All driver options have been removed and replaced with:
- - dwc_otg.fiq_enable (bool)
- - dwc_otg.fiq_fsm_enable (bool)
- - dwc_otg.fiq_fsm_mask (bitmask)
- - dwc_otg.nak_holdoff (unsigned int)
- Defaults are specified such that fiq_fsm behaves similarly to the
- previously implemented FIQ fixes.
- fiq_fsm: Push error recovery into the FIQ when fiq_fsm is used
- If the transfer associated with a QTD failed due to a bus error, the HCD
- would retry the transfer up to 3 times (implementing the USB2.0
- three-strikes retry in software).
- Due to the masking mechanism used by fiq_fsm, it is only possible to pass
- a single interrupt through to the HCD per-transfer.
- In this instance host channels would fall off the radar because the error
- reset would function, but the subsequent channel halt would be lost.
- Push the error count reset into the FIQ handler.
- fiq_fsm: Implement timeout mechanism
- For full-speed endpoints with a large packet size, interrupt latency
- runs the risk of the FIQ starting a transaction too late in a full-speed
- frame. If the device is still transmitting data when EOF2 for the
- downstream frame occurs, the hub will disable the port. This change is
- not reflected in the hub status endpoint and the device becomes
- unresponsive.
- Prevent high-bandwidth transactions from being started too late in a
- frame. The mechanism is not guaranteed: a combination of bit stuffing
- and hub latency may still result in a device overrunning.
- fiq_fsm: fix bounce buffer utilisation for Isochronous OUT
- Multi-packet isochronous OUT transactions were subject to a few bounday
- bugs. Fix them.
- Audio playback is now much more robust: however, an issue stands with
- devices that have adaptive sinks - ALSA plays samples too fast.
- dwc_otg: Return full-speed frame numbers in HS mode
- The frame counter increments on every *microframe* in high-speed mode.
- Most device drivers expect this number to be in full-speed frames - this
- caused considerable confusion to e.g. snd_usb_audio which uses the
- frame counter to estimate the number of samples played.
- fiq_fsm: save PID on completion of interrupt OUT transfers
- Also add edge case handling for interrupt transports.
- Note that for periodic split IN, data toggles are unimplemented in the
- OTG host hardware - it unconditionally accepts any PID.
- fiq_fsm: add missing case for fiq_fsm_tt_in_use()
- Certain combinations of bitrate and endpoint activity could
- result in a periodic transaction erroneously getting started
- while the previous Isochronous OUT was still active.
- fiq_fsm: clear hcintmsk for aborted transactions
- Prevents the FIQ from erroneously handling interrupts
- on a timed out channel.
- fiq_fsm: enable by default
- fiq_fsm: fix dequeues for non-periodic split transactions
- If a dequeue happened between the SSPLIT and CSPLIT phases of the
- transaction, the HCD would never receive an interrupt.
- fiq_fsm: Disable by default
- fiq_fsm: Handle HC babble errors
- The HCTSIZ transfer size field raises a babble interrupt if
- the counter wraps. Handle the resulting interrupt in this case.
- dwc_otg: fix interrupt registration for fiq_enable=0
- Additionally make the module parameter conditional for wherever
- hcd->fiq_state is touched.
- fiq_fsm: Enable by default
- ---
- arch/arm/mach-bcm2708/bcm2708.c | 24 +-
- drivers/usb/host/dwc_otg/Makefile | 3 +-
- drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c | 47 +-
- drivers/usb/host/dwc_otg/dwc_otg_driver.c | 51 +-
- drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c | 1290 ++++++++++++++++++++++++++
- drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h | 353 +++++++
- drivers/usb/host/dwc_otg/dwc_otg_fiq_stub.S | 80 ++
- drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 775 +++++++++++++---
- drivers/usb/host/dwc_otg/dwc_otg_hcd.h | 11 +
- drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 999 ++++++++++----------
- drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c | 113 ++-
- drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c | 41 +-
- drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c | 113 ---
- drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h | 48 -
- drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c | 8 +-
- 15 files changed, 2991 insertions(+), 965 deletions(-)
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_fiq_stub.S
- delete mode 100755 drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c
- delete mode 100755 drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h
- --- a/arch/arm/mach-bcm2708/bcm2708.c
- +++ b/arch/arm/mach-bcm2708/bcm2708.c
- @@ -330,22 +330,13 @@ static struct resource bcm2708_usb_resou
- .end = IRQ_HOSTPORT,
- .flags = IORESOURCE_IRQ,
- },
- + [3] = {
- + .start = IRQ_USB,
- + .end = IRQ_USB,
- + .flags = IORESOURCE_IRQ,
- + },
- };
-
- -bool fiq_fix_enable = true;
- -
- -static struct resource bcm2708_usb_resources_no_fiq_fix[] = {
- - [0] = {
- - .start = USB_BASE,
- - .end = USB_BASE + SZ_128K - 1,
- - .flags = IORESOURCE_MEM,
- - },
- - [1] = {
- - .start = IRQ_USB,
- - .end = IRQ_USB,
- - .flags = IORESOURCE_IRQ,
- - },
- -};
-
- static u64 usb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON);
-
- @@ -701,11 +692,6 @@ void __init bcm2708_init(void)
- #endif
- bcm_register_device(&bcm2708_systemtimer_device);
- bcm_register_device(&bcm2708_fb_device);
- - if (!fiq_fix_enable)
- - {
- - bcm2708_usb_device.resource = bcm2708_usb_resources_no_fiq_fix;
- - bcm2708_usb_device.num_resources = ARRAY_SIZE(bcm2708_usb_resources_no_fiq_fix);
- - }
- bcm_register_device(&bcm2708_usb_device);
- bcm_register_device(&bcm2708_uart1_device);
- bcm_register_device(&bcm2708_powerman_device);
- --- a/drivers/usb/host/dwc_otg/Makefile
- +++ b/drivers/usb/host/dwc_otg/Makefile
- @@ -36,7 +36,8 @@ dwc_otg-objs += dwc_otg_cil.o dwc_otg_ci
- dwc_otg-objs += dwc_otg_pcd_linux.o dwc_otg_pcd.o dwc_otg_pcd_intr.o
- dwc_otg-objs += dwc_otg_hcd.o dwc_otg_hcd_linux.o dwc_otg_hcd_intr.o dwc_otg_hcd_queue.o dwc_otg_hcd_ddma.o
- dwc_otg-objs += dwc_otg_adp.o
- -dwc_otg-objs += dwc_otg_mphi_fix.o
- +dwc_otg-objs += dwc_otg_fiq_fsm.o
- +dwc_otg-objs += dwc_otg_fiq_stub.o
- ifneq ($(CFI),)
- dwc_otg-objs += dwc_otg_cfi.o
- endif
- --- a/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c
- @@ -45,7 +45,6 @@
- #include "dwc_otg_driver.h"
- #include "dwc_otg_pcd.h"
- #include "dwc_otg_hcd.h"
- -#include "dwc_otg_mphi_fix.h"
-
- #ifdef DEBUG
- inline const char *op_state_str(dwc_otg_core_if_t * core_if)
- @@ -1319,7 +1318,7 @@ static int32_t dwc_otg_handle_lpm_intr(d
- /**
- * This function returns the Core Interrupt register.
- */
- -static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if, gintmsk_data_t *reenable_gintmsk)
- +static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if, gintmsk_data_t *reenable_gintmsk, dwc_otg_hcd_t *hcd)
- {
- gahbcfg_data_t gahbcfg = {.d32 = 0 };
- gintsts_data_t gintsts;
- @@ -1345,16 +1344,15 @@ static inline uint32_t dwc_otg_read_comm
- }
- gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
- gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
- - {
- - unsigned long flags;
- -
- - // Re-enable the saved interrupts
- - local_irq_save(flags);
- + if(fiq_enable) {
- local_fiq_disable();
- - gintmsk.d32 |= gintmsk_common.d32;
- - gintsts_saved.d32 &= ~gintmsk_common.d32;
- - reenable_gintmsk->d32 = gintmsk.d32;
- - local_irq_restore(flags);
- + /* Pull in the interrupts that the FIQ has masked */
- + gintmsk.d32 |= ~(hcd->fiq_state->gintmsk_saved.d32);
- + /* for the upstairs function to reenable - have to read it here in case FIQ triggers again */
- + reenable_gintmsk->d32 |= gintmsk.d32;
- + reenable_gintmsk->d32 |= ~(hcd->fiq_state->gintmsk_saved.d32);
- + reenable_gintmsk->d32 &= gintmsk_common.d32;
- + local_fiq_enable();
- }
-
- gahbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gahbcfg);
- @@ -1366,13 +1364,15 @@ static inline uint32_t dwc_otg_read_comm
- gintsts.d32, gintmsk.d32);
- }
- #endif
- - if (!fiq_fix_enable){
- + if (!fiq_enable){
- if (gahbcfg.b.glblintrmsk)
- return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32);
- else
- return 0;
- - }
- - else {
- + } else {
- + /* Our IRQ kicker is no longer the USB hardware, it's the MPHI interface.
- + * Can't trust the global interrupt mask bit in this case.
- + */
- return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32);
- }
-
- @@ -1406,7 +1406,7 @@ int32_t dwc_otg_handle_common_intr(void
- {
- int retval = 0;
- gintsts_data_t gintsts;
- - gintmsk_data_t reenable_gintmsk;
- + gintmsk_data_t gintmsk_reenable = { .d32 = 0 };
- gpwrdn_data_t gpwrdn = {.d32 = 0 };
- dwc_otg_device_t *otg_dev = dev;
- dwc_otg_core_if_t *core_if = otg_dev->core_if;
- @@ -1428,7 +1428,10 @@ int32_t dwc_otg_handle_common_intr(void
- }
-
- if (core_if->hibernation_suspend <= 0) {
- - gintsts.d32 = dwc_otg_read_common_intr(core_if, &reenable_gintmsk);
- + /* read_common will have to poke the FIQ's saved mask. We must then clear this mask at the end
- + * of this handler - god only knows why it's done like this
- + */
- + gintsts.d32 = dwc_otg_read_common_intr(core_if, &gintmsk_reenable, otg_dev->hcd);
-
- if (gintsts.b.modemismatch) {
- retval |= dwc_otg_handle_mode_mismatch_intr(core_if);
- @@ -1525,11 +1528,16 @@ int32_t dwc_otg_handle_common_intr(void
- gintsts.b.portintr = 1;
- DWC_WRITE_REG32(&core_if->core_global_regs->gintsts,gintsts.d32);
- retval |= 1;
- - reenable_gintmsk.b.portintr = 1;
- + gintmsk_reenable.b.portintr = 1;
-
- }
- -
- - DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, reenable_gintmsk.d32);
- + /* Did we actually handle anything? if so, unmask the interrupt */
- +// fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "CILOUT %1d", retval);
- +// fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "%08x", gintsts.d32);
- +// fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "%08x", gintmsk_reenable.d32);
- + if (retval) {
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, gintmsk_reenable.d32);
- + }
-
- } else {
- DWC_DEBUGPL(DBG_ANY, "gpwrdn=%08x\n", gpwrdn.d32);
- @@ -1583,6 +1591,5 @@ int32_t dwc_otg_handle_common_intr(void
- }
- if (core_if->lock)
- DWC_SPINUNLOCK(core_if->lock);
- -
- return retval;
- }
- --- a/drivers/usb/host/dwc_otg/dwc_otg_driver.c
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_driver.c
- @@ -56,6 +56,7 @@
- #include "dwc_otg_core_if.h"
- #include "dwc_otg_pcd_if.h"
- #include "dwc_otg_hcd_if.h"
- +#include "dwc_otg_fiq_fsm.h"
-
- #define DWC_DRIVER_VERSION "3.00a 10-AUG-2012"
- #define DWC_DRIVER_DESC "HS OTG USB Controller driver"
- @@ -64,7 +65,6 @@ bool microframe_schedule=true;
-
- static const char dwc_driver_name[] = "dwc_otg";
-
- -extern void* dummy_send;
-
- extern int pcd_init(
- #ifdef LM_INTERFACE
- @@ -240,13 +240,14 @@ static struct dwc_otg_driver_module_para
- .adp_enable = -1,
- };
-
- -//Global variable to switch the fiq fix on or off (declared in bcm2708.c)
- -extern bool fiq_fix_enable;
- +//Global variable to switch the fiq fix on or off
- +bool fiq_enable = 1;
- // Global variable to enable the split transaction fix
- -bool fiq_split_enable = true;
- -//Global variable to switch the nak holdoff on or off
- -bool nak_holdoff_enable = true;
- +bool fiq_fsm_enable = true;
- +//Bulk split-transaction NAK holdoff in microframes
- +uint16_t nak_holdoff = 8;
-
- +unsigned short fiq_fsm_mask = 0x07;
-
- /**
- * This function shows the Driver Version.
- @@ -800,7 +801,7 @@ static int dwc_otg_driver_probe(
- dwc_otg_device->os_dep.base = ioremap_nocache(_dev->resource[0].start,
- _dev->resource[0].end -
- _dev->resource[0].start+1);
- - if (fiq_fix_enable)
- + if (fiq_enable)
- {
- if (!request_mem_region(_dev->resource[1].start,
- _dev->resource[1].end - _dev->resource[1].start + 1,
- @@ -813,7 +814,6 @@ static int dwc_otg_driver_probe(
- dwc_otg_device->os_dep.mphi_base = ioremap_nocache(_dev->resource[1].start,
- _dev->resource[1].end -
- _dev->resource[1].start + 1);
- - dummy_send = (void *) kmalloc(16, GFP_ATOMIC);
- }
-
- #else
- @@ -902,9 +902,9 @@ static int dwc_otg_driver_probe(
- */
-
- #if defined(PLATFORM_INTERFACE)
- - devirq = platform_get_irq(_dev, 0);
- + devirq = platform_get_irq(_dev, fiq_enable ? 0 : 1);
- #else
- - devirq = _dev->irq;
- + devirq = _dev->irq;
- #endif
- DWC_DEBUGPL(DBG_CIL, "registering (common) handler for irq%d\n",
- devirq);
- @@ -1071,9 +1071,9 @@ static int __init dwc_otg_driver_init(vo
- int error;
- struct device_driver *drv;
-
- - if(fiq_split_enable && !fiq_fix_enable) {
- - printk(KERN_WARNING "dwc_otg: fiq_split_enable was set without fiq_fix_enable! Correcting.\n");
- - fiq_fix_enable = 1;
- + if(fiq_fsm_enable && !fiq_enable) {
- + printk(KERN_WARNING "dwc_otg: fiq_fsm_enable was set without fiq_enable! Correcting.\n");
- + fiq_enable = 1;
- }
-
- printk(KERN_INFO "%s: version %s (%s bus)\n", dwc_driver_name,
- @@ -1095,9 +1095,9 @@ static int __init dwc_otg_driver_init(vo
- printk(KERN_ERR "%s retval=%d\n", __func__, retval);
- return retval;
- }
- - printk(KERN_DEBUG "dwc_otg: FIQ %s\n", fiq_fix_enable ? "enabled":"disabled");
- - printk(KERN_DEBUG "dwc_otg: NAK holdoff %s\n", nak_holdoff_enable ? "enabled":"disabled");
- - printk(KERN_DEBUG "dwc_otg: FIQ split fix %s\n", fiq_split_enable ? "enabled":"disabled");
- + printk(KERN_DEBUG "dwc_otg: FIQ %s\n", fiq_enable ? "enabled":"disabled");
- + printk(KERN_DEBUG "dwc_otg: NAK holdoff %s\n", nak_holdoff ? "enabled":"disabled");
- + printk(KERN_DEBUG "dwc_otg: FIQ split-transaction FSM %s\n", fiq_fsm_enable ? "enabled":"disabled");
-
- error = driver_create_file(drv, &driver_attr_version);
- #ifdef DEBUG
- @@ -1378,12 +1378,19 @@ MODULE_PARM_DESC(otg_ver, "OTG revision
- module_param(microframe_schedule, bool, 0444);
- MODULE_PARM_DESC(microframe_schedule, "Enable the microframe scheduler");
-
- -module_param(fiq_fix_enable, bool, 0444);
- -MODULE_PARM_DESC(fiq_fix_enable, "Enable the fiq fix");
- -module_param(nak_holdoff_enable, bool, 0444);
- -MODULE_PARM_DESC(nak_holdoff_enable, "Enable the NAK holdoff");
- -module_param(fiq_split_enable, bool, 0444);
- -MODULE_PARM_DESC(fiq_split_enable, "Enable the FIQ fix on split transactions");
- +module_param(fiq_enable, bool, 0444);
- +MODULE_PARM_DESC(fiq_enable, "Enable the FIQ");
- +module_param(nak_holdoff, ushort, 0644);
- +MODULE_PARM_DESC(nak_holdoff, "Throttle duration for bulk split-transaction endpoints on a NAK. Default 8");
- +module_param(fiq_fsm_enable, bool, 0444);
- +MODULE_PARM_DESC(fiq_fsm_enable, "Enable the FIQ to perform split transactions as defined by fiq_fsm_mask");
- +module_param(fiq_fsm_mask, ushort, 0444);
- +MODULE_PARM_DESC(fiq_fsm_mask, "Bitmask of transactions to perform in the FIQ.\n"
- + "Bit 0 : Non-periodic split transactions\n"
- + "Bit 1 : Periodic split transactions\n"
- + "Bit 2 : High-speed multi-transfer isochronous\n"
- + "All other bits should be set 0.");
- +
-
- /** @page "Module Parameters"
- *
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c
- @@ -0,0 +1,1290 @@
- +/*
- + * dwc_otg_fiq_fsm.c - The finite state machine FIQ
- + *
- + * Copyright (c) 2013 Raspberry Pi Foundation
- + *
- + * Author: Jonathan Bell <jonathan@raspberrypi.org>
- + * All rights reserved.
- + *
- + * Redistribution and use in source and binary forms, with or without
- + * modification, are permitted provided that the following conditions are met:
- + * * Redistributions of source code must retain the above copyright
- + * notice, this list of conditions and the following disclaimer.
- + * * Redistributions in binary form must reproduce the above copyright
- + * notice, this list of conditions and the following disclaimer in the
- + * documentation and/or other materials provided with the distribution.
- + * * Neither the name of Raspberry Pi nor the
- + * names of its contributors may be used to endorse or promote products
- + * derived from this software without specific prior written permission.
- + *
- + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- + * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
- + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- + *
- + * This FIQ implements functionality that performs split transactions on
- + * the dwc_otg hardware without any outside intervention. A split transaction
- + * is "queued" by nominating a specific host channel to perform the entirety
- + * of a split transaction. This FIQ will then perform the microframe-precise
- + * scheduling required in each phase of the transaction until completion.
- + *
- + * The FIQ functionality is glued into the Synopsys driver via the entry point
- + * in the FSM enqueue function, and at the exit point in handling a HC interrupt
- + * for a FSM-enabled channel.
- + *
- + * NB: Large parts of this implementation have architecture-specific code.
- + * For porting this functionality to other ARM machines, the minimum is required:
- + * - An interrupt controller allowing the top-level dwc USB interrupt to be routed
- + * to the FIQ
- + * - A method of forcing a software generated interrupt from FIQ mode that then
- + * triggers an IRQ entry (with the dwc USB handler called by this IRQ number)
- + * - Guaranteed interrupt routing such that both the FIQ and SGI occur on the same
- + * processor core - there is no locking between the FIQ and IRQ (aside from
- + * local_fiq_disable)
- + *
- + */
- +
- +#include "dwc_otg_fiq_fsm.h"
- +
- +
- +char buffer[1000*16];
- +int wptr;
- +void notrace _fiq_print(enum fiq_debug_level dbg_lvl, volatile struct fiq_state *state, char *fmt, ...)
- +{
- + enum fiq_debug_level dbg_lvl_req = FIQDBG_ERR;
- + va_list args;
- + char text[17];
- + hfnum_data_t hfnum = { .d32 = FIQ_READ(state->dwc_regs_base + 0x408) };
- +
- + if((dbg_lvl & dbg_lvl_req) || dbg_lvl == FIQDBG_ERR)
- + {
- + snprintf(text, 9, " %4d:%1u ", hfnum.b.frnum/8, hfnum.b.frnum & 7);
- + va_start(args, fmt);
- + vsnprintf(text+8, 9, fmt, args);
- + va_end(args);
- +
- + memcpy(buffer + wptr, text, 16);
- + wptr = (wptr + 16) % sizeof(buffer);
- + }
- +}
- +
- +/**
- + * fiq_fsm_restart_channel() - Poke channel enable bit for a split transaction
- + * @channel: channel to re-enable
- + */
- +static void fiq_fsm_restart_channel(struct fiq_state *st, int n, int force)
- +{
- + hcchar_data_t hcchar = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR) };
- +
- + hcchar.b.chen = 0;
- + if (st->channel[n].hcchar_copy.b.eptype & 0x1) {
- + hfnum_data_t hfnum = { .d32 = FIQ_READ(st->dwc_regs_base + HFNUM) };
- + /* Hardware bug workaround: update the ssplit index */
- + if (st->channel[n].hcsplt_copy.b.spltena)
- + st->channel[n].expected_uframe = (hfnum.b.frnum + 1) & 0x3FFF;
- +
- + hcchar.b.oddfrm = (hfnum.b.frnum & 0x1) ? 0 : 1;
- + }
- +
- + FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR, hcchar.d32);
- + hcchar.d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR);
- + hcchar.b.chen = 1;
- +
- + FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR, hcchar.d32);
- + fiq_print(FIQDBG_INT, st, "HCGO %01d %01d", n, force);
- +}
- +
- +/**
- + * fiq_fsm_setup_csplit() - Prepare a host channel for a CSplit transaction stage
- + * @st: Pointer to the channel's state
- + * @n : channel number
- + *
- + * Change host channel registers to perform a complete-split transaction. Being mindful of the
- + * endpoint direction, set control regs up correctly.
- + */
- +static void notrace fiq_fsm_setup_csplit(struct fiq_state *st, int n)
- +{
- + hcsplt_data_t hcsplt = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCSPLT) };
- + hctsiz_data_t hctsiz = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ) };
- +
- + hcsplt.b.compsplt = 1;
- + if (st->channel[n].hcchar_copy.b.epdir == 1) {
- + // If IN, the CSPLIT result contains the data or a hub handshake. hctsiz = maxpacket.
- + hctsiz.b.xfersize = st->channel[n].hctsiz_copy.b.xfersize;
- + } else {
- + // If OUT, the CSPLIT result contains handshake only.
- + hctsiz.b.xfersize = 0;
- + }
- + FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCSPLT, hcsplt.d32);
- + FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ, hctsiz.d32);
- + mb();
- +}
- +
- +static inline int notrace fiq_get_xfer_len(struct fiq_state *st, int n)
- +{
- + /* The xfersize register is a bit wonky. For IN transfers, it decrements by the packet size. */
- + hctsiz_data_t hctsiz = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ) };
- +
- + if (st->channel[n].hcchar_copy.b.epdir == 0) {
- + return st->channel[n].hctsiz_copy.b.xfersize;
- + } else {
- + return st->channel[n].hctsiz_copy.b.xfersize - hctsiz.b.xfersize;
- + }
- +
- +}
- +
- +
- +/**
- + * fiq_increment_dma_buf() - update DMA address for bounce buffers after a CSPLIT
- + *
- + * Of use only for IN periodic transfers.
- + */
- +static int notrace fiq_increment_dma_buf(struct fiq_state *st, int num_channels, int n)
- +{
- + hcdma_data_t hcdma;
- + int i = st->channel[n].dma_info.index;
- + int len;
- + struct fiq_dma_blob *blob = (struct fiq_dma_blob *) st->dma_base;
- +
- + len = fiq_get_xfer_len(st, n);
- + fiq_print(FIQDBG_INT, st, "LEN: %03d", len);
- + st->channel[n].dma_info.slot_len[i] = len;
- + i++;
- + if (i > 6)
- + BUG();
- +
- + hcdma.d32 = (dma_addr_t) &blob->channel[n].index[i].buf[0];
- + FIQ_WRITE(st->dwc_regs_base + HC_DMA + (HC_OFFSET * n), hcdma.d32);
- + st->channel[n].dma_info.index = i;
- + return 0;
- +}
- +
- +/**
- + * fiq_reload_hctsiz() - for IN transactions, reset HCTSIZ
- + */
- +static void notrace fiq_fsm_reload_hctsiz(struct fiq_state *st, int n)
- +{
- + hctsiz_data_t hctsiz = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ) };
- + hctsiz.b.xfersize = st->channel[n].hctsiz_copy.b.xfersize;
- + hctsiz.b.pktcnt = 1;
- + FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ, hctsiz.d32);
- +}
- +
- +/**
- + * fiq_iso_out_advance() - update DMA address and split position bits
- + * for isochronous OUT transactions.
- + *
- + * Returns 1 if this is the last packet queued, 0 otherwise. Split-ALL and
- + * Split-BEGIN states are not handled - this is done when the transaction was queued.
- + *
- + * This function must only be called from the FIQ_ISO_OUT_ACTIVE state.
- + */
- +static int notrace fiq_iso_out_advance(struct fiq_state *st, int num_channels, int n)
- +{
- + hcsplt_data_t hcsplt;
- + hctsiz_data_t hctsiz;
- + hcdma_data_t hcdma;
- + struct fiq_dma_blob *blob = (struct fiq_dma_blob *) st->dma_base;
- + int last = 0;
- + int i = st->channel[n].dma_info.index;
- +
- + fiq_print(FIQDBG_INT, st, "ADV %01d %01d ", n, i);
- + i++;
- + if (i == 4)
- + last = 1;
- + if (st->channel[n].dma_info.slot_len[i+1] == 255)
- + last = 1;
- +
- + /* New DMA address - address of bounce buffer referred to in index */
- + hcdma.d32 = (uint32_t) &blob->channel[n].index[i].buf[0];
- + //hcdma.d32 = FIQ_READ(st->dwc_regs_base + HC_DMA + (HC_OFFSET * n));
- + //hcdma.d32 += st->channel[n].dma_info.slot_len[i];
- + fiq_print(FIQDBG_INT, st, "LAST: %01d ", last);
- + fiq_print(FIQDBG_INT, st, "LEN: %03d", st->channel[n].dma_info.slot_len[i]);
- + hcsplt.d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCSPLT);
- + hctsiz.d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ);
- + hcsplt.b.xactpos = (last) ? ISOC_XACTPOS_END : ISOC_XACTPOS_MID;
- + /* Set up new packet length */
- + hctsiz.b.pktcnt = 1;
- + hctsiz.b.xfersize = st->channel[n].dma_info.slot_len[i];
- + fiq_print(FIQDBG_INT, st, "%08x", hctsiz.d32);
- +
- + st->channel[n].dma_info.index++;
- + FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCSPLT, hcsplt.d32);
- + FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ, hctsiz.d32);
- + FIQ_WRITE(st->dwc_regs_base + HC_DMA + (HC_OFFSET * n), hcdma.d32);
- + return last;
- +}
- +
- +/**
- + * fiq_fsm_tt_next_isoc() - queue next pending isochronous out start-split on a TT
- + *
- + * Despite the limitations of the DWC core, we can force a microframe pipeline of
- + * isochronous OUT start-split transactions while waiting for a corresponding other-type
- + * of endpoint to finish its CSPLITs. TTs have big periodic buffers therefore it
- + * is very unlikely that filling the start-split FIFO will cause data loss.
- + * This allows much better interleaving of transactions in an order-independent way-
- + * there is no requirement to prioritise isochronous, just a state-space search has
- + * to be performed on each periodic start-split complete interrupt.
- + */
- +static int notrace fiq_fsm_tt_next_isoc(struct fiq_state *st, int num_channels, int n)
- +{
- + int hub_addr = st->channel[n].hub_addr;
- + int port_addr = st->channel[n].port_addr;
- + int i, poked = 0;
- + for (i = 0; i < num_channels; i++) {
- + if (i == n || st->channel[i].fsm == FIQ_PASSTHROUGH)
- + continue;
- + if (st->channel[i].hub_addr == hub_addr &&
- + st->channel[i].port_addr == port_addr) {
- + switch (st->channel[i].fsm) {
- + case FIQ_PER_ISO_OUT_PENDING:
- + if (st->channel[i].nrpackets == 1) {
- + st->channel[i].fsm = FIQ_PER_ISO_OUT_LAST;
- + } else {
- + st->channel[i].fsm = FIQ_PER_ISO_OUT_ACTIVE;
- + }
- + fiq_fsm_restart_channel(st, i, 0);
- + poked = 1;
- + break;
- +
- + default:
- + break;
- + }
- + }
- + if (poked)
- + break;
- + }
- + return poked;
- +}
- +
- +/**
- + * fiq_fsm_tt_in_use() - search for host channels using this TT
- + * @n: Channel to use as reference
- + *
- + */
- +int notrace noinline fiq_fsm_tt_in_use(struct fiq_state *st, int num_channels, int n)
- +{
- + int hub_addr = st->channel[n].hub_addr;
- + int port_addr = st->channel[n].port_addr;
- + int i, in_use = 0;
- + for (i = 0; i < num_channels; i++) {
- + if (i == n || st->channel[i].fsm == FIQ_PASSTHROUGH)
- + continue;
- + switch (st->channel[i].fsm) {
- + /* TT is reserved for channels that are in the middle of a periodic
- + * split transaction.
- + */
- + case FIQ_PER_SSPLIT_STARTED:
- + case FIQ_PER_CSPLIT_WAIT:
- + case FIQ_PER_CSPLIT_NYET1:
- + //case FIQ_PER_CSPLIT_POLL:
- + case FIQ_PER_ISO_OUT_ACTIVE:
- + case FIQ_PER_ISO_OUT_LAST:
- + if (st->channel[i].hub_addr == hub_addr &&
- + st->channel[i].port_addr == port_addr) {
- + in_use = 1;
- + }
- + break;
- + default:
- + break;
- + }
- + if (in_use)
- + break;
- + }
- + return in_use;
- +}
- +
- +/**
- + * fiq_fsm_more_csplits() - determine whether additional CSPLITs need
- + * to be issued for this IN transaction.
- + *
- + * We cannot tell the inbound PID of a data packet due to hardware limitations.
- + * we need to make an educated guess as to whether we need to queue another CSPLIT
- + * or not. A no-brainer is when we have received enough data to fill the endpoint
- + * size, but for endpoints that give variable-length data then we have to resort
- + * to heuristics.
- + *
- + * We also return whether this is the last CSPLIT to be queued, again based on
- + * heuristics. This is to allow a 1-uframe overlap of periodic split transactions.
- + * Note: requires at least 1 CSPLIT to have been performed prior to being called.
- + */
- +
- +/*
- + * We need some way of guaranteeing if a returned periodic packet of size X
- + * has a DATA0 PID.
- + * The heuristic value of 144 bytes assumes that the received data has maximal
- + * bit-stuffing and the clock frequency of the transmitting device is at the lowest
- + * permissible limit. If the transfer length results in a final packet size
- + * 144 < p <= 188, then an erroneous CSPLIT will be issued.
- + * Also used to ensure that an endpoint will nominally only return a single
- + * complete-split worth of data.
- + */
- +#define DATA0_PID_HEURISTIC 144
- +
- +static int notrace noinline fiq_fsm_more_csplits(struct fiq_state *state, int n, int *probably_last)
- +{
- +
- + int i;
- + int total_len = 0;
- + int more_needed = 1;
- + struct fiq_channel_state *st = &state->channel[n];
- +
- + for (i = 0; i < st->dma_info.index; i++) {
- + total_len += st->dma_info.slot_len[i];
- + }
- +
- + *probably_last = 0;
- +
- + if (st->hcchar_copy.b.eptype == 0x3) {
- + /*
- + * An interrupt endpoint will take max 2 CSPLITs. if we are receiving data
- + * then this is definitely the last CSPLIT.
- + */
- + *probably_last = 1;
- + } else {
- + /* Isoc IN. This is a bit risky if we are the first transaction:
- + * we may have been held off slightly. */
- + if (i > 1 && st->dma_info.slot_len[st->dma_info.index-1] <= DATA0_PID_HEURISTIC) {
- + more_needed = 0;
- + }
- + /* If in the next uframe we will receive enough data to fill the endpoint,
- + * then only issue 1 more csplit.
- + */
- + if (st->hctsiz_copy.b.xfersize - total_len <= DATA0_PID_HEURISTIC)
- + *probably_last = 1;
- + }
- +
- + if (total_len >= st->hctsiz_copy.b.xfersize ||
- + i == 6 || total_len == 0)
- + /* Note: due to bit stuffing it is possible to have > 6 CSPLITs for
- + * a single endpoint. Accepting more would completely break our scheduling mechanism though
- + * - in these extreme cases we will pass through a truncated packet.
- + */
- + more_needed = 0;
- +
- + return more_needed;
- +}
- +
- +/**
- + * fiq_fsm_too_late() - Test transaction for lateness
- + *
- + * If a SSPLIT for a large IN transaction is issued too late in a frame,
- + * the hub will disable the port to the device and respond with ERR handshakes.
- + * The hub status endpoint will not reflect this change.
- + * Returns 1 if we will issue a SSPLIT that will result in a device babble.
- + */
- +int notrace fiq_fsm_too_late(struct fiq_state *st, int n)
- +{
- + int uframe;
- + hfnum_data_t hfnum = { .d32 = FIQ_READ(st->dwc_regs_base + HFNUM) };
- + uframe = hfnum.b.frnum & 0x7;
- + if ((uframe < 6) && (st->channel[n].nrpackets + 1 + uframe > 7)) {
- + return 1;
- + } else {
- + return 0;
- + }
- +}
- +
- +
- +/**
- + * fiq_fsm_start_next_periodic() - A half-arsed attempt at a microframe pipeline
- + *
- + * Search pending transactions in the start-split pending state and queue them.
- + * Don't queue packets in uframe .5 (comes out in .6) (USB2.0 11.18.4).
- + * Note: we specifically don't do isochronous OUT transactions first because better
- + * use of the TT's start-split fifo can be achieved by pipelining an IN before an OUT.
- + */
- +static void notrace noinline fiq_fsm_start_next_periodic(struct fiq_state *st, int num_channels)
- +{
- + int n;
- + hfnum_data_t hfnum = { .d32 = FIQ_READ(st->dwc_regs_base + HFNUM) };
- + if ((hfnum.b.frnum & 0x7) == 5)
- + return;
- + for (n = 0; n < num_channels; n++) {
- + if (st->channel[n].fsm == FIQ_PER_SSPLIT_QUEUED) {
- + /* Check to see if any other transactions are using this TT */
- + if(!fiq_fsm_tt_in_use(st, num_channels, n)) {
- + if (!fiq_fsm_too_late(st, n)) {
- + st->channel[n].fsm = FIQ_PER_SSPLIT_STARTED;
- + fiq_print(FIQDBG_INT, st, "NEXTPER ");
- + fiq_fsm_restart_channel(st, n, 0);
- + } else {
- + st->channel[n].fsm = FIQ_PER_SPLIT_TIMEOUT;
- + }
- + break;
- + }
- + }
- + }
- + for (n = 0; n < num_channels; n++) {
- + if (st->channel[n].fsm == FIQ_PER_ISO_OUT_PENDING) {
- + if (!fiq_fsm_tt_in_use(st, num_channels, n)) {
- + fiq_print(FIQDBG_INT, st, "NEXTISO ");
- + st->channel[n].fsm = FIQ_PER_ISO_OUT_ACTIVE;
- + fiq_fsm_restart_channel(st, n, 0);
- + break;
- + }
- + }
- + }
- +}
- +
- +/**
- + * fiq_fsm_update_hs_isoc() - update isochronous frame and transfer data
- + * @state: Pointer to fiq_state
- + * @n: Channel transaction is active on
- + * @hcint: Copy of host channel interrupt register
- + *
- + * Returns 0 if there are no more transactions for this HC to do, 1
- + * otherwise.
- + */
- +static int notrace noinline fiq_fsm_update_hs_isoc(struct fiq_state *state, int n, hcint_data_t hcint)
- +{
- + struct fiq_channel_state *st = &state->channel[n];
- + int xfer_len = 0, nrpackets = 0;
- + hcdma_data_t hcdma;
- + fiq_print(FIQDBG_INT, state, "HSISO %02d", n);
- +
- + xfer_len = fiq_get_xfer_len(state, n);
- + st->hs_isoc_info.iso_desc[st->hs_isoc_info.index].actual_length = xfer_len;
- +
- + st->hs_isoc_info.iso_desc[st->hs_isoc_info.index].status = hcint.d32;
- +
- + st->hs_isoc_info.index++;
- + if (st->hs_isoc_info.index == st->hs_isoc_info.nrframes) {
- + return 0;
- + }
- +
- + /* grab the next DMA address offset from the array */
- + hcdma.d32 = st->hcdma_copy.d32 + st->hs_isoc_info.iso_desc[st->hs_isoc_info.index].offset;
- + FIQ_WRITE(state->dwc_regs_base + HC_DMA + (HC_OFFSET * n), hcdma.d32);
- +
- + /* We need to set multi_count. This is a bit tricky - has to be set per-transaction as
- + * the core needs to be told to send the correct number. Caution: for IN transfers,
- + * this is always set to the maximum size of the endpoint. */
- + xfer_len = st->hs_isoc_info.iso_desc[st->hs_isoc_info.index].length;
- + /* Integer divide in a FIQ: fun. FIXME: make this not suck */
- + nrpackets = (xfer_len + st->hcchar_copy.b.mps - 1) / st->hcchar_copy.b.mps;
- + if (nrpackets == 0)
- + nrpackets = 1;
- + st->hcchar_copy.b.multicnt = nrpackets;
- + st->hctsiz_copy.b.pktcnt = nrpackets;
- +
- + /* Initial PID also needs to be set */
- + if (st->hcchar_copy.b.epdir == 0) {
- + st->hctsiz_copy.b.xfersize = xfer_len;
- + switch (st->hcchar_copy.b.multicnt) {
- + case 1:
- + st->hctsiz_copy.b.pid = DWC_PID_DATA0;
- + break;
- + case 2:
- + case 3:
- + st->hctsiz_copy.b.pid = DWC_PID_MDATA;
- + break;
- + }
- +
- + } else {
- + switch (st->hcchar_copy.b.multicnt) {
- + st->hctsiz_copy.b.xfersize = nrpackets * st->hcchar_copy.b.mps;
- + case 1:
- + st->hctsiz_copy.b.pid = DWC_PID_DATA0;
- + break;
- + case 2:
- + st->hctsiz_copy.b.pid = DWC_PID_DATA1;
- + break;
- + case 3:
- + st->hctsiz_copy.b.pid = DWC_PID_DATA2;
- + break;
- + }
- + }
- + FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ, st->hctsiz_copy.d32);
- + FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR, st->hcchar_copy.d32);
- + /* Channel is enabled on hcint handler exit */
- + fiq_print(FIQDBG_INT, state, "HSISOOUT");
- + return 1;
- +}
- +
- +
- +/**
- + * fiq_fsm_do_sof() - FSM start-of-frame interrupt handler
- + * @state: Pointer to the state struct passed from banked FIQ mode registers.
- + * @num_channels: set according to the DWC hardware configuration
- + *
- + * The SOF handler in FSM mode has two functions
- + * 1. Hold off SOF from causing schedule advancement in IRQ context if there's
- + * nothing to do
- + * 2. Advance certain FSM states that require either a microframe delay, or a microframe
- + * of holdoff.
- + *
- + * The second part is architecture-specific to mach-bcm2835 -
- + * a sane interrupt controller would have a mask register for ARM interrupt sources
- + * to be promoted to the nFIQ line, but it doesn't. Instead a single interrupt
- + * number (USB) can be enabled. This means that certain parts of the USB specification
- + * that require "wait a little while, then issue another packet" cannot be fulfilled with
- + * the timing granularity required to achieve optimal throughout. The workaround is to use
- + * the SOF "timer" (125uS) to perform this task.
- + */
- +static int notrace noinline fiq_fsm_do_sof(struct fiq_state *state, int num_channels)
- +{
- + hfnum_data_t hfnum = { .d32 = FIQ_READ(state->dwc_regs_base + HFNUM) };
- + int n;
- + int kick_irq = 0;
- +
- + if ((hfnum.b.frnum & 0x7) == 1) {
- + /* We cannot issue csplits for transactions in the last frame past (n+1).1
- + * Check to see if there are any transactions that are stale.
- + * Boot them out.
- + */
- + for (n = 0; n < num_channels; n++) {
- + switch (state->channel[n].fsm) {
- + case FIQ_PER_CSPLIT_WAIT:
- + case FIQ_PER_CSPLIT_NYET1:
- + case FIQ_PER_CSPLIT_POLL:
- + case FIQ_PER_CSPLIT_LAST:
- + /* Check if we are no longer in the same full-speed frame. */
- + if (((state->channel[n].expected_uframe & 0x3FFF) & ~0x7) <
- + (hfnum.b.frnum & ~0x7))
- + state->channel[n].fsm = FIQ_PER_SPLIT_TIMEOUT;
- + break;
- + default:
- + break;
- + }
- + }
- + }
- +
- + for (n = 0; n < num_channels; n++) {
- + switch (state->channel[n].fsm) {
- +
- + case FIQ_NP_SSPLIT_RETRY:
- + case FIQ_NP_IN_CSPLIT_RETRY:
- + case FIQ_NP_OUT_CSPLIT_RETRY:
- + fiq_fsm_restart_channel(state, n, 0);
- + break;
- +
- + case FIQ_HS_ISOC_SLEEPING:
- + state->channel[n].fsm = FIQ_HS_ISOC_TURBO;
- + fiq_fsm_restart_channel(state, n, 0);
- + break;
- +
- + case FIQ_PER_SSPLIT_QUEUED:
- + if ((hfnum.b.frnum & 0x7) == 5)
- + break;
- + if(!fiq_fsm_tt_in_use(state, num_channels, n)) {
- + if (!fiq_fsm_too_late(state, n)) {
- + fiq_print(FIQDBG_INT, st, "SOF GO %01d", n);
- + fiq_fsm_restart_channel(state, n, 0);
- + state->channel[n].fsm = FIQ_PER_SSPLIT_STARTED;
- + } else {
- + /* Transaction cannot be started without risking a device babble error */
- + state->channel[n].fsm = FIQ_PER_SPLIT_TIMEOUT;
- + state->haintmsk_saved.b2.chint &= ~(1 << n);
- + FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINTMSK, 0);
- + kick_irq |= 1;
- + }
- + }
- + break;
- +
- + case FIQ_PER_ISO_OUT_PENDING:
- + /* Ordinarily, this should be poked after the SSPLIT
- + * complete interrupt for a competing transfer on the same
- + * TT. Doesn't happen for aborted transactions though.
- + */
- + if ((hfnum.b.frnum & 0x7) >= 5)
- + break;
- + if (!fiq_fsm_tt_in_use(state, num_channels, n)) {
- + /* Hardware bug. SOF can sometimes occur after the channel halt interrupt
- + * that caused this.
- + */
- + fiq_fsm_restart_channel(state, n, 0);
- + fiq_print(FIQDBG_INT, state, "SOF ISOC");
- + if (state->channel[n].nrpackets == 1) {
- + state->channel[n].fsm = FIQ_PER_ISO_OUT_LAST;
- + } else {
- + state->channel[n].fsm = FIQ_PER_ISO_OUT_ACTIVE;
- + }
- + }
- + break;
- +
- + case FIQ_PER_CSPLIT_WAIT:
- + /* we are guaranteed to be in this state if and only if the SSPLIT interrupt
- + * occurred when the bus transaction occurred. The SOF interrupt reversal bug
- + * will utterly bugger this up though.
- + */
- + if (hfnum.b.frnum != state->channel[n].expected_uframe) {
- + fiq_print(FIQDBG_INT, state, "SOFCS %d ", n);
- + state->channel[n].fsm = FIQ_PER_CSPLIT_POLL;
- + fiq_fsm_restart_channel(state, n, 0);
- + fiq_fsm_start_next_periodic(state, num_channels);
- +
- + }
- + break;
- +
- + case FIQ_PER_SPLIT_TIMEOUT:
- + case FIQ_DEQUEUE_ISSUED:
- + /* Ugly: we have to force a HCD interrupt.
- + * Poke the mask for the channel in question.
- + * We will take a fake SOF because of this, but
- + * that's OK.
- + */
- + state->haintmsk_saved.b2.chint &= ~(1 << n);
- + FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINTMSK, 0);
- + kick_irq |= 1;
- + break;
- +
- + default:
- + break;
- + }
- + }
- +
- + if (state->kick_np_queues ||
- + dwc_frame_num_le(state->next_sched_frame, hfnum.b.frnum))
- + kick_irq |= 1;
- +
- + return !kick_irq;
- +}
- +
- +
- +/**
- + * fiq_fsm_do_hcintr() - FSM host channel interrupt handler
- + * @state: Pointer to the FIQ state struct
- + * @num_channels: Number of channels as per hardware config
- + * @n: channel for which HAINT(i) was raised
- + *
- + * An important property is that only the CHHLT interrupt is unmasked. Unfortunately, AHBerr is as well.
- + */
- +static int notrace noinline fiq_fsm_do_hcintr(struct fiq_state *state, int num_channels, int n)
- +{
- + hcint_data_t hcint;
- + hcintmsk_data_t hcintmsk;
- + hcint_data_t hcint_probe;
- + hcchar_data_t hcchar;
- + int handled = 0;
- + int restart = 0;
- + int last_csplit = 0;
- + int start_next_periodic = 0;
- + struct fiq_channel_state *st = &state->channel[n];
- + hfnum_data_t hfnum;
- +
- + hcint.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINT);
- + hcintmsk.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINTMSK);
- + hcint_probe.d32 = hcint.d32 & hcintmsk.d32;
- +
- + if (st->fsm != FIQ_PASSTHROUGH) {
- + fiq_print(FIQDBG_INT, state, "HC%01d ST%02d", n, st->fsm);
- + fiq_print(FIQDBG_INT, state, "%08x", hcint.d32);
- + }
- +
- + switch (st->fsm) {
- +
- + case FIQ_PASSTHROUGH:
- + case FIQ_DEQUEUE_ISSUED:
- + /* doesn't belong to us, kick it upstairs */
- + break;
- +
- + case FIQ_PASSTHROUGH_ERRORSTATE:
- + /* We are here to emulate the error recovery mechanism of the dwc HCD.
- + * Several interrupts are unmasked if a previous transaction failed - it's
- + * death for the FIQ to attempt to handle them as the channel isn't halted.
- + * Emulate what the HCD does in this situation: mask and continue.
- + * The FSM has no other state setup so this has to be handled out-of-band.
- + */
- + fiq_print(FIQDBG_ERR, state, "ERRST %02d", n);
- + if (hcint_probe.b.nak || hcint_probe.b.ack || hcint_probe.b.datatglerr) {
- + fiq_print(FIQDBG_ERR, state, "RESET %02d", n);
- + st->nr_errors = 0;
- + hcintmsk.b.nak = 0;
- + hcintmsk.b.ack = 0;
- + hcintmsk.b.datatglerr = 0;
- + FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINTMSK, hcintmsk.d32);
- + return 1;
- + }
- + if (hcint_probe.b.chhltd) {
- + fiq_print(FIQDBG_ERR, state, "CHHLT %02d", n);
- + fiq_print(FIQDBG_ERR, state, "%08x", hcint.d32);
- + return 0;
- + }
- + break;
- +
- + /* Non-periodic state groups */
- + case FIQ_NP_SSPLIT_STARTED:
- + case FIQ_NP_SSPLIT_RETRY:
- + /* Got a HCINT for a NP SSPLIT. Expected ACK / NAK / fail */
- + if (hcint.b.ack) {
- + /* SSPLIT complete. For OUT, the data has been sent. For IN, the LS transaction
- + * will start shortly. SOF needs to kick the transaction to prevent a NYET flood.
- + */
- + if(st->hcchar_copy.b.epdir == 1)
- + st->fsm = FIQ_NP_IN_CSPLIT_RETRY;
- + else
- + st->fsm = FIQ_NP_OUT_CSPLIT_RETRY;
- + st->nr_errors = 0;
- + handled = 1;
- + fiq_fsm_setup_csplit(state, n);
- + } else if (hcint.b.nak) {
- + // No buffer space in TT. Retry on a uframe boundary.
- + st->fsm = FIQ_NP_SSPLIT_RETRY;
- + handled = 1;
- + } else if (hcint.b.xacterr) {
- + // The only other one we care about is xacterr. This implies HS bus error - retry.
- + st->nr_errors++;
- + st->fsm = FIQ_NP_SSPLIT_RETRY;
- + if (st->nr_errors >= 3) {
- + st->fsm = FIQ_NP_SPLIT_HS_ABORTED;
- + } else {
- + handled = 1;
- + restart = 1;
- + }
- + } else {
- + st->fsm = FIQ_NP_SPLIT_LS_ABORTED;
- + handled = 0;
- + restart = 0;
- + }
- + break;
- +
- + case FIQ_NP_IN_CSPLIT_RETRY:
- + /* Received a CSPLIT done interrupt.
- + * Expected Data/NAK/STALL/NYET for IN.
- + */
- + if (hcint.b.xfercomp) {
- + /* For IN, data is present. */
- + st->fsm = FIQ_NP_SPLIT_DONE;
- + } else if (hcint.b.nak) {
- + /* no endpoint data. Punt it upstairs */
- + st->fsm = FIQ_NP_SPLIT_DONE;
- + } else if (hcint.b.nyet) {
- + /* CSPLIT NYET - retry on a uframe boundary. */
- + handled = 1;
- + st->nr_errors = 0;
- + } else if (hcint.b.datatglerr) {
- + /* data toggle errors do not set the xfercomp bit. */
- + st->fsm = FIQ_NP_SPLIT_LS_ABORTED;
- + } else if (hcint.b.xacterr) {
- + /* HS error. Retry immediate */
- + st->fsm = FIQ_NP_IN_CSPLIT_RETRY;
- + st->nr_errors++;
- + if (st->nr_errors >= 3) {
- + st->fsm = FIQ_NP_SPLIT_HS_ABORTED;
- + } else {
- + handled = 1;
- + restart = 1;
- + }
- + } else if (hcint.b.stall || hcint.b.bblerr) {
- + /* A STALL implies either a LS bus error or a genuine STALL. */
- + st->fsm = FIQ_NP_SPLIT_LS_ABORTED;
- + } else {
- + /* Hardware bug. It's possible in some cases to
- + * get a channel halt with nothing else set when
- + * the response was a NYET. Treat as local 3-strikes retry.
- + */
- + hcint_data_t hcint_test = hcint;
- + hcint_test.b.chhltd = 0;
- + if (!hcint_test.d32) {
- + st->nr_errors++;
- + if (st->nr_errors >= 3) {
- + st->fsm = FIQ_NP_SPLIT_HS_ABORTED;
- + } else {
- + handled = 1;
- + }
- + } else {
- + /* Bail out if something unexpected happened */
- + st->fsm = FIQ_NP_SPLIT_HS_ABORTED;
- + }
- + }
- + break;
- +
- + case FIQ_NP_OUT_CSPLIT_RETRY:
- + /* Received a CSPLIT done interrupt.
- + * Expected ACK/NAK/STALL/NYET/XFERCOMP for OUT.*/
- + if (hcint.b.xfercomp) {
- + st->fsm = FIQ_NP_SPLIT_DONE;
- + } else if (hcint.b.nak) {
- + // The HCD will implement the holdoff on frame boundaries.
- + st->fsm = FIQ_NP_SPLIT_DONE;
- + } else if (hcint.b.nyet) {
- + // Hub still processing.
- + st->fsm = FIQ_NP_OUT_CSPLIT_RETRY;
- + handled = 1;
- + st->nr_errors = 0;
- + //restart = 1;
- + } else if (hcint.b.xacterr) {
- + /* HS error. retry immediate */
- + st->fsm = FIQ_NP_OUT_CSPLIT_RETRY;
- + st->nr_errors++;
- + if (st->nr_errors >= 3) {
- + st->fsm = FIQ_NP_SPLIT_HS_ABORTED;
- + } else {
- + handled = 1;
- + restart = 1;
- + }
- + } else if (hcint.b.stall) {
- + /* LS bus error or genuine stall */
- + st->fsm = FIQ_NP_SPLIT_LS_ABORTED;
- + } else {
- + /*
- + * Hardware bug. It's possible in some cases to get a
- + * channel halt with nothing else set when the response was a NYET.
- + * Treat as local 3-strikes retry.
- + */
- + hcint_data_t hcint_test = hcint;
- + hcint_test.b.chhltd = 0;
- + if (!hcint_test.d32) {
- + st->nr_errors++;
- + if (st->nr_errors >= 3) {
- + st->fsm = FIQ_NP_SPLIT_HS_ABORTED;
- + } else {
- + handled = 1;
- + }
- + } else {
- + // Something unexpected happened. AHBerror or babble perhaps. Let the IRQ deal with it.
- + st->fsm = FIQ_NP_SPLIT_HS_ABORTED;
- + }
- + }
- + break;
- +
- + /* Periodic split states (except isoc out) */
- + case FIQ_PER_SSPLIT_STARTED:
- + /* Expect an ACK or failure for SSPLIT */
- + if (hcint.b.ack) {
- + /*
- + * SSPLIT transfer complete interrupt - the generation of this interrupt is fraught with bugs.
- + * For a packet queued in microframe n-3 to appear in n-2, if the channel is enabled near the EOF1
- + * point for microframe n-3, the packet will not appear on the bus until microframe n.
- + * Additionally, the generation of the actual interrupt is dodgy. For a packet appearing on the bus
- + * in microframe n, sometimes the interrupt is generated immediately. Sometimes, it appears in n+1
- + * coincident with SOF for n+1.
- + * SOF is also buggy. It can sometimes be raised AFTER the first bus transaction has taken place.
- + * These appear to be caused by timing/clock crossing bugs within the core itself.
- + * State machine workaround.
- + */
- + hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM);
- + hcchar.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR);
- + fiq_fsm_setup_csplit(state, n);
- + /* Poke the oddfrm bit. If we are equivalent, we received the interrupt at the correct
- + * time. If not, then we're in the next SOF.
- + */
- + if ((hfnum.b.frnum & 0x1) == hcchar.b.oddfrm) {
- + fiq_print(FIQDBG_INT, state, "CSWAIT %01d", n);
- + st->expected_uframe = hfnum.b.frnum;
- + st->fsm = FIQ_PER_CSPLIT_WAIT;
- + } else {
- + fiq_print(FIQDBG_INT, state, "CSPOL %01d", n);
- + /* For isochronous IN endpoints,
- + * we need to hold off if we are expecting a lot of data */
- + if (st->hcchar_copy.b.mps < DATA0_PID_HEURISTIC) {
- + start_next_periodic = 1;
- + }
- + /* Danger will robinson: we are in a broken state. If our first interrupt after
- + * this is a NYET, it will be delayed by 1 uframe and result in an unrecoverable
- + * lag. Unmask the NYET interrupt.
- + */
- + st->expected_uframe = (hfnum.b.frnum + 1) & 0x3FFF;
- + st->fsm = FIQ_PER_CSPLIT_BROKEN_NYET1;
- + restart = 1;
- + }
- + handled = 1;
- + } else if (hcint.b.xacterr) {
- + /* 3-strikes retry is enabled, we have hit our max nr_errors */
- + st->fsm = FIQ_PER_SPLIT_HS_ABORTED;
- + start_next_periodic = 1;
- + } else {
- + st->fsm = FIQ_PER_SPLIT_HS_ABORTED;
- + start_next_periodic = 1;
- + }
- + /* We can now queue the next isochronous OUT transaction, if one is pending. */
- + if(fiq_fsm_tt_next_isoc(state, num_channels, n)) {
- + fiq_print(FIQDBG_INT, state, "NEXTISO ");
- + }
- + break;
- +
- + case FIQ_PER_CSPLIT_NYET1:
- + /* First CSPLIT attempt was a NYET. If we get a subsequent NYET,
- + * we are too late and the TT has dropped its CSPLIT fifo.
- + */
- + hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM);
- + hcchar.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR);
- + start_next_periodic = 1;
- + if (hcint.b.nak) {
- + st->fsm = FIQ_PER_SPLIT_DONE;
- + } else if (hcint.b.xfercomp) {
- + fiq_increment_dma_buf(state, num_channels, n);
- + st->fsm = FIQ_PER_CSPLIT_POLL;
- + st->nr_errors = 0;
- + if (fiq_fsm_more_csplits(state, n, &last_csplit)) {
- + handled = 1;
- + restart = 1;
- + if (!last_csplit)
- + start_next_periodic = 0;
- + } else {
- + st->fsm = FIQ_PER_SPLIT_DONE;
- + }
- + } else if (hcint.b.nyet) {
- + /* Doh. Data lost. */
- + st->fsm = FIQ_PER_SPLIT_NYET_ABORTED;
- + } else if (hcint.b.xacterr || hcint.b.stall || hcint.b.bblerr) {
- + st->fsm = FIQ_PER_SPLIT_LS_ABORTED;
- + } else {
- + st->fsm = FIQ_PER_SPLIT_HS_ABORTED;
- + }
- + break;
- +
- + case FIQ_PER_CSPLIT_BROKEN_NYET1:
- + /*
- + * we got here because our host channel is in the delayed-interrupt
- + * state and we cannot take a NYET interrupt any later than when it
- + * occurred. Disable then re-enable the channel if this happens to force
- + * CSPLITs to occur at the right time.
- + */
- + hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM);
- + hcchar.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR);
- + fiq_print(FIQDBG_INT, state, "BROK: %01d ", n);
- + if (hcint.b.nak) {
- + st->fsm = FIQ_PER_SPLIT_DONE;
- + start_next_periodic = 1;
- + } else if (hcint.b.xfercomp) {
- + fiq_increment_dma_buf(state, num_channels, n);
- + if (fiq_fsm_more_csplits(state, n, &last_csplit)) {
- + st->fsm = FIQ_PER_CSPLIT_POLL;
- + handled = 1;
- + restart = 1;
- + start_next_periodic = 1;
- + /* Reload HCTSIZ for the next transfer */
- + fiq_fsm_reload_hctsiz(state, n);
- + if (!last_csplit)
- + start_next_periodic = 0;
- + } else {
- + st->fsm = FIQ_PER_SPLIT_DONE;
- + }
- + } else if (hcint.b.nyet) {
- + st->fsm = FIQ_PER_SPLIT_NYET_ABORTED;
- + start_next_periodic = 1;
- + } else if (hcint.b.xacterr || hcint.b.stall || hcint.b.bblerr) {
- + /* Local 3-strikes retry is handled by the core. This is a ERR response.*/
- + st->fsm = FIQ_PER_SPLIT_LS_ABORTED;
- + } else {
- + st->fsm = FIQ_PER_SPLIT_HS_ABORTED;
- + }
- + break;
- +
- + case FIQ_PER_CSPLIT_POLL:
- + hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM);
- + hcchar.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR);
- + start_next_periodic = 1;
- + if (hcint.b.nak) {
- + st->fsm = FIQ_PER_SPLIT_DONE;
- + } else if (hcint.b.xfercomp) {
- + fiq_increment_dma_buf(state, num_channels, n);
- + if (fiq_fsm_more_csplits(state, n, &last_csplit)) {
- + handled = 1;
- + restart = 1;
- + /* Reload HCTSIZ for the next transfer */
- + fiq_fsm_reload_hctsiz(state, n);
- + if (!last_csplit)
- + start_next_periodic = 0;
- + } else {
- + st->fsm = FIQ_PER_SPLIT_DONE;
- + }
- + } else if (hcint.b.nyet) {
- + /* Are we a NYET after the first data packet? */
- + if (st->nrpackets == 0) {
- + st->fsm = FIQ_PER_CSPLIT_NYET1;
- + handled = 1;
- + restart = 1;
- + } else {
- + /* We got a NYET when polling CSPLITs. Can happen
- + * if our heuristic fails, or if someone disables us
- + * for any significant length of time.
- + */
- + if (st->nr_errors >= 3) {
- + st->fsm = FIQ_PER_SPLIT_NYET_ABORTED;
- + } else {
- + st->fsm = FIQ_PER_SPLIT_DONE;
- + }
- + }
- + } else if (hcint.b.xacterr || hcint.b.stall || hcint.b.bblerr) {
- + /* For xacterr, Local 3-strikes retry is handled by the core. This is a ERR response.*/
- + st->fsm = FIQ_PER_SPLIT_LS_ABORTED;
- + } else {
- + st->fsm = FIQ_PER_SPLIT_HS_ABORTED;
- + }
- + break;
- +
- + case FIQ_HS_ISOC_TURBO:
- + if (fiq_fsm_update_hs_isoc(state, n, hcint)) {
- + /* more transactions to come */
- + handled = 1;
- + restart = 1;
- + fiq_print(FIQDBG_INT, state, "HSISO M ");
- + } else {
- + st->fsm = FIQ_HS_ISOC_DONE;
- + fiq_print(FIQDBG_INT, state, "HSISO F ");
- + }
- + break;
- +
- + case FIQ_HS_ISOC_ABORTED:
- + /* This abort is called by the driver rewriting the state mid-transaction
- + * which allows the dequeue mechanism to work more effectively.
- + */
- + break;
- +
- + case FIQ_PER_ISO_OUT_ACTIVE:
- + if (hcint.b.ack) {
- + if(fiq_iso_out_advance(state, num_channels, n)) {
- + /* last OUT transfer */
- + st->fsm = FIQ_PER_ISO_OUT_LAST;
- + /*
- + * Assuming the periodic FIFO in the dwc core
- + * actually does its job properly, we can queue
- + * the next ssplit now and in theory, the wire
- + * transactions will be in-order.
- + */
- + // No it doesn't. It appears to process requests in host channel order.
- + //start_next_periodic = 1;
- + }
- + handled = 1;
- + restart = 1;
- + } else {
- + /*
- + * Isochronous transactions carry on regardless. Log the error
- + * and continue.
- + */
- + //explode += 1;
- + st->nr_errors++;
- + if(fiq_iso_out_advance(state, num_channels, n)) {
- + st->fsm = FIQ_PER_ISO_OUT_LAST;
- + //start_next_periodic = 1;
- + }
- + handled = 1;
- + restart = 1;
- + }
- + break;
- +
- + case FIQ_PER_ISO_OUT_LAST:
- + if (hcint.b.ack) {
- + /* All done here */
- + st->fsm = FIQ_PER_ISO_OUT_DONE;
- + } else {
- + st->fsm = FIQ_PER_ISO_OUT_DONE;
- + st->nr_errors++;
- + }
- + start_next_periodic = 1;
- + break;
- +
- + case FIQ_PER_SPLIT_TIMEOUT:
- + /* SOF kicked us because we overran. */
- + start_next_periodic = 1;
- + break;
- +
- + default:
- + break;
- + }
- +
- + if (handled) {
- + FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINT, hcint.d32);
- + } else {
- + /* Copy the regs into the state so the IRQ knows what to do */
- + st->hcint_copy.d32 = hcint.d32;
- + }
- +
- + if (restart) {
- + /* Restart always implies handled. */
- + if (restart == 2) {
- + /* For complete-split INs, the show must go on.
- + * Force a channel restart */
- + fiq_fsm_restart_channel(state, n, 1);
- + } else {
- + fiq_fsm_restart_channel(state, n, 0);
- + }
- + }
- + if (start_next_periodic) {
- + fiq_fsm_start_next_periodic(state, num_channels);
- + }
- + if (st->fsm != FIQ_PASSTHROUGH)
- + fiq_print(FIQDBG_INT, state, "FSMOUT%02d", st->fsm);
- +
- + return handled;
- +}
- +
- +
- +/**
- + * dwc_otg_fiq_fsm() - Flying State Machine (monster) FIQ
- + * @state: pointer to state struct passed from the banked FIQ mode registers.
- + * @num_channels: set according to the DWC hardware configuration
- + * @dma: pointer to DMA bounce buffers for split transaction slots
- + *
- + * The FSM FIQ performs the low-level tasks that normally would be performed by the microcode
- + * inside an EHCI or similar host controller regarding split transactions. The DWC core
- + * interrupts each and every time a split transaction packet is received or sent successfully.
- + * This results in either an interrupt storm when everything is working "properly", or
- + * the interrupt latency of the system in general breaks time-sensitive periodic split
- + * transactions. Pushing the low-level, but relatively easy state machine work into the FIQ
- + * solves these problems.
- + *
- + * Return: void
- + */
- +void notrace dwc_otg_fiq_fsm(struct fiq_state *state, int num_channels)
- +{
- + gintsts_data_t gintsts, gintsts_handled;
- + gintmsk_data_t gintmsk;
- + //hfnum_data_t hfnum;
- + haint_data_t haint, haint_handled;
- + haintmsk_data_t haintmsk;
- + int kick_irq = 0;
- +
- + gintsts_handled.d32 = 0;
- + haint_handled.d32 = 0;
- +
- + gintsts.d32 = FIQ_READ(state->dwc_regs_base + GINTSTS);
- + gintmsk.d32 = FIQ_READ(state->dwc_regs_base + GINTMSK);
- + gintsts.d32 &= gintmsk.d32;
- +
- + if (gintsts.b.sofintr) {
- + /* For FSM mode, SOF is required to keep the state machine advance for
- + * certain stages of the periodic pipeline. It's death to mask this
- + * interrupt in that case.
- + */
- +
- + if (!fiq_fsm_do_sof(state, num_channels)) {
- + /* Kick IRQ once. Queue advancement means that all pending transactions
- + * will get serviced when the IRQ finally executes.
- + */
- + if (state->gintmsk_saved.b.sofintr == 1)
- + kick_irq |= 1;
- + state->gintmsk_saved.b.sofintr = 0;
- + }
- + gintsts_handled.b.sofintr = 1;
- + }
- +
- + if (gintsts.b.hcintr) {
- + int i;
- + haint.d32 = FIQ_READ(state->dwc_regs_base + HAINT);
- + haintmsk.d32 = FIQ_READ(state->dwc_regs_base + HAINTMSK);
- + haint.d32 &= haintmsk.d32;
- + haint_handled.d32 = 0;
- + for (i=0; i<num_channels; i++) {
- + if (haint.b2.chint & (1 << i)) {
- + if(!fiq_fsm_do_hcintr(state, num_channels, i)) {
- + /* HCINT was not handled in FIQ
- + * HAINT is level-sensitive, leading to level-sensitive ginststs.b.hcint bit.
- + * Mask HAINT(i) but keep top-level hcint unmasked.
- + */
- + state->haintmsk_saved.b2.chint &= ~(1 << i);
- + } else {
- + /* do_hcintr cleaned up after itself, but clear haint */
- + haint_handled.b2.chint |= (1 << i);
- + }
- + }
- + }
- +
- + if (haint_handled.b2.chint) {
- + FIQ_WRITE(state->dwc_regs_base + HAINT, haint_handled.d32);
- + }
- +
- + if (haintmsk.d32 != (haintmsk.d32 & state->haintmsk_saved.d32)) {
- + /*
- + * This is necessary to avoid multiple retriggers of the MPHI in the case
- + * where interrupts are held off and HCINTs start to pile up.
- + * Only wake up the IRQ if a new interrupt came in, was not handled and was
- + * masked.
- + */
- + haintmsk.d32 &= state->haintmsk_saved.d32;
- + FIQ_WRITE(state->dwc_regs_base + HAINTMSK, haintmsk.d32);
- + kick_irq |= 1;
- + }
- + /* Top-Level interrupt - always handled because it's level-sensitive */
- + gintsts_handled.b.hcintr = 1;
- + }
- +
- +
- + /* Clear the bits in the saved register that were not handled but were triggered. */
- + state->gintmsk_saved.d32 &= ~(gintsts.d32 & ~gintsts_handled.d32);
- +
- + /* FIQ didn't handle something - mask has changed - write new mask */
- + if (gintmsk.d32 != (gintmsk.d32 & state->gintmsk_saved.d32)) {
- + gintmsk.d32 &= state->gintmsk_saved.d32;
- + gintmsk.b.sofintr = 1;
- + FIQ_WRITE(state->dwc_regs_base + GINTMSK, gintmsk.d32);
- +// fiq_print(FIQDBG_INT, state, "KICKGINT");
- +// fiq_print(FIQDBG_INT, state, "%08x", gintmsk.d32);
- +// fiq_print(FIQDBG_INT, state, "%08x", state->gintmsk_saved.d32);
- + kick_irq |= 1;
- + }
- +
- + if (gintsts_handled.d32) {
- + /* Only applies to edge-sensitive bits in GINTSTS */
- + FIQ_WRITE(state->dwc_regs_base + GINTSTS, gintsts_handled.d32);
- + }
- +
- + /* We got an interrupt, didn't handle it. */
- + if (kick_irq) {
- + state->mphi_int_count++;
- + FIQ_WRITE(state->mphi_regs.outdda, (int) state->dummy_send);
- + FIQ_WRITE(state->mphi_regs.outddb, (1<<29));
- +
- + }
- + state->fiq_done++;
- + mb();
- +}
- +
- +
- +/**
- + * dwc_otg_fiq_nop() - FIQ "lite"
- + * @state: pointer to state struct passed from the banked FIQ mode registers.
- + *
- + * The "nop" handler does not intervene on any interrupts other than SOF.
- + * It is limited in scope to deciding at each SOF if the IRQ SOF handler (which deals
- + * with non-periodic/periodic queues) needs to be kicked.
- + *
- + * This is done to hold off the SOF interrupt, which occurs at a rate of 8000 per second.
- + *
- + * Return: void
- + */
- +void notrace dwc_otg_fiq_nop(struct fiq_state *state)
- +{
- + gintsts_data_t gintsts, gintsts_handled;
- + gintmsk_data_t gintmsk;
- + hfnum_data_t hfnum;
- +
- + hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM);
- + gintsts.d32 = FIQ_READ(state->dwc_regs_base + GINTSTS);
- + gintmsk.d32 = FIQ_READ(state->dwc_regs_base + GINTMSK);
- + gintsts.d32 &= gintmsk.d32;
- + gintsts_handled.d32 = 0;
- +
- + if (gintsts.b.sofintr) {
- + if (!state->kick_np_queues &&
- + dwc_frame_num_gt(state->next_sched_frame, hfnum.b.frnum)) {
- + /* SOF handled, no work to do, just ACK interrupt */
- + gintsts_handled.b.sofintr = 1;
- + } else {
- + /* Kick IRQ */
- + state->gintmsk_saved.b.sofintr = 0;
- + }
- + }
- +
- + /* Reset handled interrupts */
- + if(gintsts_handled.d32) {
- + FIQ_WRITE(state->dwc_regs_base + GINTSTS, gintsts_handled.d32);
- + }
- +
- + /* Clear the bits in the saved register that were not handled but were triggered. */
- + state->gintmsk_saved.d32 &= ~(gintsts.d32 & ~gintsts_handled.d32);
- +
- + /* We got an interrupt, didn't handle it and want to mask it */
- + if (~(state->gintmsk_saved.d32)) {
- + state->mphi_int_count++;
- + gintmsk.d32 &= state->gintmsk_saved.d32;
- + FIQ_WRITE(state->dwc_regs_base + GINTMSK, gintmsk.d32);
- + /* Force a clear before another dummy send */
- + FIQ_WRITE(state->mphi_regs.intstat, (1<<29));
- + FIQ_WRITE(state->mphi_regs.outdda, (int) state->dummy_send);
- + FIQ_WRITE(state->mphi_regs.outddb, (1<<29));
- +
- + }
- + state->fiq_done++;
- + mb();
- +}
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h
- @@ -0,0 +1,353 @@
- +/*
- + * dwc_otg_fiq_fsm.h - Finite state machine FIQ header definitions
- + *
- + * Copyright (c) 2013 Raspberry Pi Foundation
- + *
- + * Author: Jonathan Bell <jonathan@raspberrypi.org>
- + * All rights reserved.
- + *
- + * Redistribution and use in source and binary forms, with or without
- + * modification, are permitted provided that the following conditions are met:
- + * * Redistributions of source code must retain the above copyright
- + * notice, this list of conditions and the following disclaimer.
- + * * Redistributions in binary form must reproduce the above copyright
- + * notice, this list of conditions and the following disclaimer in the
- + * documentation and/or other materials provided with the distribution.
- + * * Neither the name of Raspberry Pi nor the
- + * names of its contributors may be used to endorse or promote products
- + * derived from this software without specific prior written permission.
- + *
- + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- + * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
- + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- + *
- + * This FIQ implements functionality that performs split transactions on
- + * the dwc_otg hardware without any outside intervention. A split transaction
- + * is "queued" by nominating a specific host channel to perform the entirety
- + * of a split transaction. This FIQ will then perform the microframe-precise
- + * scheduling required in each phase of the transaction until completion.
- + *
- + * The FIQ functionality has been surgically implanted into the Synopsys
- + * vendor-provided driver.
- + *
- + */
- +
- +#ifndef DWC_OTG_FIQ_FSM_H_
- +#define DWC_OTG_FIQ_FSM_H_
- +
- +#include "dwc_otg_regs.h"
- +#include "dwc_otg_cil.h"
- +#include "dwc_otg_hcd.h"
- +#include <linux/kernel.h>
- +#include <linux/irqflags.h>
- +#include <linux/string.h>
- +#include <asm/barrier.h>
- +
- +#if 0
- +#define FLAME_ON(x) \
- +do { \
- + int gpioreg; \
- + \
- + gpioreg = readl(__io_address(0x20200000+0x8)); \
- + gpioreg &= ~(7 << (x-20)*3); \
- + gpioreg |= 0x1 << (x-20)*3; \
- + writel(gpioreg, __io_address(0x20200000+0x8)); \
- + \
- + writel(1<<x, __io_address(0x20200000+(0x1C))); \
- +} while (0)
- +
- +#define FLAME_OFF(x) \
- +do { \
- + writel(1<<x, __io_address(0x20200000+(0x28))); \
- +} while (0)
- +#else
- +#define FLAME_ON(x) do { } while (0)
- +#define FLAME_OFF(X) do { } while (0)
- +#endif
- +
- +/* This is a quick-and-dirty arch-specific register read/write. We know that
- + * writes to a peripheral on BCM2835 will always arrive in-order, also that
- + * reads and writes are executed in-order therefore the need for memory barriers
- + * is obviated if we're only talking to USB.
- + */
- +#define FIQ_WRITE(_addr_,_data_) (*(volatile unsigned int *) (_addr_) = (_data_))
- +#define FIQ_READ(_addr_) (*(volatile unsigned int *) (_addr_))
- +
- +/* FIQ-ified register definitions. Offsets are from dwc_regs_base. */
- +#define GINTSTS 0x014
- +#define GINTMSK 0x018
- +/* Debug register. Poll the top of the received packets FIFO. */
- +#define GRXSTSR 0x01C
- +#define HFNUM 0x408
- +#define HAINT 0x414
- +#define HAINTMSK 0x418
- +#define HPRT0 0x440
- +
- +/* HC_regs start from an offset of 0x500 */
- +#define HC_START 0x500
- +#define HC_OFFSET 0x020
- +
- +#define HC_DMA 0x514
- +
- +#define HCCHAR 0x00
- +#define HCSPLT 0x04
- +#define HCINT 0x08
- +#define HCINTMSK 0x0C
- +#define HCTSIZ 0x10
- +
- +#define ISOC_XACTPOS_ALL 0b11
- +#define ISOC_XACTPOS_BEGIN 0b10
- +#define ISOC_XACTPOS_MID 0b00
- +#define ISOC_XACTPOS_END 0b01
- +
- +#define DWC_PID_DATA2 0b01
- +#define DWC_PID_MDATA 0b11
- +#define DWC_PID_DATA1 0b10
- +#define DWC_PID_DATA0 0b00
- +
- +typedef struct {
- + volatile void* base;
- + volatile void* ctrl;
- + volatile void* outdda;
- + volatile void* outddb;
- + volatile void* intstat;
- +} mphi_regs_t;
- +
- +
- +enum fiq_debug_level {
- + FIQDBG_SCHED = (1 << 0),
- + FIQDBG_INT = (1 << 1),
- + FIQDBG_ERR = (1 << 2),
- + FIQDBG_PORTHUB = (1 << 3),
- +};
- +
- +struct fiq_state;
- +
- +extern void _fiq_print (enum fiq_debug_level dbg_lvl, volatile struct fiq_state *state, char *fmt, ...);
- +#if 0
- +#define fiq_print _fiq_print
- +#else
- +#define fiq_print(x, y, ...)
- +#endif
- +
- +extern bool fiq_enable, fiq_fsm_enable;
- +extern ushort nak_holdoff;
- +
- +/**
- + * enum fiq_fsm_state - The FIQ FSM states.
- + *
- + * This is the "core" of the FIQ FSM. Broadly, the FSM states follow the
- + * USB2.0 specification for host responses to various transaction states.
- + * There are modifications to this host state machine because of a variety of
- + * quirks and limitations in the dwc_otg hardware.
- + *
- + * The fsm state is also used to communicate back to the driver on completion of
- + * a split transaction. The end states are used in conjunction with the interrupts
- + * raised by the final transaction.
- + */
- +enum fiq_fsm_state {
- + /* FIQ isn't enabled for this host channel */
- + FIQ_PASSTHROUGH = 0,
- + /* For the first interrupt received for this channel,
- + * the FIQ has to ack any interrupts indicating success. */
- + FIQ_PASSTHROUGH_ERRORSTATE = 31,
- + /* Nonperiodic state groups */
- + FIQ_NP_SSPLIT_STARTED = 1,
- + FIQ_NP_SSPLIT_RETRY = 2,
- + FIQ_NP_OUT_CSPLIT_RETRY = 3,
- + FIQ_NP_IN_CSPLIT_RETRY = 4,
- + FIQ_NP_SPLIT_DONE = 5,
- + FIQ_NP_SPLIT_LS_ABORTED = 6,
- + /* This differentiates a HS transaction error from a LS one
- + * (handling the hub state is different) */
- + FIQ_NP_SPLIT_HS_ABORTED = 7,
- +
- + /* Periodic state groups */
- + /* Periodic transactions are either started directly by the IRQ handler
- + * or deferred if the TT is already in use.
- + */
- + FIQ_PER_SSPLIT_QUEUED = 8,
- + FIQ_PER_SSPLIT_STARTED = 9,
- + FIQ_PER_SSPLIT_LAST = 10,
- +
- +
- + FIQ_PER_ISO_OUT_PENDING = 11,
- + FIQ_PER_ISO_OUT_ACTIVE = 12,
- + FIQ_PER_ISO_OUT_LAST = 13,
- + FIQ_PER_ISO_OUT_DONE = 27,
- +
- + FIQ_PER_CSPLIT_WAIT = 14,
- + FIQ_PER_CSPLIT_NYET1 = 15,
- + FIQ_PER_CSPLIT_BROKEN_NYET1 = 28,
- + FIQ_PER_CSPLIT_NYET_FAFF = 29,
- + /* For multiple CSPLITs (large isoc IN, or delayed interrupt) */
- + FIQ_PER_CSPLIT_POLL = 16,
- + /* The last CSPLIT for a transaction has been issued, differentiates
- + * for the state machine to queue the next packet.
- + */
- + FIQ_PER_CSPLIT_LAST = 17,
- +
- + FIQ_PER_SPLIT_DONE = 18,
- + FIQ_PER_SPLIT_LS_ABORTED = 19,
- + FIQ_PER_SPLIT_HS_ABORTED = 20,
- + FIQ_PER_SPLIT_NYET_ABORTED = 21,
- + /* Frame rollover has occurred without the transaction finishing. */
- + FIQ_PER_SPLIT_TIMEOUT = 22,
- +
- + /* FIQ-accelerated HS Isochronous state groups */
- + FIQ_HS_ISOC_TURBO = 23,
- + /* For interval > 1, SOF wakes up the isochronous FSM */
- + FIQ_HS_ISOC_SLEEPING = 24,
- + FIQ_HS_ISOC_DONE = 25,
- + FIQ_HS_ISOC_ABORTED = 26,
- + FIQ_DEQUEUE_ISSUED = 30,
- + FIQ_TEST = 32,
- +};
- +
- +struct fiq_stack {
- + int magic1;
- + uint8_t stack[2048];
- + int magic2;
- +};
- +
- +
- +/**
- + * struct fiq_dma_info - DMA bounce buffer utilisation information (per-channel)
- + * @index: Number of slots reported used for IN transactions / number of slots
- + * transmitted for an OUT transaction
- + * @slot_len[6]: Number of actual transfer bytes in each slot (255 if unused)
- + *
- + * Split transaction transfers can have variable length depending on other bus
- + * traffic. The OTG core DMA engine requires 4-byte aligned addresses therefore
- + * each transaction needs a guaranteed aligned address. A maximum of 6 split transfers
- + * can happen per-frame.
- + */
- +struct fiq_dma_info {
- + u8 index;
- + u8 slot_len[6];
- +};
- +
- +struct __attribute__((packed)) fiq_split_dma_slot {
- + u8 buf[188];
- +};
- +
- +struct fiq_dma_channel {
- + struct __attribute__((packed)) fiq_split_dma_slot index[6];
- +};
- +
- +struct fiq_dma_blob {
- + struct __attribute__((packed)) fiq_dma_channel channel[0];
- +};
- +
- +/**
- + * struct fiq_hs_isoc_info - USB2.0 isochronous data
- + * @iso_frame: Pointer to the array of OTG URB iso_frame_descs.
- + * @nrframes: Total length of iso_frame_desc array
- + * @index: Current index (FIQ-maintained)
- + *
- + */
- +struct fiq_hs_isoc_info {
- + struct dwc_otg_hcd_iso_packet_desc *iso_desc;
- + unsigned int nrframes;
- + unsigned int index;
- +};
- +
- +/**
- + * struct fiq_channel_state - FIQ state machine storage
- + * @fsm: Current state of the channel as understood by the FIQ
- + * @nr_errors: Number of transaction errors on this split-transaction
- + * @hub_addr: SSPLIT/CSPLIT destination hub
- + * @port_addr: SSPLIT/CSPLIT destination port - always 1 if single TT hub
- + * @nrpackets: For isoc OUT, the number of split-OUT packets to transmit. For
- + * split-IN, number of CSPLIT data packets that were received.
- + * @hcchar_copy:
- + * @hcsplt_copy:
- + * @hcintmsk_copy:
- + * @hctsiz_copy: Copies of the host channel registers.
- + * For use as scratch, or for returning state.
- + *
- + * The fiq_channel_state is state storage between interrupts for a host channel. The
- + * FSM state is stored here. Members of this structure must only be set up by the
- + * driver prior to enabling the FIQ for this host channel, and not touched until the FIQ
- + * has updated the state to either a COMPLETE state group or ABORT state group.
- + */
- +
- +struct fiq_channel_state {
- + enum fiq_fsm_state fsm;
- + unsigned int nr_errors;
- + unsigned int hub_addr;
- + unsigned int port_addr;
- + /* Hardware bug workaround: sometimes channel halt interrupts are
- + * delayed until the next SOF. Keep track of when we expected to get interrupted. */
- + unsigned int expected_uframe;
- + /* in/out for communicating number of dma buffers used, or number of ISOC to do */
- + unsigned int nrpackets;
- + struct fiq_dma_info dma_info;
- + struct fiq_hs_isoc_info hs_isoc_info;
- + /* Copies of HC registers - in/out communication from/to IRQ handler
- + * and for ease of channel setup. A bit of mungeing is performed - for
- + * example the hctsiz.b.maxp is _always_ the max packet size of the endpoint.
- + */
- + hcchar_data_t hcchar_copy;
- + hcsplt_data_t hcsplt_copy;
- + hcint_data_t hcint_copy;
- + hcintmsk_data_t hcintmsk_copy;
- + hctsiz_data_t hctsiz_copy;
- + hcdma_data_t hcdma_copy;
- +};
- +
- +/**
- + * struct fiq_state - top-level FIQ state machine storage
- + * @mphi_regs: virtual address of the MPHI peripheral register file
- + * @dwc_regs_base: virtual address of the base of the DWC core register file
- + * @dma_base: physical address for the base of the DMA bounce buffers
- + * @dummy_send: Scratch area for sending a fake message to the MPHI peripheral
- + * @gintmsk_saved: Top-level mask of interrupts that the FIQ has not handled.
- + * Used for determining which interrupts fired to set off the IRQ handler.
- + * @haintmsk_saved: Mask of interrupts from host channels that the FIQ did not handle internally.
- + * @np_count: Non-periodic transactions in the active queue
- + * @np_sent: Count of non-periodic transactions that have completed
- + * @next_sched_frame: For periodic transactions handled by the driver's SOF-driven queuing mechanism,
- + * this is the next frame on which a SOF interrupt is required. Used to hold off
- + * passing SOF through to the driver until necessary.
- + * @channel[n]: Per-channel FIQ state. Allocated during init depending on the number of host
- + * channels configured into the core logic.
- + *
- + * This is passed as the first argument to the dwc_otg_fiq_fsm top-level FIQ handler from the asm stub.
- + * It contains top-level state information.
- + */
- +struct fiq_state {
- + mphi_regs_t mphi_regs;
- + void *dwc_regs_base;
- + dma_addr_t dma_base;
- + struct fiq_dma_blob *fiq_dmab;
- + void *dummy_send;
- + gintmsk_data_t gintmsk_saved;
- + haintmsk_data_t haintmsk_saved;
- + int mphi_int_count;
- + unsigned int fiq_done;
- + unsigned int kick_np_queues;
- + unsigned int next_sched_frame;
- +#ifdef FIQ_DEBUG
- + char * buffer;
- + unsigned int bufsiz;
- +#endif
- + struct fiq_channel_state channel[0];
- +};
- +
- +extern int fiq_fsm_too_late(struct fiq_state *st, int n);
- +
- +extern int fiq_fsm_tt_in_use(struct fiq_state *st, int num_channels, int n);
- +
- +extern void dwc_otg_fiq_fsm(struct fiq_state *state, int num_channels);
- +
- +extern void dwc_otg_fiq_nop(struct fiq_state *state);
- +
- +#endif /* DWC_OTG_FIQ_FSM_H_ */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_stub.S
- @@ -0,0 +1,80 @@
- +/*
- + * dwc_otg_fiq_fsm.S - assembly stub for the FSM FIQ
- + *
- + * Copyright (c) 2013 Raspberry Pi Foundation
- + *
- + * Author: Jonathan Bell <jonathan@raspberrypi.org>
- + * All rights reserved.
- + *
- + * Redistribution and use in source and binary forms, with or without
- + * modification, are permitted provided that the following conditions are met:
- + * * Redistributions of source code must retain the above copyright
- + * notice, this list of conditions and the following disclaimer.
- + * * Redistributions in binary form must reproduce the above copyright
- + * notice, this list of conditions and the following disclaimer in the
- + * documentation and/or other materials provided with the distribution.
- + * * Neither the name of Raspberry Pi nor the
- + * names of its contributors may be used to endorse or promote products
- + * derived from this software without specific prior written permission.
- + *
- + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- + * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
- + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- + */
- +
- +
- +#include <asm/assembler.h>
- +#include <linux/linkage.h>
- +
- +
- +.text
- +
- +.global _dwc_otg_fiq_stub_end;
- +
- +/**
- + * _dwc_otg_fiq_stub() - entry copied to the FIQ vector page to allow
- + * a C-style function call with arguments from the FIQ banked registers.
- + * r0 = &hcd->fiq_state
- + * r1 = &hcd->num_channels
- + * r2 = &hcd->dma_buffers
- + * Tramples: r0, r1, r2, r4, fp, ip
- + */
- +
- +ENTRY(_dwc_otg_fiq_stub)
- + /* Stash unbanked regs - SP will have been set up for us */
- + mov ip, sp;
- + stmdb sp!, {r0-r12, lr};
- +#ifdef FIQ_DEBUG
- + // Cycle profiling - read cycle counter at start
- + mrc p15, 0, r5, c15, c12, 1;
- +#endif
- + /* r11 = fp, don't trample it */
- + mov r4, fp;
- + /* set EABI frame size */
- + sub fp, ip, #512;
- +
- + /* for fiq NOP mode - just need state */
- + mov r0, r8;
- + /* r9 = num_channels */
- + mov r1, r9;
- + /* r10 = struct *dma_bufs */
- +// mov r2, r10;
- +
- + /* r4 = &fiq_c_function */
- + blx r4;
- +#ifdef FIQ_DEBUG
- + mrc p15, 0, r4, c15, c12, 1;
- + subs r5, r5, r4;
- + // r5 is now the cycle count time for executing the FIQ. Store it somewhere?
- +#endif
- + ldmia sp!, {r0-r12, lr};
- + subs pc, lr, #4;
- +_dwc_otg_fiq_stub_end:
- +END(_dwc_otg_fiq_stub)
- --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
- @@ -45,9 +45,10 @@
-
- #include "dwc_otg_hcd.h"
- #include "dwc_otg_regs.h"
- -#include "dwc_otg_mphi_fix.h"
- +#include "dwc_otg_fiq_fsm.h"
-
- -extern bool microframe_schedule, nak_holdoff_enable;
- +extern bool microframe_schedule;
- +extern uint16_t fiq_fsm_mask, nak_holdoff;
-
- //#define DEBUG_HOST_CHANNELS
- #ifdef DEBUG_HOST_CHANNELS
- @@ -57,12 +58,6 @@ static int last_sel_trans_num_avail_hc_a
- static int last_sel_trans_num_avail_hc_at_end = 0;
- #endif /* DEBUG_HOST_CHANNELS */
-
- -extern int g_next_sched_frame, g_np_count, g_np_sent;
- -
- -extern haint_data_t haint_saved;
- -extern hcintmsk_data_t hcintmsk_saved[MAX_EPS_CHANNELS];
- -extern hcint_data_t hcint_saved[MAX_EPS_CHANNELS];
- -extern gintsts_data_t ginsts_saved;
-
- dwc_otg_hcd_t *dwc_otg_hcd_alloc_hcd(void)
- {
- @@ -295,7 +290,7 @@ static int32_t dwc_otg_hcd_disconnect_cb
- */
- dwc_otg_hcd->flags.b.port_connect_status_change = 1;
- dwc_otg_hcd->flags.b.port_connect_status = 0;
- - if(fiq_fix_enable)
- + if(fiq_enable)
- local_fiq_disable();
- /*
- * Shutdown any transfers in process by clearing the Tx FIFO Empty
- @@ -392,20 +387,15 @@ static int32_t dwc_otg_hcd_disconnect_cb
- channel->qh = NULL;
- }
- }
- - if(fiq_split_enable) {
- + if(fiq_fsm_enable) {
- for(i=0; i < 128; i++) {
- dwc_otg_hcd->hub_port[i] = 0;
- }
- - haint_saved.d32 = 0;
- - for(i=0; i < MAX_EPS_CHANNELS; i++) {
- - hcint_saved[i].d32 = 0;
- - hcintmsk_saved[i].d32 = 0;
- - }
- }
-
- }
-
- - if(fiq_fix_enable)
- + if(fiq_enable)
- local_fiq_enable();
-
- if (dwc_otg_hcd->fops->disconnect) {
- @@ -542,7 +532,7 @@ int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_
- }
- #endif
- intr_mask.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->gintmsk);
- - if(!intr_mask.b.sofintr) needs_scheduling = 1;
- + if(!intr_mask.b.sofintr || fiq_enable) needs_scheduling = 1;
- if((((dwc_otg_qh_t *)ep_handle)->ep_type == UE_BULK) && !(qtd->urb->flags & URB_GIVEBACK_ASAP))
- /* Do not schedule SG transactions until qtd has URB_GIVEBACK_ASAP set */
- needs_scheduling = 0;
- @@ -613,6 +603,7 @@ int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_
- if (urb_qtd->in_process && qh->channel) {
- /* The QTD is in process (it has been assigned to a channel). */
- if (hcd->flags.b.port_connect_status) {
- + int n = qh->channel->hc_num;
- /*
- * If still connected (i.e. in host mode), halt the
- * channel so it can be used for other transfers. If
- @@ -620,10 +611,16 @@ int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_
- * written to halt the channel since the core is in
- * device mode.
- */
- - dwc_otg_hc_halt(hcd->core_if, qh->channel,
- - DWC_OTG_HC_XFER_URB_DEQUEUE);
- -
- - dwc_otg_hcd_release_port(hcd, qh);
- + /* In FIQ FSM mode, we need to shut down carefully.
- + * The FIQ may attempt to restart a disabled channel */
- + if (fiq_fsm_enable && (hcd->fiq_state->channel[n].fsm != FIQ_PASSTHROUGH)) {
- + qh->channel->halt_status = DWC_OTG_HC_XFER_URB_DEQUEUE;
- + qh->channel->halt_pending = 1;
- + hcd->fiq_state->channel[n].fsm = FIQ_DEQUEUE_ISSUED;
- + } else {
- + dwc_otg_hc_halt(hcd->core_if, qh->channel,
- + DWC_OTG_HC_XFER_URB_DEQUEUE);
- + }
- }
- }
-
- @@ -759,7 +756,6 @@ static void completion_tasklet_func(void
-
- usb_hcd_giveback_urb(hcd->priv, urb, urb->status);
-
- - fiq_print(FIQDBG_PORTHUB, "COMPLETE");
-
- DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
- }
- @@ -854,6 +850,34 @@ void dwc_otg_hcd_power_up(void *ptr)
- cil_hcd_start(core_if);
- }
-
- +void dwc_otg_cleanup_fiq_channel(dwc_otg_hcd_t *hcd, uint32_t num)
- +{
- + struct fiq_channel_state *st = &hcd->fiq_state->channel[num];
- + struct fiq_dma_blob *blob = hcd->fiq_dmab;
- + int i;
- +
- + st->fsm = FIQ_PASSTHROUGH;
- + st->hcchar_copy.d32 = 0;
- + st->hcsplt_copy.d32 = 0;
- + st->hcint_copy.d32 = 0;
- + st->hcintmsk_copy.d32 = 0;
- + st->hctsiz_copy.d32 = 0;
- + st->hcdma_copy.d32 = 0;
- + st->nr_errors = 0;
- + st->hub_addr = 0;
- + st->port_addr = 0;
- + st->expected_uframe = 0;
- + st->nrpackets = 0;
- + st->dma_info.index = 0;
- + for (i = 0; i < 6; i++)
- + st->dma_info.slot_len[i] = 255;
- + st->hs_isoc_info.index = 0;
- + st->hs_isoc_info.iso_desc = NULL;
- + st->hs_isoc_info.nrframes = 0;
- +
- + DWC_MEMSET(&blob->channel[num].index[0], 0x6b, 1128);
- +}
- +
- /**
- * Frees secondary storage associated with the dwc_otg_hcd structure contained
- * in the struct usb_hcd field.
- @@ -907,6 +931,7 @@ static void dwc_otg_hcd_free(dwc_otg_hcd
- DWC_TIMER_FREE(dwc_otg_hcd->conn_timer);
- DWC_TASK_FREE(dwc_otg_hcd->reset_tasklet);
- DWC_TASK_FREE(dwc_otg_hcd->completion_tasklet);
- + DWC_FREE(dwc_otg_hcd->fiq_state);
-
- #ifdef DWC_DEV_SRPCAP
- if (dwc_otg_hcd->core_if->power_down == 2 &&
- @@ -984,6 +1009,59 @@ int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd
- channel);
- }
-
- + if (fiq_enable) {
- + hcd->fiq_state = DWC_ALLOC(sizeof(struct fiq_state) + (sizeof(struct fiq_channel_state) * num_channels));
- + if (!hcd->fiq_state) {
- + retval = -DWC_E_NO_MEMORY;
- + DWC_ERROR("%s: cannot allocate fiq_state structure\n", __func__);
- + dwc_otg_hcd_free(hcd);
- + goto out;
- + }
- + DWC_MEMSET(hcd->fiq_state, 0, (sizeof(struct fiq_state) + (sizeof(struct fiq_channel_state) * num_channels)));
- +
- + for (i = 0; i < num_channels; i++) {
- + hcd->fiq_state->channel[i].fsm = FIQ_PASSTHROUGH;
- + }
- + hcd->fiq_state->dummy_send = DWC_ALLOC_ATOMIC(16);
- +
- + hcd->fiq_stack = DWC_ALLOC(sizeof(struct fiq_stack));
- + if (!hcd->fiq_stack) {
- + retval = -DWC_E_NO_MEMORY;
- + DWC_ERROR("%s: cannot allocate fiq_stack structure\n", __func__);
- + dwc_otg_hcd_free(hcd);
- + goto out;
- + }
- + hcd->fiq_stack->magic1 = 0xDEADBEEF;
- + hcd->fiq_stack->magic2 = 0xD00DFEED;
- + hcd->fiq_state->gintmsk_saved.d32 = ~0;
- + hcd->fiq_state->haintmsk_saved.b2.chint = ~0;
- +
- + /* This bit is terrible and uses no API, but necessary. The FIQ has no concept of DMA pools
- + * (and if it did, would be a lot slower). This allocates a chunk of memory (~9kiB for 8 host channels)
- + * for use as transaction bounce buffers in a 2-D array. Our access into this chunk is done by some
- + * moderately readable array casts.
- + */
- + hcd->fiq_dmab = DWC_DMA_ALLOC((sizeof(struct fiq_dma_channel) * num_channels), &hcd->fiq_state->dma_base);
- + DWC_WARN("FIQ DMA bounce buffers: virt = 0x%08x dma = 0x%08x len=%d",
- + (unsigned int)hcd->fiq_dmab, (unsigned int)hcd->fiq_state->dma_base,
- + sizeof(struct fiq_dma_channel) * num_channels);
- +
- + DWC_MEMSET(hcd->fiq_dmab, 0x6b, 9024);
- +
- + /* pointer for debug in fiq_print */
- + hcd->fiq_state->fiq_dmab = hcd->fiq_dmab;
- + if (fiq_fsm_enable) {
- + int i;
- + for (i=0; i < hcd->core_if->core_params->host_channels; i++) {
- + dwc_otg_cleanup_fiq_channel(hcd, i);
- + }
- + DWC_PRINTF("FIQ FSM acceleration enabled for :\n%s%s%s",
- + (fiq_fsm_mask & 0x1) ? "Non-periodic Split Transactions\n" : "",
- + (fiq_fsm_mask & 0x2) ? "Periodic Split Transactions\n" : "",
- + (fiq_fsm_mask & 0x4) ? "High-Speed Isochronous Endpoints\n" : "");
- + }
- + }
- +
- /* Initialize the Connection timeout timer. */
- hcd->conn_timer = DWC_TIMER_ALLOC("Connection timer",
- dwc_otg_hcd_connect_timeout, 0);
- @@ -1181,7 +1259,8 @@ static void assign_and_init_hc(dwc_otg_h
- hc->do_split = 1;
- hc->xact_pos = qtd->isoc_split_pos;
- /* We don't need to do complete splits anymore */
- - if(fiq_split_enable)
- +// if(fiq_fsm_enable)
- + if (0)
- hc->complete_split = qtd->complete_split = 0;
- else
- hc->complete_split = qtd->complete_split;
- @@ -1332,62 +1411,487 @@ static void assign_and_init_hc(dwc_otg_h
- hc->qh = qh;
- }
-
- -/*
- -** Check the transaction to see if the port / hub has already been assigned for
- -** a split transaction
- -**
- -** Return 0 - Port is already in use
- -*/
- -int dwc_otg_hcd_allocate_port(dwc_otg_hcd_t * hcd, dwc_otg_qh_t *qh)
- +
- +/**
- + * fiq_fsm_transaction_suitable() - Test a QH for compatibility with the FIQ
- + * @qh: pointer to the endpoint's queue head
- + *
- + * Transaction start/end control flow is grafted onto the existing dwc_otg
- + * mechanisms, to avoid spaghettifying the functions more than they already are.
- + * This function's eligibility check is altered by debug parameter.
- + *
- + * Returns: 0 for unsuitable, 1 implies the FIQ can be enabled for this transaction.
- + */
- +
- +int fiq_fsm_transaction_suitable(dwc_otg_qh_t *qh)
- {
- - uint32_t hub_addr, port_addr;
- + if (qh->do_split) {
- + switch (qh->ep_type) {
- + case UE_CONTROL:
- + case UE_BULK:
- + if (fiq_fsm_mask & (1 << 0))
- + return 1;
- + break;
- + case UE_INTERRUPT:
- + case UE_ISOCHRONOUS:
- + if (fiq_fsm_mask & (1 << 1))
- + return 1;
- + break;
- + default:
- + break;
- + }
- + } else if (qh->ep_type == UE_ISOCHRONOUS) {
- + if (fiq_fsm_mask & (1 << 2)) {
- + /* HS ISOCH support. We test for compatibility:
- + * - DWORD aligned buffers
- + * - Must be at least 2 transfers (otherwise pointless to use the FIQ)
- + * If yes, then the fsm enqueue function will handle the state machine setup.
- + */
- + dwc_otg_qtd_t *qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list);
- + dwc_otg_hcd_urb_t *urb = qtd->urb;
- + struct dwc_otg_hcd_iso_packet_desc (*iso_descs)[0] = &urb->iso_descs;
- + int nr_iso_frames = urb->packet_count;
- + int i;
- + uint32_t ptr;
- +
- + if (nr_iso_frames < 2)
- + return 0;
- + for (i = 0; i < nr_iso_frames; i++) {
- + ptr = urb->dma + iso_descs[i]->offset;
- + if (ptr & 0x3) {
- + printk_ratelimited("%s: Non-Dword aligned isochronous frame offset."
- + " Cannot queue FIQ-accelerated transfer to device %d endpoint %d\n",
- + __FUNCTION__, qh->channel->dev_addr, qh->channel->ep_num);
- + return 0;
- + }
- + }
- + return 1;
- + }
- + }
- + return 0;
- +}
-
- - if(!fiq_split_enable)
- - return 0;
- +/**
- + * fiq_fsm_setup_periodic_dma() - Set up DMA bounce buffers
- + * @hcd: Pointer to the dwc_otg_hcd struct
- + * @qh: Pointer to the endpoint's queue head
- + *
- + * Periodic split transactions are transmitted modulo 188 bytes.
- + * This necessitates slicing data up into buckets for isochronous out
- + * and fixing up the DMA address for all IN transfers.
- + *
- + * Returns 1 if the DMA bounce buffers have been used, 0 if the default
- + * HC buffer has been used.
- + */
- +int fiq_fsm_setup_periodic_dma(dwc_otg_hcd_t *hcd, struct fiq_channel_state *st, dwc_otg_qh_t *qh)
- + {
- + int frame_length, i = 0;
- + uint8_t *ptr = NULL;
- + dwc_hc_t *hc = qh->channel;
- + struct fiq_dma_blob *blob;
- + struct dwc_otg_hcd_iso_packet_desc *frame_desc;
-
- - hcd->fops->hub_info(hcd, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->priv, &hub_addr, &port_addr);
- + for (i = 0; i < 6; i++) {
- + st->dma_info.slot_len[i] = 255;
- + }
- + st->dma_info.index = 0;
- + i = 0;
- + if (hc->ep_is_in) {
- + /*
- + * Set dma_regs to bounce buffer. FIQ will update the
- + * state depending on transaction progress.
- + */
- + blob = (struct fiq_dma_blob *) hcd->fiq_state->dma_base;
- + st->hcdma_copy.d32 = (uint32_t) &blob->channel[hc->hc_num].index[0].buf[0];
- + /* Calculate the max number of CSPLITS such that the FIQ can time out
- + * a transaction if it fails.
- + */
- + frame_length = st->hcchar_copy.b.mps;
- + do {
- + i++;
- + frame_length -= 188;
- + } while (frame_length >= 0);
- + st->nrpackets = i;
- + return 1;
- + } else {
- + if (qh->ep_type == UE_ISOCHRONOUS) {
-
- - if(hcd->hub_port[hub_addr] & (1 << port_addr))
- - {
- - fiq_print(FIQDBG_PORTHUB, "H%dP%d:S%02d", hub_addr, port_addr, qh->skip_count);
- + dwc_otg_qtd_t *qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list);
-
- - qh->skip_count++;
- + frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index];
- + frame_length = frame_desc->length;
-
- - if(qh->skip_count > 40000)
- - {
- - printk_once(KERN_ERR "Error: Having to skip port allocation");
- - local_fiq_disable();
- - BUG();
- + /* Virtual address for bounce buffers */
- + blob = hcd->fiq_dmab;
- +
- + ptr = qtd->urb->buf + frame_desc->offset;
- + if (frame_length == 0) {
- + /*
- + * for isochronous transactions, we must still transmit a packet
- + * even if the length is zero.
- + */
- + st->dma_info.slot_len[0] = 0;
- + st->nrpackets = 1;
- + } else {
- + do {
- + if (frame_length <= 188) {
- + dwc_memcpy(&blob->channel[hc->hc_num].index[i].buf[0], ptr, frame_length);
- + st->dma_info.slot_len[i] = frame_length;
- + ptr += frame_length;
- + } else {
- + dwc_memcpy(&blob->channel[hc->hc_num].index[i].buf[0], ptr, 188);
- + st->dma_info.slot_len[i] = 188;
- + ptr += 188;
- + }
- + i++;
- + frame_length -= 188;
- + } while (frame_length > 0);
- + st->nrpackets = i;
- + }
- + ptr = qtd->urb->buf + frame_desc->offset;
- + /* Point the HC at the DMA address of the bounce buffers */
- + blob = (struct fiq_dma_blob *) hcd->fiq_state->dma_base;
- + st->hcdma_copy.d32 = (uint32_t) &blob->channel[hc->hc_num].index[0].buf[0];
- +
- + /* fixup xfersize to the actual packet size */
- + st->hctsiz_copy.b.pid = 0;
- + st->hctsiz_copy.b.xfersize = st->dma_info.slot_len[0];
- + return 1;
- + } else {
- + /* For interrupt, single OUT packet required, goes in the SSPLIT from hc_buff. */
- return 0;
- }
- - return 1;
- }
- - else
- - {
- - qh->skip_count = 0;
- - hcd->hub_port[hub_addr] |= 1 << port_addr;
- - fiq_print(FIQDBG_PORTHUB, "H%dP%d:A %d", hub_addr, port_addr, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->pipe_info.ep_num);
- -#ifdef FIQ_DEBUG
- - hcd->hub_port_alloc[hub_addr * 16 + port_addr] = dwc_otg_hcd_get_frame_number(hcd);
- -#endif
- +}
- +
- +/*
- + * Pushing a periodic request into the queue near the EOF1 point
- + * in a microframe causes erroneous behaviour (frmovrun) interrupt.
- + * Usually, the request goes out on the bus causing a transfer but
- + * the core does not transfer the data to memory.
- + * This guard interval (in number of 60MHz clocks) is required which
- + * must cater for CPU latency between reading the value and enabling
- + * the channel.
- + */
- +#define PERIODIC_FRREM_BACKOFF 1000
- +
- +int fiq_fsm_queue_isoc_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
- +{
- + dwc_hc_t *hc = qh->channel;
- + dwc_otg_hc_regs_t *hc_regs = hcd->core_if->host_if->hc_regs[hc->hc_num];
- + dwc_otg_qtd_t *qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list);
- + int frame;
- + struct fiq_channel_state *st = &hcd->fiq_state->channel[hc->hc_num];
- + int xfer_len, nrpackets;
- + hcdma_data_t hcdma;
- + hfnum_data_t hfnum;
- +
- + if (st->fsm != FIQ_PASSTHROUGH)
- return 0;
- +
- + st->nr_errors = 0;
- +
- + st->hcchar_copy.d32 = 0;
- + st->hcchar_copy.b.mps = hc->max_packet;
- + st->hcchar_copy.b.epdir = hc->ep_is_in;
- + st->hcchar_copy.b.devaddr = hc->dev_addr;
- + st->hcchar_copy.b.epnum = hc->ep_num;
- + st->hcchar_copy.b.eptype = hc->ep_type;
- +
- + st->hcintmsk_copy.b.chhltd = 1;
- +
- + frame = dwc_otg_hcd_get_frame_number(hcd);
- + st->hcchar_copy.b.oddfrm = (frame & 0x1) ? 0 : 1;
- +
- + st->hcchar_copy.b.lspddev = 0;
- + /* Enable the channel later as a final register write. */
- +
- + st->hcsplt_copy.d32 = 0;
- +
- + st->hs_isoc_info.iso_desc = (struct dwc_otg_hcd_iso_packet_desc *) &qtd->urb->iso_descs;
- + st->hs_isoc_info.nrframes = qtd->urb->packet_count;
- + /* grab the next DMA address offset from the array */
- + st->hcdma_copy.d32 = qtd->urb->dma;
- + hcdma.d32 = st->hcdma_copy.d32 + st->hs_isoc_info.iso_desc[0].offset;
- +
- + /* We need to set multi_count. This is a bit tricky - has to be set per-transaction as
- + * the core needs to be told to send the correct number. Caution: for IN transfers,
- + * this is always set to the maximum size of the endpoint. */
- + xfer_len = st->hs_isoc_info.iso_desc[0].length;
- + nrpackets = (xfer_len + st->hcchar_copy.b.mps - 1) / st->hcchar_copy.b.mps;
- + if (nrpackets == 0)
- + nrpackets = 1;
- + st->hcchar_copy.b.multicnt = nrpackets;
- + st->hctsiz_copy.b.pktcnt = nrpackets;
- +
- + /* Initial PID also needs to be set */
- + if (st->hcchar_copy.b.epdir == 0) {
- + st->hctsiz_copy.b.xfersize = xfer_len;
- + switch (st->hcchar_copy.b.multicnt) {
- + case 1:
- + st->hctsiz_copy.b.pid = DWC_PID_DATA0;
- + break;
- + case 2:
- + case 3:
- + st->hctsiz_copy.b.pid = DWC_PID_MDATA;
- + break;
- + }
- +
- + } else {
- + st->hctsiz_copy.b.xfersize = nrpackets * st->hcchar_copy.b.mps;
- + switch (st->hcchar_copy.b.multicnt) {
- + case 1:
- + st->hctsiz_copy.b.pid = DWC_PID_DATA0;
- + break;
- + case 2:
- + st->hctsiz_copy.b.pid = DWC_PID_DATA1;
- + break;
- + case 3:
- + st->hctsiz_copy.b.pid = DWC_PID_DATA2;
- + break;
- + }
- }
- +
- + fiq_print(FIQDBG_INT, hcd->fiq_state, "FSMQ %01d ", hc->hc_num);
- + fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hcchar_copy.d32);
- + fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hctsiz_copy.d32);
- + fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hcdma_copy.d32);
- + hfnum.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum);
- + local_fiq_disable();
- + DWC_WRITE_REG32(&hc_regs->hctsiz, st->hctsiz_copy.d32);
- + DWC_WRITE_REG32(&hc_regs->hcsplt, st->hcsplt_copy.d32);
- + DWC_WRITE_REG32(&hc_regs->hcdma, st->hcdma_copy.d32);
- + DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32);
- + DWC_WRITE_REG32(&hc_regs->hcintmsk, st->hcintmsk_copy.d32);
- + if (hfnum.b.frrem < PERIODIC_FRREM_BACKOFF) {
- + /* Prevent queueing near EOF1. Bad things happen if a periodic
- + * split transaction is queued very close to EOF.
- + */
- + st->fsm = FIQ_HS_ISOC_SLEEPING;
- + } else {
- + st->fsm = FIQ_HS_ISOC_TURBO;
- + st->hcchar_copy.b.chen = 1;
- + DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32);
- + }
- + mb();
- + st->hcchar_copy.b.chen = 0;
- + local_fiq_enable();
- + return 0;
- }
- -void dwc_otg_hcd_release_port(dwc_otg_hcd_t * hcd, dwc_otg_qh_t *qh)
- +
- +
- +/**
- + * fiq_fsm_queue_split_transaction() - Set up a host channel and FIQ state
- + * @hcd: Pointer to the dwc_otg_hcd struct
- + * @qh: Pointer to the endpoint's queue head
- + *
- + * This overrides the dwc_otg driver's normal method of queueing a transaction.
- + * Called from dwc_otg_hcd_queue_transactions(), this performs specific setup
- + * for the nominated host channel.
- + *
- + * For periodic transfers, it also peeks at the FIQ state to see if an immediate
- + * start is possible. If not, then the FIQ is left to start the transfer.
- + */
- +int fiq_fsm_queue_split_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
- {
- - uint32_t hub_addr, port_addr;
- + int start_immediate = 1, i;
- + hfnum_data_t hfnum;
- + dwc_hc_t *hc = qh->channel;
- + dwc_otg_hc_regs_t *hc_regs = hcd->core_if->host_if->hc_regs[hc->hc_num];
- + /* Program HC registers, setup FIQ_state, examine FIQ if periodic, start transfer (not if uframe 5) */
- + int hub_addr, port_addr, frame, uframe;
- + struct fiq_channel_state *st = &hcd->fiq_state->channel[hc->hc_num];
-
- - if(!fiq_split_enable)
- - return;
- + if (st->fsm != FIQ_PASSTHROUGH)
- + return 0;
- + st->nr_errors = 0;
-
- - hcd->fops->hub_info(hcd, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->priv, &hub_addr, &port_addr);
- + st->hcchar_copy.d32 = 0;
- + st->hcchar_copy.b.mps = hc->max_packet;
- + st->hcchar_copy.b.epdir = hc->ep_is_in;
- + st->hcchar_copy.b.devaddr = hc->dev_addr;
- + st->hcchar_copy.b.epnum = hc->ep_num;
- + st->hcchar_copy.b.eptype = hc->ep_type;
- + if (hc->ep_type & 0x1) {
- + if (hc->ep_is_in)
- + st->hcchar_copy.b.multicnt = 3;
- + else
- + /* Docs say set this to 1, but driver sets to 0! */
- + st->hcchar_copy.b.multicnt = 0;
- + } else {
- + st->hcchar_copy.b.multicnt = 1;
- + st->hcchar_copy.b.oddfrm = 0;
- + }
- + st->hcchar_copy.b.lspddev = (hc->speed == DWC_OTG_EP_SPEED_LOW) ? 1 : 0;
- + /* Enable the channel later as a final register write. */
-
- - hcd->hub_port[hub_addr] &= ~(1 << port_addr);
- -#ifdef FIQ_DEBUG
- - hcd->hub_port_alloc[hub_addr * 16 + port_addr] = -1;
- -#endif
- - fiq_print(FIQDBG_PORTHUB, "H%dP%d:RO%d", hub_addr, port_addr, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->pipe_info.ep_num);
- + st->hcsplt_copy.d32 = 0;
- + if(qh->do_split) {
- + hcd->fops->hub_info(hcd, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->priv, &hub_addr, &port_addr);
- + st->hcsplt_copy.b.compsplt = 0;
- + st->hcsplt_copy.b.spltena = 1;
- + // XACTPOS is for isoc-out only but needs initialising anyway.
- + st->hcsplt_copy.b.xactpos = ISOC_XACTPOS_ALL;
- + if((qh->ep_type == DWC_OTG_EP_TYPE_ISOC) && (!qh->ep_is_in)) {
- + /* For packetsize 0 < L < 188, ISOC_XACTPOS_ALL.
- + * for longer than this, ISOC_XACTPOS_BEGIN and the FIQ
- + * will update as necessary.
- + */
- + if (hc->xfer_len > 188) {
- + st->hcsplt_copy.b.xactpos = ISOC_XACTPOS_BEGIN;
- + }
- + }
- + st->hcsplt_copy.b.hubaddr = (uint8_t) hub_addr;
- + st->hcsplt_copy.b.prtaddr = (uint8_t) port_addr;
- + st->hub_addr = hub_addr;
- + st->port_addr = port_addr;
- + }
- +
- + st->hctsiz_copy.d32 = 0;
- + st->hctsiz_copy.b.dopng = 0;
- + st->hctsiz_copy.b.pid = hc->data_pid_start;
- +
- + if (hc->ep_is_in || (hc->xfer_len > hc->max_packet)) {
- + hc->xfer_len = hc->max_packet;
- + } else if (!hc->ep_is_in && (hc->xfer_len > 188)) {
- + hc->xfer_len = 188;
- + }
- + st->hctsiz_copy.b.xfersize = hc->xfer_len;
- +
- + st->hctsiz_copy.b.pktcnt = 1;
-
- + if (hc->ep_type & 0x1) {
- + /*
- + * For potentially multi-packet transfers, must use the DMA bounce buffers. For IN transfers,
- + * the DMA address is the address of the first 188byte slot buffer in the bounce buffer array.
- + * For multi-packet OUT transfers, we need to copy the data into the bounce buffer array so the FIQ can punt
- + * the right address out as necessary. hc->xfer_buff and hc->xfer_len have already been set
- + * in assign_and_init_hc(), but this is for the eventual transaction completion only. The FIQ
- + * must not touch internal driver state.
- + */
- + if(!fiq_fsm_setup_periodic_dma(hcd, st, qh)) {
- + if (hc->align_buff) {
- + st->hcdma_copy.d32 = hc->align_buff;
- + } else {
- + st->hcdma_copy.d32 = ((unsigned long) hc->xfer_buff & 0xFFFFFFFF);
- + }
- + }
- + } else {
- + if (hc->align_buff) {
- + st->hcdma_copy.d32 = hc->align_buff;
- + } else {
- + st->hcdma_copy.d32 = ((unsigned long) hc->xfer_buff & 0xFFFFFFFF);
- + }
- + }
- + /* The FIQ depends upon no other interrupts being enabled except channel halt.
- + * Fixup channel interrupt mask. */
- + st->hcintmsk_copy.d32 = 0;
- + st->hcintmsk_copy.b.chhltd = 1;
- + st->hcintmsk_copy.b.ahberr = 1;
- +
- + DWC_WRITE_REG32(&hc_regs->hcdma, st->hcdma_copy.d32);
- + DWC_WRITE_REG32(&hc_regs->hctsiz, st->hctsiz_copy.d32);
- + DWC_WRITE_REG32(&hc_regs->hcsplt, st->hcsplt_copy.d32);
- + DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32);
- + DWC_WRITE_REG32(&hc_regs->hcintmsk, st->hcintmsk_copy.d32);
- +
- + local_fiq_disable();
- + mb();
- +
- + if (hc->ep_type & 0x1) {
- + hfnum.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum);
- + frame = (hfnum.b.frnum & ~0x7) >> 3;
- + uframe = hfnum.b.frnum & 0x7;
- + if (hfnum.b.frrem < PERIODIC_FRREM_BACKOFF) {
- + /* Prevent queueing near EOF1. Bad things happen if a periodic
- + * split transaction is queued very close to EOF.
- + */
- + start_immediate = 0;
- + } else if (uframe == 5) {
- + start_immediate = 0;
- + } else if (hc->ep_type == UE_ISOCHRONOUS && !hc->ep_is_in) {
- + start_immediate = 0;
- + } else if (hc->ep_is_in && fiq_fsm_too_late(hcd->fiq_state, hc->hc_num)) {
- + start_immediate = 0;
- + } else {
- + /* Search through all host channels to determine if a transaction
- + * is currently in progress */
- + for (i = 0; i < hcd->core_if->core_params->host_channels; i++) {
- + if (i == hc->hc_num || hcd->fiq_state->channel[i].fsm == FIQ_PASSTHROUGH)
- + continue;
- + switch (hcd->fiq_state->channel[i].fsm) {
- + /* TT is reserved for channels that are in the middle of a periodic
- + * split transaction.
- + */
- + case FIQ_PER_SSPLIT_STARTED:
- + case FIQ_PER_CSPLIT_WAIT:
- + case FIQ_PER_CSPLIT_NYET1:
- + case FIQ_PER_CSPLIT_POLL:
- + case FIQ_PER_ISO_OUT_ACTIVE:
- + case FIQ_PER_ISO_OUT_LAST:
- + if (hcd->fiq_state->channel[i].hub_addr == hub_addr &&
- + hcd->fiq_state->channel[i].port_addr == port_addr) {
- + start_immediate = 0;
- + }
- + break;
- + default:
- + break;
- + }
- + if (!start_immediate)
- + break;
- + }
- + }
- + }
- + fiq_print(FIQDBG_INT, hcd->fiq_state, "FSMQ %01d %01d", hc->hc_num, start_immediate);
- + fiq_print(FIQDBG_INT, hcd->fiq_state, "%08d", hfnum.b.frrem);
- + //fiq_print(FIQDBG_INT, hcd->fiq_state, "H:%02dP:%02d", hub_addr, port_addr);
- + //fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hctsiz_copy.d32);
- + //fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hcdma_copy.d32);
- + switch (hc->ep_type) {
- + case UE_CONTROL:
- + case UE_BULK:
- + st->fsm = FIQ_NP_SSPLIT_STARTED;
- + break;
- + case UE_ISOCHRONOUS:
- + if (hc->ep_is_in) {
- + if (start_immediate) {
- + st->fsm = FIQ_PER_SSPLIT_STARTED;
- + } else {
- + st->fsm = FIQ_PER_SSPLIT_QUEUED;
- + }
- + } else {
- + if (start_immediate) {
- + /* Single-isoc OUT packets don't require FIQ involvement */
- + if (st->nrpackets == 1) {
- + st->fsm = FIQ_PER_ISO_OUT_LAST;
- + } else {
- + st->fsm = FIQ_PER_ISO_OUT_ACTIVE;
- + }
- + } else {
- + st->fsm = FIQ_PER_ISO_OUT_PENDING;
- + }
- + }
- + break;
- + case UE_INTERRUPT:
- + if (start_immediate) {
- + st->fsm = FIQ_PER_SSPLIT_STARTED;
- + } else {
- + st->fsm = FIQ_PER_SSPLIT_QUEUED;
- + }
- + default:
- + break;
- + }
- + if (start_immediate) {
- + /* Set the oddfrm bit as close as possible to actual queueing */
- + frame = dwc_otg_hcd_get_frame_number(hcd);
- + st->expected_uframe = (frame + 1) & 0x3FFF;
- + st->hcchar_copy.b.oddfrm = (frame & 0x1) ? 0 : 1;
- + st->hcchar_copy.b.chen = 1;
- + DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32);
- + }
- + mb();
- + local_fiq_enable();
- + return 0;
- }
-
-
- @@ -1404,16 +1908,11 @@ dwc_otg_transaction_type_e dwc_otg_hcd_s
- {
- dwc_list_link_t *qh_ptr;
- dwc_otg_qh_t *qh;
- - dwc_otg_qtd_t *qtd;
- int num_channels;
- dwc_irqflags_t flags;
- dwc_spinlock_t *channel_lock = hcd->channel_lock;
- dwc_otg_transaction_type_e ret_val = DWC_OTG_TRANSACTION_NONE;
-
- -#ifdef DEBUG_SOF
- - DWC_DEBUGPL(DBG_HCD, " Select Transactions\n");
- -#endif
- -
- #ifdef DEBUG_HOST_CHANNELS
- last_sel_trans_num_per_scheduled = 0;
- last_sel_trans_num_nonper_scheduled = 0;
- @@ -1428,26 +1927,11 @@ dwc_otg_transaction_type_e dwc_otg_hcd_s
-
- qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry);
-
- - if(qh->do_split) {
- - qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list);
- - if(!(qh->ep_type == UE_ISOCHRONOUS &&
- - (qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_MID ||
- - qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_END))) {
- - if(dwc_otg_hcd_allocate_port(hcd, qh))
- - {
- - qh_ptr = DWC_LIST_NEXT(qh_ptr);
- - g_next_sched_frame = dwc_frame_num_inc(dwc_otg_hcd_get_frame_number(hcd), 1);
- - continue;
- - }
- - }
- - }
- -
- if (microframe_schedule) {
- // Make sure we leave one channel for non periodic transactions.
- DWC_SPINLOCK_IRQSAVE(channel_lock, &flags);
- if (hcd->available_host_channels <= 1) {
- DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags);
- - if(qh->do_split) dwc_otg_hcd_release_port(hcd, qh);
- break;
- }
- hcd->available_host_channels--;
- @@ -1483,27 +1967,24 @@ dwc_otg_transaction_type_e dwc_otg_hcd_s
- !DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) {
-
- qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry);
- -
- /*
- * Check to see if this is a NAK'd retransmit, in which case ignore for retransmission
- * we hold off on bulk retransmissions to reduce NAK interrupt overhead for full-speed
- * cheeky devices that just hold off using NAKs
- */
- - if (nak_holdoff_enable && qh->do_split) {
- - if (qh->nak_frame != 0xffff &&
- - dwc_full_frame_num(qh->nak_frame) ==
- - dwc_full_frame_num(dwc_otg_hcd_get_frame_number(hcd))) {
- - /*
- - * Revisit: Need to avoid trampling on periodic scheduling.
- - * Currently we are safe because g_np_count != g_np_sent whenever we hit this,
- - * but if this behaviour is changed then periodic endpoints will get a slower
- - * polling rate.
- - */
- - g_next_sched_frame = ((qh->nak_frame + 8) & ~7) & DWC_HFNUM_MAX_FRNUM;
- - qh_ptr = DWC_LIST_NEXT(qh_ptr);
- - continue;
- - } else {
- - qh->nak_frame = 0xffff;
- + if (nak_holdoff && qh->do_split) {
- + if (qh->nak_frame != 0xffff) {
- + uint16_t next_frame = dwc_frame_num_inc(qh->nak_frame, (qh->ep_type == UE_BULK) ? nak_holdoff : 8);
- + uint16_t frame = dwc_otg_hcd_get_frame_number(hcd);
- + if (dwc_frame_num_le(frame, next_frame)) {
- + if(dwc_frame_num_le(next_frame, hcd->fiq_state->next_sched_frame)) {
- + hcd->fiq_state->next_sched_frame = next_frame;
- + }
- + qh_ptr = DWC_LIST_NEXT(qh_ptr);
- + continue;
- + } else {
- + qh->nak_frame = 0xFFFF;
- + }
- }
- }
-
- @@ -1532,12 +2013,31 @@ dwc_otg_transaction_type_e dwc_otg_hcd_s
- &qh->qh_list_entry);
- DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags);
-
- - g_np_sent++;
-
- if (!microframe_schedule)
- hcd->non_periodic_channels++;
- }
- -
- + /* we moved a non-periodic QH to the active schedule. If the inactive queue is empty,
- + * stop the FIQ from kicking us. We could potentially still have elements here if we
- + * ran out of host channels.
- + */
- + if (fiq_enable) {
- + if (DWC_LIST_EMPTY(&hcd->non_periodic_sched_inactive)) {
- + hcd->fiq_state->kick_np_queues = 0;
- + } else {
- + /* For each entry remaining in the NP inactive queue,
- + * if this a NAK'd retransmit then don't set the kick flag.
- + */
- + if(nak_holdoff) {
- + DWC_LIST_FOREACH(qh_ptr, &hcd->non_periodic_sched_inactive) {
- + qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry);
- + if (qh->nak_frame == 0xFFFF) {
- + hcd->fiq_state->kick_np_queues = 1;
- + }
- + }
- + }
- + }
- + }
- if(!DWC_LIST_EMPTY(&hcd->periodic_sched_assigned))
- ret_val |= DWC_OTG_TRANSACTION_PERIODIC;
-
- @@ -1582,6 +2082,12 @@ static int queue_transaction(dwc_otg_hcd
- hc->qh->ping_state = 0;
- }
- } else if (!hc->xfer_started) {
- + if (fiq_fsm_enable && hc->error_state) {
- + hcd->fiq_state->channel[hc->hc_num].nr_errors =
- + DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list)->error_count;
- + hcd->fiq_state->channel[hc->hc_num].fsm =
- + FIQ_PASSTHROUGH_ERRORSTATE;
- + }
- dwc_otg_hc_start_transfer(hcd->core_if, hc);
- hc->qh->ping_state = 0;
- }
- @@ -1634,7 +2140,7 @@ static void process_periodic_channels(dw
- hptxsts_data_t tx_status;
- dwc_list_link_t *qh_ptr;
- dwc_otg_qh_t *qh;
- - int status;
- + int status = 0;
- int no_queue_space = 0;
- int no_fifo_space = 0;
-
- @@ -1663,27 +2169,34 @@ static void process_periodic_channels(dw
-
- // Do not send a split start transaction any later than frame .6
- // Note, we have to schedule a periodic in .5 to make it go in .6
- - if(fiq_split_enable && qh->do_split && ((dwc_otg_hcd_get_frame_number(hcd) + 1) & 7) > 6)
- + if(fiq_fsm_enable && qh->do_split && ((dwc_otg_hcd_get_frame_number(hcd) + 1) & 7) > 6)
- {
- qh_ptr = qh_ptr->next;
- - g_next_sched_frame = dwc_otg_hcd_get_frame_number(hcd) | 7;
- + hcd->fiq_state->next_sched_frame = dwc_otg_hcd_get_frame_number(hcd) | 7;
- continue;
- }
-
- - /*
- - * Set a flag if we're queuing high-bandwidth in slave mode.
- - * The flag prevents any halts to get into the request queue in
- - * the middle of multiple high-bandwidth packets getting queued.
- - */
- - if (!hcd->core_if->dma_enable && qh->channel->multi_count > 1) {
- - hcd->core_if->queuing_high_bandwidth = 1;
- - }
- - status =
- - queue_transaction(hcd, qh->channel,
- - tx_status.b.ptxfspcavail);
- - if (status < 0) {
- - no_fifo_space = 1;
- - break;
- + if (fiq_fsm_enable && fiq_fsm_transaction_suitable(qh)) {
- + if (qh->do_split)
- + fiq_fsm_queue_split_transaction(hcd, qh);
- + else
- + fiq_fsm_queue_isoc_transaction(hcd, qh);
- + } else {
- +
- + /*
- + * Set a flag if we're queueing high-bandwidth in slave mode.
- + * The flag prevents any halts to get into the request queue in
- + * the middle of multiple high-bandwidth packets getting queued.
- + */
- + if (!hcd->core_if->dma_enable && qh->channel->multi_count > 1) {
- + hcd->core_if->queuing_high_bandwidth = 1;
- + }
- + status = queue_transaction(hcd, qh->channel,
- + tx_status.b.ptxfspcavail);
- + if (status < 0) {
- + no_fifo_space = 1;
- + break;
- + }
- }
-
- /*
- @@ -1800,25 +2313,19 @@ static void process_non_periodic_channel
- qh = DWC_LIST_ENTRY(hcd->non_periodic_qh_ptr, dwc_otg_qh_t,
- qh_list_entry);
-
- - // Do not send a split start transaction any later than frame .5
- - // non periodic transactions will start immediately in this uframe
- - if(fiq_split_enable && qh->do_split && ((dwc_otg_hcd_get_frame_number(hcd) + 1) & 7) > 6)
- - {
- - g_next_sched_frame = dwc_otg_hcd_get_frame_number(hcd) | 7;
- - break;
- - }
- + if(fiq_fsm_enable && fiq_fsm_transaction_suitable(qh)) {
- + fiq_fsm_queue_split_transaction(hcd, qh);
- + } else {
- + status = queue_transaction(hcd, qh->channel,
- + tx_status.b.nptxfspcavail);
-
- - status =
- - queue_transaction(hcd, qh->channel,
- - tx_status.b.nptxfspcavail);
- -
- - if (status > 0) {
- - more_to_do = 1;
- - } else if (status < 0) {
- - no_fifo_space = 1;
- - break;
- + if (status > 0) {
- + more_to_do = 1;
- + } else if (status < 0) {
- + no_fifo_space = 1;
- + break;
- + }
- }
- -
- /* Advance to next QH, skipping start-of-list entry. */
- hcd->non_periodic_qh_ptr = hcd->non_periodic_qh_ptr->next;
- if (hcd->non_periodic_qh_ptr == &hcd->non_periodic_sched_active) {
- --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.h
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.h
- @@ -40,6 +40,8 @@
- #include "dwc_otg_core_if.h"
- #include "dwc_list.h"
- #include "dwc_otg_cil.h"
- +#include "dwc_otg_fiq_fsm.h"
- +
-
- /**
- * @file
- @@ -585,6 +587,12 @@ struct dwc_otg_hcd {
- /** Frame List DMA address */
- dma_addr_t frame_list_dma;
-
- + struct fiq_stack *fiq_stack;
- + struct fiq_state *fiq_state;
- +
- + /** Virtual address for split transaction DMA bounce buffers */
- + struct fiq_dma_blob *fiq_dmab;
- +
- #ifdef DEBUG
- uint32_t frrem_samples;
- uint64_t frrem_accum;
- @@ -615,6 +623,9 @@ extern void dwc_otg_hcd_queue_transactio
- int dwc_otg_hcd_allocate_port(dwc_otg_hcd_t * hcd, dwc_otg_qh_t *qh);
- void dwc_otg_hcd_release_port(dwc_otg_hcd_t * dwc_otg_hcd, dwc_otg_qh_t *qh);
-
- +extern int fiq_fsm_queue_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh);
- +extern int fiq_fsm_transaction_suitable(dwc_otg_qh_t *qh);
- +extern void dwc_otg_cleanup_fiq_channel(dwc_otg_hcd_t *hcd, uint32_t num);
-
- /** @} */
-
- --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
- @@ -34,7 +34,6 @@
-
- #include "dwc_otg_hcd.h"
- #include "dwc_otg_regs.h"
- -#include "dwc_otg_mphi_fix.h"
-
- #include <linux/jiffies.h>
- #include <mach/hardware.h>
- @@ -47,33 +46,8 @@ extern bool microframe_schedule;
- * This file contains the implementation of the HCD Interrupt handlers.
- */
-
- -/*
- - * Some globals to communicate between the FIQ and INTERRUPT
- - */
- -
- -void * dummy_send;
- -mphi_regs_t c_mphi_regs;
- -volatile void *dwc_regs_base;
- int fiq_done, int_done;
-
- -gintsts_data_t gintsts_saved = {.d32 = 0};
- -hcint_data_t hcint_saved[MAX_EPS_CHANNELS];
- -hcintmsk_data_t hcintmsk_saved[MAX_EPS_CHANNELS];
- -int split_out_xfersize[MAX_EPS_CHANNELS];
- -haint_data_t haint_saved;
- -
- -int g_next_sched_frame, g_np_count, g_np_sent;
- -static int mphi_int_count = 0 ;
- -
- -hcchar_data_t nak_hcchar;
- -hctsiz_data_t nak_hctsiz;
- -hcsplt_data_t nak_hcsplt;
- -int nak_count;
- -
- -int complete_sched[MAX_EPS_CHANNELS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
- -int split_start_frame[MAX_EPS_CHANNELS];
- -int queued_port[MAX_EPS_CHANNELS];
- -
- #ifdef FIQ_DEBUG
- char buffer[1000*16];
- int wptr;
- @@ -83,12 +57,10 @@ void notrace _fiq_print(FIQDBG_T dbg_lvl
- va_list args;
- char text[17];
- hfnum_data_t hfnum = { .d32 = FIQ_READ(dwc_regs_base + 0x408) };
- - unsigned long flags;
-
- - local_irq_save(flags);
- - local_fiq_disable();
- if(dbg_lvl & dbg_lvl_req || dbg_lvl == FIQDBG_ERR)
- {
- + local_fiq_disable();
- snprintf(text, 9, "%4d%d:%d ", hfnum.b.frnum/8, hfnum.b.frnum%8, 8 - hfnum.b.frrem/937);
- va_start(args, fmt);
- vsnprintf(text+8, 9, fmt, args);
- @@ -96,410 +68,21 @@ void notrace _fiq_print(FIQDBG_T dbg_lvl
-
- memcpy(buffer + wptr, text, 16);
- wptr = (wptr + 16) % sizeof(buffer);
- + local_fiq_enable();
- }
- - local_irq_restore(flags);
- }
- #endif
-
- -void notrace fiq_queue_request(int channel, int odd_frame)
- -{
- - hcchar_data_t hcchar = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x0) };
- - hcsplt_data_t hcsplt = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x4) };
- - hctsiz_data_t hctsiz = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x10) };
- -
- - if(hcsplt.b.spltena == 0)
- - {
- - fiq_print(FIQDBG_ERR, "SPLTENA ");
- - BUG();
- - }
- -
- - if(hcchar.b.epdir == 1)
- - {
- - fiq_print(FIQDBG_SCHED, "IN Ch %d", channel);
- - }
- - else
- - {
- - hctsiz.b.xfersize = 0;
- - fiq_print(FIQDBG_SCHED, "OUT Ch %d", channel);
- - }
- - FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x10), hctsiz.d32);
- -
- - hcsplt.b.compsplt = 1;
- - FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x4), hcsplt.d32);
- -
- - // Send the Split complete
- - hcchar.b.chen = 1;
- - hcchar.b.oddfrm = odd_frame ? 1 : 0;
- -
- - // Post this for transmit on the next frame for periodic or this frame for non-periodic
- - fiq_print(FIQDBG_SCHED, "SND_%s", odd_frame ? "ODD " : "EVEN");
- -
- - FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x0), hcchar.d32);
- -}
- -
- -static int last_sof = -1;
- -
- -/*
- -** Function to handle the start of frame interrupt, choose whether we need to do anything and
- -** therefore trigger the main interrupt
- -**
- -** returns int != 0 - interrupt has been handled
- -*/
- -int diff;
- -
- -int notrace fiq_sof_handle(hfnum_data_t hfnum)
- -{
- - int handled = 0;
- - int i;
- -
- - // Just check that once we're running we don't miss a SOF
- - /*if(last_sof != -1 && (hfnum.b.frnum != ((last_sof + 1) & 0x3fff)))
- - {
- - fiq_print(FIQDBG_ERR, "LASTSOF ");
- - fiq_print(FIQDBG_ERR, "%4d%d ", last_sof / 8, last_sof & 7);
- - fiq_print(FIQDBG_ERR, "%4d%d ", hfnum.b.frnum / 8, hfnum.b.frnum & 7);
- - BUG();
- - }*/
- -
- - // Only start remembering the last sof when the interrupt has been
- - // enabled (we don't check the mask to come in here...)
- - if(last_sof != -1 || FIQ_READ(dwc_regs_base + 0x18) & (1<<3))
- - last_sof = hfnum.b.frnum;
- -
- - for(i = 0; i < MAX_EPS_CHANNELS; i++)
- - {
- - if(complete_sched[i] != -1)
- - {
- - if(complete_sched[i] <= hfnum.b.frnum || (complete_sched[i] > 0x3f00 && hfnum.b.frnum < 0xf0))
- - {
- - fiq_queue_request(i, hfnum.b.frnum & 1);
- - complete_sched[i] = -1;
- - }
- - }
- -
- - if(complete_sched[i] != -1)
- - {
- - // This is because we've seen a split complete occur with no start...
- - // most likely because missed the complete 0x3fff frames ago!
- -
- - diff = (hfnum.b.frnum + 0x3fff - complete_sched[i]) & 0x3fff ;
- - if(diff > 32 && diff < 0x3f00)
- - {
- - fiq_print(FIQDBG_ERR, "SPLTMISS");
- - BUG();
- - }
- - }
- - }
- -
- - if(g_np_count == g_np_sent && dwc_frame_num_gt(g_next_sched_frame, hfnum.b.frnum))
- - {
- - /*
- - * If np_count != np_sent that means we need to queue non-periodic (bulk) packets this packet
- - * g_next_sched_frame is the next frame we have periodic packets for
- - *
- - * if neither of these are required for this frame then just clear the interrupt
- - */
- - handled = 1;
- -
- - }
- -
- - return handled;
- -}
- -
- -int notrace port_id(hcsplt_data_t hcsplt)
- -{
- - return hcsplt.b.prtaddr + (hcsplt.b.hubaddr << 8);
- -}
- -
- -int notrace fiq_hcintr_handle(int channel, hfnum_data_t hfnum)
- -{
- - hcchar_data_t hcchar = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x0) };
- - hcsplt_data_t hcsplt = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x4) };
- - hcint_data_t hcint = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x8) };
- - hcintmsk_data_t hcintmsk = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0xc) };
- - hctsiz_data_t hctsiz = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x10)};
- -
- - hcint_saved[channel].d32 |= hcint.d32;
- - hcintmsk_saved[channel].d32 = hcintmsk.d32;
- -
- - if(hcsplt.b.spltena)
- - {
- - fiq_print(FIQDBG_PORTHUB, "ph: %4x", port_id(hcsplt));
- - if(hcint.b.chhltd)
- - {
- - fiq_print(FIQDBG_SCHED, "CH HLT %d", channel);
- - fiq_print(FIQDBG_SCHED, "%08x", hcint_saved[channel]);
- - }
- - if(hcint.b.stall || hcint.b.xacterr || hcint.b.bblerr || hcint.b.frmovrun || hcint.b.datatglerr)
- - {
- - queued_port[channel] = 0;
- - fiq_print(FIQDBG_ERR, "CHAN ERR");
- - }
- - if(hcint.b.xfercomp)
- - {
- - // Clear the port allocation and transmit anything also on this port
- - queued_port[channel] = 0;
- - fiq_print(FIQDBG_SCHED, "XFERCOMP");
- - }
- - if(hcint.b.nak)
- - {
- - queued_port[channel] = 0;
- - fiq_print(FIQDBG_SCHED, "NAK");
- - }
- - if(hcint.b.ack && !hcsplt.b.compsplt)
- - {
- - int i;
- -
- - // Do not complete isochronous out transactions
- - if(hcchar.b.eptype == 1 && hcchar.b.epdir == 0)
- - {
- - queued_port[channel] = 0;
- - fiq_print(FIQDBG_SCHED, "ISOC_OUT");
- - }
- - else
- - {
- - // Make sure we check the port / hub combination that we sent this split on.
- - // Do not queue a second request to the same port
- - for(i = 0; i < MAX_EPS_CHANNELS; i++)
- - {
- - if(port_id(hcsplt) == queued_port[i])
- - {
- - fiq_print(FIQDBG_ERR, "PORTERR ");
- - //BUG();
- - }
- - }
- -
- - split_start_frame[channel] = (hfnum.b.frnum + 1) & ~7;
- -
- - // Note, the size of an OUT is in the start split phase, not
- - // the complete split
- - split_out_xfersize[channel] = hctsiz.b.xfersize;
- -
- - hcint_saved[channel].b.chhltd = 0;
- - hcint_saved[channel].b.ack = 0;
- -
- - queued_port[channel] = port_id(hcsplt);
- -
- - if(hcchar.b.eptype & 1)
- - {
- - // Send the periodic complete in the same oddness frame as the ACK went...
- - fiq_queue_request(channel, !(hfnum.b.frnum & 1));
- - // complete_sched[channel] = dwc_frame_num_inc(hfnum.b.frnum, 1);
- - }
- - else
- - {
- - // Schedule the split complete to occur later
- - complete_sched[channel] = dwc_frame_num_inc(hfnum.b.frnum, 2);
- - fiq_print(FIQDBG_SCHED, "ACK%04d%d", complete_sched[channel]/8, complete_sched[channel]%8);
- - }
- - }
- - }
- - if(hcint.b.nyet)
- - {
- - fiq_print(FIQDBG_ERR, "NYETERR1");
- - //BUG();
- - // Can transmit a split complete up to uframe .0 of the next frame
- - if(hfnum.b.frnum <= dwc_frame_num_inc(split_start_frame[channel], 8))
- - {
- - // Send it next frame
- - if(hcchar.b.eptype & 1) // type 1 & 3 are interrupt & isoc
- - {
- - fiq_print(FIQDBG_SCHED, "NYT:SEND");
- - fiq_queue_request(channel, !(hfnum.b.frnum & 1));
- - }
- - else
- - {
- - // Schedule non-periodic access for next frame (the odd-even bit doesn't effect NP)
- - complete_sched[channel] = dwc_frame_num_inc(hfnum.b.frnum, 1);
- - fiq_print(FIQDBG_SCHED, "NYT%04d%d", complete_sched[channel]/8, complete_sched[channel]%8);
- - }
- - hcint_saved[channel].b.chhltd = 0;
- - hcint_saved[channel].b.nyet = 0;
- - }
- - else
- - {
- - queued_port[channel] = 0;
- - fiq_print(FIQDBG_ERR, "NYETERR2");
- - //BUG();
- - }
- - }
- - }
- - else
- - {
- - /*
- - * If we have any of NAK, ACK, Datatlgerr active on a
- - * non-split channel, the sole reason is to reset error
- - * counts for a previously broken transaction. The FIQ
- - * will thrash on NAK IN and ACK OUT in particular so
- - * handle it "once" and allow the IRQ to do the rest.
- - */
- - hcint.d32 &= hcintmsk.d32;
- - if(hcint.b.nak)
- - {
- - hcintmsk.b.nak = 0;
- - FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0xc), hcintmsk.d32);
- - }
- - if (hcint.b.ack)
- - {
- - hcintmsk.b.ack = 0;
- - FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0xc), hcintmsk.d32);
- - }
- - }
- -
- - // Clear the interrupt, this will also clear the HAINT bit
- - FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x8), hcint.d32);
- - return hcint_saved[channel].d32 == 0;
- -}
- -
- -gintsts_data_t gintsts;
- -gintmsk_data_t gintmsk;
- -// triggered: The set of interrupts that were triggered
- -// handled: The set of interrupts that have been handled (no IRQ is
- -// required)
- -// keep: The set of interrupts we want to keep unmasked even though we
- -// want to trigger an IRQ to handle it (SOF and HCINTR)
- -gintsts_data_t triggered, handled, keep;
- -hfnum_data_t hfnum;
- -
- -void __attribute__ ((naked)) notrace dwc_otg_hcd_handle_fiq(void)
- -{
- -
- - /* entry takes care to store registers we will be treading on here */
- - asm __volatile__ (
- - "mov ip, sp ;"
- - /* stash FIQ and normal regs */
- - "stmdb sp!, {r0-r12, lr};"
- - /* !! THIS SETS THE FRAME, adjust to > sizeof locals */
- - "sub fp, ip, #512 ;"
- - );
- -
- - // Cannot put local variables at the beginning of the function
- - // because otherwise 'C' will play with the stack pointer. any locals
- - // need to be inside the following block
- - do
- - {
- - fiq_done++;
- - gintsts.d32 = FIQ_READ(dwc_regs_base + 0x14);
- - gintmsk.d32 = FIQ_READ(dwc_regs_base + 0x18);
- - hfnum.d32 = FIQ_READ(dwc_regs_base + 0x408);
- - triggered.d32 = gintsts.d32 & gintmsk.d32;
- - handled.d32 = 0;
- - keep.d32 = 0;
- - fiq_print(FIQDBG_INT, "FIQ ");
- - fiq_print(FIQDBG_INT, "%08x", gintsts.d32);
- - fiq_print(FIQDBG_INT, "%08x", gintmsk.d32);
- - if(gintsts.d32)
- - {
- - // If port enabled
- - if((FIQ_READ(dwc_regs_base + 0x440) & 0xf) == 0x5)
- - {
- - if(gintsts.b.sofintr)
- - {
- - if(fiq_sof_handle(hfnum))
- - {
- - handled.b.sofintr = 1; /* Handled in FIQ */
- - }
- - else
- - {
- - /* Keer interrupt unmasked */
- - keep.b.sofintr = 1;
- - }
- - {
- - // Need to make sure the read and clearing of the SOF interrupt is as close as possible to avoid the possibility of missing
- - // a start of frame interrupt
- - gintsts_data_t gintsts = { .b.sofintr = 1 };
- - FIQ_WRITE((dwc_regs_base + 0x14), gintsts.d32);
- - }
- - }
- -
- - if(fiq_split_enable && gintsts.b.hcintr)
- - {
- - int i;
- - haint_data_t haint;
- - haintmsk_data_t haintmsk;
- -
- - haint.d32 = FIQ_READ(dwc_regs_base + 0x414);
- - haintmsk.d32 = FIQ_READ(dwc_regs_base + 0x418);
- - haint.d32 &= haintmsk.d32;
- - haint_saved.d32 |= haint.d32;
- -
- - fiq_print(FIQDBG_INT, "hcintr");
- - fiq_print(FIQDBG_INT, "%08x", FIQ_READ(dwc_regs_base + 0x414));
- -
- - // Go through each channel that has an enabled interrupt
- - for(i = 0; i < 16; i++)
- - if((haint.d32 >> i) & 1)
- - if(fiq_hcintr_handle(i, hfnum))
- - haint_saved.d32 &= ~(1 << i); /* this was handled */
- -
- - /* If we've handled all host channel interrupts then don't trigger the interrupt */
- - if(haint_saved.d32 == 0)
- - {
- - handled.b.hcintr = 1;
- - }
- - else
- - {
- - /* Make sure we keep the channel interrupt unmasked when triggering the IRQ */
- - keep.b.hcintr = 1;
- - }
- -
- - {
- - gintsts_data_t gintsts = { .b.hcintr = 1 };
- -
- - // Always clear the channel interrupt
- - FIQ_WRITE((dwc_regs_base + 0x14), gintsts.d32);
- - }
- - }
- - }
- - else
- - {
- - last_sof = -1;
- - }
- - }
- -
- - // Mask out the interrupts triggered - those handled - don't mask out the ones we want to keep
- - gintmsk.d32 = keep.d32 | (gintmsk.d32 & ~(triggered.d32 & ~handled.d32));
- - // Save those that were triggered but not handled
- - gintsts_saved.d32 |= triggered.d32 & ~handled.d32;
- - FIQ_WRITE(dwc_regs_base + 0x18, gintmsk.d32);
- -
- - // Clear and save any unhandled interrupts and trigger the interrupt
- - if(gintsts_saved.d32)
- - {
- - /* To enable the MPHI interrupt (INT 32)
- - */
- - FIQ_WRITE( c_mphi_regs.outdda, (int) dummy_send);
- - FIQ_WRITE( c_mphi_regs.outddb, (1 << 29));
- -
- - mphi_int_count++;
- - }
- - }
- - while(0);
- -
- - mb();
- -
- - /* exit back to normal mode restoring everything */
- - asm __volatile__ (
- - /* return FIQ regs back to pristine state
- - * and get normal regs back
- - */
- - "ldmia sp!, {r0-r12, lr};"
- -
- - /* return */
- - "subs pc, lr, #4;"
- - );
- -}
- -
- /** This function handles interrupts for the HCD. */
- int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd)
- {
- int retval = 0;
- static int last_time;
- -
- dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if;
- gintsts_data_t gintsts;
- gintmsk_data_t gintmsk;
- hfnum_data_t hfnum;
- + haintmsk_data_t haintmsk;
-
- #ifdef DEBUG
- dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
- @@ -516,15 +99,29 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_
- DWC_SPINLOCK(dwc_otg_hcd->lock);
- /* Check if HOST Mode */
- if (dwc_otg_is_host_mode(core_if)) {
- - local_fiq_disable();
- - gintmsk.d32 |= gintsts_saved.d32;
- - gintsts.d32 |= gintsts_saved.d32;
- - gintsts_saved.d32 = 0;
- - local_fiq_enable();
- + if (fiq_enable) {
- + local_fiq_disable();
- + /* Pull in from the FIQ's disabled mask */
- + gintmsk.d32 = gintmsk.d32 | ~(dwc_otg_hcd->fiq_state->gintmsk_saved.d32);
- + dwc_otg_hcd->fiq_state->gintmsk_saved.d32 = ~0;
- + }
- +
- + if (fiq_fsm_enable && ( 0x0000FFFF & ~(dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint))) {
- + gintsts.b.hcintr = 1;
- + }
- +
- + /* Danger will robinson: fake a SOF if necessary */
- + if (fiq_fsm_enable && (dwc_otg_hcd->fiq_state->gintmsk_saved.b.sofintr == 1)) {
- + gintsts.b.sofintr = 1;
- + }
- + gintsts.d32 &= gintmsk.d32;
- +
- + if (fiq_enable)
- + local_fiq_enable();
- +
- if (!gintsts.d32) {
- goto exit_handler_routine;
- }
- - gintsts.d32 &= gintmsk.d32;
-
- #ifdef DEBUG
- // We should be OK doing this because the common interrupts should already have been serviced
- @@ -544,12 +141,7 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_
- gintsts.d32, core_if);
- #endif
- hfnum.d32 = DWC_READ_REG32(&dwc_otg_hcd->core_if->host_if->host_global_regs->hfnum);
- - if (gintsts.b.sofintr && g_np_count == g_np_sent && dwc_frame_num_gt(g_next_sched_frame, hfnum.b.frnum))
- - {
- - /* Note, we should never get here if the FIQ is doing it's job properly*/
- - retval |= dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd);
- - }
- - else if (gintsts.b.sofintr) {
- + if (gintsts.b.sofintr) {
- retval |= dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd);
- }
-
- @@ -604,37 +196,43 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_
- }
-
- exit_handler_routine:
- -
- - if (fiq_fix_enable)
- - {
- + if (fiq_enable) {
- + gintmsk_data_t gintmsk_new;
- + haintmsk_data_t haintmsk_new;
- local_fiq_disable();
- - // Make sure that we don't clear the interrupt if we've still got pending work to do
- - if(gintsts_saved.d32 == 0)
- - {
- - /* Clear the MPHI interrupt */
- - DWC_WRITE_REG32(c_mphi_regs.intstat, (1<<16));
- - if (mphi_int_count >= 60)
- - {
- - DWC_WRITE_REG32(c_mphi_regs.ctrl, ((1<<31) + (1<<16)));
- - while(!(DWC_READ_REG32(c_mphi_regs.ctrl) & (1 << 17)))
- - ;
- - DWC_WRITE_REG32(c_mphi_regs.ctrl, (1<<31));
- - mphi_int_count = 0;
- - }
- - int_done++;
- - }
- -
- - // Unmask handled interrupts
- - FIQ_WRITE(dwc_regs_base + 0x18, gintmsk.d32);
- - //DWC_MODIFY_REG32((uint32_t *)IO_ADDRESS(USB_BASE + 0x8), 0 , 1);
- + gintmsk_new.d32 = *(volatile uint32_t *)&dwc_otg_hcd->fiq_state->gintmsk_saved.d32;
- + if(fiq_fsm_enable)
- + haintmsk_new.d32 = *(volatile uint32_t *)&dwc_otg_hcd->fiq_state->haintmsk_saved.d32;
- + else
- + haintmsk_new.d32 = 0x0000FFFF;
-
- + /* The FIQ could have sneaked another interrupt in. If so, don't clear MPHI */
- + if ((gintmsk_new.d32 == ~0) && (haintmsk_new.d32 == 0x0000FFFF)) {
- + DWC_WRITE_REG32(dwc_otg_hcd->fiq_state->mphi_regs.intstat, (1<<16));
- + if (dwc_otg_hcd->fiq_state->mphi_int_count >= 50) {
- + fiq_print(FIQDBG_INT, dwc_otg_hcd->fiq_state, "MPHI CLR");
- + DWC_WRITE_REG32(dwc_otg_hcd->fiq_state->mphi_regs.ctrl, ((1<<31) + (1<<16)));
- + while (!(DWC_READ_REG32(dwc_otg_hcd->fiq_state->mphi_regs.ctrl) & (1 << 17)))
- + ;
- + DWC_WRITE_REG32(dwc_otg_hcd->fiq_state->mphi_regs.ctrl, (1<<31));
- + dwc_otg_hcd->fiq_state->mphi_int_count = 0;
- + }
- + int_done++;
- + }
- + haintmsk.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->haintmsk);
- + /* Re-enable interrupts that the FIQ masked (first time round) */
- + FIQ_WRITE(dwc_otg_hcd->fiq_state->dwc_regs_base + GINTMSK, gintmsk.d32);
- local_fiq_enable();
-
- - if((jiffies / HZ) > last_time)
- - {
- + if ((jiffies / HZ) > last_time) {
- + //dwc_otg_qh_t *qh;
- + //dwc_list_link_t *cur;
- /* Once a second output the fiq and irq numbers, useful for debug */
- last_time = jiffies / HZ;
- - DWC_DEBUGPL(DBG_USER, "int_done = %d fiq_done = %d\n", int_done, fiq_done);
- + // DWC_WARN("np_kick=%d AHC=%d sched_frame=%d cur_frame=%d int_done=%d fiq_done=%d",
- + // dwc_otg_hcd->fiq_state->kick_np_queues, dwc_otg_hcd->available_host_channels,
- + // dwc_otg_hcd->fiq_state->next_sched_frame, hfnum.b.frnum, int_done, dwc_otg_hcd->fiq_state->fiq_done);
- + //printk(KERN_WARNING "Periodic queues:\n");
- }
- }
-
- @@ -686,6 +284,7 @@ static inline void track_missed_sofs(uin
- int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * hcd)
- {
- hfnum_data_t hfnum;
- + gintsts_data_t gintsts = { .d32 = 0 };
- dwc_list_link_t *qh_entry;
- dwc_otg_qh_t *qh;
- dwc_otg_transaction_type_e tr_type;
- @@ -732,8 +331,8 @@ int32_t dwc_otg_hcd_handle_sof_intr(dwc_
- }
- }
- }
- -
- - g_next_sched_frame = next_sched_frame;
- + if (fiq_enable)
- + hcd->fiq_state->next_sched_frame = next_sched_frame;
-
- tr_type = dwc_otg_hcd_select_transactions(hcd);
- if (tr_type != DWC_OTG_TRANSACTION_NONE) {
- @@ -741,10 +340,11 @@ int32_t dwc_otg_hcd_handle_sof_intr(dwc_
- did_something = 1;
- }
-
- - /* Clear interrupt */
- - gintsts.b.sofintr = 1;
- - DWC_WRITE_REG32(&hcd->core_if->core_global_regs->gintsts, gintsts.d32);
- -
- + /* Clear interrupt - but do not trample on the FIQ sof */
- + if (!fiq_fsm_enable) {
- + gintsts.b.sofintr = 1;
- + DWC_WRITE_REG32(&hcd->core_if->core_global_regs->gintsts, gintsts.d32);
- + }
- return 1;
- }
-
- @@ -1020,19 +620,21 @@ int32_t dwc_otg_hcd_handle_hc_intr(dwc_o
- {
- int i;
- int retval = 0;
- - haint_data_t haint;
- + haint_data_t haint = { .d32 = 0 } ;
-
- /* Clear appropriate bits in HCINTn to clear the interrupt bit in
- * GINTSTS */
-
- - haint.d32 = dwc_otg_read_host_all_channels_intr(dwc_otg_hcd->core_if);
- + if (!fiq_fsm_enable)
- + haint.d32 = dwc_otg_read_host_all_channels_intr(dwc_otg_hcd->core_if);
-
- // Overwrite with saved interrupts from fiq handler
- - if(fiq_split_enable)
- + if(fiq_fsm_enable)
- {
- + /* check the mask? */
- local_fiq_disable();
- - haint.d32 = haint_saved.d32;
- - haint_saved.d32 = 0;
- + haint.b2.chint |= ~(dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint);
- + dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint = ~0;
- local_fiq_enable();
- }
-
- @@ -1076,9 +678,7 @@ static uint32_t get_actual_xfer_length(d
- *short_read = (hctsiz.b.xfersize != 0);
- }
- } else if (hc->qh->do_split) {
- - if(fiq_split_enable)
- - length = split_out_xfersize[hc->hc_num];
- - else
- + //length = split_out_xfersize[hc->hc_num];
- length = qtd->ssplit_out_xfer_count;
- } else {
- length = hc->xfer_len;
- @@ -1325,19 +925,17 @@ static void release_channel(dwc_otg_hcd_
- int free_qtd;
- dwc_irqflags_t flags;
- dwc_spinlock_t *channel_lock = hcd->channel_lock;
- -#ifdef FIQ_DEBUG
- - int endp = qtd->urb ? qtd->urb->pipe_info.ep_num : 0;
- -#endif
- +
- int hog_port = 0;
-
- DWC_DEBUGPL(DBG_HCDV, " %s: channel %d, halt_status %d, xfer_len %d\n",
- __func__, hc->hc_num, halt_status, hc->xfer_len);
-
- - if(fiq_split_enable && hc->do_split) {
- + if(fiq_fsm_enable && hc->do_split) {
- if(!hc->ep_is_in && hc->ep_type == UE_ISOCHRONOUS) {
- if(hc->xact_pos == DWC_HCSPLIT_XACTPOS_MID ||
- hc->xact_pos == DWC_HCSPLIT_XACTPOS_BEGIN) {
- - hog_port = 1;
- + hog_port = 0;
- }
- }
- }
- @@ -1394,6 +992,8 @@ cleanup:
- * function clears the channel interrupt enables and conditions, so
- * there's no need to clear the Channel Halted interrupt separately.
- */
- + if (fiq_fsm_enable && hcd->fiq_state->channel[hc->hc_num].fsm != FIQ_PASSTHROUGH)
- + dwc_otg_cleanup_fiq_channel(hcd, hc->hc_num);
- dwc_otg_hc_cleanup(hcd->core_if, hc);
- DWC_CIRCLEQ_INSERT_TAIL(&hcd->free_hc_list, hc, hc_list_entry);
-
- @@ -1416,27 +1016,10 @@ cleanup:
-
- DWC_SPINLOCK_IRQSAVE(channel_lock, &flags);
- hcd->available_host_channels++;
- - fiq_print(FIQDBG_PORTHUB, "AHC = %d ", hcd->available_host_channels);
- + fiq_print(FIQDBG_INT, hcd->fiq_state, "AHC = %d ", hcd->available_host_channels);
- DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags);
- }
-
- - if(fiq_split_enable && hc->do_split)
- - {
- - if(!(hcd->hub_port[hc->hub_addr] & (1 << hc->port_addr)))
- - {
- - fiq_print(FIQDBG_ERR, "PRTNOTAL");
- - //BUG();
- - }
- - if(!hog_port && (hc->ep_type == DWC_OTG_EP_TYPE_ISOC ||
- - hc->ep_type == DWC_OTG_EP_TYPE_INTR)) {
- - hcd->hub_port[hc->hub_addr] &= ~(1 << hc->port_addr);
- -#ifdef FIQ_DEBUG
- - hcd->hub_port_alloc[hc->hub_addr * 16 + hc->port_addr] = -1;
- -#endif
- - fiq_print(FIQDBG_PORTHUB, "H%dP%d:RR%d", hc->hub_addr, hc->port_addr, endp);
- - }
- - }
- -
- /* Try to queue more transfers now that there's a free channel. */
- tr_type = dwc_otg_hcd_select_transactions(hcd);
- if (tr_type != DWC_OTG_TRANSACTION_NONE) {
- @@ -1858,7 +1441,7 @@ static int32_t handle_hc_nak_intr(dwc_ot
- switch(dwc_otg_hcd_get_pipe_type(&qtd->urb->pipe_info)) {
- case UE_BULK:
- case UE_CONTROL:
- - if (nak_holdoff_enable)
- + if (nak_holdoff && qtd->qh->do_split)
- hc->qh->nak_frame = dwc_otg_hcd_get_frame_number(hcd);
- }
-
- @@ -2074,7 +1657,7 @@ static int32_t handle_hc_nyet_intr(dwc_o
- // With the FIQ running we only ever see the failed NYET
- if (dwc_full_frame_num(frnum) !=
- dwc_full_frame_num(hc->qh->sched_frame) ||
- - fiq_split_enable) {
- + fiq_fsm_enable) {
- /*
- * No longer in the same full speed frame.
- * Treat this as a transaction error.
- @@ -2460,12 +2043,11 @@ static inline int halt_status_ok(dwc_otg
- static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd,
- dwc_hc_t * hc,
- dwc_otg_hc_regs_t * hc_regs,
- - dwc_otg_qtd_t * qtd,
- - hcint_data_t hcint,
- - hcintmsk_data_t hcintmsk)
- + dwc_otg_qtd_t * qtd)
- {
- int out_nak_enh = 0;
- -
- + hcint_data_t hcint;
- + hcintmsk_data_t hcintmsk;
- /* For core with OUT NAK enhancement, the flow for high-
- * speed CONTROL/BULK OUT is handled a little differently.
- */
- @@ -2495,11 +2077,9 @@ static void handle_hc_chhltd_intr_dma(dw
- }
-
- /* Read the HCINTn register to determine the cause for the halt. */
- - if(!fiq_split_enable)
- - {
- - hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
- - hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk);
- - }
- +
- + hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
- + hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk);
-
- if (hcint.b.xfercomp) {
- /** @todo This is here because of a possible hardware bug. Spec
- @@ -2624,15 +2204,13 @@ static void handle_hc_chhltd_intr_dma(dw
- static int32_t handle_hc_chhltd_intr(dwc_otg_hcd_t * hcd,
- dwc_hc_t * hc,
- dwc_otg_hc_regs_t * hc_regs,
- - dwc_otg_qtd_t * qtd,
- - hcint_data_t hcint,
- - hcintmsk_data_t hcintmsk)
- + dwc_otg_qtd_t * qtd)
- {
- DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: "
- "Channel Halted--\n", hc->hc_num);
-
- if (hcd->core_if->dma_enable) {
- - handle_hc_chhltd_intr_dma(hcd, hc, hc_regs, qtd, hcint, hcintmsk);
- + handle_hc_chhltd_intr_dma(hcd, hc, hc_regs, qtd);
- } else {
- #ifdef DEBUG
- if (!halt_status_ok(hcd, hc, hc_regs, qtd)) {
- @@ -2645,11 +2223,372 @@ static int32_t handle_hc_chhltd_intr(dwc
- return 1;
- }
-
- +
- +/**
- + * dwc_otg_fiq_unmangle_isoc() - Update the iso_frame_desc structure on
- + * FIQ transfer completion
- + * @hcd: Pointer to dwc_otg_hcd struct
- + * @num: Host channel number
- + *
- + * 1. Un-mangle the status as recorded in each iso_frame_desc status
- + * 2. Copy it from the dwc_otg_urb into the real URB
- + */
- +void dwc_otg_fiq_unmangle_isoc(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, dwc_otg_qtd_t *qtd, uint32_t num)
- +{
- + struct dwc_otg_hcd_urb *dwc_urb = qtd->urb;
- + int nr_frames = dwc_urb->packet_count;
- + int i;
- + hcint_data_t frame_hcint;
- +
- + for (i = 0; i < nr_frames; i++) {
- + frame_hcint.d32 = dwc_urb->iso_descs[i].status;
- + if (frame_hcint.b.xfercomp) {
- + dwc_urb->iso_descs[i].status = 0;
- + dwc_urb->actual_length += dwc_urb->iso_descs[i].actual_length;
- + } else if (frame_hcint.b.frmovrun) {
- + if (qh->ep_is_in)
- + dwc_urb->iso_descs[i].status = -DWC_E_NO_STREAM_RES;
- + else
- + dwc_urb->iso_descs[i].status = -DWC_E_COMMUNICATION;
- + dwc_urb->error_count++;
- + dwc_urb->iso_descs[i].actual_length = 0;
- + } else if (frame_hcint.b.xacterr) {
- + dwc_urb->iso_descs[i].status = -DWC_E_PROTOCOL;
- + dwc_urb->error_count++;
- + dwc_urb->iso_descs[i].actual_length = 0;
- + } else if (frame_hcint.b.bblerr) {
- + dwc_urb->iso_descs[i].status = -DWC_E_OVERFLOW;
- + dwc_urb->error_count++;
- + dwc_urb->iso_descs[i].actual_length = 0;
- + } else {
- + /* Something went wrong */
- + dwc_urb->iso_descs[i].status = -1;
- + dwc_urb->iso_descs[i].actual_length = 0;
- + dwc_urb->error_count++;
- + }
- + }
- + //printk_ratelimited(KERN_INFO "%s: HS isochronous of %d/%d frames with %d errors complete\n",
- + // __FUNCTION__, i, dwc_urb->packet_count, dwc_urb->error_count);
- + hcd->fops->complete(hcd, dwc_urb->priv, dwc_urb, 0);
- + release_channel(hcd, qh->channel, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
- +}
- +
- +/**
- + * dwc_otg_fiq_unsetup_per_dma() - Remove data from bounce buffers for split transactions
- + * @hcd: Pointer to dwc_otg_hcd struct
- + * @num: Host channel number
- + *
- + * Copies data from the FIQ bounce buffers into the URB's transfer buffer. Does not modify URB state.
- + * Returns total length of data or -1 if the buffers were not used.
- + *
- + */
- +int dwc_otg_fiq_unsetup_per_dma(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, dwc_otg_qtd_t *qtd, uint32_t num)
- +{
- + dwc_hc_t *hc = qh->channel;
- + struct fiq_dma_blob *blob = hcd->fiq_dmab;
- + struct fiq_channel_state *st = &hcd->fiq_state->channel[num];
- + uint8_t *ptr = NULL;
- + int index = 0, len = 0;
- + int i = 0;
- + if (hc->ep_is_in) {
- + /* Copy data out of the DMA bounce buffers to the URB's buffer.
- + * The align_buf is ignored as this is ignored on FSM enqueue. */
- + ptr = qtd->urb->buf;
- + if (qh->ep_type == UE_ISOCHRONOUS) {
- + /* Isoc IN transactions - grab the offset of the iso_frame_desc into the URB transfer buffer */
- + index = qtd->isoc_frame_index;
- + ptr += qtd->urb->iso_descs[index].offset;
- + } else {
- + /* Need to increment by actual_length for interrupt IN */
- + ptr += qtd->urb->actual_length;
- + }
- +
- + for (i = 0; i < st->dma_info.index; i++) {
- + len += st->dma_info.slot_len[i];
- + dwc_memcpy(ptr, &blob->channel[num].index[i].buf[0], st->dma_info.slot_len[i]);
- + ptr += st->dma_info.slot_len[i];
- + }
- + return len;
- + } else {
- + /* OUT endpoints - nothing to do. */
- + return -1;
- + }
- +
- +}
- +/**
- + * dwc_otg_hcd_handle_hc_fsm() - handle an unmasked channel interrupt
- + * from a channel handled in the FIQ
- + * @hcd: Pointer to dwc_otg_hcd struct
- + * @num: Host channel number
- + *
- + * If a host channel interrupt was received by the IRQ and this was a channel
- + * used by the FIQ, the execution flow for transfer completion is substantially
- + * different from the normal (messy) path. This function and its friends handles
- + * channel cleanup and transaction completion from a FIQ transaction.
- + */
- +int32_t dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd_t *hcd, uint32_t num)
- +{
- + struct fiq_channel_state *st = &hcd->fiq_state->channel[num];
- + dwc_hc_t *hc = hcd->hc_ptr_array[num];
- + dwc_otg_qtd_t *qtd = DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list);
- + dwc_otg_qh_t *qh = hc->qh;
- + dwc_otg_hc_regs_t *hc_regs = hcd->core_if->host_if->hc_regs[num];
- + hcint_data_t hcint = hcd->fiq_state->channel[num].hcint_copy;
- + int hostchannels = 0;
- + int ret = 0;
- + fiq_print(FIQDBG_INT, hcd->fiq_state, "OUT %01d %01d ", num , st->fsm);
- +
- + hostchannels = hcd->available_host_channels;
- + switch (st->fsm) {
- + case FIQ_TEST:
- + break;
- +
- + case FIQ_DEQUEUE_ISSUED:
- + /* hc_halt was called. QTD no longer exists. */
- + /* TODO: for a nonperiodic split transaction, need to issue a
- + * CLEAR_TT_BUFFER hub command if we were in the start-split phase.
- + */
- + release_channel(hcd, hc, NULL, hc->halt_status);
- + ret = 1;
- + break;
- +
- + case FIQ_NP_SPLIT_DONE:
- + /* Nonperiodic transaction complete. */
- + if (!hc->ep_is_in) {
- + qtd->ssplit_out_xfer_count = hc->xfer_len;
- + }
- + if (hcint.b.xfercomp) {
- + handle_hc_xfercomp_intr(hcd, hc, hc_regs, qtd);
- + } else if (hcint.b.nak) {
- + handle_hc_nak_intr(hcd, hc, hc_regs, qtd);
- + }
- + ret = 1;
- + break;
- +
- + case FIQ_NP_SPLIT_HS_ABORTED:
- + /* A HS abort is a 3-strikes on the HS bus at any point in the transaction.
- + * Normally a CLEAR_TT_BUFFER hub command would be required: we can't do that
- + * because there's no guarantee which order a non-periodic split happened in.
- + * We could end up clearing a perfectly good transaction out of the buffer.
- + */
- + if (hcint.b.xacterr) {
- + qtd->error_count += st->nr_errors;
- + handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd);
- + } else if (hcint.b.ahberr) {
- + handle_hc_ahberr_intr(hcd, hc, hc_regs, qtd);
- + } else {
- + local_fiq_disable();
- + BUG();
- + }
- + break;
- +
- + case FIQ_NP_SPLIT_LS_ABORTED:
- + /* A few cases can cause this - either an unknown state on a SSPLIT or
- + * STALL/data toggle error response on a CSPLIT */
- + if (hcint.b.stall) {
- + handle_hc_stall_intr(hcd, hc, hc_regs, qtd);
- + } else if (hcint.b.datatglerr) {
- + handle_hc_datatglerr_intr(hcd, hc, hc_regs, qtd);
- + } else if (hcint.b.bblerr) {
- + handle_hc_babble_intr(hcd, hc, hc_regs, qtd);
- + } else if (hcint.b.ahberr) {
- + handle_hc_ahberr_intr(hcd, hc, hc_regs, qtd);
- + } else {
- + local_fiq_disable();
- + BUG();
- + }
- + break;
- +
- + case FIQ_PER_SPLIT_DONE:
- + /* Isoc IN or Interrupt IN/OUT */
- +
- + /* Flow control here is different from the normal execution by the driver.
- + * We need to completely ignore most of the driver's method of handling
- + * split transactions and do it ourselves.
- + */
- + if (hc->ep_type == UE_INTERRUPT) {
- + if (hcint.b.nak) {
- + handle_hc_nak_intr(hcd, hc, hc_regs, qtd);
- + } else if (hc->ep_is_in) {
- + int len;
- + len = dwc_otg_fiq_unsetup_per_dma(hcd, hc->qh, qtd, num);
- + //printk(KERN_NOTICE "FIQ Transaction: hc=%d len=%d urb_len = %d\n", num, len, qtd->urb->length);
- + qtd->urb->actual_length += len;
- + if (qtd->urb->actual_length >= qtd->urb->length) {
- + qtd->urb->status = 0;
- + hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, qtd->urb->status);
- + release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
- + } else {
- + /* Interrupt transfer not complete yet - is it a short read? */
- + if (len < hc->max_packet) {
- + /* Interrupt transaction complete */
- + qtd->urb->status = 0;
- + hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, qtd->urb->status);
- + release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
- + } else {
- + /* Further transactions required */
- + release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE);
- + }
- + }
- + } else {
- + /* Interrupt OUT complete. */
- + dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
- + qtd->urb->actual_length += hc->xfer_len;
- + if (qtd->urb->actual_length >= qtd->urb->length) {
- + qtd->urb->status = 0;
- + hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, qtd->urb->status);
- + release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
- + } else {
- + release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE);
- + }
- + }
- + } else {
- + /* ISOC IN complete. */
- + struct dwc_otg_hcd_iso_packet_desc *frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index];
- + int len = 0;
- + /* Record errors, update qtd. */
- + if (st->nr_errors) {
- + frame_desc->actual_length = 0;
- + frame_desc->status = -DWC_E_PROTOCOL;
- + } else {
- + frame_desc->status = 0;
- + /* Unswizzle dma */
- + len = dwc_otg_fiq_unsetup_per_dma(hcd, qh, qtd, num);
- + frame_desc->actual_length = len;
- + }
- + qtd->isoc_frame_index++;
- + if (qtd->isoc_frame_index == qtd->urb->packet_count) {
- + hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0);
- + release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
- + } else {
- + release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE);
- + }
- + }
- + break;
- +
- + case FIQ_PER_ISO_OUT_DONE: {
- + struct dwc_otg_hcd_iso_packet_desc *frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index];
- + /* Record errors, update qtd. */
- + if (st->nr_errors) {
- + frame_desc->actual_length = 0;
- + frame_desc->status = -DWC_E_PROTOCOL;
- + } else {
- + frame_desc->status = 0;
- + frame_desc->actual_length = frame_desc->length;
- + }
- + qtd->isoc_frame_index++;
- + qtd->isoc_split_offset = 0;
- + if (qtd->isoc_frame_index == qtd->urb->packet_count) {
- + hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0);
- + release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
- + } else {
- + release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE);
- + }
- + }
- + break;
- +
- + case FIQ_PER_SPLIT_NYET_ABORTED:
- + /* Doh. lost the data. */
- + printk_ratelimited(KERN_INFO "Transfer to device %d endpoint 0x%x frame %d failed "
- + "- FIQ reported NYET. Data may have been lost.\n",
- + hc->dev_addr, hc->ep_num, dwc_otg_hcd_get_frame_number(hcd) >> 3);
- + if (hc->ep_type == UE_ISOCHRONOUS) {
- + struct dwc_otg_hcd_iso_packet_desc *frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index];
- + /* Record errors, update qtd. */
- + frame_desc->actual_length = 0;
- + frame_desc->status = -DWC_E_PROTOCOL;
- + qtd->isoc_frame_index++;
- + qtd->isoc_split_offset = 0;
- + if (qtd->isoc_frame_index == qtd->urb->packet_count) {
- + hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0);
- + release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
- + } else {
- + release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE);
- + }
- + } else {
- + release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS);
- + }
- + break;
- +
- + case FIQ_HS_ISOC_DONE:
- + /* The FIQ has performed a whole pile of isochronous transactions.
- + * The status is recorded as the interrupt state should the transaction
- + * fail.
- + */
- + dwc_otg_fiq_unmangle_isoc(hcd, qh, qtd, num);
- + break;
- +
- + case FIQ_PER_SPLIT_LS_ABORTED:
- + if (hcint.b.xacterr) {
- + /* Hub has responded with an ERR packet. Device
- + * has been unplugged or the port has been disabled.
- + * TODO: need to issue a reset to the hub port. */
- + qtd->error_count += 3;
- + handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd);
- + } else if (hcint.b.stall) {
- + handle_hc_stall_intr(hcd, hc, hc_regs, qtd);
- + } else if (hcint.b.bblerr) {
- + handle_hc_babble_intr(hcd, hc, hc_regs, qtd);
- + } else {
- + printk_ratelimited(KERN_INFO "Transfer to device %d endpoint 0x%x failed "
- + "- FIQ reported FSM=%d. Data may have been lost.\n",
- + st->fsm, hc->dev_addr, hc->ep_num);
- + release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS);
- + }
- + break;
- +
- + case FIQ_PER_SPLIT_HS_ABORTED:
- + /* Either the SSPLIT phase suffered transaction errors or something
- + * unexpected happened.
- + */
- + qtd->error_count += 3;
- + handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd);
- + release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS);
- + break;
- +
- + case FIQ_PER_SPLIT_TIMEOUT:
- + /* Couldn't complete in the nominated frame */
- + printk(KERN_INFO "Transfer to device %d endpoint 0x%x frame %d failed "
- + "- FIQ timed out. Data may have been lost.\n",
- + hc->dev_addr, hc->ep_num, dwc_otg_hcd_get_frame_number(hcd) >> 3);
- + if (hc->ep_type == UE_ISOCHRONOUS) {
- + struct dwc_otg_hcd_iso_packet_desc *frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index];
- + /* Record errors, update qtd. */
- + frame_desc->actual_length = 0;
- + if (hc->ep_is_in) {
- + frame_desc->status = -DWC_E_NO_STREAM_RES;
- + } else {
- + frame_desc->status = -DWC_E_COMMUNICATION;
- + }
- + qtd->isoc_frame_index++;
- + if (qtd->isoc_frame_index == qtd->urb->packet_count) {
- + hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0);
- + release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
- + } else {
- + release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE);
- + }
- + } else {
- + release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS);
- + }
- + break;
- +
- + default:
- + local_fiq_disable();
- + DWC_WARN("unexpected state received on hc=%d fsm=%d", hc->hc_num, st->fsm);
- + BUG();
- + }
- + //if (hostchannels != hcd->available_host_channels) {
- + /* should have incremented by now! */
- + // BUG();
- +// }
- + return ret;
- +}
- +
- /** Handles interrupt for a specific Host Channel */
- int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num)
- {
- int retval = 0;
- - hcint_data_t hcint, hcint_orig;
- + hcint_data_t hcint;
- hcintmsk_data_t hcintmsk;
- dwc_hc_t *hc;
- dwc_otg_hc_regs_t *hc_regs;
- @@ -2668,24 +2607,32 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc
- }
- qtd = DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list);
-
- + /*
- + * FSM mode: Check to see if this is a HC interrupt from a channel handled by the FIQ.
- + * Execution path is fundamentally different for the channels after a FIQ has completed
- + * a split transaction.
- + */
- + if (fiq_fsm_enable) {
- + switch (dwc_otg_hcd->fiq_state->channel[num].fsm) {
- + case FIQ_PASSTHROUGH:
- + break;
- + case FIQ_PASSTHROUGH_ERRORSTATE:
- + /* Hook into the error count */
- + fiq_print(FIQDBG_ERR, dwc_otg_hcd->fiq_state, "HCDERR%02d", num);
- + if (dwc_otg_hcd->fiq_state->channel[num].nr_errors) {
- + qtd->error_count = 0;
- + fiq_print(FIQDBG_ERR, dwc_otg_hcd->fiq_state, "RESET ");
- + }
- + break;
- + default:
- + dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd, num);
- + return 1;
- + }
- + }
- +
- hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
- - hcint_orig = hcint;
- hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk);
- - DWC_DEBUGPL(DBG_HCDV,
- - " hcint 0x%08x, hcintmsk 0x%08x, hcint&hcintmsk 0x%08x\n",
- - hcint.d32, hcintmsk.d32, (hcint.d32 & hcintmsk.d32));
- hcint.d32 = hcint.d32 & hcintmsk.d32;
- -
- - if(fiq_split_enable)
- - {
- - // replace with the saved interrupts from the fiq handler
- - local_fiq_disable();
- - hcint_orig.d32 = hcint_saved[num].d32;
- - hcint.d32 = hcint_orig.d32 & hcintmsk_saved[num].d32;
- - hcint_saved[num].d32 = 0;
- - local_fiq_enable();
- - }
- -
- if (!dwc_otg_hcd->core_if->dma_enable) {
- if (hcint.b.chhltd && hcint.d32 != 0x2) {
- hcint.b.chhltd = 0;
- @@ -2703,7 +2650,7 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc
- hcint.b.nyet = 0;
- }
- if (hcint.b.chhltd) {
- - retval |= handle_hc_chhltd_intr(dwc_otg_hcd, hc, hc_regs, qtd, hcint_orig, hcintmsk_saved[num]);
- + retval |= handle_hc_chhltd_intr(dwc_otg_hcd, hc, hc_regs, qtd);
- }
- if (hcint.b.ahberr) {
- retval |= handle_hc_ahberr_intr(dwc_otg_hcd, hc, hc_regs, qtd);
- --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c
- @@ -58,6 +58,7 @@
- #else
- #include <linux/usb/hcd.h>
- #endif
- +#include <asm/bug.h>
-
- #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30))
- #define USB_URB_EP_LINKING 1
- @@ -69,7 +70,8 @@
- #include "dwc_otg_dbg.h"
- #include "dwc_otg_driver.h"
- #include "dwc_otg_hcd.h"
- -#include "dwc_otg_mphi_fix.h"
- +
- +extern unsigned char _dwc_otg_fiq_stub, _dwc_otg_fiq_stub_end;
-
- /**
- * Gets the endpoint number from a _bEndpointAddress argument. The endpoint is
- @@ -80,7 +82,7 @@
-
- static const char dwc_otg_hcd_name[] = "dwc_otg_hcd";
-
- -extern bool fiq_fix_enable;
- +extern bool fiq_enable;
-
- /** @name Linux HC Driver API Functions */
- /** @{ */
- @@ -351,7 +353,6 @@ static int _complete(dwc_otg_hcd_t * hcd
- urb);
- }
- }
- -
- DWC_FREE(dwc_otg_urb);
- if (!new_entry) {
- DWC_ERROR("dwc_otg_hcd: complete: cannot allocate URB TQ entry\n");
- @@ -395,13 +396,9 @@ static struct dwc_otg_hcd_function_ops h
- static struct fiq_handler fh = {
- .name = "usb_fiq",
- };
- -struct fiq_stack_s {
- - int magic1;
- - uint8_t stack[2048];
- - int magic2;
- -} fiq_stack;
-
- -extern mphi_regs_t c_mphi_regs;
- +
- +
- /**
- * Initializes the HCD. This function allocates memory for and initializes the
- * static parts of the usb_hcd and dwc_otg_hcd structures. It also registers the
- @@ -433,20 +430,6 @@ int hcd_init(dwc_bus_dev_t *_dev)
- pci_set_consistent_dma_mask(_dev, dmamask);
- #endif
-
- - if (fiq_fix_enable)
- - {
- - // Set up fiq
- - claim_fiq(&fh);
- - set_fiq_handler(__FIQ_Branch, 4);
- - memset(®s,0,sizeof(regs));
- - regs.ARM_r8 = (long)dwc_otg_hcd_handle_fiq;
- - regs.ARM_r9 = (long)0;
- - regs.ARM_sp = (long)fiq_stack.stack + sizeof(fiq_stack.stack) - 4;
- - set_fiq_regs(®s);
- - fiq_stack.magic1 = 0xdeadbeef;
- - fiq_stack.magic2 = 0xaa995566;
- - }
- -
- /*
- * Allocate memory for the base HCD plus the DWC OTG HCD.
- * Initialize the base HCD.
- @@ -466,30 +449,7 @@ int hcd_init(dwc_bus_dev_t *_dev)
-
- hcd->regs = otg_dev->os_dep.base;
-
- - if (fiq_fix_enable)
- - {
- - volatile extern void *dwc_regs_base;
- -
- - //Set the mphi periph to the required registers
- - c_mphi_regs.base = otg_dev->os_dep.mphi_base;
- - c_mphi_regs.ctrl = otg_dev->os_dep.mphi_base + 0x4c;
- - c_mphi_regs.outdda = otg_dev->os_dep.mphi_base + 0x28;
- - c_mphi_regs.outddb = otg_dev->os_dep.mphi_base + 0x2c;
- - c_mphi_regs.intstat = otg_dev->os_dep.mphi_base + 0x50;
- -
- - dwc_regs_base = otg_dev->os_dep.base;
-
- - //Enable mphi peripheral
- - writel((1<<31),c_mphi_regs.ctrl);
- -#ifdef DEBUG
- - if (readl(c_mphi_regs.ctrl) & 0x80000000)
- - DWC_DEBUGPL(DBG_USER, "MPHI periph has been enabled\n");
- - else
- - DWC_DEBUGPL(DBG_USER, "MPHI periph has NOT been enabled\n");
- -#endif
- - // Enable FIQ interrupt from USB peripheral
- - enable_fiq(INTERRUPT_VC_USB);
- - }
- /* Initialize the DWC OTG HCD. */
- dwc_otg_hcd = dwc_otg_hcd_alloc_hcd();
- if (!dwc_otg_hcd) {
- @@ -503,6 +463,55 @@ int hcd_init(dwc_bus_dev_t *_dev)
- goto error2;
- }
-
- + if (fiq_enable)
- + {
- + if (claim_fiq(&fh)) {
- + DWC_ERROR("Can't claim FIQ");
- + goto error2;
- + }
- +
- + DWC_WARN("FIQ at 0x%08x", (fiq_fsm_enable ? (int)&dwc_otg_fiq_fsm : (int)&dwc_otg_fiq_nop));
- + DWC_WARN("FIQ ASM at 0x%08x length %d", (int)&_dwc_otg_fiq_stub, (int)(&_dwc_otg_fiq_stub_end - &_dwc_otg_fiq_stub));
- +
- + set_fiq_handler((void *) &_dwc_otg_fiq_stub, &_dwc_otg_fiq_stub_end - &_dwc_otg_fiq_stub);
- + memset(®s,0,sizeof(regs));
- +
- + regs.ARM_r8 = (long) dwc_otg_hcd->fiq_state;
- + if (fiq_fsm_enable) {
- + regs.ARM_r9 = dwc_otg_hcd->core_if->core_params->host_channels;
- + //regs.ARM_r10 = dwc_otg_hcd->dma;
- + regs.ARM_fp = (long) dwc_otg_fiq_fsm;
- + } else {
- + regs.ARM_fp = (long) dwc_otg_fiq_nop;
- + }
- +
- + regs.ARM_sp = (long) dwc_otg_hcd->fiq_stack + (sizeof(struct fiq_stack) - 4);
- +
- +// __show_regs(®s);
- + set_fiq_regs(®s);
- +
- + //Set the mphi periph to the required registers
- + dwc_otg_hcd->fiq_state->mphi_regs.base = otg_dev->os_dep.mphi_base;
- + dwc_otg_hcd->fiq_state->mphi_regs.ctrl = otg_dev->os_dep.mphi_base + 0x4c;
- + dwc_otg_hcd->fiq_state->mphi_regs.outdda = otg_dev->os_dep.mphi_base + 0x28;
- + dwc_otg_hcd->fiq_state->mphi_regs.outddb = otg_dev->os_dep.mphi_base + 0x2c;
- + dwc_otg_hcd->fiq_state->mphi_regs.intstat = otg_dev->os_dep.mphi_base + 0x50;
- + dwc_otg_hcd->fiq_state->dwc_regs_base = otg_dev->os_dep.base;
- + DWC_WARN("MPHI regs_base at 0x%08x", (int)dwc_otg_hcd->fiq_state->mphi_regs.base);
- + //Enable mphi peripheral
- + writel((1<<31),dwc_otg_hcd->fiq_state->mphi_regs.ctrl);
- +#ifdef DEBUG
- + if (readl(dwc_otg_hcd->fiq_state->mphi_regs.ctrl) & 0x80000000)
- + DWC_WARN("MPHI periph has been enabled");
- + else
- + DWC_WARN("MPHI periph has NOT been enabled");
- +#endif
- + // Enable FIQ interrupt from USB peripheral
- + enable_fiq(INTERRUPT_VC_USB);
- + local_fiq_enable();
- + }
- +
- +
- otg_dev->hcd->otg_dev = otg_dev;
- hcd->self.otg_port = dwc_otg_hcd_otg_port(dwc_otg_hcd);
- #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33) //don't support for LM(with 2.6.20.1 kernel)
- @@ -518,9 +527,9 @@ int hcd_init(dwc_bus_dev_t *_dev)
- * IRQ line, and calls hcd_start method.
- */
- #ifdef PLATFORM_INTERFACE
- - retval = usb_add_hcd(hcd, platform_get_irq(_dev, 0), IRQF_SHARED | IRQF_DISABLED);
- + retval = usb_add_hcd(hcd, platform_get_irq(_dev, fiq_enable ? 0 : 1), IRQF_SHARED | IRQF_DISABLED);
- #else
- - retval = usb_add_hcd(hcd, _dev->irq, IRQF_SHARED | IRQF_DISABLED);
- + retval = usb_add_hcd(hcd, _dev->irq, IRQF_SHARED | IRQF_DISABLED);
- #endif
- if (retval < 0) {
- goto error2;
- @@ -617,9 +626,13 @@ void hcd_stop(struct usb_hcd *hcd)
- /** Returns the current frame number. */
- static int get_frame_number(struct usb_hcd *hcd)
- {
- + hprt0_data_t hprt0;
- dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
- -
- - return dwc_otg_hcd_get_frame_number(dwc_otg_hcd);
- + hprt0.d32 = DWC_READ_REG32(dwc_otg_hcd->core_if->host_if->hprt0);
- + if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED)
- + return dwc_otg_hcd_get_frame_number(dwc_otg_hcd) >> 3;
- + else
- + return dwc_otg_hcd_get_frame_number(dwc_otg_hcd);
- }
-
- #ifdef DEBUG
- --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c
- @@ -41,7 +41,6 @@
-
- #include "dwc_otg_hcd.h"
- #include "dwc_otg_regs.h"
- -#include "dwc_otg_mphi_fix.h"
-
- extern bool microframe_schedule;
-
- @@ -577,7 +576,6 @@ static int check_max_xfer_size(dwc_otg_h
- }
-
-
- -extern int g_next_sched_frame, g_np_count, g_np_sent;
-
- /**
- * Schedules an interrupt or isochronous transfer in the periodic schedule.
- @@ -637,9 +635,9 @@ static int schedule_periodic(dwc_otg_hcd
- DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_ready, &qh->qh_list_entry);
- }
- else {
- - if(DWC_LIST_EMPTY(&hcd->periodic_sched_inactive) || dwc_frame_num_le(qh->sched_frame, g_next_sched_frame))
- + if(fiq_enable && (DWC_LIST_EMPTY(&hcd->periodic_sched_inactive) || dwc_frame_num_le(qh->sched_frame, hcd->fiq_state->next_sched_frame)))
- {
- - g_next_sched_frame = qh->sched_frame;
- + hcd->fiq_state->next_sched_frame = qh->sched_frame;
-
- }
- /* Always start in the inactive schedule. */
- @@ -680,7 +678,7 @@ int dwc_otg_hcd_qh_add(dwc_otg_hcd_t * h
- /* Always start in the inactive schedule. */
- DWC_LIST_INSERT_TAIL(&hcd->non_periodic_sched_inactive,
- &qh->qh_list_entry);
- - g_np_count++;
- + //hcd->fiq_state->kick_np_queues = 1;
- } else {
- status = schedule_periodic(hcd, qh);
- if ( !hcd->periodic_qh_count ) {
- @@ -740,13 +738,12 @@ void dwc_otg_hcd_qh_remove(dwc_otg_hcd_t
- hcd->non_periodic_qh_ptr->next;
- }
- DWC_LIST_REMOVE_INIT(&qh->qh_list_entry);
- -
- - // If we've removed the last non-periodic entry then there are none left!
- - g_np_count = g_np_sent;
- + //if (!DWC_LIST_EMPTY(&hcd->non_periodic_sched_inactive))
- + // hcd->fiq_state->kick_np_queues = 1;
- } else {
- deschedule_periodic(hcd, qh);
- hcd->periodic_qh_count--;
- - if( !hcd->periodic_qh_count ) {
- + if( !hcd->periodic_qh_count && !fiq_fsm_enable ) {
- intr_mask.b.sofintr = 1;
- DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk,
- intr_mask.d32, 0);
- @@ -771,28 +768,11 @@ void dwc_otg_hcd_qh_deactivate(dwc_otg_h
- int sched_next_periodic_split)
- {
- if (dwc_qh_is_non_per(qh)) {
- -
- - dwc_otg_qh_t *qh_tmp;
- - dwc_list_link_t *qh_list;
- - DWC_LIST_FOREACH(qh_list, &hcd->non_periodic_sched_inactive)
- - {
- - qh_tmp = DWC_LIST_ENTRY(qh_list, struct dwc_otg_qh, qh_list_entry);
- - if(qh_tmp == qh)
- - {
- - /*
- - * FIQ is being disabled because this one nevers gets a np_count increment
- - * This is still not absolutely correct, but it should fix itself with
- - * just an unnecessary extra interrupt
- - */
- - g_np_sent = g_np_count;
- - }
- - }
- -
- -
- dwc_otg_hcd_qh_remove(hcd, qh);
- if (!DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) {
- /* Add back to inactive non-periodic schedule. */
- dwc_otg_hcd_qh_add(hcd, qh);
- + //hcd->fiq_state->kick_np_queues = 1;
- }
- } else {
- uint16_t frame_number = dwc_otg_hcd_get_frame_number(hcd);
- @@ -851,9 +831,9 @@ void dwc_otg_hcd_qh_deactivate(dwc_otg_h
- DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready,
- &qh->qh_list_entry);
- } else {
- - if(!dwc_frame_num_le(g_next_sched_frame, qh->sched_frame))
- + if(fiq_enable && !dwc_frame_num_le(hcd->fiq_state->next_sched_frame, qh->sched_frame))
- {
- - g_next_sched_frame = qh->sched_frame;
- + hcd->fiq_state->next_sched_frame = qh->sched_frame;
- }
-
- DWC_LIST_MOVE_HEAD
- @@ -944,6 +924,9 @@ int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t *
- if (*qh == NULL) {
- retval = -DWC_E_NO_MEMORY;
- goto done;
- + } else {
- + if (fiq_enable)
- + hcd->fiq_state->kick_np_queues = 1;
- }
- }
- retval = dwc_otg_hcd_qh_add(hcd, *qh);
- --- a/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c
- +++ /dev/null
- @@ -1,113 +0,0 @@
- -#include "dwc_otg_regs.h"
- -#include "dwc_otg_dbg.h"
- -
- -void dwc_debug_print_core_int_reg(gintsts_data_t gintsts, const char* function_name)
- -{
- - DWC_DEBUGPL(DBG_USER, "*** Debugging from within the %s function: ***\n"
- - "curmode: %1i Modemismatch: %1i otgintr: %1i sofintr: %1i\n"
- - "rxstsqlvl: %1i nptxfempty : %1i ginnakeff: %1i goutnakeff: %1i\n"
- - "ulpickint: %1i i2cintr: %1i erlysuspend:%1i usbsuspend: %1i\n"
- - "usbreset: %1i enumdone: %1i isooutdrop: %1i eopframe: %1i\n"
- - "restoredone: %1i epmismatch: %1i inepint: %1i outepintr: %1i\n"
- - "incomplisoin:%1i incomplisoout:%1i fetsusp: %1i resetdet: %1i\n"
- - "portintr: %1i hcintr: %1i ptxfempty: %1i lpmtranrcvd:%1i\n"
- - "conidstschng:%1i disconnect: %1i sessreqintr:%1i wkupintr: %1i\n",
- - function_name,
- - gintsts.b.curmode,
- - gintsts.b.modemismatch,
- - gintsts.b.otgintr,
- - gintsts.b.sofintr,
- - gintsts.b.rxstsqlvl,
- - gintsts.b.nptxfempty,
- - gintsts.b.ginnakeff,
- - gintsts.b.goutnakeff,
- - gintsts.b.ulpickint,
- - gintsts.b.i2cintr,
- - gintsts.b.erlysuspend,
- - gintsts.b.usbsuspend,
- - gintsts.b.usbreset,
- - gintsts.b.enumdone,
- - gintsts.b.isooutdrop,
- - gintsts.b.eopframe,
- - gintsts.b.restoredone,
- - gintsts.b.epmismatch,
- - gintsts.b.inepint,
- - gintsts.b.outepintr,
- - gintsts.b.incomplisoin,
- - gintsts.b.incomplisoout,
- - gintsts.b.fetsusp,
- - gintsts.b.resetdet,
- - gintsts.b.portintr,
- - gintsts.b.hcintr,
- - gintsts.b.ptxfempty,
- - gintsts.b.lpmtranrcvd,
- - gintsts.b.conidstschng,
- - gintsts.b.disconnect,
- - gintsts.b.sessreqintr,
- - gintsts.b.wkupintr);
- - return;
- -}
- -
- -void dwc_debug_core_int_mask(gintmsk_data_t gintmsk, const char* function_name)
- -{
- - DWC_DEBUGPL(DBG_USER, "Interrupt Mask status (called from %s) :\n"
- - "modemismatch: %1i otgintr: %1i sofintr: %1i rxstsqlvl: %1i\n"
- - "nptxfempty: %1i ginnakeff: %1i goutnakeff: %1i ulpickint: %1i\n"
- - "i2cintr: %1i erlysuspend:%1i usbsuspend: %1i usbreset: %1i\n"
- - "enumdone: %1i isooutdrop: %1i eopframe: %1i restoredone: %1i\n"
- - "epmismatch: %1i inepintr: %1i outepintr: %1i incomplisoin:%1i\n"
- - "incomplisoout:%1i fetsusp: %1i resetdet: %1i portintr: %1i\n"
- - "hcintr: %1i ptxfempty: %1i lpmtranrcvd:%1i conidstschng:%1i\n"
- - "disconnect: %1i sessreqintr:%1i wkupintr: %1i\n",
- - function_name,
- - gintmsk.b.modemismatch,
- - gintmsk.b.otgintr,
- - gintmsk.b.sofintr,
- - gintmsk.b.rxstsqlvl,
- - gintmsk.b.nptxfempty,
- - gintmsk.b.ginnakeff,
- - gintmsk.b.goutnakeff,
- - gintmsk.b.ulpickint,
- - gintmsk.b.i2cintr,
- - gintmsk.b.erlysuspend,
- - gintmsk.b.usbsuspend,
- - gintmsk.b.usbreset,
- - gintmsk.b.enumdone,
- - gintmsk.b.isooutdrop,
- - gintmsk.b.eopframe,
- - gintmsk.b.restoredone,
- - gintmsk.b.epmismatch,
- - gintmsk.b.inepintr,
- - gintmsk.b.outepintr,
- - gintmsk.b.incomplisoin,
- - gintmsk.b.incomplisoout,
- - gintmsk.b.fetsusp,
- - gintmsk.b.resetdet,
- - gintmsk.b.portintr,
- - gintmsk.b.hcintr,
- - gintmsk.b.ptxfempty,
- - gintmsk.b.lpmtranrcvd,
- - gintmsk.b.conidstschng,
- - gintmsk.b.disconnect,
- - gintmsk.b.sessreqintr,
- - gintmsk.b.wkupintr);
- - return;
- -}
- -
- -void dwc_debug_otg_int(gotgint_data_t gotgint, const char* function_name)
- -{
- - DWC_DEBUGPL(DBG_USER, "otg int register (from %s function):\n"
- - "sesenddet:%1i sesreqsucstschung:%2i hstnegsucstschng:%1i\n"
- - "hstnegdet:%1i adevtoutchng: %2i debdone: %1i\n"
- - "mvic: %1i\n",
- - function_name,
- - gotgint.b.sesenddet,
- - gotgint.b.sesreqsucstschng,
- - gotgint.b.hstnegsucstschng,
- - gotgint.b.hstnegdet,
- - gotgint.b.adevtoutchng,
- - gotgint.b.debdone,
- - gotgint.b.mvic);
- -
- - return;
- -}
- --- a/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h
- +++ /dev/null
- @@ -1,48 +0,0 @@
- -#ifndef __DWC_OTG_MPHI_FIX_H__
- -#define __DWC_OTG_MPHI_FIX_H__
- -#define FIQ_WRITE(_addr_,_data_) (*(volatile uint32_t *) (_addr_) = (_data_))
- -#define FIQ_READ(_addr_) (*(volatile uint32_t *) (_addr_))
- -
- -typedef struct {
- - volatile void* base;
- - volatile void* ctrl;
- - volatile void* outdda;
- - volatile void* outddb;
- - volatile void* intstat;
- -} mphi_regs_t;
- -
- -void dwc_debug_print_core_int_reg(gintsts_data_t gintsts, const char* function_name);
- -void dwc_debug_core_int_mask(gintsts_data_t gintmsk, const char* function_name);
- -void dwc_debug_otg_int(gotgint_data_t gotgint, const char* function_name);
- -
- -extern gintsts_data_t gintsts_saved;
- -
- -#ifdef DEBUG
- -#define DWC_DBG_PRINT_CORE_INT(_arg_) dwc_debug_print_core_int_reg(_arg_,__func__)
- -#define DWC_DBG_PRINT_CORE_INT_MASK(_arg_) dwc_debug_core_int_mask(_arg_,__func__)
- -#define DWC_DBG_PRINT_OTG_INT(_arg_) dwc_debug_otg_int(_arg_,__func__)
- -
- -#else
- -#define DWC_DBG_PRINT_CORE_INT(_arg_)
- -#define DWC_DBG_PRINT_CORE_INT_MASK(_arg_)
- -#define DWC_DBG_PRINT_OTG_INT(_arg_)
- -
- -#endif
- -
- -typedef enum {
- - FIQDBG_SCHED = (1 << 0),
- - FIQDBG_INT = (1 << 1),
- - FIQDBG_ERR = (1 << 2),
- - FIQDBG_PORTHUB = (1 << 3),
- -} FIQDBG_T;
- -
- -void _fiq_print(FIQDBG_T dbg_lvl, char *fmt, ...);
- -#ifdef FIQ_DEBUG
- -#define fiq_print _fiq_print
- -#else
- -#define fiq_print(x, y, ...)
- -#endif
- -
- -extern bool fiq_fix_enable, nak_holdoff_enable, fiq_split_enable;
- -
- -#endif
- --- a/drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c
- @@ -59,6 +59,8 @@
- #include "dwc_otg_driver.h"
- #include "dwc_otg_dbg.h"
-
- +extern bool fiq_enable;
- +
- static struct gadget_wrapper {
- dwc_otg_pcd_t *pcd;
-
- @@ -1222,13 +1224,13 @@ int pcd_init(dwc_bus_dev_t *_dev)
- */
- #ifdef PLATFORM_INTERFACE
- DWC_DEBUGPL(DBG_ANY, "registering handler for irq%d\n",
- - platform_get_irq(_dev, 0));
- - retval = request_irq(platform_get_irq(_dev, 0), dwc_otg_pcd_irq,
- + platform_get_irq(_dev, fiq_enable ? 0 : 1));
- + retval = request_irq(platform_get_irq(_dev, fiq_enable ? 0 : 1), dwc_otg_pcd_irq,
- IRQF_SHARED, gadget_wrapper->gadget.name,
- otg_dev->pcd);
- if (retval != 0) {
- DWC_ERROR("request of irq%d failed\n",
- - platform_get_irq(_dev, 0));
- + platform_get_irq(_dev, fiq_enable ? 0 : 1));
- free_wrapper(gadget_wrapper);
- return -EBUSY;
- }
|