| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024140251402614027140281402914030140311403214033140341403514036140371403814039140401404114042140431404414045140461404714048140491405014051140521405314054140551405614057140581405914060140611406214063140641406514066140671406814069140701407114072140731407414075140761407714078140791408014081140821408314084140851408614087140881408914090140911409214093140941409514096140971409814099141001410114102141031410414105141061410714108141091411014111141121411314114141151411614117141181411914120141211412214123141241412514126141271412814129141301413114132141331413414135141361413714138141391414014141141421414314144141451414614147141481414914150141511415214153141541415514156141571415814159141601416114162141631416414165141661416714168141691417014171141721417314174141751417614177141781417914180141811418214183141841418514186141871418814189141901419114192141931419414195141961419714198141991420014201142021420314204142051420614207142081420914210142111421214213142141421514216142171421814219142201422114222142231422414225142261422714228142291423014231142321423314234142351423614237142381423914240142411424214243142441424514246142471424814249142501425114252142531425414255142561425714258142591426014261142621426314264142651426614267142681426914270142711427214273142741427514276142771427814279142801428114282142831428414285142861428714288142891429014291142921429314294142951429614297142981429914300143011430214303143041430514306143071430814309143101431114312143131431414315143161431714318143191432014321143221432314324143251432614327143281432914330143311433214333143341433514336143371433814339143401434114342143431434414345143461434714348143491435014351143521435314354143551435614357143581435914360143611436214363143641436514366143671436814369143701437114372143731437414375143761437714378143791438014381143821438314384143851438614387143881438914390143911439214393143941439514396143971439814399144001440114402144031440414405144061440714408144091441014411144121441314414144151441614417144181441914420144211442214423144241442514426144271442814429144301443114432144331443414435144361443714438144391444014441144421444314444144451444614447144481444914450144511445214453144541445514456144571445814459144601446114462144631446414465144661446714468144691447014471144721447314474144751447614477144781447914480144811448214483144841448514486144871448814489144901449114492144931449414495144961449714498144991450014501145021450314504145051450614507145081450914510145111451214513145141451514516145171451814519145201452114522145231452414525145261452714528145291453014531145321453314534145351453614537145381453914540145411454214543145441454514546145471454814549145501455114552145531455414555145561455714558145591456014561145621456314564145651456614567145681456914570145711457214573145741457514576145771457814579145801458114582145831458414585145861458714588145891459014591145921459314594145951459614597145981459914600146011460214603146041460514606146071460814609146101461114612146131461414615146161461714618146191462014621146221462314624146251462614627146281462914630146311463214633146341463514636146371463814639146401464114642146431464414645146461464714648146491465014651146521465314654146551465614657146581465914660146611466214663146641466514666146671466814669146701467114672146731467414675146761467714678146791468014681146821468314684146851468614687146881468914690146911469214693146941469514696146971469814699147001470114702147031470414705147061470714708147091471014711147121471314714147151471614717147181471914720147211472214723147241472514726147271472814729147301473114732147331473414735147361473714738147391474014741147421474314744147451474614747147481474914750147511475214753147541475514756147571475814759147601476114762147631476414765147661476714768147691477014771147721477314774147751477614777147781477914780147811478214783147841478514786147871478814789147901479114792147931479414795147961479714798147991480014801148021480314804148051480614807148081480914810148111481214813148141481514816148171481814819148201482114822148231482414825148261482714828148291483014831148321483314834148351483614837148381483914840148411484214843148441484514846148471484814849148501485114852148531485414855148561485714858148591486014861148621486314864148651486614867148681486914870148711487214873148741487514876148771487814879148801488114882148831488414885148861488714888148891489014891148921489314894148951489614897148981489914900149011490214903149041490514906149071490814909149101491114912149131491414915149161491714918149191492014921149221492314924149251492614927149281492914930149311493214933149341493514936149371493814939149401494114942149431494414945149461494714948149491495014951149521495314954149551495614957149581495914960149611496214963149641496514966149671496814969149701497114972149731497414975149761497714978149791498014981149821498314984149851498614987149881498914990149911499214993149941499514996149971499814999150001500115002150031500415005150061500715008150091501015011150121501315014150151501615017150181501915020150211502215023150241502515026150271502815029150301503115032150331503415035150361503715038150391504015041150421504315044150451504615047150481504915050150511505215053150541505515056150571505815059150601506115062150631506415065150661506715068150691507015071150721507315074150751507615077150781507915080150811508215083150841508515086150871508815089150901509115092150931509415095150961509715098150991510015101151021510315104151051510615107151081510915110151111511215113151141511515116151171511815119151201512115122151231512415125151261512715128151291513015131151321513315134151351513615137151381513915140151411514215143151441514515146151471514815149151501515115152151531515415155151561515715158151591516015161151621516315164151651516615167151681516915170151711517215173151741517515176151771517815179151801518115182151831518415185151861518715188151891519015191151921519315194151951519615197151981519915200152011520215203152041520515206152071520815209152101521115212152131521415215152161521715218152191522015221152221522315224152251522615227152281522915230152311523215233152341523515236152371523815239152401524115242152431524415245152461524715248152491525015251152521525315254152551525615257152581525915260152611526215263152641526515266152671526815269152701527115272152731527415275152761527715278152791528015281152821528315284152851528615287152881528915290152911529215293152941529515296152971529815299153001530115302153031530415305153061530715308153091531015311153121531315314153151531615317153181531915320153211532215323153241532515326153271532815329153301533115332153331533415335153361533715338153391534015341153421534315344153451534615347153481534915350153511535215353153541535515356153571535815359153601536115362153631536415365153661536715368153691537015371153721537315374153751537615377153781537915380153811538215383153841538515386153871538815389153901539115392153931539415395153961539715398153991540015401154021540315404154051540615407154081540915410154111541215413154141541515416154171541815419154201542115422154231542415425154261542715428154291543015431154321543315434154351543615437154381543915440154411544215443154441544515446154471544815449154501545115452154531545415455154561545715458154591546015461154621546315464154651546615467154681546915470154711547215473154741547515476154771547815479154801548115482154831548415485154861548715488154891549015491154921549315494154951549615497154981549915500155011550215503155041550515506155071550815509155101551115512155131551415515155161551715518155191552015521155221552315524155251552615527155281552915530155311553215533155341553515536155371553815539155401554115542155431554415545155461554715548155491555015551155521555315554155551555615557155581555915560155611556215563155641556515566155671556815569155701557115572155731557415575155761557715578155791558015581155821558315584155851558615587155881558915590155911559215593155941559515596155971559815599156001560115602156031560415605156061560715608156091561015611156121561315614156151561615617156181561915620156211562215623156241562515626156271562815629156301563115632156331563415635156361563715638156391564015641156421564315644156451564615647156481564915650156511565215653156541565515656156571565815659156601566115662156631566415665156661566715668156691567015671156721567315674156751567615677156781567915680156811568215683156841568515686156871568815689156901569115692156931569415695156961569715698156991570015701157021570315704157051570615707157081570915710157111571215713157141571515716157171571815719157201572115722157231572415725157261572715728157291573015731157321573315734157351573615737157381573915740157411574215743157441574515746157471574815749157501575115752157531575415755157561575715758157591576015761157621576315764157651576615767157681576915770157711577215773157741577515776157771577815779157801578115782157831578415785157861578715788157891579015791157921579315794157951579615797157981579915800158011580215803158041580515806158071580815809158101581115812158131581415815158161581715818158191582015821158221582315824158251582615827158281582915830158311583215833158341583515836158371583815839158401584115842158431584415845158461584715848158491585015851158521585315854158551585615857158581585915860158611586215863158641586515866158671586815869158701587115872158731587415875158761587715878158791588015881158821588315884158851588615887158881588915890158911589215893158941589515896158971589815899159001590115902159031590415905159061590715908159091591015911159121591315914159151591615917159181591915920159211592215923159241592515926159271592815929159301593115932159331593415935159361593715938159391594015941159421594315944159451594615947159481594915950159511595215953159541595515956159571595815959159601596115962159631596415965159661596715968159691597015971159721597315974159751597615977159781597915980159811598215983159841598515986159871598815989159901599115992159931599415995159961599715998159991600016001160021600316004160051600616007160081600916010160111601216013160141601516016160171601816019160201602116022160231602416025160261602716028160291603016031160321603316034160351603616037160381603916040160411604216043160441604516046160471604816049160501605116052160531605416055160561605716058160591606016061160621606316064160651606616067160681606916070160711607216073160741607516076160771607816079160801608116082160831608416085160861608716088160891609016091160921609316094160951609616097160981609916100161011610216103161041610516106161071610816109161101611116112161131611416115161161611716118161191612016121161221612316124161251612616127161281612916130161311613216133161341613516136161371613816139161401614116142161431614416145161461614716148161491615016151161521615316154161551615616157161581615916160161611616216163161641616516166161671616816169161701617116172161731617416175161761617716178161791618016181161821618316184161851618616187161881618916190161911619216193161941619516196161971619816199162001620116202162031620416205162061620716208162091621016211162121621316214162151621616217162181621916220162211622216223162241622516226162271622816229162301623116232162331623416235162361623716238162391624016241162421624316244162451624616247162481624916250162511625216253162541625516256162571625816259162601626116262162631626416265162661626716268162691627016271162721627316274162751627616277162781627916280162811628216283162841628516286162871628816289162901629116292162931629416295162961629716298162991630016301163021630316304163051630616307163081630916310163111631216313163141631516316163171631816319163201632116322163231632416325163261632716328163291633016331163321633316334163351633616337163381633916340163411634216343163441634516346163471634816349163501635116352163531635416355163561635716358163591636016361163621636316364163651636616367163681636916370163711637216373163741637516376163771637816379163801638116382163831638416385163861638716388163891639016391163921639316394163951639616397163981639916400164011640216403164041640516406164071640816409164101641116412164131641416415164161641716418164191642016421164221642316424164251642616427164281642916430164311643216433164341643516436164371643816439164401644116442164431644416445164461644716448164491645016451164521645316454164551645616457164581645916460164611646216463164641646516466164671646816469164701647116472164731647416475164761647716478164791648016481164821648316484164851648616487164881648916490164911649216493164941649516496164971649816499165001650116502165031650416505165061650716508165091651016511165121651316514165151651616517165181651916520165211652216523165241652516526165271652816529165301653116532165331653416535165361653716538165391654016541165421654316544165451654616547165481654916550165511655216553165541655516556165571655816559165601656116562165631656416565165661656716568165691657016571165721657316574165751657616577165781657916580165811658216583165841658516586165871658816589165901659116592165931659416595165961659716598165991660016601166021660316604166051660616607166081660916610166111661216613166141661516616166171661816619166201662116622166231662416625166261662716628166291663016631166321663316634166351663616637166381663916640166411664216643166441664516646166471664816649166501665116652166531665416655166561665716658166591666016661166621666316664166651666616667166681666916670166711667216673166741667516676166771667816679166801668116682166831668416685166861668716688166891669016691166921669316694166951669616697166981669916700167011670216703167041670516706167071670816709167101671116712167131671416715167161671716718167191672016721167221672316724167251672616727167281672916730167311673216733167341673516736167371673816739167401674116742167431674416745167461674716748167491675016751167521675316754167551675616757167581675916760167611676216763167641676516766167671676816769167701677116772167731677416775167761677716778167791678016781167821678316784167851678616787167881678916790167911679216793167941679516796167971679816799168001680116802168031680416805168061680716808168091681016811168121681316814168151681616817168181681916820168211682216823168241682516826168271682816829168301683116832168331683416835168361683716838168391684016841168421684316844168451684616847168481684916850168511685216853168541685516856168571685816859168601686116862168631686416865168661686716868168691687016871168721687316874168751687616877168781687916880168811688216883168841688516886168871688816889168901689116892168931689416895168961689716898168991690016901169021690316904169051690616907169081690916910169111691216913169141691516916169171691816919169201692116922169231692416925169261692716928169291693016931169321693316934169351693616937169381693916940169411694216943169441694516946169471694816949169501695116952169531695416955169561695716958169591696016961169621696316964169651696616967169681696916970169711697216973169741697516976169771697816979169801698116982169831698416985169861698716988169891699016991169921699316994169951699616997169981699917000170011700217003170041700517006170071700817009170101701117012170131701417015170161701717018170191702017021170221702317024170251702617027170281702917030170311703217033170341703517036170371703817039170401704117042170431704417045170461704717048170491705017051170521705317054170551705617057170581705917060170611706217063170641706517066170671706817069170701707117072170731707417075170761707717078170791708017081170821708317084170851708617087170881708917090170911709217093170941709517096170971709817099171001710117102171031710417105171061710717108171091711017111171121711317114171151711617117171181711917120171211712217123171241712517126171271712817129171301713117132171331713417135171361713717138171391714017141171421714317144171451714617147171481714917150171511715217153171541715517156171571715817159171601716117162171631716417165171661716717168171691717017171171721717317174171751717617177171781717917180171811718217183171841718517186171871718817189171901719117192171931719417195171961719717198171991720017201172021720317204172051720617207172081720917210172111721217213172141721517216172171721817219172201722117222172231722417225172261722717228172291723017231172321723317234172351723617237172381723917240172411724217243172441724517246172471724817249172501725117252172531725417255172561725717258172591726017261172621726317264172651726617267172681726917270172711727217273172741727517276172771727817279172801728117282172831728417285172861728717288172891729017291172921729317294172951729617297172981729917300173011730217303173041730517306173071730817309173101731117312173131731417315173161731717318173191732017321173221732317324173251732617327173281732917330173311733217333173341733517336173371733817339173401734117342173431734417345173461734717348173491735017351173521735317354173551735617357173581735917360173611736217363173641736517366173671736817369173701737117372173731737417375173761737717378173791738017381173821738317384173851738617387173881738917390173911739217393173941739517396173971739817399174001740117402174031740417405174061740717408174091741017411174121741317414174151741617417174181741917420174211742217423174241742517426174271742817429174301743117432174331743417435174361743717438174391744017441174421744317444174451744617447174481744917450174511745217453174541745517456174571745817459174601746117462174631746417465174661746717468174691747017471174721747317474174751747617477174781747917480174811748217483174841748517486174871748817489174901749117492174931749417495174961749717498174991750017501175021750317504175051750617507175081750917510175111751217513175141751517516175171751817519175201752117522175231752417525175261752717528175291753017531175321753317534175351753617537175381753917540175411754217543175441754517546175471754817549175501755117552175531755417555175561755717558175591756017561175621756317564175651756617567175681756917570175711757217573175741757517576175771757817579175801758117582175831758417585175861758717588175891759017591175921759317594175951759617597175981759917600176011760217603176041760517606176071760817609176101761117612176131761417615176161761717618176191762017621176221762317624176251762617627176281762917630176311763217633176341763517636176371763817639176401764117642176431764417645176461764717648176491765017651176521765317654176551765617657176581765917660176611766217663176641766517666176671766817669176701767117672176731767417675176761767717678176791768017681176821768317684176851768617687176881768917690176911769217693176941769517696176971769817699177001770117702177031770417705177061770717708177091771017711177121771317714177151771617717177181771917720177211772217723177241772517726177271772817729177301773117732177331773417735177361773717738177391774017741177421774317744177451774617747177481774917750177511775217753177541775517756177571775817759177601776117762177631776417765177661776717768177691777017771177721777317774177751777617777177781777917780177811778217783177841778517786177871778817789177901779117792177931779417795177961779717798177991780017801178021780317804178051780617807178081780917810178111781217813178141781517816178171781817819178201782117822178231782417825178261782717828178291783017831178321783317834178351783617837178381783917840178411784217843178441784517846178471784817849178501785117852178531785417855178561785717858178591786017861178621786317864178651786617867178681786917870178711787217873178741787517876178771787817879178801788117882178831788417885178861788717888178891789017891178921789317894178951789617897178981789917900179011790217903179041790517906179071790817909179101791117912179131791417915179161791717918179191792017921179221792317924179251792617927179281792917930179311793217933179341793517936179371793817939179401794117942179431794417945179461794717948179491795017951179521795317954179551795617957179581795917960179611796217963179641796517966179671796817969179701797117972179731797417975179761797717978179791798017981179821798317984179851798617987179881798917990179911799217993179941799517996179971799817999180001800118002180031800418005180061800718008180091801018011180121801318014180151801618017180181801918020180211802218023180241802518026180271802818029180301803118032180331803418035180361803718038180391804018041180421804318044180451804618047180481804918050180511805218053180541805518056180571805818059180601806118062180631806418065180661806718068180691807018071180721807318074180751807618077180781807918080180811808218083180841808518086180871808818089180901809118092180931809418095180961809718098180991810018101181021810318104181051810618107181081810918110181111811218113181141811518116181171811818119181201812118122181231812418125181261812718128181291813018131181321813318134181351813618137181381813918140181411814218143181441814518146181471814818149181501815118152181531815418155181561815718158181591816018161181621816318164181651816618167181681816918170181711817218173181741817518176181771817818179181801818118182181831818418185181861818718188181891819018191181921819318194181951819618197181981819918200182011820218203182041820518206182071820818209182101821118212182131821418215182161821718218182191822018221182221822318224182251822618227182281822918230182311823218233182341823518236182371823818239182401824118242182431824418245182461824718248182491825018251182521825318254182551825618257182581825918260182611826218263182641826518266182671826818269182701827118272182731827418275182761827718278182791828018281182821828318284182851828618287182881828918290182911829218293182941829518296182971829818299183001830118302183031830418305183061830718308183091831018311183121831318314183151831618317183181831918320183211832218323183241832518326183271832818329183301833118332183331833418335183361833718338183391834018341183421834318344183451834618347183481834918350183511835218353183541835518356183571835818359183601836118362183631836418365183661836718368183691837018371183721837318374183751837618377183781837918380183811838218383183841838518386183871838818389183901839118392183931839418395183961839718398183991840018401184021840318404184051840618407184081840918410184111841218413184141841518416184171841818419184201842118422184231842418425184261842718428184291843018431184321843318434184351843618437184381843918440184411844218443184441844518446184471844818449184501845118452184531845418455184561845718458184591846018461184621846318464184651846618467184681846918470184711847218473184741847518476184771847818479184801848118482184831848418485184861848718488184891849018491184921849318494184951849618497184981849918500185011850218503185041850518506185071850818509185101851118512185131851418515185161851718518185191852018521185221852318524185251852618527185281852918530185311853218533185341853518536185371853818539185401854118542185431854418545185461854718548185491855018551185521855318554185551855618557185581855918560185611856218563185641856518566185671856818569185701857118572185731857418575185761857718578185791858018581185821858318584185851858618587185881858918590185911859218593185941859518596185971859818599186001860118602186031860418605186061860718608186091861018611186121861318614186151861618617186181861918620186211862218623186241862518626186271862818629186301863118632186331863418635186361863718638186391864018641186421864318644186451864618647186481864918650186511865218653186541865518656186571865818659186601866118662186631866418665186661866718668186691867018671186721867318674186751867618677186781867918680186811868218683186841868518686186871868818689186901869118692186931869418695186961869718698186991870018701187021870318704187051870618707187081870918710187111871218713187141871518716187171871818719187201872118722187231872418725187261872718728187291873018731187321873318734187351873618737187381873918740187411874218743187441874518746187471874818749187501875118752187531875418755187561875718758187591876018761187621876318764187651876618767187681876918770187711877218773187741877518776187771877818779187801878118782187831878418785187861878718788187891879018791187921879318794187951879618797187981879918800188011880218803188041880518806188071880818809188101881118812188131881418815188161881718818188191882018821188221882318824188251882618827188281882918830188311883218833188341883518836188371883818839188401884118842188431884418845188461884718848188491885018851188521885318854188551885618857188581885918860188611886218863188641886518866188671886818869188701887118872188731887418875188761887718878188791888018881188821888318884188851888618887188881888918890188911889218893188941889518896188971889818899189001890118902189031890418905189061890718908189091891018911189121891318914189151891618917189181891918920189211892218923189241892518926189271892818929189301893118932189331893418935189361893718938189391894018941189421894318944189451894618947189481894918950189511895218953189541895518956189571895818959189601896118962189631896418965189661896718968189691897018971189721897318974189751897618977189781897918980189811898218983189841898518986189871898818989189901899118992189931899418995189961899718998189991900019001190021900319004190051900619007190081900919010190111901219013190141901519016190171901819019190201902119022190231902419025190261902719028190291903019031190321903319034190351903619037190381903919040190411904219043190441904519046190471904819049190501905119052190531905419055190561905719058190591906019061190621906319064190651906619067190681906919070190711907219073190741907519076190771907819079190801908119082190831908419085190861908719088190891909019091190921909319094190951909619097190981909919100191011910219103191041910519106191071910819109191101911119112191131911419115191161911719118191191912019121191221912319124191251912619127191281912919130191311913219133191341913519136191371913819139191401914119142191431914419145191461914719148191491915019151191521915319154191551915619157191581915919160191611916219163191641916519166191671916819169191701917119172191731917419175191761917719178191791918019181191821918319184191851918619187191881918919190191911919219193191941919519196191971919819199192001920119202192031920419205192061920719208192091921019211192121921319214192151921619217192181921919220192211922219223192241922519226192271922819229192301923119232192331923419235192361923719238192391924019241192421924319244192451924619247192481924919250192511925219253192541925519256192571925819259192601926119262192631926419265192661926719268192691927019271192721927319274192751927619277192781927919280192811928219283192841928519286192871928819289192901929119292192931929419295192961929719298192991930019301193021930319304193051930619307193081930919310193111931219313193141931519316193171931819319193201932119322193231932419325193261932719328193291933019331193321933319334193351933619337193381933919340193411934219343193441934519346193471934819349193501935119352193531935419355193561935719358193591936019361193621936319364193651936619367193681936919370193711937219373193741937519376193771937819379193801938119382193831938419385193861938719388193891939019391193921939319394193951939619397193981939919400194011940219403194041940519406194071940819409194101941119412194131941419415194161941719418194191942019421194221942319424194251942619427194281942919430194311943219433194341943519436194371943819439194401944119442194431944419445194461944719448194491945019451194521945319454194551945619457194581945919460194611946219463194641946519466194671946819469194701947119472194731947419475194761947719478194791948019481194821948319484194851948619487194881948919490194911949219493194941949519496194971949819499195001950119502195031950419505195061950719508195091951019511195121951319514195151951619517195181951919520195211952219523195241952519526195271952819529195301953119532195331953419535195361953719538195391954019541195421954319544195451954619547195481954919550195511955219553195541955519556195571955819559195601956119562195631956419565195661956719568195691957019571195721957319574195751957619577195781957919580195811958219583195841958519586195871958819589195901959119592195931959419595195961959719598195991960019601196021960319604196051960619607196081960919610196111961219613196141961519616196171961819619196201962119622196231962419625196261962719628196291963019631196321963319634196351963619637196381963919640196411964219643196441964519646196471964819649196501965119652196531965419655196561965719658196591966019661196621966319664196651966619667196681966919670196711967219673196741967519676196771967819679196801968119682196831968419685196861968719688196891969019691196921969319694196951969619697196981969919700197011970219703197041970519706197071970819709197101971119712197131971419715197161971719718197191972019721197221972319724197251972619727197281972919730197311973219733197341973519736197371973819739197401974119742197431974419745197461974719748197491975019751197521975319754197551975619757197581975919760197611976219763197641976519766197671976819769197701977119772197731977419775197761977719778197791978019781197821978319784197851978619787197881978919790197911979219793197941979519796197971979819799198001980119802198031980419805198061980719808198091981019811198121981319814198151981619817198181981919820198211982219823198241982519826198271982819829198301983119832198331983419835198361983719838198391984019841198421984319844198451984619847198481984919850198511985219853198541985519856198571985819859198601986119862198631986419865198661986719868198691987019871198721987319874198751987619877198781987919880198811988219883198841988519886198871988819889198901989119892198931989419895198961989719898198991990019901199021990319904199051990619907199081990919910199111991219913199141991519916199171991819919199201992119922199231992419925199261992719928199291993019931199321993319934199351993619937199381993919940199411994219943199441994519946199471994819949199501995119952199531995419955199561995719958199591996019961199621996319964199651996619967199681996919970199711997219973199741997519976199771997819979199801998119982199831998419985199861998719988199891999019991199921999319994199951999619997199981999920000200012000220003200042000520006200072000820009200102001120012200132001420015200162001720018200192002020021200222002320024200252002620027200282002920030200312003220033200342003520036200372003820039200402004120042200432004420045200462004720048200492005020051200522005320054200552005620057200582005920060200612006220063200642006520066200672006820069200702007120072200732007420075200762007720078200792008020081200822008320084200852008620087200882008920090200912009220093200942009520096200972009820099201002010120102201032010420105201062010720108201092011020111201122011320114201152011620117201182011920120201212012220123201242012520126201272012820129201302013120132201332013420135201362013720138201392014020141201422014320144201452014620147201482014920150201512015220153201542015520156201572015820159201602016120162201632016420165201662016720168201692017020171201722017320174201752017620177201782017920180201812018220183201842018520186201872018820189201902019120192201932019420195201962019720198201992020020201202022020320204202052020620207202082020920210202112021220213202142021520216202172021820219202202022120222202232022420225202262022720228202292023020231202322023320234202352023620237202382023920240202412024220243202442024520246202472024820249202502025120252202532025420255202562025720258202592026020261202622026320264202652026620267202682026920270202712027220273202742027520276202772027820279202802028120282202832028420285202862028720288202892029020291202922029320294202952029620297202982029920300203012030220303203042030520306203072030820309203102031120312203132031420315203162031720318203192032020321203222032320324203252032620327203282032920330203312033220333203342033520336203372033820339203402034120342203432034420345203462034720348203492035020351203522035320354203552035620357203582035920360203612036220363203642036520366203672036820369203702037120372203732037420375203762037720378203792038020381203822038320384203852038620387203882038920390203912039220393203942039520396203972039820399204002040120402204032040420405204062040720408204092041020411204122041320414204152041620417204182041920420204212042220423204242042520426204272042820429204302043120432204332043420435204362043720438204392044020441204422044320444204452044620447204482044920450204512045220453204542045520456204572045820459204602046120462204632046420465204662046720468204692047020471204722047320474204752047620477204782047920480204812048220483204842048520486204872048820489204902049120492204932049420495204962049720498204992050020501205022050320504205052050620507205082050920510205112051220513205142051520516205172051820519205202052120522205232052420525205262052720528205292053020531205322053320534205352053620537205382053920540205412054220543205442054520546205472054820549205502055120552205532055420555205562055720558205592056020561205622056320564205652056620567205682056920570205712057220573205742057520576205772057820579205802058120582205832058420585205862058720588205892059020591205922059320594205952059620597205982059920600206012060220603206042060520606206072060820609206102061120612206132061420615206162061720618206192062020621206222062320624206252062620627206282062920630206312063220633206342063520636206372063820639206402064120642206432064420645206462064720648206492065020651206522065320654206552065620657206582065920660206612066220663206642066520666206672066820669206702067120672206732067420675206762067720678206792068020681206822068320684206852068620687206882068920690206912069220693206942069520696206972069820699207002070120702207032070420705207062070720708207092071020711207122071320714207152071620717207182071920720207212072220723207242072520726207272072820729207302073120732207332073420735207362073720738207392074020741207422074320744207452074620747207482074920750207512075220753207542075520756207572075820759207602076120762207632076420765207662076720768207692077020771207722077320774207752077620777207782077920780207812078220783207842078520786207872078820789207902079120792207932079420795207962079720798207992080020801208022080320804208052080620807208082080920810208112081220813208142081520816208172081820819208202082120822208232082420825208262082720828208292083020831208322083320834208352083620837208382083920840208412084220843208442084520846208472084820849208502085120852208532085420855208562085720858208592086020861208622086320864208652086620867208682086920870208712087220873208742087520876208772087820879208802088120882208832088420885208862088720888208892089020891208922089320894208952089620897208982089920900209012090220903209042090520906209072090820909209102091120912209132091420915209162091720918209192092020921209222092320924209252092620927209282092920930209312093220933209342093520936209372093820939209402094120942209432094420945209462094720948209492095020951209522095320954209552095620957209582095920960209612096220963209642096520966209672096820969209702097120972209732097420975209762097720978209792098020981209822098320984209852098620987209882098920990209912099220993209942099520996209972099820999210002100121002210032100421005210062100721008210092101021011210122101321014210152101621017210182101921020210212102221023210242102521026210272102821029210302103121032210332103421035210362103721038210392104021041210422104321044210452104621047210482104921050210512105221053210542105521056210572105821059210602106121062210632106421065210662106721068210692107021071210722107321074210752107621077210782107921080210812108221083210842108521086210872108821089210902109121092210932109421095210962109721098210992110021101211022110321104211052110621107211082110921110211112111221113211142111521116211172111821119211202112121122211232112421125211262112721128211292113021131211322113321134211352113621137211382113921140211412114221143211442114521146211472114821149211502115121152211532115421155211562115721158211592116021161211622116321164211652116621167211682116921170211712117221173211742117521176211772117821179211802118121182211832118421185211862118721188211892119021191211922119321194211952119621197211982119921200212012120221203212042120521206212072120821209212102121121212212132121421215212162121721218212192122021221212222122321224212252122621227212282122921230212312123221233212342123521236212372123821239212402124121242212432124421245212462124721248212492125021251212522125321254212552125621257212582125921260212612126221263212642126521266212672126821269212702127121272212732127421275212762127721278212792128021281212822128321284212852128621287212882128921290212912129221293212942129521296212972129821299213002130121302213032130421305213062130721308213092131021311213122131321314213152131621317213182131921320213212132221323213242132521326213272132821329213302133121332213332133421335213362133721338213392134021341213422134321344213452134621347213482134921350213512135221353213542135521356213572135821359213602136121362213632136421365213662136721368213692137021371213722137321374213752137621377213782137921380213812138221383213842138521386213872138821389213902139121392213932139421395213962139721398213992140021401214022140321404214052140621407214082140921410214112141221413214142141521416214172141821419214202142121422214232142421425214262142721428214292143021431214322143321434214352143621437214382143921440214412144221443214442144521446214472144821449214502145121452214532145421455214562145721458214592146021461214622146321464214652146621467214682146921470214712147221473214742147521476214772147821479214802148121482214832148421485214862148721488214892149021491214922149321494214952149621497214982149921500215012150221503215042150521506215072150821509215102151121512215132151421515215162151721518215192152021521215222152321524215252152621527215282152921530215312153221533215342153521536215372153821539215402154121542215432154421545215462154721548215492155021551215522155321554215552155621557215582155921560215612156221563215642156521566215672156821569215702157121572215732157421575215762157721578215792158021581215822158321584215852158621587215882158921590215912159221593215942159521596215972159821599216002160121602216032160421605216062160721608216092161021611216122161321614216152161621617216182161921620216212162221623216242162521626216272162821629216302163121632216332163421635216362163721638216392164021641216422164321644216452164621647216482164921650216512165221653216542165521656216572165821659216602166121662216632166421665216662166721668216692167021671216722167321674216752167621677216782167921680216812168221683216842168521686216872168821689216902169121692216932169421695216962169721698216992170021701217022170321704217052170621707217082170921710217112171221713217142171521716217172171821719217202172121722217232172421725217262172721728217292173021731217322173321734217352173621737217382173921740217412174221743217442174521746217472174821749217502175121752217532175421755217562175721758217592176021761217622176321764217652176621767217682176921770217712177221773217742177521776217772177821779217802178121782217832178421785217862178721788217892179021791217922179321794217952179621797217982179921800218012180221803218042180521806218072180821809218102181121812218132181421815218162181721818218192182021821218222182321824218252182621827218282182921830218312183221833218342183521836218372183821839218402184121842218432184421845218462184721848218492185021851218522185321854218552185621857218582185921860218612186221863218642186521866218672186821869218702187121872218732187421875218762187721878218792188021881218822188321884218852188621887218882188921890218912189221893218942189521896218972189821899219002190121902219032190421905219062190721908219092191021911219122191321914219152191621917219182191921920219212192221923219242192521926219272192821929219302193121932219332193421935219362193721938219392194021941219422194321944219452194621947219482194921950219512195221953219542195521956219572195821959219602196121962219632196421965219662196721968219692197021971219722197321974219752197621977219782197921980219812198221983219842198521986219872198821989219902199121992219932199421995219962199721998219992200022001220022200322004220052200622007220082200922010220112201222013220142201522016220172201822019220202202122022220232202422025220262202722028220292203022031220322203322034220352203622037220382203922040220412204222043220442204522046220472204822049220502205122052220532205422055220562205722058220592206022061220622206322064220652206622067220682206922070220712207222073220742207522076220772207822079220802208122082220832208422085220862208722088220892209022091220922209322094220952209622097220982209922100221012210222103221042210522106221072210822109221102211122112221132211422115221162211722118221192212022121221222212322124221252212622127221282212922130221312213222133221342213522136221372213822139221402214122142221432214422145221462214722148221492215022151221522215322154221552215622157221582215922160221612216222163221642216522166221672216822169221702217122172221732217422175221762217722178221792218022181221822218322184221852218622187221882218922190221912219222193221942219522196221972219822199222002220122202222032220422205222062220722208222092221022211222122221322214222152221622217222182221922220222212222222223222242222522226222272222822229222302223122232222332223422235222362223722238222392224022241222422224322244222452224622247222482224922250222512225222253222542225522256222572225822259222602226122262222632226422265222662226722268222692227022271222722227322274222752227622277222782227922280222812228222283222842228522286222872228822289222902229122292222932229422295222962229722298222992230022301223022230322304223052230622307223082230922310223112231222313223142231522316223172231822319223202232122322223232232422325223262232722328223292233022331223322233322334223352233622337223382233922340223412234222343223442234522346223472234822349223502235122352223532235422355223562235722358223592236022361223622236322364223652236622367223682236922370223712237222373223742237522376223772237822379223802238122382223832238422385223862238722388223892239022391223922239322394223952239622397223982239922400224012240222403224042240522406224072240822409224102241122412224132241422415224162241722418224192242022421224222242322424224252242622427224282242922430224312243222433224342243522436224372243822439224402244122442224432244422445224462244722448224492245022451224522245322454224552245622457224582245922460224612246222463224642246522466224672246822469224702247122472224732247422475224762247722478224792248022481224822248322484224852248622487224882248922490224912249222493224942249522496224972249822499225002250122502225032250422505225062250722508225092251022511225122251322514225152251622517225182251922520225212252222523225242252522526225272252822529225302253122532225332253422535225362253722538225392254022541225422254322544225452254622547225482254922550225512255222553225542255522556225572255822559225602256122562225632256422565225662256722568225692257022571225722257322574225752257622577225782257922580225812258222583225842258522586225872258822589225902259122592225932259422595225962259722598225992260022601226022260322604226052260622607226082260922610226112261222613226142261522616226172261822619226202262122622226232262422625226262262722628226292263022631226322263322634226352263622637226382263922640226412264222643226442264522646226472264822649226502265122652226532265422655226562265722658226592266022661226622266322664226652266622667226682266922670226712267222673226742267522676226772267822679226802268122682226832268422685226862268722688226892269022691226922269322694226952269622697226982269922700227012270222703227042270522706227072270822709227102271122712227132271422715227162271722718227192272022721227222272322724227252272622727227282272922730227312273222733227342273522736227372273822739227402274122742227432274422745227462274722748227492275022751227522275322754227552275622757227582275922760227612276222763227642276522766227672276822769227702277122772227732277422775227762277722778227792278022781227822278322784227852278622787227882278922790227912279222793227942279522796227972279822799228002280122802228032280422805228062280722808228092281022811228122281322814228152281622817228182281922820228212282222823228242282522826228272282822829228302283122832228332283422835228362283722838228392284022841228422284322844228452284622847228482284922850228512285222853228542285522856228572285822859228602286122862228632286422865228662286722868228692287022871228722287322874228752287622877228782287922880228812288222883228842288522886228872288822889228902289122892228932289422895228962289722898228992290022901229022290322904229052290622907229082290922910229112291222913229142291522916229172291822919229202292122922229232292422925229262292722928229292293022931229322293322934229352293622937229382293922940229412294222943229442294522946229472294822949229502295122952229532295422955229562295722958229592296022961229622296322964229652296622967229682296922970229712297222973229742297522976229772297822979229802298122982229832298422985229862298722988229892299022991229922299322994229952299622997229982299923000230012300223003230042300523006230072300823009230102301123012230132301423015230162301723018230192302023021230222302323024230252302623027230282302923030230312303223033230342303523036230372303823039230402304123042230432304423045230462304723048230492305023051230522305323054230552305623057230582305923060230612306223063230642306523066230672306823069230702307123072230732307423075230762307723078230792308023081230822308323084230852308623087230882308923090230912309223093230942309523096230972309823099231002310123102231032310423105231062310723108231092311023111231122311323114231152311623117231182311923120231212312223123231242312523126231272312823129231302313123132231332313423135231362313723138231392314023141231422314323144231452314623147231482314923150231512315223153231542315523156231572315823159231602316123162231632316423165231662316723168231692317023171231722317323174231752317623177231782317923180231812318223183231842318523186231872318823189231902319123192231932319423195231962319723198231992320023201232022320323204232052320623207232082320923210232112321223213232142321523216232172321823219232202322123222232232322423225232262322723228232292323023231232322323323234232352323623237232382323923240232412324223243232442324523246232472324823249232502325123252232532325423255232562325723258232592326023261232622326323264232652326623267232682326923270232712327223273232742327523276232772327823279232802328123282232832328423285232862328723288232892329023291232922329323294232952329623297232982329923300233012330223303233042330523306233072330823309233102331123312233132331423315233162331723318233192332023321233222332323324233252332623327233282332923330233312333223333233342333523336233372333823339233402334123342233432334423345233462334723348233492335023351233522335323354233552335623357233582335923360233612336223363233642336523366233672336823369233702337123372233732337423375233762337723378233792338023381233822338323384233852338623387233882338923390233912339223393233942339523396233972339823399234002340123402234032340423405234062340723408234092341023411234122341323414234152341623417234182341923420234212342223423234242342523426234272342823429234302343123432234332343423435234362343723438234392344023441234422344323444234452344623447234482344923450234512345223453234542345523456234572345823459234602346123462234632346423465234662346723468234692347023471234722347323474234752347623477234782347923480234812348223483234842348523486234872348823489234902349123492234932349423495234962349723498234992350023501235022350323504235052350623507235082350923510235112351223513235142351523516235172351823519235202352123522235232352423525235262352723528235292353023531235322353323534235352353623537235382353923540235412354223543235442354523546235472354823549235502355123552235532355423555235562355723558235592356023561235622356323564235652356623567235682356923570235712357223573235742357523576235772357823579235802358123582235832358423585235862358723588235892359023591235922359323594235952359623597235982359923600236012360223603236042360523606236072360823609236102361123612236132361423615236162361723618236192362023621236222362323624236252362623627236282362923630236312363223633236342363523636236372363823639236402364123642236432364423645236462364723648236492365023651236522365323654236552365623657236582365923660236612366223663236642366523666236672366823669236702367123672236732367423675236762367723678236792368023681236822368323684236852368623687236882368923690236912369223693236942369523696236972369823699237002370123702237032370423705237062370723708237092371023711237122371323714237152371623717237182371923720237212372223723237242372523726237272372823729237302373123732237332373423735237362373723738237392374023741237422374323744237452374623747237482374923750237512375223753237542375523756237572375823759237602376123762237632376423765237662376723768237692377023771237722377323774237752377623777237782377923780237812378223783237842378523786237872378823789237902379123792237932379423795237962379723798237992380023801238022380323804238052380623807238082380923810238112381223813238142381523816238172381823819238202382123822238232382423825238262382723828238292383023831238322383323834238352383623837238382383923840238412384223843238442384523846238472384823849238502385123852238532385423855238562385723858238592386023861238622386323864238652386623867238682386923870238712387223873238742387523876238772387823879238802388123882238832388423885238862388723888238892389023891238922389323894238952389623897238982389923900239012390223903239042390523906239072390823909239102391123912239132391423915239162391723918239192392023921239222392323924239252392623927239282392923930239312393223933239342393523936239372393823939239402394123942239432394423945239462394723948239492395023951239522395323954239552395623957239582395923960239612396223963239642396523966239672396823969239702397123972239732397423975239762397723978239792398023981239822398323984239852398623987239882398923990239912399223993239942399523996239972399823999240002400124002240032400424005240062400724008240092401024011240122401324014240152401624017240182401924020240212402224023240242402524026240272402824029240302403124032240332403424035240362403724038240392404024041240422404324044240452404624047240482404924050240512405224053240542405524056240572405824059240602406124062240632406424065240662406724068240692407024071240722407324074240752407624077240782407924080240812408224083240842408524086240872408824089240902409124092240932409424095240962409724098240992410024101241022410324104241052410624107241082410924110241112411224113241142411524116241172411824119241202412124122241232412424125241262412724128241292413024131241322413324134241352413624137241382413924140241412414224143241442414524146241472414824149241502415124152241532415424155241562415724158241592416024161241622416324164241652416624167241682416924170241712417224173241742417524176241772417824179241802418124182241832418424185241862418724188241892419024191241922419324194241952419624197241982419924200242012420224203242042420524206242072420824209242102421124212242132421424215242162421724218242192422024221242222422324224242252422624227242282422924230242312423224233242342423524236242372423824239242402424124242242432424424245242462424724248242492425024251242522425324254242552425624257242582425924260242612426224263242642426524266242672426824269242702427124272242732427424275242762427724278242792428024281242822428324284242852428624287242882428924290242912429224293242942429524296242972429824299243002430124302243032430424305243062430724308243092431024311243122431324314243152431624317243182431924320243212432224323243242432524326243272432824329243302433124332243332433424335243362433724338243392434024341243422434324344243452434624347243482434924350243512435224353243542435524356243572435824359243602436124362243632436424365243662436724368243692437024371243722437324374243752437624377243782437924380243812438224383243842438524386243872438824389243902439124392243932439424395243962439724398243992440024401244022440324404244052440624407244082440924410244112441224413244142441524416244172441824419244202442124422244232442424425244262442724428244292443024431244322443324434244352443624437244382443924440244412444224443244442444524446244472444824449244502445124452244532445424455244562445724458244592446024461244622446324464244652446624467244682446924470244712447224473244742447524476244772447824479244802448124482244832448424485244862448724488244892449024491244922449324494244952449624497244982449924500245012450224503245042450524506245072450824509245102451124512245132451424515245162451724518245192452024521245222452324524245252452624527245282452924530245312453224533245342453524536245372453824539245402454124542245432454424545245462454724548245492455024551245522455324554245552455624557245582455924560245612456224563245642456524566245672456824569245702457124572245732457424575245762457724578245792458024581245822458324584245852458624587245882458924590245912459224593245942459524596245972459824599246002460124602246032460424605246062460724608246092461024611246122461324614246152461624617246182461924620246212462224623246242462524626246272462824629246302463124632246332463424635246362463724638246392464024641246422464324644246452464624647246482464924650246512465224653246542465524656246572465824659246602466124662246632466424665246662466724668246692467024671246722467324674246752467624677246782467924680246812468224683246842468524686246872468824689246902469124692246932469424695246962469724698246992470024701247022470324704247052470624707247082470924710247112471224713247142471524716247172471824719247202472124722247232472424725247262472724728247292473024731247322473324734247352473624737247382473924740247412474224743247442474524746247472474824749247502475124752247532475424755247562475724758247592476024761247622476324764247652476624767247682476924770247712477224773247742477524776247772477824779247802478124782247832478424785247862478724788247892479024791247922479324794247952479624797247982479924800248012480224803248042480524806248072480824809248102481124812248132481424815248162481724818248192482024821248222482324824248252482624827248282482924830248312483224833248342483524836248372483824839248402484124842248432484424845248462484724848248492485024851248522485324854248552485624857248582485924860248612486224863248642486524866248672486824869248702487124872248732487424875248762487724878248792488024881248822488324884248852488624887248882488924890248912489224893248942489524896248972489824899249002490124902249032490424905249062490724908249092491024911249122491324914249152491624917249182491924920249212492224923249242492524926249272492824929249302493124932249332493424935249362493724938249392494024941249422494324944249452494624947249482494924950249512495224953249542495524956249572495824959249602496124962249632496424965249662496724968249692497024971249722497324974249752497624977249782497924980249812498224983249842498524986249872498824989249902499124992249932499424995249962499724998249992500025001250022500325004250052500625007250082500925010250112501225013250142501525016250172501825019250202502125022250232502425025250262502725028250292503025031250322503325034250352503625037250382503925040250412504225043250442504525046250472504825049250502505125052250532505425055250562505725058250592506025061250622506325064250652506625067250682506925070250712507225073250742507525076250772507825079250802508125082250832508425085250862508725088250892509025091250922509325094250952509625097250982509925100251012510225103251042510525106251072510825109251102511125112251132511425115251162511725118251192512025121251222512325124251252512625127251282512925130251312513225133251342513525136251372513825139251402514125142251432514425145251462514725148251492515025151251522515325154251552515625157251582515925160251612516225163251642516525166251672516825169251702517125172251732517425175251762517725178251792518025181251822518325184251852518625187251882518925190251912519225193251942519525196251972519825199252002520125202252032520425205252062520725208252092521025211252122521325214252152521625217252182521925220252212522225223252242522525226252272522825229252302523125232252332523425235252362523725238252392524025241252422524325244252452524625247252482524925250252512525225253252542525525256252572525825259252602526125262252632526425265252662526725268252692527025271252722527325274252752527625277252782527925280252812528225283252842528525286252872528825289252902529125292252932529425295252962529725298252992530025301253022530325304253052530625307253082530925310253112531225313253142531525316253172531825319253202532125322253232532425325253262532725328253292533025331253322533325334253352533625337253382533925340253412534225343253442534525346253472534825349253502535125352253532535425355253562535725358253592536025361253622536325364253652536625367253682536925370253712537225373253742537525376253772537825379253802538125382253832538425385253862538725388253892539025391253922539325394253952539625397253982539925400254012540225403254042540525406254072540825409254102541125412254132541425415254162541725418254192542025421254222542325424254252542625427254282542925430254312543225433254342543525436254372543825439254402544125442254432544425445254462544725448254492545025451254522545325454254552545625457254582545925460254612546225463254642546525466254672546825469254702547125472254732547425475254762547725478254792548025481254822548325484254852548625487254882548925490254912549225493254942549525496254972549825499255002550125502255032550425505255062550725508255092551025511255122551325514255152551625517255182551925520255212552225523255242552525526255272552825529255302553125532255332553425535255362553725538255392554025541255422554325544255452554625547255482554925550255512555225553255542555525556255572555825559255602556125562255632556425565255662556725568255692557025571255722557325574255752557625577255782557925580255812558225583255842558525586255872558825589255902559125592255932559425595255962559725598255992560025601256022560325604256052560625607256082560925610256112561225613256142561525616256172561825619256202562125622256232562425625256262562725628256292563025631256322563325634256352563625637256382563925640256412564225643256442564525646256472564825649256502565125652256532565425655256562565725658256592566025661256622566325664256652566625667256682566925670256712567225673256742567525676256772567825679256802568125682256832568425685256862568725688256892569025691256922569325694256952569625697256982569925700257012570225703257042570525706257072570825709257102571125712257132571425715257162571725718257192572025721257222572325724257252572625727257282572925730257312573225733257342573525736257372573825739257402574125742257432574425745257462574725748257492575025751257522575325754257552575625757257582575925760257612576225763257642576525766257672576825769257702577125772257732577425775257762577725778257792578025781257822578325784257852578625787257882578925790257912579225793257942579525796257972579825799258002580125802258032580425805258062580725808258092581025811258122581325814258152581625817258182581925820258212582225823258242582525826258272582825829258302583125832258332583425835258362583725838258392584025841258422584325844258452584625847258482584925850258512585225853258542585525856258572585825859258602586125862258632586425865258662586725868258692587025871258722587325874258752587625877258782587925880258812588225883258842588525886258872588825889258902589125892258932589425895258962589725898258992590025901259022590325904259052590625907259082590925910259112591225913259142591525916259172591825919259202592125922259232592425925259262592725928259292593025931259322593325934259352593625937259382593925940259412594225943259442594525946259472594825949259502595125952259532595425955259562595725958259592596025961259622596325964259652596625967259682596925970259712597225973259742597525976259772597825979259802598125982259832598425985259862598725988259892599025991259922599325994259952599625997259982599926000260012600226003260042600526006260072600826009260102601126012260132601426015260162601726018260192602026021260222602326024260252602626027260282602926030260312603226033260342603526036260372603826039260402604126042260432604426045260462604726048260492605026051260522605326054260552605626057260582605926060260612606226063260642606526066260672606826069260702607126072260732607426075260762607726078260792608026081260822608326084260852608626087260882608926090260912609226093260942609526096260972609826099261002610126102261032610426105261062610726108261092611026111261122611326114261152611626117261182611926120261212612226123261242612526126261272612826129261302613126132261332613426135261362613726138261392614026141261422614326144261452614626147261482614926150261512615226153261542615526156261572615826159261602616126162261632616426165261662616726168261692617026171261722617326174261752617626177261782617926180261812618226183261842618526186261872618826189261902619126192261932619426195261962619726198261992620026201262022620326204262052620626207262082620926210262112621226213262142621526216262172621826219262202622126222262232622426225262262622726228262292623026231262322623326234262352623626237262382623926240262412624226243262442624526246262472624826249262502625126252262532625426255262562625726258262592626026261262622626326264262652626626267262682626926270262712627226273262742627526276262772627826279262802628126282262832628426285262862628726288262892629026291262922629326294262952629626297262982629926300263012630226303263042630526306263072630826309263102631126312263132631426315263162631726318263192632026321263222632326324263252632626327263282632926330263312633226333263342633526336263372633826339263402634126342263432634426345263462634726348263492635026351263522635326354263552635626357263582635926360263612636226363263642636526366263672636826369263702637126372263732637426375263762637726378263792638026381263822638326384263852638626387263882638926390263912639226393263942639526396263972639826399264002640126402264032640426405264062640726408264092641026411264122641326414264152641626417264182641926420264212642226423264242642526426264272642826429264302643126432264332643426435264362643726438264392644026441264422644326444264452644626447264482644926450264512645226453264542645526456264572645826459264602646126462264632646426465264662646726468264692647026471264722647326474264752647626477264782647926480264812648226483264842648526486264872648826489264902649126492264932649426495264962649726498264992650026501265022650326504265052650626507265082650926510265112651226513265142651526516265172651826519265202652126522265232652426525265262652726528265292653026531265322653326534265352653626537265382653926540265412654226543265442654526546265472654826549265502655126552265532655426555265562655726558265592656026561265622656326564265652656626567265682656926570265712657226573265742657526576265772657826579265802658126582265832658426585265862658726588265892659026591265922659326594265952659626597265982659926600266012660226603266042660526606266072660826609266102661126612266132661426615266162661726618266192662026621266222662326624266252662626627266282662926630266312663226633266342663526636266372663826639266402664126642266432664426645266462664726648266492665026651266522665326654266552665626657266582665926660266612666226663266642666526666266672666826669266702667126672266732667426675266762667726678266792668026681266822668326684266852668626687266882668926690266912669226693266942669526696266972669826699267002670126702267032670426705267062670726708267092671026711267122671326714267152671626717267182671926720267212672226723267242672526726267272672826729267302673126732267332673426735267362673726738267392674026741267422674326744267452674626747267482674926750267512675226753267542675526756267572675826759267602676126762267632676426765267662676726768267692677026771267722677326774267752677626777267782677926780267812678226783267842678526786267872678826789267902679126792267932679426795267962679726798267992680026801268022680326804268052680626807268082680926810268112681226813268142681526816268172681826819268202682126822268232682426825268262682726828268292683026831268322683326834268352683626837268382683926840268412684226843268442684526846268472684826849268502685126852268532685426855268562685726858268592686026861268622686326864268652686626867268682686926870268712687226873268742687526876268772687826879268802688126882268832688426885268862688726888268892689026891268922689326894268952689626897268982689926900269012690226903269042690526906269072690826909269102691126912269132691426915269162691726918269192692026921269222692326924269252692626927269282692926930269312693226933269342693526936269372693826939269402694126942269432694426945269462694726948269492695026951269522695326954269552695626957269582695926960269612696226963269642696526966269672696826969269702697126972269732697426975269762697726978269792698026981269822698326984269852698626987269882698926990269912699226993269942699526996269972699826999270002700127002270032700427005270062700727008270092701027011270122701327014270152701627017270182701927020270212702227023270242702527026270272702827029270302703127032270332703427035270362703727038270392704027041270422704327044270452704627047270482704927050270512705227053270542705527056270572705827059270602706127062270632706427065270662706727068270692707027071270722707327074270752707627077270782707927080270812708227083270842708527086270872708827089270902709127092270932709427095270962709727098270992710027101271022710327104271052710627107271082710927110271112711227113271142711527116271172711827119271202712127122271232712427125271262712727128271292713027131271322713327134271352713627137271382713927140271412714227143271442714527146271472714827149271502715127152271532715427155271562715727158271592716027161271622716327164271652716627167271682716927170271712717227173271742717527176271772717827179271802718127182271832718427185271862718727188271892719027191271922719327194271952719627197271982719927200272012720227203272042720527206272072720827209272102721127212272132721427215272162721727218272192722027221272222722327224272252722627227272282722927230272312723227233272342723527236272372723827239272402724127242272432724427245272462724727248272492725027251272522725327254272552725627257272582725927260272612726227263272642726527266272672726827269272702727127272272732727427275272762727727278272792728027281272822728327284272852728627287272882728927290272912729227293272942729527296272972729827299273002730127302273032730427305273062730727308273092731027311273122731327314273152731627317273182731927320273212732227323273242732527326273272732827329273302733127332273332733427335273362733727338273392734027341273422734327344273452734627347273482734927350273512735227353273542735527356273572735827359273602736127362273632736427365273662736727368273692737027371273722737327374273752737627377273782737927380273812738227383273842738527386273872738827389273902739127392273932739427395273962739727398273992740027401274022740327404274052740627407274082740927410274112741227413274142741527416274172741827419274202742127422274232742427425274262742727428274292743027431274322743327434274352743627437274382743927440274412744227443274442744527446274472744827449274502745127452274532745427455274562745727458274592746027461274622746327464274652746627467274682746927470274712747227473274742747527476274772747827479274802748127482274832748427485274862748727488274892749027491274922749327494274952749627497274982749927500275012750227503275042750527506275072750827509275102751127512275132751427515275162751727518275192752027521275222752327524275252752627527275282752927530275312753227533275342753527536275372753827539275402754127542275432754427545275462754727548275492755027551275522755327554275552755627557275582755927560275612756227563275642756527566275672756827569275702757127572275732757427575275762757727578275792758027581275822758327584275852758627587275882758927590275912759227593275942759527596275972759827599276002760127602276032760427605276062760727608276092761027611276122761327614276152761627617276182761927620276212762227623276242762527626276272762827629276302763127632276332763427635276362763727638276392764027641276422764327644276452764627647276482764927650276512765227653276542765527656276572765827659276602766127662276632766427665276662766727668276692767027671276722767327674276752767627677276782767927680276812768227683276842768527686276872768827689276902769127692276932769427695276962769727698276992770027701277022770327704277052770627707277082770927710277112771227713277142771527716277172771827719277202772127722277232772427725277262772727728277292773027731277322773327734277352773627737277382773927740277412774227743277442774527746277472774827749277502775127752277532775427755277562775727758277592776027761277622776327764277652776627767277682776927770277712777227773277742777527776277772777827779277802778127782277832778427785277862778727788277892779027791277922779327794277952779627797277982779927800278012780227803278042780527806278072780827809278102781127812278132781427815278162781727818278192782027821278222782327824278252782627827278282782927830278312783227833278342783527836278372783827839278402784127842278432784427845278462784727848278492785027851278522785327854278552785627857278582785927860278612786227863278642786527866278672786827869278702787127872278732787427875278762787727878278792788027881278822788327884278852788627887278882788927890278912789227893278942789527896278972789827899279002790127902279032790427905279062790727908279092791027911279122791327914279152791627917279182791927920279212792227923279242792527926279272792827929279302793127932279332793427935279362793727938279392794027941279422794327944279452794627947279482794927950279512795227953279542795527956279572795827959279602796127962279632796427965279662796727968279692797027971279722797327974279752797627977279782797927980279812798227983279842798527986279872798827989279902799127992279932799427995279962799727998279992800028001280022800328004280052800628007280082800928010280112801228013280142801528016280172801828019280202802128022280232802428025280262802728028280292803028031280322803328034280352803628037280382803928040280412804228043280442804528046280472804828049280502805128052280532805428055280562805728058280592806028061280622806328064280652806628067280682806928070280712807228073280742807528076280772807828079280802808128082280832808428085280862808728088280892809028091280922809328094280952809628097280982809928100281012810228103281042810528106281072810828109281102811128112281132811428115281162811728118281192812028121281222812328124281252812628127281282812928130281312813228133281342813528136281372813828139281402814128142281432814428145281462814728148281492815028151281522815328154281552815628157281582815928160281612816228163281642816528166281672816828169281702817128172281732817428175281762817728178281792818028181281822818328184281852818628187281882818928190281912819228193281942819528196281972819828199282002820128202282032820428205282062820728208282092821028211282122821328214282152821628217282182821928220282212822228223282242822528226282272822828229282302823128232282332823428235282362823728238282392824028241282422824328244282452824628247282482824928250282512825228253282542825528256282572825828259282602826128262282632826428265282662826728268282692827028271282722827328274282752827628277282782827928280282812828228283282842828528286282872828828289282902829128292282932829428295282962829728298282992830028301283022830328304283052830628307283082830928310283112831228313283142831528316283172831828319283202832128322283232832428325283262832728328283292833028331283322833328334283352833628337283382833928340283412834228343283442834528346283472834828349283502835128352283532835428355283562835728358283592836028361283622836328364283652836628367283682836928370283712837228373283742837528376283772837828379283802838128382283832838428385283862838728388283892839028391283922839328394283952839628397283982839928400284012840228403284042840528406284072840828409284102841128412284132841428415284162841728418284192842028421284222842328424284252842628427284282842928430284312843228433284342843528436284372843828439284402844128442284432844428445284462844728448284492845028451284522845328454284552845628457284582845928460284612846228463284642846528466284672846828469284702847128472284732847428475284762847728478284792848028481284822848328484284852848628487284882848928490284912849228493284942849528496284972849828499285002850128502285032850428505285062850728508285092851028511285122851328514285152851628517285182851928520285212852228523285242852528526285272852828529285302853128532285332853428535285362853728538285392854028541285422854328544285452854628547285482854928550285512855228553285542855528556285572855828559285602856128562285632856428565285662856728568285692857028571285722857328574285752857628577285782857928580285812858228583285842858528586285872858828589285902859128592285932859428595285962859728598285992860028601286022860328604286052860628607286082860928610286112861228613286142861528616286172861828619286202862128622286232862428625286262862728628286292863028631286322863328634286352863628637286382863928640286412864228643286442864528646286472864828649286502865128652286532865428655286562865728658286592866028661286622866328664286652866628667286682866928670286712867228673286742867528676286772867828679286802868128682286832868428685286862868728688286892869028691286922869328694286952869628697286982869928700287012870228703287042870528706287072870828709287102871128712287132871428715287162871728718287192872028721287222872328724287252872628727287282872928730287312873228733287342873528736287372873828739287402874128742287432874428745287462874728748287492875028751287522875328754287552875628757287582875928760287612876228763287642876528766287672876828769287702877128772287732877428775287762877728778287792878028781287822878328784287852878628787287882878928790287912879228793287942879528796287972879828799288002880128802288032880428805288062880728808288092881028811288122881328814288152881628817288182881928820288212882228823288242882528826288272882828829288302883128832288332883428835288362883728838288392884028841288422884328844288452884628847288482884928850288512885228853288542885528856288572885828859288602886128862288632886428865288662886728868288692887028871288722887328874288752887628877288782887928880288812888228883288842888528886288872888828889288902889128892288932889428895288962889728898288992890028901289022890328904289052890628907289082890928910289112891228913289142891528916289172891828919289202892128922289232892428925289262892728928289292893028931289322893328934289352893628937289382893928940289412894228943289442894528946289472894828949289502895128952289532895428955289562895728958289592896028961289622896328964289652896628967289682896928970289712897228973289742897528976289772897828979289802898128982289832898428985289862898728988289892899028991289922899328994289952899628997289982899929000290012900229003290042900529006290072900829009290102901129012290132901429015290162901729018290192902029021290222902329024290252902629027290282902929030290312903229033290342903529036290372903829039290402904129042290432904429045290462904729048290492905029051290522905329054290552905629057290582905929060290612906229063290642906529066290672906829069290702907129072290732907429075290762907729078290792908029081290822908329084290852908629087290882908929090290912909229093290942909529096290972909829099291002910129102291032910429105291062910729108291092911029111291122911329114291152911629117291182911929120291212912229123291242912529126291272912829129291302913129132291332913429135291362913729138291392914029141291422914329144291452914629147291482914929150291512915229153291542915529156291572915829159291602916129162291632916429165291662916729168291692917029171291722917329174291752917629177291782917929180291812918229183291842918529186291872918829189291902919129192291932919429195291962919729198291992920029201292022920329204292052920629207292082920929210292112921229213292142921529216292172921829219292202922129222292232922429225292262922729228292292923029231292322923329234292352923629237292382923929240292412924229243292442924529246292472924829249292502925129252292532925429255292562925729258292592926029261292622926329264292652926629267292682926929270292712927229273292742927529276292772927829279292802928129282292832928429285292862928729288292892929029291292922929329294292952929629297292982929929300293012930229303293042930529306293072930829309293102931129312293132931429315293162931729318293192932029321293222932329324293252932629327293282932929330293312933229333293342933529336293372933829339293402934129342293432934429345293462934729348293492935029351293522935329354293552935629357293582935929360293612936229363293642936529366293672936829369293702937129372293732937429375293762937729378293792938029381293822938329384293852938629387293882938929390293912939229393293942939529396293972939829399294002940129402294032940429405294062940729408294092941029411294122941329414294152941629417294182941929420294212942229423294242942529426294272942829429294302943129432294332943429435294362943729438294392944029441294422944329444294452944629447294482944929450294512945229453294542945529456294572945829459294602946129462294632946429465294662946729468294692947029471294722947329474294752947629477294782947929480294812948229483294842948529486294872948829489294902949129492294932949429495294962949729498294992950029501295022950329504295052950629507295082950929510295112951229513295142951529516295172951829519295202952129522295232952429525295262952729528295292953029531295322953329534295352953629537295382953929540295412954229543295442954529546295472954829549295502955129552295532955429555295562955729558295592956029561295622956329564295652956629567295682956929570295712957229573295742957529576295772957829579295802958129582295832958429585295862958729588295892959029591295922959329594295952959629597295982959929600296012960229603296042960529606296072960829609296102961129612296132961429615296162961729618296192962029621296222962329624296252962629627296282962929630296312963229633296342963529636296372963829639296402964129642296432964429645296462964729648296492965029651296522965329654296552965629657296582965929660296612966229663296642966529666296672966829669296702967129672296732967429675296762967729678296792968029681296822968329684296852968629687296882968929690296912969229693296942969529696296972969829699297002970129702297032970429705297062970729708297092971029711297122971329714297152971629717297182971929720297212972229723297242972529726297272972829729297302973129732297332973429735297362973729738297392974029741297422974329744297452974629747297482974929750297512975229753297542975529756297572975829759297602976129762297632976429765297662976729768297692977029771297722977329774297752977629777297782977929780297812978229783297842978529786297872978829789297902979129792297932979429795297962979729798297992980029801298022980329804298052980629807298082980929810298112981229813298142981529816298172981829819298202982129822298232982429825298262982729828298292983029831298322983329834298352983629837298382983929840298412984229843298442984529846298472984829849298502985129852298532985429855298562985729858298592986029861298622986329864298652986629867298682986929870298712987229873298742987529876298772987829879298802988129882298832988429885298862988729888298892989029891298922989329894298952989629897298982989929900299012990229903299042990529906299072990829909299102991129912299132991429915299162991729918299192992029921299222992329924299252992629927299282992929930299312993229933299342993529936299372993829939299402994129942299432994429945299462994729948299492995029951299522995329954299552995629957299582995929960299612996229963299642996529966299672996829969299702997129972299732997429975299762997729978299792998029981299822998329984299852998629987299882998929990299912999229993299942999529996299972999829999300003000130002300033000430005300063000730008300093001030011300123001330014300153001630017300183001930020300213002230023300243002530026300273002830029300303003130032300333003430035300363003730038300393004030041300423004330044300453004630047300483004930050300513005230053300543005530056300573005830059300603006130062300633006430065300663006730068300693007030071300723007330074300753007630077300783007930080300813008230083300843008530086300873008830089300903009130092300933009430095300963009730098300993010030101301023010330104301053010630107301083010930110301113011230113301143011530116301173011830119301203012130122301233012430125301263012730128301293013030131301323013330134301353013630137301383013930140301413014230143301443014530146301473014830149301503015130152301533015430155301563015730158301593016030161301623016330164301653016630167301683016930170301713017230173301743017530176301773017830179301803018130182301833018430185301863018730188301893019030191301923019330194301953019630197301983019930200302013020230203302043020530206302073020830209302103021130212302133021430215302163021730218302193022030221302223022330224302253022630227302283022930230302313023230233302343023530236302373023830239302403024130242302433024430245302463024730248302493025030251302523025330254302553025630257302583025930260302613026230263302643026530266302673026830269302703027130272302733027430275302763027730278302793028030281302823028330284302853028630287302883028930290302913029230293302943029530296302973029830299303003030130302303033030430305303063030730308303093031030311303123031330314303153031630317303183031930320303213032230323303243032530326303273032830329303303033130332303333033430335303363033730338303393034030341303423034330344303453034630347303483034930350303513035230353303543035530356303573035830359303603036130362303633036430365303663036730368303693037030371303723037330374303753037630377303783037930380303813038230383303843038530386303873038830389303903039130392303933039430395303963039730398303993040030401304023040330404304053040630407304083040930410304113041230413304143041530416304173041830419304203042130422304233042430425304263042730428304293043030431304323043330434304353043630437304383043930440304413044230443304443044530446304473044830449304503045130452304533045430455304563045730458304593046030461304623046330464304653046630467304683046930470304713047230473304743047530476304773047830479304803048130482304833048430485304863048730488304893049030491304923049330494304953049630497304983049930500305013050230503305043050530506305073050830509305103051130512305133051430515305163051730518305193052030521305223052330524305253052630527305283052930530305313053230533305343053530536305373053830539305403054130542305433054430545305463054730548305493055030551305523055330554305553055630557305583055930560305613056230563305643056530566305673056830569305703057130572305733057430575305763057730578305793058030581305823058330584305853058630587305883058930590305913059230593305943059530596305973059830599306003060130602306033060430605306063060730608306093061030611306123061330614306153061630617306183061930620306213062230623306243062530626306273062830629306303063130632306333063430635306363063730638306393064030641306423064330644306453064630647306483064930650306513065230653306543065530656306573065830659306603066130662306633066430665306663066730668306693067030671306723067330674306753067630677306783067930680306813068230683306843068530686306873068830689306903069130692306933069430695306963069730698306993070030701307023070330704307053070630707307083070930710307113071230713307143071530716307173071830719307203072130722307233072430725307263072730728307293073030731307323073330734307353073630737307383073930740307413074230743307443074530746307473074830749307503075130752307533075430755307563075730758307593076030761307623076330764307653076630767307683076930770307713077230773307743077530776307773077830779307803078130782307833078430785307863078730788307893079030791307923079330794307953079630797307983079930800308013080230803308043080530806308073080830809308103081130812308133081430815308163081730818308193082030821308223082330824308253082630827308283082930830308313083230833308343083530836308373083830839308403084130842308433084430845308463084730848308493085030851308523085330854308553085630857308583085930860308613086230863308643086530866308673086830869308703087130872308733087430875308763087730878308793088030881308823088330884308853088630887308883088930890308913089230893308943089530896308973089830899309003090130902309033090430905309063090730908309093091030911309123091330914309153091630917309183091930920309213092230923309243092530926309273092830929309303093130932309333093430935309363093730938309393094030941309423094330944309453094630947309483094930950309513095230953309543095530956309573095830959309603096130962309633096430965309663096730968309693097030971309723097330974309753097630977309783097930980309813098230983309843098530986309873098830989309903099130992309933099430995309963099730998309993100031001310023100331004310053100631007310083100931010310113101231013310143101531016310173101831019310203102131022310233102431025310263102731028310293103031031310323103331034310353103631037310383103931040310413104231043310443104531046310473104831049310503105131052310533105431055310563105731058310593106031061310623106331064310653106631067310683106931070310713107231073310743107531076310773107831079310803108131082310833108431085310863108731088310893109031091310923109331094310953109631097310983109931100311013110231103311043110531106311073110831109311103111131112311133111431115311163111731118311193112031121311223112331124311253112631127311283112931130311313113231133311343113531136311373113831139311403114131142311433114431145311463114731148311493115031151311523115331154311553115631157311583115931160311613116231163311643116531166311673116831169311703117131172311733117431175311763117731178311793118031181311823118331184311853118631187311883118931190311913119231193311943119531196311973119831199312003120131202312033120431205312063120731208312093121031211312123121331214312153121631217312183121931220312213122231223312243122531226312273122831229312303123131232312333123431235312363123731238312393124031241312423124331244312453124631247312483124931250312513125231253312543125531256312573125831259312603126131262312633126431265312663126731268312693127031271312723127331274312753127631277312783127931280312813128231283312843128531286312873128831289312903129131292312933129431295312963129731298312993130031301313023130331304313053130631307313083130931310313113131231313313143131531316313173131831319313203132131322313233132431325313263132731328313293133031331313323133331334313353133631337313383133931340313413134231343313443134531346313473134831349313503135131352313533135431355313563135731358313593136031361313623136331364313653136631367313683136931370313713137231373313743137531376313773137831379313803138131382313833138431385313863138731388313893139031391313923139331394313953139631397313983139931400314013140231403314043140531406314073140831409314103141131412314133141431415314163141731418314193142031421314223142331424314253142631427314283142931430314313143231433314343143531436314373143831439314403144131442314433144431445314463144731448314493145031451314523145331454314553145631457314583145931460314613146231463314643146531466314673146831469314703147131472314733147431475314763147731478314793148031481314823148331484314853148631487314883148931490314913149231493314943149531496314973149831499315003150131502315033150431505315063150731508315093151031511315123151331514315153151631517315183151931520315213152231523315243152531526315273152831529315303153131532315333153431535315363153731538315393154031541315423154331544315453154631547315483154931550315513155231553315543155531556315573155831559315603156131562315633156431565315663156731568315693157031571315723157331574315753157631577315783157931580315813158231583315843158531586315873158831589315903159131592315933159431595315963159731598315993160031601316023160331604316053160631607316083160931610316113161231613316143161531616316173161831619316203162131622316233162431625316263162731628316293163031631316323163331634316353163631637316383163931640316413164231643316443164531646316473164831649316503165131652316533165431655316563165731658316593166031661316623166331664316653166631667316683166931670316713167231673316743167531676316773167831679316803168131682316833168431685316863168731688316893169031691316923169331694316953169631697316983169931700317013170231703317043170531706317073170831709317103171131712317133171431715317163171731718317193172031721317223172331724317253172631727317283172931730317313173231733317343173531736317373173831739317403174131742317433174431745317463174731748317493175031751317523175331754317553175631757317583175931760317613176231763317643176531766317673176831769317703177131772317733177431775317763177731778317793178031781317823178331784317853178631787317883178931790317913179231793317943179531796317973179831799318003180131802318033180431805318063180731808318093181031811318123181331814318153181631817318183181931820318213182231823318243182531826318273182831829318303183131832318333183431835318363183731838318393184031841318423184331844318453184631847318483184931850318513185231853318543185531856318573185831859318603186131862318633186431865318663186731868318693187031871318723187331874318753187631877318783187931880318813188231883318843188531886318873188831889318903189131892318933189431895318963189731898318993190031901319023190331904319053190631907319083190931910319113191231913319143191531916319173191831919319203192131922319233192431925319263192731928319293193031931319323193331934319353193631937319383193931940319413194231943319443194531946319473194831949319503195131952319533195431955319563195731958319593196031961319623196331964319653196631967319683196931970319713197231973319743197531976319773197831979319803198131982319833198431985319863198731988319893199031991319923199331994319953199631997319983199932000320013200232003320043200532006320073200832009320103201132012320133201432015320163201732018320193202032021320223202332024320253202632027320283202932030320313203232033320343203532036320373203832039320403204132042320433204432045320463204732048320493205032051320523205332054320553205632057320583205932060320613206232063320643206532066320673206832069320703207132072320733207432075320763207732078320793208032081320823208332084320853208632087320883208932090320913209232093320943209532096320973209832099321003210132102321033210432105321063210732108321093211032111321123211332114321153211632117321183211932120321213212232123321243212532126321273212832129321303213132132321333213432135321363213732138321393214032141321423214332144321453214632147321483214932150321513215232153321543215532156321573215832159321603216132162321633216432165321663216732168321693217032171321723217332174321753217632177321783217932180321813218232183321843218532186321873218832189321903219132192321933219432195321963219732198321993220032201322023220332204322053220632207322083220932210322113221232213322143221532216322173221832219322203222132222322233222432225322263222732228322293223032231322323223332234322353223632237322383223932240322413224232243322443224532246322473224832249322503225132252322533225432255322563225732258322593226032261322623226332264322653226632267322683226932270322713227232273322743227532276322773227832279322803228132282322833228432285322863228732288322893229032291322923229332294322953229632297322983229932300323013230232303323043230532306323073230832309323103231132312323133231432315323163231732318323193232032321323223232332324323253232632327323283232932330323313233232333323343233532336323373233832339323403234132342323433234432345323463234732348323493235032351323523235332354323553235632357323583235932360323613236232363323643236532366323673236832369323703237132372323733237432375323763237732378323793238032381323823238332384323853238632387323883238932390323913239232393323943239532396323973239832399324003240132402324033240432405324063240732408324093241032411324123241332414324153241632417324183241932420324213242232423324243242532426324273242832429324303243132432324333243432435324363243732438324393244032441324423244332444324453244632447324483244932450324513245232453324543245532456324573245832459324603246132462324633246432465324663246732468324693247032471324723247332474324753247632477324783247932480324813248232483324843248532486324873248832489324903249132492324933249432495324963249732498324993250032501325023250332504325053250632507325083250932510325113251232513325143251532516325173251832519325203252132522325233252432525325263252732528325293253032531325323253332534325353253632537325383253932540325413254232543325443254532546325473254832549325503255132552325533255432555325563255732558325593256032561325623256332564325653256632567325683256932570325713257232573325743257532576325773257832579325803258132582325833258432585325863258732588325893259032591325923259332594325953259632597325983259932600326013260232603326043260532606326073260832609326103261132612326133261432615326163261732618326193262032621326223262332624326253262632627326283262932630326313263232633326343263532636326373263832639326403264132642326433264432645326463264732648326493265032651326523265332654326553265632657326583265932660326613266232663326643266532666326673266832669326703267132672326733267432675326763267732678326793268032681326823268332684326853268632687326883268932690326913269232693326943269532696326973269832699327003270132702327033270432705327063270732708327093271032711327123271332714327153271632717327183271932720327213272232723327243272532726327273272832729327303273132732327333273432735327363273732738327393274032741327423274332744327453274632747327483274932750327513275232753327543275532756327573275832759327603276132762327633276432765327663276732768327693277032771327723277332774327753277632777327783277932780327813278232783327843278532786327873278832789327903279132792327933279432795327963279732798327993280032801328023280332804328053280632807328083280932810328113281232813328143281532816328173281832819328203282132822328233282432825328263282732828328293283032831328323283332834328353283632837328383283932840328413284232843328443284532846328473284832849328503285132852328533285432855328563285732858328593286032861328623286332864328653286632867328683286932870328713287232873328743287532876328773287832879328803288132882328833288432885328863288732888328893289032891328923289332894328953289632897328983289932900329013290232903329043290532906329073290832909329103291132912329133291432915329163291732918329193292032921329223292332924329253292632927329283292932930329313293232933329343293532936329373293832939329403294132942329433294432945329463294732948329493295032951329523295332954329553295632957329583295932960329613296232963329643296532966329673296832969329703297132972329733297432975329763297732978329793298032981329823298332984329853298632987329883298932990329913299232993329943299532996329973299832999330003300133002330033300433005330063300733008330093301033011330123301333014330153301633017330183301933020330213302233023330243302533026330273302833029330303303133032330333303433035330363303733038330393304033041330423304333044330453304633047330483304933050330513305233053330543305533056330573305833059330603306133062330633306433065330663306733068330693307033071330723307333074330753307633077330783307933080330813308233083330843308533086330873308833089330903309133092330933309433095330963309733098330993310033101331023310333104331053310633107331083310933110331113311233113331143311533116331173311833119331203312133122331233312433125331263312733128331293313033131331323313333134331353313633137331383313933140331413314233143331443314533146331473314833149331503315133152331533315433155331563315733158331593316033161331623316333164331653316633167331683316933170331713317233173331743317533176331773317833179331803318133182331833318433185331863318733188331893319033191331923319333194331953319633197331983319933200332013320233203332043320533206332073320833209332103321133212332133321433215332163321733218332193322033221332223322333224332253322633227332283322933230332313323233233332343323533236332373323833239332403324133242332433324433245332463324733248332493325033251332523325333254332553325633257332583325933260332613326233263332643326533266332673326833269332703327133272332733327433275332763327733278332793328033281332823328333284332853328633287332883328933290332913329233293332943329533296332973329833299333003330133302333033330433305333063330733308333093331033311333123331333314333153331633317333183331933320333213332233323333243332533326333273332833329333303333133332333333333433335333363333733338333393334033341333423334333344333453334633347333483334933350333513335233353333543335533356333573335833359333603336133362333633336433365333663336733368333693337033371333723337333374333753337633377333783337933380333813338233383333843338533386333873338833389333903339133392333933339433395333963339733398333993340033401334023340333404334053340633407334083340933410334113341233413334143341533416334173341833419334203342133422334233342433425334263342733428334293343033431334323343333434334353343633437334383343933440334413344233443334443344533446334473344833449334503345133452334533345433455334563345733458334593346033461334623346333464334653346633467334683346933470334713347233473334743347533476334773347833479334803348133482334833348433485334863348733488334893349033491334923349333494334953349633497334983349933500335013350233503335043350533506335073350833509335103351133512335133351433515335163351733518335193352033521335223352333524335253352633527335283352933530335313353233533335343353533536335373353833539335403354133542335433354433545335463354733548335493355033551335523355333554335553355633557335583355933560335613356233563335643356533566335673356833569335703357133572335733357433575335763357733578335793358033581335823358333584335853358633587335883358933590335913359233593335943359533596335973359833599336003360133602336033360433605336063360733608336093361033611336123361333614336153361633617336183361933620336213362233623336243362533626336273362833629336303363133632336333363433635336363363733638336393364033641336423364333644336453364633647336483364933650336513365233653336543365533656336573365833659336603366133662336633366433665336663366733668336693367033671336723367333674336753367633677336783367933680336813368233683336843368533686336873368833689336903369133692336933369433695336963369733698336993370033701337023370333704337053370633707337083370933710337113371233713337143371533716337173371833719337203372133722337233372433725337263372733728337293373033731337323373333734337353373633737337383373933740337413374233743337443374533746337473374833749337503375133752337533375433755337563375733758337593376033761337623376333764337653376633767337683376933770337713377233773337743377533776337773377833779337803378133782337833378433785337863378733788337893379033791337923379333794337953379633797337983379933800338013380233803338043380533806338073380833809338103381133812338133381433815338163381733818338193382033821338223382333824338253382633827338283382933830338313383233833338343383533836338373383833839338403384133842338433384433845338463384733848338493385033851338523385333854338553385633857338583385933860338613386233863338643386533866338673386833869338703387133872338733387433875338763387733878338793388033881338823388333884338853388633887338883388933890338913389233893338943389533896338973389833899339003390133902339033390433905339063390733908339093391033911339123391333914339153391633917339183391933920339213392233923339243392533926339273392833929339303393133932339333393433935339363393733938339393394033941339423394333944339453394633947339483394933950339513395233953339543395533956339573395833959339603396133962339633396433965339663396733968339693397033971339723397333974339753397633977339783397933980339813398233983339843398533986339873398833989339903399133992339933399433995339963399733998339993400034001340023400334004340053400634007340083400934010340113401234013340143401534016340173401834019340203402134022340233402434025340263402734028340293403034031340323403334034340353403634037340383403934040340413404234043340443404534046340473404834049340503405134052340533405434055340563405734058340593406034061340623406334064340653406634067340683406934070340713407234073340743407534076340773407834079340803408134082340833408434085340863408734088340893409034091340923409334094340953409634097340983409934100341013410234103341043410534106341073410834109341103411134112341133411434115341163411734118341193412034121341223412334124341253412634127341283412934130341313413234133341343413534136341373413834139341403414134142341433414434145341463414734148341493415034151341523415334154341553415634157341583415934160341613416234163341643416534166341673416834169341703417134172341733417434175341763417734178341793418034181341823418334184341853418634187341883418934190341913419234193341943419534196341973419834199342003420134202342033420434205342063420734208342093421034211342123421334214342153421634217342183421934220342213422234223342243422534226342273422834229342303423134232342333423434235342363423734238342393424034241342423424334244342453424634247342483424934250342513425234253342543425534256342573425834259342603426134262342633426434265342663426734268342693427034271342723427334274342753427634277342783427934280342813428234283342843428534286342873428834289342903429134292342933429434295342963429734298342993430034301343023430334304343053430634307343083430934310343113431234313343143431534316343173431834319343203432134322343233432434325343263432734328343293433034331343323433334334343353433634337343383433934340343413434234343343443434534346343473434834349343503435134352343533435434355343563435734358343593436034361343623436334364343653436634367343683436934370343713437234373343743437534376343773437834379343803438134382343833438434385343863438734388343893439034391343923439334394343953439634397343983439934400344013440234403344043440534406344073440834409344103441134412344133441434415344163441734418344193442034421344223442334424344253442634427344283442934430344313443234433344343443534436344373443834439344403444134442344433444434445344463444734448344493445034451344523445334454344553445634457344583445934460344613446234463344643446534466344673446834469344703447134472344733447434475344763447734478344793448034481344823448334484344853448634487344883448934490344913449234493344943449534496344973449834499345003450134502345033450434505345063450734508345093451034511345123451334514345153451634517345183451934520345213452234523345243452534526345273452834529345303453134532345333453434535345363453734538345393454034541345423454334544345453454634547345483454934550345513455234553345543455534556345573455834559345603456134562345633456434565345663456734568345693457034571345723457334574345753457634577345783457934580345813458234583345843458534586345873458834589345903459134592345933459434595345963459734598345993460034601346023460334604346053460634607346083460934610346113461234613346143461534616346173461834619346203462134622346233462434625346263462734628346293463034631346323463334634346353463634637346383463934640346413464234643346443464534646346473464834649346503465134652346533465434655346563465734658346593466034661346623466334664346653466634667346683466934670346713467234673346743467534676346773467834679346803468134682346833468434685346863468734688346893469034691346923469334694346953469634697346983469934700347013470234703347043470534706347073470834709347103471134712347133471434715347163471734718347193472034721347223472334724347253472634727347283472934730347313473234733347343473534736347373473834739347403474134742347433474434745347463474734748347493475034751347523475334754347553475634757347583475934760347613476234763347643476534766347673476834769347703477134772347733477434775347763477734778347793478034781347823478334784347853478634787347883478934790347913479234793347943479534796347973479834799348003480134802348033480434805348063480734808348093481034811348123481334814348153481634817348183481934820348213482234823348243482534826348273482834829348303483134832348333483434835348363483734838348393484034841348423484334844348453484634847348483484934850348513485234853348543485534856348573485834859348603486134862348633486434865348663486734868348693487034871348723487334874348753487634877348783487934880348813488234883348843488534886348873488834889348903489134892348933489434895348963489734898348993490034901349023490334904349053490634907349083490934910349113491234913349143491534916349173491834919349203492134922349233492434925349263492734928349293493034931349323493334934349353493634937349383493934940349413494234943349443494534946349473494834949349503495134952349533495434955349563495734958349593496034961349623496334964349653496634967349683496934970349713497234973349743497534976349773497834979349803498134982349833498434985349863498734988349893499034991349923499334994349953499634997349983499935000350013500235003350043500535006350073500835009350103501135012350133501435015350163501735018350193502035021350223502335024350253502635027350283502935030350313503235033350343503535036350373503835039350403504135042350433504435045350463504735048350493505035051350523505335054350553505635057350583505935060350613506235063350643506535066350673506835069350703507135072350733507435075350763507735078350793508035081350823508335084350853508635087350883508935090350913509235093350943509535096350973509835099351003510135102351033510435105351063510735108351093511035111351123511335114351153511635117351183511935120351213512235123351243512535126351273512835129351303513135132351333513435135351363513735138351393514035141351423514335144351453514635147351483514935150351513515235153351543515535156351573515835159351603516135162351633516435165351663516735168351693517035171351723517335174351753517635177351783517935180351813518235183351843518535186351873518835189351903519135192351933519435195351963519735198351993520035201352023520335204352053520635207352083520935210352113521235213352143521535216352173521835219352203522135222352233522435225352263522735228352293523035231352323523335234352353523635237352383523935240352413524235243352443524535246352473524835249352503525135252352533525435255352563525735258352593526035261352623526335264352653526635267352683526935270352713527235273352743527535276352773527835279352803528135282352833528435285352863528735288352893529035291352923529335294352953529635297352983529935300353013530235303353043530535306353073530835309353103531135312353133531435315353163531735318353193532035321353223532335324353253532635327353283532935330353313533235333353343533535336353373533835339353403534135342353433534435345353463534735348353493535035351353523535335354353553535635357353583535935360353613536235363353643536535366353673536835369353703537135372353733537435375353763537735378353793538035381353823538335384353853538635387353883538935390353913539235393353943539535396353973539835399354003540135402354033540435405354063540735408354093541035411354123541335414354153541635417354183541935420354213542235423354243542535426354273542835429354303543135432354333543435435354363543735438354393544035441354423544335444354453544635447354483544935450354513545235453354543545535456354573545835459354603546135462354633546435465354663546735468354693547035471354723547335474354753547635477354783547935480354813548235483354843548535486354873548835489354903549135492354933549435495354963549735498354993550035501355023550335504355053550635507355083550935510355113551235513355143551535516355173551835519355203552135522355233552435525355263552735528355293553035531355323553335534355353553635537355383553935540355413554235543355443554535546355473554835549355503555135552355533555435555355563555735558355593556035561355623556335564355653556635567355683556935570355713557235573355743557535576355773557835579355803558135582355833558435585355863558735588355893559035591355923559335594355953559635597355983559935600356013560235603356043560535606356073560835609356103561135612356133561435615356163561735618356193562035621356223562335624356253562635627356283562935630356313563235633356343563535636356373563835639356403564135642356433564435645356463564735648356493565035651356523565335654356553565635657356583565935660356613566235663356643566535666356673566835669356703567135672356733567435675356763567735678356793568035681356823568335684356853568635687356883568935690356913569235693356943569535696356973569835699357003570135702357033570435705357063570735708357093571035711357123571335714357153571635717357183571935720357213572235723357243572535726357273572835729357303573135732357333573435735357363573735738357393574035741357423574335744357453574635747357483574935750357513575235753357543575535756357573575835759357603576135762357633576435765357663576735768357693577035771357723577335774357753577635777357783577935780357813578235783357843578535786357873578835789357903579135792357933579435795357963579735798357993580035801358023580335804358053580635807358083580935810358113581235813358143581535816358173581835819358203582135822358233582435825358263582735828358293583035831358323583335834358353583635837358383583935840358413584235843358443584535846358473584835849358503585135852358533585435855358563585735858358593586035861358623586335864358653586635867358683586935870358713587235873358743587535876358773587835879358803588135882358833588435885358863588735888358893589035891358923589335894358953589635897358983589935900359013590235903359043590535906359073590835909359103591135912359133591435915359163591735918359193592035921359223592335924359253592635927359283592935930359313593235933359343593535936359373593835939359403594135942359433594435945359463594735948359493595035951359523595335954359553595635957359583595935960359613596235963359643596535966359673596835969359703597135972359733597435975359763597735978359793598035981359823598335984359853598635987359883598935990359913599235993359943599535996359973599835999360003600136002360033600436005360063600736008360093601036011360123601336014360153601636017360183601936020360213602236023360243602536026360273602836029360303603136032360333603436035360363603736038360393604036041360423604336044360453604636047360483604936050360513605236053360543605536056360573605836059360603606136062360633606436065360663606736068360693607036071360723607336074360753607636077360783607936080360813608236083360843608536086360873608836089360903609136092360933609436095360963609736098360993610036101361023610336104361053610636107361083610936110361113611236113361143611536116361173611836119361203612136122361233612436125361263612736128361293613036131361323613336134361353613636137361383613936140361413614236143361443614536146361473614836149361503615136152361533615436155361563615736158361593616036161361623616336164361653616636167361683616936170361713617236173361743617536176361773617836179361803618136182361833618436185361863618736188361893619036191361923619336194361953619636197361983619936200362013620236203362043620536206362073620836209362103621136212362133621436215362163621736218362193622036221362223622336224362253622636227362283622936230362313623236233362343623536236362373623836239362403624136242362433624436245362463624736248362493625036251362523625336254362553625636257362583625936260362613626236263362643626536266362673626836269362703627136272362733627436275362763627736278362793628036281362823628336284362853628636287362883628936290362913629236293362943629536296362973629836299363003630136302363033630436305363063630736308363093631036311363123631336314363153631636317363183631936320363213632236323363243632536326363273632836329363303633136332363333633436335363363633736338363393634036341363423634336344363453634636347363483634936350363513635236353363543635536356363573635836359363603636136362363633636436365363663636736368363693637036371363723637336374363753637636377363783637936380363813638236383363843638536386363873638836389363903639136392363933639436395363963639736398363993640036401364023640336404364053640636407364083640936410364113641236413364143641536416364173641836419364203642136422364233642436425364263642736428364293643036431364323643336434364353643636437364383643936440364413644236443364443644536446364473644836449364503645136452364533645436455364563645736458364593646036461364623646336464364653646636467364683646936470364713647236473364743647536476364773647836479364803648136482364833648436485364863648736488364893649036491364923649336494364953649636497364983649936500365013650236503365043650536506365073650836509365103651136512365133651436515365163651736518365193652036521365223652336524365253652636527365283652936530365313653236533365343653536536365373653836539365403654136542365433654436545365463654736548365493655036551365523655336554365553655636557365583655936560365613656236563365643656536566365673656836569365703657136572365733657436575365763657736578365793658036581365823658336584365853658636587365883658936590365913659236593365943659536596365973659836599366003660136602366033660436605366063660736608366093661036611366123661336614366153661636617366183661936620366213662236623366243662536626366273662836629366303663136632366333663436635366363663736638366393664036641366423664336644366453664636647366483664936650366513665236653366543665536656366573665836659366603666136662366633666436665366663666736668366693667036671366723667336674366753667636677366783667936680366813668236683366843668536686366873668836689366903669136692366933669436695366963669736698366993670036701367023670336704367053670636707367083670936710367113671236713367143671536716367173671836719367203672136722367233672436725367263672736728367293673036731367323673336734367353673636737367383673936740367413674236743367443674536746367473674836749367503675136752367533675436755367563675736758367593676036761367623676336764367653676636767367683676936770367713677236773367743677536776367773677836779367803678136782367833678436785367863678736788367893679036791367923679336794367953679636797367983679936800368013680236803368043680536806368073680836809368103681136812368133681436815368163681736818368193682036821368223682336824368253682636827368283682936830368313683236833368343683536836368373683836839368403684136842368433684436845368463684736848368493685036851368523685336854368553685636857368583685936860368613686236863368643686536866368673686836869368703687136872368733687436875368763687736878368793688036881368823688336884368853688636887368883688936890368913689236893368943689536896368973689836899369003690136902369033690436905369063690736908369093691036911369123691336914369153691636917369183691936920369213692236923369243692536926369273692836929369303693136932369333693436935369363693736938369393694036941369423694336944369453694636947369483694936950369513695236953369543695536956369573695836959369603696136962369633696436965369663696736968369693697036971369723697336974369753697636977369783697936980369813698236983369843698536986369873698836989369903699136992369933699436995369963699736998369993700037001370023700337004370053700637007370083700937010370113701237013370143701537016370173701837019370203702137022370233702437025370263702737028370293703037031370323703337034370353703637037370383703937040370413704237043370443704537046370473704837049370503705137052370533705437055370563705737058370593706037061370623706337064370653706637067370683706937070370713707237073370743707537076370773707837079370803708137082370833708437085370863708737088370893709037091370923709337094370953709637097370983709937100371013710237103371043710537106371073710837109371103711137112371133711437115371163711737118371193712037121371223712337124371253712637127371283712937130371313713237133371343713537136371373713837139371403714137142371433714437145371463714737148371493715037151371523715337154371553715637157371583715937160371613716237163371643716537166371673716837169371703717137172371733717437175371763717737178371793718037181371823718337184371853718637187371883718937190371913719237193371943719537196371973719837199372003720137202372033720437205372063720737208372093721037211372123721337214372153721637217372183721937220372213722237223372243722537226372273722837229372303723137232372333723437235372363723737238372393724037241372423724337244372453724637247372483724937250372513725237253372543725537256372573725837259372603726137262372633726437265372663726737268372693727037271372723727337274372753727637277372783727937280372813728237283372843728537286372873728837289372903729137292372933729437295372963729737298372993730037301373023730337304373053730637307373083730937310373113731237313373143731537316373173731837319373203732137322373233732437325373263732737328373293733037331373323733337334373353733637337373383733937340373413734237343373443734537346373473734837349373503735137352373533735437355373563735737358373593736037361373623736337364373653736637367373683736937370373713737237373373743737537376373773737837379373803738137382373833738437385373863738737388373893739037391373923739337394373953739637397373983739937400374013740237403374043740537406374073740837409374103741137412374133741437415374163741737418374193742037421374223742337424374253742637427374283742937430374313743237433374343743537436374373743837439374403744137442374433744437445374463744737448374493745037451374523745337454374553745637457374583745937460374613746237463374643746537466374673746837469374703747137472374733747437475374763747737478374793748037481374823748337484374853748637487374883748937490374913749237493374943749537496374973749837499375003750137502375033750437505375063750737508375093751037511375123751337514375153751637517375183751937520375213752237523375243752537526375273752837529375303753137532375333753437535375363753737538375393754037541375423754337544375453754637547375483754937550375513755237553375543755537556375573755837559375603756137562375633756437565375663756737568375693757037571375723757337574375753757637577375783757937580375813758237583375843758537586375873758837589375903759137592375933759437595375963759737598375993760037601376023760337604376053760637607376083760937610376113761237613376143761537616376173761837619376203762137622376233762437625376263762737628376293763037631376323763337634376353763637637376383763937640376413764237643376443764537646376473764837649376503765137652376533765437655376563765737658376593766037661376623766337664376653766637667376683766937670376713767237673376743767537676376773767837679376803768137682376833768437685376863768737688376893769037691376923769337694376953769637697376983769937700377013770237703377043770537706377073770837709377103771137712377133771437715377163771737718377193772037721377223772337724377253772637727377283772937730377313773237733377343773537736377373773837739377403774137742377433774437745377463774737748377493775037751377523775337754377553775637757377583775937760377613776237763377643776537766377673776837769377703777137772377733777437775377763777737778377793778037781377823778337784377853778637787377883778937790377913779237793377943779537796377973779837799378003780137802378033780437805378063780737808378093781037811378123781337814378153781637817378183781937820378213782237823378243782537826378273782837829378303783137832378333783437835378363783737838378393784037841378423784337844378453784637847378483784937850378513785237853378543785537856378573785837859378603786137862378633786437865378663786737868378693787037871378723787337874378753787637877378783787937880378813788237883378843788537886378873788837889378903789137892378933789437895378963789737898378993790037901379023790337904379053790637907379083790937910379113791237913379143791537916379173791837919379203792137922379233792437925379263792737928379293793037931379323793337934379353793637937379383793937940379413794237943379443794537946379473794837949379503795137952379533795437955379563795737958379593796037961379623796337964379653796637967379683796937970379713797237973379743797537976379773797837979379803798137982379833798437985379863798737988379893799037991379923799337994379953799637997379983799938000380013800238003380043800538006380073800838009380103801138012380133801438015380163801738018380193802038021380223802338024380253802638027380283802938030380313803238033380343803538036380373803838039380403804138042380433804438045380463804738048380493805038051380523805338054380553805638057380583805938060380613806238063380643806538066380673806838069380703807138072380733807438075380763807738078380793808038081380823808338084380853808638087380883808938090380913809238093380943809538096380973809838099381003810138102381033810438105381063810738108381093811038111381123811338114381153811638117381183811938120381213812238123381243812538126381273812838129381303813138132381333813438135381363813738138381393814038141381423814338144381453814638147381483814938150381513815238153381543815538156381573815838159381603816138162381633816438165381663816738168381693817038171381723817338174381753817638177381783817938180381813818238183381843818538186381873818838189381903819138192381933819438195381963819738198381993820038201382023820338204382053820638207382083820938210382113821238213382143821538216382173821838219382203822138222382233822438225382263822738228382293823038231382323823338234382353823638237382383823938240382413824238243382443824538246382473824838249382503825138252382533825438255382563825738258382593826038261382623826338264382653826638267382683826938270382713827238273382743827538276382773827838279382803828138282382833828438285382863828738288382893829038291382923829338294382953829638297382983829938300383013830238303383043830538306383073830838309383103831138312383133831438315383163831738318383193832038321383223832338324383253832638327383283832938330383313833238333383343833538336383373833838339383403834138342383433834438345383463834738348383493835038351383523835338354383553835638357383583835938360383613836238363383643836538366383673836838369383703837138372383733837438375383763837738378383793838038381383823838338384383853838638387383883838938390383913839238393383943839538396383973839838399384003840138402384033840438405384063840738408384093841038411384123841338414384153841638417384183841938420384213842238423384243842538426384273842838429384303843138432384333843438435384363843738438384393844038441384423844338444384453844638447384483844938450384513845238453384543845538456384573845838459384603846138462384633846438465384663846738468384693847038471384723847338474384753847638477384783847938480384813848238483384843848538486384873848838489384903849138492384933849438495384963849738498384993850038501385023850338504385053850638507385083850938510385113851238513385143851538516385173851838519385203852138522385233852438525385263852738528385293853038531385323853338534385353853638537385383853938540385413854238543385443854538546385473854838549385503855138552385533855438555385563855738558385593856038561385623856338564385653856638567385683856938570385713857238573385743857538576385773857838579385803858138582385833858438585385863858738588385893859038591385923859338594385953859638597385983859938600386013860238603386043860538606386073860838609386103861138612386133861438615386163861738618386193862038621386223862338624386253862638627386283862938630386313863238633386343863538636386373863838639386403864138642386433864438645386463864738648386493865038651386523865338654386553865638657386583865938660386613866238663386643866538666386673866838669386703867138672386733867438675386763867738678386793868038681386823868338684386853868638687386883868938690386913869238693386943869538696386973869838699387003870138702387033870438705387063870738708387093871038711387123871338714387153871638717387183871938720387213872238723387243872538726387273872838729387303873138732387333873438735387363873738738387393874038741387423874338744387453874638747387483874938750387513875238753387543875538756387573875838759387603876138762387633876438765387663876738768387693877038771387723877338774387753877638777387783877938780387813878238783387843878538786387873878838789387903879138792387933879438795387963879738798387993880038801388023880338804388053880638807388083880938810388113881238813388143881538816388173881838819388203882138822388233882438825388263882738828388293883038831388323883338834388353883638837388383883938840388413884238843388443884538846388473884838849388503885138852388533885438855388563885738858388593886038861388623886338864388653886638867388683886938870388713887238873388743887538876388773887838879388803888138882388833888438885388863888738888388893889038891388923889338894388953889638897388983889938900389013890238903389043890538906389073890838909389103891138912389133891438915389163891738918389193892038921389223892338924389253892638927389283892938930389313893238933389343893538936389373893838939389403894138942389433894438945389463894738948389493895038951389523895338954389553895638957389583895938960389613896238963389643896538966389673896838969389703897138972389733897438975389763897738978389793898038981389823898338984389853898638987389883898938990389913899238993389943899538996389973899838999390003900139002390033900439005390063900739008390093901039011390123901339014390153901639017390183901939020390213902239023390243902539026390273902839029390303903139032390333903439035390363903739038390393904039041390423904339044390453904639047390483904939050390513905239053390543905539056390573905839059390603906139062390633906439065390663906739068390693907039071390723907339074390753907639077390783907939080390813908239083390843908539086390873908839089390903909139092390933909439095390963909739098390993910039101391023910339104391053910639107391083910939110391113911239113391143911539116391173911839119391203912139122391233912439125391263912739128391293913039131391323913339134391353913639137391383913939140391413914239143391443914539146391473914839149391503915139152391533915439155391563915739158391593916039161391623916339164391653916639167391683916939170391713917239173391743917539176391773917839179391803918139182391833918439185391863918739188391893919039191391923919339194391953919639197391983919939200392013920239203392043920539206392073920839209392103921139212392133921439215392163921739218392193922039221392223922339224392253922639227392283922939230392313923239233392343923539236392373923839239392403924139242392433924439245392463924739248392493925039251392523925339254392553925639257392583925939260392613926239263392643926539266392673926839269392703927139272392733927439275392763927739278392793928039281392823928339284392853928639287392883928939290392913929239293392943929539296392973929839299393003930139302393033930439305393063930739308393093931039311393123931339314393153931639317393183931939320393213932239323393243932539326393273932839329393303933139332393333933439335393363933739338393393934039341393423934339344393453934639347393483934939350393513935239353393543935539356393573935839359393603936139362393633936439365393663936739368393693937039371393723937339374393753937639377393783937939380393813938239383393843938539386393873938839389393903939139392393933939439395393963939739398393993940039401394023940339404394053940639407394083940939410394113941239413394143941539416394173941839419394203942139422394233942439425394263942739428394293943039431394323943339434394353943639437394383943939440394413944239443394443944539446394473944839449394503945139452394533945439455394563945739458394593946039461394623946339464394653946639467394683946939470394713947239473394743947539476394773947839479394803948139482394833948439485394863948739488394893949039491394923949339494394953949639497394983949939500395013950239503395043950539506395073950839509395103951139512395133951439515395163951739518395193952039521395223952339524395253952639527395283952939530395313953239533395343953539536395373953839539395403954139542395433954439545395463954739548395493955039551395523955339554395553955639557395583955939560395613956239563395643956539566395673956839569395703957139572395733957439575395763957739578395793958039581395823958339584395853958639587395883958939590395913959239593395943959539596395973959839599396003960139602396033960439605396063960739608396093961039611396123961339614396153961639617396183961939620396213962239623396243962539626396273962839629396303963139632396333963439635396363963739638396393964039641396423964339644396453964639647396483964939650396513965239653396543965539656396573965839659396603966139662396633966439665396663966739668396693967039671396723967339674396753967639677396783967939680396813968239683396843968539686396873968839689396903969139692396933969439695396963969739698396993970039701397023970339704397053970639707397083970939710397113971239713397143971539716397173971839719397203972139722397233972439725397263972739728397293973039731397323973339734397353973639737397383973939740397413974239743397443974539746397473974839749397503975139752397533975439755397563975739758397593976039761397623976339764397653976639767397683976939770397713977239773397743977539776397773977839779397803978139782397833978439785397863978739788397893979039791397923979339794397953979639797397983979939800398013980239803398043980539806398073980839809398103981139812398133981439815398163981739818398193982039821398223982339824398253982639827398283982939830398313983239833398343983539836398373983839839398403984139842398433984439845398463984739848398493985039851398523985339854398553985639857398583985939860398613986239863398643986539866398673986839869398703987139872398733987439875398763987739878398793988039881398823988339884398853988639887398883988939890398913989239893398943989539896398973989839899399003990139902399033990439905399063990739908399093991039911399123991339914399153991639917399183991939920399213992239923399243992539926399273992839929399303993139932399333993439935399363993739938399393994039941399423994339944399453994639947399483994939950399513995239953399543995539956399573995839959399603996139962399633996439965399663996739968399693997039971399723997339974399753997639977399783997939980399813998239983399843998539986399873998839989399903999139992399933999439995399963999739998399994000040001400024000340004400054000640007400084000940010400114001240013400144001540016400174001840019400204002140022400234002440025400264002740028400294003040031400324003340034400354003640037400384003940040400414004240043400444004540046400474004840049400504005140052400534005440055400564005740058400594006040061400624006340064400654006640067400684006940070400714007240073400744007540076400774007840079400804008140082400834008440085400864008740088400894009040091400924009340094400954009640097400984009940100401014010240103401044010540106401074010840109401104011140112401134011440115401164011740118401194012040121401224012340124401254012640127401284012940130401314013240133401344013540136401374013840139401404014140142401434014440145401464014740148401494015040151401524015340154401554015640157401584015940160401614016240163401644016540166401674016840169401704017140172401734017440175401764017740178401794018040181401824018340184401854018640187401884018940190401914019240193401944019540196401974019840199402004020140202402034020440205402064020740208402094021040211402124021340214402154021640217402184021940220402214022240223402244022540226402274022840229402304023140232402334023440235402364023740238402394024040241402424024340244402454024640247402484024940250402514025240253402544025540256402574025840259402604026140262402634026440265402664026740268402694027040271402724027340274402754027640277402784027940280402814028240283402844028540286402874028840289402904029140292402934029440295402964029740298402994030040301403024030340304403054030640307403084030940310403114031240313403144031540316403174031840319403204032140322403234032440325403264032740328403294033040331403324033340334403354033640337403384033940340403414034240343403444034540346403474034840349403504035140352403534035440355403564035740358403594036040361403624036340364403654036640367403684036940370403714037240373403744037540376403774037840379403804038140382403834038440385403864038740388403894039040391403924039340394403954039640397403984039940400404014040240403404044040540406404074040840409404104041140412404134041440415404164041740418404194042040421404224042340424404254042640427404284042940430404314043240433404344043540436404374043840439404404044140442404434044440445404464044740448404494045040451404524045340454404554045640457404584045940460404614046240463404644046540466404674046840469404704047140472404734047440475404764047740478404794048040481404824048340484404854048640487404884048940490404914049240493404944049540496404974049840499405004050140502405034050440505405064050740508405094051040511405124051340514405154051640517405184051940520405214052240523405244052540526405274052840529405304053140532405334053440535405364053740538405394054040541405424054340544405454054640547405484054940550405514055240553405544055540556405574055840559405604056140562405634056440565405664056740568405694057040571405724057340574405754057640577405784057940580405814058240583405844058540586405874058840589405904059140592405934059440595405964059740598405994060040601406024060340604406054060640607406084060940610406114061240613406144061540616406174061840619406204062140622406234062440625406264062740628406294063040631406324063340634406354063640637406384063940640406414064240643406444064540646406474064840649406504065140652406534065440655406564065740658406594066040661406624066340664406654066640667406684066940670406714067240673406744067540676406774067840679406804068140682406834068440685406864068740688406894069040691406924069340694406954069640697406984069940700407014070240703407044070540706407074070840709407104071140712407134071440715407164071740718407194072040721407224072340724407254072640727407284072940730407314073240733407344073540736407374073840739407404074140742407434074440745407464074740748407494075040751407524075340754407554075640757407584075940760407614076240763407644076540766407674076840769407704077140772407734077440775407764077740778407794078040781407824078340784407854078640787407884078940790407914079240793407944079540796407974079840799408004080140802408034080440805408064080740808408094081040811408124081340814408154081640817408184081940820408214082240823408244082540826408274082840829408304083140832408334083440835408364083740838408394084040841408424084340844408454084640847408484084940850408514085240853408544085540856408574085840859408604086140862408634086440865408664086740868408694087040871408724087340874408754087640877408784087940880408814088240883408844088540886408874088840889408904089140892408934089440895408964089740898408994090040901409024090340904409054090640907409084090940910409114091240913409144091540916409174091840919409204092140922409234092440925409264092740928409294093040931409324093340934409354093640937409384093940940409414094240943409444094540946409474094840949409504095140952409534095440955409564095740958409594096040961409624096340964409654096640967409684096940970409714097240973409744097540976409774097840979409804098140982409834098440985409864098740988409894099040991409924099340994409954099640997409984099941000410014100241003410044100541006410074100841009410104101141012410134101441015410164101741018410194102041021410224102341024410254102641027410284102941030410314103241033410344103541036410374103841039410404104141042410434104441045410464104741048410494105041051410524105341054410554105641057410584105941060410614106241063410644106541066410674106841069410704107141072410734107441075410764107741078410794108041081410824108341084410854108641087410884108941090410914109241093410944109541096410974109841099411004110141102411034110441105411064110741108411094111041111411124111341114411154111641117411184111941120411214112241123411244112541126411274112841129411304113141132411334113441135411364113741138411394114041141411424114341144411454114641147411484114941150411514115241153411544115541156411574115841159411604116141162411634116441165411664116741168411694117041171411724117341174411754117641177411784117941180411814118241183411844118541186411874118841189411904119141192411934119441195411964119741198411994120041201412024120341204412054120641207412084120941210412114121241213412144121541216412174121841219412204122141222412234122441225412264122741228412294123041231412324123341234412354123641237412384123941240412414124241243412444124541246412474124841249412504125141252412534125441255412564125741258412594126041261412624126341264412654126641267412684126941270412714127241273412744127541276412774127841279412804128141282412834128441285412864128741288412894129041291412924129341294412954129641297412984129941300413014130241303413044130541306413074130841309413104131141312413134131441315413164131741318413194132041321413224132341324413254132641327413284132941330413314133241333413344133541336413374133841339413404134141342413434134441345413464134741348413494135041351413524135341354413554135641357413584135941360413614136241363413644136541366413674136841369413704137141372413734137441375413764137741378413794138041381413824138341384413854138641387413884138941390413914139241393413944139541396413974139841399414004140141402414034140441405414064140741408414094141041411414124141341414414154141641417414184141941420414214142241423414244142541426414274142841429414304143141432414334143441435414364143741438414394144041441414424144341444414454144641447414484144941450414514145241453414544145541456414574145841459414604146141462414634146441465414664146741468414694147041471414724147341474414754147641477414784147941480414814148241483414844148541486414874148841489414904149141492414934149441495414964149741498414994150041501415024150341504415054150641507415084150941510415114151241513415144151541516415174151841519415204152141522415234152441525415264152741528415294153041531415324153341534415354153641537415384153941540415414154241543415444154541546415474154841549415504155141552415534155441555415564155741558415594156041561415624156341564415654156641567415684156941570415714157241573415744157541576415774157841579415804158141582415834158441585415864158741588415894159041591415924159341594415954159641597415984159941600416014160241603416044160541606416074160841609416104161141612416134161441615416164161741618416194162041621416224162341624416254162641627416284162941630416314163241633416344163541636416374163841639416404164141642416434164441645416464164741648416494165041651416524165341654416554165641657416584165941660416614166241663416644166541666416674166841669416704167141672416734167441675416764167741678416794168041681416824168341684416854168641687416884168941690416914169241693416944169541696416974169841699417004170141702417034170441705417064170741708417094171041711417124171341714417154171641717417184171941720417214172241723417244172541726417274172841729417304173141732417334173441735417364173741738417394174041741417424174341744417454174641747417484174941750417514175241753417544175541756417574175841759417604176141762417634176441765417664176741768417694177041771417724177341774417754177641777417784177941780417814178241783417844178541786417874178841789417904179141792417934179441795417964179741798417994180041801418024180341804418054180641807418084180941810418114181241813418144181541816418174181841819418204182141822418234182441825418264182741828418294183041831418324183341834418354183641837418384183941840418414184241843418444184541846418474184841849418504185141852418534185441855418564185741858418594186041861418624186341864418654186641867418684186941870418714187241873418744187541876418774187841879418804188141882418834188441885418864188741888418894189041891418924189341894418954189641897418984189941900419014190241903419044190541906419074190841909419104191141912419134191441915419164191741918419194192041921419224192341924419254192641927419284192941930419314193241933419344193541936419374193841939419404194141942419434194441945419464194741948419494195041951419524195341954419554195641957419584195941960419614196241963419644196541966419674196841969419704197141972419734197441975419764197741978419794198041981419824198341984419854198641987419884198941990419914199241993419944199541996419974199841999420004200142002420034200442005420064200742008420094201042011420124201342014420154201642017420184201942020420214202242023420244202542026420274202842029420304203142032420334203442035420364203742038420394204042041420424204342044420454204642047420484204942050420514205242053420544205542056420574205842059420604206142062420634206442065420664206742068420694207042071420724207342074420754207642077420784207942080420814208242083420844208542086420874208842089420904209142092420934209442095420964209742098420994210042101421024210342104421054210642107421084210942110421114211242113421144211542116421174211842119421204212142122421234212442125421264212742128421294213042131421324213342134421354213642137421384213942140421414214242143421444214542146421474214842149421504215142152421534215442155421564215742158421594216042161421624216342164421654216642167421684216942170421714217242173421744217542176421774217842179421804218142182421834218442185421864218742188421894219042191421924219342194421954219642197421984219942200422014220242203422044220542206422074220842209422104221142212422134221442215422164221742218422194222042221422224222342224422254222642227422284222942230422314223242233422344223542236422374223842239422404224142242422434224442245422464224742248422494225042251422524225342254422554225642257422584225942260422614226242263422644226542266422674226842269422704227142272422734227442275422764227742278422794228042281422824228342284422854228642287422884228942290422914229242293422944229542296422974229842299423004230142302423034230442305423064230742308423094231042311423124231342314423154231642317423184231942320423214232242323423244232542326423274232842329423304233142332423334233442335423364233742338423394234042341423424234342344423454234642347423484234942350423514235242353423544235542356423574235842359423604236142362423634236442365423664236742368423694237042371423724237342374423754237642377423784237942380423814238242383423844238542386423874238842389423904239142392423934239442395423964239742398423994240042401424024240342404424054240642407424084240942410424114241242413424144241542416424174241842419424204242142422424234242442425424264242742428424294243042431424324243342434424354243642437424384243942440424414244242443424444244542446424474244842449424504245142452424534245442455424564245742458424594246042461424624246342464424654246642467424684246942470424714247242473424744247542476424774247842479424804248142482424834248442485424864248742488424894249042491424924249342494424954249642497424984249942500425014250242503425044250542506425074250842509425104251142512425134251442515425164251742518425194252042521425224252342524425254252642527425284252942530425314253242533425344253542536425374253842539425404254142542425434254442545425464254742548425494255042551425524255342554425554255642557425584255942560425614256242563425644256542566425674256842569425704257142572425734257442575425764257742578425794258042581425824258342584425854258642587425884258942590425914259242593425944259542596425974259842599426004260142602426034260442605426064260742608426094261042611426124261342614426154261642617426184261942620426214262242623426244262542626426274262842629426304263142632426334263442635426364263742638426394264042641426424264342644426454264642647426484264942650426514265242653426544265542656426574265842659426604266142662426634266442665426664266742668426694267042671426724267342674426754267642677426784267942680426814268242683426844268542686426874268842689426904269142692426934269442695426964269742698426994270042701427024270342704427054270642707427084270942710427114271242713427144271542716427174271842719427204272142722427234272442725427264272742728427294273042731427324273342734427354273642737427384273942740427414274242743427444274542746427474274842749427504275142752427534275442755427564275742758427594276042761427624276342764427654276642767427684276942770427714277242773427744277542776427774277842779427804278142782427834278442785427864278742788427894279042791427924279342794427954279642797427984279942800428014280242803428044280542806428074280842809428104281142812428134281442815428164281742818428194282042821428224282342824428254282642827428284282942830428314283242833428344283542836428374283842839428404284142842428434284442845428464284742848428494285042851428524285342854428554285642857428584285942860428614286242863428644286542866428674286842869428704287142872428734287442875428764287742878428794288042881428824288342884428854288642887428884288942890428914289242893428944289542896428974289842899429004290142902429034290442905429064290742908429094291042911429124291342914429154291642917429184291942920429214292242923429244292542926429274292842929429304293142932429334293442935429364293742938429394294042941429424294342944429454294642947429484294942950429514295242953429544295542956429574295842959429604296142962429634296442965429664296742968429694297042971429724297342974429754297642977429784297942980429814298242983429844298542986429874298842989429904299142992429934299442995429964299742998429994300043001430024300343004430054300643007430084300943010430114301243013430144301543016430174301843019430204302143022430234302443025430264302743028430294303043031430324303343034430354303643037430384303943040430414304243043430444304543046430474304843049430504305143052430534305443055430564305743058430594306043061430624306343064430654306643067430684306943070430714307243073430744307543076430774307843079430804308143082430834308443085430864308743088430894309043091430924309343094430954309643097430984309943100431014310243103431044310543106431074310843109431104311143112431134311443115431164311743118431194312043121431224312343124431254312643127431284312943130431314313243133431344313543136431374313843139431404314143142431434314443145431464314743148431494315043151431524315343154431554315643157431584315943160431614316243163431644316543166431674316843169431704317143172431734317443175431764317743178431794318043181431824318343184431854318643187431884318943190431914319243193431944319543196431974319843199432004320143202432034320443205432064320743208432094321043211432124321343214432154321643217432184321943220432214322243223432244322543226432274322843229432304323143232432334323443235432364323743238432394324043241432424324343244432454324643247432484324943250432514325243253432544325543256432574325843259432604326143262432634326443265432664326743268432694327043271432724327343274432754327643277432784327943280432814328243283432844328543286432874328843289432904329143292432934329443295432964329743298432994330043301433024330343304433054330643307433084330943310433114331243313433144331543316433174331843319433204332143322433234332443325433264332743328433294333043331433324333343334433354333643337433384333943340433414334243343433444334543346433474334843349433504335143352433534335443355433564335743358433594336043361433624336343364433654336643367433684336943370433714337243373433744337543376433774337843379433804338143382433834338443385433864338743388433894339043391433924339343394433954339643397433984339943400434014340243403434044340543406434074340843409434104341143412434134341443415434164341743418434194342043421434224342343424434254342643427434284342943430434314343243433434344343543436434374343843439434404344143442434434344443445434464344743448434494345043451434524345343454434554345643457434584345943460434614346243463434644346543466434674346843469434704347143472434734347443475434764347743478434794348043481434824348343484434854348643487434884348943490434914349243493434944349543496434974349843499435004350143502435034350443505435064350743508435094351043511435124351343514435154351643517435184351943520435214352243523435244352543526435274352843529435304353143532435334353443535435364353743538435394354043541435424354343544435454354643547435484354943550435514355243553435544355543556435574355843559435604356143562435634356443565435664356743568435694357043571435724357343574435754357643577435784357943580435814358243583435844358543586435874358843589435904359143592435934359443595435964359743598435994360043601436024360343604436054360643607436084360943610436114361243613436144361543616436174361843619436204362143622436234362443625436264362743628436294363043631436324363343634436354363643637436384363943640436414364243643436444364543646436474364843649436504365143652436534365443655436564365743658436594366043661436624366343664436654366643667436684366943670436714367243673436744367543676436774367843679436804368143682436834368443685436864368743688436894369043691436924369343694436954369643697436984369943700437014370243703437044370543706437074370843709437104371143712437134371443715437164371743718437194372043721437224372343724437254372643727437284372943730437314373243733437344373543736437374373843739437404374143742437434374443745437464374743748437494375043751437524375343754437554375643757437584375943760437614376243763437644376543766437674376843769437704377143772437734377443775437764377743778437794378043781437824378343784437854378643787437884378943790437914379243793437944379543796437974379843799438004380143802438034380443805438064380743808438094381043811438124381343814438154381643817438184381943820438214382243823438244382543826438274382843829438304383143832438334383443835438364383743838438394384043841438424384343844438454384643847438484384943850438514385243853438544385543856438574385843859438604386143862438634386443865438664386743868438694387043871438724387343874438754387643877438784387943880438814388243883438844388543886438874388843889438904389143892438934389443895438964389743898438994390043901439024390343904439054390643907439084390943910439114391243913439144391543916439174391843919439204392143922439234392443925439264392743928439294393043931439324393343934439354393643937439384393943940439414394243943439444394543946439474394843949439504395143952439534395443955439564395743958439594396043961439624396343964439654396643967439684396943970439714397243973439744397543976439774397843979439804398143982439834398443985439864398743988439894399043991439924399343994439954399643997439984399944000440014400244003440044400544006440074400844009440104401144012440134401444015440164401744018440194402044021440224402344024440254402644027440284402944030440314403244033440344403544036440374403844039440404404144042440434404444045440464404744048440494405044051440524405344054440554405644057440584405944060440614406244063440644406544066440674406844069440704407144072440734407444075440764407744078440794408044081440824408344084440854408644087440884408944090440914409244093440944409544096440974409844099441004410144102441034410444105441064410744108441094411044111441124411344114441154411644117441184411944120441214412244123441244412544126441274412844129441304413144132441334413444135441364413744138441394414044141441424414344144441454414644147441484414944150441514415244153441544415544156441574415844159441604416144162441634416444165441664416744168441694417044171441724417344174441754417644177441784417944180441814418244183441844418544186441874418844189441904419144192441934419444195441964419744198441994420044201442024420344204442054420644207442084420944210442114421244213442144421544216442174421844219442204422144222442234422444225442264422744228442294423044231442324423344234442354423644237442384423944240442414424244243442444424544246442474424844249442504425144252442534425444255442564425744258442594426044261442624426344264442654426644267442684426944270442714427244273442744427544276442774427844279442804428144282442834428444285442864428744288442894429044291442924429344294442954429644297442984429944300443014430244303443044430544306443074430844309443104431144312443134431444315443164431744318443194432044321443224432344324443254432644327443284432944330443314433244333443344433544336443374433844339443404434144342443434434444345443464434744348443494435044351443524435344354443554435644357443584435944360443614436244363443644436544366443674436844369443704437144372443734437444375443764437744378443794438044381443824438344384443854438644387443884438944390443914439244393443944439544396443974439844399444004440144402444034440444405444064440744408444094441044411444124441344414444154441644417444184441944420444214442244423444244442544426444274442844429444304443144432444334443444435444364443744438444394444044441444424444344444444454444644447444484444944450444514445244453444544445544456444574445844459444604446144462444634446444465444664446744468444694447044471444724447344474444754447644477444784447944480444814448244483444844448544486444874448844489444904449144492444934449444495444964449744498444994450044501445024450344504445054450644507445084450944510445114451244513445144451544516445174451844519445204452144522445234452444525445264452744528445294453044531445324453344534445354453644537445384453944540445414454244543445444454544546445474454844549445504455144552445534455444555445564455744558445594456044561445624456344564445654456644567445684456944570445714457244573445744457544576445774457844579445804458144582445834458444585445864458744588445894459044591445924459344594445954459644597445984459944600446014460244603446044460544606446074460844609446104461144612446134461444615446164461744618446194462044621446224462344624446254462644627446284462944630446314463244633446344463544636446374463844639446404464144642446434464444645446464464744648446494465044651446524465344654446554465644657446584465944660446614466244663446644466544666446674466844669446704467144672446734467444675446764467744678446794468044681446824468344684446854468644687446884468944690446914469244693446944469544696446974469844699447004470144702447034470444705447064470744708447094471044711447124471344714447154471644717447184471944720447214472244723447244472544726447274472844729447304473144732447334473444735447364473744738447394474044741447424474344744447454474644747447484474944750447514475244753447544475544756447574475844759447604476144762447634476444765447664476744768447694477044771447724477344774447754477644777447784477944780447814478244783447844478544786447874478844789447904479144792447934479444795447964479744798447994480044801448024480344804448054480644807448084480944810448114481244813448144481544816448174481844819448204482144822448234482444825448264482744828448294483044831448324483344834448354483644837448384483944840448414484244843448444484544846448474484844849448504485144852448534485444855448564485744858448594486044861448624486344864448654486644867448684486944870448714487244873448744487544876448774487844879448804488144882448834488444885448864488744888448894489044891448924489344894448954489644897448984489944900449014490244903449044490544906449074490844909449104491144912449134491444915449164491744918449194492044921449224492344924449254492644927449284492944930449314493244933449344493544936449374493844939449404494144942449434494444945449464494744948449494495044951449524495344954449554495644957449584495944960449614496244963449644496544966449674496844969449704497144972449734497444975449764497744978449794498044981449824498344984449854498644987449884498944990449914499244993449944499544996449974499844999450004500145002450034500445005450064500745008450094501045011450124501345014450154501645017450184501945020450214502245023450244502545026450274502845029450304503145032450334503445035450364503745038450394504045041450424504345044450454504645047450484504945050450514505245053450544505545056450574505845059450604506145062450634506445065450664506745068450694507045071450724507345074450754507645077450784507945080450814508245083450844508545086450874508845089450904509145092450934509445095450964509745098450994510045101451024510345104451054510645107451084510945110451114511245113451144511545116451174511845119451204512145122451234512445125451264512745128451294513045131451324513345134451354513645137451384513945140451414514245143451444514545146451474514845149451504515145152451534515445155451564515745158451594516045161451624516345164451654516645167451684516945170451714517245173451744517545176451774517845179451804518145182451834518445185451864518745188451894519045191451924519345194451954519645197451984519945200452014520245203452044520545206452074520845209452104521145212452134521445215452164521745218452194522045221452224522345224452254522645227452284522945230452314523245233452344523545236452374523845239452404524145242452434524445245452464524745248452494525045251452524525345254452554525645257452584525945260452614526245263452644526545266452674526845269452704527145272452734527445275452764527745278452794528045281452824528345284452854528645287452884528945290452914529245293452944529545296452974529845299453004530145302453034530445305453064530745308453094531045311453124531345314453154531645317453184531945320453214532245323453244532545326453274532845329453304533145332453334533445335453364533745338453394534045341453424534345344453454534645347453484534945350453514535245353453544535545356453574535845359453604536145362453634536445365453664536745368453694537045371453724537345374453754537645377453784537945380453814538245383453844538545386453874538845389453904539145392453934539445395453964539745398453994540045401454024540345404454054540645407454084540945410454114541245413454144541545416454174541845419454204542145422454234542445425454264542745428454294543045431454324543345434454354543645437454384543945440454414544245443454444544545446454474544845449454504545145452454534545445455454564545745458454594546045461454624546345464454654546645467454684546945470454714547245473454744547545476454774547845479454804548145482454834548445485454864548745488454894549045491454924549345494454954549645497454984549945500455014550245503455044550545506455074550845509455104551145512455134551445515455164551745518455194552045521455224552345524455254552645527455284552945530455314553245533455344553545536455374553845539455404554145542455434554445545455464554745548455494555045551455524555345554455554555645557455584555945560455614556245563455644556545566455674556845569455704557145572455734557445575455764557745578455794558045581455824558345584455854558645587455884558945590455914559245593455944559545596455974559845599456004560145602456034560445605456064560745608456094561045611456124561345614456154561645617456184561945620456214562245623456244562545626456274562845629456304563145632456334563445635456364563745638456394564045641456424564345644456454564645647456484564945650456514565245653456544565545656456574565845659456604566145662456634566445665456664566745668456694567045671456724567345674456754567645677456784567945680456814568245683456844568545686456874568845689456904569145692456934569445695456964569745698456994570045701457024570345704457054570645707457084570945710457114571245713457144571545716457174571845719457204572145722457234572445725457264572745728457294573045731457324573345734457354573645737457384573945740457414574245743457444574545746457474574845749457504575145752457534575445755457564575745758457594576045761457624576345764457654576645767457684576945770457714577245773457744577545776457774577845779457804578145782457834578445785457864578745788457894579045791457924579345794457954579645797457984579945800458014580245803458044580545806458074580845809458104581145812458134581445815458164581745818458194582045821458224582345824458254582645827458284582945830458314583245833458344583545836458374583845839458404584145842458434584445845458464584745848458494585045851458524585345854458554585645857458584585945860458614586245863458644586545866458674586845869458704587145872458734587445875458764587745878458794588045881458824588345884458854588645887458884588945890458914589245893458944589545896458974589845899459004590145902459034590445905459064590745908459094591045911459124591345914459154591645917459184591945920459214592245923459244592545926459274592845929459304593145932459334593445935459364593745938459394594045941459424594345944459454594645947459484594945950459514595245953459544595545956459574595845959459604596145962459634596445965459664596745968459694597045971459724597345974459754597645977459784597945980459814598245983459844598545986459874598845989459904599145992459934599445995459964599745998459994600046001460024600346004460054600646007460084600946010460114601246013460144601546016460174601846019460204602146022460234602446025460264602746028460294603046031460324603346034460354603646037460384603946040460414604246043460444604546046460474604846049460504605146052460534605446055460564605746058460594606046061460624606346064460654606646067460684606946070460714607246073460744607546076460774607846079460804608146082460834608446085460864608746088460894609046091460924609346094460954609646097460984609946100461014610246103461044610546106461074610846109461104611146112461134611446115461164611746118461194612046121461224612346124461254612646127461284612946130461314613246133461344613546136461374613846139461404614146142461434614446145461464614746148461494615046151461524615346154461554615646157461584615946160461614616246163461644616546166461674616846169461704617146172461734617446175461764617746178461794618046181461824618346184461854618646187461884618946190461914619246193461944619546196461974619846199462004620146202462034620446205462064620746208462094621046211462124621346214462154621646217462184621946220462214622246223462244622546226462274622846229462304623146232462334623446235462364623746238462394624046241462424624346244462454624646247462484624946250462514625246253462544625546256462574625846259462604626146262462634626446265462664626746268462694627046271462724627346274462754627646277462784627946280462814628246283462844628546286462874628846289462904629146292462934629446295462964629746298462994630046301463024630346304463054630646307463084630946310463114631246313463144631546316463174631846319463204632146322463234632446325463264632746328463294633046331463324633346334463354633646337463384633946340463414634246343463444634546346463474634846349463504635146352463534635446355463564635746358463594636046361463624636346364463654636646367463684636946370463714637246373463744637546376463774637846379463804638146382463834638446385463864638746388463894639046391463924639346394463954639646397463984639946400464014640246403464044640546406464074640846409464104641146412464134641446415464164641746418464194642046421464224642346424464254642646427464284642946430464314643246433464344643546436464374643846439464404644146442464434644446445464464644746448464494645046451464524645346454464554645646457464584645946460464614646246463464644646546466464674646846469464704647146472464734647446475464764647746478464794648046481464824648346484464854648646487464884648946490464914649246493464944649546496464974649846499465004650146502465034650446505465064650746508465094651046511465124651346514465154651646517465184651946520465214652246523465244652546526465274652846529465304653146532465334653446535465364653746538465394654046541465424654346544465454654646547465484654946550465514655246553465544655546556465574655846559465604656146562465634656446565465664656746568465694657046571465724657346574465754657646577465784657946580465814658246583465844658546586465874658846589465904659146592465934659446595465964659746598465994660046601466024660346604466054660646607466084660946610466114661246613466144661546616466174661846619466204662146622466234662446625466264662746628466294663046631466324663346634466354663646637466384663946640466414664246643466444664546646466474664846649466504665146652466534665446655466564665746658466594666046661466624666346664466654666646667466684666946670466714667246673466744667546676466774667846679466804668146682466834668446685466864668746688466894669046691466924669346694466954669646697466984669946700467014670246703467044670546706467074670846709467104671146712467134671446715467164671746718467194672046721467224672346724467254672646727467284672946730467314673246733467344673546736467374673846739467404674146742467434674446745467464674746748467494675046751467524675346754467554675646757467584675946760467614676246763467644676546766467674676846769467704677146772467734677446775467764677746778467794678046781467824678346784467854678646787467884678946790467914679246793467944679546796467974679846799468004680146802468034680446805468064680746808468094681046811468124681346814468154681646817468184681946820468214682246823468244682546826468274682846829468304683146832468334683446835468364683746838468394684046841468424684346844468454684646847468484684946850468514685246853468544685546856468574685846859468604686146862468634686446865468664686746868468694687046871468724687346874468754687646877468784687946880468814688246883468844688546886468874688846889468904689146892468934689446895468964689746898468994690046901469024690346904469054690646907469084690946910469114691246913469144691546916469174691846919469204692146922469234692446925469264692746928469294693046931469324693346934469354693646937469384693946940469414694246943469444694546946469474694846949469504695146952469534695446955469564695746958469594696046961469624696346964469654696646967469684696946970469714697246973469744697546976469774697846979469804698146982469834698446985469864698746988469894699046991469924699346994469954699646997469984699947000470014700247003470044700547006470074700847009470104701147012470134701447015470164701747018470194702047021470224702347024470254702647027470284702947030470314703247033470344703547036470374703847039470404704147042470434704447045470464704747048470494705047051470524705347054470554705647057470584705947060470614706247063470644706547066470674706847069470704707147072470734707447075470764707747078470794708047081470824708347084470854708647087470884708947090470914709247093470944709547096470974709847099471004710147102471034710447105471064710747108471094711047111471124711347114471154711647117471184711947120471214712247123471244712547126471274712847129471304713147132471334713447135471364713747138471394714047141471424714347144471454714647147471484714947150471514715247153471544715547156471574715847159471604716147162471634716447165471664716747168471694717047171471724717347174471754717647177471784717947180471814718247183471844718547186471874718847189471904719147192471934719447195471964719747198471994720047201472024720347204472054720647207472084720947210472114721247213472144721547216472174721847219472204722147222472234722447225472264722747228472294723047231472324723347234472354723647237472384723947240472414724247243472444724547246472474724847249472504725147252472534725447255472564725747258472594726047261472624726347264472654726647267472684726947270472714727247273472744727547276472774727847279472804728147282472834728447285472864728747288472894729047291472924729347294472954729647297472984729947300473014730247303473044730547306473074730847309473104731147312473134731447315473164731747318473194732047321473224732347324473254732647327473284732947330473314733247333473344733547336473374733847339473404734147342473434734447345473464734747348473494735047351473524735347354473554735647357473584735947360473614736247363473644736547366473674736847369473704737147372473734737447375473764737747378473794738047381473824738347384473854738647387473884738947390473914739247393473944739547396473974739847399474004740147402474034740447405474064740747408474094741047411474124741347414474154741647417474184741947420474214742247423474244742547426474274742847429474304743147432474334743447435474364743747438474394744047441474424744347444474454744647447474484744947450474514745247453474544745547456474574745847459474604746147462474634746447465474664746747468474694747047471474724747347474474754747647477474784747947480474814748247483474844748547486474874748847489474904749147492474934749447495474964749747498474994750047501475024750347504475054750647507475084750947510475114751247513475144751547516475174751847519475204752147522475234752447525475264752747528475294753047531475324753347534475354753647537475384753947540475414754247543475444754547546475474754847549475504755147552475534755447555475564755747558475594756047561475624756347564475654756647567475684756947570475714757247573475744757547576475774757847579475804758147582475834758447585475864758747588475894759047591475924759347594475954759647597475984759947600476014760247603476044760547606476074760847609476104761147612476134761447615476164761747618476194762047621476224762347624476254762647627476284762947630476314763247633476344763547636476374763847639476404764147642476434764447645476464764747648476494765047651476524765347654476554765647657476584765947660476614766247663476644766547666476674766847669476704767147672476734767447675476764767747678476794768047681476824768347684476854768647687476884768947690476914769247693476944769547696476974769847699477004770147702477034770447705477064770747708477094771047711477124771347714477154771647717477184771947720477214772247723477244772547726477274772847729477304773147732477334773447735477364773747738477394774047741477424774347744477454774647747477484774947750477514775247753477544775547756477574775847759477604776147762477634776447765477664776747768477694777047771477724777347774477754777647777477784777947780477814778247783477844778547786477874778847789477904779147792477934779447795477964779747798477994780047801478024780347804478054780647807478084780947810478114781247813478144781547816478174781847819478204782147822478234782447825478264782747828478294783047831478324783347834478354783647837478384783947840478414784247843478444784547846478474784847849478504785147852478534785447855478564785747858478594786047861478624786347864478654786647867478684786947870478714787247873478744787547876478774787847879478804788147882478834788447885478864788747888478894789047891478924789347894478954789647897478984789947900479014790247903479044790547906479074790847909479104791147912479134791447915479164791747918479194792047921479224792347924479254792647927479284792947930479314793247933479344793547936479374793847939479404794147942479434794447945479464794747948479494795047951479524795347954479554795647957479584795947960479614796247963479644796547966479674796847969479704797147972479734797447975479764797747978479794798047981479824798347984479854798647987479884798947990479914799247993479944799547996479974799847999480004800148002480034800448005480064800748008480094801048011480124801348014480154801648017480184801948020480214802248023480244802548026480274802848029480304803148032480334803448035480364803748038480394804048041480424804348044480454804648047480484804948050480514805248053480544805548056480574805848059480604806148062480634806448065480664806748068480694807048071480724807348074480754807648077480784807948080480814808248083480844808548086480874808848089480904809148092480934809448095480964809748098480994810048101481024810348104481054810648107481084810948110481114811248113481144811548116481174811848119481204812148122481234812448125481264812748128481294813048131481324813348134481354813648137481384813948140481414814248143481444814548146481474814848149481504815148152481534815448155481564815748158481594816048161481624816348164481654816648167481684816948170481714817248173481744817548176481774817848179481804818148182481834818448185481864818748188481894819048191481924819348194481954819648197481984819948200482014820248203482044820548206482074820848209482104821148212482134821448215482164821748218482194822048221482224822348224482254822648227482284822948230482314823248233482344823548236482374823848239482404824148242482434824448245482464824748248482494825048251482524825348254482554825648257482584825948260482614826248263482644826548266482674826848269482704827148272482734827448275482764827748278482794828048281482824828348284482854828648287482884828948290482914829248293482944829548296482974829848299483004830148302483034830448305483064830748308483094831048311483124831348314483154831648317483184831948320483214832248323483244832548326483274832848329483304833148332483334833448335483364833748338483394834048341483424834348344483454834648347483484834948350483514835248353483544835548356483574835848359483604836148362483634836448365483664836748368483694837048371483724837348374483754837648377483784837948380483814838248383483844838548386483874838848389483904839148392483934839448395483964839748398483994840048401484024840348404484054840648407484084840948410484114841248413484144841548416484174841848419484204842148422484234842448425484264842748428484294843048431484324843348434484354843648437484384843948440484414844248443484444844548446484474844848449484504845148452484534845448455484564845748458484594846048461484624846348464484654846648467484684846948470484714847248473484744847548476484774847848479484804848148482484834848448485484864848748488484894849048491484924849348494484954849648497484984849948500485014850248503485044850548506485074850848509485104851148512485134851448515485164851748518485194852048521485224852348524485254852648527485284852948530485314853248533485344853548536485374853848539485404854148542485434854448545485464854748548485494855048551485524855348554485554855648557485584855948560485614856248563485644856548566485674856848569485704857148572485734857448575485764857748578485794858048581485824858348584485854858648587485884858948590485914859248593485944859548596485974859848599486004860148602486034860448605486064860748608486094861048611486124861348614486154861648617486184861948620486214862248623486244862548626486274862848629486304863148632486334863448635486364863748638486394864048641486424864348644486454864648647486484864948650486514865248653486544865548656486574865848659486604866148662486634866448665486664866748668486694867048671486724867348674486754867648677486784867948680486814868248683486844868548686486874868848689486904869148692486934869448695486964869748698486994870048701487024870348704487054870648707487084870948710487114871248713487144871548716487174871848719487204872148722487234872448725487264872748728487294873048731487324873348734487354873648737487384873948740487414874248743487444874548746487474874848749487504875148752487534875448755487564875748758487594876048761487624876348764487654876648767487684876948770487714877248773487744877548776487774877848779487804878148782487834878448785487864878748788487894879048791487924879348794487954879648797487984879948800488014880248803488044880548806488074880848809488104881148812488134881448815488164881748818488194882048821488224882348824488254882648827488284882948830488314883248833488344883548836488374883848839488404884148842488434884448845488464884748848488494885048851488524885348854488554885648857488584885948860488614886248863488644886548866488674886848869488704887148872488734887448875488764887748878488794888048881488824888348884488854888648887488884888948890488914889248893488944889548896488974889848899489004890148902489034890448905489064890748908489094891048911489124891348914489154891648917489184891948920489214892248923489244892548926489274892848929489304893148932489334893448935489364893748938489394894048941489424894348944489454894648947489484894948950489514895248953489544895548956489574895848959489604896148962489634896448965489664896748968489694897048971489724897348974489754897648977489784897948980489814898248983489844898548986489874898848989489904899148992489934899448995489964899748998489994900049001490024900349004490054900649007490084900949010490114901249013490144901549016490174901849019490204902149022490234902449025490264902749028490294903049031490324903349034490354903649037490384903949040490414904249043490444904549046490474904849049490504905149052490534905449055490564905749058490594906049061490624906349064490654906649067490684906949070490714907249073490744907549076490774907849079490804908149082490834908449085490864908749088490894909049091490924909349094490954909649097490984909949100491014910249103491044910549106491074910849109491104911149112491134911449115491164911749118491194912049121491224912349124491254912649127491284912949130491314913249133491344913549136491374913849139491404914149142491434914449145491464914749148491494915049151491524915349154491554915649157491584915949160491614916249163491644916549166491674916849169491704917149172491734917449175491764917749178491794918049181491824918349184491854918649187491884918949190491914919249193491944919549196491974919849199492004920149202492034920449205492064920749208492094921049211492124921349214492154921649217492184921949220492214922249223492244922549226492274922849229492304923149232492334923449235492364923749238492394924049241492424924349244492454924649247492484924949250492514925249253492544925549256492574925849259492604926149262492634926449265492664926749268492694927049271492724927349274492754927649277492784927949280492814928249283492844928549286492874928849289492904929149292492934929449295492964929749298492994930049301493024930349304493054930649307493084930949310493114931249313493144931549316493174931849319493204932149322493234932449325493264932749328493294933049331493324933349334493354933649337493384933949340493414934249343493444934549346493474934849349493504935149352493534935449355493564935749358493594936049361493624936349364493654936649367493684936949370493714937249373493744937549376493774937849379493804938149382493834938449385493864938749388493894939049391493924939349394493954939649397493984939949400494014940249403494044940549406494074940849409494104941149412494134941449415494164941749418494194942049421494224942349424494254942649427494284942949430494314943249433494344943549436494374943849439494404944149442494434944449445494464944749448494494945049451494524945349454494554945649457494584945949460494614946249463494644946549466494674946849469494704947149472494734947449475494764947749478494794948049481494824948349484494854948649487494884948949490494914949249493494944949549496494974949849499495004950149502495034950449505495064950749508495094951049511495124951349514495154951649517495184951949520495214952249523495244952549526495274952849529495304953149532495334953449535495364953749538495394954049541495424954349544495454954649547495484954949550495514955249553495544955549556495574955849559495604956149562495634956449565495664956749568495694957049571495724957349574495754957649577495784957949580495814958249583495844958549586495874958849589495904959149592495934959449595495964959749598495994960049601496024960349604496054960649607496084960949610496114961249613496144961549616496174961849619496204962149622496234962449625496264962749628496294963049631496324963349634496354963649637496384963949640496414964249643496444964549646496474964849649496504965149652496534965449655496564965749658496594966049661496624966349664496654966649667496684966949670496714967249673496744967549676496774967849679496804968149682496834968449685496864968749688496894969049691496924969349694496954969649697496984969949700497014970249703497044970549706497074970849709497104971149712497134971449715497164971749718497194972049721497224972349724497254972649727497284972949730497314973249733497344973549736497374973849739497404974149742497434974449745497464974749748497494975049751497524975349754497554975649757497584975949760497614976249763497644976549766497674976849769497704977149772497734977449775497764977749778497794978049781497824978349784497854978649787497884978949790497914979249793497944979549796497974979849799498004980149802498034980449805498064980749808498094981049811498124981349814498154981649817498184981949820498214982249823498244982549826498274982849829498304983149832498334983449835498364983749838498394984049841498424984349844498454984649847498484984949850498514985249853498544985549856498574985849859498604986149862498634986449865498664986749868498694987049871498724987349874498754987649877498784987949880498814988249883498844988549886498874988849889498904989149892498934989449895498964989749898498994990049901499024990349904499054990649907499084990949910499114991249913499144991549916499174991849919499204992149922499234992449925499264992749928499294993049931499324993349934499354993649937499384993949940499414994249943499444994549946499474994849949499504995149952499534995449955499564995749958499594996049961499624996349964499654996649967499684996949970499714997249973499744997549976499774997849979499804998149982499834998449985499864998749988499894999049991499924999349994499954999649997499984999950000500015000250003500045000550006500075000850009500105001150012500135001450015500165001750018500195002050021500225002350024500255002650027500285002950030500315003250033500345003550036500375003850039500405004150042500435004450045500465004750048500495005050051500525005350054500555005650057500585005950060500615006250063500645006550066500675006850069500705007150072500735007450075500765007750078500795008050081500825008350084500855008650087500885008950090500915009250093500945009550096500975009850099501005010150102501035010450105501065010750108501095011050111501125011350114501155011650117501185011950120501215012250123501245012550126501275012850129501305013150132501335013450135501365013750138501395014050141501425014350144501455014650147501485014950150501515015250153501545015550156501575015850159501605016150162501635016450165501665016750168501695017050171501725017350174501755017650177501785017950180501815018250183501845018550186501875018850189501905019150192501935019450195501965019750198501995020050201502025020350204502055020650207502085020950210502115021250213502145021550216502175021850219502205022150222502235022450225502265022750228502295023050231502325023350234502355023650237502385023950240502415024250243502445024550246502475024850249502505025150252502535025450255502565025750258502595026050261502625026350264502655026650267502685026950270502715027250273502745027550276502775027850279502805028150282502835028450285502865028750288502895029050291502925029350294502955029650297502985029950300503015030250303503045030550306503075030850309503105031150312503135031450315503165031750318503195032050321503225032350324503255032650327503285032950330503315033250333503345033550336503375033850339503405034150342503435034450345503465034750348503495035050351503525035350354503555035650357503585035950360503615036250363503645036550366503675036850369503705037150372503735037450375503765037750378503795038050381503825038350384503855038650387503885038950390503915039250393503945039550396503975039850399504005040150402504035040450405504065040750408504095041050411504125041350414504155041650417504185041950420504215042250423504245042550426504275042850429504305043150432504335043450435504365043750438504395044050441504425044350444504455044650447504485044950450504515045250453504545045550456504575045850459504605046150462504635046450465504665046750468504695047050471504725047350474504755047650477504785047950480504815048250483504845048550486504875048850489504905049150492504935049450495504965049750498504995050050501505025050350504505055050650507505085050950510505115051250513505145051550516505175051850519505205052150522505235052450525505265052750528505295053050531505325053350534505355053650537505385053950540505415054250543505445054550546505475054850549505505055150552505535055450555505565055750558505595056050561505625056350564505655056650567505685056950570505715057250573505745057550576505775057850579505805058150582505835058450585505865058750588505895059050591505925059350594505955059650597505985059950600506015060250603506045060550606506075060850609506105061150612506135061450615506165061750618506195062050621506225062350624506255062650627506285062950630506315063250633506345063550636506375063850639506405064150642506435064450645506465064750648506495065050651506525065350654506555065650657506585065950660506615066250663506645066550666506675066850669506705067150672506735067450675506765067750678506795068050681506825068350684506855068650687506885068950690506915069250693506945069550696506975069850699507005070150702507035070450705507065070750708507095071050711507125071350714507155071650717507185071950720507215072250723507245072550726507275072850729507305073150732507335073450735507365073750738507395074050741507425074350744507455074650747507485074950750507515075250753507545075550756507575075850759507605076150762507635076450765507665076750768507695077050771507725077350774507755077650777507785077950780507815078250783507845078550786507875078850789507905079150792507935079450795507965079750798507995080050801508025080350804508055080650807508085080950810508115081250813508145081550816508175081850819508205082150822508235082450825508265082750828508295083050831508325083350834508355083650837508385083950840508415084250843508445084550846508475084850849508505085150852508535085450855508565085750858508595086050861508625086350864508655086650867508685086950870508715087250873508745087550876508775087850879508805088150882508835088450885508865088750888508895089050891508925089350894508955089650897508985089950900509015090250903509045090550906509075090850909509105091150912509135091450915509165091750918509195092050921509225092350924509255092650927509285092950930509315093250933509345093550936509375093850939509405094150942509435094450945509465094750948509495095050951509525095350954509555095650957509585095950960509615096250963509645096550966509675096850969509705097150972509735097450975509765097750978509795098050981509825098350984509855098650987509885098950990509915099250993509945099550996509975099850999510005100151002510035100451005510065100751008510095101051011510125101351014510155101651017510185101951020510215102251023510245102551026510275102851029510305103151032510335103451035510365103751038510395104051041510425104351044510455104651047510485104951050510515105251053510545105551056510575105851059510605106151062510635106451065510665106751068510695107051071510725107351074510755107651077510785107951080510815108251083510845108551086510875108851089510905109151092510935109451095510965109751098510995110051101511025110351104511055110651107511085110951110511115111251113511145111551116511175111851119511205112151122511235112451125511265112751128511295113051131511325113351134511355113651137511385113951140511415114251143511445114551146511475114851149511505115151152511535115451155511565115751158511595116051161511625116351164511655116651167511685116951170511715117251173511745117551176511775117851179511805118151182511835118451185511865118751188511895119051191511925119351194511955119651197511985119951200512015120251203512045120551206512075120851209512105121151212512135121451215512165121751218512195122051221512225122351224512255122651227512285122951230512315123251233512345123551236512375123851239512405124151242512435124451245512465124751248512495125051251512525125351254512555125651257512585125951260512615126251263512645126551266512675126851269512705127151272512735127451275512765127751278512795128051281512825128351284512855128651287512885128951290512915129251293512945129551296512975129851299513005130151302513035130451305513065130751308513095131051311513125131351314513155131651317513185131951320513215132251323513245132551326513275132851329513305133151332513335133451335513365133751338513395134051341513425134351344513455134651347513485134951350513515135251353513545135551356513575135851359513605136151362513635136451365513665136751368513695137051371513725137351374513755137651377513785137951380513815138251383513845138551386513875138851389513905139151392513935139451395513965139751398513995140051401514025140351404514055140651407514085140951410514115141251413514145141551416514175141851419514205142151422514235142451425514265142751428514295143051431514325143351434514355143651437514385143951440514415144251443514445144551446514475144851449514505145151452514535145451455514565145751458514595146051461514625146351464514655146651467514685146951470514715147251473514745147551476514775147851479514805148151482514835148451485514865148751488514895149051491514925149351494514955149651497514985149951500515015150251503515045150551506515075150851509515105151151512515135151451515515165151751518515195152051521515225152351524515255152651527515285152951530515315153251533515345153551536515375153851539515405154151542515435154451545515465154751548515495155051551515525155351554515555155651557515585155951560515615156251563515645156551566515675156851569515705157151572515735157451575515765157751578515795158051581515825158351584515855158651587515885158951590515915159251593515945159551596515975159851599516005160151602516035160451605516065160751608516095161051611516125161351614516155161651617516185161951620516215162251623516245162551626516275162851629516305163151632516335163451635516365163751638516395164051641516425164351644516455164651647516485164951650516515165251653516545165551656516575165851659516605166151662516635166451665516665166751668516695167051671516725167351674516755167651677516785167951680516815168251683516845168551686516875168851689516905169151692516935169451695516965169751698516995170051701517025170351704517055170651707517085170951710517115171251713517145171551716517175171851719517205172151722517235172451725517265172751728517295173051731517325173351734517355173651737517385173951740517415174251743517445174551746517475174851749517505175151752517535175451755517565175751758517595176051761517625176351764517655176651767517685176951770517715177251773517745177551776517775177851779517805178151782517835178451785517865178751788517895179051791517925179351794517955179651797517985179951800518015180251803518045180551806518075180851809518105181151812518135181451815518165181751818518195182051821518225182351824518255182651827518285182951830518315183251833518345183551836518375183851839518405184151842518435184451845518465184751848518495185051851518525185351854518555185651857518585185951860518615186251863518645186551866518675186851869518705187151872518735187451875518765187751878518795188051881518825188351884518855188651887518885188951890518915189251893518945189551896518975189851899519005190151902519035190451905519065190751908519095191051911519125191351914519155191651917519185191951920519215192251923519245192551926519275192851929519305193151932519335193451935519365193751938519395194051941519425194351944519455194651947519485194951950519515195251953519545195551956519575195851959519605196151962519635196451965519665196751968519695197051971519725197351974519755197651977519785197951980519815198251983519845198551986519875198851989519905199151992519935199451995519965199751998519995200052001520025200352004520055200652007520085200952010520115201252013520145201552016520175201852019520205202152022520235202452025520265202752028520295203052031520325203352034520355203652037520385203952040520415204252043520445204552046520475204852049520505205152052520535205452055520565205752058520595206052061520625206352064520655206652067520685206952070520715207252073520745207552076520775207852079520805208152082520835208452085520865208752088520895209052091520925209352094520955209652097520985209952100521015210252103521045210552106521075210852109521105211152112521135211452115521165211752118521195212052121521225212352124521255212652127521285212952130521315213252133521345213552136521375213852139521405214152142521435214452145521465214752148521495215052151521525215352154521555215652157521585215952160521615216252163521645216552166521675216852169521705217152172521735217452175521765217752178521795218052181521825218352184521855218652187521885218952190521915219252193521945219552196521975219852199522005220152202522035220452205522065220752208522095221052211522125221352214522155221652217522185221952220522215222252223522245222552226522275222852229522305223152232522335223452235522365223752238522395224052241522425224352244522455224652247522485224952250522515225252253522545225552256522575225852259522605226152262522635226452265522665226752268522695227052271522725227352274522755227652277522785227952280522815228252283522845228552286522875228852289522905229152292522935229452295522965229752298522995230052301523025230352304523055230652307523085230952310523115231252313523145231552316523175231852319523205232152322523235232452325523265232752328523295233052331523325233352334523355233652337523385233952340523415234252343523445234552346523475234852349523505235152352523535235452355523565235752358523595236052361523625236352364523655236652367523685236952370523715237252373523745237552376523775237852379523805238152382523835238452385523865238752388523895239052391523925239352394523955239652397523985239952400524015240252403524045240552406524075240852409524105241152412524135241452415524165241752418524195242052421524225242352424524255242652427524285242952430524315243252433524345243552436524375243852439524405244152442524435244452445524465244752448524495245052451524525245352454524555245652457524585245952460524615246252463524645246552466524675246852469524705247152472524735247452475524765247752478524795248052481524825248352484524855248652487524885248952490524915249252493524945249552496524975249852499525005250152502525035250452505525065250752508525095251052511525125251352514525155251652517525185251952520525215252252523525245252552526525275252852529525305253152532525335253452535525365253752538525395254052541525425254352544525455254652547525485254952550525515255252553525545255552556525575255852559525605256152562525635256452565525665256752568525695257052571525725257352574525755257652577525785257952580525815258252583525845258552586525875258852589525905259152592525935259452595525965259752598525995260052601526025260352604526055260652607526085260952610526115261252613526145261552616526175261852619526205262152622526235262452625526265262752628526295263052631526325263352634526355263652637526385263952640526415264252643526445264552646526475264852649526505265152652526535265452655526565265752658526595266052661526625266352664526655266652667526685266952670526715267252673526745267552676526775267852679526805268152682526835268452685526865268752688526895269052691526925269352694526955269652697526985269952700527015270252703527045270552706527075270852709527105271152712527135271452715527165271752718527195272052721527225272352724527255272652727527285272952730527315273252733527345273552736527375273852739527405274152742527435274452745527465274752748527495275052751527525275352754527555275652757527585275952760527615276252763527645276552766527675276852769527705277152772527735277452775527765277752778527795278052781527825278352784527855278652787527885278952790527915279252793527945279552796527975279852799528005280152802528035280452805528065280752808528095281052811528125281352814528155281652817528185281952820528215282252823528245282552826528275282852829528305283152832528335283452835528365283752838528395284052841528425284352844528455284652847528485284952850528515285252853528545285552856528575285852859528605286152862528635286452865528665286752868528695287052871528725287352874528755287652877528785287952880528815288252883528845288552886528875288852889528905289152892528935289452895528965289752898528995290052901529025290352904529055290652907529085290952910529115291252913529145291552916529175291852919529205292152922529235292452925529265292752928529295293052931529325293352934529355293652937529385293952940529415294252943529445294552946529475294852949529505295152952529535295452955529565295752958529595296052961529625296352964529655296652967529685296952970529715297252973529745297552976529775297852979529805298152982529835298452985529865298752988529895299052991529925299352994529955299652997529985299953000530015300253003530045300553006530075300853009530105301153012530135301453015530165301753018530195302053021530225302353024530255302653027530285302953030530315303253033530345303553036530375303853039530405304153042530435304453045530465304753048530495305053051530525305353054530555305653057530585305953060530615306253063530645306553066530675306853069530705307153072530735307453075530765307753078530795308053081530825308353084530855308653087530885308953090530915309253093530945309553096530975309853099531005310153102531035310453105531065310753108531095311053111531125311353114531155311653117531185311953120531215312253123531245312553126531275312853129531305313153132531335313453135531365313753138531395314053141531425314353144531455314653147531485314953150531515315253153531545315553156531575315853159531605316153162531635316453165531665316753168531695317053171531725317353174531755317653177531785317953180531815318253183531845318553186531875318853189531905319153192531935319453195531965319753198531995320053201532025320353204532055320653207532085320953210532115321253213532145321553216532175321853219532205322153222532235322453225532265322753228532295323053231532325323353234532355323653237532385323953240532415324253243532445324553246532475324853249532505325153252532535325453255532565325753258532595326053261532625326353264532655326653267532685326953270532715327253273532745327553276532775327853279532805328153282532835328453285532865328753288532895329053291532925329353294532955329653297532985329953300533015330253303533045330553306533075330853309533105331153312533135331453315533165331753318533195332053321533225332353324533255332653327533285332953330533315333253333533345333553336533375333853339533405334153342533435334453345533465334753348533495335053351533525335353354533555335653357533585335953360533615336253363533645336553366533675336853369533705337153372533735337453375533765337753378533795338053381533825338353384533855338653387533885338953390533915339253393533945339553396533975339853399534005340153402534035340453405534065340753408534095341053411534125341353414534155341653417534185341953420534215342253423534245342553426534275342853429534305343153432534335343453435534365343753438534395344053441534425344353444534455344653447534485344953450534515345253453534545345553456534575345853459534605346153462534635346453465534665346753468534695347053471534725347353474534755347653477534785347953480534815348253483534845348553486534875348853489534905349153492534935349453495534965349753498534995350053501535025350353504535055350653507535085350953510535115351253513535145351553516535175351853519535205352153522535235352453525535265352753528535295353053531535325353353534535355353653537535385353953540535415354253543535445354553546535475354853549535505355153552535535355453555535565355753558535595356053561535625356353564535655356653567535685356953570535715357253573535745357553576535775357853579535805358153582535835358453585535865358753588535895359053591535925359353594535955359653597535985359953600536015360253603536045360553606536075360853609536105361153612536135361453615536165361753618536195362053621536225362353624536255362653627536285362953630536315363253633536345363553636536375363853639536405364153642536435364453645536465364753648536495365053651536525365353654536555365653657536585365953660536615366253663536645366553666536675366853669536705367153672536735367453675536765367753678536795368053681536825368353684536855368653687536885368953690536915369253693536945369553696536975369853699537005370153702537035370453705537065370753708537095371053711537125371353714537155371653717537185371953720537215372253723537245372553726537275372853729537305373153732537335373453735537365373753738537395374053741537425374353744537455374653747537485374953750537515375253753537545375553756537575375853759537605376153762537635376453765537665376753768537695377053771537725377353774537755377653777537785377953780537815378253783537845378553786537875378853789537905379153792537935379453795537965379753798537995380053801538025380353804538055380653807538085380953810538115381253813538145381553816538175381853819538205382153822538235382453825538265382753828538295383053831538325383353834538355383653837538385383953840538415384253843538445384553846538475384853849538505385153852538535385453855538565385753858538595386053861538625386353864538655386653867538685386953870538715387253873538745387553876538775387853879538805388153882538835388453885538865388753888538895389053891538925389353894538955389653897538985389953900539015390253903539045390553906539075390853909539105391153912539135391453915539165391753918539195392053921539225392353924539255392653927539285392953930539315393253933539345393553936539375393853939539405394153942539435394453945539465394753948539495395053951539525395353954539555395653957539585395953960539615396253963539645396553966539675396853969539705397153972539735397453975539765397753978539795398053981539825398353984539855398653987539885398953990539915399253993539945399553996539975399853999540005400154002540035400454005540065400754008540095401054011540125401354014540155401654017540185401954020540215402254023540245402554026540275402854029540305403154032540335403454035540365403754038540395404054041540425404354044540455404654047540485404954050540515405254053540545405554056540575405854059540605406154062540635406454065540665406754068540695407054071540725407354074540755407654077540785407954080540815408254083540845408554086540875408854089540905409154092540935409454095540965409754098540995410054101541025410354104541055410654107541085410954110541115411254113541145411554116541175411854119541205412154122541235412454125541265412754128541295413054131541325413354134541355413654137541385413954140541415414254143541445414554146541475414854149541505415154152541535415454155541565415754158541595416054161541625416354164541655416654167541685416954170541715417254173541745417554176541775417854179541805418154182541835418454185541865418754188541895419054191541925419354194541955419654197541985419954200542015420254203542045420554206542075420854209542105421154212542135421454215542165421754218542195422054221542225422354224542255422654227542285422954230542315423254233542345423554236542375423854239542405424154242542435424454245542465424754248542495425054251542525425354254542555425654257542585425954260542615426254263542645426554266542675426854269542705427154272542735427454275542765427754278542795428054281542825428354284542855428654287542885428954290542915429254293542945429554296542975429854299543005430154302543035430454305543065430754308543095431054311543125431354314543155431654317543185431954320543215432254323543245432554326543275432854329543305433154332543335433454335543365433754338543395434054341543425434354344543455434654347543485434954350543515435254353543545435554356543575435854359543605436154362543635436454365543665436754368543695437054371543725437354374543755437654377543785437954380543815438254383543845438554386543875438854389543905439154392543935439454395543965439754398543995440054401544025440354404544055440654407544085440954410544115441254413544145441554416544175441854419544205442154422544235442454425544265442754428544295443054431544325443354434544355443654437544385443954440544415444254443544445444554446544475444854449544505445154452544535445454455544565445754458544595446054461544625446354464544655446654467544685446954470544715447254473544745447554476544775447854479544805448154482544835448454485544865448754488544895449054491544925449354494544955449654497544985449954500545015450254503545045450554506545075450854509545105451154512545135451454515545165451754518545195452054521545225452354524545255452654527545285452954530545315453254533545345453554536545375453854539545405454154542545435454454545545465454754548545495455054551545525455354554545555455654557545585455954560545615456254563545645456554566545675456854569545705457154572545735457454575545765457754578545795458054581545825458354584545855458654587545885458954590545915459254593545945459554596545975459854599546005460154602546035460454605546065460754608546095461054611546125461354614546155461654617546185461954620546215462254623546245462554626546275462854629546305463154632546335463454635546365463754638546395464054641546425464354644546455464654647546485464954650546515465254653546545465554656546575465854659546605466154662546635466454665546665466754668546695467054671546725467354674546755467654677546785467954680546815468254683546845468554686546875468854689546905469154692546935469454695546965469754698546995470054701547025470354704547055470654707547085470954710547115471254713547145471554716547175471854719547205472154722547235472454725547265472754728547295473054731547325473354734547355473654737547385473954740547415474254743547445474554746547475474854749547505475154752547535475454755547565475754758547595476054761547625476354764547655476654767547685476954770547715477254773547745477554776547775477854779547805478154782547835478454785547865478754788547895479054791547925479354794547955479654797547985479954800548015480254803548045480554806548075480854809548105481154812548135481454815548165481754818548195482054821548225482354824548255482654827548285482954830548315483254833548345483554836548375483854839548405484154842548435484454845548465484754848548495485054851548525485354854548555485654857548585485954860548615486254863548645486554866548675486854869548705487154872548735487454875548765487754878548795488054881548825488354884548855488654887548885488954890548915489254893548945489554896548975489854899549005490154902549035490454905549065490754908549095491054911549125491354914549155491654917549185491954920549215492254923549245492554926549275492854929549305493154932549335493454935549365493754938549395494054941549425494354944549455494654947549485494954950549515495254953549545495554956549575495854959549605496154962549635496454965549665496754968549695497054971549725497354974549755497654977549785497954980549815498254983549845498554986549875498854989549905499154992549935499454995549965499754998549995500055001550025500355004550055500655007550085500955010550115501255013550145501555016550175501855019550205502155022550235502455025550265502755028550295503055031550325503355034550355503655037550385503955040550415504255043550445504555046550475504855049550505505155052550535505455055550565505755058550595506055061550625506355064550655506655067550685506955070550715507255073550745507555076550775507855079550805508155082550835508455085550865508755088550895509055091550925509355094550955509655097550985509955100551015510255103551045510555106551075510855109551105511155112551135511455115551165511755118551195512055121551225512355124551255512655127551285512955130551315513255133551345513555136551375513855139551405514155142551435514455145551465514755148551495515055151551525515355154551555515655157551585515955160551615516255163551645516555166551675516855169551705517155172551735517455175551765517755178551795518055181551825518355184551855518655187551885518955190551915519255193551945519555196551975519855199552005520155202552035520455205552065520755208552095521055211552125521355214552155521655217552185521955220552215522255223552245522555226552275522855229552305523155232552335523455235552365523755238552395524055241552425524355244552455524655247552485524955250552515525255253552545525555256552575525855259552605526155262552635526455265552665526755268552695527055271552725527355274552755527655277552785527955280552815528255283552845528555286552875528855289552905529155292552935529455295552965529755298552995530055301553025530355304553055530655307553085530955310553115531255313553145531555316553175531855319553205532155322553235532455325553265532755328553295533055331553325533355334553355533655337553385533955340553415534255343553445534555346553475534855349553505535155352553535535455355553565535755358553595536055361553625536355364553655536655367553685536955370553715537255373553745537555376553775537855379553805538155382553835538455385553865538755388553895539055391553925539355394553955539655397553985539955400554015540255403554045540555406554075540855409554105541155412554135541455415554165541755418554195542055421554225542355424554255542655427554285542955430554315543255433554345543555436554375543855439554405544155442554435544455445554465544755448554495545055451554525545355454554555545655457554585545955460554615546255463554645546555466554675546855469554705547155472554735547455475554765547755478554795548055481554825548355484554855548655487554885548955490554915549255493554945549555496554975549855499555005550155502555035550455505555065550755508555095551055511555125551355514555155551655517555185551955520555215552255523555245552555526555275552855529555305553155532555335553455535555365553755538555395554055541555425554355544555455554655547555485554955550555515555255553555545555555556555575555855559555605556155562555635556455565555665556755568555695557055571555725557355574555755557655577555785557955580555815558255583555845558555586555875558855589555905559155592555935559455595555965559755598555995560055601556025560355604556055560655607556085560955610556115561255613556145561555616556175561855619556205562155622556235562455625556265562755628556295563055631556325563355634556355563655637556385563955640556415564255643556445564555646556475564855649556505565155652556535565455655556565565755658556595566055661556625566355664556655566655667556685566955670556715567255673556745567555676556775567855679556805568155682556835568455685556865568755688556895569055691556925569355694556955569655697556985569955700557015570255703557045570555706557075570855709557105571155712557135571455715557165571755718557195572055721557225572355724557255572655727557285572955730557315573255733557345573555736557375573855739557405574155742557435574455745557465574755748557495575055751557525575355754557555575655757557585575955760557615576255763557645576555766557675576855769557705577155772557735577455775557765577755778557795578055781557825578355784557855578655787557885578955790557915579255793557945579555796557975579855799558005580155802558035580455805558065580755808558095581055811558125581355814558155581655817558185581955820558215582255823558245582555826558275582855829558305583155832558335583455835558365583755838558395584055841558425584355844558455584655847558485584955850558515585255853558545585555856558575585855859558605586155862558635586455865558665586755868558695587055871558725587355874558755587655877558785587955880558815588255883558845588555886558875588855889558905589155892558935589455895558965589755898558995590055901559025590355904559055590655907559085590955910559115591255913559145591555916559175591855919559205592155922559235592455925559265592755928559295593055931559325593355934559355593655937559385593955940559415594255943559445594555946559475594855949559505595155952559535595455955559565595755958559595596055961559625596355964559655596655967559685596955970559715597255973559745597555976559775597855979559805598155982559835598455985559865598755988559895599055991559925599355994559955599655997559985599956000560015600256003560045600556006560075600856009560105601156012560135601456015560165601756018560195602056021560225602356024560255602656027560285602956030560315603256033560345603556036560375603856039560405604156042560435604456045560465604756048560495605056051560525605356054560555605656057560585605956060560615606256063560645606556066560675606856069560705607156072560735607456075560765607756078560795608056081560825608356084560855608656087560885608956090560915609256093560945609556096560975609856099561005610156102561035610456105561065610756108561095611056111561125611356114561155611656117561185611956120561215612256123561245612556126561275612856129561305613156132561335613456135561365613756138561395614056141561425614356144561455614656147561485614956150561515615256153561545615556156561575615856159561605616156162561635616456165561665616756168561695617056171561725617356174561755617656177561785617956180561815618256183561845618556186561875618856189561905619156192561935619456195561965619756198561995620056201562025620356204562055620656207562085620956210562115621256213562145621556216562175621856219562205622156222562235622456225562265622756228562295623056231562325623356234562355623656237562385623956240562415624256243562445624556246562475624856249562505625156252562535625456255562565625756258562595626056261562625626356264562655626656267562685626956270562715627256273562745627556276562775627856279562805628156282562835628456285562865628756288562895629056291562925629356294562955629656297562985629956300563015630256303563045630556306563075630856309563105631156312563135631456315563165631756318563195632056321563225632356324563255632656327563285632956330563315633256333563345633556336563375633856339563405634156342563435634456345563465634756348563495635056351563525635356354563555635656357563585635956360563615636256363563645636556366563675636856369563705637156372563735637456375563765637756378563795638056381563825638356384563855638656387563885638956390563915639256393563945639556396563975639856399564005640156402564035640456405564065640756408564095641056411564125641356414564155641656417564185641956420564215642256423564245642556426564275642856429564305643156432564335643456435564365643756438564395644056441564425644356444564455644656447564485644956450564515645256453564545645556456564575645856459564605646156462564635646456465564665646756468564695647056471564725647356474564755647656477564785647956480564815648256483564845648556486564875648856489564905649156492564935649456495564965649756498564995650056501565025650356504565055650656507565085650956510565115651256513565145651556516565175651856519565205652156522565235652456525565265652756528565295653056531565325653356534565355653656537565385653956540565415654256543565445654556546565475654856549565505655156552565535655456555565565655756558565595656056561565625656356564565655656656567565685656956570565715657256573565745657556576565775657856579565805658156582565835658456585565865658756588565895659056591565925659356594565955659656597565985659956600566015660256603566045660556606566075660856609566105661156612566135661456615566165661756618566195662056621566225662356624566255662656627566285662956630566315663256633566345663556636566375663856639566405664156642566435664456645566465664756648566495665056651566525665356654566555665656657566585665956660566615666256663566645666556666566675666856669566705667156672566735667456675566765667756678566795668056681566825668356684566855668656687566885668956690566915669256693566945669556696566975669856699567005670156702567035670456705567065670756708567095671056711567125671356714567155671656717567185671956720567215672256723567245672556726567275672856729567305673156732567335673456735567365673756738567395674056741567425674356744567455674656747567485674956750567515675256753567545675556756567575675856759567605676156762567635676456765567665676756768567695677056771567725677356774567755677656777567785677956780567815678256783567845678556786567875678856789567905679156792567935679456795567965679756798567995680056801568025680356804568055680656807568085680956810568115681256813568145681556816568175681856819568205682156822568235682456825568265682756828568295683056831568325683356834568355683656837568385683956840568415684256843568445684556846568475684856849568505685156852568535685456855568565685756858568595686056861568625686356864568655686656867568685686956870568715687256873568745687556876568775687856879568805688156882568835688456885568865688756888568895689056891568925689356894568955689656897568985689956900569015690256903569045690556906569075690856909569105691156912569135691456915569165691756918569195692056921569225692356924569255692656927569285692956930569315693256933569345693556936569375693856939569405694156942569435694456945569465694756948569495695056951569525695356954569555695656957569585695956960569615696256963569645696556966569675696856969569705697156972569735697456975569765697756978569795698056981569825698356984569855698656987569885698956990569915699256993569945699556996569975699856999570005700157002570035700457005570065700757008570095701057011570125701357014570155701657017570185701957020570215702257023570245702557026570275702857029570305703157032570335703457035570365703757038570395704057041570425704357044570455704657047570485704957050570515705257053570545705557056570575705857059570605706157062570635706457065570665706757068570695707057071570725707357074570755707657077570785707957080570815708257083570845708557086570875708857089570905709157092570935709457095570965709757098570995710057101571025710357104571055710657107571085710957110571115711257113571145711557116571175711857119571205712157122571235712457125571265712757128571295713057131571325713357134571355713657137571385713957140571415714257143571445714557146571475714857149571505715157152571535715457155571565715757158571595716057161571625716357164571655716657167571685716957170571715717257173571745717557176571775717857179571805718157182571835718457185571865718757188571895719057191571925719357194571955719657197571985719957200572015720257203572045720557206572075720857209572105721157212572135721457215572165721757218572195722057221572225722357224572255722657227572285722957230572315723257233572345723557236572375723857239572405724157242572435724457245572465724757248572495725057251572525725357254572555725657257572585725957260572615726257263572645726557266572675726857269572705727157272572735727457275572765727757278572795728057281572825728357284572855728657287572885728957290572915729257293572945729557296572975729857299573005730157302573035730457305573065730757308573095731057311573125731357314573155731657317573185731957320573215732257323573245732557326573275732857329573305733157332573335733457335573365733757338573395734057341573425734357344573455734657347573485734957350573515735257353573545735557356573575735857359573605736157362573635736457365573665736757368573695737057371573725737357374573755737657377573785737957380573815738257383573845738557386573875738857389573905739157392573935739457395573965739757398573995740057401574025740357404574055740657407574085740957410574115741257413574145741557416574175741857419574205742157422574235742457425574265742757428574295743057431574325743357434574355743657437574385743957440574415744257443574445744557446574475744857449574505745157452574535745457455574565745757458574595746057461574625746357464574655746657467574685746957470574715747257473574745747557476574775747857479574805748157482574835748457485574865748757488574895749057491574925749357494574955749657497574985749957500575015750257503575045750557506575075750857509575105751157512575135751457515575165751757518575195752057521575225752357524575255752657527575285752957530575315753257533575345753557536575375753857539575405754157542575435754457545575465754757548575495755057551575525755357554575555755657557575585755957560575615756257563575645756557566575675756857569575705757157572575735757457575575765757757578575795758057581575825758357584575855758657587575885758957590575915759257593575945759557596575975759857599576005760157602576035760457605576065760757608576095761057611576125761357614576155761657617576185761957620576215762257623576245762557626576275762857629576305763157632576335763457635576365763757638576395764057641576425764357644576455764657647576485764957650576515765257653576545765557656576575765857659576605766157662576635766457665576665766757668576695767057671576725767357674576755767657677576785767957680576815768257683576845768557686576875768857689576905769157692576935769457695576965769757698576995770057701577025770357704577055770657707577085770957710577115771257713577145771557716577175771857719577205772157722577235772457725577265772757728577295773057731577325773357734577355773657737577385773957740577415774257743577445774557746577475774857749577505775157752577535775457755577565775757758577595776057761577625776357764577655776657767577685776957770577715777257773577745777557776577775777857779577805778157782577835778457785577865778757788577895779057791577925779357794577955779657797577985779957800578015780257803578045780557806578075780857809578105781157812578135781457815578165781757818578195782057821578225782357824578255782657827578285782957830578315783257833578345783557836578375783857839578405784157842578435784457845578465784757848578495785057851578525785357854578555785657857578585785957860578615786257863578645786557866578675786857869578705787157872578735787457875578765787757878578795788057881578825788357884578855788657887578885788957890578915789257893578945789557896578975789857899579005790157902579035790457905579065790757908579095791057911579125791357914579155791657917579185791957920579215792257923579245792557926579275792857929579305793157932579335793457935579365793757938579395794057941579425794357944579455794657947579485794957950579515795257953579545795557956579575795857959579605796157962579635796457965579665796757968579695797057971579725797357974579755797657977579785797957980579815798257983579845798557986579875798857989579905799157992579935799457995579965799757998579995800058001580025800358004580055800658007580085800958010580115801258013580145801558016580175801858019580205802158022580235802458025580265802758028580295803058031580325803358034580355803658037580385803958040580415804258043580445804558046580475804858049580505805158052580535805458055580565805758058580595806058061580625806358064580655806658067580685806958070580715807258073580745807558076580775807858079580805808158082580835808458085580865808758088580895809058091580925809358094580955809658097580985809958100581015810258103581045810558106581075810858109581105811158112581135811458115581165811758118581195812058121581225812358124581255812658127581285812958130581315813258133581345813558136581375813858139581405814158142581435814458145581465814758148581495815058151581525815358154581555815658157581585815958160581615816258163581645816558166581675816858169581705817158172581735817458175581765817758178581795818058181581825818358184581855818658187581885818958190581915819258193581945819558196581975819858199582005820158202582035820458205582065820758208582095821058211582125821358214582155821658217582185821958220582215822258223582245822558226582275822858229582305823158232582335823458235582365823758238582395824058241582425824358244582455824658247582485824958250582515825258253582545825558256582575825858259582605826158262582635826458265582665826758268582695827058271582725827358274582755827658277582785827958280582815828258283582845828558286582875828858289582905829158292582935829458295582965829758298582995830058301583025830358304583055830658307583085830958310583115831258313583145831558316583175831858319583205832158322583235832458325583265832758328583295833058331583325833358334583355833658337583385833958340583415834258343583445834558346583475834858349583505835158352583535835458355583565835758358583595836058361583625836358364583655836658367583685836958370583715837258373583745837558376583775837858379583805838158382583835838458385583865838758388583895839058391583925839358394583955839658397583985839958400584015840258403584045840558406584075840858409584105841158412584135841458415584165841758418584195842058421584225842358424584255842658427584285842958430584315843258433584345843558436584375843858439584405844158442584435844458445584465844758448584495845058451584525845358454584555845658457584585845958460584615846258463584645846558466584675846858469584705847158472584735847458475584765847758478584795848058481584825848358484584855848658487584885848958490584915849258493584945849558496584975849858499585005850158502585035850458505585065850758508585095851058511585125851358514585155851658517585185851958520585215852258523585245852558526585275852858529585305853158532585335853458535585365853758538585395854058541585425854358544585455854658547585485854958550585515855258553585545855558556585575855858559585605856158562585635856458565585665856758568585695857058571585725857358574585755857658577585785857958580585815858258583585845858558586585875858858589585905859158592585935859458595585965859758598585995860058601586025860358604586055860658607586085860958610586115861258613586145861558616586175861858619586205862158622586235862458625586265862758628586295863058631586325863358634586355863658637586385863958640586415864258643586445864558646586475864858649586505865158652586535865458655586565865758658586595866058661586625866358664586655866658667586685866958670586715867258673586745867558676586775867858679586805868158682586835868458685586865868758688586895869058691586925869358694586955869658697586985869958700587015870258703587045870558706587075870858709587105871158712587135871458715587165871758718587195872058721587225872358724587255872658727587285872958730587315873258733587345873558736587375873858739587405874158742587435874458745587465874758748587495875058751587525875358754587555875658757587585875958760587615876258763587645876558766587675876858769587705877158772587735877458775587765877758778587795878058781587825878358784587855878658787587885878958790587915879258793587945879558796587975879858799588005880158802588035880458805588065880758808588095881058811588125881358814588155881658817588185881958820588215882258823588245882558826588275882858829588305883158832588335883458835588365883758838588395884058841588425884358844588455884658847588485884958850588515885258853588545885558856588575885858859588605886158862588635886458865588665886758868588695887058871588725887358874588755887658877588785887958880588815888258883588845888558886588875888858889588905889158892588935889458895588965889758898588995890058901589025890358904589055890658907589085890958910589115891258913589145891558916589175891858919589205892158922589235892458925589265892758928589295893058931589325893358934589355893658937589385893958940589415894258943589445894558946589475894858949589505895158952589535895458955589565895758958589595896058961589625896358964589655896658967589685896958970589715897258973589745897558976589775897858979589805898158982589835898458985589865898758988589895899058991589925899358994589955899658997589985899959000590015900259003590045900559006590075900859009590105901159012590135901459015590165901759018590195902059021590225902359024590255902659027590285902959030590315903259033590345903559036590375903859039590405904159042590435904459045590465904759048590495905059051590525905359054590555905659057590585905959060590615906259063590645906559066590675906859069590705907159072590735907459075590765907759078590795908059081590825908359084590855908659087590885908959090590915909259093590945909559096590975909859099591005910159102591035910459105591065910759108591095911059111591125911359114591155911659117591185911959120591215912259123591245912559126591275912859129591305913159132591335913459135591365913759138591395914059141591425914359144591455914659147591485914959150591515915259153591545915559156591575915859159591605916159162591635916459165591665916759168591695917059171591725917359174591755917659177591785917959180591815918259183591845918559186591875918859189591905919159192591935919459195591965919759198591995920059201592025920359204592055920659207592085920959210592115921259213592145921559216592175921859219592205922159222592235922459225592265922759228592295923059231592325923359234592355923659237592385923959240592415924259243592445924559246592475924859249592505925159252592535925459255592565925759258592595926059261592625926359264592655926659267592685926959270592715927259273592745927559276592775927859279592805928159282592835928459285592865928759288592895929059291592925929359294592955929659297592985929959300593015930259303593045930559306593075930859309593105931159312593135931459315593165931759318593195932059321593225932359324593255932659327593285932959330593315933259333593345933559336593375933859339593405934159342593435934459345593465934759348593495935059351593525935359354593555935659357593585935959360593615936259363593645936559366593675936859369593705937159372593735937459375593765937759378593795938059381593825938359384593855938659387593885938959390593915939259393593945939559396593975939859399594005940159402594035940459405594065940759408594095941059411594125941359414594155941659417594185941959420594215942259423594245942559426594275942859429594305943159432594335943459435594365943759438594395944059441594425944359444594455944659447594485944959450594515945259453594545945559456594575945859459594605946159462594635946459465594665946759468594695947059471594725947359474594755947659477594785947959480594815948259483594845948559486594875948859489594905949159492594935949459495594965949759498594995950059501595025950359504595055950659507595085950959510595115951259513595145951559516595175951859519595205952159522595235952459525595265952759528595295953059531595325953359534595355953659537595385953959540595415954259543595445954559546595475954859549595505955159552595535955459555595565955759558595595956059561595625956359564595655956659567595685956959570595715957259573595745957559576595775957859579595805958159582595835958459585595865958759588595895959059591595925959359594595955959659597595985959959600596015960259603596045960559606596075960859609596105961159612596135961459615596165961759618596195962059621596225962359624596255962659627596285962959630596315963259633596345963559636596375963859639596405964159642596435964459645596465964759648596495965059651596525965359654596555965659657596585965959660596615966259663596645966559666596675966859669596705967159672596735967459675596765967759678596795968059681596825968359684596855968659687596885968959690596915969259693596945969559696596975969859699597005970159702597035970459705597065970759708597095971059711597125971359714597155971659717597185971959720597215972259723597245972559726597275972859729597305973159732597335973459735597365973759738597395974059741597425974359744597455974659747597485974959750597515975259753597545975559756597575975859759597605976159762597635976459765597665976759768597695977059771597725977359774597755977659777597785977959780597815978259783597845978559786597875978859789597905979159792597935979459795597965979759798597995980059801598025980359804598055980659807598085980959810598115981259813598145981559816598175981859819598205982159822598235982459825598265982759828598295983059831598325983359834598355983659837598385983959840598415984259843598445984559846598475984859849598505985159852598535985459855598565985759858598595986059861598625986359864598655986659867598685986959870598715987259873598745987559876598775987859879598805988159882598835988459885598865988759888598895989059891598925989359894598955989659897598985989959900599015990259903599045990559906599075990859909599105991159912599135991459915599165991759918599195992059921599225992359924599255992659927599285992959930599315993259933599345993559936599375993859939599405994159942599435994459945599465994759948599495995059951599525995359954599555995659957599585995959960599615996259963599645996559966599675996859969599705997159972599735997459975599765997759978599795998059981599825998359984599855998659987599885998959990599915999259993599945999559996599975999859999600006000160002600036000460005600066000760008600096001060011600126001360014600156001660017600186001960020600216002260023600246002560026600276002860029600306003160032600336003460035600366003760038600396004060041600426004360044600456004660047600486004960050600516005260053600546005560056600576005860059600606006160062600636006460065600666006760068600696007060071600726007360074600756007660077600786007960080600816008260083600846008560086600876008860089600906009160092600936009460095600966009760098600996010060101601026010360104601056010660107601086010960110601116011260113601146011560116601176011860119601206012160122601236012460125601266012760128601296013060131601326013360134601356013660137601386013960140601416014260143601446014560146601476014860149601506015160152601536015460155601566015760158601596016060161601626016360164601656016660167601686016960170601716017260173601746017560176601776017860179601806018160182601836018460185601866018760188601896019060191601926019360194601956019660197601986019960200602016020260203602046020560206602076020860209602106021160212602136021460215602166021760218602196022060221602226022360224602256022660227602286022960230602316023260233602346023560236602376023860239602406024160242602436024460245602466024760248602496025060251602526025360254602556025660257602586025960260602616026260263602646026560266602676026860269602706027160272602736027460275602766027760278602796028060281602826028360284602856028660287602886028960290602916029260293602946029560296602976029860299603006030160302603036030460305603066030760308603096031060311603126031360314603156031660317603186031960320603216032260323603246032560326603276032860329603306033160332603336033460335603366033760338603396034060341603426034360344603456034660347603486034960350603516035260353603546035560356603576035860359603606036160362603636036460365603666036760368603696037060371603726037360374603756037660377603786037960380603816038260383603846038560386603876038860389603906039160392603936039460395603966039760398603996040060401604026040360404604056040660407604086040960410604116041260413604146041560416604176041860419604206042160422604236042460425604266042760428604296043060431604326043360434604356043660437604386043960440604416044260443604446044560446604476044860449604506045160452604536045460455604566045760458604596046060461604626046360464604656046660467604686046960470604716047260473604746047560476604776047860479604806048160482604836048460485604866048760488604896049060491604926049360494604956049660497604986049960500605016050260503605046050560506605076050860509605106051160512605136051460515605166051760518605196052060521605226052360524605256052660527605286052960530605316053260533605346053560536605376053860539605406054160542605436054460545605466054760548605496055060551605526055360554605556055660557605586055960560605616056260563605646056560566605676056860569605706057160572605736057460575605766057760578605796058060581605826058360584605856058660587605886058960590605916059260593605946059560596605976059860599606006060160602606036060460605606066060760608606096061060611606126061360614606156061660617606186061960620606216062260623606246062560626606276062860629606306063160632606336063460635606366063760638606396064060641606426064360644606456064660647606486064960650606516065260653606546065560656606576065860659606606066160662606636066460665606666066760668606696067060671606726067360674606756067660677606786067960680606816068260683606846068560686606876068860689606906069160692606936069460695606966069760698606996070060701607026070360704607056070660707607086070960710607116071260713607146071560716607176071860719607206072160722607236072460725607266072760728607296073060731607326073360734607356073660737607386073960740607416074260743607446074560746607476074860749607506075160752607536075460755607566075760758607596076060761607626076360764607656076660767607686076960770607716077260773607746077560776607776077860779607806078160782607836078460785607866078760788607896079060791607926079360794607956079660797607986079960800608016080260803608046080560806608076080860809608106081160812608136081460815608166081760818608196082060821608226082360824608256082660827608286082960830608316083260833608346083560836608376083860839608406084160842608436084460845608466084760848608496085060851608526085360854608556085660857608586085960860608616086260863608646086560866608676086860869608706087160872608736087460875608766087760878608796088060881608826088360884608856088660887608886088960890608916089260893608946089560896608976089860899609006090160902609036090460905609066090760908609096091060911609126091360914609156091660917609186091960920609216092260923609246092560926609276092860929609306093160932609336093460935609366093760938609396094060941609426094360944609456094660947609486094960950609516095260953609546095560956609576095860959609606096160962609636096460965609666096760968609696097060971609726097360974609756097660977609786097960980609816098260983609846098560986609876098860989609906099160992609936099460995609966099760998609996100061001610026100361004610056100661007610086100961010610116101261013610146101561016610176101861019610206102161022 |
- From fe24a2249a07c6c70767d6030b4b467a5dc6717f Mon Sep 17 00:00:00 2001
- From: popcornmix <[email protected]>
- Date: Wed, 1 May 2013 19:46:17 +0100
- Subject: [PATCH] Add dwc_otg driver
- MIME-Version: 1.0
- Content-Type: text/plain; charset=UTF-8
- Content-Transfer-Encoding: 8bit
- Signed-off-by: popcornmix <[email protected]>
- usb: dwc: fix lockdep false positive
- Signed-off-by: Kari Suvanto <[email protected]>
- usb: dwc: fix inconsistent lock state
- Signed-off-by: Kari Suvanto <[email protected]>
- Add FIQ patch to dwc_otg driver. Enable with dwc_otg.fiq_fix_enable=1. Should give about 10% more ARM performance.
- Thanks to Gordon and Costas
- Avoid dynamic memory allocation for channel lock in USB driver. Thanks ddv2005.
- Add NAK holdoff scheme. Enabled by default, disable with dwc_otg.nak_holdoff_enable=0. Thanks gsh
- Make sure we wait for the reset to finish
- dwc_otg: fix bug in dwc_otg_hcd.c resulting in silent kernel
- memory corruption, escalating to OOPS under high USB load.
- dwc_otg: Fix unsafe access of QTD during URB enqueue
- In dwc_otg_hcd_urb_enqueue during qtd creation, it was possible that the
- transaction could complete almost immediately after the qtd was assigned
- to a host channel during URB enqueue, which meant the qtd pointer was no
- longer valid having been completed and removed. Usually, this resulted in
- an OOPS during URB submission. By predetermining whether transactions
- need to be queued or not, this unsafe pointer access is avoided.
- This bug was only evident on the Pi model A where a device was attached
- that had no periodic endpoints (e.g. USB pendrive or some wlan devices).
- dwc_otg: Fix incorrect URB allocation error handling
- If the memory allocation for a dwc_otg_urb failed, the kernel would OOPS
- because for some reason a member of the *unallocated* struct was set to
- zero. Error handling changed to fail correctly.
- dwc_otg: fix potential use-after-free case in interrupt handler
- If a transaction had previously aborted, certain interrupts are
- enabled to track error counts and reset where necessary. On IN
- endpoints the host generates an ACK interrupt near-simultaneously
- with completion of transfer. In the case where this transfer had
- previously had an error, this results in a use-after-free on
- the QTD memory space with a 1-byte length being overwritten to
- 0x00.
- dwc_otg: add handling of SPLIT transaction data toggle errors
- Previously a data toggle error on packets from a USB1.1 device behind
- a TT would result in the Pi locking up as the driver never handled
- the associated interrupt. Patch adds basic retry mechanism and
- interrupt acknowledgement to cater for either a chance toggle error or
- for devices that have a broken initial toggle state (FT8U232/FT232BM).
- dwc_otg: implement tasklet for returning URBs to usbcore hcd layer
- The dwc_otg driver interrupt handler for transfer completion will spend
- a very long time with interrupts disabled when a URB is completed -
- this is because usb_hcd_giveback_urb is called from within the handler
- which for a USB device driver with complicated processing (e.g. webcam)
- will take an exorbitant amount of time to complete. This results in
- missed completion interrupts for other USB packets which lead to them
- being dropped due to microframe overruns.
- This patch splits returning the URB to the usb hcd layer into a
- high-priority tasklet. This will have most benefit for isochronous IN
- transfers but will also have incidental benefit where multiple periodic
- devices are active at once.
- dwc_otg: fix NAK holdoff and allow on split transactions only
- This corrects a bug where if a single active non-periodic endpoint
- had at least one transaction in its qh, on frnum == MAX_FRNUM the qh
- would get skipped and never get queued again. This would result in
- a silent device until error detection (automatic or otherwise) would
- either reset the device or flush and requeue the URBs.
- Additionally the NAK holdoff was enabled for all transactions - this
- would potentially stall a HS endpoint for 1ms if a previous error state
- enabled this interrupt and the next response was a NAK. Fix so that
- only split transactions get held off.
- dwc_otg: Call usb_hcd_unlink_urb_from_ep with lock held in completion handler
- usb_hcd_unlink_urb_from_ep must be called with the HCD lock held. Calling it
- asynchronously in the tasklet was not safe (regression in
- c4564d4a1a0a9b10d4419e48239f5d99e88d2667).
- This change unlinks it from the endpoint prior to queueing it for handling in
- the tasklet, and also adds a check to ensure the urb is OK to be unlinked
- before doing so.
- NULL pointer dereference kernel oopses had been observed in usb_hcd_giveback_urb
- when a USB device was unplugged/replugged during data transfer. This effect
- was reproduced using automated USB port power control, hundreds of replug
- events were performed during active transfers to confirm that the problem was
- eliminated.
- USB fix using a FIQ to implement split transactions
- This commit adds a FIQ implementaion that schedules
- the split transactions using a FIQ so we don't get
- held off by the interrupt latency of Linux
- dwc_otg: fix device attributes and avoid kernel warnings on boot
- dcw_otg: avoid logging function that can cause panics
- See: https://github.com/raspberrypi/firmware/issues/21
- Thanks to cleverca22 for fix
- dwc_otg: mask correct interrupts after transaction error recovery
- The dwc_otg driver will unmask certain interrupts on a transaction
- that previously halted in the error state in order to reset the
- QTD error count. The various fine-grained interrupt handlers do not
- consider that other interrupts besides themselves were unmasked.
- By disabling the two other interrupts only ever enabled in DMA mode
- for this purpose, we can avoid unnecessary function calls in the
- IRQ handler. This will also prevent an unneccesary FIQ interrupt
- from being generated if the FIQ is enabled.
- dwc_otg: fiq: prevent FIQ thrash and incorrect state passing to IRQ
- In the case of a transaction to a device that had previously aborted
- due to an error, several interrupts are enabled to reset the error
- count when a device responds. This has the side-effect of making the
- FIQ thrash because the hardware will generate multiple instances of
- a NAK on an IN bulk/interrupt endpoint and multiple instances of ACK
- on an OUT bulk/interrupt endpoint. Make the FIQ mask and clear the
- associated interrupts.
- Additionally, on non-split transactions make sure that only unmasked
- interrupts are cleared. This caused a hard-to-trigger but serious
- race condition when you had the combination of an endpoint awaiting
- error recovery and a transaction completed on an endpoint - due to
- the sequencing and timing of interrupts generated by the dwc_otg core,
- it was possible to confuse the IRQ handler.
- Fix function tracing
- dwc_otg: whitespace cleanup in dwc_otg_urb_enqueue
- dwc_otg: prevent OOPSes during device disconnects
- The dwc_otg_urb_enqueue function is thread-unsafe. In particular the
- access of urb->hcpriv, usb_hcd_link_urb_to_ep, dwc_otg_urb->qtd and
- friends does not occur within a critical section and so if a device
- was unplugged during activity there was a high chance that the
- usbcore hub_thread would try to disable the endpoint with partially-
- formed entries in the URB queue. This would result in BUG() or null
- pointer dereferences.
- Fix so that access of urb->hcpriv, enqueuing to the hardware and
- adding to usbcore endpoint URB lists is contained within a single
- critical section.
- dwc_otg: prevent BUG() in TT allocation if hub address is > 16
- A fixed-size array is used to track TT allocation. This was
- previously set to 16 which caused a crash because
- dwc_otg_hcd_allocate_port would read past the end of the array.
- This was hit if a hub was plugged in which enumerated as addr > 16,
- due to previous device resets or unplugs.
- Also add #ifdef FIQ_DEBUG around hcd->hub_port_alloc[], which grows
- to a large size if 128 hub addresses are supported. This field is
- for debug only for tracking which frame an allocate happened in.
- dwc_otg: make channel halts with unknown state less damaging
- If the IRQ received a channel halt interrupt through the FIQ
- with no other bits set, the IRQ would not release the host
- channel and never complete the URB.
- Add catchall handling to treat as a transaction error and retry.
- dwc_otg: fiq_split: use TTs with more granularity
- This fixes certain issues with split transaction scheduling.
- - Isochronous multi-packet OUT transactions now hog the TT until
- they are completed - this prevents hubs aborting transactions
- if they get a periodic start-split out-of-order
- - Don't perform TT allocation on non-periodic endpoints - this
- allows simultaneous use of the TT's bulk/control and periodic
- transaction buffers
- This commit will mainly affect USB audio playback.
- dwc_otg: fix potential sleep while atomic during urb enqueue
- Fixes a regression introduced with eb1b482a. Kmalloc called from
- dwc_otg_hcd_qtd_add / dwc_otg_hcd_qtd_create did not always have
- the GPF_ATOMIC flag set. Force this flag when inside the larger
- critical section.
- dwc_otg: make fiq_split_enable imply fiq_fix_enable
- Failing to set up the FIQ correctly would result in
- "IRQ 32: nobody cared" errors in dmesg.
- dwc_otg: prevent crashes on host port disconnects
- Fix several issues resulting in crashes or inconsistent state
- if a Model A root port was disconnected.
- - Clean up queue heads properly in kill_urbs_in_qh_list by
- removing the empty QHs from the schedule lists
- - Set the halt status properly to prevent IRQ handlers from
- using freed memory
- - Add fiq_split related cleanup for saved registers
- - Make microframe scheduling reclaim host channels if
- active during a disconnect
- - Abort URBs with -ESHUTDOWN status response, informing
- device drivers so they respond in a more correct fashion
- and don't try to resubmit URBs
- - Prevent IRQ handlers from attempting to handle channel
- interrupts if the associated URB was dequeued (and the
- driver state was cleared)
- dwc_otg: prevent leaking URBs during enqueue
- A dwc_otg_urb would get leaked if the HCD enqueue function
- failed for any reason. Free the URB at the appropriate points.
- dwc_otg: Enable NAK holdoff for control split transactions
- Certain low-speed devices take a very long time to complete a
- data or status stage of a control transaction, producing NAK
- responses until they complete internal processing - the USB2.0
- spec limit is up to 500mS. This causes the same type of interrupt
- storm as seen with USB-serial dongles prior to c8edb238.
- In certain circumstances, usually while booting, this interrupt
- storm could cause SD card timeouts.
- dwc_otg: Fix for occasional lockup on boot when doing a USB reset
- dwc_otg: Don't issue traffic to LS devices in FS mode
- Issuing low-speed packets when the root port is in full-speed mode
- causes the root port to stop responding. Explicitly fail when
- enqueuing URBs to a LS endpoint on a FS bus.
- Fix ARM architecture issue with local_irq_restore()
- If local_fiq_enable() is called before a local_irq_restore(flags) where
- the flags variable has the F bit set, the FIQ will be erroneously disabled.
- Fixup arch_local_irq_restore to avoid trampling the F bit in CPSR.
- Also fix some of the hacks previously implemented for previous dwc_otg
- incarnations.
- 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
- dwc_otg: Fix various issues with root port and transaction errors
- Process the host port interrupts correctly (and don't trample them).
- Root port hotplug now functional again.
- Fix a few thinkos with the transaction error passthrough for fiq_fsm.
- fiq_fsm: Implement hack for Split Interrupt transactions
- Hubs aren't too picky about which endpoint we send Control type split
- transactions to. By treating Interrupt transfers as Control, it is
- possible to use the non-periodic queue in the OTG core as well as the
- non-periodic FIFOs in the hub itself. This massively reduces the
- microframe exclusivity/contention that periodic split transactions
- otherwise have to enforce.
- It goes without saying that this is a fairly egregious USB specification
- violation, but it works.
- Original idea by Hans Petter Selasky @ FreeBSD.org.
- dwc_otg: FIQ support on SMP. Set up FIQ stack and handler on Core 0 only.
- dwc_otg: introduce fiq_fsm_spin(un|)lock()
- SMP safety for the FIQ relies on register read-modify write cycles being
- completed in the correct order. Several places in the DWC code modify
- registers also touched by the FIQ. Protect these by a bare-bones lock
- mechanism.
- This also makes it possible to run the FIQ and IRQ handlers on different
- cores.
- fiq_fsm: fix build on bcm2708 and bcm2709 platforms
- dwc_otg: put some barriers back where they should be for UP
- bcm2709/dwc_otg: Setup FIQ on core 1 if >1 core active
- dwc_otg: fixup read-modify-write in critical paths
- Be more careful about read-modify-write on registers that the FIQ
- also touches.
- Guard fiq_fsm_spin_lock with fiq_enable check
- fiq_fsm: Falling out of the state machine isn't fatal
- This edge case can be hit if the port is disabled while the FIQ is
- in the middle of a transaction. Make the effects less severe.
- Also get rid of the useless return value.
- squash: dwc_otg: Allow to build without SMP
- usb: core: make overcurrent messages more prominent
- Hub overcurrent messages are more serious than "debug". Increase loglevel.
- usb: dwc_otg: Don't use dma_to_virt()
- Commit 6ce0d20 changes dma_to_virt() which breaks this driver.
- Open code the old dma_to_virt() implementation to work around this.
- Limit the use of __bus_to_virt() to cases where transfer_buffer_length
- is set and transfer_buffer is not set. This is done to increase the
- chance that this driver will also work on ARCH_BCM2835.
- transfer_buffer should not be NULL if the length is set, but the
- comment in the code indicates that there are situations where this
- might happen. drivers/usb/isp1760/isp1760-hcd.c also has a similar
- comment pointing to a possible: 'usb storage / SCSI bug'.
- Signed-off-by: Noralf Trønnes <[email protected]>
- dwc_otg: Fix crash when fiq_enable=0
- dwc_otg: fiq_fsm: Make high-speed isochronous strided transfers work properly
- Certain low-bandwidth high-speed USB devices (specialist audio devices,
- compressed-frame webcams) have packet intervals > 1 microframe.
- Stride these transfers in the FIQ by using the start-of-frame interrupt
- to restart the channel at the right time.
- dwc_otg: Force host mode to fix incorrect compute module boards
- dwc_otg: Add ARCH_BCM2835 support
- Signed-off-by: Noralf Trønnes <[email protected]>
- dwc_otg: Simplify FIQ irq number code
- Dropping ATAGS means we can simplify the FIQ irq number code.
- Also add error checking on the returned irq number.
- Signed-off-by: Noralf Trønnes <[email protected]>
- dwc_otg: Remove duplicate gadget probe/unregister function
- dwc_otg: Properly set the HFIR
- Douglas Anderson reported:
- According to the most up to date version of the dwc2 databook, the FRINT
- field of the HFIR register should be programmed to:
- * 125 us * (PHY clock freq for HS) - 1
- * 1000 us * (PHY clock freq for FS/LS) - 1
- This is opposed to older versions of the doc that claimed it should be:
- * 125 us * (PHY clock freq for HS)
- * 1000 us * (PHY clock freq for FS/LS)
- and reported lower timing jitter on a USB analyser
- dcw_otg: trim xfer length when buffer larger than allocated size is received
- dwc_otg: Don't free qh align buffers in atomic context
- dwc_otg: Enable the hack for Split Interrupt transactions by default
- dwc_otg.fiq_fsm_mask=0xF has long been a suggestion for users with audio stutters or other USB bandwidth issues.
- So far we are aware of many success stories but no failure caused by this setting.
- Make it a default to learn more.
- See: https://www.raspberrypi.org/forums/viewtopic.php?f=28&t=70437
- Signed-off-by: popcornmix <[email protected]>
- dwc_otg: Use kzalloc when suitable
- dwc_otg: Pass struct device to dma_alloc*()
- This makes it possible to get the bus address from Device Tree.
- Signed-off-by: Noralf Trønnes <[email protected]>
- dwc_otg: fix summarize urb->actual_length for isochronous transfers
- Kernel does not copy input data of ISO transfers to userspace
- if actual_length is set only in ISO transfers and not summarized
- in urb->actual_length. Fixes raspberrypi/linux#903
- fiq_fsm: Use correct states when starting isoc OUT transfers
- In fiq_fsm_start_next_periodic() if an isochronous OUT transfer
- was selected, no regard was given as to whether this was a single-packet
- transfer or a multi-packet staged transfer.
- For single-packet transfers, this had the effect of repeatedly sending
- OUT packets with bogus data and lengths.
- Eventually if the channel was repeatedly enabled enough times, this
- would lock up the OTG core and no further bus transfers would happen.
- Set the FSM state up properly if we select a single-packet transfer.
- Fixes https://github.com/raspberrypi/linux/issues/1842
- dwc_otg: make nak_holdoff work as intended with empty queues
- If URBs reading from non-periodic split endpoints were dequeued and
- the last transfer from the endpoint was a NAK handshake, the resulting
- qh->nak_frame value was stale which would result in unnecessarily long
- polling intervals for the first subsequent transfer with a fresh URB.
- Fixup qh->nak_frame in dwc_otg_hcd_urb_dequeue and also guard against
- a case where a single URB is submitted to the endpoint, a NAK was
- received on the transfer immediately prior to receiving data and the
- device subsequently resubmits another URB past the qh->nak_frame interval.
- Fixes https://github.com/raspberrypi/linux/issues/1709
- dwc_otg: fix split transaction data toggle handling around dequeues
- See https://github.com/raspberrypi/linux/issues/1709
- Fix several issues regarding endpoint state when URBs are dequeued
- - If the HCD is disconnected, flush FIQ-enabled channels properly
- - Save the data toggle state for bulk endpoints if the last transfer
- from an endpoint where URBs were dequeued returned a data packet
- - Reset hc->start_pkt_count properly in assign_and_init_hc()
- dwc_otg: fix several potential crash sources
- On root port disconnect events, the host driver state is cleared and
- in-progress host channels are forcibly stopped. This doesn't play
- well with the FIQ running in the background, so:
- - Guard the disconnect callback with both the host spinlock and FIQ
- spinlock
- - Move qtd dereference in dwc_otg_handle_hc_fsm() after the early-out
- so we don't dereference a qtd that has gone away
- - Turn catch-all BUG()s in dwc_otg_handle_hc_fsm() into warnings.
- dwc_otg: delete hcd->channel_lock
- The lock serves no purpose as it is only held while the HCD spinlock
- is already being held.
- dwc_otg: remove unnecessary dma-mode channel halts on disconnect interrupt
- Host channels are already halted in kill_urbs_in_qh_list() with the
- subsequent interrupt processing behaving as if the URB was dequeued
- via HCD callback.
- There's no need to clobber the host channel registers a second time
- as this exposes races between the driver and host channel resulting
- in hcd->free_hc_list becoming corrupted.
- dwcotg: Allow to build without FIQ on ARM64
- Signed-off-by: popcornmix <[email protected]>
- dwc_otg: make periodic scheduling behave properly for FS buses
- If the root port is in full-speed mode, transfer times at 12mbit/s
- would be calculated but matched against high-speed quotas.
- Reinitialise hcd->frame_usecs[i] on each port enable event so that
- full-speed bandwidth can be tracked sensibly.
- Also, don't bother using the FIQ for transfers when in full-speed
- mode - at the slower bus speed, interrupt frequency is reduced by
- an order of magnitude.
- Related issue: https://github.com/raspberrypi/linux/issues/2020
- dwc_otg: fiq_fsm: Make isochronous compatibility checks work properly
- Get rid of the spammy printk and local pointer mangling.
- Also, there is a nominal benefit for using fiq_fsm for isochronous
- transfers in FS mode (~1.1k IRQs per second vs 2.1k IRQs per second)
- so remove the root port speed check.
- dwc_otg: add module parameter int_ep_interval_min
- Add a module parameter (defaulting to ignored) that clamps the polling rate
- of high-speed Interrupt endpoints to a minimum microframe interval.
- The parameter is modifiable at runtime as it is used when activating new
- endpoints (such as on device connect).
- dwc_otg: fiq_fsm: Add non-periodic TT exclusivity constraints
- Certain hub types do not discriminate between pipe direction (IN or OUT)
- when considering non-periodic transfers. Therefore these hubs get confused
- if multiple transfers are issued in different directions with the same
- device address and endpoint number.
- Constrain queuing non-periodic split transactions so they are performed
- serially in such cases.
- Related: https://github.com/raspberrypi/linux/issues/2024
- dwc_otg: Fixup change to DRIVER_ATTR interface
- dwc_otg: Fix compilation warnings
- Signed-off-by: Phil Elwell <[email protected]>
- USB_DWCOTG: Disable building dwc_otg as a module (#2265)
- When dwc_otg is built as a module, build will fail with the following
- error:
- ERROR: "DWC_TASK_HI_SCHEDULE" [drivers/usb/host/dwc_otg/dwc_otg.ko] undefined!
- scripts/Makefile.modpost:91: recipe for target '__modpost' failed
- make[1]: *** [__modpost] Error 1
- Makefile:1199: recipe for target 'modules' failed
- make: *** [modules] Error 2
- Even if the error is solved by including the missing
- DWC_TASK_HI_SCHEDULE function, the kernel will panic when loading
- dwc_otg.
- As a workaround, simply prevent user from building dwc_otg as a module
- as the current kernel does not support it.
- See: https://github.com/raspberrypi/linux/issues/2258
- Signed-off-by: Malik Olivier Boussejra <[email protected]>
- dwc_otg: New timer API
- dwc_otg: Fix removed ACCESS_ONCE->READ_ONCE
- dwc_otg: don't unconditionally force host mode in dwc_otg_cil_init()
- Add the ability to disable force_host_mode for those that want to use
- dwc_otg in both device and host modes.
- dwc_otg: Fix a regression when dequeueing isochronous transfers
- In 282bed95 (dwc_otg: make nak_holdoff work as intended with empty queues)
- the dequeue mechanism was changed to leave FIQ-enabled transfers to run
- to completion - to avoid leaving hub TT buffers with stale packets lying
- around.
- This broke FIQ-accelerated isochronous transfers, as this then meant that
- dozens of transfers were performed after the dequeue function returned.
- Restore the state machine fence for isochronous transfers.
- fiq_fsm: rewind DMA pointer for OUT transactions that fail (#2288)
- See: https://github.com/raspberrypi/linux/issues/2140
- dwc_otg: add smp_mb() to prevent driver state corruption on boot
- Occasional crashes have been seen where the FIQ code dereferences
- invalid/random pointers immediately after being set up, leading to
- panic on boot.
- The crash occurs as the FIQ code races against hcd_init_fiq() and
- the hcd_init_fiq() code races against the outstanding memory stores
- from dwc_otg_hcd_init(). Use explicit barriers after touching
- driver state.
- usb: dwc_otg: fix memory corruption in dwc_otg driver
- [Upstream commit 51b1b6491752ac066ee8d32cc66042fcc955fef6]
- The move from the staging tree to the main tree exposed a
- longstanding memory corruption bug in the dwc2 driver. The
- reordering of the driver initialization caused the dwc2 driver
- to corrupt the initialization data of the sdhci driver on the
- Raspberry Pi platform, which made the bug show up.
- The error is in calling to_usb_device(hsotg->dev), since ->dev
- is not a member of struct usb_device. The easiest fix is to
- just remove the offending code, since it is not really needed.
- Thanks to Stephen Warren for tracking down the cause of this.
- Reported-by: Andre Heider <[email protected]>
- Tested-by: Stephen Warren <[email protected]>
- Signed-off-by: Paul Zimmerman <[email protected]>
- Signed-off-by: Greg Kroah-Hartman <[email protected]>
- [lukas: port from upstream dwc2 to out-of-tree dwc_otg driver]
- Signed-off-by: Lukas Wunner <[email protected]>
- usb: dwb_otg: Fix unreachable switch statement warning
- This warning appears with GCC 7.3.0 from toolchains.bootlin.com:
- ../drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c: In function ‘fiq_fsm_update_hs_isoc’:
- ../drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c:595:61: warning: statement will never be executed [-Wswitch-unreachable]
- st->hctsiz_copy.b.xfersize = nrpackets * st->hcchar_copy.b.mps;
- ~~~~~~~~~~~~~~~~~^~~~
- Signed-off-by: Nathan Chancellor <[email protected]>
- dwc_otg: fiq_fsm: fix incorrect DMA register offset calculation
- Rationalise the offset and update all call sites.
- Fixes https://github.com/raspberrypi/linux/issues/2408
- dwc_otg: fix bug with port_addr assignment for single-TT hubs
- See https://github.com/raspberrypi/linux/issues/2734
- The "Hub Port" field in the split transaction packet was always set
- to 1 for single-TT hubs. The majority of single-TT hub products
- apparently ignore this field and broadcast to all downstream enabled
- ports, which masked the issue. A subset of hub devices apparently
- need the port number to be exact or split transactions will fail.
- usb: dwc_otg: Clean up build warnings on 64bit kernels
- No functional changes. Almost all are changes to logging lines.
- Signed-off-by: Dave Stevenson <[email protected]>
- usb: dwc_otg: Use dma allocation for mphi dummy_send buffer
- The FIQ driver used a kzalloc'ed buffer for dummy_send,
- passing a kernel virtual address to the hardware block.
- The buffer is only ever used for a dummy read, so it
- should be harmless, but there is the chance that it will
- cause exceptions.
- Use a dma allocation so that we have a genuine bus address,
- and read from that.
- Free the allocation when done for good measure.
- Signed-off-by: Dave Stevenson <[email protected]>
- dwc_otg: only do_split when we actually need to do a split
- The previous test would fail if the root port was in fullspeed mode
- and there was a hub between the FS device and the root port. While
- the transfer worked, the schedule mangling performed for high-speed
- split transfers would break leading to an 8ms polling interval.
- dwc_otg: fix locking around dequeueing and killing URBs
- kill_urbs_in_qh_list() is practically only ever called with the fiq lock
- already held, so don't spinlock twice in the case where we need to cancel
- an isochronous transfer.
- Also fix up a case where the global interrupt register could be read with
- the fiq lock not held.
- Fixes the deadlock seen in https://github.com/raspberrypi/linux/issues/2907
- ARM64/DWC_OTG: Port dwc_otg driver to ARM64
- In ARM64, the FIQ mechanism used by this driver is not current
- implemented. As a workaround, reqular IRQ is used instead
- of FIQ.
- In a separate change, the IRQ-CPU mapping is round robined
- on ARM64 to increase concurrency and allow multiple interrupts
- to be serviced at a time. This reduces the need for FIQ.
- Tests Run:
- This mechanism is most likely to break when multiple USB devices
- are attached at the same time. So the system was tested under
- stress.
- Devices:
- 1. USB Speakers playing back a FLAC audio through VLC
- at 96KHz.(Higher then typically, but supported on my speakers).
- 2. sftp transferring large files through the buildin ethernet
- connection which is connected through USB.
- 3. Keyboard and mouse attached and being used.
- Although I do occasionally hear some glitches, the music seems to
- play quite well.
- Signed-off-by: Michael Zoran <[email protected]>
- usb: dwc_otg: Clean up interrupt claiming code
- The FIQ/IRQ interrupt number identification code is scattered through
- the dwc_otg driver. Rationalise it, simplifying the code and solving
- an existing issue.
- See: https://github.com/raspberrypi/linux/issues/2612
- Signed-off-by: Phil Elwell <[email protected]>
- dwc_otg: Choose appropriate IRQ handover strategy
- 2711 has no MPHI peripheral, but the ARM Control block can fake
- interrupts. Use the size of the DTB "mphi" reg block to determine
- which is required.
- Signed-off-by: Phil Elwell <[email protected]>
- usb: host: dwc_otg: fix compiling in separate directory
- The dwc_otg Makefile does not respect the O=path argument correctly:
- include paths in CFLAGS are given relatively to object path, not source
- path. Compiling in a separate directory yields #include errors.
- Signed-off-by: Marek Behún <[email protected]>
- dwc_otg: use align_buf for small IN control transfers (#3150)
- The hardware will do a 4-byte write to memory on any IN packet received
- that is between 1 and 3 bytes long. This tramples memory in the uvcvideo
- driver, as it uses a sequence of 1- and 2-byte control transfers to
- query the min/max/range/step of each individual camera control and
- gives us buffers that are offsets into a struct.
- Catch small control transfers in the data phase and use the align_buf
- to bounce the correct number of bytes into the URB's buffer.
- In general, short packets on non-control endpoints should be OK as URBs
- should have enough buffer space for a wMaxPacket size transfer.
- See: https://github.com/raspberrypi/linux/issues/3148
- Signed-off-by: Jonathan Bell <[email protected]>
- dwc_otg: Declare DMA capability with HCD_DMA flag
- Following [1], USB controllers have to declare DMA capabilities in
- order for them to be used by adding the HCD_DMA flag to their hc_driver
- struct.
- [1] 7b81cb6bddd2 ("usb: add a HCD_DMA flag instead of guestimating DMA capabilities")
- Signed-off-by: Phil Elwell <[email protected]>
- dwc_otg: checking the urb->transfer_buffer too early (#3332)
- After enable the HIGHMEM and VMSPLIT_3G, the dwc_otg driver doesn't
- work well on Pi2/3 boards with 1G physical ram. Users experience
- the failure when copying a file of 600M size to the USB stick. And
- at the same time, the dmesg shows:
- usb 1-1.1.2: reset high-speed USB device number 8 using dwc_otg
- sd 0:0:0:0: [sda] tag#0 FAILED Result: hostbyte=DID_ERROR driverbyte=DRIVER_OK
- blk_update_request: I/O error, dev sda, sector 3024048 op 0x1:(WRITE) flags 0x4000 phys_seg 15 prio class 0
- When this happens, the sg_buf sent to the driver is located in the
- highmem region, the usb_sg_init() in the core/message.c will leave
- transfer_buffer to NULL if the sg_buf is in highmem, but in the
- dwc_otg driver, it returns -EINVAL unconditionally if transfer_buffer
- is NULL.
- The driver can handle the situation of buffer to be NULL, if it is in
- DMA mode, it will convert an address from transfer_dma.
- But if the conversion fails or it is in the PIO mode, we should check
- buffer and return -EINVAL if it is NULL.
- BugLink: https://bugs.launchpad.net/bugs/1852510
- Signed-off-by: Hui Wang <[email protected]>
- dwc_otg: constrain endpoint max packet and transfer size on split IN
- The hcd would unconditionally set the transfer length to the endpoint
- packet size for non-isoc IN transfers. If the remaining buffer length
- was less than the length of returned data, random memory would get
- scribbled over, with bad effects if it crossed a page boundary.
- Force a babble error if this happens by limiting the max transfer size
- to the available buffer space. DMA will stop writing to memory on a
- babble condition.
- The hardware expects xfersize to be an integer multiple of maxpacket
- size, so override hcchar.b.mps as well.
- Signed-off-by: Jonathan Bell <[email protected]>
- dwc_otg: fiq_fsm: pause when cancelling split transactions
- Non-periodic splits will DMA to/from the driver-provided transfer_buffer,
- which may be freed immediately after the dequeue call returns. Block until
- we know the transfer is complete.
- A similar delay is needed when cleaning up disconnects, as the FIQ could
- have started a periodic transfer in the previous microframe to the one
- that triggered a disconnect.
- Signed-off-by: Jonathan Bell <[email protected]>
- dwc_otg: fiq_fsm: add a barrier on entry into FIQ handler(s)
- On BCM2835, there is no hardware guarantee that multiple outstanding
- reads to different peripherals will complete in-order. The FIQ code
- uses peripheral reads without barriers for performance, so in the case
- where a read to a slow peripheral was issued immediately prior to FIQ
- entry, the first peripheral read that the FIQ did could end up with
- wrong read data returned.
- Add dsb(sy) on entry so that all outstanding reads are retired.
- The FIQ only issues reads to the dwc_otg core, so per-read barriers
- in the handler itself are not required.
- On BCM2836 and BCM2837 the barrier is not strictly required due to
- differences in how the peripheral bus is implemented, but having
- arch-specific handlers that introduce different latencies is risky.
- Signed-off-by: Jonathan Bell <[email protected]>
- dwc_otg: whitelist_table is now productlist_table
- dwc_otg: initialise sched_frame for periodic QHs that were parked
- If a periodic QH has no remaining QTDs, then it is removed from all
- periodic schedules. When re-adding, initialise the sched_frame and
- start_split_frame from the current value of the frame counter.
- See https://bugs.launchpad.net/raspbian/+bug/1819560
- and
- https://github.com/raspberrypi/linux/issues/3883
- Signed-off-by: Jonathan Bell <[email protected]>
- dwc_otg: Minimise header and fix build warnings
- Delete a large amount of unused declaration from "usb.h", some of which
- were causing build warnings, and get the module building cleanly.
- Signed-off-by: Phil Elwell <[email protected]>
- dwc-otg: fix clang -Wignored-attributes warning
- warning: attribute declaration must precede definition
- dwc-otg: fix clang -Wsometimes-uninitialized warning
- warning: variable 'retval' is used uninitialized whenever 'if' condition is false
- dwc-otg: fix clang -Wpointer-bool-conversion warning
- warning: address of array 'desc->wMaxPacketSize' will always evaluate to 'true'
- The wMaxPacketSize field is actually a two element array which content should
- be accessed via the UGETW macro.
- dwc_otg: fix an undeclared variable
- Replace an undeclared variable used by DWC_DEBUGPL with the real endpoint address. DWC_DEBUGPL does nothing with DEBUG undefined so it did not go wrong before.
- Signed-off-by: Zixuan Wang <[email protected]>
- dwc_otg: Update NetBSD usb.h header licence
- NetBSD have changed their licensing requirements such that the 2-clause
- licence is preferred. Update usb.h in the downstream dwc_otg code
- accordingly.
- See https://www.netbsd.org/about/redistribution.html for more
- information.
- Signed-off-by: Phil Elwell <[email protected]>
- dwc_otg: pay attention to qh->interval when rescheduling periodic queues
- A regression introduced in https://github.com/raspberrypi/linux/pull/3887
- meant that if the newly scheduled transfer immediately returned data, and
- the driver resubmitted a single URB after every transfer, then the effective
- polling interval would end up being approx 1ms.
- Use the larger of SCHEDULE_SLOP or the configured endpoint interval.
- Signed-off-by: Jonathan Bell <[email protected]>
- ---
- arch/arm/include/asm/irqflags.h | 16 +-
- arch/arm/kernel/fiqasm.S | 4 +
- drivers/usb/Makefile | 1 +
- drivers/usb/core/generic.c | 1 +
- drivers/usb/core/hub.c | 2 +-
- drivers/usb/core/message.c | 79 +
- drivers/usb/core/otg_productlist.h | 114 +-
- drivers/usb/gadget/file_storage.c | 3676 +++++++++
- drivers/usb/host/Kconfig | 10 +
- drivers/usb/host/Makefile | 1 +
- drivers/usb/host/dwc_common_port/Makefile | 58 +
- .../usb/host/dwc_common_port/Makefile.fbsd | 17 +
- .../usb/host/dwc_common_port/Makefile.linux | 49 +
- drivers/usb/host/dwc_common_port/changes.txt | 174 +
- .../usb/host/dwc_common_port/doc/doxygen.cfg | 270 +
- drivers/usb/host/dwc_common_port/dwc_cc.c | 532 ++
- drivers/usb/host/dwc_common_port/dwc_cc.h | 224 +
- .../host/dwc_common_port/dwc_common_fbsd.c | 1308 +++
- .../host/dwc_common_port/dwc_common_linux.c | 1409 ++++
- .../host/dwc_common_port/dwc_common_nbsd.c | 1275 +++
- drivers/usb/host/dwc_common_port/dwc_crypto.c | 308 +
- drivers/usb/host/dwc_common_port/dwc_crypto.h | 111 +
- drivers/usb/host/dwc_common_port/dwc_dh.c | 291 +
- drivers/usb/host/dwc_common_port/dwc_dh.h | 106 +
- drivers/usb/host/dwc_common_port/dwc_list.h | 594 ++
- drivers/usb/host/dwc_common_port/dwc_mem.c | 245 +
- drivers/usb/host/dwc_common_port/dwc_modpow.c | 636 ++
- drivers/usb/host/dwc_common_port/dwc_modpow.h | 34 +
- .../usb/host/dwc_common_port/dwc_notifier.c | 319 +
- .../usb/host/dwc_common_port/dwc_notifier.h | 122 +
- drivers/usb/host/dwc_common_port/dwc_os.h | 1275 +++
- drivers/usb/host/dwc_common_port/usb.h | 275 +
- drivers/usb/host/dwc_otg/Makefile | 85 +
- drivers/usb/host/dwc_otg/doc/doxygen.cfg | 224 +
- drivers/usb/host/dwc_otg/dummy_audio.c | 1574 ++++
- drivers/usb/host/dwc_otg/dwc_cfi_common.h | 142 +
- drivers/usb/host/dwc_otg/dwc_otg_adp.c | 854 ++
- drivers/usb/host/dwc_otg/dwc_otg_adp.h | 80 +
- drivers/usb/host/dwc_otg/dwc_otg_attr.c | 1212 +++
- drivers/usb/host/dwc_otg/dwc_otg_attr.h | 89 +
- drivers/usb/host/dwc_otg/dwc_otg_cfi.c | 1876 +++++
- drivers/usb/host/dwc_otg/dwc_otg_cfi.h | 320 +
- drivers/usb/host/dwc_otg/dwc_otg_cil.c | 7146 +++++++++++++++++
- drivers/usb/host/dwc_otg/dwc_otg_cil.h | 1464 ++++
- drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c | 1601 ++++
- drivers/usb/host/dwc_otg/dwc_otg_core_if.h | 705 ++
- drivers/usb/host/dwc_otg/dwc_otg_dbg.h | 117 +
- drivers/usb/host/dwc_otg/dwc_otg_driver.c | 1772 ++++
- drivers/usb/host/dwc_otg/dwc_otg_driver.h | 86 +
- drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c | 1433 ++++
- drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h | 399 +
- drivers/usb/host/dwc_otg/dwc_otg_fiq_stub.S | 80 +
- drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 4363 ++++++++++
- drivers/usb/host/dwc_otg/dwc_otg_hcd.h | 870 ++
- drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c | 1135 +++
- drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h | 421 +
- drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 2757 +++++++
- drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c | 1086 +++
- drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c | 974 +++
- drivers/usb/host/dwc_otg/dwc_otg_os_dep.h | 200 +
- drivers/usb/host/dwc_otg/dwc_otg_pcd.c | 2725 +++++++
- drivers/usb/host/dwc_otg/dwc_otg_pcd.h | 273 +
- drivers/usb/host/dwc_otg/dwc_otg_pcd_if.h | 361 +
- drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c | 5148 ++++++++++++
- drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c | 1262 +++
- drivers/usb/host/dwc_otg/dwc_otg_regs.h | 2550 ++++++
- drivers/usb/host/dwc_otg/test/Makefile | 16 +
- drivers/usb/host/dwc_otg/test/dwc_otg_test.pm | 337 +
- .../usb/host/dwc_otg/test/test_mod_param.pl | 133 +
- drivers/usb/host/dwc_otg/test/test_sysfs.pl | 193 +
- 70 files changed, 59583 insertions(+), 16 deletions(-)
- create mode 100644 drivers/usb/gadget/file_storage.c
- create mode 100644 drivers/usb/host/dwc_common_port/Makefile
- create mode 100644 drivers/usb/host/dwc_common_port/Makefile.fbsd
- create mode 100644 drivers/usb/host/dwc_common_port/Makefile.linux
- create mode 100644 drivers/usb/host/dwc_common_port/changes.txt
- create mode 100644 drivers/usb/host/dwc_common_port/doc/doxygen.cfg
- create mode 100644 drivers/usb/host/dwc_common_port/dwc_cc.c
- create mode 100644 drivers/usb/host/dwc_common_port/dwc_cc.h
- create mode 100644 drivers/usb/host/dwc_common_port/dwc_common_fbsd.c
- create mode 100644 drivers/usb/host/dwc_common_port/dwc_common_linux.c
- create mode 100644 drivers/usb/host/dwc_common_port/dwc_common_nbsd.c
- create mode 100644 drivers/usb/host/dwc_common_port/dwc_crypto.c
- create mode 100644 drivers/usb/host/dwc_common_port/dwc_crypto.h
- create mode 100644 drivers/usb/host/dwc_common_port/dwc_dh.c
- create mode 100644 drivers/usb/host/dwc_common_port/dwc_dh.h
- create mode 100644 drivers/usb/host/dwc_common_port/dwc_list.h
- create mode 100644 drivers/usb/host/dwc_common_port/dwc_mem.c
- create mode 100644 drivers/usb/host/dwc_common_port/dwc_modpow.c
- create mode 100644 drivers/usb/host/dwc_common_port/dwc_modpow.h
- create mode 100644 drivers/usb/host/dwc_common_port/dwc_notifier.c
- create mode 100644 drivers/usb/host/dwc_common_port/dwc_notifier.h
- create mode 100644 drivers/usb/host/dwc_common_port/dwc_os.h
- create mode 100644 drivers/usb/host/dwc_common_port/usb.h
- create mode 100644 drivers/usb/host/dwc_otg/Makefile
- create mode 100644 drivers/usb/host/dwc_otg/doc/doxygen.cfg
- create mode 100644 drivers/usb/host/dwc_otg/dummy_audio.c
- create mode 100644 drivers/usb/host/dwc_otg/dwc_cfi_common.h
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_adp.c
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_adp.h
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_attr.c
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_attr.h
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_cfi.c
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_cfi.h
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_cil.c
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_cil.h
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_core_if.h
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_dbg.h
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_driver.c
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_driver.h
- 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
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_hcd.c
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_hcd.h
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_os_dep.h
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_pcd.c
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_pcd.h
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_pcd_if.h
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c
- create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_regs.h
- create mode 100644 drivers/usb/host/dwc_otg/test/Makefile
- create mode 100644 drivers/usb/host/dwc_otg/test/dwc_otg_test.pm
- create mode 100644 drivers/usb/host/dwc_otg/test/test_mod_param.pl
- create mode 100644 drivers/usb/host/dwc_otg/test/test_sysfs.pl
- --- a/arch/arm/include/asm/irqflags.h
- +++ b/arch/arm/include/asm/irqflags.h
- @@ -163,13 +163,23 @@ static inline unsigned long arch_local_s
- }
-
- /*
- - * restore saved IRQ & FIQ state
- + * restore saved IRQ state
- */
- #define arch_local_irq_restore arch_local_irq_restore
- static inline void arch_local_irq_restore(unsigned long flags)
- {
- - asm volatile(
- - " msr " IRQMASK_REG_NAME_W ", %0 @ local_irq_restore"
- + unsigned long temp = 0;
- + flags &= ~(1 << 6);
- + asm volatile (
- + " mrs %0, cpsr"
- + : "=r" (temp)
- + :
- + : "memory", "cc");
- + /* Preserve FIQ bit */
- + temp &= (1 << 6);
- + flags = flags | temp;
- + asm volatile (
- + " msr cpsr_c, %0 @ local_irq_restore"
- :
- : "r" (flags)
- : "memory", "cc");
- --- a/arch/arm/kernel/fiqasm.S
- +++ b/arch/arm/kernel/fiqasm.S
- @@ -47,3 +47,7 @@ ENTRY(__get_fiq_regs)
- mov r0, r0 @ avoid hazard prior to ARMv4
- ret lr
- ENDPROC(__get_fiq_regs)
- +
- +ENTRY(__FIQ_Branch)
- + mov pc, r8
- +ENDPROC(__FIQ_Branch)
- --- a/drivers/usb/Makefile
- +++ b/drivers/usb/Makefile
- @@ -9,6 +9,7 @@ obj-$(CONFIG_USB_COMMON) += common/
- obj-$(CONFIG_USB) += core/
- obj-$(CONFIG_USB_SUPPORT) += phy/
-
- +obj-$(CONFIG_USB_DWCOTG) += host/
- obj-$(CONFIG_USB_DWC3) += dwc3/
- obj-$(CONFIG_USB_DWC2) += dwc2/
- obj-$(CONFIG_USB_ISP1760) += isp1760/
- --- a/drivers/usb/core/generic.c
- +++ b/drivers/usb/core/generic.c
- @@ -190,6 +190,7 @@ int usb_choose_configuration(struct usb_
- dev_warn(&udev->dev,
- "no configuration chosen from %d choice%s\n",
- num_configs, plural(num_configs));
- + dev_warn(&udev->dev, "No support over %dmA\n", udev->bus_mA);
- }
- return i;
- }
- --- a/drivers/usb/core/hub.c
- +++ b/drivers/usb/core/hub.c
- @@ -5677,7 +5677,7 @@ static void port_event(struct usb_hub *h
- port_dev->over_current_count++;
- port_over_current_notify(port_dev);
-
- - dev_dbg(&port_dev->dev, "over-current change #%u\n",
- + dev_notice(&port_dev->dev, "over-current change #%u\n",
- port_dev->over_current_count);
- usb_clear_port_feature(hdev, port1,
- USB_PORT_FEAT_C_OVER_CURRENT);
- --- a/drivers/usb/core/message.c
- +++ b/drivers/usb/core/message.c
- @@ -2135,6 +2135,85 @@ free_interfaces:
- if (cp->string == NULL &&
- !(dev->quirks & USB_QUIRK_CONFIG_INTF_STRINGS))
- cp->string = usb_cache_string(dev, cp->desc.iConfiguration);
- +/* Uncomment this define to enable the HS Electrical Test support */
- +#define DWC_HS_ELECT_TST 1
- +#ifdef DWC_HS_ELECT_TST
- + /* Here we implement the HS Electrical Test support. The
- + * tester uses a vendor ID of 0x1A0A to indicate we should
- + * run a special test sequence. The product ID tells us
- + * which sequence to run. We invoke the test sequence by
- + * sending a non-standard SetFeature command to our root
- + * hub port. Our dwc_otg_hcd_hub_control() routine will
- + * recognize the command and perform the desired test
- + * sequence.
- + */
- + if (dev->descriptor.idVendor == 0x1A0A) {
- + /* HSOTG Electrical Test */
- + dev_warn(&dev->dev, "VID from HSOTG Electrical Test Fixture\n");
- +
- + if (dev->bus && dev->bus->root_hub) {
- + struct usb_device *hdev = dev->bus->root_hub;
- + dev_warn(&dev->dev, "Got PID 0x%x\n", dev->descriptor.idProduct);
- +
- + switch (dev->descriptor.idProduct) {
- + case 0x0101: /* TEST_SE0_NAK */
- + dev_warn(&dev->dev, "TEST_SE0_NAK\n");
- + usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
- + USB_REQ_SET_FEATURE, USB_RT_PORT,
- + USB_PORT_FEAT_TEST, 0x300, NULL, 0, HZ);
- + break;
- +
- + case 0x0102: /* TEST_J */
- + dev_warn(&dev->dev, "TEST_J\n");
- + usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
- + USB_REQ_SET_FEATURE, USB_RT_PORT,
- + USB_PORT_FEAT_TEST, 0x100, NULL, 0, HZ);
- + break;
- +
- + case 0x0103: /* TEST_K */
- + dev_warn(&dev->dev, "TEST_K\n");
- + usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
- + USB_REQ_SET_FEATURE, USB_RT_PORT,
- + USB_PORT_FEAT_TEST, 0x200, NULL, 0, HZ);
- + break;
- +
- + case 0x0104: /* TEST_PACKET */
- + dev_warn(&dev->dev, "TEST_PACKET\n");
- + usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
- + USB_REQ_SET_FEATURE, USB_RT_PORT,
- + USB_PORT_FEAT_TEST, 0x400, NULL, 0, HZ);
- + break;
- +
- + case 0x0105: /* TEST_FORCE_ENABLE */
- + dev_warn(&dev->dev, "TEST_FORCE_ENABLE\n");
- + usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
- + USB_REQ_SET_FEATURE, USB_RT_PORT,
- + USB_PORT_FEAT_TEST, 0x500, NULL, 0, HZ);
- + break;
- +
- + case 0x0106: /* HS_HOST_PORT_SUSPEND_RESUME */
- + dev_warn(&dev->dev, "HS_HOST_PORT_SUSPEND_RESUME\n");
- + usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
- + USB_REQ_SET_FEATURE, USB_RT_PORT,
- + USB_PORT_FEAT_TEST, 0x600, NULL, 0, 40 * HZ);
- + break;
- +
- + case 0x0107: /* SINGLE_STEP_GET_DEVICE_DESCRIPTOR setup */
- + dev_warn(&dev->dev, "SINGLE_STEP_GET_DEVICE_DESCRIPTOR setup\n");
- + usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
- + USB_REQ_SET_FEATURE, USB_RT_PORT,
- + USB_PORT_FEAT_TEST, 0x700, NULL, 0, 40 * HZ);
- + break;
- +
- + case 0x0108: /* SINGLE_STEP_GET_DEVICE_DESCRIPTOR execute */
- + dev_warn(&dev->dev, "SINGLE_STEP_GET_DEVICE_DESCRIPTOR execute\n");
- + usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
- + USB_REQ_SET_FEATURE, USB_RT_PORT,
- + USB_PORT_FEAT_TEST, 0x800, NULL, 0, 40 * HZ);
- + }
- + }
- + }
- +#endif /* DWC_HS_ELECT_TST */
-
- /* Now that the interfaces are installed, re-enable LPM. */
- usb_unlocked_enable_lpm(dev);
- --- a/drivers/usb/core/otg_productlist.h
- +++ b/drivers/usb/core/otg_productlist.h
- @@ -11,33 +11,82 @@
- static struct usb_device_id productlist_table[] = {
-
- /* hubs are optional in OTG, but very handy ... */
- +#define CERT_WITHOUT_HUBS
- +#if defined(CERT_WITHOUT_HUBS)
- +{ USB_DEVICE( 0x0000, 0x0000 ), }, /* Root HUB Only*/
- +#else
- { USB_DEVICE_INFO(USB_CLASS_HUB, 0, 0), },
- { USB_DEVICE_INFO(USB_CLASS_HUB, 0, 1), },
- +{ USB_DEVICE_INFO(USB_CLASS_HUB, 0, 2), },
- +#endif
-
- #ifdef CONFIG_USB_PRINTER /* ignoring nonstatic linkage! */
- /* FIXME actually, printers are NOT supposed to use device classes;
- * they're supposed to use interface classes...
- */
- -{ USB_DEVICE_INFO(7, 1, 1) },
- -{ USB_DEVICE_INFO(7, 1, 2) },
- -{ USB_DEVICE_INFO(7, 1, 3) },
- +//{ USB_DEVICE_INFO(7, 1, 1) },
- +//{ USB_DEVICE_INFO(7, 1, 2) },
- +//{ USB_DEVICE_INFO(7, 1, 3) },
- #endif
-
- #ifdef CONFIG_USB_NET_CDCETHER
- /* Linux-USB CDC Ethernet gadget */
- -{ USB_DEVICE(0x0525, 0xa4a1), },
- +//{ USB_DEVICE(0x0525, 0xa4a1), },
- /* Linux-USB CDC Ethernet + RNDIS gadget */
- -{ USB_DEVICE(0x0525, 0xa4a2), },
- +//{ USB_DEVICE(0x0525, 0xa4a2), },
- #endif
-
- #if IS_ENABLED(CONFIG_USB_TEST)
- /* gadget zero, for testing */
- -{ USB_DEVICE(0x0525, 0xa4a0), },
- +//{ USB_DEVICE(0x0525, 0xa4a0), },
- #endif
-
- +/* OPT Tester */
- +{ USB_DEVICE( 0x1a0a, 0x0101 ), }, /* TEST_SE0_NAK */
- +{ USB_DEVICE( 0x1a0a, 0x0102 ), }, /* Test_J */
- +{ USB_DEVICE( 0x1a0a, 0x0103 ), }, /* Test_K */
- +{ USB_DEVICE( 0x1a0a, 0x0104 ), }, /* Test_PACKET */
- +{ USB_DEVICE( 0x1a0a, 0x0105 ), }, /* Test_FORCE_ENABLE */
- +{ USB_DEVICE( 0x1a0a, 0x0106 ), }, /* HS_PORT_SUSPEND_RESUME */
- +{ USB_DEVICE( 0x1a0a, 0x0107 ), }, /* SINGLE_STEP_GET_DESCRIPTOR setup */
- +{ USB_DEVICE( 0x1a0a, 0x0108 ), }, /* SINGLE_STEP_GET_DESCRIPTOR execute */
- +
- +/* Sony cameras */
- +{ USB_DEVICE_VER(0x054c,0x0010,0x0410, 0x0500), },
- +
- +/* Memory Devices */
- +//{ USB_DEVICE( 0x0781, 0x5150 ), }, /* SanDisk */
- +//{ USB_DEVICE( 0x05DC, 0x0080 ), }, /* Lexar */
- +//{ USB_DEVICE( 0x4146, 0x9281 ), }, /* IOMEGA */
- +//{ USB_DEVICE( 0x067b, 0x2507 ), }, /* Hammer 20GB External HD */
- +{ USB_DEVICE( 0x0EA0, 0x2168 ), }, /* Ours Technology Inc. (BUFFALO ClipDrive)*/
- +//{ USB_DEVICE( 0x0457, 0x0150 ), }, /* Silicon Integrated Systems Corp. */
- +
- +/* HP Printers */
- +//{ USB_DEVICE( 0x03F0, 0x1102 ), }, /* HP Photosmart 245 */
- +//{ USB_DEVICE( 0x03F0, 0x1302 ), }, /* HP Photosmart 370 Series */
- +
- +/* Speakers */
- +//{ USB_DEVICE( 0x0499, 0x3002 ), }, /* YAMAHA YST-MS35D USB Speakers */
- +//{ USB_DEVICE( 0x0672, 0x1041 ), }, /* Labtec USB Headset */
- +
- { } /* Terminating entry */
- };
-
- +static inline void report_errors(struct usb_device *dev)
- +{
- + /* OTG MESSAGE: report errors here, customize to match your product */
- + dev_info(&dev->dev, "device Vendor:%04x Product:%04x is not supported\n",
- + le16_to_cpu(dev->descriptor.idVendor),
- + le16_to_cpu(dev->descriptor.idProduct));
- + if (USB_CLASS_HUB == dev->descriptor.bDeviceClass){
- + dev_printk(KERN_CRIT, &dev->dev, "Unsupported Hub Topology\n");
- + } else {
- + dev_printk(KERN_CRIT, &dev->dev, "Attached Device is not Supported\n");
- + }
- +}
- +
- +
- static int is_targeted(struct usb_device *dev)
- {
- struct usb_device_id *id = productlist_table;
- @@ -87,16 +136,57 @@ static int is_targeted(struct usb_device
- continue;
-
- return 1;
- - }
- + /* NOTE: can't use usb_match_id() since interface caches
- + * aren't set up yet. this is cut/paste from that code.
- + */
- + for (id = productlist_table; id->match_flags; id++) {
- +#ifdef DEBUG
- + dev_dbg(&dev->dev,
- + "ID: V:%04x P:%04x DC:%04x SC:%04x PR:%04x \n",
- + id->idVendor,
- + id->idProduct,
- + id->bDeviceClass,
- + id->bDeviceSubClass,
- + id->bDeviceProtocol);
- +#endif
-
- - /* add other match criteria here ... */
- + if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
- + id->idVendor != le16_to_cpu(dev->descriptor.idVendor))
- + continue;
- +
- + if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) &&
- + id->idProduct != le16_to_cpu(dev->descriptor.idProduct))
- + continue;
- +
- + /* No need to test id->bcdDevice_lo != 0, since 0 is never
- + greater than any unsigned number. */
- + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) &&
- + (id->bcdDevice_lo > le16_to_cpu(dev->descriptor.bcdDevice)))
- + continue;
- +
- + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) &&
- + (id->bcdDevice_hi < le16_to_cpu(dev->descriptor.bcdDevice)))
- + continue;
- +
- + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) &&
- + (id->bDeviceClass != dev->descriptor.bDeviceClass))
- + continue;
- +
- + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) &&
- + (id->bDeviceSubClass != dev->descriptor.bDeviceSubClass))
- + continue;
- +
- + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) &&
- + (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))
- + continue;
-
- + return 1;
- + }
- + }
-
- - /* OTG MESSAGE: report errors here, customize to match your product */
- - dev_err(&dev->dev, "device v%04x p%04x is not supported\n",
- - le16_to_cpu(dev->descriptor.idVendor),
- - le16_to_cpu(dev->descriptor.idProduct));
- + /* add other match criteria here ... */
-
- + report_errors(dev);
- return 0;
- }
-
- --- /dev/null
- +++ b/drivers/usb/gadget/file_storage.c
- @@ -0,0 +1,3676 @@
- +/*
- + * file_storage.c -- File-backed USB Storage Gadget, for USB development
- + *
- + * Copyright (C) 2003-2008 Alan Stern
- + * All rights reserved.
- + *
- + * Redistribution and use in source and binary forms, with or without
- + * modification, are permitted provided that the following conditions
- + * are met:
- + * 1. Redistributions of source code must retain the above copyright
- + * notice, this list of conditions, and the following disclaimer,
- + * without modification.
- + * 2. 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.
- + * 3. The names of the above-listed copyright holders may not be used
- + * to endorse or promote products derived from this software without
- + * specific prior written permission.
- + *
- + * ALTERNATIVELY, this software may be distributed under the terms of the
- + * GNU General Public License ("GPL") as published by the Free Software
- + * Foundation, either version 2 of that License or (at your option) any
- + * later version.
- + *
- + * 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 THE COPYRIGHT OWNER OR
- + * CONTRIBUTORS 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.
- + */
- +
- +
- +/*
- + * The File-backed Storage Gadget acts as a USB Mass Storage device,
- + * appearing to the host as a disk drive or as a CD-ROM drive. In addition
- + * to providing an example of a genuinely useful gadget driver for a USB
- + * device, it also illustrates a technique of double-buffering for increased
- + * throughput. Last but not least, it gives an easy way to probe the
- + * behavior of the Mass Storage drivers in a USB host.
- + *
- + * Backing storage is provided by a regular file or a block device, specified
- + * by the "file" module parameter. Access can be limited to read-only by
- + * setting the optional "ro" module parameter. (For CD-ROM emulation,
- + * access is always read-only.) The gadget will indicate that it has
- + * removable media if the optional "removable" module parameter is set.
- + *
- + * The gadget supports the Control-Bulk (CB), Control-Bulk-Interrupt (CBI),
- + * and Bulk-Only (also known as Bulk-Bulk-Bulk or BBB) transports, selected
- + * by the optional "transport" module parameter. It also supports the
- + * following protocols: RBC (0x01), ATAPI or SFF-8020i (0x02), QIC-157 (0c03),
- + * UFI (0x04), SFF-8070i (0x05), and transparent SCSI (0x06), selected by
- + * the optional "protocol" module parameter. In addition, the default
- + * Vendor ID, Product ID, release number and serial number can be overridden.
- + *
- + * There is support for multiple logical units (LUNs), each of which has
- + * its own backing file. The number of LUNs can be set using the optional
- + * "luns" module parameter (anywhere from 1 to 8), and the corresponding
- + * files are specified using comma-separated lists for "file" and "ro".
- + * The default number of LUNs is taken from the number of "file" elements;
- + * it is 1 if "file" is not given. If "removable" is not set then a backing
- + * file must be specified for each LUN. If it is set, then an unspecified
- + * or empty backing filename means the LUN's medium is not loaded. Ideally
- + * each LUN would be settable independently as a disk drive or a CD-ROM
- + * drive, but currently all LUNs have to be the same type. The CD-ROM
- + * emulation includes a single data track and no audio tracks; hence there
- + * need be only one backing file per LUN.
- + *
- + * Requirements are modest; only a bulk-in and a bulk-out endpoint are
- + * needed (an interrupt-out endpoint is also needed for CBI). The memory
- + * requirement amounts to two 16K buffers, size configurable by a parameter.
- + * Support is included for both full-speed and high-speed operation.
- + *
- + * Note that the driver is slightly non-portable in that it assumes a
- + * single memory/DMA buffer will be useable for bulk-in, bulk-out, and
- + * interrupt-in endpoints. With most device controllers this isn't an
- + * issue, but there may be some with hardware restrictions that prevent
- + * a buffer from being used by more than one endpoint.
- + *
- + * Module options:
- + *
- + * file=filename[,filename...]
- + * Required if "removable" is not set, names of
- + * the files or block devices used for
- + * backing storage
- + * serial=HHHH... Required serial number (string of hex chars)
- + * ro=b[,b...] Default false, booleans for read-only access
- + * removable Default false, boolean for removable media
- + * luns=N Default N = number of filenames, number of
- + * LUNs to support
- + * nofua=b[,b...] Default false, booleans for ignore FUA flag
- + * in SCSI WRITE(10,12) commands
- + * stall Default determined according to the type of
- + * USB device controller (usually true),
- + * boolean to permit the driver to halt
- + * bulk endpoints
- + * cdrom Default false, boolean for whether to emulate
- + * a CD-ROM drive
- + * transport=XXX Default BBB, transport name (CB, CBI, or BBB)
- + * protocol=YYY Default SCSI, protocol name (RBC, 8020 or
- + * ATAPI, QIC, UFI, 8070, or SCSI;
- + * also 1 - 6)
- + * vendor=0xVVVV Default 0x0525 (NetChip), USB Vendor ID
- + * product=0xPPPP Default 0xa4a5 (FSG), USB Product ID
- + * release=0xRRRR Override the USB release number (bcdDevice)
- + * buflen=N Default N=16384, buffer size used (will be
- + * rounded down to a multiple of
- + * PAGE_CACHE_SIZE)
- + *
- + * If CONFIG_USB_FILE_STORAGE_TEST is not set, only the "file", "serial", "ro",
- + * "removable", "luns", "nofua", "stall", and "cdrom" options are available;
- + * default values are used for everything else.
- + *
- + * The pathnames of the backing files and the ro settings are available in
- + * the attribute files "file", "nofua", and "ro" in the lun<n> subdirectory of
- + * the gadget's sysfs directory. If the "removable" option is set, writing to
- + * these files will simulate ejecting/loading the medium (writing an empty
- + * line means eject) and adjusting a write-enable tab. Changes to the ro
- + * setting are not allowed when the medium is loaded or if CD-ROM emulation
- + * is being used.
- + *
- + * This gadget driver is heavily based on "Gadget Zero" by David Brownell.
- + * The driver's SCSI command interface was based on the "Information
- + * technology - Small Computer System Interface - 2" document from
- + * X3T9.2 Project 375D, Revision 10L, 7-SEP-93, available at
- + * <http://www.t10.org/ftp/t10/drafts/s2/s2-r10l.pdf>. The single exception
- + * is opcode 0x23 (READ FORMAT CAPACITIES), which was based on the
- + * "Universal Serial Bus Mass Storage Class UFI Command Specification"
- + * document, Revision 1.0, December 14, 1998, available at
- + * <http://www.usb.org/developers/devclass_docs/usbmass-ufi10.pdf>.
- + */
- +
- +
- +/*
- + * Driver Design
- + *
- + * The FSG driver is fairly straightforward. There is a main kernel
- + * thread that handles most of the work. Interrupt routines field
- + * callbacks from the controller driver: bulk- and interrupt-request
- + * completion notifications, endpoint-0 events, and disconnect events.
- + * Completion events are passed to the main thread by wakeup calls. Many
- + * ep0 requests are handled at interrupt time, but SetInterface,
- + * SetConfiguration, and device reset requests are forwarded to the
- + * thread in the form of "exceptions" using SIGUSR1 signals (since they
- + * should interrupt any ongoing file I/O operations).
- + *
- + * The thread's main routine implements the standard command/data/status
- + * parts of a SCSI interaction. It and its subroutines are full of tests
- + * for pending signals/exceptions -- all this polling is necessary since
- + * the kernel has no setjmp/longjmp equivalents. (Maybe this is an
- + * indication that the driver really wants to be running in userspace.)
- + * An important point is that so long as the thread is alive it keeps an
- + * open reference to the backing file. This will prevent unmounting
- + * the backing file's underlying filesystem and could cause problems
- + * during system shutdown, for example. To prevent such problems, the
- + * thread catches INT, TERM, and KILL signals and converts them into
- + * an EXIT exception.
- + *
- + * In normal operation the main thread is started during the gadget's
- + * fsg_bind() callback and stopped during fsg_unbind(). But it can also
- + * exit when it receives a signal, and there's no point leaving the
- + * gadget running when the thread is dead. So just before the thread
- + * exits, it deregisters the gadget driver. This makes things a little
- + * tricky: The driver is deregistered at two places, and the exiting
- + * thread can indirectly call fsg_unbind() which in turn can tell the
- + * thread to exit. The first problem is resolved through the use of the
- + * REGISTERED atomic bitflag; the driver will only be deregistered once.
- + * The second problem is resolved by having fsg_unbind() check
- + * fsg->state; it won't try to stop the thread if the state is already
- + * FSG_STATE_TERMINATED.
- + *
- + * To provide maximum throughput, the driver uses a circular pipeline of
- + * buffer heads (struct fsg_buffhd). In principle the pipeline can be
- + * arbitrarily long; in practice the benefits don't justify having more
- + * than 2 stages (i.e., double buffering). But it helps to think of the
- + * pipeline as being a long one. Each buffer head contains a bulk-in and
- + * a bulk-out request pointer (since the buffer can be used for both
- + * output and input -- directions always are given from the host's
- + * point of view) as well as a pointer to the buffer and various state
- + * variables.
- + *
- + * Use of the pipeline follows a simple protocol. There is a variable
- + * (fsg->next_buffhd_to_fill) that points to the next buffer head to use.
- + * At any time that buffer head may still be in use from an earlier
- + * request, so each buffer head has a state variable indicating whether
- + * it is EMPTY, FULL, or BUSY. Typical use involves waiting for the
- + * buffer head to be EMPTY, filling the buffer either by file I/O or by
- + * USB I/O (during which the buffer head is BUSY), and marking the buffer
- + * head FULL when the I/O is complete. Then the buffer will be emptied
- + * (again possibly by USB I/O, during which it is marked BUSY) and
- + * finally marked EMPTY again (possibly by a completion routine).
- + *
- + * A module parameter tells the driver to avoid stalling the bulk
- + * endpoints wherever the transport specification allows. This is
- + * necessary for some UDCs like the SuperH, which cannot reliably clear a
- + * halt on a bulk endpoint. However, under certain circumstances the
- + * Bulk-only specification requires a stall. In such cases the driver
- + * will halt the endpoint and set a flag indicating that it should clear
- + * the halt in software during the next device reset. Hopefully this
- + * will permit everything to work correctly. Furthermore, although the
- + * specification allows the bulk-out endpoint to halt when the host sends
- + * too much data, implementing this would cause an unavoidable race.
- + * The driver will always use the "no-stall" approach for OUT transfers.
- + *
- + * One subtle point concerns sending status-stage responses for ep0
- + * requests. Some of these requests, such as device reset, can involve
- + * interrupting an ongoing file I/O operation, which might take an
- + * arbitrarily long time. During that delay the host might give up on
- + * the original ep0 request and issue a new one. When that happens the
- + * driver should not notify the host about completion of the original
- + * request, as the host will no longer be waiting for it. So the driver
- + * assigns to each ep0 request a unique tag, and it keeps track of the
- + * tag value of the request associated with a long-running exception
- + * (device-reset, interface-change, or configuration-change). When the
- + * exception handler is finished, the status-stage response is submitted
- + * only if the current ep0 request tag is equal to the exception request
- + * tag. Thus only the most recently received ep0 request will get a
- + * status-stage response.
- + *
- + * Warning: This driver source file is too long. It ought to be split up
- + * into a header file plus about 3 separate .c files, to handle the details
- + * of the Gadget, USB Mass Storage, and SCSI protocols.
- + */
- +
- +
- +/* #define VERBOSE_DEBUG */
- +/* #define DUMP_MSGS */
- +
- +
- +#include <linux/blkdev.h>
- +#include <linux/completion.h>
- +#include <linux/dcache.h>
- +#include <linux/delay.h>
- +#include <linux/device.h>
- +#include <linux/fcntl.h>
- +#include <linux/file.h>
- +#include <linux/fs.h>
- +#include <linux/kref.h>
- +#include <linux/kthread.h>
- +#include <linux/limits.h>
- +#include <linux/module.h>
- +#include <linux/rwsem.h>
- +#include <linux/slab.h>
- +#include <linux/spinlock.h>
- +#include <linux/string.h>
- +#include <linux/freezer.h>
- +#include <linux/utsname.h>
- +
- +#include <linux/usb/ch9.h>
- +#include <linux/usb/gadget.h>
- +
- +#include "gadget_chips.h"
- +
- +
- +
- +/*
- + * Kbuild is not very cooperative with respect to linking separately
- + * compiled library objects into one module. So for now we won't use
- + * separate compilation ... ensuring init/exit sections work to shrink
- + * the runtime footprint, and giving us at least some parts of what
- + * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
- + */
- +#include "usbstring.c"
- +#include "config.c"
- +#include "epautoconf.c"
- +
- +/*-------------------------------------------------------------------------*/
- +
- +#define DRIVER_DESC "File-backed Storage Gadget"
- +#define DRIVER_NAME "g_file_storage"
- +#define DRIVER_VERSION "1 September 2010"
- +
- +static char fsg_string_manufacturer[64];
- +static const char fsg_string_product[] = DRIVER_DESC;
- +static const char fsg_string_config[] = "Self-powered";
- +static const char fsg_string_interface[] = "Mass Storage";
- +
- +
- +#include "storage_common.c"
- +
- +
- +MODULE_DESCRIPTION(DRIVER_DESC);
- +MODULE_AUTHOR("Alan Stern");
- +MODULE_LICENSE("Dual BSD/GPL");
- +
- +/*
- + * This driver assumes self-powered hardware and has no way for users to
- + * trigger remote wakeup. It uses autoconfiguration to select endpoints
- + * and endpoint addresses.
- + */
- +
- +
- +/*-------------------------------------------------------------------------*/
- +
- +
- +/* Encapsulate the module parameter settings */
- +
- +static struct {
- + char *file[FSG_MAX_LUNS];
- + char *serial;
- + bool ro[FSG_MAX_LUNS];
- + bool nofua[FSG_MAX_LUNS];
- + unsigned int num_filenames;
- + unsigned int num_ros;
- + unsigned int num_nofuas;
- + unsigned int nluns;
- +
- + bool removable;
- + bool can_stall;
- + bool cdrom;
- +
- + char *transport_parm;
- + char *protocol_parm;
- + unsigned short vendor;
- + unsigned short product;
- + unsigned short release;
- + unsigned int buflen;
- +
- + int transport_type;
- + char *transport_name;
- + int protocol_type;
- + char *protocol_name;
- +
- +} mod_data = { // Default values
- + .transport_parm = "BBB",
- + .protocol_parm = "SCSI",
- + .removable = 0,
- + .can_stall = 1,
- + .cdrom = 0,
- + .vendor = FSG_VENDOR_ID,
- + .product = FSG_PRODUCT_ID,
- + .release = 0xffff, // Use controller chip type
- + .buflen = 16384,
- + };
- +
- +
- +module_param_array_named(file, mod_data.file, charp, &mod_data.num_filenames,
- + S_IRUGO);
- +MODULE_PARM_DESC(file, "names of backing files or devices");
- +
- +module_param_named(serial, mod_data.serial, charp, S_IRUGO);
- +MODULE_PARM_DESC(serial, "USB serial number");
- +
- +module_param_array_named(ro, mod_data.ro, bool, &mod_data.num_ros, S_IRUGO);
- +MODULE_PARM_DESC(ro, "true to force read-only");
- +
- +module_param_array_named(nofua, mod_data.nofua, bool, &mod_data.num_nofuas,
- + S_IRUGO);
- +MODULE_PARM_DESC(nofua, "true to ignore SCSI WRITE(10,12) FUA bit");
- +
- +module_param_named(luns, mod_data.nluns, uint, S_IRUGO);
- +MODULE_PARM_DESC(luns, "number of LUNs");
- +
- +module_param_named(removable, mod_data.removable, bool, S_IRUGO);
- +MODULE_PARM_DESC(removable, "true to simulate removable media");
- +
- +module_param_named(stall, mod_data.can_stall, bool, S_IRUGO);
- +MODULE_PARM_DESC(stall, "false to prevent bulk stalls");
- +
- +module_param_named(cdrom, mod_data.cdrom, bool, S_IRUGO);
- +MODULE_PARM_DESC(cdrom, "true to emulate cdrom instead of disk");
- +
- +/* In the non-TEST version, only the module parameters listed above
- + * are available. */
- +#ifdef CONFIG_USB_FILE_STORAGE_TEST
- +
- +module_param_named(transport, mod_data.transport_parm, charp, S_IRUGO);
- +MODULE_PARM_DESC(transport, "type of transport (BBB, CBI, or CB)");
- +
- +module_param_named(protocol, mod_data.protocol_parm, charp, S_IRUGO);
- +MODULE_PARM_DESC(protocol, "type of protocol (RBC, 8020, QIC, UFI, "
- + "8070, or SCSI)");
- +
- +module_param_named(vendor, mod_data.vendor, ushort, S_IRUGO);
- +MODULE_PARM_DESC(vendor, "USB Vendor ID");
- +
- +module_param_named(product, mod_data.product, ushort, S_IRUGO);
- +MODULE_PARM_DESC(product, "USB Product ID");
- +
- +module_param_named(release, mod_data.release, ushort, S_IRUGO);
- +MODULE_PARM_DESC(release, "USB release number");
- +
- +module_param_named(buflen, mod_data.buflen, uint, S_IRUGO);
- +MODULE_PARM_DESC(buflen, "I/O buffer size");
- +
- +#endif /* CONFIG_USB_FILE_STORAGE_TEST */
- +
- +
- +/*
- + * These definitions will permit the compiler to avoid generating code for
- + * parts of the driver that aren't used in the non-TEST version. Even gcc
- + * can recognize when a test of a constant expression yields a dead code
- + * path.
- + */
- +
- +#ifdef CONFIG_USB_FILE_STORAGE_TEST
- +
- +#define transport_is_bbb() (mod_data.transport_type == USB_PR_BULK)
- +#define transport_is_cbi() (mod_data.transport_type == USB_PR_CBI)
- +#define protocol_is_scsi() (mod_data.protocol_type == USB_SC_SCSI)
- +
- +#else
- +
- +#define transport_is_bbb() 1
- +#define transport_is_cbi() 0
- +#define protocol_is_scsi() 1
- +
- +#endif /* CONFIG_USB_FILE_STORAGE_TEST */
- +
- +
- +/*-------------------------------------------------------------------------*/
- +
- +
- +struct fsg_dev {
- + /* lock protects: state, all the req_busy's, and cbbuf_cmnd */
- + spinlock_t lock;
- + struct usb_gadget *gadget;
- +
- + /* filesem protects: backing files in use */
- + struct rw_semaphore filesem;
- +
- + /* reference counting: wait until all LUNs are released */
- + struct kref ref;
- +
- + struct usb_ep *ep0; // Handy copy of gadget->ep0
- + struct usb_request *ep0req; // For control responses
- + unsigned int ep0_req_tag;
- + const char *ep0req_name;
- +
- + struct usb_request *intreq; // For interrupt responses
- + int intreq_busy;
- + struct fsg_buffhd *intr_buffhd;
- +
- + unsigned int bulk_out_maxpacket;
- + enum fsg_state state; // For exception handling
- + unsigned int exception_req_tag;
- +
- + u8 config, new_config;
- +
- + unsigned int running : 1;
- + unsigned int bulk_in_enabled : 1;
- + unsigned int bulk_out_enabled : 1;
- + unsigned int intr_in_enabled : 1;
- + unsigned int phase_error : 1;
- + unsigned int short_packet_received : 1;
- + unsigned int bad_lun_okay : 1;
- +
- + unsigned long atomic_bitflags;
- +#define REGISTERED 0
- +#define IGNORE_BULK_OUT 1
- +#define SUSPENDED 2
- +
- + struct usb_ep *bulk_in;
- + struct usb_ep *bulk_out;
- + struct usb_ep *intr_in;
- +
- + struct fsg_buffhd *next_buffhd_to_fill;
- + struct fsg_buffhd *next_buffhd_to_drain;
- +
- + int thread_wakeup_needed;
- + struct completion thread_notifier;
- + struct task_struct *thread_task;
- +
- + int cmnd_size;
- + u8 cmnd[MAX_COMMAND_SIZE];
- + enum data_direction data_dir;
- + u32 data_size;
- + u32 data_size_from_cmnd;
- + u32 tag;
- + unsigned int lun;
- + u32 residue;
- + u32 usb_amount_left;
- +
- + /* The CB protocol offers no way for a host to know when a command
- + * has completed. As a result the next command may arrive early,
- + * and we will still have to handle it. For that reason we need
- + * a buffer to store new commands when using CB (or CBI, which
- + * does not oblige a host to wait for command completion either). */
- + int cbbuf_cmnd_size;
- + u8 cbbuf_cmnd[MAX_COMMAND_SIZE];
- +
- + unsigned int nluns;
- + struct fsg_lun *luns;
- + struct fsg_lun *curlun;
- + /* Must be the last entry */
- + struct fsg_buffhd buffhds[];
- +};
- +
- +typedef void (*fsg_routine_t)(struct fsg_dev *);
- +
- +static int exception_in_progress(struct fsg_dev *fsg)
- +{
- + return (fsg->state > FSG_STATE_IDLE);
- +}
- +
- +/* Make bulk-out requests be divisible by the maxpacket size */
- +static void set_bulk_out_req_length(struct fsg_dev *fsg,
- + struct fsg_buffhd *bh, unsigned int length)
- +{
- + unsigned int rem;
- +
- + bh->bulk_out_intended_length = length;
- + rem = length % fsg->bulk_out_maxpacket;
- + if (rem > 0)
- + length += fsg->bulk_out_maxpacket - rem;
- + bh->outreq->length = length;
- +}
- +
- +static struct fsg_dev *the_fsg;
- +static struct usb_gadget_driver fsg_driver;
- +
- +
- +/*-------------------------------------------------------------------------*/
- +
- +static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep)
- +{
- + const char *name;
- +
- + if (ep == fsg->bulk_in)
- + name = "bulk-in";
- + else if (ep == fsg->bulk_out)
- + name = "bulk-out";
- + else
- + name = ep->name;
- + DBG(fsg, "%s set halt\n", name);
- + return usb_ep_set_halt(ep);
- +}
- +
- +
- +/*-------------------------------------------------------------------------*/
- +
- +/*
- + * DESCRIPTORS ... most are static, but strings and (full) configuration
- + * descriptors are built on demand. Also the (static) config and interface
- + * descriptors are adjusted during fsg_bind().
- + */
- +
- +/* There is only one configuration. */
- +#define CONFIG_VALUE 1
- +
- +static struct usb_device_descriptor
- +device_desc = {
- + .bLength = sizeof device_desc,
- + .bDescriptorType = USB_DT_DEVICE,
- +
- + .bcdUSB = cpu_to_le16(0x0200),
- + .bDeviceClass = USB_CLASS_PER_INTERFACE,
- +
- + /* The next three values can be overridden by module parameters */
- + .idVendor = cpu_to_le16(FSG_VENDOR_ID),
- + .idProduct = cpu_to_le16(FSG_PRODUCT_ID),
- + .bcdDevice = cpu_to_le16(0xffff),
- +
- + .iManufacturer = FSG_STRING_MANUFACTURER,
- + .iProduct = FSG_STRING_PRODUCT,
- + .iSerialNumber = FSG_STRING_SERIAL,
- + .bNumConfigurations = 1,
- +};
- +
- +static struct usb_config_descriptor
- +config_desc = {
- + .bLength = sizeof config_desc,
- + .bDescriptorType = USB_DT_CONFIG,
- +
- + /* wTotalLength computed by usb_gadget_config_buf() */
- + .bNumInterfaces = 1,
- + .bConfigurationValue = CONFIG_VALUE,
- + .iConfiguration = FSG_STRING_CONFIG,
- + .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
- + .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2,
- +};
- +
- +
- +static struct usb_qualifier_descriptor
- +dev_qualifier = {
- + .bLength = sizeof dev_qualifier,
- + .bDescriptorType = USB_DT_DEVICE_QUALIFIER,
- +
- + .bcdUSB = cpu_to_le16(0x0200),
- + .bDeviceClass = USB_CLASS_PER_INTERFACE,
- +
- + .bNumConfigurations = 1,
- +};
- +
- +static int populate_bos(struct fsg_dev *fsg, u8 *buf)
- +{
- + memcpy(buf, &fsg_bos_desc, USB_DT_BOS_SIZE);
- + buf += USB_DT_BOS_SIZE;
- +
- + memcpy(buf, &fsg_ext_cap_desc, USB_DT_USB_EXT_CAP_SIZE);
- + buf += USB_DT_USB_EXT_CAP_SIZE;
- +
- + memcpy(buf, &fsg_ss_cap_desc, USB_DT_USB_SS_CAP_SIZE);
- +
- + return USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE
- + + USB_DT_USB_EXT_CAP_SIZE;
- +}
- +
- +/*
- + * Config descriptors must agree with the code that sets configurations
- + * and with code managing interfaces and their altsettings. They must
- + * also handle different speeds and other-speed requests.
- + */
- +static int populate_config_buf(struct usb_gadget *gadget,
- + u8 *buf, u8 type, unsigned index)
- +{
- + enum usb_device_speed speed = gadget->speed;
- + int len;
- + const struct usb_descriptor_header **function;
- +
- + if (index > 0)
- + return -EINVAL;
- +
- + if (gadget_is_dualspeed(gadget) && type == USB_DT_OTHER_SPEED_CONFIG)
- + speed = (USB_SPEED_FULL + USB_SPEED_HIGH) - speed;
- + function = gadget_is_dualspeed(gadget) && speed == USB_SPEED_HIGH
- + ? (const struct usb_descriptor_header **)fsg_hs_function
- + : (const struct usb_descriptor_header **)fsg_fs_function;
- +
- + /* for now, don't advertise srp-only devices */
- + if (!gadget_is_otg(gadget))
- + function++;
- +
- + len = usb_gadget_config_buf(&config_desc, buf, EP0_BUFSIZE, function);
- + ((struct usb_config_descriptor *) buf)->bDescriptorType = type;
- + return len;
- +}
- +
- +
- +/*-------------------------------------------------------------------------*/
- +
- +/* These routines may be called in process context or in_irq */
- +
- +/* Caller must hold fsg->lock */
- +static void wakeup_thread(struct fsg_dev *fsg)
- +{
- + /* Tell the main thread that something has happened */
- + fsg->thread_wakeup_needed = 1;
- + if (fsg->thread_task)
- + wake_up_process(fsg->thread_task);
- +}
- +
- +
- +static void raise_exception(struct fsg_dev *fsg, enum fsg_state new_state)
- +{
- + unsigned long flags;
- +
- + /* Do nothing if a higher-priority exception is already in progress.
- + * If a lower-or-equal priority exception is in progress, preempt it
- + * and notify the main thread by sending it a signal. */
- + spin_lock_irqsave(&fsg->lock, flags);
- + if (fsg->state <= new_state) {
- + fsg->exception_req_tag = fsg->ep0_req_tag;
- + fsg->state = new_state;
- + if (fsg->thread_task)
- + send_sig_info(SIGUSR1, SEND_SIG_FORCED,
- + fsg->thread_task);
- + }
- + spin_unlock_irqrestore(&fsg->lock, flags);
- +}
- +
- +
- +/*-------------------------------------------------------------------------*/
- +
- +/* The disconnect callback and ep0 routines. These always run in_irq,
- + * except that ep0_queue() is called in the main thread to acknowledge
- + * completion of various requests: set config, set interface, and
- + * Bulk-only device reset. */
- +
- +static void fsg_disconnect(struct usb_gadget *gadget)
- +{
- + struct fsg_dev *fsg = get_gadget_data(gadget);
- +
- + DBG(fsg, "disconnect or port reset\n");
- + raise_exception(fsg, FSG_STATE_DISCONNECT);
- +}
- +
- +
- +static int ep0_queue(struct fsg_dev *fsg)
- +{
- + int rc;
- +
- + rc = usb_ep_queue(fsg->ep0, fsg->ep0req, GFP_ATOMIC);
- + if (rc != 0 && rc != -ESHUTDOWN) {
- +
- + /* We can't do much more than wait for a reset */
- + WARNING(fsg, "error in submission: %s --> %d\n",
- + fsg->ep0->name, rc);
- + }
- + return rc;
- +}
- +
- +static void ep0_complete(struct usb_ep *ep, struct usb_request *req)
- +{
- + struct fsg_dev *fsg = ep->driver_data;
- +
- + if (req->actual > 0)
- + dump_msg(fsg, fsg->ep0req_name, req->buf, req->actual);
- + if (req->status || req->actual != req->length)
- + DBG(fsg, "%s --> %d, %u/%u\n", __func__,
- + req->status, req->actual, req->length);
- + if (req->status == -ECONNRESET) // Request was cancelled
- + usb_ep_fifo_flush(ep);
- +
- + if (req->status == 0 && req->context)
- + ((fsg_routine_t) (req->context))(fsg);
- +}
- +
- +
- +/*-------------------------------------------------------------------------*/
- +
- +/* Bulk and interrupt endpoint completion handlers.
- + * These always run in_irq. */
- +
- +static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req)
- +{
- + struct fsg_dev *fsg = ep->driver_data;
- + struct fsg_buffhd *bh = req->context;
- +
- + if (req->status || req->actual != req->length)
- + DBG(fsg, "%s --> %d, %u/%u\n", __func__,
- + req->status, req->actual, req->length);
- + if (req->status == -ECONNRESET) // Request was cancelled
- + usb_ep_fifo_flush(ep);
- +
- + /* Hold the lock while we update the request and buffer states */
- + smp_wmb();
- + spin_lock(&fsg->lock);
- + bh->inreq_busy = 0;
- + bh->state = BUF_STATE_EMPTY;
- + wakeup_thread(fsg);
- + spin_unlock(&fsg->lock);
- +}
- +
- +static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req)
- +{
- + struct fsg_dev *fsg = ep->driver_data;
- + struct fsg_buffhd *bh = req->context;
- +
- + dump_msg(fsg, "bulk-out", req->buf, req->actual);
- + if (req->status || req->actual != bh->bulk_out_intended_length)
- + DBG(fsg, "%s --> %d, %u/%u\n", __func__,
- + req->status, req->actual,
- + bh->bulk_out_intended_length);
- + if (req->status == -ECONNRESET) // Request was cancelled
- + usb_ep_fifo_flush(ep);
- +
- + /* Hold the lock while we update the request and buffer states */
- + smp_wmb();
- + spin_lock(&fsg->lock);
- + bh->outreq_busy = 0;
- + bh->state = BUF_STATE_FULL;
- + wakeup_thread(fsg);
- + spin_unlock(&fsg->lock);
- +}
- +
- +
- +#ifdef CONFIG_USB_FILE_STORAGE_TEST
- +static void intr_in_complete(struct usb_ep *ep, struct usb_request *req)
- +{
- + struct fsg_dev *fsg = ep->driver_data;
- + struct fsg_buffhd *bh = req->context;
- +
- + if (req->status || req->actual != req->length)
- + DBG(fsg, "%s --> %d, %u/%u\n", __func__,
- + req->status, req->actual, req->length);
- + if (req->status == -ECONNRESET) // Request was cancelled
- + usb_ep_fifo_flush(ep);
- +
- + /* Hold the lock while we update the request and buffer states */
- + smp_wmb();
- + spin_lock(&fsg->lock);
- + fsg->intreq_busy = 0;
- + bh->state = BUF_STATE_EMPTY;
- + wakeup_thread(fsg);
- + spin_unlock(&fsg->lock);
- +}
- +
- +#else
- +static void intr_in_complete(struct usb_ep *ep, struct usb_request *req)
- +{}
- +#endif /* CONFIG_USB_FILE_STORAGE_TEST */
- +
- +
- +/*-------------------------------------------------------------------------*/
- +
- +/* Ep0 class-specific handlers. These always run in_irq. */
- +
- +#ifdef CONFIG_USB_FILE_STORAGE_TEST
- +static void received_cbi_adsc(struct fsg_dev *fsg, struct fsg_buffhd *bh)
- +{
- + struct usb_request *req = fsg->ep0req;
- + static u8 cbi_reset_cmnd[6] = {
- + SEND_DIAGNOSTIC, 4, 0xff, 0xff, 0xff, 0xff};
- +
- + /* Error in command transfer? */
- + if (req->status || req->length != req->actual ||
- + req->actual < 6 || req->actual > MAX_COMMAND_SIZE) {
- +
- + /* Not all controllers allow a protocol stall after
- + * receiving control-out data, but we'll try anyway. */
- + fsg_set_halt(fsg, fsg->ep0);
- + return; // Wait for reset
- + }
- +
- + /* Is it the special reset command? */
- + if (req->actual >= sizeof cbi_reset_cmnd &&
- + memcmp(req->buf, cbi_reset_cmnd,
- + sizeof cbi_reset_cmnd) == 0) {
- +
- + /* Raise an exception to stop the current operation
- + * and reinitialize our state. */
- + DBG(fsg, "cbi reset request\n");
- + raise_exception(fsg, FSG_STATE_RESET);
- + return;
- + }
- +
- + VDBG(fsg, "CB[I] accept device-specific command\n");
- + spin_lock(&fsg->lock);
- +
- + /* Save the command for later */
- + if (fsg->cbbuf_cmnd_size)
- + WARNING(fsg, "CB[I] overwriting previous command\n");
- + fsg->cbbuf_cmnd_size = req->actual;
- + memcpy(fsg->cbbuf_cmnd, req->buf, fsg->cbbuf_cmnd_size);
- +
- + wakeup_thread(fsg);
- + spin_unlock(&fsg->lock);
- +}
- +
- +#else
- +static void received_cbi_adsc(struct fsg_dev *fsg, struct fsg_buffhd *bh)
- +{}
- +#endif /* CONFIG_USB_FILE_STORAGE_TEST */
- +
- +
- +static int class_setup_req(struct fsg_dev *fsg,
- + const struct usb_ctrlrequest *ctrl)
- +{
- + struct usb_request *req = fsg->ep0req;
- + int value = -EOPNOTSUPP;
- + u16 w_index = le16_to_cpu(ctrl->wIndex);
- + u16 w_value = le16_to_cpu(ctrl->wValue);
- + u16 w_length = le16_to_cpu(ctrl->wLength);
- +
- + if (!fsg->config)
- + return value;
- +
- + /* Handle Bulk-only class-specific requests */
- + if (transport_is_bbb()) {
- + switch (ctrl->bRequest) {
- +
- + case US_BULK_RESET_REQUEST:
- + if (ctrl->bRequestType != (USB_DIR_OUT |
- + USB_TYPE_CLASS | USB_RECIP_INTERFACE))
- + break;
- + if (w_index != 0 || w_value != 0 || w_length != 0) {
- + value = -EDOM;
- + break;
- + }
- +
- + /* Raise an exception to stop the current operation
- + * and reinitialize our state. */
- + DBG(fsg, "bulk reset request\n");
- + raise_exception(fsg, FSG_STATE_RESET);
- + value = DELAYED_STATUS;
- + break;
- +
- + case US_BULK_GET_MAX_LUN:
- + if (ctrl->bRequestType != (USB_DIR_IN |
- + USB_TYPE_CLASS | USB_RECIP_INTERFACE))
- + break;
- + if (w_index != 0 || w_value != 0 || w_length != 1) {
- + value = -EDOM;
- + break;
- + }
- + VDBG(fsg, "get max LUN\n");
- + *(u8 *) req->buf = fsg->nluns - 1;
- + value = 1;
- + break;
- + }
- + }
- +
- + /* Handle CBI class-specific requests */
- + else {
- + switch (ctrl->bRequest) {
- +
- + case USB_CBI_ADSC_REQUEST:
- + if (ctrl->bRequestType != (USB_DIR_OUT |
- + USB_TYPE_CLASS | USB_RECIP_INTERFACE))
- + break;
- + if (w_index != 0 || w_value != 0) {
- + value = -EDOM;
- + break;
- + }
- + if (w_length > MAX_COMMAND_SIZE) {
- + value = -EOVERFLOW;
- + break;
- + }
- + value = w_length;
- + fsg->ep0req->context = received_cbi_adsc;
- + break;
- + }
- + }
- +
- + if (value == -EOPNOTSUPP)
- + VDBG(fsg,
- + "unknown class-specific control req "
- + "%02x.%02x v%04x i%04x l%u\n",
- + ctrl->bRequestType, ctrl->bRequest,
- + le16_to_cpu(ctrl->wValue), w_index, w_length);
- + return value;
- +}
- +
- +
- +/*-------------------------------------------------------------------------*/
- +
- +/* Ep0 standard request handlers. These always run in_irq. */
- +
- +static int standard_setup_req(struct fsg_dev *fsg,
- + const struct usb_ctrlrequest *ctrl)
- +{
- + struct usb_request *req = fsg->ep0req;
- + int value = -EOPNOTSUPP;
- + u16 w_index = le16_to_cpu(ctrl->wIndex);
- + u16 w_value = le16_to_cpu(ctrl->wValue);
- +
- + /* Usually this just stores reply data in the pre-allocated ep0 buffer,
- + * but config change events will also reconfigure hardware. */
- + switch (ctrl->bRequest) {
- +
- + case USB_REQ_GET_DESCRIPTOR:
- + if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD |
- + USB_RECIP_DEVICE))
- + break;
- + switch (w_value >> 8) {
- +
- + case USB_DT_DEVICE:
- + VDBG(fsg, "get device descriptor\n");
- + device_desc.bMaxPacketSize0 = fsg->ep0->maxpacket;
- + value = sizeof device_desc;
- + memcpy(req->buf, &device_desc, value);
- + break;
- + case USB_DT_DEVICE_QUALIFIER:
- + VDBG(fsg, "get device qualifier\n");
- + if (!gadget_is_dualspeed(fsg->gadget) ||
- + fsg->gadget->speed == USB_SPEED_SUPER)
- + break;
- + /*
- + * Assume ep0 uses the same maxpacket value for both
- + * speeds
- + */
- + dev_qualifier.bMaxPacketSize0 = fsg->ep0->maxpacket;
- + value = sizeof dev_qualifier;
- + memcpy(req->buf, &dev_qualifier, value);
- + break;
- +
- + case USB_DT_OTHER_SPEED_CONFIG:
- + VDBG(fsg, "get other-speed config descriptor\n");
- + if (!gadget_is_dualspeed(fsg->gadget) ||
- + fsg->gadget->speed == USB_SPEED_SUPER)
- + break;
- + goto get_config;
- + case USB_DT_CONFIG:
- + VDBG(fsg, "get configuration descriptor\n");
- +get_config:
- + value = populate_config_buf(fsg->gadget,
- + req->buf,
- + w_value >> 8,
- + w_value & 0xff);
- + break;
- +
- + case USB_DT_STRING:
- + VDBG(fsg, "get string descriptor\n");
- +
- + /* wIndex == language code */
- + value = usb_gadget_get_string(&fsg_stringtab,
- + w_value & 0xff, req->buf);
- + break;
- +
- + case USB_DT_BOS:
- + VDBG(fsg, "get bos descriptor\n");
- +
- + if (gadget_is_superspeed(fsg->gadget))
- + value = populate_bos(fsg, req->buf);
- + break;
- + }
- +
- + break;
- +
- + /* One config, two speeds */
- + case USB_REQ_SET_CONFIGURATION:
- + if (ctrl->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD |
- + USB_RECIP_DEVICE))
- + break;
- + VDBG(fsg, "set configuration\n");
- + if (w_value == CONFIG_VALUE || w_value == 0) {
- + fsg->new_config = w_value;
- +
- + /* Raise an exception to wipe out previous transaction
- + * state (queued bufs, etc) and set the new config. */
- + raise_exception(fsg, FSG_STATE_CONFIG_CHANGE);
- + value = DELAYED_STATUS;
- + }
- + break;
- + case USB_REQ_GET_CONFIGURATION:
- + if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD |
- + USB_RECIP_DEVICE))
- + break;
- + VDBG(fsg, "get configuration\n");
- + *(u8 *) req->buf = fsg->config;
- + value = 1;
- + break;
- +
- + case USB_REQ_SET_INTERFACE:
- + if (ctrl->bRequestType != (USB_DIR_OUT| USB_TYPE_STANDARD |
- + USB_RECIP_INTERFACE))
- + break;
- + if (fsg->config && w_index == 0) {
- +
- + /* Raise an exception to wipe out previous transaction
- + * state (queued bufs, etc) and install the new
- + * interface altsetting. */
- + raise_exception(fsg, FSG_STATE_INTERFACE_CHANGE);
- + value = DELAYED_STATUS;
- + }
- + break;
- + case USB_REQ_GET_INTERFACE:
- + if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD |
- + USB_RECIP_INTERFACE))
- + break;
- + if (!fsg->config)
- + break;
- + if (w_index != 0) {
- + value = -EDOM;
- + break;
- + }
- + VDBG(fsg, "get interface\n");
- + *(u8 *) req->buf = 0;
- + value = 1;
- + break;
- +
- + default:
- + VDBG(fsg,
- + "unknown control req %02x.%02x v%04x i%04x l%u\n",
- + ctrl->bRequestType, ctrl->bRequest,
- + w_value, w_index, le16_to_cpu(ctrl->wLength));
- + }
- +
- + return value;
- +}
- +
- +
- +static int fsg_setup(struct usb_gadget *gadget,
- + const struct usb_ctrlrequest *ctrl)
- +{
- + struct fsg_dev *fsg = get_gadget_data(gadget);
- + int rc;
- + int w_length = le16_to_cpu(ctrl->wLength);
- +
- + ++fsg->ep0_req_tag; // Record arrival of a new request
- + fsg->ep0req->context = NULL;
- + fsg->ep0req->length = 0;
- + dump_msg(fsg, "ep0-setup", (u8 *) ctrl, sizeof(*ctrl));
- +
- + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS)
- + rc = class_setup_req(fsg, ctrl);
- + else
- + rc = standard_setup_req(fsg, ctrl);
- +
- + /* Respond with data/status or defer until later? */
- + if (rc >= 0 && rc != DELAYED_STATUS) {
- + rc = min(rc, w_length);
- + fsg->ep0req->length = rc;
- + fsg->ep0req->zero = rc < w_length;
- + fsg->ep0req_name = (ctrl->bRequestType & USB_DIR_IN ?
- + "ep0-in" : "ep0-out");
- + rc = ep0_queue(fsg);
- + }
- +
- + /* Device either stalls (rc < 0) or reports success */
- + return rc;
- +}
- +
- +
- +/*-------------------------------------------------------------------------*/
- +
- +/* All the following routines run in process context */
- +
- +
- +/* Use this for bulk or interrupt transfers, not ep0 */
- +static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep,
- + struct usb_request *req, int *pbusy,
- + enum fsg_buffer_state *state)
- +{
- + int rc;
- +
- + if (ep == fsg->bulk_in)
- + dump_msg(fsg, "bulk-in", req->buf, req->length);
- + else if (ep == fsg->intr_in)
- + dump_msg(fsg, "intr-in", req->buf, req->length);
- +
- + spin_lock_irq(&fsg->lock);
- + *pbusy = 1;
- + *state = BUF_STATE_BUSY;
- + spin_unlock_irq(&fsg->lock);
- + rc = usb_ep_queue(ep, req, GFP_KERNEL);
- + if (rc != 0) {
- + *pbusy = 0;
- + *state = BUF_STATE_EMPTY;
- +
- + /* We can't do much more than wait for a reset */
- +
- + /* Note: currently the net2280 driver fails zero-length
- + * submissions if DMA is enabled. */
- + if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP &&
- + req->length == 0))
- + WARNING(fsg, "error in submission: %s --> %d\n",
- + ep->name, rc);
- + }
- +}
- +
- +
- +static int sleep_thread(struct fsg_dev *fsg)
- +{
- + int rc = 0;
- +
- + /* Wait until a signal arrives or we are woken up */
- + for (;;) {
- + try_to_freeze();
- + set_current_state(TASK_INTERRUPTIBLE);
- + if (signal_pending(current)) {
- + rc = -EINTR;
- + break;
- + }
- + if (fsg->thread_wakeup_needed)
- + break;
- + schedule();
- + }
- + __set_current_state(TASK_RUNNING);
- + fsg->thread_wakeup_needed = 0;
- + return rc;
- +}
- +
- +
- +/*-------------------------------------------------------------------------*/
- +
- +static int do_read(struct fsg_dev *fsg)
- +{
- + struct fsg_lun *curlun = fsg->curlun;
- + u32 lba;
- + struct fsg_buffhd *bh;
- + int rc;
- + u32 amount_left;
- + loff_t file_offset, file_offset_tmp;
- + unsigned int amount;
- + ssize_t nread;
- +
- + /* Get the starting Logical Block Address and check that it's
- + * not too big */
- + if (fsg->cmnd[0] == READ_6)
- + lba = get_unaligned_be24(&fsg->cmnd[1]);
- + else {
- + lba = get_unaligned_be32(&fsg->cmnd[2]);
- +
- + /* We allow DPO (Disable Page Out = don't save data in the
- + * cache) and FUA (Force Unit Access = don't read from the
- + * cache), but we don't implement them. */
- + if ((fsg->cmnd[1] & ~0x18) != 0) {
- + curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
- + return -EINVAL;
- + }
- + }
- + if (lba >= curlun->num_sectors) {
- + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
- + return -EINVAL;
- + }
- + file_offset = ((loff_t) lba) << curlun->blkbits;
- +
- + /* Carry out the file reads */
- + amount_left = fsg->data_size_from_cmnd;
- + if (unlikely(amount_left == 0))
- + return -EIO; // No default reply
- +
- + for (;;) {
- +
- + /* Figure out how much we need to read:
- + * Try to read the remaining amount.
- + * But don't read more than the buffer size.
- + * And don't try to read past the end of the file.
- + */
- + amount = min((unsigned int) amount_left, mod_data.buflen);
- + amount = min((loff_t) amount,
- + curlun->file_length - file_offset);
- +
- + /* Wait for the next buffer to become available */
- + bh = fsg->next_buffhd_to_fill;
- + while (bh->state != BUF_STATE_EMPTY) {
- + rc = sleep_thread(fsg);
- + if (rc)
- + return rc;
- + }
- +
- + /* If we were asked to read past the end of file,
- + * end with an empty buffer. */
- + if (amount == 0) {
- + curlun->sense_data =
- + SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
- + curlun->sense_data_info = file_offset >> curlun->blkbits;
- + curlun->info_valid = 1;
- + bh->inreq->length = 0;
- + bh->state = BUF_STATE_FULL;
- + break;
- + }
- +
- + /* Perform the read */
- + file_offset_tmp = file_offset;
- + nread = vfs_read(curlun->filp,
- + (char __user *) bh->buf,
- + amount, &file_offset_tmp);
- + VLDBG(curlun, "file read %u @ %llu -> %d\n", amount,
- + (unsigned long long) file_offset,
- + (int) nread);
- + if (signal_pending(current))
- + return -EINTR;
- +
- + if (nread < 0) {
- + LDBG(curlun, "error in file read: %d\n",
- + (int) nread);
- + nread = 0;
- + } else if (nread < amount) {
- + LDBG(curlun, "partial file read: %d/%u\n",
- + (int) nread, amount);
- + nread = round_down(nread, curlun->blksize);
- + }
- + file_offset += nread;
- + amount_left -= nread;
- + fsg->residue -= nread;
- +
- + /* Except at the end of the transfer, nread will be
- + * equal to the buffer size, which is divisible by the
- + * bulk-in maxpacket size.
- + */
- + bh->inreq->length = nread;
- + bh->state = BUF_STATE_FULL;
- +
- + /* If an error occurred, report it and its position */
- + if (nread < amount) {
- + curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
- + curlun->sense_data_info = file_offset >> curlun->blkbits;
- + curlun->info_valid = 1;
- + break;
- + }
- +
- + if (amount_left == 0)
- + break; // No more left to read
- +
- + /* Send this buffer and go read some more */
- + bh->inreq->zero = 0;
- + start_transfer(fsg, fsg->bulk_in, bh->inreq,
- + &bh->inreq_busy, &bh->state);
- + fsg->next_buffhd_to_fill = bh->next;
- + }
- +
- + return -EIO; // No default reply
- +}
- +
- +
- +/*-------------------------------------------------------------------------*/
- +
- +static int do_write(struct fsg_dev *fsg)
- +{
- + struct fsg_lun *curlun = fsg->curlun;
- + u32 lba;
- + struct fsg_buffhd *bh;
- + int get_some_more;
- + u32 amount_left_to_req, amount_left_to_write;
- + loff_t usb_offset, file_offset, file_offset_tmp;
- + unsigned int amount;
- + ssize_t nwritten;
- + int rc;
- +
- + if (curlun->ro) {
- + curlun->sense_data = SS_WRITE_PROTECTED;
- + return -EINVAL;
- + }
- + spin_lock(&curlun->filp->f_lock);
- + curlun->filp->f_flags &= ~O_SYNC; // Default is not to wait
- + spin_unlock(&curlun->filp->f_lock);
- +
- + /* Get the starting Logical Block Address and check that it's
- + * not too big */
- + if (fsg->cmnd[0] == WRITE_6)
- + lba = get_unaligned_be24(&fsg->cmnd[1]);
- + else {
- + lba = get_unaligned_be32(&fsg->cmnd[2]);
- +
- + /* We allow DPO (Disable Page Out = don't save data in the
- + * cache) and FUA (Force Unit Access = write directly to the
- + * medium). We don't implement DPO; we implement FUA by
- + * performing synchronous output. */
- + if ((fsg->cmnd[1] & ~0x18) != 0) {
- + curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
- + return -EINVAL;
- + }
- + /* FUA */
- + if (!curlun->nofua && (fsg->cmnd[1] & 0x08)) {
- + spin_lock(&curlun->filp->f_lock);
- + curlun->filp->f_flags |= O_DSYNC;
- + spin_unlock(&curlun->filp->f_lock);
- + }
- + }
- + if (lba >= curlun->num_sectors) {
- + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
- + return -EINVAL;
- + }
- +
- + /* Carry out the file writes */
- + get_some_more = 1;
- + file_offset = usb_offset = ((loff_t) lba) << curlun->blkbits;
- + amount_left_to_req = amount_left_to_write = fsg->data_size_from_cmnd;
- +
- + while (amount_left_to_write > 0) {
- +
- + /* Queue a request for more data from the host */
- + bh = fsg->next_buffhd_to_fill;
- + if (bh->state == BUF_STATE_EMPTY && get_some_more) {
- +
- + /* Figure out how much we want to get:
- + * Try to get the remaining amount,
- + * but not more than the buffer size.
- + */
- + amount = min(amount_left_to_req, mod_data.buflen);
- +
- + /* Beyond the end of the backing file? */
- + if (usb_offset >= curlun->file_length) {
- + get_some_more = 0;
- + curlun->sense_data =
- + SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
- + curlun->sense_data_info = usb_offset >> curlun->blkbits;
- + curlun->info_valid = 1;
- + continue;
- + }
- +
- + /* Get the next buffer */
- + usb_offset += amount;
- + fsg->usb_amount_left -= amount;
- + amount_left_to_req -= amount;
- + if (amount_left_to_req == 0)
- + get_some_more = 0;
- +
- + /* Except at the end of the transfer, amount will be
- + * equal to the buffer size, which is divisible by
- + * the bulk-out maxpacket size.
- + */
- + set_bulk_out_req_length(fsg, bh, amount);
- + start_transfer(fsg, fsg->bulk_out, bh->outreq,
- + &bh->outreq_busy, &bh->state);
- + fsg->next_buffhd_to_fill = bh->next;
- + continue;
- + }
- +
- + /* Write the received data to the backing file */
- + bh = fsg->next_buffhd_to_drain;
- + if (bh->state == BUF_STATE_EMPTY && !get_some_more)
- + break; // We stopped early
- + if (bh->state == BUF_STATE_FULL) {
- + smp_rmb();
- + fsg->next_buffhd_to_drain = bh->next;
- + bh->state = BUF_STATE_EMPTY;
- +
- + /* Did something go wrong with the transfer? */
- + if (bh->outreq->status != 0) {
- + curlun->sense_data = SS_COMMUNICATION_FAILURE;
- + curlun->sense_data_info = file_offset >> curlun->blkbits;
- + curlun->info_valid = 1;
- + break;
- + }
- +
- + amount = bh->outreq->actual;
- + if (curlun->file_length - file_offset < amount) {
- + LERROR(curlun,
- + "write %u @ %llu beyond end %llu\n",
- + amount, (unsigned long long) file_offset,
- + (unsigned long long) curlun->file_length);
- + amount = curlun->file_length - file_offset;
- + }
- +
- + /* Don't accept excess data. The spec doesn't say
- + * what to do in this case. We'll ignore the error.
- + */
- + amount = min(amount, bh->bulk_out_intended_length);
- +
- + /* Don't write a partial block */
- + amount = round_down(amount, curlun->blksize);
- + if (amount == 0)
- + goto empty_write;
- +
- + /* Perform the write */
- + file_offset_tmp = file_offset;
- + nwritten = vfs_write(curlun->filp,
- + (char __user *) bh->buf,
- + amount, &file_offset_tmp);
- + VLDBG(curlun, "file write %u @ %llu -> %d\n", amount,
- + (unsigned long long) file_offset,
- + (int) nwritten);
- + if (signal_pending(current))
- + return -EINTR; // Interrupted!
- +
- + if (nwritten < 0) {
- + LDBG(curlun, "error in file write: %d\n",
- + (int) nwritten);
- + nwritten = 0;
- + } else if (nwritten < amount) {
- + LDBG(curlun, "partial file write: %d/%u\n",
- + (int) nwritten, amount);
- + nwritten = round_down(nwritten, curlun->blksize);
- + }
- + file_offset += nwritten;
- + amount_left_to_write -= nwritten;
- + fsg->residue -= nwritten;
- +
- + /* If an error occurred, report it and its position */
- + if (nwritten < amount) {
- + curlun->sense_data = SS_WRITE_ERROR;
- + curlun->sense_data_info = file_offset >> curlun->blkbits;
- + curlun->info_valid = 1;
- + break;
- + }
- +
- + empty_write:
- + /* Did the host decide to stop early? */
- + if (bh->outreq->actual < bh->bulk_out_intended_length) {
- + fsg->short_packet_received = 1;
- + break;
- + }
- + continue;
- + }
- +
- + /* Wait for something to happen */
- + rc = sleep_thread(fsg);
- + if (rc)
- + return rc;
- + }
- +
- + return -EIO; // No default reply
- +}
- +
- +
- +/*-------------------------------------------------------------------------*/
- +
- +static int do_synchronize_cache(struct fsg_dev *fsg)
- +{
- + struct fsg_lun *curlun = fsg->curlun;
- + int rc;
- +
- + /* We ignore the requested LBA and write out all file's
- + * dirty data buffers. */
- + rc = fsg_lun_fsync_sub(curlun);
- + if (rc)
- + curlun->sense_data = SS_WRITE_ERROR;
- + return 0;
- +}
- +
- +
- +/*-------------------------------------------------------------------------*/
- +
- +static void invalidate_sub(struct fsg_lun *curlun)
- +{
- + struct file *filp = curlun->filp;
- + struct inode *inode = filp->f_path.dentry->d_inode;
- + unsigned long rc;
- +
- + rc = invalidate_mapping_pages(inode->i_mapping, 0, -1);
- + VLDBG(curlun, "invalidate_mapping_pages -> %ld\n", rc);
- +}
- +
- +static int do_verify(struct fsg_dev *fsg)
- +{
- + struct fsg_lun *curlun = fsg->curlun;
- + u32 lba;
- + u32 verification_length;
- + struct fsg_buffhd *bh = fsg->next_buffhd_to_fill;
- + loff_t file_offset, file_offset_tmp;
- + u32 amount_left;
- + unsigned int amount;
- + ssize_t nread;
- +
- + /* Get the starting Logical Block Address and check that it's
- + * not too big */
- + lba = get_unaligned_be32(&fsg->cmnd[2]);
- + if (lba >= curlun->num_sectors) {
- + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
- + return -EINVAL;
- + }
- +
- + /* We allow DPO (Disable Page Out = don't save data in the
- + * cache) but we don't implement it. */
- + if ((fsg->cmnd[1] & ~0x10) != 0) {
- + curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
- + return -EINVAL;
- + }
- +
- + verification_length = get_unaligned_be16(&fsg->cmnd[7]);
- + if (unlikely(verification_length == 0))
- + return -EIO; // No default reply
- +
- + /* Prepare to carry out the file verify */
- + amount_left = verification_length << curlun->blkbits;
- + file_offset = ((loff_t) lba) << curlun->blkbits;
- +
- + /* Write out all the dirty buffers before invalidating them */
- + fsg_lun_fsync_sub(curlun);
- + if (signal_pending(current))
- + return -EINTR;
- +
- + invalidate_sub(curlun);
- + if (signal_pending(current))
- + return -EINTR;
- +
- + /* Just try to read the requested blocks */
- + while (amount_left > 0) {
- +
- + /* Figure out how much we need to read:
- + * Try to read the remaining amount, but not more than
- + * the buffer size.
- + * And don't try to read past the end of the file.
- + */
- + amount = min((unsigned int) amount_left, mod_data.buflen);
- + amount = min((loff_t) amount,
- + curlun->file_length - file_offset);
- + if (amount == 0) {
- + curlun->sense_data =
- + SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
- + curlun->sense_data_info = file_offset >> curlun->blkbits;
- + curlun->info_valid = 1;
- + break;
- + }
- +
- + /* Perform the read */
- + file_offset_tmp = file_offset;
- + nread = vfs_read(curlun->filp,
- + (char __user *) bh->buf,
- + amount, &file_offset_tmp);
- + VLDBG(curlun, "file read %u @ %llu -> %d\n", amount,
- + (unsigned long long) file_offset,
- + (int) nread);
- + if (signal_pending(current))
- + return -EINTR;
- +
- + if (nread < 0) {
- + LDBG(curlun, "error in file verify: %d\n",
- + (int) nread);
- + nread = 0;
- + } else if (nread < amount) {
- + LDBG(curlun, "partial file verify: %d/%u\n",
- + (int) nread, amount);
- + nread = round_down(nread, curlun->blksize);
- + }
- + if (nread == 0) {
- + curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
- + curlun->sense_data_info = file_offset >> curlun->blkbits;
- + curlun->info_valid = 1;
- + break;
- + }
- + file_offset += nread;
- + amount_left -= nread;
- + }
- + return 0;
- +}
- +
- +
- +/*-------------------------------------------------------------------------*/
- +
- +static int do_inquiry(struct fsg_dev *fsg, struct fsg_buffhd *bh)
- +{
- + u8 *buf = (u8 *) bh->buf;
- +
- + static char vendor_id[] = "Linux ";
- + static char product_disk_id[] = "File-Stor Gadget";
- + static char product_cdrom_id[] = "File-CD Gadget ";
- +
- + if (!fsg->curlun) { // Unsupported LUNs are okay
- + fsg->bad_lun_okay = 1;
- + memset(buf, 0, 36);
- + buf[0] = 0x7f; // Unsupported, no device-type
- + buf[4] = 31; // Additional length
- + return 36;
- + }
- +
- + memset(buf, 0, 8);
- + buf[0] = (mod_data.cdrom ? TYPE_ROM : TYPE_DISK);
- + if (mod_data.removable)
- + buf[1] = 0x80;
- + buf[2] = 2; // ANSI SCSI level 2
- + buf[3] = 2; // SCSI-2 INQUIRY data format
- + buf[4] = 31; // Additional length
- + // No special options
- + sprintf(buf + 8, "%-8s%-16s%04x", vendor_id,
- + (mod_data.cdrom ? product_cdrom_id :
- + product_disk_id),
- + mod_data.release);
- + return 36;
- +}
- +
- +
- +static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
- +{
- + struct fsg_lun *curlun = fsg->curlun;
- + u8 *buf = (u8 *) bh->buf;
- + u32 sd, sdinfo;
- + int valid;
- +
- + /*
- + * From the SCSI-2 spec., section 7.9 (Unit attention condition):
- + *
- + * If a REQUEST SENSE command is received from an initiator
- + * with a pending unit attention condition (before the target
- + * generates the contingent allegiance condition), then the
- + * target shall either:
- + * a) report any pending sense data and preserve the unit
- + * attention condition on the logical unit, or,
- + * b) report the unit attention condition, may discard any
- + * pending sense data, and clear the unit attention
- + * condition on the logical unit for that initiator.
- + *
- + * FSG normally uses option a); enable this code to use option b).
- + */
- +#if 0
- + if (curlun && curlun->unit_attention_data != SS_NO_SENSE) {
- + curlun->sense_data = curlun->unit_attention_data;
- + curlun->unit_attention_data = SS_NO_SENSE;
- + }
- +#endif
- +
- + if (!curlun) { // Unsupported LUNs are okay
- + fsg->bad_lun_okay = 1;
- + sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
- + sdinfo = 0;
- + valid = 0;
- + } else {
- + sd = curlun->sense_data;
- + sdinfo = curlun->sense_data_info;
- + valid = curlun->info_valid << 7;
- + curlun->sense_data = SS_NO_SENSE;
- + curlun->sense_data_info = 0;
- + curlun->info_valid = 0;
- + }
- +
- + memset(buf, 0, 18);
- + buf[0] = valid | 0x70; // Valid, current error
- + buf[2] = SK(sd);
- + put_unaligned_be32(sdinfo, &buf[3]); /* Sense information */
- + buf[7] = 18 - 8; // Additional sense length
- + buf[12] = ASC(sd);
- + buf[13] = ASCQ(sd);
- + return 18;
- +}
- +
- +
- +static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh)
- +{
- + struct fsg_lun *curlun = fsg->curlun;
- + u32 lba = get_unaligned_be32(&fsg->cmnd[2]);
- + int pmi = fsg->cmnd[8];
- + u8 *buf = (u8 *) bh->buf;
- +
- + /* Check the PMI and LBA fields */
- + if (pmi > 1 || (pmi == 0 && lba != 0)) {
- + curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
- + return -EINVAL;
- + }
- +
- + put_unaligned_be32(curlun->num_sectors - 1, &buf[0]);
- + /* Max logical block */
- + put_unaligned_be32(curlun->blksize, &buf[4]); /* Block length */
- + return 8;
- +}
- +
- +
- +static int do_read_header(struct fsg_dev *fsg, struct fsg_buffhd *bh)
- +{
- + struct fsg_lun *curlun = fsg->curlun;
- + int msf = fsg->cmnd[1] & 0x02;
- + u32 lba = get_unaligned_be32(&fsg->cmnd[2]);
- + u8 *buf = (u8 *) bh->buf;
- +
- + if ((fsg->cmnd[1] & ~0x02) != 0) { /* Mask away MSF */
- + curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
- + return -EINVAL;
- + }
- + if (lba >= curlun->num_sectors) {
- + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
- + return -EINVAL;
- + }
- +
- + memset(buf, 0, 8);
- + buf[0] = 0x01; /* 2048 bytes of user data, rest is EC */
- + store_cdrom_address(&buf[4], msf, lba);
- + return 8;
- +}
- +
- +
- +static int do_read_toc(struct fsg_dev *fsg, struct fsg_buffhd *bh)
- +{
- + struct fsg_lun *curlun = fsg->curlun;
- + int msf = fsg->cmnd[1] & 0x02;
- + int start_track = fsg->cmnd[6];
- + u8 *buf = (u8 *) bh->buf;
- +
- + if ((fsg->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */
- + start_track > 1) {
- + curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
- + return -EINVAL;
- + }
- +
- + memset(buf, 0, 20);
- + buf[1] = (20-2); /* TOC data length */
- + buf[2] = 1; /* First track number */
- + buf[3] = 1; /* Last track number */
- + buf[5] = 0x16; /* Data track, copying allowed */
- + buf[6] = 0x01; /* Only track is number 1 */
- + store_cdrom_address(&buf[8], msf, 0);
- +
- + buf[13] = 0x16; /* Lead-out track is data */
- + buf[14] = 0xAA; /* Lead-out track number */
- + store_cdrom_address(&buf[16], msf, curlun->num_sectors);
- + return 20;
- +}
- +
- +
- +static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
- +{
- + struct fsg_lun *curlun = fsg->curlun;
- + int mscmnd = fsg->cmnd[0];
- + u8 *buf = (u8 *) bh->buf;
- + u8 *buf0 = buf;
- + int pc, page_code;
- + int changeable_values, all_pages;
- + int valid_page = 0;
- + int len, limit;
- +
- + if ((fsg->cmnd[1] & ~0x08) != 0) { // Mask away DBD
- + curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
- + return -EINVAL;
- + }
- + pc = fsg->cmnd[2] >> 6;
- + page_code = fsg->cmnd[2] & 0x3f;
- + if (pc == 3) {
- + curlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED;
- + return -EINVAL;
- + }
- + changeable_values = (pc == 1);
- + all_pages = (page_code == 0x3f);
- +
- + /* Write the mode parameter header. Fixed values are: default
- + * medium type, no cache control (DPOFUA), and no block descriptors.
- + * The only variable value is the WriteProtect bit. We will fill in
- + * the mode data length later. */
- + memset(buf, 0, 8);
- + if (mscmnd == MODE_SENSE) {
- + buf[2] = (curlun->ro ? 0x80 : 0x00); // WP, DPOFUA
- + buf += 4;
- + limit = 255;
- + } else { // MODE_SENSE_10
- + buf[3] = (curlun->ro ? 0x80 : 0x00); // WP, DPOFUA
- + buf += 8;
- + limit = 65535; // Should really be mod_data.buflen
- + }
- +
- + /* No block descriptors */
- +
- + /* The mode pages, in numerical order. The only page we support
- + * is the Caching page. */
- + if (page_code == 0x08 || all_pages) {
- + valid_page = 1;
- + buf[0] = 0x08; // Page code
- + buf[1] = 10; // Page length
- + memset(buf+2, 0, 10); // None of the fields are changeable
- +
- + if (!changeable_values) {
- + buf[2] = 0x04; // Write cache enable,
- + // Read cache not disabled
- + // No cache retention priorities
- + put_unaligned_be16(0xffff, &buf[4]);
- + /* Don't disable prefetch */
- + /* Minimum prefetch = 0 */
- + put_unaligned_be16(0xffff, &buf[8]);
- + /* Maximum prefetch */
- + put_unaligned_be16(0xffff, &buf[10]);
- + /* Maximum prefetch ceiling */
- + }
- + buf += 12;
- + }
- +
- + /* Check that a valid page was requested and the mode data length
- + * isn't too long. */
- + len = buf - buf0;
- + if (!valid_page || len > limit) {
- + curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
- + return -EINVAL;
- + }
- +
- + /* Store the mode data length */
- + if (mscmnd == MODE_SENSE)
- + buf0[0] = len - 1;
- + else
- + put_unaligned_be16(len - 2, buf0);
- + return len;
- +}
- +
- +
- +static int do_start_stop(struct fsg_dev *fsg)
- +{
- + struct fsg_lun *curlun = fsg->curlun;
- + int loej, start;
- +
- + if (!mod_data.removable) {
- + curlun->sense_data = SS_INVALID_COMMAND;
- + return -EINVAL;
- + }
- +
- + // int immed = fsg->cmnd[1] & 0x01;
- + loej = fsg->cmnd[4] & 0x02;
- + start = fsg->cmnd[4] & 0x01;
- +
- +#ifdef CONFIG_USB_FILE_STORAGE_TEST
- + if ((fsg->cmnd[1] & ~0x01) != 0 || // Mask away Immed
- + (fsg->cmnd[4] & ~0x03) != 0) { // Mask LoEj, Start
- + curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
- + return -EINVAL;
- + }
- +
- + if (!start) {
- +
- + /* Are we allowed to unload the media? */
- + if (curlun->prevent_medium_removal) {
- + LDBG(curlun, "unload attempt prevented\n");
- + curlun->sense_data = SS_MEDIUM_REMOVAL_PREVENTED;
- + return -EINVAL;
- + }
- + if (loej) { // Simulate an unload/eject
- + up_read(&fsg->filesem);
- + down_write(&fsg->filesem);
- + fsg_lun_close(curlun);
- + up_write(&fsg->filesem);
- + down_read(&fsg->filesem);
- + }
- + } else {
- +
- + /* Our emulation doesn't support mounting; the medium is
- + * available for use as soon as it is loaded. */
- + if (!fsg_lun_is_open(curlun)) {
- + curlun->sense_data = SS_MEDIUM_NOT_PRESENT;
- + return -EINVAL;
- + }
- + }
- +#endif
- + return 0;
- +}
- +
- +
- +static int do_prevent_allow(struct fsg_dev *fsg)
- +{
- + struct fsg_lun *curlun = fsg->curlun;
- + int prevent;
- +
- + if (!mod_data.removable) {
- + curlun->sense_data = SS_INVALID_COMMAND;
- + return -EINVAL;
- + }
- +
- + prevent = fsg->cmnd[4] & 0x01;
- + if ((fsg->cmnd[4] & ~0x01) != 0) { // Mask away Prevent
- + curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
- + return -EINVAL;
- + }
- +
- + if (curlun->prevent_medium_removal && !prevent)
- + fsg_lun_fsync_sub(curlun);
- + curlun->prevent_medium_removal = prevent;
- + return 0;
- +}
- +
- +
- +static int do_read_format_capacities(struct fsg_dev *fsg,
- + struct fsg_buffhd *bh)
- +{
- + struct fsg_lun *curlun = fsg->curlun;
- + u8 *buf = (u8 *) bh->buf;
- +
- + buf[0] = buf[1] = buf[2] = 0;
- + buf[3] = 8; // Only the Current/Maximum Capacity Descriptor
- + buf += 4;
- +
- + put_unaligned_be32(curlun->num_sectors, &buf[0]);
- + /* Number of blocks */
- + put_unaligned_be32(curlun->blksize, &buf[4]); /* Block length */
- + buf[4] = 0x02; /* Current capacity */
- + return 12;
- +}
- +
- +
- +static int do_mode_select(struct fsg_dev *fsg, struct fsg_buffhd *bh)
- +{
- + struct fsg_lun *curlun = fsg->curlun;
- +
- + /* We don't support MODE SELECT */
- + curlun->sense_data = SS_INVALID_COMMAND;
- + return -EINVAL;
- +}
- +
- +
- +/*-------------------------------------------------------------------------*/
- +
- +static int halt_bulk_in_endpoint(struct fsg_dev *fsg)
- +{
- + int rc;
- +
- + rc = fsg_set_halt(fsg, fsg->bulk_in);
- + if (rc == -EAGAIN)
- + VDBG(fsg, "delayed bulk-in endpoint halt\n");
- + while (rc != 0) {
- + if (rc != -EAGAIN) {
- + WARNING(fsg, "usb_ep_set_halt -> %d\n", rc);
- + rc = 0;
- + break;
- + }
- +
- + /* Wait for a short time and then try again */
- + if (msleep_interruptible(100) != 0)
- + return -EINTR;
- + rc = usb_ep_set_halt(fsg->bulk_in);
- + }
- + return rc;
- +}
- +
- +static int wedge_bulk_in_endpoint(struct fsg_dev *fsg)
- +{
- + int rc;
- +
- + DBG(fsg, "bulk-in set wedge\n");
- + rc = usb_ep_set_wedge(fsg->bulk_in);
- + if (rc == -EAGAIN)
- + VDBG(fsg, "delayed bulk-in endpoint wedge\n");
- + while (rc != 0) {
- + if (rc != -EAGAIN) {
- + WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc);
- + rc = 0;
- + break;
- + }
- +
- + /* Wait for a short time and then try again */
- + if (msleep_interruptible(100) != 0)
- + return -EINTR;
- + rc = usb_ep_set_wedge(fsg->bulk_in);
- + }
- + return rc;
- +}
- +
- +static int throw_away_data(struct fsg_dev *fsg)
- +{
- + struct fsg_buffhd *bh;
- + u32 amount;
- + int rc;
- +
- + while ((bh = fsg->next_buffhd_to_drain)->state != BUF_STATE_EMPTY ||
- + fsg->usb_amount_left > 0) {
- +
- + /* Throw away the data in a filled buffer */
- + if (bh->state == BUF_STATE_FULL) {
- + smp_rmb();
- + bh->state = BUF_STATE_EMPTY;
- + fsg->next_buffhd_to_drain = bh->next;
- +
- + /* A short packet or an error ends everything */
- + if (bh->outreq->actual < bh->bulk_out_intended_length ||
- + bh->outreq->status != 0) {
- + raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT);
- + return -EINTR;
- + }
- + continue;
- + }
- +
- + /* Try to submit another request if we need one */
- + bh = fsg->next_buffhd_to_fill;
- + if (bh->state == BUF_STATE_EMPTY && fsg->usb_amount_left > 0) {
- + amount = min(fsg->usb_amount_left,
- + (u32) mod_data.buflen);
- +
- + /* Except at the end of the transfer, amount will be
- + * equal to the buffer size, which is divisible by
- + * the bulk-out maxpacket size.
- + */
- + set_bulk_out_req_length(fsg, bh, amount);
- + start_transfer(fsg, fsg->bulk_out, bh->outreq,
- + &bh->outreq_busy, &bh->state);
- + fsg->next_buffhd_to_fill = bh->next;
- + fsg->usb_amount_left -= amount;
- + continue;
- + }
- +
- + /* Otherwise wait for something to happen */
- + rc = sleep_thread(fsg);
- + if (rc)
- + return rc;
- + }
- + return 0;
- +}
- +
- +
- +static int finish_reply(struct fsg_dev *fsg)
- +{
- + struct fsg_buffhd *bh = fsg->next_buffhd_to_fill;
- + int rc = 0;
- +
- + switch (fsg->data_dir) {
- + case DATA_DIR_NONE:
- + break; // Nothing to send
- +
- + /* If we don't know whether the host wants to read or write,
- + * this must be CB or CBI with an unknown command. We mustn't
- + * try to send or receive any data. So stall both bulk pipes
- + * if we can and wait for a reset. */
- + case DATA_DIR_UNKNOWN:
- + if (mod_data.can_stall) {
- + fsg_set_halt(fsg, fsg->bulk_out);
- + rc = halt_bulk_in_endpoint(fsg);
- + }
- + break;
- +
- + /* All but the last buffer of data must have already been sent */
- + case DATA_DIR_TO_HOST:
- + if (fsg->data_size == 0)
- + ; // Nothing to send
- +
- + /* If there's no residue, simply send the last buffer */
- + else if (fsg->residue == 0) {
- + bh->inreq->zero = 0;
- + start_transfer(fsg, fsg->bulk_in, bh->inreq,
- + &bh->inreq_busy, &bh->state);
- + fsg->next_buffhd_to_fill = bh->next;
- + }
- +
- + /* There is a residue. For CB and CBI, simply mark the end
- + * of the data with a short packet. However, if we are
- + * allowed to stall, there was no data at all (residue ==
- + * data_size), and the command failed (invalid LUN or
- + * sense data is set), then halt the bulk-in endpoint
- + * instead. */
- + else if (!transport_is_bbb()) {
- + if (mod_data.can_stall &&
- + fsg->residue == fsg->data_size &&
- + (!fsg->curlun || fsg->curlun->sense_data != SS_NO_SENSE)) {
- + bh->state = BUF_STATE_EMPTY;
- + rc = halt_bulk_in_endpoint(fsg);
- + } else {
- + bh->inreq->zero = 1;
- + start_transfer(fsg, fsg->bulk_in, bh->inreq,
- + &bh->inreq_busy, &bh->state);
- + fsg->next_buffhd_to_fill = bh->next;
- + }
- + }
- +
- + /*
- + * For Bulk-only, mark the end of the data with a short
- + * packet. If we are allowed to stall, halt the bulk-in
- + * endpoint. (Note: This violates the Bulk-Only Transport
- + * specification, which requires us to pad the data if we
- + * don't halt the endpoint. Presumably nobody will mind.)
- + */
- + else {
- + bh->inreq->zero = 1;
- + start_transfer(fsg, fsg->bulk_in, bh->inreq,
- + &bh->inreq_busy, &bh->state);
- + fsg->next_buffhd_to_fill = bh->next;
- + if (mod_data.can_stall)
- + rc = halt_bulk_in_endpoint(fsg);
- + }
- + break;
- +
- + /* We have processed all we want from the data the host has sent.
- + * There may still be outstanding bulk-out requests. */
- + case DATA_DIR_FROM_HOST:
- + if (fsg->residue == 0)
- + ; // Nothing to receive
- +
- + /* Did the host stop sending unexpectedly early? */
- + else if (fsg->short_packet_received) {
- + raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT);
- + rc = -EINTR;
- + }
- +
- + /* We haven't processed all the incoming data. Even though
- + * we may be allowed to stall, doing so would cause a race.
- + * The controller may already have ACK'ed all the remaining
- + * bulk-out packets, in which case the host wouldn't see a
- + * STALL. Not realizing the endpoint was halted, it wouldn't
- + * clear the halt -- leading to problems later on. */
- +#if 0
- + else if (mod_data.can_stall) {
- + fsg_set_halt(fsg, fsg->bulk_out);
- + raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT);
- + rc = -EINTR;
- + }
- +#endif
- +
- + /* We can't stall. Read in the excess data and throw it
- + * all away. */
- + else
- + rc = throw_away_data(fsg);
- + break;
- + }
- + return rc;
- +}
- +
- +
- +static int send_status(struct fsg_dev *fsg)
- +{
- + struct fsg_lun *curlun = fsg->curlun;
- + struct fsg_buffhd *bh;
- + int rc;
- + u8 status = US_BULK_STAT_OK;
- + u32 sd, sdinfo = 0;
- +
- + /* Wait for the next buffer to become available */
- + bh = fsg->next_buffhd_to_fill;
- + while (bh->state != BUF_STATE_EMPTY) {
- + rc = sleep_thread(fsg);
- + if (rc)
- + return rc;
- + }
- +
- + if (curlun) {
- + sd = curlun->sense_data;
- + sdinfo = curlun->sense_data_info;
- + } else if (fsg->bad_lun_okay)
- + sd = SS_NO_SENSE;
- + else
- + sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
- +
- + if (fsg->phase_error) {
- + DBG(fsg, "sending phase-error status\n");
- + status = US_BULK_STAT_PHASE;
- + sd = SS_INVALID_COMMAND;
- + } else if (sd != SS_NO_SENSE) {
- + DBG(fsg, "sending command-failure status\n");
- + status = US_BULK_STAT_FAIL;
- + VDBG(fsg, " sense data: SK x%02x, ASC x%02x, ASCQ x%02x;"
- + " info x%x\n",
- + SK(sd), ASC(sd), ASCQ(sd), sdinfo);
- + }
- +
- + if (transport_is_bbb()) {
- + struct bulk_cs_wrap *csw = bh->buf;
- +
- + /* Store and send the Bulk-only CSW */
- + csw->Signature = cpu_to_le32(US_BULK_CS_SIGN);
- + csw->Tag = fsg->tag;
- + csw->Residue = cpu_to_le32(fsg->residue);
- + csw->Status = status;
- +
- + bh->inreq->length = US_BULK_CS_WRAP_LEN;
- + bh->inreq->zero = 0;
- + start_transfer(fsg, fsg->bulk_in, bh->inreq,
- + &bh->inreq_busy, &bh->state);
- +
- + } else if (mod_data.transport_type == USB_PR_CB) {
- +
- + /* Control-Bulk transport has no status phase! */
- + return 0;
- +
- + } else { // USB_PR_CBI
- + struct interrupt_data *buf = bh->buf;
- +
- + /* Store and send the Interrupt data. UFI sends the ASC
- + * and ASCQ bytes. Everything else sends a Type (which
- + * is always 0) and the status Value. */
- + if (mod_data.protocol_type == USB_SC_UFI) {
- + buf->bType = ASC(sd);
- + buf->bValue = ASCQ(sd);
- + } else {
- + buf->bType = 0;
- + buf->bValue = status;
- + }
- + fsg->intreq->length = CBI_INTERRUPT_DATA_LEN;
- +
- + fsg->intr_buffhd = bh; // Point to the right buffhd
- + fsg->intreq->buf = bh->inreq->buf;
- + fsg->intreq->context = bh;
- + start_transfer(fsg, fsg->intr_in, fsg->intreq,
- + &fsg->intreq_busy, &bh->state);
- + }
- +
- + fsg->next_buffhd_to_fill = bh->next;
- + return 0;
- +}
- +
- +
- +/*-------------------------------------------------------------------------*/
- +
- +/* Check whether the command is properly formed and whether its data size
- + * and direction agree with the values we already have. */
- +static int check_command(struct fsg_dev *fsg, int cmnd_size,
- + enum data_direction data_dir, unsigned int mask,
- + int needs_medium, const char *name)
- +{
- + int i;
- + int lun = fsg->cmnd[1] >> 5;
- + static const char dirletter[4] = {'u', 'o', 'i', 'n'};
- + char hdlen[20];
- + struct fsg_lun *curlun;
- +
- + /* Adjust the expected cmnd_size for protocol encapsulation padding.
- + * Transparent SCSI doesn't pad. */
- + if (protocol_is_scsi())
- + ;
- +
- + /* There's some disagreement as to whether RBC pads commands or not.
- + * We'll play it safe and accept either form. */
- + else if (mod_data.protocol_type == USB_SC_RBC) {
- + if (fsg->cmnd_size == 12)
- + cmnd_size = 12;
- +
- + /* All the other protocols pad to 12 bytes */
- + } else
- + cmnd_size = 12;
- +
- + hdlen[0] = 0;
- + if (fsg->data_dir != DATA_DIR_UNKNOWN)
- + sprintf(hdlen, ", H%c=%u", dirletter[(int) fsg->data_dir],
- + fsg->data_size);
- + VDBG(fsg, "SCSI command: %s; Dc=%d, D%c=%u; Hc=%d%s\n",
- + name, cmnd_size, dirletter[(int) data_dir],
- + fsg->data_size_from_cmnd, fsg->cmnd_size, hdlen);
- +
- + /* We can't reply at all until we know the correct data direction
- + * and size. */
- + if (fsg->data_size_from_cmnd == 0)
- + data_dir = DATA_DIR_NONE;
- + if (fsg->data_dir == DATA_DIR_UNKNOWN) { // CB or CBI
- + fsg->data_dir = data_dir;
- + fsg->data_size = fsg->data_size_from_cmnd;
- +
- + } else { // Bulk-only
- + if (fsg->data_size < fsg->data_size_from_cmnd) {
- +
- + /* Host data size < Device data size is a phase error.
- + * Carry out the command, but only transfer as much
- + * as we are allowed. */
- + fsg->data_size_from_cmnd = fsg->data_size;
- + fsg->phase_error = 1;
- + }
- + }
- + fsg->residue = fsg->usb_amount_left = fsg->data_size;
- +
- + /* Conflicting data directions is a phase error */
- + if (fsg->data_dir != data_dir && fsg->data_size_from_cmnd > 0) {
- + fsg->phase_error = 1;
- + return -EINVAL;
- + }
- +
- + /* Verify the length of the command itself */
- + if (cmnd_size != fsg->cmnd_size) {
- +
- + /* Special case workaround: There are plenty of buggy SCSI
- + * implementations. Many have issues with cbw->Length
- + * field passing a wrong command size. For those cases we
- + * always try to work around the problem by using the length
- + * sent by the host side provided it is at least as large
- + * as the correct command length.
- + * Examples of such cases would be MS-Windows, which issues
- + * REQUEST SENSE with cbw->Length == 12 where it should
- + * be 6, and xbox360 issuing INQUIRY, TEST UNIT READY and
- + * REQUEST SENSE with cbw->Length == 10 where it should
- + * be 6 as well.
- + */
- + if (cmnd_size <= fsg->cmnd_size) {
- + DBG(fsg, "%s is buggy! Expected length %d "
- + "but we got %d\n", name,
- + cmnd_size, fsg->cmnd_size);
- + cmnd_size = fsg->cmnd_size;
- + } else {
- + fsg->phase_error = 1;
- + return -EINVAL;
- + }
- + }
- +
- + /* Check that the LUN values are consistent */
- + if (transport_is_bbb()) {
- + if (fsg->lun != lun)
- + DBG(fsg, "using LUN %d from CBW, "
- + "not LUN %d from CDB\n",
- + fsg->lun, lun);
- + }
- +
- + /* Check the LUN */
- + curlun = fsg->curlun;
- + if (curlun) {
- + if (fsg->cmnd[0] != REQUEST_SENSE) {
- + curlun->sense_data = SS_NO_SENSE;
- + curlun->sense_data_info = 0;
- + curlun->info_valid = 0;
- + }
- + } else {
- + fsg->bad_lun_okay = 0;
- +
- + /* INQUIRY and REQUEST SENSE commands are explicitly allowed
- + * to use unsupported LUNs; all others may not. */
- + if (fsg->cmnd[0] != INQUIRY &&
- + fsg->cmnd[0] != REQUEST_SENSE) {
- + DBG(fsg, "unsupported LUN %d\n", fsg->lun);
- + return -EINVAL;
- + }
- + }
- +
- + /* If a unit attention condition exists, only INQUIRY and
- + * REQUEST SENSE commands are allowed; anything else must fail. */
- + if (curlun && curlun->unit_attention_data != SS_NO_SENSE &&
- + fsg->cmnd[0] != INQUIRY &&
- + fsg->cmnd[0] != REQUEST_SENSE) {
- + curlun->sense_data = curlun->unit_attention_data;
- + curlun->unit_attention_data = SS_NO_SENSE;
- + return -EINVAL;
- + }
- +
- + /* Check that only command bytes listed in the mask are non-zero */
- + fsg->cmnd[1] &= 0x1f; // Mask away the LUN
- + for (i = 1; i < cmnd_size; ++i) {
- + if (fsg->cmnd[i] && !(mask & (1 << i))) {
- + if (curlun)
- + curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
- + return -EINVAL;
- + }
- + }
- +
- + /* If the medium isn't mounted and the command needs to access
- + * it, return an error. */
- + if (curlun && !fsg_lun_is_open(curlun) && needs_medium) {
- + curlun->sense_data = SS_MEDIUM_NOT_PRESENT;
- + return -EINVAL;
- + }
- +
- + return 0;
- +}
- +
- +/* wrapper of check_command for data size in blocks handling */
- +static int check_command_size_in_blocks(struct fsg_dev *fsg, int cmnd_size,
- + enum data_direction data_dir, unsigned int mask,
- + int needs_medium, const char *name)
- +{
- + if (fsg->curlun)
- + fsg->data_size_from_cmnd <<= fsg->curlun->blkbits;
- + return check_command(fsg, cmnd_size, data_dir,
- + mask, needs_medium, name);
- +}
- +
- +static int do_scsi_command(struct fsg_dev *fsg)
- +{
- + struct fsg_buffhd *bh;
- + int rc;
- + int reply = -EINVAL;
- + int i;
- + static char unknown[16];
- +
- + dump_cdb(fsg);
- +
- + /* Wait for the next buffer to become available for data or status */
- + bh = fsg->next_buffhd_to_drain = fsg->next_buffhd_to_fill;
- + while (bh->state != BUF_STATE_EMPTY) {
- + rc = sleep_thread(fsg);
- + if (rc)
- + return rc;
- + }
- + fsg->phase_error = 0;
- + fsg->short_packet_received = 0;
- +
- + down_read(&fsg->filesem); // We're using the backing file
- + switch (fsg->cmnd[0]) {
- +
- + case INQUIRY:
- + fsg->data_size_from_cmnd = fsg->cmnd[4];
- + if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST,
- + (1<<4), 0,
- + "INQUIRY")) == 0)
- + reply = do_inquiry(fsg, bh);
- + break;
- +
- + case MODE_SELECT:
- + fsg->data_size_from_cmnd = fsg->cmnd[4];
- + if ((reply = check_command(fsg, 6, DATA_DIR_FROM_HOST,
- + (1<<1) | (1<<4), 0,
- + "MODE SELECT(6)")) == 0)
- + reply = do_mode_select(fsg, bh);
- + break;
- +
- + case MODE_SELECT_10:
- + fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
- + if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST,
- + (1<<1) | (3<<7), 0,
- + "MODE SELECT(10)")) == 0)
- + reply = do_mode_select(fsg, bh);
- + break;
- +
- + case MODE_SENSE:
- + fsg->data_size_from_cmnd = fsg->cmnd[4];
- + if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST,
- + (1<<1) | (1<<2) | (1<<4), 0,
- + "MODE SENSE(6)")) == 0)
- + reply = do_mode_sense(fsg, bh);
- + break;
- +
- + case MODE_SENSE_10:
- + fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
- + if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
- + (1<<1) | (1<<2) | (3<<7), 0,
- + "MODE SENSE(10)")) == 0)
- + reply = do_mode_sense(fsg, bh);
- + break;
- +
- + case ALLOW_MEDIUM_REMOVAL:
- + fsg->data_size_from_cmnd = 0;
- + if ((reply = check_command(fsg, 6, DATA_DIR_NONE,
- + (1<<4), 0,
- + "PREVENT-ALLOW MEDIUM REMOVAL")) == 0)
- + reply = do_prevent_allow(fsg);
- + break;
- +
- + case READ_6:
- + i = fsg->cmnd[4];
- + fsg->data_size_from_cmnd = (i == 0) ? 256 : i;
- + if ((reply = check_command_size_in_blocks(fsg, 6,
- + DATA_DIR_TO_HOST,
- + (7<<1) | (1<<4), 1,
- + "READ(6)")) == 0)
- + reply = do_read(fsg);
- + break;
- +
- + case READ_10:
- + fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
- + if ((reply = check_command_size_in_blocks(fsg, 10,
- + DATA_DIR_TO_HOST,
- + (1<<1) | (0xf<<2) | (3<<7), 1,
- + "READ(10)")) == 0)
- + reply = do_read(fsg);
- + break;
- +
- + case READ_12:
- + fsg->data_size_from_cmnd = get_unaligned_be32(&fsg->cmnd[6]);
- + if ((reply = check_command_size_in_blocks(fsg, 12,
- + DATA_DIR_TO_HOST,
- + (1<<1) | (0xf<<2) | (0xf<<6), 1,
- + "READ(12)")) == 0)
- + reply = do_read(fsg);
- + break;
- +
- + case READ_CAPACITY:
- + fsg->data_size_from_cmnd = 8;
- + if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
- + (0xf<<2) | (1<<8), 1,
- + "READ CAPACITY")) == 0)
- + reply = do_read_capacity(fsg, bh);
- + break;
- +
- + case READ_HEADER:
- + if (!mod_data.cdrom)
- + goto unknown_cmnd;
- + fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
- + if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
- + (3<<7) | (0x1f<<1), 1,
- + "READ HEADER")) == 0)
- + reply = do_read_header(fsg, bh);
- + break;
- +
- + case READ_TOC:
- + if (!mod_data.cdrom)
- + goto unknown_cmnd;
- + fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
- + if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
- + (7<<6) | (1<<1), 1,
- + "READ TOC")) == 0)
- + reply = do_read_toc(fsg, bh);
- + break;
- +
- + case READ_FORMAT_CAPACITIES:
- + fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
- + if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
- + (3<<7), 1,
- + "READ FORMAT CAPACITIES")) == 0)
- + reply = do_read_format_capacities(fsg, bh);
- + break;
- +
- + case REQUEST_SENSE:
- + fsg->data_size_from_cmnd = fsg->cmnd[4];
- + if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST,
- + (1<<4), 0,
- + "REQUEST SENSE")) == 0)
- + reply = do_request_sense(fsg, bh);
- + break;
- +
- + case START_STOP:
- + fsg->data_size_from_cmnd = 0;
- + if ((reply = check_command(fsg, 6, DATA_DIR_NONE,
- + (1<<1) | (1<<4), 0,
- + "START-STOP UNIT")) == 0)
- + reply = do_start_stop(fsg);
- + break;
- +
- + case SYNCHRONIZE_CACHE:
- + fsg->data_size_from_cmnd = 0;
- + if ((reply = check_command(fsg, 10, DATA_DIR_NONE,
- + (0xf<<2) | (3<<7), 1,
- + "SYNCHRONIZE CACHE")) == 0)
- + reply = do_synchronize_cache(fsg);
- + break;
- +
- + case TEST_UNIT_READY:
- + fsg->data_size_from_cmnd = 0;
- + reply = check_command(fsg, 6, DATA_DIR_NONE,
- + 0, 1,
- + "TEST UNIT READY");
- + break;
- +
- + /* Although optional, this command is used by MS-Windows. We
- + * support a minimal version: BytChk must be 0. */
- + case VERIFY:
- + fsg->data_size_from_cmnd = 0;
- + if ((reply = check_command(fsg, 10, DATA_DIR_NONE,
- + (1<<1) | (0xf<<2) | (3<<7), 1,
- + "VERIFY")) == 0)
- + reply = do_verify(fsg);
- + break;
- +
- + case WRITE_6:
- + i = fsg->cmnd[4];
- + fsg->data_size_from_cmnd = (i == 0) ? 256 : i;
- + if ((reply = check_command_size_in_blocks(fsg, 6,
- + DATA_DIR_FROM_HOST,
- + (7<<1) | (1<<4), 1,
- + "WRITE(6)")) == 0)
- + reply = do_write(fsg);
- + break;
- +
- + case WRITE_10:
- + fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
- + if ((reply = check_command_size_in_blocks(fsg, 10,
- + DATA_DIR_FROM_HOST,
- + (1<<1) | (0xf<<2) | (3<<7), 1,
- + "WRITE(10)")) == 0)
- + reply = do_write(fsg);
- + break;
- +
- + case WRITE_12:
- + fsg->data_size_from_cmnd = get_unaligned_be32(&fsg->cmnd[6]);
- + if ((reply = check_command_size_in_blocks(fsg, 12,
- + DATA_DIR_FROM_HOST,
- + (1<<1) | (0xf<<2) | (0xf<<6), 1,
- + "WRITE(12)")) == 0)
- + reply = do_write(fsg);
- + break;
- +
- + /* Some mandatory commands that we recognize but don't implement.
- + * They don't mean much in this setting. It's left as an exercise
- + * for anyone interested to implement RESERVE and RELEASE in terms
- + * of Posix locks. */
- + case FORMAT_UNIT:
- + case RELEASE:
- + case RESERVE:
- + case SEND_DIAGNOSTIC:
- + // Fall through
- +
- + default:
- + unknown_cmnd:
- + fsg->data_size_from_cmnd = 0;
- + sprintf(unknown, "Unknown x%02x", fsg->cmnd[0]);
- + if ((reply = check_command(fsg, fsg->cmnd_size,
- + DATA_DIR_UNKNOWN, ~0, 0, unknown)) == 0) {
- + fsg->curlun->sense_data = SS_INVALID_COMMAND;
- + reply = -EINVAL;
- + }
- + break;
- + }
- + up_read(&fsg->filesem);
- +
- + if (reply == -EINTR || signal_pending(current))
- + return -EINTR;
- +
- + /* Set up the single reply buffer for finish_reply() */
- + if (reply == -EINVAL)
- + reply = 0; // Error reply length
- + if (reply >= 0 && fsg->data_dir == DATA_DIR_TO_HOST) {
- + reply = min((u32) reply, fsg->data_size_from_cmnd);
- + bh->inreq->length = reply;
- + bh->state = BUF_STATE_FULL;
- + fsg->residue -= reply;
- + } // Otherwise it's already set
- +
- + return 0;
- +}
- +
- +
- +/*-------------------------------------------------------------------------*/
- +
- +static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
- +{
- + struct usb_request *req = bh->outreq;
- + struct bulk_cb_wrap *cbw = req->buf;
- +
- + /* Was this a real packet? Should it be ignored? */
- + if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags))
- + return -EINVAL;
- +
- + /* Is the CBW valid? */
- + if (req->actual != US_BULK_CB_WRAP_LEN ||
- + cbw->Signature != cpu_to_le32(
- + US_BULK_CB_SIGN)) {
- + DBG(fsg, "invalid CBW: len %u sig 0x%x\n",
- + req->actual,
- + le32_to_cpu(cbw->Signature));
- +
- + /* The Bulk-only spec says we MUST stall the IN endpoint
- + * (6.6.1), so it's unavoidable. It also says we must
- + * retain this state until the next reset, but there's
- + * no way to tell the controller driver it should ignore
- + * Clear-Feature(HALT) requests.
- + *
- + * We aren't required to halt the OUT endpoint; instead
- + * we can simply accept and discard any data received
- + * until the next reset. */
- + wedge_bulk_in_endpoint(fsg);
- + set_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
- + return -EINVAL;
- + }
- +
- + /* Is the CBW meaningful? */
- + if (cbw->Lun >= FSG_MAX_LUNS || cbw->Flags & ~US_BULK_FLAG_IN ||
- + cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) {
- + DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, "
- + "cmdlen %u\n",
- + cbw->Lun, cbw->Flags, cbw->Length);
- +
- + /* We can do anything we want here, so let's stall the
- + * bulk pipes if we are allowed to. */
- + if (mod_data.can_stall) {
- + fsg_set_halt(fsg, fsg->bulk_out);
- + halt_bulk_in_endpoint(fsg);
- + }
- + return -EINVAL;
- + }
- +
- + /* Save the command for later */
- + fsg->cmnd_size = cbw->Length;
- + memcpy(fsg->cmnd, cbw->CDB, fsg->cmnd_size);
- + if (cbw->Flags & US_BULK_FLAG_IN)
- + fsg->data_dir = DATA_DIR_TO_HOST;
- + else
- + fsg->data_dir = DATA_DIR_FROM_HOST;
- + fsg->data_size = le32_to_cpu(cbw->DataTransferLength);
- + if (fsg->data_size == 0)
- + fsg->data_dir = DATA_DIR_NONE;
- + fsg->lun = cbw->Lun;
- + fsg->tag = cbw->Tag;
- + return 0;
- +}
- +
- +
- +static int get_next_command(struct fsg_dev *fsg)
- +{
- + struct fsg_buffhd *bh;
- + int rc = 0;
- +
- + if (transport_is_bbb()) {
- +
- + /* Wait for the next buffer to become available */
- + bh = fsg->next_buffhd_to_fill;
- + while (bh->state != BUF_STATE_EMPTY) {
- + rc = sleep_thread(fsg);
- + if (rc)
- + return rc;
- + }
- +
- + /* Queue a request to read a Bulk-only CBW */
- + set_bulk_out_req_length(fsg, bh, US_BULK_CB_WRAP_LEN);
- + start_transfer(fsg, fsg->bulk_out, bh->outreq,
- + &bh->outreq_busy, &bh->state);
- +
- + /* We will drain the buffer in software, which means we
- + * can reuse it for the next filling. No need to advance
- + * next_buffhd_to_fill. */
- +
- + /* Wait for the CBW to arrive */
- + while (bh->state != BUF_STATE_FULL) {
- + rc = sleep_thread(fsg);
- + if (rc)
- + return rc;
- + }
- + smp_rmb();
- + rc = received_cbw(fsg, bh);
- + bh->state = BUF_STATE_EMPTY;
- +
- + } else { // USB_PR_CB or USB_PR_CBI
- +
- + /* Wait for the next command to arrive */
- + while (fsg->cbbuf_cmnd_size == 0) {
- + rc = sleep_thread(fsg);
- + if (rc)
- + return rc;
- + }
- +
- + /* Is the previous status interrupt request still busy?
- + * The host is allowed to skip reading the status,
- + * so we must cancel it. */
- + if (fsg->intreq_busy)
- + usb_ep_dequeue(fsg->intr_in, fsg->intreq);
- +
- + /* Copy the command and mark the buffer empty */
- + fsg->data_dir = DATA_DIR_UNKNOWN;
- + spin_lock_irq(&fsg->lock);
- + fsg->cmnd_size = fsg->cbbuf_cmnd_size;
- + memcpy(fsg->cmnd, fsg->cbbuf_cmnd, fsg->cmnd_size);
- + fsg->cbbuf_cmnd_size = 0;
- + spin_unlock_irq(&fsg->lock);
- +
- + /* Use LUN from the command */
- + fsg->lun = fsg->cmnd[1] >> 5;
- + }
- +
- + /* Update current lun */
- + if (fsg->lun >= 0 && fsg->lun < fsg->nluns)
- + fsg->curlun = &fsg->luns[fsg->lun];
- + else
- + fsg->curlun = NULL;
- +
- + return rc;
- +}
- +
- +
- +/*-------------------------------------------------------------------------*/
- +
- +static int enable_endpoint(struct fsg_dev *fsg, struct usb_ep *ep,
- + const struct usb_endpoint_descriptor *d)
- +{
- + int rc;
- +
- + ep->driver_data = fsg;
- + ep->desc = d;
- + rc = usb_ep_enable(ep);
- + if (rc)
- + ERROR(fsg, "can't enable %s, result %d\n", ep->name, rc);
- + return rc;
- +}
- +
- +static int alloc_request(struct fsg_dev *fsg, struct usb_ep *ep,
- + struct usb_request **preq)
- +{
- + *preq = usb_ep_alloc_request(ep, GFP_ATOMIC);
- + if (*preq)
- + return 0;
- + ERROR(fsg, "can't allocate request for %s\n", ep->name);
- + return -ENOMEM;
- +}
- +
- +/*
- + * Reset interface setting and re-init endpoint state (toggle etc).
- + * Call with altsetting < 0 to disable the interface. The only other
- + * available altsetting is 0, which enables the interface.
- + */
- +static int do_set_interface(struct fsg_dev *fsg, int altsetting)
- +{
- + int rc = 0;
- + int i;
- + const struct usb_endpoint_descriptor *d;
- +
- + if (fsg->running)
- + DBG(fsg, "reset interface\n");
- +
- +reset:
- + /* Deallocate the requests */
- + for (i = 0; i < fsg_num_buffers; ++i) {
- + struct fsg_buffhd *bh = &fsg->buffhds[i];
- +
- + if (bh->inreq) {
- + usb_ep_free_request(fsg->bulk_in, bh->inreq);
- + bh->inreq = NULL;
- + }
- + if (bh->outreq) {
- + usb_ep_free_request(fsg->bulk_out, bh->outreq);
- + bh->outreq = NULL;
- + }
- + }
- + if (fsg->intreq) {
- + usb_ep_free_request(fsg->intr_in, fsg->intreq);
- + fsg->intreq = NULL;
- + }
- +
- + /* Disable the endpoints */
- + if (fsg->bulk_in_enabled) {
- + usb_ep_disable(fsg->bulk_in);
- + fsg->bulk_in_enabled = 0;
- + }
- + if (fsg->bulk_out_enabled) {
- + usb_ep_disable(fsg->bulk_out);
- + fsg->bulk_out_enabled = 0;
- + }
- + if (fsg->intr_in_enabled) {
- + usb_ep_disable(fsg->intr_in);
- + fsg->intr_in_enabled = 0;
- + }
- +
- + fsg->running = 0;
- + if (altsetting < 0 || rc != 0)
- + return rc;
- +
- + DBG(fsg, "set interface %d\n", altsetting);
- +
- + /* Enable the endpoints */
- + d = fsg_ep_desc(fsg->gadget,
- + &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc,
- + &fsg_ss_bulk_in_desc);
- + if ((rc = enable_endpoint(fsg, fsg->bulk_in, d)) != 0)
- + goto reset;
- + fsg->bulk_in_enabled = 1;
- +
- + d = fsg_ep_desc(fsg->gadget,
- + &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc,
- + &fsg_ss_bulk_out_desc);
- + if ((rc = enable_endpoint(fsg, fsg->bulk_out, d)) != 0)
- + goto reset;
- + fsg->bulk_out_enabled = 1;
- + fsg->bulk_out_maxpacket = usb_endpoint_maxp(d);
- + clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
- +
- + if (transport_is_cbi()) {
- + d = fsg_ep_desc(fsg->gadget,
- + &fsg_fs_intr_in_desc, &fsg_hs_intr_in_desc,
- + &fsg_ss_intr_in_desc);
- + if ((rc = enable_endpoint(fsg, fsg->intr_in, d)) != 0)
- + goto reset;
- + fsg->intr_in_enabled = 1;
- + }
- +
- + /* Allocate the requests */
- + for (i = 0; i < fsg_num_buffers; ++i) {
- + struct fsg_buffhd *bh = &fsg->buffhds[i];
- +
- + if ((rc = alloc_request(fsg, fsg->bulk_in, &bh->inreq)) != 0)
- + goto reset;
- + if ((rc = alloc_request(fsg, fsg->bulk_out, &bh->outreq)) != 0)
- + goto reset;
- + bh->inreq->buf = bh->outreq->buf = bh->buf;
- + bh->inreq->context = bh->outreq->context = bh;
- + bh->inreq->complete = bulk_in_complete;
- + bh->outreq->complete = bulk_out_complete;
- + }
- + if (transport_is_cbi()) {
- + if ((rc = alloc_request(fsg, fsg->intr_in, &fsg->intreq)) != 0)
- + goto reset;
- + fsg->intreq->complete = intr_in_complete;
- + }
- +
- + fsg->running = 1;
- + for (i = 0; i < fsg->nluns; ++i)
- + fsg->luns[i].unit_attention_data = SS_RESET_OCCURRED;
- + return rc;
- +}
- +
- +
- +/*
- + * Change our operational configuration. This code must agree with the code
- + * that returns config descriptors, and with interface altsetting code.
- + *
- + * It's also responsible for power management interactions. Some
- + * configurations might not work with our current power sources.
- + * For now we just assume the gadget is always self-powered.
- + */
- +static int do_set_config(struct fsg_dev *fsg, u8 new_config)
- +{
- + int rc = 0;
- +
- + /* Disable the single interface */
- + if (fsg->config != 0) {
- + DBG(fsg, "reset config\n");
- + fsg->config = 0;
- + rc = do_set_interface(fsg, -1);
- + }
- +
- + /* Enable the interface */
- + if (new_config != 0) {
- + fsg->config = new_config;
- + if ((rc = do_set_interface(fsg, 0)) != 0)
- + fsg->config = 0; // Reset on errors
- + else
- + INFO(fsg, "%s config #%d\n",
- + usb_speed_string(fsg->gadget->speed),
- + fsg->config);
- + }
- + return rc;
- +}
- +
- +
- +/*-------------------------------------------------------------------------*/
- +
- +static void handle_exception(struct fsg_dev *fsg)
- +{
- + siginfo_t info;
- + int sig;
- + int i;
- + int num_active;
- + struct fsg_buffhd *bh;
- + enum fsg_state old_state;
- + u8 new_config;
- + struct fsg_lun *curlun;
- + unsigned int exception_req_tag;
- + int rc;
- +
- + /* Clear the existing signals. Anything but SIGUSR1 is converted
- + * into a high-priority EXIT exception. */
- + for (;;) {
- + sig = dequeue_signal_lock(current, ¤t->blocked, &info);
- + if (!sig)
- + break;
- + if (sig != SIGUSR1) {
- + if (fsg->state < FSG_STATE_EXIT)
- + DBG(fsg, "Main thread exiting on signal\n");
- + raise_exception(fsg, FSG_STATE_EXIT);
- + }
- + }
- +
- + /* Cancel all the pending transfers */
- + if (fsg->intreq_busy)
- + usb_ep_dequeue(fsg->intr_in, fsg->intreq);
- + for (i = 0; i < fsg_num_buffers; ++i) {
- + bh = &fsg->buffhds[i];
- + if (bh->inreq_busy)
- + usb_ep_dequeue(fsg->bulk_in, bh->inreq);
- + if (bh->outreq_busy)
- + usb_ep_dequeue(fsg->bulk_out, bh->outreq);
- + }
- +
- + /* Wait until everything is idle */
- + for (;;) {
- + num_active = fsg->intreq_busy;
- + for (i = 0; i < fsg_num_buffers; ++i) {
- + bh = &fsg->buffhds[i];
- + num_active += bh->inreq_busy + bh->outreq_busy;
- + }
- + if (num_active == 0)
- + break;
- + if (sleep_thread(fsg))
- + return;
- + }
- +
- + /* Clear out the controller's fifos */
- + if (fsg->bulk_in_enabled)
- + usb_ep_fifo_flush(fsg->bulk_in);
- + if (fsg->bulk_out_enabled)
- + usb_ep_fifo_flush(fsg->bulk_out);
- + if (fsg->intr_in_enabled)
- + usb_ep_fifo_flush(fsg->intr_in);
- +
- + /* Reset the I/O buffer states and pointers, the SCSI
- + * state, and the exception. Then invoke the handler. */
- + spin_lock_irq(&fsg->lock);
- +
- + for (i = 0; i < fsg_num_buffers; ++i) {
- + bh = &fsg->buffhds[i];
- + bh->state = BUF_STATE_EMPTY;
- + }
- + fsg->next_buffhd_to_fill = fsg->next_buffhd_to_drain =
- + &fsg->buffhds[0];
- +
- + exception_req_tag = fsg->exception_req_tag;
- + new_config = fsg->new_config;
- + old_state = fsg->state;
- +
- + if (old_state == FSG_STATE_ABORT_BULK_OUT)
- + fsg->state = FSG_STATE_STATUS_PHASE;
- + else {
- + for (i = 0; i < fsg->nluns; ++i) {
- + curlun = &fsg->luns[i];
- + curlun->prevent_medium_removal = 0;
- + curlun->sense_data = curlun->unit_attention_data =
- + SS_NO_SENSE;
- + curlun->sense_data_info = 0;
- + curlun->info_valid = 0;
- + }
- + fsg->state = FSG_STATE_IDLE;
- + }
- + spin_unlock_irq(&fsg->lock);
- +
- + /* Carry out any extra actions required for the exception */
- + switch (old_state) {
- + default:
- + break;
- +
- + case FSG_STATE_ABORT_BULK_OUT:
- + send_status(fsg);
- + spin_lock_irq(&fsg->lock);
- + if (fsg->state == FSG_STATE_STATUS_PHASE)
- + fsg->state = FSG_STATE_IDLE;
- + spin_unlock_irq(&fsg->lock);
- + break;
- +
- + case FSG_STATE_RESET:
- + /* In case we were forced against our will to halt a
- + * bulk endpoint, clear the halt now. (The SuperH UDC
- + * requires this.) */
- + if (test_and_clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags))
- + usb_ep_clear_halt(fsg->bulk_in);
- +
- + if (transport_is_bbb()) {
- + if (fsg->ep0_req_tag == exception_req_tag)
- + ep0_queue(fsg); // Complete the status stage
- +
- + } else if (transport_is_cbi())
- + send_status(fsg); // Status by interrupt pipe
- +
- + /* Technically this should go here, but it would only be
- + * a waste of time. Ditto for the INTERFACE_CHANGE and
- + * CONFIG_CHANGE cases. */
- + // for (i = 0; i < fsg->nluns; ++i)
- + // fsg->luns[i].unit_attention_data = SS_RESET_OCCURRED;
- + break;
- +
- + case FSG_STATE_INTERFACE_CHANGE:
- + rc = do_set_interface(fsg, 0);
- + if (fsg->ep0_req_tag != exception_req_tag)
- + break;
- + if (rc != 0) // STALL on errors
- + fsg_set_halt(fsg, fsg->ep0);
- + else // Complete the status stage
- + ep0_queue(fsg);
- + break;
- +
- + case FSG_STATE_CONFIG_CHANGE:
- + rc = do_set_config(fsg, new_config);
- + if (fsg->ep0_req_tag != exception_req_tag)
- + break;
- + if (rc != 0) // STALL on errors
- + fsg_set_halt(fsg, fsg->ep0);
- + else // Complete the status stage
- + ep0_queue(fsg);
- + break;
- +
- + case FSG_STATE_DISCONNECT:
- + for (i = 0; i < fsg->nluns; ++i)
- + fsg_lun_fsync_sub(fsg->luns + i);
- + do_set_config(fsg, 0); // Unconfigured state
- + break;
- +
- + case FSG_STATE_EXIT:
- + case FSG_STATE_TERMINATED:
- + do_set_config(fsg, 0); // Free resources
- + spin_lock_irq(&fsg->lock);
- + fsg->state = FSG_STATE_TERMINATED; // Stop the thread
- + spin_unlock_irq(&fsg->lock);
- + break;
- + }
- +}
- +
- +
- +/*-------------------------------------------------------------------------*/
- +
- +static int fsg_main_thread(void *fsg_)
- +{
- + struct fsg_dev *fsg = fsg_;
- +
- + /* Allow the thread to be killed by a signal, but set the signal mask
- + * to block everything but INT, TERM, KILL, and USR1. */
- + allow_signal(SIGINT);
- + allow_signal(SIGTERM);
- + allow_signal(SIGKILL);
- + allow_signal(SIGUSR1);
- +
- + /* Allow the thread to be frozen */
- + set_freezable();
- +
- + /* Arrange for userspace references to be interpreted as kernel
- + * pointers. That way we can pass a kernel pointer to a routine
- + * that expects a __user pointer and it will work okay. */
- + set_fs(get_ds());
- +
- + /* The main loop */
- + while (fsg->state != FSG_STATE_TERMINATED) {
- + if (exception_in_progress(fsg) || signal_pending(current)) {
- + handle_exception(fsg);
- + continue;
- + }
- +
- + if (!fsg->running) {
- + sleep_thread(fsg);
- + continue;
- + }
- +
- + if (get_next_command(fsg))
- + continue;
- +
- + spin_lock_irq(&fsg->lock);
- + if (!exception_in_progress(fsg))
- + fsg->state = FSG_STATE_DATA_PHASE;
- + spin_unlock_irq(&fsg->lock);
- +
- + if (do_scsi_command(fsg) || finish_reply(fsg))
- + continue;
- +
- + spin_lock_irq(&fsg->lock);
- + if (!exception_in_progress(fsg))
- + fsg->state = FSG_STATE_STATUS_PHASE;
- + spin_unlock_irq(&fsg->lock);
- +
- + if (send_status(fsg))
- + continue;
- +
- + spin_lock_irq(&fsg->lock);
- + if (!exception_in_progress(fsg))
- + fsg->state = FSG_STATE_IDLE;
- + spin_unlock_irq(&fsg->lock);
- + }
- +
- + spin_lock_irq(&fsg->lock);
- + fsg->thread_task = NULL;
- + spin_unlock_irq(&fsg->lock);
- +
- + /* If we are exiting because of a signal, unregister the
- + * gadget driver. */
- + if (test_and_clear_bit(REGISTERED, &fsg->atomic_bitflags))
- + usb_gadget_unregister_driver(&fsg_driver);
- +
- + /* Let the unbind and cleanup routines know the thread has exited */
- + kthread_complete_and_exit(&fsg->thread_notifier, 0);
- +}
- +
- +
- +/*-------------------------------------------------------------------------*/
- +
- +
- +/* The write permissions and store_xxx pointers are set in fsg_bind() */
- +static DEVICE_ATTR(ro, 0444, fsg_show_ro, NULL);
- +static DEVICE_ATTR(nofua, 0644, fsg_show_nofua, NULL);
- +static DEVICE_ATTR(file, 0444, fsg_show_file, NULL);
- +
- +
- +/*-------------------------------------------------------------------------*/
- +
- +static void fsg_release(struct kref *ref)
- +{
- + struct fsg_dev *fsg = container_of(ref, struct fsg_dev, ref);
- +
- + kfree(fsg->luns);
- + kfree(fsg);
- +}
- +
- +static void lun_release(struct device *dev)
- +{
- + struct rw_semaphore *filesem = dev_get_drvdata(dev);
- + struct fsg_dev *fsg =
- + container_of(filesem, struct fsg_dev, filesem);
- +
- + kref_put(&fsg->ref, fsg_release);
- +}
- +
- +static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget)
- +{
- + struct fsg_dev *fsg = get_gadget_data(gadget);
- + int i;
- + struct fsg_lun *curlun;
- + struct usb_request *req = fsg->ep0req;
- +
- + DBG(fsg, "unbind\n");
- + clear_bit(REGISTERED, &fsg->atomic_bitflags);
- +
- + /* If the thread isn't already dead, tell it to exit now */
- + if (fsg->state != FSG_STATE_TERMINATED) {
- + raise_exception(fsg, FSG_STATE_EXIT);
- + wait_for_completion(&fsg->thread_notifier);
- +
- + /* The cleanup routine waits for this completion also */
- + complete(&fsg->thread_notifier);
- + }
- +
- + /* Unregister the sysfs attribute files and the LUNs */
- + for (i = 0; i < fsg->nluns; ++i) {
- + curlun = &fsg->luns[i];
- + if (curlun->registered) {
- + device_remove_file(&curlun->dev, &dev_attr_nofua);
- + device_remove_file(&curlun->dev, &dev_attr_ro);
- + device_remove_file(&curlun->dev, &dev_attr_file);
- + fsg_lun_close(curlun);
- + device_unregister(&curlun->dev);
- + curlun->registered = 0;
- + }
- + }
- +
- + /* Free the data buffers */
- + for (i = 0; i < fsg_num_buffers; ++i)
- + kfree(fsg->buffhds[i].buf);
- +
- + /* Free the request and buffer for endpoint 0 */
- + if (req) {
- + kfree(req->buf);
- + usb_ep_free_request(fsg->ep0, req);
- + }
- +
- + set_gadget_data(gadget, NULL);
- +}
- +
- +
- +static int __init check_parameters(struct fsg_dev *fsg)
- +{
- + int prot;
- + int gcnum;
- +
- + /* Store the default values */
- + mod_data.transport_type = USB_PR_BULK;
- + mod_data.transport_name = "Bulk-only";
- + mod_data.protocol_type = USB_SC_SCSI;
- + mod_data.protocol_name = "Transparent SCSI";
- +
- + /* Some peripheral controllers are known not to be able to
- + * halt bulk endpoints correctly. If one of them is present,
- + * disable stalls.
- + */
- + if (gadget_is_at91(fsg->gadget))
- + mod_data.can_stall = 0;
- +
- + if (mod_data.release == 0xffff) { // Parameter wasn't set
- + gcnum = usb_gadget_controller_number(fsg->gadget);
- + if (gcnum >= 0)
- + mod_data.release = 0x0300 + gcnum;
- + else {
- + WARNING(fsg, "controller '%s' not recognized\n",
- + fsg->gadget->name);
- + mod_data.release = 0x0399;
- + }
- + }
- +
- + prot = simple_strtol(mod_data.protocol_parm, NULL, 0);
- +
- +#ifdef CONFIG_USB_FILE_STORAGE_TEST
- + if (strnicmp(mod_data.transport_parm, "BBB", 10) == 0) {
- + ; // Use default setting
- + } else if (strnicmp(mod_data.transport_parm, "CB", 10) == 0) {
- + mod_data.transport_type = USB_PR_CB;
- + mod_data.transport_name = "Control-Bulk";
- + } else if (strnicmp(mod_data.transport_parm, "CBI", 10) == 0) {
- + mod_data.transport_type = USB_PR_CBI;
- + mod_data.transport_name = "Control-Bulk-Interrupt";
- + } else {
- + ERROR(fsg, "invalid transport: %s\n", mod_data.transport_parm);
- + return -EINVAL;
- + }
- +
- + if (strnicmp(mod_data.protocol_parm, "SCSI", 10) == 0 ||
- + prot == USB_SC_SCSI) {
- + ; // Use default setting
- + } else if (strnicmp(mod_data.protocol_parm, "RBC", 10) == 0 ||
- + prot == USB_SC_RBC) {
- + mod_data.protocol_type = USB_SC_RBC;
- + mod_data.protocol_name = "RBC";
- + } else if (strnicmp(mod_data.protocol_parm, "8020", 4) == 0 ||
- + strnicmp(mod_data.protocol_parm, "ATAPI", 10) == 0 ||
- + prot == USB_SC_8020) {
- + mod_data.protocol_type = USB_SC_8020;
- + mod_data.protocol_name = "8020i (ATAPI)";
- + } else if (strnicmp(mod_data.protocol_parm, "QIC", 3) == 0 ||
- + prot == USB_SC_QIC) {
- + mod_data.protocol_type = USB_SC_QIC;
- + mod_data.protocol_name = "QIC-157";
- + } else if (strnicmp(mod_data.protocol_parm, "UFI", 10) == 0 ||
- + prot == USB_SC_UFI) {
- + mod_data.protocol_type = USB_SC_UFI;
- + mod_data.protocol_name = "UFI";
- + } else if (strnicmp(mod_data.protocol_parm, "8070", 4) == 0 ||
- + prot == USB_SC_8070) {
- + mod_data.protocol_type = USB_SC_8070;
- + mod_data.protocol_name = "8070i";
- + } else {
- + ERROR(fsg, "invalid protocol: %s\n", mod_data.protocol_parm);
- + return -EINVAL;
- + }
- +
- + mod_data.buflen &= PAGE_CACHE_MASK;
- + if (mod_data.buflen <= 0) {
- + ERROR(fsg, "invalid buflen\n");
- + return -ETOOSMALL;
- + }
- +
- +#endif /* CONFIG_USB_FILE_STORAGE_TEST */
- +
- + /* Serial string handling.
- + * On a real device, the serial string would be loaded
- + * from permanent storage. */
- + if (mod_data.serial) {
- + const char *ch;
- + unsigned len = 0;
- +
- + /* Sanity check :
- + * The CB[I] specification limits the serial string to
- + * 12 uppercase hexadecimal characters.
- + * BBB need at least 12 uppercase hexadecimal characters,
- + * with a maximum of 126. */
- + for (ch = mod_data.serial; *ch; ++ch) {
- + ++len;
- + if ((*ch < '0' || *ch > '9') &&
- + (*ch < 'A' || *ch > 'F')) { /* not uppercase hex */
- + WARNING(fsg,
- + "Invalid serial string character: %c\n",
- + *ch);
- + goto no_serial;
- + }
- + }
- + if (len > 126 ||
- + (mod_data.transport_type == USB_PR_BULK && len < 12) ||
- + (mod_data.transport_type != USB_PR_BULK && len > 12)) {
- + WARNING(fsg, "Invalid serial string length!\n");
- + goto no_serial;
- + }
- + fsg_strings[FSG_STRING_SERIAL - 1].s = mod_data.serial;
- + } else {
- + WARNING(fsg, "No serial-number string provided!\n");
- + no_serial:
- + device_desc.iSerialNumber = 0;
- + }
- +
- + return 0;
- +}
- +
- +
- +static int __init fsg_bind(struct usb_gadget *gadget)
- +{
- + struct fsg_dev *fsg = the_fsg;
- + int rc;
- + int i;
- + struct fsg_lun *curlun;
- + struct usb_ep *ep;
- + struct usb_request *req;
- + char *pathbuf, *p;
- +
- + fsg->gadget = gadget;
- + set_gadget_data(gadget, fsg);
- + fsg->ep0 = gadget->ep0;
- + fsg->ep0->driver_data = fsg;
- +
- + if ((rc = check_parameters(fsg)) != 0)
- + goto out;
- +
- + if (mod_data.removable) { // Enable the store_xxx attributes
- + dev_attr_file.attr.mode = 0644;
- + dev_attr_file.store = fsg_store_file;
- + if (!mod_data.cdrom) {
- + dev_attr_ro.attr.mode = 0644;
- + dev_attr_ro.store = fsg_store_ro;
- + }
- + }
- +
- + /* Only for removable media? */
- + dev_attr_nofua.attr.mode = 0644;
- + dev_attr_nofua.store = fsg_store_nofua;
- +
- + /* Find out how many LUNs there should be */
- + i = mod_data.nluns;
- + if (i == 0)
- + i = max(mod_data.num_filenames, 1u);
- + if (i > FSG_MAX_LUNS) {
- + ERROR(fsg, "invalid number of LUNs: %d\n", i);
- + rc = -EINVAL;
- + goto out;
- + }
- +
- + /* Create the LUNs, open their backing files, and register the
- + * LUN devices in sysfs. */
- + fsg->luns = kzalloc(i * sizeof(struct fsg_lun), GFP_KERNEL);
- + if (!fsg->luns) {
- + rc = -ENOMEM;
- + goto out;
- + }
- + fsg->nluns = i;
- +
- + for (i = 0; i < fsg->nluns; ++i) {
- + curlun = &fsg->luns[i];
- + curlun->cdrom = !!mod_data.cdrom;
- + curlun->ro = mod_data.cdrom || mod_data.ro[i];
- + curlun->initially_ro = curlun->ro;
- + curlun->removable = mod_data.removable;
- + curlun->nofua = mod_data.nofua[i];
- + curlun->dev.release = lun_release;
- + curlun->dev.parent = &gadget->dev;
- + curlun->dev.driver = &fsg_driver.driver;
- + dev_set_drvdata(&curlun->dev, &fsg->filesem);
- + dev_set_name(&curlun->dev,"%s-lun%d",
- + dev_name(&gadget->dev), i);
- +
- + kref_get(&fsg->ref);
- + rc = device_register(&curlun->dev);
- + if (rc) {
- + INFO(fsg, "failed to register LUN%d: %d\n", i, rc);
- + put_device(&curlun->dev);
- + goto out;
- + }
- + curlun->registered = 1;
- +
- + rc = device_create_file(&curlun->dev, &dev_attr_ro);
- + if (rc)
- + goto out;
- + rc = device_create_file(&curlun->dev, &dev_attr_nofua);
- + if (rc)
- + goto out;
- + rc = device_create_file(&curlun->dev, &dev_attr_file);
- + if (rc)
- + goto out;
- +
- + if (mod_data.file[i] && *mod_data.file[i]) {
- + rc = fsg_lun_open(curlun, mod_data.file[i]);
- + if (rc)
- + goto out;
- + } else if (!mod_data.removable) {
- + ERROR(fsg, "no file given for LUN%d\n", i);
- + rc = -EINVAL;
- + goto out;
- + }
- + }
- +
- + /* Find all the endpoints we will use */
- + usb_ep_autoconfig_reset(gadget);
- + ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc);
- + if (!ep)
- + goto autoconf_fail;
- + ep->driver_data = fsg; // claim the endpoint
- + fsg->bulk_in = ep;
- +
- + ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc);
- + if (!ep)
- + goto autoconf_fail;
- + ep->driver_data = fsg; // claim the endpoint
- + fsg->bulk_out = ep;
- +
- + if (transport_is_cbi()) {
- + ep = usb_ep_autoconfig(gadget, &fsg_fs_intr_in_desc);
- + if (!ep)
- + goto autoconf_fail;
- + ep->driver_data = fsg; // claim the endpoint
- + fsg->intr_in = ep;
- + }
- +
- + /* Fix up the descriptors */
- + device_desc.idVendor = cpu_to_le16(mod_data.vendor);
- + device_desc.idProduct = cpu_to_le16(mod_data.product);
- + device_desc.bcdDevice = cpu_to_le16(mod_data.release);
- +
- + i = (transport_is_cbi() ? 3 : 2); // Number of endpoints
- + fsg_intf_desc.bNumEndpoints = i;
- + fsg_intf_desc.bInterfaceSubClass = mod_data.protocol_type;
- + fsg_intf_desc.bInterfaceProtocol = mod_data.transport_type;
- + fsg_fs_function[i + FSG_FS_FUNCTION_PRE_EP_ENTRIES] = NULL;
- +
- + if (gadget_is_dualspeed(gadget)) {
- + fsg_hs_function[i + FSG_HS_FUNCTION_PRE_EP_ENTRIES] = NULL;
- +
- + /* Assume endpoint addresses are the same for both speeds */
- + fsg_hs_bulk_in_desc.bEndpointAddress =
- + fsg_fs_bulk_in_desc.bEndpointAddress;
- + fsg_hs_bulk_out_desc.bEndpointAddress =
- + fsg_fs_bulk_out_desc.bEndpointAddress;
- + fsg_hs_intr_in_desc.bEndpointAddress =
- + fsg_fs_intr_in_desc.bEndpointAddress;
- + }
- +
- + if (gadget_is_superspeed(gadget)) {
- + unsigned max_burst;
- +
- + fsg_ss_function[i + FSG_SS_FUNCTION_PRE_EP_ENTRIES] = NULL;
- +
- + /* Calculate bMaxBurst, we know packet size is 1024 */
- + max_burst = min_t(unsigned, mod_data.buflen / 1024, 15);
- +
- + /* Assume endpoint addresses are the same for both speeds */
- + fsg_ss_bulk_in_desc.bEndpointAddress =
- + fsg_fs_bulk_in_desc.bEndpointAddress;
- + fsg_ss_bulk_in_comp_desc.bMaxBurst = max_burst;
- +
- + fsg_ss_bulk_out_desc.bEndpointAddress =
- + fsg_fs_bulk_out_desc.bEndpointAddress;
- + fsg_ss_bulk_out_comp_desc.bMaxBurst = max_burst;
- + }
- +
- + if (gadget_is_otg(gadget))
- + fsg_otg_desc.bmAttributes |= USB_OTG_HNP;
- +
- + rc = -ENOMEM;
- +
- + /* Allocate the request and buffer for endpoint 0 */
- + fsg->ep0req = req = usb_ep_alloc_request(fsg->ep0, GFP_KERNEL);
- + if (!req)
- + goto out;
- + req->buf = kmalloc(EP0_BUFSIZE, GFP_KERNEL);
- + if (!req->buf)
- + goto out;
- + req->complete = ep0_complete;
- +
- + /* Allocate the data buffers */
- + for (i = 0; i < fsg_num_buffers; ++i) {
- + struct fsg_buffhd *bh = &fsg->buffhds[i];
- +
- + /* Allocate for the bulk-in endpoint. We assume that
- + * the buffer will also work with the bulk-out (and
- + * interrupt-in) endpoint. */
- + bh->buf = kmalloc(mod_data.buflen, GFP_KERNEL);
- + if (!bh->buf)
- + goto out;
- + bh->next = bh + 1;
- + }
- + fsg->buffhds[fsg_num_buffers - 1].next = &fsg->buffhds[0];
- +
- + /* This should reflect the actual gadget power source */
- + usb_gadget_set_selfpowered(gadget);
- +
- + snprintf(fsg_string_manufacturer, sizeof fsg_string_manufacturer,
- + "%s %s with %s",
- + init_utsname()->sysname, init_utsname()->release,
- + gadget->name);
- +
- + fsg->thread_task = kthread_create(fsg_main_thread, fsg,
- + "file-storage-gadget");
- + if (IS_ERR(fsg->thread_task)) {
- + rc = PTR_ERR(fsg->thread_task);
- + goto out;
- + }
- +
- + INFO(fsg, DRIVER_DESC ", version: " DRIVER_VERSION "\n");
- + INFO(fsg, "NOTE: This driver is deprecated. "
- + "Consider using g_mass_storage instead.\n");
- + INFO(fsg, "Number of LUNs=%d\n", fsg->nluns);
- +
- + pathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
- + for (i = 0; i < fsg->nluns; ++i) {
- + curlun = &fsg->luns[i];
- + if (fsg_lun_is_open(curlun)) {
- + p = NULL;
- + if (pathbuf) {
- + p = d_path(&curlun->filp->f_path,
- + pathbuf, PATH_MAX);
- + if (IS_ERR(p))
- + p = NULL;
- + }
- + LINFO(curlun, "ro=%d, nofua=%d, file: %s\n",
- + curlun->ro, curlun->nofua, (p ? p : "(error)"));
- + }
- + }
- + kfree(pathbuf);
- +
- + DBG(fsg, "transport=%s (x%02x)\n",
- + mod_data.transport_name, mod_data.transport_type);
- + DBG(fsg, "protocol=%s (x%02x)\n",
- + mod_data.protocol_name, mod_data.protocol_type);
- + DBG(fsg, "VendorID=x%04x, ProductID=x%04x, Release=x%04x\n",
- + mod_data.vendor, mod_data.product, mod_data.release);
- + DBG(fsg, "removable=%d, stall=%d, cdrom=%d, buflen=%u\n",
- + mod_data.removable, mod_data.can_stall,
- + mod_data.cdrom, mod_data.buflen);
- + DBG(fsg, "I/O thread pid: %d\n", task_pid_nr(fsg->thread_task));
- +
- + set_bit(REGISTERED, &fsg->atomic_bitflags);
- +
- + /* Tell the thread to start working */
- + wake_up_process(fsg->thread_task);
- + return 0;
- +
- +autoconf_fail:
- + ERROR(fsg, "unable to autoconfigure all endpoints\n");
- + rc = -ENOTSUPP;
- +
- +out:
- + fsg->state = FSG_STATE_TERMINATED; // The thread is dead
- + fsg_unbind(gadget);
- + complete(&fsg->thread_notifier);
- + return rc;
- +}
- +
- +
- +/*-------------------------------------------------------------------------*/
- +
- +static void fsg_suspend(struct usb_gadget *gadget)
- +{
- + struct fsg_dev *fsg = get_gadget_data(gadget);
- +
- + DBG(fsg, "suspend\n");
- + set_bit(SUSPENDED, &fsg->atomic_bitflags);
- +}
- +
- +static void fsg_resume(struct usb_gadget *gadget)
- +{
- + struct fsg_dev *fsg = get_gadget_data(gadget);
- +
- + DBG(fsg, "resume\n");
- + clear_bit(SUSPENDED, &fsg->atomic_bitflags);
- +}
- +
- +
- +/*-------------------------------------------------------------------------*/
- +
- +static struct usb_gadget_driver fsg_driver = {
- + .max_speed = USB_SPEED_SUPER,
- + .function = (char *) fsg_string_product,
- + .unbind = fsg_unbind,
- + .disconnect = fsg_disconnect,
- + .setup = fsg_setup,
- + .suspend = fsg_suspend,
- + .resume = fsg_resume,
- +
- + .driver = {
- + .name = DRIVER_NAME,
- + .owner = THIS_MODULE,
- + // .release = ...
- + // .suspend = ...
- + // .resume = ...
- + },
- +};
- +
- +
- +static int __init fsg_alloc(void)
- +{
- + struct fsg_dev *fsg;
- +
- + fsg = kzalloc(sizeof *fsg +
- + fsg_num_buffers * sizeof *(fsg->buffhds), GFP_KERNEL);
- +
- + if (!fsg)
- + return -ENOMEM;
- + spin_lock_init(&fsg->lock);
- + init_rwsem(&fsg->filesem);
- + kref_init(&fsg->ref);
- + init_completion(&fsg->thread_notifier);
- +
- + the_fsg = fsg;
- + return 0;
- +}
- +
- +
- +static int __init fsg_init(void)
- +{
- + int rc;
- + struct fsg_dev *fsg;
- +
- + rc = fsg_num_buffers_validate();
- + if (rc != 0)
- + return rc;
- +
- + if ((rc = fsg_alloc()) != 0)
- + return rc;
- + fsg = the_fsg;
- + if ((rc = usb_gadget_probe_driver(&fsg_driver, fsg_bind)) != 0)
- + kref_put(&fsg->ref, fsg_release);
- + return rc;
- +}
- +module_init(fsg_init);
- +
- +
- +static void __exit fsg_cleanup(void)
- +{
- + struct fsg_dev *fsg = the_fsg;
- +
- + /* Unregister the driver iff the thread hasn't already done so */
- + if (test_and_clear_bit(REGISTERED, &fsg->atomic_bitflags))
- + usb_gadget_unregister_driver(&fsg_driver);
- +
- + /* Wait for the thread to finish up */
- + wait_for_completion(&fsg->thread_notifier);
- +
- + kref_put(&fsg->ref, fsg_release);
- +}
- +module_exit(fsg_cleanup);
- --- a/drivers/usb/host/Kconfig
- +++ b/drivers/usb/host/Kconfig
- @@ -742,6 +742,16 @@ config USB_RENESAS_USBHS_HCD
- To compile this driver as a module, choose M here: the
- module will be called renesas-usbhs.
-
- +config USB_DWCOTG
- + bool "Synopsis DWC host support"
- + depends on USB && (FIQ || ARM64)
- + help
- + The Synopsis DWC controller is a dual-role
- + host/peripheral/OTG ("On The Go") USB controllers.
- +
- + Enable this option to support this IP in host controller mode.
- + If unsure, say N.
- +
- config USB_HCD_BCMA
- tristate "BCMA usb host driver"
- depends on BCMA
- --- a/drivers/usb/host/Makefile
- +++ b/drivers/usb/host/Makefile
- @@ -78,6 +78,7 @@ obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd
- obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o
- obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o
- obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o
- +obj-$(CONFIG_USB_DWCOTG) += dwc_otg/ dwc_common_port/
- obj-$(CONFIG_USB_FSL_USB2) += fsl-mph-dr-of.o
- obj-$(CONFIG_USB_EHCI_FSL) += fsl-mph-dr-of.o
- obj-$(CONFIG_USB_EHCI_FSL) += ehci-fsl.o
- --- /dev/null
- +++ b/drivers/usb/host/dwc_common_port/Makefile
- @@ -0,0 +1,58 @@
- +#
- +# Makefile for DWC_common library
- +#
- +
- +ifneq ($(KERNELRELEASE),)
- +
- +ccflags-y += -DDWC_LINUX
- +#ccflags-y += -DDEBUG
- +#ccflags-y += -DDWC_DEBUG_REGS
- +#ccflags-y += -DDWC_DEBUG_MEMORY
- +
- +ccflags-y += -DDWC_LIBMODULE
- +ccflags-y += -DDWC_CCLIB
- +#ccflags-y += -DDWC_CRYPTOLIB
- +ccflags-y += -DDWC_NOTIFYLIB
- +ccflags-y += -DDWC_UTFLIB
- +
- +obj-$(CONFIG_USB_DWCOTG) += dwc_common_port_lib.o
- +dwc_common_port_lib-objs := dwc_cc.o dwc_modpow.o dwc_dh.o \
- + dwc_crypto.o dwc_notifier.o \
- + dwc_common_linux.o dwc_mem.o
- +
- +kernrelwd := $(subst ., ,$(KERNELRELEASE))
- +kernrel3 := $(word 1,$(kernrelwd)).$(word 2,$(kernrelwd)).$(word 3,$(kernrelwd))
- +
- +ifneq ($(kernrel3),2.6.20)
- +# grayg - I only know that we use ccflags-y in 2.6.31 actually
- +ccflags-y += $(CPPFLAGS)
- +endif
- +
- +else
- +
- +#ifeq ($(KDIR),)
- +#$(error Must give "KDIR=/path/to/kernel/source" on command line or in environment)
- +#endif
- +
- +ifeq ($(ARCH),)
- +$(error Must give "ARCH=<arch>" on command line or in environment. Also, if \
- + cross-compiling, must give "CROSS_COMPILE=/path/to/compiler/plus/tool-prefix-")
- +endif
- +
- +ifeq ($(DOXYGEN),)
- +DOXYGEN := doxygen
- +endif
- +
- +default:
- + $(MAKE) -C$(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules
- +
- +docs: $(wildcard *.[hc]) doc/doxygen.cfg
- + $(DOXYGEN) doc/doxygen.cfg
- +
- +tags: $(wildcard *.[hc])
- + $(CTAGS) -e $(wildcard *.[hc]) $(wildcard linux/*.[hc]) $(wildcard $(KDIR)/include/linux/usb*.h)
- +
- +endif
- +
- +clean:
- + rm -rf *.o *.ko .*.cmd *.mod.c .*.o.d .*.o.tmp modules.order Module.markers Module.symvers .tmp_versions/
- --- /dev/null
- +++ b/drivers/usb/host/dwc_common_port/Makefile.fbsd
- @@ -0,0 +1,17 @@
- +CFLAGS += -I/sys/i386/compile/GENERIC -I/sys/i386/include -I/usr/include
- +CFLAGS += -DDWC_FREEBSD
- +CFLAGS += -DDEBUG
- +#CFLAGS += -DDWC_DEBUG_REGS
- +#CFLAGS += -DDWC_DEBUG_MEMORY
- +
- +#CFLAGS += -DDWC_LIBMODULE
- +#CFLAGS += -DDWC_CCLIB
- +#CFLAGS += -DDWC_CRYPTOLIB
- +#CFLAGS += -DDWC_NOTIFYLIB
- +#CFLAGS += -DDWC_UTFLIB
- +
- +KMOD = dwc_common_port_lib
- +SRCS = dwc_cc.c dwc_modpow.c dwc_dh.c dwc_crypto.c dwc_notifier.c \
- + dwc_common_fbsd.c dwc_mem.c
- +
- +.include <bsd.kmod.mk>
- --- /dev/null
- +++ b/drivers/usb/host/dwc_common_port/Makefile.linux
- @@ -0,0 +1,49 @@
- +#
- +# Makefile for DWC_common library
- +#
- +ifneq ($(KERNELRELEASE),)
- +
- +ccflags-y += -DDWC_LINUX
- +#ccflags-y += -DDEBUG
- +#ccflags-y += -DDWC_DEBUG_REGS
- +#ccflags-y += -DDWC_DEBUG_MEMORY
- +
- +ccflags-y += -DDWC_LIBMODULE
- +ccflags-y += -DDWC_CCLIB
- +ccflags-y += -DDWC_CRYPTOLIB
- +ccflags-y += -DDWC_NOTIFYLIB
- +ccflags-y += -DDWC_UTFLIB
- +
- +obj-m := dwc_common_port_lib.o
- +dwc_common_port_lib-objs := dwc_cc.o dwc_modpow.o dwc_dh.o \
- + dwc_crypto.o dwc_notifier.o \
- + dwc_common_linux.o dwc_mem.o
- +
- +else
- +
- +ifeq ($(KDIR),)
- +$(error Must give "KDIR=/path/to/kernel/source" on command line or in environment)
- +endif
- +
- +ifeq ($(ARCH),)
- +$(error Must give "ARCH=<arch>" on command line or in environment. Also, if \
- + cross-compiling, must give "CROSS_COMPILE=/path/to/compiler/plus/tool-prefix-")
- +endif
- +
- +ifeq ($(DOXYGEN),)
- +DOXYGEN := doxygen
- +endif
- +
- +default:
- + $(MAKE) -C$(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules
- +
- +docs: $(wildcard *.[hc]) doc/doxygen.cfg
- + $(DOXYGEN) doc/doxygen.cfg
- +
- +tags: $(wildcard *.[hc])
- + $(CTAGS) -e $(wildcard *.[hc]) $(wildcard linux/*.[hc]) $(wildcard $(KDIR)/include/linux/usb*.h)
- +
- +endif
- +
- +clean:
- + rm -rf *.o *.ko .*.cmd *.mod.c .*.o.d .*.o.tmp modules.order Module.markers Module.symvers .tmp_versions/
- --- /dev/null
- +++ b/drivers/usb/host/dwc_common_port/changes.txt
- @@ -0,0 +1,174 @@
- +
- +dwc_read_reg32() and friends now take an additional parameter, a pointer to an
- +IO context struct. The IO context struct should live in an os-dependent struct
- +in your driver. As an example, the dwc_usb3 driver has an os-dependent struct
- +named 'os_dep' embedded in the main device struct. So there these calls look
- +like this:
- +
- + dwc_read_reg32(&usb3_dev->os_dep.ioctx, &pcd->dev_global_regs->dcfg);
- +
- + dwc_write_reg32(&usb3_dev->os_dep.ioctx,
- + &pcd->dev_global_regs->dcfg, 0);
- +
- +Note that for the existing Linux driver ports, it is not necessary to actually
- +define the 'ioctx' member in the os-dependent struct. Since Linux does not
- +require an IO context, its macros for dwc_read_reg32() and friends do not
- +use the context pointer, so it is optimized away by the compiler. But it is
- +necessary to add the pointer parameter to all of the call sites, to be ready
- +for any future ports (such as FreeBSD) which do require an IO context.
- +
- +
- +Similarly, dwc_alloc(), dwc_alloc_atomic(), dwc_strdup(), and dwc_free() now
- +take an additional parameter, a pointer to a memory context. Examples:
- +
- + addr = dwc_alloc(&usb3_dev->os_dep.memctx, size);
- +
- + dwc_free(&usb3_dev->os_dep.memctx, addr);
- +
- +Again, for the Linux ports, it is not necessary to actually define the memctx
- +member, but it is necessary to add the pointer parameter to all of the call
- +sites.
- +
- +
- +Same for dwc_dma_alloc() and dwc_dma_free(). Examples:
- +
- + virt_addr = dwc_dma_alloc(&usb3_dev->os_dep.dmactx, size, &phys_addr);
- +
- + dwc_dma_free(&usb3_dev->os_dep.dmactx, size, virt_addr, phys_addr);
- +
- +
- +Same for dwc_mutex_alloc() and dwc_mutex_free(). Examples:
- +
- + mutex = dwc_mutex_alloc(&usb3_dev->os_dep.mtxctx);
- +
- + dwc_mutex_free(&usb3_dev->os_dep.mtxctx, mutex);
- +
- +
- +Same for dwc_spinlock_alloc() and dwc_spinlock_free(). Examples:
- +
- + lock = dwc_spinlock_alloc(&usb3_dev->osdep.splctx);
- +
- + dwc_spinlock_free(&usb3_dev->osdep.splctx, lock);
- +
- +
- +Same for dwc_timer_alloc(). Example:
- +
- + timer = dwc_timer_alloc(&usb3_dev->os_dep.tmrctx, "dwc_usb3_tmr1",
- + cb_func, cb_data);
- +
- +
- +Same for dwc_waitq_alloc(). Example:
- +
- + waitq = dwc_waitq_alloc(&usb3_dev->os_dep.wtqctx);
- +
- +
- +Same for dwc_thread_run(). Example:
- +
- + thread = dwc_thread_run(&usb3_dev->os_dep.thdctx, func,
- + "dwc_usb3_thd1", data);
- +
- +
- +Same for dwc_workq_alloc(). Example:
- +
- + workq = dwc_workq_alloc(&usb3_dev->osdep.wkqctx, "dwc_usb3_wkq1");
- +
- +
- +Same for dwc_task_alloc(). Example:
- +
- + task = dwc_task_alloc(&usb3_dev->os_dep.tskctx, "dwc_usb3_tsk1",
- + cb_func, cb_data);
- +
- +
- +In addition to the context pointer additions, a few core functions have had
- +other changes made to their parameters:
- +
- +The 'flags' parameter to dwc_spinlock_irqsave() and dwc_spinunlock_irqrestore()
- +has been changed from a uint64_t to a dwc_irqflags_t.
- +
- +dwc_thread_should_stop() now takes a 'dwc_thread_t *' parameter, because the
- +FreeBSD equivalent of that function requires it.
- +
- +And, in addition to the context pointer, dwc_task_alloc() also adds a
- +'char *name' parameter, to be consistent with dwc_thread_run() and
- +dwc_workq_alloc(), and because the FreeBSD equivalent of that function
- +requires a unique name.
- +
- +
- +Here is a complete list of the core functions that now take a pointer to a
- +context as their first parameter:
- +
- + dwc_read_reg32
- + dwc_read_reg64
- + dwc_write_reg32
- + dwc_write_reg64
- + dwc_modify_reg32
- + dwc_modify_reg64
- + dwc_alloc
- + dwc_alloc_atomic
- + dwc_strdup
- + dwc_free
- + dwc_dma_alloc
- + dwc_dma_free
- + dwc_mutex_alloc
- + dwc_mutex_free
- + dwc_spinlock_alloc
- + dwc_spinlock_free
- + dwc_timer_alloc
- + dwc_waitq_alloc
- + dwc_thread_run
- + dwc_workq_alloc
- + dwc_task_alloc Also adds a 'char *name' as its 2nd parameter
- +
- +And here are the core functions that have other changes to their parameters:
- +
- + dwc_spinlock_irqsave 'flags' param is now a 'dwc_irqflags_t *'
- + dwc_spinunlock_irqrestore 'flags' param is now a 'dwc_irqflags_t'
- + dwc_thread_should_stop Adds a 'dwc_thread_t *' parameter
- +
- +
- +
- +The changes to the core functions also require some of the other library
- +functions to change:
- +
- + dwc_cc_if_alloc() and dwc_cc_if_free() now take a 'void *memctx'
- + (for memory allocation) as the 1st param and a 'void *mtxctx'
- + (for mutex allocation) as the 2nd param.
- +
- + dwc_cc_clear(), dwc_cc_add(), dwc_cc_change(), dwc_cc_remove(),
- + dwc_cc_data_for_save(), and dwc_cc_restore_from_data() now take a
- + 'void *memctx' as the 1st param.
- +
- + dwc_dh_modpow(), dwc_dh_pk(), and dwc_dh_derive_keys() now take a
- + 'void *memctx' as the 1st param.
- +
- + dwc_modpow() now takes a 'void *memctx' as the 1st param.
- +
- + dwc_alloc_notification_manager() now takes a 'void *memctx' as the
- + 1st param and a 'void *wkqctx' (for work queue allocation) as the 2nd
- + param, and also now returns an integer value that is non-zero if
- + allocation of its data structures or work queue fails.
- +
- + dwc_register_notifier() now takes a 'void *memctx' as the 1st param.
- +
- + dwc_memory_debug_start() now takes a 'void *mem_ctx' as the first
- + param, and also now returns an integer value that is non-zero if
- + allocation of its data structures fails.
- +
- +
- +
- +Other miscellaneous changes:
- +
- +The DEBUG_MEMORY and DEBUG_REGS #define's have been renamed to
- +DWC_DEBUG_MEMORY and DWC_DEBUG_REGS.
- +
- +The following #define's have been added to allow selectively compiling library
- +features:
- +
- + DWC_CCLIB
- + DWC_CRYPTOLIB
- + DWC_NOTIFYLIB
- + DWC_UTFLIB
- +
- +A DWC_LIBMODULE #define has also been added. If this is not defined, then the
- +module code in dwc_common_linux.c is not compiled in. This allows linking the
- +library code directly into a driver module, instead of as a standalone module.
- --- /dev/null
- +++ b/drivers/usb/host/dwc_common_port/doc/doxygen.cfg
- @@ -0,0 +1,270 @@
- +# Doxyfile 1.4.5
- +
- +#---------------------------------------------------------------------------
- +# Project related configuration options
- +#---------------------------------------------------------------------------
- +PROJECT_NAME = "Synopsys DWC Portability and Common Library for UWB"
- +PROJECT_NUMBER =
- +OUTPUT_DIRECTORY = doc
- +CREATE_SUBDIRS = NO
- +OUTPUT_LANGUAGE = English
- +BRIEF_MEMBER_DESC = YES
- +REPEAT_BRIEF = YES
- +ABBREVIATE_BRIEF = "The $name class" \
- + "The $name widget" \
- + "The $name file" \
- + is \
- + provides \
- + specifies \
- + contains \
- + represents \
- + a \
- + an \
- + the
- +ALWAYS_DETAILED_SEC = YES
- +INLINE_INHERITED_MEMB = NO
- +FULL_PATH_NAMES = NO
- +STRIP_FROM_PATH = ..
- +STRIP_FROM_INC_PATH =
- +SHORT_NAMES = NO
- +JAVADOC_AUTOBRIEF = YES
- +MULTILINE_CPP_IS_BRIEF = NO
- +DETAILS_AT_TOP = YES
- +INHERIT_DOCS = YES
- +SEPARATE_MEMBER_PAGES = NO
- +TAB_SIZE = 8
- +ALIASES =
- +OPTIMIZE_OUTPUT_FOR_C = YES
- +OPTIMIZE_OUTPUT_JAVA = NO
- +BUILTIN_STL_SUPPORT = NO
- +DISTRIBUTE_GROUP_DOC = NO
- +SUBGROUPING = NO
- +#---------------------------------------------------------------------------
- +# Build related configuration options
- +#---------------------------------------------------------------------------
- +EXTRACT_ALL = NO
- +EXTRACT_PRIVATE = NO
- +EXTRACT_STATIC = YES
- +EXTRACT_LOCAL_CLASSES = NO
- +EXTRACT_LOCAL_METHODS = NO
- +HIDE_UNDOC_MEMBERS = NO
- +HIDE_UNDOC_CLASSES = NO
- +HIDE_FRIEND_COMPOUNDS = NO
- +HIDE_IN_BODY_DOCS = NO
- +INTERNAL_DOCS = NO
- +CASE_SENSE_NAMES = YES
- +HIDE_SCOPE_NAMES = NO
- +SHOW_INCLUDE_FILES = NO
- +INLINE_INFO = YES
- +SORT_MEMBER_DOCS = NO
- +SORT_BRIEF_DOCS = NO
- +SORT_BY_SCOPE_NAME = NO
- +GENERATE_TODOLIST = YES
- +GENERATE_TESTLIST = YES
- +GENERATE_BUGLIST = YES
- +GENERATE_DEPRECATEDLIST= YES
- +ENABLED_SECTIONS =
- +MAX_INITIALIZER_LINES = 30
- +SHOW_USED_FILES = YES
- +SHOW_DIRECTORIES = YES
- +FILE_VERSION_FILTER =
- +#---------------------------------------------------------------------------
- +# configuration options related to warning and progress messages
- +#---------------------------------------------------------------------------
- +QUIET = YES
- +WARNINGS = YES
- +WARN_IF_UNDOCUMENTED = NO
- +WARN_IF_DOC_ERROR = YES
- +WARN_NO_PARAMDOC = YES
- +WARN_FORMAT = "$file:$line: $text"
- +WARN_LOGFILE =
- +#---------------------------------------------------------------------------
- +# configuration options related to the input files
- +#---------------------------------------------------------------------------
- +INPUT = .
- +FILE_PATTERNS = *.c \
- + *.cc \
- + *.cxx \
- + *.cpp \
- + *.c++ \
- + *.d \
- + *.java \
- + *.ii \
- + *.ixx \
- + *.ipp \
- + *.i++ \
- + *.inl \
- + *.h \
- + *.hh \
- + *.hxx \
- + *.hpp \
- + *.h++ \
- + *.idl \
- + *.odl \
- + *.cs \
- + *.php \
- + *.php3 \
- + *.inc \
- + *.m \
- + *.mm \
- + *.dox \
- + *.py \
- + *.C \
- + *.CC \
- + *.C++ \
- + *.II \
- + *.I++ \
- + *.H \
- + *.HH \
- + *.H++ \
- + *.CS \
- + *.PHP \
- + *.PHP3 \
- + *.M \
- + *.MM \
- + *.PY
- +RECURSIVE = NO
- +EXCLUDE =
- +EXCLUDE_SYMLINKS = NO
- +EXCLUDE_PATTERNS =
- +EXAMPLE_PATH =
- +EXAMPLE_PATTERNS = *
- +EXAMPLE_RECURSIVE = NO
- +IMAGE_PATH =
- +INPUT_FILTER =
- +FILTER_PATTERNS =
- +FILTER_SOURCE_FILES = NO
- +#---------------------------------------------------------------------------
- +# configuration options related to source browsing
- +#---------------------------------------------------------------------------
- +SOURCE_BROWSER = NO
- +INLINE_SOURCES = NO
- +STRIP_CODE_COMMENTS = YES
- +REFERENCED_BY_RELATION = YES
- +REFERENCES_RELATION = YES
- +USE_HTAGS = NO
- +VERBATIM_HEADERS = NO
- +#---------------------------------------------------------------------------
- +# configuration options related to the alphabetical class index
- +#---------------------------------------------------------------------------
- +ALPHABETICAL_INDEX = NO
- +COLS_IN_ALPHA_INDEX = 5
- +IGNORE_PREFIX =
- +#---------------------------------------------------------------------------
- +# configuration options related to the HTML output
- +#---------------------------------------------------------------------------
- +GENERATE_HTML = YES
- +HTML_OUTPUT = html
- +HTML_FILE_EXTENSION = .html
- +HTML_HEADER =
- +HTML_FOOTER =
- +HTML_STYLESHEET =
- +HTML_ALIGN_MEMBERS = YES
- +GENERATE_HTMLHELP = NO
- +CHM_FILE =
- +HHC_LOCATION =
- +GENERATE_CHI = NO
- +BINARY_TOC = NO
- +TOC_EXPAND = NO
- +DISABLE_INDEX = NO
- +ENUM_VALUES_PER_LINE = 4
- +GENERATE_TREEVIEW = YES
- +TREEVIEW_WIDTH = 250
- +#---------------------------------------------------------------------------
- +# configuration options related to the LaTeX output
- +#---------------------------------------------------------------------------
- +GENERATE_LATEX = NO
- +LATEX_OUTPUT = latex
- +LATEX_CMD_NAME = latex
- +MAKEINDEX_CMD_NAME = makeindex
- +COMPACT_LATEX = NO
- +PAPER_TYPE = a4wide
- +EXTRA_PACKAGES =
- +LATEX_HEADER =
- +PDF_HYPERLINKS = NO
- +USE_PDFLATEX = NO
- +LATEX_BATCHMODE = NO
- +LATEX_HIDE_INDICES = NO
- +#---------------------------------------------------------------------------
- +# configuration options related to the RTF output
- +#---------------------------------------------------------------------------
- +GENERATE_RTF = NO
- +RTF_OUTPUT = rtf
- +COMPACT_RTF = NO
- +RTF_HYPERLINKS = NO
- +RTF_STYLESHEET_FILE =
- +RTF_EXTENSIONS_FILE =
- +#---------------------------------------------------------------------------
- +# configuration options related to the man page output
- +#---------------------------------------------------------------------------
- +GENERATE_MAN = NO
- +MAN_OUTPUT = man
- +MAN_EXTENSION = .3
- +MAN_LINKS = NO
- +#---------------------------------------------------------------------------
- +# configuration options related to the XML output
- +#---------------------------------------------------------------------------
- +GENERATE_XML = NO
- +XML_OUTPUT = xml
- +XML_SCHEMA =
- +XML_DTD =
- +XML_PROGRAMLISTING = YES
- +#---------------------------------------------------------------------------
- +# configuration options for the AutoGen Definitions output
- +#---------------------------------------------------------------------------
- +GENERATE_AUTOGEN_DEF = NO
- +#---------------------------------------------------------------------------
- +# configuration options related to the Perl module output
- +#---------------------------------------------------------------------------
- +GENERATE_PERLMOD = NO
- +PERLMOD_LATEX = NO
- +PERLMOD_PRETTY = YES
- +PERLMOD_MAKEVAR_PREFIX =
- +#---------------------------------------------------------------------------
- +# Configuration options related to the preprocessor
- +#---------------------------------------------------------------------------
- +ENABLE_PREPROCESSING = YES
- +MACRO_EXPANSION = NO
- +EXPAND_ONLY_PREDEF = NO
- +SEARCH_INCLUDES = YES
- +INCLUDE_PATH =
- +INCLUDE_FILE_PATTERNS =
- +PREDEFINED = DEBUG DEBUG_MEMORY
- +EXPAND_AS_DEFINED =
- +SKIP_FUNCTION_MACROS = YES
- +#---------------------------------------------------------------------------
- +# Configuration::additions related to external references
- +#---------------------------------------------------------------------------
- +TAGFILES =
- +GENERATE_TAGFILE =
- +ALLEXTERNALS = NO
- +EXTERNAL_GROUPS = YES
- +PERL_PATH = /usr/bin/perl
- +#---------------------------------------------------------------------------
- +# Configuration options related to the dot tool
- +#---------------------------------------------------------------------------
- +CLASS_DIAGRAMS = YES
- +HIDE_UNDOC_RELATIONS = YES
- +HAVE_DOT = NO
- +CLASS_GRAPH = YES
- +COLLABORATION_GRAPH = YES
- +GROUP_GRAPHS = YES
- +UML_LOOK = NO
- +TEMPLATE_RELATIONS = NO
- +INCLUDE_GRAPH = NO
- +INCLUDED_BY_GRAPH = YES
- +CALL_GRAPH = NO
- +GRAPHICAL_HIERARCHY = YES
- +DIRECTORY_GRAPH = YES
- +DOT_IMAGE_FORMAT = png
- +DOT_PATH =
- +DOTFILE_DIRS =
- +MAX_DOT_GRAPH_DEPTH = 1000
- +DOT_TRANSPARENT = NO
- +DOT_MULTI_TARGETS = NO
- +GENERATE_LEGEND = YES
- +DOT_CLEANUP = YES
- +#---------------------------------------------------------------------------
- +# Configuration::additions related to the search engine
- +#---------------------------------------------------------------------------
- +SEARCHENGINE = NO
- --- /dev/null
- +++ b/drivers/usb/host/dwc_common_port/dwc_cc.c
- @@ -0,0 +1,532 @@
- +/* =========================================================================
- + * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_cc.c $
- + * $Revision: #4 $
- + * $Date: 2010/11/04 $
- + * $Change: 1621692 $
- + *
- + * Synopsys Portability Library Software and documentation
- + * (hereinafter, "Software") is an Unsupported proprietary work of
- + * Synopsys, Inc. unless otherwise expressly agreed to in writing
- + * between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product
- + * under any End User Software License Agreement or Agreement for
- + * Licensed Product with Synopsys or any supplement thereto. You are
- + * permitted to use and redistribute this Software in source and binary
- + * forms, with or without modification, provided that redistributions
- + * of source code must retain this notice. You may not view, use,
- + * disclose, copy or distribute this file or any information contained
- + * herein except pursuant to this license grant from Synopsys. If you
- + * do not agree with this notice, including the disclaimer below, then
- + * you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
- + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- + * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL
- + * SYNOPSYS 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.
- + * ========================================================================= */
- +#ifdef DWC_CCLIB
- +
- +#include "dwc_cc.h"
- +
- +typedef struct dwc_cc
- +{
- + uint32_t uid;
- + uint8_t chid[16];
- + uint8_t cdid[16];
- + uint8_t ck[16];
- + uint8_t *name;
- + uint8_t length;
- + DWC_CIRCLEQ_ENTRY(dwc_cc) list_entry;
- +} dwc_cc_t;
- +
- +DWC_CIRCLEQ_HEAD(context_list, dwc_cc);
- +
- +/** The main structure for CC management. */
- +struct dwc_cc_if
- +{
- + dwc_mutex_t *mutex;
- + char *filename;
- +
- + unsigned is_host:1;
- +
- + dwc_notifier_t *notifier;
- +
- + struct context_list list;
- +};
- +
- +#ifdef DEBUG
- +static inline void dump_bytes(char *name, uint8_t *bytes, int len)
- +{
- + int i;
- + DWC_PRINTF("%s: ", name);
- + for (i=0; i<len; i++) {
- + DWC_PRINTF("%02x ", bytes[i]);
- + }
- + DWC_PRINTF("\n");
- +}
- +#else
- +#define dump_bytes(x...)
- +#endif
- +
- +static dwc_cc_t *alloc_cc(void *mem_ctx, uint8_t *name, uint32_t length)
- +{
- + dwc_cc_t *cc = dwc_alloc(mem_ctx, sizeof(dwc_cc_t));
- + if (!cc) {
- + return NULL;
- + }
- + DWC_MEMSET(cc, 0, sizeof(dwc_cc_t));
- +
- + if (name) {
- + cc->length = length;
- + cc->name = dwc_alloc(mem_ctx, length);
- + if (!cc->name) {
- + dwc_free(mem_ctx, cc);
- + return NULL;
- + }
- +
- + DWC_MEMCPY(cc->name, name, length);
- + }
- +
- + return cc;
- +}
- +
- +static void free_cc(void *mem_ctx, dwc_cc_t *cc)
- +{
- + if (cc->name) {
- + dwc_free(mem_ctx, cc->name);
- + }
- + dwc_free(mem_ctx, cc);
- +}
- +
- +static uint32_t next_uid(dwc_cc_if_t *cc_if)
- +{
- + uint32_t uid = 0;
- + dwc_cc_t *cc;
- + DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) {
- + if (cc->uid > uid) {
- + uid = cc->uid;
- + }
- + }
- +
- + if (uid == 0) {
- + uid = 255;
- + }
- +
- + return uid + 1;
- +}
- +
- +static dwc_cc_t *cc_find(dwc_cc_if_t *cc_if, uint32_t uid)
- +{
- + dwc_cc_t *cc;
- + DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) {
- + if (cc->uid == uid) {
- + return cc;
- + }
- + }
- + return NULL;
- +}
- +
- +static unsigned int cc_data_size(dwc_cc_if_t *cc_if)
- +{
- + unsigned int size = 0;
- + dwc_cc_t *cc;
- + DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) {
- + size += (48 + 1);
- + if (cc->name) {
- + size += cc->length;
- + }
- + }
- + return size;
- +}
- +
- +static uint32_t cc_match_chid(dwc_cc_if_t *cc_if, uint8_t *chid)
- +{
- + uint32_t uid = 0;
- + dwc_cc_t *cc;
- +
- + DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) {
- + if (DWC_MEMCMP(cc->chid, chid, 16) == 0) {
- + uid = cc->uid;
- + break;
- + }
- + }
- + return uid;
- +}
- +static uint32_t cc_match_cdid(dwc_cc_if_t *cc_if, uint8_t *cdid)
- +{
- + uint32_t uid = 0;
- + dwc_cc_t *cc;
- +
- + DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) {
- + if (DWC_MEMCMP(cc->cdid, cdid, 16) == 0) {
- + uid = cc->uid;
- + break;
- + }
- + }
- + return uid;
- +}
- +
- +/* Internal cc_add */
- +static int32_t cc_add(void *mem_ctx, dwc_cc_if_t *cc_if, uint8_t *chid,
- + uint8_t *cdid, uint8_t *ck, uint8_t *name, uint8_t length)
- +{
- + dwc_cc_t *cc;
- + uint32_t uid;
- +
- + if (cc_if->is_host) {
- + uid = cc_match_cdid(cc_if, cdid);
- + }
- + else {
- + uid = cc_match_chid(cc_if, chid);
- + }
- +
- + if (uid) {
- + DWC_DEBUGC("Replacing previous connection context id=%d name=%p name_len=%d", uid, name, length);
- + cc = cc_find(cc_if, uid);
- + }
- + else {
- + cc = alloc_cc(mem_ctx, name, length);
- + cc->uid = next_uid(cc_if);
- + DWC_CIRCLEQ_INSERT_TAIL(&cc_if->list, cc, list_entry);
- + }
- +
- + DWC_MEMCPY(&(cc->chid[0]), chid, 16);
- + DWC_MEMCPY(&(cc->cdid[0]), cdid, 16);
- + DWC_MEMCPY(&(cc->ck[0]), ck, 16);
- +
- + DWC_DEBUGC("Added connection context id=%d name=%p name_len=%d", cc->uid, name, length);
- + dump_bytes("CHID", cc->chid, 16);
- + dump_bytes("CDID", cc->cdid, 16);
- + dump_bytes("CK", cc->ck, 16);
- + return cc->uid;
- +}
- +
- +/* Internal cc_clear */
- +static void cc_clear(void *mem_ctx, dwc_cc_if_t *cc_if)
- +{
- + while (!DWC_CIRCLEQ_EMPTY(&cc_if->list)) {
- + dwc_cc_t *cc = DWC_CIRCLEQ_FIRST(&cc_if->list);
- + DWC_CIRCLEQ_REMOVE_INIT(&cc_if->list, cc, list_entry);
- + free_cc(mem_ctx, cc);
- + }
- +}
- +
- +dwc_cc_if_t *dwc_cc_if_alloc(void *mem_ctx, void *mtx_ctx,
- + dwc_notifier_t *notifier, unsigned is_host)
- +{
- + dwc_cc_if_t *cc_if = NULL;
- +
- + /* Allocate a common_cc_if structure */
- + cc_if = dwc_alloc(mem_ctx, sizeof(dwc_cc_if_t));
- +
- + if (!cc_if)
- + return NULL;
- +
- +#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES))
- + DWC_MUTEX_ALLOC_LINUX_DEBUG(cc_if->mutex);
- +#else
- + cc_if->mutex = dwc_mutex_alloc(mtx_ctx);
- +#endif
- + if (!cc_if->mutex) {
- + dwc_free(mem_ctx, cc_if);
- + return NULL;
- + }
- +
- + DWC_CIRCLEQ_INIT(&cc_if->list);
- + cc_if->is_host = is_host;
- + cc_if->notifier = notifier;
- + return cc_if;
- +}
- +
- +void dwc_cc_if_free(void *mem_ctx, void *mtx_ctx, dwc_cc_if_t *cc_if)
- +{
- +#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES))
- + DWC_MUTEX_FREE(cc_if->mutex);
- +#else
- + dwc_mutex_free(mtx_ctx, cc_if->mutex);
- +#endif
- + cc_clear(mem_ctx, cc_if);
- + dwc_free(mem_ctx, cc_if);
- +}
- +
- +static void cc_changed(dwc_cc_if_t *cc_if)
- +{
- + if (cc_if->notifier) {
- + dwc_notify(cc_if->notifier, DWC_CC_LIST_CHANGED_NOTIFICATION, cc_if);
- + }
- +}
- +
- +void dwc_cc_clear(void *mem_ctx, dwc_cc_if_t *cc_if)
- +{
- + DWC_MUTEX_LOCK(cc_if->mutex);
- + cc_clear(mem_ctx, cc_if);
- + DWC_MUTEX_UNLOCK(cc_if->mutex);
- + cc_changed(cc_if);
- +}
- +
- +int32_t dwc_cc_add(void *mem_ctx, dwc_cc_if_t *cc_if, uint8_t *chid,
- + uint8_t *cdid, uint8_t *ck, uint8_t *name, uint8_t length)
- +{
- + uint32_t uid;
- +
- + DWC_MUTEX_LOCK(cc_if->mutex);
- + uid = cc_add(mem_ctx, cc_if, chid, cdid, ck, name, length);
- + DWC_MUTEX_UNLOCK(cc_if->mutex);
- + cc_changed(cc_if);
- +
- + return uid;
- +}
- +
- +void dwc_cc_change(void *mem_ctx, dwc_cc_if_t *cc_if, int32_t id, uint8_t *chid,
- + uint8_t *cdid, uint8_t *ck, uint8_t *name, uint8_t length)
- +{
- + dwc_cc_t* cc;
- +
- + DWC_DEBUGC("Change connection context %d", id);
- +
- + DWC_MUTEX_LOCK(cc_if->mutex);
- + cc = cc_find(cc_if, id);
- + if (!cc) {
- + DWC_ERROR("Uid %d not found in cc list\n", id);
- + DWC_MUTEX_UNLOCK(cc_if->mutex);
- + return;
- + }
- +
- + if (chid) {
- + DWC_MEMCPY(&(cc->chid[0]), chid, 16);
- + }
- + if (cdid) {
- + DWC_MEMCPY(&(cc->cdid[0]), cdid, 16);
- + }
- + if (ck) {
- + DWC_MEMCPY(&(cc->ck[0]), ck, 16);
- + }
- +
- + if (name) {
- + if (cc->name) {
- + dwc_free(mem_ctx, cc->name);
- + }
- + cc->name = dwc_alloc(mem_ctx, length);
- + if (!cc->name) {
- + DWC_ERROR("Out of memory in dwc_cc_change()\n");
- + DWC_MUTEX_UNLOCK(cc_if->mutex);
- + return;
- + }
- + cc->length = length;
- + DWC_MEMCPY(cc->name, name, length);
- + }
- +
- + DWC_MUTEX_UNLOCK(cc_if->mutex);
- +
- + cc_changed(cc_if);
- +
- + DWC_DEBUGC("Changed connection context id=%d\n", id);
- + dump_bytes("New CHID", cc->chid, 16);
- + dump_bytes("New CDID", cc->cdid, 16);
- + dump_bytes("New CK", cc->ck, 16);
- +}
- +
- +void dwc_cc_remove(void *mem_ctx, dwc_cc_if_t *cc_if, int32_t id)
- +{
- + dwc_cc_t *cc;
- +
- + DWC_DEBUGC("Removing connection context %d", id);
- +
- + DWC_MUTEX_LOCK(cc_if->mutex);
- + cc = cc_find(cc_if, id);
- + if (!cc) {
- + DWC_ERROR("Uid %d not found in cc list\n", id);
- + DWC_MUTEX_UNLOCK(cc_if->mutex);
- + return;
- + }
- +
- + DWC_CIRCLEQ_REMOVE_INIT(&cc_if->list, cc, list_entry);
- + DWC_MUTEX_UNLOCK(cc_if->mutex);
- + free_cc(mem_ctx, cc);
- +
- + cc_changed(cc_if);
- +}
- +
- +uint8_t *dwc_cc_data_for_save(void *mem_ctx, dwc_cc_if_t *cc_if, unsigned int *length)
- +{
- + uint8_t *buf, *x;
- + uint8_t zero = 0;
- + dwc_cc_t *cc;
- +
- + DWC_MUTEX_LOCK(cc_if->mutex);
- + *length = cc_data_size(cc_if);
- + if (!(*length)) {
- + DWC_MUTEX_UNLOCK(cc_if->mutex);
- + return NULL;
- + }
- +
- + DWC_DEBUGC("Creating data for saving (length=%d)", *length);
- +
- + buf = dwc_alloc(mem_ctx, *length);
- + if (!buf) {
- + *length = 0;
- + DWC_MUTEX_UNLOCK(cc_if->mutex);
- + return NULL;
- + }
- +
- + x = buf;
- + DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) {
- + DWC_MEMCPY(x, cc->chid, 16);
- + x += 16;
- + DWC_MEMCPY(x, cc->cdid, 16);
- + x += 16;
- + DWC_MEMCPY(x, cc->ck, 16);
- + x += 16;
- + if (cc->name) {
- + DWC_MEMCPY(x, &cc->length, 1);
- + x += 1;
- + DWC_MEMCPY(x, cc->name, cc->length);
- + x += cc->length;
- + }
- + else {
- + DWC_MEMCPY(x, &zero, 1);
- + x += 1;
- + }
- + }
- + DWC_MUTEX_UNLOCK(cc_if->mutex);
- +
- + return buf;
- +}
- +
- +void dwc_cc_restore_from_data(void *mem_ctx, dwc_cc_if_t *cc_if, uint8_t *data, uint32_t length)
- +{
- + uint8_t name_length;
- + uint8_t *name;
- + uint8_t *chid;
- + uint8_t *cdid;
- + uint8_t *ck;
- + uint32_t i = 0;
- +
- + DWC_MUTEX_LOCK(cc_if->mutex);
- + cc_clear(mem_ctx, cc_if);
- +
- + while (i < length) {
- + chid = &data[i];
- + i += 16;
- + cdid = &data[i];
- + i += 16;
- + ck = &data[i];
- + i += 16;
- +
- + name_length = data[i];
- + i ++;
- +
- + if (name_length) {
- + name = &data[i];
- + i += name_length;
- + }
- + else {
- + name = NULL;
- + }
- +
- + /* check to see if we haven't overflown the buffer */
- + if (i > length) {
- + DWC_ERROR("Data format error while attempting to load CCs "
- + "(nlen=%d, iter=%d, buflen=%d).\n", name_length, i, length);
- + break;
- + }
- +
- + cc_add(mem_ctx, cc_if, chid, cdid, ck, name, name_length);
- + }
- + DWC_MUTEX_UNLOCK(cc_if->mutex);
- +
- + cc_changed(cc_if);
- +}
- +
- +uint32_t dwc_cc_match_chid(dwc_cc_if_t *cc_if, uint8_t *chid)
- +{
- + uint32_t uid = 0;
- +
- + DWC_MUTEX_LOCK(cc_if->mutex);
- + uid = cc_match_chid(cc_if, chid);
- + DWC_MUTEX_UNLOCK(cc_if->mutex);
- + return uid;
- +}
- +uint32_t dwc_cc_match_cdid(dwc_cc_if_t *cc_if, uint8_t *cdid)
- +{
- + uint32_t uid = 0;
- +
- + DWC_MUTEX_LOCK(cc_if->mutex);
- + uid = cc_match_cdid(cc_if, cdid);
- + DWC_MUTEX_UNLOCK(cc_if->mutex);
- + return uid;
- +}
- +
- +uint8_t *dwc_cc_ck(dwc_cc_if_t *cc_if, int32_t id)
- +{
- + uint8_t *ck = NULL;
- + dwc_cc_t *cc;
- +
- + DWC_MUTEX_LOCK(cc_if->mutex);
- + cc = cc_find(cc_if, id);
- + if (cc) {
- + ck = cc->ck;
- + }
- + DWC_MUTEX_UNLOCK(cc_if->mutex);
- +
- + return ck;
- +
- +}
- +
- +uint8_t *dwc_cc_chid(dwc_cc_if_t *cc_if, int32_t id)
- +{
- + uint8_t *retval = NULL;
- + dwc_cc_t *cc;
- +
- + DWC_MUTEX_LOCK(cc_if->mutex);
- + cc = cc_find(cc_if, id);
- + if (cc) {
- + retval = cc->chid;
- + }
- + DWC_MUTEX_UNLOCK(cc_if->mutex);
- +
- + return retval;
- +}
- +
- +uint8_t *dwc_cc_cdid(dwc_cc_if_t *cc_if, int32_t id)
- +{
- + uint8_t *retval = NULL;
- + dwc_cc_t *cc;
- +
- + DWC_MUTEX_LOCK(cc_if->mutex);
- + cc = cc_find(cc_if, id);
- + if (cc) {
- + retval = cc->cdid;
- + }
- + DWC_MUTEX_UNLOCK(cc_if->mutex);
- +
- + return retval;
- +}
- +
- +uint8_t *dwc_cc_name(dwc_cc_if_t *cc_if, int32_t id, uint8_t *length)
- +{
- + uint8_t *retval = NULL;
- + dwc_cc_t *cc;
- +
- + DWC_MUTEX_LOCK(cc_if->mutex);
- + *length = 0;
- + cc = cc_find(cc_if, id);
- + if (cc) {
- + *length = cc->length;
- + retval = cc->name;
- + }
- + DWC_MUTEX_UNLOCK(cc_if->mutex);
- +
- + return retval;
- +}
- +
- +#endif /* DWC_CCLIB */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_common_port/dwc_cc.h
- @@ -0,0 +1,224 @@
- +/* =========================================================================
- + * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_cc.h $
- + * $Revision: #4 $
- + * $Date: 2010/09/28 $
- + * $Change: 1596182 $
- + *
- + * Synopsys Portability Library Software and documentation
- + * (hereinafter, "Software") is an Unsupported proprietary work of
- + * Synopsys, Inc. unless otherwise expressly agreed to in writing
- + * between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product
- + * under any End User Software License Agreement or Agreement for
- + * Licensed Product with Synopsys or any supplement thereto. You are
- + * permitted to use and redistribute this Software in source and binary
- + * forms, with or without modification, provided that redistributions
- + * of source code must retain this notice. You may not view, use,
- + * disclose, copy or distribute this file or any information contained
- + * herein except pursuant to this license grant from Synopsys. If you
- + * do not agree with this notice, including the disclaimer below, then
- + * you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
- + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- + * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL
- + * SYNOPSYS 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.
- + * ========================================================================= */
- +#ifndef _DWC_CC_H_
- +#define _DWC_CC_H_
- +
- +#ifdef __cplusplus
- +extern "C" {
- +#endif
- +
- +/** @file
- + *
- + * This file defines the Context Context library.
- + *
- + * The main data structure is dwc_cc_if_t which is returned by either the
- + * dwc_cc_if_alloc function or returned by the module to the user via a provided
- + * function. The data structure is opaque and should only be manipulated via the
- + * functions provied in this API.
- + *
- + * It manages a list of connection contexts and operations can be performed to
- + * add, remove, query, search, and change, those contexts. Additionally,
- + * a dwc_notifier_t object can be requested from the manager so that
- + * the user can be notified whenever the context list has changed.
- + */
- +
- +#include "dwc_os.h"
- +#include "dwc_list.h"
- +#include "dwc_notifier.h"
- +
- +
- +/* Notifications */
- +#define DWC_CC_LIST_CHANGED_NOTIFICATION "DWC_CC_LIST_CHANGED_NOTIFICATION"
- +
- +struct dwc_cc_if;
- +typedef struct dwc_cc_if dwc_cc_if_t;
- +
- +
- +/** @name Connection Context Operations */
- +/** @{ */
- +
- +/** This function allocates memory for a dwc_cc_if_t structure, initializes
- + * fields to default values, and returns a pointer to the structure or NULL on
- + * error. */
- +extern dwc_cc_if_t *dwc_cc_if_alloc(void *mem_ctx, void *mtx_ctx,
- + dwc_notifier_t *notifier, unsigned is_host);
- +
- +/** Frees the memory for the specified CC structure allocated from
- + * dwc_cc_if_alloc(). */
- +extern void dwc_cc_if_free(void *mem_ctx, void *mtx_ctx, dwc_cc_if_t *cc_if);
- +
- +/** Removes all contexts from the connection context list */
- +extern void dwc_cc_clear(void *mem_ctx, dwc_cc_if_t *cc_if);
- +
- +/** Adds a connection context (CHID, CK, CDID, Name) to the connection context list.
- + * If a CHID already exists, the CK and name are overwritten. Statistics are
- + * not overwritten.
- + *
- + * @param cc_if The cc_if structure.
- + * @param chid A pointer to the 16-byte CHID. This value will be copied.
- + * @param ck A pointer to the 16-byte CK. This value will be copied.
- + * @param cdid A pointer to the 16-byte CDID. This value will be copied.
- + * @param name An optional host friendly name as defined in the association model
- + * spec. Must be a UTF16-LE unicode string. Can be NULL to indicated no name.
- + * @param length The length othe unicode string.
- + * @return A unique identifier used to refer to this context that is valid for
- + * as long as this context is still in the list. */
- +extern int32_t dwc_cc_add(void *mem_ctx, dwc_cc_if_t *cc_if, uint8_t *chid,
- + uint8_t *cdid, uint8_t *ck, uint8_t *name,
- + uint8_t length);
- +
- +/** Changes the CHID, CK, CDID, or Name values of a connection context in the
- + * list, preserving any accumulated statistics. This would typically be called
- + * if the host decideds to change the context with a SET_CONNECTION request.
- + *
- + * @param cc_if The cc_if structure.
- + * @param id The identifier of the connection context.
- + * @param chid A pointer to the 16-byte CHID. This value will be copied. NULL
- + * indicates no change.
- + * @param cdid A pointer to the 16-byte CDID. This value will be copied. NULL
- + * indicates no change.
- + * @param ck A pointer to the 16-byte CK. This value will be copied. NULL
- + * indicates no change.
- + * @param name Host friendly name UTF16-LE. NULL indicates no change.
- + * @param length Length of name. */
- +extern void dwc_cc_change(void *mem_ctx, dwc_cc_if_t *cc_if, int32_t id,
- + uint8_t *chid, uint8_t *cdid, uint8_t *ck,
- + uint8_t *name, uint8_t length);
- +
- +/** Remove the specified connection context.
- + * @param cc_if The cc_if structure.
- + * @param id The identifier of the connection context to remove. */
- +extern void dwc_cc_remove(void *mem_ctx, dwc_cc_if_t *cc_if, int32_t id);
- +
- +/** Get a binary block of data for the connection context list and attributes.
- + * This data can be used by the OS specific driver to save the connection
- + * context list into non-volatile memory.
- + *
- + * @param cc_if The cc_if structure.
- + * @param length Return the length of the data buffer.
- + * @return A pointer to the data buffer. The memory for this buffer should be
- + * freed with DWC_FREE() after use. */
- +extern uint8_t *dwc_cc_data_for_save(void *mem_ctx, dwc_cc_if_t *cc_if,
- + unsigned int *length);
- +
- +/** Restore the connection context list from the binary data that was previously
- + * returned from a call to dwc_cc_data_for_save. This can be used by the OS specific
- + * driver to load a connection context list from non-volatile memory.
- + *
- + * @param cc_if The cc_if structure.
- + * @param data The data bytes as returned from dwc_cc_data_for_save.
- + * @param length The length of the data. */
- +extern void dwc_cc_restore_from_data(void *mem_ctx, dwc_cc_if_t *cc_if,
- + uint8_t *data, unsigned int length);
- +
- +/** Find the connection context from the specified CHID.
- + *
- + * @param cc_if The cc_if structure.
- + * @param chid A pointer to the CHID data.
- + * @return A non-zero identifier of the connection context if the CHID matches.
- + * Otherwise returns 0. */
- +extern uint32_t dwc_cc_match_chid(dwc_cc_if_t *cc_if, uint8_t *chid);
- +
- +/** Find the connection context from the specified CDID.
- + *
- + * @param cc_if The cc_if structure.
- + * @param cdid A pointer to the CDID data.
- + * @return A non-zero identifier of the connection context if the CHID matches.
- + * Otherwise returns 0. */
- +extern uint32_t dwc_cc_match_cdid(dwc_cc_if_t *cc_if, uint8_t *cdid);
- +
- +/** Retrieve the CK from the specified connection context.
- + *
- + * @param cc_if The cc_if structure.
- + * @param id The identifier of the connection context.
- + * @return A pointer to the CK data. The memory does not need to be freed. */
- +extern uint8_t *dwc_cc_ck(dwc_cc_if_t *cc_if, int32_t id);
- +
- +/** Retrieve the CHID from the specified connection context.
- + *
- + * @param cc_if The cc_if structure.
- + * @param id The identifier of the connection context.
- + * @return A pointer to the CHID data. The memory does not need to be freed. */
- +extern uint8_t *dwc_cc_chid(dwc_cc_if_t *cc_if, int32_t id);
- +
- +/** Retrieve the CDID from the specified connection context.
- + *
- + * @param cc_if The cc_if structure.
- + * @param id The identifier of the connection context.
- + * @return A pointer to the CDID data. The memory does not need to be freed. */
- +extern uint8_t *dwc_cc_cdid(dwc_cc_if_t *cc_if, int32_t id);
- +
- +extern uint8_t *dwc_cc_name(dwc_cc_if_t *cc_if, int32_t id, uint8_t *length);
- +
- +/** Checks a buffer for non-zero.
- + * @param id A pointer to a 16 byte buffer.
- + * @return true if the 16 byte value is non-zero. */
- +static inline unsigned dwc_assoc_is_not_zero_id(uint8_t *id) {
- + int i;
- + for (i=0; i<16; i++) {
- + if (id[i]) return 1;
- + }
- + return 0;
- +}
- +
- +/** Checks a buffer for zero.
- + * @param id A pointer to a 16 byte buffer.
- + * @return true if the 16 byte value is zero. */
- +static inline unsigned dwc_assoc_is_zero_id(uint8_t *id) {
- + return !dwc_assoc_is_not_zero_id(id);
- +}
- +
- +/** Prints an ASCII representation for the 16-byte chid, cdid, or ck, into
- + * buffer. */
- +static inline int dwc_print_id_string(char *buffer, uint8_t *id) {
- + char *ptr = buffer;
- + int i;
- + for (i=0; i<16; i++) {
- + ptr += DWC_SPRINTF(ptr, "%02x", id[i]);
- + if (i < 15) {
- + ptr += DWC_SPRINTF(ptr, " ");
- + }
- + }
- + return ptr - buffer;
- +}
- +
- +/** @} */
- +
- +#ifdef __cplusplus
- +}
- +#endif
- +
- +#endif /* _DWC_CC_H_ */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_common_port/dwc_common_fbsd.c
- @@ -0,0 +1,1308 @@
- +#include "dwc_os.h"
- +#include "dwc_list.h"
- +
- +#ifdef DWC_CCLIB
- +# include "dwc_cc.h"
- +#endif
- +
- +#ifdef DWC_CRYPTOLIB
- +# include "dwc_modpow.h"
- +# include "dwc_dh.h"
- +# include "dwc_crypto.h"
- +#endif
- +
- +#ifdef DWC_NOTIFYLIB
- +# include "dwc_notifier.h"
- +#endif
- +
- +/* OS-Level Implementations */
- +
- +/* This is the FreeBSD 7.0 kernel implementation of the DWC platform library. */
- +
- +
- +/* MISC */
- +
- +void *DWC_MEMSET(void *dest, uint8_t byte, uint32_t size)
- +{
- + return memset(dest, byte, size);
- +}
- +
- +void *DWC_MEMCPY(void *dest, void const *src, uint32_t size)
- +{
- + return memcpy(dest, src, size);
- +}
- +
- +void *DWC_MEMMOVE(void *dest, void *src, uint32_t size)
- +{
- + bcopy(src, dest, size);
- + return dest;
- +}
- +
- +int DWC_MEMCMP(void *m1, void *m2, uint32_t size)
- +{
- + return memcmp(m1, m2, size);
- +}
- +
- +int DWC_STRNCMP(void *s1, void *s2, uint32_t size)
- +{
- + return strncmp(s1, s2, size);
- +}
- +
- +int DWC_STRCMP(void *s1, void *s2)
- +{
- + return strcmp(s1, s2);
- +}
- +
- +int DWC_STRLEN(char const *str)
- +{
- + return strlen(str);
- +}
- +
- +char *DWC_STRCPY(char *to, char const *from)
- +{
- + return strcpy(to, from);
- +}
- +
- +char *DWC_STRDUP(char const *str)
- +{
- + int len = DWC_STRLEN(str) + 1;
- + char *new = DWC_ALLOC_ATOMIC(len);
- +
- + if (!new) {
- + return NULL;
- + }
- +
- + DWC_MEMCPY(new, str, len);
- + return new;
- +}
- +
- +int DWC_ATOI(char *str, int32_t *value)
- +{
- + char *end = NULL;
- +
- + *value = strtol(str, &end, 0);
- + if (*end == '\0') {
- + return 0;
- + }
- +
- + return -1;
- +}
- +
- +int DWC_ATOUI(char *str, uint32_t *value)
- +{
- + char *end = NULL;
- +
- + *value = strtoul(str, &end, 0);
- + if (*end == '\0') {
- + return 0;
- + }
- +
- + return -1;
- +}
- +
- +
- +#ifdef DWC_UTFLIB
- +/* From usbstring.c */
- +
- +int DWC_UTF8_TO_UTF16LE(uint8_t const *s, uint16_t *cp, unsigned len)
- +{
- + int count = 0;
- + u8 c;
- + u16 uchar;
- +
- + /* this insists on correct encodings, though not minimal ones.
- + * BUT it currently rejects legit 4-byte UTF-8 code points,
- + * which need surrogate pairs. (Unicode 3.1 can use them.)
- + */
- + while (len != 0 && (c = (u8) *s++) != 0) {
- + if (unlikely(c & 0x80)) {
- + // 2-byte sequence:
- + // 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx
- + if ((c & 0xe0) == 0xc0) {
- + uchar = (c & 0x1f) << 6;
- +
- + c = (u8) *s++;
- + if ((c & 0xc0) != 0xc0)
- + goto fail;
- + c &= 0x3f;
- + uchar |= c;
- +
- + // 3-byte sequence (most CJKV characters):
- + // zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx
- + } else if ((c & 0xf0) == 0xe0) {
- + uchar = (c & 0x0f) << 12;
- +
- + c = (u8) *s++;
- + if ((c & 0xc0) != 0xc0)
- + goto fail;
- + c &= 0x3f;
- + uchar |= c << 6;
- +
- + c = (u8) *s++;
- + if ((c & 0xc0) != 0xc0)
- + goto fail;
- + c &= 0x3f;
- + uchar |= c;
- +
- + /* no bogus surrogates */
- + if (0xd800 <= uchar && uchar <= 0xdfff)
- + goto fail;
- +
- + // 4-byte sequence (surrogate pairs, currently rare):
- + // 11101110wwwwzzzzyy + 110111yyyyxxxxxx
- + // = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
- + // (uuuuu = wwww + 1)
- + // FIXME accept the surrogate code points (only)
- + } else
- + goto fail;
- + } else
- + uchar = c;
- + put_unaligned (cpu_to_le16 (uchar), cp++);
- + count++;
- + len--;
- + }
- + return count;
- +fail:
- + return -1;
- +}
- +
- +#endif /* DWC_UTFLIB */
- +
- +
- +/* dwc_debug.h */
- +
- +dwc_bool_t DWC_IN_IRQ(void)
- +{
- +// return in_irq();
- + return 0;
- +}
- +
- +dwc_bool_t DWC_IN_BH(void)
- +{
- +// return in_softirq();
- + return 0;
- +}
- +
- +void DWC_VPRINTF(char *format, va_list args)
- +{
- + vprintf(format, args);
- +}
- +
- +int DWC_VSNPRINTF(char *str, int size, char *format, va_list args)
- +{
- + return vsnprintf(str, size, format, args);
- +}
- +
- +void DWC_PRINTF(char *format, ...)
- +{
- + va_list args;
- +
- + va_start(args, format);
- + DWC_VPRINTF(format, args);
- + va_end(args);
- +}
- +
- +int DWC_SPRINTF(char *buffer, char *format, ...)
- +{
- + int retval;
- + va_list args;
- +
- + va_start(args, format);
- + retval = vsprintf(buffer, format, args);
- + va_end(args);
- + return retval;
- +}
- +
- +int DWC_SNPRINTF(char *buffer, int size, char *format, ...)
- +{
- + int retval;
- + va_list args;
- +
- + va_start(args, format);
- + retval = vsnprintf(buffer, size, format, args);
- + va_end(args);
- + return retval;
- +}
- +
- +void __DWC_WARN(char *format, ...)
- +{
- + va_list args;
- +
- + va_start(args, format);
- + DWC_VPRINTF(format, args);
- + va_end(args);
- +}
- +
- +void __DWC_ERROR(char *format, ...)
- +{
- + va_list args;
- +
- + va_start(args, format);
- + DWC_VPRINTF(format, args);
- + va_end(args);
- +}
- +
- +void DWC_EXCEPTION(char *format, ...)
- +{
- + va_list args;
- +
- + va_start(args, format);
- + DWC_VPRINTF(format, args);
- + va_end(args);
- +// BUG_ON(1); ???
- +}
- +
- +#ifdef DEBUG
- +void __DWC_DEBUG(char *format, ...)
- +{
- + va_list args;
- +
- + va_start(args, format);
- + DWC_VPRINTF(format, args);
- + va_end(args);
- +}
- +#endif
- +
- +
- +/* dwc_mem.h */
- +
- +#if 0
- +dwc_pool_t *DWC_DMA_POOL_CREATE(uint32_t size,
- + uint32_t align,
- + uint32_t alloc)
- +{
- + struct dma_pool *pool = dma_pool_create("Pool", NULL,
- + size, align, alloc);
- + return (dwc_pool_t *)pool;
- +}
- +
- +void DWC_DMA_POOL_DESTROY(dwc_pool_t *pool)
- +{
- + dma_pool_destroy((struct dma_pool *)pool);
- +}
- +
- +void *DWC_DMA_POOL_ALLOC(dwc_pool_t *pool, uint64_t *dma_addr)
- +{
- +// return dma_pool_alloc((struct dma_pool *)pool, GFP_KERNEL, dma_addr);
- + return dma_pool_alloc((struct dma_pool *)pool, M_WAITOK, dma_addr);
- +}
- +
- +void *DWC_DMA_POOL_ZALLOC(dwc_pool_t *pool, uint64_t *dma_addr)
- +{
- + void *vaddr = DWC_DMA_POOL_ALLOC(pool, dma_addr);
- + memset(..);
- +}
- +
- +void DWC_DMA_POOL_FREE(dwc_pool_t *pool, void *vaddr, void *daddr)
- +{
- + dma_pool_free(pool, vaddr, daddr);
- +}
- +#endif
- +
- +static void dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
- +{
- + if (error)
- + return;
- + *(bus_addr_t *)arg = segs[0].ds_addr;
- +}
- +
- +void *__DWC_DMA_ALLOC(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr)
- +{
- + dwc_dmactx_t *dma = (dwc_dmactx_t *)dma_ctx;
- + int error;
- +
- + error = bus_dma_tag_create(
- +#if __FreeBSD_version >= 700000
- + bus_get_dma_tag(dma->dev), /* parent */
- +#else
- + NULL, /* parent */
- +#endif
- + 4, 0, /* alignment, bounds */
- + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
- + BUS_SPACE_MAXADDR, /* highaddr */
- + NULL, NULL, /* filter, filterarg */
- + size, /* maxsize */
- + 1, /* nsegments */
- + size, /* maxsegsize */
- + 0, /* flags */
- + NULL, /* lockfunc */
- + NULL, /* lockarg */
- + &dma->dma_tag);
- + if (error) {
- + device_printf(dma->dev, "%s: bus_dma_tag_create failed: %d\n",
- + __func__, error);
- + goto fail_0;
- + }
- +
- + error = bus_dmamem_alloc(dma->dma_tag, &dma->dma_vaddr,
- + BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &dma->dma_map);
- + if (error) {
- + device_printf(dma->dev, "%s: bus_dmamem_alloc(%ju) failed: %d\n",
- + __func__, (uintmax_t)size, error);
- + goto fail_1;
- + }
- +
- + dma->dma_paddr = 0;
- + error = bus_dmamap_load(dma->dma_tag, dma->dma_map, dma->dma_vaddr, size,
- + dmamap_cb, &dma->dma_paddr, BUS_DMA_NOWAIT);
- + if (error || dma->dma_paddr == 0) {
- + device_printf(dma->dev, "%s: bus_dmamap_load failed: %d\n",
- + __func__, error);
- + goto fail_2;
- + }
- +
- + *dma_addr = dma->dma_paddr;
- + return dma->dma_vaddr;
- +
- +fail_2:
- + bus_dmamap_unload(dma->dma_tag, dma->dma_map);
- +fail_1:
- + bus_dmamem_free(dma->dma_tag, dma->dma_vaddr, dma->dma_map);
- + bus_dma_tag_destroy(dma->dma_tag);
- +fail_0:
- + dma->dma_map = NULL;
- + dma->dma_tag = NULL;
- +
- + return NULL;
- +}
- +
- +void __DWC_DMA_FREE(void *dma_ctx, uint32_t size, void *virt_addr, dwc_dma_t dma_addr)
- +{
- + dwc_dmactx_t *dma = (dwc_dmactx_t *)dma_ctx;
- +
- + if (dma->dma_tag == NULL)
- + return;
- + if (dma->dma_map != NULL) {
- + bus_dmamap_sync(dma->dma_tag, dma->dma_map,
- + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
- + bus_dmamap_unload(dma->dma_tag, dma->dma_map);
- + bus_dmamem_free(dma->dma_tag, dma->dma_vaddr, dma->dma_map);
- + dma->dma_map = NULL;
- + }
- +
- + bus_dma_tag_destroy(dma->dma_tag);
- + dma->dma_tag = NULL;
- +}
- +
- +void *__DWC_ALLOC(void *mem_ctx, uint32_t size)
- +{
- + return malloc(size, M_DEVBUF, M_WAITOK | M_ZERO);
- +}
- +
- +void *__DWC_ALLOC_ATOMIC(void *mem_ctx, uint32_t size)
- +{
- + return malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO);
- +}
- +
- +void __DWC_FREE(void *mem_ctx, void *addr)
- +{
- + free(addr, M_DEVBUF);
- +}
- +
- +
- +#ifdef DWC_CRYPTOLIB
- +/* dwc_crypto.h */
- +
- +void DWC_RANDOM_BYTES(uint8_t *buffer, uint32_t length)
- +{
- + get_random_bytes(buffer, length);
- +}
- +
- +int DWC_AES_CBC(uint8_t *message, uint32_t messagelen, uint8_t *key, uint32_t keylen, uint8_t iv[16], uint8_t *out)
- +{
- + struct crypto_blkcipher *tfm;
- + struct blkcipher_desc desc;
- + struct scatterlist sgd;
- + struct scatterlist sgs;
- +
- + tfm = crypto_alloc_blkcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC);
- + if (tfm == NULL) {
- + printk("failed to load transform for aes CBC\n");
- + return -1;
- + }
- +
- + crypto_blkcipher_setkey(tfm, key, keylen);
- + crypto_blkcipher_set_iv(tfm, iv, 16);
- +
- + sg_init_one(&sgd, out, messagelen);
- + sg_init_one(&sgs, message, messagelen);
- +
- + desc.tfm = tfm;
- + desc.flags = 0;
- +
- + if (crypto_blkcipher_encrypt(&desc, &sgd, &sgs, messagelen)) {
- + crypto_free_blkcipher(tfm);
- + DWC_ERROR("AES CBC encryption failed");
- + return -1;
- + }
- +
- + crypto_free_blkcipher(tfm);
- + return 0;
- +}
- +
- +int DWC_SHA256(uint8_t *message, uint32_t len, uint8_t *out)
- +{
- + struct crypto_hash *tfm;
- + struct hash_desc desc;
- + struct scatterlist sg;
- +
- + tfm = crypto_alloc_hash("sha256", 0, CRYPTO_ALG_ASYNC);
- + if (IS_ERR(tfm)) {
- + DWC_ERROR("Failed to load transform for sha256: %ld", PTR_ERR(tfm));
- + return 0;
- + }
- + desc.tfm = tfm;
- + desc.flags = 0;
- +
- + sg_init_one(&sg, message, len);
- + crypto_hash_digest(&desc, &sg, len, out);
- + crypto_free_hash(tfm);
- +
- + return 1;
- +}
- +
- +int DWC_HMAC_SHA256(uint8_t *message, uint32_t messagelen,
- + uint8_t *key, uint32_t keylen, uint8_t *out)
- +{
- + struct crypto_hash *tfm;
- + struct hash_desc desc;
- + struct scatterlist sg;
- +
- + tfm = crypto_alloc_hash("hmac(sha256)", 0, CRYPTO_ALG_ASYNC);
- + if (IS_ERR(tfm)) {
- + DWC_ERROR("Failed to load transform for hmac(sha256): %ld", PTR_ERR(tfm));
- + return 0;
- + }
- + desc.tfm = tfm;
- + desc.flags = 0;
- +
- + sg_init_one(&sg, message, messagelen);
- + crypto_hash_setkey(tfm, key, keylen);
- + crypto_hash_digest(&desc, &sg, messagelen, out);
- + crypto_free_hash(tfm);
- +
- + return 1;
- +}
- +
- +#endif /* DWC_CRYPTOLIB */
- +
- +
- +/* Byte Ordering Conversions */
- +
- +uint32_t DWC_CPU_TO_LE32(uint32_t *p)
- +{
- +#ifdef __LITTLE_ENDIAN
- + return *p;
- +#else
- + uint8_t *u_p = (uint8_t *)p;
- +
- + return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
- +#endif
- +}
- +
- +uint32_t DWC_CPU_TO_BE32(uint32_t *p)
- +{
- +#ifdef __BIG_ENDIAN
- + return *p;
- +#else
- + uint8_t *u_p = (uint8_t *)p;
- +
- + return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
- +#endif
- +}
- +
- +uint32_t DWC_LE32_TO_CPU(uint32_t *p)
- +{
- +#ifdef __LITTLE_ENDIAN
- + return *p;
- +#else
- + uint8_t *u_p = (uint8_t *)p;
- +
- + return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
- +#endif
- +}
- +
- +uint32_t DWC_BE32_TO_CPU(uint32_t *p)
- +{
- +#ifdef __BIG_ENDIAN
- + return *p;
- +#else
- + uint8_t *u_p = (uint8_t *)p;
- +
- + return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
- +#endif
- +}
- +
- +uint16_t DWC_CPU_TO_LE16(uint16_t *p)
- +{
- +#ifdef __LITTLE_ENDIAN
- + return *p;
- +#else
- + uint8_t *u_p = (uint8_t *)p;
- + return (u_p[1] | (u_p[0] << 8));
- +#endif
- +}
- +
- +uint16_t DWC_CPU_TO_BE16(uint16_t *p)
- +{
- +#ifdef __BIG_ENDIAN
- + return *p;
- +#else
- + uint8_t *u_p = (uint8_t *)p;
- + return (u_p[1] | (u_p[0] << 8));
- +#endif
- +}
- +
- +uint16_t DWC_LE16_TO_CPU(uint16_t *p)
- +{
- +#ifdef __LITTLE_ENDIAN
- + return *p;
- +#else
- + uint8_t *u_p = (uint8_t *)p;
- + return (u_p[1] | (u_p[0] << 8));
- +#endif
- +}
- +
- +uint16_t DWC_BE16_TO_CPU(uint16_t *p)
- +{
- +#ifdef __BIG_ENDIAN
- + return *p;
- +#else
- + uint8_t *u_p = (uint8_t *)p;
- + return (u_p[1] | (u_p[0] << 8));
- +#endif
- +}
- +
- +
- +/* Registers */
- +
- +uint32_t DWC_READ_REG32(void *io_ctx, uint32_t volatile *reg)
- +{
- + dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
- + bus_size_t ior = (bus_size_t)reg;
- +
- + return bus_space_read_4(io->iot, io->ioh, ior);
- +}
- +
- +#if 0
- +uint64_t DWC_READ_REG64(void *io_ctx, uint64_t volatile *reg)
- +{
- + dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
- + bus_size_t ior = (bus_size_t)reg;
- +
- + return bus_space_read_8(io->iot, io->ioh, ior);
- +}
- +#endif
- +
- +void DWC_WRITE_REG32(void *io_ctx, uint32_t volatile *reg, uint32_t value)
- +{
- + dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
- + bus_size_t ior = (bus_size_t)reg;
- +
- + bus_space_write_4(io->iot, io->ioh, ior, value);
- +}
- +
- +#if 0
- +void DWC_WRITE_REG64(void *io_ctx, uint64_t volatile *reg, uint64_t value)
- +{
- + dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
- + bus_size_t ior = (bus_size_t)reg;
- +
- + bus_space_write_8(io->iot, io->ioh, ior, value);
- +}
- +#endif
- +
- +void DWC_MODIFY_REG32(void *io_ctx, uint32_t volatile *reg, uint32_t clear_mask,
- + uint32_t set_mask)
- +{
- + dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
- + bus_size_t ior = (bus_size_t)reg;
- +
- + bus_space_write_4(io->iot, io->ioh, ior,
- + (bus_space_read_4(io->iot, io->ioh, ior) &
- + ~clear_mask) | set_mask);
- +}
- +
- +#if 0
- +void DWC_MODIFY_REG64(void *io_ctx, uint64_t volatile *reg, uint64_t clear_mask,
- + uint64_t set_mask)
- +{
- + dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
- + bus_size_t ior = (bus_size_t)reg;
- +
- + bus_space_write_8(io->iot, io->ioh, ior,
- + (bus_space_read_8(io->iot, io->ioh, ior) &
- + ~clear_mask) | set_mask);
- +}
- +#endif
- +
- +
- +/* Locking */
- +
- +dwc_spinlock_t *DWC_SPINLOCK_ALLOC(void)
- +{
- + struct mtx *sl = DWC_ALLOC(sizeof(*sl));
- +
- + if (!sl) {
- + DWC_ERROR("Cannot allocate memory for spinlock");
- + return NULL;
- + }
- +
- + mtx_init(sl, "dw3spn", NULL, MTX_SPIN);
- + return (dwc_spinlock_t *)sl;
- +}
- +
- +void DWC_SPINLOCK_FREE(dwc_spinlock_t *lock)
- +{
- + struct mtx *sl = (struct mtx *)lock;
- +
- + mtx_destroy(sl);
- + DWC_FREE(sl);
- +}
- +
- +void DWC_SPINLOCK(dwc_spinlock_t *lock)
- +{
- + mtx_lock_spin((struct mtx *)lock); // ???
- +}
- +
- +void DWC_SPINUNLOCK(dwc_spinlock_t *lock)
- +{
- + mtx_unlock_spin((struct mtx *)lock); // ???
- +}
- +
- +void DWC_SPINLOCK_IRQSAVE(dwc_spinlock_t *lock, dwc_irqflags_t *flags)
- +{
- + mtx_lock_spin((struct mtx *)lock);
- +}
- +
- +void DWC_SPINUNLOCK_IRQRESTORE(dwc_spinlock_t *lock, dwc_irqflags_t flags)
- +{
- + mtx_unlock_spin((struct mtx *)lock);
- +}
- +
- +dwc_mutex_t *DWC_MUTEX_ALLOC(void)
- +{
- + struct mtx *m;
- + dwc_mutex_t *mutex = (dwc_mutex_t *)DWC_ALLOC(sizeof(struct mtx));
- +
- + if (!mutex) {
- + DWC_ERROR("Cannot allocate memory for mutex");
- + return NULL;
- + }
- +
- + m = (struct mtx *)mutex;
- + mtx_init(m, "dw3mtx", NULL, MTX_DEF);
- + return mutex;
- +}
- +
- +#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES))
- +#else
- +void DWC_MUTEX_FREE(dwc_mutex_t *mutex)
- +{
- + mtx_destroy((struct mtx *)mutex);
- + DWC_FREE(mutex);
- +}
- +#endif
- +
- +void DWC_MUTEX_LOCK(dwc_mutex_t *mutex)
- +{
- + struct mtx *m = (struct mtx *)mutex;
- +
- + mtx_lock(m);
- +}
- +
- +int DWC_MUTEX_TRYLOCK(dwc_mutex_t *mutex)
- +{
- + struct mtx *m = (struct mtx *)mutex;
- +
- + return mtx_trylock(m);
- +}
- +
- +void DWC_MUTEX_UNLOCK(dwc_mutex_t *mutex)
- +{
- + struct mtx *m = (struct mtx *)mutex;
- +
- + mtx_unlock(m);
- +}
- +
- +
- +/* Timing */
- +
- +void DWC_UDELAY(uint32_t usecs)
- +{
- + DELAY(usecs);
- +}
- +
- +void DWC_MDELAY(uint32_t msecs)
- +{
- + do {
- + DELAY(1000);
- + } while (--msecs);
- +}
- +
- +void DWC_MSLEEP(uint32_t msecs)
- +{
- + struct timeval tv;
- +
- + tv.tv_sec = msecs / 1000;
- + tv.tv_usec = (msecs - tv.tv_sec * 1000) * 1000;
- + pause("dw3slp", tvtohz(&tv));
- +}
- +
- +uint32_t DWC_TIME(void)
- +{
- + struct timeval tv;
- +
- + microuptime(&tv); // or getmicrouptime? (less precise, but faster)
- + return tv.tv_sec * 1000 + tv.tv_usec / 1000;
- +}
- +
- +
- +/* Timers */
- +
- +struct dwc_timer {
- + struct callout t;
- + char *name;
- + dwc_spinlock_t *lock;
- + dwc_timer_callback_t cb;
- + void *data;
- +};
- +
- +dwc_timer_t *DWC_TIMER_ALLOC(char *name, dwc_timer_callback_t cb, void *data)
- +{
- + dwc_timer_t *t = DWC_ALLOC(sizeof(*t));
- +
- + if (!t) {
- + DWC_ERROR("Cannot allocate memory for timer");
- + return NULL;
- + }
- +
- + callout_init(&t->t, 1);
- +
- + t->name = DWC_STRDUP(name);
- + if (!t->name) {
- + DWC_ERROR("Cannot allocate memory for timer->name");
- + goto no_name;
- + }
- +
- + t->lock = DWC_SPINLOCK_ALLOC();
- + if (!t->lock) {
- + DWC_ERROR("Cannot allocate memory for lock");
- + goto no_lock;
- + }
- +
- + t->cb = cb;
- + t->data = data;
- +
- + return t;
- +
- + no_lock:
- + DWC_FREE(t->name);
- + no_name:
- + DWC_FREE(t);
- +
- + return NULL;
- +}
- +
- +void DWC_TIMER_FREE(dwc_timer_t *timer)
- +{
- + callout_stop(&timer->t);
- + DWC_SPINLOCK_FREE(timer->lock);
- + DWC_FREE(timer->name);
- + DWC_FREE(timer);
- +}
- +
- +void DWC_TIMER_SCHEDULE(dwc_timer_t *timer, uint32_t time)
- +{
- + struct timeval tv;
- +
- + tv.tv_sec = time / 1000;
- + tv.tv_usec = (time - tv.tv_sec * 1000) * 1000;
- + callout_reset(&timer->t, tvtohz(&tv), timer->cb, timer->data);
- +}
- +
- +void DWC_TIMER_CANCEL(dwc_timer_t *timer)
- +{
- + callout_stop(&timer->t);
- +}
- +
- +
- +/* Wait Queues */
- +
- +struct dwc_waitq {
- + struct mtx lock;
- + int abort;
- +};
- +
- +dwc_waitq_t *DWC_WAITQ_ALLOC(void)
- +{
- + dwc_waitq_t *wq = DWC_ALLOC(sizeof(*wq));
- +
- + if (!wq) {
- + DWC_ERROR("Cannot allocate memory for waitqueue");
- + return NULL;
- + }
- +
- + mtx_init(&wq->lock, "dw3wtq", NULL, MTX_DEF);
- + wq->abort = 0;
- +
- + return wq;
- +}
- +
- +void DWC_WAITQ_FREE(dwc_waitq_t *wq)
- +{
- + mtx_destroy(&wq->lock);
- + DWC_FREE(wq);
- +}
- +
- +int32_t DWC_WAITQ_WAIT(dwc_waitq_t *wq, dwc_waitq_condition_t cond, void *data)
- +{
- +// intrmask_t ipl;
- + int result = 0;
- +
- + mtx_lock(&wq->lock);
- +// ipl = splbio();
- +
- + /* Skip the sleep if already aborted or triggered */
- + if (!wq->abort && !cond(data)) {
- +// splx(ipl);
- + result = msleep(wq, &wq->lock, PCATCH, "dw3wat", 0); // infinite timeout
- +// ipl = splbio();
- + }
- +
- + if (result == ERESTART) { // signaled - restart
- + result = -DWC_E_RESTART;
- +
- + } else if (result == EINTR) { // signaled - interrupt
- + result = -DWC_E_ABORT;
- +
- + } else if (wq->abort) {
- + result = -DWC_E_ABORT;
- +
- + } else {
- + result = 0;
- + }
- +
- + wq->abort = 0;
- +// splx(ipl);
- + mtx_unlock(&wq->lock);
- + return result;
- +}
- +
- +int32_t DWC_WAITQ_WAIT_TIMEOUT(dwc_waitq_t *wq, dwc_waitq_condition_t cond,
- + void *data, int32_t msecs)
- +{
- + struct timeval tv, tv1, tv2;
- +// intrmask_t ipl;
- + int result = 0;
- +
- + tv.tv_sec = msecs / 1000;
- + tv.tv_usec = (msecs - tv.tv_sec * 1000) * 1000;
- +
- + mtx_lock(&wq->lock);
- +// ipl = splbio();
- +
- + /* Skip the sleep if already aborted or triggered */
- + if (!wq->abort && !cond(data)) {
- +// splx(ipl);
- + getmicrouptime(&tv1);
- + result = msleep(wq, &wq->lock, PCATCH, "dw3wto", tvtohz(&tv));
- + getmicrouptime(&tv2);
- +// ipl = splbio();
- + }
- +
- + if (result == 0) { // awoken
- + if (wq->abort) {
- + result = -DWC_E_ABORT;
- + } else {
- + tv2.tv_usec -= tv1.tv_usec;
- + if (tv2.tv_usec < 0) {
- + tv2.tv_usec += 1000000;
- + tv2.tv_sec--;
- + }
- +
- + tv2.tv_sec -= tv1.tv_sec;
- + result = tv2.tv_sec * 1000 + tv2.tv_usec / 1000;
- + result = msecs - result;
- + if (result <= 0)
- + result = 1;
- + }
- + } else if (result == ERESTART) { // signaled - restart
- + result = -DWC_E_RESTART;
- +
- + } else if (result == EINTR) { // signaled - interrupt
- + result = -DWC_E_ABORT;
- +
- + } else { // timed out
- + result = -DWC_E_TIMEOUT;
- + }
- +
- + wq->abort = 0;
- +// splx(ipl);
- + mtx_unlock(&wq->lock);
- + return result;
- +}
- +
- +void DWC_WAITQ_TRIGGER(dwc_waitq_t *wq)
- +{
- + wakeup(wq);
- +}
- +
- +void DWC_WAITQ_ABORT(dwc_waitq_t *wq)
- +{
- +// intrmask_t ipl;
- +
- + mtx_lock(&wq->lock);
- +// ipl = splbio();
- + wq->abort = 1;
- + wakeup(wq);
- +// splx(ipl);
- + mtx_unlock(&wq->lock);
- +}
- +
- +
- +/* Threading */
- +
- +struct dwc_thread {
- + struct proc *proc;
- + int abort;
- +};
- +
- +dwc_thread_t *DWC_THREAD_RUN(dwc_thread_function_t func, char *name, void *data)
- +{
- + int retval;
- + dwc_thread_t *thread = DWC_ALLOC(sizeof(*thread));
- +
- + if (!thread) {
- + return NULL;
- + }
- +
- + thread->abort = 0;
- + retval = kthread_create((void (*)(void *))func, data, &thread->proc,
- + RFPROC | RFNOWAIT, 0, "%s", name);
- + if (retval) {
- + DWC_FREE(thread);
- + return NULL;
- + }
- +
- + return thread;
- +}
- +
- +int DWC_THREAD_STOP(dwc_thread_t *thread)
- +{
- + int retval;
- +
- + thread->abort = 1;
- + retval = tsleep(&thread->abort, 0, "dw3stp", 60 * hz);
- +
- + if (retval == 0) {
- + /* DWC_THREAD_EXIT() will free the thread struct */
- + return 0;
- + }
- +
- + /* NOTE: We leak the thread struct if thread doesn't die */
- +
- + if (retval == EWOULDBLOCK) {
- + return -DWC_E_TIMEOUT;
- + }
- +
- + return -DWC_E_UNKNOWN;
- +}
- +
- +dwc_bool_t DWC_THREAD_SHOULD_STOP(dwc_thread_t *thread)
- +{
- + return thread->abort;
- +}
- +
- +void DWC_THREAD_EXIT(dwc_thread_t *thread)
- +{
- + wakeup(&thread->abort);
- + DWC_FREE(thread);
- + kthread_exit(0);
- +}
- +
- +
- +/* tasklets
- + - Runs in interrupt context (cannot sleep)
- + - Each tasklet runs on a single CPU [ How can we ensure this on FreeBSD? Does it matter? ]
- + - Different tasklets can be running simultaneously on different CPUs [ shouldn't matter ]
- + */
- +struct dwc_tasklet {
- + struct task t;
- + dwc_tasklet_callback_t cb;
- + void *data;
- +};
- +
- +static void tasklet_callback(void *data, int pending) // what to do with pending ???
- +{
- + dwc_tasklet_t *task = (dwc_tasklet_t *)data;
- +
- + task->cb(task->data);
- +}
- +
- +dwc_tasklet_t *DWC_TASK_ALLOC(char *name, dwc_tasklet_callback_t cb, void *data)
- +{
- + dwc_tasklet_t *task = DWC_ALLOC(sizeof(*task));
- +
- + if (task) {
- + task->cb = cb;
- + task->data = data;
- + TASK_INIT(&task->t, 0, tasklet_callback, task);
- + } else {
- + DWC_ERROR("Cannot allocate memory for tasklet");
- + }
- +
- + return task;
- +}
- +
- +void DWC_TASK_FREE(dwc_tasklet_t *task)
- +{
- + taskqueue_drain(taskqueue_fast, &task->t); // ???
- + DWC_FREE(task);
- +}
- +
- +void DWC_TASK_SCHEDULE(dwc_tasklet_t *task)
- +{
- + /* Uses predefined system queue */
- + taskqueue_enqueue_fast(taskqueue_fast, &task->t);
- +}
- +
- +
- +/* workqueues
- + - Runs in process context (can sleep)
- + */
- +typedef struct work_container {
- + dwc_work_callback_t cb;
- + void *data;
- + dwc_workq_t *wq;
- + char *name;
- + int hz;
- +
- +#ifdef DEBUG
- + DWC_CIRCLEQ_ENTRY(work_container) entry;
- +#endif
- + struct task task;
- +} work_container_t;
- +
- +#ifdef DEBUG
- +DWC_CIRCLEQ_HEAD(work_container_queue, work_container);
- +#endif
- +
- +struct dwc_workq {
- + struct taskqueue *taskq;
- + dwc_spinlock_t *lock;
- + dwc_waitq_t *waitq;
- + int pending;
- +
- +#ifdef DEBUG
- + struct work_container_queue entries;
- +#endif
- +};
- +
- +static void do_work(void *data, int pending) // what to do with pending ???
- +{
- + work_container_t *container = (work_container_t *)data;
- + dwc_workq_t *wq = container->wq;
- + dwc_irqflags_t flags;
- +
- + if (container->hz) {
- + pause("dw3wrk", container->hz);
- + }
- +
- + container->cb(container->data);
- + DWC_DEBUG("Work done: %s, container=%p", container->name, container);
- +
- + DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
- +
- +#ifdef DEBUG
- + DWC_CIRCLEQ_REMOVE(&wq->entries, container, entry);
- +#endif
- + if (container->name)
- + DWC_FREE(container->name);
- + DWC_FREE(container);
- + wq->pending--;
- + DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
- + DWC_WAITQ_TRIGGER(wq->waitq);
- +}
- +
- +static int work_done(void *data)
- +{
- + dwc_workq_t *workq = (dwc_workq_t *)data;
- +
- + return workq->pending == 0;
- +}
- +
- +int DWC_WORKQ_WAIT_WORK_DONE(dwc_workq_t *workq, int timeout)
- +{
- + return DWC_WAITQ_WAIT_TIMEOUT(workq->waitq, work_done, workq, timeout);
- +}
- +
- +dwc_workq_t *DWC_WORKQ_ALLOC(char *name)
- +{
- + dwc_workq_t *wq = DWC_ALLOC(sizeof(*wq));
- +
- + if (!wq) {
- + DWC_ERROR("Cannot allocate memory for workqueue");
- + return NULL;
- + }
- +
- + wq->taskq = taskqueue_create(name, M_NOWAIT, taskqueue_thread_enqueue, &wq->taskq);
- + if (!wq->taskq) {
- + DWC_ERROR("Cannot allocate memory for taskqueue");
- + goto no_taskq;
- + }
- +
- + wq->pending = 0;
- +
- + wq->lock = DWC_SPINLOCK_ALLOC();
- + if (!wq->lock) {
- + DWC_ERROR("Cannot allocate memory for spinlock");
- + goto no_lock;
- + }
- +
- + wq->waitq = DWC_WAITQ_ALLOC();
- + if (!wq->waitq) {
- + DWC_ERROR("Cannot allocate memory for waitqueue");
- + goto no_waitq;
- + }
- +
- + taskqueue_start_threads(&wq->taskq, 1, PWAIT, "%s taskq", "dw3tsk");
- +
- +#ifdef DEBUG
- + DWC_CIRCLEQ_INIT(&wq->entries);
- +#endif
- + return wq;
- +
- + no_waitq:
- + DWC_SPINLOCK_FREE(wq->lock);
- + no_lock:
- + taskqueue_free(wq->taskq);
- + no_taskq:
- + DWC_FREE(wq);
- +
- + return NULL;
- +}
- +
- +void DWC_WORKQ_FREE(dwc_workq_t *wq)
- +{
- +#ifdef DEBUG
- + dwc_irqflags_t flags;
- +
- + DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
- +
- + if (wq->pending != 0) {
- + struct work_container *container;
- +
- + DWC_ERROR("Destroying work queue with pending work");
- +
- + DWC_CIRCLEQ_FOREACH(container, &wq->entries, entry) {
- + DWC_ERROR("Work %s still pending", container->name);
- + }
- + }
- +
- + DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
- +#endif
- + DWC_WAITQ_FREE(wq->waitq);
- + DWC_SPINLOCK_FREE(wq->lock);
- + taskqueue_free(wq->taskq);
- + DWC_FREE(wq);
- +}
- +
- +void DWC_WORKQ_SCHEDULE(dwc_workq_t *wq, dwc_work_callback_t cb, void *data,
- + char *format, ...)
- +{
- + dwc_irqflags_t flags;
- + work_container_t *container;
- + static char name[128];
- + va_list args;
- +
- + va_start(args, format);
- + DWC_VSNPRINTF(name, 128, format, args);
- + va_end(args);
- +
- + DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
- + wq->pending++;
- + DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
- + DWC_WAITQ_TRIGGER(wq->waitq);
- +
- + container = DWC_ALLOC_ATOMIC(sizeof(*container));
- + if (!container) {
- + DWC_ERROR("Cannot allocate memory for container");
- + return;
- + }
- +
- + container->name = DWC_STRDUP(name);
- + if (!container->name) {
- + DWC_ERROR("Cannot allocate memory for container->name");
- + DWC_FREE(container);
- + return;
- + }
- +
- + container->cb = cb;
- + container->data = data;
- + container->wq = wq;
- + container->hz = 0;
- +
- + DWC_DEBUG("Queueing work: %s, container=%p", container->name, container);
- +
- + TASK_INIT(&container->task, 0, do_work, container);
- +
- +#ifdef DEBUG
- + DWC_CIRCLEQ_INSERT_TAIL(&wq->entries, container, entry);
- +#endif
- + taskqueue_enqueue_fast(wq->taskq, &container->task);
- +}
- +
- +void DWC_WORKQ_SCHEDULE_DELAYED(dwc_workq_t *wq, dwc_work_callback_t cb,
- + void *data, uint32_t time, char *format, ...)
- +{
- + dwc_irqflags_t flags;
- + work_container_t *container;
- + static char name[128];
- + struct timeval tv;
- + va_list args;
- +
- + va_start(args, format);
- + DWC_VSNPRINTF(name, 128, format, args);
- + va_end(args);
- +
- + DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
- + wq->pending++;
- + DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
- + DWC_WAITQ_TRIGGER(wq->waitq);
- +
- + container = DWC_ALLOC_ATOMIC(sizeof(*container));
- + if (!container) {
- + DWC_ERROR("Cannot allocate memory for container");
- + return;
- + }
- +
- + container->name = DWC_STRDUP(name);
- + if (!container->name) {
- + DWC_ERROR("Cannot allocate memory for container->name");
- + DWC_FREE(container);
- + return;
- + }
- +
- + container->cb = cb;
- + container->data = data;
- + container->wq = wq;
- +
- + tv.tv_sec = time / 1000;
- + tv.tv_usec = (time - tv.tv_sec * 1000) * 1000;
- + container->hz = tvtohz(&tv);
- +
- + DWC_DEBUG("Queueing work: %s, container=%p", container->name, container);
- +
- + TASK_INIT(&container->task, 0, do_work, container);
- +
- +#ifdef DEBUG
- + DWC_CIRCLEQ_INSERT_TAIL(&wq->entries, container, entry);
- +#endif
- + taskqueue_enqueue_fast(wq->taskq, &container->task);
- +}
- +
- +int DWC_WORKQ_PENDING(dwc_workq_t *wq)
- +{
- + return wq->pending;
- +}
- --- /dev/null
- +++ b/drivers/usb/host/dwc_common_port/dwc_common_linux.c
- @@ -0,0 +1,1409 @@
- +#include <linux/kernel.h>
- +#include <linux/init.h>
- +#include <linux/module.h>
- +#include <linux/kthread.h>
- +
- +#ifdef DWC_CCLIB
- +# include "dwc_cc.h"
- +#endif
- +
- +#ifdef DWC_CRYPTOLIB
- +# include "dwc_modpow.h"
- +# include "dwc_dh.h"
- +# include "dwc_crypto.h"
- +#endif
- +
- +#ifdef DWC_NOTIFYLIB
- +# include "dwc_notifier.h"
- +#endif
- +
- +/* OS-Level Implementations */
- +
- +/* This is the Linux kernel implementation of the DWC platform library. */
- +#include <linux/moduleparam.h>
- +#include <linux/ctype.h>
- +#include <linux/crypto.h>
- +#include <linux/delay.h>
- +#include <linux/device.h>
- +#include <linux/dma-mapping.h>
- +#include <linux/cdev.h>
- +#include <linux/errno.h>
- +#include <linux/interrupt.h>
- +#include <linux/jiffies.h>
- +#include <linux/list.h>
- +#include <linux/pci.h>
- +#include <linux/random.h>
- +#include <linux/scatterlist.h>
- +#include <linux/slab.h>
- +#include <linux/stat.h>
- +#include <linux/string.h>
- +#include <linux/timer.h>
- +#include <linux/usb.h>
- +
- +#include <linux/version.h>
- +
- +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
- +# include <linux/usb/gadget.h>
- +#else
- +# include <linux/usb_gadget.h>
- +#endif
- +
- +#include <asm/io.h>
- +#include <asm/page.h>
- +#include <asm/uaccess.h>
- +#include <asm/unaligned.h>
- +
- +#include "dwc_os.h"
- +#include "dwc_list.h"
- +
- +
- +/* MISC */
- +
- +void *DWC_MEMSET(void *dest, uint8_t byte, uint32_t size)
- +{
- + return memset(dest, byte, size);
- +}
- +
- +void *DWC_MEMCPY(void *dest, void const *src, uint32_t size)
- +{
- + return memcpy(dest, src, size);
- +}
- +
- +void *DWC_MEMMOVE(void *dest, void *src, uint32_t size)
- +{
- + return memmove(dest, src, size);
- +}
- +
- +int DWC_MEMCMP(void *m1, void *m2, uint32_t size)
- +{
- + return memcmp(m1, m2, size);
- +}
- +
- +int DWC_STRNCMP(void *s1, void *s2, uint32_t size)
- +{
- + return strncmp(s1, s2, size);
- +}
- +
- +int DWC_STRCMP(void *s1, void *s2)
- +{
- + return strcmp(s1, s2);
- +}
- +
- +int DWC_STRLEN(char const *str)
- +{
- + return strlen(str);
- +}
- +
- +char *DWC_STRCPY(char *to, char const *from)
- +{
- + return strcpy(to, from);
- +}
- +
- +char *DWC_STRDUP(char const *str)
- +{
- + int len = DWC_STRLEN(str) + 1;
- + char *new = DWC_ALLOC_ATOMIC(len);
- +
- + if (!new) {
- + return NULL;
- + }
- +
- + DWC_MEMCPY(new, str, len);
- + return new;
- +}
- +
- +int DWC_ATOI(const char *str, int32_t *value)
- +{
- + char *end = NULL;
- +
- + *value = simple_strtol(str, &end, 0);
- + if (*end == '\0') {
- + return 0;
- + }
- +
- + return -1;
- +}
- +
- +int DWC_ATOUI(const char *str, uint32_t *value)
- +{
- + char *end = NULL;
- +
- + *value = simple_strtoul(str, &end, 0);
- + if (*end == '\0') {
- + return 0;
- + }
- +
- + return -1;
- +}
- +
- +
- +#ifdef DWC_UTFLIB
- +/* From usbstring.c */
- +
- +int DWC_UTF8_TO_UTF16LE(uint8_t const *s, uint16_t *cp, unsigned len)
- +{
- + int count = 0;
- + u8 c;
- + u16 uchar;
- +
- + /* this insists on correct encodings, though not minimal ones.
- + * BUT it currently rejects legit 4-byte UTF-8 code points,
- + * which need surrogate pairs. (Unicode 3.1 can use them.)
- + */
- + while (len != 0 && (c = (u8) *s++) != 0) {
- + if (unlikely(c & 0x80)) {
- + // 2-byte sequence:
- + // 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx
- + if ((c & 0xe0) == 0xc0) {
- + uchar = (c & 0x1f) << 6;
- +
- + c = (u8) *s++;
- + if ((c & 0xc0) != 0xc0)
- + goto fail;
- + c &= 0x3f;
- + uchar |= c;
- +
- + // 3-byte sequence (most CJKV characters):
- + // zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx
- + } else if ((c & 0xf0) == 0xe0) {
- + uchar = (c & 0x0f) << 12;
- +
- + c = (u8) *s++;
- + if ((c & 0xc0) != 0xc0)
- + goto fail;
- + c &= 0x3f;
- + uchar |= c << 6;
- +
- + c = (u8) *s++;
- + if ((c & 0xc0) != 0xc0)
- + goto fail;
- + c &= 0x3f;
- + uchar |= c;
- +
- + /* no bogus surrogates */
- + if (0xd800 <= uchar && uchar <= 0xdfff)
- + goto fail;
- +
- + // 4-byte sequence (surrogate pairs, currently rare):
- + // 11101110wwwwzzzzyy + 110111yyyyxxxxxx
- + // = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
- + // (uuuuu = wwww + 1)
- + // FIXME accept the surrogate code points (only)
- + } else
- + goto fail;
- + } else
- + uchar = c;
- + put_unaligned (cpu_to_le16 (uchar), cp++);
- + count++;
- + len--;
- + }
- + return count;
- +fail:
- + return -1;
- +}
- +#endif /* DWC_UTFLIB */
- +
- +
- +/* dwc_debug.h */
- +
- +dwc_bool_t DWC_IN_IRQ(void)
- +{
- + return in_irq();
- +}
- +
- +dwc_bool_t DWC_IN_BH(void)
- +{
- + return in_softirq();
- +}
- +
- +void DWC_VPRINTF(char *format, va_list args)
- +{
- + vprintk(format, args);
- +}
- +
- +int DWC_VSNPRINTF(char *str, int size, char *format, va_list args)
- +{
- + return vsnprintf(str, size, format, args);
- +}
- +
- +void DWC_PRINTF(char *format, ...)
- +{
- + va_list args;
- +
- + va_start(args, format);
- + DWC_VPRINTF(format, args);
- + va_end(args);
- +}
- +
- +int DWC_SPRINTF(char *buffer, char *format, ...)
- +{
- + int retval;
- + va_list args;
- +
- + va_start(args, format);
- + retval = vsprintf(buffer, format, args);
- + va_end(args);
- + return retval;
- +}
- +
- +int DWC_SNPRINTF(char *buffer, int size, char *format, ...)
- +{
- + int retval;
- + va_list args;
- +
- + va_start(args, format);
- + retval = vsnprintf(buffer, size, format, args);
- + va_end(args);
- + return retval;
- +}
- +
- +void __DWC_WARN(char *format, ...)
- +{
- + va_list args;
- +
- + va_start(args, format);
- + DWC_PRINTF(KERN_WARNING);
- + DWC_VPRINTF(format, args);
- + va_end(args);
- +}
- +
- +void __DWC_ERROR(char *format, ...)
- +{
- + va_list args;
- +
- + va_start(args, format);
- + DWC_PRINTF(KERN_ERR);
- + DWC_VPRINTF(format, args);
- + va_end(args);
- +}
- +
- +void DWC_EXCEPTION(char *format, ...)
- +{
- + va_list args;
- +
- + va_start(args, format);
- + DWC_PRINTF(KERN_ERR);
- + DWC_VPRINTF(format, args);
- + va_end(args);
- + BUG_ON(1);
- +}
- +
- +#ifdef DEBUG
- +void __DWC_DEBUG(char *format, ...)
- +{
- + va_list args;
- +
- + va_start(args, format);
- + DWC_PRINTF(KERN_DEBUG);
- + DWC_VPRINTF(format, args);
- + va_end(args);
- +}
- +#endif
- +
- +
- +/* dwc_mem.h */
- +
- +#if 0
- +dwc_pool_t *DWC_DMA_POOL_CREATE(uint32_t size,
- + uint32_t align,
- + uint32_t alloc)
- +{
- + struct dma_pool *pool = dma_pool_create("Pool", NULL,
- + size, align, alloc);
- + return (dwc_pool_t *)pool;
- +}
- +
- +void DWC_DMA_POOL_DESTROY(dwc_pool_t *pool)
- +{
- + dma_pool_destroy((struct dma_pool *)pool);
- +}
- +
- +void *DWC_DMA_POOL_ALLOC(dwc_pool_t *pool, uint64_t *dma_addr)
- +{
- + return dma_pool_alloc((struct dma_pool *)pool, GFP_KERNEL, dma_addr);
- +}
- +
- +void *DWC_DMA_POOL_ZALLOC(dwc_pool_t *pool, uint64_t *dma_addr)
- +{
- + void *vaddr = DWC_DMA_POOL_ALLOC(pool, dma_addr);
- + memset(..);
- +}
- +
- +void DWC_DMA_POOL_FREE(dwc_pool_t *pool, void *vaddr, void *daddr)
- +{
- + dma_pool_free(pool, vaddr, daddr);
- +}
- +#endif
- +
- +void *__DWC_DMA_ALLOC(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr)
- +{
- + return dma_alloc_coherent(dma_ctx, size, dma_addr, GFP_KERNEL | GFP_DMA32);
- +}
- +
- +void *__DWC_DMA_ALLOC_ATOMIC(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr)
- +{
- + return dma_alloc_coherent(dma_ctx, size, dma_addr, GFP_ATOMIC);
- +}
- +
- +void __DWC_DMA_FREE(void *dma_ctx, uint32_t size, void *virt_addr, dwc_dma_t dma_addr)
- +{
- + dma_free_coherent(dma_ctx, size, virt_addr, dma_addr);
- +}
- +
- +void *__DWC_ALLOC(void *mem_ctx, uint32_t size)
- +{
- + return kzalloc(size, GFP_KERNEL);
- +}
- +
- +void *__DWC_ALLOC_ATOMIC(void *mem_ctx, uint32_t size)
- +{
- + return kzalloc(size, GFP_ATOMIC);
- +}
- +
- +void __DWC_FREE(void *mem_ctx, void *addr)
- +{
- + kfree(addr);
- +}
- +
- +
- +#ifdef DWC_CRYPTOLIB
- +/* dwc_crypto.h */
- +
- +void DWC_RANDOM_BYTES(uint8_t *buffer, uint32_t length)
- +{
- + get_random_bytes(buffer, length);
- +}
- +
- +int DWC_AES_CBC(uint8_t *message, uint32_t messagelen, uint8_t *key, uint32_t keylen, uint8_t iv[16], uint8_t *out)
- +{
- + struct crypto_blkcipher *tfm;
- + struct blkcipher_desc desc;
- + struct scatterlist sgd;
- + struct scatterlist sgs;
- +
- + tfm = crypto_alloc_blkcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC);
- + if (tfm == NULL) {
- + printk("failed to load transform for aes CBC\n");
- + return -1;
- + }
- +
- + crypto_blkcipher_setkey(tfm, key, keylen);
- + crypto_blkcipher_set_iv(tfm, iv, 16);
- +
- + sg_init_one(&sgd, out, messagelen);
- + sg_init_one(&sgs, message, messagelen);
- +
- + desc.tfm = tfm;
- + desc.flags = 0;
- +
- + if (crypto_blkcipher_encrypt(&desc, &sgd, &sgs, messagelen)) {
- + crypto_free_blkcipher(tfm);
- + DWC_ERROR("AES CBC encryption failed");
- + return -1;
- + }
- +
- + crypto_free_blkcipher(tfm);
- + return 0;
- +}
- +
- +int DWC_SHA256(uint8_t *message, uint32_t len, uint8_t *out)
- +{
- + struct crypto_hash *tfm;
- + struct hash_desc desc;
- + struct scatterlist sg;
- +
- + tfm = crypto_alloc_hash("sha256", 0, CRYPTO_ALG_ASYNC);
- + if (IS_ERR(tfm)) {
- + DWC_ERROR("Failed to load transform for sha256: %ld\n", PTR_ERR(tfm));
- + return 0;
- + }
- + desc.tfm = tfm;
- + desc.flags = 0;
- +
- + sg_init_one(&sg, message, len);
- + crypto_hash_digest(&desc, &sg, len, out);
- + crypto_free_hash(tfm);
- +
- + return 1;
- +}
- +
- +int DWC_HMAC_SHA256(uint8_t *message, uint32_t messagelen,
- + uint8_t *key, uint32_t keylen, uint8_t *out)
- +{
- + struct crypto_hash *tfm;
- + struct hash_desc desc;
- + struct scatterlist sg;
- +
- + tfm = crypto_alloc_hash("hmac(sha256)", 0, CRYPTO_ALG_ASYNC);
- + if (IS_ERR(tfm)) {
- + DWC_ERROR("Failed to load transform for hmac(sha256): %ld\n", PTR_ERR(tfm));
- + return 0;
- + }
- + desc.tfm = tfm;
- + desc.flags = 0;
- +
- + sg_init_one(&sg, message, messagelen);
- + crypto_hash_setkey(tfm, key, keylen);
- + crypto_hash_digest(&desc, &sg, messagelen, out);
- + crypto_free_hash(tfm);
- +
- + return 1;
- +}
- +#endif /* DWC_CRYPTOLIB */
- +
- +
- +/* Byte Ordering Conversions */
- +
- +uint32_t DWC_CPU_TO_LE32(uint32_t *p)
- +{
- +#ifdef __LITTLE_ENDIAN
- + return *p;
- +#else
- + uint8_t *u_p = (uint8_t *)p;
- +
- + return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
- +#endif
- +}
- +
- +uint32_t DWC_CPU_TO_BE32(uint32_t *p)
- +{
- +#ifdef __BIG_ENDIAN
- + return *p;
- +#else
- + uint8_t *u_p = (uint8_t *)p;
- +
- + return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
- +#endif
- +}
- +
- +uint32_t DWC_LE32_TO_CPU(uint32_t *p)
- +{
- +#ifdef __LITTLE_ENDIAN
- + return *p;
- +#else
- + uint8_t *u_p = (uint8_t *)p;
- +
- + return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
- +#endif
- +}
- +
- +uint32_t DWC_BE32_TO_CPU(uint32_t *p)
- +{
- +#ifdef __BIG_ENDIAN
- + return *p;
- +#else
- + uint8_t *u_p = (uint8_t *)p;
- +
- + return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
- +#endif
- +}
- +
- +uint16_t DWC_CPU_TO_LE16(uint16_t *p)
- +{
- +#ifdef __LITTLE_ENDIAN
- + return *p;
- +#else
- + uint8_t *u_p = (uint8_t *)p;
- + return (u_p[1] | (u_p[0] << 8));
- +#endif
- +}
- +
- +uint16_t DWC_CPU_TO_BE16(uint16_t *p)
- +{
- +#ifdef __BIG_ENDIAN
- + return *p;
- +#else
- + uint8_t *u_p = (uint8_t *)p;
- + return (u_p[1] | (u_p[0] << 8));
- +#endif
- +}
- +
- +uint16_t DWC_LE16_TO_CPU(uint16_t *p)
- +{
- +#ifdef __LITTLE_ENDIAN
- + return *p;
- +#else
- + uint8_t *u_p = (uint8_t *)p;
- + return (u_p[1] | (u_p[0] << 8));
- +#endif
- +}
- +
- +uint16_t DWC_BE16_TO_CPU(uint16_t *p)
- +{
- +#ifdef __BIG_ENDIAN
- + return *p;
- +#else
- + uint8_t *u_p = (uint8_t *)p;
- + return (u_p[1] | (u_p[0] << 8));
- +#endif
- +}
- +
- +
- +/* Registers */
- +
- +uint32_t DWC_READ_REG32(uint32_t volatile *reg)
- +{
- + return readl(reg);
- +}
- +
- +#if 0
- +uint64_t DWC_READ_REG64(uint64_t volatile *reg)
- +{
- +}
- +#endif
- +
- +void DWC_WRITE_REG32(uint32_t volatile *reg, uint32_t value)
- +{
- + writel(value, reg);
- +}
- +
- +#if 0
- +void DWC_WRITE_REG64(uint64_t volatile *reg, uint64_t value)
- +{
- +}
- +#endif
- +
- +void DWC_MODIFY_REG32(uint32_t volatile *reg, uint32_t clear_mask, uint32_t set_mask)
- +{
- + writel((readl(reg) & ~clear_mask) | set_mask, reg);
- +}
- +
- +#if 0
- +void DWC_MODIFY_REG64(uint64_t volatile *reg, uint64_t clear_mask, uint64_t set_mask)
- +{
- +}
- +#endif
- +
- +
- +/* Locking */
- +
- +dwc_spinlock_t *DWC_SPINLOCK_ALLOC(void)
- +{
- + spinlock_t *sl = (spinlock_t *)1;
- +
- +#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP)
- + sl = DWC_ALLOC(sizeof(*sl));
- + if (!sl) {
- + DWC_ERROR("Cannot allocate memory for spinlock\n");
- + return NULL;
- + }
- +
- + spin_lock_init(sl);
- +#endif
- + return (dwc_spinlock_t *)sl;
- +}
- +
- +void DWC_SPINLOCK_FREE(dwc_spinlock_t *lock)
- +{
- +#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP)
- + DWC_FREE(lock);
- +#endif
- +}
- +
- +void DWC_SPINLOCK(dwc_spinlock_t *lock)
- +{
- +#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP)
- + spin_lock((spinlock_t *)lock);
- +#endif
- +}
- +
- +void DWC_SPINUNLOCK(dwc_spinlock_t *lock)
- +{
- +#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP)
- + spin_unlock((spinlock_t *)lock);
- +#endif
- +}
- +
- +void DWC_SPINLOCK_IRQSAVE(dwc_spinlock_t *lock, dwc_irqflags_t *flags)
- +{
- + dwc_irqflags_t f;
- +
- +#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP)
- + spin_lock_irqsave((spinlock_t *)lock, f);
- +#else
- + local_irq_save(f);
- +#endif
- + *flags = f;
- +}
- +
- +void DWC_SPINUNLOCK_IRQRESTORE(dwc_spinlock_t *lock, dwc_irqflags_t flags)
- +{
- +#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP)
- + spin_unlock_irqrestore((spinlock_t *)lock, flags);
- +#else
- + local_irq_restore(flags);
- +#endif
- +}
- +
- +dwc_mutex_t *DWC_MUTEX_ALLOC(void)
- +{
- + struct mutex *m;
- + dwc_mutex_t *mutex = (dwc_mutex_t *)DWC_ALLOC(sizeof(struct mutex));
- +
- + if (!mutex) {
- + DWC_ERROR("Cannot allocate memory for mutex\n");
- + return NULL;
- + }
- +
- + m = (struct mutex *)mutex;
- + mutex_init(m);
- + return mutex;
- +}
- +
- +#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES))
- +#else
- +void DWC_MUTEX_FREE(dwc_mutex_t *mutex)
- +{
- + mutex_destroy((struct mutex *)mutex);
- + DWC_FREE(mutex);
- +}
- +#endif
- +
- +void DWC_MUTEX_LOCK(dwc_mutex_t *mutex)
- +{
- + struct mutex *m = (struct mutex *)mutex;
- + mutex_lock(m);
- +}
- +
- +int DWC_MUTEX_TRYLOCK(dwc_mutex_t *mutex)
- +{
- + struct mutex *m = (struct mutex *)mutex;
- + return mutex_trylock(m);
- +}
- +
- +void DWC_MUTEX_UNLOCK(dwc_mutex_t *mutex)
- +{
- + struct mutex *m = (struct mutex *)mutex;
- + mutex_unlock(m);
- +}
- +
- +
- +/* Timing */
- +
- +void DWC_UDELAY(uint32_t usecs)
- +{
- + udelay(usecs);
- +}
- +
- +void DWC_MDELAY(uint32_t msecs)
- +{
- + mdelay(msecs);
- +}
- +
- +void DWC_MSLEEP(uint32_t msecs)
- +{
- + msleep(msecs);
- +}
- +
- +uint32_t DWC_TIME(void)
- +{
- + return jiffies_to_msecs(jiffies);
- +}
- +
- +
- +/* Timers */
- +
- +struct dwc_timer {
- + struct timer_list t;
- + char *name;
- + dwc_timer_callback_t cb;
- + void *data;
- + uint8_t scheduled;
- + dwc_spinlock_t *lock;
- +};
- +
- +static void timer_callback(struct timer_list *tt)
- +{
- + dwc_timer_t *timer = from_timer(timer, tt, t);
- + dwc_irqflags_t flags;
- +
- + DWC_SPINLOCK_IRQSAVE(timer->lock, &flags);
- + timer->scheduled = 0;
- + DWC_SPINUNLOCK_IRQRESTORE(timer->lock, flags);
- + DWC_DEBUGC("Timer %s callback", timer->name);
- + timer->cb(timer->data);
- +}
- +
- +dwc_timer_t *DWC_TIMER_ALLOC(char *name, dwc_timer_callback_t cb, void *data)
- +{
- + dwc_timer_t *t = DWC_ALLOC(sizeof(*t));
- +
- + if (!t) {
- + DWC_ERROR("Cannot allocate memory for timer");
- + return NULL;
- + }
- +
- + t->name = DWC_STRDUP(name);
- + if (!t->name) {
- + DWC_ERROR("Cannot allocate memory for timer->name");
- + goto no_name;
- + }
- +
- +#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_SPINLOCK))
- + DWC_SPINLOCK_ALLOC_LINUX_DEBUG(t->lock);
- +#else
- + t->lock = DWC_SPINLOCK_ALLOC();
- +#endif
- + if (!t->lock) {
- + DWC_ERROR("Cannot allocate memory for lock");
- + goto no_lock;
- + }
- +
- + t->scheduled = 0;
- + t->t.expires = jiffies;
- + timer_setup(&t->t, timer_callback, 0);
- +
- + t->cb = cb;
- + t->data = data;
- +
- + return t;
- +
- + no_lock:
- + DWC_FREE(t->name);
- + no_name:
- + DWC_FREE(t);
- + return NULL;
- +}
- +
- +void DWC_TIMER_FREE(dwc_timer_t *timer)
- +{
- + dwc_irqflags_t flags;
- +
- + DWC_SPINLOCK_IRQSAVE(timer->lock, &flags);
- +
- + if (timer->scheduled) {
- + del_timer(&timer->t);
- + timer->scheduled = 0;
- + }
- +
- + DWC_SPINUNLOCK_IRQRESTORE(timer->lock, flags);
- + DWC_SPINLOCK_FREE(timer->lock);
- + DWC_FREE(timer->name);
- + DWC_FREE(timer);
- +}
- +
- +void DWC_TIMER_SCHEDULE(dwc_timer_t *timer, uint32_t time)
- +{
- + dwc_irqflags_t flags;
- +
- + DWC_SPINLOCK_IRQSAVE(timer->lock, &flags);
- +
- + if (!timer->scheduled) {
- + timer->scheduled = 1;
- + DWC_DEBUGC("Scheduling timer %s to expire in +%d msec", timer->name, time);
- + timer->t.expires = jiffies + msecs_to_jiffies(time);
- + add_timer(&timer->t);
- + } else {
- + DWC_DEBUGC("Modifying timer %s to expire in +%d msec", timer->name, time);
- + mod_timer(&timer->t, jiffies + msecs_to_jiffies(time));
- + }
- +
- + DWC_SPINUNLOCK_IRQRESTORE(timer->lock, flags);
- +}
- +
- +void DWC_TIMER_CANCEL(dwc_timer_t *timer)
- +{
- + del_timer(&timer->t);
- +}
- +
- +
- +/* Wait Queues */
- +
- +struct dwc_waitq {
- + wait_queue_head_t queue;
- + int abort;
- +};
- +
- +dwc_waitq_t *DWC_WAITQ_ALLOC(void)
- +{
- + dwc_waitq_t *wq = DWC_ALLOC(sizeof(*wq));
- +
- + if (!wq) {
- + DWC_ERROR("Cannot allocate memory for waitqueue\n");
- + return NULL;
- + }
- +
- + init_waitqueue_head(&wq->queue);
- + wq->abort = 0;
- + return wq;
- +}
- +
- +void DWC_WAITQ_FREE(dwc_waitq_t *wq)
- +{
- + DWC_FREE(wq);
- +}
- +
- +int32_t DWC_WAITQ_WAIT(dwc_waitq_t *wq, dwc_waitq_condition_t cond, void *data)
- +{
- + int result = wait_event_interruptible(wq->queue,
- + cond(data) || wq->abort);
- + if (result == -ERESTARTSYS) {
- + wq->abort = 0;
- + return -DWC_E_RESTART;
- + }
- +
- + if (wq->abort == 1) {
- + wq->abort = 0;
- + return -DWC_E_ABORT;
- + }
- +
- + wq->abort = 0;
- +
- + if (result == 0) {
- + return 0;
- + }
- +
- + return -DWC_E_UNKNOWN;
- +}
- +
- +int32_t DWC_WAITQ_WAIT_TIMEOUT(dwc_waitq_t *wq, dwc_waitq_condition_t cond,
- + void *data, int32_t msecs)
- +{
- + int32_t tmsecs;
- + int result = wait_event_interruptible_timeout(wq->queue,
- + cond(data) || wq->abort,
- + msecs_to_jiffies(msecs));
- + if (result == -ERESTARTSYS) {
- + wq->abort = 0;
- + return -DWC_E_RESTART;
- + }
- +
- + if (wq->abort == 1) {
- + wq->abort = 0;
- + return -DWC_E_ABORT;
- + }
- +
- + wq->abort = 0;
- +
- + if (result > 0) {
- + tmsecs = jiffies_to_msecs(result);
- + if (!tmsecs) {
- + return 1;
- + }
- +
- + return tmsecs;
- + }
- +
- + if (result == 0) {
- + return -DWC_E_TIMEOUT;
- + }
- +
- + return -DWC_E_UNKNOWN;
- +}
- +
- +void DWC_WAITQ_TRIGGER(dwc_waitq_t *wq)
- +{
- + wq->abort = 0;
- + wake_up_interruptible(&wq->queue);
- +}
- +
- +void DWC_WAITQ_ABORT(dwc_waitq_t *wq)
- +{
- + wq->abort = 1;
- + wake_up_interruptible(&wq->queue);
- +}
- +
- +
- +/* Threading */
- +
- +dwc_thread_t *DWC_THREAD_RUN(dwc_thread_function_t func, char *name, void *data)
- +{
- + struct task_struct *thread = kthread_run(func, data, name);
- +
- + if (thread == ERR_PTR(-ENOMEM)) {
- + return NULL;
- + }
- +
- + return (dwc_thread_t *)thread;
- +}
- +
- +int DWC_THREAD_STOP(dwc_thread_t *thread)
- +{
- + return kthread_stop((struct task_struct *)thread);
- +}
- +
- +dwc_bool_t DWC_THREAD_SHOULD_STOP(void)
- +{
- + return kthread_should_stop();
- +}
- +
- +
- +/* tasklets
- + - run in interrupt context (cannot sleep)
- + - each tasklet runs on a single CPU
- + - different tasklets can be running simultaneously on different CPUs
- + */
- +struct dwc_tasklet {
- + struct tasklet_struct t;
- + dwc_tasklet_callback_t cb;
- + void *data;
- +};
- +
- +static void tasklet_callback(unsigned long data)
- +{
- + dwc_tasklet_t *t = (dwc_tasklet_t *)data;
- + t->cb(t->data);
- +}
- +
- +dwc_tasklet_t *DWC_TASK_ALLOC(char *name, dwc_tasklet_callback_t cb, void *data)
- +{
- + dwc_tasklet_t *t = DWC_ALLOC(sizeof(*t));
- +
- + if (t) {
- + t->cb = cb;
- + t->data = data;
- + tasklet_init(&t->t, tasklet_callback, (unsigned long)t);
- + } else {
- + DWC_ERROR("Cannot allocate memory for tasklet\n");
- + }
- +
- + return t;
- +}
- +
- +void DWC_TASK_FREE(dwc_tasklet_t *task)
- +{
- + DWC_FREE(task);
- +}
- +
- +void DWC_TASK_SCHEDULE(dwc_tasklet_t *task)
- +{
- + tasklet_schedule(&task->t);
- +}
- +
- +void DWC_TASK_HI_SCHEDULE(dwc_tasklet_t *task)
- +{
- + tasklet_hi_schedule(&task->t);
- +}
- +
- +
- +/* workqueues
- + - run in process context (can sleep)
- + */
- +typedef struct work_container {
- + dwc_work_callback_t cb;
- + void *data;
- + dwc_workq_t *wq;
- + char *name;
- +
- +#ifdef DEBUG
- + DWC_CIRCLEQ_ENTRY(work_container) entry;
- +#endif
- + struct delayed_work work;
- +} work_container_t;
- +
- +#ifdef DEBUG
- +DWC_CIRCLEQ_HEAD(work_container_queue, work_container);
- +#endif
- +
- +struct dwc_workq {
- + struct workqueue_struct *wq;
- + dwc_spinlock_t *lock;
- + dwc_waitq_t *waitq;
- + int pending;
- +
- +#ifdef DEBUG
- + struct work_container_queue entries;
- +#endif
- +};
- +
- +static void do_work(struct work_struct *work)
- +{
- + dwc_irqflags_t flags;
- + struct delayed_work *dw = container_of(work, struct delayed_work, work);
- + work_container_t *container = container_of(dw, struct work_container, work);
- + dwc_workq_t *wq = container->wq;
- +
- + container->cb(container->data);
- +
- +#ifdef DEBUG
- + DWC_CIRCLEQ_REMOVE(&wq->entries, container, entry);
- +#endif
- + DWC_DEBUGC("Work done: %s, container=%p", container->name, container);
- + if (container->name) {
- + DWC_FREE(container->name);
- + }
- + DWC_FREE(container);
- +
- + DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
- + wq->pending--;
- + DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
- + DWC_WAITQ_TRIGGER(wq->waitq);
- +}
- +
- +static int work_done(void *data)
- +{
- + dwc_workq_t *workq = (dwc_workq_t *)data;
- + return workq->pending == 0;
- +}
- +
- +int DWC_WORKQ_WAIT_WORK_DONE(dwc_workq_t *workq, int timeout)
- +{
- + return DWC_WAITQ_WAIT_TIMEOUT(workq->waitq, work_done, workq, timeout);
- +}
- +
- +dwc_workq_t *DWC_WORKQ_ALLOC(char *name)
- +{
- + dwc_workq_t *wq = DWC_ALLOC(sizeof(*wq));
- +
- + if (!wq) {
- + return NULL;
- + }
- +
- + wq->wq = create_singlethread_workqueue(name);
- + if (!wq->wq) {
- + goto no_wq;
- + }
- +
- + wq->pending = 0;
- +
- +#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_SPINLOCK))
- + DWC_SPINLOCK_ALLOC_LINUX_DEBUG(wq->lock);
- +#else
- + wq->lock = DWC_SPINLOCK_ALLOC();
- +#endif
- + if (!wq->lock) {
- + goto no_lock;
- + }
- +
- + wq->waitq = DWC_WAITQ_ALLOC();
- + if (!wq->waitq) {
- + goto no_waitq;
- + }
- +
- +#ifdef DEBUG
- + DWC_CIRCLEQ_INIT(&wq->entries);
- +#endif
- + return wq;
- +
- + no_waitq:
- + DWC_SPINLOCK_FREE(wq->lock);
- + no_lock:
- + destroy_workqueue(wq->wq);
- + no_wq:
- + DWC_FREE(wq);
- +
- + return NULL;
- +}
- +
- +void DWC_WORKQ_FREE(dwc_workq_t *wq)
- +{
- +#ifdef DEBUG
- + if (wq->pending != 0) {
- + struct work_container *wc;
- + DWC_ERROR("Destroying work queue with pending work");
- + DWC_CIRCLEQ_FOREACH(wc, &wq->entries, entry) {
- + DWC_ERROR("Work %s still pending", wc->name);
- + }
- + }
- +#endif
- + destroy_workqueue(wq->wq);
- + DWC_SPINLOCK_FREE(wq->lock);
- + DWC_WAITQ_FREE(wq->waitq);
- + DWC_FREE(wq);
- +}
- +
- +void DWC_WORKQ_SCHEDULE(dwc_workq_t *wq, dwc_work_callback_t cb, void *data,
- + char *format, ...)
- +{
- + dwc_irqflags_t flags;
- + work_container_t *container;
- + static char name[128];
- + va_list args;
- +
- + va_start(args, format);
- + DWC_VSNPRINTF(name, 128, format, args);
- + va_end(args);
- +
- + DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
- + wq->pending++;
- + DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
- + DWC_WAITQ_TRIGGER(wq->waitq);
- +
- + container = DWC_ALLOC_ATOMIC(sizeof(*container));
- + if (!container) {
- + DWC_ERROR("Cannot allocate memory for container\n");
- + return;
- + }
- +
- + container->name = DWC_STRDUP(name);
- + if (!container->name) {
- + DWC_ERROR("Cannot allocate memory for container->name\n");
- + DWC_FREE(container);
- + return;
- + }
- +
- + container->cb = cb;
- + container->data = data;
- + container->wq = wq;
- + DWC_DEBUGC("Queueing work: %s, container=%p", container->name, container);
- + INIT_WORK(&container->work.work, do_work);
- +
- +#ifdef DEBUG
- + DWC_CIRCLEQ_INSERT_TAIL(&wq->entries, container, entry);
- +#endif
- + queue_work(wq->wq, &container->work.work);
- +}
- +
- +void DWC_WORKQ_SCHEDULE_DELAYED(dwc_workq_t *wq, dwc_work_callback_t cb,
- + void *data, uint32_t time, char *format, ...)
- +{
- + dwc_irqflags_t flags;
- + work_container_t *container;
- + static char name[128];
- + va_list args;
- +
- + va_start(args, format);
- + DWC_VSNPRINTF(name, 128, format, args);
- + va_end(args);
- +
- + DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
- + wq->pending++;
- + DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
- + DWC_WAITQ_TRIGGER(wq->waitq);
- +
- + container = DWC_ALLOC_ATOMIC(sizeof(*container));
- + if (!container) {
- + DWC_ERROR("Cannot allocate memory for container\n");
- + return;
- + }
- +
- + container->name = DWC_STRDUP(name);
- + if (!container->name) {
- + DWC_ERROR("Cannot allocate memory for container->name\n");
- + DWC_FREE(container);
- + return;
- + }
- +
- + container->cb = cb;
- + container->data = data;
- + container->wq = wq;
- + DWC_DEBUGC("Queueing work: %s, container=%p", container->name, container);
- + INIT_DELAYED_WORK(&container->work, do_work);
- +
- +#ifdef DEBUG
- + DWC_CIRCLEQ_INSERT_TAIL(&wq->entries, container, entry);
- +#endif
- + queue_delayed_work(wq->wq, &container->work, msecs_to_jiffies(time));
- +}
- +
- +int DWC_WORKQ_PENDING(dwc_workq_t *wq)
- +{
- + return wq->pending;
- +}
- +
- +
- +#ifdef DWC_LIBMODULE
- +
- +#ifdef DWC_CCLIB
- +/* CC */
- +EXPORT_SYMBOL(dwc_cc_if_alloc);
- +EXPORT_SYMBOL(dwc_cc_if_free);
- +EXPORT_SYMBOL(dwc_cc_clear);
- +EXPORT_SYMBOL(dwc_cc_add);
- +EXPORT_SYMBOL(dwc_cc_remove);
- +EXPORT_SYMBOL(dwc_cc_change);
- +EXPORT_SYMBOL(dwc_cc_data_for_save);
- +EXPORT_SYMBOL(dwc_cc_restore_from_data);
- +EXPORT_SYMBOL(dwc_cc_match_chid);
- +EXPORT_SYMBOL(dwc_cc_match_cdid);
- +EXPORT_SYMBOL(dwc_cc_ck);
- +EXPORT_SYMBOL(dwc_cc_chid);
- +EXPORT_SYMBOL(dwc_cc_cdid);
- +EXPORT_SYMBOL(dwc_cc_name);
- +#endif /* DWC_CCLIB */
- +
- +#ifdef DWC_CRYPTOLIB
- +# ifndef CONFIG_MACH_IPMATE
- +/* Modpow */
- +EXPORT_SYMBOL(dwc_modpow);
- +
- +/* DH */
- +EXPORT_SYMBOL(dwc_dh_modpow);
- +EXPORT_SYMBOL(dwc_dh_derive_keys);
- +EXPORT_SYMBOL(dwc_dh_pk);
- +# endif /* CONFIG_MACH_IPMATE */
- +
- +/* Crypto */
- +EXPORT_SYMBOL(dwc_wusb_aes_encrypt);
- +EXPORT_SYMBOL(dwc_wusb_cmf);
- +EXPORT_SYMBOL(dwc_wusb_prf);
- +EXPORT_SYMBOL(dwc_wusb_fill_ccm_nonce);
- +EXPORT_SYMBOL(dwc_wusb_gen_nonce);
- +EXPORT_SYMBOL(dwc_wusb_gen_key);
- +EXPORT_SYMBOL(dwc_wusb_gen_mic);
- +#endif /* DWC_CRYPTOLIB */
- +
- +/* Notification */
- +#ifdef DWC_NOTIFYLIB
- +EXPORT_SYMBOL(dwc_alloc_notification_manager);
- +EXPORT_SYMBOL(dwc_free_notification_manager);
- +EXPORT_SYMBOL(dwc_register_notifier);
- +EXPORT_SYMBOL(dwc_unregister_notifier);
- +EXPORT_SYMBOL(dwc_add_observer);
- +EXPORT_SYMBOL(dwc_remove_observer);
- +EXPORT_SYMBOL(dwc_notify);
- +#endif
- +
- +/* Memory Debugging Routines */
- +#ifdef DWC_DEBUG_MEMORY
- +EXPORT_SYMBOL(dwc_alloc_debug);
- +EXPORT_SYMBOL(dwc_alloc_atomic_debug);
- +EXPORT_SYMBOL(dwc_free_debug);
- +EXPORT_SYMBOL(dwc_dma_alloc_debug);
- +EXPORT_SYMBOL(dwc_dma_free_debug);
- +#endif
- +
- +EXPORT_SYMBOL(DWC_MEMSET);
- +EXPORT_SYMBOL(DWC_MEMCPY);
- +EXPORT_SYMBOL(DWC_MEMMOVE);
- +EXPORT_SYMBOL(DWC_MEMCMP);
- +EXPORT_SYMBOL(DWC_STRNCMP);
- +EXPORT_SYMBOL(DWC_STRCMP);
- +EXPORT_SYMBOL(DWC_STRLEN);
- +EXPORT_SYMBOL(DWC_STRCPY);
- +EXPORT_SYMBOL(DWC_STRDUP);
- +EXPORT_SYMBOL(DWC_ATOI);
- +EXPORT_SYMBOL(DWC_ATOUI);
- +
- +#ifdef DWC_UTFLIB
- +EXPORT_SYMBOL(DWC_UTF8_TO_UTF16LE);
- +#endif /* DWC_UTFLIB */
- +
- +EXPORT_SYMBOL(DWC_IN_IRQ);
- +EXPORT_SYMBOL(DWC_IN_BH);
- +EXPORT_SYMBOL(DWC_VPRINTF);
- +EXPORT_SYMBOL(DWC_VSNPRINTF);
- +EXPORT_SYMBOL(DWC_PRINTF);
- +EXPORT_SYMBOL(DWC_SPRINTF);
- +EXPORT_SYMBOL(DWC_SNPRINTF);
- +EXPORT_SYMBOL(__DWC_WARN);
- +EXPORT_SYMBOL(__DWC_ERROR);
- +EXPORT_SYMBOL(DWC_EXCEPTION);
- +
- +#ifdef DEBUG
- +EXPORT_SYMBOL(__DWC_DEBUG);
- +#endif
- +
- +EXPORT_SYMBOL(__DWC_DMA_ALLOC);
- +EXPORT_SYMBOL(__DWC_DMA_ALLOC_ATOMIC);
- +EXPORT_SYMBOL(__DWC_DMA_FREE);
- +EXPORT_SYMBOL(__DWC_ALLOC);
- +EXPORT_SYMBOL(__DWC_ALLOC_ATOMIC);
- +EXPORT_SYMBOL(__DWC_FREE);
- +
- +#ifdef DWC_CRYPTOLIB
- +EXPORT_SYMBOL(DWC_RANDOM_BYTES);
- +EXPORT_SYMBOL(DWC_AES_CBC);
- +EXPORT_SYMBOL(DWC_SHA256);
- +EXPORT_SYMBOL(DWC_HMAC_SHA256);
- +#endif
- +
- +EXPORT_SYMBOL(DWC_CPU_TO_LE32);
- +EXPORT_SYMBOL(DWC_CPU_TO_BE32);
- +EXPORT_SYMBOL(DWC_LE32_TO_CPU);
- +EXPORT_SYMBOL(DWC_BE32_TO_CPU);
- +EXPORT_SYMBOL(DWC_CPU_TO_LE16);
- +EXPORT_SYMBOL(DWC_CPU_TO_BE16);
- +EXPORT_SYMBOL(DWC_LE16_TO_CPU);
- +EXPORT_SYMBOL(DWC_BE16_TO_CPU);
- +EXPORT_SYMBOL(DWC_READ_REG32);
- +EXPORT_SYMBOL(DWC_WRITE_REG32);
- +EXPORT_SYMBOL(DWC_MODIFY_REG32);
- +
- +#if 0
- +EXPORT_SYMBOL(DWC_READ_REG64);
- +EXPORT_SYMBOL(DWC_WRITE_REG64);
- +EXPORT_SYMBOL(DWC_MODIFY_REG64);
- +#endif
- +
- +EXPORT_SYMBOL(DWC_SPINLOCK_ALLOC);
- +EXPORT_SYMBOL(DWC_SPINLOCK_FREE);
- +EXPORT_SYMBOL(DWC_SPINLOCK);
- +EXPORT_SYMBOL(DWC_SPINUNLOCK);
- +EXPORT_SYMBOL(DWC_SPINLOCK_IRQSAVE);
- +EXPORT_SYMBOL(DWC_SPINUNLOCK_IRQRESTORE);
- +EXPORT_SYMBOL(DWC_MUTEX_ALLOC);
- +
- +#if (!defined(DWC_LINUX) || !defined(CONFIG_DEBUG_MUTEXES))
- +EXPORT_SYMBOL(DWC_MUTEX_FREE);
- +#endif
- +
- +EXPORT_SYMBOL(DWC_MUTEX_LOCK);
- +EXPORT_SYMBOL(DWC_MUTEX_TRYLOCK);
- +EXPORT_SYMBOL(DWC_MUTEX_UNLOCK);
- +EXPORT_SYMBOL(DWC_UDELAY);
- +EXPORT_SYMBOL(DWC_MDELAY);
- +EXPORT_SYMBOL(DWC_MSLEEP);
- +EXPORT_SYMBOL(DWC_TIME);
- +EXPORT_SYMBOL(DWC_TIMER_ALLOC);
- +EXPORT_SYMBOL(DWC_TIMER_FREE);
- +EXPORT_SYMBOL(DWC_TIMER_SCHEDULE);
- +EXPORT_SYMBOL(DWC_TIMER_CANCEL);
- +EXPORT_SYMBOL(DWC_WAITQ_ALLOC);
- +EXPORT_SYMBOL(DWC_WAITQ_FREE);
- +EXPORT_SYMBOL(DWC_WAITQ_WAIT);
- +EXPORT_SYMBOL(DWC_WAITQ_WAIT_TIMEOUT);
- +EXPORT_SYMBOL(DWC_WAITQ_TRIGGER);
- +EXPORT_SYMBOL(DWC_WAITQ_ABORT);
- +EXPORT_SYMBOL(DWC_THREAD_RUN);
- +EXPORT_SYMBOL(DWC_THREAD_STOP);
- +EXPORT_SYMBOL(DWC_THREAD_SHOULD_STOP);
- +EXPORT_SYMBOL(DWC_TASK_ALLOC);
- +EXPORT_SYMBOL(DWC_TASK_FREE);
- +EXPORT_SYMBOL(DWC_TASK_SCHEDULE);
- +EXPORT_SYMBOL(DWC_WORKQ_WAIT_WORK_DONE);
- +EXPORT_SYMBOL(DWC_WORKQ_ALLOC);
- +EXPORT_SYMBOL(DWC_WORKQ_FREE);
- +EXPORT_SYMBOL(DWC_WORKQ_SCHEDULE);
- +EXPORT_SYMBOL(DWC_WORKQ_SCHEDULE_DELAYED);
- +EXPORT_SYMBOL(DWC_WORKQ_PENDING);
- +
- +static int dwc_common_port_init_module(void)
- +{
- + int result = 0;
- +
- + printk(KERN_DEBUG "Module dwc_common_port init\n" );
- +
- +#ifdef DWC_DEBUG_MEMORY
- + result = dwc_memory_debug_start(NULL);
- + if (result) {
- + printk(KERN_ERR
- + "dwc_memory_debug_start() failed with error %d\n",
- + result);
- + return result;
- + }
- +#endif
- +
- +#ifdef DWC_NOTIFYLIB
- + result = dwc_alloc_notification_manager(NULL, NULL);
- + if (result) {
- + printk(KERN_ERR
- + "dwc_alloc_notification_manager() failed with error %d\n",
- + result);
- + return result;
- + }
- +#endif
- + return result;
- +}
- +
- +static void dwc_common_port_exit_module(void)
- +{
- + printk(KERN_DEBUG "Module dwc_common_port exit\n" );
- +
- +#ifdef DWC_NOTIFYLIB
- + dwc_free_notification_manager();
- +#endif
- +
- +#ifdef DWC_DEBUG_MEMORY
- + dwc_memory_debug_stop();
- +#endif
- +}
- +
- +module_init(dwc_common_port_init_module);
- +module_exit(dwc_common_port_exit_module);
- +
- +MODULE_DESCRIPTION("DWC Common Library - Portable version");
- +MODULE_AUTHOR("Synopsys Inc.");
- +MODULE_LICENSE ("GPL");
- +
- +#endif /* DWC_LIBMODULE */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_common_port/dwc_common_nbsd.c
- @@ -0,0 +1,1275 @@
- +#include "dwc_os.h"
- +#include "dwc_list.h"
- +
- +#ifdef DWC_CCLIB
- +# include "dwc_cc.h"
- +#endif
- +
- +#ifdef DWC_CRYPTOLIB
- +# include "dwc_modpow.h"
- +# include "dwc_dh.h"
- +# include "dwc_crypto.h"
- +#endif
- +
- +#ifdef DWC_NOTIFYLIB
- +# include "dwc_notifier.h"
- +#endif
- +
- +/* OS-Level Implementations */
- +
- +/* This is the NetBSD 4.0.1 kernel implementation of the DWC platform library. */
- +
- +
- +/* MISC */
- +
- +void *DWC_MEMSET(void *dest, uint8_t byte, uint32_t size)
- +{
- + return memset(dest, byte, size);
- +}
- +
- +void *DWC_MEMCPY(void *dest, void const *src, uint32_t size)
- +{
- + return memcpy(dest, src, size);
- +}
- +
- +void *DWC_MEMMOVE(void *dest, void *src, uint32_t size)
- +{
- + bcopy(src, dest, size);
- + return dest;
- +}
- +
- +int DWC_MEMCMP(void *m1, void *m2, uint32_t size)
- +{
- + return memcmp(m1, m2, size);
- +}
- +
- +int DWC_STRNCMP(void *s1, void *s2, uint32_t size)
- +{
- + return strncmp(s1, s2, size);
- +}
- +
- +int DWC_STRCMP(void *s1, void *s2)
- +{
- + return strcmp(s1, s2);
- +}
- +
- +int DWC_STRLEN(char const *str)
- +{
- + return strlen(str);
- +}
- +
- +char *DWC_STRCPY(char *to, char const *from)
- +{
- + return strcpy(to, from);
- +}
- +
- +char *DWC_STRDUP(char const *str)
- +{
- + int len = DWC_STRLEN(str) + 1;
- + char *new = DWC_ALLOC_ATOMIC(len);
- +
- + if (!new) {
- + return NULL;
- + }
- +
- + DWC_MEMCPY(new, str, len);
- + return new;
- +}
- +
- +int DWC_ATOI(char *str, int32_t *value)
- +{
- + char *end = NULL;
- +
- + /* NetBSD doesn't have 'strtol' in the kernel, but 'strtoul'
- + * should be equivalent on 2's complement machines
- + */
- + *value = strtoul(str, &end, 0);
- + if (*end == '\0') {
- + return 0;
- + }
- +
- + return -1;
- +}
- +
- +int DWC_ATOUI(char *str, uint32_t *value)
- +{
- + char *end = NULL;
- +
- + *value = strtoul(str, &end, 0);
- + if (*end == '\0') {
- + return 0;
- + }
- +
- + return -1;
- +}
- +
- +
- +#ifdef DWC_UTFLIB
- +/* From usbstring.c */
- +
- +int DWC_UTF8_TO_UTF16LE(uint8_t const *s, uint16_t *cp, unsigned len)
- +{
- + int count = 0;
- + u8 c;
- + u16 uchar;
- +
- + /* this insists on correct encodings, though not minimal ones.
- + * BUT it currently rejects legit 4-byte UTF-8 code points,
- + * which need surrogate pairs. (Unicode 3.1 can use them.)
- + */
- + while (len != 0 && (c = (u8) *s++) != 0) {
- + if (unlikely(c & 0x80)) {
- + // 2-byte sequence:
- + // 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx
- + if ((c & 0xe0) == 0xc0) {
- + uchar = (c & 0x1f) << 6;
- +
- + c = (u8) *s++;
- + if ((c & 0xc0) != 0xc0)
- + goto fail;
- + c &= 0x3f;
- + uchar |= c;
- +
- + // 3-byte sequence (most CJKV characters):
- + // zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx
- + } else if ((c & 0xf0) == 0xe0) {
- + uchar = (c & 0x0f) << 12;
- +
- + c = (u8) *s++;
- + if ((c & 0xc0) != 0xc0)
- + goto fail;
- + c &= 0x3f;
- + uchar |= c << 6;
- +
- + c = (u8) *s++;
- + if ((c & 0xc0) != 0xc0)
- + goto fail;
- + c &= 0x3f;
- + uchar |= c;
- +
- + /* no bogus surrogates */
- + if (0xd800 <= uchar && uchar <= 0xdfff)
- + goto fail;
- +
- + // 4-byte sequence (surrogate pairs, currently rare):
- + // 11101110wwwwzzzzyy + 110111yyyyxxxxxx
- + // = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
- + // (uuuuu = wwww + 1)
- + // FIXME accept the surrogate code points (only)
- + } else
- + goto fail;
- + } else
- + uchar = c;
- + put_unaligned (cpu_to_le16 (uchar), cp++);
- + count++;
- + len--;
- + }
- + return count;
- +fail:
- + return -1;
- +}
- +
- +#endif /* DWC_UTFLIB */
- +
- +
- +/* dwc_debug.h */
- +
- +dwc_bool_t DWC_IN_IRQ(void)
- +{
- +// return in_irq();
- + return 0;
- +}
- +
- +dwc_bool_t DWC_IN_BH(void)
- +{
- +// return in_softirq();
- + return 0;
- +}
- +
- +void DWC_VPRINTF(char *format, va_list args)
- +{
- + vprintf(format, args);
- +}
- +
- +int DWC_VSNPRINTF(char *str, int size, char *format, va_list args)
- +{
- + return vsnprintf(str, size, format, args);
- +}
- +
- +void DWC_PRINTF(char *format, ...)
- +{
- + va_list args;
- +
- + va_start(args, format);
- + DWC_VPRINTF(format, args);
- + va_end(args);
- +}
- +
- +int DWC_SPRINTF(char *buffer, char *format, ...)
- +{
- + int retval;
- + va_list args;
- +
- + va_start(args, format);
- + retval = vsprintf(buffer, format, args);
- + va_end(args);
- + return retval;
- +}
- +
- +int DWC_SNPRINTF(char *buffer, int size, char *format, ...)
- +{
- + int retval;
- + va_list args;
- +
- + va_start(args, format);
- + retval = vsnprintf(buffer, size, format, args);
- + va_end(args);
- + return retval;
- +}
- +
- +void __DWC_WARN(char *format, ...)
- +{
- + va_list args;
- +
- + va_start(args, format);
- + DWC_VPRINTF(format, args);
- + va_end(args);
- +}
- +
- +void __DWC_ERROR(char *format, ...)
- +{
- + va_list args;
- +
- + va_start(args, format);
- + DWC_VPRINTF(format, args);
- + va_end(args);
- +}
- +
- +void DWC_EXCEPTION(char *format, ...)
- +{
- + va_list args;
- +
- + va_start(args, format);
- + DWC_VPRINTF(format, args);
- + va_end(args);
- +// BUG_ON(1); ???
- +}
- +
- +#ifdef DEBUG
- +void __DWC_DEBUG(char *format, ...)
- +{
- + va_list args;
- +
- + va_start(args, format);
- + DWC_VPRINTF(format, args);
- + va_end(args);
- +}
- +#endif
- +
- +
- +/* dwc_mem.h */
- +
- +#if 0
- +dwc_pool_t *DWC_DMA_POOL_CREATE(uint32_t size,
- + uint32_t align,
- + uint32_t alloc)
- +{
- + struct dma_pool *pool = dma_pool_create("Pool", NULL,
- + size, align, alloc);
- + return (dwc_pool_t *)pool;
- +}
- +
- +void DWC_DMA_POOL_DESTROY(dwc_pool_t *pool)
- +{
- + dma_pool_destroy((struct dma_pool *)pool);
- +}
- +
- +void *DWC_DMA_POOL_ALLOC(dwc_pool_t *pool, uint64_t *dma_addr)
- +{
- +// return dma_pool_alloc((struct dma_pool *)pool, GFP_KERNEL, dma_addr);
- + return dma_pool_alloc((struct dma_pool *)pool, M_WAITOK, dma_addr);
- +}
- +
- +void *DWC_DMA_POOL_ZALLOC(dwc_pool_t *pool, uint64_t *dma_addr)
- +{
- + void *vaddr = DWC_DMA_POOL_ALLOC(pool, dma_addr);
- + memset(..);
- +}
- +
- +void DWC_DMA_POOL_FREE(dwc_pool_t *pool, void *vaddr, void *daddr)
- +{
- + dma_pool_free(pool, vaddr, daddr);
- +}
- +#endif
- +
- +void *__DWC_DMA_ALLOC(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr)
- +{
- + dwc_dmactx_t *dma = (dwc_dmactx_t *)dma_ctx;
- + int error;
- +
- + error = bus_dmamem_alloc(dma->dma_tag, size, 1, size, dma->segs,
- + sizeof(dma->segs) / sizeof(dma->segs[0]),
- + &dma->nsegs, BUS_DMA_NOWAIT);
- + if (error) {
- + printf("%s: bus_dmamem_alloc(%ju) failed: %d\n", __func__,
- + (uintmax_t)size, error);
- + goto fail_0;
- + }
- +
- + error = bus_dmamem_map(dma->dma_tag, dma->segs, dma->nsegs, size,
- + (caddr_t *)&dma->dma_vaddr,
- + BUS_DMA_NOWAIT | BUS_DMA_COHERENT);
- + if (error) {
- + printf("%s: bus_dmamem_map failed: %d\n", __func__, error);
- + goto fail_1;
- + }
- +
- + error = bus_dmamap_create(dma->dma_tag, size, 1, size, 0,
- + BUS_DMA_NOWAIT, &dma->dma_map);
- + if (error) {
- + printf("%s: bus_dmamap_create failed: %d\n", __func__, error);
- + goto fail_2;
- + }
- +
- + error = bus_dmamap_load(dma->dma_tag, dma->dma_map, dma->dma_vaddr,
- + size, NULL, BUS_DMA_NOWAIT);
- + if (error) {
- + printf("%s: bus_dmamap_load failed: %d\n", __func__, error);
- + goto fail_3;
- + }
- +
- + dma->dma_paddr = (bus_addr_t)dma->segs[0].ds_addr;
- + *dma_addr = dma->dma_paddr;
- + return dma->dma_vaddr;
- +
- +fail_3:
- + bus_dmamap_destroy(dma->dma_tag, dma->dma_map);
- +fail_2:
- + bus_dmamem_unmap(dma->dma_tag, dma->dma_vaddr, size);
- +fail_1:
- + bus_dmamem_free(dma->dma_tag, dma->segs, dma->nsegs);
- +fail_0:
- + dma->dma_map = NULL;
- + dma->dma_vaddr = NULL;
- + dma->nsegs = 0;
- +
- + return NULL;
- +}
- +
- +void __DWC_DMA_FREE(void *dma_ctx, uint32_t size, void *virt_addr, dwc_dma_t dma_addr)
- +{
- + dwc_dmactx_t *dma = (dwc_dmactx_t *)dma_ctx;
- +
- + if (dma->dma_map != NULL) {
- + bus_dmamap_sync(dma->dma_tag, dma->dma_map, 0, size,
- + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
- + bus_dmamap_unload(dma->dma_tag, dma->dma_map);
- + bus_dmamap_destroy(dma->dma_tag, dma->dma_map);
- + bus_dmamem_unmap(dma->dma_tag, dma->dma_vaddr, size);
- + bus_dmamem_free(dma->dma_tag, dma->segs, dma->nsegs);
- + dma->dma_paddr = 0;
- + dma->dma_map = NULL;
- + dma->dma_vaddr = NULL;
- + dma->nsegs = 0;
- + }
- +}
- +
- +void *__DWC_ALLOC(void *mem_ctx, uint32_t size)
- +{
- + return malloc(size, M_DEVBUF, M_WAITOK | M_ZERO);
- +}
- +
- +void *__DWC_ALLOC_ATOMIC(void *mem_ctx, uint32_t size)
- +{
- + return malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO);
- +}
- +
- +void __DWC_FREE(void *mem_ctx, void *addr)
- +{
- + free(addr, M_DEVBUF);
- +}
- +
- +
- +#ifdef DWC_CRYPTOLIB
- +/* dwc_crypto.h */
- +
- +void DWC_RANDOM_BYTES(uint8_t *buffer, uint32_t length)
- +{
- + get_random_bytes(buffer, length);
- +}
- +
- +int DWC_AES_CBC(uint8_t *message, uint32_t messagelen, uint8_t *key, uint32_t keylen, uint8_t iv[16], uint8_t *out)
- +{
- + struct crypto_blkcipher *tfm;
- + struct blkcipher_desc desc;
- + struct scatterlist sgd;
- + struct scatterlist sgs;
- +
- + tfm = crypto_alloc_blkcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC);
- + if (tfm == NULL) {
- + printk("failed to load transform for aes CBC\n");
- + return -1;
- + }
- +
- + crypto_blkcipher_setkey(tfm, key, keylen);
- + crypto_blkcipher_set_iv(tfm, iv, 16);
- +
- + sg_init_one(&sgd, out, messagelen);
- + sg_init_one(&sgs, message, messagelen);
- +
- + desc.tfm = tfm;
- + desc.flags = 0;
- +
- + if (crypto_blkcipher_encrypt(&desc, &sgd, &sgs, messagelen)) {
- + crypto_free_blkcipher(tfm);
- + DWC_ERROR("AES CBC encryption failed");
- + return -1;
- + }
- +
- + crypto_free_blkcipher(tfm);
- + return 0;
- +}
- +
- +int DWC_SHA256(uint8_t *message, uint32_t len, uint8_t *out)
- +{
- + struct crypto_hash *tfm;
- + struct hash_desc desc;
- + struct scatterlist sg;
- +
- + tfm = crypto_alloc_hash("sha256", 0, CRYPTO_ALG_ASYNC);
- + if (IS_ERR(tfm)) {
- + DWC_ERROR("Failed to load transform for sha256: %ld", PTR_ERR(tfm));
- + return 0;
- + }
- + desc.tfm = tfm;
- + desc.flags = 0;
- +
- + sg_init_one(&sg, message, len);
- + crypto_hash_digest(&desc, &sg, len, out);
- + crypto_free_hash(tfm);
- +
- + return 1;
- +}
- +
- +int DWC_HMAC_SHA256(uint8_t *message, uint32_t messagelen,
- + uint8_t *key, uint32_t keylen, uint8_t *out)
- +{
- + struct crypto_hash *tfm;
- + struct hash_desc desc;
- + struct scatterlist sg;
- +
- + tfm = crypto_alloc_hash("hmac(sha256)", 0, CRYPTO_ALG_ASYNC);
- + if (IS_ERR(tfm)) {
- + DWC_ERROR("Failed to load transform for hmac(sha256): %ld", PTR_ERR(tfm));
- + return 0;
- + }
- + desc.tfm = tfm;
- + desc.flags = 0;
- +
- + sg_init_one(&sg, message, messagelen);
- + crypto_hash_setkey(tfm, key, keylen);
- + crypto_hash_digest(&desc, &sg, messagelen, out);
- + crypto_free_hash(tfm);
- +
- + return 1;
- +}
- +
- +#endif /* DWC_CRYPTOLIB */
- +
- +
- +/* Byte Ordering Conversions */
- +
- +uint32_t DWC_CPU_TO_LE32(uint32_t *p)
- +{
- +#ifdef __LITTLE_ENDIAN
- + return *p;
- +#else
- + uint8_t *u_p = (uint8_t *)p;
- +
- + return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
- +#endif
- +}
- +
- +uint32_t DWC_CPU_TO_BE32(uint32_t *p)
- +{
- +#ifdef __BIG_ENDIAN
- + return *p;
- +#else
- + uint8_t *u_p = (uint8_t *)p;
- +
- + return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
- +#endif
- +}
- +
- +uint32_t DWC_LE32_TO_CPU(uint32_t *p)
- +{
- +#ifdef __LITTLE_ENDIAN
- + return *p;
- +#else
- + uint8_t *u_p = (uint8_t *)p;
- +
- + return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
- +#endif
- +}
- +
- +uint32_t DWC_BE32_TO_CPU(uint32_t *p)
- +{
- +#ifdef __BIG_ENDIAN
- + return *p;
- +#else
- + uint8_t *u_p = (uint8_t *)p;
- +
- + return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24));
- +#endif
- +}
- +
- +uint16_t DWC_CPU_TO_LE16(uint16_t *p)
- +{
- +#ifdef __LITTLE_ENDIAN
- + return *p;
- +#else
- + uint8_t *u_p = (uint8_t *)p;
- + return (u_p[1] | (u_p[0] << 8));
- +#endif
- +}
- +
- +uint16_t DWC_CPU_TO_BE16(uint16_t *p)
- +{
- +#ifdef __BIG_ENDIAN
- + return *p;
- +#else
- + uint8_t *u_p = (uint8_t *)p;
- + return (u_p[1] | (u_p[0] << 8));
- +#endif
- +}
- +
- +uint16_t DWC_LE16_TO_CPU(uint16_t *p)
- +{
- +#ifdef __LITTLE_ENDIAN
- + return *p;
- +#else
- + uint8_t *u_p = (uint8_t *)p;
- + return (u_p[1] | (u_p[0] << 8));
- +#endif
- +}
- +
- +uint16_t DWC_BE16_TO_CPU(uint16_t *p)
- +{
- +#ifdef __BIG_ENDIAN
- + return *p;
- +#else
- + uint8_t *u_p = (uint8_t *)p;
- + return (u_p[1] | (u_p[0] << 8));
- +#endif
- +}
- +
- +
- +/* Registers */
- +
- +uint32_t DWC_READ_REG32(void *io_ctx, uint32_t volatile *reg)
- +{
- + dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
- + bus_size_t ior = (bus_size_t)reg;
- +
- + return bus_space_read_4(io->iot, io->ioh, ior);
- +}
- +
- +#if 0
- +uint64_t DWC_READ_REG64(void *io_ctx, uint64_t volatile *reg)
- +{
- + dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
- + bus_size_t ior = (bus_size_t)reg;
- +
- + return bus_space_read_8(io->iot, io->ioh, ior);
- +}
- +#endif
- +
- +void DWC_WRITE_REG32(void *io_ctx, uint32_t volatile *reg, uint32_t value)
- +{
- + dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
- + bus_size_t ior = (bus_size_t)reg;
- +
- + bus_space_write_4(io->iot, io->ioh, ior, value);
- +}
- +
- +#if 0
- +void DWC_WRITE_REG64(void *io_ctx, uint64_t volatile *reg, uint64_t value)
- +{
- + dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
- + bus_size_t ior = (bus_size_t)reg;
- +
- + bus_space_write_8(io->iot, io->ioh, ior, value);
- +}
- +#endif
- +
- +void DWC_MODIFY_REG32(void *io_ctx, uint32_t volatile *reg, uint32_t clear_mask,
- + uint32_t set_mask)
- +{
- + dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
- + bus_size_t ior = (bus_size_t)reg;
- +
- + bus_space_write_4(io->iot, io->ioh, ior,
- + (bus_space_read_4(io->iot, io->ioh, ior) &
- + ~clear_mask) | set_mask);
- +}
- +
- +#if 0
- +void DWC_MODIFY_REG64(void *io_ctx, uint64_t volatile *reg, uint64_t clear_mask,
- + uint64_t set_mask)
- +{
- + dwc_ioctx_t *io = (dwc_ioctx_t *)io_ctx;
- + bus_size_t ior = (bus_size_t)reg;
- +
- + bus_space_write_8(io->iot, io->ioh, ior,
- + (bus_space_read_8(io->iot, io->ioh, ior) &
- + ~clear_mask) | set_mask);
- +}
- +#endif
- +
- +
- +/* Locking */
- +
- +dwc_spinlock_t *DWC_SPINLOCK_ALLOC(void)
- +{
- + struct simplelock *sl = DWC_ALLOC(sizeof(*sl));
- +
- + if (!sl) {
- + DWC_ERROR("Cannot allocate memory for spinlock");
- + return NULL;
- + }
- +
- + simple_lock_init(sl);
- + return (dwc_spinlock_t *)sl;
- +}
- +
- +void DWC_SPINLOCK_FREE(dwc_spinlock_t *lock)
- +{
- + struct simplelock *sl = (struct simplelock *)lock;
- +
- + DWC_FREE(sl);
- +}
- +
- +void DWC_SPINLOCK(dwc_spinlock_t *lock)
- +{
- + simple_lock((struct simplelock *)lock);
- +}
- +
- +void DWC_SPINUNLOCK(dwc_spinlock_t *lock)
- +{
- + simple_unlock((struct simplelock *)lock);
- +}
- +
- +void DWC_SPINLOCK_IRQSAVE(dwc_spinlock_t *lock, dwc_irqflags_t *flags)
- +{
- + simple_lock((struct simplelock *)lock);
- + *flags = splbio();
- +}
- +
- +void DWC_SPINUNLOCK_IRQRESTORE(dwc_spinlock_t *lock, dwc_irqflags_t flags)
- +{
- + splx(flags);
- + simple_unlock((struct simplelock *)lock);
- +}
- +
- +dwc_mutex_t *DWC_MUTEX_ALLOC(void)
- +{
- + dwc_mutex_t *mutex = DWC_ALLOC(sizeof(struct lock));
- +
- + if (!mutex) {
- + DWC_ERROR("Cannot allocate memory for mutex");
- + return NULL;
- + }
- +
- + lockinit((struct lock *)mutex, 0, "dw3mtx", 0, 0);
- + return mutex;
- +}
- +
- +#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES))
- +#else
- +void DWC_MUTEX_FREE(dwc_mutex_t *mutex)
- +{
- + DWC_FREE(mutex);
- +}
- +#endif
- +
- +void DWC_MUTEX_LOCK(dwc_mutex_t *mutex)
- +{
- + lockmgr((struct lock *)mutex, LK_EXCLUSIVE, NULL);
- +}
- +
- +int DWC_MUTEX_TRYLOCK(dwc_mutex_t *mutex)
- +{
- + int status;
- +
- + status = lockmgr((struct lock *)mutex, LK_EXCLUSIVE | LK_NOWAIT, NULL);
- + return status == 0;
- +}
- +
- +void DWC_MUTEX_UNLOCK(dwc_mutex_t *mutex)
- +{
- + lockmgr((struct lock *)mutex, LK_RELEASE, NULL);
- +}
- +
- +
- +/* Timing */
- +
- +void DWC_UDELAY(uint32_t usecs)
- +{
- + DELAY(usecs);
- +}
- +
- +void DWC_MDELAY(uint32_t msecs)
- +{
- + do {
- + DELAY(1000);
- + } while (--msecs);
- +}
- +
- +void DWC_MSLEEP(uint32_t msecs)
- +{
- + struct timeval tv;
- +
- + tv.tv_sec = msecs / 1000;
- + tv.tv_usec = (msecs - tv.tv_sec * 1000) * 1000;
- + tsleep(&tv, 0, "dw3slp", tvtohz(&tv));
- +}
- +
- +uint32_t DWC_TIME(void)
- +{
- + struct timeval tv;
- +
- + microuptime(&tv); // or getmicrouptime? (less precise, but faster)
- + return tv.tv_sec * 1000 + tv.tv_usec / 1000;
- +}
- +
- +
- +/* Timers */
- +
- +struct dwc_timer {
- + struct callout t;
- + char *name;
- + dwc_spinlock_t *lock;
- + dwc_timer_callback_t cb;
- + void *data;
- +};
- +
- +dwc_timer_t *DWC_TIMER_ALLOC(char *name, dwc_timer_callback_t cb, void *data)
- +{
- + dwc_timer_t *t = DWC_ALLOC(sizeof(*t));
- +
- + if (!t) {
- + DWC_ERROR("Cannot allocate memory for timer");
- + return NULL;
- + }
- +
- + callout_init(&t->t);
- +
- + t->name = DWC_STRDUP(name);
- + if (!t->name) {
- + DWC_ERROR("Cannot allocate memory for timer->name");
- + goto no_name;
- + }
- +
- + t->lock = DWC_SPINLOCK_ALLOC();
- + if (!t->lock) {
- + DWC_ERROR("Cannot allocate memory for timer->lock");
- + goto no_lock;
- + }
- +
- + t->cb = cb;
- + t->data = data;
- +
- + return t;
- +
- + no_lock:
- + DWC_FREE(t->name);
- + no_name:
- + DWC_FREE(t);
- +
- + return NULL;
- +}
- +
- +void DWC_TIMER_FREE(dwc_timer_t *timer)
- +{
- + callout_stop(&timer->t);
- + DWC_SPINLOCK_FREE(timer->lock);
- + DWC_FREE(timer->name);
- + DWC_FREE(timer);
- +}
- +
- +void DWC_TIMER_SCHEDULE(dwc_timer_t *timer, uint32_t time)
- +{
- + struct timeval tv;
- +
- + tv.tv_sec = time / 1000;
- + tv.tv_usec = (time - tv.tv_sec * 1000) * 1000;
- + callout_reset(&timer->t, tvtohz(&tv), timer->cb, timer->data);
- +}
- +
- +void DWC_TIMER_CANCEL(dwc_timer_t *timer)
- +{
- + callout_stop(&timer->t);
- +}
- +
- +
- +/* Wait Queues */
- +
- +struct dwc_waitq {
- + struct simplelock lock;
- + int abort;
- +};
- +
- +dwc_waitq_t *DWC_WAITQ_ALLOC(void)
- +{
- + dwc_waitq_t *wq = DWC_ALLOC(sizeof(*wq));
- +
- + if (!wq) {
- + DWC_ERROR("Cannot allocate memory for waitqueue");
- + return NULL;
- + }
- +
- + simple_lock_init(&wq->lock);
- + wq->abort = 0;
- +
- + return wq;
- +}
- +
- +void DWC_WAITQ_FREE(dwc_waitq_t *wq)
- +{
- + DWC_FREE(wq);
- +}
- +
- +int32_t DWC_WAITQ_WAIT(dwc_waitq_t *wq, dwc_waitq_condition_t cond, void *data)
- +{
- + int ipl;
- + int result = 0;
- +
- + simple_lock(&wq->lock);
- + ipl = splbio();
- +
- + /* Skip the sleep if already aborted or triggered */
- + if (!wq->abort && !cond(data)) {
- + splx(ipl);
- + result = ltsleep(wq, PCATCH, "dw3wat", 0, &wq->lock); // infinite timeout
- + ipl = splbio();
- + }
- +
- + if (result == 0) { // awoken
- + if (wq->abort) {
- + wq->abort = 0;
- + result = -DWC_E_ABORT;
- + } else {
- + result = 0;
- + }
- +
- + splx(ipl);
- + simple_unlock(&wq->lock);
- + } else {
- + wq->abort = 0;
- + splx(ipl);
- + simple_unlock(&wq->lock);
- +
- + if (result == ERESTART) { // signaled - restart
- + result = -DWC_E_RESTART;
- + } else { // signaled - must be EINTR
- + result = -DWC_E_ABORT;
- + }
- + }
- +
- + return result;
- +}
- +
- +int32_t DWC_WAITQ_WAIT_TIMEOUT(dwc_waitq_t *wq, dwc_waitq_condition_t cond,
- + void *data, int32_t msecs)
- +{
- + struct timeval tv, tv1, tv2;
- + int ipl;
- + int result = 0;
- +
- + tv.tv_sec = msecs / 1000;
- + tv.tv_usec = (msecs - tv.tv_sec * 1000) * 1000;
- +
- + simple_lock(&wq->lock);
- + ipl = splbio();
- +
- + /* Skip the sleep if already aborted or triggered */
- + if (!wq->abort && !cond(data)) {
- + splx(ipl);
- + getmicrouptime(&tv1);
- + result = ltsleep(wq, PCATCH, "dw3wto", tvtohz(&tv), &wq->lock);
- + getmicrouptime(&tv2);
- + ipl = splbio();
- + }
- +
- + if (result == 0) { // awoken
- + if (wq->abort) {
- + wq->abort = 0;
- + splx(ipl);
- + simple_unlock(&wq->lock);
- + result = -DWC_E_ABORT;
- + } else {
- + splx(ipl);
- + simple_unlock(&wq->lock);
- +
- + tv2.tv_usec -= tv1.tv_usec;
- + if (tv2.tv_usec < 0) {
- + tv2.tv_usec += 1000000;
- + tv2.tv_sec--;
- + }
- +
- + tv2.tv_sec -= tv1.tv_sec;
- + result = tv2.tv_sec * 1000 + tv2.tv_usec / 1000;
- + result = msecs - result;
- + if (result <= 0)
- + result = 1;
- + }
- + } else {
- + wq->abort = 0;
- + splx(ipl);
- + simple_unlock(&wq->lock);
- +
- + if (result == ERESTART) { // signaled - restart
- + result = -DWC_E_RESTART;
- +
- + } else if (result == EINTR) { // signaled - interrupt
- + result = -DWC_E_ABORT;
- +
- + } else { // timed out
- + result = -DWC_E_TIMEOUT;
- + }
- + }
- +
- + return result;
- +}
- +
- +void DWC_WAITQ_TRIGGER(dwc_waitq_t *wq)
- +{
- + wakeup(wq);
- +}
- +
- +void DWC_WAITQ_ABORT(dwc_waitq_t *wq)
- +{
- + int ipl;
- +
- + simple_lock(&wq->lock);
- + ipl = splbio();
- + wq->abort = 1;
- + wakeup(wq);
- + splx(ipl);
- + simple_unlock(&wq->lock);
- +}
- +
- +
- +/* Threading */
- +
- +struct dwc_thread {
- + struct proc *proc;
- + int abort;
- +};
- +
- +dwc_thread_t *DWC_THREAD_RUN(dwc_thread_function_t func, char *name, void *data)
- +{
- + int retval;
- + dwc_thread_t *thread = DWC_ALLOC(sizeof(*thread));
- +
- + if (!thread) {
- + return NULL;
- + }
- +
- + thread->abort = 0;
- + retval = kthread_create1((void (*)(void *))func, data, &thread->proc,
- + "%s", name);
- + if (retval) {
- + DWC_FREE(thread);
- + return NULL;
- + }
- +
- + return thread;
- +}
- +
- +int DWC_THREAD_STOP(dwc_thread_t *thread)
- +{
- + int retval;
- +
- + thread->abort = 1;
- + retval = tsleep(&thread->abort, 0, "dw3stp", 60 * hz);
- +
- + if (retval == 0) {
- + /* DWC_THREAD_EXIT() will free the thread struct */
- + return 0;
- + }
- +
- + /* NOTE: We leak the thread struct if thread doesn't die */
- +
- + if (retval == EWOULDBLOCK) {
- + return -DWC_E_TIMEOUT;
- + }
- +
- + return -DWC_E_UNKNOWN;
- +}
- +
- +dwc_bool_t DWC_THREAD_SHOULD_STOP(dwc_thread_t *thread)
- +{
- + return thread->abort;
- +}
- +
- +void DWC_THREAD_EXIT(dwc_thread_t *thread)
- +{
- + wakeup(&thread->abort);
- + DWC_FREE(thread);
- + kthread_exit(0);
- +}
- +
- +/* tasklets
- + - Runs in interrupt context (cannot sleep)
- + - Each tasklet runs on a single CPU
- + - Different tasklets can be running simultaneously on different CPUs
- + [ On NetBSD there is no corresponding mechanism, drivers don't have bottom-
- + halves. So we just call the callback directly from DWC_TASK_SCHEDULE() ]
- + */
- +struct dwc_tasklet {
- + dwc_tasklet_callback_t cb;
- + void *data;
- +};
- +
- +static void tasklet_callback(void *data)
- +{
- + dwc_tasklet_t *task = (dwc_tasklet_t *)data;
- +
- + task->cb(task->data);
- +}
- +
- +dwc_tasklet_t *DWC_TASK_ALLOC(char *name, dwc_tasklet_callback_t cb, void *data)
- +{
- + dwc_tasklet_t *task = DWC_ALLOC(sizeof(*task));
- +
- + if (task) {
- + task->cb = cb;
- + task->data = data;
- + } else {
- + DWC_ERROR("Cannot allocate memory for tasklet");
- + }
- +
- + return task;
- +}
- +
- +void DWC_TASK_FREE(dwc_tasklet_t *task)
- +{
- + DWC_FREE(task);
- +}
- +
- +void DWC_TASK_SCHEDULE(dwc_tasklet_t *task)
- +{
- + tasklet_callback(task);
- +}
- +
- +
- +/* workqueues
- + - Runs in process context (can sleep)
- + */
- +typedef struct work_container {
- + dwc_work_callback_t cb;
- + void *data;
- + dwc_workq_t *wq;
- + char *name;
- + int hz;
- + struct work task;
- +} work_container_t;
- +
- +struct dwc_workq {
- + struct workqueue *taskq;
- + dwc_spinlock_t *lock;
- + dwc_waitq_t *waitq;
- + int pending;
- + struct work_container *container;
- +};
- +
- +static void do_work(struct work *task, void *data)
- +{
- + dwc_workq_t *wq = (dwc_workq_t *)data;
- + work_container_t *container = wq->container;
- + dwc_irqflags_t flags;
- +
- + if (container->hz) {
- + tsleep(container, 0, "dw3wrk", container->hz);
- + }
- +
- + container->cb(container->data);
- + DWC_DEBUG("Work done: %s, container=%p", container->name, container);
- +
- + DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
- + if (container->name)
- + DWC_FREE(container->name);
- + DWC_FREE(container);
- + wq->pending--;
- + DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
- + DWC_WAITQ_TRIGGER(wq->waitq);
- +}
- +
- +static int work_done(void *data)
- +{
- + dwc_workq_t *workq = (dwc_workq_t *)data;
- +
- + return workq->pending == 0;
- +}
- +
- +int DWC_WORKQ_WAIT_WORK_DONE(dwc_workq_t *workq, int timeout)
- +{
- + return DWC_WAITQ_WAIT_TIMEOUT(workq->waitq, work_done, workq, timeout);
- +}
- +
- +dwc_workq_t *DWC_WORKQ_ALLOC(char *name)
- +{
- + int result;
- + dwc_workq_t *wq = DWC_ALLOC(sizeof(*wq));
- +
- + if (!wq) {
- + DWC_ERROR("Cannot allocate memory for workqueue");
- + return NULL;
- + }
- +
- + result = workqueue_create(&wq->taskq, name, do_work, wq, 0 /*PWAIT*/,
- + IPL_BIO, 0);
- + if (result) {
- + DWC_ERROR("Cannot create workqueue");
- + goto no_taskq;
- + }
- +
- + wq->pending = 0;
- +
- + wq->lock = DWC_SPINLOCK_ALLOC();
- + if (!wq->lock) {
- + DWC_ERROR("Cannot allocate memory for spinlock");
- + goto no_lock;
- + }
- +
- + wq->waitq = DWC_WAITQ_ALLOC();
- + if (!wq->waitq) {
- + DWC_ERROR("Cannot allocate memory for waitqueue");
- + goto no_waitq;
- + }
- +
- + return wq;
- +
- + no_waitq:
- + DWC_SPINLOCK_FREE(wq->lock);
- + no_lock:
- + workqueue_destroy(wq->taskq);
- + no_taskq:
- + DWC_FREE(wq);
- +
- + return NULL;
- +}
- +
- +void DWC_WORKQ_FREE(dwc_workq_t *wq)
- +{
- +#ifdef DEBUG
- + dwc_irqflags_t flags;
- +
- + DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
- +
- + if (wq->pending != 0) {
- + struct work_container *container = wq->container;
- +
- + DWC_ERROR("Destroying work queue with pending work");
- +
- + if (container && container->name) {
- + DWC_ERROR("Work %s still pending", container->name);
- + }
- + }
- +
- + DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
- +#endif
- + DWC_WAITQ_FREE(wq->waitq);
- + DWC_SPINLOCK_FREE(wq->lock);
- + workqueue_destroy(wq->taskq);
- + DWC_FREE(wq);
- +}
- +
- +void DWC_WORKQ_SCHEDULE(dwc_workq_t *wq, dwc_work_callback_t cb, void *data,
- + char *format, ...)
- +{
- + dwc_irqflags_t flags;
- + work_container_t *container;
- + static char name[128];
- + va_list args;
- +
- + va_start(args, format);
- + DWC_VSNPRINTF(name, 128, format, args);
- + va_end(args);
- +
- + DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
- + wq->pending++;
- + DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
- + DWC_WAITQ_TRIGGER(wq->waitq);
- +
- + container = DWC_ALLOC_ATOMIC(sizeof(*container));
- + if (!container) {
- + DWC_ERROR("Cannot allocate memory for container");
- + return;
- + }
- +
- + container->name = DWC_STRDUP(name);
- + if (!container->name) {
- + DWC_ERROR("Cannot allocate memory for container->name");
- + DWC_FREE(container);
- + return;
- + }
- +
- + container->cb = cb;
- + container->data = data;
- + container->wq = wq;
- + container->hz = 0;
- + wq->container = container;
- +
- + DWC_DEBUG("Queueing work: %s, container=%p", container->name, container);
- + workqueue_enqueue(wq->taskq, &container->task);
- +}
- +
- +void DWC_WORKQ_SCHEDULE_DELAYED(dwc_workq_t *wq, dwc_work_callback_t cb,
- + void *data, uint32_t time, char *format, ...)
- +{
- + dwc_irqflags_t flags;
- + work_container_t *container;
- + static char name[128];
- + struct timeval tv;
- + va_list args;
- +
- + va_start(args, format);
- + DWC_VSNPRINTF(name, 128, format, args);
- + va_end(args);
- +
- + DWC_SPINLOCK_IRQSAVE(wq->lock, &flags);
- + wq->pending++;
- + DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags);
- + DWC_WAITQ_TRIGGER(wq->waitq);
- +
- + container = DWC_ALLOC_ATOMIC(sizeof(*container));
- + if (!container) {
- + DWC_ERROR("Cannot allocate memory for container");
- + return;
- + }
- +
- + container->name = DWC_STRDUP(name);
- + if (!container->name) {
- + DWC_ERROR("Cannot allocate memory for container->name");
- + DWC_FREE(container);
- + return;
- + }
- +
- + container->cb = cb;
- + container->data = data;
- + container->wq = wq;
- + tv.tv_sec = time / 1000;
- + tv.tv_usec = (time - tv.tv_sec * 1000) * 1000;
- + container->hz = tvtohz(&tv);
- + wq->container = container;
- +
- + DWC_DEBUG("Queueing work: %s, container=%p", container->name, container);
- + workqueue_enqueue(wq->taskq, &container->task);
- +}
- +
- +int DWC_WORKQ_PENDING(dwc_workq_t *wq)
- +{
- + return wq->pending;
- +}
- --- /dev/null
- +++ b/drivers/usb/host/dwc_common_port/dwc_crypto.c
- @@ -0,0 +1,308 @@
- +/* =========================================================================
- + * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_crypto.c $
- + * $Revision: #5 $
- + * $Date: 2010/09/28 $
- + * $Change: 1596182 $
- + *
- + * Synopsys Portability Library Software and documentation
- + * (hereinafter, "Software") is an Unsupported proprietary work of
- + * Synopsys, Inc. unless otherwise expressly agreed to in writing
- + * between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product
- + * under any End User Software License Agreement or Agreement for
- + * Licensed Product with Synopsys or any supplement thereto. You are
- + * permitted to use and redistribute this Software in source and binary
- + * forms, with or without modification, provided that redistributions
- + * of source code must retain this notice. You may not view, use,
- + * disclose, copy or distribute this file or any information contained
- + * herein except pursuant to this license grant from Synopsys. If you
- + * do not agree with this notice, including the disclaimer below, then
- + * you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
- + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- + * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL
- + * SYNOPSYS 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.
- + * ========================================================================= */
- +
- +/** @file
- + * This file contains the WUSB cryptographic routines.
- + */
- +
- +#ifdef DWC_CRYPTOLIB
- +
- +#include "dwc_crypto.h"
- +#include "usb.h"
- +
- +#ifdef DEBUG
- +static inline void dump_bytes(char *name, uint8_t *bytes, int len)
- +{
- + int i;
- + DWC_PRINTF("%s: ", name);
- + for (i=0; i<len; i++) {
- + DWC_PRINTF("%02x ", bytes[i]);
- + }
- + DWC_PRINTF("\n");
- +}
- +#else
- +#define dump_bytes(x...)
- +#endif
- +
- +/* Display a block */
- +void show_block(const u8 *blk, const char *prefix, const char *suffix, int a)
- +{
- +#ifdef DWC_DEBUG_CRYPTO
- + int i, blksize = 16;
- +
- + DWC_DEBUG("%s", prefix);
- +
- + if (suffix == NULL) {
- + suffix = "\n";
- + blksize = a;
- + }
- +
- + for (i = 0; i < blksize; i++)
- + DWC_PRINT("%02x%s", *blk++, ((i & 3) == 3) ? " " : " ");
- + DWC_PRINT(suffix);
- +#endif
- +}
- +
- +/**
- + * Encrypts an array of bytes using the AES encryption engine.
- + * If <code>dst</code> == <code>src</code>, then the bytes will be encrypted
- + * in-place.
- + *
- + * @return 0 on success, negative error code on error.
- + */
- +int dwc_wusb_aes_encrypt(u8 *src, u8 *key, u8 *dst)
- +{
- + u8 block_t[16];
- + DWC_MEMSET(block_t, 0, 16);
- +
- + return DWC_AES_CBC(src, 16, key, 16, block_t, dst);
- +}
- +
- +/**
- + * The CCM-MAC-FUNCTION described in section 6.5 of the WUSB spec.
- + * This function takes a data string and returns the encrypted CBC
- + * Counter-mode MIC.
- + *
- + * @param key The 128-bit symmetric key.
- + * @param nonce The CCM nonce.
- + * @param label The unique 14-byte ASCII text label.
- + * @param bytes The byte array to be encrypted.
- + * @param len Length of the byte array.
- + * @param result Byte array to receive the 8-byte encrypted MIC.
- + */
- +void dwc_wusb_cmf(u8 *key, u8 *nonce,
- + char *label, u8 *bytes, int len, u8 *result)
- +{
- + u8 block_m[16];
- + u8 block_x[16];
- + u8 block_t[8];
- + int idx, blkNum;
- + u16 la = (u16)(len + 14);
- +
- + /* Set the AES-128 key */
- + //dwc_aes_setkey(tfm, key, 16);
- +
- + /* Fill block B0 from flags = 0x59, N, and l(m) = 0 */
- + block_m[0] = 0x59;
- + for (idx = 0; idx < 13; idx++)
- + block_m[idx + 1] = nonce[idx];
- + block_m[14] = 0;
- + block_m[15] = 0;
- +
- + /* Produce the CBC IV */
- + dwc_wusb_aes_encrypt(block_m, key, block_x);
- + show_block(block_m, "CBC IV in: ", "\n", 0);
- + show_block(block_x, "CBC IV out:", "\n", 0);
- +
- + /* Fill block B1 from l(a) = Blen + 14, and A */
- + block_x[0] ^= (u8)(la >> 8);
- + block_x[1] ^= (u8)la;
- + for (idx = 0; idx < 14; idx++)
- + block_x[idx + 2] ^= label[idx];
- + show_block(block_x, "After xor: ", "b1\n", 16);
- +
- + dwc_wusb_aes_encrypt(block_x, key, block_x);
- + show_block(block_x, "After AES: ", "b1\n", 16);
- +
- + idx = 0;
- + blkNum = 0;
- +
- + /* Fill remaining blocks with B */
- + while (len-- > 0) {
- + block_x[idx] ^= *bytes++;
- + if (++idx >= 16) {
- + idx = 0;
- + show_block(block_x, "After xor: ", "\n", blkNum);
- + dwc_wusb_aes_encrypt(block_x, key, block_x);
- + show_block(block_x, "After AES: ", "\n", blkNum);
- + blkNum++;
- + }
- + }
- +
- + /* Handle partial last block */
- + if (idx > 0) {
- + show_block(block_x, "After xor: ", "\n", blkNum);
- + dwc_wusb_aes_encrypt(block_x, key, block_x);
- + show_block(block_x, "After AES: ", "\n", blkNum);
- + }
- +
- + /* Save the MIC tag */
- + DWC_MEMCPY(block_t, block_x, 8);
- + show_block(block_t, "MIC tag : ", NULL, 8);
- +
- + /* Fill block A0 from flags = 0x01, N, and counter = 0 */
- + block_m[0] = 0x01;
- + block_m[14] = 0;
- + block_m[15] = 0;
- +
- + /* Encrypt the counter */
- + dwc_wusb_aes_encrypt(block_m, key, block_x);
- + show_block(block_x, "CTR[MIC] : ", NULL, 8);
- +
- + /* XOR with MIC tag */
- + for (idx = 0; idx < 8; idx++) {
- + block_t[idx] ^= block_x[idx];
- + }
- +
- + /* Return result to caller */
- + DWC_MEMCPY(result, block_t, 8);
- + show_block(result, "CCM-MIC : ", NULL, 8);
- +
- +}
- +
- +/**
- + * The PRF function described in section 6.5 of the WUSB spec. This function
- + * concatenates MIC values returned from dwc_cmf() to create a value of
- + * the requested length.
- + *
- + * @param prf_len Length of the PRF function in bits (64, 128, or 256).
- + * @param key, nonce, label, bytes, len Same as for dwc_cmf().
- + * @param result Byte array to receive the result.
- + */
- +void dwc_wusb_prf(int prf_len, u8 *key,
- + u8 *nonce, char *label, u8 *bytes, int len, u8 *result)
- +{
- + int i;
- +
- + nonce[0] = 0;
- + for (i = 0; i < prf_len >> 6; i++, nonce[0]++) {
- + dwc_wusb_cmf(key, nonce, label, bytes, len, result);
- + result += 8;
- + }
- +}
- +
- +/**
- + * Fills in CCM Nonce per the WUSB spec.
- + *
- + * @param[in] haddr Host address.
- + * @param[in] daddr Device address.
- + * @param[in] tkid Session Key(PTK) identifier.
- + * @param[out] nonce Pointer to where the CCM Nonce output is to be written.
- + */
- +void dwc_wusb_fill_ccm_nonce(uint16_t haddr, uint16_t daddr, uint8_t *tkid,
- + uint8_t *nonce)
- +{
- +
- + DWC_DEBUG("%s %x %x\n", __func__, daddr, haddr);
- +
- + DWC_MEMSET(&nonce[0], 0, 16);
- +
- + DWC_MEMCPY(&nonce[6], tkid, 3);
- + nonce[9] = daddr & 0xFF;
- + nonce[10] = (daddr >> 8) & 0xFF;
- + nonce[11] = haddr & 0xFF;
- + nonce[12] = (haddr >> 8) & 0xFF;
- +
- + dump_bytes("CCM nonce", nonce, 16);
- +}
- +
- +/**
- + * Generates a 16-byte cryptographic-grade random number for the Host/Device
- + * Nonce.
- + */
- +void dwc_wusb_gen_nonce(uint16_t addr, uint8_t *nonce)
- +{
- + uint8_t inonce[16];
- + uint32_t temp[4];
- +
- + /* Fill in the Nonce */
- + DWC_MEMSET(&inonce[0], 0, sizeof(inonce));
- + inonce[9] = addr & 0xFF;
- + inonce[10] = (addr >> 8) & 0xFF;
- + inonce[11] = inonce[9];
- + inonce[12] = inonce[10];
- +
- + /* Collect "randomness samples" */
- + DWC_RANDOM_BYTES((uint8_t *)temp, 16);
- +
- + dwc_wusb_prf_128((uint8_t *)temp, nonce,
- + "Random Numbers", (uint8_t *)temp, sizeof(temp),
- + nonce);
- +}
- +
- +/**
- + * Generates the Session Key (PTK) and Key Confirmation Key (KCK) per the
- + * WUSB spec.
- + *
- + * @param[in] ccm_nonce Pointer to CCM Nonce.
- + * @param[in] mk Master Key to derive the session from
- + * @param[in] hnonce Pointer to Host Nonce.
- + * @param[in] dnonce Pointer to Device Nonce.
- + * @param[out] kck Pointer to where the KCK output is to be written.
- + * @param[out] ptk Pointer to where the PTK output is to be written.
- + */
- +void dwc_wusb_gen_key(uint8_t *ccm_nonce, uint8_t *mk, uint8_t *hnonce,
- + uint8_t *dnonce, uint8_t *kck, uint8_t *ptk)
- +{
- + uint8_t idata[32];
- + uint8_t odata[32];
- +
- + dump_bytes("ck", mk, 16);
- + dump_bytes("hnonce", hnonce, 16);
- + dump_bytes("dnonce", dnonce, 16);
- +
- + /* The data is the HNonce and DNonce concatenated */
- + DWC_MEMCPY(&idata[0], hnonce, 16);
- + DWC_MEMCPY(&idata[16], dnonce, 16);
- +
- + dwc_wusb_prf_256(mk, ccm_nonce, "Pair-wise keys", idata, 32, odata);
- +
- + /* Low 16 bytes of the result is the KCK, high 16 is the PTK */
- + DWC_MEMCPY(kck, &odata[0], 16);
- + DWC_MEMCPY(ptk, &odata[16], 16);
- +
- + dump_bytes("kck", kck, 16);
- + dump_bytes("ptk", ptk, 16);
- +}
- +
- +/**
- + * Generates the Message Integrity Code over the Handshake data per the
- + * WUSB spec.
- + *
- + * @param ccm_nonce Pointer to CCM Nonce.
- + * @param kck Pointer to Key Confirmation Key.
- + * @param data Pointer to Handshake data to be checked.
- + * @param mic Pointer to where the MIC output is to be written.
- + */
- +void dwc_wusb_gen_mic(uint8_t *ccm_nonce, uint8_t *kck,
- + uint8_t *data, uint8_t *mic)
- +{
- +
- + dwc_wusb_prf_64(kck, ccm_nonce, "out-of-bandMIC",
- + data, WUSB_HANDSHAKE_LEN_FOR_MIC, mic);
- +}
- +
- +#endif /* DWC_CRYPTOLIB */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_common_port/dwc_crypto.h
- @@ -0,0 +1,111 @@
- +/* =========================================================================
- + * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_crypto.h $
- + * $Revision: #3 $
- + * $Date: 2010/09/28 $
- + * $Change: 1596182 $
- + *
- + * Synopsys Portability Library Software and documentation
- + * (hereinafter, "Software") is an Unsupported proprietary work of
- + * Synopsys, Inc. unless otherwise expressly agreed to in writing
- + * between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product
- + * under any End User Software License Agreement or Agreement for
- + * Licensed Product with Synopsys or any supplement thereto. You are
- + * permitted to use and redistribute this Software in source and binary
- + * forms, with or without modification, provided that redistributions
- + * of source code must retain this notice. You may not view, use,
- + * disclose, copy or distribute this file or any information contained
- + * herein except pursuant to this license grant from Synopsys. If you
- + * do not agree with this notice, including the disclaimer below, then
- + * you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
- + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- + * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL
- + * SYNOPSYS 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.
- + * ========================================================================= */
- +
- +#ifndef _DWC_CRYPTO_H_
- +#define _DWC_CRYPTO_H_
- +
- +#ifdef __cplusplus
- +extern "C" {
- +#endif
- +
- +/** @file
- + *
- + * This file contains declarations for the WUSB Cryptographic routines as
- + * defined in the WUSB spec. They are only to be used internally by the DWC UWB
- + * modules.
- + */
- +
- +#include "dwc_os.h"
- +
- +int dwc_wusb_aes_encrypt(u8 *src, u8 *key, u8 *dst);
- +
- +void dwc_wusb_cmf(u8 *key, u8 *nonce,
- + char *label, u8 *bytes, int len, u8 *result);
- +void dwc_wusb_prf(int prf_len, u8 *key,
- + u8 *nonce, char *label, u8 *bytes, int len, u8 *result);
- +
- +/**
- + * The PRF-64 function described in section 6.5 of the WUSB spec.
- + *
- + * @param key, nonce, label, bytes, len, result Same as for dwc_prf().
- + */
- +static inline void dwc_wusb_prf_64(u8 *key, u8 *nonce,
- + char *label, u8 *bytes, int len, u8 *result)
- +{
- + dwc_wusb_prf(64, key, nonce, label, bytes, len, result);
- +}
- +
- +/**
- + * The PRF-128 function described in section 6.5 of the WUSB spec.
- + *
- + * @param key, nonce, label, bytes, len, result Same as for dwc_prf().
- + */
- +static inline void dwc_wusb_prf_128(u8 *key, u8 *nonce,
- + char *label, u8 *bytes, int len, u8 *result)
- +{
- + dwc_wusb_prf(128, key, nonce, label, bytes, len, result);
- +}
- +
- +/**
- + * The PRF-256 function described in section 6.5 of the WUSB spec.
- + *
- + * @param key, nonce, label, bytes, len, result Same as for dwc_prf().
- + */
- +static inline void dwc_wusb_prf_256(u8 *key, u8 *nonce,
- + char *label, u8 *bytes, int len, u8 *result)
- +{
- + dwc_wusb_prf(256, key, nonce, label, bytes, len, result);
- +}
- +
- +
- +void dwc_wusb_fill_ccm_nonce(uint16_t haddr, uint16_t daddr, uint8_t *tkid,
- + uint8_t *nonce);
- +void dwc_wusb_gen_nonce(uint16_t addr,
- + uint8_t *nonce);
- +
- +void dwc_wusb_gen_key(uint8_t *ccm_nonce, uint8_t *mk,
- + uint8_t *hnonce, uint8_t *dnonce,
- + uint8_t *kck, uint8_t *ptk);
- +
- +
- +void dwc_wusb_gen_mic(uint8_t *ccm_nonce, uint8_t
- + *kck, uint8_t *data, uint8_t *mic);
- +
- +#ifdef __cplusplus
- +}
- +#endif
- +
- +#endif /* _DWC_CRYPTO_H_ */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_common_port/dwc_dh.c
- @@ -0,0 +1,291 @@
- +/* =========================================================================
- + * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_dh.c $
- + * $Revision: #3 $
- + * $Date: 2010/09/28 $
- + * $Change: 1596182 $
- + *
- + * Synopsys Portability Library Software and documentation
- + * (hereinafter, "Software") is an Unsupported proprietary work of
- + * Synopsys, Inc. unless otherwise expressly agreed to in writing
- + * between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product
- + * under any End User Software License Agreement or Agreement for
- + * Licensed Product with Synopsys or any supplement thereto. You are
- + * permitted to use and redistribute this Software in source and binary
- + * forms, with or without modification, provided that redistributions
- + * of source code must retain this notice. You may not view, use,
- + * disclose, copy or distribute this file or any information contained
- + * herein except pursuant to this license grant from Synopsys. If you
- + * do not agree with this notice, including the disclaimer below, then
- + * you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
- + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- + * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL
- + * SYNOPSYS 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.
- + * ========================================================================= */
- +#ifdef DWC_CRYPTOLIB
- +
- +#ifndef CONFIG_MACH_IPMATE
- +
- +#include "dwc_dh.h"
- +#include "dwc_modpow.h"
- +
- +#ifdef DEBUG
- +/* This function prints out a buffer in the format described in the Association
- + * Model specification. */
- +static void dh_dump(char *str, void *_num, int len)
- +{
- + uint8_t *num = _num;
- + int i;
- + DWC_PRINTF("%s\n", str);
- + for (i = 0; i < len; i ++) {
- + DWC_PRINTF("%02x", num[i]);
- + if (((i + 1) % 2) == 0) DWC_PRINTF(" ");
- + if (((i + 1) % 26) == 0) DWC_PRINTF("\n");
- + }
- +
- + DWC_PRINTF("\n");
- +}
- +#else
- +#define dh_dump(_x...) do {; } while(0)
- +#endif
- +
- +/* Constant g value */
- +static __u32 dh_g[] = {
- + 0x02000000,
- +};
- +
- +/* Constant p value */
- +static __u32 dh_p[] = {
- + 0xFFFFFFFF, 0xFFFFFFFF, 0xA2DA0FC9, 0x34C26821, 0x8B62C6C4, 0xD11CDC80, 0x084E0229, 0x74CC678A,
- + 0xA6BE0B02, 0x229B133B, 0x79084A51, 0xDD04348E, 0xB31995EF, 0x1B433ACD, 0x6D0A2B30, 0x37145FF2,
- + 0x6D35E14F, 0x45C2516D, 0x76B585E4, 0xC67E5E62, 0xE9424CF4, 0x6BED37A6, 0xB65CFF0B, 0xEDB706F4,
- + 0xFB6B38EE, 0xA59F895A, 0x11249FAE, 0xE61F4B7C, 0x51662849, 0x3D5BE4EC, 0xB87C00C2, 0x05BF63A1,
- + 0x3648DA98, 0x9AD3551C, 0xA83F1669, 0x5FCF24FD, 0x235D6583, 0x96ADA3DC, 0x56F3621C, 0xBB528520,
- + 0x0729D59E, 0x6D969670, 0x4E350C67, 0x0498BC4A, 0x086C74F1, 0x7C2118CA, 0x465E9032, 0x3BCE362E,
- + 0x2C779EE3, 0x03860E18, 0xA283279B, 0x8FA207EC, 0xF05DC5B5, 0xC9524C6F, 0xF6CB2BDE, 0x18175895,
- + 0x7C499539, 0xE56A95EA, 0x1826D215, 0x1005FA98, 0x5A8E7215, 0x2DC4AA8A, 0x0D1733AD, 0x337A5004,
- + 0xAB2155A8, 0x64BA1CDF, 0x0485FBEC, 0x0AEFDB58, 0x5771EA8A, 0x7D0C065D, 0x850F97B3, 0xC7E4E1A6,
- + 0x8CAEF5AB, 0xD73309DB, 0xE0948C1E, 0x9D61254A, 0x26D2E3CE, 0x6BEED21A, 0x06FA2FF1, 0x64088AD9,
- + 0x730276D8, 0x646AC83E, 0x182B1F52, 0x0C207B17, 0x5717E1BB, 0x6C5D617A, 0xC0880977, 0xE246D9BA,
- + 0xA04FE208, 0x31ABE574, 0xFC5BDB43, 0x8E10FDE0, 0x20D1824B, 0xCAD23AA9, 0xFFFFFFFF, 0xFFFFFFFF,
- +};
- +
- +static void dh_swap_bytes(void *_in, void *_out, uint32_t len)
- +{
- + uint8_t *in = _in;
- + uint8_t *out = _out;
- + int i;
- + for (i=0; i<len; i++) {
- + out[i] = in[len-1-i];
- + }
- +}
- +
- +/* Computes the modular exponentiation (num^exp % mod). num, exp, and mod are
- + * big endian numbers of size len, in bytes. Each len value must be a multiple
- + * of 4. */
- +int dwc_dh_modpow(void *mem_ctx, void *num, uint32_t num_len,
- + void *exp, uint32_t exp_len,
- + void *mod, uint32_t mod_len,
- + void *out)
- +{
- + /* modpow() takes little endian numbers. AM uses big-endian. This
- + * function swaps bytes of numbers before passing onto modpow. */
- +
- + int retval = 0;
- + uint32_t *result;
- +
- + uint32_t *bignum_num = dwc_alloc(mem_ctx, num_len + 4);
- + uint32_t *bignum_exp = dwc_alloc(mem_ctx, exp_len + 4);
- + uint32_t *bignum_mod = dwc_alloc(mem_ctx, mod_len + 4);
- +
- + dh_swap_bytes(num, &bignum_num[1], num_len);
- + bignum_num[0] = num_len / 4;
- +
- + dh_swap_bytes(exp, &bignum_exp[1], exp_len);
- + bignum_exp[0] = exp_len / 4;
- +
- + dh_swap_bytes(mod, &bignum_mod[1], mod_len);
- + bignum_mod[0] = mod_len / 4;
- +
- + result = dwc_modpow(mem_ctx, bignum_num, bignum_exp, bignum_mod);
- + if (!result) {
- + retval = -1;
- + goto dh_modpow_nomem;
- + }
- +
- + dh_swap_bytes(&result[1], out, result[0] * 4);
- + dwc_free(mem_ctx, result);
- +
- + dh_modpow_nomem:
- + dwc_free(mem_ctx, bignum_num);
- + dwc_free(mem_ctx, bignum_exp);
- + dwc_free(mem_ctx, bignum_mod);
- + return retval;
- +}
- +
- +
- +int dwc_dh_pk(void *mem_ctx, uint8_t nd, uint8_t *exp, uint8_t *pk, uint8_t *hash)
- +{
- + int retval;
- + uint8_t m3[385];
- +
- +#ifndef DH_TEST_VECTORS
- + DWC_RANDOM_BYTES(exp, 32);
- +#endif
- +
- + /* Compute the pkd */
- + if ((retval = dwc_dh_modpow(mem_ctx, dh_g, 4,
- + exp, 32,
- + dh_p, 384, pk))) {
- + return retval;
- + }
- +
- + m3[384] = nd;
- + DWC_MEMCPY(&m3[0], pk, 384);
- + DWC_SHA256(m3, 385, hash);
- +
- + dh_dump("PK", pk, 384);
- + dh_dump("SHA-256(M3)", hash, 32);
- + return 0;
- +}
- +
- +int dwc_dh_derive_keys(void *mem_ctx, uint8_t nd, uint8_t *pkh, uint8_t *pkd,
- + uint8_t *exp, int is_host,
- + char *dd, uint8_t *ck, uint8_t *kdk)
- +{
- + int retval;
- + uint8_t mv[784];
- + uint8_t sha_result[32];
- + uint8_t dhkey[384];
- + uint8_t shared_secret[384];
- + char *message;
- + uint32_t vd;
- +
- + uint8_t *pk;
- +
- + if (is_host) {
- + pk = pkd;
- + }
- + else {
- + pk = pkh;
- + }
- +
- + if ((retval = dwc_dh_modpow(mem_ctx, pk, 384,
- + exp, 32,
- + dh_p, 384, shared_secret))) {
- + return retval;
- + }
- + dh_dump("Shared Secret", shared_secret, 384);
- +
- + DWC_SHA256(shared_secret, 384, dhkey);
- + dh_dump("DHKEY", dhkey, 384);
- +
- + DWC_MEMCPY(&mv[0], pkd, 384);
- + DWC_MEMCPY(&mv[384], pkh, 384);
- + DWC_MEMCPY(&mv[768], "displayed digest", 16);
- + dh_dump("MV", mv, 784);
- +
- + DWC_SHA256(mv, 784, sha_result);
- + dh_dump("SHA-256(MV)", sha_result, 32);
- + dh_dump("First 32-bits of SHA-256(MV)", sha_result, 4);
- +
- + dh_swap_bytes(sha_result, &vd, 4);
- +#ifdef DEBUG
- + DWC_PRINTF("Vd (decimal) = %d\n", vd);
- +#endif
- +
- + switch (nd) {
- + case 2:
- + vd = vd % 100;
- + DWC_SPRINTF(dd, "%02d", vd);
- + break;
- + case 3:
- + vd = vd % 1000;
- + DWC_SPRINTF(dd, "%03d", vd);
- + break;
- + case 4:
- + vd = vd % 10000;
- + DWC_SPRINTF(dd, "%04d", vd);
- + break;
- + }
- +#ifdef DEBUG
- + DWC_PRINTF("Display Digits: %s\n", dd);
- +#endif
- +
- + message = "connection key";
- + DWC_HMAC_SHA256(message, DWC_STRLEN(message), dhkey, 32, sha_result);
- + dh_dump("HMAC(SHA-256, DHKey, connection key)", sha_result, 32);
- + DWC_MEMCPY(ck, sha_result, 16);
- +
- + message = "key derivation key";
- + DWC_HMAC_SHA256(message, DWC_STRLEN(message), dhkey, 32, sha_result);
- + dh_dump("HMAC(SHA-256, DHKey, key derivation key)", sha_result, 32);
- + DWC_MEMCPY(kdk, sha_result, 32);
- +
- + return 0;
- +}
- +
- +
- +#ifdef DH_TEST_VECTORS
- +
- +static __u8 dh_a[] = {
- + 0x44, 0x00, 0x51, 0xd6,
- + 0xf0, 0xb5, 0x5e, 0xa9,
- + 0x67, 0xab, 0x31, 0xc6,
- + 0x8a, 0x8b, 0x5e, 0x37,
- + 0xd9, 0x10, 0xda, 0xe0,
- + 0xe2, 0xd4, 0x59, 0xa4,
- + 0x86, 0x45, 0x9c, 0xaa,
- + 0xdf, 0x36, 0x75, 0x16,
- +};
- +
- +static __u8 dh_b[] = {
- + 0x5d, 0xae, 0xc7, 0x86,
- + 0x79, 0x80, 0xa3, 0x24,
- + 0x8c, 0xe3, 0x57, 0x8f,
- + 0xc7, 0x5f, 0x1b, 0x0f,
- + 0x2d, 0xf8, 0x9d, 0x30,
- + 0x6f, 0xa4, 0x52, 0xcd,
- + 0xe0, 0x7a, 0x04, 0x8a,
- + 0xde, 0xd9, 0x26, 0x56,
- +};
- +
- +void dwc_run_dh_test_vectors(void *mem_ctx)
- +{
- + uint8_t pkd[384];
- + uint8_t pkh[384];
- + uint8_t hashd[32];
- + uint8_t hashh[32];
- + uint8_t ck[16];
- + uint8_t kdk[32];
- + char dd[5];
- +
- + DWC_PRINTF("\n\n\nDH_TEST_VECTORS\n\n");
- +
- + /* compute the PKd and SHA-256(PKd || Nd) */
- + DWC_PRINTF("Computing PKd\n");
- + dwc_dh_pk(mem_ctx, 2, dh_a, pkd, hashd);
- +
- + /* compute the PKd and SHA-256(PKh || Nd) */
- + DWC_PRINTF("Computing PKh\n");
- + dwc_dh_pk(mem_ctx, 2, dh_b, pkh, hashh);
- +
- + /* compute the dhkey */
- + dwc_dh_derive_keys(mem_ctx, 2, pkh, pkd, dh_a, 0, dd, ck, kdk);
- +}
- +#endif /* DH_TEST_VECTORS */
- +
- +#endif /* !CONFIG_MACH_IPMATE */
- +
- +#endif /* DWC_CRYPTOLIB */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_common_port/dwc_dh.h
- @@ -0,0 +1,106 @@
- +/* =========================================================================
- + * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_dh.h $
- + * $Revision: #4 $
- + * $Date: 2010/09/28 $
- + * $Change: 1596182 $
- + *
- + * Synopsys Portability Library Software and documentation
- + * (hereinafter, "Software") is an Unsupported proprietary work of
- + * Synopsys, Inc. unless otherwise expressly agreed to in writing
- + * between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product
- + * under any End User Software License Agreement or Agreement for
- + * Licensed Product with Synopsys or any supplement thereto. You are
- + * permitted to use and redistribute this Software in source and binary
- + * forms, with or without modification, provided that redistributions
- + * of source code must retain this notice. You may not view, use,
- + * disclose, copy or distribute this file or any information contained
- + * herein except pursuant to this license grant from Synopsys. If you
- + * do not agree with this notice, including the disclaimer below, then
- + * you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
- + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- + * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL
- + * SYNOPSYS 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.
- + * ========================================================================= */
- +#ifndef _DWC_DH_H_
- +#define _DWC_DH_H_
- +
- +#ifdef __cplusplus
- +extern "C" {
- +#endif
- +
- +#include "dwc_os.h"
- +
- +/** @file
- + *
- + * This file defines the common functions on device and host for performing
- + * numeric association as defined in the WUSB spec. They are only to be
- + * used internally by the DWC UWB modules. */
- +
- +extern int dwc_dh_sha256(uint8_t *message, uint32_t len, uint8_t *out);
- +extern int dwc_dh_hmac_sha256(uint8_t *message, uint32_t messagelen,
- + uint8_t *key, uint32_t keylen,
- + uint8_t *out);
- +extern int dwc_dh_modpow(void *mem_ctx, void *num, uint32_t num_len,
- + void *exp, uint32_t exp_len,
- + void *mod, uint32_t mod_len,
- + void *out);
- +
- +/** Computes PKD or PKH, and SHA-256(PKd || Nd)
- + *
- + * PK = g^exp mod p.
- + *
- + * Input:
- + * Nd = Number of digits on the device.
- + *
- + * Output:
- + * exp = A 32-byte buffer to be filled with a randomly generated number.
- + * used as either A or B.
- + * pk = A 384-byte buffer to be filled with the PKH or PKD.
- + * hash = A 32-byte buffer to be filled with SHA-256(PK || ND).
- + */
- +extern int dwc_dh_pk(void *mem_ctx, uint8_t nd, uint8_t *exp, uint8_t *pkd, uint8_t *hash);
- +
- +/** Computes the DHKEY, and VD.
- + *
- + * If called from host, then it will comput DHKEY=PKD^exp % p.
- + * If called from device, then it will comput DHKEY=PKH^exp % p.
- + *
- + * Input:
- + * pkd = The PKD value.
- + * pkh = The PKH value.
- + * exp = The A value (if device) or B value (if host) generated in dwc_wudev_dh_pk.
- + * is_host = Set to non zero if a WUSB host is calling this function.
- + *
- + * Output:
- +
- + * dd = A pointer to an buffer to be set to the displayed digits string to be shown
- + * to the user. This buffer should be at 5 bytes long to hold 4 digits plus a
- + * null termination character. This buffer can be used directly for display.
- + * ck = A 16-byte buffer to be filled with the CK.
- + * kdk = A 32-byte buffer to be filled with the KDK.
- + */
- +extern int dwc_dh_derive_keys(void *mem_ctx, uint8_t nd, uint8_t *pkh, uint8_t *pkd,
- + uint8_t *exp, int is_host,
- + char *dd, uint8_t *ck, uint8_t *kdk);
- +
- +#ifdef DH_TEST_VECTORS
- +extern void dwc_run_dh_test_vectors(void);
- +#endif
- +
- +#ifdef __cplusplus
- +}
- +#endif
- +
- +#endif /* _DWC_DH_H_ */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_common_port/dwc_list.h
- @@ -0,0 +1,594 @@
- +/* $OpenBSD: queue.h,v 1.26 2004/05/04 16:59:32 grange Exp $ */
- +/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
- +
- +/*
- + * Copyright (c) 1991, 1993
- + * The Regents of the University of California. All rights reserved.
- + *
- + * Redistribution and use in source and binary forms, with or without
- + * modification, are permitted provided that the following conditions
- + * are met:
- + * 1. Redistributions of source code must retain the above copyright
- + * notice, this list of conditions and the following disclaimer.
- + * 2. 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.
- + * 3. Neither the name of the University 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 REGENTS 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 THE REGENTS OR CONTRIBUTORS 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.
- + *
- + * @(#)queue.h 8.5 (Berkeley) 8/20/94
- + */
- +
- +#ifndef _DWC_LIST_H_
- +#define _DWC_LIST_H_
- +
- +#ifdef __cplusplus
- +extern "C" {
- +#endif
- +
- +/** @file
- + *
- + * This file defines linked list operations. It is derived from BSD with
- + * only the MACRO names being prefixed with DWC_. This is because a few of
- + * these names conflict with those on Linux. For documentation on use, see the
- + * inline comments in the source code. The original license for this source
- + * code applies and is preserved in the dwc_list.h source file.
- + */
- +
- +/*
- + * This file defines five types of data structures: singly-linked lists,
- + * lists, simple queues, tail queues, and circular queues.
- + *
- + *
- + * A singly-linked list is headed by a single forward pointer. The elements
- + * are singly linked for minimum space and pointer manipulation overhead at
- + * the expense of O(n) removal for arbitrary elements. New elements can be
- + * added to the list after an existing element or at the head of the list.
- + * Elements being removed from the head of the list should use the explicit
- + * macro for this purpose for optimum efficiency. A singly-linked list may
- + * only be traversed in the forward direction. Singly-linked lists are ideal
- + * for applications with large datasets and few or no removals or for
- + * implementing a LIFO queue.
- + *
- + * A list is headed by a single forward pointer (or an array of forward
- + * pointers for a hash table header). The elements are doubly linked
- + * so that an arbitrary element can be removed without a need to
- + * traverse the list. New elements can be added to the list before
- + * or after an existing element or at the head of the list. A list
- + * may only be traversed in the forward direction.
- + *
- + * A simple queue is headed by a pair of pointers, one the head of the
- + * list and the other to the tail of the list. The elements are singly
- + * linked to save space, so elements can only be removed from the
- + * head of the list. New elements can be added to the list before or after
- + * an existing element, at the head of the list, or at the end of the
- + * list. A simple queue may only be traversed in the forward direction.
- + *
- + * A tail queue is headed by a pair of pointers, one to the head of the
- + * list and the other to the tail of the list. The elements are doubly
- + * linked so that an arbitrary element can be removed without a need to
- + * traverse the list. New elements can be added to the list before or
- + * after an existing element, at the head of the list, or at the end of
- + * the list. A tail queue may be traversed in either direction.
- + *
- + * A circle queue is headed by a pair of pointers, one to the head of the
- + * list and the other to the tail of the list. The elements are doubly
- + * linked so that an arbitrary element can be removed without a need to
- + * traverse the list. New elements can be added to the list before or after
- + * an existing element, at the head of the list, or at the end of the list.
- + * A circle queue may be traversed in either direction, but has a more
- + * complex end of list detection.
- + *
- + * For details on the use of these macros, see the queue(3) manual page.
- + */
- +
- +/*
- + * Double-linked List.
- + */
- +
- +typedef struct dwc_list_link {
- + struct dwc_list_link *next;
- + struct dwc_list_link *prev;
- +} dwc_list_link_t;
- +
- +#define DWC_LIST_INIT(link) do { \
- + (link)->next = (link); \
- + (link)->prev = (link); \
- +} while (0)
- +
- +#define DWC_LIST_FIRST(link) ((link)->next)
- +#define DWC_LIST_LAST(link) ((link)->prev)
- +#define DWC_LIST_END(link) (link)
- +#define DWC_LIST_NEXT(link) ((link)->next)
- +#define DWC_LIST_PREV(link) ((link)->prev)
- +#define DWC_LIST_EMPTY(link) \
- + (DWC_LIST_FIRST(link) == DWC_LIST_END(link))
- +#define DWC_LIST_ENTRY(link, type, field) \
- + (type *)((uint8_t *)(link) - (size_t)(&((type *)0)->field))
- +
- +#if 0
- +#define DWC_LIST_INSERT_HEAD(list, link) do { \
- + (link)->next = (list)->next; \
- + (link)->prev = (list); \
- + (list)->next->prev = (link); \
- + (list)->next = (link); \
- +} while (0)
- +
- +#define DWC_LIST_INSERT_TAIL(list, link) do { \
- + (link)->next = (list); \
- + (link)->prev = (list)->prev; \
- + (list)->prev->next = (link); \
- + (list)->prev = (link); \
- +} while (0)
- +#else
- +#define DWC_LIST_INSERT_HEAD(list, link) do { \
- + dwc_list_link_t *__next__ = (list)->next; \
- + __next__->prev = (link); \
- + (link)->next = __next__; \
- + (link)->prev = (list); \
- + (list)->next = (link); \
- +} while (0)
- +
- +#define DWC_LIST_INSERT_TAIL(list, link) do { \
- + dwc_list_link_t *__prev__ = (list)->prev; \
- + (list)->prev = (link); \
- + (link)->next = (list); \
- + (link)->prev = __prev__; \
- + __prev__->next = (link); \
- +} while (0)
- +#endif
- +
- +#if 0
- +static inline void __list_add(struct list_head *new,
- + struct list_head *prev,
- + struct list_head *next)
- +{
- + next->prev = new;
- + new->next = next;
- + new->prev = prev;
- + prev->next = new;
- +}
- +
- +static inline void list_add(struct list_head *new, struct list_head *head)
- +{
- + __list_add(new, head, head->next);
- +}
- +
- +static inline void list_add_tail(struct list_head *new, struct list_head *head)
- +{
- + __list_add(new, head->prev, head);
- +}
- +
- +static inline void __list_del(struct list_head * prev, struct list_head * next)
- +{
- + next->prev = prev;
- + prev->next = next;
- +}
- +
- +static inline void list_del(struct list_head *entry)
- +{
- + __list_del(entry->prev, entry->next);
- + entry->next = LIST_POISON1;
- + entry->prev = LIST_POISON2;
- +}
- +#endif
- +
- +#define DWC_LIST_REMOVE(link) do { \
- + (link)->next->prev = (link)->prev; \
- + (link)->prev->next = (link)->next; \
- +} while (0)
- +
- +#define DWC_LIST_REMOVE_INIT(link) do { \
- + DWC_LIST_REMOVE(link); \
- + DWC_LIST_INIT(link); \
- +} while (0)
- +
- +#define DWC_LIST_MOVE_HEAD(list, link) do { \
- + DWC_LIST_REMOVE(link); \
- + DWC_LIST_INSERT_HEAD(list, link); \
- +} while (0)
- +
- +#define DWC_LIST_MOVE_TAIL(list, link) do { \
- + DWC_LIST_REMOVE(link); \
- + DWC_LIST_INSERT_TAIL(list, link); \
- +} while (0)
- +
- +#define DWC_LIST_FOREACH(var, list) \
- + for((var) = DWC_LIST_FIRST(list); \
- + (var) != DWC_LIST_END(list); \
- + (var) = DWC_LIST_NEXT(var))
- +
- +#define DWC_LIST_FOREACH_SAFE(var, var2, list) \
- + for((var) = DWC_LIST_FIRST(list), (var2) = DWC_LIST_NEXT(var); \
- + (var) != DWC_LIST_END(list); \
- + (var) = (var2), (var2) = DWC_LIST_NEXT(var2))
- +
- +#define DWC_LIST_FOREACH_REVERSE(var, list) \
- + for((var) = DWC_LIST_LAST(list); \
- + (var) != DWC_LIST_END(list); \
- + (var) = DWC_LIST_PREV(var))
- +
- +/*
- + * Singly-linked List definitions.
- + */
- +#define DWC_SLIST_HEAD(name, type) \
- +struct name { \
- + struct type *slh_first; /* first element */ \
- +}
- +
- +#define DWC_SLIST_HEAD_INITIALIZER(head) \
- + { NULL }
- +
- +#define DWC_SLIST_ENTRY(type) \
- +struct { \
- + struct type *sle_next; /* next element */ \
- +}
- +
- +/*
- + * Singly-linked List access methods.
- + */
- +#define DWC_SLIST_FIRST(head) ((head)->slh_first)
- +#define DWC_SLIST_END(head) NULL
- +#define DWC_SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
- +#define DWC_SLIST_NEXT(elm, field) ((elm)->field.sle_next)
- +
- +#define DWC_SLIST_FOREACH(var, head, field) \
- + for((var) = SLIST_FIRST(head); \
- + (var) != SLIST_END(head); \
- + (var) = SLIST_NEXT(var, field))
- +
- +#define DWC_SLIST_FOREACH_PREVPTR(var, varp, head, field) \
- + for((varp) = &SLIST_FIRST((head)); \
- + ((var) = *(varp)) != SLIST_END(head); \
- + (varp) = &SLIST_NEXT((var), field))
- +
- +/*
- + * Singly-linked List functions.
- + */
- +#define DWC_SLIST_INIT(head) { \
- + SLIST_FIRST(head) = SLIST_END(head); \
- +}
- +
- +#define DWC_SLIST_INSERT_AFTER(slistelm, elm, field) do { \
- + (elm)->field.sle_next = (slistelm)->field.sle_next; \
- + (slistelm)->field.sle_next = (elm); \
- +} while (0)
- +
- +#define DWC_SLIST_INSERT_HEAD(head, elm, field) do { \
- + (elm)->field.sle_next = (head)->slh_first; \
- + (head)->slh_first = (elm); \
- +} while (0)
- +
- +#define DWC_SLIST_REMOVE_NEXT(head, elm, field) do { \
- + (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
- +} while (0)
- +
- +#define DWC_SLIST_REMOVE_HEAD(head, field) do { \
- + (head)->slh_first = (head)->slh_first->field.sle_next; \
- +} while (0)
- +
- +#define DWC_SLIST_REMOVE(head, elm, type, field) do { \
- + if ((head)->slh_first == (elm)) { \
- + SLIST_REMOVE_HEAD((head), field); \
- + } \
- + else { \
- + struct type *curelm = (head)->slh_first; \
- + while( curelm->field.sle_next != (elm) ) \
- + curelm = curelm->field.sle_next; \
- + curelm->field.sle_next = \
- + curelm->field.sle_next->field.sle_next; \
- + } \
- +} while (0)
- +
- +/*
- + * Simple queue definitions.
- + */
- +#define DWC_SIMPLEQ_HEAD(name, type) \
- +struct name { \
- + struct type *sqh_first; /* first element */ \
- + struct type **sqh_last; /* addr of last next element */ \
- +}
- +
- +#define DWC_SIMPLEQ_HEAD_INITIALIZER(head) \
- + { NULL, &(head).sqh_first }
- +
- +#define DWC_SIMPLEQ_ENTRY(type) \
- +struct { \
- + struct type *sqe_next; /* next element */ \
- +}
- +
- +/*
- + * Simple queue access methods.
- + */
- +#define DWC_SIMPLEQ_FIRST(head) ((head)->sqh_first)
- +#define DWC_SIMPLEQ_END(head) NULL
- +#define DWC_SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
- +#define DWC_SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
- +
- +#define DWC_SIMPLEQ_FOREACH(var, head, field) \
- + for((var) = SIMPLEQ_FIRST(head); \
- + (var) != SIMPLEQ_END(head); \
- + (var) = SIMPLEQ_NEXT(var, field))
- +
- +/*
- + * Simple queue functions.
- + */
- +#define DWC_SIMPLEQ_INIT(head) do { \
- + (head)->sqh_first = NULL; \
- + (head)->sqh_last = &(head)->sqh_first; \
- +} while (0)
- +
- +#define DWC_SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
- + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
- + (head)->sqh_last = &(elm)->field.sqe_next; \
- + (head)->sqh_first = (elm); \
- +} while (0)
- +
- +#define DWC_SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
- + (elm)->field.sqe_next = NULL; \
- + *(head)->sqh_last = (elm); \
- + (head)->sqh_last = &(elm)->field.sqe_next; \
- +} while (0)
- +
- +#define DWC_SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
- + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
- + (head)->sqh_last = &(elm)->field.sqe_next; \
- + (listelm)->field.sqe_next = (elm); \
- +} while (0)
- +
- +#define DWC_SIMPLEQ_REMOVE_HEAD(head, field) do { \
- + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
- + (head)->sqh_last = &(head)->sqh_first; \
- +} while (0)
- +
- +/*
- + * Tail queue definitions.
- + */
- +#define DWC_TAILQ_HEAD(name, type) \
- +struct name { \
- + struct type *tqh_first; /* first element */ \
- + struct type **tqh_last; /* addr of last next element */ \
- +}
- +
- +#define DWC_TAILQ_HEAD_INITIALIZER(head) \
- + { NULL, &(head).tqh_first }
- +
- +#define DWC_TAILQ_ENTRY(type) \
- +struct { \
- + struct type *tqe_next; /* next element */ \
- + struct type **tqe_prev; /* address of previous next element */ \
- +}
- +
- +/*
- + * tail queue access methods
- + */
- +#define DWC_TAILQ_FIRST(head) ((head)->tqh_first)
- +#define DWC_TAILQ_END(head) NULL
- +#define DWC_TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
- +#define DWC_TAILQ_LAST(head, headname) \
- + (*(((struct headname *)((head)->tqh_last))->tqh_last))
- +/* XXX */
- +#define DWC_TAILQ_PREV(elm, headname, field) \
- + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
- +#define DWC_TAILQ_EMPTY(head) \
- + (DWC_TAILQ_FIRST(head) == DWC_TAILQ_END(head))
- +
- +#define DWC_TAILQ_FOREACH(var, head, field) \
- + for ((var) = DWC_TAILQ_FIRST(head); \
- + (var) != DWC_TAILQ_END(head); \
- + (var) = DWC_TAILQ_NEXT(var, field))
- +
- +#define DWC_TAILQ_FOREACH_REVERSE(var, head, headname, field) \
- + for ((var) = DWC_TAILQ_LAST(head, headname); \
- + (var) != DWC_TAILQ_END(head); \
- + (var) = DWC_TAILQ_PREV(var, headname, field))
- +
- +/*
- + * Tail queue functions.
- + */
- +#define DWC_TAILQ_INIT(head) do { \
- + (head)->tqh_first = NULL; \
- + (head)->tqh_last = &(head)->tqh_first; \
- +} while (0)
- +
- +#define DWC_TAILQ_INSERT_HEAD(head, elm, field) do { \
- + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
- + (head)->tqh_first->field.tqe_prev = \
- + &(elm)->field.tqe_next; \
- + else \
- + (head)->tqh_last = &(elm)->field.tqe_next; \
- + (head)->tqh_first = (elm); \
- + (elm)->field.tqe_prev = &(head)->tqh_first; \
- +} while (0)
- +
- +#define DWC_TAILQ_INSERT_TAIL(head, elm, field) do { \
- + (elm)->field.tqe_next = NULL; \
- + (elm)->field.tqe_prev = (head)->tqh_last; \
- + *(head)->tqh_last = (elm); \
- + (head)->tqh_last = &(elm)->field.tqe_next; \
- +} while (0)
- +
- +#define DWC_TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
- + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
- + (elm)->field.tqe_next->field.tqe_prev = \
- + &(elm)->field.tqe_next; \
- + else \
- + (head)->tqh_last = &(elm)->field.tqe_next; \
- + (listelm)->field.tqe_next = (elm); \
- + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
- +} while (0)
- +
- +#define DWC_TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
- + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
- + (elm)->field.tqe_next = (listelm); \
- + *(listelm)->field.tqe_prev = (elm); \
- + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
- +} while (0)
- +
- +#define DWC_TAILQ_REMOVE(head, elm, field) do { \
- + if (((elm)->field.tqe_next) != NULL) \
- + (elm)->field.tqe_next->field.tqe_prev = \
- + (elm)->field.tqe_prev; \
- + else \
- + (head)->tqh_last = (elm)->field.tqe_prev; \
- + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
- +} while (0)
- +
- +#define DWC_TAILQ_REPLACE(head, elm, elm2, field) do { \
- + if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
- + (elm2)->field.tqe_next->field.tqe_prev = \
- + &(elm2)->field.tqe_next; \
- + else \
- + (head)->tqh_last = &(elm2)->field.tqe_next; \
- + (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
- + *(elm2)->field.tqe_prev = (elm2); \
- +} while (0)
- +
- +/*
- + * Circular queue definitions.
- + */
- +#define DWC_CIRCLEQ_HEAD(name, type) \
- +struct name { \
- + struct type *cqh_first; /* first element */ \
- + struct type *cqh_last; /* last element */ \
- +}
- +
- +#define DWC_CIRCLEQ_HEAD_INITIALIZER(head) \
- + { DWC_CIRCLEQ_END(&head), DWC_CIRCLEQ_END(&head) }
- +
- +#define DWC_CIRCLEQ_ENTRY(type) \
- +struct { \
- + struct type *cqe_next; /* next element */ \
- + struct type *cqe_prev; /* previous element */ \
- +}
- +
- +/*
- + * Circular queue access methods
- + */
- +#define DWC_CIRCLEQ_FIRST(head) ((head)->cqh_first)
- +#define DWC_CIRCLEQ_LAST(head) ((head)->cqh_last)
- +#define DWC_CIRCLEQ_END(head) ((void *)(head))
- +#define DWC_CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
- +#define DWC_CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
- +#define DWC_CIRCLEQ_EMPTY(head) \
- + (DWC_CIRCLEQ_FIRST(head) == DWC_CIRCLEQ_END(head))
- +
- +#define DWC_CIRCLEQ_EMPTY_ENTRY(elm, field) (((elm)->field.cqe_next == NULL) && ((elm)->field.cqe_prev == NULL))
- +
- +#define DWC_CIRCLEQ_FOREACH(var, head, field) \
- + for((var) = DWC_CIRCLEQ_FIRST(head); \
- + (var) != DWC_CIRCLEQ_END(head); \
- + (var) = DWC_CIRCLEQ_NEXT(var, field))
- +
- +#define DWC_CIRCLEQ_FOREACH_SAFE(var, var2, head, field) \
- + for((var) = DWC_CIRCLEQ_FIRST(head), var2 = DWC_CIRCLEQ_NEXT(var, field); \
- + (var) != DWC_CIRCLEQ_END(head); \
- + (var) = var2, var2 = DWC_CIRCLEQ_NEXT(var, field))
- +
- +#define DWC_CIRCLEQ_FOREACH_REVERSE(var, head, field) \
- + for((var) = DWC_CIRCLEQ_LAST(head); \
- + (var) != DWC_CIRCLEQ_END(head); \
- + (var) = DWC_CIRCLEQ_PREV(var, field))
- +
- +/*
- + * Circular queue functions.
- + */
- +#define DWC_CIRCLEQ_INIT(head) do { \
- + (head)->cqh_first = DWC_CIRCLEQ_END(head); \
- + (head)->cqh_last = DWC_CIRCLEQ_END(head); \
- +} while (0)
- +
- +#define DWC_CIRCLEQ_INIT_ENTRY(elm, field) do { \
- + (elm)->field.cqe_next = NULL; \
- + (elm)->field.cqe_prev = NULL; \
- +} while (0)
- +
- +#define DWC_CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
- + (elm)->field.cqe_next = (listelm)->field.cqe_next; \
- + (elm)->field.cqe_prev = (listelm); \
- + if ((listelm)->field.cqe_next == DWC_CIRCLEQ_END(head)) \
- + (head)->cqh_last = (elm); \
- + else \
- + (listelm)->field.cqe_next->field.cqe_prev = (elm); \
- + (listelm)->field.cqe_next = (elm); \
- +} while (0)
- +
- +#define DWC_CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
- + (elm)->field.cqe_next = (listelm); \
- + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
- + if ((listelm)->field.cqe_prev == DWC_CIRCLEQ_END(head)) \
- + (head)->cqh_first = (elm); \
- + else \
- + (listelm)->field.cqe_prev->field.cqe_next = (elm); \
- + (listelm)->field.cqe_prev = (elm); \
- +} while (0)
- +
- +#define DWC_CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
- + (elm)->field.cqe_next = (head)->cqh_first; \
- + (elm)->field.cqe_prev = DWC_CIRCLEQ_END(head); \
- + if ((head)->cqh_last == DWC_CIRCLEQ_END(head)) \
- + (head)->cqh_last = (elm); \
- + else \
- + (head)->cqh_first->field.cqe_prev = (elm); \
- + (head)->cqh_first = (elm); \
- +} while (0)
- +
- +#define DWC_CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
- + (elm)->field.cqe_next = DWC_CIRCLEQ_END(head); \
- + (elm)->field.cqe_prev = (head)->cqh_last; \
- + if ((head)->cqh_first == DWC_CIRCLEQ_END(head)) \
- + (head)->cqh_first = (elm); \
- + else \
- + (head)->cqh_last->field.cqe_next = (elm); \
- + (head)->cqh_last = (elm); \
- +} while (0)
- +
- +#define DWC_CIRCLEQ_REMOVE(head, elm, field) do { \
- + if ((elm)->field.cqe_next == DWC_CIRCLEQ_END(head)) \
- + (head)->cqh_last = (elm)->field.cqe_prev; \
- + else \
- + (elm)->field.cqe_next->field.cqe_prev = \
- + (elm)->field.cqe_prev; \
- + if ((elm)->field.cqe_prev == DWC_CIRCLEQ_END(head)) \
- + (head)->cqh_first = (elm)->field.cqe_next; \
- + else \
- + (elm)->field.cqe_prev->field.cqe_next = \
- + (elm)->field.cqe_next; \
- +} while (0)
- +
- +#define DWC_CIRCLEQ_REMOVE_INIT(head, elm, field) do { \
- + DWC_CIRCLEQ_REMOVE(head, elm, field); \
- + DWC_CIRCLEQ_INIT_ENTRY(elm, field); \
- +} while (0)
- +
- +#define DWC_CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
- + if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
- + DWC_CIRCLEQ_END(head)) \
- + (head).cqh_last = (elm2); \
- + else \
- + (elm2)->field.cqe_next->field.cqe_prev = (elm2); \
- + if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
- + DWC_CIRCLEQ_END(head)) \
- + (head).cqh_first = (elm2); \
- + else \
- + (elm2)->field.cqe_prev->field.cqe_next = (elm2); \
- +} while (0)
- +
- +#ifdef __cplusplus
- +}
- +#endif
- +
- +#endif /* _DWC_LIST_H_ */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_common_port/dwc_mem.c
- @@ -0,0 +1,245 @@
- +/* Memory Debugging */
- +#ifdef DWC_DEBUG_MEMORY
- +
- +#include "dwc_os.h"
- +#include "dwc_list.h"
- +
- +struct allocation {
- + void *addr;
- + void *ctx;
- + char *func;
- + int line;
- + uint32_t size;
- + int dma;
- + DWC_CIRCLEQ_ENTRY(allocation) entry;
- +};
- +
- +DWC_CIRCLEQ_HEAD(allocation_queue, allocation);
- +
- +struct allocation_manager {
- + void *mem_ctx;
- + struct allocation_queue allocations;
- +
- + /* statistics */
- + int num;
- + int num_freed;
- + int num_active;
- + uint32_t total;
- + uint32_t cur;
- + uint32_t max;
- +};
- +
- +static struct allocation_manager *manager = NULL;
- +
- +static int add_allocation(void *ctx, uint32_t size, char const *func, int line, void *addr,
- + int dma)
- +{
- + struct allocation *a;
- +
- + DWC_ASSERT(manager != NULL, "manager not allocated");
- +
- + a = __DWC_ALLOC_ATOMIC(manager->mem_ctx, sizeof(*a));
- + if (!a) {
- + return -DWC_E_NO_MEMORY;
- + }
- +
- + a->func = __DWC_ALLOC_ATOMIC(manager->mem_ctx, DWC_STRLEN(func) + 1);
- + if (!a->func) {
- + __DWC_FREE(manager->mem_ctx, a);
- + return -DWC_E_NO_MEMORY;
- + }
- +
- + DWC_MEMCPY(a->func, func, DWC_STRLEN(func) + 1);
- + a->addr = addr;
- + a->ctx = ctx;
- + a->line = line;
- + a->size = size;
- + a->dma = dma;
- + DWC_CIRCLEQ_INSERT_TAIL(&manager->allocations, a, entry);
- +
- + /* Update stats */
- + manager->num++;
- + manager->num_active++;
- + manager->total += size;
- + manager->cur += size;
- +
- + if (manager->max < manager->cur) {
- + manager->max = manager->cur;
- + }
- +
- + return 0;
- +}
- +
- +static struct allocation *find_allocation(void *ctx, void *addr)
- +{
- + struct allocation *a;
- +
- + DWC_CIRCLEQ_FOREACH(a, &manager->allocations, entry) {
- + if (a->ctx == ctx && a->addr == addr) {
- + return a;
- + }
- + }
- +
- + return NULL;
- +}
- +
- +static void free_allocation(void *ctx, void *addr, char const *func, int line)
- +{
- + struct allocation *a = find_allocation(ctx, addr);
- +
- + if (!a) {
- + DWC_ASSERT(0,
- + "Free of address %p that was never allocated or already freed %s:%d",
- + addr, func, line);
- + return;
- + }
- +
- + DWC_CIRCLEQ_REMOVE(&manager->allocations, a, entry);
- +
- + manager->num_active--;
- + manager->num_freed++;
- + manager->cur -= a->size;
- + __DWC_FREE(manager->mem_ctx, a->func);
- + __DWC_FREE(manager->mem_ctx, a);
- +}
- +
- +int dwc_memory_debug_start(void *mem_ctx)
- +{
- + DWC_ASSERT(manager == NULL, "Memory debugging has already started\n");
- +
- + if (manager) {
- + return -DWC_E_BUSY;
- + }
- +
- + manager = __DWC_ALLOC(mem_ctx, sizeof(*manager));
- + if (!manager) {
- + return -DWC_E_NO_MEMORY;
- + }
- +
- + DWC_CIRCLEQ_INIT(&manager->allocations);
- + manager->mem_ctx = mem_ctx;
- + manager->num = 0;
- + manager->num_freed = 0;
- + manager->num_active = 0;
- + manager->total = 0;
- + manager->cur = 0;
- + manager->max = 0;
- +
- + return 0;
- +}
- +
- +void dwc_memory_debug_stop(void)
- +{
- + struct allocation *a;
- +
- + dwc_memory_debug_report();
- +
- + DWC_CIRCLEQ_FOREACH(a, &manager->allocations, entry) {
- + DWC_ERROR("Memory leaked from %s:%d\n", a->func, a->line);
- + free_allocation(a->ctx, a->addr, NULL, -1);
- + }
- +
- + __DWC_FREE(manager->mem_ctx, manager);
- +}
- +
- +void dwc_memory_debug_report(void)
- +{
- + struct allocation *a;
- +
- + DWC_PRINTF("\n\n\n----------------- Memory Debugging Report -----------------\n\n");
- + DWC_PRINTF("Num Allocations = %d\n", manager->num);
- + DWC_PRINTF("Freed = %d\n", manager->num_freed);
- + DWC_PRINTF("Active = %d\n", manager->num_active);
- + DWC_PRINTF("Current Memory Used = %d\n", manager->cur);
- + DWC_PRINTF("Total Memory Used = %d\n", manager->total);
- + DWC_PRINTF("Maximum Memory Used at Once = %d\n", manager->max);
- + DWC_PRINTF("Unfreed allocations:\n");
- +
- + DWC_CIRCLEQ_FOREACH(a, &manager->allocations, entry) {
- + DWC_PRINTF(" addr=%p, size=%d from %s:%d, DMA=%d\n",
- + a->addr, a->size, a->func, a->line, a->dma);
- + }
- +}
- +
- +/* The replacement functions */
- +void *dwc_alloc_debug(void *mem_ctx, uint32_t size, char const *func, int line)
- +{
- + void *addr = __DWC_ALLOC(mem_ctx, size);
- +
- + if (!addr) {
- + return NULL;
- + }
- +
- + if (add_allocation(mem_ctx, size, func, line, addr, 0)) {
- + __DWC_FREE(mem_ctx, addr);
- + return NULL;
- + }
- +
- + return addr;
- +}
- +
- +void *dwc_alloc_atomic_debug(void *mem_ctx, uint32_t size, char const *func,
- + int line)
- +{
- + void *addr = __DWC_ALLOC_ATOMIC(mem_ctx, size);
- +
- + if (!addr) {
- + return NULL;
- + }
- +
- + if (add_allocation(mem_ctx, size, func, line, addr, 0)) {
- + __DWC_FREE(mem_ctx, addr);
- + return NULL;
- + }
- +
- + return addr;
- +}
- +
- +void dwc_free_debug(void *mem_ctx, void *addr, char const *func, int line)
- +{
- + free_allocation(mem_ctx, addr, func, line);
- + __DWC_FREE(mem_ctx, addr);
- +}
- +
- +void *dwc_dma_alloc_debug(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr,
- + char const *func, int line)
- +{
- + void *addr = __DWC_DMA_ALLOC(dma_ctx, size, dma_addr);
- +
- + if (!addr) {
- + return NULL;
- + }
- +
- + if (add_allocation(dma_ctx, size, func, line, addr, 1)) {
- + __DWC_DMA_FREE(dma_ctx, size, addr, *dma_addr);
- + return NULL;
- + }
- +
- + return addr;
- +}
- +
- +void *dwc_dma_alloc_atomic_debug(void *dma_ctx, uint32_t size,
- + dwc_dma_t *dma_addr, char const *func, int line)
- +{
- + void *addr = __DWC_DMA_ALLOC_ATOMIC(dma_ctx, size, dma_addr);
- +
- + if (!addr) {
- + return NULL;
- + }
- +
- + if (add_allocation(dma_ctx, size, func, line, addr, 1)) {
- + __DWC_DMA_FREE(dma_ctx, size, addr, *dma_addr);
- + return NULL;
- + }
- +
- + return addr;
- +}
- +
- +void dwc_dma_free_debug(void *dma_ctx, uint32_t size, void *virt_addr,
- + dwc_dma_t dma_addr, char const *func, int line)
- +{
- + free_allocation(dma_ctx, virt_addr, func, line);
- + __DWC_DMA_FREE(dma_ctx, size, virt_addr, dma_addr);
- +}
- +
- +#endif /* DWC_DEBUG_MEMORY */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_common_port/dwc_modpow.c
- @@ -0,0 +1,636 @@
- +/* Bignum routines adapted from PUTTY sources. PuTTY copyright notice follows.
- + *
- + * PuTTY is copyright 1997-2007 Simon Tatham.
- + *
- + * Portions copyright Robert de Bath, Joris van Rantwijk, Delian
- + * Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry,
- + * Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus
- + * Kuhn, and CORE SDI S.A.
- + *
- + * Permission is hereby granted, free of charge, to any person
- + * obtaining a copy of this software and associated documentation files
- + * (the "Software"), to deal in the Software without restriction,
- + * including without limitation the rights to use, copy, modify, merge,
- + * publish, distribute, sublicense, and/or sell copies of the Software,
- + * and to permit persons to whom the Software is furnished to do so,
- + * subject to the following conditions:
- + *
- + * The above copyright notice and this permission notice shall be
- + * included in all copies or substantial portions of the Software.
- +
- + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- + * NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE
- + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- + *
- + */
- +#ifdef DWC_CRYPTOLIB
- +
- +#ifndef CONFIG_MACH_IPMATE
- +
- +#include "dwc_modpow.h"
- +
- +#define BIGNUM_INT_MASK 0xFFFFFFFFUL
- +#define BIGNUM_TOP_BIT 0x80000000UL
- +#define BIGNUM_INT_BITS 32
- +
- +
- +static void *snmalloc(void *mem_ctx, size_t n, size_t size)
- +{
- + void *p;
- + size *= n;
- + if (size == 0) size = 1;
- + p = dwc_alloc(mem_ctx, size);
- + return p;
- +}
- +
- +#define snewn(ctx, n, type) ((type *)snmalloc((ctx), (n), sizeof(type)))
- +#define sfree dwc_free
- +
- +/*
- + * Usage notes:
- + * * Do not call the DIVMOD_WORD macro with expressions such as array
- + * subscripts, as some implementations object to this (see below).
- + * * Note that none of the division methods below will cope if the
- + * quotient won't fit into BIGNUM_INT_BITS. Callers should be careful
- + * to avoid this case.
- + * If this condition occurs, in the case of the x86 DIV instruction,
- + * an overflow exception will occur, which (according to a correspondent)
- + * will manifest on Windows as something like
- + * 0xC0000095: Integer overflow
- + * The C variant won't give the right answer, either.
- + */
- +
- +#define MUL_WORD(w1, w2) ((BignumDblInt)w1 * w2)
- +
- +#if defined __GNUC__ && defined __i386__
- +#define DIVMOD_WORD(q, r, hi, lo, w) \
- + __asm__("div %2" : \
- + "=d" (r), "=a" (q) : \
- + "r" (w), "d" (hi), "a" (lo))
- +#else
- +#define DIVMOD_WORD(q, r, hi, lo, w) do { \
- + BignumDblInt n = (((BignumDblInt)hi) << BIGNUM_INT_BITS) | lo; \
- + q = n / w; \
- + r = n % w; \
- +} while (0)
- +#endif
- +
- +// q = n / w;
- +// r = n % w;
- +
- +#define BIGNUM_INT_BYTES (BIGNUM_INT_BITS / 8)
- +
- +#define BIGNUM_INTERNAL
- +
- +static Bignum newbn(void *mem_ctx, int length)
- +{
- + Bignum b = snewn(mem_ctx, length + 1, BignumInt);
- + //if (!b)
- + //abort(); /* FIXME */
- + DWC_MEMSET(b, 0, (length + 1) * sizeof(*b));
- + b[0] = length;
- + return b;
- +}
- +
- +void freebn(void *mem_ctx, Bignum b)
- +{
- + /*
- + * Burn the evidence, just in case.
- + */
- + DWC_MEMSET(b, 0, sizeof(b[0]) * (b[0] + 1));
- + sfree(mem_ctx, b);
- +}
- +
- +/*
- + * Compute c = a * b.
- + * Input is in the first len words of a and b.
- + * Result is returned in the first 2*len words of c.
- + */
- +static void internal_mul(BignumInt *a, BignumInt *b,
- + BignumInt *c, int len)
- +{
- + int i, j;
- + BignumDblInt t;
- +
- + for (j = 0; j < 2 * len; j++)
- + c[j] = 0;
- +
- + for (i = len - 1; i >= 0; i--) {
- + t = 0;
- + for (j = len - 1; j >= 0; j--) {
- + t += MUL_WORD(a[i], (BignumDblInt) b[j]);
- + t += (BignumDblInt) c[i + j + 1];
- + c[i + j + 1] = (BignumInt) t;
- + t = t >> BIGNUM_INT_BITS;
- + }
- + c[i] = (BignumInt) t;
- + }
- +}
- +
- +static void internal_add_shifted(BignumInt *number,
- + unsigned n, int shift)
- +{
- + int word = 1 + (shift / BIGNUM_INT_BITS);
- + int bshift = shift % BIGNUM_INT_BITS;
- + BignumDblInt addend;
- +
- + addend = (BignumDblInt)n << bshift;
- +
- + while (addend) {
- + addend += number[word];
- + number[word] = (BignumInt) addend & BIGNUM_INT_MASK;
- + addend >>= BIGNUM_INT_BITS;
- + word++;
- + }
- +}
- +
- +/*
- + * Compute a = a % m.
- + * Input in first alen words of a and first mlen words of m.
- + * Output in first alen words of a
- + * (of which first alen-mlen words will be zero).
- + * The MSW of m MUST have its high bit set.
- + * Quotient is accumulated in the `quotient' array, which is a Bignum
- + * rather than the internal bigendian format. Quotient parts are shifted
- + * left by `qshift' before adding into quot.
- + */
- +static void internal_mod(BignumInt *a, int alen,
- + BignumInt *m, int mlen,
- + BignumInt *quot, int qshift)
- +{
- + BignumInt m0, m1;
- + unsigned int h;
- + int i, k;
- +
- + m0 = m[0];
- + if (mlen > 1)
- + m1 = m[1];
- + else
- + m1 = 0;
- +
- + for (i = 0; i <= alen - mlen; i++) {
- + BignumDblInt t;
- + unsigned int q, r, c, ai1;
- +
- + if (i == 0) {
- + h = 0;
- + } else {
- + h = a[i - 1];
- + a[i - 1] = 0;
- + }
- +
- + if (i == alen - 1)
- + ai1 = 0;
- + else
- + ai1 = a[i + 1];
- +
- + /* Find q = h:a[i] / m0 */
- + if (h >= m0) {
- + /*
- + * Special case.
- + *
- + * To illustrate it, suppose a BignumInt is 8 bits, and
- + * we are dividing (say) A1:23:45:67 by A1:B2:C3. Then
- + * our initial division will be 0xA123 / 0xA1, which
- + * will give a quotient of 0x100 and a divide overflow.
- + * However, the invariants in this division algorithm
- + * are not violated, since the full number A1:23:... is
- + * _less_ than the quotient prefix A1:B2:... and so the
- + * following correction loop would have sorted it out.
- + *
- + * In this situation we set q to be the largest
- + * quotient we _can_ stomach (0xFF, of course).
- + */
- + q = BIGNUM_INT_MASK;
- + } else {
- + /* Macro doesn't want an array subscript expression passed
- + * into it (see definition), so use a temporary. */
- + BignumInt tmplo = a[i];
- + DIVMOD_WORD(q, r, h, tmplo, m0);
- +
- + /* Refine our estimate of q by looking at
- + h:a[i]:a[i+1] / m0:m1 */
- + t = MUL_WORD(m1, q);
- + if (t > ((BignumDblInt) r << BIGNUM_INT_BITS) + ai1) {
- + q--;
- + t -= m1;
- + r = (r + m0) & BIGNUM_INT_MASK; /* overflow? */
- + if (r >= (BignumDblInt) m0 &&
- + t > ((BignumDblInt) r << BIGNUM_INT_BITS) + ai1) q--;
- + }
- + }
- +
- + /* Subtract q * m from a[i...] */
- + c = 0;
- + for (k = mlen - 1; k >= 0; k--) {
- + t = MUL_WORD(q, m[k]);
- + t += c;
- + c = (unsigned)(t >> BIGNUM_INT_BITS);
- + if ((BignumInt) t > a[i + k])
- + c++;
- + a[i + k] -= (BignumInt) t;
- + }
- +
- + /* Add back m in case of borrow */
- + if (c != h) {
- + t = 0;
- + for (k = mlen - 1; k >= 0; k--) {
- + t += m[k];
- + t += a[i + k];
- + a[i + k] = (BignumInt) t;
- + t = t >> BIGNUM_INT_BITS;
- + }
- + q--;
- + }
- + if (quot)
- + internal_add_shifted(quot, q, qshift + BIGNUM_INT_BITS * (alen - mlen - i));
- + }
- +}
- +
- +/*
- + * Compute p % mod.
- + * The most significant word of mod MUST be non-zero.
- + * We assume that the result array is the same size as the mod array.
- + * We optionally write out a quotient if `quotient' is non-NULL.
- + * We can avoid writing out the result if `result' is NULL.
- + */
- +void bigdivmod(void *mem_ctx, Bignum p, Bignum mod, Bignum result, Bignum quotient)
- +{
- + BignumInt *n, *m;
- + int mshift;
- + int plen, mlen, i, j;
- +
- + /* Allocate m of size mlen, copy mod to m */
- + /* We use big endian internally */
- + mlen = mod[0];
- + m = snewn(mem_ctx, mlen, BignumInt);
- + //if (!m)
- + //abort(); /* FIXME */
- + for (j = 0; j < mlen; j++)
- + m[j] = mod[mod[0] - j];
- +
- + /* Shift m left to make msb bit set */
- + for (mshift = 0; mshift < BIGNUM_INT_BITS-1; mshift++)
- + if ((m[0] << mshift) & BIGNUM_TOP_BIT)
- + break;
- + if (mshift) {
- + for (i = 0; i < mlen - 1; i++)
- + m[i] = (m[i] << mshift) | (m[i + 1] >> (BIGNUM_INT_BITS - mshift));
- + m[mlen - 1] = m[mlen - 1] << mshift;
- + }
- +
- + plen = p[0];
- + /* Ensure plen > mlen */
- + if (plen <= mlen)
- + plen = mlen + 1;
- +
- + /* Allocate n of size plen, copy p to n */
- + n = snewn(mem_ctx, plen, BignumInt);
- + //if (!n)
- + //abort(); /* FIXME */
- + for (j = 0; j < plen; j++)
- + n[j] = 0;
- + for (j = 1; j <= (int)p[0]; j++)
- + n[plen - j] = p[j];
- +
- + /* Main computation */
- + internal_mod(n, plen, m, mlen, quotient, mshift);
- +
- + /* Fixup result in case the modulus was shifted */
- + if (mshift) {
- + for (i = plen - mlen - 1; i < plen - 1; i++)
- + n[i] = (n[i] << mshift) | (n[i + 1] >> (BIGNUM_INT_BITS - mshift));
- + n[plen - 1] = n[plen - 1] << mshift;
- + internal_mod(n, plen, m, mlen, quotient, 0);
- + for (i = plen - 1; i >= plen - mlen; i--)
- + n[i] = (n[i] >> mshift) | (n[i - 1] << (BIGNUM_INT_BITS - mshift));
- + }
- +
- + /* Copy result to buffer */
- + if (result) {
- + for (i = 1; i <= (int)result[0]; i++) {
- + int j = plen - i;
- + result[i] = j >= 0 ? n[j] : 0;
- + }
- + }
- +
- + /* Free temporary arrays */
- + for (i = 0; i < mlen; i++)
- + m[i] = 0;
- + sfree(mem_ctx, m);
- + for (i = 0; i < plen; i++)
- + n[i] = 0;
- + sfree(mem_ctx, n);
- +}
- +
- +/*
- + * Simple remainder.
- + */
- +Bignum bigmod(void *mem_ctx, Bignum a, Bignum b)
- +{
- + Bignum r = newbn(mem_ctx, b[0]);
- + bigdivmod(mem_ctx, a, b, r, NULL);
- + return r;
- +}
- +
- +/*
- + * Compute (base ^ exp) % mod.
- + */
- +Bignum dwc_modpow(void *mem_ctx, Bignum base_in, Bignum exp, Bignum mod)
- +{
- + BignumInt *a, *b, *n, *m;
- + int mshift;
- + int mlen, i, j;
- + Bignum base, result;
- +
- + /*
- + * The most significant word of mod needs to be non-zero. It
- + * should already be, but let's make sure.
- + */
- + //assert(mod[mod[0]] != 0);
- +
- + /*
- + * Make sure the base is smaller than the modulus, by reducing
- + * it modulo the modulus if not.
- + */
- + base = bigmod(mem_ctx, base_in, mod);
- +
- + /* Allocate m of size mlen, copy mod to m */
- + /* We use big endian internally */
- + mlen = mod[0];
- + m = snewn(mem_ctx, mlen, BignumInt);
- + //if (!m)
- + //abort(); /* FIXME */
- + for (j = 0; j < mlen; j++)
- + m[j] = mod[mod[0] - j];
- +
- + /* Shift m left to make msb bit set */
- + for (mshift = 0; mshift < BIGNUM_INT_BITS - 1; mshift++)
- + if ((m[0] << mshift) & BIGNUM_TOP_BIT)
- + break;
- + if (mshift) {
- + for (i = 0; i < mlen - 1; i++)
- + m[i] =
- + (m[i] << mshift) | (m[i + 1] >>
- + (BIGNUM_INT_BITS - mshift));
- + m[mlen - 1] = m[mlen - 1] << mshift;
- + }
- +
- + /* Allocate n of size mlen, copy base to n */
- + n = snewn(mem_ctx, mlen, BignumInt);
- + //if (!n)
- + //abort(); /* FIXME */
- + i = mlen - base[0];
- + for (j = 0; j < i; j++)
- + n[j] = 0;
- + for (j = 0; j < base[0]; j++)
- + n[i + j] = base[base[0] - j];
- +
- + /* Allocate a and b of size 2*mlen. Set a = 1 */
- + a = snewn(mem_ctx, 2 * mlen, BignumInt);
- + //if (!a)
- + //abort(); /* FIXME */
- + b = snewn(mem_ctx, 2 * mlen, BignumInt);
- + //if (!b)
- + //abort(); /* FIXME */
- + for (i = 0; i < 2 * mlen; i++)
- + a[i] = 0;
- + a[2 * mlen - 1] = 1;
- +
- + /* Skip leading zero bits of exp. */
- + i = 0;
- + j = BIGNUM_INT_BITS - 1;
- + while (i < exp[0] && (exp[exp[0] - i] & (1 << j)) == 0) {
- + j--;
- + if (j < 0) {
- + i++;
- + j = BIGNUM_INT_BITS - 1;
- + }
- + }
- +
- + /* Main computation */
- + while (i < exp[0]) {
- + while (j >= 0) {
- + internal_mul(a + mlen, a + mlen, b, mlen);
- + internal_mod(b, mlen * 2, m, mlen, NULL, 0);
- + if ((exp[exp[0] - i] & (1 << j)) != 0) {
- + internal_mul(b + mlen, n, a, mlen);
- + internal_mod(a, mlen * 2, m, mlen, NULL, 0);
- + } else {
- + BignumInt *t;
- + t = a;
- + a = b;
- + b = t;
- + }
- + j--;
- + }
- + i++;
- + j = BIGNUM_INT_BITS - 1;
- + }
- +
- + /* Fixup result in case the modulus was shifted */
- + if (mshift) {
- + for (i = mlen - 1; i < 2 * mlen - 1; i++)
- + a[i] =
- + (a[i] << mshift) | (a[i + 1] >>
- + (BIGNUM_INT_BITS - mshift));
- + a[2 * mlen - 1] = a[2 * mlen - 1] << mshift;
- + internal_mod(a, mlen * 2, m, mlen, NULL, 0);
- + for (i = 2 * mlen - 1; i >= mlen; i--)
- + a[i] =
- + (a[i] >> mshift) | (a[i - 1] <<
- + (BIGNUM_INT_BITS - mshift));
- + }
- +
- + /* Copy result to buffer */
- + result = newbn(mem_ctx, mod[0]);
- + for (i = 0; i < mlen; i++)
- + result[result[0] - i] = a[i + mlen];
- + while (result[0] > 1 && result[result[0]] == 0)
- + result[0]--;
- +
- + /* Free temporary arrays */
- + for (i = 0; i < 2 * mlen; i++)
- + a[i] = 0;
- + sfree(mem_ctx, a);
- + for (i = 0; i < 2 * mlen; i++)
- + b[i] = 0;
- + sfree(mem_ctx, b);
- + for (i = 0; i < mlen; i++)
- + m[i] = 0;
- + sfree(mem_ctx, m);
- + for (i = 0; i < mlen; i++)
- + n[i] = 0;
- + sfree(mem_ctx, n);
- +
- + freebn(mem_ctx, base);
- +
- + return result;
- +}
- +
- +
- +#ifdef UNITTEST
- +
- +static __u32 dh_p[] = {
- + 96,
- + 0xFFFFFFFF,
- + 0xFFFFFFFF,
- + 0xA93AD2CA,
- + 0x4B82D120,
- + 0xE0FD108E,
- + 0x43DB5BFC,
- + 0x74E5AB31,
- + 0x08E24FA0,
- + 0xBAD946E2,
- + 0x770988C0,
- + 0x7A615D6C,
- + 0xBBE11757,
- + 0x177B200C,
- + 0x521F2B18,
- + 0x3EC86A64,
- + 0xD8760273,
- + 0xD98A0864,
- + 0xF12FFA06,
- + 0x1AD2EE6B,
- + 0xCEE3D226,
- + 0x4A25619D,
- + 0x1E8C94E0,
- + 0xDB0933D7,
- + 0xABF5AE8C,
- + 0xA6E1E4C7,
- + 0xB3970F85,
- + 0x5D060C7D,
- + 0x8AEA7157,
- + 0x58DBEF0A,
- + 0xECFB8504,
- + 0xDF1CBA64,
- + 0xA85521AB,
- + 0x04507A33,
- + 0xAD33170D,
- + 0x8AAAC42D,
- + 0x15728E5A,
- + 0x98FA0510,
- + 0x15D22618,
- + 0xEA956AE5,
- + 0x3995497C,
- + 0x95581718,
- + 0xDE2BCBF6,
- + 0x6F4C52C9,
- + 0xB5C55DF0,
- + 0xEC07A28F,
- + 0x9B2783A2,
- + 0x180E8603,
- + 0xE39E772C,
- + 0x2E36CE3B,
- + 0x32905E46,
- + 0xCA18217C,
- + 0xF1746C08,
- + 0x4ABC9804,
- + 0x670C354E,
- + 0x7096966D,
- + 0x9ED52907,
- + 0x208552BB,
- + 0x1C62F356,
- + 0xDCA3AD96,
- + 0x83655D23,
- + 0xFD24CF5F,
- + 0x69163FA8,
- + 0x1C55D39A,
- + 0x98DA4836,
- + 0xA163BF05,
- + 0xC2007CB8,
- + 0xECE45B3D,
- + 0x49286651,
- + 0x7C4B1FE6,
- + 0xAE9F2411,
- + 0x5A899FA5,
- + 0xEE386BFB,
- + 0xF406B7ED,
- + 0x0BFF5CB6,
- + 0xA637ED6B,
- + 0xF44C42E9,
- + 0x625E7EC6,
- + 0xE485B576,
- + 0x6D51C245,
- + 0x4FE1356D,
- + 0xF25F1437,
- + 0x302B0A6D,
- + 0xCD3A431B,
- + 0xEF9519B3,
- + 0x8E3404DD,
- + 0x514A0879,
- + 0x3B139B22,
- + 0x020BBEA6,
- + 0x8A67CC74,
- + 0x29024E08,
- + 0x80DC1CD1,
- + 0xC4C6628B,
- + 0x2168C234,
- + 0xC90FDAA2,
- + 0xFFFFFFFF,
- + 0xFFFFFFFF,
- +};
- +
- +static __u32 dh_a[] = {
- + 8,
- + 0xdf367516,
- + 0x86459caa,
- + 0xe2d459a4,
- + 0xd910dae0,
- + 0x8a8b5e37,
- + 0x67ab31c6,
- + 0xf0b55ea9,
- + 0x440051d6,
- +};
- +
- +static __u32 dh_b[] = {
- + 8,
- + 0xded92656,
- + 0xe07a048a,
- + 0x6fa452cd,
- + 0x2df89d30,
- + 0xc75f1b0f,
- + 0x8ce3578f,
- + 0x7980a324,
- + 0x5daec786,
- +};
- +
- +static __u32 dh_g[] = {
- + 1,
- + 2,
- +};
- +
- +int main(void)
- +{
- + int i;
- + __u32 *k;
- + k = dwc_modpow(NULL, dh_g, dh_a, dh_p);
- +
- + printf("\n\n");
- + for (i=0; i<k[0]; i++) {
- + __u32 word32 = k[k[0] - i];
- + __u16 l = word32 & 0xffff;
- + __u16 m = (word32 & 0xffff0000) >> 16;
- + printf("%04x %04x ", m, l);
- + if (!((i + 1)%13)) printf("\n");
- + }
- + printf("\n\n");
- +
- + if ((k[0] == 0x60) && (k[1] == 0x28e490e5) && (k[0x60] == 0x5a0d3d4e)) {
- + printf("PASS\n\n");
- + }
- + else {
- + printf("FAIL\n\n");
- + }
- +
- +}
- +
- +#endif /* UNITTEST */
- +
- +#endif /* CONFIG_MACH_IPMATE */
- +
- +#endif /*DWC_CRYPTOLIB */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_common_port/dwc_modpow.h
- @@ -0,0 +1,34 @@
- +/*
- + * dwc_modpow.h
- + * See dwc_modpow.c for license and changes
- + */
- +#ifndef _DWC_MODPOW_H
- +#define _DWC_MODPOW_H
- +
- +#ifdef __cplusplus
- +extern "C" {
- +#endif
- +
- +#include "dwc_os.h"
- +
- +/** @file
- + *
- + * This file defines the module exponentiation function which is only used
- + * internally by the DWC UWB modules for calculation of PKs during numeric
- + * association. The routine is taken from the PUTTY, an open source terminal
- + * emulator. The PUTTY License is preserved in the dwc_modpow.c file.
- + *
- + */
- +
- +typedef uint32_t BignumInt;
- +typedef uint64_t BignumDblInt;
- +typedef BignumInt *Bignum;
- +
- +/* Compute modular exponentiaion */
- +extern Bignum dwc_modpow(void *mem_ctx, Bignum base_in, Bignum exp, Bignum mod);
- +
- +#ifdef __cplusplus
- +}
- +#endif
- +
- +#endif /* _LINUX_BIGNUM_H */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_common_port/dwc_notifier.c
- @@ -0,0 +1,319 @@
- +#ifdef DWC_NOTIFYLIB
- +
- +#include "dwc_notifier.h"
- +#include "dwc_list.h"
- +
- +typedef struct dwc_observer {
- + void *observer;
- + dwc_notifier_callback_t callback;
- + void *data;
- + char *notification;
- + DWC_CIRCLEQ_ENTRY(dwc_observer) list_entry;
- +} observer_t;
- +
- +DWC_CIRCLEQ_HEAD(observer_queue, dwc_observer);
- +
- +typedef struct dwc_notifier {
- + void *mem_ctx;
- + void *object;
- + struct observer_queue observers;
- + DWC_CIRCLEQ_ENTRY(dwc_notifier) list_entry;
- +} notifier_t;
- +
- +DWC_CIRCLEQ_HEAD(notifier_queue, dwc_notifier);
- +
- +typedef struct manager {
- + void *mem_ctx;
- + void *wkq_ctx;
- + dwc_workq_t *wq;
- +// dwc_mutex_t *mutex;
- + struct notifier_queue notifiers;
- +} manager_t;
- +
- +static manager_t *manager = NULL;
- +
- +static int create_manager(void *mem_ctx, void *wkq_ctx)
- +{
- + manager = dwc_alloc(mem_ctx, sizeof(manager_t));
- + if (!manager) {
- + return -DWC_E_NO_MEMORY;
- + }
- +
- + DWC_CIRCLEQ_INIT(&manager->notifiers);
- +
- + manager->wq = dwc_workq_alloc(wkq_ctx, "DWC Notification WorkQ");
- + if (!manager->wq) {
- + return -DWC_E_NO_MEMORY;
- + }
- +
- + return 0;
- +}
- +
- +static void free_manager(void)
- +{
- + dwc_workq_free(manager->wq);
- +
- + /* All notifiers must have unregistered themselves before this module
- + * can be removed. Hitting this assertion indicates a programmer
- + * error. */
- + DWC_ASSERT(DWC_CIRCLEQ_EMPTY(&manager->notifiers),
- + "Notification manager being freed before all notifiers have been removed");
- + dwc_free(manager->mem_ctx, manager);
- +}
- +
- +#ifdef DEBUG
- +static void dump_manager(void)
- +{
- + notifier_t *n;
- + observer_t *o;
- +
- + DWC_ASSERT(manager, "Notification manager not found");
- +
- + DWC_DEBUG("List of all notifiers and observers:\n");
- + DWC_CIRCLEQ_FOREACH(n, &manager->notifiers, list_entry) {
- + DWC_DEBUG("Notifier %p has observers:\n", n->object);
- + DWC_CIRCLEQ_FOREACH(o, &n->observers, list_entry) {
- + DWC_DEBUG(" %p watching %s\n", o->observer, o->notification);
- + }
- + }
- +}
- +#else
- +#define dump_manager(...)
- +#endif
- +
- +static observer_t *alloc_observer(void *mem_ctx, void *observer, char *notification,
- + dwc_notifier_callback_t callback, void *data)
- +{
- + observer_t *new_observer = dwc_alloc(mem_ctx, sizeof(observer_t));
- +
- + if (!new_observer) {
- + return NULL;
- + }
- +
- + DWC_CIRCLEQ_INIT_ENTRY(new_observer, list_entry);
- + new_observer->observer = observer;
- + new_observer->notification = notification;
- + new_observer->callback = callback;
- + new_observer->data = data;
- + return new_observer;
- +}
- +
- +static void free_observer(void *mem_ctx, observer_t *observer)
- +{
- + dwc_free(mem_ctx, observer);
- +}
- +
- +static notifier_t *alloc_notifier(void *mem_ctx, void *object)
- +{
- + notifier_t *notifier;
- +
- + if (!object) {
- + return NULL;
- + }
- +
- + notifier = dwc_alloc(mem_ctx, sizeof(notifier_t));
- + if (!notifier) {
- + return NULL;
- + }
- +
- + DWC_CIRCLEQ_INIT(¬ifier->observers);
- + DWC_CIRCLEQ_INIT_ENTRY(notifier, list_entry);
- +
- + notifier->mem_ctx = mem_ctx;
- + notifier->object = object;
- + return notifier;
- +}
- +
- +static void free_notifier(notifier_t *notifier)
- +{
- + observer_t *observer;
- +
- + DWC_CIRCLEQ_FOREACH(observer, ¬ifier->observers, list_entry) {
- + free_observer(notifier->mem_ctx, observer);
- + }
- +
- + dwc_free(notifier->mem_ctx, notifier);
- +}
- +
- +static notifier_t *find_notifier(void *object)
- +{
- + notifier_t *notifier;
- +
- + DWC_ASSERT(manager, "Notification manager not found");
- +
- + if (!object) {
- + return NULL;
- + }
- +
- + DWC_CIRCLEQ_FOREACH(notifier, &manager->notifiers, list_entry) {
- + if (notifier->object == object) {
- + return notifier;
- + }
- + }
- +
- + return NULL;
- +}
- +
- +int dwc_alloc_notification_manager(void *mem_ctx, void *wkq_ctx)
- +{
- + return create_manager(mem_ctx, wkq_ctx);
- +}
- +
- +void dwc_free_notification_manager(void)
- +{
- + free_manager();
- +}
- +
- +dwc_notifier_t *dwc_register_notifier(void *mem_ctx, void *object)
- +{
- + notifier_t *notifier;
- +
- + DWC_ASSERT(manager, "Notification manager not found");
- +
- + notifier = find_notifier(object);
- + if (notifier) {
- + DWC_ERROR("Notifier %p is already registered\n", object);
- + return NULL;
- + }
- +
- + notifier = alloc_notifier(mem_ctx, object);
- + if (!notifier) {
- + return NULL;
- + }
- +
- + DWC_CIRCLEQ_INSERT_TAIL(&manager->notifiers, notifier, list_entry);
- +
- + DWC_INFO("Notifier %p registered", object);
- + dump_manager();
- +
- + return notifier;
- +}
- +
- +void dwc_unregister_notifier(dwc_notifier_t *notifier)
- +{
- + DWC_ASSERT(manager, "Notification manager not found");
- +
- + if (!DWC_CIRCLEQ_EMPTY(¬ifier->observers)) {
- + observer_t *o;
- +
- + DWC_ERROR("Notifier %p has active observers when removing\n", notifier->object);
- + DWC_CIRCLEQ_FOREACH(o, ¬ifier->observers, list_entry) {
- + DWC_DEBUGC(" %p watching %s\n", o->observer, o->notification);
- + }
- +
- + DWC_ASSERT(DWC_CIRCLEQ_EMPTY(¬ifier->observers),
- + "Notifier %p has active observers when removing", notifier);
- + }
- +
- + DWC_CIRCLEQ_REMOVE_INIT(&manager->notifiers, notifier, list_entry);
- + free_notifier(notifier);
- +
- + DWC_INFO("Notifier unregistered");
- + dump_manager();
- +}
- +
- +/* Add an observer to observe the notifier for a particular state, event, or notification. */
- +int dwc_add_observer(void *observer, void *object, char *notification,
- + dwc_notifier_callback_t callback, void *data)
- +{
- + notifier_t *notifier = find_notifier(object);
- + observer_t *new_observer;
- +
- + if (!notifier) {
- + DWC_ERROR("Notifier %p is not found when adding observer\n", object);
- + return -DWC_E_INVALID;
- + }
- +
- + new_observer = alloc_observer(notifier->mem_ctx, observer, notification, callback, data);
- + if (!new_observer) {
- + return -DWC_E_NO_MEMORY;
- + }
- +
- + DWC_CIRCLEQ_INSERT_TAIL(¬ifier->observers, new_observer, list_entry);
- +
- + DWC_INFO("Added observer %p to notifier %p observing notification %s, callback=%p, data=%p",
- + observer, object, notification, callback, data);
- +
- + dump_manager();
- + return 0;
- +}
- +
- +int dwc_remove_observer(void *observer)
- +{
- + notifier_t *n;
- +
- + DWC_ASSERT(manager, "Notification manager not found");
- +
- + DWC_CIRCLEQ_FOREACH(n, &manager->notifiers, list_entry) {
- + observer_t *o;
- + observer_t *o2;
- +
- + DWC_CIRCLEQ_FOREACH_SAFE(o, o2, &n->observers, list_entry) {
- + if (o->observer == observer) {
- + DWC_CIRCLEQ_REMOVE_INIT(&n->observers, o, list_entry);
- + DWC_INFO("Removing observer %p from notifier %p watching notification %s:",
- + o->observer, n->object, o->notification);
- + free_observer(n->mem_ctx, o);
- + }
- + }
- + }
- +
- + dump_manager();
- + return 0;
- +}
- +
- +typedef struct callback_data {
- + void *mem_ctx;
- + dwc_notifier_callback_t cb;
- + void *observer;
- + void *data;
- + void *object;
- + char *notification;
- + void *notification_data;
- +} cb_data_t;
- +
- +static void cb_task(void *data)
- +{
- + cb_data_t *cb = (cb_data_t *)data;
- +
- + cb->cb(cb->object, cb->notification, cb->observer, cb->notification_data, cb->data);
- + dwc_free(cb->mem_ctx, cb);
- +}
- +
- +void dwc_notify(dwc_notifier_t *notifier, char *notification, void *notification_data)
- +{
- + observer_t *o;
- +
- + DWC_ASSERT(manager, "Notification manager not found");
- +
- + DWC_CIRCLEQ_FOREACH(o, ¬ifier->observers, list_entry) {
- + int len = DWC_STRLEN(notification);
- +
- + if (DWC_STRLEN(o->notification) != len) {
- + continue;
- + }
- +
- + if (DWC_STRNCMP(o->notification, notification, len) == 0) {
- + cb_data_t *cb_data = dwc_alloc(notifier->mem_ctx, sizeof(cb_data_t));
- +
- + if (!cb_data) {
- + DWC_ERROR("Failed to allocate callback data\n");
- + return;
- + }
- +
- + cb_data->mem_ctx = notifier->mem_ctx;
- + cb_data->cb = o->callback;
- + cb_data->observer = o->observer;
- + cb_data->data = o->data;
- + cb_data->object = notifier->object;
- + cb_data->notification = notification;
- + cb_data->notification_data = notification_data;
- + DWC_DEBUGC("Observer found %p for notification %s\n", o->observer, notification);
- + DWC_WORKQ_SCHEDULE(manager->wq, cb_task, cb_data,
- + "Notify callback from %p for Notification %s, to observer %p",
- + cb_data->object, notification, cb_data->observer);
- + }
- + }
- +}
- +
- +#endif /* DWC_NOTIFYLIB */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_common_port/dwc_notifier.h
- @@ -0,0 +1,122 @@
- +
- +#ifndef __DWC_NOTIFIER_H__
- +#define __DWC_NOTIFIER_H__
- +
- +#ifdef __cplusplus
- +extern "C" {
- +#endif
- +
- +#include "dwc_os.h"
- +
- +/** @file
- + *
- + * A simple implementation of the Observer pattern. Any "module" can
- + * register as an observer or notifier. The notion of "module" is abstract and
- + * can mean anything used to identify either an observer or notifier. Usually
- + * it will be a pointer to a data structure which contains some state, ie an
- + * object.
- + *
- + * Before any notifiers can be added, the global notification manager must be
- + * brought up with dwc_alloc_notification_manager().
- + * dwc_free_notification_manager() will bring it down and free all resources.
- + * These would typically be called upon module load and unload. The
- + * notification manager is a single global instance that handles all registered
- + * observable modules and observers so this should be done only once.
- + *
- + * A module can be observable by using Notifications to publicize some general
- + * information about it's state or operation. It does not care who listens, or
- + * even if anyone listens, or what they do with the information. The observable
- + * modules do not need to know any information about it's observers or their
- + * interface, or their state or data.
- + *
- + * Any module can register to emit Notifications. It should publish a list of
- + * notifications that it can emit and their behavior, such as when they will get
- + * triggered, and what information will be provided to the observer. Then it
- + * should register itself as an observable module. See dwc_register_notifier().
- + *
- + * Any module can observe any observable, registered module, provided it has a
- + * handle to the other module and knows what notifications to observe. See
- + * dwc_add_observer().
- + *
- + * A function of type dwc_notifier_callback_t is called whenever a notification
- + * is triggered with one or more observers observing it. This function is
- + * called in it's own process so it may sleep or block if needed. It is
- + * guaranteed to be called sometime after the notification has occurred and will
- + * be called once per each time the notification is triggered. It will NOT be
- + * called in the same process context used to trigger the notification.
- + *
- + * @section Limitiations
- + *
- + * Keep in mind that Notifications that can be triggered in rapid sucession may
- + * schedule too many processes too handle. Be aware of this limitation when
- + * designing to use notifications, and only add notifications for appropriate
- + * observable information.
- + *
- + * Also Notification callbacks are not synchronous. If you need to synchronize
- + * the behavior between module/observer you must use other means. And perhaps
- + * that will mean Notifications are not the proper solution.
- + */
- +
- +struct dwc_notifier;
- +typedef struct dwc_notifier dwc_notifier_t;
- +
- +/** The callback function must be of this type.
- + *
- + * @param object This is the object that is being observed.
- + * @param notification This is the notification that was triggered.
- + * @param observer This is the observer
- + * @param notification_data This is notification-specific data that the notifier
- + * has included in this notification. The value of this should be published in
- + * the documentation of the observable module with the notifications.
- + * @param user_data This is any custom data that the observer provided when
- + * adding itself as an observer to the notification. */
- +typedef void (*dwc_notifier_callback_t)(void *object, char *notification, void *observer,
- + void *notification_data, void *user_data);
- +
- +/** Brings up the notification manager. */
- +extern int dwc_alloc_notification_manager(void *mem_ctx, void *wkq_ctx);
- +/** Brings down the notification manager. */
- +extern void dwc_free_notification_manager(void);
- +
- +/** This function registers an observable module. A dwc_notifier_t object is
- + * returned to the observable module. This is an opaque object that is used by
- + * the observable module to trigger notifications. This object should only be
- + * accessible to functions that are authorized to trigger notifications for this
- + * module. Observers do not need this object. */
- +extern dwc_notifier_t *dwc_register_notifier(void *mem_ctx, void *object);
- +
- +/** This function unregisters an observable module. All observers have to be
- + * removed prior to unregistration. */
- +extern void dwc_unregister_notifier(dwc_notifier_t *notifier);
- +
- +/** Add a module as an observer to the observable module. The observable module
- + * needs to have previously registered with the notification manager.
- + *
- + * @param observer The observer module
- + * @param object The module to observe
- + * @param notification The notification to observe
- + * @param callback The callback function to call
- + * @param user_data Any additional user data to pass into the callback function */
- +extern int dwc_add_observer(void *observer, void *object, char *notification,
- + dwc_notifier_callback_t callback, void *user_data);
- +
- +/** Removes the specified observer from all notifications that it is currently
- + * observing. */
- +extern int dwc_remove_observer(void *observer);
- +
- +/** This function triggers a Notification. It should be called by the
- + * observable module, or any module or library which the observable module
- + * allows to trigger notification on it's behalf. Such as the dwc_cc_t.
- + *
- + * dwc_notify is a non-blocking function. Callbacks are scheduled called in
- + * their own process context for each trigger. Callbacks can be blocking.
- + * dwc_notify can be called from interrupt context if needed.
- + *
- + */
- +void dwc_notify(dwc_notifier_t *notifier, char *notification, void *notification_data);
- +
- +#ifdef __cplusplus
- +}
- +#endif
- +
- +#endif /* __DWC_NOTIFIER_H__ */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_common_port/dwc_os.h
- @@ -0,0 +1,1275 @@
- +/* =========================================================================
- + * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_os.h $
- + * $Revision: #14 $
- + * $Date: 2010/11/04 $
- + * $Change: 1621695 $
- + *
- + * Synopsys Portability Library Software and documentation
- + * (hereinafter, "Software") is an Unsupported proprietary work of
- + * Synopsys, Inc. unless otherwise expressly agreed to in writing
- + * between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product
- + * under any End User Software License Agreement or Agreement for
- + * Licensed Product with Synopsys or any supplement thereto. You are
- + * permitted to use and redistribute this Software in source and binary
- + * forms, with or without modification, provided that redistributions
- + * of source code must retain this notice. You may not view, use,
- + * disclose, copy or distribute this file or any information contained
- + * herein except pursuant to this license grant from Synopsys. If you
- + * do not agree with this notice, including the disclaimer below, then
- + * you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
- + * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- + * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL
- + * SYNOPSYS 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.
- + * ========================================================================= */
- +#ifndef _DWC_OS_H_
- +#define _DWC_OS_H_
- +
- +#ifdef __cplusplus
- +extern "C" {
- +#endif
- +
- +/** @file
- + *
- + * DWC portability library, low level os-wrapper functions
- + *
- + */
- +
- +/* These basic types need to be defined by some OS header file or custom header
- + * file for your specific target architecture.
- + *
- + * uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t
- + *
- + * Any custom or alternate header file must be added and enabled here.
- + */
- +
- +#ifdef DWC_LINUX
- +# include <linux/types.h>
- +# ifdef CONFIG_DEBUG_MUTEXES
- +# include <linux/mutex.h>
- +# endif
- +# include <linux/spinlock.h>
- +# include <linux/errno.h>
- +#endif
- +
- +#if defined(DWC_FREEBSD) || defined(DWC_NETBSD)
- +# include <os_dep.h>
- +#endif
- +
- +
- +/** @name Primitive Types and Values */
- +
- +/** We define a boolean type for consistency. Can be either YES or NO */
- +typedef uint8_t dwc_bool_t;
- +#define YES 1
- +#define NO 0
- +
- +#ifdef DWC_LINUX
- +
- +/** @name Error Codes */
- +#define DWC_E_INVALID EINVAL
- +#define DWC_E_NO_MEMORY ENOMEM
- +#define DWC_E_NO_DEVICE ENODEV
- +#define DWC_E_NOT_SUPPORTED EOPNOTSUPP
- +#define DWC_E_TIMEOUT ETIMEDOUT
- +#define DWC_E_BUSY EBUSY
- +#define DWC_E_AGAIN EAGAIN
- +#define DWC_E_RESTART ERESTART
- +#define DWC_E_ABORT ECONNABORTED
- +#define DWC_E_SHUTDOWN ESHUTDOWN
- +#define DWC_E_NO_DATA ENODATA
- +#define DWC_E_DISCONNECT ECONNRESET
- +#define DWC_E_UNKNOWN EINVAL
- +#define DWC_E_NO_STREAM_RES ENOSR
- +#define DWC_E_COMMUNICATION ECOMM
- +#define DWC_E_OVERFLOW EOVERFLOW
- +#define DWC_E_PROTOCOL EPROTO
- +#define DWC_E_IN_PROGRESS EINPROGRESS
- +#define DWC_E_PIPE EPIPE
- +#define DWC_E_IO EIO
- +#define DWC_E_NO_SPACE ENOSPC
- +
- +#else
- +
- +/** @name Error Codes */
- +#define DWC_E_INVALID 1001
- +#define DWC_E_NO_MEMORY 1002
- +#define DWC_E_NO_DEVICE 1003
- +#define DWC_E_NOT_SUPPORTED 1004
- +#define DWC_E_TIMEOUT 1005
- +#define DWC_E_BUSY 1006
- +#define DWC_E_AGAIN 1007
- +#define DWC_E_RESTART 1008
- +#define DWC_E_ABORT 1009
- +#define DWC_E_SHUTDOWN 1010
- +#define DWC_E_NO_DATA 1011
- +#define DWC_E_DISCONNECT 2000
- +#define DWC_E_UNKNOWN 3000
- +#define DWC_E_NO_STREAM_RES 4001
- +#define DWC_E_COMMUNICATION 4002
- +#define DWC_E_OVERFLOW 4003
- +#define DWC_E_PROTOCOL 4004
- +#define DWC_E_IN_PROGRESS 4005
- +#define DWC_E_PIPE 4006
- +#define DWC_E_IO 4007
- +#define DWC_E_NO_SPACE 4008
- +
- +#endif
- +
- +
- +/** @name Tracing/Logging Functions
- + *
- + * These function provide the capability to add tracing, debugging, and error
- + * messages, as well exceptions as assertions. The WUDEV uses these
- + * extensively. These could be logged to the main console, the serial port, an
- + * internal buffer, etc. These functions could also be no-op if they are too
- + * expensive on your system. By default undefining the DEBUG macro already
- + * no-ops some of these functions. */
- +
- +/** Returns non-zero if in interrupt context. */
- +extern dwc_bool_t DWC_IN_IRQ(void);
- +#define dwc_in_irq DWC_IN_IRQ
- +
- +/** Returns "IRQ" if DWC_IN_IRQ is true. */
- +static inline char *dwc_irq(void) {
- + return DWC_IN_IRQ() ? "IRQ" : "";
- +}
- +
- +/** Returns non-zero if in bottom-half context. */
- +extern dwc_bool_t DWC_IN_BH(void);
- +#define dwc_in_bh DWC_IN_BH
- +
- +/** Returns "BH" if DWC_IN_BH is true. */
- +static inline char *dwc_bh(void) {
- + return DWC_IN_BH() ? "BH" : "";
- +}
- +
- +/**
- + * A vprintf() clone. Just call vprintf if you've got it.
- + */
- +extern void DWC_VPRINTF(char *format, va_list args);
- +#define dwc_vprintf DWC_VPRINTF
- +
- +/**
- + * A vsnprintf() clone. Just call vprintf if you've got it.
- + */
- +extern int DWC_VSNPRINTF(char *str, int size, char *format, va_list args);
- +#define dwc_vsnprintf DWC_VSNPRINTF
- +
- +/**
- + * printf() clone. Just call printf if you've go it.
- + */
- +extern void DWC_PRINTF(char *format, ...)
- +/* This provides compiler level static checking of the parameters if you're
- + * using GCC. */
- +#ifdef __GNUC__
- + __attribute__ ((format(printf, 1, 2)));
- +#else
- + ;
- +#endif
- +#define dwc_printf DWC_PRINTF
- +
- +/**
- + * sprintf() clone. Just call sprintf if you've got it.
- + */
- +extern int DWC_SPRINTF(char *string, char *format, ...)
- +#ifdef __GNUC__
- + __attribute__ ((format(printf, 2, 3)));
- +#else
- + ;
- +#endif
- +#define dwc_sprintf DWC_SPRINTF
- +
- +/**
- + * snprintf() clone. Just call snprintf if you've got it.
- + */
- +extern int DWC_SNPRINTF(char *string, int size, char *format, ...)
- +#ifdef __GNUC__
- + __attribute__ ((format(printf, 3, 4)));
- +#else
- + ;
- +#endif
- +#define dwc_snprintf DWC_SNPRINTF
- +
- +/**
- + * Prints a WARNING message. On systems that don't differentiate between
- + * warnings and regular log messages, just print it. Indicates that something
- + * may be wrong with the driver. Works like printf().
- + *
- + * Use the DWC_WARN macro to call this function.
- + */
- +extern void __DWC_WARN(char *format, ...)
- +#ifdef __GNUC__
- + __attribute__ ((format(printf, 1, 2)));
- +#else
- + ;
- +#endif
- +
- +/**
- + * Prints an error message. On systems that don't differentiate between errors
- + * and regular log messages, just print it. Indicates that something went wrong
- + * with the driver. Works like printf().
- + *
- + * Use the DWC_ERROR macro to call this function.
- + */
- +extern void __DWC_ERROR(char *format, ...)
- +#ifdef __GNUC__
- + __attribute__ ((format(printf, 1, 2)));
- +#else
- + ;
- +#endif
- +
- +/**
- + * Prints an exception error message and takes some user-defined action such as
- + * print out a backtrace or trigger a breakpoint. Indicates that something went
- + * abnormally wrong with the driver such as programmer error, or other
- + * exceptional condition. It should not be ignored so even on systems without
- + * printing capability, some action should be taken to notify the developer of
- + * it. Works like printf().
- + */
- +extern void DWC_EXCEPTION(char *format, ...)
- +#ifdef __GNUC__
- + __attribute__ ((format(printf, 1, 2)));
- +#else
- + ;
- +#endif
- +#define dwc_exception DWC_EXCEPTION
- +
- +#ifndef DWC_OTG_DEBUG_LEV
- +#define DWC_OTG_DEBUG_LEV 0
- +#endif
- +
- +#ifdef DEBUG
- +/**
- + * Prints out a debug message. Used for logging/trace messages.
- + *
- + * Use the DWC_DEBUG macro to call this function
- + */
- +extern void __DWC_DEBUG(char *format, ...)
- +#ifdef __GNUC__
- + __attribute__ ((format(printf, 1, 2)));
- +#else
- + ;
- +#endif
- +#else
- +#define __DWC_DEBUG printk
- +#endif
- +
- +/**
- + * Prints out a Debug message.
- + */
- +#define DWC_DEBUG(_format, _args...) __DWC_DEBUG("DEBUG:%s:%s: " _format "\n", \
- + __func__, dwc_irq(), ## _args)
- +#define dwc_debug DWC_DEBUG
- +/**
- + * Prints out a Debug message if enabled at compile time.
- + */
- +#if DWC_OTG_DEBUG_LEV > 0
- +#define DWC_DEBUGC(_format, _args...) DWC_DEBUG(_format, ##_args )
- +#else
- +#define DWC_DEBUGC(_format, _args...)
- +#endif
- +#define dwc_debugc DWC_DEBUGC
- +/**
- + * Prints out an informative message.
- + */
- +#define DWC_INFO(_format, _args...) DWC_PRINTF("INFO:%s: " _format "\n", \
- + dwc_irq(), ## _args)
- +#define dwc_info DWC_INFO
- +/**
- + * Prints out an informative message if enabled at compile time.
- + */
- +#if DWC_OTG_DEBUG_LEV > 1
- +#define DWC_INFOC(_format, _args...) DWC_INFO(_format, ##_args )
- +#else
- +#define DWC_INFOC(_format, _args...)
- +#endif
- +#define dwc_infoc DWC_INFOC
- +/**
- + * Prints out a warning message.
- + */
- +#define DWC_WARN(_format, _args...) __DWC_WARN("WARN:%s:%s:%d: " _format "\n", \
- + dwc_irq(), __func__, __LINE__, ## _args)
- +#define dwc_warn DWC_WARN
- +/**
- + * Prints out an error message.
- + */
- +#define DWC_ERROR(_format, _args...) __DWC_ERROR("ERROR:%s:%s:%d: " _format "\n", \
- + dwc_irq(), __func__, __LINE__, ## _args)
- +#define dwc_error DWC_ERROR
- +
- +#define DWC_PROTO_ERROR(_format, _args...) __DWC_WARN("ERROR:%s:%s:%d: " _format "\n", \
- + dwc_irq(), __func__, __LINE__, ## _args)
- +#define dwc_proto_error DWC_PROTO_ERROR
- +
- +#ifdef DEBUG
- +/** Prints out a exception error message if the _expr expression fails. Disabled
- + * if DEBUG is not enabled. */
- +#define DWC_ASSERT(_expr, _format, _args...) do { \
- + if (!(_expr)) { DWC_EXCEPTION("%s:%s:%d: " _format "\n", dwc_irq(), \
- + __FILE__, __LINE__, ## _args); } \
- + } while (0)
- +#else
- +#define DWC_ASSERT(_x...)
- +#endif
- +#define dwc_assert DWC_ASSERT
- +
- +
- +/** @name Byte Ordering
- + * The following functions are for conversions between processor's byte ordering
- + * and specific ordering you want.
- + */
- +
- +/** Converts 32 bit data in CPU byte ordering to little endian. */
- +extern uint32_t DWC_CPU_TO_LE32(uint32_t *p);
- +#define dwc_cpu_to_le32 DWC_CPU_TO_LE32
- +
- +/** Converts 32 bit data in CPU byte orderint to big endian. */
- +extern uint32_t DWC_CPU_TO_BE32(uint32_t *p);
- +#define dwc_cpu_to_be32 DWC_CPU_TO_BE32
- +
- +/** Converts 32 bit little endian data to CPU byte ordering. */
- +extern uint32_t DWC_LE32_TO_CPU(uint32_t *p);
- +#define dwc_le32_to_cpu DWC_LE32_TO_CPU
- +
- +/** Converts 32 bit big endian data to CPU byte ordering. */
- +extern uint32_t DWC_BE32_TO_CPU(uint32_t *p);
- +#define dwc_be32_to_cpu DWC_BE32_TO_CPU
- +
- +/** Converts 16 bit data in CPU byte ordering to little endian. */
- +extern uint16_t DWC_CPU_TO_LE16(uint16_t *p);
- +#define dwc_cpu_to_le16 DWC_CPU_TO_LE16
- +
- +/** Converts 16 bit data in CPU byte orderint to big endian. */
- +extern uint16_t DWC_CPU_TO_BE16(uint16_t *p);
- +#define dwc_cpu_to_be16 DWC_CPU_TO_BE16
- +
- +/** Converts 16 bit little endian data to CPU byte ordering. */
- +extern uint16_t DWC_LE16_TO_CPU(uint16_t *p);
- +#define dwc_le16_to_cpu DWC_LE16_TO_CPU
- +
- +/** Converts 16 bit bi endian data to CPU byte ordering. */
- +extern uint16_t DWC_BE16_TO_CPU(uint16_t *p);
- +#define dwc_be16_to_cpu DWC_BE16_TO_CPU
- +
- +
- +/** @name Register Read/Write
- + *
- + * The following six functions should be implemented to read/write registers of
- + * 32-bit and 64-bit sizes. All modules use this to read/write register values.
- + * The reg value is a pointer to the register calculated from the void *base
- + * variable passed into the driver when it is started. */
- +
- +#ifdef DWC_LINUX
- +/* Linux doesn't need any extra parameters for register read/write, so we
- + * just throw away the IO context parameter.
- + */
- +/** Reads the content of a 32-bit register. */
- +extern uint32_t DWC_READ_REG32(uint32_t volatile *reg);
- +#define dwc_read_reg32(_ctx_,_reg_) DWC_READ_REG32(_reg_)
- +
- +/** Reads the content of a 64-bit register. */
- +extern uint64_t DWC_READ_REG64(uint64_t volatile *reg);
- +#define dwc_read_reg64(_ctx_,_reg_) DWC_READ_REG64(_reg_)
- +
- +/** Writes to a 32-bit register. */
- +extern void DWC_WRITE_REG32(uint32_t volatile *reg, uint32_t value);
- +#define dwc_write_reg32(_ctx_,_reg_,_val_) DWC_WRITE_REG32(_reg_, _val_)
- +
- +/** Writes to a 64-bit register. */
- +extern void DWC_WRITE_REG64(uint64_t volatile *reg, uint64_t value);
- +#define dwc_write_reg64(_ctx_,_reg_,_val_) DWC_WRITE_REG64(_reg_, _val_)
- +
- +/**
- + * Modify bit values in a register. Using the
- + * algorithm: (reg_contents & ~clear_mask) | set_mask.
- + */
- +extern void DWC_MODIFY_REG32(uint32_t volatile *reg, uint32_t clear_mask, uint32_t set_mask);
- +#define dwc_modify_reg32(_ctx_,_reg_,_cmsk_,_smsk_) DWC_MODIFY_REG32(_reg_,_cmsk_,_smsk_)
- +extern void DWC_MODIFY_REG64(uint64_t volatile *reg, uint64_t clear_mask, uint64_t set_mask);
- +#define dwc_modify_reg64(_ctx_,_reg_,_cmsk_,_smsk_) DWC_MODIFY_REG64(_reg_,_cmsk_,_smsk_)
- +
- +#endif /* DWC_LINUX */
- +
- +#if defined(DWC_FREEBSD) || defined(DWC_NETBSD)
- +typedef struct dwc_ioctx {
- + struct device *dev;
- + bus_space_tag_t iot;
- + bus_space_handle_t ioh;
- +} dwc_ioctx_t;
- +
- +/** BSD needs two extra parameters for register read/write, so we pass
- + * them in using the IO context parameter.
- + */
- +/** Reads the content of a 32-bit register. */
- +extern uint32_t DWC_READ_REG32(void *io_ctx, uint32_t volatile *reg);
- +#define dwc_read_reg32 DWC_READ_REG32
- +
- +/** Reads the content of a 64-bit register. */
- +extern uint64_t DWC_READ_REG64(void *io_ctx, uint64_t volatile *reg);
- +#define dwc_read_reg64 DWC_READ_REG64
- +
- +/** Writes to a 32-bit register. */
- +extern void DWC_WRITE_REG32(void *io_ctx, uint32_t volatile *reg, uint32_t value);
- +#define dwc_write_reg32 DWC_WRITE_REG32
- +
- +/** Writes to a 64-bit register. */
- +extern void DWC_WRITE_REG64(void *io_ctx, uint64_t volatile *reg, uint64_t value);
- +#define dwc_write_reg64 DWC_WRITE_REG64
- +
- +/**
- + * Modify bit values in a register. Using the
- + * algorithm: (reg_contents & ~clear_mask) | set_mask.
- + */
- +extern void DWC_MODIFY_REG32(void *io_ctx, uint32_t volatile *reg, uint32_t clear_mask, uint32_t set_mask);
- +#define dwc_modify_reg32 DWC_MODIFY_REG32
- +extern void DWC_MODIFY_REG64(void *io_ctx, uint64_t volatile *reg, uint64_t clear_mask, uint64_t set_mask);
- +#define dwc_modify_reg64 DWC_MODIFY_REG64
- +
- +#endif /* DWC_FREEBSD || DWC_NETBSD */
- +
- +/** @cond */
- +
- +/** @name Some convenience MACROS used internally. Define DWC_DEBUG_REGS to log the
- + * register writes. */
- +
- +#ifdef DWC_LINUX
- +
- +# ifdef DWC_DEBUG_REGS
- +
- +#define dwc_define_read_write_reg_n(_reg,_container_type) \
- +static inline uint32_t dwc_read_##_reg##_n(_container_type *container, int num) { \
- + return DWC_READ_REG32(&container->regs->_reg[num]); \
- +} \
- +static inline void dwc_write_##_reg##_n(_container_type *container, int num, uint32_t data) { \
- + DWC_DEBUG("WRITING %8s[%d]: %p: %08x", #_reg, num, \
- + &(((uint32_t*)container->regs->_reg)[num]), data); \
- + DWC_WRITE_REG32(&(((uint32_t*)container->regs->_reg)[num]), data); \
- +}
- +
- +#define dwc_define_read_write_reg(_reg,_container_type) \
- +static inline uint32_t dwc_read_##_reg(_container_type *container) { \
- + return DWC_READ_REG32(&container->regs->_reg); \
- +} \
- +static inline void dwc_write_##_reg(_container_type *container, uint32_t data) { \
- + DWC_DEBUG("WRITING %11s: %p: %08x", #_reg, &container->regs->_reg, data); \
- + DWC_WRITE_REG32(&container->regs->_reg, data); \
- +}
- +
- +# else /* DWC_DEBUG_REGS */
- +
- +#define dwc_define_read_write_reg_n(_reg,_container_type) \
- +static inline uint32_t dwc_read_##_reg##_n(_container_type *container, int num) { \
- + return DWC_READ_REG32(&container->regs->_reg[num]); \
- +} \
- +static inline void dwc_write_##_reg##_n(_container_type *container, int num, uint32_t data) { \
- + DWC_WRITE_REG32(&(((uint32_t*)container->regs->_reg)[num]), data); \
- +}
- +
- +#define dwc_define_read_write_reg(_reg,_container_type) \
- +static inline uint32_t dwc_read_##_reg(_container_type *container) { \
- + return DWC_READ_REG32(&container->regs->_reg); \
- +} \
- +static inline void dwc_write_##_reg(_container_type *container, uint32_t data) { \
- + DWC_WRITE_REG32(&container->regs->_reg, data); \
- +}
- +
- +# endif /* DWC_DEBUG_REGS */
- +
- +#endif /* DWC_LINUX */
- +
- +#if defined(DWC_FREEBSD) || defined(DWC_NETBSD)
- +
- +# ifdef DWC_DEBUG_REGS
- +
- +#define dwc_define_read_write_reg_n(_reg,_container_type) \
- +static inline uint32_t dwc_read_##_reg##_n(void *io_ctx, _container_type *container, int num) { \
- + return DWC_READ_REG32(io_ctx, &container->regs->_reg[num]); \
- +} \
- +static inline void dwc_write_##_reg##_n(void *io_ctx, _container_type *container, int num, uint32_t data) { \
- + DWC_DEBUG("WRITING %8s[%d]: %p: %08x", #_reg, num, \
- + &(((uint32_t*)container->regs->_reg)[num]), data); \
- + DWC_WRITE_REG32(io_ctx, &(((uint32_t*)container->regs->_reg)[num]), data); \
- +}
- +
- +#define dwc_define_read_write_reg(_reg,_container_type) \
- +static inline uint32_t dwc_read_##_reg(void *io_ctx, _container_type *container) { \
- + return DWC_READ_REG32(io_ctx, &container->regs->_reg); \
- +} \
- +static inline void dwc_write_##_reg(void *io_ctx, _container_type *container, uint32_t data) { \
- + DWC_DEBUG("WRITING %11s: %p: %08x", #_reg, &container->regs->_reg, data); \
- + DWC_WRITE_REG32(io_ctx, &container->regs->_reg, data); \
- +}
- +
- +# else /* DWC_DEBUG_REGS */
- +
- +#define dwc_define_read_write_reg_n(_reg,_container_type) \
- +static inline uint32_t dwc_read_##_reg##_n(void *io_ctx, _container_type *container, int num) { \
- + return DWC_READ_REG32(io_ctx, &container->regs->_reg[num]); \
- +} \
- +static inline void dwc_write_##_reg##_n(void *io_ctx, _container_type *container, int num, uint32_t data) { \
- + DWC_WRITE_REG32(io_ctx, &(((uint32_t*)container->regs->_reg)[num]), data); \
- +}
- +
- +#define dwc_define_read_write_reg(_reg,_container_type) \
- +static inline uint32_t dwc_read_##_reg(void *io_ctx, _container_type *container) { \
- + return DWC_READ_REG32(io_ctx, &container->regs->_reg); \
- +} \
- +static inline void dwc_write_##_reg(void *io_ctx, _container_type *container, uint32_t data) { \
- + DWC_WRITE_REG32(io_ctx, &container->regs->_reg, data); \
- +}
- +
- +# endif /* DWC_DEBUG_REGS */
- +
- +#endif /* DWC_FREEBSD || DWC_NETBSD */
- +
- +/** @endcond */
- +
- +
- +#ifdef DWC_CRYPTOLIB
- +/** @name Crypto Functions
- + *
- + * These are the low-level cryptographic functions used by the driver. */
- +
- +/** Perform AES CBC */
- +extern int DWC_AES_CBC(uint8_t *message, uint32_t messagelen, uint8_t *key, uint32_t keylen, uint8_t iv[16], uint8_t *out);
- +#define dwc_aes_cbc DWC_AES_CBC
- +
- +/** Fill the provided buffer with random bytes. These should be cryptographic grade random numbers. */
- +extern void DWC_RANDOM_BYTES(uint8_t *buffer, uint32_t length);
- +#define dwc_random_bytes DWC_RANDOM_BYTES
- +
- +/** Perform the SHA-256 hash function */
- +extern int DWC_SHA256(uint8_t *message, uint32_t len, uint8_t *out);
- +#define dwc_sha256 DWC_SHA256
- +
- +/** Calculated the HMAC-SHA256 */
- +extern int DWC_HMAC_SHA256(uint8_t *message, uint32_t messagelen, uint8_t *key, uint32_t keylen, uint8_t *out);
- +#define dwc_hmac_sha256 DWC_HMAC_SHA256
- +
- +#endif /* DWC_CRYPTOLIB */
- +
- +
- +/** @name Memory Allocation
- + *
- + * These function provide access to memory allocation. There are only 2 DMA
- + * functions and 3 Regular memory functions that need to be implemented. None
- + * of the memory debugging routines need to be implemented. The allocation
- + * routines all ZERO the contents of the memory.
- + *
- + * Defining DWC_DEBUG_MEMORY turns on memory debugging and statistic gathering.
- + * This checks for memory leaks, keeping track of alloc/free pairs. It also
- + * keeps track of how much memory the driver is using at any given time. */
- +
- +#define DWC_PAGE_SIZE 4096
- +#define DWC_PAGE_OFFSET(addr) (((uint32_t)addr) & 0xfff)
- +#define DWC_PAGE_ALIGNED(addr) ((((uint32_t)addr) & 0xfff) == 0)
- +
- +#define DWC_INVALID_DMA_ADDR 0x0
- +
- +#ifdef DWC_LINUX
- +/** Type for a DMA address */
- +typedef dma_addr_t dwc_dma_t;
- +#endif
- +
- +#if defined(DWC_FREEBSD) || defined(DWC_NETBSD)
- +typedef bus_addr_t dwc_dma_t;
- +#endif
- +
- +#ifdef DWC_FREEBSD
- +typedef struct dwc_dmactx {
- + struct device *dev;
- + bus_dma_tag_t dma_tag;
- + bus_dmamap_t dma_map;
- + bus_addr_t dma_paddr;
- + void *dma_vaddr;
- +} dwc_dmactx_t;
- +#endif
- +
- +#ifdef DWC_NETBSD
- +typedef struct dwc_dmactx {
- + struct device *dev;
- + bus_dma_tag_t dma_tag;
- + bus_dmamap_t dma_map;
- + bus_dma_segment_t segs[1];
- + int nsegs;
- + bus_addr_t dma_paddr;
- + void *dma_vaddr;
- +} dwc_dmactx_t;
- +#endif
- +
- +/* @todo these functions will be added in the future */
- +#if 0
- +/**
- + * Creates a DMA pool from which you can allocate DMA buffers. Buffers
- + * allocated from this pool will be guaranteed to meet the size, alignment, and
- + * boundary requirements specified.
- + *
- + * @param[in] size Specifies the size of the buffers that will be allocated from
- + * this pool.
- + * @param[in] align Specifies the byte alignment requirements of the buffers
- + * allocated from this pool. Must be a power of 2.
- + * @param[in] boundary Specifies the N-byte boundary that buffers allocated from
- + * this pool must not cross.
- + *
- + * @returns A pointer to an internal opaque structure which is not to be
- + * accessed outside of these library functions. Use this handle to specify
- + * which pools to allocate/free DMA buffers from and also to destroy the pool,
- + * when you are done with it.
- + */
- +extern dwc_pool_t *DWC_DMA_POOL_CREATE(uint32_t size, uint32_t align, uint32_t boundary);
- +
- +/**
- + * Destroy a DMA pool. All buffers allocated from that pool must be freed first.
- + */
- +extern void DWC_DMA_POOL_DESTROY(dwc_pool_t *pool);
- +
- +/**
- + * Allocate a buffer from the specified DMA pool and zeros its contents.
- + */
- +extern void *DWC_DMA_POOL_ALLOC(dwc_pool_t *pool, uint64_t *dma_addr);
- +
- +/**
- + * Free a previously allocated buffer from the DMA pool.
- + */
- +extern void DWC_DMA_POOL_FREE(dwc_pool_t *pool, void *vaddr, void *daddr);
- +#endif
- +
- +/** Allocates a DMA capable buffer and zeroes its contents. */
- +extern void *__DWC_DMA_ALLOC(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr);
- +
- +/** Allocates a DMA capable buffer and zeroes its contents in atomic contest */
- +extern void *__DWC_DMA_ALLOC_ATOMIC(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr);
- +
- +/** Frees a previously allocated buffer. */
- +extern void __DWC_DMA_FREE(void *dma_ctx, uint32_t size, void *virt_addr, dwc_dma_t dma_addr);
- +
- +/** Allocates a block of memory and zeroes its contents. */
- +extern void *__DWC_ALLOC(void *mem_ctx, uint32_t size);
- +
- +/** Allocates a block of memory and zeroes its contents, in an atomic manner
- + * which can be used inside interrupt context. The size should be sufficiently
- + * small, a few KB at most, such that failures are not likely to occur. Can just call
- + * __DWC_ALLOC if it is atomic. */
- +extern void *__DWC_ALLOC_ATOMIC(void *mem_ctx, uint32_t size);
- +
- +/** Frees a previously allocated buffer. */
- +extern void __DWC_FREE(void *mem_ctx, void *addr);
- +
- +#ifndef DWC_DEBUG_MEMORY
- +
- +#define DWC_ALLOC(_size_) __DWC_ALLOC(NULL, _size_)
- +#define DWC_ALLOC_ATOMIC(_size_) __DWC_ALLOC_ATOMIC(NULL, _size_)
- +#define DWC_FREE(_addr_) __DWC_FREE(NULL, _addr_)
- +
- +# ifdef DWC_LINUX
- +#define DWC_DMA_ALLOC(_dev, _size_, _dma_) __DWC_DMA_ALLOC(_dev, _size_, _dma_)
- +#define DWC_DMA_ALLOC_ATOMIC(_dev, _size_, _dma_) __DWC_DMA_ALLOC_ATOMIC(_dev, _size_, _dma_)
- +#define DWC_DMA_FREE(_dev, _size_,_virt_, _dma_) __DWC_DMA_FREE(_dev, _size_, _virt_, _dma_)
- +# endif
- +
- +# if defined(DWC_FREEBSD) || defined(DWC_NETBSD)
- +#define DWC_DMA_ALLOC __DWC_DMA_ALLOC
- +#define DWC_DMA_FREE __DWC_DMA_FREE
- +# endif
- +extern void *dwc_dma_alloc_atomic_debug(uint32_t size, dwc_dma_t *dma_addr, char const *func, int line);
- +
- +#else /* DWC_DEBUG_MEMORY */
- +
- +extern void *dwc_alloc_debug(void *mem_ctx, uint32_t size, char const *func, int line);
- +extern void *dwc_alloc_atomic_debug(void *mem_ctx, uint32_t size, char const *func, int line);
- +extern void dwc_free_debug(void *mem_ctx, void *addr, char const *func, int line);
- +extern void *dwc_dma_alloc_debug(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr,
- + char const *func, int line);
- +extern void *dwc_dma_alloc_atomic_debug(void *dma_ctx, uint32_t size, dwc_dma_t *dma_addr,
- + char const *func, int line);
- +extern void dwc_dma_free_debug(void *dma_ctx, uint32_t size, void *virt_addr,
- + dwc_dma_t dma_addr, char const *func, int line);
- +
- +extern int dwc_memory_debug_start(void *mem_ctx);
- +extern void dwc_memory_debug_stop(void);
- +extern void dwc_memory_debug_report(void);
- +
- +#define DWC_ALLOC(_size_) dwc_alloc_debug(NULL, _size_, __func__, __LINE__)
- +#define DWC_ALLOC_ATOMIC(_size_) dwc_alloc_atomic_debug(NULL, _size_, \
- + __func__, __LINE__)
- +#define DWC_FREE(_addr_) dwc_free_debug(NULL, _addr_, __func__, __LINE__)
- +
- +# ifdef DWC_LINUX
- +#define DWC_DMA_ALLOC(_dev, _size_, _dma_) \
- + dwc_dma_alloc_debug(_dev, _size_, _dma_, __func__, __LINE__)
- +#define DWC_DMA_ALLOC_ATOMIC(_dev, _size_, _dma_) \
- + dwc_dma_alloc_atomic_debug(_dev, _size_, _dma_, __func__, __LINE__)
- +#define DWC_DMA_FREE(_dev, _size_, _virt_, _dma_) \
- + dwc_dma_free_debug(_dev, _size_, _virt_, _dma_, __func__, __LINE__)
- +# endif
- +
- +# if defined(DWC_FREEBSD) || defined(DWC_NETBSD)
- +#define DWC_DMA_ALLOC(_ctx_,_size_,_dma_) dwc_dma_alloc_debug(_ctx_, _size_, \
- + _dma_, __func__, __LINE__)
- +#define DWC_DMA_FREE(_ctx_,_size_,_virt_,_dma_) dwc_dma_free_debug(_ctx_, _size_, \
- + _virt_, _dma_, __func__, __LINE__)
- +# endif
- +
- +#endif /* DWC_DEBUG_MEMORY */
- +
- +#define dwc_alloc(_ctx_,_size_) DWC_ALLOC(_size_)
- +#define dwc_alloc_atomic(_ctx_,_size_) DWC_ALLOC_ATOMIC(_size_)
- +#define dwc_free(_ctx_,_addr_) DWC_FREE(_addr_)
- +
- +#ifdef DWC_LINUX
- +/* Linux doesn't need any extra parameters for DMA buffer allocation, so we
- + * just throw away the DMA context parameter.
- + */
- +#define dwc_dma_alloc(_ctx_,_size_,_dma_) DWC_DMA_ALLOC(_size_, _dma_)
- +#define dwc_dma_alloc_atomic(_ctx_,_size_,_dma_) DWC_DMA_ALLOC_ATOMIC(_size_, _dma_)
- +#define dwc_dma_free(_ctx_,_size_,_virt_,_dma_) DWC_DMA_FREE(_size_, _virt_, _dma_)
- +#endif
- +
- +#if defined(DWC_FREEBSD) || defined(DWC_NETBSD)
- +/** BSD needs several extra parameters for DMA buffer allocation, so we pass
- + * them in using the DMA context parameter.
- + */
- +#define dwc_dma_alloc DWC_DMA_ALLOC
- +#define dwc_dma_free DWC_DMA_FREE
- +#endif
- +
- +
- +/** @name Memory and String Processing */
- +
- +/** memset() clone */
- +extern void *DWC_MEMSET(void *dest, uint8_t byte, uint32_t size);
- +#define dwc_memset DWC_MEMSET
- +
- +/** memcpy() clone */
- +extern void *DWC_MEMCPY(void *dest, void const *src, uint32_t size);
- +#define dwc_memcpy DWC_MEMCPY
- +
- +/** memmove() clone */
- +extern void *DWC_MEMMOVE(void *dest, void *src, uint32_t size);
- +#define dwc_memmove DWC_MEMMOVE
- +
- +/** memcmp() clone */
- +extern int DWC_MEMCMP(void *m1, void *m2, uint32_t size);
- +#define dwc_memcmp DWC_MEMCMP
- +
- +/** strcmp() clone */
- +extern int DWC_STRCMP(void *s1, void *s2);
- +#define dwc_strcmp DWC_STRCMP
- +
- +/** strncmp() clone */
- +extern int DWC_STRNCMP(void *s1, void *s2, uint32_t size);
- +#define dwc_strncmp DWC_STRNCMP
- +
- +/** strlen() clone, for NULL terminated ASCII strings */
- +extern int DWC_STRLEN(char const *str);
- +#define dwc_strlen DWC_STRLEN
- +
- +/** strcpy() clone, for NULL terminated ASCII strings */
- +extern char *DWC_STRCPY(char *to, const char *from);
- +#define dwc_strcpy DWC_STRCPY
- +
- +/** strdup() clone. If you wish to use memory allocation debugging, this
- + * implementation of strdup should use the DWC_* memory routines instead of
- + * calling a predefined strdup. Otherwise the memory allocated by this routine
- + * will not be seen by the debugging routines. */
- +extern char *DWC_STRDUP(char const *str);
- +#define dwc_strdup(_ctx_,_str_) DWC_STRDUP(_str_)
- +
- +/** NOT an atoi() clone. Read the description carefully. Returns an integer
- + * converted from the string str in base 10 unless the string begins with a "0x"
- + * in which case it is base 16. String must be a NULL terminated sequence of
- + * ASCII characters and may optionally begin with whitespace, a + or -, and a
- + * "0x" prefix if base 16. The remaining characters must be valid digits for
- + * the number and end with a NULL character. If any invalid characters are
- + * encountered or it returns with a negative error code and the results of the
- + * conversion are undefined. On sucess it returns 0. Overflow conditions are
- + * undefined. An example implementation using atoi() can be referenced from the
- + * Linux implementation. */
- +extern int DWC_ATOI(const char *str, int32_t *value);
- +#define dwc_atoi DWC_ATOI
- +
- +/** Same as above but for unsigned. */
- +extern int DWC_ATOUI(const char *str, uint32_t *value);
- +#define dwc_atoui DWC_ATOUI
- +
- +#ifdef DWC_UTFLIB
- +/** This routine returns a UTF16LE unicode encoded string from a UTF8 string. */
- +extern int DWC_UTF8_TO_UTF16LE(uint8_t const *utf8string, uint16_t *utf16string, unsigned len);
- +#define dwc_utf8_to_utf16le DWC_UTF8_TO_UTF16LE
- +#endif
- +
- +
- +/** @name Wait queues
- + *
- + * Wait queues provide a means of synchronizing between threads or processes. A
- + * process can block on a waitq if some condition is not true, waiting for it to
- + * become true. When the waitq is triggered all waiting process will get
- + * unblocked and the condition will be check again. Waitqs should be triggered
- + * every time a condition can potentially change.*/
- +struct dwc_waitq;
- +
- +/** Type for a waitq */
- +typedef struct dwc_waitq dwc_waitq_t;
- +
- +/** The type of waitq condition callback function. This is called every time
- + * condition is evaluated. */
- +typedef int (*dwc_waitq_condition_t)(void *data);
- +
- +/** Allocate a waitq */
- +extern dwc_waitq_t *DWC_WAITQ_ALLOC(void);
- +#define dwc_waitq_alloc(_ctx_) DWC_WAITQ_ALLOC()
- +
- +/** Free a waitq */
- +extern void DWC_WAITQ_FREE(dwc_waitq_t *wq);
- +#define dwc_waitq_free DWC_WAITQ_FREE
- +
- +/** Check the condition and if it is false, block on the waitq. When unblocked, check the
- + * condition again. The function returns when the condition becomes true. The return value
- + * is 0 on condition true, DWC_WAITQ_ABORTED on abort or killed, or DWC_WAITQ_UNKNOWN on error. */
- +extern int32_t DWC_WAITQ_WAIT(dwc_waitq_t *wq, dwc_waitq_condition_t cond, void *data);
- +#define dwc_waitq_wait DWC_WAITQ_WAIT
- +
- +/** Check the condition and if it is false, block on the waitq. When unblocked,
- + * check the condition again. The function returns when the condition become
- + * true or the timeout has passed. The return value is 0 on condition true or
- + * DWC_TIMED_OUT on timeout, or DWC_WAITQ_ABORTED, or DWC_WAITQ_UNKNOWN on
- + * error. */
- +extern int32_t DWC_WAITQ_WAIT_TIMEOUT(dwc_waitq_t *wq, dwc_waitq_condition_t cond,
- + void *data, int32_t msecs);
- +#define dwc_waitq_wait_timeout DWC_WAITQ_WAIT_TIMEOUT
- +
- +/** Trigger a waitq, unblocking all processes. This should be called whenever a condition
- + * has potentially changed. */
- +extern void DWC_WAITQ_TRIGGER(dwc_waitq_t *wq);
- +#define dwc_waitq_trigger DWC_WAITQ_TRIGGER
- +
- +/** Unblock all processes waiting on the waitq with an ABORTED result. */
- +extern void DWC_WAITQ_ABORT(dwc_waitq_t *wq);
- +#define dwc_waitq_abort DWC_WAITQ_ABORT
- +
- +
- +/** @name Threads
- + *
- + * A thread must be explicitly stopped. It must check DWC_THREAD_SHOULD_STOP
- + * whenever it is woken up, and then return. The DWC_THREAD_STOP function
- + * returns the value from the thread.
- + */
- +
- +struct dwc_thread;
- +
- +/** Type for a thread */
- +typedef struct dwc_thread dwc_thread_t;
- +
- +/** The thread function */
- +typedef int (*dwc_thread_function_t)(void *data);
- +
- +/** Create a thread and start it running the thread_function. Returns a handle
- + * to the thread */
- +extern dwc_thread_t *DWC_THREAD_RUN(dwc_thread_function_t func, char *name, void *data);
- +#define dwc_thread_run(_ctx_,_func_,_name_,_data_) DWC_THREAD_RUN(_func_, _name_, _data_)
- +
- +/** Stops a thread. Return the value returned by the thread. Or will return
- + * DWC_ABORT if the thread never started. */
- +extern int DWC_THREAD_STOP(dwc_thread_t *thread);
- +#define dwc_thread_stop DWC_THREAD_STOP
- +
- +/** Signifies to the thread that it must stop. */
- +#ifdef DWC_LINUX
- +/* Linux doesn't need any parameters for kthread_should_stop() */
- +extern dwc_bool_t DWC_THREAD_SHOULD_STOP(void);
- +#define dwc_thread_should_stop(_thrd_) DWC_THREAD_SHOULD_STOP()
- +
- +/* No thread_exit function in Linux */
- +#define dwc_thread_exit(_thrd_)
- +#endif
- +
- +#if defined(DWC_FREEBSD) || defined(DWC_NETBSD)
- +/** BSD needs the thread pointer for kthread_suspend_check() */
- +extern dwc_bool_t DWC_THREAD_SHOULD_STOP(dwc_thread_t *thread);
- +#define dwc_thread_should_stop DWC_THREAD_SHOULD_STOP
- +
- +/** The thread must call this to exit. */
- +extern void DWC_THREAD_EXIT(dwc_thread_t *thread);
- +#define dwc_thread_exit DWC_THREAD_EXIT
- +#endif
- +
- +
- +/** @name Work queues
- + *
- + * Workqs are used to queue a callback function to be called at some later time,
- + * in another thread. */
- +struct dwc_workq;
- +
- +/** Type for a workq */
- +typedef struct dwc_workq dwc_workq_t;
- +
- +/** The type of the callback function to be called. */
- +typedef void (*dwc_work_callback_t)(void *data);
- +
- +/** Allocate a workq */
- +extern dwc_workq_t *DWC_WORKQ_ALLOC(char *name);
- +#define dwc_workq_alloc(_ctx_,_name_) DWC_WORKQ_ALLOC(_name_)
- +
- +/** Free a workq. All work must be completed before being freed. */
- +extern void DWC_WORKQ_FREE(dwc_workq_t *workq);
- +#define dwc_workq_free DWC_WORKQ_FREE
- +
- +/** Schedule a callback on the workq, passing in data. The function will be
- + * scheduled at some later time. */
- +extern void DWC_WORKQ_SCHEDULE(dwc_workq_t *workq, dwc_work_callback_t cb,
- + void *data, char *format, ...)
- +#ifdef __GNUC__
- + __attribute__ ((format(printf, 4, 5)));
- +#else
- + ;
- +#endif
- +#define dwc_workq_schedule DWC_WORKQ_SCHEDULE
- +
- +/** Schedule a callback on the workq, that will be called until at least
- + * given number miliseconds have passed. */
- +extern void DWC_WORKQ_SCHEDULE_DELAYED(dwc_workq_t *workq, dwc_work_callback_t cb,
- + void *data, uint32_t time, char *format, ...)
- +#ifdef __GNUC__
- + __attribute__ ((format(printf, 5, 6)));
- +#else
- + ;
- +#endif
- +#define dwc_workq_schedule_delayed DWC_WORKQ_SCHEDULE_DELAYED
- +
- +/** The number of processes in the workq */
- +extern int DWC_WORKQ_PENDING(dwc_workq_t *workq);
- +#define dwc_workq_pending DWC_WORKQ_PENDING
- +
- +/** Blocks until all the work in the workq is complete or timed out. Returns <
- + * 0 on timeout. */
- +extern int DWC_WORKQ_WAIT_WORK_DONE(dwc_workq_t *workq, int timeout);
- +#define dwc_workq_wait_work_done DWC_WORKQ_WAIT_WORK_DONE
- +
- +
- +/** @name Tasklets
- + *
- + */
- +struct dwc_tasklet;
- +
- +/** Type for a tasklet */
- +typedef struct dwc_tasklet dwc_tasklet_t;
- +
- +/** The type of the callback function to be called */
- +typedef void (*dwc_tasklet_callback_t)(void *data);
- +
- +/** Allocates a tasklet */
- +extern dwc_tasklet_t *DWC_TASK_ALLOC(char *name, dwc_tasklet_callback_t cb, void *data);
- +#define dwc_task_alloc(_ctx_,_name_,_cb_,_data_) DWC_TASK_ALLOC(_name_, _cb_, _data_)
- +
- +/** Frees a tasklet */
- +extern void DWC_TASK_FREE(dwc_tasklet_t *task);
- +#define dwc_task_free DWC_TASK_FREE
- +
- +/** Schedules a tasklet to run */
- +extern void DWC_TASK_SCHEDULE(dwc_tasklet_t *task);
- +#define dwc_task_schedule DWC_TASK_SCHEDULE
- +
- +extern void DWC_TASK_HI_SCHEDULE(dwc_tasklet_t *task);
- +#define dwc_task_hi_schedule DWC_TASK_HI_SCHEDULE
- +
- +/** @name Timer
- + *
- + * Callbacks must be small and atomic.
- + */
- +struct dwc_timer;
- +
- +/** Type for a timer */
- +typedef struct dwc_timer dwc_timer_t;
- +
- +/** The type of the callback function to be called */
- +typedef void (*dwc_timer_callback_t)(void *data);
- +
- +/** Allocates a timer */
- +extern dwc_timer_t *DWC_TIMER_ALLOC(char *name, dwc_timer_callback_t cb, void *data);
- +#define dwc_timer_alloc(_ctx_,_name_,_cb_,_data_) DWC_TIMER_ALLOC(_name_,_cb_,_data_)
- +
- +/** Frees a timer */
- +extern void DWC_TIMER_FREE(dwc_timer_t *timer);
- +#define dwc_timer_free DWC_TIMER_FREE
- +
- +/** Schedules the timer to run at time ms from now. And will repeat at every
- + * repeat_interval msec therafter
- + *
- + * Modifies a timer that is still awaiting execution to a new expiration time.
- + * The mod_time is added to the old time. */
- +extern void DWC_TIMER_SCHEDULE(dwc_timer_t *timer, uint32_t time);
- +#define dwc_timer_schedule DWC_TIMER_SCHEDULE
- +
- +/** Disables the timer from execution. */
- +extern void DWC_TIMER_CANCEL(dwc_timer_t *timer);
- +#define dwc_timer_cancel DWC_TIMER_CANCEL
- +
- +
- +/** @name Spinlocks
- + *
- + * These locks are used when the work between the lock/unlock is atomic and
- + * short. Interrupts are also disabled during the lock/unlock and thus they are
- + * suitable to lock between interrupt/non-interrupt context. They also lock
- + * between processes if you have multiple CPUs or Preemption. If you don't have
- + * multiple CPUS or Preemption, then the you can simply implement the
- + * DWC_SPINLOCK and DWC_SPINUNLOCK to disable and enable interrupts. Because
- + * the work between the lock/unlock is atomic, the process context will never
- + * change, and so you never have to lock between processes. */
- +
- +struct dwc_spinlock;
- +
- +/** Type for a spinlock */
- +typedef struct dwc_spinlock dwc_spinlock_t;
- +
- +/** Type for the 'flags' argument to spinlock funtions */
- +typedef unsigned long dwc_irqflags_t;
- +
- +/** Returns an initialized lock variable. This function should allocate and
- + * initialize the OS-specific data structure used for locking. This data
- + * structure is to be used for the DWC_LOCK and DWC_UNLOCK functions and should
- + * be freed by the DWC_FREE_LOCK when it is no longer used.
- + *
- + * For Linux Spinlock Debugging make it macro because the debugging routines use
- + * the symbol name to determine recursive locking. Using a wrapper function
- + * makes it falsely think recursive locking occurs. */
- +#if defined(DWC_LINUX) && defined(CONFIG_DEBUG_SPINLOCK)
- +#define DWC_SPINLOCK_ALLOC_LINUX_DEBUG(lock) ({ \
- + lock = DWC_ALLOC(sizeof(spinlock_t)); \
- + if (lock) { \
- + spin_lock_init((spinlock_t *)lock); \
- + } \
- +})
- +#else
- +extern dwc_spinlock_t *DWC_SPINLOCK_ALLOC(void);
- +#define dwc_spinlock_alloc(_ctx_) DWC_SPINLOCK_ALLOC()
- +#endif
- +
- +/** Frees an initialized lock variable. */
- +extern void DWC_SPINLOCK_FREE(dwc_spinlock_t *lock);
- +#define dwc_spinlock_free(_ctx_,_lock_) DWC_SPINLOCK_FREE(_lock_)
- +
- +/** Disables interrupts and blocks until it acquires the lock.
- + *
- + * @param lock Pointer to the spinlock.
- + * @param flags Unsigned long for irq flags storage.
- + */
- +extern void DWC_SPINLOCK_IRQSAVE(dwc_spinlock_t *lock, dwc_irqflags_t *flags);
- +#define dwc_spinlock_irqsave DWC_SPINLOCK_IRQSAVE
- +
- +/** Re-enables the interrupt and releases the lock.
- + *
- + * @param lock Pointer to the spinlock.
- + * @param flags Unsigned long for irq flags storage. Must be the same as was
- + * passed into DWC_LOCK.
- + */
- +extern void DWC_SPINUNLOCK_IRQRESTORE(dwc_spinlock_t *lock, dwc_irqflags_t flags);
- +#define dwc_spinunlock_irqrestore DWC_SPINUNLOCK_IRQRESTORE
- +
- +/** Blocks until it acquires the lock.
- + *
- + * @param lock Pointer to the spinlock.
- + */
- +extern void DWC_SPINLOCK(dwc_spinlock_t *lock);
- +#define dwc_spinlock DWC_SPINLOCK
- +
- +/** Releases the lock.
- + *
- + * @param lock Pointer to the spinlock.
- + */
- +extern void DWC_SPINUNLOCK(dwc_spinlock_t *lock);
- +#define dwc_spinunlock DWC_SPINUNLOCK
- +
- +
- +/** @name Mutexes
- + *
- + * Unlike spinlocks Mutexes lock only between processes and the work between the
- + * lock/unlock CAN block, therefore it CANNOT be called from interrupt context.
- + */
- +
- +struct dwc_mutex;
- +
- +/** Type for a mutex */
- +typedef struct dwc_mutex dwc_mutex_t;
- +
- +/* For Linux Mutex Debugging make it inline because the debugging routines use
- + * the symbol to determine recursive locking. This makes it falsely think
- + * recursive locking occurs. */
- +#if defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES)
- +#define DWC_MUTEX_ALLOC_LINUX_DEBUG(__mutexp) ({ \
- + __mutexp = (dwc_mutex_t *)DWC_ALLOC(sizeof(struct mutex)); \
- + mutex_init((struct mutex *)__mutexp); \
- +})
- +#endif
- +
- +/** Allocate a mutex */
- +extern dwc_mutex_t *DWC_MUTEX_ALLOC(void);
- +#define dwc_mutex_alloc(_ctx_) DWC_MUTEX_ALLOC()
- +
- +/* For memory leak debugging when using Linux Mutex Debugging */
- +#if defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES)
- +#define DWC_MUTEX_FREE(__mutexp) do { \
- + mutex_destroy((struct mutex *)__mutexp); \
- + DWC_FREE(__mutexp); \
- +} while(0)
- +#else
- +/** Free a mutex */
- +extern void DWC_MUTEX_FREE(dwc_mutex_t *mutex);
- +#define dwc_mutex_free(_ctx_,_mutex_) DWC_MUTEX_FREE(_mutex_)
- +#endif
- +
- +/** Lock a mutex */
- +extern void DWC_MUTEX_LOCK(dwc_mutex_t *mutex);
- +#define dwc_mutex_lock DWC_MUTEX_LOCK
- +
- +/** Non-blocking lock returns 1 on successful lock. */
- +extern int DWC_MUTEX_TRYLOCK(dwc_mutex_t *mutex);
- +#define dwc_mutex_trylock DWC_MUTEX_TRYLOCK
- +
- +/** Unlock a mutex */
- +extern void DWC_MUTEX_UNLOCK(dwc_mutex_t *mutex);
- +#define dwc_mutex_unlock DWC_MUTEX_UNLOCK
- +
- +
- +/** @name Time */
- +
- +/** Microsecond delay.
- + *
- + * @param usecs Microseconds to delay.
- + */
- +extern void DWC_UDELAY(uint32_t usecs);
- +#define dwc_udelay DWC_UDELAY
- +
- +/** Millisecond delay.
- + *
- + * @param msecs Milliseconds to delay.
- + */
- +extern void DWC_MDELAY(uint32_t msecs);
- +#define dwc_mdelay DWC_MDELAY
- +
- +/** Non-busy waiting.
- + * Sleeps for specified number of milliseconds.
- + *
- + * @param msecs Milliseconds to sleep.
- + */
- +extern void DWC_MSLEEP(uint32_t msecs);
- +#define dwc_msleep DWC_MSLEEP
- +
- +/**
- + * Returns number of milliseconds since boot.
- + */
- +extern uint32_t DWC_TIME(void);
- +#define dwc_time DWC_TIME
- +
- +
- +
- +
- +/* @mainpage DWC Portability and Common Library
- + *
- + * This is the documentation for the DWC Portability and Common Library.
- + *
- + * @section intro Introduction
- + *
- + * The DWC Portability library consists of wrapper calls and data structures to
- + * all low-level functions which are typically provided by the OS. The WUDEV
- + * driver uses only these functions. In order to port the WUDEV driver, only
- + * the functions in this library need to be re-implemented, with the same
- + * behavior as documented here.
- + *
- + * The Common library consists of higher level functions, which rely only on
- + * calling the functions from the DWC Portability library. These common
- + * routines are shared across modules. Some of the common libraries need to be
- + * used directly by the driver programmer when porting WUDEV. Such as the
- + * parameter and notification libraries.
- + *
- + * @section low Portability Library OS Wrapper Functions
- + *
- + * Any function starting with DWC and in all CAPS is a low-level OS-wrapper that
- + * needs to be implemented when porting, for example DWC_MUTEX_ALLOC(). All of
- + * these functions are included in the dwc_os.h file.
- + *
- + * There are many functions here covering a wide array of OS services. Please
- + * see dwc_os.h for details, and implementation notes for each function.
- + *
- + * @section common Common Library Functions
- + *
- + * Any function starting with dwc and in all lowercase is a common library
- + * routine. These functions have a portable implementation and do not need to
- + * be reimplemented when porting. The common routines can be used by any
- + * driver, and some must be used by the end user to control the drivers. For
- + * example, you must use the Parameter common library in order to set the
- + * parameters in the WUDEV module.
- + *
- + * The common libraries consist of the following:
- + *
- + * - Connection Contexts - Used internally and can be used by end-user. See dwc_cc.h
- + * - Parameters - Used internally and can be used by end-user. See dwc_params.h
- + * - Notifications - Used internally and can be used by end-user. See dwc_notifier.h
- + * - Lists - Used internally and can be used by end-user. See dwc_list.h
- + * - Memory Debugging - Used internally and can be used by end-user. See dwc_os.h
- + * - Modpow - Used internally only. See dwc_modpow.h
- + * - DH - Used internally only. See dwc_dh.h
- + * - Crypto - Used internally only. See dwc_crypto.h
- + *
- + *
- + * @section prereq Prerequistes For dwc_os.h
- + * @subsection types Data Types
- + *
- + * The dwc_os.h file assumes that several low-level data types are pre defined for the
- + * compilation environment. These data types are:
- + *
- + * - uint8_t - unsigned 8-bit data type
- + * - int8_t - signed 8-bit data type
- + * - uint16_t - unsigned 16-bit data type
- + * - int16_t - signed 16-bit data type
- + * - uint32_t - unsigned 32-bit data type
- + * - int32_t - signed 32-bit data type
- + * - uint64_t - unsigned 64-bit data type
- + * - int64_t - signed 64-bit data type
- + *
- + * Ensure that these are defined before using dwc_os.h. The easiest way to do
- + * that is to modify the top of the file to include the appropriate header.
- + * This is already done for the Linux environment. If the DWC_LINUX macro is
- + * defined, the correct header will be added. A standard header <stdint.h> is
- + * also used for environments where standard C headers are available.
- + *
- + * @subsection stdarg Variable Arguments
- + *
- + * Variable arguments are provided by a standard C header <stdarg.h>. it is
- + * available in Both the Linux and ANSI C enviornment. An equivalent must be
- + * provided in your enviornment in order to use dwc_os.h with the debug and
- + * tracing message functionality.
- + *
- + * @subsection thread Threading
- + *
- + * WUDEV Core must be run on an operating system that provides for multiple
- + * threads/processes. Threading can be implemented in many ways, even in
- + * embedded systems without an operating system. At the bare minimum, the
- + * system should be able to start any number of processes at any time to handle
- + * special work. It need not be a pre-emptive system. Process context can
- + * change upon a call to a blocking function. The hardware interrupt context
- + * that calls the module's ISR() function must be differentiable from process
- + * context, even if your processes are impemented via a hardware interrupt.
- + * Further locking mechanism between process must exist (or be implemented), and
- + * process context must have a way to disable interrupts for a period of time to
- + * lock them out. If all of this exists, the functions in dwc_os.h related to
- + * threading should be able to be implemented with the defined behavior.
- + *
- + */
- +
- +#ifdef __cplusplus
- +}
- +#endif
- +
- +#endif /* _DWC_OS_H_ */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_common_port/usb.h
- @@ -0,0 +1,275 @@
- +/*
- + * Copyright (c) 1998 The NetBSD Foundation, Inc.
- + * All rights reserved.
- + *
- + * This code is derived from software contributed to The NetBSD Foundation
- + * by Lennart Augustsson ([email protected]) at
- + * Carlstedt Research & Technology.
- + *
- + * Redistribution and use in source and binary forms, with or without
- + * modification, are permitted provided that the following conditions
- + * are met:
- + * 1. Redistributions of source code must retain the above copyright
- + * notice, this list of conditions and the following disclaimer.
- + * 2. 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.
- + *
- + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 THE FOUNDATION OR CONTRIBUTORS
- + * 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.
- + */
- +
- +/* Modified by Synopsys, Inc, 12/12/2007 */
- +
- +
- +#ifndef _USB_H_
- +#define _USB_H_
- +
- +#ifdef __cplusplus
- +extern "C" {
- +#endif
- +
- +/*
- + * The USB records contain some unaligned little-endian word
- + * components. The U[SG]ETW macros take care of both the alignment
- + * and endian problem and should always be used to access non-byte
- + * values.
- + */
- +typedef u_int8_t uByte;
- +typedef u_int8_t uWord[2];
- +typedef u_int8_t uDWord[4];
- +
- +#define UGETW(w) ((w)[0] | ((w)[1] << 8))
- +#define USETW(w,v) ((w)[0] = (u_int8_t)(v), (w)[1] = (u_int8_t)((v) >> 8))
- +#define UGETDW(w) ((w)[0] | ((w)[1] << 8) | ((w)[2] << 16) | ((w)[3] << 24))
- +#define USETDW(w,v) ((w)[0] = (u_int8_t)(v), \
- + (w)[1] = (u_int8_t)((v) >> 8), \
- + (w)[2] = (u_int8_t)((v) >> 16), \
- + (w)[3] = (u_int8_t)((v) >> 24))
- +
- +#define UPACKED __attribute__((__packed__))
- +
- +typedef struct {
- + uByte bmRequestType;
- + uByte bRequest;
- + uWord wValue;
- + uWord wIndex;
- + uWord wLength;
- +} UPACKED usb_device_request_t;
- +
- +#define UT_GET_DIR(a) ((a) & 0x80)
- +#define UT_WRITE 0x00
- +#define UT_READ 0x80
- +
- +#define UT_GET_TYPE(a) ((a) & 0x60)
- +#define UT_STANDARD 0x00
- +#define UT_CLASS 0x20
- +#define UT_VENDOR 0x40
- +
- +#define UT_GET_RECIPIENT(a) ((a) & 0x1f)
- +#define UT_DEVICE 0x00
- +#define UT_INTERFACE 0x01
- +#define UT_ENDPOINT 0x02
- +#define UT_OTHER 0x03
- +
- +/* Requests */
- +#define UR_GET_STATUS 0x00
- +#define USTAT_STANDARD_STATUS 0x00
- +#define WUSTAT_WUSB_FEATURE 0x01
- +#define WUSTAT_CHANNEL_INFO 0x02
- +#define WUSTAT_RECEIVED_DATA 0x03
- +#define WUSTAT_MAS_AVAILABILITY 0x04
- +#define WUSTAT_CURRENT_TRANSMIT_POWER 0x05
- +#define UR_CLEAR_FEATURE 0x01
- +#define UR_SET_FEATURE 0x03
- +#define UR_SET_AND_TEST_FEATURE 0x0c
- +#define UR_SET_ADDRESS 0x05
- +#define UR_GET_DESCRIPTOR 0x06
- +#define UDESC_DEVICE 0x01
- +#define UDESC_CONFIG 0x02
- +#define UDESC_STRING 0x03
- +#define UDESC_INTERFACE 0x04
- +#define UDESC_ENDPOINT 0x05
- +#define UDESC_SS_USB_COMPANION 0x30
- +#define UDESC_DEVICE_QUALIFIER 0x06
- +#define UDESC_OTHER_SPEED_CONFIGURATION 0x07
- +#define UDESC_INTERFACE_POWER 0x08
- +#define UDESC_OTG 0x09
- +#define WUDESC_SECURITY 0x0c
- +#define WUDESC_KEY 0x0d
- +#define WUD_GET_KEY_INDEX(_wValue_) ((_wValue_) & 0xf)
- +#define WUD_GET_KEY_TYPE(_wValue_) (((_wValue_) & 0x30) >> 4)
- +#define WUD_KEY_TYPE_ASSOC 0x01
- +#define WUD_KEY_TYPE_GTK 0x02
- +#define WUD_GET_KEY_ORIGIN(_wValue_) (((_wValue_) & 0x40) >> 6)
- +#define WUD_KEY_ORIGIN_HOST 0x00
- +#define WUD_KEY_ORIGIN_DEVICE 0x01
- +#define WUDESC_ENCRYPTION_TYPE 0x0e
- +#define WUDESC_BOS 0x0f
- +#define WUDESC_DEVICE_CAPABILITY 0x10
- +#define WUDESC_WIRELESS_ENDPOINT_COMPANION 0x11
- +#define UDESC_BOS 0x0f
- +#define UDESC_DEVICE_CAPABILITY 0x10
- +#define UDESC_CS_DEVICE 0x21 /* class specific */
- +#define UDESC_CS_CONFIG 0x22
- +#define UDESC_CS_STRING 0x23
- +#define UDESC_CS_INTERFACE 0x24
- +#define UDESC_CS_ENDPOINT 0x25
- +#define UDESC_HUB 0x29
- +#define UR_SET_DESCRIPTOR 0x07
- +#define UR_GET_CONFIG 0x08
- +#define UR_SET_CONFIG 0x09
- +#define UR_GET_INTERFACE 0x0a
- +#define UR_SET_INTERFACE 0x0b
- +#define UR_SYNCH_FRAME 0x0c
- +#define WUR_SET_ENCRYPTION 0x0d
- +#define WUR_GET_ENCRYPTION 0x0e
- +#define WUR_SET_HANDSHAKE 0x0f
- +#define WUR_GET_HANDSHAKE 0x10
- +#define WUR_SET_CONNECTION 0x11
- +#define WUR_SET_SECURITY_DATA 0x12
- +#define WUR_GET_SECURITY_DATA 0x13
- +#define WUR_SET_WUSB_DATA 0x14
- +#define WUDATA_DRPIE_INFO 0x01
- +#define WUDATA_TRANSMIT_DATA 0x02
- +#define WUDATA_TRANSMIT_PARAMS 0x03
- +#define WUDATA_RECEIVE_PARAMS 0x04
- +#define WUDATA_TRANSMIT_POWER 0x05
- +#define WUR_LOOPBACK_DATA_WRITE 0x15
- +#define WUR_LOOPBACK_DATA_READ 0x16
- +#define WUR_SET_INTERFACE_DS 0x17
- +
- +/* Feature numbers */
- +#define UF_ENDPOINT_HALT 0
- +#define UF_DEVICE_REMOTE_WAKEUP 1
- +#define UF_TEST_MODE 2
- +#define UF_DEVICE_B_HNP_ENABLE 3
- +#define UF_DEVICE_A_HNP_SUPPORT 4
- +#define UF_DEVICE_A_ALT_HNP_SUPPORT 5
- +#define WUF_WUSB 3
- +#define WUF_TX_DRPIE 0x0
- +#define WUF_DEV_XMIT_PACKET 0x1
- +#define WUF_COUNT_PACKETS 0x2
- +#define WUF_CAPTURE_PACKETS 0x3
- +#define UF_FUNCTION_SUSPEND 0
- +#define UF_U1_ENABLE 48
- +#define UF_U2_ENABLE 49
- +#define UF_LTM_ENABLE 50
- +
- +/* Class requests from the USB 2.0 hub spec, table 11-15 */
- +#define UCR_CLEAR_HUB_FEATURE (0x2000 | UR_CLEAR_FEATURE)
- +#define UCR_CLEAR_PORT_FEATURE (0x2300 | UR_CLEAR_FEATURE)
- +#define UCR_GET_HUB_DESCRIPTOR (0xa000 | UR_GET_DESCRIPTOR)
- +#define UCR_GET_HUB_STATUS (0xa000 | UR_GET_STATUS)
- +#define UCR_GET_PORT_STATUS (0xa300 | UR_GET_STATUS)
- +#define UCR_SET_HUB_FEATURE (0x2000 | UR_SET_FEATURE)
- +#define UCR_SET_PORT_FEATURE (0x2300 | UR_SET_FEATURE)
- +#define UCR_SET_AND_TEST_PORT_FEATURE (0xa300 | UR_SET_AND_TEST_FEATURE)
- +
- +#ifdef _MSC_VER
- +#include <pshpack1.h>
- +#endif
- +
- +typedef struct {
- + uByte bLength;
- + uByte bDescriptorType;
- + uByte bEndpointAddress;
- +#define UE_GET_DIR(a) ((a) & 0x80)
- +#define UE_SET_DIR(a,d) ((a) | (((d)&1) << 7))
- +#define UE_DIR_IN 0x80
- +#define UE_DIR_OUT 0x00
- +#define UE_ADDR 0x0f
- +#define UE_GET_ADDR(a) ((a) & UE_ADDR)
- + uByte bmAttributes;
- +#define UE_XFERTYPE 0x03
- +#define UE_CONTROL 0x00
- +#define UE_ISOCHRONOUS 0x01
- +#define UE_BULK 0x02
- +#define UE_INTERRUPT 0x03
- +#define UE_GET_XFERTYPE(a) ((a) & UE_XFERTYPE)
- +#define UE_ISO_TYPE 0x0c
- +#define UE_ISO_ASYNC 0x04
- +#define UE_ISO_ADAPT 0x08
- +#define UE_ISO_SYNC 0x0c
- +#define UE_GET_ISO_TYPE(a) ((a) & UE_ISO_TYPE)
- + uWord wMaxPacketSize;
- + uByte bInterval;
- +} UPACKED usb_endpoint_descriptor_t;
- +#define USB_ENDPOINT_DESCRIPTOR_SIZE 7
- +
- +/* Hub specific request */
- +#define UR_GET_BUS_STATE 0x02
- +#define UR_CLEAR_TT_BUFFER 0x08
- +#define UR_RESET_TT 0x09
- +#define UR_GET_TT_STATE 0x0a
- +#define UR_STOP_TT 0x0b
- +
- +/* Hub features */
- +#define UHF_C_HUB_LOCAL_POWER 0
- +#define UHF_C_HUB_OVER_CURRENT 1
- +#define UHF_PORT_CONNECTION 0
- +#define UHF_PORT_ENABLE 1
- +#define UHF_PORT_SUSPEND 2
- +#define UHF_PORT_OVER_CURRENT 3
- +#define UHF_PORT_RESET 4
- +#define UHF_PORT_L1 5
- +#define UHF_PORT_POWER 8
- +#define UHF_PORT_LOW_SPEED 9
- +#define UHF_PORT_HIGH_SPEED 10
- +#define UHF_C_PORT_CONNECTION 16
- +#define UHF_C_PORT_ENABLE 17
- +#define UHF_C_PORT_SUSPEND 18
- +#define UHF_C_PORT_OVER_CURRENT 19
- +#define UHF_C_PORT_RESET 20
- +#define UHF_C_PORT_L1 23
- +#define UHF_PORT_TEST 21
- +#define UHF_PORT_INDICATOR 22
- +
- +typedef struct {
- + uByte bDescLength;
- + uByte bDescriptorType;
- + uByte bNbrPorts;
- + uWord wHubCharacteristics;
- +#define UHD_PWR 0x0003
- +#define UHD_PWR_GANGED 0x0000
- +#define UHD_PWR_INDIVIDUAL 0x0001
- +#define UHD_PWR_NO_SWITCH 0x0002
- +#define UHD_COMPOUND 0x0004
- +#define UHD_OC 0x0018
- +#define UHD_OC_GLOBAL 0x0000
- +#define UHD_OC_INDIVIDUAL 0x0008
- +#define UHD_OC_NONE 0x0010
- +#define UHD_TT_THINK 0x0060
- +#define UHD_TT_THINK_8 0x0000
- +#define UHD_TT_THINK_16 0x0020
- +#define UHD_TT_THINK_24 0x0040
- +#define UHD_TT_THINK_32 0x0060
- +#define UHD_PORT_IND 0x0080
- + uByte bPwrOn2PwrGood; /* delay in 2 ms units */
- +#define UHD_PWRON_FACTOR 2
- + uByte bHubContrCurrent;
- + uByte DeviceRemovable[32]; /* max 255 ports */
- +#define UHD_NOT_REMOV(desc, i) \
- + (((desc)->DeviceRemovable[(i)/8] >> ((i) % 8)) & 1)
- + /* deprecated */ uByte PortPowerCtrlMask[1];
- +} UPACKED usb_hub_descriptor_t;
- +#define USB_HUB_DESCRIPTOR_SIZE 9 /* includes deprecated PortPowerCtrlMask */
- +
- +#ifdef _MSC_VER
- +#include <poppack.h>
- +#endif
- +
- +#ifdef __cplusplus
- +}
- +#endif
- +
- +#endif /* _USB_H_ */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/Makefile
- @@ -0,0 +1,85 @@
- +#
- +# Makefile for DWC_otg Highspeed USB controller driver
- +#
- +
- +ifneq ($(KERNELRELEASE),)
- +
- +# Use the BUS_INTERFACE variable to compile the software for either
- +# PCI(PCI_INTERFACE) or LM(LM_INTERFACE) bus.
- +ifeq ($(BUS_INTERFACE),)
- +# BUS_INTERFACE = -DPCI_INTERFACE
- +# BUS_INTERFACE = -DLM_INTERFACE
- + BUS_INTERFACE = -DPLATFORM_INTERFACE
- +endif
- +
- +#ccflags-y += -DDEBUG
- +#ccflags-y += -DDWC_OTG_DEBUGLEV=1 # reduce common debug msgs
- +
- +# Use one of the following flags to compile the software in host-only or
- +# device-only mode.
- +#ccflags-y += -DDWC_HOST_ONLY
- +#ccflags-y += -DDWC_DEVICE_ONLY
- +
- +ccflags-y += -Dlinux -DDWC_HS_ELECT_TST
- +#ccflags-y += -DDWC_EN_ISOC
- +ccflags-y += -I$(srctree)/drivers/usb/host/dwc_common_port
- +#ccflags-y += -I$(PORTLIB)
- +ccflags-y += -DDWC_LINUX
- +ccflags-y += $(CFI)
- +ccflags-y += $(BUS_INTERFACE)
- +#ccflags-y += -DDWC_DEV_SRPCAP
- +
- +obj-$(CONFIG_USB_DWCOTG) += dwc_otg.o
- +
- +dwc_otg-objs := dwc_otg_driver.o dwc_otg_attr.o
- +dwc_otg-objs += dwc_otg_cil.o dwc_otg_cil_intr.o
- +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_fiq_fsm.o
- +ifneq ($(CONFIG_ARM64),y)
- +dwc_otg-objs += dwc_otg_fiq_stub.o
- +endif
- +
- +ifneq ($(CFI),)
- +dwc_otg-objs += dwc_otg_cfi.o
- +endif
- +
- +kernrelwd := $(subst ., ,$(KERNELRELEASE))
- +kernrel3 := $(word 1,$(kernrelwd)).$(word 2,$(kernrelwd)).$(word 3,$(kernrelwd))
- +
- +ifneq ($(kernrel3),2.6.20)
- +ccflags-y += $(CPPFLAGS)
- +endif
- +
- +else
- +
- +PWD := $(shell pwd)
- +PORTLIB := $(PWD)/../dwc_common_port
- +
- +# Command paths
- +CTAGS := $(CTAGS)
- +DOXYGEN := $(DOXYGEN)
- +
- +default: portlib
- + $(MAKE) -C$(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules
- +
- +install: default
- + $(MAKE) -C$(KDIR) M=$(PORTLIB) modules_install
- + $(MAKE) -C$(KDIR) M=$(PWD) modules_install
- +
- +portlib:
- + $(MAKE) -C$(KDIR) M=$(PORTLIB) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules
- + cp $(PORTLIB)/Module.symvers $(PWD)/
- +
- +docs: $(wildcard *.[hc]) doc/doxygen.cfg
- + $(DOXYGEN) doc/doxygen.cfg
- +
- +tags: $(wildcard *.[hc])
- + $(CTAGS) -e $(wildcard *.[hc]) $(wildcard linux/*.[hc]) $(wildcard $(KDIR)/include/linux/usb*.h)
- +
- +
- +clean:
- + rm -rf *.o *.ko .*cmd *.mod.c .tmp_versions Module.symvers
- +
- +endif
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/doc/doxygen.cfg
- @@ -0,0 +1,224 @@
- +# Doxyfile 1.3.9.1
- +
- +#---------------------------------------------------------------------------
- +# Project related configuration options
- +#---------------------------------------------------------------------------
- +PROJECT_NAME = "DesignWare USB 2.0 OTG Controller (DWC_otg) Device Driver"
- +PROJECT_NUMBER = v3.00a
- +OUTPUT_DIRECTORY = ./doc/
- +CREATE_SUBDIRS = NO
- +OUTPUT_LANGUAGE = English
- +BRIEF_MEMBER_DESC = YES
- +REPEAT_BRIEF = YES
- +ABBREVIATE_BRIEF = "The $name class" \
- + "The $name widget" \
- + "The $name file" \
- + is \
- + provides \
- + specifies \
- + contains \
- + represents \
- + a \
- + an \
- + the
- +ALWAYS_DETAILED_SEC = NO
- +INLINE_INHERITED_MEMB = NO
- +FULL_PATH_NAMES = NO
- +STRIP_FROM_PATH =
- +STRIP_FROM_INC_PATH =
- +SHORT_NAMES = NO
- +JAVADOC_AUTOBRIEF = YES
- +MULTILINE_CPP_IS_BRIEF = NO
- +INHERIT_DOCS = YES
- +DISTRIBUTE_GROUP_DOC = NO
- +TAB_SIZE = 8
- +ALIASES =
- +OPTIMIZE_OUTPUT_FOR_C = YES
- +OPTIMIZE_OUTPUT_JAVA = NO
- +SUBGROUPING = YES
- +#---------------------------------------------------------------------------
- +# Build related configuration options
- +#---------------------------------------------------------------------------
- +EXTRACT_ALL = NO
- +EXTRACT_PRIVATE = YES
- +EXTRACT_STATIC = YES
- +EXTRACT_LOCAL_CLASSES = YES
- +EXTRACT_LOCAL_METHODS = NO
- +HIDE_UNDOC_MEMBERS = NO
- +HIDE_UNDOC_CLASSES = NO
- +HIDE_FRIEND_COMPOUNDS = NO
- +HIDE_IN_BODY_DOCS = NO
- +INTERNAL_DOCS = NO
- +CASE_SENSE_NAMES = NO
- +HIDE_SCOPE_NAMES = NO
- +SHOW_INCLUDE_FILES = YES
- +INLINE_INFO = YES
- +SORT_MEMBER_DOCS = NO
- +SORT_BRIEF_DOCS = NO
- +SORT_BY_SCOPE_NAME = NO
- +GENERATE_TODOLIST = YES
- +GENERATE_TESTLIST = YES
- +GENERATE_BUGLIST = YES
- +GENERATE_DEPRECATEDLIST= YES
- +ENABLED_SECTIONS =
- +MAX_INITIALIZER_LINES = 30
- +SHOW_USED_FILES = YES
- +SHOW_DIRECTORIES = YES
- +#---------------------------------------------------------------------------
- +# configuration options related to warning and progress messages
- +#---------------------------------------------------------------------------
- +QUIET = YES
- +WARNINGS = YES
- +WARN_IF_UNDOCUMENTED = NO
- +WARN_IF_DOC_ERROR = YES
- +WARN_FORMAT = "$file:$line: $text"
- +WARN_LOGFILE =
- +#---------------------------------------------------------------------------
- +# configuration options related to the input files
- +#---------------------------------------------------------------------------
- +INPUT = .
- +FILE_PATTERNS = *.c \
- + *.h \
- + ./linux/*.c \
- + ./linux/*.h
- +RECURSIVE = NO
- +EXCLUDE = ./test/ \
- + ./dwc_otg/.AppleDouble/
- +EXCLUDE_SYMLINKS = YES
- +EXCLUDE_PATTERNS = *.mod.*
- +EXAMPLE_PATH =
- +EXAMPLE_PATTERNS = *
- +EXAMPLE_RECURSIVE = NO
- +IMAGE_PATH =
- +INPUT_FILTER =
- +FILTER_PATTERNS =
- +FILTER_SOURCE_FILES = NO
- +#---------------------------------------------------------------------------
- +# configuration options related to source browsing
- +#---------------------------------------------------------------------------
- +SOURCE_BROWSER = YES
- +INLINE_SOURCES = NO
- +STRIP_CODE_COMMENTS = YES
- +REFERENCED_BY_RELATION = NO
- +REFERENCES_RELATION = NO
- +VERBATIM_HEADERS = NO
- +#---------------------------------------------------------------------------
- +# configuration options related to the alphabetical class index
- +#---------------------------------------------------------------------------
- +ALPHABETICAL_INDEX = NO
- +COLS_IN_ALPHA_INDEX = 5
- +IGNORE_PREFIX =
- +#---------------------------------------------------------------------------
- +# configuration options related to the HTML output
- +#---------------------------------------------------------------------------
- +GENERATE_HTML = YES
- +HTML_OUTPUT = html
- +HTML_FILE_EXTENSION = .html
- +HTML_HEADER =
- +HTML_FOOTER =
- +HTML_STYLESHEET =
- +HTML_ALIGN_MEMBERS = YES
- +GENERATE_HTMLHELP = NO
- +CHM_FILE =
- +HHC_LOCATION =
- +GENERATE_CHI = NO
- +BINARY_TOC = NO
- +TOC_EXPAND = NO
- +DISABLE_INDEX = NO
- +ENUM_VALUES_PER_LINE = 4
- +GENERATE_TREEVIEW = YES
- +TREEVIEW_WIDTH = 250
- +#---------------------------------------------------------------------------
- +# configuration options related to the LaTeX output
- +#---------------------------------------------------------------------------
- +GENERATE_LATEX = NO
- +LATEX_OUTPUT = latex
- +LATEX_CMD_NAME = latex
- +MAKEINDEX_CMD_NAME = makeindex
- +COMPACT_LATEX = NO
- +PAPER_TYPE = a4wide
- +EXTRA_PACKAGES =
- +LATEX_HEADER =
- +PDF_HYPERLINKS = NO
- +USE_PDFLATEX = NO
- +LATEX_BATCHMODE = NO
- +LATEX_HIDE_INDICES = NO
- +#---------------------------------------------------------------------------
- +# configuration options related to the RTF output
- +#---------------------------------------------------------------------------
- +GENERATE_RTF = NO
- +RTF_OUTPUT = rtf
- +COMPACT_RTF = NO
- +RTF_HYPERLINKS = NO
- +RTF_STYLESHEET_FILE =
- +RTF_EXTENSIONS_FILE =
- +#---------------------------------------------------------------------------
- +# configuration options related to the man page output
- +#---------------------------------------------------------------------------
- +GENERATE_MAN = NO
- +MAN_OUTPUT = man
- +MAN_EXTENSION = .3
- +MAN_LINKS = NO
- +#---------------------------------------------------------------------------
- +# configuration options related to the XML output
- +#---------------------------------------------------------------------------
- +GENERATE_XML = NO
- +XML_OUTPUT = xml
- +XML_SCHEMA =
- +XML_DTD =
- +XML_PROGRAMLISTING = YES
- +#---------------------------------------------------------------------------
- +# configuration options for the AutoGen Definitions output
- +#---------------------------------------------------------------------------
- +GENERATE_AUTOGEN_DEF = NO
- +#---------------------------------------------------------------------------
- +# configuration options related to the Perl module output
- +#---------------------------------------------------------------------------
- +GENERATE_PERLMOD = NO
- +PERLMOD_LATEX = NO
- +PERLMOD_PRETTY = YES
- +PERLMOD_MAKEVAR_PREFIX =
- +#---------------------------------------------------------------------------
- +# Configuration options related to the preprocessor
- +#---------------------------------------------------------------------------
- +ENABLE_PREPROCESSING = YES
- +MACRO_EXPANSION = YES
- +EXPAND_ONLY_PREDEF = YES
- +SEARCH_INCLUDES = YES
- +INCLUDE_PATH =
- +INCLUDE_FILE_PATTERNS =
- +PREDEFINED = DEVICE_ATTR DWC_EN_ISOC
- +EXPAND_AS_DEFINED = DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW DWC_OTG_DEVICE_ATTR_BITFIELD_STORE DWC_OTG_DEVICE_ATTR_BITFIELD_RW DWC_OTG_DEVICE_ATTR_BITFIELD_RO DWC_OTG_DEVICE_ATTR_REG_SHOW DWC_OTG_DEVICE_ATTR_REG_STORE DWC_OTG_DEVICE_ATTR_REG32_RW DWC_OTG_DEVICE_ATTR_REG32_RO DWC_EN_ISOC
- +SKIP_FUNCTION_MACROS = NO
- +#---------------------------------------------------------------------------
- +# Configuration::additions related to external references
- +#---------------------------------------------------------------------------
- +TAGFILES =
- +GENERATE_TAGFILE =
- +ALLEXTERNALS = NO
- +EXTERNAL_GROUPS = YES
- +PERL_PATH = /usr/bin/perl
- +#---------------------------------------------------------------------------
- +# Configuration options related to the dot tool
- +#---------------------------------------------------------------------------
- +CLASS_DIAGRAMS = YES
- +HIDE_UNDOC_RELATIONS = YES
- +HAVE_DOT = NO
- +CLASS_GRAPH = YES
- +COLLABORATION_GRAPH = YES
- +UML_LOOK = NO
- +TEMPLATE_RELATIONS = NO
- +INCLUDE_GRAPH = YES
- +INCLUDED_BY_GRAPH = YES
- +CALL_GRAPH = NO
- +GRAPHICAL_HIERARCHY = YES
- +DOT_IMAGE_FORMAT = png
- +DOT_PATH =
- +DOTFILE_DIRS =
- +MAX_DOT_GRAPH_DEPTH = 1000
- +GENERATE_LEGEND = YES
- +DOT_CLEANUP = YES
- +#---------------------------------------------------------------------------
- +# Configuration::additions related to the search engine
- +#---------------------------------------------------------------------------
- +SEARCHENGINE = NO
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dummy_audio.c
- @@ -0,0 +1,1574 @@
- +/*
- + * zero.c -- Gadget Zero, for USB development
- + *
- + * Copyright (C) 2003-2004 David Brownell
- + * All rights reserved.
- + *
- + * Redistribution and use in source and binary forms, with or without
- + * modification, are permitted provided that the following conditions
- + * are met:
- + * 1. Redistributions of source code must retain the above copyright
- + * notice, this list of conditions, and the following disclaimer,
- + * without modification.
- + * 2. 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.
- + * 3. The names of the above-listed copyright holders may not be used
- + * to endorse or promote products derived from this software without
- + * specific prior written permission.
- + *
- + * ALTERNATIVELY, this software may be distributed under the terms of the
- + * GNU General Public License ("GPL") as published by the Free Software
- + * Foundation, either version 2 of that License or (at your option) any
- + * later version.
- + *
- + * 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 THE COPYRIGHT OWNER OR
- + * CONTRIBUTORS 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.
- + */
- +
- +
- +/*
- + * Gadget Zero only needs two bulk endpoints, and is an example of how you
- + * can write a hardware-agnostic gadget driver running inside a USB device.
- + *
- + * Hardware details are visible (see CONFIG_USB_ZERO_* below) but don't
- + * affect most of the driver.
- + *
- + * Use it with the Linux host/master side "usbtest" driver to get a basic
- + * functional test of your device-side usb stack, or with "usb-skeleton".
- + *
- + * It supports two similar configurations. One sinks whatever the usb host
- + * writes, and in return sources zeroes. The other loops whatever the host
- + * writes back, so the host can read it. Module options include:
- + *
- + * buflen=N default N=4096, buffer size used
- + * qlen=N default N=32, how many buffers in the loopback queue
- + * loopdefault default false, list loopback config first
- + *
- + * Many drivers will only have one configuration, letting them be much
- + * simpler if they also don't support high speed operation (like this
- + * driver does).
- + */
- +
- +#include <linux/config.h>
- +#include <linux/module.h>
- +#include <linux/kernel.h>
- +#include <linux/delay.h>
- +#include <linux/ioport.h>
- +#include <linux/sched.h>
- +#include <linux/slab.h>
- +#include <linux/smp_lock.h>
- +#include <linux/errno.h>
- +#include <linux/init.h>
- +#include <linux/timer.h>
- +#include <linux/list.h>
- +#include <linux/interrupt.h>
- +#include <linux/uts.h>
- +#include <linux/version.h>
- +#include <linux/device.h>
- +#include <linux/moduleparam.h>
- +#include <linux/proc_fs.h>
- +
- +#include <asm/byteorder.h>
- +#include <asm/io.h>
- +#include <asm/irq.h>
- +#include <asm/system.h>
- +#include <asm/unaligned.h>
- +
- +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
- +# include <linux/usb/ch9.h>
- +#else
- +# include <linux/usb_ch9.h>
- +#endif
- +
- +#include <linux/usb_gadget.h>
- +
- +
- +/*-------------------------------------------------------------------------*/
- +/*-------------------------------------------------------------------------*/
- +
- +
- +static int utf8_to_utf16le(const char *s, u16 *cp, unsigned len)
- +{
- + int count = 0;
- + u8 c;
- + u16 uchar;
- +
- + /* this insists on correct encodings, though not minimal ones.
- + * BUT it currently rejects legit 4-byte UTF-8 code points,
- + * which need surrogate pairs. (Unicode 3.1 can use them.)
- + */
- + while (len != 0 && (c = (u8) *s++) != 0) {
- + if (unlikely(c & 0x80)) {
- + // 2-byte sequence:
- + // 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx
- + if ((c & 0xe0) == 0xc0) {
- + uchar = (c & 0x1f) << 6;
- +
- + c = (u8) *s++;
- + if ((c & 0xc0) != 0xc0)
- + goto fail;
- + c &= 0x3f;
- + uchar |= c;
- +
- + // 3-byte sequence (most CJKV characters):
- + // zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx
- + } else if ((c & 0xf0) == 0xe0) {
- + uchar = (c & 0x0f) << 12;
- +
- + c = (u8) *s++;
- + if ((c & 0xc0) != 0xc0)
- + goto fail;
- + c &= 0x3f;
- + uchar |= c << 6;
- +
- + c = (u8) *s++;
- + if ((c & 0xc0) != 0xc0)
- + goto fail;
- + c &= 0x3f;
- + uchar |= c;
- +
- + /* no bogus surrogates */
- + if (0xd800 <= uchar && uchar <= 0xdfff)
- + goto fail;
- +
- + // 4-byte sequence (surrogate pairs, currently rare):
- + // 11101110wwwwzzzzyy + 110111yyyyxxxxxx
- + // = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
- + // (uuuuu = wwww + 1)
- + // FIXME accept the surrogate code points (only)
- +
- + } else
- + goto fail;
- + } else
- + uchar = c;
- + put_unaligned (cpu_to_le16 (uchar), cp++);
- + count++;
- + len--;
- + }
- + return count;
- +fail:
- + return -1;
- +}
- +
- +
- +/**
- + * usb_gadget_get_string - fill out a string descriptor
- + * @table: of c strings encoded using UTF-8
- + * @id: string id, from low byte of wValue in get string descriptor
- + * @buf: at least 256 bytes
- + *
- + * Finds the UTF-8 string matching the ID, and converts it into a
- + * string descriptor in utf16-le.
- + * Returns length of descriptor (always even) or negative errno
- + *
- + * If your driver needs stings in multiple languages, you'll probably
- + * "switch (wIndex) { ... }" in your ep0 string descriptor logic,
- + * using this routine after choosing which set of UTF-8 strings to use.
- + * Note that US-ASCII is a strict subset of UTF-8; any string bytes with
- + * the eighth bit set will be multibyte UTF-8 characters, not ISO-8859/1
- + * characters (which are also widely used in C strings).
- + */
- +int
- +usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf)
- +{
- + struct usb_string *s;
- + int len;
- +
- + /* descriptor 0 has the language id */
- + if (id == 0) {
- + buf [0] = 4;
- + buf [1] = USB_DT_STRING;
- + buf [2] = (u8) table->language;
- + buf [3] = (u8) (table->language >> 8);
- + return 4;
- + }
- + for (s = table->strings; s && s->s; s++)
- + if (s->id == id)
- + break;
- +
- + /* unrecognized: stall. */
- + if (!s || !s->s)
- + return -EINVAL;
- +
- + /* string descriptors have length, tag, then UTF16-LE text */
- + len = min ((size_t) 126, strlen (s->s));
- + memset (buf + 2, 0, 2 * len); /* zero all the bytes */
- + len = utf8_to_utf16le(s->s, (u16 *)&buf[2], len);
- + if (len < 0)
- + return -EINVAL;
- + buf [0] = (len + 1) * 2;
- + buf [1] = USB_DT_STRING;
- + return buf [0];
- +}
- +
- +
- +/*-------------------------------------------------------------------------*/
- +/*-------------------------------------------------------------------------*/
- +
- +
- +/**
- + * usb_descriptor_fillbuf - fill buffer with descriptors
- + * @buf: Buffer to be filled
- + * @buflen: Size of buf
- + * @src: Array of descriptor pointers, terminated by null pointer.
- + *
- + * Copies descriptors into the buffer, returning the length or a
- + * negative error code if they can't all be copied. Useful when
- + * assembling descriptors for an associated set of interfaces used
- + * as part of configuring a composite device; or in other cases where
- + * sets of descriptors need to be marshaled.
- + */
- +int
- +usb_descriptor_fillbuf(void *buf, unsigned buflen,
- + const struct usb_descriptor_header **src)
- +{
- + u8 *dest = buf;
- +
- + if (!src)
- + return -EINVAL;
- +
- + /* fill buffer from src[] until null descriptor ptr */
- + for (; 0 != *src; src++) {
- + unsigned len = (*src)->bLength;
- +
- + if (len > buflen)
- + return -EINVAL;
- + memcpy(dest, *src, len);
- + buflen -= len;
- + dest += len;
- + }
- + return dest - (u8 *)buf;
- +}
- +
- +
- +/**
- + * usb_gadget_config_buf - builts a complete configuration descriptor
- + * @config: Header for the descriptor, including characteristics such
- + * as power requirements and number of interfaces.
- + * @desc: Null-terminated vector of pointers to the descriptors (interface,
- + * endpoint, etc) defining all functions in this device configuration.
- + * @buf: Buffer for the resulting configuration descriptor.
- + * @length: Length of buffer. If this is not big enough to hold the
- + * entire configuration descriptor, an error code will be returned.
- + *
- + * This copies descriptors into the response buffer, building a descriptor
- + * for that configuration. It returns the buffer length or a negative
- + * status code. The config.wTotalLength field is set to match the length
- + * of the result, but other descriptor fields (including power usage and
- + * interface count) must be set by the caller.
- + *
- + * Gadget drivers could use this when constructing a config descriptor
- + * in response to USB_REQ_GET_DESCRIPTOR. They will need to patch the
- + * resulting bDescriptorType value if USB_DT_OTHER_SPEED_CONFIG is needed.
- + */
- +int usb_gadget_config_buf(
- + const struct usb_config_descriptor *config,
- + void *buf,
- + unsigned length,
- + const struct usb_descriptor_header **desc
- +)
- +{
- + struct usb_config_descriptor *cp = buf;
- + int len;
- +
- + /* config descriptor first */
- + if (length < USB_DT_CONFIG_SIZE || !desc)
- + return -EINVAL;
- + *cp = *config;
- +
- + /* then interface/endpoint/class/vendor/... */
- + len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8*)buf,
- + length - USB_DT_CONFIG_SIZE, desc);
- + if (len < 0)
- + return len;
- + len += USB_DT_CONFIG_SIZE;
- + if (len > 0xffff)
- + return -EINVAL;
- +
- + /* patch up the config descriptor */
- + cp->bLength = USB_DT_CONFIG_SIZE;
- + cp->bDescriptorType = USB_DT_CONFIG;
- + cp->wTotalLength = cpu_to_le16(len);
- + cp->bmAttributes |= USB_CONFIG_ATT_ONE;
- + return len;
- +}
- +
- +/*-------------------------------------------------------------------------*/
- +/*-------------------------------------------------------------------------*/
- +
- +
- +#define RBUF_LEN (1024*1024)
- +static int rbuf_start;
- +static int rbuf_len;
- +static __u8 rbuf[RBUF_LEN];
- +
- +/*-------------------------------------------------------------------------*/
- +
- +#define DRIVER_VERSION "St Patrick's Day 2004"
- +
- +static const char shortname [] = "zero";
- +static const char longname [] = "YAMAHA YST-MS35D USB Speaker ";
- +
- +static const char source_sink [] = "source and sink data";
- +static const char loopback [] = "loop input to output";
- +
- +/*-------------------------------------------------------------------------*/
- +
- +/*
- + * driver assumes self-powered hardware, and
- + * has no way for users to trigger remote wakeup.
- + *
- + * this version autoconfigures as much as possible,
- + * which is reasonable for most "bulk-only" drivers.
- + */
- +static const char *EP_IN_NAME; /* source */
- +static const char *EP_OUT_NAME; /* sink */
- +
- +/*-------------------------------------------------------------------------*/
- +
- +/* big enough to hold our biggest descriptor */
- +#define USB_BUFSIZ 512
- +
- +struct zero_dev {
- + spinlock_t lock;
- + struct usb_gadget *gadget;
- + struct usb_request *req; /* for control responses */
- +
- + /* when configured, we have one of two configs:
- + * - source data (in to host) and sink it (out from host)
- + * - or loop it back (out from host back in to host)
- + */
- + u8 config;
- + struct usb_ep *in_ep, *out_ep;
- +
- + /* autoresume timer */
- + struct timer_list resume;
- +};
- +
- +#define xprintk(d,level,fmt,args...) \
- + dev_printk(level , &(d)->gadget->dev , fmt , ## args)
- +
- +#ifdef DEBUG
- +#define DBG(dev,fmt,args...) \
- + xprintk(dev , KERN_DEBUG , fmt , ## args)
- +#else
- +#define DBG(dev,fmt,args...) \
- + do { } while (0)
- +#endif /* DEBUG */
- +
- +#ifdef VERBOSE
- +#define VDBG DBG
- +#else
- +#define VDBG(dev,fmt,args...) \
- + do { } while (0)
- +#endif /* VERBOSE */
- +
- +#define ERROR(dev,fmt,args...) \
- + xprintk(dev , KERN_ERR , fmt , ## args)
- +#define WARN(dev,fmt,args...) \
- + xprintk(dev , KERN_WARNING , fmt , ## args)
- +#define INFO(dev,fmt,args...) \
- + xprintk(dev , KERN_INFO , fmt , ## args)
- +
- +/*-------------------------------------------------------------------------*/
- +
- +static unsigned buflen = 4096;
- +static unsigned qlen = 32;
- +static unsigned pattern = 0;
- +
- +module_param (buflen, uint, S_IRUGO|S_IWUSR);
- +module_param (qlen, uint, S_IRUGO|S_IWUSR);
- +module_param (pattern, uint, S_IRUGO|S_IWUSR);
- +
- +/*
- + * if it's nonzero, autoresume says how many seconds to wait
- + * before trying to wake up the host after suspend.
- + */
- +static unsigned autoresume = 0;
- +module_param (autoresume, uint, 0);
- +
- +/*
- + * Normally the "loopback" configuration is second (index 1) so
- + * it's not the default. Here's where to change that order, to
- + * work better with hosts where config changes are problematic.
- + * Or controllers (like superh) that only support one config.
- + */
- +static int loopdefault = 0;
- +
- +module_param (loopdefault, bool, S_IRUGO|S_IWUSR);
- +
- +/*-------------------------------------------------------------------------*/
- +
- +/* Thanks to NetChip Technologies for donating this product ID.
- + *
- + * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
- + * Instead: allocate your own, using normal USB-IF procedures.
- + */
- +#ifndef CONFIG_USB_ZERO_HNPTEST
- +#define DRIVER_VENDOR_NUM 0x0525 /* NetChip */
- +#define DRIVER_PRODUCT_NUM 0xa4a0 /* Linux-USB "Gadget Zero" */
- +#else
- +#define DRIVER_VENDOR_NUM 0x1a0a /* OTG test device IDs */
- +#define DRIVER_PRODUCT_NUM 0xbadd
- +#endif
- +
- +/*-------------------------------------------------------------------------*/
- +
- +/*
- + * DESCRIPTORS ... most are static, but strings and (full)
- + * configuration descriptors are built on demand.
- + */
- +
- +/*
- +#define STRING_MANUFACTURER 25
- +#define STRING_PRODUCT 42
- +#define STRING_SERIAL 101
- +*/
- +#define STRING_MANUFACTURER 1
- +#define STRING_PRODUCT 2
- +#define STRING_SERIAL 3
- +
- +#define STRING_SOURCE_SINK 250
- +#define STRING_LOOPBACK 251
- +
- +/*
- + * This device advertises two configurations; these numbers work
- + * on a pxa250 as well as more flexible hardware.
- + */
- +#define CONFIG_SOURCE_SINK 3
- +#define CONFIG_LOOPBACK 2
- +
- +/*
- +static struct usb_device_descriptor
- +device_desc = {
- + .bLength = sizeof device_desc,
- + .bDescriptorType = USB_DT_DEVICE,
- +
- + .bcdUSB = __constant_cpu_to_le16 (0x0200),
- + .bDeviceClass = USB_CLASS_VENDOR_SPEC,
- +
- + .idVendor = __constant_cpu_to_le16 (DRIVER_VENDOR_NUM),
- + .idProduct = __constant_cpu_to_le16 (DRIVER_PRODUCT_NUM),
- + .iManufacturer = STRING_MANUFACTURER,
- + .iProduct = STRING_PRODUCT,
- + .iSerialNumber = STRING_SERIAL,
- + .bNumConfigurations = 2,
- +};
- +*/
- +static struct usb_device_descriptor
- +device_desc = {
- + .bLength = sizeof device_desc,
- + .bDescriptorType = USB_DT_DEVICE,
- + .bcdUSB = __constant_cpu_to_le16 (0x0100),
- + .bDeviceClass = USB_CLASS_PER_INTERFACE,
- + .bDeviceSubClass = 0,
- + .bDeviceProtocol = 0,
- + .bMaxPacketSize0 = 64,
- + .bcdDevice = __constant_cpu_to_le16 (0x0100),
- + .idVendor = __constant_cpu_to_le16 (0x0499),
- + .idProduct = __constant_cpu_to_le16 (0x3002),
- + .iManufacturer = STRING_MANUFACTURER,
- + .iProduct = STRING_PRODUCT,
- + .iSerialNumber = STRING_SERIAL,
- + .bNumConfigurations = 1,
- +};
- +
- +static struct usb_config_descriptor
- +z_config = {
- + .bLength = sizeof z_config,
- + .bDescriptorType = USB_DT_CONFIG,
- +
- + /* compute wTotalLength on the fly */
- + .bNumInterfaces = 2,
- + .bConfigurationValue = 1,
- + .iConfiguration = 0,
- + .bmAttributes = 0x40,
- + .bMaxPower = 0, /* self-powered */
- +};
- +
- +
- +static struct usb_otg_descriptor
- +otg_descriptor = {
- + .bLength = sizeof otg_descriptor,
- + .bDescriptorType = USB_DT_OTG,
- +
- + .bmAttributes = USB_OTG_SRP,
- +};
- +
- +/* one interface in each configuration */
- +#ifdef CONFIG_USB_GADGET_DUALSPEED
- +
- +/*
- + * usb 2.0 devices need to expose both high speed and full speed
- + * descriptors, unless they only run at full speed.
- + *
- + * that means alternate endpoint descriptors (bigger packets)
- + * and a "device qualifier" ... plus more construction options
- + * for the config descriptor.
- + */
- +
- +static struct usb_qualifier_descriptor
- +dev_qualifier = {
- + .bLength = sizeof dev_qualifier,
- + .bDescriptorType = USB_DT_DEVICE_QUALIFIER,
- +
- + .bcdUSB = __constant_cpu_to_le16 (0x0200),
- + .bDeviceClass = USB_CLASS_VENDOR_SPEC,
- +
- + .bNumConfigurations = 2,
- +};
- +
- +
- +struct usb_cs_as_general_descriptor {
- + __u8 bLength;
- + __u8 bDescriptorType;
- +
- + __u8 bDescriptorSubType;
- + __u8 bTerminalLink;
- + __u8 bDelay;
- + __u16 wFormatTag;
- +} __attribute__ ((packed));
- +
- +struct usb_cs_as_format_descriptor {
- + __u8 bLength;
- + __u8 bDescriptorType;
- +
- + __u8 bDescriptorSubType;
- + __u8 bFormatType;
- + __u8 bNrChannels;
- + __u8 bSubframeSize;
- + __u8 bBitResolution;
- + __u8 bSamfreqType;
- + __u8 tLowerSamFreq[3];
- + __u8 tUpperSamFreq[3];
- +} __attribute__ ((packed));
- +
- +static const struct usb_interface_descriptor
- +z_audio_control_if_desc = {
- + .bLength = sizeof z_audio_control_if_desc,
- + .bDescriptorType = USB_DT_INTERFACE,
- + .bInterfaceNumber = 0,
- + .bAlternateSetting = 0,
- + .bNumEndpoints = 0,
- + .bInterfaceClass = USB_CLASS_AUDIO,
- + .bInterfaceSubClass = 0x1,
- + .bInterfaceProtocol = 0,
- + .iInterface = 0,
- +};
- +
- +static const struct usb_interface_descriptor
- +z_audio_if_desc = {
- + .bLength = sizeof z_audio_if_desc,
- + .bDescriptorType = USB_DT_INTERFACE,
- + .bInterfaceNumber = 1,
- + .bAlternateSetting = 0,
- + .bNumEndpoints = 0,
- + .bInterfaceClass = USB_CLASS_AUDIO,
- + .bInterfaceSubClass = 0x2,
- + .bInterfaceProtocol = 0,
- + .iInterface = 0,
- +};
- +
- +static const struct usb_interface_descriptor
- +z_audio_if_desc2 = {
- + .bLength = sizeof z_audio_if_desc,
- + .bDescriptorType = USB_DT_INTERFACE,
- + .bInterfaceNumber = 1,
- + .bAlternateSetting = 1,
- + .bNumEndpoints = 1,
- + .bInterfaceClass = USB_CLASS_AUDIO,
- + .bInterfaceSubClass = 0x2,
- + .bInterfaceProtocol = 0,
- + .iInterface = 0,
- +};
- +
- +static const struct usb_cs_as_general_descriptor
- +z_audio_cs_as_if_desc = {
- + .bLength = 7,
- + .bDescriptorType = 0x24,
- +
- + .bDescriptorSubType = 0x01,
- + .bTerminalLink = 0x01,
- + .bDelay = 0x0,
- + .wFormatTag = __constant_cpu_to_le16 (0x0001)
- +};
- +
- +
- +static const struct usb_cs_as_format_descriptor
- +z_audio_cs_as_format_desc = {
- + .bLength = 0xe,
- + .bDescriptorType = 0x24,
- +
- + .bDescriptorSubType = 2,
- + .bFormatType = 1,
- + .bNrChannels = 1,
- + .bSubframeSize = 1,
- + .bBitResolution = 8,
- + .bSamfreqType = 0,
- + .tLowerSamFreq = {0x7e, 0x13, 0x00},
- + .tUpperSamFreq = {0xe2, 0xd6, 0x00},
- +};
- +
- +static const struct usb_endpoint_descriptor
- +z_iso_ep = {
- + .bLength = 0x09,
- + .bDescriptorType = 0x05,
- + .bEndpointAddress = 0x04,
- + .bmAttributes = 0x09,
- + .wMaxPacketSize = 0x0038,
- + .bInterval = 0x01,
- + .bRefresh = 0x00,
- + .bSynchAddress = 0x00,
- +};
- +
- +static char z_iso_ep2[] = {0x07, 0x25, 0x01, 0x00, 0x02, 0x00, 0x02};
- +
- +// 9 bytes
- +static char z_ac_interface_header_desc[] =
- +{ 0x09, 0x24, 0x01, 0x00, 0x01, 0x2b, 0x00, 0x01, 0x01 };
- +
- +// 12 bytes
- +static char z_0[] = {0x0c, 0x24, 0x02, 0x01, 0x01, 0x01, 0x00, 0x02,
- + 0x03, 0x00, 0x00, 0x00};
- +// 13 bytes
- +static char z_1[] = {0x0d, 0x24, 0x06, 0x02, 0x01, 0x02, 0x15, 0x00,
- + 0x02, 0x00, 0x02, 0x00, 0x00};
- +// 9 bytes
- +static char z_2[] = {0x09, 0x24, 0x03, 0x03, 0x01, 0x03, 0x00, 0x02,
- + 0x00};
- +
- +static char za_0[] = {0x09, 0x04, 0x01, 0x02, 0x01, 0x01, 0x02, 0x00,
- + 0x00};
- +
- +static char za_1[] = {0x07, 0x24, 0x01, 0x01, 0x00, 0x01, 0x00};
- +
- +static char za_2[] = {0x0e, 0x24, 0x02, 0x01, 0x02, 0x01, 0x08, 0x00,
- + 0x7e, 0x13, 0x00, 0xe2, 0xd6, 0x00};
- +
- +static char za_3[] = {0x09, 0x05, 0x04, 0x09, 0x70, 0x00, 0x01, 0x00,
- + 0x00};
- +
- +static char za_4[] = {0x07, 0x25, 0x01, 0x00, 0x02, 0x00, 0x02};
- +
- +static char za_5[] = {0x09, 0x04, 0x01, 0x03, 0x01, 0x01, 0x02, 0x00,
- + 0x00};
- +
- +static char za_6[] = {0x07, 0x24, 0x01, 0x01, 0x00, 0x01, 0x00};
- +
- +static char za_7[] = {0x0e, 0x24, 0x02, 0x01, 0x01, 0x02, 0x10, 0x00,
- + 0x7e, 0x13, 0x00, 0xe2, 0xd6, 0x00};
- +
- +static char za_8[] = {0x09, 0x05, 0x04, 0x09, 0x70, 0x00, 0x01, 0x00,
- + 0x00};
- +
- +static char za_9[] = {0x07, 0x25, 0x01, 0x00, 0x02, 0x00, 0x02};
- +
- +static char za_10[] = {0x09, 0x04, 0x01, 0x04, 0x01, 0x01, 0x02, 0x00,
- + 0x00};
- +
- +static char za_11[] = {0x07, 0x24, 0x01, 0x01, 0x00, 0x01, 0x00};
- +
- +static char za_12[] = {0x0e, 0x24, 0x02, 0x01, 0x02, 0x02, 0x10, 0x00,
- + 0x73, 0x13, 0x00, 0xe2, 0xd6, 0x00};
- +
- +static char za_13[] = {0x09, 0x05, 0x04, 0x09, 0xe0, 0x00, 0x01, 0x00,
- + 0x00};
- +
- +static char za_14[] = {0x07, 0x25, 0x01, 0x00, 0x02, 0x00, 0x02};
- +
- +static char za_15[] = {0x09, 0x04, 0x01, 0x05, 0x01, 0x01, 0x02, 0x00,
- + 0x00};
- +
- +static char za_16[] = {0x07, 0x24, 0x01, 0x01, 0x00, 0x01, 0x00};
- +
- +static char za_17[] = {0x0e, 0x24, 0x02, 0x01, 0x01, 0x03, 0x14, 0x00,
- + 0x7e, 0x13, 0x00, 0xe2, 0xd6, 0x00};
- +
- +static char za_18[] = {0x09, 0x05, 0x04, 0x09, 0xa8, 0x00, 0x01, 0x00,
- + 0x00};
- +
- +static char za_19[] = {0x07, 0x25, 0x01, 0x00, 0x02, 0x00, 0x02};
- +
- +static char za_20[] = {0x09, 0x04, 0x01, 0x06, 0x01, 0x01, 0x02, 0x00,
- + 0x00};
- +
- +static char za_21[] = {0x07, 0x24, 0x01, 0x01, 0x00, 0x01, 0x00};
- +
- +static char za_22[] = {0x0e, 0x24, 0x02, 0x01, 0x02, 0x03, 0x14, 0x00,
- + 0x7e, 0x13, 0x00, 0xe2, 0xd6, 0x00};
- +
- +static char za_23[] = {0x09, 0x05, 0x04, 0x09, 0x50, 0x01, 0x01, 0x00,
- + 0x00};
- +
- +static char za_24[] = {0x07, 0x25, 0x01, 0x00, 0x02, 0x00, 0x02};
- +
- +
- +
- +static const struct usb_descriptor_header *z_function [] = {
- + (struct usb_descriptor_header *) &z_audio_control_if_desc,
- + (struct usb_descriptor_header *) &z_ac_interface_header_desc,
- + (struct usb_descriptor_header *) &z_0,
- + (struct usb_descriptor_header *) &z_1,
- + (struct usb_descriptor_header *) &z_2,
- + (struct usb_descriptor_header *) &z_audio_if_desc,
- + (struct usb_descriptor_header *) &z_audio_if_desc2,
- + (struct usb_descriptor_header *) &z_audio_cs_as_if_desc,
- + (struct usb_descriptor_header *) &z_audio_cs_as_format_desc,
- + (struct usb_descriptor_header *) &z_iso_ep,
- + (struct usb_descriptor_header *) &z_iso_ep2,
- + (struct usb_descriptor_header *) &za_0,
- + (struct usb_descriptor_header *) &za_1,
- + (struct usb_descriptor_header *) &za_2,
- + (struct usb_descriptor_header *) &za_3,
- + (struct usb_descriptor_header *) &za_4,
- + (struct usb_descriptor_header *) &za_5,
- + (struct usb_descriptor_header *) &za_6,
- + (struct usb_descriptor_header *) &za_7,
- + (struct usb_descriptor_header *) &za_8,
- + (struct usb_descriptor_header *) &za_9,
- + (struct usb_descriptor_header *) &za_10,
- + (struct usb_descriptor_header *) &za_11,
- + (struct usb_descriptor_header *) &za_12,
- + (struct usb_descriptor_header *) &za_13,
- + (struct usb_descriptor_header *) &za_14,
- + (struct usb_descriptor_header *) &za_15,
- + (struct usb_descriptor_header *) &za_16,
- + (struct usb_descriptor_header *) &za_17,
- + (struct usb_descriptor_header *) &za_18,
- + (struct usb_descriptor_header *) &za_19,
- + (struct usb_descriptor_header *) &za_20,
- + (struct usb_descriptor_header *) &za_21,
- + (struct usb_descriptor_header *) &za_22,
- + (struct usb_descriptor_header *) &za_23,
- + (struct usb_descriptor_header *) &za_24,
- + NULL,
- +};
- +
- +/* maxpacket and other transfer characteristics vary by speed. */
- +#define ep_desc(g,hs,fs) (((g)->speed==USB_SPEED_HIGH)?(hs):(fs))
- +
- +#else
- +
- +/* if there's no high speed support, maxpacket doesn't change. */
- +#define ep_desc(g,hs,fs) fs
- +
- +#endif /* !CONFIG_USB_GADGET_DUALSPEED */
- +
- +static char manufacturer [40];
- +//static char serial [40];
- +static char serial [] = "Ser 00 em";
- +
- +/* static strings, in UTF-8 */
- +static struct usb_string strings [] = {
- + { STRING_MANUFACTURER, manufacturer, },
- + { STRING_PRODUCT, longname, },
- + { STRING_SERIAL, serial, },
- + { STRING_LOOPBACK, loopback, },
- + { STRING_SOURCE_SINK, source_sink, },
- + { } /* end of list */
- +};
- +
- +static struct usb_gadget_strings stringtab = {
- + .language = 0x0409, /* en-us */
- + .strings = strings,
- +};
- +
- +/*
- + * config descriptors are also handcrafted. these must agree with code
- + * that sets configurations, and with code managing interfaces and their
- + * altsettings. other complexity may come from:
- + *
- + * - high speed support, including "other speed config" rules
- + * - multiple configurations
- + * - interfaces with alternate settings
- + * - embedded class or vendor-specific descriptors
- + *
- + * this handles high speed, and has a second config that could as easily
- + * have been an alternate interface setting (on most hardware).
- + *
- + * NOTE: to demonstrate (and test) more USB capabilities, this driver
- + * should include an altsetting to test interrupt transfers, including
- + * high bandwidth modes at high speed. (Maybe work like Intel's test
- + * device?)
- + */
- +static int
- +config_buf (struct usb_gadget *gadget, u8 *buf, u8 type, unsigned index)
- +{
- + int len;
- + const struct usb_descriptor_header **function;
- +
- + function = z_function;
- + len = usb_gadget_config_buf (&z_config, buf, USB_BUFSIZ, function);
- + if (len < 0)
- + return len;
- + ((struct usb_config_descriptor *) buf)->bDescriptorType = type;
- + return len;
- +}
- +
- +/*-------------------------------------------------------------------------*/
- +
- +static struct usb_request *
- +alloc_ep_req (struct usb_ep *ep, unsigned length)
- +{
- + struct usb_request *req;
- +
- + req = usb_ep_alloc_request (ep, GFP_ATOMIC);
- + if (req) {
- + req->length = length;
- + req->buf = usb_ep_alloc_buffer (ep, length,
- + &req->dma, GFP_ATOMIC);
- + if (!req->buf) {
- + usb_ep_free_request (ep, req);
- + req = NULL;
- + }
- + }
- + return req;
- +}
- +
- +static void free_ep_req (struct usb_ep *ep, struct usb_request *req)
- +{
- + if (req->buf)
- + usb_ep_free_buffer (ep, req->buf, req->dma, req->length);
- + usb_ep_free_request (ep, req);
- +}
- +
- +/*-------------------------------------------------------------------------*/
- +
- +/* optionally require specific source/sink data patterns */
- +
- +static int
- +check_read_data (
- + struct zero_dev *dev,
- + struct usb_ep *ep,
- + struct usb_request *req
- +)
- +{
- + unsigned i;
- + u8 *buf = req->buf;
- +
- + for (i = 0; i < req->actual; i++, buf++) {
- + switch (pattern) {
- + /* all-zeroes has no synchronization issues */
- + case 0:
- + if (*buf == 0)
- + continue;
- + break;
- + /* mod63 stays in sync with short-terminated transfers,
- + * or otherwise when host and gadget agree on how large
- + * each usb transfer request should be. resync is done
- + * with set_interface or set_config.
- + */
- + case 1:
- + if (*buf == (u8)(i % 63))
- + continue;
- + break;
- + }
- + ERROR (dev, "bad OUT byte, buf [%d] = %d\n", i, *buf);
- + usb_ep_set_halt (ep);
- + return -EINVAL;
- + }
- + return 0;
- +}
- +
- +/*-------------------------------------------------------------------------*/
- +
- +static void zero_reset_config (struct zero_dev *dev)
- +{
- + if (dev->config == 0)
- + return;
- +
- + DBG (dev, "reset config\n");
- +
- + /* just disable endpoints, forcing completion of pending i/o.
- + * all our completion handlers free their requests in this case.
- + */
- + if (dev->in_ep) {
- + usb_ep_disable (dev->in_ep);
- + dev->in_ep = NULL;
- + }
- + if (dev->out_ep) {
- + usb_ep_disable (dev->out_ep);
- + dev->out_ep = NULL;
- + }
- + dev->config = 0;
- + del_timer (&dev->resume);
- +}
- +
- +#define _write(f, buf, sz) (f->f_op->write(f, buf, sz, &f->f_pos))
- +
- +static void
- +zero_isoc_complete (struct usb_ep *ep, struct usb_request *req)
- +{
- + struct zero_dev *dev = ep->driver_data;
- + int status = req->status;
- + int i, j;
- +
- + switch (status) {
- +
- + case 0: /* normal completion? */
- + //printk ("\nzero ---------------> isoc normal completion %d bytes\n", req->actual);
- + for (i=0, j=rbuf_start; i<req->actual; i++) {
- + //printk ("%02x ", ((__u8*)req->buf)[i]);
- + rbuf[j] = ((__u8*)req->buf)[i];
- + j++;
- + if (j >= RBUF_LEN) j=0;
- + }
- + rbuf_start = j;
- + //printk ("\n\n");
- +
- + if (rbuf_len < RBUF_LEN) {
- + rbuf_len += req->actual;
- + if (rbuf_len > RBUF_LEN) {
- + rbuf_len = RBUF_LEN;
- + }
- + }
- +
- + break;
- +
- + /* this endpoint is normally active while we're configured */
- + case -ECONNABORTED: /* hardware forced ep reset */
- + case -ECONNRESET: /* request dequeued */
- + case -ESHUTDOWN: /* disconnect from host */
- + VDBG (dev, "%s gone (%d), %d/%d\n", ep->name, status,
- + req->actual, req->length);
- + if (ep == dev->out_ep)
- + check_read_data (dev, ep, req);
- + free_ep_req (ep, req);
- + return;
- +
- + case -EOVERFLOW: /* buffer overrun on read means that
- + * we didn't provide a big enough
- + * buffer.
- + */
- + default:
- +#if 1
- + DBG (dev, "%s complete --> %d, %d/%d\n", ep->name,
- + status, req->actual, req->length);
- +#endif
- + case -EREMOTEIO: /* short read */
- + break;
- + }
- +
- + status = usb_ep_queue (ep, req, GFP_ATOMIC);
- + if (status) {
- + ERROR (dev, "kill %s: resubmit %d bytes --> %d\n",
- + ep->name, req->length, status);
- + usb_ep_set_halt (ep);
- + /* FIXME recover later ... somehow */
- + }
- +}
- +
- +static struct usb_request *
- +zero_start_isoc_ep (struct usb_ep *ep, int gfp_flags)
- +{
- + struct usb_request *req;
- + int status;
- +
- + req = alloc_ep_req (ep, 512);
- + if (!req)
- + return NULL;
- +
- + req->complete = zero_isoc_complete;
- +
- + status = usb_ep_queue (ep, req, gfp_flags);
- + if (status) {
- + struct zero_dev *dev = ep->driver_data;
- +
- + ERROR (dev, "start %s --> %d\n", ep->name, status);
- + free_ep_req (ep, req);
- + req = NULL;
- + }
- +
- + return req;
- +}
- +
- +/* change our operational config. this code must agree with the code
- + * that returns config descriptors, and altsetting code.
- + *
- + * it's also responsible for power management interactions. some
- + * configurations might not work with our current power sources.
- + *
- + * note that some device controller hardware will constrain what this
- + * code can do, perhaps by disallowing more than one configuration or
- + * by limiting configuration choices (like the pxa2xx).
- + */
- +static int
- +zero_set_config (struct zero_dev *dev, unsigned number, int gfp_flags)
- +{
- + int result = 0;
- + struct usb_gadget *gadget = dev->gadget;
- + const struct usb_endpoint_descriptor *d;
- + struct usb_ep *ep;
- +
- + if (number == dev->config)
- + return 0;
- +
- + zero_reset_config (dev);
- +
- + gadget_for_each_ep (ep, gadget) {
- +
- + if (strcmp (ep->name, "ep4") == 0) {
- +
- + d = (struct usb_endpoint_descripter *)&za_23; // isoc ep desc for audio i/f alt setting 6
- + result = usb_ep_enable (ep, d);
- +
- + if (result == 0) {
- + ep->driver_data = dev;
- + dev->in_ep = ep;
- +
- + if (zero_start_isoc_ep (ep, gfp_flags) != 0) {
- +
- + dev->in_ep = ep;
- + continue;
- + }
- +
- + usb_ep_disable (ep);
- + result = -EIO;
- + }
- + }
- +
- + }
- +
- + dev->config = number;
- + return result;
- +}
- +
- +/*-------------------------------------------------------------------------*/
- +
- +static void zero_setup_complete (struct usb_ep *ep, struct usb_request *req)
- +{
- + if (req->status || req->actual != req->length)
- + DBG ((struct zero_dev *) ep->driver_data,
- + "setup complete --> %d, %d/%d\n",
- + req->status, req->actual, req->length);
- +}
- +
- +/*
- + * The setup() callback implements all the ep0 functionality that's
- + * not handled lower down, in hardware or the hardware driver (like
- + * device and endpoint feature flags, and their status). It's all
- + * housekeeping for the gadget function we're implementing. Most of
- + * the work is in config-specific setup.
- + */
- +static int
- +zero_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
- +{
- + struct zero_dev *dev = get_gadget_data (gadget);
- + struct usb_request *req = dev->req;
- + int value = -EOPNOTSUPP;
- +
- + /* usually this stores reply data in the pre-allocated ep0 buffer,
- + * but config change events will reconfigure hardware.
- + */
- + req->zero = 0;
- + switch (ctrl->bRequest) {
- +
- + case USB_REQ_GET_DESCRIPTOR:
- +
- + switch (ctrl->wValue >> 8) {
- +
- + case USB_DT_DEVICE:
- + value = min (ctrl->wLength, (u16) sizeof device_desc);
- + memcpy (req->buf, &device_desc, value);
- + break;
- +#ifdef CONFIG_USB_GADGET_DUALSPEED
- + case USB_DT_DEVICE_QUALIFIER:
- + if (!gadget->is_dualspeed)
- + break;
- + value = min (ctrl->wLength, (u16) sizeof dev_qualifier);
- + memcpy (req->buf, &dev_qualifier, value);
- + break;
- +
- + case USB_DT_OTHER_SPEED_CONFIG:
- + if (!gadget->is_dualspeed)
- + break;
- + // FALLTHROUGH
- +#endif /* CONFIG_USB_GADGET_DUALSPEED */
- + case USB_DT_CONFIG:
- + value = config_buf (gadget, req->buf,
- + ctrl->wValue >> 8,
- + ctrl->wValue & 0xff);
- + if (value >= 0)
- + value = min (ctrl->wLength, (u16) value);
- + break;
- +
- + case USB_DT_STRING:
- + /* wIndex == language code.
- + * this driver only handles one language, you can
- + * add string tables for other languages, using
- + * any UTF-8 characters
- + */
- + value = usb_gadget_get_string (&stringtab,
- + ctrl->wValue & 0xff, req->buf);
- + if (value >= 0) {
- + value = min (ctrl->wLength, (u16) value);
- + }
- + break;
- + }
- + break;
- +
- + /* currently two configs, two speeds */
- + case USB_REQ_SET_CONFIGURATION:
- + if (ctrl->bRequestType != 0)
- + goto unknown;
- +
- + spin_lock (&dev->lock);
- + value = zero_set_config (dev, ctrl->wValue, GFP_ATOMIC);
- + spin_unlock (&dev->lock);
- + break;
- + case USB_REQ_GET_CONFIGURATION:
- + if (ctrl->bRequestType != USB_DIR_IN)
- + goto unknown;
- + *(u8 *)req->buf = dev->config;
- + value = min (ctrl->wLength, (u16) 1);
- + break;
- +
- + /* until we add altsetting support, or other interfaces,
- + * only 0/0 are possible. pxa2xx only supports 0/0 (poorly)
- + * and already killed pending endpoint I/O.
- + */
- + case USB_REQ_SET_INTERFACE:
- +
- + if (ctrl->bRequestType != USB_RECIP_INTERFACE)
- + goto unknown;
- + spin_lock (&dev->lock);
- + if (dev->config) {
- + u8 config = dev->config;
- +
- + /* resets interface configuration, forgets about
- + * previous transaction state (queued bufs, etc)
- + * and re-inits endpoint state (toggle etc)
- + * no response queued, just zero status == success.
- + * if we had more than one interface we couldn't
- + * use this "reset the config" shortcut.
- + */
- + zero_reset_config (dev);
- + zero_set_config (dev, config, GFP_ATOMIC);
- + value = 0;
- + }
- + spin_unlock (&dev->lock);
- + break;
- + case USB_REQ_GET_INTERFACE:
- + if ((ctrl->bRequestType == 0x21) && (ctrl->wIndex == 0x02)) {
- + value = ctrl->wLength;
- + break;
- + }
- + else {
- + if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
- + goto unknown;
- + if (!dev->config)
- + break;
- + if (ctrl->wIndex != 0) {
- + value = -EDOM;
- + break;
- + }
- + *(u8 *)req->buf = 0;
- + value = min (ctrl->wLength, (u16) 1);
- + }
- + break;
- +
- + /*
- + * These are the same vendor-specific requests supported by
- + * Intel's USB 2.0 compliance test devices. We exceed that
- + * device spec by allowing multiple-packet requests.
- + */
- + case 0x5b: /* control WRITE test -- fill the buffer */
- + if (ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR))
- + goto unknown;
- + if (ctrl->wValue || ctrl->wIndex)
- + break;
- + /* just read that many bytes into the buffer */
- + if (ctrl->wLength > USB_BUFSIZ)
- + break;
- + value = ctrl->wLength;
- + break;
- + case 0x5c: /* control READ test -- return the buffer */
- + if (ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR))
- + goto unknown;
- + if (ctrl->wValue || ctrl->wIndex)
- + break;
- + /* expect those bytes are still in the buffer; send back */
- + if (ctrl->wLength > USB_BUFSIZ
- + || ctrl->wLength != req->length)
- + break;
- + value = ctrl->wLength;
- + break;
- +
- + case 0x01: // SET_CUR
- + case 0x02:
- + case 0x03:
- + case 0x04:
- + case 0x05:
- + value = ctrl->wLength;
- + break;
- + case 0x81:
- + switch (ctrl->wValue) {
- + case 0x0201:
- + case 0x0202:
- + ((u8*)req->buf)[0] = 0x00;
- + ((u8*)req->buf)[1] = 0xe3;
- + break;
- + case 0x0300:
- + case 0x0500:
- + ((u8*)req->buf)[0] = 0x00;
- + break;
- + }
- + //((u8*)req->buf)[0] = 0x81;
- + //((u8*)req->buf)[1] = 0x81;
- + value = ctrl->wLength;
- + break;
- + case 0x82:
- + switch (ctrl->wValue) {
- + case 0x0201:
- + case 0x0202:
- + ((u8*)req->buf)[0] = 0x00;
- + ((u8*)req->buf)[1] = 0xc3;
- + break;
- + case 0x0300:
- + case 0x0500:
- + ((u8*)req->buf)[0] = 0x00;
- + break;
- + }
- + //((u8*)req->buf)[0] = 0x82;
- + //((u8*)req->buf)[1] = 0x82;
- + value = ctrl->wLength;
- + break;
- + case 0x83:
- + switch (ctrl->wValue) {
- + case 0x0201:
- + case 0x0202:
- + ((u8*)req->buf)[0] = 0x00;
- + ((u8*)req->buf)[1] = 0x00;
- + break;
- + case 0x0300:
- + ((u8*)req->buf)[0] = 0x60;
- + break;
- + case 0x0500:
- + ((u8*)req->buf)[0] = 0x18;
- + break;
- + }
- + //((u8*)req->buf)[0] = 0x83;
- + //((u8*)req->buf)[1] = 0x83;
- + value = ctrl->wLength;
- + break;
- + case 0x84:
- + switch (ctrl->wValue) {
- + case 0x0201:
- + case 0x0202:
- + ((u8*)req->buf)[0] = 0x00;
- + ((u8*)req->buf)[1] = 0x01;
- + break;
- + case 0x0300:
- + case 0x0500:
- + ((u8*)req->buf)[0] = 0x08;
- + break;
- + }
- + //((u8*)req->buf)[0] = 0x84;
- + //((u8*)req->buf)[1] = 0x84;
- + value = ctrl->wLength;
- + break;
- + case 0x85:
- + ((u8*)req->buf)[0] = 0x85;
- + ((u8*)req->buf)[1] = 0x85;
- + value = ctrl->wLength;
- + break;
- +
- +
- + default:
- +unknown:
- + printk("unknown control req%02x.%02x v%04x i%04x l%d\n",
- + ctrl->bRequestType, ctrl->bRequest,
- + ctrl->wValue, ctrl->wIndex, ctrl->wLength);
- + }
- +
- + /* respond with data transfer before status phase? */
- + if (value >= 0) {
- + req->length = value;
- + req->zero = value < ctrl->wLength
- + && (value % gadget->ep0->maxpacket) == 0;
- + value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC);
- + if (value < 0) {
- + DBG (dev, "ep_queue < 0 --> %d\n", value);
- + req->status = 0;
- + zero_setup_complete (gadget->ep0, req);
- + }
- + }
- +
- + /* device either stalls (value < 0) or reports success */
- + return value;
- +}
- +
- +static void
- +zero_disconnect (struct usb_gadget *gadget)
- +{
- + struct zero_dev *dev = get_gadget_data (gadget);
- + unsigned long flags;
- +
- + spin_lock_irqsave (&dev->lock, flags);
- + zero_reset_config (dev);
- +
- + /* a more significant application might have some non-usb
- + * activities to quiesce here, saving resources like power
- + * or pushing the notification up a network stack.
- + */
- + spin_unlock_irqrestore (&dev->lock, flags);
- +
- + /* next we may get setup() calls to enumerate new connections;
- + * or an unbind() during shutdown (including removing module).
- + */
- +}
- +
- +static void
- +zero_autoresume (unsigned long _dev)
- +{
- + struct zero_dev *dev = (struct zero_dev *) _dev;
- + int status;
- +
- + /* normally the host would be woken up for something
- + * more significant than just a timer firing...
- + */
- + if (dev->gadget->speed != USB_SPEED_UNKNOWN) {
- + status = usb_gadget_wakeup (dev->gadget);
- + DBG (dev, "wakeup --> %d\n", status);
- + }
- +}
- +
- +/*-------------------------------------------------------------------------*/
- +
- +static void
- +zero_unbind (struct usb_gadget *gadget)
- +{
- + struct zero_dev *dev = get_gadget_data (gadget);
- +
- + DBG (dev, "unbind\n");
- +
- + /* we've already been disconnected ... no i/o is active */
- + if (dev->req)
- + free_ep_req (gadget->ep0, dev->req);
- + del_timer_sync (&dev->resume);
- + kfree (dev);
- + set_gadget_data (gadget, NULL);
- +}
- +
- +static int
- +zero_bind (struct usb_gadget *gadget)
- +{
- + struct zero_dev *dev;
- + //struct usb_ep *ep;
- +
- + printk("binding\n");
- + /*
- + * DRIVER POLICY CHOICE: you may want to do this differently.
- + * One thing to avoid is reusing a bcdDevice revision code
- + * with different host-visible configurations or behavior
- + * restrictions -- using ep1in/ep2out vs ep1out/ep3in, etc
- + */
- + //device_desc.bcdDevice = __constant_cpu_to_le16 (0x0201);
- +
- +
- + /* ok, we made sense of the hardware ... */
- + dev = kzalloc (sizeof *dev, SLAB_KERNEL);
- + if (!dev)
- + return -ENOMEM;
- + spin_lock_init (&dev->lock);
- + dev->gadget = gadget;
- + set_gadget_data (gadget, dev);
- +
- + /* preallocate control response and buffer */
- + dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
- + if (!dev->req)
- + goto enomem;
- + dev->req->buf = usb_ep_alloc_buffer (gadget->ep0, USB_BUFSIZ,
- + &dev->req->dma, GFP_KERNEL);
- + if (!dev->req->buf)
- + goto enomem;
- +
- + dev->req->complete = zero_setup_complete;
- +
- + device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
- +
- +#ifdef CONFIG_USB_GADGET_DUALSPEED
- + /* assume ep0 uses the same value for both speeds ... */
- + dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0;
- +
- + /* and that all endpoints are dual-speed */
- + //hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress;
- + //hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress;
- +#endif
- +
- + usb_gadget_set_selfpowered (gadget);
- +
- + init_timer (&dev->resume);
- + dev->resume.function = zero_autoresume;
- + dev->resume.data = (unsigned long) dev;
- +
- + gadget->ep0->driver_data = dev;
- +
- + INFO (dev, "%s, version: " DRIVER_VERSION "\n", longname);
- + INFO (dev, "using %s, OUT %s IN %s\n", gadget->name,
- + EP_OUT_NAME, EP_IN_NAME);
- +
- + snprintf (manufacturer, sizeof manufacturer,
- + UTS_SYSNAME " " UTS_RELEASE " with %s",
- + gadget->name);
- +
- + return 0;
- +
- +enomem:
- + zero_unbind (gadget);
- + return -ENOMEM;
- +}
- +
- +/*-------------------------------------------------------------------------*/
- +
- +static void
- +zero_suspend (struct usb_gadget *gadget)
- +{
- + struct zero_dev *dev = get_gadget_data (gadget);
- +
- + if (gadget->speed == USB_SPEED_UNKNOWN)
- + return;
- +
- + if (autoresume) {
- + mod_timer (&dev->resume, jiffies + (HZ * autoresume));
- + DBG (dev, "suspend, wakeup in %d seconds\n", autoresume);
- + } else
- + DBG (dev, "suspend\n");
- +}
- +
- +static void
- +zero_resume (struct usb_gadget *gadget)
- +{
- + struct zero_dev *dev = get_gadget_data (gadget);
- +
- + DBG (dev, "resume\n");
- + del_timer (&dev->resume);
- +}
- +
- +
- +/*-------------------------------------------------------------------------*/
- +
- +static struct usb_gadget_driver zero_driver = {
- +#ifdef CONFIG_USB_GADGET_DUALSPEED
- + .speed = USB_SPEED_HIGH,
- +#else
- + .speed = USB_SPEED_FULL,
- +#endif
- + .function = (char *) longname,
- + .bind = zero_bind,
- + .unbind = zero_unbind,
- +
- + .setup = zero_setup,
- + .disconnect = zero_disconnect,
- +
- + .suspend = zero_suspend,
- + .resume = zero_resume,
- +
- + .driver = {
- + .name = (char *) shortname,
- + // .shutdown = ...
- + // .suspend = ...
- + // .resume = ...
- + },
- +};
- +
- +MODULE_AUTHOR ("David Brownell");
- +MODULE_LICENSE ("Dual BSD/GPL");
- +
- +static struct proc_dir_entry *pdir, *pfile;
- +
- +static int isoc_read_data (char *page, char **start,
- + off_t off, int count,
- + int *eof, void *data)
- +{
- + int i;
- + static int c = 0;
- + static int done = 0;
- + static int s = 0;
- +
- +/*
- + printk ("\ncount: %d\n", count);
- + printk ("rbuf_start: %d\n", rbuf_start);
- + printk ("rbuf_len: %d\n", rbuf_len);
- + printk ("off: %d\n", off);
- + printk ("start: %p\n\n", *start);
- +*/
- + if (done) {
- + c = 0;
- + done = 0;
- + *eof = 1;
- + return 0;
- + }
- +
- + if (c == 0) {
- + if (rbuf_len == RBUF_LEN)
- + s = rbuf_start;
- + else s = 0;
- + }
- +
- + for (i=0; i<count && c<rbuf_len; i++, c++) {
- + page[i] = rbuf[(c+s) % RBUF_LEN];
- + }
- + *start = page;
- +
- + if (c >= rbuf_len) {
- + *eof = 1;
- + done = 1;
- + }
- +
- +
- + return i;
- +}
- +
- +static int __init init (void)
- +{
- +
- + int retval = 0;
- +
- + pdir = proc_mkdir("isoc_test", NULL);
- + if(pdir == NULL) {
- + retval = -ENOMEM;
- + printk("Error creating dir\n");
- + goto done;
- + }
- + pdir->owner = THIS_MODULE;
- +
- + pfile = create_proc_read_entry("isoc_data",
- + 0444, pdir,
- + isoc_read_data,
- + NULL);
- + if (pfile == NULL) {
- + retval = -ENOMEM;
- + printk("Error creating file\n");
- + goto no_file;
- + }
- + pfile->owner = THIS_MODULE;
- +
- + return usb_gadget_register_driver (&zero_driver);
- +
- + no_file:
- + remove_proc_entry("isoc_data", NULL);
- + done:
- + return retval;
- +}
- +module_init (init);
- +
- +static void __exit cleanup (void)
- +{
- +
- + usb_gadget_unregister_driver (&zero_driver);
- +
- + remove_proc_entry("isoc_data", pdir);
- + remove_proc_entry("isoc_test", NULL);
- +}
- +module_exit (cleanup);
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_cfi_common.h
- @@ -0,0 +1,142 @@
- +/* ==========================================================================
- + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
- + * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
- + * otherwise expressly agreed to in writing between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product under
- + * any End User Software License Agreement or Agreement for Licensed Product
- + * with Synopsys or any supplement thereto. You are permitted to use and
- + * redistribute this Software in source and binary forms, with or without
- + * modification, provided that redistributions of source code must retain this
- + * notice. You may not view, use, disclose, copy or distribute this file or
- + * any information contained herein except pursuant to this license grant from
- + * Synopsys. If you do not agree with this notice, including the disclaimer
- + * below, then you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
- + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- + * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
- + * ========================================================================== */
- +
- +#if !defined(__DWC_CFI_COMMON_H__)
- +#define __DWC_CFI_COMMON_H__
- +
- +//#include <linux/types.h>
- +
- +/**
- + * @file
- + *
- + * This file contains the CFI specific common constants, interfaces
- + * (functions and macros) and structures for Linux. No PCD specific
- + * data structure or definition is to be included in this file.
- + *
- + */
- +
- +/** This is a request for all Core Features */
- +#define VEN_CORE_GET_FEATURES 0xB1
- +
- +/** This is a request to get the value of a specific Core Feature */
- +#define VEN_CORE_GET_FEATURE 0xB2
- +
- +/** This command allows the host to set the value of a specific Core Feature */
- +#define VEN_CORE_SET_FEATURE 0xB3
- +
- +/** This command allows the host to set the default values of
- + * either all or any specific Core Feature
- + */
- +#define VEN_CORE_RESET_FEATURES 0xB4
- +
- +/** This command forces the PCD to write the deferred values of a Core Features */
- +#define VEN_CORE_ACTIVATE_FEATURES 0xB5
- +
- +/** This request reads a DWORD value from a register at the specified offset */
- +#define VEN_CORE_READ_REGISTER 0xB6
- +
- +/** This request writes a DWORD value into a register at the specified offset */
- +#define VEN_CORE_WRITE_REGISTER 0xB7
- +
- +/** This structure is the header of the Core Features dataset returned to
- + * the Host
- + */
- +struct cfi_all_features_header {
- +/** The features header structure length is */
- +#define CFI_ALL_FEATURES_HDR_LEN 8
- + /**
- + * The total length of the features dataset returned to the Host
- + */
- + uint16_t wTotalLen;
- +
- + /**
- + * CFI version number inBinary-Coded Decimal (i.e., 1.00 is 100H).
- + * This field identifies the version of the CFI Specification with which
- + * the device is compliant.
- + */
- + uint16_t wVersion;
- +
- + /** The ID of the Core */
- + uint16_t wCoreID;
- +#define CFI_CORE_ID_UDC 1
- +#define CFI_CORE_ID_OTG 2
- +#define CFI_CORE_ID_WUDEV 3
- +
- + /** Number of features returned by VEN_CORE_GET_FEATURES request */
- + uint16_t wNumFeatures;
- +} UPACKED;
- +
- +typedef struct cfi_all_features_header cfi_all_features_header_t;
- +
- +/** This structure is a header of the Core Feature descriptor dataset returned to
- + * the Host after the VEN_CORE_GET_FEATURES request
- + */
- +struct cfi_feature_desc_header {
- +#define CFI_FEATURE_DESC_HDR_LEN 8
- +
- + /** The feature ID */
- + uint16_t wFeatureID;
- +
- + /** Length of this feature descriptor in bytes - including the
- + * length of the feature name string
- + */
- + uint16_t wLength;
- +
- + /** The data length of this feature in bytes */
- + uint16_t wDataLength;
- +
- + /**
- + * Attributes of this features
- + * D0: Access rights
- + * 0 - Read/Write
- + * 1 - Read only
- + */
- + uint8_t bmAttributes;
- +#define CFI_FEATURE_ATTR_RO 1
- +#define CFI_FEATURE_ATTR_RW 0
- +
- + /** Length of the feature name in bytes */
- + uint8_t bNameLen;
- +
- + /** The feature name buffer */
- + //uint8_t *name;
- +} UPACKED;
- +
- +typedef struct cfi_feature_desc_header cfi_feature_desc_header_t;
- +
- +/**
- + * This structure describes a NULL terminated string referenced by its id field.
- + * It is very similar to usb_string structure but has the id field type set to 16-bit.
- + */
- +struct cfi_string {
- + uint16_t id;
- + const uint8_t *s;
- +};
- +typedef struct cfi_string cfi_string_t;
- +
- +#endif
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_adp.c
- @@ -0,0 +1,854 @@
- +/* ==========================================================================
- + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_adp.c $
- + * $Revision: #12 $
- + * $Date: 2011/10/26 $
- + * $Change: 1873028 $
- + *
- + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
- + * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
- + * otherwise expressly agreed to in writing between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product under
- + * any End User Software License Agreement or Agreement for Licensed Product
- + * with Synopsys or any supplement thereto. You are permitted to use and
- + * redistribute this Software in source and binary forms, with or without
- + * modification, provided that redistributions of source code must retain this
- + * notice. You may not view, use, disclose, copy or distribute this file or
- + * any information contained herein except pursuant to this license grant from
- + * Synopsys. If you do not agree with this notice, including the disclaimer
- + * below, then you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
- + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- + * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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 "dwc_os.h"
- +#include "dwc_otg_regs.h"
- +#include "dwc_otg_cil.h"
- +#include "dwc_otg_adp.h"
- +
- +/** @file
- + *
- + * This file contains the most of the Attach Detect Protocol implementation for
- + * the driver to support OTG Rev2.0.
- + *
- + */
- +
- +void dwc_otg_adp_write_reg(dwc_otg_core_if_t * core_if, uint32_t value)
- +{
- + adpctl_data_t adpctl;
- +
- + adpctl.d32 = value;
- + adpctl.b.ar = 0x2;
- +
- + DWC_WRITE_REG32(&core_if->core_global_regs->adpctl, adpctl.d32);
- +
- + while (adpctl.b.ar) {
- + adpctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->adpctl);
- + }
- +
- +}
- +
- +/**
- + * Function is called to read ADP registers
- + */
- +uint32_t dwc_otg_adp_read_reg(dwc_otg_core_if_t * core_if)
- +{
- + adpctl_data_t adpctl;
- +
- + adpctl.d32 = 0;
- + adpctl.b.ar = 0x1;
- +
- + DWC_WRITE_REG32(&core_if->core_global_regs->adpctl, adpctl.d32);
- +
- + while (adpctl.b.ar) {
- + adpctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->adpctl);
- + }
- +
- + return adpctl.d32;
- +}
- +
- +/**
- + * Function is called to read ADPCTL register and filter Write-clear bits
- + */
- +uint32_t dwc_otg_adp_read_reg_filter(dwc_otg_core_if_t * core_if)
- +{
- + adpctl_data_t adpctl;
- +
- + adpctl.d32 = dwc_otg_adp_read_reg(core_if);
- + adpctl.b.adp_tmout_int = 0;
- + adpctl.b.adp_prb_int = 0;
- + adpctl.b.adp_tmout_int = 0;
- +
- + return adpctl.d32;
- +}
- +
- +/**
- + * Function is called to write ADP registers
- + */
- +void dwc_otg_adp_modify_reg(dwc_otg_core_if_t * core_if, uint32_t clr,
- + uint32_t set)
- +{
- + dwc_otg_adp_write_reg(core_if,
- + (dwc_otg_adp_read_reg(core_if) & (~clr)) | set);
- +}
- +
- +static void adp_sense_timeout(void *ptr)
- +{
- + dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *) ptr;
- + core_if->adp.sense_timer_started = 0;
- + DWC_PRINTF("ADP SENSE TIMEOUT\n");
- + if (core_if->adp_enable) {
- + dwc_otg_adp_sense_stop(core_if);
- + dwc_otg_adp_probe_start(core_if);
- + }
- +}
- +
- +/**
- + * This function is called when the ADP vbus timer expires. Timeout is 1.1s.
- + */
- +static void adp_vbuson_timeout(void *ptr)
- +{
- + gpwrdn_data_t gpwrdn;
- + dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *) ptr;
- + hprt0_data_t hprt0 = {.d32 = 0 };
- + pcgcctl_data_t pcgcctl = {.d32 = 0 };
- + DWC_PRINTF("%s: 1.1 seconds expire after turning on VBUS\n",__FUNCTION__);
- + if (core_if) {
- + core_if->adp.vbuson_timer_started = 0;
- + /* Turn off vbus */
- + hprt0.b.prtpwr = 1;
- + DWC_MODIFY_REG32(core_if->host_if->hprt0, hprt0.d32, 0);
- + gpwrdn.d32 = 0;
- +
- + /* Power off the core */
- + if (core_if->power_down == 2) {
- + /* Enable Wakeup Logic */
- +// gpwrdn.b.wkupactiv = 1;
- + gpwrdn.b.pmuactv = 0;
- + gpwrdn.b.pwrdnrstn = 1;
- + gpwrdn.b.pwrdnclmp = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0,
- + gpwrdn.d32);
- +
- + /* Suspend the Phy Clock */
- + pcgcctl.b.stoppclk = 1;
- + DWC_MODIFY_REG32(core_if->pcgcctl, 0, pcgcctl.d32);
- +
- + /* Switch on VDD */
- +// gpwrdn.b.wkupactiv = 1;
- + gpwrdn.b.pmuactv = 1;
- + gpwrdn.b.pwrdnrstn = 1;
- + gpwrdn.b.pwrdnclmp = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0,
- + gpwrdn.d32);
- + } else {
- + /* Enable Power Down Logic */
- + gpwrdn.b.pmuintsel = 1;
- + gpwrdn.b.pmuactv = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
- + }
- +
- + /* Power off the core */
- + if (core_if->power_down == 2) {
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pwrdnswtch = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn,
- + gpwrdn.d32, 0);
- + }
- +
- + /* Unmask SRP detected interrupt from Power Down Logic */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.srp_det_msk = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
- +
- + dwc_otg_adp_probe_start(core_if);
- + dwc_otg_dump_global_registers(core_if);
- + dwc_otg_dump_host_registers(core_if);
- + }
- +
- +}
- +
- +/**
- + * Start the ADP Initial Probe timer to detect if Port Connected interrupt is
- + * not asserted within 1.1 seconds.
- + *
- + * @param core_if the pointer to core_if strucure.
- + */
- +void dwc_otg_adp_vbuson_timer_start(dwc_otg_core_if_t * core_if)
- +{
- + core_if->adp.vbuson_timer_started = 1;
- + if (core_if->adp.vbuson_timer)
- + {
- + DWC_PRINTF("SCHEDULING VBUSON TIMER\n");
- + /* 1.1 secs + 60ms necessary for cil_hcd_start*/
- + DWC_TIMER_SCHEDULE(core_if->adp.vbuson_timer, 1160);
- + } else {
- + DWC_WARN("VBUSON_TIMER = %p\n",core_if->adp.vbuson_timer);
- + }
- +}
- +
- +#if 0
- +/**
- + * Masks all DWC OTG core interrupts
- + *
- + */
- +static void mask_all_interrupts(dwc_otg_core_if_t * core_if)
- +{
- + int i;
- + gahbcfg_data_t ahbcfg = {.d32 = 0 };
- +
- + /* Mask Host Interrupts */
- +
- + /* Clear and disable HCINTs */
- + for (i = 0; i < core_if->core_params->host_channels; i++) {
- + DWC_WRITE_REG32(&core_if->host_if->hc_regs[i]->hcintmsk, 0);
- + DWC_WRITE_REG32(&core_if->host_if->hc_regs[i]->hcint, 0xFFFFFFFF);
- +
- + }
- +
- + /* Clear and disable HAINT */
- + DWC_WRITE_REG32(&core_if->host_if->host_global_regs->haintmsk, 0x0000);
- + DWC_WRITE_REG32(&core_if->host_if->host_global_regs->haint, 0xFFFFFFFF);
- +
- + /* Mask Device Interrupts */
- + if (!core_if->multiproc_int_enable) {
- + /* Clear and disable IN Endpoint interrupts */
- + DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->diepmsk, 0);
- + for (i = 0; i <= core_if->dev_if->num_in_eps; i++) {
- + DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[i]->
- + diepint, 0xFFFFFFFF);
- + }
- +
- + /* Clear and disable OUT Endpoint interrupts */
- + DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->doepmsk, 0);
- + for (i = 0; i <= core_if->dev_if->num_out_eps; i++) {
- + DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[i]->
- + doepint, 0xFFFFFFFF);
- + }
- +
- + /* Clear and disable DAINT */
- + DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->daint,
- + 0xFFFFFFFF);
- + DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->daintmsk, 0);
- + } else {
- + for (i = 0; i < core_if->dev_if->num_in_eps; ++i) {
- + DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->
- + diepeachintmsk[i], 0);
- + DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[i]->
- + diepint, 0xFFFFFFFF);
- + }
- +
- + for (i = 0; i < core_if->dev_if->num_out_eps; ++i) {
- + DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->
- + doepeachintmsk[i], 0);
- + DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[i]->
- + doepint, 0xFFFFFFFF);
- + }
- +
- + DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->deachintmsk,
- + 0);
- + DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->deachint,
- + 0xFFFFFFFF);
- +
- + }
- +
- + /* Disable interrupts */
- + ahbcfg.b.glblintrmsk = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gahbcfg, ahbcfg.d32, 0);
- +
- + /* Disable all interrupts. */
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, 0);
- +
- + /* Clear any pending interrupts */
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
- +
- + /* Clear any pending OTG Interrupts */
- + DWC_WRITE_REG32(&core_if->core_global_regs->gotgint, 0xFFFFFFFF);
- +}
- +
- +/**
- + * Unmask Port Connection Detected interrupt
- + *
- + */
- +static void unmask_conn_det_intr(dwc_otg_core_if_t * core_if)
- +{
- + gintmsk_data_t gintmsk = {.d32 = 0,.b.portintr = 1 };
- +
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, gintmsk.d32);
- +}
- +#endif
- +
- +/**
- + * Starts the ADP Probing
- + *
- + * @param core_if the pointer to core_if structure.
- + */
- +uint32_t dwc_otg_adp_probe_start(dwc_otg_core_if_t * core_if)
- +{
- +
- + adpctl_data_t adpctl = {.d32 = 0};
- + gpwrdn_data_t gpwrdn;
- +#if 0
- + adpctl_data_t adpctl_int = {.d32 = 0, .b.adp_prb_int = 1,
- + .b.adp_sns_int = 1, b.adp_tmout_int};
- +#endif
- + dwc_otg_disable_global_interrupts(core_if);
- + DWC_PRINTF("ADP Probe Start\n");
- + core_if->adp.probe_enabled = 1;
- +
- + adpctl.b.adpres = 1;
- + dwc_otg_adp_write_reg(core_if, adpctl.d32);
- +
- + while (adpctl.b.adpres) {
- + adpctl.d32 = dwc_otg_adp_read_reg(core_if);
- + }
- +
- + adpctl.d32 = 0;
- + gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
- +
- + /* In Host mode unmask SRP detected interrupt */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.sts_chngint_msk = 1;
- + if (!gpwrdn.b.idsts) {
- + gpwrdn.b.srp_det_msk = 1;
- + }
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
- +
- + adpctl.b.adp_tmout_int_msk = 1;
- + adpctl.b.adp_prb_int_msk = 1;
- + adpctl.b.prb_dschg = 1;
- + adpctl.b.prb_delta = 1;
- + adpctl.b.prb_per = 1;
- + adpctl.b.adpen = 1;
- + adpctl.b.enaprb = 1;
- +
- + dwc_otg_adp_write_reg(core_if, adpctl.d32);
- + DWC_PRINTF("ADP Probe Finish\n");
- + return 0;
- +}
- +
- +/**
- + * Starts the ADP Sense timer to detect if ADP Sense interrupt is not asserted
- + * within 3 seconds.
- + *
- + * @param core_if the pointer to core_if strucure.
- + */
- +void dwc_otg_adp_sense_timer_start(dwc_otg_core_if_t * core_if)
- +{
- + core_if->adp.sense_timer_started = 1;
- + DWC_TIMER_SCHEDULE(core_if->adp.sense_timer, 3000 /* 3 secs */ );
- +}
- +
- +/**
- + * Starts the ADP Sense
- + *
- + * @param core_if the pointer to core_if strucure.
- + */
- +uint32_t dwc_otg_adp_sense_start(dwc_otg_core_if_t * core_if)
- +{
- + adpctl_data_t adpctl;
- +
- + DWC_PRINTF("ADP Sense Start\n");
- +
- + /* Unmask ADP sense interrupt and mask all other from the core */
- + adpctl.d32 = dwc_otg_adp_read_reg_filter(core_if);
- + adpctl.b.adp_sns_int_msk = 1;
- + dwc_otg_adp_write_reg(core_if, adpctl.d32);
- + dwc_otg_disable_global_interrupts(core_if); // vahrama
- +
- + /* Set ADP reset bit*/
- + adpctl.d32 = dwc_otg_adp_read_reg_filter(core_if);
- + adpctl.b.adpres = 1;
- + dwc_otg_adp_write_reg(core_if, adpctl.d32);
- +
- + while (adpctl.b.adpres) {
- + adpctl.d32 = dwc_otg_adp_read_reg(core_if);
- + }
- +
- + adpctl.b.adpres = 0;
- + adpctl.b.adpen = 1;
- + adpctl.b.enasns = 1;
- + dwc_otg_adp_write_reg(core_if, adpctl.d32);
- +
- + dwc_otg_adp_sense_timer_start(core_if);
- +
- + return 0;
- +}
- +
- +/**
- + * Stops the ADP Probing
- + *
- + * @param core_if the pointer to core_if strucure.
- + */
- +uint32_t dwc_otg_adp_probe_stop(dwc_otg_core_if_t * core_if)
- +{
- +
- + adpctl_data_t adpctl;
- + DWC_PRINTF("Stop ADP probe\n");
- + core_if->adp.probe_enabled = 0;
- + core_if->adp.probe_counter = 0;
- + adpctl.d32 = dwc_otg_adp_read_reg(core_if);
- +
- + adpctl.b.adpen = 0;
- + adpctl.b.adp_prb_int = 1;
- + adpctl.b.adp_tmout_int = 1;
- + adpctl.b.adp_sns_int = 1;
- + dwc_otg_adp_write_reg(core_if, adpctl.d32);
- +
- + return 0;
- +}
- +
- +/**
- + * Stops the ADP Sensing
- + *
- + * @param core_if the pointer to core_if strucure.
- + */
- +uint32_t dwc_otg_adp_sense_stop(dwc_otg_core_if_t * core_if)
- +{
- + adpctl_data_t adpctl;
- +
- + core_if->adp.sense_enabled = 0;
- +
- + adpctl.d32 = dwc_otg_adp_read_reg_filter(core_if);
- + adpctl.b.enasns = 0;
- + adpctl.b.adp_sns_int = 1;
- + dwc_otg_adp_write_reg(core_if, adpctl.d32);
- +
- + return 0;
- +}
- +
- +/**
- + * Called to turn on the VBUS after initial ADP probe in host mode.
- + * If port power was already enabled in cil_hcd_start function then
- + * only schedule a timer.
- + *
- + * @param core_if the pointer to core_if structure.
- + */
- +void dwc_otg_adp_turnon_vbus(dwc_otg_core_if_t * core_if)
- +{
- + hprt0_data_t hprt0 = {.d32 = 0 };
- + hprt0.d32 = dwc_otg_read_hprt0(core_if);
- + DWC_PRINTF("Turn on VBUS for 1.1s, port power is %d\n", hprt0.b.prtpwr);
- +
- + if (hprt0.b.prtpwr == 0) {
- + hprt0.b.prtpwr = 1;
- + //DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
- + }
- +
- + dwc_otg_adp_vbuson_timer_start(core_if);
- +}
- +
- +/**
- + * Called right after driver is loaded
- + * to perform initial actions for ADP
- + *
- + * @param core_if the pointer to core_if structure.
- + * @param is_host - flag for current mode of operation either from GINTSTS or GPWRDN
- + */
- +void dwc_otg_adp_start(dwc_otg_core_if_t * core_if, uint8_t is_host)
- +{
- + gpwrdn_data_t gpwrdn;
- +
- + DWC_PRINTF("ADP Initial Start\n");
- + core_if->adp.adp_started = 1;
- +
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
- + dwc_otg_disable_global_interrupts(core_if);
- + if (is_host) {
- + DWC_PRINTF("HOST MODE\n");
- + /* Enable Power Down Logic Interrupt*/
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pmuintsel = 1;
- + gpwrdn.b.pmuactv = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
- + /* Initialize first ADP probe to obtain Ramp Time value */
- + core_if->adp.initial_probe = 1;
- + dwc_otg_adp_probe_start(core_if);
- + } else {
- + gotgctl_data_t gotgctl;
- + gotgctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->gotgctl);
- + DWC_PRINTF("DEVICE MODE\n");
- + if (gotgctl.b.bsesvld == 0) {
- + /* Enable Power Down Logic Interrupt*/
- + gpwrdn.d32 = 0;
- + DWC_PRINTF("VBUS is not valid - start ADP probe\n");
- + gpwrdn.b.pmuintsel = 1;
- + gpwrdn.b.pmuactv = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
- + core_if->adp.initial_probe = 1;
- + dwc_otg_adp_probe_start(core_if);
- + } else {
- + DWC_PRINTF("VBUS is valid - initialize core as a Device\n");
- + core_if->op_state = B_PERIPHERAL;
- + dwc_otg_core_init(core_if);
- + dwc_otg_enable_global_interrupts(core_if);
- + cil_pcd_start(core_if);
- + dwc_otg_dump_global_registers(core_if);
- + dwc_otg_dump_dev_registers(core_if);
- + }
- + }
- +}
- +
- +void dwc_otg_adp_init(dwc_otg_core_if_t * core_if)
- +{
- + core_if->adp.adp_started = 0;
- + core_if->adp.initial_probe = 0;
- + core_if->adp.probe_timer_values[0] = -1;
- + core_if->adp.probe_timer_values[1] = -1;
- + core_if->adp.probe_enabled = 0;
- + core_if->adp.sense_enabled = 0;
- + core_if->adp.sense_timer_started = 0;
- + core_if->adp.vbuson_timer_started = 0;
- + core_if->adp.probe_counter = 0;
- + core_if->adp.gpwrdn = 0;
- + core_if->adp.attached = DWC_OTG_ADP_UNKOWN;
- + /* Initialize timers */
- + core_if->adp.sense_timer =
- + DWC_TIMER_ALLOC("ADP SENSE TIMER", adp_sense_timeout, core_if);
- + core_if->adp.vbuson_timer =
- + DWC_TIMER_ALLOC("ADP VBUS ON TIMER", adp_vbuson_timeout, core_if);
- + if (!core_if->adp.sense_timer || !core_if->adp.vbuson_timer)
- + {
- + DWC_ERROR("Could not allocate memory for ADP timers\n");
- + }
- +}
- +
- +void dwc_otg_adp_remove(dwc_otg_core_if_t * core_if)
- +{
- + gpwrdn_data_t gpwrdn = { .d32 = 0 };
- + gpwrdn.b.pmuintsel = 1;
- + gpwrdn.b.pmuactv = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- +
- + if (core_if->adp.probe_enabled)
- + dwc_otg_adp_probe_stop(core_if);
- + if (core_if->adp.sense_enabled)
- + dwc_otg_adp_sense_stop(core_if);
- + if (core_if->adp.sense_timer_started)
- + DWC_TIMER_CANCEL(core_if->adp.sense_timer);
- + if (core_if->adp.vbuson_timer_started)
- + DWC_TIMER_CANCEL(core_if->adp.vbuson_timer);
- + DWC_TIMER_FREE(core_if->adp.sense_timer);
- + DWC_TIMER_FREE(core_if->adp.vbuson_timer);
- +}
- +
- +/////////////////////////////////////////////////////////////////////
- +////////////// ADP Interrupt Handlers ///////////////////////////////
- +/////////////////////////////////////////////////////////////////////
- +/**
- + * This function sets Ramp Timer values
- + */
- +static uint32_t set_timer_value(dwc_otg_core_if_t * core_if, uint32_t val)
- +{
- + if (core_if->adp.probe_timer_values[0] == -1) {
- + core_if->adp.probe_timer_values[0] = val;
- + core_if->adp.probe_timer_values[1] = -1;
- + return 1;
- + } else {
- + core_if->adp.probe_timer_values[1] =
- + core_if->adp.probe_timer_values[0];
- + core_if->adp.probe_timer_values[0] = val;
- + return 0;
- + }
- +}
- +
- +/**
- + * This function compares Ramp Timer values
- + */
- +static uint32_t compare_timer_values(dwc_otg_core_if_t * core_if)
- +{
- + uint32_t diff;
- + if (core_if->adp.probe_timer_values[0]>=core_if->adp.probe_timer_values[1])
- + diff = core_if->adp.probe_timer_values[0]-core_if->adp.probe_timer_values[1];
- + else
- + diff = core_if->adp.probe_timer_values[1]-core_if->adp.probe_timer_values[0];
- + if(diff < 2) {
- + return 0;
- + } else {
- + return 1;
- + }
- +}
- +
- +/**
- + * This function handles ADP Probe Interrupts
- + */
- +static int32_t dwc_otg_adp_handle_prb_intr(dwc_otg_core_if_t * core_if,
- + uint32_t val)
- +{
- + adpctl_data_t adpctl = {.d32 = 0 };
- + gpwrdn_data_t gpwrdn, temp;
- + adpctl.d32 = val;
- +
- + temp.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
- + core_if->adp.probe_counter++;
- + core_if->adp.gpwrdn = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
- + if (adpctl.b.rtim == 0 && !temp.b.idsts){
- + DWC_PRINTF("RTIM value is 0\n");
- + goto exit;
- + }
- + if (set_timer_value(core_if, adpctl.b.rtim) &&
- + core_if->adp.initial_probe) {
- + core_if->adp.initial_probe = 0;
- + dwc_otg_adp_probe_stop(core_if);
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pmuactv = 1;
- + gpwrdn.b.pmuintsel = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
- +
- + /* check which value is for device mode and which for Host mode */
- + if (!temp.b.idsts) { /* considered host mode value is 0 */
- + /*
- + * Turn on VBUS after initial ADP probe.
- + */
- + core_if->op_state = A_HOST;
- + dwc_otg_enable_global_interrupts(core_if);
- + DWC_SPINUNLOCK(core_if->lock);
- + cil_hcd_start(core_if);
- + dwc_otg_adp_turnon_vbus(core_if);
- + DWC_SPINLOCK(core_if->lock);
- + } else {
- + /*
- + * Initiate SRP after initial ADP probe.
- + */
- + dwc_otg_enable_global_interrupts(core_if);
- + dwc_otg_initiate_srp(core_if);
- + }
- + } else if (core_if->adp.probe_counter > 2){
- + gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
- + if (compare_timer_values(core_if)) {
- + DWC_PRINTF("Difference in timer values !!! \n");
- +// core_if->adp.attached = DWC_OTG_ADP_ATTACHED;
- + dwc_otg_adp_probe_stop(core_if);
- +
- + /* Power on the core */
- + if (core_if->power_down == 2) {
- + gpwrdn.b.pwrdnswtch = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->
- + gpwrdn, 0, gpwrdn.d32);
- + }
- +
- + /* check which value is for device mode and which for Host mode */
- + if (!temp.b.idsts) { /* considered host mode value is 0 */
- + /* Disable Interrupt from Power Down Logic */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pmuintsel = 1;
- + gpwrdn.b.pmuactv = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->
- + gpwrdn, gpwrdn.d32, 0);
- +
- + /*
- + * Initialize the Core for Host mode.
- + */
- + core_if->op_state = A_HOST;
- + dwc_otg_core_init(core_if);
- + dwc_otg_enable_global_interrupts(core_if);
- + cil_hcd_start(core_if);
- + } else {
- + gotgctl_data_t gotgctl;
- + /* Mask SRP detected interrupt from Power Down Logic */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.srp_det_msk = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->
- + gpwrdn, gpwrdn.d32, 0);
- +
- + /* Disable Power Down Logic */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pmuintsel = 1;
- + gpwrdn.b.pmuactv = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->
- + gpwrdn, gpwrdn.d32, 0);
- +
- + /*
- + * Initialize the Core for Device mode.
- + */
- + core_if->op_state = B_PERIPHERAL;
- + dwc_otg_core_init(core_if);
- + dwc_otg_enable_global_interrupts(core_if);
- + cil_pcd_start(core_if);
- +
- + gotgctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->gotgctl);
- + if (!gotgctl.b.bsesvld) {
- + dwc_otg_initiate_srp(core_if);
- + }
- + }
- + }
- + if (core_if->power_down == 2) {
- + if (gpwrdn.b.bsessvld) {
- + /* Mask SRP detected interrupt from Power Down Logic */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.srp_det_msk = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- +
- + /* Disable Power Down Logic */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pmuactv = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- +
- + /*
- + * Initialize the Core for Device mode.
- + */
- + core_if->op_state = B_PERIPHERAL;
- + dwc_otg_core_init(core_if);
- + dwc_otg_enable_global_interrupts(core_if);
- + cil_pcd_start(core_if);
- + }
- + }
- + }
- +exit:
- + /* Clear interrupt */
- + adpctl.d32 = dwc_otg_adp_read_reg(core_if);
- + adpctl.b.adp_prb_int = 1;
- + dwc_otg_adp_write_reg(core_if, adpctl.d32);
- +
- + return 0;
- +}
- +
- +/**
- + * This function hadles ADP Sense Interrupt
- + */
- +static int32_t dwc_otg_adp_handle_sns_intr(dwc_otg_core_if_t * core_if)
- +{
- + adpctl_data_t adpctl;
- + /* Stop ADP Sense timer */
- + DWC_TIMER_CANCEL(core_if->adp.sense_timer);
- +
- + /* Restart ADP Sense timer */
- + dwc_otg_adp_sense_timer_start(core_if);
- +
- + /* Clear interrupt */
- + adpctl.d32 = dwc_otg_adp_read_reg(core_if);
- + adpctl.b.adp_sns_int = 1;
- + dwc_otg_adp_write_reg(core_if, adpctl.d32);
- +
- + return 0;
- +}
- +
- +/**
- + * This function handles ADP Probe Interrupts
- + */
- +static int32_t dwc_otg_adp_handle_prb_tmout_intr(dwc_otg_core_if_t * core_if,
- + uint32_t val)
- +{
- + adpctl_data_t adpctl = {.d32 = 0 };
- + adpctl.d32 = val;
- + set_timer_value(core_if, adpctl.b.rtim);
- +
- + /* Clear interrupt */
- + adpctl.d32 = dwc_otg_adp_read_reg(core_if);
- + adpctl.b.adp_tmout_int = 1;
- + dwc_otg_adp_write_reg(core_if, adpctl.d32);
- +
- + return 0;
- +}
- +
- +/**
- + * ADP Interrupt handler.
- + *
- + */
- +int32_t dwc_otg_adp_handle_intr(dwc_otg_core_if_t * core_if)
- +{
- + int retval = 0;
- + adpctl_data_t adpctl = {.d32 = 0};
- +
- + adpctl.d32 = dwc_otg_adp_read_reg(core_if);
- + DWC_PRINTF("ADPCTL = %08x\n",adpctl.d32);
- +
- + if (adpctl.b.adp_sns_int & adpctl.b.adp_sns_int_msk) {
- + DWC_PRINTF("ADP Sense interrupt\n");
- + retval |= dwc_otg_adp_handle_sns_intr(core_if);
- + }
- + if (adpctl.b.adp_tmout_int & adpctl.b.adp_tmout_int_msk) {
- + DWC_PRINTF("ADP timeout interrupt\n");
- + retval |= dwc_otg_adp_handle_prb_tmout_intr(core_if, adpctl.d32);
- + }
- + if (adpctl.b.adp_prb_int & adpctl.b.adp_prb_int_msk) {
- + DWC_PRINTF("ADP Probe interrupt\n");
- + adpctl.b.adp_prb_int = 1;
- + retval |= dwc_otg_adp_handle_prb_intr(core_if, adpctl.d32);
- + }
- +
- +// dwc_otg_adp_modify_reg(core_if, adpctl.d32, 0);
- + //dwc_otg_adp_write_reg(core_if, adpctl.d32);
- + DWC_PRINTF("RETURN FROM ADP ISR\n");
- +
- + return retval;
- +}
- +
- +/**
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + */
- +int32_t dwc_otg_adp_handle_srp_intr(dwc_otg_core_if_t * core_if)
- +{
- +
- +#ifndef DWC_HOST_ONLY
- + hprt0_data_t hprt0;
- + gpwrdn_data_t gpwrdn;
- + DWC_DEBUGPL(DBG_ANY, "++ Power Down Logic Session Request Interrupt++\n");
- +
- + gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
- + /* check which value is for device mode and which for Host mode */
- + if (!gpwrdn.b.idsts) { /* considered host mode value is 0 */
- + DWC_PRINTF("SRP: Host mode\n");
- +
- + if (core_if->adp_enable) {
- + dwc_otg_adp_probe_stop(core_if);
- +
- + /* Power on the core */
- + if (core_if->power_down == 2) {
- + gpwrdn.b.pwrdnswtch = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->
- + gpwrdn, 0, gpwrdn.d32);
- + }
- +
- + core_if->op_state = A_HOST;
- + dwc_otg_core_init(core_if);
- + dwc_otg_enable_global_interrupts(core_if);
- + cil_hcd_start(core_if);
- + }
- +
- + /* Turn on the port power bit. */
- + hprt0.d32 = dwc_otg_read_hprt0(core_if);
- + hprt0.b.prtpwr = 1;
- + DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
- +
- + /* Start the Connection timer. So a message can be displayed
- + * if connect does not occur within 10 seconds. */
- + cil_hcd_session_start(core_if);
- + } else {
- + DWC_PRINTF("SRP: Device mode %s\n", __FUNCTION__);
- + if (core_if->adp_enable) {
- + dwc_otg_adp_probe_stop(core_if);
- +
- + /* Power on the core */
- + if (core_if->power_down == 2) {
- + gpwrdn.b.pwrdnswtch = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->
- + gpwrdn, 0, gpwrdn.d32);
- + }
- +
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pmuactv = 0;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0,
- + gpwrdn.d32);
- +
- + core_if->op_state = B_PERIPHERAL;
- + dwc_otg_core_init(core_if);
- + dwc_otg_enable_global_interrupts(core_if);
- + cil_pcd_start(core_if);
- + }
- + }
- +#endif
- + return 1;
- +}
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_adp.h
- @@ -0,0 +1,80 @@
- +/* ==========================================================================
- + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_adp.h $
- + * $Revision: #7 $
- + * $Date: 2011/10/24 $
- + * $Change: 1871159 $
- + *
- + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
- + * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
- + * otherwise expressly agreed to in writing between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product under
- + * any End User Software License Agreement or Agreement for Licensed Product
- + * with Synopsys or any supplement thereto. You are permitted to use and
- + * redistribute this Software in source and binary forms, with or without
- + * modification, provided that redistributions of source code must retain this
- + * notice. You may not view, use, disclose, copy or distribute this file or
- + * any information contained herein except pursuant to this license grant from
- + * Synopsys. If you do not agree with this notice, including the disclaimer
- + * below, then you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
- + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- + * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
- + * ========================================================================== */
- +
- +#ifndef __DWC_OTG_ADP_H__
- +#define __DWC_OTG_ADP_H__
- +
- +/**
- + * @file
- + *
- + * This file contains the Attach Detect Protocol interfaces and defines
- + * (functions) and structures for Linux.
- + *
- + */
- +
- +#define DWC_OTG_ADP_UNATTACHED 0
- +#define DWC_OTG_ADP_ATTACHED 1
- +#define DWC_OTG_ADP_UNKOWN 2
- +
- +typedef struct dwc_otg_adp {
- + uint32_t adp_started;
- + uint32_t initial_probe;
- + int32_t probe_timer_values[2];
- + uint32_t probe_enabled;
- + uint32_t sense_enabled;
- + dwc_timer_t *sense_timer;
- + uint32_t sense_timer_started;
- + dwc_timer_t *vbuson_timer;
- + uint32_t vbuson_timer_started;
- + uint32_t attached;
- + uint32_t probe_counter;
- + uint32_t gpwrdn;
- +} dwc_otg_adp_t;
- +
- +/**
- + * Attach Detect Protocol functions
- + */
- +
- +extern void dwc_otg_adp_write_reg(dwc_otg_core_if_t * core_if, uint32_t value);
- +extern uint32_t dwc_otg_adp_read_reg(dwc_otg_core_if_t * core_if);
- +extern uint32_t dwc_otg_adp_probe_start(dwc_otg_core_if_t * core_if);
- +extern uint32_t dwc_otg_adp_sense_start(dwc_otg_core_if_t * core_if);
- +extern uint32_t dwc_otg_adp_probe_stop(dwc_otg_core_if_t * core_if);
- +extern uint32_t dwc_otg_adp_sense_stop(dwc_otg_core_if_t * core_if);
- +extern void dwc_otg_adp_start(dwc_otg_core_if_t * core_if, uint8_t is_host);
- +extern void dwc_otg_adp_init(dwc_otg_core_if_t * core_if);
- +extern void dwc_otg_adp_remove(dwc_otg_core_if_t * core_if);
- +extern int32_t dwc_otg_adp_handle_intr(dwc_otg_core_if_t * core_if);
- +extern int32_t dwc_otg_adp_handle_srp_intr(dwc_otg_core_if_t * core_if);
- +
- +#endif //__DWC_OTG_ADP_H__
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_attr.c
- @@ -0,0 +1,1212 @@
- +/* ==========================================================================
- + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_attr.c $
- + * $Revision: #44 $
- + * $Date: 2010/11/29 $
- + * $Change: 1636033 $
- + *
- + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
- + * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
- + * otherwise expressly agreed to in writing between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product under
- + * any End User Software License Agreement or Agreement for Licensed Product
- + * with Synopsys or any supplement thereto. You are permitted to use and
- + * redistribute this Software in source and binary forms, with or without
- + * modification, provided that redistributions of source code must retain this
- + * notice. You may not view, use, disclose, copy or distribute this file or
- + * any information contained herein except pursuant to this license grant from
- + * Synopsys. If you do not agree with this notice, including the disclaimer
- + * below, then you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
- + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- + * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
- + * ========================================================================== */
- +
- +/** @file
- + *
- + * The diagnostic interface will provide access to the controller for
- + * bringing up the hardware and testing. The Linux driver attributes
- + * feature will be used to provide the Linux Diagnostic
- + * Interface. These attributes are accessed through sysfs.
- + */
- +
- +/** @page "Linux Module Attributes"
- + *
- + * The Linux module attributes feature is used to provide the Linux
- + * Diagnostic Interface. These attributes are accessed through sysfs.
- + * The diagnostic interface will provide access to the controller for
- + * bringing up the hardware and testing.
- +
- + The following table shows the attributes.
- + <table>
- + <tr>
- + <td><b> Name</b></td>
- + <td><b> Description</b></td>
- + <td><b> Access</b></td>
- + </tr>
- +
- + <tr>
- + <td> mode </td>
- + <td> Returns the current mode: 0 for device mode, 1 for host mode</td>
- + <td> Read</td>
- + </tr>
- +
- + <tr>
- + <td> hnpcapable </td>
- + <td> Gets or sets the "HNP-capable" bit in the Core USB Configuraton Register.
- + Read returns the current value.</td>
- + <td> Read/Write</td>
- + </tr>
- +
- + <tr>
- + <td> srpcapable </td>
- + <td> Gets or sets the "SRP-capable" bit in the Core USB Configuraton Register.
- + Read returns the current value.</td>
- + <td> Read/Write</td>
- + </tr>
- +
- + <tr>
- + <td> hsic_connect </td>
- + <td> Gets or sets the "HSIC-Connect" bit in the GLPMCFG Register.
- + Read returns the current value.</td>
- + <td> Read/Write</td>
- + </tr>
- +
- + <tr>
- + <td> inv_sel_hsic </td>
- + <td> Gets or sets the "Invert Select HSIC" bit in the GLPMFG Register.
- + Read returns the current value.</td>
- + <td> Read/Write</td>
- + </tr>
- +
- + <tr>
- + <td> hnp </td>
- + <td> Initiates the Host Negotiation Protocol. Read returns the status.</td>
- + <td> Read/Write</td>
- + </tr>
- +
- + <tr>
- + <td> srp </td>
- + <td> Initiates the Session Request Protocol. Read returns the status.</td>
- + <td> Read/Write</td>
- + </tr>
- +
- + <tr>
- + <td> buspower </td>
- + <td> Gets or sets the Power State of the bus (0 - Off or 1 - On)</td>
- + <td> Read/Write</td>
- + </tr>
- +
- + <tr>
- + <td> bussuspend </td>
- + <td> Suspends the USB bus.</td>
- + <td> Read/Write</td>
- + </tr>
- +
- + <tr>
- + <td> busconnected </td>
- + <td> Gets the connection status of the bus</td>
- + <td> Read</td>
- + </tr>
- +
- + <tr>
- + <td> gotgctl </td>
- + <td> Gets or sets the Core Control Status Register.</td>
- + <td> Read/Write</td>
- + </tr>
- +
- + <tr>
- + <td> gusbcfg </td>
- + <td> Gets or sets the Core USB Configuration Register</td>
- + <td> Read/Write</td>
- + </tr>
- +
- + <tr>
- + <td> grxfsiz </td>
- + <td> Gets or sets the Receive FIFO Size Register</td>
- + <td> Read/Write</td>
- + </tr>
- +
- + <tr>
- + <td> gnptxfsiz </td>
- + <td> Gets or sets the non-periodic Transmit Size Register</td>
- + <td> Read/Write</td>
- + </tr>
- +
- + <tr>
- + <td> gpvndctl </td>
- + <td> Gets or sets the PHY Vendor Control Register</td>
- + <td> Read/Write</td>
- + </tr>
- +
- + <tr>
- + <td> ggpio </td>
- + <td> Gets the value in the lower 16-bits of the General Purpose IO Register
- + or sets the upper 16 bits.</td>
- + <td> Read/Write</td>
- + </tr>
- +
- + <tr>
- + <td> guid </td>
- + <td> Gets or sets the value of the User ID Register</td>
- + <td> Read/Write</td>
- + </tr>
- +
- + <tr>
- + <td> gsnpsid </td>
- + <td> Gets the value of the Synopsys ID Regester</td>
- + <td> Read</td>
- + </tr>
- +
- + <tr>
- + <td> devspeed </td>
- + <td> Gets or sets the device speed setting in the DCFG register</td>
- + <td> Read/Write</td>
- + </tr>
- +
- + <tr>
- + <td> enumspeed </td>
- + <td> Gets the device enumeration Speed.</td>
- + <td> Read</td>
- + </tr>
- +
- + <tr>
- + <td> hptxfsiz </td>
- + <td> Gets the value of the Host Periodic Transmit FIFO</td>
- + <td> Read</td>
- + </tr>
- +
- + <tr>
- + <td> hprt0 </td>
- + <td> Gets or sets the value in the Host Port Control and Status Register</td>
- + <td> Read/Write</td>
- + </tr>
- +
- + <tr>
- + <td> regoffset </td>
- + <td> Sets the register offset for the next Register Access</td>
- + <td> Read/Write</td>
- + </tr>
- +
- + <tr>
- + <td> regvalue </td>
- + <td> Gets or sets the value of the register at the offset in the regoffset attribute.</td>
- + <td> Read/Write</td>
- + </tr>
- +
- + <tr>
- + <td> remote_wakeup </td>
- + <td> On read, shows the status of Remote Wakeup. On write, initiates a remote
- + wakeup of the host. When bit 0 is 1 and Remote Wakeup is enabled, the Remote
- + Wakeup signalling bit in the Device Control Register is set for 1
- + milli-second.</td>
- + <td> Read/Write</td>
- + </tr>
- +
- + <tr>
- + <td> rem_wakeup_pwrdn </td>
- + <td> On read, shows the status core - hibernated or not. On write, initiates
- + a remote wakeup of the device from Hibernation. </td>
- + <td> Read/Write</td>
- + </tr>
- +
- + <tr>
- + <td> mode_ch_tim_en </td>
- + <td> This bit is used to enable or disable the host core to wait for 200 PHY
- + clock cycles at the end of Resume to change the opmode signal to the PHY to 00
- + after Suspend or LPM. </td>
- + <td> Read/Write</td>
- + </tr>
- +
- + <tr>
- + <td> fr_interval </td>
- + <td> On read, shows the value of HFIR Frame Interval. On write, dynamically
- + reload HFIR register during runtime. The application can write a value to this
- + register only after the Port Enable bit of the Host Port Control and Status
- + register (HPRT.PrtEnaPort) has been set </td>
- + <td> Read/Write</td>
- + </tr>
- +
- + <tr>
- + <td> disconnect_us </td>
- + <td> On read, shows the status of disconnect_device_us. On write, sets disconnect_us
- + which causes soft disconnect for 100us. Applicable only for device mode of operation.</td>
- + <td> Read/Write</td>
- + </tr>
- +
- + <tr>
- + <td> regdump </td>
- + <td> Dumps the contents of core registers.</td>
- + <td> Read</td>
- + </tr>
- +
- + <tr>
- + <td> spramdump </td>
- + <td> Dumps the contents of core registers.</td>
- + <td> Read</td>
- + </tr>
- +
- + <tr>
- + <td> hcddump </td>
- + <td> Dumps the current HCD state.</td>
- + <td> Read</td>
- + </tr>
- +
- + <tr>
- + <td> hcd_frrem </td>
- + <td> Shows the average value of the Frame Remaining
- + field in the Host Frame Number/Frame Remaining register when an SOF interrupt
- + occurs. This can be used to determine the average interrupt latency. Also
- + shows the average Frame Remaining value for start_transfer and the "a" and
- + "b" sample points. The "a" and "b" sample points may be used during debugging
- + bto determine how long it takes to execute a section of the HCD code.</td>
- + <td> Read</td>
- + </tr>
- +
- + <tr>
- + <td> rd_reg_test </td>
- + <td> Displays the time required to read the GNPTXFSIZ register many times
- + (the output shows the number of times the register is read).
- + <td> Read</td>
- + </tr>
- +
- + <tr>
- + <td> wr_reg_test </td>
- + <td> Displays the time required to write the GNPTXFSIZ register many times
- + (the output shows the number of times the register is written).
- + <td> Read</td>
- + </tr>
- +
- + <tr>
- + <td> lpm_response </td>
- + <td> Gets or sets lpm_response mode. Applicable only in device mode.
- + <td> Write</td>
- + </tr>
- +
- + <tr>
- + <td> sleep_status </td>
- + <td> Shows sleep status of device.
- + <td> Read</td>
- + </tr>
- +
- + </table>
- +
- + Example usage:
- + To get the current mode:
- + cat /sys/devices/lm0/mode
- +
- + To power down the USB:
- + echo 0 > /sys/devices/lm0/buspower
- + */
- +
- +#include "dwc_otg_os_dep.h"
- +#include "dwc_os.h"
- +#include "dwc_otg_driver.h"
- +#include "dwc_otg_attr.h"
- +#include "dwc_otg_core_if.h"
- +#include "dwc_otg_pcd_if.h"
- +#include "dwc_otg_hcd_if.h"
- +
- +/*
- + * MACROs for defining sysfs attribute
- + */
- +#ifdef LM_INTERFACE
- +
- +#define DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_string_) \
- +static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \
- +{ \
- + struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); \
- + dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); \
- + uint32_t val; \
- + val = dwc_otg_get_##_otg_attr_name_ (otg_dev->core_if); \
- + return sprintf (buf, "%s = 0x%x\n", _string_, val); \
- +}
- +#define DWC_OTG_DEVICE_ATTR_BITFIELD_STORE(_otg_attr_name_,_string_) \
- +static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \
- + const char *buf, size_t count) \
- +{ \
- + struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); \
- + dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); \
- + uint32_t set = simple_strtoul(buf, NULL, 16); \
- + dwc_otg_set_##_otg_attr_name_(otg_dev->core_if, set);\
- + return count; \
- +}
- +
- +#elif defined(PCI_INTERFACE)
- +
- +#define DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_string_) \
- +static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \
- +{ \
- + dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); \
- + uint32_t val; \
- + val = dwc_otg_get_##_otg_attr_name_ (otg_dev->core_if); \
- + return sprintf (buf, "%s = 0x%x\n", _string_, val); \
- +}
- +#define DWC_OTG_DEVICE_ATTR_BITFIELD_STORE(_otg_attr_name_,_string_) \
- +static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \
- + const char *buf, size_t count) \
- +{ \
- + dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); \
- + uint32_t set = simple_strtoul(buf, NULL, 16); \
- + dwc_otg_set_##_otg_attr_name_(otg_dev->core_if, set);\
- + return count; \
- +}
- +
- +#elif defined(PLATFORM_INTERFACE)
- +
- +#define DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_string_) \
- +static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \
- +{ \
- + struct platform_device *platform_dev = \
- + container_of(_dev, struct platform_device, dev); \
- + dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); \
- + uint32_t val; \
- + DWC_PRINTF("%s(%p) -> platform_dev %p, otg_dev %p\n", \
- + __func__, _dev, platform_dev, otg_dev); \
- + val = dwc_otg_get_##_otg_attr_name_ (otg_dev->core_if); \
- + return sprintf (buf, "%s = 0x%x\n", _string_, val); \
- +}
- +#define DWC_OTG_DEVICE_ATTR_BITFIELD_STORE(_otg_attr_name_,_string_) \
- +static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \
- + const char *buf, size_t count) \
- +{ \
- + struct platform_device *platform_dev = container_of(_dev, struct platform_device, dev); \
- + dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); \
- + uint32_t set = simple_strtoul(buf, NULL, 16); \
- + dwc_otg_set_##_otg_attr_name_(otg_dev->core_if, set);\
- + return count; \
- +}
- +#endif
- +
- +/*
- + * MACROs for defining sysfs attribute for 32-bit registers
- + */
- +#ifdef LM_INTERFACE
- +#define DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_string_) \
- +static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \
- +{ \
- + struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); \
- + dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); \
- + uint32_t val; \
- + val = dwc_otg_get_##_otg_attr_name_ (otg_dev->core_if); \
- + return sprintf (buf, "%s = 0x%08x\n", _string_, val); \
- +}
- +#define DWC_OTG_DEVICE_ATTR_REG_STORE(_otg_attr_name_,_string_) \
- +static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \
- + const char *buf, size_t count) \
- +{ \
- + struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); \
- + dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); \
- + uint32_t val = simple_strtoul(buf, NULL, 16); \
- + dwc_otg_set_##_otg_attr_name_ (otg_dev->core_if, val); \
- + return count; \
- +}
- +#elif defined(PCI_INTERFACE)
- +#define DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_string_) \
- +static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \
- +{ \
- + dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); \
- + uint32_t val; \
- + val = dwc_otg_get_##_otg_attr_name_ (otg_dev->core_if); \
- + return sprintf (buf, "%s = 0x%08x\n", _string_, val); \
- +}
- +#define DWC_OTG_DEVICE_ATTR_REG_STORE(_otg_attr_name_,_string_) \
- +static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \
- + const char *buf, size_t count) \
- +{ \
- + dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); \
- + uint32_t val = simple_strtoul(buf, NULL, 16); \
- + dwc_otg_set_##_otg_attr_name_ (otg_dev->core_if, val); \
- + return count; \
- +}
- +
- +#elif defined(PLATFORM_INTERFACE)
- +#include "dwc_otg_dbg.h"
- +#define DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_string_) \
- +static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \
- +{ \
- + struct platform_device *platform_dev = container_of(_dev, struct platform_device, dev); \
- + dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); \
- + uint32_t val; \
- + DWC_PRINTF("%s(%p) -> platform_dev %p, otg_dev %p\n", \
- + __func__, _dev, platform_dev, otg_dev); \
- + val = dwc_otg_get_##_otg_attr_name_ (otg_dev->core_if); \
- + return sprintf (buf, "%s = 0x%08x\n", _string_, val); \
- +}
- +#define DWC_OTG_DEVICE_ATTR_REG_STORE(_otg_attr_name_,_string_) \
- +static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \
- + const char *buf, size_t count) \
- +{ \
- + struct platform_device *platform_dev = container_of(_dev, struct platform_device, dev); \
- + dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); \
- + uint32_t val = simple_strtoul(buf, NULL, 16); \
- + dwc_otg_set_##_otg_attr_name_ (otg_dev->core_if, val); \
- + return count; \
- +}
- +
- +#endif
- +
- +#define DWC_OTG_DEVICE_ATTR_BITFIELD_RW(_otg_attr_name_,_string_) \
- +DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_string_) \
- +DWC_OTG_DEVICE_ATTR_BITFIELD_STORE(_otg_attr_name_,_string_) \
- +DEVICE_ATTR(_otg_attr_name_,0644,_otg_attr_name_##_show,_otg_attr_name_##_store);
- +
- +#define DWC_OTG_DEVICE_ATTR_BITFIELD_RO(_otg_attr_name_,_string_) \
- +DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_string_) \
- +DEVICE_ATTR(_otg_attr_name_,0444,_otg_attr_name_##_show,NULL);
- +
- +#define DWC_OTG_DEVICE_ATTR_REG32_RW(_otg_attr_name_,_addr_,_string_) \
- +DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_string_) \
- +DWC_OTG_DEVICE_ATTR_REG_STORE(_otg_attr_name_,_string_) \
- +DEVICE_ATTR(_otg_attr_name_,0644,_otg_attr_name_##_show,_otg_attr_name_##_store);
- +
- +#define DWC_OTG_DEVICE_ATTR_REG32_RO(_otg_attr_name_,_addr_,_string_) \
- +DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_string_) \
- +DEVICE_ATTR(_otg_attr_name_,0444,_otg_attr_name_##_show,NULL);
- +
- +/** @name Functions for Show/Store of Attributes */
- +/**@{*/
- +
- +/**
- + * Helper function returning the otg_device structure of the given device
- + */
- +static dwc_otg_device_t *dwc_otg_drvdev(struct device *_dev)
- +{
- + dwc_otg_device_t *otg_dev;
- + DWC_OTG_GETDRVDEV(otg_dev, _dev);
- + return otg_dev;
- +}
- +
- +/**
- + * Show the register offset of the Register Access.
- + */
- +static ssize_t regoffset_show(struct device *_dev,
- + struct device_attribute *attr, char *buf)
- +{
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- + return snprintf(buf, sizeof("0xFFFFFFFF\n") + 1, "0x%08x\n",
- + otg_dev->os_dep.reg_offset);
- +}
- +
- +/**
- + * Set the register offset for the next Register Access Read/Write
- + */
- +static ssize_t regoffset_store(struct device *_dev,
- + struct device_attribute *attr,
- + const char *buf, size_t count)
- +{
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- + uint32_t offset = simple_strtoul(buf, NULL, 16);
- +#if defined(LM_INTERFACE) || defined(PLATFORM_INTERFACE)
- + if (offset < SZ_256K) {
- +#elif defined(PCI_INTERFACE)
- + if (offset < 0x00040000) {
- +#endif
- + otg_dev->os_dep.reg_offset = offset;
- + } else {
- + dev_err(_dev, "invalid offset\n");
- + }
- +
- + return count;
- +}
- +
- +DEVICE_ATTR(regoffset, S_IRUGO | S_IWUSR, regoffset_show, regoffset_store);
- +
- +/**
- + * Show the value of the register at the offset in the reg_offset
- + * attribute.
- + */
- +static ssize_t regvalue_show(struct device *_dev,
- + struct device_attribute *attr, char *buf)
- +{
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- + uint32_t val;
- + volatile uint32_t *addr;
- +
- + if (otg_dev->os_dep.reg_offset != 0xFFFFFFFF && 0 != otg_dev->os_dep.base) {
- + /* Calculate the address */
- + addr = (uint32_t *) (otg_dev->os_dep.reg_offset +
- + (uint8_t *) otg_dev->os_dep.base);
- + val = DWC_READ_REG32(addr);
- + return snprintf(buf,
- + sizeof("Reg@0xFFFFFFFF = 0xFFFFFFFF\n") + 1,
- + "Reg@0x%06x = 0x%08x\n", otg_dev->os_dep.reg_offset,
- + val);
- + } else {
- + dev_err(_dev, "Invalid offset (0x%0x)\n", otg_dev->os_dep.reg_offset);
- + return sprintf(buf, "invalid offset\n");
- + }
- +}
- +
- +/**
- + * Store the value in the register at the offset in the reg_offset
- + * attribute.
- + *
- + */
- +static ssize_t regvalue_store(struct device *_dev,
- + struct device_attribute *attr,
- + const char *buf, size_t count)
- +{
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- + volatile uint32_t *addr;
- + uint32_t val = simple_strtoul(buf, NULL, 16);
- + //dev_dbg(_dev, "Offset=0x%08x Val=0x%08x\n", otg_dev->reg_offset, val);
- + if (otg_dev->os_dep.reg_offset != 0xFFFFFFFF && 0 != otg_dev->os_dep.base) {
- + /* Calculate the address */
- + addr = (uint32_t *) (otg_dev->os_dep.reg_offset +
- + (uint8_t *) otg_dev->os_dep.base);
- + DWC_WRITE_REG32(addr, val);
- + } else {
- + dev_err(_dev, "Invalid Register Offset (0x%08x)\n",
- + otg_dev->os_dep.reg_offset);
- + }
- + return count;
- +}
- +
- +DEVICE_ATTR(regvalue, S_IRUGO | S_IWUSR, regvalue_show, regvalue_store);
- +
- +/*
- + * Attributes
- + */
- +DWC_OTG_DEVICE_ATTR_BITFIELD_RO(mode, "Mode");
- +DWC_OTG_DEVICE_ATTR_BITFIELD_RW(hnpcapable, "HNPCapable");
- +DWC_OTG_DEVICE_ATTR_BITFIELD_RW(srpcapable, "SRPCapable");
- +DWC_OTG_DEVICE_ATTR_BITFIELD_RW(hsic_connect, "HSIC Connect");
- +DWC_OTG_DEVICE_ATTR_BITFIELD_RW(inv_sel_hsic, "Invert Select HSIC");
- +
- +//DWC_OTG_DEVICE_ATTR_BITFIELD_RW(buspower,&(otg_dev->core_if->core_global_regs->gotgctl),(1<<8),8,"Mode");
- +//DWC_OTG_DEVICE_ATTR_BITFIELD_RW(bussuspend,&(otg_dev->core_if->core_global_regs->gotgctl),(1<<8),8,"Mode");
- +DWC_OTG_DEVICE_ATTR_BITFIELD_RO(busconnected, "Bus Connected");
- +
- +DWC_OTG_DEVICE_ATTR_REG32_RW(gotgctl, 0, "GOTGCTL");
- +DWC_OTG_DEVICE_ATTR_REG32_RW(gusbcfg,
- + &(otg_dev->core_if->core_global_regs->gusbcfg),
- + "GUSBCFG");
- +DWC_OTG_DEVICE_ATTR_REG32_RW(grxfsiz,
- + &(otg_dev->core_if->core_global_regs->grxfsiz),
- + "GRXFSIZ");
- +DWC_OTG_DEVICE_ATTR_REG32_RW(gnptxfsiz,
- + &(otg_dev->core_if->core_global_regs->gnptxfsiz),
- + "GNPTXFSIZ");
- +DWC_OTG_DEVICE_ATTR_REG32_RW(gpvndctl,
- + &(otg_dev->core_if->core_global_regs->gpvndctl),
- + "GPVNDCTL");
- +DWC_OTG_DEVICE_ATTR_REG32_RW(ggpio,
- + &(otg_dev->core_if->core_global_regs->ggpio),
- + "GGPIO");
- +DWC_OTG_DEVICE_ATTR_REG32_RW(guid, &(otg_dev->core_if->core_global_regs->guid),
- + "GUID");
- +DWC_OTG_DEVICE_ATTR_REG32_RO(gsnpsid,
- + &(otg_dev->core_if->core_global_regs->gsnpsid),
- + "GSNPSID");
- +DWC_OTG_DEVICE_ATTR_BITFIELD_RW(devspeed, "Device Speed");
- +DWC_OTG_DEVICE_ATTR_BITFIELD_RO(enumspeed, "Device Enumeration Speed");
- +
- +DWC_OTG_DEVICE_ATTR_REG32_RO(hptxfsiz,
- + &(otg_dev->core_if->core_global_regs->hptxfsiz),
- + "HPTXFSIZ");
- +DWC_OTG_DEVICE_ATTR_REG32_RW(hprt0, otg_dev->core_if->host_if->hprt0, "HPRT0");
- +
- +/**
- + * @todo Add code to initiate the HNP.
- + */
- +/**
- + * Show the HNP status bit
- + */
- +static ssize_t hnp_show(struct device *_dev,
- + struct device_attribute *attr, char *buf)
- +{
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- + return sprintf(buf, "HstNegScs = 0x%x\n",
- + dwc_otg_get_hnpstatus(otg_dev->core_if));
- +}
- +
- +/**
- + * Set the HNP Request bit
- + */
- +static ssize_t hnp_store(struct device *_dev,
- + struct device_attribute *attr,
- + const char *buf, size_t count)
- +{
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- + uint32_t in = simple_strtoul(buf, NULL, 16);
- + dwc_otg_set_hnpreq(otg_dev->core_if, in);
- + return count;
- +}
- +
- +DEVICE_ATTR(hnp, 0644, hnp_show, hnp_store);
- +
- +/**
- + * @todo Add code to initiate the SRP.
- + */
- +/**
- + * Show the SRP status bit
- + */
- +static ssize_t srp_show(struct device *_dev,
- + struct device_attribute *attr, char *buf)
- +{
- +#ifndef DWC_HOST_ONLY
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- + return sprintf(buf, "SesReqScs = 0x%x\n",
- + dwc_otg_get_srpstatus(otg_dev->core_if));
- +#else
- + return sprintf(buf, "Host Only Mode!\n");
- +#endif
- +}
- +
- +/**
- + * Set the SRP Request bit
- + */
- +static ssize_t srp_store(struct device *_dev,
- + struct device_attribute *attr,
- + const char *buf, size_t count)
- +{
- +#ifndef DWC_HOST_ONLY
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- + dwc_otg_pcd_initiate_srp(otg_dev->pcd);
- +#endif
- + return count;
- +}
- +
- +DEVICE_ATTR(srp, 0644, srp_show, srp_store);
- +
- +/**
- + * @todo Need to do more for power on/off?
- + */
- +/**
- + * Show the Bus Power status
- + */
- +static ssize_t buspower_show(struct device *_dev,
- + struct device_attribute *attr, char *buf)
- +{
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- + return sprintf(buf, "Bus Power = 0x%x\n",
- + dwc_otg_get_prtpower(otg_dev->core_if));
- +}
- +
- +/**
- + * Set the Bus Power status
- + */
- +static ssize_t buspower_store(struct device *_dev,
- + struct device_attribute *attr,
- + const char *buf, size_t count)
- +{
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- + uint32_t on = simple_strtoul(buf, NULL, 16);
- + dwc_otg_set_prtpower(otg_dev->core_if, on);
- + return count;
- +}
- +
- +DEVICE_ATTR(buspower, 0644, buspower_show, buspower_store);
- +
- +/**
- + * @todo Need to do more for suspend?
- + */
- +/**
- + * Show the Bus Suspend status
- + */
- +static ssize_t bussuspend_show(struct device *_dev,
- + struct device_attribute *attr, char *buf)
- +{
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- + return sprintf(buf, "Bus Suspend = 0x%x\n",
- + dwc_otg_get_prtsuspend(otg_dev->core_if));
- +}
- +
- +/**
- + * Set the Bus Suspend status
- + */
- +static ssize_t bussuspend_store(struct device *_dev,
- + struct device_attribute *attr,
- + const char *buf, size_t count)
- +{
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- + uint32_t in = simple_strtoul(buf, NULL, 16);
- + dwc_otg_set_prtsuspend(otg_dev->core_if, in);
- + return count;
- +}
- +
- +DEVICE_ATTR(bussuspend, 0644, bussuspend_show, bussuspend_store);
- +
- +/**
- + * Show the Mode Change Ready Timer status
- + */
- +static ssize_t mode_ch_tim_en_show(struct device *_dev,
- + struct device_attribute *attr, char *buf)
- +{
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- + return sprintf(buf, "Mode Change Ready Timer Enable = 0x%x\n",
- + dwc_otg_get_mode_ch_tim(otg_dev->core_if));
- +}
- +
- +/**
- + * Set the Mode Change Ready Timer status
- + */
- +static ssize_t mode_ch_tim_en_store(struct device *_dev,
- + struct device_attribute *attr,
- + const char *buf, size_t count)
- +{
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- + uint32_t in = simple_strtoul(buf, NULL, 16);
- + dwc_otg_set_mode_ch_tim(otg_dev->core_if, in);
- + return count;
- +}
- +
- +DEVICE_ATTR(mode_ch_tim_en, 0644, mode_ch_tim_en_show, mode_ch_tim_en_store);
- +
- +/**
- + * Show the value of HFIR Frame Interval bitfield
- + */
- +static ssize_t fr_interval_show(struct device *_dev,
- + struct device_attribute *attr, char *buf)
- +{
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- + return sprintf(buf, "Frame Interval = 0x%x\n",
- + dwc_otg_get_fr_interval(otg_dev->core_if));
- +}
- +
- +/**
- + * Set the HFIR Frame Interval value
- + */
- +static ssize_t fr_interval_store(struct device *_dev,
- + struct device_attribute *attr,
- + const char *buf, size_t count)
- +{
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- + uint32_t in = simple_strtoul(buf, NULL, 10);
- + dwc_otg_set_fr_interval(otg_dev->core_if, in);
- + return count;
- +}
- +
- +DEVICE_ATTR(fr_interval, 0644, fr_interval_show, fr_interval_store);
- +
- +/**
- + * Show the status of Remote Wakeup.
- + */
- +static ssize_t remote_wakeup_show(struct device *_dev,
- + struct device_attribute *attr, char *buf)
- +{
- +#ifndef DWC_HOST_ONLY
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- +
- + return sprintf(buf,
- + "Remote Wakeup Sig = %d Enabled = %d LPM Remote Wakeup = %d\n",
- + dwc_otg_get_remotewakesig(otg_dev->core_if),
- + dwc_otg_pcd_get_rmwkup_enable(otg_dev->pcd),
- + dwc_otg_get_lpm_remotewakeenabled(otg_dev->core_if));
- +#else
- + return sprintf(buf, "Host Only Mode!\n");
- +#endif /* DWC_HOST_ONLY */
- +}
- +
- +/**
- + * Initiate a remote wakeup of the host. The Device control register
- + * Remote Wakeup Signal bit is written if the PCD Remote wakeup enable
- + * flag is set.
- + *
- + */
- +static ssize_t remote_wakeup_store(struct device *_dev,
- + struct device_attribute *attr,
- + const char *buf, size_t count)
- +{
- +#ifndef DWC_HOST_ONLY
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- + uint32_t val = simple_strtoul(buf, NULL, 16);
- +
- + if (val & 1) {
- + dwc_otg_pcd_remote_wakeup(otg_dev->pcd, 1);
- + } else {
- + dwc_otg_pcd_remote_wakeup(otg_dev->pcd, 0);
- + }
- +#endif /* DWC_HOST_ONLY */
- + return count;
- +}
- +
- +DEVICE_ATTR(remote_wakeup, S_IRUGO | S_IWUSR, remote_wakeup_show,
- + remote_wakeup_store);
- +
- +/**
- + * Show the whether core is hibernated or not.
- + */
- +static ssize_t rem_wakeup_pwrdn_show(struct device *_dev,
- + struct device_attribute *attr, char *buf)
- +{
- +#ifndef DWC_HOST_ONLY
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- +
- + if (dwc_otg_get_core_state(otg_dev->core_if)) {
- + DWC_PRINTF("Core is in hibernation\n");
- + } else {
- + DWC_PRINTF("Core is not in hibernation\n");
- + }
- +#endif /* DWC_HOST_ONLY */
- + return 0;
- +}
- +
- +extern int dwc_otg_device_hibernation_restore(dwc_otg_core_if_t * core_if,
- + int rem_wakeup, int reset);
- +
- +/**
- + * Initiate a remote wakeup of the device to exit from hibernation.
- + */
- +static ssize_t rem_wakeup_pwrdn_store(struct device *_dev,
- + struct device_attribute *attr,
- + const char *buf, size_t count)
- +{
- +#ifndef DWC_HOST_ONLY
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- + dwc_otg_device_hibernation_restore(otg_dev->core_if, 1, 0);
- +#endif
- + return count;
- +}
- +
- +DEVICE_ATTR(rem_wakeup_pwrdn, S_IRUGO | S_IWUSR, rem_wakeup_pwrdn_show,
- + rem_wakeup_pwrdn_store);
- +
- +static ssize_t disconnect_us(struct device *_dev,
- + struct device_attribute *attr,
- + const char *buf, size_t count)
- +{
- +
- +#ifndef DWC_HOST_ONLY
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- + uint32_t val = simple_strtoul(buf, NULL, 16);
- + DWC_PRINTF("The Passed value is %04x\n", val);
- +
- + dwc_otg_pcd_disconnect_us(otg_dev->pcd, 50);
- +
- +#endif /* DWC_HOST_ONLY */
- + return count;
- +}
- +
- +DEVICE_ATTR(disconnect_us, S_IWUSR, 0, disconnect_us);
- +
- +/**
- + * Dump global registers and either host or device registers (depending on the
- + * current mode of the core).
- + */
- +static ssize_t regdump_show(struct device *_dev,
- + struct device_attribute *attr, char *buf)
- +{
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- +
- + dwc_otg_dump_global_registers(otg_dev->core_if);
- + if (dwc_otg_is_host_mode(otg_dev->core_if)) {
- + dwc_otg_dump_host_registers(otg_dev->core_if);
- + } else {
- + dwc_otg_dump_dev_registers(otg_dev->core_if);
- +
- + }
- + return sprintf(buf, "Register Dump\n");
- +}
- +
- +DEVICE_ATTR(regdump, S_IRUGO, regdump_show, 0);
- +
- +/**
- + * Dump global registers and either host or device registers (depending on the
- + * current mode of the core).
- + */
- +static ssize_t spramdump_show(struct device *_dev,
- + struct device_attribute *attr, char *buf)
- +{
- +#if 0
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- +
- + dwc_otg_dump_spram(otg_dev->core_if);
- +#endif
- +
- + return sprintf(buf, "SPRAM Dump\n");
- +}
- +
- +DEVICE_ATTR(spramdump, S_IRUGO, spramdump_show, 0);
- +
- +/**
- + * Dump the current hcd state.
- + */
- +static ssize_t hcddump_show(struct device *_dev,
- + struct device_attribute *attr, char *buf)
- +{
- +#ifndef DWC_DEVICE_ONLY
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- + dwc_otg_hcd_dump_state(otg_dev->hcd);
- +#endif /* DWC_DEVICE_ONLY */
- + return sprintf(buf, "HCD Dump\n");
- +}
- +
- +DEVICE_ATTR(hcddump, S_IRUGO, hcddump_show, 0);
- +
- +/**
- + * Dump the average frame remaining at SOF. This can be used to
- + * determine average interrupt latency. Frame remaining is also shown for
- + * start transfer and two additional sample points.
- + */
- +static ssize_t hcd_frrem_show(struct device *_dev,
- + struct device_attribute *attr, char *buf)
- +{
- +#ifndef DWC_DEVICE_ONLY
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- +
- + dwc_otg_hcd_dump_frrem(otg_dev->hcd);
- +#endif /* DWC_DEVICE_ONLY */
- + return sprintf(buf, "HCD Dump Frame Remaining\n");
- +}
- +
- +DEVICE_ATTR(hcd_frrem, S_IRUGO, hcd_frrem_show, 0);
- +
- +/**
- + * Displays the time required to read the GNPTXFSIZ register many times (the
- + * output shows the number of times the register is read).
- + */
- +#define RW_REG_COUNT 10000000
- +#define MSEC_PER_JIFFIE 1000/HZ
- +static ssize_t rd_reg_test_show(struct device *_dev,
- + struct device_attribute *attr, char *buf)
- +{
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- + int i;
- + int time;
- + int start_jiffies;
- +
- + printk("HZ %d, MSEC_PER_JIFFIE %d, loops_per_jiffy %lu\n",
- + HZ, MSEC_PER_JIFFIE, loops_per_jiffy);
- + start_jiffies = jiffies;
- + for (i = 0; i < RW_REG_COUNT; i++) {
- + dwc_otg_get_gnptxfsiz(otg_dev->core_if);
- + }
- + time = jiffies - start_jiffies;
- + return sprintf(buf,
- + "Time to read GNPTXFSIZ reg %d times: %d msecs (%d jiffies)\n",
- + RW_REG_COUNT, time * MSEC_PER_JIFFIE, time);
- +}
- +
- +DEVICE_ATTR(rd_reg_test, S_IRUGO, rd_reg_test_show, 0);
- +
- +/**
- + * Displays the time required to write the GNPTXFSIZ register many times (the
- + * output shows the number of times the register is written).
- + */
- +static ssize_t wr_reg_test_show(struct device *_dev,
- + struct device_attribute *attr, char *buf)
- +{
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- + uint32_t reg_val;
- + int i;
- + int time;
- + int start_jiffies;
- +
- + printk("HZ %d, MSEC_PER_JIFFIE %d, loops_per_jiffy %lu\n",
- + HZ, MSEC_PER_JIFFIE, loops_per_jiffy);
- + reg_val = dwc_otg_get_gnptxfsiz(otg_dev->core_if);
- + start_jiffies = jiffies;
- + for (i = 0; i < RW_REG_COUNT; i++) {
- + dwc_otg_set_gnptxfsiz(otg_dev->core_if, reg_val);
- + }
- + time = jiffies - start_jiffies;
- + return sprintf(buf,
- + "Time to write GNPTXFSIZ reg %d times: %d msecs (%d jiffies)\n",
- + RW_REG_COUNT, time * MSEC_PER_JIFFIE, time);
- +}
- +
- +DEVICE_ATTR(wr_reg_test, S_IRUGO, wr_reg_test_show, 0);
- +
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- +
- +/**
- +* Show the lpm_response attribute.
- +*/
- +static ssize_t lpmresp_show(struct device *_dev,
- + struct device_attribute *attr, char *buf)
- +{
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- +
- + if (!dwc_otg_get_param_lpm_enable(otg_dev->core_if))
- + return sprintf(buf, "** LPM is DISABLED **\n");
- +
- + if (!dwc_otg_is_device_mode(otg_dev->core_if)) {
- + return sprintf(buf, "** Current mode is not device mode\n");
- + }
- + return sprintf(buf, "lpm_response = %d\n",
- + dwc_otg_get_lpmresponse(otg_dev->core_if));
- +}
- +
- +/**
- +* Store the lpm_response attribute.
- +*/
- +static ssize_t lpmresp_store(struct device *_dev,
- + struct device_attribute *attr,
- + const char *buf, size_t count)
- +{
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- + uint32_t val = simple_strtoul(buf, NULL, 16);
- +
- + if (!dwc_otg_get_param_lpm_enable(otg_dev->core_if)) {
- + return 0;
- + }
- +
- + if (!dwc_otg_is_device_mode(otg_dev->core_if)) {
- + return 0;
- + }
- +
- + dwc_otg_set_lpmresponse(otg_dev->core_if, val);
- + return count;
- +}
- +
- +DEVICE_ATTR(lpm_response, S_IRUGO | S_IWUSR, lpmresp_show, lpmresp_store);
- +
- +/**
- +* Show the sleep_status attribute.
- +*/
- +static ssize_t sleepstatus_show(struct device *_dev,
- + struct device_attribute *attr, char *buf)
- +{
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- + return sprintf(buf, "Sleep Status = %d\n",
- + dwc_otg_get_lpm_portsleepstatus(otg_dev->core_if));
- +}
- +
- +/**
- + * Store the sleep_status attribure.
- + */
- +static ssize_t sleepstatus_store(struct device *_dev,
- + struct device_attribute *attr,
- + const char *buf, size_t count)
- +{
- + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev);
- + dwc_otg_core_if_t *core_if = otg_dev->core_if;
- +
- + if (dwc_otg_get_lpm_portsleepstatus(otg_dev->core_if)) {
- + if (dwc_otg_is_host_mode(core_if)) {
- +
- + DWC_PRINTF("Host initiated resume\n");
- + dwc_otg_set_prtresume(otg_dev->core_if, 1);
- + }
- + }
- +
- + return count;
- +}
- +
- +DEVICE_ATTR(sleep_status, S_IRUGO | S_IWUSR, sleepstatus_show,
- + sleepstatus_store);
- +
- +#endif /* CONFIG_USB_DWC_OTG_LPM_ENABLE */
- +
- +/**@}*/
- +
- +/**
- + * Create the device files
- + */
- +void dwc_otg_attr_create(
- +#ifdef LM_INTERFACE
- + struct lm_device *dev
- +#elif defined(PCI_INTERFACE)
- + struct pci_dev *dev
- +#elif defined(PLATFORM_INTERFACE)
- + struct platform_device *dev
- +#endif
- + )
- +{
- + int error;
- +
- + error = device_create_file(&dev->dev, &dev_attr_regoffset);
- + error = device_create_file(&dev->dev, &dev_attr_regvalue);
- + error = device_create_file(&dev->dev, &dev_attr_mode);
- + error = device_create_file(&dev->dev, &dev_attr_hnpcapable);
- + error = device_create_file(&dev->dev, &dev_attr_srpcapable);
- + error = device_create_file(&dev->dev, &dev_attr_hsic_connect);
- + error = device_create_file(&dev->dev, &dev_attr_inv_sel_hsic);
- + error = device_create_file(&dev->dev, &dev_attr_hnp);
- + error = device_create_file(&dev->dev, &dev_attr_srp);
- + error = device_create_file(&dev->dev, &dev_attr_buspower);
- + error = device_create_file(&dev->dev, &dev_attr_bussuspend);
- + error = device_create_file(&dev->dev, &dev_attr_mode_ch_tim_en);
- + error = device_create_file(&dev->dev, &dev_attr_fr_interval);
- + error = device_create_file(&dev->dev, &dev_attr_busconnected);
- + error = device_create_file(&dev->dev, &dev_attr_gotgctl);
- + error = device_create_file(&dev->dev, &dev_attr_gusbcfg);
- + error = device_create_file(&dev->dev, &dev_attr_grxfsiz);
- + error = device_create_file(&dev->dev, &dev_attr_gnptxfsiz);
- + error = device_create_file(&dev->dev, &dev_attr_gpvndctl);
- + error = device_create_file(&dev->dev, &dev_attr_ggpio);
- + error = device_create_file(&dev->dev, &dev_attr_guid);
- + error = device_create_file(&dev->dev, &dev_attr_gsnpsid);
- + error = device_create_file(&dev->dev, &dev_attr_devspeed);
- + error = device_create_file(&dev->dev, &dev_attr_enumspeed);
- + error = device_create_file(&dev->dev, &dev_attr_hptxfsiz);
- + error = device_create_file(&dev->dev, &dev_attr_hprt0);
- + error = device_create_file(&dev->dev, &dev_attr_remote_wakeup);
- + error = device_create_file(&dev->dev, &dev_attr_rem_wakeup_pwrdn);
- + error = device_create_file(&dev->dev, &dev_attr_disconnect_us);
- + error = device_create_file(&dev->dev, &dev_attr_regdump);
- + error = device_create_file(&dev->dev, &dev_attr_spramdump);
- + error = device_create_file(&dev->dev, &dev_attr_hcddump);
- + error = device_create_file(&dev->dev, &dev_attr_hcd_frrem);
- + error = device_create_file(&dev->dev, &dev_attr_rd_reg_test);
- + error = device_create_file(&dev->dev, &dev_attr_wr_reg_test);
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- + error = device_create_file(&dev->dev, &dev_attr_lpm_response);
- + error = device_create_file(&dev->dev, &dev_attr_sleep_status);
- +#endif
- +}
- +
- +/**
- + * Remove the device files
- + */
- +void dwc_otg_attr_remove(
- +#ifdef LM_INTERFACE
- + struct lm_device *dev
- +#elif defined(PCI_INTERFACE)
- + struct pci_dev *dev
- +#elif defined(PLATFORM_INTERFACE)
- + struct platform_device *dev
- +#endif
- + )
- +{
- + device_remove_file(&dev->dev, &dev_attr_regoffset);
- + device_remove_file(&dev->dev, &dev_attr_regvalue);
- + device_remove_file(&dev->dev, &dev_attr_mode);
- + device_remove_file(&dev->dev, &dev_attr_hnpcapable);
- + device_remove_file(&dev->dev, &dev_attr_srpcapable);
- + device_remove_file(&dev->dev, &dev_attr_hsic_connect);
- + device_remove_file(&dev->dev, &dev_attr_inv_sel_hsic);
- + device_remove_file(&dev->dev, &dev_attr_hnp);
- + device_remove_file(&dev->dev, &dev_attr_srp);
- + device_remove_file(&dev->dev, &dev_attr_buspower);
- + device_remove_file(&dev->dev, &dev_attr_bussuspend);
- + device_remove_file(&dev->dev, &dev_attr_mode_ch_tim_en);
- + device_remove_file(&dev->dev, &dev_attr_fr_interval);
- + device_remove_file(&dev->dev, &dev_attr_busconnected);
- + device_remove_file(&dev->dev, &dev_attr_gotgctl);
- + device_remove_file(&dev->dev, &dev_attr_gusbcfg);
- + device_remove_file(&dev->dev, &dev_attr_grxfsiz);
- + device_remove_file(&dev->dev, &dev_attr_gnptxfsiz);
- + device_remove_file(&dev->dev, &dev_attr_gpvndctl);
- + device_remove_file(&dev->dev, &dev_attr_ggpio);
- + device_remove_file(&dev->dev, &dev_attr_guid);
- + device_remove_file(&dev->dev, &dev_attr_gsnpsid);
- + device_remove_file(&dev->dev, &dev_attr_devspeed);
- + device_remove_file(&dev->dev, &dev_attr_enumspeed);
- + device_remove_file(&dev->dev, &dev_attr_hptxfsiz);
- + device_remove_file(&dev->dev, &dev_attr_hprt0);
- + device_remove_file(&dev->dev, &dev_attr_remote_wakeup);
- + device_remove_file(&dev->dev, &dev_attr_rem_wakeup_pwrdn);
- + device_remove_file(&dev->dev, &dev_attr_disconnect_us);
- + device_remove_file(&dev->dev, &dev_attr_regdump);
- + device_remove_file(&dev->dev, &dev_attr_spramdump);
- + device_remove_file(&dev->dev, &dev_attr_hcddump);
- + device_remove_file(&dev->dev, &dev_attr_hcd_frrem);
- + device_remove_file(&dev->dev, &dev_attr_rd_reg_test);
- + device_remove_file(&dev->dev, &dev_attr_wr_reg_test);
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- + device_remove_file(&dev->dev, &dev_attr_lpm_response);
- + device_remove_file(&dev->dev, &dev_attr_sleep_status);
- +#endif
- +}
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_attr.h
- @@ -0,0 +1,89 @@
- +/* ==========================================================================
- + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_attr.h $
- + * $Revision: #13 $
- + * $Date: 2010/06/21 $
- + * $Change: 1532021 $
- + *
- + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
- + * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
- + * otherwise expressly agreed to in writing between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product under
- + * any End User Software License Agreement or Agreement for Licensed Product
- + * with Synopsys or any supplement thereto. You are permitted to use and
- + * redistribute this Software in source and binary forms, with or without
- + * modification, provided that redistributions of source code must retain this
- + * notice. You may not view, use, disclose, copy or distribute this file or
- + * any information contained herein except pursuant to this license grant from
- + * Synopsys. If you do not agree with this notice, including the disclaimer
- + * below, then you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
- + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- + * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
- + * ========================================================================== */
- +
- +#if !defined(__DWC_OTG_ATTR_H__)
- +#define __DWC_OTG_ATTR_H__
- +
- +/** @file
- + * This file contains the interface to the Linux device attributes.
- + */
- +extern struct device_attribute dev_attr_regoffset;
- +extern struct device_attribute dev_attr_regvalue;
- +
- +extern struct device_attribute dev_attr_mode;
- +extern struct device_attribute dev_attr_hnpcapable;
- +extern struct device_attribute dev_attr_srpcapable;
- +extern struct device_attribute dev_attr_hnp;
- +extern struct device_attribute dev_attr_srp;
- +extern struct device_attribute dev_attr_buspower;
- +extern struct device_attribute dev_attr_bussuspend;
- +extern struct device_attribute dev_attr_mode_ch_tim_en;
- +extern struct device_attribute dev_attr_fr_interval;
- +extern struct device_attribute dev_attr_busconnected;
- +extern struct device_attribute dev_attr_gotgctl;
- +extern struct device_attribute dev_attr_gusbcfg;
- +extern struct device_attribute dev_attr_grxfsiz;
- +extern struct device_attribute dev_attr_gnptxfsiz;
- +extern struct device_attribute dev_attr_gpvndctl;
- +extern struct device_attribute dev_attr_ggpio;
- +extern struct device_attribute dev_attr_guid;
- +extern struct device_attribute dev_attr_gsnpsid;
- +extern struct device_attribute dev_attr_devspeed;
- +extern struct device_attribute dev_attr_enumspeed;
- +extern struct device_attribute dev_attr_hptxfsiz;
- +extern struct device_attribute dev_attr_hprt0;
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- +extern struct device_attribute dev_attr_lpm_response;
- +extern struct device_attribute devi_attr_sleep_status;
- +#endif
- +
- +void dwc_otg_attr_create(
- +#ifdef LM_INTERFACE
- + struct lm_device *dev
- +#elif defined(PCI_INTERFACE)
- + struct pci_dev *dev
- +#elif defined(PLATFORM_INTERFACE)
- + struct platform_device *dev
- +#endif
- + );
- +
- +void dwc_otg_attr_remove(
- +#ifdef LM_INTERFACE
- + struct lm_device *dev
- +#elif defined(PCI_INTERFACE)
- + struct pci_dev *dev
- +#elif defined(PLATFORM_INTERFACE)
- + struct platform_device *dev
- +#endif
- + );
- +#endif
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_cfi.c
- @@ -0,0 +1,1876 @@
- +/* ==========================================================================
- + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
- + * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
- + * otherwise expressly agreed to in writing between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product under
- + * any End User Software License Agreement or Agreement for Licensed Product
- + * with Synopsys or any supplement thereto. You are permitted to use and
- + * redistribute this Software in source and binary forms, with or without
- + * modification, provided that redistributions of source code must retain this
- + * notice. You may not view, use, disclose, copy or distribute this file or
- + * any information contained herein except pursuant to this license grant from
- + * Synopsys. If you do not agree with this notice, including the disclaimer
- + * below, then you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
- + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- + * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
- + * ========================================================================== */
- +
- +/** @file
- + *
- + * This file contains the most of the CFI(Core Feature Interface)
- + * implementation for the OTG.
- + */
- +
- +#ifdef DWC_UTE_CFI
- +
- +#include "dwc_otg_pcd.h"
- +#include "dwc_otg_cfi.h"
- +
- +/** This definition should actually migrate to the Portability Library */
- +#define DWC_CONSTANT_CPU_TO_LE16(x) (x)
- +
- +extern dwc_otg_pcd_ep_t *get_ep_by_addr(dwc_otg_pcd_t * pcd, u16 wIndex);
- +
- +static int cfi_core_features_buf(uint8_t * buf, uint16_t buflen);
- +static int cfi_get_feature_value(uint8_t * buf, uint16_t buflen,
- + struct dwc_otg_pcd *pcd,
- + struct cfi_usb_ctrlrequest *ctrl_req);
- +static int cfi_set_feature_value(struct dwc_otg_pcd *pcd);
- +static int cfi_ep_get_sg_val(uint8_t * buf, struct dwc_otg_pcd *pcd,
- + struct cfi_usb_ctrlrequest *req);
- +static int cfi_ep_get_concat_val(uint8_t * buf, struct dwc_otg_pcd *pcd,
- + struct cfi_usb_ctrlrequest *req);
- +static int cfi_ep_get_align_val(uint8_t * buf, struct dwc_otg_pcd *pcd,
- + struct cfi_usb_ctrlrequest *req);
- +static int cfi_preproc_reset(struct dwc_otg_pcd *pcd,
- + struct cfi_usb_ctrlrequest *req);
- +static void cfi_free_ep_bs_dyn_data(cfi_ep_t * cfiep);
- +
- +static uint16_t get_dfifo_size(dwc_otg_core_if_t * core_if);
- +static int32_t get_rxfifo_size(dwc_otg_core_if_t * core_if, uint16_t wValue);
- +static int32_t get_txfifo_size(struct dwc_otg_pcd *pcd, uint16_t wValue);
- +
- +static uint8_t resize_fifos(dwc_otg_core_if_t * core_if);
- +
- +/** This is the header of the all features descriptor */
- +static cfi_all_features_header_t all_props_desc_header = {
- + .wVersion = DWC_CONSTANT_CPU_TO_LE16(0x100),
- + .wCoreID = DWC_CONSTANT_CPU_TO_LE16(CFI_CORE_ID_OTG),
- + .wNumFeatures = DWC_CONSTANT_CPU_TO_LE16(9),
- +};
- +
- +/** This is an array of statically allocated feature descriptors */
- +static cfi_feature_desc_header_t prop_descs[] = {
- +
- + /* FT_ID_DMA_MODE */
- + {
- + .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_MODE),
- + .bmAttributes = CFI_FEATURE_ATTR_RW,
- + .wDataLength = DWC_CONSTANT_CPU_TO_LE16(1),
- + },
- +
- + /* FT_ID_DMA_BUFFER_SETUP */
- + {
- + .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_BUFFER_SETUP),
- + .bmAttributes = CFI_FEATURE_ATTR_RW,
- + .wDataLength = DWC_CONSTANT_CPU_TO_LE16(6),
- + },
- +
- + /* FT_ID_DMA_BUFF_ALIGN */
- + {
- + .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_BUFF_ALIGN),
- + .bmAttributes = CFI_FEATURE_ATTR_RW,
- + .wDataLength = DWC_CONSTANT_CPU_TO_LE16(2),
- + },
- +
- + /* FT_ID_DMA_CONCAT_SETUP */
- + {
- + .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_CONCAT_SETUP),
- + .bmAttributes = CFI_FEATURE_ATTR_RW,
- + //.wDataLength = DWC_CONSTANT_CPU_TO_LE16(6),
- + },
- +
- + /* FT_ID_DMA_CIRCULAR */
- + {
- + .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_CIRCULAR),
- + .bmAttributes = CFI_FEATURE_ATTR_RW,
- + .wDataLength = DWC_CONSTANT_CPU_TO_LE16(6),
- + },
- +
- + /* FT_ID_THRESHOLD_SETUP */
- + {
- + .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_THRESHOLD_SETUP),
- + .bmAttributes = CFI_FEATURE_ATTR_RW,
- + .wDataLength = DWC_CONSTANT_CPU_TO_LE16(6),
- + },
- +
- + /* FT_ID_DFIFO_DEPTH */
- + {
- + .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DFIFO_DEPTH),
- + .bmAttributes = CFI_FEATURE_ATTR_RO,
- + .wDataLength = DWC_CONSTANT_CPU_TO_LE16(2),
- + },
- +
- + /* FT_ID_TX_FIFO_DEPTH */
- + {
- + .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_TX_FIFO_DEPTH),
- + .bmAttributes = CFI_FEATURE_ATTR_RW,
- + .wDataLength = DWC_CONSTANT_CPU_TO_LE16(2),
- + },
- +
- + /* FT_ID_RX_FIFO_DEPTH */
- + {
- + .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_RX_FIFO_DEPTH),
- + .bmAttributes = CFI_FEATURE_ATTR_RW,
- + .wDataLength = DWC_CONSTANT_CPU_TO_LE16(2),
- + }
- +};
- +
- +/** The table of feature names */
- +cfi_string_t prop_name_table[] = {
- + {FT_ID_DMA_MODE, "dma_mode"},
- + {FT_ID_DMA_BUFFER_SETUP, "buffer_setup"},
- + {FT_ID_DMA_BUFF_ALIGN, "buffer_align"},
- + {FT_ID_DMA_CONCAT_SETUP, "concat_setup"},
- + {FT_ID_DMA_CIRCULAR, "buffer_circular"},
- + {FT_ID_THRESHOLD_SETUP, "threshold_setup"},
- + {FT_ID_DFIFO_DEPTH, "dfifo_depth"},
- + {FT_ID_TX_FIFO_DEPTH, "txfifo_depth"},
- + {FT_ID_RX_FIFO_DEPTH, "rxfifo_depth"},
- + {}
- +};
- +
- +/************************************************************************/
- +
- +/**
- + * Returns the name of the feature by its ID
- + * or NULL if no featute ID matches.
- + *
- + */
- +const uint8_t *get_prop_name(uint16_t prop_id, int *len)
- +{
- + cfi_string_t *pstr;
- + *len = 0;
- +
- + for (pstr = prop_name_table; pstr && pstr->s; pstr++) {
- + if (pstr->id == prop_id) {
- + *len = DWC_STRLEN(pstr->s);
- + return pstr->s;
- + }
- + }
- + return NULL;
- +}
- +
- +/**
- + * This function handles all CFI specific control requests.
- + *
- + * Return a negative value to stall the DCE.
- + */
- +int cfi_setup(struct dwc_otg_pcd *pcd, struct cfi_usb_ctrlrequest *ctrl)
- +{
- + int retval = 0;
- + dwc_otg_pcd_ep_t *ep = NULL;
- + cfiobject_t *cfi = pcd->cfi;
- + struct dwc_otg_core_if *coreif = GET_CORE_IF(pcd);
- + uint16_t wLen = DWC_LE16_TO_CPU(&ctrl->wLength);
- + uint16_t wValue = DWC_LE16_TO_CPU(&ctrl->wValue);
- + uint16_t wIndex = DWC_LE16_TO_CPU(&ctrl->wIndex);
- + uint32_t regaddr = 0;
- + uint32_t regval = 0;
- +
- + /* Save this Control Request in the CFI object.
- + * The data field will be assigned in the data stage completion CB function.
- + */
- + cfi->ctrl_req = *ctrl;
- + cfi->ctrl_req.data = NULL;
- +
- + cfi->need_gadget_att = 0;
- + cfi->need_status_in_complete = 0;
- +
- + switch (ctrl->bRequest) {
- + case VEN_CORE_GET_FEATURES:
- + retval = cfi_core_features_buf(cfi->buf_in.buf, CFI_IN_BUF_LEN);
- + if (retval >= 0) {
- + //dump_msg(cfi->buf_in.buf, retval);
- + ep = &pcd->ep0;
- +
- + retval = min((uint16_t) retval, wLen);
- + /* Transfer this buffer to the host through the EP0-IN EP */
- + ep->dwc_ep.dma_addr = cfi->buf_in.addr;
- + ep->dwc_ep.start_xfer_buff = cfi->buf_in.buf;
- + ep->dwc_ep.xfer_buff = cfi->buf_in.buf;
- + ep->dwc_ep.xfer_len = retval;
- + ep->dwc_ep.xfer_count = 0;
- + ep->dwc_ep.sent_zlp = 0;
- + ep->dwc_ep.total_len = ep->dwc_ep.xfer_len;
- +
- + pcd->ep0_pending = 1;
- + dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep);
- + }
- + retval = 0;
- + break;
- +
- + case VEN_CORE_GET_FEATURE:
- + CFI_INFO("VEN_CORE_GET_FEATURE\n");
- + retval = cfi_get_feature_value(cfi->buf_in.buf, CFI_IN_BUF_LEN,
- + pcd, ctrl);
- + if (retval >= 0) {
- + ep = &pcd->ep0;
- +
- + retval = min((uint16_t) retval, wLen);
- + /* Transfer this buffer to the host through the EP0-IN EP */
- + ep->dwc_ep.dma_addr = cfi->buf_in.addr;
- + ep->dwc_ep.start_xfer_buff = cfi->buf_in.buf;
- + ep->dwc_ep.xfer_buff = cfi->buf_in.buf;
- + ep->dwc_ep.xfer_len = retval;
- + ep->dwc_ep.xfer_count = 0;
- + ep->dwc_ep.sent_zlp = 0;
- + ep->dwc_ep.total_len = ep->dwc_ep.xfer_len;
- +
- + pcd->ep0_pending = 1;
- + dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep);
- + }
- + CFI_INFO("VEN_CORE_GET_FEATURE=%d\n", retval);
- + dump_msg(cfi->buf_in.buf, retval);
- + break;
- +
- + case VEN_CORE_SET_FEATURE:
- + CFI_INFO("VEN_CORE_SET_FEATURE\n");
- + /* Set up an XFER to get the data stage of the control request,
- + * which is the new value of the feature to be modified.
- + */
- + ep = &pcd->ep0;
- + ep->dwc_ep.is_in = 0;
- + ep->dwc_ep.dma_addr = cfi->buf_out.addr;
- + ep->dwc_ep.start_xfer_buff = cfi->buf_out.buf;
- + ep->dwc_ep.xfer_buff = cfi->buf_out.buf;
- + ep->dwc_ep.xfer_len = wLen;
- + ep->dwc_ep.xfer_count = 0;
- + ep->dwc_ep.sent_zlp = 0;
- + ep->dwc_ep.total_len = ep->dwc_ep.xfer_len;
- +
- + pcd->ep0_pending = 1;
- + /* Read the control write's data stage */
- + dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep);
- + retval = 0;
- + break;
- +
- + case VEN_CORE_RESET_FEATURES:
- + CFI_INFO("VEN_CORE_RESET_FEATURES\n");
- + cfi->need_gadget_att = 1;
- + cfi->need_status_in_complete = 1;
- + retval = cfi_preproc_reset(pcd, ctrl);
- + CFI_INFO("VEN_CORE_RESET_FEATURES = (%d)\n", retval);
- + break;
- +
- + case VEN_CORE_ACTIVATE_FEATURES:
- + CFI_INFO("VEN_CORE_ACTIVATE_FEATURES\n");
- + break;
- +
- + case VEN_CORE_READ_REGISTER:
- + CFI_INFO("VEN_CORE_READ_REGISTER\n");
- + /* wValue optionally contains the HI WORD of the register offset and
- + * wIndex contains the LOW WORD of the register offset
- + */
- + if (wValue == 0) {
- + /* @TODO - MAS - fix the access to the base field */
- + regaddr = 0;
- + //regaddr = (uint32_t) pcd->otg_dev->os_dep.base;
- + //GET_CORE_IF(pcd)->co
- + regaddr |= wIndex;
- + } else {
- + regaddr = (wValue << 16) | wIndex;
- + }
- +
- + /* Read a 32-bit value of the memory at the regaddr */
- + regval = DWC_READ_REG32((uint32_t *) regaddr);
- +
- + ep = &pcd->ep0;
- + dwc_memcpy(cfi->buf_in.buf, ®val, sizeof(uint32_t));
- + ep->dwc_ep.is_in = 1;
- + ep->dwc_ep.dma_addr = cfi->buf_in.addr;
- + ep->dwc_ep.start_xfer_buff = cfi->buf_in.buf;
- + ep->dwc_ep.xfer_buff = cfi->buf_in.buf;
- + ep->dwc_ep.xfer_len = wLen;
- + ep->dwc_ep.xfer_count = 0;
- + ep->dwc_ep.sent_zlp = 0;
- + ep->dwc_ep.total_len = ep->dwc_ep.xfer_len;
- +
- + pcd->ep0_pending = 1;
- + dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep);
- + cfi->need_gadget_att = 0;
- + retval = 0;
- + break;
- +
- + case VEN_CORE_WRITE_REGISTER:
- + CFI_INFO("VEN_CORE_WRITE_REGISTER\n");
- + /* Set up an XFER to get the data stage of the control request,
- + * which is the new value of the register to be modified.
- + */
- + ep = &pcd->ep0;
- + ep->dwc_ep.is_in = 0;
- + ep->dwc_ep.dma_addr = cfi->buf_out.addr;
- + ep->dwc_ep.start_xfer_buff = cfi->buf_out.buf;
- + ep->dwc_ep.xfer_buff = cfi->buf_out.buf;
- + ep->dwc_ep.xfer_len = wLen;
- + ep->dwc_ep.xfer_count = 0;
- + ep->dwc_ep.sent_zlp = 0;
- + ep->dwc_ep.total_len = ep->dwc_ep.xfer_len;
- +
- + pcd->ep0_pending = 1;
- + /* Read the control write's data stage */
- + dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep);
- + retval = 0;
- + break;
- +
- + default:
- + retval = -DWC_E_NOT_SUPPORTED;
- + break;
- + }
- +
- + return retval;
- +}
- +
- +/**
- + * This function prepares the core features descriptors and copies its
- + * raw representation into the buffer <buf>.
- + *
- + * The buffer structure is as follows:
- + * all_features_header (8 bytes)
- + * features_#1 (8 bytes + feature name string length)
- + * features_#2 (8 bytes + feature name string length)
- + * .....
- + * features_#n - where n=the total count of feature descriptors
- + */
- +static int cfi_core_features_buf(uint8_t * buf, uint16_t buflen)
- +{
- + cfi_feature_desc_header_t *prop_hdr = prop_descs;
- + cfi_feature_desc_header_t *prop;
- + cfi_all_features_header_t *all_props_hdr = &all_props_desc_header;
- + cfi_all_features_header_t *tmp;
- + uint8_t *tmpbuf = buf;
- + const uint8_t *pname = NULL;
- + int i, j, namelen = 0, totlen;
- +
- + /* Prepare and copy the core features into the buffer */
- + CFI_INFO("%s:\n", __func__);
- +
- + tmp = (cfi_all_features_header_t *) tmpbuf;
- + *tmp = *all_props_hdr;
- + tmpbuf += CFI_ALL_FEATURES_HDR_LEN;
- +
- + j = sizeof(prop_descs) / sizeof(cfi_all_features_header_t);
- + for (i = 0; i < j; i++, prop_hdr++) {
- + pname = get_prop_name(prop_hdr->wFeatureID, &namelen);
- + prop = (cfi_feature_desc_header_t *) tmpbuf;
- + *prop = *prop_hdr;
- +
- + prop->bNameLen = namelen;
- + prop->wLength =
- + DWC_CONSTANT_CPU_TO_LE16(CFI_FEATURE_DESC_HDR_LEN +
- + namelen);
- +
- + tmpbuf += CFI_FEATURE_DESC_HDR_LEN;
- + dwc_memcpy(tmpbuf, pname, namelen);
- + tmpbuf += namelen;
- + }
- +
- + totlen = tmpbuf - buf;
- +
- + if (totlen > 0) {
- + tmp = (cfi_all_features_header_t *) buf;
- + tmp->wTotalLen = DWC_CONSTANT_CPU_TO_LE16(totlen);
- + }
- +
- + return totlen;
- +}
- +
- +/**
- + * This function releases all the dynamic memory in the CFI object.
- + */
- +static void cfi_release(cfiobject_t * cfiobj)
- +{
- + cfi_ep_t *cfiep;
- + dwc_list_link_t *tmp;
- +
- + CFI_INFO("%s\n", __func__);
- +
- + if (cfiobj->buf_in.buf) {
- + DWC_DMA_FREE(CFI_IN_BUF_LEN, cfiobj->buf_in.buf,
- + cfiobj->buf_in.addr);
- + cfiobj->buf_in.buf = NULL;
- + }
- +
- + if (cfiobj->buf_out.buf) {
- + DWC_DMA_FREE(CFI_OUT_BUF_LEN, cfiobj->buf_out.buf,
- + cfiobj->buf_out.addr);
- + cfiobj->buf_out.buf = NULL;
- + }
- +
- + /* Free the Buffer Setup values for each EP */
- + //list_for_each_entry(cfiep, &cfiobj->active_eps, lh) {
- + DWC_LIST_FOREACH(tmp, &cfiobj->active_eps) {
- + cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh);
- + cfi_free_ep_bs_dyn_data(cfiep);
- + }
- +}
- +
- +/**
- + * This function frees the dynamically allocated EP buffer setup data.
- + */
- +static void cfi_free_ep_bs_dyn_data(cfi_ep_t * cfiep)
- +{
- + if (cfiep->bm_sg) {
- + DWC_FREE(cfiep->bm_sg);
- + cfiep->bm_sg = NULL;
- + }
- +
- + if (cfiep->bm_align) {
- + DWC_FREE(cfiep->bm_align);
- + cfiep->bm_align = NULL;
- + }
- +
- + if (cfiep->bm_concat) {
- + if (NULL != cfiep->bm_concat->wTxBytes) {
- + DWC_FREE(cfiep->bm_concat->wTxBytes);
- + cfiep->bm_concat->wTxBytes = NULL;
- + }
- + DWC_FREE(cfiep->bm_concat);
- + cfiep->bm_concat = NULL;
- + }
- +}
- +
- +/**
- + * This function initializes the default values of the features
- + * for a specific endpoint and should be called only once when
- + * the EP is enabled first time.
- + */
- +static int cfi_ep_init_defaults(struct dwc_otg_pcd *pcd, cfi_ep_t * cfiep)
- +{
- + int retval = 0;
- +
- + cfiep->bm_sg = DWC_ALLOC(sizeof(ddma_sg_buffer_setup_t));
- + if (NULL == cfiep->bm_sg) {
- + CFI_INFO("Failed to allocate memory for SG feature value\n");
- + return -DWC_E_NO_MEMORY;
- + }
- + dwc_memset(cfiep->bm_sg, 0, sizeof(ddma_sg_buffer_setup_t));
- +
- + /* For the Concatenation feature's default value we do not allocate
- + * memory for the wTxBytes field - it will be done in the set_feature_value
- + * request handler.
- + */
- + cfiep->bm_concat = DWC_ALLOC(sizeof(ddma_concat_buffer_setup_t));
- + if (NULL == cfiep->bm_concat) {
- + CFI_INFO
- + ("Failed to allocate memory for CONCATENATION feature value\n");
- + DWC_FREE(cfiep->bm_sg);
- + return -DWC_E_NO_MEMORY;
- + }
- + dwc_memset(cfiep->bm_concat, 0, sizeof(ddma_concat_buffer_setup_t));
- +
- + cfiep->bm_align = DWC_ALLOC(sizeof(ddma_align_buffer_setup_t));
- + if (NULL == cfiep->bm_align) {
- + CFI_INFO
- + ("Failed to allocate memory for Alignment feature value\n");
- + DWC_FREE(cfiep->bm_sg);
- + DWC_FREE(cfiep->bm_concat);
- + return -DWC_E_NO_MEMORY;
- + }
- + dwc_memset(cfiep->bm_align, 0, sizeof(ddma_align_buffer_setup_t));
- +
- + return retval;
- +}
- +
- +/**
- + * The callback function that notifies the CFI on the activation of
- + * an endpoint in the PCD. The following steps are done in this function:
- + *
- + * Create a dynamically allocated cfi_ep_t object (a CFI wrapper to the PCD's
- + * active endpoint)
- + * Create MAX_DMA_DESCS_PER_EP count DMA Descriptors for the EP
- + * Set the Buffer Mode to standard
- + * Initialize the default values for all EP modes (SG, Circular, Concat, Align)
- + * Add the cfi_ep_t object to the list of active endpoints in the CFI object
- + */
- +static int cfi_ep_enable(struct cfiobject *cfi, struct dwc_otg_pcd *pcd,
- + struct dwc_otg_pcd_ep *ep)
- +{
- + cfi_ep_t *cfiep;
- + int retval = -DWC_E_NOT_SUPPORTED;
- +
- + CFI_INFO("%s: epname=%s; epnum=0x%02x\n", __func__,
- + "EP_" /*ep->ep.name */ , ep->desc->bEndpointAddress);
- + /* MAS - Check whether this endpoint already is in the list */
- + cfiep = get_cfi_ep_by_pcd_ep(cfi, ep);
- +
- + if (NULL == cfiep) {
- + /* Allocate a cfi_ep_t object */
- + cfiep = DWC_ALLOC(sizeof(cfi_ep_t));
- + if (NULL == cfiep) {
- + CFI_INFO
- + ("Unable to allocate memory for <cfiep> in function %s\n",
- + __func__);
- + return -DWC_E_NO_MEMORY;
- + }
- + dwc_memset(cfiep, 0, sizeof(cfi_ep_t));
- +
- + /* Save the dwc_otg_pcd_ep pointer in the cfiep object */
- + cfiep->ep = ep;
- +
- + /* Allocate the DMA Descriptors chain of MAX_DMA_DESCS_PER_EP count */
- + ep->dwc_ep.descs =
- + DWC_DMA_ALLOC(MAX_DMA_DESCS_PER_EP *
- + sizeof(dwc_otg_dma_desc_t),
- + &ep->dwc_ep.descs_dma_addr);
- +
- + if (NULL == ep->dwc_ep.descs) {
- + DWC_FREE(cfiep);
- + return -DWC_E_NO_MEMORY;
- + }
- +
- + DWC_LIST_INIT(&cfiep->lh);
- +
- + /* Set the buffer mode to BM_STANDARD. It will be modified
- + * when building descriptors for a specific buffer mode */
- + ep->dwc_ep.buff_mode = BM_STANDARD;
- +
- + /* Create and initialize the default values for this EP's Buffer modes */
- + if ((retval = cfi_ep_init_defaults(pcd, cfiep)) < 0)
- + return retval;
- +
- + /* Add the cfi_ep_t object to the CFI object's list of active endpoints */
- + DWC_LIST_INSERT_TAIL(&cfi->active_eps, &cfiep->lh);
- + retval = 0;
- + } else { /* The sought EP already is in the list */
- + CFI_INFO("%s: The sought EP already is in the list\n",
- + __func__);
- + }
- +
- + return retval;
- +}
- +
- +/**
- + * This function is called when the data stage of a 3-stage Control Write request
- + * is complete.
- + *
- + */
- +static int cfi_ctrl_write_complete(struct cfiobject *cfi,
- + struct dwc_otg_pcd *pcd)
- +{
- + uint32_t addr, reg_value;
- + uint16_t wIndex, wValue;
- + uint8_t bRequest;
- + uint8_t *buf = cfi->buf_out.buf;
- + //struct usb_ctrlrequest *ctrl_req = &cfi->ctrl_req_saved;
- + struct cfi_usb_ctrlrequest *ctrl_req = &cfi->ctrl_req;
- + int retval = -DWC_E_NOT_SUPPORTED;
- +
- + CFI_INFO("%s\n", __func__);
- +
- + bRequest = ctrl_req->bRequest;
- + wIndex = DWC_CONSTANT_CPU_TO_LE16(ctrl_req->wIndex);
- + wValue = DWC_CONSTANT_CPU_TO_LE16(ctrl_req->wValue);
- +
- + /*
- + * Save the pointer to the data stage in the ctrl_req's <data> field.
- + * The request should be already saved in the command stage by now.
- + */
- + ctrl_req->data = cfi->buf_out.buf;
- + cfi->need_status_in_complete = 0;
- + cfi->need_gadget_att = 0;
- +
- + switch (bRequest) {
- + case VEN_CORE_WRITE_REGISTER:
- + /* The buffer contains raw data of the new value for the register */
- + reg_value = *((uint32_t *) buf);
- + if (wValue == 0) {
- + addr = 0;
- + //addr = (uint32_t) pcd->otg_dev->os_dep.base;
- + addr += wIndex;
- + } else {
- + addr = (wValue << 16) | wIndex;
- + }
- +
- + //writel(reg_value, addr);
- +
- + retval = 0;
- + cfi->need_status_in_complete = 1;
- + break;
- +
- + case VEN_CORE_SET_FEATURE:
- + /* The buffer contains raw data of the new value of the feature */
- + retval = cfi_set_feature_value(pcd);
- + if (retval < 0)
- + return retval;
- +
- + cfi->need_status_in_complete = 1;
- + break;
- +
- + default:
- + break;
- + }
- +
- + return retval;
- +}
- +
- +/**
- + * This function builds the DMA descriptors for the SG buffer mode.
- + */
- +static void cfi_build_sg_descs(struct cfiobject *cfi, cfi_ep_t * cfiep,
- + dwc_otg_pcd_request_t * req)
- +{
- + struct dwc_otg_pcd_ep *ep = cfiep->ep;
- + ddma_sg_buffer_setup_t *sgval = cfiep->bm_sg;
- + struct dwc_otg_dma_desc *desc = cfiep->ep->dwc_ep.descs;
- + struct dwc_otg_dma_desc *desc_last = cfiep->ep->dwc_ep.descs;
- + dma_addr_t buff_addr = req->dma;
- + int i;
- + uint32_t txsize, off;
- +
- + txsize = sgval->wSize;
- + off = sgval->bOffset;
- +
- +// CFI_INFO("%s: %s TXSIZE=0x%08x; OFFSET=0x%08x\n",
- +// __func__, cfiep->ep->ep.name, txsize, off);
- +
- + for (i = 0; i < sgval->bCount; i++) {
- + desc->status.b.bs = BS_HOST_BUSY;
- + desc->buf = buff_addr;
- + desc->status.b.l = 0;
- + desc->status.b.ioc = 0;
- + desc->status.b.sp = 0;
- + desc->status.b.bytes = txsize;
- + desc->status.b.bs = BS_HOST_READY;
- +
- + /* Set the next address of the buffer */
- + buff_addr += txsize + off;
- + desc_last = desc;
- + desc++;
- + }
- +
- + /* Set the last, ioc and sp bits on the Last DMA Descriptor */
- + desc_last->status.b.l = 1;
- + desc_last->status.b.ioc = 1;
- + desc_last->status.b.sp = ep->dwc_ep.sent_zlp;
- + /* Save the last DMA descriptor pointer */
- + cfiep->dma_desc_last = desc_last;
- + cfiep->desc_count = sgval->bCount;
- +}
- +
- +/**
- + * This function builds the DMA descriptors for the Concatenation buffer mode.
- + */
- +static void cfi_build_concat_descs(struct cfiobject *cfi, cfi_ep_t * cfiep,
- + dwc_otg_pcd_request_t * req)
- +{
- + struct dwc_otg_pcd_ep *ep = cfiep->ep;
- + ddma_concat_buffer_setup_t *concatval = cfiep->bm_concat;
- + struct dwc_otg_dma_desc *desc = cfiep->ep->dwc_ep.descs;
- + struct dwc_otg_dma_desc *desc_last = cfiep->ep->dwc_ep.descs;
- + dma_addr_t buff_addr = req->dma;
- + int i;
- + uint16_t *txsize;
- +
- + txsize = concatval->wTxBytes;
- +
- + for (i = 0; i < concatval->hdr.bDescCount; i++) {
- + desc->buf = buff_addr;
- + desc->status.b.bs = BS_HOST_BUSY;
- + desc->status.b.l = 0;
- + desc->status.b.ioc = 0;
- + desc->status.b.sp = 0;
- + desc->status.b.bytes = *txsize;
- + desc->status.b.bs = BS_HOST_READY;
- +
- + txsize++;
- + /* Set the next address of the buffer */
- + buff_addr += UGETW(ep->desc->wMaxPacketSize);
- + desc_last = desc;
- + desc++;
- + }
- +
- + /* Set the last, ioc and sp bits on the Last DMA Descriptor */
- + desc_last->status.b.l = 1;
- + desc_last->status.b.ioc = 1;
- + desc_last->status.b.sp = ep->dwc_ep.sent_zlp;
- + cfiep->dma_desc_last = desc_last;
- + cfiep->desc_count = concatval->hdr.bDescCount;
- +}
- +
- +/**
- + * This function builds the DMA descriptors for the Circular buffer mode
- + */
- +static void cfi_build_circ_descs(struct cfiobject *cfi, cfi_ep_t * cfiep,
- + dwc_otg_pcd_request_t * req)
- +{
- + /* @todo: MAS - add implementation when this feature needs to be tested */
- +}
- +
- +/**
- + * This function builds the DMA descriptors for the Alignment buffer mode
- + */
- +static void cfi_build_align_descs(struct cfiobject *cfi, cfi_ep_t * cfiep,
- + dwc_otg_pcd_request_t * req)
- +{
- + struct dwc_otg_pcd_ep *ep = cfiep->ep;
- + ddma_align_buffer_setup_t *alignval = cfiep->bm_align;
- + struct dwc_otg_dma_desc *desc = cfiep->ep->dwc_ep.descs;
- + dma_addr_t buff_addr = req->dma;
- +
- + desc->status.b.bs = BS_HOST_BUSY;
- + desc->status.b.l = 1;
- + desc->status.b.ioc = 1;
- + desc->status.b.sp = ep->dwc_ep.sent_zlp;
- + desc->status.b.bytes = req->length;
- + /* Adjust the buffer alignment */
- + desc->buf = (buff_addr + alignval->bAlign);
- + desc->status.b.bs = BS_HOST_READY;
- + cfiep->dma_desc_last = desc;
- + cfiep->desc_count = 1;
- +}
- +
- +/**
- + * This function builds the DMA descriptors chain for different modes of the
- + * buffer setup of an endpoint.
- + */
- +static void cfi_build_descriptors(struct cfiobject *cfi,
- + struct dwc_otg_pcd *pcd,
- + struct dwc_otg_pcd_ep *ep,
- + dwc_otg_pcd_request_t * req)
- +{
- + cfi_ep_t *cfiep;
- +
- + /* Get the cfiep by the dwc_otg_pcd_ep */
- + cfiep = get_cfi_ep_by_pcd_ep(cfi, ep);
- + if (NULL == cfiep) {
- + CFI_INFO("%s: Unable to find a matching active endpoint\n",
- + __func__);
- + return;
- + }
- +
- + cfiep->xfer_len = req->length;
- +
- + /* Iterate through all the DMA descriptors */
- + switch (cfiep->ep->dwc_ep.buff_mode) {
- + case BM_SG:
- + cfi_build_sg_descs(cfi, cfiep, req);
- + break;
- +
- + case BM_CONCAT:
- + cfi_build_concat_descs(cfi, cfiep, req);
- + break;
- +
- + case BM_CIRCULAR:
- + cfi_build_circ_descs(cfi, cfiep, req);
- + break;
- +
- + case BM_ALIGN:
- + cfi_build_align_descs(cfi, cfiep, req);
- + break;
- +
- + default:
- + break;
- + }
- +}
- +
- +/**
- + * Allocate DMA buffer for different Buffer modes.
- + */
- +static void *cfi_ep_alloc_buf(struct cfiobject *cfi, struct dwc_otg_pcd *pcd,
- + struct dwc_otg_pcd_ep *ep, dma_addr_t * dma,
- + unsigned size, gfp_t flags)
- +{
- + return DWC_DMA_ALLOC(size, dma);
- +}
- +
- +/**
- + * This function initializes the CFI object.
- + */
- +int init_cfi(cfiobject_t * cfiobj)
- +{
- + CFI_INFO("%s\n", __func__);
- +
- + /* Allocate a buffer for IN XFERs */
- + cfiobj->buf_in.buf =
- + DWC_DMA_ALLOC(CFI_IN_BUF_LEN, &cfiobj->buf_in.addr);
- + if (NULL == cfiobj->buf_in.buf) {
- + CFI_INFO("Unable to allocate buffer for INs\n");
- + return -DWC_E_NO_MEMORY;
- + }
- +
- + /* Allocate a buffer for OUT XFERs */
- + cfiobj->buf_out.buf =
- + DWC_DMA_ALLOC(CFI_OUT_BUF_LEN, &cfiobj->buf_out.addr);
- + if (NULL == cfiobj->buf_out.buf) {
- + CFI_INFO("Unable to allocate buffer for OUT\n");
- + return -DWC_E_NO_MEMORY;
- + }
- +
- + /* Initialize the callback function pointers */
- + cfiobj->ops.release = cfi_release;
- + cfiobj->ops.ep_enable = cfi_ep_enable;
- + cfiobj->ops.ctrl_write_complete = cfi_ctrl_write_complete;
- + cfiobj->ops.build_descriptors = cfi_build_descriptors;
- + cfiobj->ops.ep_alloc_buf = cfi_ep_alloc_buf;
- +
- + /* Initialize the list of active endpoints in the CFI object */
- + DWC_LIST_INIT(&cfiobj->active_eps);
- +
- + return 0;
- +}
- +
- +/**
- + * This function reads the required feature's current value into the buffer
- + *
- + * @retval: Returns negative as error, or the data length of the feature
- + */
- +static int cfi_get_feature_value(uint8_t * buf, uint16_t buflen,
- + struct dwc_otg_pcd *pcd,
- + struct cfi_usb_ctrlrequest *ctrl_req)
- +{
- + int retval = -DWC_E_NOT_SUPPORTED;
- + struct dwc_otg_core_if *coreif = GET_CORE_IF(pcd);
- + uint16_t dfifo, rxfifo, txfifo;
- +
- + switch (ctrl_req->wIndex) {
- + /* Whether the DDMA is enabled or not */
- + case FT_ID_DMA_MODE:
- + *buf = (coreif->dma_enable && coreif->dma_desc_enable) ? 1 : 0;
- + retval = 1;
- + break;
- +
- + case FT_ID_DMA_BUFFER_SETUP:
- + retval = cfi_ep_get_sg_val(buf, pcd, ctrl_req);
- + break;
- +
- + case FT_ID_DMA_BUFF_ALIGN:
- + retval = cfi_ep_get_align_val(buf, pcd, ctrl_req);
- + break;
- +
- + case FT_ID_DMA_CONCAT_SETUP:
- + retval = cfi_ep_get_concat_val(buf, pcd, ctrl_req);
- + break;
- +
- + case FT_ID_DMA_CIRCULAR:
- + CFI_INFO("GetFeature value (FT_ID_DMA_CIRCULAR)\n");
- + break;
- +
- + case FT_ID_THRESHOLD_SETUP:
- + CFI_INFO("GetFeature value (FT_ID_THRESHOLD_SETUP)\n");
- + break;
- +
- + case FT_ID_DFIFO_DEPTH:
- + dfifo = get_dfifo_size(coreif);
- + *((uint16_t *) buf) = dfifo;
- + retval = sizeof(uint16_t);
- + break;
- +
- + case FT_ID_TX_FIFO_DEPTH:
- + retval = get_txfifo_size(pcd, ctrl_req->wValue);
- + if (retval >= 0) {
- + txfifo = retval;
- + *((uint16_t *) buf) = txfifo;
- + retval = sizeof(uint16_t);
- + }
- + break;
- +
- + case FT_ID_RX_FIFO_DEPTH:
- + retval = get_rxfifo_size(coreif, ctrl_req->wValue);
- + if (retval >= 0) {
- + rxfifo = retval;
- + *((uint16_t *) buf) = rxfifo;
- + retval = sizeof(uint16_t);
- + }
- + break;
- + }
- +
- + return retval;
- +}
- +
- +/**
- + * This function resets the SG for the specified EP to its default value
- + */
- +static int cfi_reset_sg_val(cfi_ep_t * cfiep)
- +{
- + dwc_memset(cfiep->bm_sg, 0, sizeof(ddma_sg_buffer_setup_t));
- + return 0;
- +}
- +
- +/**
- + * This function resets the Alignment for the specified EP to its default value
- + */
- +static int cfi_reset_align_val(cfi_ep_t * cfiep)
- +{
- + dwc_memset(cfiep->bm_sg, 0, sizeof(ddma_sg_buffer_setup_t));
- + return 0;
- +}
- +
- +/**
- + * This function resets the Concatenation for the specified EP to its default value
- + * This function will also set the value of the wTxBytes field to NULL after
- + * freeing the memory previously allocated for this field.
- + */
- +static int cfi_reset_concat_val(cfi_ep_t * cfiep)
- +{
- + /* First we need to free the wTxBytes field */
- + if (cfiep->bm_concat->wTxBytes) {
- + DWC_FREE(cfiep->bm_concat->wTxBytes);
- + cfiep->bm_concat->wTxBytes = NULL;
- + }
- +
- + dwc_memset(cfiep->bm_concat, 0, sizeof(ddma_concat_buffer_setup_t));
- + return 0;
- +}
- +
- +/**
- + * This function resets all the buffer setups of the specified endpoint
- + */
- +static int cfi_ep_reset_all_setup_vals(cfi_ep_t * cfiep)
- +{
- + cfi_reset_sg_val(cfiep);
- + cfi_reset_align_val(cfiep);
- + cfi_reset_concat_val(cfiep);
- + return 0;
- +}
- +
- +static int cfi_handle_reset_fifo_val(struct dwc_otg_pcd *pcd, uint8_t ep_addr,
- + uint8_t rx_rst, uint8_t tx_rst)
- +{
- + int retval = -DWC_E_INVALID;
- + uint16_t tx_siz[15];
- + uint16_t rx_siz = 0;
- + dwc_otg_pcd_ep_t *ep = NULL;
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
- + dwc_otg_core_params_t *params = GET_CORE_IF(pcd)->core_params;
- +
- + if (rx_rst) {
- + rx_siz = params->dev_rx_fifo_size;
- + params->dev_rx_fifo_size = GET_CORE_IF(pcd)->init_rxfsiz;
- + }
- +
- + if (tx_rst) {
- + if (ep_addr == 0) {
- + int i;
- +
- + for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
- + tx_siz[i] =
- + core_if->core_params->dev_tx_fifo_size[i];
- + core_if->core_params->dev_tx_fifo_size[i] =
- + core_if->init_txfsiz[i];
- + }
- + } else {
- +
- + ep = get_ep_by_addr(pcd, ep_addr);
- +
- + if (NULL == ep) {
- + CFI_INFO
- + ("%s: Unable to get the endpoint addr=0x%02x\n",
- + __func__, ep_addr);
- + return -DWC_E_INVALID;
- + }
- +
- + tx_siz[0] =
- + params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num -
- + 1];
- + params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - 1] =
- + GET_CORE_IF(pcd)->init_txfsiz[ep->
- + dwc_ep.tx_fifo_num -
- + 1];
- + }
- + }
- +
- + if (resize_fifos(GET_CORE_IF(pcd))) {
- + retval = 0;
- + } else {
- + CFI_INFO
- + ("%s: Error resetting the feature Reset All(FIFO size)\n",
- + __func__);
- + if (rx_rst) {
- + params->dev_rx_fifo_size = rx_siz;
- + }
- +
- + if (tx_rst) {
- + if (ep_addr == 0) {
- + int i;
- + for (i = 0; i < core_if->hwcfg4.b.num_in_eps;
- + i++) {
- + core_if->
- + core_params->dev_tx_fifo_size[i] =
- + tx_siz[i];
- + }
- + } else {
- + params->dev_tx_fifo_size[ep->
- + dwc_ep.tx_fifo_num -
- + 1] = tx_siz[0];
- + }
- + }
- + retval = -DWC_E_INVALID;
- + }
- + return retval;
- +}
- +
- +static int cfi_handle_reset_all(struct dwc_otg_pcd *pcd, uint8_t addr)
- +{
- + int retval = 0;
- + cfi_ep_t *cfiep;
- + cfiobject_t *cfi = pcd->cfi;
- + dwc_list_link_t *tmp;
- +
- + retval = cfi_handle_reset_fifo_val(pcd, addr, 1, 1);
- + if (retval < 0) {
- + return retval;
- + }
- +
- + /* If the EP address is known then reset the features for only that EP */
- + if (addr) {
- + cfiep = get_cfi_ep_by_addr(pcd->cfi, addr);
- + if (NULL == cfiep) {
- + CFI_INFO("%s: Error getting the EP address 0x%02x\n",
- + __func__, addr);
- + return -DWC_E_INVALID;
- + }
- + retval = cfi_ep_reset_all_setup_vals(cfiep);
- + cfiep->ep->dwc_ep.buff_mode = BM_STANDARD;
- + }
- + /* Otherwise (wValue == 0), reset all features of all EP's */
- + else {
- + /* Traverse all the active EP's and reset the feature(s) value(s) */
- + //list_for_each_entry(cfiep, &cfi->active_eps, lh) {
- + DWC_LIST_FOREACH(tmp, &cfi->active_eps) {
- + cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh);
- + retval = cfi_ep_reset_all_setup_vals(cfiep);
- + cfiep->ep->dwc_ep.buff_mode = BM_STANDARD;
- + if (retval < 0) {
- + CFI_INFO
- + ("%s: Error resetting the feature Reset All\n",
- + __func__);
- + return retval;
- + }
- + }
- + }
- + return retval;
- +}
- +
- +static int cfi_handle_reset_dma_buff_setup(struct dwc_otg_pcd *pcd,
- + uint8_t addr)
- +{
- + int retval = 0;
- + cfi_ep_t *cfiep;
- + cfiobject_t *cfi = pcd->cfi;
- + dwc_list_link_t *tmp;
- +
- + /* If the EP address is known then reset the features for only that EP */
- + if (addr) {
- + cfiep = get_cfi_ep_by_addr(pcd->cfi, addr);
- + if (NULL == cfiep) {
- + CFI_INFO("%s: Error getting the EP address 0x%02x\n",
- + __func__, addr);
- + return -DWC_E_INVALID;
- + }
- + retval = cfi_reset_sg_val(cfiep);
- + }
- + /* Otherwise (wValue == 0), reset all features of all EP's */
- + else {
- + /* Traverse all the active EP's and reset the feature(s) value(s) */
- + //list_for_each_entry(cfiep, &cfi->active_eps, lh) {
- + DWC_LIST_FOREACH(tmp, &cfi->active_eps) {
- + cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh);
- + retval = cfi_reset_sg_val(cfiep);
- + if (retval < 0) {
- + CFI_INFO
- + ("%s: Error resetting the feature Buffer Setup\n",
- + __func__);
- + return retval;
- + }
- + }
- + }
- + return retval;
- +}
- +
- +static int cfi_handle_reset_concat_val(struct dwc_otg_pcd *pcd, uint8_t addr)
- +{
- + int retval = 0;
- + cfi_ep_t *cfiep;
- + cfiobject_t *cfi = pcd->cfi;
- + dwc_list_link_t *tmp;
- +
- + /* If the EP address is known then reset the features for only that EP */
- + if (addr) {
- + cfiep = get_cfi_ep_by_addr(pcd->cfi, addr);
- + if (NULL == cfiep) {
- + CFI_INFO("%s: Error getting the EP address 0x%02x\n",
- + __func__, addr);
- + return -DWC_E_INVALID;
- + }
- + retval = cfi_reset_concat_val(cfiep);
- + }
- + /* Otherwise (wValue == 0), reset all features of all EP's */
- + else {
- + /* Traverse all the active EP's and reset the feature(s) value(s) */
- + //list_for_each_entry(cfiep, &cfi->active_eps, lh) {
- + DWC_LIST_FOREACH(tmp, &cfi->active_eps) {
- + cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh);
- + retval = cfi_reset_concat_val(cfiep);
- + if (retval < 0) {
- + CFI_INFO
- + ("%s: Error resetting the feature Concatenation Value\n",
- + __func__);
- + return retval;
- + }
- + }
- + }
- + return retval;
- +}
- +
- +static int cfi_handle_reset_align_val(struct dwc_otg_pcd *pcd, uint8_t addr)
- +{
- + int retval = 0;
- + cfi_ep_t *cfiep;
- + cfiobject_t *cfi = pcd->cfi;
- + dwc_list_link_t *tmp;
- +
- + /* If the EP address is known then reset the features for only that EP */
- + if (addr) {
- + cfiep = get_cfi_ep_by_addr(pcd->cfi, addr);
- + if (NULL == cfiep) {
- + CFI_INFO("%s: Error getting the EP address 0x%02x\n",
- + __func__, addr);
- + return -DWC_E_INVALID;
- + }
- + retval = cfi_reset_align_val(cfiep);
- + }
- + /* Otherwise (wValue == 0), reset all features of all EP's */
- + else {
- + /* Traverse all the active EP's and reset the feature(s) value(s) */
- + //list_for_each_entry(cfiep, &cfi->active_eps, lh) {
- + DWC_LIST_FOREACH(tmp, &cfi->active_eps) {
- + cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh);
- + retval = cfi_reset_align_val(cfiep);
- + if (retval < 0) {
- + CFI_INFO
- + ("%s: Error resetting the feature Aliignment Value\n",
- + __func__);
- + return retval;
- + }
- + }
- + }
- + return retval;
- +
- +}
- +
- +static int cfi_preproc_reset(struct dwc_otg_pcd *pcd,
- + struct cfi_usb_ctrlrequest *req)
- +{
- + int retval = 0;
- +
- + switch (req->wIndex) {
- + case 0:
- + /* Reset all features */
- + retval = cfi_handle_reset_all(pcd, req->wValue & 0xff);
- + break;
- +
- + case FT_ID_DMA_BUFFER_SETUP:
- + /* Reset the SG buffer setup */
- + retval =
- + cfi_handle_reset_dma_buff_setup(pcd, req->wValue & 0xff);
- + break;
- +
- + case FT_ID_DMA_CONCAT_SETUP:
- + /* Reset the Concatenation buffer setup */
- + retval = cfi_handle_reset_concat_val(pcd, req->wValue & 0xff);
- + break;
- +
- + case FT_ID_DMA_BUFF_ALIGN:
- + /* Reset the Alignment buffer setup */
- + retval = cfi_handle_reset_align_val(pcd, req->wValue & 0xff);
- + break;
- +
- + case FT_ID_TX_FIFO_DEPTH:
- + retval =
- + cfi_handle_reset_fifo_val(pcd, req->wValue & 0xff, 0, 1);
- + pcd->cfi->need_gadget_att = 0;
- + break;
- +
- + case FT_ID_RX_FIFO_DEPTH:
- + retval = cfi_handle_reset_fifo_val(pcd, 0, 1, 0);
- + pcd->cfi->need_gadget_att = 0;
- + break;
- + default:
- + break;
- + }
- + return retval;
- +}
- +
- +/**
- + * This function sets a new value for the SG buffer setup.
- + */
- +static int cfi_ep_set_sg_val(uint8_t * buf, struct dwc_otg_pcd *pcd)
- +{
- + uint8_t inaddr, outaddr;
- + cfi_ep_t *epin, *epout;
- + ddma_sg_buffer_setup_t *psgval;
- + uint32_t desccount, size;
- +
- + CFI_INFO("%s\n", __func__);
- +
- + psgval = (ddma_sg_buffer_setup_t *) buf;
- + desccount = (uint32_t) psgval->bCount;
- + size = (uint32_t) psgval->wSize;
- +
- + /* Check the DMA descriptor count */
- + if ((desccount > MAX_DMA_DESCS_PER_EP) || (desccount == 0)) {
- + CFI_INFO
- + ("%s: The count of DMA Descriptors should be between 1 and %d\n",
- + __func__, MAX_DMA_DESCS_PER_EP);
- + return -DWC_E_INVALID;
- + }
- +
- + /* Check the DMA descriptor count */
- +
- + if (size == 0) {
- +
- + CFI_INFO("%s: The transfer size should be at least 1 byte\n",
- + __func__);
- +
- + return -DWC_E_INVALID;
- +
- + }
- +
- + inaddr = psgval->bInEndpointAddress;
- + outaddr = psgval->bOutEndpointAddress;
- +
- + epin = get_cfi_ep_by_addr(pcd->cfi, inaddr);
- + epout = get_cfi_ep_by_addr(pcd->cfi, outaddr);
- +
- + if (NULL == epin || NULL == epout) {
- + CFI_INFO
- + ("%s: Unable to get the endpoints inaddr=0x%02x outaddr=0x%02x\n",
- + __func__, inaddr, outaddr);
- + return -DWC_E_INVALID;
- + }
- +
- + epin->ep->dwc_ep.buff_mode = BM_SG;
- + dwc_memcpy(epin->bm_sg, psgval, sizeof(ddma_sg_buffer_setup_t));
- +
- + epout->ep->dwc_ep.buff_mode = BM_SG;
- + dwc_memcpy(epout->bm_sg, psgval, sizeof(ddma_sg_buffer_setup_t));
- +
- + return 0;
- +}
- +
- +/**
- + * This function sets a new value for the buffer Alignment setup.
- + */
- +static int cfi_ep_set_alignment_val(uint8_t * buf, struct dwc_otg_pcd *pcd)
- +{
- + cfi_ep_t *ep;
- + uint8_t addr;
- + ddma_align_buffer_setup_t *palignval;
- +
- + palignval = (ddma_align_buffer_setup_t *) buf;
- + addr = palignval->bEndpointAddress;
- +
- + ep = get_cfi_ep_by_addr(pcd->cfi, addr);
- +
- + if (NULL == ep) {
- + CFI_INFO("%s: Unable to get the endpoint addr=0x%02x\n",
- + __func__, addr);
- + return -DWC_E_INVALID;
- + }
- +
- + ep->ep->dwc_ep.buff_mode = BM_ALIGN;
- + dwc_memcpy(ep->bm_align, palignval, sizeof(ddma_align_buffer_setup_t));
- +
- + return 0;
- +}
- +
- +/**
- + * This function sets a new value for the Concatenation buffer setup.
- + */
- +static int cfi_ep_set_concat_val(uint8_t * buf, struct dwc_otg_pcd *pcd)
- +{
- + uint8_t addr;
- + cfi_ep_t *ep;
- + struct _ddma_concat_buffer_setup_hdr *pConcatValHdr;
- + uint16_t *pVals;
- + uint32_t desccount;
- + int i;
- + uint16_t mps;
- +
- + pConcatValHdr = (struct _ddma_concat_buffer_setup_hdr *)buf;
- + desccount = (uint32_t) pConcatValHdr->bDescCount;
- + pVals = (uint16_t *) (buf + BS_CONCAT_VAL_HDR_LEN);
- +
- + /* Check the DMA descriptor count */
- + if (desccount > MAX_DMA_DESCS_PER_EP) {
- + CFI_INFO("%s: Maximum DMA Descriptor count should be %d\n",
- + __func__, MAX_DMA_DESCS_PER_EP);
- + return -DWC_E_INVALID;
- + }
- +
- + addr = pConcatValHdr->bEndpointAddress;
- + ep = get_cfi_ep_by_addr(pcd->cfi, addr);
- + if (NULL == ep) {
- + CFI_INFO("%s: Unable to get the endpoint addr=0x%02x\n",
- + __func__, addr);
- + return -DWC_E_INVALID;
- + }
- +
- + mps = UGETW(ep->ep->desc->wMaxPacketSize);
- +
- +#if 0
- + for (i = 0; i < desccount; i++) {
- + CFI_INFO("%s: wTxSize[%d]=0x%04x\n", __func__, i, pVals[i]);
- + }
- + CFI_INFO("%s: epname=%s; mps=%d\n", __func__, ep->ep->ep.name, mps);
- +#endif
- +
- + /* Check the wTxSizes to be less than or equal to the mps */
- + for (i = 0; i < desccount; i++) {
- + if (pVals[i] > mps) {
- + CFI_INFO
- + ("%s: ERROR - the wTxSize[%d] should be <= MPS (wTxSize=%d)\n",
- + __func__, i, pVals[i]);
- + return -DWC_E_INVALID;
- + }
- + }
- +
- + ep->ep->dwc_ep.buff_mode = BM_CONCAT;
- + dwc_memcpy(ep->bm_concat, pConcatValHdr, BS_CONCAT_VAL_HDR_LEN);
- +
- + /* Free the previously allocated storage for the wTxBytes */
- + if (ep->bm_concat->wTxBytes) {
- + DWC_FREE(ep->bm_concat->wTxBytes);
- + }
- +
- + /* Allocate a new storage for the wTxBytes field */
- + ep->bm_concat->wTxBytes =
- + DWC_ALLOC(sizeof(uint16_t) * pConcatValHdr->bDescCount);
- + if (NULL == ep->bm_concat->wTxBytes) {
- + CFI_INFO("%s: Unable to allocate memory\n", __func__);
- + return -DWC_E_NO_MEMORY;
- + }
- +
- + /* Copy the new values into the wTxBytes filed */
- + dwc_memcpy(ep->bm_concat->wTxBytes, buf + BS_CONCAT_VAL_HDR_LEN,
- + sizeof(uint16_t) * pConcatValHdr->bDescCount);
- +
- + return 0;
- +}
- +
- +/**
- + * This function calculates the total of all FIFO sizes
- + *
- + * @param core_if Programming view of DWC_otg controller
- + *
- + * @return The total of data FIFO sizes.
- + *
- + */
- +static uint16_t get_dfifo_size(dwc_otg_core_if_t * core_if)
- +{
- + dwc_otg_core_params_t *params = core_if->core_params;
- + uint16_t dfifo_total = 0;
- + int i;
- +
- + /* The shared RxFIFO size */
- + dfifo_total =
- + params->dev_rx_fifo_size + params->dev_nperio_tx_fifo_size;
- +
- + /* Add up each TxFIFO size to the total */
- + for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
- + dfifo_total += params->dev_tx_fifo_size[i];
- + }
- +
- + return dfifo_total;
- +}
- +
- +/**
- + * This function returns Rx FIFO size
- + *
- + * @param core_if Programming view of DWC_otg controller
- + *
- + * @return The total of data FIFO sizes.
- + *
- + */
- +static int32_t get_rxfifo_size(dwc_otg_core_if_t * core_if, uint16_t wValue)
- +{
- + switch (wValue >> 8) {
- + case 0:
- + return (core_if->pwron_rxfsiz <
- + 32768) ? core_if->pwron_rxfsiz : 32768;
- + break;
- + case 1:
- + return core_if->core_params->dev_rx_fifo_size;
- + break;
- + default:
- + return -DWC_E_INVALID;
- + break;
- + }
- +}
- +
- +/**
- + * This function returns Tx FIFO size for IN EP
- + *
- + * @param core_if Programming view of DWC_otg controller
- + *
- + * @return The total of data FIFO sizes.
- + *
- + */
- +static int32_t get_txfifo_size(struct dwc_otg_pcd *pcd, uint16_t wValue)
- +{
- + dwc_otg_pcd_ep_t *ep;
- +
- + ep = get_ep_by_addr(pcd, wValue & 0xff);
- +
- + if (NULL == ep) {
- + CFI_INFO("%s: Unable to get the endpoint addr=0x%02x\n",
- + __func__, wValue & 0xff);
- + return -DWC_E_INVALID;
- + }
- +
- + if (!ep->dwc_ep.is_in) {
- + CFI_INFO
- + ("%s: No Tx FIFO assingned to the Out endpoint addr=0x%02x\n",
- + __func__, wValue & 0xff);
- + return -DWC_E_INVALID;
- + }
- +
- + switch (wValue >> 8) {
- + case 0:
- + return (GET_CORE_IF(pcd)->pwron_txfsiz
- + [ep->dwc_ep.tx_fifo_num - 1] <
- + 768) ? GET_CORE_IF(pcd)->pwron_txfsiz[ep->
- + dwc_ep.tx_fifo_num
- + - 1] : 32768;
- + break;
- + case 1:
- + return GET_CORE_IF(pcd)->core_params->
- + dev_tx_fifo_size[ep->dwc_ep.num - 1];
- + break;
- + default:
- + return -DWC_E_INVALID;
- + break;
- + }
- +}
- +
- +/**
- + * This function checks if the submitted combination of
- + * device mode FIFO sizes is possible or not.
- + *
- + * @param core_if Programming view of DWC_otg controller
- + *
- + * @return 1 if possible, 0 otherwise.
- + *
- + */
- +static uint8_t check_fifo_sizes(dwc_otg_core_if_t * core_if)
- +{
- + uint16_t dfifo_actual = 0;
- + dwc_otg_core_params_t *params = core_if->core_params;
- + uint16_t start_addr = 0;
- + int i;
- +
- + dfifo_actual =
- + params->dev_rx_fifo_size + params->dev_nperio_tx_fifo_size;
- +
- + for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
- + dfifo_actual += params->dev_tx_fifo_size[i];
- + }
- +
- + if (dfifo_actual > core_if->total_fifo_size) {
- + return 0;
- + }
- +
- + if (params->dev_rx_fifo_size > 32768 || params->dev_rx_fifo_size < 16)
- + return 0;
- +
- + if (params->dev_nperio_tx_fifo_size > 32768
- + || params->dev_nperio_tx_fifo_size < 16)
- + return 0;
- +
- + for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
- +
- + if (params->dev_tx_fifo_size[i] > 768
- + || params->dev_tx_fifo_size[i] < 4)
- + return 0;
- + }
- +
- + if (params->dev_rx_fifo_size > core_if->pwron_rxfsiz)
- + return 0;
- + start_addr = params->dev_rx_fifo_size;
- +
- + if (params->dev_nperio_tx_fifo_size > core_if->pwron_gnptxfsiz)
- + return 0;
- + start_addr += params->dev_nperio_tx_fifo_size;
- +
- + for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
- +
- + if (params->dev_tx_fifo_size[i] > core_if->pwron_txfsiz[i])
- + return 0;
- + start_addr += params->dev_tx_fifo_size[i];
- + }
- +
- + return 1;
- +}
- +
- +/**
- + * This function resizes Device mode FIFOs
- + *
- + * @param core_if Programming view of DWC_otg controller
- + *
- + * @return 1 if successful, 0 otherwise
- + *
- + */
- +static uint8_t resize_fifos(dwc_otg_core_if_t * core_if)
- +{
- + int i = 0;
- + dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
- + dwc_otg_core_params_t *params = core_if->core_params;
- + uint32_t rx_fifo_size;
- + fifosize_data_t nptxfifosize;
- + fifosize_data_t txfifosize[15];
- +
- + uint32_t rx_fsz_bak;
- + uint32_t nptxfsz_bak;
- + uint32_t txfsz_bak[15];
- +
- + uint16_t start_address;
- + uint8_t retval = 1;
- +
- + if (!check_fifo_sizes(core_if)) {
- + return 0;
- + }
- +
- + /* Configure data FIFO sizes */
- + if (core_if->hwcfg2.b.dynamic_fifo && params->enable_dynamic_fifo) {
- + rx_fsz_bak = DWC_READ_REG32(&global_regs->grxfsiz);
- + rx_fifo_size = params->dev_rx_fifo_size;
- + DWC_WRITE_REG32(&global_regs->grxfsiz, rx_fifo_size);
- +
- + /*
- + * Tx FIFOs These FIFOs are numbered from 1 to 15.
- + * Indexes of the FIFO size module parameters in the
- + * dev_tx_fifo_size array and the FIFO size registers in
- + * the dtxfsiz array run from 0 to 14.
- + */
- +
- + /* Non-periodic Tx FIFO */
- + nptxfsz_bak = DWC_READ_REG32(&global_regs->gnptxfsiz);
- + nptxfifosize.b.depth = params->dev_nperio_tx_fifo_size;
- + start_address = params->dev_rx_fifo_size;
- + nptxfifosize.b.startaddr = start_address;
- +
- + DWC_WRITE_REG32(&global_regs->gnptxfsiz, nptxfifosize.d32);
- +
- + start_address += nptxfifosize.b.depth;
- +
- + for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
- + txfsz_bak[i] = DWC_READ_REG32(&global_regs->dtxfsiz[i]);
- +
- + txfifosize[i].b.depth = params->dev_tx_fifo_size[i];
- + txfifosize[i].b.startaddr = start_address;
- + DWC_WRITE_REG32(&global_regs->dtxfsiz[i],
- + txfifosize[i].d32);
- +
- + start_address += txfifosize[i].b.depth;
- + }
- +
- + /** Check if register values are set correctly */
- + if (rx_fifo_size != DWC_READ_REG32(&global_regs->grxfsiz)) {
- + retval = 0;
- + }
- +
- + if (nptxfifosize.d32 != DWC_READ_REG32(&global_regs->gnptxfsiz)) {
- + retval = 0;
- + }
- +
- + for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
- + if (txfifosize[i].d32 !=
- + DWC_READ_REG32(&global_regs->dtxfsiz[i])) {
- + retval = 0;
- + }
- + }
- +
- + /** If register values are not set correctly, reset old values */
- + if (retval == 0) {
- + DWC_WRITE_REG32(&global_regs->grxfsiz, rx_fsz_bak);
- +
- + /* Non-periodic Tx FIFO */
- + DWC_WRITE_REG32(&global_regs->gnptxfsiz, nptxfsz_bak);
- +
- + for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
- + DWC_WRITE_REG32(&global_regs->dtxfsiz[i],
- + txfsz_bak[i]);
- + }
- + }
- + } else {
- + return 0;
- + }
- +
- + /* Flush the FIFOs */
- + dwc_otg_flush_tx_fifo(core_if, 0x10); /* all Tx FIFOs */
- + dwc_otg_flush_rx_fifo(core_if);
- +
- + return retval;
- +}
- +
- +/**
- + * This function sets a new value for the buffer Alignment setup.
- + */
- +static int cfi_ep_set_tx_fifo_val(uint8_t * buf, dwc_otg_pcd_t * pcd)
- +{
- + int retval;
- + uint32_t fsiz;
- + uint16_t size;
- + uint16_t ep_addr;
- + dwc_otg_pcd_ep_t *ep;
- + dwc_otg_core_params_t *params = GET_CORE_IF(pcd)->core_params;
- + tx_fifo_size_setup_t *ptxfifoval;
- +
- + ptxfifoval = (tx_fifo_size_setup_t *) buf;
- + ep_addr = ptxfifoval->bEndpointAddress;
- + size = ptxfifoval->wDepth;
- +
- + ep = get_ep_by_addr(pcd, ep_addr);
- +
- + CFI_INFO
- + ("%s: Set Tx FIFO size: endpoint addr=0x%02x, depth=%d, FIFO Num=%d\n",
- + __func__, ep_addr, size, ep->dwc_ep.tx_fifo_num);
- +
- + if (NULL == ep) {
- + CFI_INFO("%s: Unable to get the endpoint addr=0x%02x\n",
- + __func__, ep_addr);
- + return -DWC_E_INVALID;
- + }
- +
- + fsiz = params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - 1];
- + params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - 1] = size;
- +
- + if (resize_fifos(GET_CORE_IF(pcd))) {
- + retval = 0;
- + } else {
- + CFI_INFO
- + ("%s: Error setting the feature Tx FIFO Size for EP%d\n",
- + __func__, ep_addr);
- + params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - 1] = fsiz;
- + retval = -DWC_E_INVALID;
- + }
- +
- + return retval;
- +}
- +
- +/**
- + * This function sets a new value for the buffer Alignment setup.
- + */
- +static int cfi_set_rx_fifo_val(uint8_t * buf, dwc_otg_pcd_t * pcd)
- +{
- + int retval;
- + uint32_t fsiz;
- + uint16_t size;
- + dwc_otg_core_params_t *params = GET_CORE_IF(pcd)->core_params;
- + rx_fifo_size_setup_t *prxfifoval;
- +
- + prxfifoval = (rx_fifo_size_setup_t *) buf;
- + size = prxfifoval->wDepth;
- +
- + fsiz = params->dev_rx_fifo_size;
- + params->dev_rx_fifo_size = size;
- +
- + if (resize_fifos(GET_CORE_IF(pcd))) {
- + retval = 0;
- + } else {
- + CFI_INFO("%s: Error setting the feature Rx FIFO Size\n",
- + __func__);
- + params->dev_rx_fifo_size = fsiz;
- + retval = -DWC_E_INVALID;
- + }
- +
- + return retval;
- +}
- +
- +/**
- + * This function reads the SG of an EP's buffer setup into the buffer buf
- + */
- +static int cfi_ep_get_sg_val(uint8_t * buf, struct dwc_otg_pcd *pcd,
- + struct cfi_usb_ctrlrequest *req)
- +{
- + int retval = -DWC_E_INVALID;
- + uint8_t addr;
- + cfi_ep_t *ep;
- +
- + /* The Low Byte of the wValue contains a non-zero address of the endpoint */
- + addr = req->wValue & 0xFF;
- + if (addr == 0) /* The address should be non-zero */
- + return retval;
- +
- + ep = get_cfi_ep_by_addr(pcd->cfi, addr);
- + if (NULL == ep) {
- + CFI_INFO("%s: Unable to get the endpoint address(0x%02x)\n",
- + __func__, addr);
- + return retval;
- + }
- +
- + dwc_memcpy(buf, ep->bm_sg, BS_SG_VAL_DESC_LEN);
- + retval = BS_SG_VAL_DESC_LEN;
- + return retval;
- +}
- +
- +/**
- + * This function reads the Concatenation value of an EP's buffer mode into
- + * the buffer buf
- + */
- +static int cfi_ep_get_concat_val(uint8_t * buf, struct dwc_otg_pcd *pcd,
- + struct cfi_usb_ctrlrequest *req)
- +{
- + int retval = -DWC_E_INVALID;
- + uint8_t addr;
- + cfi_ep_t *ep;
- + uint8_t desc_count;
- +
- + /* The Low Byte of the wValue contains a non-zero address of the endpoint */
- + addr = req->wValue & 0xFF;
- + if (addr == 0) /* The address should be non-zero */
- + return retval;
- +
- + ep = get_cfi_ep_by_addr(pcd->cfi, addr);
- + if (NULL == ep) {
- + CFI_INFO("%s: Unable to get the endpoint address(0x%02x)\n",
- + __func__, addr);
- + return retval;
- + }
- +
- + /* Copy the header to the buffer */
- + dwc_memcpy(buf, ep->bm_concat, BS_CONCAT_VAL_HDR_LEN);
- + /* Advance the buffer pointer by the header size */
- + buf += BS_CONCAT_VAL_HDR_LEN;
- +
- + desc_count = ep->bm_concat->hdr.bDescCount;
- + /* Copy alll the wTxBytes to the buffer */
- + dwc_memcpy(buf, ep->bm_concat->wTxBytes, sizeof(uid16_t) * desc_count);
- +
- + retval = BS_CONCAT_VAL_HDR_LEN + sizeof(uid16_t) * desc_count;
- + return retval;
- +}
- +
- +/**
- + * This function reads the buffer Alignment value of an EP's buffer mode into
- + * the buffer buf
- + *
- + * @return The total number of bytes copied to the buffer or negative error code.
- + */
- +static int cfi_ep_get_align_val(uint8_t * buf, struct dwc_otg_pcd *pcd,
- + struct cfi_usb_ctrlrequest *req)
- +{
- + int retval = -DWC_E_INVALID;
- + uint8_t addr;
- + cfi_ep_t *ep;
- +
- + /* The Low Byte of the wValue contains a non-zero address of the endpoint */
- + addr = req->wValue & 0xFF;
- + if (addr == 0) /* The address should be non-zero */
- + return retval;
- +
- + ep = get_cfi_ep_by_addr(pcd->cfi, addr);
- + if (NULL == ep) {
- + CFI_INFO("%s: Unable to get the endpoint address(0x%02x)\n",
- + __func__, addr);
- + return retval;
- + }
- +
- + dwc_memcpy(buf, ep->bm_align, BS_ALIGN_VAL_HDR_LEN);
- + retval = BS_ALIGN_VAL_HDR_LEN;
- +
- + return retval;
- +}
- +
- +/**
- + * This function sets a new value for the specified feature
- + *
- + * @param pcd A pointer to the PCD object
- + *
- + * @return 0 if successful, negative error code otherwise to stall the DCE.
- + */
- +static int cfi_set_feature_value(struct dwc_otg_pcd *pcd)
- +{
- + int retval = -DWC_E_NOT_SUPPORTED;
- + uint16_t wIndex, wValue;
- + uint8_t bRequest;
- + struct dwc_otg_core_if *coreif;
- + cfiobject_t *cfi = pcd->cfi;
- + struct cfi_usb_ctrlrequest *ctrl_req;
- + uint8_t *buf;
- + ctrl_req = &cfi->ctrl_req;
- +
- + buf = pcd->cfi->ctrl_req.data;
- +
- + coreif = GET_CORE_IF(pcd);
- + bRequest = ctrl_req->bRequest;
- + wIndex = DWC_CONSTANT_CPU_TO_LE16(ctrl_req->wIndex);
- + wValue = DWC_CONSTANT_CPU_TO_LE16(ctrl_req->wValue);
- +
- + /* See which feature is to be modified */
- + switch (wIndex) {
- + case FT_ID_DMA_BUFFER_SETUP:
- + /* Modify the feature */
- + if ((retval = cfi_ep_set_sg_val(buf, pcd)) < 0)
- + return retval;
- +
- + /* And send this request to the gadget */
- + cfi->need_gadget_att = 1;
- + break;
- +
- + case FT_ID_DMA_BUFF_ALIGN:
- + if ((retval = cfi_ep_set_alignment_val(buf, pcd)) < 0)
- + return retval;
- + cfi->need_gadget_att = 1;
- + break;
- +
- + case FT_ID_DMA_CONCAT_SETUP:
- + /* Modify the feature */
- + if ((retval = cfi_ep_set_concat_val(buf, pcd)) < 0)
- + return retval;
- + cfi->need_gadget_att = 1;
- + break;
- +
- + case FT_ID_DMA_CIRCULAR:
- + CFI_INFO("FT_ID_DMA_CIRCULAR\n");
- + break;
- +
- + case FT_ID_THRESHOLD_SETUP:
- + CFI_INFO("FT_ID_THRESHOLD_SETUP\n");
- + break;
- +
- + case FT_ID_DFIFO_DEPTH:
- + CFI_INFO("FT_ID_DFIFO_DEPTH\n");
- + break;
- +
- + case FT_ID_TX_FIFO_DEPTH:
- + CFI_INFO("FT_ID_TX_FIFO_DEPTH\n");
- + if ((retval = cfi_ep_set_tx_fifo_val(buf, pcd)) < 0)
- + return retval;
- + cfi->need_gadget_att = 0;
- + break;
- +
- + case FT_ID_RX_FIFO_DEPTH:
- + CFI_INFO("FT_ID_RX_FIFO_DEPTH\n");
- + if ((retval = cfi_set_rx_fifo_val(buf, pcd)) < 0)
- + return retval;
- + cfi->need_gadget_att = 0;
- + break;
- + }
- +
- + return retval;
- +}
- +
- +#endif //DWC_UTE_CFI
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_cfi.h
- @@ -0,0 +1,320 @@
- +/* ==========================================================================
- + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
- + * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
- + * otherwise expressly agreed to in writing between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product under
- + * any End User Software License Agreement or Agreement for Licensed Product
- + * with Synopsys or any supplement thereto. You are permitted to use and
- + * redistribute this Software in source and binary forms, with or without
- + * modification, provided that redistributions of source code must retain this
- + * notice. You may not view, use, disclose, copy or distribute this file or
- + * any information contained herein except pursuant to this license grant from
- + * Synopsys. If you do not agree with this notice, including the disclaimer
- + * below, then you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
- + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- + * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
- + * ========================================================================== */
- +
- +#if !defined(__DWC_OTG_CFI_H__)
- +#define __DWC_OTG_CFI_H__
- +
- +#include "dwc_otg_pcd.h"
- +#include "dwc_cfi_common.h"
- +
- +/**
- + * @file
- + * This file contains the CFI related OTG PCD specific common constants,
- + * interfaces(functions and macros) and data structures.The CFI Protocol is an
- + * optional interface for internal testing purposes that a DUT may implement to
- + * support testing of configurable features.
- + *
- + */
- +
- +struct dwc_otg_pcd;
- +struct dwc_otg_pcd_ep;
- +
- +/** OTG CFI Features (properties) ID constants */
- +/** This is a request for all Core Features */
- +#define FT_ID_DMA_MODE 0x0001
- +#define FT_ID_DMA_BUFFER_SETUP 0x0002
- +#define FT_ID_DMA_BUFF_ALIGN 0x0003
- +#define FT_ID_DMA_CONCAT_SETUP 0x0004
- +#define FT_ID_DMA_CIRCULAR 0x0005
- +#define FT_ID_THRESHOLD_SETUP 0x0006
- +#define FT_ID_DFIFO_DEPTH 0x0007
- +#define FT_ID_TX_FIFO_DEPTH 0x0008
- +#define FT_ID_RX_FIFO_DEPTH 0x0009
- +
- +/**********************************************************/
- +#define CFI_INFO_DEF
- +
- +#ifdef CFI_INFO_DEF
- +#define CFI_INFO(fmt...) DWC_PRINTF("CFI: " fmt);
- +#else
- +#define CFI_INFO(fmt...)
- +#endif
- +
- +#define min(x,y) ({ \
- + x < y ? x : y; })
- +
- +#define max(x,y) ({ \
- + x > y ? x : y; })
- +
- +/**
- + * Descriptor DMA SG Buffer setup structure (SG buffer). This structure is
- + * also used for setting up a buffer for Circular DDMA.
- + */
- +struct _ddma_sg_buffer_setup {
- +#define BS_SG_VAL_DESC_LEN 6
- + /* The OUT EP address */
- + uint8_t bOutEndpointAddress;
- + /* The IN EP address */
- + uint8_t bInEndpointAddress;
- + /* Number of bytes to put between transfer segments (must be DWORD boundaries) */
- + uint8_t bOffset;
- + /* The number of transfer segments (a DMA descriptors per each segment) */
- + uint8_t bCount;
- + /* Size (in byte) of each transfer segment */
- + uint16_t wSize;
- +} __attribute__ ((packed));
- +typedef struct _ddma_sg_buffer_setup ddma_sg_buffer_setup_t;
- +
- +/** Descriptor DMA Concatenation Buffer setup structure */
- +struct _ddma_concat_buffer_setup_hdr {
- +#define BS_CONCAT_VAL_HDR_LEN 4
- + /* The endpoint for which the buffer is to be set up */
- + uint8_t bEndpointAddress;
- + /* The count of descriptors to be used */
- + uint8_t bDescCount;
- + /* The total size of the transfer */
- + uint16_t wSize;
- +} __attribute__ ((packed));
- +typedef struct _ddma_concat_buffer_setup_hdr ddma_concat_buffer_setup_hdr_t;
- +
- +/** Descriptor DMA Concatenation Buffer setup structure */
- +struct _ddma_concat_buffer_setup {
- + /* The SG header */
- + ddma_concat_buffer_setup_hdr_t hdr;
- +
- + /* The XFER sizes pointer (allocated dynamically) */
- + uint16_t *wTxBytes;
- +} __attribute__ ((packed));
- +typedef struct _ddma_concat_buffer_setup ddma_concat_buffer_setup_t;
- +
- +/** Descriptor DMA Alignment Buffer setup structure */
- +struct _ddma_align_buffer_setup {
- +#define BS_ALIGN_VAL_HDR_LEN 2
- + uint8_t bEndpointAddress;
- + uint8_t bAlign;
- +} __attribute__ ((packed));
- +typedef struct _ddma_align_buffer_setup ddma_align_buffer_setup_t;
- +
- +/** Transmit FIFO Size setup structure */
- +struct _tx_fifo_size_setup {
- + uint8_t bEndpointAddress;
- + uint16_t wDepth;
- +} __attribute__ ((packed));
- +typedef struct _tx_fifo_size_setup tx_fifo_size_setup_t;
- +
- +/** Transmit FIFO Size setup structure */
- +struct _rx_fifo_size_setup {
- + uint16_t wDepth;
- +} __attribute__ ((packed));
- +typedef struct _rx_fifo_size_setup rx_fifo_size_setup_t;
- +
- +/**
- + * struct cfi_usb_ctrlrequest - the CFI implementation of the struct usb_ctrlrequest
- + * This structure encapsulates the standard usb_ctrlrequest and adds a pointer
- + * to the data returned in the data stage of a 3-stage Control Write requests.
- + */
- +struct cfi_usb_ctrlrequest {
- + uint8_t bRequestType;
- + uint8_t bRequest;
- + uint16_t wValue;
- + uint16_t wIndex;
- + uint16_t wLength;
- + uint8_t *data;
- +} UPACKED;
- +
- +/*---------------------------------------------------------------------------*/
- +
- +/**
- + * The CFI wrapper of the enabled and activated dwc_otg_pcd_ep structures.
- + * This structure is used to store the buffer setup data for any
- + * enabled endpoint in the PCD.
- + */
- +struct cfi_ep {
- + /* Entry for the list container */
- + dwc_list_link_t lh;
- + /* Pointer to the active PCD endpoint structure */
- + struct dwc_otg_pcd_ep *ep;
- + /* The last descriptor in the chain of DMA descriptors of the endpoint */
- + struct dwc_otg_dma_desc *dma_desc_last;
- + /* The SG feature value */
- + ddma_sg_buffer_setup_t *bm_sg;
- + /* The Circular feature value */
- + ddma_sg_buffer_setup_t *bm_circ;
- + /* The Concatenation feature value */
- + ddma_concat_buffer_setup_t *bm_concat;
- + /* The Alignment feature value */
- + ddma_align_buffer_setup_t *bm_align;
- + /* XFER length */
- + uint32_t xfer_len;
- + /*
- + * Count of DMA descriptors currently used.
- + * The total should not exceed the MAX_DMA_DESCS_PER_EP value
- + * defined in the dwc_otg_cil.h
- + */
- + uint32_t desc_count;
- +};
- +typedef struct cfi_ep cfi_ep_t;
- +
- +typedef struct cfi_dma_buff {
- +#define CFI_IN_BUF_LEN 1024
- +#define CFI_OUT_BUF_LEN 1024
- + dma_addr_t addr;
- + uint8_t *buf;
- +} cfi_dma_buff_t;
- +
- +struct cfiobject;
- +
- +/**
- + * This is the interface for the CFI operations.
- + *
- + * @param ep_enable Called when any endpoint is enabled and activated.
- + * @param release Called when the CFI object is released and it needs to correctly
- + * deallocate the dynamic memory
- + * @param ctrl_write_complete Called when the data stage of the request is complete
- + */
- +typedef struct cfi_ops {
- + int (*ep_enable) (struct cfiobject * cfi, struct dwc_otg_pcd * pcd,
- + struct dwc_otg_pcd_ep * ep);
- + void *(*ep_alloc_buf) (struct cfiobject * cfi, struct dwc_otg_pcd * pcd,
- + struct dwc_otg_pcd_ep * ep, dma_addr_t * dma,
- + unsigned size, gfp_t flags);
- + void (*release) (struct cfiobject * cfi);
- + int (*ctrl_write_complete) (struct cfiobject * cfi,
- + struct dwc_otg_pcd * pcd);
- + void (*build_descriptors) (struct cfiobject * cfi,
- + struct dwc_otg_pcd * pcd,
- + struct dwc_otg_pcd_ep * ep,
- + dwc_otg_pcd_request_t * req);
- +} cfi_ops_t;
- +
- +struct cfiobject {
- + cfi_ops_t ops;
- + struct dwc_otg_pcd *pcd;
- + struct usb_gadget *gadget;
- +
- + /* Buffers used to send/receive CFI-related request data */
- + cfi_dma_buff_t buf_in;
- + cfi_dma_buff_t buf_out;
- +
- + /* CFI specific Control request wrapper */
- + struct cfi_usb_ctrlrequest ctrl_req;
- +
- + /* The list of active EP's in the PCD of type cfi_ep_t */
- + dwc_list_link_t active_eps;
- +
- + /* This flag shall control the propagation of a specific request
- + * to the gadget's processing routines.
- + * 0 - no gadget handling
- + * 1 - the gadget needs to know about this request (w/o completing a status
- + * phase - just return a 0 to the _setup callback)
- + */
- + uint8_t need_gadget_att;
- +
- + /* Flag indicating whether the status IN phase needs to be
- + * completed by the PCD
- + */
- + uint8_t need_status_in_complete;
- +};
- +typedef struct cfiobject cfiobject_t;
- +
- +#define DUMP_MSG
- +
- +#if defined(DUMP_MSG)
- +static inline void dump_msg(const u8 * buf, unsigned int length)
- +{
- + unsigned int start, num, i;
- + char line[52], *p;
- +
- + if (length >= 512)
- + return;
- +
- + start = 0;
- + while (length > 0) {
- + num = min(length, 16u);
- + p = line;
- + for (i = 0; i < num; ++i) {
- + if (i == 8)
- + *p++ = ' ';
- + DWC_SPRINTF(p, " %02x", buf[i]);
- + p += 3;
- + }
- + *p = 0;
- + DWC_DEBUG("%6x: %s\n", start, line);
- + buf += num;
- + start += num;
- + length -= num;
- + }
- +}
- +#else
- +static inline void dump_msg(const u8 * buf, unsigned int length)
- +{
- +}
- +#endif
- +
- +/**
- + * This function returns a pointer to cfi_ep_t object with the addr address.
- + */
- +static inline struct cfi_ep *get_cfi_ep_by_addr(struct cfiobject *cfi,
- + uint8_t addr)
- +{
- + struct cfi_ep *pcfiep;
- + dwc_list_link_t *tmp;
- +
- + DWC_LIST_FOREACH(tmp, &cfi->active_eps) {
- + pcfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh);
- +
- + if (pcfiep->ep->desc->bEndpointAddress == addr) {
- + return pcfiep;
- + }
- + }
- +
- + return NULL;
- +}
- +
- +/**
- + * This function returns a pointer to cfi_ep_t object that matches
- + * the dwc_otg_pcd_ep object.
- + */
- +static inline struct cfi_ep *get_cfi_ep_by_pcd_ep(struct cfiobject *cfi,
- + struct dwc_otg_pcd_ep *ep)
- +{
- + struct cfi_ep *pcfiep = NULL;
- + dwc_list_link_t *tmp;
- +
- + DWC_LIST_FOREACH(tmp, &cfi->active_eps) {
- + pcfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh);
- + if (pcfiep->ep == ep) {
- + return pcfiep;
- + }
- + }
- + return NULL;
- +}
- +
- +int cfi_setup(struct dwc_otg_pcd *pcd, struct cfi_usb_ctrlrequest *ctrl);
- +
- +#endif /* (__DWC_OTG_CFI_H__) */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_cil.c
- @@ -0,0 +1,7146 @@
- +/* ==========================================================================
- + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_cil.c $
- + * $Revision: #191 $
- + * $Date: 2012/08/10 $
- + * $Change: 2047372 $
- + *
- + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
- + * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
- + * otherwise expressly agreed to in writing between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product under
- + * any End User Software License Agreement or Agreement for Licensed Product
- + * with Synopsys or any supplement thereto. You are permitted to use and
- + * redistribute this Software in source and binary forms, with or without
- + * modification, provided that redistributions of source code must retain this
- + * notice. You may not view, use, disclose, copy or distribute this file or
- + * any information contained herein except pursuant to this license grant from
- + * Synopsys. If you do not agree with this notice, including the disclaimer
- + * below, then you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
- + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- + * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
- + * ========================================================================== */
- +
- +/** @file
- + *
- + * The Core Interface Layer provides basic services for accessing and
- + * managing the DWC_otg hardware. These services are used by both the
- + * Host Controller Driver and the Peripheral Controller Driver.
- + *
- + * The CIL manages the memory map for the core so that the HCD and PCD
- + * don't have to do this separately. It also handles basic tasks like
- + * reading/writing the registers and data FIFOs in the controller.
- + * Some of the data access functions provide encapsulation of several
- + * operations required to perform a task, such as writing multiple
- + * registers to start a transfer. Finally, the CIL performs basic
- + * services that are not specific to either the host or device modes
- + * of operation. These services include management of the OTG Host
- + * Negotiation Protocol (HNP) and Session Request Protocol (SRP). A
- + * Diagnostic API is also provided to allow testing of the controller
- + * hardware.
- + *
- + * The Core Interface Layer has the following requirements:
- + * - Provides basic controller operations.
- + * - Minimal use of OS services.
- + * - The OS services used will be abstracted by using inline functions
- + * or macros.
- + *
- + */
- +
- +#include "dwc_os.h"
- +#include "dwc_otg_regs.h"
- +#include "dwc_otg_cil.h"
- +
- +extern bool cil_force_host;
- +
- +static int dwc_otg_setup_params(dwc_otg_core_if_t * core_if);
- +
- +/**
- + * This function is called to initialize the DWC_otg CSR data
- + * structures. The register addresses in the device and host
- + * structures are initialized from the base address supplied by the
- + * caller. The calling function must make the OS calls to get the
- + * base address of the DWC_otg controller registers. The core_params
- + * argument holds the parameters that specify how the core should be
- + * configured.
- + *
- + * @param reg_base_addr Base address of DWC_otg core registers
- + *
- + */
- +dwc_otg_core_if_t *dwc_otg_cil_init(const uint32_t * reg_base_addr)
- +{
- + dwc_otg_core_if_t *core_if = 0;
- + dwc_otg_dev_if_t *dev_if = 0;
- + dwc_otg_host_if_t *host_if = 0;
- + uint8_t *reg_base = (uint8_t *) reg_base_addr;
- + int i = 0;
- +
- + DWC_DEBUGPL(DBG_CILV, "%s(%p)\n", __func__, reg_base_addr);
- +
- + core_if = DWC_ALLOC(sizeof(dwc_otg_core_if_t));
- +
- + if (core_if == NULL) {
- + DWC_DEBUGPL(DBG_CIL,
- + "Allocation of dwc_otg_core_if_t failed\n");
- + return 0;
- + }
- + core_if->core_global_regs = (dwc_otg_core_global_regs_t *) reg_base;
- +
- + /*
- + * Allocate the Device Mode structures.
- + */
- + dev_if = DWC_ALLOC(sizeof(dwc_otg_dev_if_t));
- +
- + if (dev_if == NULL) {
- + DWC_DEBUGPL(DBG_CIL, "Allocation of dwc_otg_dev_if_t failed\n");
- + DWC_FREE(core_if);
- + return 0;
- + }
- +
- + dev_if->dev_global_regs =
- + (dwc_otg_device_global_regs_t *) (reg_base +
- + DWC_DEV_GLOBAL_REG_OFFSET);
- +
- + for (i = 0; i < MAX_EPS_CHANNELS; i++) {
- + dev_if->in_ep_regs[i] = (dwc_otg_dev_in_ep_regs_t *)
- + (reg_base + DWC_DEV_IN_EP_REG_OFFSET +
- + (i * DWC_EP_REG_OFFSET));
- +
- + dev_if->out_ep_regs[i] = (dwc_otg_dev_out_ep_regs_t *)
- + (reg_base + DWC_DEV_OUT_EP_REG_OFFSET +
- + (i * DWC_EP_REG_OFFSET));
- + DWC_DEBUGPL(DBG_CILV, "in_ep_regs[%d]->diepctl=%p\n",
- + i, &dev_if->in_ep_regs[i]->diepctl);
- + DWC_DEBUGPL(DBG_CILV, "out_ep_regs[%d]->doepctl=%p\n",
- + i, &dev_if->out_ep_regs[i]->doepctl);
- + }
- +
- + dev_if->speed = 0; // unknown
- +
- + core_if->dev_if = dev_if;
- +
- + /*
- + * Allocate the Host Mode structures.
- + */
- + host_if = DWC_ALLOC(sizeof(dwc_otg_host_if_t));
- +
- + if (host_if == NULL) {
- + DWC_DEBUGPL(DBG_CIL,
- + "Allocation of dwc_otg_host_if_t failed\n");
- + DWC_FREE(dev_if);
- + DWC_FREE(core_if);
- + return 0;
- + }
- +
- + host_if->host_global_regs = (dwc_otg_host_global_regs_t *)
- + (reg_base + DWC_OTG_HOST_GLOBAL_REG_OFFSET);
- +
- + host_if->hprt0 =
- + (uint32_t *) (reg_base + DWC_OTG_HOST_PORT_REGS_OFFSET);
- +
- + for (i = 0; i < MAX_EPS_CHANNELS; i++) {
- + host_if->hc_regs[i] = (dwc_otg_hc_regs_t *)
- + (reg_base + DWC_OTG_HOST_CHAN_REGS_OFFSET +
- + (i * DWC_OTG_CHAN_REGS_OFFSET));
- + DWC_DEBUGPL(DBG_CILV, "hc_reg[%d]->hcchar=%p\n",
- + i, &host_if->hc_regs[i]->hcchar);
- + }
- +
- + host_if->num_host_channels = MAX_EPS_CHANNELS;
- + core_if->host_if = host_if;
- +
- + for (i = 0; i < MAX_EPS_CHANNELS; i++) {
- + core_if->data_fifo[i] =
- + (uint32_t *) (reg_base + DWC_OTG_DATA_FIFO_OFFSET +
- + (i * DWC_OTG_DATA_FIFO_SIZE));
- + DWC_DEBUGPL(DBG_CILV, "data_fifo[%d]=0x%08lx\n",
- + i, (unsigned long)core_if->data_fifo[i]);
- + }
- +
- + core_if->pcgcctl = (uint32_t *) (reg_base + DWC_OTG_PCGCCTL_OFFSET);
- +
- + /* Initiate lx_state to L3 disconnected state */
- + core_if->lx_state = DWC_OTG_L3;
- + /*
- + * Store the contents of the hardware configuration registers here for
- + * easy access later.
- + */
- + core_if->hwcfg1.d32 =
- + DWC_READ_REG32(&core_if->core_global_regs->ghwcfg1);
- + core_if->hwcfg2.d32 =
- + DWC_READ_REG32(&core_if->core_global_regs->ghwcfg2);
- + core_if->hwcfg3.d32 =
- + DWC_READ_REG32(&core_if->core_global_regs->ghwcfg3);
- + core_if->hwcfg4.d32 =
- + DWC_READ_REG32(&core_if->core_global_regs->ghwcfg4);
- +
- + /* Force host mode to get HPTXFSIZ exact power on value */
- + {
- + gusbcfg_data_t gusbcfg = {.d32 = 0 };
- + gusbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
- + gusbcfg.b.force_host_mode = 1;
- + DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, gusbcfg.d32);
- + dwc_mdelay(100);
- + core_if->hptxfsiz.d32 =
- + DWC_READ_REG32(&core_if->core_global_regs->hptxfsiz);
- + gusbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
- + if (cil_force_host)
- + gusbcfg.b.force_host_mode = 1;
- + else
- + gusbcfg.b.force_host_mode = 0;
- + DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, gusbcfg.d32);
- + dwc_mdelay(100);
- + }
- +
- + DWC_DEBUGPL(DBG_CILV, "hwcfg1=%08x\n", core_if->hwcfg1.d32);
- + DWC_DEBUGPL(DBG_CILV, "hwcfg2=%08x\n", core_if->hwcfg2.d32);
- + DWC_DEBUGPL(DBG_CILV, "hwcfg3=%08x\n", core_if->hwcfg3.d32);
- + DWC_DEBUGPL(DBG_CILV, "hwcfg4=%08x\n", core_if->hwcfg4.d32);
- +
- + core_if->hcfg.d32 =
- + DWC_READ_REG32(&core_if->host_if->host_global_regs->hcfg);
- + core_if->dcfg.d32 =
- + DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg);
- +
- + DWC_DEBUGPL(DBG_CILV, "hcfg=%08x\n", core_if->hcfg.d32);
- + DWC_DEBUGPL(DBG_CILV, "dcfg=%08x\n", core_if->dcfg.d32);
- +
- + DWC_DEBUGPL(DBG_CILV, "op_mode=%0x\n", core_if->hwcfg2.b.op_mode);
- + DWC_DEBUGPL(DBG_CILV, "arch=%0x\n", core_if->hwcfg2.b.architecture);
- + DWC_DEBUGPL(DBG_CILV, "num_dev_ep=%d\n", core_if->hwcfg2.b.num_dev_ep);
- + DWC_DEBUGPL(DBG_CILV, "num_host_chan=%d\n",
- + core_if->hwcfg2.b.num_host_chan);
- + DWC_DEBUGPL(DBG_CILV, "nonperio_tx_q_depth=0x%0x\n",
- + core_if->hwcfg2.b.nonperio_tx_q_depth);
- + DWC_DEBUGPL(DBG_CILV, "host_perio_tx_q_depth=0x%0x\n",
- + core_if->hwcfg2.b.host_perio_tx_q_depth);
- + DWC_DEBUGPL(DBG_CILV, "dev_token_q_depth=0x%0x\n",
- + core_if->hwcfg2.b.dev_token_q_depth);
- +
- + DWC_DEBUGPL(DBG_CILV, "Total FIFO SZ=%d\n",
- + core_if->hwcfg3.b.dfifo_depth);
- + DWC_DEBUGPL(DBG_CILV, "xfer_size_cntr_width=%0x\n",
- + core_if->hwcfg3.b.xfer_size_cntr_width);
- +
- + /*
- + * Set the SRP sucess bit for FS-I2c
- + */
- + core_if->srp_success = 0;
- + core_if->srp_timer_started = 0;
- +
- + /*
- + * Create new workqueue and init works
- + */
- + core_if->wq_otg = DWC_WORKQ_ALLOC("dwc_otg");
- + if (core_if->wq_otg == 0) {
- + DWC_WARN("DWC_WORKQ_ALLOC failed\n");
- + DWC_FREE(host_if);
- + DWC_FREE(dev_if);
- + DWC_FREE(core_if);
- + return 0;
- + }
- +
- + core_if->snpsid = DWC_READ_REG32(&core_if->core_global_regs->gsnpsid);
- +
- + DWC_PRINTF("Core Release: %x.%x%x%x\n",
- + (core_if->snpsid >> 12 & 0xF),
- + (core_if->snpsid >> 8 & 0xF),
- + (core_if->snpsid >> 4 & 0xF), (core_if->snpsid & 0xF));
- +
- + core_if->wkp_timer = DWC_TIMER_ALLOC("Wake Up Timer",
- + w_wakeup_detected, core_if);
- + if (core_if->wkp_timer == 0) {
- + DWC_WARN("DWC_TIMER_ALLOC failed\n");
- + DWC_FREE(host_if);
- + DWC_FREE(dev_if);
- + DWC_WORKQ_FREE(core_if->wq_otg);
- + DWC_FREE(core_if);
- + return 0;
- + }
- +
- + if (dwc_otg_setup_params(core_if)) {
- + DWC_WARN("Error while setting core params\n");
- + }
- +
- + core_if->hibernation_suspend = 0;
- +
- + /** ADP initialization */
- + dwc_otg_adp_init(core_if);
- +
- + return core_if;
- +}
- +
- +/**
- + * This function frees the structures allocated by dwc_otg_cil_init().
- + *
- + * @param core_if The core interface pointer returned from
- + * dwc_otg_cil_init().
- + *
- + */
- +void dwc_otg_cil_remove(dwc_otg_core_if_t * core_if)
- +{
- + dctl_data_t dctl = {.d32 = 0 };
- + DWC_DEBUGPL(DBG_CILV, "%s(%p)\n", __func__, core_if);
- +
- + /* Disable all interrupts */
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gahbcfg, 1, 0);
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, 0);
- +
- + dctl.b.sftdiscon = 1;
- + if (core_if->snpsid >= OTG_CORE_REV_3_00a) {
- + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, 0,
- + dctl.d32);
- + }
- +
- + if (core_if->wq_otg) {
- + DWC_WORKQ_WAIT_WORK_DONE(core_if->wq_otg, 500);
- + DWC_WORKQ_FREE(core_if->wq_otg);
- + }
- + if (core_if->dev_if) {
- + DWC_FREE(core_if->dev_if);
- + }
- + if (core_if->host_if) {
- + DWC_FREE(core_if->host_if);
- + }
- +
- + /** Remove ADP Stuff */
- + dwc_otg_adp_remove(core_if);
- + if (core_if->core_params) {
- + DWC_FREE(core_if->core_params);
- + }
- + if (core_if->wkp_timer) {
- + DWC_TIMER_FREE(core_if->wkp_timer);
- + }
- + if (core_if->srp_timer) {
- + DWC_TIMER_FREE(core_if->srp_timer);
- + }
- + DWC_FREE(core_if);
- +}
- +
- +/**
- + * This function enables the controller's Global Interrupt in the AHB Config
- + * register.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + */
- +void dwc_otg_enable_global_interrupts(dwc_otg_core_if_t * core_if)
- +{
- + gahbcfg_data_t ahbcfg = {.d32 = 0 };
- + ahbcfg.b.glblintrmsk = 1; /* Enable interrupts */
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gahbcfg, 0, ahbcfg.d32);
- +}
- +
- +/**
- + * This function disables the controller's Global Interrupt in the AHB Config
- + * register.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + */
- +void dwc_otg_disable_global_interrupts(dwc_otg_core_if_t * core_if)
- +{
- + gahbcfg_data_t ahbcfg = {.d32 = 0 };
- + ahbcfg.b.glblintrmsk = 1; /* Disable interrupts */
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gahbcfg, ahbcfg.d32, 0);
- +}
- +
- +/**
- + * This function initializes the commmon interrupts, used in both
- + * device and host modes.
- + *
- + * @param core_if Programming view of the DWC_otg controller
- + *
- + */
- +static void dwc_otg_enable_common_interrupts(dwc_otg_core_if_t * core_if)
- +{
- + dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
- + gintmsk_data_t intr_mask = {.d32 = 0 };
- +
- + /* Clear any pending OTG Interrupts */
- + DWC_WRITE_REG32(&global_regs->gotgint, 0xFFFFFFFF);
- +
- + /* Clear any pending interrupts */
- + DWC_WRITE_REG32(&global_regs->gintsts, 0xFFFFFFFF);
- +
- + /*
- + * Enable the interrupts in the GINTMSK.
- + */
- + intr_mask.b.modemismatch = 1;
- + intr_mask.b.otgintr = 1;
- +
- + if (!core_if->dma_enable) {
- + intr_mask.b.rxstsqlvl = 1;
- + }
- +
- + intr_mask.b.conidstschng = 1;
- + intr_mask.b.wkupintr = 1;
- + intr_mask.b.disconnect = 0;
- + intr_mask.b.usbsuspend = 1;
- + intr_mask.b.sessreqintr = 1;
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- + if (core_if->core_params->lpm_enable) {
- + intr_mask.b.lpmtranrcvd = 1;
- + }
- +#endif
- + DWC_WRITE_REG32(&global_regs->gintmsk, intr_mask.d32);
- +}
- +
- +/*
- + * The restore operation is modified to support Synopsys Emulated Powerdown and
- + * Hibernation. This function is for exiting from Device mode hibernation by
- + * Host Initiated Resume/Reset and Device Initiated Remote-Wakeup.
- + * @param core_if Programming view of DWC_otg controller.
- + * @param rem_wakeup - indicates whether resume is initiated by Device or Host.
- + * @param reset - indicates whether resume is initiated by Reset.
- + */
- +int dwc_otg_device_hibernation_restore(dwc_otg_core_if_t * core_if,
- + int rem_wakeup, int reset)
- +{
- + gpwrdn_data_t gpwrdn = {.d32 = 0 };
- + pcgcctl_data_t pcgcctl = {.d32 = 0 };
- + dctl_data_t dctl = {.d32 = 0 };
- +
- + int timeout = 2000;
- +
- + if (!core_if->hibernation_suspend) {
- + DWC_PRINTF("Already exited from Hibernation\n");
- + return 1;
- + }
- +
- + DWC_DEBUGPL(DBG_PCD, "%s called\n", __FUNCTION__);
- + /* Switch-on voltage to the core */
- + gpwrdn.b.pwrdnswtch = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- + dwc_udelay(10);
- +
- + /* Reset core */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pwrdnrstn = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- + dwc_udelay(10);
- +
- + /* Assert Restore signal */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.restore = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
- + dwc_udelay(10);
- +
- + /* Disable power clamps */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pwrdnclmp = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- +
- + if (rem_wakeup) {
- + dwc_udelay(70);
- + }
- +
- + /* Deassert Reset core */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pwrdnrstn = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
- + dwc_udelay(10);
- +
- + /* Disable PMU interrupt */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pmuintsel = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- +
- + /* Mask interrupts from gpwrdn */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.connect_det_msk = 1;
- + gpwrdn.b.srp_det_msk = 1;
- + gpwrdn.b.disconn_det_msk = 1;
- + gpwrdn.b.rst_det_msk = 1;
- + gpwrdn.b.lnstchng_msk = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- +
- + /* Indicates that we are going out from hibernation */
- + core_if->hibernation_suspend = 0;
- +
- + /*
- + * Set Restore Essential Regs bit in PCGCCTL register, restore_mode = 1
- + * indicates restore from remote_wakeup
- + */
- + restore_essential_regs(core_if, rem_wakeup, 0);
- +
- + /*
- + * Wait a little for seeing new value of variable hibernation_suspend if
- + * Restore done interrupt received before polling
- + */
- + dwc_udelay(10);
- +
- + if (core_if->hibernation_suspend == 0) {
- + /*
- + * Wait For Restore_done Interrupt. This mechanism of polling the
- + * interrupt is introduced to avoid any possible race conditions
- + */
- + do {
- + gintsts_data_t gintsts;
- + gintsts.d32 =
- + DWC_READ_REG32(&core_if->core_global_regs->gintsts);
- + if (gintsts.b.restoredone) {
- + gintsts.d32 = 0;
- + gintsts.b.restoredone = 1;
- + DWC_WRITE_REG32(&core_if->core_global_regs->
- + gintsts, gintsts.d32);
- + DWC_PRINTF("Restore Done Interrupt seen\n");
- + break;
- + }
- + dwc_udelay(10);
- + } while (--timeout);
- + if (!timeout) {
- + DWC_PRINTF("Restore Done interrupt wasn't generated here\n");
- + }
- + }
- + /* Clear all pending interupts */
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
- +
- + /* De-assert Restore */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.restore = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- + dwc_udelay(10);
- +
- + if (!rem_wakeup) {
- + pcgcctl.d32 = 0;
- + pcgcctl.b.rstpdwnmodule = 1;
- + DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0);
- + }
- +
- + /* Restore GUSBCFG and DCFG */
- + DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg,
- + core_if->gr_backup->gusbcfg_local);
- + DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dcfg,
- + core_if->dr_backup->dcfg);
- +
- + /* De-assert Wakeup Logic */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pmuactv = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- + dwc_udelay(10);
- +
- + if (!rem_wakeup) {
- + /* Set Device programming done bit */
- + dctl.b.pwronprgdone = 1;
- + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, 0, dctl.d32);
- + } else {
- + /* Start Remote Wakeup Signaling */
- + dctl.d32 = core_if->dr_backup->dctl;
- + dctl.b.rmtwkupsig = 1;
- + DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32);
- + }
- +
- + dwc_mdelay(2);
- + /* Clear all pending interupts */
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
- +
- + /* Restore global registers */
- + dwc_otg_restore_global_regs(core_if);
- + /* Restore device global registers */
- + dwc_otg_restore_dev_regs(core_if, rem_wakeup);
- +
- + if (rem_wakeup) {
- + dwc_mdelay(7);
- + dctl.d32 = 0;
- + dctl.b.rmtwkupsig = 1;
- + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32, 0);
- + }
- +
- + core_if->hibernation_suspend = 0;
- + /* The core will be in ON STATE */
- + core_if->lx_state = DWC_OTG_L0;
- + DWC_PRINTF("Hibernation recovery completes here\n");
- +
- + return 1;
- +}
- +
- +/*
- + * The restore operation is modified to support Synopsys Emulated Powerdown and
- + * Hibernation. This function is for exiting from Host mode hibernation by
- + * Host Initiated Resume/Reset and Device Initiated Remote-Wakeup.
- + * @param core_if Programming view of DWC_otg controller.
- + * @param rem_wakeup - indicates whether resume is initiated by Device or Host.
- + * @param reset - indicates whether resume is initiated by Reset.
- + */
- +int dwc_otg_host_hibernation_restore(dwc_otg_core_if_t * core_if,
- + int rem_wakeup, int reset)
- +{
- + gpwrdn_data_t gpwrdn = {.d32 = 0 };
- + hprt0_data_t hprt0 = {.d32 = 0 };
- +
- + int timeout = 2000;
- +
- + DWC_DEBUGPL(DBG_HCD, "%s called\n", __FUNCTION__);
- + /* Switch-on voltage to the core */
- + gpwrdn.b.pwrdnswtch = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- + dwc_udelay(10);
- +
- + /* Reset core */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pwrdnrstn = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- + dwc_udelay(10);
- +
- + /* Assert Restore signal */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.restore = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
- + dwc_udelay(10);
- +
- + /* Disable power clamps */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pwrdnclmp = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- +
- + if (!rem_wakeup) {
- + dwc_udelay(50);
- + }
- +
- + /* Deassert Reset core */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pwrdnrstn = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
- + dwc_udelay(10);
- +
- + /* Disable PMU interrupt */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pmuintsel = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- +
- + gpwrdn.d32 = 0;
- + gpwrdn.b.connect_det_msk = 1;
- + gpwrdn.b.srp_det_msk = 1;
- + gpwrdn.b.disconn_det_msk = 1;
- + gpwrdn.b.rst_det_msk = 1;
- + gpwrdn.b.lnstchng_msk = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- +
- + /* Indicates that we are going out from hibernation */
- + core_if->hibernation_suspend = 0;
- +
- + /* Set Restore Essential Regs bit in PCGCCTL register */
- + restore_essential_regs(core_if, rem_wakeup, 1);
- +
- + /* Wait a little for seeing new value of variable hibernation_suspend if
- + * Restore done interrupt received before polling */
- + dwc_udelay(10);
- +
- + if (core_if->hibernation_suspend == 0) {
- + /* Wait For Restore_done Interrupt. This mechanism of polling the
- + * interrupt is introduced to avoid any possible race conditions
- + */
- + do {
- + gintsts_data_t gintsts;
- + gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
- + if (gintsts.b.restoredone) {
- + gintsts.d32 = 0;
- + gintsts.b.restoredone = 1;
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
- + DWC_DEBUGPL(DBG_HCD,"Restore Done Interrupt seen\n");
- + break;
- + }
- + dwc_udelay(10);
- + } while (--timeout);
- + if (!timeout) {
- + DWC_WARN("Restore Done interrupt wasn't generated\n");
- + }
- + }
- +
- + /* Set the flag's value to 0 again after receiving restore done interrupt */
- + core_if->hibernation_suspend = 0;
- +
- + /* This step is not described in functional spec but if not wait for this
- + * delay, mismatch interrupts occurred because just after restore core is
- + * in Device mode(gintsts.curmode == 0) */
- + dwc_mdelay(100);
- +
- + /* Clear all pending interrupts */
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
- +
- + /* De-assert Restore */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.restore = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- + dwc_udelay(10);
- +
- + /* Restore GUSBCFG and HCFG */
- + DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg,
- + core_if->gr_backup->gusbcfg_local);
- + DWC_WRITE_REG32(&core_if->host_if->host_global_regs->hcfg,
- + core_if->hr_backup->hcfg_local);
- +
- + /* De-assert Wakeup Logic */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pmuactv = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- + dwc_udelay(10);
- +
- + /* Start the Resume operation by programming HPRT0 */
- + hprt0.d32 = core_if->hr_backup->hprt0_local;
- + hprt0.b.prtpwr = 1;
- + hprt0.b.prtena = 0;
- + hprt0.b.prtsusp = 0;
- + DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
- +
- + DWC_PRINTF("Resume Starts Now\n");
- + if (!reset) { // Indicates it is Resume Operation
- + hprt0.d32 = core_if->hr_backup->hprt0_local;
- + hprt0.b.prtres = 1;
- + hprt0.b.prtpwr = 1;
- + hprt0.b.prtena = 0;
- + hprt0.b.prtsusp = 0;
- + DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
- +
- + if (!rem_wakeup)
- + hprt0.b.prtres = 0;
- + /* Wait for Resume time and then program HPRT again */
- + dwc_mdelay(100);
- + DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
- +
- + } else { // Indicates it is Reset Operation
- + hprt0.d32 = core_if->hr_backup->hprt0_local;
- + hprt0.b.prtrst = 1;
- + hprt0.b.prtpwr = 1;
- + hprt0.b.prtena = 0;
- + hprt0.b.prtsusp = 0;
- + DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
- + /* Wait for Reset time and then program HPRT again */
- + dwc_mdelay(60);
- + hprt0.b.prtrst = 0;
- + DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
- + }
- + /* Clear all interrupt status */
- + hprt0.d32 = dwc_otg_read_hprt0(core_if);
- + hprt0.b.prtconndet = 1;
- + hprt0.b.prtenchng = 1;
- + DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
- +
- + /* Clear all pending interupts */
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
- +
- + /* Restore global registers */
- + dwc_otg_restore_global_regs(core_if);
- + /* Restore host global registers */
- + dwc_otg_restore_host_regs(core_if, reset);
- +
- + /* The core will be in ON STATE */
- + core_if->lx_state = DWC_OTG_L0;
- + DWC_PRINTF("Hibernation recovery is complete here\n");
- + return 0;
- +}
- +
- +/** Saves some register values into system memory. */
- +int dwc_otg_save_global_regs(dwc_otg_core_if_t * core_if)
- +{
- + struct dwc_otg_global_regs_backup *gr;
- + int i;
- +
- + gr = core_if->gr_backup;
- + if (!gr) {
- + gr = DWC_ALLOC(sizeof(*gr));
- + if (!gr) {
- + return -DWC_E_NO_MEMORY;
- + }
- + core_if->gr_backup = gr;
- + }
- +
- + gr->gotgctl_local = DWC_READ_REG32(&core_if->core_global_regs->gotgctl);
- + gr->gintmsk_local = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
- + gr->gahbcfg_local = DWC_READ_REG32(&core_if->core_global_regs->gahbcfg);
- + gr->gusbcfg_local = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
- + gr->grxfsiz_local = DWC_READ_REG32(&core_if->core_global_regs->grxfsiz);
- + gr->gnptxfsiz_local = DWC_READ_REG32(&core_if->core_global_regs->gnptxfsiz);
- + gr->hptxfsiz_local = DWC_READ_REG32(&core_if->core_global_regs->hptxfsiz);
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- + gr->glpmcfg_local = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
- +#endif
- + gr->gi2cctl_local = DWC_READ_REG32(&core_if->core_global_regs->gi2cctl);
- + gr->pcgcctl_local = DWC_READ_REG32(core_if->pcgcctl);
- + gr->gdfifocfg_local =
- + DWC_READ_REG32(&core_if->core_global_regs->gdfifocfg);
- + for (i = 0; i < MAX_EPS_CHANNELS; i++) {
- + gr->dtxfsiz_local[i] =
- + DWC_READ_REG32(&(core_if->core_global_regs->dtxfsiz[i]));
- + }
- +
- + DWC_DEBUGPL(DBG_ANY, "===========Backing Global registers==========\n");
- + DWC_DEBUGPL(DBG_ANY, "Backed up gotgctl = %08x\n", gr->gotgctl_local);
- + DWC_DEBUGPL(DBG_ANY, "Backed up gintmsk = %08x\n", gr->gintmsk_local);
- + DWC_DEBUGPL(DBG_ANY, "Backed up gahbcfg = %08x\n", gr->gahbcfg_local);
- + DWC_DEBUGPL(DBG_ANY, "Backed up gusbcfg = %08x\n", gr->gusbcfg_local);
- + DWC_DEBUGPL(DBG_ANY, "Backed up grxfsiz = %08x\n", gr->grxfsiz_local);
- + DWC_DEBUGPL(DBG_ANY, "Backed up gnptxfsiz = %08x\n",
- + gr->gnptxfsiz_local);
- + DWC_DEBUGPL(DBG_ANY, "Backed up hptxfsiz = %08x\n",
- + gr->hptxfsiz_local);
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- + DWC_DEBUGPL(DBG_ANY, "Backed up glpmcfg = %08x\n", gr->glpmcfg_local);
- +#endif
- + DWC_DEBUGPL(DBG_ANY, "Backed up gi2cctl = %08x\n", gr->gi2cctl_local);
- + DWC_DEBUGPL(DBG_ANY, "Backed up pcgcctl = %08x\n", gr->pcgcctl_local);
- + DWC_DEBUGPL(DBG_ANY,"Backed up gdfifocfg = %08x\n",gr->gdfifocfg_local);
- +
- + return 0;
- +}
- +
- +/** Saves GINTMSK register before setting the msk bits. */
- +int dwc_otg_save_gintmsk_reg(dwc_otg_core_if_t * core_if)
- +{
- + struct dwc_otg_global_regs_backup *gr;
- +
- + gr = core_if->gr_backup;
- + if (!gr) {
- + gr = DWC_ALLOC(sizeof(*gr));
- + if (!gr) {
- + return -DWC_E_NO_MEMORY;
- + }
- + core_if->gr_backup = gr;
- + }
- +
- + gr->gintmsk_local = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
- +
- + DWC_DEBUGPL(DBG_ANY,"=============Backing GINTMSK registers============\n");
- + DWC_DEBUGPL(DBG_ANY, "Backed up gintmsk = %08x\n", gr->gintmsk_local);
- +
- + return 0;
- +}
- +
- +int dwc_otg_save_dev_regs(dwc_otg_core_if_t * core_if)
- +{
- + struct dwc_otg_dev_regs_backup *dr;
- + int i;
- +
- + dr = core_if->dr_backup;
- + if (!dr) {
- + dr = DWC_ALLOC(sizeof(*dr));
- + if (!dr) {
- + return -DWC_E_NO_MEMORY;
- + }
- + core_if->dr_backup = dr;
- + }
- +
- + dr->dcfg = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg);
- + dr->dctl = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dctl);
- + dr->daintmsk =
- + DWC_READ_REG32(&core_if->dev_if->dev_global_regs->daintmsk);
- + dr->diepmsk =
- + DWC_READ_REG32(&core_if->dev_if->dev_global_regs->diepmsk);
- + dr->doepmsk =
- + DWC_READ_REG32(&core_if->dev_if->dev_global_regs->doepmsk);
- +
- + for (i = 0; i < core_if->dev_if->num_in_eps; ++i) {
- + dr->diepctl[i] =
- + DWC_READ_REG32(&core_if->dev_if->in_ep_regs[i]->diepctl);
- + dr->dieptsiz[i] =
- + DWC_READ_REG32(&core_if->dev_if->in_ep_regs[i]->dieptsiz);
- + dr->diepdma[i] =
- + DWC_READ_REG32(&core_if->dev_if->in_ep_regs[i]->diepdma);
- + }
- +
- + DWC_DEBUGPL(DBG_ANY,
- + "=============Backing Host registers==============\n");
- + DWC_DEBUGPL(DBG_ANY, "Backed up dcfg = %08x\n", dr->dcfg);
- + DWC_DEBUGPL(DBG_ANY, "Backed up dctl = %08x\n", dr->dctl);
- + DWC_DEBUGPL(DBG_ANY, "Backed up daintmsk = %08x\n",
- + dr->daintmsk);
- + DWC_DEBUGPL(DBG_ANY, "Backed up diepmsk = %08x\n", dr->diepmsk);
- + DWC_DEBUGPL(DBG_ANY, "Backed up doepmsk = %08x\n", dr->doepmsk);
- + for (i = 0; i < core_if->dev_if->num_in_eps; ++i) {
- + DWC_DEBUGPL(DBG_ANY, "Backed up diepctl[%d] = %08x\n", i,
- + dr->diepctl[i]);
- + DWC_DEBUGPL(DBG_ANY, "Backed up dieptsiz[%d] = %08x\n",
- + i, dr->dieptsiz[i]);
- + DWC_DEBUGPL(DBG_ANY, "Backed up diepdma[%d] = %08x\n", i,
- + dr->diepdma[i]);
- + }
- +
- + return 0;
- +}
- +
- +int dwc_otg_save_host_regs(dwc_otg_core_if_t * core_if)
- +{
- + struct dwc_otg_host_regs_backup *hr;
- + int i;
- +
- + hr = core_if->hr_backup;
- + if (!hr) {
- + hr = DWC_ALLOC(sizeof(*hr));
- + if (!hr) {
- + return -DWC_E_NO_MEMORY;
- + }
- + core_if->hr_backup = hr;
- + }
- +
- + hr->hcfg_local =
- + DWC_READ_REG32(&core_if->host_if->host_global_regs->hcfg);
- + hr->haintmsk_local =
- + DWC_READ_REG32(&core_if->host_if->host_global_regs->haintmsk);
- + for (i = 0; i < dwc_otg_get_param_host_channels(core_if); ++i) {
- + hr->hcintmsk_local[i] =
- + DWC_READ_REG32(&core_if->host_if->hc_regs[i]->hcintmsk);
- + }
- + hr->hprt0_local = DWC_READ_REG32(core_if->host_if->hprt0);
- + hr->hfir_local =
- + DWC_READ_REG32(&core_if->host_if->host_global_regs->hfir);
- +
- + DWC_DEBUGPL(DBG_ANY,
- + "=============Backing Host registers===============\n");
- + DWC_DEBUGPL(DBG_ANY, "Backed up hcfg = %08x\n",
- + hr->hcfg_local);
- + DWC_DEBUGPL(DBG_ANY, "Backed up haintmsk = %08x\n", hr->haintmsk_local);
- + for (i = 0; i < dwc_otg_get_param_host_channels(core_if); ++i) {
- + DWC_DEBUGPL(DBG_ANY, "Backed up hcintmsk[%02d]=%08x\n", i,
- + hr->hcintmsk_local[i]);
- + }
- + DWC_DEBUGPL(DBG_ANY, "Backed up hprt0 = %08x\n",
- + hr->hprt0_local);
- + DWC_DEBUGPL(DBG_ANY, "Backed up hfir = %08x\n",
- + hr->hfir_local);
- +
- + return 0;
- +}
- +
- +int dwc_otg_restore_global_regs(dwc_otg_core_if_t *core_if)
- +{
- + struct dwc_otg_global_regs_backup *gr;
- + int i;
- +
- + gr = core_if->gr_backup;
- + if (!gr) {
- + return -DWC_E_INVALID;
- + }
- +
- + DWC_WRITE_REG32(&core_if->core_global_regs->gotgctl, gr->gotgctl_local);
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, gr->gintmsk_local);
- + DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, gr->gusbcfg_local);
- + DWC_WRITE_REG32(&core_if->core_global_regs->gahbcfg, gr->gahbcfg_local);
- + DWC_WRITE_REG32(&core_if->core_global_regs->grxfsiz, gr->grxfsiz_local);
- + DWC_WRITE_REG32(&core_if->core_global_regs->gnptxfsiz,
- + gr->gnptxfsiz_local);
- + DWC_WRITE_REG32(&core_if->core_global_regs->hptxfsiz,
- + gr->hptxfsiz_local);
- + DWC_WRITE_REG32(&core_if->core_global_regs->gdfifocfg,
- + gr->gdfifocfg_local);
- + for (i = 0; i < MAX_EPS_CHANNELS; i++) {
- + DWC_WRITE_REG32(&core_if->core_global_regs->dtxfsiz[i],
- + gr->dtxfsiz_local[i]);
- + }
- +
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
- + DWC_WRITE_REG32(core_if->host_if->hprt0, 0x0000100A);
- + DWC_WRITE_REG32(&core_if->core_global_regs->gahbcfg,
- + (gr->gahbcfg_local));
- + return 0;
- +}
- +
- +int dwc_otg_restore_dev_regs(dwc_otg_core_if_t * core_if, int rem_wakeup)
- +{
- + struct dwc_otg_dev_regs_backup *dr;
- + int i;
- +
- + dr = core_if->dr_backup;
- +
- + if (!dr) {
- + return -DWC_E_INVALID;
- + }
- +
- + if (!rem_wakeup) {
- + DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl,
- + dr->dctl);
- + }
- +
- + DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->daintmsk, dr->daintmsk);
- + DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->diepmsk, dr->diepmsk);
- + DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->doepmsk, dr->doepmsk);
- +
- + for (i = 0; i < core_if->dev_if->num_in_eps; ++i) {
- + DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[i]->dieptsiz, dr->dieptsiz[i]);
- + DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[i]->diepdma, dr->diepdma[i]);
- + DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[i]->diepctl, dr->diepctl[i]);
- + }
- +
- + return 0;
- +}
- +
- +int dwc_otg_restore_host_regs(dwc_otg_core_if_t * core_if, int reset)
- +{
- + struct dwc_otg_host_regs_backup *hr;
- + int i;
- + hr = core_if->hr_backup;
- +
- + if (!hr) {
- + return -DWC_E_INVALID;
- + }
- +
- + DWC_WRITE_REG32(&core_if->host_if->host_global_regs->hcfg, hr->hcfg_local);
- + //if (!reset)
- + //{
- + // DWC_WRITE_REG32(&core_if->host_if->host_global_regs->hfir, hr->hfir_local);
- + //}
- +
- + DWC_WRITE_REG32(&core_if->host_if->host_global_regs->haintmsk,
- + hr->haintmsk_local);
- + for (i = 0; i < dwc_otg_get_param_host_channels(core_if); ++i) {
- + DWC_WRITE_REG32(&core_if->host_if->hc_regs[i]->hcintmsk,
- + hr->hcintmsk_local[i]);
- + }
- +
- + return 0;
- +}
- +
- +int restore_lpm_i2c_regs(dwc_otg_core_if_t * core_if)
- +{
- + struct dwc_otg_global_regs_backup *gr;
- +
- + gr = core_if->gr_backup;
- +
- + /* Restore values for LPM and I2C */
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- + DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg, gr->glpmcfg_local);
- +#endif
- + DWC_WRITE_REG32(&core_if->core_global_regs->gi2cctl, gr->gi2cctl_local);
- +
- + return 0;
- +}
- +
- +int restore_essential_regs(dwc_otg_core_if_t * core_if, int rmode, int is_host)
- +{
- + struct dwc_otg_global_regs_backup *gr;
- + pcgcctl_data_t pcgcctl = {.d32 = 0 };
- + gahbcfg_data_t gahbcfg = {.d32 = 0 };
- + gusbcfg_data_t gusbcfg = {.d32 = 0 };
- + gintmsk_data_t gintmsk = {.d32 = 0 };
- +
- + /* Restore LPM and I2C registers */
- + restore_lpm_i2c_regs(core_if);
- +
- + /* Set PCGCCTL to 0 */
- + DWC_WRITE_REG32(core_if->pcgcctl, 0x00000000);
- +
- + gr = core_if->gr_backup;
- + /* Load restore values for [31:14] bits */
- + DWC_WRITE_REG32(core_if->pcgcctl,
- + ((gr->pcgcctl_local & 0xffffc000) | 0x00020000));
- +
- + /* Umnask global Interrupt in GAHBCFG and restore it */
- + gahbcfg.d32 = gr->gahbcfg_local;
- + gahbcfg.b.glblintrmsk = 1;
- + DWC_WRITE_REG32(&core_if->core_global_regs->gahbcfg, gahbcfg.d32);
- +
- + /* Clear all pending interupts */
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
- +
- + /* Unmask restore done interrupt */
- + gintmsk.b.restoredone = 1;
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, gintmsk.d32);
- +
- + /* Restore GUSBCFG and HCFG/DCFG */
- + gusbcfg.d32 = core_if->gr_backup->gusbcfg_local;
- + DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, gusbcfg.d32);
- +
- + if (is_host) {
- + hcfg_data_t hcfg = {.d32 = 0 };
- + hcfg.d32 = core_if->hr_backup->hcfg_local;
- + DWC_WRITE_REG32(&core_if->host_if->host_global_regs->hcfg,
- + hcfg.d32);
- +
- + /* Load restore values for [31:14] bits */
- + pcgcctl.d32 = gr->pcgcctl_local & 0xffffc000;
- + pcgcctl.d32 = gr->pcgcctl_local | 0x00020000;
- +
- + if (rmode)
- + pcgcctl.b.restoremode = 1;
- + DWC_WRITE_REG32(core_if->pcgcctl, pcgcctl.d32);
- + dwc_udelay(10);
- +
- + /* Load restore values for [31:14] bits and set EssRegRestored bit */
- + pcgcctl.d32 = gr->pcgcctl_local | 0xffffc000;
- + pcgcctl.d32 = gr->pcgcctl_local & 0xffffc000;
- + pcgcctl.b.ess_reg_restored = 1;
- + if (rmode)
- + pcgcctl.b.restoremode = 1;
- + DWC_WRITE_REG32(core_if->pcgcctl, pcgcctl.d32);
- + } else {
- + dcfg_data_t dcfg = {.d32 = 0 };
- + dcfg.d32 = core_if->dr_backup->dcfg;
- + DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dcfg, dcfg.d32);
- +
- + /* Load restore values for [31:14] bits */
- + pcgcctl.d32 = gr->pcgcctl_local & 0xffffc000;
- + pcgcctl.d32 = gr->pcgcctl_local | 0x00020000;
- + if (!rmode) {
- + pcgcctl.d32 |= 0x208;
- + }
- + DWC_WRITE_REG32(core_if->pcgcctl, pcgcctl.d32);
- + dwc_udelay(10);
- +
- + /* Load restore values for [31:14] bits */
- + pcgcctl.d32 = gr->pcgcctl_local & 0xffffc000;
- + pcgcctl.d32 = gr->pcgcctl_local | 0x00020000;
- + pcgcctl.b.ess_reg_restored = 1;
- + if (!rmode)
- + pcgcctl.d32 |= 0x208;
- + DWC_WRITE_REG32(core_if->pcgcctl, pcgcctl.d32);
- + }
- +
- + return 0;
- +}
- +
- +/**
- + * Initializes the FSLSPClkSel field of the HCFG register depending on the PHY
- + * type.
- + */
- +static void init_fslspclksel(dwc_otg_core_if_t * core_if)
- +{
- + uint32_t val;
- + hcfg_data_t hcfg;
- +
- + if (((core_if->hwcfg2.b.hs_phy_type == 2) &&
- + (core_if->hwcfg2.b.fs_phy_type == 1) &&
- + (core_if->core_params->ulpi_fs_ls)) ||
- + (core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS)) {
- + /* Full speed PHY */
- + val = DWC_HCFG_48_MHZ;
- + } else {
- + /* High speed PHY running at full speed or high speed */
- + val = DWC_HCFG_30_60_MHZ;
- + }
- +
- + DWC_DEBUGPL(DBG_CIL, "Initializing HCFG.FSLSPClkSel to 0x%1x\n", val);
- + hcfg.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->hcfg);
- + hcfg.b.fslspclksel = val;
- + DWC_WRITE_REG32(&core_if->host_if->host_global_regs->hcfg, hcfg.d32);
- +}
- +
- +/**
- + * Initializes the DevSpd field of the DCFG register depending on the PHY type
- + * and the enumeration speed of the device.
- + */
- +static void init_devspd(dwc_otg_core_if_t * core_if)
- +{
- + uint32_t val;
- + dcfg_data_t dcfg;
- +
- + if (((core_if->hwcfg2.b.hs_phy_type == 2) &&
- + (core_if->hwcfg2.b.fs_phy_type == 1) &&
- + (core_if->core_params->ulpi_fs_ls)) ||
- + (core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS)) {
- + /* Full speed PHY */
- + val = 0x3;
- + } else if (core_if->core_params->speed == DWC_SPEED_PARAM_FULL) {
- + /* High speed PHY running at full speed */
- + val = 0x1;
- + } else {
- + /* High speed PHY running at high speed */
- + val = 0x0;
- + }
- +
- + DWC_DEBUGPL(DBG_CIL, "Initializing DCFG.DevSpd to 0x%1x\n", val);
- +
- + dcfg.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg);
- + dcfg.b.devspd = val;
- + DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dcfg, dcfg.d32);
- +}
- +
- +/**
- + * This function calculates the number of IN EPS
- + * using GHWCFG1 and GHWCFG2 registers values
- + *
- + * @param core_if Programming view of the DWC_otg controller
- + */
- +static uint32_t calc_num_in_eps(dwc_otg_core_if_t * core_if)
- +{
- + uint32_t num_in_eps = 0;
- + uint32_t num_eps = core_if->hwcfg2.b.num_dev_ep;
- + uint32_t hwcfg1 = core_if->hwcfg1.d32 >> 3;
- + uint32_t num_tx_fifos = core_if->hwcfg4.b.num_in_eps;
- + int i;
- +
- + for (i = 0; i < num_eps; ++i) {
- + if (!(hwcfg1 & 0x1))
- + num_in_eps++;
- +
- + hwcfg1 >>= 2;
- + }
- +
- + if (core_if->hwcfg4.b.ded_fifo_en) {
- + num_in_eps =
- + (num_in_eps > num_tx_fifos) ? num_tx_fifos : num_in_eps;
- + }
- +
- + return num_in_eps;
- +}
- +
- +/**
- + * This function calculates the number of OUT EPS
- + * using GHWCFG1 and GHWCFG2 registers values
- + *
- + * @param core_if Programming view of the DWC_otg controller
- + */
- +static uint32_t calc_num_out_eps(dwc_otg_core_if_t * core_if)
- +{
- + uint32_t num_out_eps = 0;
- + uint32_t num_eps = core_if->hwcfg2.b.num_dev_ep;
- + uint32_t hwcfg1 = core_if->hwcfg1.d32 >> 2;
- + int i;
- +
- + for (i = 0; i < num_eps; ++i) {
- + if (!(hwcfg1 & 0x1))
- + num_out_eps++;
- +
- + hwcfg1 >>= 2;
- + }
- + return num_out_eps;
- +}
- +
- +/**
- + * This function initializes the DWC_otg controller registers and
- + * prepares the core for device mode or host mode operation.
- + *
- + * @param core_if Programming view of the DWC_otg controller
- + *
- + */
- +void dwc_otg_core_init(dwc_otg_core_if_t * core_if)
- +{
- + int i = 0;
- + dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
- + dwc_otg_dev_if_t *dev_if = core_if->dev_if;
- + gahbcfg_data_t ahbcfg = {.d32 = 0 };
- + gusbcfg_data_t usbcfg = {.d32 = 0 };
- + gi2cctl_data_t i2cctl = {.d32 = 0 };
- +
- + DWC_DEBUGPL(DBG_CILV, "dwc_otg_core_init(%p) regs at %p\n",
- + core_if, global_regs);
- +
- + /* Common Initialization */
- + usbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg);
- +
- + /* Program the ULPI External VBUS bit if needed */
- + usbcfg.b.ulpi_ext_vbus_drv =
- + (core_if->core_params->phy_ulpi_ext_vbus ==
- + DWC_PHY_ULPI_EXTERNAL_VBUS) ? 1 : 0;
- +
- + /* Set external TS Dline pulsing */
- + usbcfg.b.term_sel_dl_pulse =
- + (core_if->core_params->ts_dline == 1) ? 1 : 0;
- + DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32);
- +
- + /* Reset the Controller */
- + dwc_otg_core_reset(core_if);
- +
- + core_if->adp_enable = core_if->core_params->adp_supp_enable;
- + core_if->power_down = core_if->core_params->power_down;
- + core_if->otg_sts = 0;
- +
- + /* Initialize parameters from Hardware configuration registers. */
- + dev_if->num_in_eps = calc_num_in_eps(core_if);
- + dev_if->num_out_eps = calc_num_out_eps(core_if);
- +
- + DWC_DEBUGPL(DBG_CIL, "num_dev_perio_in_ep=%d\n",
- + core_if->hwcfg4.b.num_dev_perio_in_ep);
- +
- + for (i = 0; i < core_if->hwcfg4.b.num_dev_perio_in_ep; i++) {
- + dev_if->perio_tx_fifo_size[i] =
- + DWC_READ_REG32(&global_regs->dtxfsiz[i]) >> 16;
- + DWC_DEBUGPL(DBG_CIL, "Periodic Tx FIFO SZ #%d=0x%0x\n",
- + i, dev_if->perio_tx_fifo_size[i]);
- + }
- +
- + for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
- + dev_if->tx_fifo_size[i] =
- + DWC_READ_REG32(&global_regs->dtxfsiz[i]) >> 16;
- + DWC_DEBUGPL(DBG_CIL, "Tx FIFO SZ #%d=0x%0x\n",
- + i, dev_if->tx_fifo_size[i]);
- + }
- +
- + core_if->total_fifo_size = core_if->hwcfg3.b.dfifo_depth;
- + core_if->rx_fifo_size = DWC_READ_REG32(&global_regs->grxfsiz);
- + core_if->nperio_tx_fifo_size =
- + DWC_READ_REG32(&global_regs->gnptxfsiz) >> 16;
- +
- + DWC_DEBUGPL(DBG_CIL, "Total FIFO SZ=%d\n", core_if->total_fifo_size);
- + DWC_DEBUGPL(DBG_CIL, "Rx FIFO SZ=%d\n", core_if->rx_fifo_size);
- + DWC_DEBUGPL(DBG_CIL, "NP Tx FIFO SZ=%d\n",
- + core_if->nperio_tx_fifo_size);
- +
- + /* This programming sequence needs to happen in FS mode before any other
- + * programming occurs */
- + if ((core_if->core_params->speed == DWC_SPEED_PARAM_FULL) &&
- + (core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS)) {
- + /* If FS mode with FS PHY */
- +
- + /* core_init() is now called on every switch so only call the
- + * following for the first time through. */
- + if (!core_if->phy_init_done) {
- + core_if->phy_init_done = 1;
- + DWC_DEBUGPL(DBG_CIL, "FS_PHY detected\n");
- + usbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg);
- + usbcfg.b.physel = 1;
- + DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32);
- +
- + /* Reset after a PHY select */
- + dwc_otg_core_reset(core_if);
- + }
- +
- + /* Program DCFG.DevSpd or HCFG.FSLSPclkSel to 48Mhz in FS. Also
- + * do this on HNP Dev/Host mode switches (done in dev_init and
- + * host_init). */
- + if (dwc_otg_is_host_mode(core_if)) {
- + init_fslspclksel(core_if);
- + } else {
- + init_devspd(core_if);
- + }
- +
- + if (core_if->core_params->i2c_enable) {
- + DWC_DEBUGPL(DBG_CIL, "FS_PHY Enabling I2c\n");
- + /* Program GUSBCFG.OtgUtmifsSel to I2C */
- + usbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg);
- + usbcfg.b.otgutmifssel = 1;
- + DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32);
- +
- + /* Program GI2CCTL.I2CEn */
- + i2cctl.d32 = DWC_READ_REG32(&global_regs->gi2cctl);
- + i2cctl.b.i2cdevaddr = 1;
- + i2cctl.b.i2cen = 0;
- + DWC_WRITE_REG32(&global_regs->gi2cctl, i2cctl.d32);
- + i2cctl.b.i2cen = 1;
- + DWC_WRITE_REG32(&global_regs->gi2cctl, i2cctl.d32);
- + }
- +
- + } /* endif speed == DWC_SPEED_PARAM_FULL */
- + else {
- + /* High speed PHY. */
- + if (!core_if->phy_init_done) {
- + core_if->phy_init_done = 1;
- + /* HS PHY parameters. These parameters are preserved
- + * during soft reset so only program the first time. Do
- + * a soft reset immediately after setting phyif. */
- +
- + if (core_if->core_params->phy_type == 2) {
- + /* ULPI interface */
- + usbcfg.b.ulpi_utmi_sel = 1;
- + usbcfg.b.phyif = 0;
- + usbcfg.b.ddrsel =
- + core_if->core_params->phy_ulpi_ddr;
- + } else if (core_if->core_params->phy_type == 1) {
- + /* UTMI+ interface */
- + usbcfg.b.ulpi_utmi_sel = 0;
- + if (core_if->core_params->phy_utmi_width == 16) {
- + usbcfg.b.phyif = 1;
- +
- + } else {
- + usbcfg.b.phyif = 0;
- + }
- + } else {
- + DWC_ERROR("FS PHY TYPE\n");
- + }
- + DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32);
- + /* Reset after setting the PHY parameters */
- + dwc_otg_core_reset(core_if);
- + }
- + }
- +
- + if ((core_if->hwcfg2.b.hs_phy_type == 2) &&
- + (core_if->hwcfg2.b.fs_phy_type == 1) &&
- + (core_if->core_params->ulpi_fs_ls)) {
- + DWC_DEBUGPL(DBG_CIL, "Setting ULPI FSLS\n");
- + usbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg);
- + usbcfg.b.ulpi_fsls = 1;
- + usbcfg.b.ulpi_clk_sus_m = 1;
- + DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32);
- + } else {
- + usbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg);
- + usbcfg.b.ulpi_fsls = 0;
- + usbcfg.b.ulpi_clk_sus_m = 0;
- + DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32);
- + }
- +
- + /* Program the GAHBCFG Register. */
- + switch (core_if->hwcfg2.b.architecture) {
- +
- + case DWC_SLAVE_ONLY_ARCH:
- + DWC_DEBUGPL(DBG_CIL, "Slave Only Mode\n");
- + ahbcfg.b.nptxfemplvl_txfemplvl =
- + DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY;
- + ahbcfg.b.ptxfemplvl = DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY;
- + core_if->dma_enable = 0;
- + core_if->dma_desc_enable = 0;
- + break;
- +
- + case DWC_EXT_DMA_ARCH:
- + DWC_DEBUGPL(DBG_CIL, "External DMA Mode\n");
- + {
- + uint8_t brst_sz = core_if->core_params->dma_burst_size;
- + ahbcfg.b.hburstlen = 0;
- + while (brst_sz > 1) {
- + ahbcfg.b.hburstlen++;
- + brst_sz >>= 1;
- + }
- + }
- + core_if->dma_enable = (core_if->core_params->dma_enable != 0);
- + core_if->dma_desc_enable =
- + (core_if->core_params->dma_desc_enable != 0);
- + break;
- +
- + case DWC_INT_DMA_ARCH:
- + DWC_DEBUGPL(DBG_CIL, "Internal DMA Mode\n");
- + /* Old value was DWC_GAHBCFG_INT_DMA_BURST_INCR - done for
- + Host mode ISOC in issue fix - vahrama */
- + /* Broadcom had altered to (1<<3)|(0<<0) - WRESP=1, max 4 beats */
- + ahbcfg.b.hburstlen = (1<<3)|(0<<0);//DWC_GAHBCFG_INT_DMA_BURST_INCR4;
- + core_if->dma_enable = (core_if->core_params->dma_enable != 0);
- + core_if->dma_desc_enable =
- + (core_if->core_params->dma_desc_enable != 0);
- + break;
- +
- + }
- + if (core_if->dma_enable) {
- + if (core_if->dma_desc_enable) {
- + DWC_PRINTF("Using Descriptor DMA mode\n");
- + } else {
- + DWC_PRINTF("Using Buffer DMA mode\n");
- +
- + }
- + } else {
- + DWC_PRINTF("Using Slave mode\n");
- + core_if->dma_desc_enable = 0;
- + }
- +
- + if (core_if->core_params->ahb_single) {
- + ahbcfg.b.ahbsingle = 1;
- + }
- +
- + ahbcfg.b.dmaenable = core_if->dma_enable;
- + DWC_WRITE_REG32(&global_regs->gahbcfg, ahbcfg.d32);
- +
- + core_if->en_multiple_tx_fifo = core_if->hwcfg4.b.ded_fifo_en;
- +
- + core_if->pti_enh_enable = core_if->core_params->pti_enable != 0;
- + core_if->multiproc_int_enable = core_if->core_params->mpi_enable;
- + DWC_PRINTF("Periodic Transfer Interrupt Enhancement - %s\n",
- + ((core_if->pti_enh_enable) ? "enabled" : "disabled"));
- + DWC_PRINTF("Multiprocessor Interrupt Enhancement - %s\n",
- + ((core_if->multiproc_int_enable) ? "enabled" : "disabled"));
- +
- + /*
- + * Program the GUSBCFG register.
- + */
- + usbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg);
- +
- + switch (core_if->hwcfg2.b.op_mode) {
- + case DWC_MODE_HNP_SRP_CAPABLE:
- + usbcfg.b.hnpcap = (core_if->core_params->otg_cap ==
- + DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE);
- + usbcfg.b.srpcap = (core_if->core_params->otg_cap !=
- + DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE);
- + break;
- +
- + case DWC_MODE_SRP_ONLY_CAPABLE:
- + usbcfg.b.hnpcap = 0;
- + usbcfg.b.srpcap = (core_if->core_params->otg_cap !=
- + DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE);
- + break;
- +
- + case DWC_MODE_NO_HNP_SRP_CAPABLE:
- + usbcfg.b.hnpcap = 0;
- + usbcfg.b.srpcap = 0;
- + break;
- +
- + case DWC_MODE_SRP_CAPABLE_DEVICE:
- + usbcfg.b.hnpcap = 0;
- + usbcfg.b.srpcap = (core_if->core_params->otg_cap !=
- + DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE);
- + break;
- +
- + case DWC_MODE_NO_SRP_CAPABLE_DEVICE:
- + usbcfg.b.hnpcap = 0;
- + usbcfg.b.srpcap = 0;
- + break;
- +
- + case DWC_MODE_SRP_CAPABLE_HOST:
- + usbcfg.b.hnpcap = 0;
- + usbcfg.b.srpcap = (core_if->core_params->otg_cap !=
- + DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE);
- + break;
- +
- + case DWC_MODE_NO_SRP_CAPABLE_HOST:
- + usbcfg.b.hnpcap = 0;
- + usbcfg.b.srpcap = 0;
- + break;
- + }
- +
- + DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32);
- +
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- + if (core_if->core_params->lpm_enable) {
- + glpmcfg_data_t lpmcfg = {.d32 = 0 };
- +
- + /* To enable LPM support set lpm_cap_en bit */
- + lpmcfg.b.lpm_cap_en = 1;
- +
- + /* Make AppL1Res ACK */
- + lpmcfg.b.appl_resp = 1;
- +
- + /* Retry 3 times */
- + lpmcfg.b.retry_count = 3;
- +
- + DWC_MODIFY_REG32(&core_if->core_global_regs->glpmcfg,
- + 0, lpmcfg.d32);
- +
- + }
- +#endif
- + if (core_if->core_params->ic_usb_cap) {
- + gusbcfg_data_t gusbcfg = {.d32 = 0 };
- + gusbcfg.b.ic_usb_cap = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gusbcfg,
- + 0, gusbcfg.d32);
- + }
- + {
- + gotgctl_data_t gotgctl = {.d32 = 0 };
- + gotgctl.b.otgver = core_if->core_params->otg_ver;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gotgctl, 0,
- + gotgctl.d32);
- + /* Set OTG version supported */
- + core_if->otg_ver = core_if->core_params->otg_ver;
- + DWC_PRINTF("OTG VER PARAM: %d, OTG VER FLAG: %d\n",
- + core_if->core_params->otg_ver, core_if->otg_ver);
- + }
- +
- +
- + /* Enable common interrupts */
- + dwc_otg_enable_common_interrupts(core_if);
- +
- + /* Do device or host intialization based on mode during PCD
- + * and HCD initialization */
- + if (dwc_otg_is_host_mode(core_if)) {
- + DWC_DEBUGPL(DBG_ANY, "Host Mode\n");
- + core_if->op_state = A_HOST;
- + } else {
- + DWC_DEBUGPL(DBG_ANY, "Device Mode\n");
- + core_if->op_state = B_PERIPHERAL;
- +#ifdef DWC_DEVICE_ONLY
- + dwc_otg_core_dev_init(core_if);
- +#endif
- + }
- +}
- +
- +/**
- + * This function enables the Device mode interrupts.
- + *
- + * @param core_if Programming view of DWC_otg controller
- + */
- +void dwc_otg_enable_device_interrupts(dwc_otg_core_if_t * core_if)
- +{
- + gintmsk_data_t intr_mask = {.d32 = 0 };
- + dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
- +
- + DWC_DEBUGPL(DBG_CIL, "%s()\n", __func__);
- +
- + /* Disable all interrupts. */
- + DWC_WRITE_REG32(&global_regs->gintmsk, 0);
- +
- + /* Clear any pending interrupts */
- + DWC_WRITE_REG32(&global_regs->gintsts, 0xFFFFFFFF);
- +
- + /* Enable the common interrupts */
- + dwc_otg_enable_common_interrupts(core_if);
- +
- + /* Enable interrupts */
- + intr_mask.b.usbreset = 1;
- + intr_mask.b.enumdone = 1;
- + /* Disable Disconnect interrupt in Device mode */
- + intr_mask.b.disconnect = 0;
- +
- + if (!core_if->multiproc_int_enable) {
- + intr_mask.b.inepintr = 1;
- + intr_mask.b.outepintr = 1;
- + }
- +
- + intr_mask.b.erlysuspend = 1;
- +
- + if (core_if->en_multiple_tx_fifo == 0) {
- + intr_mask.b.epmismatch = 1;
- + }
- +
- + //intr_mask.b.incomplisoout = 1;
- + intr_mask.b.incomplisoin = 1;
- +
- +/* Enable the ignore frame number for ISOC xfers - MAS */
- +/* Disable to support high bandwith ISOC transfers - manukz */
- +#if 0
- +#ifdef DWC_UTE_PER_IO
- + if (core_if->dma_enable) {
- + if (core_if->dma_desc_enable) {
- + dctl_data_t dctl1 = {.d32 = 0 };
- + dctl1.b.ifrmnum = 1;
- + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->
- + dctl, 0, dctl1.d32);
- + DWC_DEBUG("----Enabled Ignore frame number (0x%08x)",
- + DWC_READ_REG32(&core_if->dev_if->
- + dev_global_regs->dctl));
- + }
- + }
- +#endif
- +#endif
- +#ifdef DWC_EN_ISOC
- + if (core_if->dma_enable) {
- + if (core_if->dma_desc_enable == 0) {
- + if (core_if->pti_enh_enable) {
- + dctl_data_t dctl = {.d32 = 0 };
- + dctl.b.ifrmnum = 1;
- + DWC_MODIFY_REG32(&core_if->
- + dev_if->dev_global_regs->dctl,
- + 0, dctl.d32);
- + } else {
- + intr_mask.b.incomplisoin = 1;
- + intr_mask.b.incomplisoout = 1;
- + }
- + }
- + } else {
- + intr_mask.b.incomplisoin = 1;
- + intr_mask.b.incomplisoout = 1;
- + }
- +#endif /* DWC_EN_ISOC */
- +
- + /** @todo NGS: Should this be a module parameter? */
- +#ifdef USE_PERIODIC_EP
- + intr_mask.b.isooutdrop = 1;
- + intr_mask.b.eopframe = 1;
- + intr_mask.b.incomplisoin = 1;
- + intr_mask.b.incomplisoout = 1;
- +#endif
- +
- + DWC_MODIFY_REG32(&global_regs->gintmsk, intr_mask.d32, intr_mask.d32);
- +
- + DWC_DEBUGPL(DBG_CIL, "%s() gintmsk=%0x\n", __func__,
- + DWC_READ_REG32(&global_regs->gintmsk));
- +}
- +
- +/**
- + * This function initializes the DWC_otg controller registers for
- + * device mode.
- + *
- + * @param core_if Programming view of DWC_otg controller
- + *
- + */
- +void dwc_otg_core_dev_init(dwc_otg_core_if_t * core_if)
- +{
- + int i;
- + dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
- + dwc_otg_dev_if_t *dev_if = core_if->dev_if;
- + dwc_otg_core_params_t *params = core_if->core_params;
- + dcfg_data_t dcfg = {.d32 = 0 };
- + depctl_data_t diepctl = {.d32 = 0 };
- + grstctl_t resetctl = {.d32 = 0 };
- + uint32_t rx_fifo_size;
- + fifosize_data_t nptxfifosize;
- + fifosize_data_t txfifosize;
- + dthrctl_data_t dthrctl;
- + fifosize_data_t ptxfifosize;
- + uint16_t rxfsiz, nptxfsiz;
- + gdfifocfg_data_t gdfifocfg = {.d32 = 0 };
- + hwcfg3_data_t hwcfg3 = {.d32 = 0 };
- +
- + /* Restart the Phy Clock */
- + DWC_WRITE_REG32(core_if->pcgcctl, 0);
- +
- + /* Device configuration register */
- + init_devspd(core_if);
- + dcfg.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dcfg);
- + dcfg.b.descdma = (core_if->dma_desc_enable) ? 1 : 0;
- + dcfg.b.perfrint = DWC_DCFG_FRAME_INTERVAL_80;
- + /* Enable Device OUT NAK in case of DDMA mode*/
- + if (core_if->core_params->dev_out_nak) {
- + dcfg.b.endevoutnak = 1;
- + }
- +
- + if (core_if->core_params->cont_on_bna) {
- + dctl_data_t dctl = {.d32 = 0 };
- + dctl.b.encontonbna = 1;
- + DWC_MODIFY_REG32(&dev_if->dev_global_regs->dctl, 0, dctl.d32);
- + }
- +
- +
- + DWC_WRITE_REG32(&dev_if->dev_global_regs->dcfg, dcfg.d32);
- +
- + /* Configure data FIFO sizes */
- + if (core_if->hwcfg2.b.dynamic_fifo && params->enable_dynamic_fifo) {
- + DWC_DEBUGPL(DBG_CIL, "Total FIFO Size=%d\n",
- + core_if->total_fifo_size);
- + DWC_DEBUGPL(DBG_CIL, "Rx FIFO Size=%d\n",
- + params->dev_rx_fifo_size);
- + DWC_DEBUGPL(DBG_CIL, "NP Tx FIFO Size=%d\n",
- + params->dev_nperio_tx_fifo_size);
- +
- + /* Rx FIFO */
- + DWC_DEBUGPL(DBG_CIL, "initial grxfsiz=%08x\n",
- + DWC_READ_REG32(&global_regs->grxfsiz));
- +
- +#ifdef DWC_UTE_CFI
- + core_if->pwron_rxfsiz = DWC_READ_REG32(&global_regs->grxfsiz);
- + core_if->init_rxfsiz = params->dev_rx_fifo_size;
- +#endif
- + rx_fifo_size = params->dev_rx_fifo_size;
- + DWC_WRITE_REG32(&global_regs->grxfsiz, rx_fifo_size);
- +
- + DWC_DEBUGPL(DBG_CIL, "new grxfsiz=%08x\n",
- + DWC_READ_REG32(&global_regs->grxfsiz));
- +
- + /** Set Periodic Tx FIFO Mask all bits 0 */
- + core_if->p_tx_msk = 0;
- +
- + /** Set Tx FIFO Mask all bits 0 */
- + core_if->tx_msk = 0;
- +
- + if (core_if->en_multiple_tx_fifo == 0) {
- + /* Non-periodic Tx FIFO */
- + DWC_DEBUGPL(DBG_CIL, "initial gnptxfsiz=%08x\n",
- + DWC_READ_REG32(&global_regs->gnptxfsiz));
- +
- + nptxfifosize.b.depth = params->dev_nperio_tx_fifo_size;
- + nptxfifosize.b.startaddr = params->dev_rx_fifo_size;
- +
- + DWC_WRITE_REG32(&global_regs->gnptxfsiz,
- + nptxfifosize.d32);
- +
- + DWC_DEBUGPL(DBG_CIL, "new gnptxfsiz=%08x\n",
- + DWC_READ_REG32(&global_regs->gnptxfsiz));
- +
- + /**@todo NGS: Fix Periodic FIFO Sizing! */
- + /*
- + * Periodic Tx FIFOs These FIFOs are numbered from 1 to 15.
- + * Indexes of the FIFO size module parameters in the
- + * dev_perio_tx_fifo_size array and the FIFO size registers in
- + * the dptxfsiz array run from 0 to 14.
- + */
- + /** @todo Finish debug of this */
- + ptxfifosize.b.startaddr =
- + nptxfifosize.b.startaddr + nptxfifosize.b.depth;
- + for (i = 0; i < core_if->hwcfg4.b.num_dev_perio_in_ep; i++) {
- + ptxfifosize.b.depth =
- + params->dev_perio_tx_fifo_size[i];
- + DWC_DEBUGPL(DBG_CIL,
- + "initial dtxfsiz[%d]=%08x\n", i,
- + DWC_READ_REG32(&global_regs->dtxfsiz
- + [i]));
- + DWC_WRITE_REG32(&global_regs->dtxfsiz[i],
- + ptxfifosize.d32);
- + DWC_DEBUGPL(DBG_CIL, "new dtxfsiz[%d]=%08x\n",
- + i,
- + DWC_READ_REG32(&global_regs->dtxfsiz
- + [i]));
- + ptxfifosize.b.startaddr += ptxfifosize.b.depth;
- + }
- + } else {
- + /*
- + * Tx FIFOs These FIFOs are numbered from 1 to 15.
- + * Indexes of the FIFO size module parameters in the
- + * dev_tx_fifo_size array and the FIFO size registers in
- + * the dtxfsiz array run from 0 to 14.
- + */
- +
- + /* Non-periodic Tx FIFO */
- + DWC_DEBUGPL(DBG_CIL, "initial gnptxfsiz=%08x\n",
- + DWC_READ_REG32(&global_regs->gnptxfsiz));
- +
- +#ifdef DWC_UTE_CFI
- + core_if->pwron_gnptxfsiz =
- + (DWC_READ_REG32(&global_regs->gnptxfsiz) >> 16);
- + core_if->init_gnptxfsiz =
- + params->dev_nperio_tx_fifo_size;
- +#endif
- + nptxfifosize.b.depth = params->dev_nperio_tx_fifo_size;
- + nptxfifosize.b.startaddr = params->dev_rx_fifo_size;
- +
- + DWC_WRITE_REG32(&global_regs->gnptxfsiz,
- + nptxfifosize.d32);
- +
- + DWC_DEBUGPL(DBG_CIL, "new gnptxfsiz=%08x\n",
- + DWC_READ_REG32(&global_regs->gnptxfsiz));
- +
- + txfifosize.b.startaddr =
- + nptxfifosize.b.startaddr + nptxfifosize.b.depth;
- +
- + for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) {
- +
- + txfifosize.b.depth =
- + params->dev_tx_fifo_size[i];
- +
- + DWC_DEBUGPL(DBG_CIL,
- + "initial dtxfsiz[%d]=%08x\n",
- + i,
- + DWC_READ_REG32(&global_regs->dtxfsiz
- + [i]));
- +
- +#ifdef DWC_UTE_CFI
- + core_if->pwron_txfsiz[i] =
- + (DWC_READ_REG32
- + (&global_regs->dtxfsiz[i]) >> 16);
- + core_if->init_txfsiz[i] =
- + params->dev_tx_fifo_size[i];
- +#endif
- + DWC_WRITE_REG32(&global_regs->dtxfsiz[i],
- + txfifosize.d32);
- +
- + DWC_DEBUGPL(DBG_CIL,
- + "new dtxfsiz[%d]=%08x\n",
- + i,
- + DWC_READ_REG32(&global_regs->dtxfsiz
- + [i]));
- +
- + txfifosize.b.startaddr += txfifosize.b.depth;
- + }
- + if (core_if->snpsid <= OTG_CORE_REV_2_94a) {
- + /* Calculating DFIFOCFG for Device mode to include RxFIFO and NPTXFIFO */
- + gdfifocfg.d32 = DWC_READ_REG32(&global_regs->gdfifocfg);
- + hwcfg3.d32 = DWC_READ_REG32(&global_regs->ghwcfg3);
- + gdfifocfg.b.gdfifocfg = (DWC_READ_REG32(&global_regs->ghwcfg3) >> 16);
- + DWC_WRITE_REG32(&global_regs->gdfifocfg, gdfifocfg.d32);
- + rxfsiz = (DWC_READ_REG32(&global_regs->grxfsiz) & 0x0000ffff);
- + nptxfsiz = (DWC_READ_REG32(&global_regs->gnptxfsiz) >> 16);
- + gdfifocfg.b.epinfobase = rxfsiz + nptxfsiz;
- + DWC_WRITE_REG32(&global_regs->gdfifocfg, gdfifocfg.d32);
- + }
- + }
- +
- + /* Flush the FIFOs */
- + dwc_otg_flush_tx_fifo(core_if, 0x10); /* all Tx FIFOs */
- + dwc_otg_flush_rx_fifo(core_if);
- +
- + /* Flush the Learning Queue. */
- + resetctl.b.intknqflsh = 1;
- + DWC_WRITE_REG32(&core_if->core_global_regs->grstctl, resetctl.d32);
- +
- + if (!core_if->core_params->en_multiple_tx_fifo && core_if->dma_enable) {
- + core_if->start_predict = 0;
- + for (i = 0; i<= core_if->dev_if->num_in_eps; ++i) {
- + core_if->nextep_seq[i] = 0xff; // 0xff - EP not active
- + }
- + core_if->nextep_seq[0] = 0;
- + core_if->first_in_nextep_seq = 0;
- + diepctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[0]->diepctl);
- + diepctl.b.nextep = 0;
- + DWC_WRITE_REG32(&dev_if->in_ep_regs[0]->diepctl, diepctl.d32);
- +
- + /* Update IN Endpoint Mismatch Count by active IN NP EP count + 1 */
- + dcfg.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dcfg);
- + dcfg.b.epmscnt = 2;
- + DWC_WRITE_REG32(&dev_if->dev_global_regs->dcfg, dcfg.d32);
- +
- + DWC_DEBUGPL(DBG_CILV,"%s first_in_nextep_seq= %2d; nextep_seq[]:\n",
- + __func__, core_if->first_in_nextep_seq);
- + for (i=0; i <= core_if->dev_if->num_in_eps; i++) {
- + DWC_DEBUGPL(DBG_CILV, "%2d ", core_if->nextep_seq[i]);
- + }
- + DWC_DEBUGPL(DBG_CILV,"\n");
- + }
- +
- + /* Clear all pending Device Interrupts */
- + /** @todo - if the condition needed to be checked
- + * or in any case all pending interrutps should be cleared?
- + */
- + if (core_if->multiproc_int_enable) {
- + for (i = 0; i < core_if->dev_if->num_in_eps; ++i) {
- + DWC_WRITE_REG32(&dev_if->
- + dev_global_regs->diepeachintmsk[i], 0);
- + }
- + }
- +
- + for (i = 0; i < core_if->dev_if->num_out_eps; ++i) {
- + DWC_WRITE_REG32(&dev_if->
- + dev_global_regs->doepeachintmsk[i], 0);
- + }
- +
- + DWC_WRITE_REG32(&dev_if->dev_global_regs->deachint, 0xFFFFFFFF);
- + DWC_WRITE_REG32(&dev_if->dev_global_regs->deachintmsk, 0);
- + } else {
- + DWC_WRITE_REG32(&dev_if->dev_global_regs->diepmsk, 0);
- + DWC_WRITE_REG32(&dev_if->dev_global_regs->doepmsk, 0);
- + DWC_WRITE_REG32(&dev_if->dev_global_regs->daint, 0xFFFFFFFF);
- + DWC_WRITE_REG32(&dev_if->dev_global_regs->daintmsk, 0);
- + }
- +
- + for (i = 0; i <= dev_if->num_in_eps; i++) {
- + depctl_data_t depctl;
- + depctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl);
- + if (depctl.b.epena) {
- + depctl.d32 = 0;
- + depctl.b.epdis = 1;
- + depctl.b.snak = 1;
- + } else {
- + depctl.d32 = 0;
- + }
- +
- + DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepctl, depctl.d32);
- +
- + DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->dieptsiz, 0);
- + DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepdma, 0);
- + DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepint, 0xFF);
- + }
- +
- + for (i = 0; i <= dev_if->num_out_eps; i++) {
- + depctl_data_t depctl;
- + depctl.d32 = DWC_READ_REG32(&dev_if->out_ep_regs[i]->doepctl);
- + if (depctl.b.epena) {
- + dctl_data_t dctl = {.d32 = 0 };
- + gintmsk_data_t gintsts = {.d32 = 0 };
- + doepint_data_t doepint = {.d32 = 0 };
- + dctl.b.sgoutnak = 1;
- + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, 0, dctl.d32);
- + do {
- + dwc_udelay(10);
- + gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
- + } while (!gintsts.b.goutnakeff);
- + gintsts.d32 = 0;
- + gintsts.b.goutnakeff = 1;
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
- +
- + depctl.d32 = 0;
- + depctl.b.epdis = 1;
- + depctl.b.snak = 1;
- + DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[i]->doepctl, depctl.d32);
- + do {
- + dwc_udelay(10);
- + doepint.d32 = DWC_READ_REG32(&core_if->dev_if->
- + out_ep_regs[i]->doepint);
- + } while (!doepint.b.epdisabled);
- +
- + doepint.b.epdisabled = 1;
- + DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[i]->doepint, doepint.d32);
- +
- + dctl.d32 = 0;
- + dctl.b.cgoutnak = 1;
- + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, 0, dctl.d32);
- + } else {
- + depctl.d32 = 0;
- + }
- +
- + DWC_WRITE_REG32(&dev_if->out_ep_regs[i]->doepctl, depctl.d32);
- +
- + DWC_WRITE_REG32(&dev_if->out_ep_regs[i]->doeptsiz, 0);
- + DWC_WRITE_REG32(&dev_if->out_ep_regs[i]->doepdma, 0);
- + DWC_WRITE_REG32(&dev_if->out_ep_regs[i]->doepint, 0xFF);
- + }
- +
- + if (core_if->en_multiple_tx_fifo && core_if->dma_enable) {
- + dev_if->non_iso_tx_thr_en = params->thr_ctl & 0x1;
- + dev_if->iso_tx_thr_en = (params->thr_ctl >> 1) & 0x1;
- + dev_if->rx_thr_en = (params->thr_ctl >> 2) & 0x1;
- +
- + dev_if->rx_thr_length = params->rx_thr_length;
- + dev_if->tx_thr_length = params->tx_thr_length;
- +
- + dev_if->setup_desc_index = 0;
- +
- + dthrctl.d32 = 0;
- + dthrctl.b.non_iso_thr_en = dev_if->non_iso_tx_thr_en;
- + dthrctl.b.iso_thr_en = dev_if->iso_tx_thr_en;
- + dthrctl.b.tx_thr_len = dev_if->tx_thr_length;
- + dthrctl.b.rx_thr_en = dev_if->rx_thr_en;
- + dthrctl.b.rx_thr_len = dev_if->rx_thr_length;
- + dthrctl.b.ahb_thr_ratio = params->ahb_thr_ratio;
- +
- + DWC_WRITE_REG32(&dev_if->dev_global_regs->dtknqr3_dthrctl,
- + dthrctl.d32);
- +
- + DWC_DEBUGPL(DBG_CIL,
- + "Non ISO Tx Thr - %d\nISO Tx Thr - %d\nRx Thr - %d\nTx Thr Len - %d\nRx Thr Len - %d\n",
- + dthrctl.b.non_iso_thr_en, dthrctl.b.iso_thr_en,
- + dthrctl.b.rx_thr_en, dthrctl.b.tx_thr_len,
- + dthrctl.b.rx_thr_len);
- +
- + }
- +
- + dwc_otg_enable_device_interrupts(core_if);
- +
- + {
- + diepmsk_data_t msk = {.d32 = 0 };
- + msk.b.txfifoundrn = 1;
- + if (core_if->multiproc_int_enable) {
- + DWC_MODIFY_REG32(&dev_if->dev_global_regs->
- + diepeachintmsk[0], msk.d32, msk.d32);
- + } else {
- + DWC_MODIFY_REG32(&dev_if->dev_global_regs->diepmsk,
- + msk.d32, msk.d32);
- + }
- + }
- +
- + if (core_if->multiproc_int_enable) {
- + /* Set NAK on Babble */
- + dctl_data_t dctl = {.d32 = 0 };
- + dctl.b.nakonbble = 1;
- + DWC_MODIFY_REG32(&dev_if->dev_global_regs->dctl, 0, dctl.d32);
- + }
- +
- + if (core_if->snpsid >= OTG_CORE_REV_2_94a) {
- + dctl_data_t dctl = {.d32 = 0 };
- + dctl.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dctl);
- + dctl.b.sftdiscon = 0;
- + DWC_WRITE_REG32(&dev_if->dev_global_regs->dctl, dctl.d32);
- + }
- +}
- +
- +/**
- + * This function enables the Host mode interrupts.
- + *
- + * @param core_if Programming view of DWC_otg controller
- + */
- +void dwc_otg_enable_host_interrupts(dwc_otg_core_if_t * core_if)
- +{
- + dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
- + gintmsk_data_t intr_mask = {.d32 = 0 };
- +
- + DWC_DEBUGPL(DBG_CIL, "%s(%p)\n", __func__, core_if);
- +
- + /* Disable all interrupts. */
- + DWC_WRITE_REG32(&global_regs->gintmsk, 0);
- +
- + /* Clear any pending interrupts. */
- + DWC_WRITE_REG32(&global_regs->gintsts, 0xFFFFFFFF);
- +
- + /* Enable the common interrupts */
- + dwc_otg_enable_common_interrupts(core_if);
- +
- + /*
- + * Enable host mode interrupts without disturbing common
- + * interrupts.
- + */
- +
- + intr_mask.b.disconnect = 1;
- + intr_mask.b.portintr = 1;
- + intr_mask.b.hcintr = 1;
- +
- + DWC_MODIFY_REG32(&global_regs->gintmsk, intr_mask.d32, intr_mask.d32);
- +}
- +
- +/**
- + * This function disables the Host Mode interrupts.
- + *
- + * @param core_if Programming view of DWC_otg controller
- + */
- +void dwc_otg_disable_host_interrupts(dwc_otg_core_if_t * core_if)
- +{
- + dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
- + gintmsk_data_t intr_mask = {.d32 = 0 };
- +
- + DWC_DEBUGPL(DBG_CILV, "%s()\n", __func__);
- +
- + /*
- + * Disable host mode interrupts without disturbing common
- + * interrupts.
- + */
- + intr_mask.b.sofintr = 1;
- + intr_mask.b.portintr = 1;
- + intr_mask.b.hcintr = 1;
- + intr_mask.b.ptxfempty = 1;
- + intr_mask.b.nptxfempty = 1;
- +
- + DWC_MODIFY_REG32(&global_regs->gintmsk, intr_mask.d32, 0);
- +}
- +
- +/**
- + * This function initializes the DWC_otg controller registers for
- + * host mode.
- + *
- + * This function flushes the Tx and Rx FIFOs and it flushes any entries in the
- + * request queues. Host channels are reset to ensure that they are ready for
- + * performing transfers.
- + *
- + * @param core_if Programming view of DWC_otg controller
- + *
- + */
- +void dwc_otg_core_host_init(dwc_otg_core_if_t * core_if)
- +{
- + dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
- + dwc_otg_host_if_t *host_if = core_if->host_if;
- + dwc_otg_core_params_t *params = core_if->core_params;
- + hprt0_data_t hprt0 = {.d32 = 0 };
- + fifosize_data_t nptxfifosize;
- + fifosize_data_t ptxfifosize;
- + uint16_t rxfsiz, nptxfsiz, hptxfsiz;
- + gdfifocfg_data_t gdfifocfg = {.d32 = 0 };
- + int i;
- + hcchar_data_t hcchar;
- + hcfg_data_t hcfg;
- + hfir_data_t hfir;
- + dwc_otg_hc_regs_t *hc_regs;
- + int num_channels;
- + gotgctl_data_t gotgctl = {.d32 = 0 };
- +
- + DWC_DEBUGPL(DBG_CILV, "%s(%p)\n", __func__, core_if);
- +
- + /* Restart the Phy Clock */
- + DWC_WRITE_REG32(core_if->pcgcctl, 0);
- +
- + /* Initialize Host Configuration Register */
- + init_fslspclksel(core_if);
- + if (core_if->core_params->speed == DWC_SPEED_PARAM_FULL) {
- + hcfg.d32 = DWC_READ_REG32(&host_if->host_global_regs->hcfg);
- + hcfg.b.fslssupp = 1;
- + DWC_WRITE_REG32(&host_if->host_global_regs->hcfg, hcfg.d32);
- +
- + }
- +
- + /* This bit allows dynamic reloading of the HFIR register
- + * during runtime. This bit needs to be programmed during
- + * initial configuration and its value must not be changed
- + * during runtime.*/
- + if (core_if->core_params->reload_ctl == 1) {
- + hfir.d32 = DWC_READ_REG32(&host_if->host_global_regs->hfir);
- + hfir.b.hfirrldctrl = 1;
- + DWC_WRITE_REG32(&host_if->host_global_regs->hfir, hfir.d32);
- + }
- +
- + if (core_if->core_params->dma_desc_enable) {
- + uint8_t op_mode = core_if->hwcfg2.b.op_mode;
- + if (!
- + (core_if->hwcfg4.b.desc_dma
- + && (core_if->snpsid >= OTG_CORE_REV_2_90a)
- + && ((op_mode == DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG)
- + || (op_mode == DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG)
- + || (op_mode ==
- + DWC_HWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE_OTG)
- + || (op_mode == DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST)
- + || (op_mode ==
- + DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST)))) {
- +
- + DWC_ERROR("Host can't operate in Descriptor DMA mode.\n"
- + "Either core version is below 2.90a or "
- + "GHWCFG2, GHWCFG4 registers' values do not allow Descriptor DMA in host mode.\n"
- + "To run the driver in Buffer DMA host mode set dma_desc_enable "
- + "module parameter to 0.\n");
- + return;
- + }
- + hcfg.d32 = DWC_READ_REG32(&host_if->host_global_regs->hcfg);
- + hcfg.b.descdma = 1;
- + DWC_WRITE_REG32(&host_if->host_global_regs->hcfg, hcfg.d32);
- + }
- +
- + /* Configure data FIFO sizes */
- + if (core_if->hwcfg2.b.dynamic_fifo && params->enable_dynamic_fifo) {
- + DWC_DEBUGPL(DBG_CIL, "Total FIFO Size=%d\n",
- + core_if->total_fifo_size);
- + DWC_DEBUGPL(DBG_CIL, "Rx FIFO Size=%d\n",
- + params->host_rx_fifo_size);
- + DWC_DEBUGPL(DBG_CIL, "NP Tx FIFO Size=%d\n",
- + params->host_nperio_tx_fifo_size);
- + DWC_DEBUGPL(DBG_CIL, "P Tx FIFO Size=%d\n",
- + params->host_perio_tx_fifo_size);
- +
- + /* Rx FIFO */
- + DWC_DEBUGPL(DBG_CIL, "initial grxfsiz=%08x\n",
- + DWC_READ_REG32(&global_regs->grxfsiz));
- + DWC_WRITE_REG32(&global_regs->grxfsiz,
- + params->host_rx_fifo_size);
- + DWC_DEBUGPL(DBG_CIL, "new grxfsiz=%08x\n",
- + DWC_READ_REG32(&global_regs->grxfsiz));
- +
- + /* Non-periodic Tx FIFO */
- + DWC_DEBUGPL(DBG_CIL, "initial gnptxfsiz=%08x\n",
- + DWC_READ_REG32(&global_regs->gnptxfsiz));
- + nptxfifosize.b.depth = params->host_nperio_tx_fifo_size;
- + nptxfifosize.b.startaddr = params->host_rx_fifo_size;
- + DWC_WRITE_REG32(&global_regs->gnptxfsiz, nptxfifosize.d32);
- + DWC_DEBUGPL(DBG_CIL, "new gnptxfsiz=%08x\n",
- + DWC_READ_REG32(&global_regs->gnptxfsiz));
- +
- + /* Periodic Tx FIFO */
- + DWC_DEBUGPL(DBG_CIL, "initial hptxfsiz=%08x\n",
- + DWC_READ_REG32(&global_regs->hptxfsiz));
- + ptxfifosize.b.depth = params->host_perio_tx_fifo_size;
- + ptxfifosize.b.startaddr =
- + nptxfifosize.b.startaddr + nptxfifosize.b.depth;
- + DWC_WRITE_REG32(&global_regs->hptxfsiz, ptxfifosize.d32);
- + DWC_DEBUGPL(DBG_CIL, "new hptxfsiz=%08x\n",
- + DWC_READ_REG32(&global_regs->hptxfsiz));
- +
- + if (core_if->en_multiple_tx_fifo
- + && core_if->snpsid <= OTG_CORE_REV_2_94a) {
- + /* Global DFIFOCFG calculation for Host mode - include RxFIFO, NPTXFIFO and HPTXFIFO */
- + gdfifocfg.d32 = DWC_READ_REG32(&global_regs->gdfifocfg);
- + rxfsiz = (DWC_READ_REG32(&global_regs->grxfsiz) & 0x0000ffff);
- + nptxfsiz = (DWC_READ_REG32(&global_regs->gnptxfsiz) >> 16);
- + hptxfsiz = (DWC_READ_REG32(&global_regs->hptxfsiz) >> 16);
- + gdfifocfg.b.epinfobase = rxfsiz + nptxfsiz + hptxfsiz;
- + DWC_WRITE_REG32(&global_regs->gdfifocfg, gdfifocfg.d32);
- + }
- + }
- +
- + /* TODO - check this */
- + /* Clear Host Set HNP Enable in the OTG Control Register */
- + gotgctl.b.hstsethnpen = 1;
- + DWC_MODIFY_REG32(&global_regs->gotgctl, gotgctl.d32, 0);
- + /* Make sure the FIFOs are flushed. */
- + dwc_otg_flush_tx_fifo(core_if, 0x10 /* all TX FIFOs */ );
- + dwc_otg_flush_rx_fifo(core_if);
- +
- + /* Clear Host Set HNP Enable in the OTG Control Register */
- + gotgctl.b.hstsethnpen = 1;
- + DWC_MODIFY_REG32(&global_regs->gotgctl, gotgctl.d32, 0);
- +
- + if (!core_if->core_params->dma_desc_enable) {
- + /* Flush out any leftover queued requests. */
- + num_channels = core_if->core_params->host_channels;
- +
- + for (i = 0; i < num_channels; i++) {
- + hc_regs = core_if->host_if->hc_regs[i];
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- + hcchar.b.chen = 0;
- + hcchar.b.chdis = 1;
- + hcchar.b.epdir = 0;
- + DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
- + }
- +
- + /* Halt all channels to put them into a known state. */
- + for (i = 0; i < num_channels; i++) {
- + int count = 0;
- + hc_regs = core_if->host_if->hc_regs[i];
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- + hcchar.b.chen = 1;
- + hcchar.b.chdis = 1;
- + hcchar.b.epdir = 0;
- + DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
- + DWC_DEBUGPL(DBG_HCDV, "%s: Halt channel %d regs %p\n", __func__, i, hc_regs);
- + do {
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- + if (++count > 1000) {
- + DWC_ERROR
- + ("%s: Unable to clear halt on channel %d (timeout HCCHAR 0x%X @%p)\n",
- + __func__, i, hcchar.d32, &hc_regs->hcchar);
- + break;
- + }
- + dwc_udelay(1);
- + } while (hcchar.b.chen);
- + }
- + }
- +
- + /* Turn on the vbus power. */
- + DWC_PRINTF("Init: Port Power? op_state=%d\n", core_if->op_state);
- + if (core_if->op_state == A_HOST) {
- + hprt0.d32 = dwc_otg_read_hprt0(core_if);
- + DWC_PRINTF("Init: Power Port (%d)\n", hprt0.b.prtpwr);
- + if (hprt0.b.prtpwr == 0) {
- + hprt0.b.prtpwr = 1;
- + DWC_WRITE_REG32(host_if->hprt0, hprt0.d32);
- + }
- + }
- +
- + dwc_otg_enable_host_interrupts(core_if);
- +}
- +
- +/**
- + * Prepares a host channel for transferring packets to/from a specific
- + * endpoint. The HCCHARn register is set up with the characteristics specified
- + * in _hc. Host channel interrupts that may need to be serviced while this
- + * transfer is in progress are enabled.
- + *
- + * @param core_if Programming view of DWC_otg controller
- + * @param hc Information needed to initialize the host channel
- + */
- +void dwc_otg_hc_init(dwc_otg_core_if_t * core_if, dwc_hc_t * hc)
- +{
- + hcintmsk_data_t hc_intr_mask;
- + hcchar_data_t hcchar;
- + hcsplt_data_t hcsplt;
- +
- + uint8_t hc_num = hc->hc_num;
- + dwc_otg_host_if_t *host_if = core_if->host_if;
- + dwc_otg_hc_regs_t *hc_regs = host_if->hc_regs[hc_num];
- +
- + /* Clear old interrupt conditions for this host channel. */
- + hc_intr_mask.d32 = 0xFFFFFFFF;
- + hc_intr_mask.b.reserved14_31 = 0;
- + DWC_WRITE_REG32(&hc_regs->hcint, hc_intr_mask.d32);
- +
- + /* Enable channel interrupts required for this transfer. */
- + hc_intr_mask.d32 = 0;
- + hc_intr_mask.b.chhltd = 1;
- + if (core_if->dma_enable) {
- + /* For Descriptor DMA mode core halts the channel on AHB error. Interrupt is not required */
- + if (!core_if->dma_desc_enable)
- + hc_intr_mask.b.ahberr = 1;
- + else {
- + if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC)
- + hc_intr_mask.b.xfercompl = 1;
- + }
- +
- + if (hc->error_state && !hc->do_split &&
- + hc->ep_type != DWC_OTG_EP_TYPE_ISOC) {
- + hc_intr_mask.b.ack = 1;
- + if (hc->ep_is_in) {
- + hc_intr_mask.b.datatglerr = 1;
- + if (hc->ep_type != DWC_OTG_EP_TYPE_INTR) {
- + hc_intr_mask.b.nak = 1;
- + }
- + }
- + }
- + } else {
- + switch (hc->ep_type) {
- + case DWC_OTG_EP_TYPE_CONTROL:
- + case DWC_OTG_EP_TYPE_BULK:
- + hc_intr_mask.b.xfercompl = 1;
- + hc_intr_mask.b.stall = 1;
- + hc_intr_mask.b.xacterr = 1;
- + hc_intr_mask.b.datatglerr = 1;
- + if (hc->ep_is_in) {
- + hc_intr_mask.b.bblerr = 1;
- + } else {
- + hc_intr_mask.b.nak = 1;
- + hc_intr_mask.b.nyet = 1;
- + if (hc->do_ping) {
- + hc_intr_mask.b.ack = 1;
- + }
- + }
- +
- + if (hc->do_split) {
- + hc_intr_mask.b.nak = 1;
- + if (hc->complete_split) {
- + hc_intr_mask.b.nyet = 1;
- + } else {
- + hc_intr_mask.b.ack = 1;
- + }
- + }
- +
- + if (hc->error_state) {
- + hc_intr_mask.b.ack = 1;
- + }
- + break;
- + case DWC_OTG_EP_TYPE_INTR:
- + hc_intr_mask.b.xfercompl = 1;
- + hc_intr_mask.b.nak = 1;
- + hc_intr_mask.b.stall = 1;
- + hc_intr_mask.b.xacterr = 1;
- + hc_intr_mask.b.datatglerr = 1;
- + hc_intr_mask.b.frmovrun = 1;
- +
- + if (hc->ep_is_in) {
- + hc_intr_mask.b.bblerr = 1;
- + }
- + if (hc->error_state) {
- + hc_intr_mask.b.ack = 1;
- + }
- + if (hc->do_split) {
- + if (hc->complete_split) {
- + hc_intr_mask.b.nyet = 1;
- + } else {
- + hc_intr_mask.b.ack = 1;
- + }
- + }
- + break;
- + case DWC_OTG_EP_TYPE_ISOC:
- + hc_intr_mask.b.xfercompl = 1;
- + hc_intr_mask.b.frmovrun = 1;
- + hc_intr_mask.b.ack = 1;
- +
- + if (hc->ep_is_in) {
- + hc_intr_mask.b.xacterr = 1;
- + hc_intr_mask.b.bblerr = 1;
- + }
- + break;
- + }
- + }
- + DWC_WRITE_REG32(&hc_regs->hcintmsk, hc_intr_mask.d32);
- +
- + /*
- + * Program the HCCHARn register with the endpoint characteristics for
- + * the current transfer.
- + */
- + hcchar.d32 = 0;
- + hcchar.b.devaddr = hc->dev_addr;
- + hcchar.b.epnum = hc->ep_num;
- + hcchar.b.epdir = hc->ep_is_in;
- + hcchar.b.lspddev = (hc->speed == DWC_OTG_EP_SPEED_LOW);
- + hcchar.b.eptype = hc->ep_type;
- + hcchar.b.mps = hc->max_packet;
- +
- + DWC_WRITE_REG32(&host_if->hc_regs[hc_num]->hcchar, hcchar.d32);
- +
- + DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d, Dev Addr %d, EP #%d\n",
- + __func__, hc->hc_num, hcchar.b.devaddr, hcchar.b.epnum);
- + DWC_DEBUGPL(DBG_HCDV, " Is In %d, Is Low Speed %d, EP Type %d, "
- + "Max Pkt %d, Multi Cnt %d\n",
- + hcchar.b.epdir, hcchar.b.lspddev, hcchar.b.eptype,
- + hcchar.b.mps, hcchar.b.multicnt);
- +
- + /*
- + * Program the HCSPLIT register for SPLITs
- + */
- + hcsplt.d32 = 0;
- + if (hc->do_split) {
- + DWC_DEBUGPL(DBG_HCDV, "Programming HC %d with split --> %s\n",
- + hc->hc_num,
- + hc->complete_split ? "CSPLIT" : "SSPLIT");
- + hcsplt.b.compsplt = hc->complete_split;
- + hcsplt.b.xactpos = hc->xact_pos;
- + hcsplt.b.hubaddr = hc->hub_addr;
- + hcsplt.b.prtaddr = hc->port_addr;
- + DWC_DEBUGPL(DBG_HCDV, "\t comp split %d\n", hc->complete_split);
- + DWC_DEBUGPL(DBG_HCDV, "\t xact pos %d\n", hc->xact_pos);
- + DWC_DEBUGPL(DBG_HCDV, "\t hub addr %d\n", hc->hub_addr);
- + DWC_DEBUGPL(DBG_HCDV, "\t port addr %d\n", hc->port_addr);
- + DWC_DEBUGPL(DBG_HCDV, "\t is_in %d\n", hc->ep_is_in);
- + DWC_DEBUGPL(DBG_HCDV, "\t Max Pkt: %d\n", hcchar.b.mps);
- + DWC_DEBUGPL(DBG_HCDV, "\t xferlen: %d\n", hc->xfer_len);
- + }
- + DWC_WRITE_REG32(&host_if->hc_regs[hc_num]->hcsplt, hcsplt.d32);
- +
- +}
- +
- +/**
- + * Attempts to halt a host channel. This function should only be called in
- + * Slave mode or to abort a transfer in either Slave mode or DMA mode. Under
- + * normal circumstances in DMA mode, the controller halts the channel when the
- + * transfer is complete or a condition occurs that requires application
- + * intervention.
- + *
- + * In slave mode, checks for a free request queue entry, then sets the Channel
- + * Enable and Channel Disable bits of the Host Channel Characteristics
- + * register of the specified channel to intiate the halt. If there is no free
- + * request queue entry, sets only the Channel Disable bit of the HCCHARn
- + * register to flush requests for this channel. In the latter case, sets a
- + * flag to indicate that the host channel needs to be halted when a request
- + * queue slot is open.
- + *
- + * In DMA mode, always sets the Channel Enable and Channel Disable bits of the
- + * HCCHARn register. The controller ensures there is space in the request
- + * queue before submitting the halt request.
- + *
- + * Some time may elapse before the core flushes any posted requests for this
- + * host channel and halts. The Channel Halted interrupt handler completes the
- + * deactivation of the host channel.
- + *
- + * @param core_if Controller register interface.
- + * @param hc Host channel to halt.
- + * @param halt_status Reason for halting the channel.
- + */
- +void dwc_otg_hc_halt(dwc_otg_core_if_t * core_if,
- + dwc_hc_t * hc, dwc_otg_halt_status_e halt_status)
- +{
- + gnptxsts_data_t nptxsts;
- + hptxsts_data_t hptxsts;
- + hcchar_data_t hcchar;
- + dwc_otg_hc_regs_t *hc_regs;
- + dwc_otg_core_global_regs_t *global_regs;
- + dwc_otg_host_global_regs_t *host_global_regs;
- +
- + hc_regs = core_if->host_if->hc_regs[hc->hc_num];
- + global_regs = core_if->core_global_regs;
- + host_global_regs = core_if->host_if->host_global_regs;
- +
- + DWC_ASSERT(!(halt_status == DWC_OTG_HC_XFER_NO_HALT_STATUS),
- + "halt_status = %d\n", halt_status);
- +
- + if (halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE ||
- + halt_status == DWC_OTG_HC_XFER_AHB_ERR) {
- + /*
- + * Disable all channel interrupts except Ch Halted. The QTD
- + * and QH state associated with this transfer has been cleared
- + * (in the case of URB_DEQUEUE), so the channel needs to be
- + * shut down carefully to prevent crashes.
- + */
- + hcintmsk_data_t hcintmsk;
- + hcintmsk.d32 = 0;
- + hcintmsk.b.chhltd = 1;
- + DWC_WRITE_REG32(&hc_regs->hcintmsk, hcintmsk.d32);
- +
- + /*
- + * Make sure no other interrupts besides halt are currently
- + * pending. Handling another interrupt could cause a crash due
- + * to the QTD and QH state.
- + */
- + DWC_WRITE_REG32(&hc_regs->hcint, ~hcintmsk.d32);
- +
- + /*
- + * Make sure the halt status is set to URB_DEQUEUE or AHB_ERR
- + * even if the channel was already halted for some other
- + * reason.
- + */
- + hc->halt_status = halt_status;
- +
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- + if (hcchar.b.chen == 0) {
- + /*
- + * The channel is either already halted or it hasn't
- + * started yet. In DMA mode, the transfer may halt if
- + * it finishes normally or a condition occurs that
- + * requires driver intervention. Don't want to halt
- + * the channel again. In either Slave or DMA mode,
- + * it's possible that the transfer has been assigned
- + * to a channel, but not started yet when an URB is
- + * dequeued. Don't want to halt a channel that hasn't
- + * started yet.
- + */
- + return;
- + }
- + }
- + if (hc->halt_pending) {
- + /*
- + * A halt has already been issued for this channel. This might
- + * happen when a transfer is aborted by a higher level in
- + * the stack.
- + */
- +#ifdef DEBUG
- + DWC_PRINTF
- + ("*** %s: Channel %d, _hc->halt_pending already set ***\n",
- + __func__, hc->hc_num);
- +
- +#endif
- + return;
- + }
- +
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- +
- + /* No need to set the bit in DDMA for disabling the channel */
- + //TODO check it everywhere channel is disabled
- + if (!core_if->core_params->dma_desc_enable)
- + hcchar.b.chen = 1;
- + hcchar.b.chdis = 1;
- +
- + if (!core_if->dma_enable) {
- + /* Check for space in the request queue to issue the halt. */
- + if (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL ||
- + hc->ep_type == DWC_OTG_EP_TYPE_BULK) {
- + nptxsts.d32 = DWC_READ_REG32(&global_regs->gnptxsts);
- + if (nptxsts.b.nptxqspcavail == 0) {
- + hcchar.b.chen = 0;
- + }
- + } else {
- + hptxsts.d32 =
- + DWC_READ_REG32(&host_global_regs->hptxsts);
- + if ((hptxsts.b.ptxqspcavail == 0)
- + || (core_if->queuing_high_bandwidth)) {
- + hcchar.b.chen = 0;
- + }
- + }
- + }
- + DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
- +
- + hc->halt_status = halt_status;
- +
- + if (hcchar.b.chen) {
- + hc->halt_pending = 1;
- + hc->halt_on_queue = 0;
- + } else {
- + hc->halt_on_queue = 1;
- + }
- +
- + DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num);
- + DWC_DEBUGPL(DBG_HCDV, " hcchar: 0x%08x\n", hcchar.d32);
- + DWC_DEBUGPL(DBG_HCDV, " halt_pending: %d\n", hc->halt_pending);
- + DWC_DEBUGPL(DBG_HCDV, " halt_on_queue: %d\n", hc->halt_on_queue);
- + DWC_DEBUGPL(DBG_HCDV, " halt_status: %d\n", hc->halt_status);
- +
- + return;
- +}
- +
- +/**
- + * Clears the transfer state for a host channel. This function is normally
- + * called after a transfer is done and the host channel is being released.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param hc Identifies the host channel to clean up.
- + */
- +void dwc_otg_hc_cleanup(dwc_otg_core_if_t * core_if, dwc_hc_t * hc)
- +{
- + dwc_otg_hc_regs_t *hc_regs;
- +
- + hc->xfer_started = 0;
- +
- + /*
- + * Clear channel interrupt enables and any unhandled channel interrupt
- + * conditions.
- + */
- + hc_regs = core_if->host_if->hc_regs[hc->hc_num];
- + DWC_WRITE_REG32(&hc_regs->hcintmsk, 0);
- + DWC_WRITE_REG32(&hc_regs->hcint, 0xFFFFFFFF);
- +#ifdef DEBUG
- + DWC_TIMER_CANCEL(core_if->hc_xfer_timer[hc->hc_num]);
- +#endif
- +}
- +
- +/**
- + * Sets the channel property that indicates in which frame a periodic transfer
- + * should occur. This is always set to the _next_ frame. This function has no
- + * effect on non-periodic transfers.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param hc Identifies the host channel to set up and its properties.
- + * @param hcchar Current value of the HCCHAR register for the specified host
- + * channel.
- + */
- +static inline void hc_set_even_odd_frame(dwc_otg_core_if_t * core_if,
- + dwc_hc_t * hc, hcchar_data_t * hcchar)
- +{
- + if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
- + hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
- + hfnum_data_t hfnum;
- + hfnum.d32 =
- + DWC_READ_REG32(&core_if->host_if->host_global_regs->hfnum);
- +
- + /* 1 if _next_ frame is odd, 0 if it's even */
- + hcchar->b.oddfrm = (hfnum.b.frnum & 0x1) ? 0 : 1;
- +#ifdef DEBUG
- + if (hc->ep_type == DWC_OTG_EP_TYPE_INTR && hc->do_split
- + && !hc->complete_split) {
- + switch (hfnum.b.frnum & 0x7) {
- + case 7:
- + core_if->hfnum_7_samples++;
- + core_if->hfnum_7_frrem_accum += hfnum.b.frrem;
- + break;
- + case 0:
- + core_if->hfnum_0_samples++;
- + core_if->hfnum_0_frrem_accum += hfnum.b.frrem;
- + break;
- + default:
- + core_if->hfnum_other_samples++;
- + core_if->hfnum_other_frrem_accum +=
- + hfnum.b.frrem;
- + break;
- + }
- + }
- +#endif
- + }
- +}
- +
- +#ifdef DEBUG
- +void hc_xfer_timeout(void *ptr)
- +{
- + hc_xfer_info_t *xfer_info = NULL;
- + int hc_num = 0;
- +
- + if (ptr)
- + xfer_info = (hc_xfer_info_t *) ptr;
- +
- + if (!xfer_info->hc) {
- + DWC_ERROR("xfer_info->hc = %p\n", xfer_info->hc);
- + return;
- + }
- +
- + hc_num = xfer_info->hc->hc_num;
- + DWC_WARN("%s: timeout on channel %d\n", __func__, hc_num);
- + DWC_WARN(" start_hcchar_val 0x%08x\n",
- + xfer_info->core_if->start_hcchar_val[hc_num]);
- +}
- +#endif
- +
- +void ep_xfer_timeout(void *ptr)
- +{
- + ep_xfer_info_t *xfer_info = NULL;
- + int ep_num = 0;
- + dctl_data_t dctl = {.d32 = 0 };
- + gintsts_data_t gintsts = {.d32 = 0 };
- + gintmsk_data_t gintmsk = {.d32 = 0 };
- +
- + if (ptr)
- + xfer_info = (ep_xfer_info_t *) ptr;
- +
- + if (!xfer_info->ep) {
- + DWC_ERROR("xfer_info->ep = %p\n", xfer_info->ep);
- + return;
- + }
- +
- + ep_num = xfer_info->ep->num;
- + DWC_WARN("%s: timeout on endpoit %d\n", __func__, ep_num);
- + /* Put the sate to 2 as it was time outed */
- + xfer_info->state = 2;
- +
- + dctl.d32 =
- + DWC_READ_REG32(&xfer_info->core_if->dev_if->dev_global_regs->dctl);
- + gintsts.d32 =
- + DWC_READ_REG32(&xfer_info->core_if->core_global_regs->gintsts);
- + gintmsk.d32 =
- + DWC_READ_REG32(&xfer_info->core_if->core_global_regs->gintmsk);
- +
- + if (!gintmsk.b.goutnakeff) {
- + /* Unmask it */
- + gintmsk.b.goutnakeff = 1;
- + DWC_WRITE_REG32(&xfer_info->core_if->core_global_regs->gintmsk,
- + gintmsk.d32);
- +
- + }
- +
- + if (!gintsts.b.goutnakeff) {
- + dctl.b.sgoutnak = 1;
- + }
- + DWC_WRITE_REG32(&xfer_info->core_if->dev_if->dev_global_regs->dctl,
- + dctl.d32);
- +
- +}
- +
- +void set_pid_isoc(dwc_hc_t * hc)
- +{
- + /* Set up the initial PID for the transfer. */
- + if (hc->speed == DWC_OTG_EP_SPEED_HIGH) {
- + if (hc->ep_is_in) {
- + if (hc->multi_count == 1) {
- + hc->data_pid_start = DWC_OTG_HC_PID_DATA0;
- + } else if (hc->multi_count == 2) {
- + hc->data_pid_start = DWC_OTG_HC_PID_DATA1;
- + } else {
- + hc->data_pid_start = DWC_OTG_HC_PID_DATA2;
- + }
- + } else {
- + if (hc->multi_count == 1) {
- + hc->data_pid_start = DWC_OTG_HC_PID_DATA0;
- + } else {
- + hc->data_pid_start = DWC_OTG_HC_PID_MDATA;
- + }
- + }
- + } else {
- + hc->data_pid_start = DWC_OTG_HC_PID_DATA0;
- + }
- +}
- +
- +/**
- + * This function does the setup for a data transfer for a host channel and
- + * starts the transfer. May be called in either Slave mode or DMA mode. In
- + * Slave mode, the caller must ensure that there is sufficient space in the
- + * request queue and Tx Data FIFO.
- + *
- + * For an OUT transfer in Slave mode, it loads a data packet into the
- + * appropriate FIFO. If necessary, additional data packets will be loaded in
- + * the Host ISR.
- + *
- + * For an IN transfer in Slave mode, a data packet is requested. The data
- + * packets are unloaded from the Rx FIFO in the Host ISR. If necessary,
- + * additional data packets are requested in the Host ISR.
- + *
- + * For a PING transfer in Slave mode, the Do Ping bit is set in the HCTSIZ
- + * register along with a packet count of 1 and the channel is enabled. This
- + * causes a single PING transaction to occur. Other fields in HCTSIZ are
- + * simply set to 0 since no data transfer occurs in this case.
- + *
- + * For a PING transfer in DMA mode, the HCTSIZ register is initialized with
- + * all the information required to perform the subsequent data transfer. In
- + * addition, the Do Ping bit is set in the HCTSIZ register. In this case, the
- + * controller performs the entire PING protocol, then starts the data
- + * transfer.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param hc Information needed to initialize the host channel. The xfer_len
- + * value may be reduced to accommodate the max widths of the XferSize and
- + * PktCnt fields in the HCTSIZn register. The multi_count value may be changed
- + * to reflect the final xfer_len value.
- + */
- +void dwc_otg_hc_start_transfer(dwc_otg_core_if_t * core_if, dwc_hc_t * hc)
- +{
- + hcchar_data_t hcchar;
- + hctsiz_data_t hctsiz;
- + uint16_t num_packets;
- + uint32_t max_hc_xfer_size = core_if->core_params->max_transfer_size;
- + uint16_t max_hc_pkt_count = core_if->core_params->max_packet_count;
- + dwc_otg_hc_regs_t *hc_regs = core_if->host_if->hc_regs[hc->hc_num];
- +
- + hctsiz.d32 = 0;
- +
- + if (hc->do_ping) {
- + if (!core_if->dma_enable) {
- + dwc_otg_hc_do_ping(core_if, hc);
- + hc->xfer_started = 1;
- + return;
- + } else {
- + hctsiz.b.dopng = 1;
- + }
- + }
- +
- + if (hc->do_split) {
- + num_packets = 1;
- +
- + if (hc->complete_split && !hc->ep_is_in) {
- + /* For CSPLIT OUT Transfer, set the size to 0 so the
- + * core doesn't expect any data written to the FIFO */
- + hc->xfer_len = 0;
- + } else 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;
- + }
- +
- + hctsiz.b.xfersize = hc->xfer_len;
- + } else {
- + /*
- + * Ensure that the transfer length and packet count will fit
- + * in the widths allocated for them in the HCTSIZn register.
- + */
- + if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
- + hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
- + /*
- + * Make sure the transfer size is no larger than one
- + * (micro)frame's worth of data. (A check was done
- + * when the periodic transfer was accepted to ensure
- + * that a (micro)frame's worth of data can be
- + * programmed into a channel.)
- + */
- + uint32_t max_periodic_len =
- + hc->multi_count * hc->max_packet;
- + if (hc->xfer_len > max_periodic_len) {
- + hc->xfer_len = max_periodic_len;
- + } else {
- + }
- + } else if (hc->xfer_len > max_hc_xfer_size) {
- + /* Make sure that xfer_len is a multiple of max packet size. */
- + hc->xfer_len = max_hc_xfer_size - hc->max_packet + 1;
- + }
- +
- + if (hc->xfer_len > 0) {
- + num_packets =
- + (hc->xfer_len + hc->max_packet -
- + 1) / hc->max_packet;
- + if (num_packets > max_hc_pkt_count) {
- + num_packets = max_hc_pkt_count;
- + hc->xfer_len = num_packets * hc->max_packet;
- + }
- + } else {
- + /* Need 1 packet for transfer length of 0. */
- + num_packets = 1;
- + }
- +
- + if (hc->ep_is_in) {
- + /* Always program an integral # of max packets for IN transfers. */
- + hc->xfer_len = num_packets * hc->max_packet;
- + }
- +
- + if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
- + hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
- + /*
- + * Make sure that the multi_count field matches the
- + * actual transfer length.
- + */
- + hc->multi_count = num_packets;
- + }
- +
- + if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC)
- + set_pid_isoc(hc);
- +
- + hctsiz.b.xfersize = hc->xfer_len;
- + }
- +
- + hc->start_pkt_count = num_packets;
- + hctsiz.b.pktcnt = num_packets;
- + hctsiz.b.pid = hc->data_pid_start;
- + DWC_WRITE_REG32(&hc_regs->hctsiz, hctsiz.d32);
- +
- + DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num);
- + DWC_DEBUGPL(DBG_HCDV, " Xfer Size: %d\n", hctsiz.b.xfersize);
- + DWC_DEBUGPL(DBG_HCDV, " Num Pkts: %d\n", hctsiz.b.pktcnt);
- + DWC_DEBUGPL(DBG_HCDV, " Start PID: %d\n", hctsiz.b.pid);
- +
- + if (core_if->dma_enable) {
- + dwc_dma_t dma_addr;
- + if (hc->align_buff) {
- + dma_addr = hc->align_buff;
- + } else {
- + dma_addr = ((unsigned long)hc->xfer_buff & 0xffffffff);
- + }
- + DWC_WRITE_REG32(&hc_regs->hcdma, dma_addr);
- + }
- +
- + /* Start the split */
- + if (hc->do_split) {
- + hcsplt_data_t hcsplt;
- + hcsplt.d32 = DWC_READ_REG32(&hc_regs->hcsplt);
- + hcsplt.b.spltena = 1;
- + DWC_WRITE_REG32(&hc_regs->hcsplt, hcsplt.d32);
- + }
- +
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- + hcchar.b.multicnt = hc->multi_count;
- + hc_set_even_odd_frame(core_if, hc, &hcchar);
- +#ifdef DEBUG
- + core_if->start_hcchar_val[hc->hc_num] = hcchar.d32;
- + if (hcchar.b.chdis) {
- + DWC_WARN("%s: chdis set, channel %d, hcchar 0x%08x\n",
- + __func__, hc->hc_num, hcchar.d32);
- + }
- +#endif
- +
- + /* Set host channel enable after all other setup is complete. */
- + hcchar.b.chen = 1;
- + hcchar.b.chdis = 0;
- + DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
- +
- + hc->xfer_started = 1;
- + hc->requests++;
- +
- + if (!core_if->dma_enable && !hc->ep_is_in && hc->xfer_len > 0) {
- + /* Load OUT packet into the appropriate Tx FIFO. */
- + dwc_otg_hc_write_packet(core_if, hc);
- + }
- +#ifdef DEBUG
- + if (hc->ep_type != DWC_OTG_EP_TYPE_INTR) {
- + DWC_DEBUGPL(DBG_HCDV, "transfer %d from core_if %p\n",
- + hc->hc_num, core_if);//GRAYG
- + core_if->hc_xfer_info[hc->hc_num].core_if = core_if;
- + core_if->hc_xfer_info[hc->hc_num].hc = hc;
- +
- + /* Start a timer for this transfer. */
- + DWC_TIMER_SCHEDULE(core_if->hc_xfer_timer[hc->hc_num], 10000);
- + }
- +#endif
- +}
- +
- +/**
- + * This function does the setup for a data transfer for a host channel
- + * and starts the transfer in Descriptor DMA mode.
- + *
- + * Initializes HCTSIZ register. For a PING transfer the Do Ping bit is set.
- + * Sets PID and NTD values. For periodic transfers
- + * initializes SCHED_INFO field with micro-frame bitmap.
- + *
- + * Initializes HCDMA register with descriptor list address and CTD value
- + * then starts the transfer via enabling the channel.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param hc Information needed to initialize the host channel.
- + */
- +void dwc_otg_hc_start_transfer_ddma(dwc_otg_core_if_t * core_if, dwc_hc_t * hc)
- +{
- + dwc_otg_hc_regs_t *hc_regs = core_if->host_if->hc_regs[hc->hc_num];
- + hcchar_data_t hcchar;
- + hctsiz_data_t hctsiz;
- + hcdma_data_t hcdma;
- +
- + hctsiz.d32 = 0;
- +
- + if (hc->do_ping)
- + hctsiz.b_ddma.dopng = 1;
- +
- + if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC)
- + set_pid_isoc(hc);
- +
- + /* Packet Count and Xfer Size are not used in Descriptor DMA mode */
- + hctsiz.b_ddma.pid = hc->data_pid_start;
- + hctsiz.b_ddma.ntd = hc->ntd - 1; /* 0 - 1 descriptor, 1 - 2 descriptors, etc. */
- + hctsiz.b_ddma.schinfo = hc->schinfo; /* Non-zero only for high-speed interrupt endpoints */
- +
- + DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num);
- + DWC_DEBUGPL(DBG_HCDV, " Start PID: %d\n", hctsiz.b.pid);
- + DWC_DEBUGPL(DBG_HCDV, " NTD: %d\n", hctsiz.b_ddma.ntd);
- +
- + DWC_WRITE_REG32(&hc_regs->hctsiz, hctsiz.d32);
- +
- + hcdma.d32 = 0;
- + hcdma.b.dma_addr = ((uint32_t) hc->desc_list_addr) >> 11;
- +
- + /* Always start from first descriptor. */
- + hcdma.b.ctd = 0;
- + DWC_WRITE_REG32(&hc_regs->hcdma, hcdma.d32);
- +
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- + hcchar.b.multicnt = hc->multi_count;
- +
- +#ifdef DEBUG
- + core_if->start_hcchar_val[hc->hc_num] = hcchar.d32;
- + if (hcchar.b.chdis) {
- + DWC_WARN("%s: chdis set, channel %d, hcchar 0x%08x\n",
- + __func__, hc->hc_num, hcchar.d32);
- + }
- +#endif
- +
- + /* Set host channel enable after all other setup is complete. */
- + hcchar.b.chen = 1;
- + hcchar.b.chdis = 0;
- +
- + DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
- +
- + hc->xfer_started = 1;
- + hc->requests++;
- +
- +#ifdef DEBUG
- + if ((hc->ep_type != DWC_OTG_EP_TYPE_INTR)
- + && (hc->ep_type != DWC_OTG_EP_TYPE_ISOC)) {
- + DWC_DEBUGPL(DBG_HCDV, "DMA transfer %d from core_if %p\n",
- + hc->hc_num, core_if);//GRAYG
- + core_if->hc_xfer_info[hc->hc_num].core_if = core_if;
- + core_if->hc_xfer_info[hc->hc_num].hc = hc;
- + /* Start a timer for this transfer. */
- + DWC_TIMER_SCHEDULE(core_if->hc_xfer_timer[hc->hc_num], 10000);
- + }
- +#endif
- +
- +}
- +
- +/**
- + * This function continues a data transfer that was started by previous call
- + * to <code>dwc_otg_hc_start_transfer</code>. The caller must ensure there is
- + * sufficient space in the request queue and Tx Data FIFO. This function
- + * should only be called in Slave mode. In DMA mode, the controller acts
- + * autonomously to complete transfers programmed to a host channel.
- + *
- + * For an OUT transfer, a new data packet is loaded into the appropriate FIFO
- + * if there is any data remaining to be queued. For an IN transfer, another
- + * data packet is always requested. For the SETUP phase of a control transfer,
- + * this function does nothing.
- + *
- + * @return 1 if a new request is queued, 0 if no more requests are required
- + * for this transfer.
- + */
- +int dwc_otg_hc_continue_transfer(dwc_otg_core_if_t * core_if, dwc_hc_t * hc)
- +{
- + DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num);
- +
- + if (hc->do_split) {
- + /* SPLITs always queue just once per channel */
- + return 0;
- + } else if (hc->data_pid_start == DWC_OTG_HC_PID_SETUP) {
- + /* SETUPs are queued only once since they can't be NAKed. */
- + return 0;
- + } else if (hc->ep_is_in) {
- + /*
- + * Always queue another request for other IN transfers. If
- + * back-to-back INs are issued and NAKs are received for both,
- + * the driver may still be processing the first NAK when the
- + * second NAK is received. When the interrupt handler clears
- + * the NAK interrupt for the first NAK, the second NAK will
- + * not be seen. So we can't depend on the NAK interrupt
- + * handler to requeue a NAKed request. Instead, IN requests
- + * are issued each time this function is called. When the
- + * transfer completes, the extra requests for the channel will
- + * be flushed.
- + */
- + hcchar_data_t hcchar;
- + dwc_otg_hc_regs_t *hc_regs =
- + core_if->host_if->hc_regs[hc->hc_num];
- +
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- + hc_set_even_odd_frame(core_if, hc, &hcchar);
- + hcchar.b.chen = 1;
- + hcchar.b.chdis = 0;
- + DWC_DEBUGPL(DBG_HCDV, " IN xfer: hcchar = 0x%08x\n",
- + hcchar.d32);
- + DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
- + hc->requests++;
- + return 1;
- + } else {
- + /* OUT transfers. */
- + if (hc->xfer_count < hc->xfer_len) {
- + if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
- + hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
- + hcchar_data_t hcchar;
- + dwc_otg_hc_regs_t *hc_regs;
- + hc_regs = core_if->host_if->hc_regs[hc->hc_num];
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- + hc_set_even_odd_frame(core_if, hc, &hcchar);
- + }
- +
- + /* Load OUT packet into the appropriate Tx FIFO. */
- + dwc_otg_hc_write_packet(core_if, hc);
- + hc->requests++;
- + return 1;
- + } else {
- + return 0;
- + }
- + }
- +}
- +
- +/**
- + * Starts a PING transfer. This function should only be called in Slave mode.
- + * The Do Ping bit is set in the HCTSIZ register, then the channel is enabled.
- + */
- +void dwc_otg_hc_do_ping(dwc_otg_core_if_t * core_if, dwc_hc_t * hc)
- +{
- + hcchar_data_t hcchar;
- + hctsiz_data_t hctsiz;
- + dwc_otg_hc_regs_t *hc_regs = core_if->host_if->hc_regs[hc->hc_num];
- +
- + DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num);
- +
- + hctsiz.d32 = 0;
- + hctsiz.b.dopng = 1;
- + hctsiz.b.pktcnt = 1;
- + DWC_WRITE_REG32(&hc_regs->hctsiz, hctsiz.d32);
- +
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- + hcchar.b.chen = 1;
- + hcchar.b.chdis = 0;
- + DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
- +}
- +
- +/*
- + * This function writes a packet into the Tx FIFO associated with the Host
- + * Channel. For a channel associated with a non-periodic EP, the non-periodic
- + * Tx FIFO is written. For a channel associated with a periodic EP, the
- + * periodic Tx FIFO is written. This function should only be called in Slave
- + * mode.
- + *
- + * Upon return the xfer_buff and xfer_count fields in _hc are incremented by
- + * then number of bytes written to the Tx FIFO.
- + */
- +void dwc_otg_hc_write_packet(dwc_otg_core_if_t * core_if, dwc_hc_t * hc)
- +{
- + uint32_t i;
- + uint32_t remaining_count;
- + uint32_t byte_count;
- + uint32_t dword_count;
- +
- + uint32_t *data_buff = (uint32_t *) (hc->xfer_buff);
- + uint32_t *data_fifo = core_if->data_fifo[hc->hc_num];
- +
- + remaining_count = hc->xfer_len - hc->xfer_count;
- + if (remaining_count > hc->max_packet) {
- + byte_count = hc->max_packet;
- + } else {
- + byte_count = remaining_count;
- + }
- +
- + dword_count = (byte_count + 3) / 4;
- +
- + if ((((unsigned long)data_buff) & 0x3) == 0) {
- + /* xfer_buff is DWORD aligned. */
- + for (i = 0; i < dword_count; i++, data_buff++) {
- + DWC_WRITE_REG32(data_fifo, *data_buff);
- + }
- + } else {
- + /* xfer_buff is not DWORD aligned. */
- + for (i = 0; i < dword_count; i++, data_buff++) {
- + uint32_t data;
- + data =
- + (data_buff[0] | data_buff[1] << 8 | data_buff[2] <<
- + 16 | data_buff[3] << 24);
- + DWC_WRITE_REG32(data_fifo, data);
- + }
- + }
- +
- + hc->xfer_count += byte_count;
- + hc->xfer_buff += byte_count;
- +}
- +
- +/**
- + * Gets the current USB frame number. This is the frame number from the last
- + * SOF packet.
- + */
- +uint32_t dwc_otg_get_frame_number(dwc_otg_core_if_t * core_if)
- +{
- + dsts_data_t dsts;
- + dsts.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
- +
- + /* read current frame/microframe number from DSTS register */
- + return dsts.b.soffn;
- +}
- +
- +/**
- + * Calculates and gets the frame Interval value of HFIR register according PHY
- + * type and speed.The application can modify a value of HFIR register only after
- + * the Port Enable bit of the Host Port Control and Status register
- + * (HPRT.PrtEnaPort) has been set.
- +*/
- +
- +uint32_t calc_frame_interval(dwc_otg_core_if_t * core_if)
- +{
- + gusbcfg_data_t usbcfg;
- + hwcfg2_data_t hwcfg2;
- + hprt0_data_t hprt0;
- + int clock = 60; // default value
- + usbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
- + hwcfg2.d32 = DWC_READ_REG32(&core_if->core_global_regs->ghwcfg2);
- + hprt0.d32 = DWC_READ_REG32(core_if->host_if->hprt0);
- + if (!usbcfg.b.physel && usbcfg.b.ulpi_utmi_sel && !usbcfg.b.phyif)
- + clock = 60;
- + if (usbcfg.b.physel && hwcfg2.b.fs_phy_type == 3)
- + clock = 48;
- + if (!usbcfg.b.phylpwrclksel && !usbcfg.b.physel &&
- + !usbcfg.b.ulpi_utmi_sel && usbcfg.b.phyif)
- + clock = 30;
- + if (!usbcfg.b.phylpwrclksel && !usbcfg.b.physel &&
- + !usbcfg.b.ulpi_utmi_sel && !usbcfg.b.phyif)
- + clock = 60;
- + if (usbcfg.b.phylpwrclksel && !usbcfg.b.physel &&
- + !usbcfg.b.ulpi_utmi_sel && usbcfg.b.phyif)
- + clock = 48;
- + if (usbcfg.b.physel && !usbcfg.b.phyif && hwcfg2.b.fs_phy_type == 2)
- + clock = 48;
- + if (usbcfg.b.physel && hwcfg2.b.fs_phy_type == 1)
- + clock = 48;
- + if (hprt0.b.prtspd == 0)
- + /* High speed case */
- + return 125 * clock - 1;
- + else
- + /* FS/LS case */
- + return 1000 * clock - 1;
- +}
- +
- +/**
- + * This function reads a setup packet from the Rx FIFO into the destination
- + * buffer. This function is called from the Rx Status Queue Level (RxStsQLvl)
- + * Interrupt routine when a SETUP packet has been received in Slave mode.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param dest Destination buffer for packet data.
- + */
- +void dwc_otg_read_setup_packet(dwc_otg_core_if_t * core_if, uint32_t * dest)
- +{
- + device_grxsts_data_t status;
- + /* Get the 8 bytes of a setup transaction data */
- +
- + /* Pop 2 DWORDS off the receive data FIFO into memory */
- + dest[0] = DWC_READ_REG32(core_if->data_fifo[0]);
- + dest[1] = DWC_READ_REG32(core_if->data_fifo[0]);
- + if (core_if->snpsid >= OTG_CORE_REV_3_00a) {
- + status.d32 =
- + DWC_READ_REG32(&core_if->core_global_regs->grxstsp);
- + DWC_DEBUGPL(DBG_ANY,
- + "EP:%d BCnt:%d " "pktsts:%x Frame:%d(0x%0x)\n",
- + status.b.epnum, status.b.bcnt, status.b.pktsts,
- + status.b.fn, status.b.fn);
- + }
- +}
- +
- +/**
- + * This function enables EP0 OUT to receive SETUP packets and configures EP0
- + * IN for transmitting packets. It is normally called when the
- + * "Enumeration Done" interrupt occurs.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param ep The EP0 data.
- + */
- +void dwc_otg_ep0_activate(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
- +{
- + dwc_otg_dev_if_t *dev_if = core_if->dev_if;
- + dsts_data_t dsts;
- + depctl_data_t diepctl;
- + depctl_data_t doepctl;
- + dctl_data_t dctl = {.d32 = 0 };
- +
- + ep->stp_rollover = 0;
- + /* Read the Device Status and Endpoint 0 Control registers */
- + dsts.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dsts);
- + diepctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[0]->diepctl);
- + doepctl.d32 = DWC_READ_REG32(&dev_if->out_ep_regs[0]->doepctl);
- +
- + /* Set the MPS of the IN EP based on the enumeration speed */
- + switch (dsts.b.enumspd) {
- + case DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ:
- + case DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ:
- + case DWC_DSTS_ENUMSPD_FS_PHY_48MHZ:
- + diepctl.b.mps = DWC_DEP0CTL_MPS_64;
- + break;
- + case DWC_DSTS_ENUMSPD_LS_PHY_6MHZ:
- + diepctl.b.mps = DWC_DEP0CTL_MPS_8;
- + break;
- + }
- +
- + DWC_WRITE_REG32(&dev_if->in_ep_regs[0]->diepctl, diepctl.d32);
- +
- + /* Enable OUT EP for receive */
- + if (core_if->snpsid <= OTG_CORE_REV_2_94a) {
- + doepctl.b.epena = 1;
- + DWC_WRITE_REG32(&dev_if->out_ep_regs[0]->doepctl, doepctl.d32);
- + }
- +#ifdef VERBOSE
- + DWC_DEBUGPL(DBG_PCDV, "doepctl0=%0x\n",
- + DWC_READ_REG32(&dev_if->out_ep_regs[0]->doepctl));
- + DWC_DEBUGPL(DBG_PCDV, "diepctl0=%0x\n",
- + DWC_READ_REG32(&dev_if->in_ep_regs[0]->diepctl));
- +#endif
- + dctl.b.cgnpinnak = 1;
- +
- + DWC_MODIFY_REG32(&dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32);
- + DWC_DEBUGPL(DBG_PCDV, "dctl=%0x\n",
- + DWC_READ_REG32(&dev_if->dev_global_regs->dctl));
- +
- +}
- +
- +/**
- + * This function activates an EP. The Device EP control register for
- + * the EP is configured as defined in the ep structure. Note: This
- + * function is not used for EP0.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param ep The EP to activate.
- + */
- +void dwc_otg_ep_activate(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
- +{
- + dwc_otg_dev_if_t *dev_if = core_if->dev_if;
- + depctl_data_t depctl;
- + volatile uint32_t *addr;
- + daint_data_t daintmsk = {.d32 = 0 };
- + dcfg_data_t dcfg;
- + uint8_t i;
- +
- + DWC_DEBUGPL(DBG_PCDV, "%s() EP%d-%s\n", __func__, ep->num,
- + (ep->is_in ? "IN" : "OUT"));
- +
- +#ifdef DWC_UTE_PER_IO
- + ep->xiso_frame_num = 0xFFFFFFFF;
- + ep->xiso_active_xfers = 0;
- + ep->xiso_queued_xfers = 0;
- +#endif
- + /* Read DEPCTLn register */
- + if (ep->is_in == 1) {
- + addr = &dev_if->in_ep_regs[ep->num]->diepctl;
- + daintmsk.ep.in = 1 << ep->num;
- + } else {
- + addr = &dev_if->out_ep_regs[ep->num]->doepctl;
- + daintmsk.ep.out = 1 << ep->num;
- + }
- +
- + /* If the EP is already active don't change the EP Control
- + * register. */
- + depctl.d32 = DWC_READ_REG32(addr);
- + if (!depctl.b.usbactep) {
- + depctl.b.mps = ep->maxpacket;
- + depctl.b.eptype = ep->type;
- + depctl.b.txfnum = ep->tx_fifo_num;
- +
- + if (ep->type == DWC_OTG_EP_TYPE_ISOC) {
- + depctl.b.setd0pid = 1; // ???
- + } else {
- + depctl.b.setd0pid = 1;
- + }
- + depctl.b.usbactep = 1;
- +
- + /* Update nextep_seq array and EPMSCNT in DCFG*/
- + if (!(depctl.b.eptype & 1) && (ep->is_in == 1)) { // NP IN EP
- + for (i = 0; i <= core_if->dev_if->num_in_eps; i++) {
- + if (core_if->nextep_seq[i] == core_if->first_in_nextep_seq)
- + break;
- + }
- + core_if->nextep_seq[i] = ep->num;
- + core_if->nextep_seq[ep->num] = core_if->first_in_nextep_seq;
- + depctl.b.nextep = core_if->nextep_seq[ep->num];
- + dcfg.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dcfg);
- + dcfg.b.epmscnt++;
- + DWC_WRITE_REG32(&dev_if->dev_global_regs->dcfg, dcfg.d32);
- +
- + DWC_DEBUGPL(DBG_PCDV,
- + "%s first_in_nextep_seq= %2d; nextep_seq[]:\n",
- + __func__, core_if->first_in_nextep_seq);
- + for (i=0; i <= core_if->dev_if->num_in_eps; i++) {
- + DWC_DEBUGPL(DBG_PCDV, "%2d\n",
- + core_if->nextep_seq[i]);
- + }
- +
- + }
- +
- +
- + DWC_WRITE_REG32(addr, depctl.d32);
- + DWC_DEBUGPL(DBG_PCDV, "DEPCTL=%08x\n", DWC_READ_REG32(addr));
- + }
- +
- + /* Enable the Interrupt for this EP */
- + if (core_if->multiproc_int_enable) {
- + if (ep->is_in == 1) {
- + diepmsk_data_t diepmsk = {.d32 = 0 };
- + diepmsk.b.xfercompl = 1;
- + diepmsk.b.timeout = 1;
- + diepmsk.b.epdisabled = 1;
- + diepmsk.b.ahberr = 1;
- + diepmsk.b.intknepmis = 1;
- + if (!core_if->en_multiple_tx_fifo && core_if->dma_enable)
- + diepmsk.b.intknepmis = 0;
- + diepmsk.b.txfifoundrn = 1; //?????
- + if (ep->type == DWC_OTG_EP_TYPE_ISOC) {
- + diepmsk.b.nak = 1;
- + }
- +
- +
- +
- +/*
- + if (core_if->dma_desc_enable) {
- + diepmsk.b.bna = 1;
- + }
- +*/
- +/*
- + if (core_if->dma_enable) {
- + doepmsk.b.nak = 1;
- + }
- +*/
- + DWC_WRITE_REG32(&dev_if->dev_global_regs->
- + diepeachintmsk[ep->num], diepmsk.d32);
- +
- + } else {
- + doepmsk_data_t doepmsk = {.d32 = 0 };
- + doepmsk.b.xfercompl = 1;
- + doepmsk.b.ahberr = 1;
- + doepmsk.b.epdisabled = 1;
- + if (ep->type == DWC_OTG_EP_TYPE_ISOC)
- + doepmsk.b.outtknepdis = 1;
- +
- +/*
- +
- + if (core_if->dma_desc_enable) {
- + doepmsk.b.bna = 1;
- + }
- +*/
- +/*
- + doepmsk.b.babble = 1;
- + doepmsk.b.nyet = 1;
- + doepmsk.b.nak = 1;
- +*/
- + DWC_WRITE_REG32(&dev_if->dev_global_regs->
- + doepeachintmsk[ep->num], doepmsk.d32);
- + }
- + DWC_MODIFY_REG32(&dev_if->dev_global_regs->deachintmsk,
- + 0, daintmsk.d32);
- + } else {
- + if (ep->type == DWC_OTG_EP_TYPE_ISOC) {
- + if (ep->is_in) {
- + diepmsk_data_t diepmsk = {.d32 = 0 };
- + diepmsk.b.nak = 1;
- + DWC_MODIFY_REG32(&dev_if->dev_global_regs->diepmsk, 0, diepmsk.d32);
- + } else {
- + doepmsk_data_t doepmsk = {.d32 = 0 };
- + doepmsk.b.outtknepdis = 1;
- + DWC_MODIFY_REG32(&dev_if->dev_global_regs->doepmsk, 0, doepmsk.d32);
- + }
- + }
- + DWC_MODIFY_REG32(&dev_if->dev_global_regs->daintmsk,
- + 0, daintmsk.d32);
- + }
- +
- + DWC_DEBUGPL(DBG_PCDV, "DAINTMSK=%0x\n",
- + DWC_READ_REG32(&dev_if->dev_global_regs->daintmsk));
- +
- + ep->stall_clear_flag = 0;
- +
- + return;
- +}
- +
- +/**
- + * This function deactivates an EP. This is done by clearing the USB Active
- + * EP bit in the Device EP control register. Note: This function is not used
- + * for EP0. EP0 cannot be deactivated.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param ep The EP to deactivate.
- + */
- +void dwc_otg_ep_deactivate(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
- +{
- + depctl_data_t depctl = {.d32 = 0 };
- + volatile uint32_t *addr;
- + daint_data_t daintmsk = {.d32 = 0 };
- + dcfg_data_t dcfg;
- + uint8_t i = 0;
- +
- +#ifdef DWC_UTE_PER_IO
- + ep->xiso_frame_num = 0xFFFFFFFF;
- + ep->xiso_active_xfers = 0;
- + ep->xiso_queued_xfers = 0;
- +#endif
- +
- + /* Read DEPCTLn register */
- + if (ep->is_in == 1) {
- + addr = &core_if->dev_if->in_ep_regs[ep->num]->diepctl;
- + daintmsk.ep.in = 1 << ep->num;
- + } else {
- + addr = &core_if->dev_if->out_ep_regs[ep->num]->doepctl;
- + daintmsk.ep.out = 1 << ep->num;
- + }
- +
- + depctl.d32 = DWC_READ_REG32(addr);
- +
- + depctl.b.usbactep = 0;
- +
- + /* Update nextep_seq array and EPMSCNT in DCFG*/
- + if (!(depctl.b.eptype & 1) && ep->is_in == 1) { // NP EP IN
- + for (i = 0; i <= core_if->dev_if->num_in_eps; i++) {
- + if (core_if->nextep_seq[i] == ep->num)
- + break;
- + }
- + core_if->nextep_seq[i] = core_if->nextep_seq[ep->num];
- + if (core_if->first_in_nextep_seq == ep->num)
- + core_if->first_in_nextep_seq = i;
- + core_if->nextep_seq[ep->num] = 0xff;
- + depctl.b.nextep = 0;
- + dcfg.d32 =
- + DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg);
- + dcfg.b.epmscnt--;
- + DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dcfg,
- + dcfg.d32);
- +
- + DWC_DEBUGPL(DBG_PCDV,
- + "%s first_in_nextep_seq= %2d; nextep_seq[]:\n",
- + __func__, core_if->first_in_nextep_seq);
- + for (i=0; i <= core_if->dev_if->num_in_eps; i++) {
- + DWC_DEBUGPL(DBG_PCDV, "%2d\n", core_if->nextep_seq[i]);
- + }
- + }
- +
- + if (ep->is_in == 1)
- + depctl.b.txfnum = 0;
- +
- + if (core_if->dma_desc_enable)
- + depctl.b.epdis = 1;
- +
- + DWC_WRITE_REG32(addr, depctl.d32);
- + depctl.d32 = DWC_READ_REG32(addr);
- + if (core_if->dma_enable && ep->type == DWC_OTG_EP_TYPE_ISOC
- + && depctl.b.epena) {
- + depctl_data_t depctl = {.d32 = 0};
- + if (ep->is_in) {
- + diepint_data_t diepint = {.d32 = 0};
- +
- + depctl.b.snak = 1;
- + DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[ep->num]->
- + diepctl, depctl.d32);
- + do {
- + dwc_udelay(10);
- + diepint.d32 =
- + DWC_READ_REG32(&core_if->
- + dev_if->in_ep_regs[ep->num]->
- + diepint);
- + } while (!diepint.b.inepnakeff);
- + diepint.b.inepnakeff = 1;
- + DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[ep->num]->
- + diepint, diepint.d32);
- + depctl.d32 = 0;
- + depctl.b.epdis = 1;
- + DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[ep->num]->
- + diepctl, depctl.d32);
- + do {
- + dwc_udelay(10);
- + diepint.d32 =
- + DWC_READ_REG32(&core_if->
- + dev_if->in_ep_regs[ep->num]->
- + diepint);
- + } while (!diepint.b.epdisabled);
- + diepint.b.epdisabled = 1;
- + DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[ep->num]->
- + diepint, diepint.d32);
- + } else {
- + dctl_data_t dctl = {.d32 = 0};
- + gintmsk_data_t gintsts = {.d32 = 0};
- + doepint_data_t doepint = {.d32 = 0};
- + dctl.b.sgoutnak = 1;
- + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->
- + dctl, 0, dctl.d32);
- + do {
- + dwc_udelay(10);
- + gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
- + } while (!gintsts.b.goutnakeff);
- + gintsts.d32 = 0;
- + gintsts.b.goutnakeff = 1;
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
- +
- + depctl.d32 = 0;
- + depctl.b.epdis = 1;
- + depctl.b.snak = 1;
- + DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[ep->num]->doepctl, depctl.d32);
- + do
- + {
- + dwc_udelay(10);
- + doepint.d32 = DWC_READ_REG32(&core_if->dev_if->
- + out_ep_regs[ep->num]->doepint);
- + } while (!doepint.b.epdisabled);
- +
- + doepint.b.epdisabled = 1;
- + DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[ep->num]->doepint, doepint.d32);
- +
- + dctl.d32 = 0;
- + dctl.b.cgoutnak = 1;
- + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, 0, dctl.d32);
- + }
- + }
- +
- + /* Disable the Interrupt for this EP */
- + if (core_if->multiproc_int_enable) {
- + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->deachintmsk,
- + daintmsk.d32, 0);
- +
- + if (ep->is_in == 1) {
- + DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->
- + diepeachintmsk[ep->num], 0);
- + } else {
- + DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->
- + doepeachintmsk[ep->num], 0);
- + }
- + } else {
- + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->daintmsk,
- + daintmsk.d32, 0);
- + }
- +
- +}
- +
- +/**
- + * This function initializes dma descriptor chain.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param ep The EP to start the transfer on.
- + */
- +static void init_dma_desc_chain(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
- +{
- + dwc_otg_dev_dma_desc_t *dma_desc;
- + uint32_t offset;
- + uint32_t xfer_est;
- + int i;
- + unsigned maxxfer_local, total_len;
- +
- + if (!ep->is_in && ep->type == DWC_OTG_EP_TYPE_INTR &&
- + (ep->maxpacket%4)) {
- + maxxfer_local = ep->maxpacket;
- + total_len = ep->xfer_len;
- + } else {
- + maxxfer_local = ep->maxxfer;
- + total_len = ep->total_len;
- + }
- +
- + ep->desc_cnt = (total_len / maxxfer_local) +
- + ((total_len % maxxfer_local) ? 1 : 0);
- +
- + if (!ep->desc_cnt)
- + ep->desc_cnt = 1;
- +
- + if (ep->desc_cnt > MAX_DMA_DESC_CNT)
- + ep->desc_cnt = MAX_DMA_DESC_CNT;
- +
- + dma_desc = ep->desc_addr;
- + if (maxxfer_local == ep->maxpacket) {
- + if ((total_len % maxxfer_local) &&
- + (total_len/maxxfer_local < MAX_DMA_DESC_CNT)) {
- + xfer_est = (ep->desc_cnt - 1) * maxxfer_local +
- + (total_len % maxxfer_local);
- + } else
- + xfer_est = ep->desc_cnt * maxxfer_local;
- + } else
- + xfer_est = total_len;
- + offset = 0;
- + for (i = 0; i < ep->desc_cnt; ++i) {
- + /** DMA Descriptor Setup */
- + if (xfer_est > maxxfer_local) {
- + dma_desc->status.b.bs = BS_HOST_BUSY;
- + dma_desc->status.b.l = 0;
- + dma_desc->status.b.ioc = 0;
- + dma_desc->status.b.sp = 0;
- + dma_desc->status.b.bytes = maxxfer_local;
- + dma_desc->buf = ep->dma_addr + offset;
- + dma_desc->status.b.sts = 0;
- + dma_desc->status.b.bs = BS_HOST_READY;
- +
- + xfer_est -= maxxfer_local;
- + offset += maxxfer_local;
- + } else {
- + dma_desc->status.b.bs = BS_HOST_BUSY;
- + dma_desc->status.b.l = 1;
- + dma_desc->status.b.ioc = 1;
- + if (ep->is_in) {
- + dma_desc->status.b.sp =
- + (xfer_est %
- + ep->maxpacket) ? 1 : ((ep->
- + sent_zlp) ? 1 : 0);
- + dma_desc->status.b.bytes = xfer_est;
- + } else {
- + if (maxxfer_local == ep->maxpacket)
- + dma_desc->status.b.bytes = xfer_est;
- + else
- + dma_desc->status.b.bytes =
- + xfer_est + ((4 - (xfer_est & 0x3)) & 0x3);
- + }
- +
- + dma_desc->buf = ep->dma_addr + offset;
- + dma_desc->status.b.sts = 0;
- + dma_desc->status.b.bs = BS_HOST_READY;
- + }
- + dma_desc++;
- + }
- +}
- +/**
- + * This function is called when to write ISOC data into appropriate dedicated
- + * periodic FIFO.
- + */
- +static int32_t write_isoc_tx_fifo(dwc_otg_core_if_t * core_if, dwc_ep_t * dwc_ep)
- +{
- + dwc_otg_dev_if_t *dev_if = core_if->dev_if;
- + dwc_otg_dev_in_ep_regs_t *ep_regs;
- + dtxfsts_data_t txstatus = {.d32 = 0 };
- + uint32_t len = 0;
- + int epnum = dwc_ep->num;
- + int dwords;
- +
- + DWC_DEBUGPL(DBG_PCD, "Dedicated TxFifo Empty: %d \n", epnum);
- +
- + ep_regs = core_if->dev_if->in_ep_regs[epnum];
- +
- + len = dwc_ep->xfer_len - dwc_ep->xfer_count;
- +
- + if (len > dwc_ep->maxpacket) {
- + len = dwc_ep->maxpacket;
- + }
- +
- + dwords = (len + 3) / 4;
- +
- + /* While there is space in the queue and space in the FIFO and
- + * More data to tranfer, Write packets to the Tx FIFO */
- + txstatus.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dtxfsts);
- + DWC_DEBUGPL(DBG_PCDV, "b4 dtxfsts[%d]=0x%08x\n", epnum, txstatus.d32);
- +
- + while (txstatus.b.txfspcavail > dwords &&
- + dwc_ep->xfer_count < dwc_ep->xfer_len && dwc_ep->xfer_len != 0) {
- + /* Write the FIFO */
- + dwc_otg_ep_write_packet(core_if, dwc_ep, 0);
- +
- + len = dwc_ep->xfer_len - dwc_ep->xfer_count;
- + if (len > dwc_ep->maxpacket) {
- + len = dwc_ep->maxpacket;
- + }
- +
- + dwords = (len + 3) / 4;
- + txstatus.d32 =
- + DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dtxfsts);
- + DWC_DEBUGPL(DBG_PCDV, "dtxfsts[%d]=0x%08x\n", epnum,
- + txstatus.d32);
- + }
- +
- + DWC_DEBUGPL(DBG_PCDV, "b4 dtxfsts[%d]=0x%08x\n", epnum,
- + DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dtxfsts));
- +
- + return 1;
- +}
- +/**
- + * This function does the setup for a data transfer for an EP and
- + * starts the transfer. For an IN transfer, the packets will be
- + * loaded into the appropriate Tx FIFO in the ISR. For OUT transfers,
- + * the packets are unloaded from the Rx FIFO in the ISR. the ISR.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param ep The EP to start the transfer on.
- + */
- +
- +void dwc_otg_ep_start_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
- +{
- + depctl_data_t depctl;
- + deptsiz_data_t deptsiz;
- + gintmsk_data_t intr_mask = {.d32 = 0 };
- +
- + DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "%s()\n", __func__);
- + DWC_DEBUGPL(DBG_PCD, "ep%d-%s xfer_len=%d xfer_cnt=%d "
- + "xfer_buff=%p start_xfer_buff=%p, total_len = %d\n",
- + ep->num, (ep->is_in ? "IN" : "OUT"), ep->xfer_len,
- + ep->xfer_count, ep->xfer_buff, ep->start_xfer_buff,
- + ep->total_len);
- + /* IN endpoint */
- + if (ep->is_in == 1) {
- + dwc_otg_dev_in_ep_regs_t *in_regs =
- + core_if->dev_if->in_ep_regs[ep->num];
- +
- + gnptxsts_data_t gtxstatus;
- +
- + gtxstatus.d32 =
- + DWC_READ_REG32(&core_if->core_global_regs->gnptxsts);
- +
- + if (core_if->en_multiple_tx_fifo == 0
- + && gtxstatus.b.nptxqspcavail == 0 && !core_if->dma_enable) {
- +#ifdef DEBUG
- + DWC_PRINTF("TX Queue Full (0x%0x)\n", gtxstatus.d32);
- +#endif
- + return;
- + }
- +
- + depctl.d32 = DWC_READ_REG32(&(in_regs->diepctl));
- + deptsiz.d32 = DWC_READ_REG32(&(in_regs->dieptsiz));
- +
- + if (ep->maxpacket > ep->maxxfer / MAX_PKT_CNT)
- + ep->xfer_len += (ep->maxxfer < (ep->total_len - ep->xfer_len)) ?
- + ep->maxxfer : (ep->total_len - ep->xfer_len);
- + else
- + ep->xfer_len += (MAX_PKT_CNT * ep->maxpacket < (ep->total_len - ep->xfer_len)) ?
- + MAX_PKT_CNT * ep->maxpacket : (ep->total_len - ep->xfer_len);
- +
- +
- + /* Zero Length Packet? */
- + if ((ep->xfer_len - ep->xfer_count) == 0) {
- + deptsiz.b.xfersize = 0;
- + deptsiz.b.pktcnt = 1;
- + } else {
- + /* Program the transfer size and packet count
- + * as follows: xfersize = N * maxpacket +
- + * short_packet pktcnt = N + (short_packet
- + * exist ? 1 : 0)
- + */
- + deptsiz.b.xfersize = ep->xfer_len - ep->xfer_count;
- + deptsiz.b.pktcnt =
- + (ep->xfer_len - ep->xfer_count - 1 +
- + ep->maxpacket) / ep->maxpacket;
- + if (deptsiz.b.pktcnt > MAX_PKT_CNT) {
- + deptsiz.b.pktcnt = MAX_PKT_CNT;
- + deptsiz.b.xfersize = deptsiz.b.pktcnt * ep->maxpacket;
- + }
- + if (ep->type == DWC_OTG_EP_TYPE_ISOC)
- + deptsiz.b.mc = deptsiz.b.pktcnt;
- + }
- +
- + /* Write the DMA register */
- + if (core_if->dma_enable) {
- + if (core_if->dma_desc_enable == 0) {
- + if (ep->type != DWC_OTG_EP_TYPE_ISOC)
- + deptsiz.b.mc = 1;
- + DWC_WRITE_REG32(&in_regs->dieptsiz,
- + deptsiz.d32);
- + DWC_WRITE_REG32(&(in_regs->diepdma),
- + (uint32_t) ep->dma_addr);
- + } else {
- +#ifdef DWC_UTE_CFI
- + /* The descriptor chain should be already initialized by now */
- + if (ep->buff_mode != BM_STANDARD) {
- + DWC_WRITE_REG32(&in_regs->diepdma,
- + ep->descs_dma_addr);
- + } else {
- +#endif
- + init_dma_desc_chain(core_if, ep);
- + /** DIEPDMAn Register write */
- + DWC_WRITE_REG32(&in_regs->diepdma,
- + ep->dma_desc_addr);
- +#ifdef DWC_UTE_CFI
- + }
- +#endif
- + }
- + } else {
- + DWC_WRITE_REG32(&in_regs->dieptsiz, deptsiz.d32);
- + if (ep->type != DWC_OTG_EP_TYPE_ISOC) {
- + /**
- + * Enable the Non-Periodic Tx FIFO empty interrupt,
- + * or the Tx FIFO epmty interrupt in dedicated Tx FIFO mode,
- + * the data will be written into the fifo by the ISR.
- + */
- + if (core_if->en_multiple_tx_fifo == 0) {
- + intr_mask.b.nptxfempty = 1;
- + DWC_MODIFY_REG32
- + (&core_if->core_global_regs->gintmsk,
- + intr_mask.d32, intr_mask.d32);
- + } else {
- + /* Enable the Tx FIFO Empty Interrupt for this EP */
- + if (ep->xfer_len > 0) {
- + uint32_t fifoemptymsk = 0;
- + fifoemptymsk = 1 << ep->num;
- + DWC_MODIFY_REG32
- + (&core_if->dev_if->dev_global_regs->dtknqr4_fifoemptymsk,
- + 0, fifoemptymsk);
- +
- + }
- + }
- + } else {
- + write_isoc_tx_fifo(core_if, ep);
- + }
- + }
- + if (!core_if->core_params->en_multiple_tx_fifo && core_if->dma_enable)
- + depctl.b.nextep = core_if->nextep_seq[ep->num];
- +
- + if (ep->type == DWC_OTG_EP_TYPE_ISOC) {
- + dsts_data_t dsts = {.d32 = 0};
- + if (ep->bInterval == 1) {
- + dsts.d32 =
- + DWC_READ_REG32(&core_if->dev_if->
- + dev_global_regs->dsts);
- + ep->frame_num = dsts.b.soffn + ep->bInterval;
- + if (ep->frame_num > 0x3FFF) {
- + ep->frm_overrun = 1;
- + ep->frame_num &= 0x3FFF;
- + } else
- + ep->frm_overrun = 0;
- + if (ep->frame_num & 0x1) {
- + depctl.b.setd1pid = 1;
- + } else {
- + depctl.b.setd0pid = 1;
- + }
- + }
- + }
- + /* EP enable, IN data in FIFO */
- + depctl.b.cnak = 1;
- + depctl.b.epena = 1;
- + DWC_WRITE_REG32(&in_regs->diepctl, depctl.d32);
- +
- + } else {
- + /* OUT endpoint */
- + dwc_otg_dev_out_ep_regs_t *out_regs =
- + core_if->dev_if->out_ep_regs[ep->num];
- +
- + depctl.d32 = DWC_READ_REG32(&(out_regs->doepctl));
- + deptsiz.d32 = DWC_READ_REG32(&(out_regs->doeptsiz));
- +
- + if (!core_if->dma_desc_enable) {
- + if (ep->maxpacket > ep->maxxfer / MAX_PKT_CNT)
- + ep->xfer_len += (ep->maxxfer < (ep->total_len - ep->xfer_len)) ?
- + ep->maxxfer : (ep->total_len - ep->xfer_len);
- + else
- + ep->xfer_len += (MAX_PKT_CNT * ep->maxpacket < (ep->total_len
- + - ep->xfer_len)) ? MAX_PKT_CNT * ep->maxpacket : (ep->total_len - ep->xfer_len);
- + }
- +
- + /* Program the transfer size and packet count as follows:
- + *
- + * pktcnt = N
- + * xfersize = N * maxpacket
- + */
- + if ((ep->xfer_len - ep->xfer_count) == 0) {
- + /* Zero Length Packet */
- + deptsiz.b.xfersize = ep->maxpacket;
- + deptsiz.b.pktcnt = 1;
- + } else {
- + deptsiz.b.pktcnt =
- + (ep->xfer_len - ep->xfer_count +
- + (ep->maxpacket - 1)) / ep->maxpacket;
- + if (deptsiz.b.pktcnt > MAX_PKT_CNT) {
- + deptsiz.b.pktcnt = MAX_PKT_CNT;
- + }
- + if (!core_if->dma_desc_enable) {
- + ep->xfer_len =
- + deptsiz.b.pktcnt * ep->maxpacket + ep->xfer_count;
- + }
- + deptsiz.b.xfersize = ep->xfer_len - ep->xfer_count;
- + }
- +
- + DWC_DEBUGPL(DBG_PCDV, "ep%d xfersize=%d pktcnt=%d\n",
- + ep->num, deptsiz.b.xfersize, deptsiz.b.pktcnt);
- +
- + if (core_if->dma_enable) {
- + if (!core_if->dma_desc_enable) {
- + DWC_WRITE_REG32(&out_regs->doeptsiz,
- + deptsiz.d32);
- +
- + DWC_WRITE_REG32(&(out_regs->doepdma),
- + (uint32_t) ep->dma_addr);
- + } else {
- +#ifdef DWC_UTE_CFI
- + /* The descriptor chain should be already initialized by now */
- + if (ep->buff_mode != BM_STANDARD) {
- + DWC_WRITE_REG32(&out_regs->doepdma,
- + ep->descs_dma_addr);
- + } else {
- +#endif
- + /** This is used for interrupt out transfers*/
- + if (!ep->xfer_len)
- + ep->xfer_len = ep->total_len;
- + init_dma_desc_chain(core_if, ep);
- +
- + if (core_if->core_params->dev_out_nak) {
- + if (ep->type == DWC_OTG_EP_TYPE_BULK) {
- + deptsiz.b.pktcnt = (ep->total_len +
- + (ep->maxpacket - 1)) / ep->maxpacket;
- + deptsiz.b.xfersize = ep->total_len;
- + /* Remember initial value of doeptsiz */
- + core_if->start_doeptsiz_val[ep->num] = deptsiz.d32;
- + DWC_WRITE_REG32(&out_regs->doeptsiz,
- + deptsiz.d32);
- + }
- + }
- + /** DOEPDMAn Register write */
- + DWC_WRITE_REG32(&out_regs->doepdma,
- + ep->dma_desc_addr);
- +#ifdef DWC_UTE_CFI
- + }
- +#endif
- + }
- + } else {
- + DWC_WRITE_REG32(&out_regs->doeptsiz, deptsiz.d32);
- + }
- +
- + if (ep->type == DWC_OTG_EP_TYPE_ISOC) {
- + dsts_data_t dsts = {.d32 = 0};
- + if (ep->bInterval == 1) {
- + dsts.d32 =
- + DWC_READ_REG32(&core_if->dev_if->
- + dev_global_regs->dsts);
- + ep->frame_num = dsts.b.soffn + ep->bInterval;
- + if (ep->frame_num > 0x3FFF) {
- + ep->frm_overrun = 1;
- + ep->frame_num &= 0x3FFF;
- + } else
- + ep->frm_overrun = 0;
- +
- + if (ep->frame_num & 0x1) {
- + depctl.b.setd1pid = 1;
- + } else {
- + depctl.b.setd0pid = 1;
- + }
- + }
- + }
- +
- + /* EP enable */
- + depctl.b.cnak = 1;
- + depctl.b.epena = 1;
- +
- + DWC_WRITE_REG32(&out_regs->doepctl, depctl.d32);
- +
- + DWC_DEBUGPL(DBG_PCD, "DOEPCTL=%08x DOEPTSIZ=%08x\n",
- + DWC_READ_REG32(&out_regs->doepctl),
- + DWC_READ_REG32(&out_regs->doeptsiz));
- + DWC_DEBUGPL(DBG_PCD, "DAINTMSK=%08x GINTMSK=%08x\n",
- + DWC_READ_REG32(&core_if->dev_if->dev_global_regs->
- + daintmsk),
- + DWC_READ_REG32(&core_if->core_global_regs->
- + gintmsk));
- +
- + /* Timer is scheduling only for out bulk transfers for
- + * "Device DDMA OUT NAK Enhancement" feature to inform user
- + * about received data payload in case of timeout
- + */
- + if (core_if->core_params->dev_out_nak) {
- + if (ep->type == DWC_OTG_EP_TYPE_BULK) {
- + core_if->ep_xfer_info[ep->num].core_if = core_if;
- + core_if->ep_xfer_info[ep->num].ep = ep;
- + core_if->ep_xfer_info[ep->num].state = 1;
- +
- + /* Start a timer for this transfer. */
- + DWC_TIMER_SCHEDULE(core_if->ep_xfer_timer[ep->num], 10000);
- + }
- + }
- + }
- +}
- +
- +/**
- + * This function setup a zero length transfer in Buffer DMA and
- + * Slave modes for usb requests with zero field set
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param ep The EP to start the transfer on.
- + *
- + */
- +void dwc_otg_ep_start_zl_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
- +{
- +
- + depctl_data_t depctl;
- + deptsiz_data_t deptsiz;
- + gintmsk_data_t intr_mask = {.d32 = 0 };
- +
- + DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "%s()\n", __func__);
- + DWC_PRINTF("zero length transfer is called\n");
- +
- + /* IN endpoint */
- + if (ep->is_in == 1) {
- + dwc_otg_dev_in_ep_regs_t *in_regs =
- + core_if->dev_if->in_ep_regs[ep->num];
- +
- + depctl.d32 = DWC_READ_REG32(&(in_regs->diepctl));
- + deptsiz.d32 = DWC_READ_REG32(&(in_regs->dieptsiz));
- +
- + deptsiz.b.xfersize = 0;
- + deptsiz.b.pktcnt = 1;
- +
- + /* Write the DMA register */
- + if (core_if->dma_enable) {
- + if (core_if->dma_desc_enable == 0) {
- + deptsiz.b.mc = 1;
- + DWC_WRITE_REG32(&in_regs->dieptsiz,
- + deptsiz.d32);
- + DWC_WRITE_REG32(&(in_regs->diepdma),
- + (uint32_t) ep->dma_addr);
- + }
- + } else {
- + DWC_WRITE_REG32(&in_regs->dieptsiz, deptsiz.d32);
- + /**
- + * Enable the Non-Periodic Tx FIFO empty interrupt,
- + * or the Tx FIFO epmty interrupt in dedicated Tx FIFO mode,
- + * the data will be written into the fifo by the ISR.
- + */
- + if (core_if->en_multiple_tx_fifo == 0) {
- + intr_mask.b.nptxfempty = 1;
- + DWC_MODIFY_REG32(&core_if->
- + core_global_regs->gintmsk,
- + intr_mask.d32, intr_mask.d32);
- + } else {
- + /* Enable the Tx FIFO Empty Interrupt for this EP */
- + if (ep->xfer_len > 0) {
- + uint32_t fifoemptymsk = 0;
- + fifoemptymsk = 1 << ep->num;
- + DWC_MODIFY_REG32(&core_if->
- + dev_if->dev_global_regs->dtknqr4_fifoemptymsk,
- + 0, fifoemptymsk);
- + }
- + }
- + }
- +
- + if (!core_if->core_params->en_multiple_tx_fifo && core_if->dma_enable)
- + depctl.b.nextep = core_if->nextep_seq[ep->num];
- + /* EP enable, IN data in FIFO */
- + depctl.b.cnak = 1;
- + depctl.b.epena = 1;
- + DWC_WRITE_REG32(&in_regs->diepctl, depctl.d32);
- +
- + } else {
- + /* OUT endpoint */
- + dwc_otg_dev_out_ep_regs_t *out_regs =
- + core_if->dev_if->out_ep_regs[ep->num];
- +
- + depctl.d32 = DWC_READ_REG32(&(out_regs->doepctl));
- + deptsiz.d32 = DWC_READ_REG32(&(out_regs->doeptsiz));
- +
- + /* Zero Length Packet */
- + deptsiz.b.xfersize = ep->maxpacket;
- + deptsiz.b.pktcnt = 1;
- +
- + if (core_if->dma_enable) {
- + if (!core_if->dma_desc_enable) {
- + DWC_WRITE_REG32(&out_regs->doeptsiz,
- + deptsiz.d32);
- +
- + DWC_WRITE_REG32(&(out_regs->doepdma),
- + (uint32_t) ep->dma_addr);
- + }
- + } else {
- + DWC_WRITE_REG32(&out_regs->doeptsiz, deptsiz.d32);
- + }
- +
- + /* EP enable */
- + depctl.b.cnak = 1;
- + depctl.b.epena = 1;
- +
- + DWC_WRITE_REG32(&out_regs->doepctl, depctl.d32);
- +
- + }
- +}
- +
- +/**
- + * This function does the setup for a data transfer for EP0 and starts
- + * the transfer. For an IN transfer, the packets will be loaded into
- + * the appropriate Tx FIFO in the ISR. For OUT transfers, the packets are
- + * unloaded from the Rx FIFO in the ISR.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param ep The EP0 data.
- + */
- +void dwc_otg_ep0_start_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
- +{
- + depctl_data_t depctl;
- + deptsiz0_data_t deptsiz;
- + gintmsk_data_t intr_mask = {.d32 = 0 };
- + dwc_otg_dev_dma_desc_t *dma_desc;
- +
- + DWC_DEBUGPL(DBG_PCD, "ep%d-%s xfer_len=%d xfer_cnt=%d "
- + "xfer_buff=%p start_xfer_buff=%p \n",
- + ep->num, (ep->is_in ? "IN" : "OUT"), ep->xfer_len,
- + ep->xfer_count, ep->xfer_buff, ep->start_xfer_buff);
- +
- + ep->total_len = ep->xfer_len;
- +
- + /* IN endpoint */
- + if (ep->is_in == 1) {
- + dwc_otg_dev_in_ep_regs_t *in_regs =
- + core_if->dev_if->in_ep_regs[0];
- +
- + gnptxsts_data_t gtxstatus;
- +
- + if (core_if->snpsid >= OTG_CORE_REV_3_00a) {
- + depctl.d32 = DWC_READ_REG32(&in_regs->diepctl);
- + if (depctl.b.epena)
- + return;
- + }
- +
- + gtxstatus.d32 =
- + DWC_READ_REG32(&core_if->core_global_regs->gnptxsts);
- +
- + /* If dedicated FIFO every time flush fifo before enable ep*/
- + if (core_if->en_multiple_tx_fifo && core_if->snpsid >= OTG_CORE_REV_3_00a)
- + dwc_otg_flush_tx_fifo(core_if, ep->tx_fifo_num);
- +
- + if (core_if->en_multiple_tx_fifo == 0
- + && gtxstatus.b.nptxqspcavail == 0
- + && !core_if->dma_enable) {
- +#ifdef DEBUG
- + deptsiz.d32 = DWC_READ_REG32(&in_regs->dieptsiz);
- + DWC_DEBUGPL(DBG_PCD, "DIEPCTL0=%0x\n",
- + DWC_READ_REG32(&in_regs->diepctl));
- + DWC_DEBUGPL(DBG_PCD, "DIEPTSIZ0=%0x (sz=%d, pcnt=%d)\n",
- + deptsiz.d32,
- + deptsiz.b.xfersize, deptsiz.b.pktcnt);
- + DWC_PRINTF("TX Queue or FIFO Full (0x%0x)\n",
- + gtxstatus.d32);
- +#endif
- + return;
- + }
- +
- + depctl.d32 = DWC_READ_REG32(&in_regs->diepctl);
- + deptsiz.d32 = DWC_READ_REG32(&in_regs->dieptsiz);
- +
- + /* Zero Length Packet? */
- + if (ep->xfer_len == 0) {
- + deptsiz.b.xfersize = 0;
- + deptsiz.b.pktcnt = 1;
- + } else {
- + /* Program the transfer size and packet count
- + * as follows: xfersize = N * maxpacket +
- + * short_packet pktcnt = N + (short_packet
- + * exist ? 1 : 0)
- + */
- + if (ep->xfer_len > ep->maxpacket) {
- + ep->xfer_len = ep->maxpacket;
- + deptsiz.b.xfersize = ep->maxpacket;
- + } else {
- + deptsiz.b.xfersize = ep->xfer_len;
- + }
- + deptsiz.b.pktcnt = 1;
- +
- + }
- + DWC_DEBUGPL(DBG_PCDV,
- + "IN len=%d xfersize=%d pktcnt=%d [%08x]\n",
- + ep->xfer_len, deptsiz.b.xfersize, deptsiz.b.pktcnt,
- + deptsiz.d32);
- +
- + /* Write the DMA register */
- + if (core_if->dma_enable) {
- + if (core_if->dma_desc_enable == 0) {
- + DWC_WRITE_REG32(&in_regs->dieptsiz,
- + deptsiz.d32);
- +
- + DWC_WRITE_REG32(&(in_regs->diepdma),
- + (uint32_t) ep->dma_addr);
- + } else {
- + dma_desc = core_if->dev_if->in_desc_addr;
- +
- + /** DMA Descriptor Setup */
- + dma_desc->status.b.bs = BS_HOST_BUSY;
- + dma_desc->status.b.l = 1;
- + dma_desc->status.b.ioc = 1;
- + dma_desc->status.b.sp =
- + (ep->xfer_len == ep->maxpacket) ? 0 : 1;
- + dma_desc->status.b.bytes = ep->xfer_len;
- + dma_desc->buf = ep->dma_addr;
- + dma_desc->status.b.sts = 0;
- + dma_desc->status.b.bs = BS_HOST_READY;
- +
- + /** DIEPDMA0 Register write */
- + DWC_WRITE_REG32(&in_regs->diepdma,
- + core_if->
- + dev_if->dma_in_desc_addr);
- + }
- + } else {
- + DWC_WRITE_REG32(&in_regs->dieptsiz, deptsiz.d32);
- + }
- +
- + if (!core_if->core_params->en_multiple_tx_fifo && core_if->dma_enable)
- + depctl.b.nextep = core_if->nextep_seq[ep->num];
- + /* EP enable, IN data in FIFO */
- + depctl.b.cnak = 1;
- + depctl.b.epena = 1;
- + DWC_WRITE_REG32(&in_regs->diepctl, depctl.d32);
- +
- + /**
- + * Enable the Non-Periodic Tx FIFO empty interrupt, the
- + * data will be written into the fifo by the ISR.
- + */
- + if (!core_if->dma_enable) {
- + if (core_if->en_multiple_tx_fifo == 0) {
- + intr_mask.b.nptxfempty = 1;
- + DWC_MODIFY_REG32(&core_if->
- + core_global_regs->gintmsk,
- + intr_mask.d32, intr_mask.d32);
- + } else {
- + /* Enable the Tx FIFO Empty Interrupt for this EP */
- + if (ep->xfer_len > 0) {
- + uint32_t fifoemptymsk = 0;
- + fifoemptymsk |= 1 << ep->num;
- + DWC_MODIFY_REG32(&core_if->
- + dev_if->dev_global_regs->dtknqr4_fifoemptymsk,
- + 0, fifoemptymsk);
- + }
- + }
- + }
- + } else {
- + /* OUT endpoint */
- + dwc_otg_dev_out_ep_regs_t *out_regs =
- + core_if->dev_if->out_ep_regs[0];
- +
- + depctl.d32 = DWC_READ_REG32(&out_regs->doepctl);
- + deptsiz.d32 = DWC_READ_REG32(&out_regs->doeptsiz);
- +
- + /* Program the transfer size and packet count as follows:
- + * xfersize = N * (maxpacket + 4 - (maxpacket % 4))
- + * pktcnt = N */
- + /* Zero Length Packet */
- + deptsiz.b.xfersize = ep->maxpacket;
- + deptsiz.b.pktcnt = 1;
- + if (core_if->snpsid >= OTG_CORE_REV_3_00a)
- + deptsiz.b.supcnt = 3;
- +
- + DWC_DEBUGPL(DBG_PCDV, "len=%d xfersize=%d pktcnt=%d\n",
- + ep->xfer_len, deptsiz.b.xfersize, deptsiz.b.pktcnt);
- +
- + if (core_if->dma_enable) {
- + if (!core_if->dma_desc_enable) {
- + DWC_WRITE_REG32(&out_regs->doeptsiz,
- + deptsiz.d32);
- +
- + DWC_WRITE_REG32(&(out_regs->doepdma),
- + (uint32_t) ep->dma_addr);
- + } else {
- + dma_desc = core_if->dev_if->out_desc_addr;
- +
- + /** DMA Descriptor Setup */
- + dma_desc->status.b.bs = BS_HOST_BUSY;
- + if (core_if->snpsid >= OTG_CORE_REV_3_00a) {
- + dma_desc->status.b.mtrf = 0;
- + dma_desc->status.b.sr = 0;
- + }
- + dma_desc->status.b.l = 1;
- + dma_desc->status.b.ioc = 1;
- + dma_desc->status.b.bytes = ep->maxpacket;
- + dma_desc->buf = ep->dma_addr;
- + dma_desc->status.b.sts = 0;
- + dma_desc->status.b.bs = BS_HOST_READY;
- +
- + /** DOEPDMA0 Register write */
- + DWC_WRITE_REG32(&out_regs->doepdma,
- + core_if->dev_if->
- + dma_out_desc_addr);
- + }
- + } else {
- + DWC_WRITE_REG32(&out_regs->doeptsiz, deptsiz.d32);
- + }
- +
- + /* EP enable */
- + depctl.b.cnak = 1;
- + depctl.b.epena = 1;
- + DWC_WRITE_REG32(&(out_regs->doepctl), depctl.d32);
- + }
- +}
- +
- +/**
- + * This function continues control IN transfers started by
- + * dwc_otg_ep0_start_transfer, when the transfer does not fit in a
- + * single packet. NOTE: The DIEPCTL0/DOEPCTL0 registers only have one
- + * bit for the packet count.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param ep The EP0 data.
- + */
- +void dwc_otg_ep0_continue_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
- +{
- + depctl_data_t depctl;
- + deptsiz0_data_t deptsiz;
- + gintmsk_data_t intr_mask = {.d32 = 0 };
- + dwc_otg_dev_dma_desc_t *dma_desc;
- +
- + if (ep->is_in == 1) {
- + dwc_otg_dev_in_ep_regs_t *in_regs =
- + core_if->dev_if->in_ep_regs[0];
- + gnptxsts_data_t tx_status = {.d32 = 0 };
- +
- + tx_status.d32 =
- + DWC_READ_REG32(&core_if->core_global_regs->gnptxsts);
- + /** @todo Should there be check for room in the Tx
- + * Status Queue. If not remove the code above this comment. */
- +
- + depctl.d32 = DWC_READ_REG32(&in_regs->diepctl);
- + deptsiz.d32 = DWC_READ_REG32(&in_regs->dieptsiz);
- +
- + /* Program the transfer size and packet count
- + * as follows: xfersize = N * maxpacket +
- + * short_packet pktcnt = N + (short_packet
- + * exist ? 1 : 0)
- + */
- +
- + if (core_if->dma_desc_enable == 0) {
- + deptsiz.b.xfersize =
- + (ep->total_len - ep->xfer_count) >
- + ep->maxpacket ? ep->maxpacket : (ep->total_len -
- + ep->xfer_count);
- + deptsiz.b.pktcnt = 1;
- + if (core_if->dma_enable == 0) {
- + ep->xfer_len += deptsiz.b.xfersize;
- + } else {
- + ep->xfer_len = deptsiz.b.xfersize;
- + }
- + DWC_WRITE_REG32(&in_regs->dieptsiz, deptsiz.d32);
- + } else {
- + ep->xfer_len =
- + (ep->total_len - ep->xfer_count) >
- + ep->maxpacket ? ep->maxpacket : (ep->total_len -
- + ep->xfer_count);
- +
- + dma_desc = core_if->dev_if->in_desc_addr;
- +
- + /** DMA Descriptor Setup */
- + dma_desc->status.b.bs = BS_HOST_BUSY;
- + dma_desc->status.b.l = 1;
- + dma_desc->status.b.ioc = 1;
- + dma_desc->status.b.sp =
- + (ep->xfer_len == ep->maxpacket) ? 0 : 1;
- + dma_desc->status.b.bytes = ep->xfer_len;
- + dma_desc->buf = ep->dma_addr;
- + dma_desc->status.b.sts = 0;
- + dma_desc->status.b.bs = BS_HOST_READY;
- +
- + /** DIEPDMA0 Register write */
- + DWC_WRITE_REG32(&in_regs->diepdma,
- + core_if->dev_if->dma_in_desc_addr);
- + }
- +
- + DWC_DEBUGPL(DBG_PCDV,
- + "IN len=%d xfersize=%d pktcnt=%d [%08x]\n",
- + ep->xfer_len, deptsiz.b.xfersize, deptsiz.b.pktcnt,
- + deptsiz.d32);
- +
- + /* Write the DMA register */
- + if (core_if->hwcfg2.b.architecture == DWC_INT_DMA_ARCH) {
- + if (core_if->dma_desc_enable == 0)
- + DWC_WRITE_REG32(&(in_regs->diepdma),
- + (uint32_t) ep->dma_addr);
- + }
- + if (!core_if->core_params->en_multiple_tx_fifo && core_if->dma_enable)
- + depctl.b.nextep = core_if->nextep_seq[ep->num];
- + /* EP enable, IN data in FIFO */
- + depctl.b.cnak = 1;
- + depctl.b.epena = 1;
- + DWC_WRITE_REG32(&in_regs->diepctl, depctl.d32);
- +
- + /**
- + * Enable the Non-Periodic Tx FIFO empty interrupt, the
- + * data will be written into the fifo by the ISR.
- + */
- + if (!core_if->dma_enable) {
- + if (core_if->en_multiple_tx_fifo == 0) {
- + /* First clear it from GINTSTS */
- + intr_mask.b.nptxfempty = 1;
- + DWC_MODIFY_REG32(&core_if->
- + core_global_regs->gintmsk,
- + intr_mask.d32, intr_mask.d32);
- +
- + } else {
- + /* Enable the Tx FIFO Empty Interrupt for this EP */
- + if (ep->xfer_len > 0) {
- + uint32_t fifoemptymsk = 0;
- + fifoemptymsk |= 1 << ep->num;
- + DWC_MODIFY_REG32(&core_if->
- + dev_if->dev_global_regs->dtknqr4_fifoemptymsk,
- + 0, fifoemptymsk);
- + }
- + }
- + }
- + } else {
- + dwc_otg_dev_out_ep_regs_t *out_regs =
- + core_if->dev_if->out_ep_regs[0];
- +
- + depctl.d32 = DWC_READ_REG32(&out_regs->doepctl);
- + deptsiz.d32 = DWC_READ_REG32(&out_regs->doeptsiz);
- +
- + /* Program the transfer size and packet count
- + * as follows: xfersize = N * maxpacket +
- + * short_packet pktcnt = N + (short_packet
- + * exist ? 1 : 0)
- + */
- + deptsiz.b.xfersize = ep->maxpacket;
- + deptsiz.b.pktcnt = 1;
- +
- + if (core_if->dma_desc_enable == 0) {
- + DWC_WRITE_REG32(&out_regs->doeptsiz, deptsiz.d32);
- + } else {
- + dma_desc = core_if->dev_if->out_desc_addr;
- +
- + /** DMA Descriptor Setup */
- + dma_desc->status.b.bs = BS_HOST_BUSY;
- + dma_desc->status.b.l = 1;
- + dma_desc->status.b.ioc = 1;
- + dma_desc->status.b.bytes = ep->maxpacket;
- + dma_desc->buf = ep->dma_addr;
- + dma_desc->status.b.sts = 0;
- + dma_desc->status.b.bs = BS_HOST_READY;
- +
- + /** DOEPDMA0 Register write */
- + DWC_WRITE_REG32(&out_regs->doepdma,
- + core_if->dev_if->dma_out_desc_addr);
- + }
- +
- + DWC_DEBUGPL(DBG_PCDV,
- + "IN len=%d xfersize=%d pktcnt=%d [%08x]\n",
- + ep->xfer_len, deptsiz.b.xfersize, deptsiz.b.pktcnt,
- + deptsiz.d32);
- +
- + /* Write the DMA register */
- + if (core_if->hwcfg2.b.architecture == DWC_INT_DMA_ARCH) {
- + if (core_if->dma_desc_enable == 0)
- + DWC_WRITE_REG32(&(out_regs->doepdma),
- + (uint32_t) ep->dma_addr);
- +
- + }
- +
- + /* EP enable, IN data in FIFO */
- + depctl.b.cnak = 1;
- + depctl.b.epena = 1;
- + DWC_WRITE_REG32(&out_regs->doepctl, depctl.d32);
- +
- + }
- +}
- +
- +#ifdef DEBUG
- +void dump_msg(const u8 * buf, unsigned int length)
- +{
- + unsigned int start, num, i;
- + char line[52], *p;
- +
- + if (length >= 512)
- + return;
- + start = 0;
- + while (length > 0) {
- + num = length < 16u ? length : 16u;
- + p = line;
- + for (i = 0; i < num; ++i) {
- + if (i == 8)
- + *p++ = ' ';
- + DWC_SPRINTF(p, " %02x", buf[i]);
- + p += 3;
- + }
- + *p = 0;
- + DWC_PRINTF("%6x: %s\n", start, line);
- + buf += num;
- + start += num;
- + length -= num;
- + }
- +}
- +#else
- +static inline void dump_msg(const u8 * buf, unsigned int length)
- +{
- +}
- +#endif
- +
- +/**
- + * This function writes a packet into the Tx FIFO associated with the
- + * EP. For non-periodic EPs the non-periodic Tx FIFO is written. For
- + * periodic EPs the periodic Tx FIFO associated with the EP is written
- + * with all packets for the next micro-frame.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param ep The EP to write packet for.
- + * @param dma Indicates if DMA is being used.
- + */
- +void dwc_otg_ep_write_packet(dwc_otg_core_if_t * core_if, dwc_ep_t * ep,
- + int dma)
- +{
- + /**
- + * The buffer is padded to DWORD on a per packet basis in
- + * slave/dma mode if the MPS is not DWORD aligned. The last
- + * packet, if short, is also padded to a multiple of DWORD.
- + *
- + * ep->xfer_buff always starts DWORD aligned in memory and is a
- + * multiple of DWORD in length
- + *
- + * ep->xfer_len can be any number of bytes
- + *
- + * ep->xfer_count is a multiple of ep->maxpacket until the last
- + * packet
- + *
- + * FIFO access is DWORD */
- +
- + uint32_t i;
- + uint32_t byte_count;
- + uint32_t dword_count;
- + uint32_t *fifo;
- + uint32_t *data_buff = (uint32_t *) ep->xfer_buff;
- +
- + DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "%s(%p,%p)\n", __func__, core_if,
- + ep);
- + if (ep->xfer_count >= ep->xfer_len) {
- + DWC_WARN("%s() No data for EP%d!!!\n", __func__, ep->num);
- + return;
- + }
- +
- + /* Find the byte length of the packet either short packet or MPS */
- + if ((ep->xfer_len - ep->xfer_count) < ep->maxpacket) {
- + byte_count = ep->xfer_len - ep->xfer_count;
- + } else {
- + byte_count = ep->maxpacket;
- + }
- +
- + /* Find the DWORD length, padded by extra bytes as neccessary if MPS
- + * is not a multiple of DWORD */
- + dword_count = (byte_count + 3) / 4;
- +
- +#ifdef VERBOSE
- + dump_msg(ep->xfer_buff, byte_count);
- +#endif
- +
- + /**@todo NGS Where are the Periodic Tx FIFO addresses
- + * intialized? What should this be? */
- +
- + fifo = core_if->data_fifo[ep->num];
- +
- + DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "fifo=%p buff=%p *p=%08x bc=%d\n",
- + fifo, data_buff, *data_buff, byte_count);
- +
- + if (!dma) {
- + for (i = 0; i < dword_count; i++, data_buff++) {
- + DWC_WRITE_REG32(fifo, *data_buff);
- + }
- + }
- +
- + ep->xfer_count += byte_count;
- + ep->xfer_buff += byte_count;
- + ep->dma_addr += byte_count;
- +}
- +
- +/**
- + * Set the EP STALL.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param ep The EP to set the stall on.
- + */
- +void dwc_otg_ep_set_stall(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
- +{
- + depctl_data_t depctl;
- + volatile uint32_t *depctl_addr;
- +
- + DWC_DEBUGPL(DBG_PCD, "%s ep%d-%s\n", __func__, ep->num,
- + (ep->is_in ? "IN" : "OUT"));
- +
- + if (ep->is_in == 1) {
- + depctl_addr = &(core_if->dev_if->in_ep_regs[ep->num]->diepctl);
- + depctl.d32 = DWC_READ_REG32(depctl_addr);
- +
- + /* set the disable and stall bits */
- + if (depctl.b.epena) {
- + depctl.b.epdis = 1;
- + }
- + depctl.b.stall = 1;
- + DWC_WRITE_REG32(depctl_addr, depctl.d32);
- + } else {
- + depctl_addr = &(core_if->dev_if->out_ep_regs[ep->num]->doepctl);
- + depctl.d32 = DWC_READ_REG32(depctl_addr);
- +
- + /* set the stall bit */
- + depctl.b.stall = 1;
- + DWC_WRITE_REG32(depctl_addr, depctl.d32);
- + }
- +
- + DWC_DEBUGPL(DBG_PCD, "DEPCTL=%0x\n", DWC_READ_REG32(depctl_addr));
- +
- + return;
- +}
- +
- +/**
- + * Clear the EP STALL.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param ep The EP to clear stall from.
- + */
- +void dwc_otg_ep_clear_stall(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
- +{
- + depctl_data_t depctl;
- + volatile uint32_t *depctl_addr;
- +
- + DWC_DEBUGPL(DBG_PCD, "%s ep%d-%s\n", __func__, ep->num,
- + (ep->is_in ? "IN" : "OUT"));
- +
- + if (ep->is_in == 1) {
- + depctl_addr = &(core_if->dev_if->in_ep_regs[ep->num]->diepctl);
- + } else {
- + depctl_addr = &(core_if->dev_if->out_ep_regs[ep->num]->doepctl);
- + }
- +
- + depctl.d32 = DWC_READ_REG32(depctl_addr);
- +
- + /* clear the stall bits */
- + depctl.b.stall = 0;
- +
- + /*
- + * USB Spec 9.4.5: For endpoints using data toggle, regardless
- + * of whether an endpoint has the Halt feature set, a
- + * ClearFeature(ENDPOINT_HALT) request always results in the
- + * data toggle being reinitialized to DATA0.
- + */
- + if (ep->type == DWC_OTG_EP_TYPE_INTR ||
- + ep->type == DWC_OTG_EP_TYPE_BULK) {
- + depctl.b.setd0pid = 1; /* DATA0 */
- + }
- +
- + DWC_WRITE_REG32(depctl_addr, depctl.d32);
- + DWC_DEBUGPL(DBG_PCD, "DEPCTL=%0x\n", DWC_READ_REG32(depctl_addr));
- + return;
- +}
- +
- +/**
- + * This function reads a packet from the Rx FIFO into the destination
- + * buffer. To read SETUP data use dwc_otg_read_setup_packet.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param dest Destination buffer for the packet.
- + * @param bytes Number of bytes to copy to the destination.
- + */
- +void dwc_otg_read_packet(dwc_otg_core_if_t * core_if,
- + uint8_t * dest, uint16_t bytes)
- +{
- + int i;
- + int word_count = (bytes + 3) / 4;
- +
- + volatile uint32_t *fifo = core_if->data_fifo[0];
- + uint32_t *data_buff = (uint32_t *) dest;
- +
- + /**
- + * @todo Account for the case where _dest is not dword aligned. This
- + * requires reading data from the FIFO into a uint32_t temp buffer,
- + * then moving it into the data buffer.
- + */
- +
- + DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "%s(%p,%p,%d)\n", __func__,
- + core_if, dest, bytes);
- +
- + for (i = 0; i < word_count; i++, data_buff++) {
- + *data_buff = DWC_READ_REG32(fifo);
- + }
- +
- + return;
- +}
- +
- +/**
- + * This functions reads the device registers and prints them
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + */
- +void dwc_otg_dump_dev_registers(dwc_otg_core_if_t * core_if)
- +{
- + int i;
- + volatile uint32_t *addr;
- +
- + DWC_PRINTF("Device Global Registers\n");
- + addr = &core_if->dev_if->dev_global_regs->dcfg;
- + DWC_PRINTF("DCFG @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- + addr = &core_if->dev_if->dev_global_regs->dctl;
- + DWC_PRINTF("DCTL @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- + addr = &core_if->dev_if->dev_global_regs->dsts;
- + DWC_PRINTF("DSTS @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- + addr = &core_if->dev_if->dev_global_regs->diepmsk;
- + DWC_PRINTF("DIEPMSK @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + addr = &core_if->dev_if->dev_global_regs->doepmsk;
- + DWC_PRINTF("DOEPMSK @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + addr = &core_if->dev_if->dev_global_regs->daint;
- + DWC_PRINTF("DAINT @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + addr = &core_if->dev_if->dev_global_regs->daintmsk;
- + DWC_PRINTF("DAINTMSK @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + addr = &core_if->dev_if->dev_global_regs->dtknqr1;
- + DWC_PRINTF("DTKNQR1 @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + if (core_if->hwcfg2.b.dev_token_q_depth > 6) {
- + addr = &core_if->dev_if->dev_global_regs->dtknqr2;
- + DWC_PRINTF("DTKNQR2 @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- + }
- +
- + addr = &core_if->dev_if->dev_global_regs->dvbusdis;
- + DWC_PRINTF("DVBUSID @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- +
- + addr = &core_if->dev_if->dev_global_regs->dvbuspulse;
- + DWC_PRINTF("DVBUSPULSE @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- +
- + addr = &core_if->dev_if->dev_global_regs->dtknqr3_dthrctl;
- + DWC_PRINTF("DTKNQR3_DTHRCTL @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- +
- + if (core_if->hwcfg2.b.dev_token_q_depth > 22) {
- + addr = &core_if->dev_if->dev_global_regs->dtknqr4_fifoemptymsk;
- + DWC_PRINTF("DTKNQR4 @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- + }
- +
- + addr = &core_if->dev_if->dev_global_regs->dtknqr4_fifoemptymsk;
- + DWC_PRINTF("FIFOEMPMSK @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- +
- + if (core_if->hwcfg2.b.multi_proc_int) {
- +
- + addr = &core_if->dev_if->dev_global_regs->deachint;
- + DWC_PRINTF("DEACHINT @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- + addr = &core_if->dev_if->dev_global_regs->deachintmsk;
- + DWC_PRINTF("DEACHINTMSK @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- +
- + for (i = 0; i <= core_if->dev_if->num_in_eps; i++) {
- + addr =
- + &core_if->dev_if->
- + dev_global_regs->diepeachintmsk[i];
- + DWC_PRINTF("DIEPEACHINTMSK[%d] @0x%08lX : 0x%08X\n",
- + i, (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + }
- +
- + for (i = 0; i <= core_if->dev_if->num_out_eps; i++) {
- + addr =
- + &core_if->dev_if->
- + dev_global_regs->doepeachintmsk[i];
- + DWC_PRINTF("DOEPEACHINTMSK[%d] @0x%08lX : 0x%08X\n",
- + i, (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + }
- + }
- +
- + for (i = 0; i <= core_if->dev_if->num_in_eps; i++) {
- + DWC_PRINTF("Device IN EP %d Registers\n", i);
- + addr = &core_if->dev_if->in_ep_regs[i]->diepctl;
- + DWC_PRINTF("DIEPCTL @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- + addr = &core_if->dev_if->in_ep_regs[i]->diepint;
- + DWC_PRINTF("DIEPINT @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- + addr = &core_if->dev_if->in_ep_regs[i]->dieptsiz;
- + DWC_PRINTF("DIETSIZ @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- + addr = &core_if->dev_if->in_ep_regs[i]->diepdma;
- + DWC_PRINTF("DIEPDMA @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- + addr = &core_if->dev_if->in_ep_regs[i]->dtxfsts;
- + DWC_PRINTF("DTXFSTS @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- + addr = &core_if->dev_if->in_ep_regs[i]->diepdmab;
- + DWC_PRINTF("DIEPDMAB @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, 0 /*DWC_READ_REG32(addr) */ );
- + }
- +
- + for (i = 0; i <= core_if->dev_if->num_out_eps; i++) {
- + DWC_PRINTF("Device OUT EP %d Registers\n", i);
- + addr = &core_if->dev_if->out_ep_regs[i]->doepctl;
- + DWC_PRINTF("DOEPCTL @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- + addr = &core_if->dev_if->out_ep_regs[i]->doepint;
- + DWC_PRINTF("DOEPINT @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- + addr = &core_if->dev_if->out_ep_regs[i]->doeptsiz;
- + DWC_PRINTF("DOETSIZ @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- + addr = &core_if->dev_if->out_ep_regs[i]->doepdma;
- + DWC_PRINTF("DOEPDMA @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- + if (core_if->dma_enable) { /* Don't access this register in SLAVE mode */
- + addr = &core_if->dev_if->out_ep_regs[i]->doepdmab;
- + DWC_PRINTF("DOEPDMAB @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- + }
- +
- + }
- +}
- +
- +/**
- + * This functions reads the SPRAM and prints its content
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + */
- +void dwc_otg_dump_spram(dwc_otg_core_if_t * core_if)
- +{
- + volatile uint8_t *addr, *start_addr, *end_addr;
- +
- + DWC_PRINTF("SPRAM Data:\n");
- + start_addr = (void *)core_if->core_global_regs;
- + DWC_PRINTF("Base Address: 0x%8lX\n", (unsigned long)start_addr);
- + start_addr += 0x00028000;
- + end_addr = (void *)core_if->core_global_regs;
- + end_addr += 0x000280e0;
- +
- + for (addr = start_addr; addr < end_addr; addr += 16) {
- + DWC_PRINTF
- + ("0x%8lX:\t%2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X\n",
- + (unsigned long)addr, addr[0], addr[1], addr[2], addr[3],
- + addr[4], addr[5], addr[6], addr[7], addr[8], addr[9],
- + addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]
- + );
- + }
- +
- + return;
- +}
- +
- +/**
- + * This function reads the host registers and prints them
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + */
- +void dwc_otg_dump_host_registers(dwc_otg_core_if_t * core_if)
- +{
- + int i;
- + volatile uint32_t *addr;
- +
- + DWC_PRINTF("Host Global Registers\n");
- + addr = &core_if->host_if->host_global_regs->hcfg;
- + DWC_PRINTF("HCFG @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- + addr = &core_if->host_if->host_global_regs->hfir;
- + DWC_PRINTF("HFIR @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- + addr = &core_if->host_if->host_global_regs->hfnum;
- + DWC_PRINTF("HFNUM @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + addr = &core_if->host_if->host_global_regs->hptxsts;
- + DWC_PRINTF("HPTXSTS @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + addr = &core_if->host_if->host_global_regs->haint;
- + DWC_PRINTF("HAINT @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + addr = &core_if->host_if->host_global_regs->haintmsk;
- + DWC_PRINTF("HAINTMSK @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + if (core_if->dma_desc_enable) {
- + addr = &core_if->host_if->host_global_regs->hflbaddr;
- + DWC_PRINTF("HFLBADDR @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- + }
- +
- + addr = core_if->host_if->hprt0;
- + DWC_PRINTF("HPRT0 @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- +
- + for (i = 0; i < core_if->core_params->host_channels; i++) {
- + DWC_PRINTF("Host Channel %d Specific Registers\n", i);
- + addr = &core_if->host_if->hc_regs[i]->hcchar;
- + DWC_PRINTF("HCCHAR @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- + addr = &core_if->host_if->hc_regs[i]->hcsplt;
- + DWC_PRINTF("HCSPLT @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- + addr = &core_if->host_if->hc_regs[i]->hcint;
- + DWC_PRINTF("HCINT @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- + addr = &core_if->host_if->hc_regs[i]->hcintmsk;
- + DWC_PRINTF("HCINTMSK @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- + addr = &core_if->host_if->hc_regs[i]->hctsiz;
- + DWC_PRINTF("HCTSIZ @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- + addr = &core_if->host_if->hc_regs[i]->hcdma;
- + DWC_PRINTF("HCDMA @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- + if (core_if->dma_desc_enable) {
- + addr = &core_if->host_if->hc_regs[i]->hcdmab;
- + DWC_PRINTF("HCDMAB @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- + }
- +
- + }
- + return;
- +}
- +
- +/**
- + * This function reads the core global registers and prints them
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + */
- +void dwc_otg_dump_global_registers(dwc_otg_core_if_t * core_if)
- +{
- + int i, ep_num;
- + volatile uint32_t *addr;
- + char *txfsiz;
- +
- + DWC_PRINTF("Core Global Registers\n");
- + addr = &core_if->core_global_regs->gotgctl;
- + DWC_PRINTF("GOTGCTL @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + addr = &core_if->core_global_regs->gotgint;
- + DWC_PRINTF("GOTGINT @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + addr = &core_if->core_global_regs->gahbcfg;
- + DWC_PRINTF("GAHBCFG @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + addr = &core_if->core_global_regs->gusbcfg;
- + DWC_PRINTF("GUSBCFG @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + addr = &core_if->core_global_regs->grstctl;
- + DWC_PRINTF("GRSTCTL @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + addr = &core_if->core_global_regs->gintsts;
- + DWC_PRINTF("GINTSTS @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + addr = &core_if->core_global_regs->gintmsk;
- + DWC_PRINTF("GINTMSK @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + addr = &core_if->core_global_regs->grxstsr;
- + DWC_PRINTF("GRXSTSR @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + addr = &core_if->core_global_regs->grxfsiz;
- + DWC_PRINTF("GRXFSIZ @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + addr = &core_if->core_global_regs->gnptxfsiz;
- + DWC_PRINTF("GNPTXFSIZ @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + addr = &core_if->core_global_regs->gnptxsts;
- + DWC_PRINTF("GNPTXSTS @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + addr = &core_if->core_global_regs->gi2cctl;
- + DWC_PRINTF("GI2CCTL @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + addr = &core_if->core_global_regs->gpvndctl;
- + DWC_PRINTF("GPVNDCTL @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + addr = &core_if->core_global_regs->ggpio;
- + DWC_PRINTF("GGPIO @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + addr = &core_if->core_global_regs->guid;
- + DWC_PRINTF("GUID @0x%08lX : 0x%08X\n",
- + (unsigned long)addr, DWC_READ_REG32(addr));
- + addr = &core_if->core_global_regs->gsnpsid;
- + DWC_PRINTF("GSNPSID @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + addr = &core_if->core_global_regs->ghwcfg1;
- + DWC_PRINTF("GHWCFG1 @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + addr = &core_if->core_global_regs->ghwcfg2;
- + DWC_PRINTF("GHWCFG2 @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + addr = &core_if->core_global_regs->ghwcfg3;
- + DWC_PRINTF("GHWCFG3 @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + addr = &core_if->core_global_regs->ghwcfg4;
- + DWC_PRINTF("GHWCFG4 @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + addr = &core_if->core_global_regs->glpmcfg;
- + DWC_PRINTF("GLPMCFG @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + addr = &core_if->core_global_regs->gpwrdn;
- + DWC_PRINTF("GPWRDN @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + addr = &core_if->core_global_regs->gdfifocfg;
- + DWC_PRINTF("GDFIFOCFG @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- + addr = &core_if->core_global_regs->adpctl;
- + DWC_PRINTF("ADPCTL @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + dwc_otg_adp_read_reg(core_if));
- + addr = &core_if->core_global_regs->hptxfsiz;
- + DWC_PRINTF("HPTXFSIZ @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- +
- + if (core_if->en_multiple_tx_fifo == 0) {
- + ep_num = core_if->hwcfg4.b.num_dev_perio_in_ep;
- + txfsiz = "DPTXFSIZ";
- + } else {
- + ep_num = core_if->hwcfg4.b.num_in_eps;
- + txfsiz = "DIENPTXF";
- + }
- + for (i = 0; i < ep_num; i++) {
- + addr = &core_if->core_global_regs->dtxfsiz[i];
- + DWC_PRINTF("%s[%d] @0x%08lX : 0x%08X\n", txfsiz, i + 1,
- + (unsigned long)addr, DWC_READ_REG32(addr));
- + }
- + addr = core_if->pcgcctl;
- + DWC_PRINTF("PCGCCTL @0x%08lX : 0x%08X\n", (unsigned long)addr,
- + DWC_READ_REG32(addr));
- +}
- +
- +/**
- + * Flush a Tx FIFO.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param num Tx FIFO to flush.
- + */
- +void dwc_otg_flush_tx_fifo(dwc_otg_core_if_t * core_if, const int num)
- +{
- + dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
- + volatile grstctl_t greset = {.d32 = 0 };
- + int count = 0;
- +
- + DWC_DEBUGPL((DBG_CIL | DBG_PCDV), "Flush Tx FIFO %d\n", num);
- +
- + greset.b.txfflsh = 1;
- + greset.b.txfnum = num;
- + DWC_WRITE_REG32(&global_regs->grstctl, greset.d32);
- +
- + do {
- + greset.d32 = DWC_READ_REG32(&global_regs->grstctl);
- + if (++count > 10000) {
- + DWC_WARN("%s() HANG! GRSTCTL=%0x GNPTXSTS=0x%08x\n",
- + __func__, greset.d32,
- + DWC_READ_REG32(&global_regs->gnptxsts));
- + break;
- + }
- + dwc_udelay(1);
- + } while (greset.b.txfflsh == 1);
- +
- + /* Wait for 3 PHY Clocks */
- + dwc_udelay(1);
- +}
- +
- +/**
- + * Flush Rx FIFO.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + */
- +void dwc_otg_flush_rx_fifo(dwc_otg_core_if_t * core_if)
- +{
- + dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
- + volatile grstctl_t greset = {.d32 = 0 };
- + int count = 0;
- +
- + DWC_DEBUGPL((DBG_CIL | DBG_PCDV), "%s\n", __func__);
- + /*
- + *
- + */
- + greset.b.rxfflsh = 1;
- + DWC_WRITE_REG32(&global_regs->grstctl, greset.d32);
- +
- + do {
- + greset.d32 = DWC_READ_REG32(&global_regs->grstctl);
- + if (++count > 10000) {
- + DWC_WARN("%s() HANG! GRSTCTL=%0x\n", __func__,
- + greset.d32);
- + break;
- + }
- + dwc_udelay(1);
- + } while (greset.b.rxfflsh == 1);
- +
- + /* Wait for 3 PHY Clocks */
- + dwc_udelay(1);
- +}
- +
- +/**
- + * Do core a soft reset of the core. Be careful with this because it
- + * resets all the internal state machines of the core.
- + */
- +void dwc_otg_core_reset(dwc_otg_core_if_t * core_if)
- +{
- + dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
- + volatile grstctl_t greset = {.d32 = 0 };
- + int count = 0;
- +
- + DWC_DEBUGPL(DBG_CILV, "%s\n", __func__);
- + /* Wait for AHB master IDLE state. */
- + do {
- + dwc_udelay(10);
- + greset.d32 = DWC_READ_REG32(&global_regs->grstctl);
- + if (++count > 100000) {
- + DWC_WARN("%s() HANG! AHB Idle GRSTCTL=%0x\n", __func__,
- + greset.d32);
- + return;
- + }
- + }
- + while (greset.b.ahbidle == 0);
- +
- + /* Core Soft Reset */
- + count = 0;
- + greset.b.csftrst = 1;
- + DWC_WRITE_REG32(&global_regs->grstctl, greset.d32);
- + do {
- + greset.d32 = DWC_READ_REG32(&global_regs->grstctl);
- + if (++count > 10000) {
- + DWC_WARN("%s() HANG! Soft Reset GRSTCTL=%0x\n",
- + __func__, greset.d32);
- + break;
- + }
- + dwc_udelay(1);
- + }
- + while (greset.b.csftrst == 1);
- +
- + /* Wait for 3 PHY Clocks */
- + dwc_mdelay(100);
- +}
- +
- +uint8_t dwc_otg_is_device_mode(dwc_otg_core_if_t * _core_if)
- +{
- + return (dwc_otg_mode(_core_if) != DWC_HOST_MODE);
- +}
- +
- +uint8_t dwc_otg_is_host_mode(dwc_otg_core_if_t * _core_if)
- +{
- + return (dwc_otg_mode(_core_if) == DWC_HOST_MODE);
- +}
- +
- +/**
- + * Register HCD callbacks. The callbacks are used to start and stop
- + * the HCD for interrupt processing.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param cb the HCD callback structure.
- + * @param p pointer to be passed to callback function (usb_hcd*).
- + */
- +void dwc_otg_cil_register_hcd_callbacks(dwc_otg_core_if_t * core_if,
- + dwc_otg_cil_callbacks_t * cb, void *p)
- +{
- + core_if->hcd_cb = cb;
- + cb->p = p;
- +}
- +
- +/**
- + * Register PCD callbacks. The callbacks are used to start and stop
- + * the PCD for interrupt processing.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param cb the PCD callback structure.
- + * @param p pointer to be passed to callback function (pcd*).
- + */
- +void dwc_otg_cil_register_pcd_callbacks(dwc_otg_core_if_t * core_if,
- + dwc_otg_cil_callbacks_t * cb, void *p)
- +{
- + core_if->pcd_cb = cb;
- + cb->p = p;
- +}
- +
- +#ifdef DWC_EN_ISOC
- +
- +/**
- + * This function writes isoc data per 1 (micro)frame into tx fifo
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param ep The EP to start the transfer on.
- + *
- + */
- +void write_isoc_frame_data(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
- +{
- + dwc_otg_dev_in_ep_regs_t *ep_regs;
- + dtxfsts_data_t txstatus = {.d32 = 0 };
- + uint32_t len = 0;
- + uint32_t dwords;
- +
- + ep->xfer_len = ep->data_per_frame;
- + ep->xfer_count = 0;
- +
- + ep_regs = core_if->dev_if->in_ep_regs[ep->num];
- +
- + len = ep->xfer_len - ep->xfer_count;
- +
- + if (len > ep->maxpacket) {
- + len = ep->maxpacket;
- + }
- +
- + dwords = (len + 3) / 4;
- +
- + /* While there is space in the queue and space in the FIFO and
- + * More data to tranfer, Write packets to the Tx FIFO */
- + txstatus.d32 =
- + DWC_READ_REG32(&core_if->dev_if->in_ep_regs[ep->num]->dtxfsts);
- + DWC_DEBUGPL(DBG_PCDV, "b4 dtxfsts[%d]=0x%08x\n", ep->num, txstatus.d32);
- +
- + while (txstatus.b.txfspcavail > dwords &&
- + ep->xfer_count < ep->xfer_len && ep->xfer_len != 0) {
- + /* Write the FIFO */
- + dwc_otg_ep_write_packet(core_if, ep, 0);
- +
- + len = ep->xfer_len - ep->xfer_count;
- + if (len > ep->maxpacket) {
- + len = ep->maxpacket;
- + }
- +
- + dwords = (len + 3) / 4;
- + txstatus.d32 =
- + DWC_READ_REG32(&core_if->dev_if->in_ep_regs[ep->num]->
- + dtxfsts);
- + DWC_DEBUGPL(DBG_PCDV, "dtxfsts[%d]=0x%08x\n", ep->num,
- + txstatus.d32);
- + }
- +}
- +
- +/**
- + * This function initializes a descriptor chain for Isochronous transfer
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param ep The EP to start the transfer on.
- + *
- + */
- +void dwc_otg_iso_ep_start_frm_transfer(dwc_otg_core_if_t * core_if,
- + dwc_ep_t * ep)
- +{
- + deptsiz_data_t deptsiz = {.d32 = 0 };
- + depctl_data_t depctl = {.d32 = 0 };
- + dsts_data_t dsts = {.d32 = 0 };
- + volatile uint32_t *addr;
- +
- + if (ep->is_in) {
- + addr = &core_if->dev_if->in_ep_regs[ep->num]->diepctl;
- + } else {
- + addr = &core_if->dev_if->out_ep_regs[ep->num]->doepctl;
- + }
- +
- + ep->xfer_len = ep->data_per_frame;
- + ep->xfer_count = 0;
- + ep->xfer_buff = ep->cur_pkt_addr;
- + ep->dma_addr = ep->cur_pkt_dma_addr;
- +
- + if (ep->is_in) {
- + /* Program the transfer size and packet count
- + * as follows: xfersize = N * maxpacket +
- + * short_packet pktcnt = N + (short_packet
- + * exist ? 1 : 0)
- + */
- + deptsiz.b.xfersize = ep->xfer_len;
- + deptsiz.b.pktcnt =
- + (ep->xfer_len - 1 + ep->maxpacket) / ep->maxpacket;
- + deptsiz.b.mc = deptsiz.b.pktcnt;
- + DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[ep->num]->dieptsiz,
- + deptsiz.d32);
- +
- + /* Write the DMA register */
- + if (core_if->dma_enable) {
- + DWC_WRITE_REG32(&
- + (core_if->dev_if->in_ep_regs[ep->num]->
- + diepdma), (uint32_t) ep->dma_addr);
- + }
- + } else {
- + deptsiz.b.pktcnt =
- + (ep->xfer_len + (ep->maxpacket - 1)) / ep->maxpacket;
- + deptsiz.b.xfersize = deptsiz.b.pktcnt * ep->maxpacket;
- +
- + DWC_WRITE_REG32(&core_if->dev_if->
- + out_ep_regs[ep->num]->doeptsiz, deptsiz.d32);
- +
- + if (core_if->dma_enable) {
- + DWC_WRITE_REG32(&
- + (core_if->dev_if->
- + out_ep_regs[ep->num]->doepdma),
- + (uint32_t) ep->dma_addr);
- + }
- + }
- +
- + /** Enable endpoint, clear nak */
- +
- + depctl.d32 = 0;
- + if (ep->bInterval == 1) {
- + dsts.d32 =
- + DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
- + ep->next_frame = dsts.b.soffn + ep->bInterval;
- +
- + if (ep->next_frame & 0x1) {
- + depctl.b.setd1pid = 1;
- + } else {
- + depctl.b.setd0pid = 1;
- + }
- + } else {
- + ep->next_frame += ep->bInterval;
- +
- + if (ep->next_frame & 0x1) {
- + depctl.b.setd1pid = 1;
- + } else {
- + depctl.b.setd0pid = 1;
- + }
- + }
- + depctl.b.epena = 1;
- + depctl.b.cnak = 1;
- +
- + DWC_MODIFY_REG32(addr, 0, depctl.d32);
- + depctl.d32 = DWC_READ_REG32(addr);
- +
- + if (ep->is_in && core_if->dma_enable == 0) {
- + write_isoc_frame_data(core_if, ep);
- + }
- +
- +}
- +#endif /* DWC_EN_ISOC */
- +
- +static void dwc_otg_set_uninitialized(int32_t * p, int size)
- +{
- + int i;
- + for (i = 0; i < size; i++) {
- + p[i] = -1;
- + }
- +}
- +
- +static int dwc_otg_param_initialized(int32_t val)
- +{
- + return val != -1;
- +}
- +
- +static int dwc_otg_setup_params(dwc_otg_core_if_t * core_if)
- +{
- + int i;
- + core_if->core_params = DWC_ALLOC(sizeof(*core_if->core_params));
- + if (!core_if->core_params) {
- + return -DWC_E_NO_MEMORY;
- + }
- + dwc_otg_set_uninitialized((int32_t *) core_if->core_params,
- + sizeof(*core_if->core_params) /
- + sizeof(int32_t));
- + DWC_PRINTF("Setting default values for core params\n");
- + dwc_otg_set_param_otg_cap(core_if, dwc_param_otg_cap_default);
- + dwc_otg_set_param_dma_enable(core_if, dwc_param_dma_enable_default);
- + dwc_otg_set_param_dma_desc_enable(core_if,
- + dwc_param_dma_desc_enable_default);
- + dwc_otg_set_param_opt(core_if, dwc_param_opt_default);
- + dwc_otg_set_param_dma_burst_size(core_if,
- + dwc_param_dma_burst_size_default);
- + dwc_otg_set_param_host_support_fs_ls_low_power(core_if,
- + dwc_param_host_support_fs_ls_low_power_default);
- + dwc_otg_set_param_enable_dynamic_fifo(core_if,
- + dwc_param_enable_dynamic_fifo_default);
- + dwc_otg_set_param_data_fifo_size(core_if,
- + dwc_param_data_fifo_size_default);
- + dwc_otg_set_param_dev_rx_fifo_size(core_if,
- + dwc_param_dev_rx_fifo_size_default);
- + dwc_otg_set_param_dev_nperio_tx_fifo_size(core_if,
- + dwc_param_dev_nperio_tx_fifo_size_default);
- + dwc_otg_set_param_host_rx_fifo_size(core_if,
- + dwc_param_host_rx_fifo_size_default);
- + dwc_otg_set_param_host_nperio_tx_fifo_size(core_if,
- + dwc_param_host_nperio_tx_fifo_size_default);
- + dwc_otg_set_param_host_perio_tx_fifo_size(core_if,
- + dwc_param_host_perio_tx_fifo_size_default);
- + dwc_otg_set_param_max_transfer_size(core_if,
- + dwc_param_max_transfer_size_default);
- + dwc_otg_set_param_max_packet_count(core_if,
- + dwc_param_max_packet_count_default);
- + dwc_otg_set_param_host_channels(core_if,
- + dwc_param_host_channels_default);
- + dwc_otg_set_param_dev_endpoints(core_if,
- + dwc_param_dev_endpoints_default);
- + dwc_otg_set_param_phy_type(core_if, dwc_param_phy_type_default);
- + dwc_otg_set_param_speed(core_if, dwc_param_speed_default);
- + dwc_otg_set_param_host_ls_low_power_phy_clk(core_if,
- + dwc_param_host_ls_low_power_phy_clk_default);
- + dwc_otg_set_param_phy_ulpi_ddr(core_if, dwc_param_phy_ulpi_ddr_default);
- + dwc_otg_set_param_phy_ulpi_ext_vbus(core_if,
- + dwc_param_phy_ulpi_ext_vbus_default);
- + dwc_otg_set_param_phy_utmi_width(core_if,
- + dwc_param_phy_utmi_width_default);
- + dwc_otg_set_param_ts_dline(core_if, dwc_param_ts_dline_default);
- + dwc_otg_set_param_i2c_enable(core_if, dwc_param_i2c_enable_default);
- + dwc_otg_set_param_ulpi_fs_ls(core_if, dwc_param_ulpi_fs_ls_default);
- + dwc_otg_set_param_en_multiple_tx_fifo(core_if,
- + dwc_param_en_multiple_tx_fifo_default);
- + for (i = 0; i < 15; i++) {
- + dwc_otg_set_param_dev_perio_tx_fifo_size(core_if,
- + dwc_param_dev_perio_tx_fifo_size_default,
- + i);
- + }
- +
- + for (i = 0; i < 15; i++) {
- + dwc_otg_set_param_dev_tx_fifo_size(core_if,
- + dwc_param_dev_tx_fifo_size_default,
- + i);
- + }
- + dwc_otg_set_param_thr_ctl(core_if, dwc_param_thr_ctl_default);
- + dwc_otg_set_param_mpi_enable(core_if, dwc_param_mpi_enable_default);
- + dwc_otg_set_param_pti_enable(core_if, dwc_param_pti_enable_default);
- + dwc_otg_set_param_lpm_enable(core_if, dwc_param_lpm_enable_default);
- + dwc_otg_set_param_ic_usb_cap(core_if, dwc_param_ic_usb_cap_default);
- + dwc_otg_set_param_tx_thr_length(core_if,
- + dwc_param_tx_thr_length_default);
- + dwc_otg_set_param_rx_thr_length(core_if,
- + dwc_param_rx_thr_length_default);
- + dwc_otg_set_param_ahb_thr_ratio(core_if,
- + dwc_param_ahb_thr_ratio_default);
- + dwc_otg_set_param_power_down(core_if, dwc_param_power_down_default);
- + dwc_otg_set_param_reload_ctl(core_if, dwc_param_reload_ctl_default);
- + dwc_otg_set_param_dev_out_nak(core_if, dwc_param_dev_out_nak_default);
- + dwc_otg_set_param_cont_on_bna(core_if, dwc_param_cont_on_bna_default);
- + dwc_otg_set_param_ahb_single(core_if, dwc_param_ahb_single_default);
- + dwc_otg_set_param_otg_ver(core_if, dwc_param_otg_ver_default);
- + dwc_otg_set_param_adp_enable(core_if, dwc_param_adp_enable_default);
- + DWC_PRINTF("Finished setting default values for core params\n");
- +
- + return 0;
- +}
- +
- +uint8_t dwc_otg_is_dma_enable(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->dma_enable;
- +}
- +
- +/* Checks if the parameter is outside of its valid range of values */
- +#define DWC_OTG_PARAM_TEST(_param_, _low_, _high_) \
- + (((_param_) < (_low_)) || \
- + ((_param_) > (_high_)))
- +
- +/* Parameter access functions */
- +int dwc_otg_set_param_otg_cap(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + int valid;
- + int retval = 0;
- + if (DWC_OTG_PARAM_TEST(val, 0, 2)) {
- + DWC_WARN("Wrong value for otg_cap parameter\n");
- + DWC_WARN("otg_cap parameter must be 0,1 or 2\n");
- + retval = -DWC_E_INVALID;
- + goto out;
- + }
- +
- + valid = 1;
- + switch (val) {
- + case DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE:
- + if (core_if->hwcfg2.b.op_mode !=
- + DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG)
- + valid = 0;
- + break;
- + case DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE:
- + if ((core_if->hwcfg2.b.op_mode !=
- + DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG)
- + && (core_if->hwcfg2.b.op_mode !=
- + DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG)
- + && (core_if->hwcfg2.b.op_mode !=
- + DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE)
- + && (core_if->hwcfg2.b.op_mode !=
- + DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST)) {
- + valid = 0;
- + }
- + break;
- + case DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE:
- + /* always valid */
- + break;
- + }
- + if (!valid) {
- + if (dwc_otg_param_initialized(core_if->core_params->otg_cap)) {
- + DWC_ERROR
- + ("%d invalid for otg_cap paremter. Check HW configuration.\n",
- + val);
- + }
- + val =
- + (((core_if->hwcfg2.b.op_mode ==
- + DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG)
- + || (core_if->hwcfg2.b.op_mode ==
- + DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG)
- + || (core_if->hwcfg2.b.op_mode ==
- + DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE)
- + || (core_if->hwcfg2.b.op_mode ==
- + DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST)) ?
- + DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE :
- + DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE);
- + retval = -DWC_E_INVALID;
- + }
- +
- + core_if->core_params->otg_cap = val;
- +out:
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_otg_cap(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->otg_cap;
- +}
- +
- +int dwc_otg_set_param_opt(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
- + DWC_WARN("Wrong value for opt parameter\n");
- + return -DWC_E_INVALID;
- + }
- + core_if->core_params->opt = val;
- + return 0;
- +}
- +
- +int32_t dwc_otg_get_param_opt(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->opt;
- +}
- +
- +int dwc_otg_set_param_dma_enable(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + int retval = 0;
- + if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
- + DWC_WARN("Wrong value for dma enable\n");
- + return -DWC_E_INVALID;
- + }
- +
- + if ((val == 1) && (core_if->hwcfg2.b.architecture == 0)) {
- + if (dwc_otg_param_initialized(core_if->core_params->dma_enable)) {
- + DWC_ERROR
- + ("%d invalid for dma_enable paremter. Check HW configuration.\n",
- + val);
- + }
- + val = 0;
- + retval = -DWC_E_INVALID;
- + }
- +
- + core_if->core_params->dma_enable = val;
- + if (val == 0) {
- + dwc_otg_set_param_dma_desc_enable(core_if, 0);
- + }
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_dma_enable(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->dma_enable;
- +}
- +
- +int dwc_otg_set_param_dma_desc_enable(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + int retval = 0;
- + if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
- + DWC_WARN("Wrong value for dma_enable\n");
- + DWC_WARN("dma_desc_enable must be 0 or 1\n");
- + return -DWC_E_INVALID;
- + }
- +
- + if ((val == 1)
- + && ((dwc_otg_get_param_dma_enable(core_if) == 0)
- + || (core_if->hwcfg4.b.desc_dma == 0))) {
- + if (dwc_otg_param_initialized
- + (core_if->core_params->dma_desc_enable)) {
- + DWC_ERROR
- + ("%d invalid for dma_desc_enable paremter. Check HW configuration.\n",
- + val);
- + }
- + val = 0;
- + retval = -DWC_E_INVALID;
- + }
- + core_if->core_params->dma_desc_enable = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_dma_desc_enable(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->dma_desc_enable;
- +}
- +
- +int dwc_otg_set_param_host_support_fs_ls_low_power(dwc_otg_core_if_t * core_if,
- + int32_t val)
- +{
- + if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
- + DWC_WARN("Wrong value for host_support_fs_low_power\n");
- + DWC_WARN("host_support_fs_low_power must be 0 or 1\n");
- + return -DWC_E_INVALID;
- + }
- + core_if->core_params->host_support_fs_ls_low_power = val;
- + return 0;
- +}
- +
- +int32_t dwc_otg_get_param_host_support_fs_ls_low_power(dwc_otg_core_if_t *
- + core_if)
- +{
- + return core_if->core_params->host_support_fs_ls_low_power;
- +}
- +
- +int dwc_otg_set_param_enable_dynamic_fifo(dwc_otg_core_if_t * core_if,
- + int32_t val)
- +{
- + int retval = 0;
- + if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
- + DWC_WARN("Wrong value for enable_dynamic_fifo\n");
- + DWC_WARN("enable_dynamic_fifo must be 0 or 1\n");
- + return -DWC_E_INVALID;
- + }
- +
- + if ((val == 1) && (core_if->hwcfg2.b.dynamic_fifo == 0)) {
- + if (dwc_otg_param_initialized
- + (core_if->core_params->enable_dynamic_fifo)) {
- + DWC_ERROR
- + ("%d invalid for enable_dynamic_fifo paremter. Check HW configuration.\n",
- + val);
- + }
- + val = 0;
- + retval = -DWC_E_INVALID;
- + }
- + core_if->core_params->enable_dynamic_fifo = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_enable_dynamic_fifo(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->enable_dynamic_fifo;
- +}
- +
- +int dwc_otg_set_param_data_fifo_size(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + int retval = 0;
- + if (DWC_OTG_PARAM_TEST(val, 32, 32768)) {
- + DWC_WARN("Wrong value for data_fifo_size\n");
- + DWC_WARN("data_fifo_size must be 32-32768\n");
- + return -DWC_E_INVALID;
- + }
- +
- + if (val > core_if->hwcfg3.b.dfifo_depth) {
- + if (dwc_otg_param_initialized
- + (core_if->core_params->data_fifo_size)) {
- + DWC_ERROR
- + ("%d invalid for data_fifo_size parameter. Check HW configuration.\n",
- + val);
- + }
- + val = core_if->hwcfg3.b.dfifo_depth;
- + retval = -DWC_E_INVALID;
- + }
- +
- + core_if->core_params->data_fifo_size = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_data_fifo_size(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->data_fifo_size;
- +}
- +
- +int dwc_otg_set_param_dev_rx_fifo_size(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + int retval = 0;
- + if (DWC_OTG_PARAM_TEST(val, 16, 32768)) {
- + DWC_WARN("Wrong value for dev_rx_fifo_size\n");
- + DWC_WARN("dev_rx_fifo_size must be 16-32768\n");
- + return -DWC_E_INVALID;
- + }
- +
- + if (val > DWC_READ_REG32(&core_if->core_global_regs->grxfsiz)) {
- + if (dwc_otg_param_initialized(core_if->core_params->dev_rx_fifo_size)) {
- + DWC_WARN("%d invalid for dev_rx_fifo_size parameter\n", val);
- + }
- + val = DWC_READ_REG32(&core_if->core_global_regs->grxfsiz);
- + retval = -DWC_E_INVALID;
- + }
- +
- + core_if->core_params->dev_rx_fifo_size = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_dev_rx_fifo_size(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->dev_rx_fifo_size;
- +}
- +
- +int dwc_otg_set_param_dev_nperio_tx_fifo_size(dwc_otg_core_if_t * core_if,
- + int32_t val)
- +{
- + int retval = 0;
- +
- + if (DWC_OTG_PARAM_TEST(val, 16, 32768)) {
- + DWC_WARN("Wrong value for dev_nperio_tx_fifo\n");
- + DWC_WARN("dev_nperio_tx_fifo must be 16-32768\n");
- + return -DWC_E_INVALID;
- + }
- +
- + if (val > (DWC_READ_REG32(&core_if->core_global_regs->gnptxfsiz) >> 16)) {
- + if (dwc_otg_param_initialized
- + (core_if->core_params->dev_nperio_tx_fifo_size)) {
- + DWC_ERROR
- + ("%d invalid for dev_nperio_tx_fifo_size. Check HW configuration.\n",
- + val);
- + }
- + val =
- + (DWC_READ_REG32(&core_if->core_global_regs->gnptxfsiz) >>
- + 16);
- + retval = -DWC_E_INVALID;
- + }
- +
- + core_if->core_params->dev_nperio_tx_fifo_size = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_dev_nperio_tx_fifo_size(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->dev_nperio_tx_fifo_size;
- +}
- +
- +int dwc_otg_set_param_host_rx_fifo_size(dwc_otg_core_if_t * core_if,
- + int32_t val)
- +{
- + int retval = 0;
- +
- + if (DWC_OTG_PARAM_TEST(val, 16, 32768)) {
- + DWC_WARN("Wrong value for host_rx_fifo_size\n");
- + DWC_WARN("host_rx_fifo_size must be 16-32768\n");
- + return -DWC_E_INVALID;
- + }
- +
- + if (val > DWC_READ_REG32(&core_if->core_global_regs->grxfsiz)) {
- + if (dwc_otg_param_initialized
- + (core_if->core_params->host_rx_fifo_size)) {
- + DWC_ERROR
- + ("%d invalid for host_rx_fifo_size. Check HW configuration.\n",
- + val);
- + }
- + val = DWC_READ_REG32(&core_if->core_global_regs->grxfsiz);
- + retval = -DWC_E_INVALID;
- + }
- +
- + core_if->core_params->host_rx_fifo_size = val;
- + return retval;
- +
- +}
- +
- +int32_t dwc_otg_get_param_host_rx_fifo_size(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->host_rx_fifo_size;
- +}
- +
- +int dwc_otg_set_param_host_nperio_tx_fifo_size(dwc_otg_core_if_t * core_if,
- + int32_t val)
- +{
- + int retval = 0;
- +
- + if (DWC_OTG_PARAM_TEST(val, 16, 32768)) {
- + DWC_WARN("Wrong value for host_nperio_tx_fifo_size\n");
- + DWC_WARN("host_nperio_tx_fifo_size must be 16-32768\n");
- + return -DWC_E_INVALID;
- + }
- +
- + if (val > (DWC_READ_REG32(&core_if->core_global_regs->gnptxfsiz) >> 16)) {
- + if (dwc_otg_param_initialized
- + (core_if->core_params->host_nperio_tx_fifo_size)) {
- + DWC_ERROR
- + ("%d invalid for host_nperio_tx_fifo_size. Check HW configuration.\n",
- + val);
- + }
- + val =
- + (DWC_READ_REG32(&core_if->core_global_regs->gnptxfsiz) >>
- + 16);
- + retval = -DWC_E_INVALID;
- + }
- +
- + core_if->core_params->host_nperio_tx_fifo_size = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_host_nperio_tx_fifo_size(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->host_nperio_tx_fifo_size;
- +}
- +
- +int dwc_otg_set_param_host_perio_tx_fifo_size(dwc_otg_core_if_t * core_if,
- + int32_t val)
- +{
- + int retval = 0;
- + if (DWC_OTG_PARAM_TEST(val, 16, 32768)) {
- + DWC_WARN("Wrong value for host_perio_tx_fifo_size\n");
- + DWC_WARN("host_perio_tx_fifo_size must be 16-32768\n");
- + return -DWC_E_INVALID;
- + }
- +
- + if (val > ((core_if->hptxfsiz.d32) >> 16)) {
- + if (dwc_otg_param_initialized
- + (core_if->core_params->host_perio_tx_fifo_size)) {
- + DWC_ERROR
- + ("%d invalid for host_perio_tx_fifo_size. Check HW configuration.\n",
- + val);
- + }
- + val = (core_if->hptxfsiz.d32) >> 16;
- + retval = -DWC_E_INVALID;
- + }
- +
- + core_if->core_params->host_perio_tx_fifo_size = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_host_perio_tx_fifo_size(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->host_perio_tx_fifo_size;
- +}
- +
- +int dwc_otg_set_param_max_transfer_size(dwc_otg_core_if_t * core_if,
- + int32_t val)
- +{
- + int retval = 0;
- +
- + if (DWC_OTG_PARAM_TEST(val, 2047, 524288)) {
- + DWC_WARN("Wrong value for max_transfer_size\n");
- + DWC_WARN("max_transfer_size must be 2047-524288\n");
- + return -DWC_E_INVALID;
- + }
- +
- + if (val >= (1 << (core_if->hwcfg3.b.xfer_size_cntr_width + 11))) {
- + if (dwc_otg_param_initialized
- + (core_if->core_params->max_transfer_size)) {
- + DWC_ERROR
- + ("%d invalid for max_transfer_size. Check HW configuration.\n",
- + val);
- + }
- + val =
- + ((1 << (core_if->hwcfg3.b.packet_size_cntr_width + 11)) -
- + 1);
- + retval = -DWC_E_INVALID;
- + }
- +
- + core_if->core_params->max_transfer_size = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_max_transfer_size(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->max_transfer_size;
- +}
- +
- +int dwc_otg_set_param_max_packet_count(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + int retval = 0;
- +
- + if (DWC_OTG_PARAM_TEST(val, 15, 511)) {
- + DWC_WARN("Wrong value for max_packet_count\n");
- + DWC_WARN("max_packet_count must be 15-511\n");
- + return -DWC_E_INVALID;
- + }
- +
- + if (val > (1 << (core_if->hwcfg3.b.packet_size_cntr_width + 4))) {
- + if (dwc_otg_param_initialized
- + (core_if->core_params->max_packet_count)) {
- + DWC_ERROR
- + ("%d invalid for max_packet_count. Check HW configuration.\n",
- + val);
- + }
- + val =
- + ((1 << (core_if->hwcfg3.b.packet_size_cntr_width + 4)) - 1);
- + retval = -DWC_E_INVALID;
- + }
- +
- + core_if->core_params->max_packet_count = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_max_packet_count(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->max_packet_count;
- +}
- +
- +int dwc_otg_set_param_host_channels(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + int retval = 0;
- +
- + if (DWC_OTG_PARAM_TEST(val, 1, 16)) {
- + DWC_WARN("Wrong value for host_channels\n");
- + DWC_WARN("host_channels must be 1-16\n");
- + return -DWC_E_INVALID;
- + }
- +
- + if (val > (core_if->hwcfg2.b.num_host_chan + 1)) {
- + if (dwc_otg_param_initialized
- + (core_if->core_params->host_channels)) {
- + DWC_ERROR
- + ("%d invalid for host_channels. Check HW configurations.\n",
- + val);
- + }
- + val = (core_if->hwcfg2.b.num_host_chan + 1);
- + retval = -DWC_E_INVALID;
- + }
- +
- + core_if->core_params->host_channels = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_host_channels(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->host_channels;
- +}
- +
- +int dwc_otg_set_param_dev_endpoints(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + int retval = 0;
- +
- + if (DWC_OTG_PARAM_TEST(val, 1, 15)) {
- + DWC_WARN("Wrong value for dev_endpoints\n");
- + DWC_WARN("dev_endpoints must be 1-15\n");
- + return -DWC_E_INVALID;
- + }
- +
- + if (val > (core_if->hwcfg2.b.num_dev_ep)) {
- + if (dwc_otg_param_initialized
- + (core_if->core_params->dev_endpoints)) {
- + DWC_ERROR
- + ("%d invalid for dev_endpoints. Check HW configurations.\n",
- + val);
- + }
- + val = core_if->hwcfg2.b.num_dev_ep;
- + retval = -DWC_E_INVALID;
- + }
- +
- + core_if->core_params->dev_endpoints = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_dev_endpoints(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->dev_endpoints;
- +}
- +
- +int dwc_otg_set_param_phy_type(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + int retval = 0;
- + int valid = 0;
- +
- + if (DWC_OTG_PARAM_TEST(val, 0, 2)) {
- + DWC_WARN("Wrong value for phy_type\n");
- + DWC_WARN("phy_type must be 0,1 or 2\n");
- + return -DWC_E_INVALID;
- + }
- +#ifndef NO_FS_PHY_HW_CHECKS
- + if ((val == DWC_PHY_TYPE_PARAM_UTMI) &&
- + ((core_if->hwcfg2.b.hs_phy_type == 1) ||
- + (core_if->hwcfg2.b.hs_phy_type == 3))) {
- + valid = 1;
- + } else if ((val == DWC_PHY_TYPE_PARAM_ULPI) &&
- + ((core_if->hwcfg2.b.hs_phy_type == 2) ||
- + (core_if->hwcfg2.b.hs_phy_type == 3))) {
- + valid = 1;
- + } else if ((val == DWC_PHY_TYPE_PARAM_FS) &&
- + (core_if->hwcfg2.b.fs_phy_type == 1)) {
- + valid = 1;
- + }
- + if (!valid) {
- + if (dwc_otg_param_initialized(core_if->core_params->phy_type)) {
- + DWC_ERROR
- + ("%d invalid for phy_type. Check HW configurations.\n",
- + val);
- + }
- + if (core_if->hwcfg2.b.hs_phy_type) {
- + if ((core_if->hwcfg2.b.hs_phy_type == 3) ||
- + (core_if->hwcfg2.b.hs_phy_type == 1)) {
- + val = DWC_PHY_TYPE_PARAM_UTMI;
- + } else {
- + val = DWC_PHY_TYPE_PARAM_ULPI;
- + }
- + }
- + retval = -DWC_E_INVALID;
- + }
- +#endif
- + core_if->core_params->phy_type = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_phy_type(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->phy_type;
- +}
- +
- +int dwc_otg_set_param_speed(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + int retval = 0;
- + if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
- + DWC_WARN("Wrong value for speed parameter\n");
- + DWC_WARN("max_speed parameter must be 0 or 1\n");
- + return -DWC_E_INVALID;
- + }
- + if ((val == 0)
- + && dwc_otg_get_param_phy_type(core_if) == DWC_PHY_TYPE_PARAM_FS) {
- + if (dwc_otg_param_initialized(core_if->core_params->speed)) {
- + DWC_ERROR
- + ("%d invalid for speed paremter. Check HW configuration.\n",
- + val);
- + }
- + val =
- + (dwc_otg_get_param_phy_type(core_if) ==
- + DWC_PHY_TYPE_PARAM_FS ? 1 : 0);
- + retval = -DWC_E_INVALID;
- + }
- + core_if->core_params->speed = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_speed(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->speed;
- +}
- +
- +int dwc_otg_set_param_host_ls_low_power_phy_clk(dwc_otg_core_if_t * core_if,
- + int32_t val)
- +{
- + int retval = 0;
- +
- + if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
- + DWC_WARN
- + ("Wrong value for host_ls_low_power_phy_clk parameter\n");
- + DWC_WARN("host_ls_low_power_phy_clk must be 0 or 1\n");
- + return -DWC_E_INVALID;
- + }
- +
- + if ((val == DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ)
- + && (dwc_otg_get_param_phy_type(core_if) == DWC_PHY_TYPE_PARAM_FS)) {
- + if (dwc_otg_param_initialized
- + (core_if->core_params->host_ls_low_power_phy_clk)) {
- + DWC_ERROR
- + ("%d invalid for host_ls_low_power_phy_clk. Check HW configuration.\n",
- + val);
- + }
- + val =
- + (dwc_otg_get_param_phy_type(core_if) ==
- + DWC_PHY_TYPE_PARAM_FS) ?
- + DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ :
- + DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ;
- + retval = -DWC_E_INVALID;
- + }
- +
- + core_if->core_params->host_ls_low_power_phy_clk = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_host_ls_low_power_phy_clk(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->host_ls_low_power_phy_clk;
- +}
- +
- +int dwc_otg_set_param_phy_ulpi_ddr(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
- + DWC_WARN("Wrong value for phy_ulpi_ddr\n");
- + DWC_WARN("phy_upli_ddr must be 0 or 1\n");
- + return -DWC_E_INVALID;
- + }
- +
- + core_if->core_params->phy_ulpi_ddr = val;
- + return 0;
- +}
- +
- +int32_t dwc_otg_get_param_phy_ulpi_ddr(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->phy_ulpi_ddr;
- +}
- +
- +int dwc_otg_set_param_phy_ulpi_ext_vbus(dwc_otg_core_if_t * core_if,
- + int32_t val)
- +{
- + if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
- + DWC_WARN("Wrong valaue for phy_ulpi_ext_vbus\n");
- + DWC_WARN("phy_ulpi_ext_vbus must be 0 or 1\n");
- + return -DWC_E_INVALID;
- + }
- +
- + core_if->core_params->phy_ulpi_ext_vbus = val;
- + return 0;
- +}
- +
- +int32_t dwc_otg_get_param_phy_ulpi_ext_vbus(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->phy_ulpi_ext_vbus;
- +}
- +
- +int dwc_otg_set_param_phy_utmi_width(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + if (DWC_OTG_PARAM_TEST(val, 8, 8) && DWC_OTG_PARAM_TEST(val, 16, 16)) {
- + DWC_WARN("Wrong valaue for phy_utmi_width\n");
- + DWC_WARN("phy_utmi_width must be 8 or 16\n");
- + return -DWC_E_INVALID;
- + }
- +
- + core_if->core_params->phy_utmi_width = val;
- + return 0;
- +}
- +
- +int32_t dwc_otg_get_param_phy_utmi_width(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->phy_utmi_width;
- +}
- +
- +int dwc_otg_set_param_ulpi_fs_ls(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
- + DWC_WARN("Wrong valaue for ulpi_fs_ls\n");
- + DWC_WARN("ulpi_fs_ls must be 0 or 1\n");
- + return -DWC_E_INVALID;
- + }
- +
- + core_if->core_params->ulpi_fs_ls = val;
- + return 0;
- +}
- +
- +int32_t dwc_otg_get_param_ulpi_fs_ls(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->ulpi_fs_ls;
- +}
- +
- +int dwc_otg_set_param_ts_dline(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
- + DWC_WARN("Wrong valaue for ts_dline\n");
- + DWC_WARN("ts_dline must be 0 or 1\n");
- + return -DWC_E_INVALID;
- + }
- +
- + core_if->core_params->ts_dline = val;
- + return 0;
- +}
- +
- +int32_t dwc_otg_get_param_ts_dline(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->ts_dline;
- +}
- +
- +int dwc_otg_set_param_i2c_enable(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + int retval = 0;
- + if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
- + DWC_WARN("Wrong valaue for i2c_enable\n");
- + DWC_WARN("i2c_enable must be 0 or 1\n");
- + return -DWC_E_INVALID;
- + }
- +#ifndef NO_FS_PHY_HW_CHECK
- + if (val == 1 && core_if->hwcfg3.b.i2c == 0) {
- + if (dwc_otg_param_initialized(core_if->core_params->i2c_enable)) {
- + DWC_ERROR
- + ("%d invalid for i2c_enable. Check HW configuration.\n",
- + val);
- + }
- + val = 0;
- + retval = -DWC_E_INVALID;
- + }
- +#endif
- +
- + core_if->core_params->i2c_enable = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_i2c_enable(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->i2c_enable;
- +}
- +
- +int dwc_otg_set_param_dev_perio_tx_fifo_size(dwc_otg_core_if_t * core_if,
- + int32_t val, int fifo_num)
- +{
- + int retval = 0;
- +
- + if (DWC_OTG_PARAM_TEST(val, 4, 768)) {
- + DWC_WARN("Wrong value for dev_perio_tx_fifo_size\n");
- + DWC_WARN("dev_perio_tx_fifo_size must be 4-768\n");
- + return -DWC_E_INVALID;
- + }
- +
- + if (val >
- + (DWC_READ_REG32(&core_if->core_global_regs->dtxfsiz[fifo_num]))) {
- + if (dwc_otg_param_initialized
- + (core_if->core_params->dev_perio_tx_fifo_size[fifo_num])) {
- + DWC_ERROR
- + ("`%d' invalid for parameter `dev_perio_fifo_size_%d'. Check HW configuration.\n",
- + val, fifo_num);
- + }
- + val = (DWC_READ_REG32(&core_if->core_global_regs->dtxfsiz[fifo_num]));
- + retval = -DWC_E_INVALID;
- + }
- +
- + core_if->core_params->dev_perio_tx_fifo_size[fifo_num] = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_dev_perio_tx_fifo_size(dwc_otg_core_if_t * core_if,
- + int fifo_num)
- +{
- + return core_if->core_params->dev_perio_tx_fifo_size[fifo_num];
- +}
- +
- +int dwc_otg_set_param_en_multiple_tx_fifo(dwc_otg_core_if_t * core_if,
- + int32_t val)
- +{
- + int retval = 0;
- + if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
- + DWC_WARN("Wrong valaue for en_multiple_tx_fifo,\n");
- + DWC_WARN("en_multiple_tx_fifo must be 0 or 1\n");
- + return -DWC_E_INVALID;
- + }
- +
- + if (val == 1 && core_if->hwcfg4.b.ded_fifo_en == 0) {
- + if (dwc_otg_param_initialized
- + (core_if->core_params->en_multiple_tx_fifo)) {
- + DWC_ERROR
- + ("%d invalid for parameter en_multiple_tx_fifo. Check HW configuration.\n",
- + val);
- + }
- + val = 0;
- + retval = -DWC_E_INVALID;
- + }
- +
- + core_if->core_params->en_multiple_tx_fifo = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_en_multiple_tx_fifo(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->en_multiple_tx_fifo;
- +}
- +
- +int dwc_otg_set_param_dev_tx_fifo_size(dwc_otg_core_if_t * core_if, int32_t val,
- + int fifo_num)
- +{
- + int retval = 0;
- +
- + if (DWC_OTG_PARAM_TEST(val, 4, 768)) {
- + DWC_WARN("Wrong value for dev_tx_fifo_size\n");
- + DWC_WARN("dev_tx_fifo_size must be 4-768\n");
- + return -DWC_E_INVALID;
- + }
- +
- + if (val >
- + (DWC_READ_REG32(&core_if->core_global_regs->dtxfsiz[fifo_num]))) {
- + if (dwc_otg_param_initialized
- + (core_if->core_params->dev_tx_fifo_size[fifo_num])) {
- + DWC_ERROR
- + ("`%d' invalid for parameter `dev_tx_fifo_size_%d'. Check HW configuration.\n",
- + val, fifo_num);
- + }
- + val = (DWC_READ_REG32(&core_if->core_global_regs->dtxfsiz[fifo_num]));
- + retval = -DWC_E_INVALID;
- + }
- +
- + core_if->core_params->dev_tx_fifo_size[fifo_num] = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_dev_tx_fifo_size(dwc_otg_core_if_t * core_if,
- + int fifo_num)
- +{
- + return core_if->core_params->dev_tx_fifo_size[fifo_num];
- +}
- +
- +int dwc_otg_set_param_thr_ctl(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + int retval = 0;
- +
- + if (DWC_OTG_PARAM_TEST(val, 0, 7)) {
- + DWC_WARN("Wrong value for thr_ctl\n");
- + DWC_WARN("thr_ctl must be 0-7\n");
- + return -DWC_E_INVALID;
- + }
- +
- + if ((val != 0) &&
- + (!dwc_otg_get_param_dma_enable(core_if) ||
- + !core_if->hwcfg4.b.ded_fifo_en)) {
- + if (dwc_otg_param_initialized(core_if->core_params->thr_ctl)) {
- + DWC_ERROR
- + ("%d invalid for parameter thr_ctl. Check HW configuration.\n",
- + val);
- + }
- + val = 0;
- + retval = -DWC_E_INVALID;
- + }
- +
- + core_if->core_params->thr_ctl = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_thr_ctl(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->thr_ctl;
- +}
- +
- +int dwc_otg_set_param_lpm_enable(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + int retval = 0;
- +
- + if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
- + DWC_WARN("Wrong value for lpm_enable\n");
- + DWC_WARN("lpm_enable must be 0 or 1\n");
- + return -DWC_E_INVALID;
- + }
- +
- + if (val && !core_if->hwcfg3.b.otg_lpm_en) {
- + if (dwc_otg_param_initialized(core_if->core_params->lpm_enable)) {
- + DWC_ERROR
- + ("%d invalid for parameter lpm_enable. Check HW configuration.\n",
- + val);
- + }
- + val = 0;
- + retval = -DWC_E_INVALID;
- + }
- +
- + core_if->core_params->lpm_enable = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_lpm_enable(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->lpm_enable;
- +}
- +
- +int dwc_otg_set_param_tx_thr_length(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + if (DWC_OTG_PARAM_TEST(val, 8, 128)) {
- + DWC_WARN("Wrong valaue for tx_thr_length\n");
- + DWC_WARN("tx_thr_length must be 8 - 128\n");
- + return -DWC_E_INVALID;
- + }
- +
- + core_if->core_params->tx_thr_length = val;
- + return 0;
- +}
- +
- +int32_t dwc_otg_get_param_tx_thr_length(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->tx_thr_length;
- +}
- +
- +int dwc_otg_set_param_rx_thr_length(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + if (DWC_OTG_PARAM_TEST(val, 8, 128)) {
- + DWC_WARN("Wrong valaue for rx_thr_length\n");
- + DWC_WARN("rx_thr_length must be 8 - 128\n");
- + return -DWC_E_INVALID;
- + }
- +
- + core_if->core_params->rx_thr_length = val;
- + return 0;
- +}
- +
- +int32_t dwc_otg_get_param_rx_thr_length(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->rx_thr_length;
- +}
- +
- +int dwc_otg_set_param_dma_burst_size(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + if (DWC_OTG_PARAM_TEST(val, 1, 1) &&
- + DWC_OTG_PARAM_TEST(val, 4, 4) &&
- + DWC_OTG_PARAM_TEST(val, 8, 8) &&
- + DWC_OTG_PARAM_TEST(val, 16, 16) &&
- + DWC_OTG_PARAM_TEST(val, 32, 32) &&
- + DWC_OTG_PARAM_TEST(val, 64, 64) &&
- + DWC_OTG_PARAM_TEST(val, 128, 128) &&
- + DWC_OTG_PARAM_TEST(val, 256, 256)) {
- + DWC_WARN("`%d' invalid for parameter `dma_burst_size'\n", val);
- + return -DWC_E_INVALID;
- + }
- + core_if->core_params->dma_burst_size = val;
- + return 0;
- +}
- +
- +int32_t dwc_otg_get_param_dma_burst_size(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->dma_burst_size;
- +}
- +
- +int dwc_otg_set_param_pti_enable(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + int retval = 0;
- + if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
- + DWC_WARN("`%d' invalid for parameter `pti_enable'\n", val);
- + return -DWC_E_INVALID;
- + }
- + if (val && (core_if->snpsid < OTG_CORE_REV_2_72a)) {
- + if (dwc_otg_param_initialized(core_if->core_params->pti_enable)) {
- + DWC_ERROR
- + ("%d invalid for parameter pti_enable. Check HW configuration.\n",
- + val);
- + }
- + retval = -DWC_E_INVALID;
- + val = 0;
- + }
- + core_if->core_params->pti_enable = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_pti_enable(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->pti_enable;
- +}
- +
- +int dwc_otg_set_param_mpi_enable(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + int retval = 0;
- + if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
- + DWC_WARN("`%d' invalid for parameter `mpi_enable'\n", val);
- + return -DWC_E_INVALID;
- + }
- + if (val && (core_if->hwcfg2.b.multi_proc_int == 0)) {
- + if (dwc_otg_param_initialized(core_if->core_params->mpi_enable)) {
- + DWC_ERROR
- + ("%d invalid for parameter mpi_enable. Check HW configuration.\n",
- + val);
- + }
- + retval = -DWC_E_INVALID;
- + val = 0;
- + }
- + core_if->core_params->mpi_enable = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_mpi_enable(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->mpi_enable;
- +}
- +
- +int dwc_otg_set_param_adp_enable(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + int retval = 0;
- + if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
- + DWC_WARN("`%d' invalid for parameter `adp_enable'\n", val);
- + return -DWC_E_INVALID;
- + }
- + if (val && (core_if->hwcfg3.b.adp_supp == 0)) {
- + if (dwc_otg_param_initialized
- + (core_if->core_params->adp_supp_enable)) {
- + DWC_ERROR
- + ("%d invalid for parameter adp_enable. Check HW configuration.\n",
- + val);
- + }
- + retval = -DWC_E_INVALID;
- + val = 0;
- + }
- + core_if->core_params->adp_supp_enable = val;
- + /*Set OTG version 2.0 in case of enabling ADP*/
- + if (val)
- + dwc_otg_set_param_otg_ver(core_if, 1);
- +
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_adp_enable(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->adp_supp_enable;
- +}
- +
- +int dwc_otg_set_param_ic_usb_cap(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + int retval = 0;
- + if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
- + DWC_WARN("`%d' invalid for parameter `ic_usb_cap'\n", val);
- + DWC_WARN("ic_usb_cap must be 0 or 1\n");
- + return -DWC_E_INVALID;
- + }
- +
- + if (val && (core_if->hwcfg2.b.otg_enable_ic_usb == 0)) {
- + if (dwc_otg_param_initialized(core_if->core_params->ic_usb_cap)) {
- + DWC_ERROR
- + ("%d invalid for parameter ic_usb_cap. Check HW configuration.\n",
- + val);
- + }
- + retval = -DWC_E_INVALID;
- + val = 0;
- + }
- + core_if->core_params->ic_usb_cap = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_ic_usb_cap(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->ic_usb_cap;
- +}
- +
- +int dwc_otg_set_param_ahb_thr_ratio(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + int retval = 0;
- + int valid = 1;
- +
- + if (DWC_OTG_PARAM_TEST(val, 0, 3)) {
- + DWC_WARN("`%d' invalid for parameter `ahb_thr_ratio'\n", val);
- + DWC_WARN("ahb_thr_ratio must be 0 - 3\n");
- + return -DWC_E_INVALID;
- + }
- +
- + if (val
- + && (core_if->snpsid < OTG_CORE_REV_2_81a
- + || !dwc_otg_get_param_thr_ctl(core_if))) {
- + valid = 0;
- + } else if (val
- + && ((dwc_otg_get_param_tx_thr_length(core_if) / (1 << val)) <
- + 4)) {
- + valid = 0;
- + }
- + if (valid == 0) {
- + if (dwc_otg_param_initialized
- + (core_if->core_params->ahb_thr_ratio)) {
- + DWC_ERROR
- + ("%d invalid for parameter ahb_thr_ratio. Check HW configuration.\n",
- + val);
- + }
- + retval = -DWC_E_INVALID;
- + val = 0;
- + }
- +
- + core_if->core_params->ahb_thr_ratio = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_ahb_thr_ratio(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->ahb_thr_ratio;
- +}
- +
- +int dwc_otg_set_param_power_down(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + int retval = 0;
- + int valid = 1;
- + hwcfg4_data_t hwcfg4 = {.d32 = 0 };
- + hwcfg4.d32 = DWC_READ_REG32(&core_if->core_global_regs->ghwcfg4);
- +
- + if (DWC_OTG_PARAM_TEST(val, 0, 3)) {
- + DWC_WARN("`%d' invalid for parameter `power_down'\n", val);
- + DWC_WARN("power_down must be 0 - 2\n");
- + return -DWC_E_INVALID;
- + }
- +
- + if ((val == 2) && (core_if->snpsid < OTG_CORE_REV_2_91a)) {
- + valid = 0;
- + }
- + if ((val == 3)
- + && ((core_if->snpsid < OTG_CORE_REV_3_00a)
- + || (hwcfg4.b.xhiber == 0))) {
- + valid = 0;
- + }
- + if (valid == 0) {
- + if (dwc_otg_param_initialized(core_if->core_params->power_down)) {
- + DWC_ERROR
- + ("%d invalid for parameter power_down. Check HW configuration.\n",
- + val);
- + }
- + retval = -DWC_E_INVALID;
- + val = 0;
- + }
- + core_if->core_params->power_down = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_power_down(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->power_down;
- +}
- +
- +int dwc_otg_set_param_reload_ctl(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + int retval = 0;
- + int valid = 1;
- +
- + if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
- + DWC_WARN("`%d' invalid for parameter `reload_ctl'\n", val);
- + DWC_WARN("reload_ctl must be 0 or 1\n");
- + return -DWC_E_INVALID;
- + }
- +
- + if ((val == 1) && (core_if->snpsid < OTG_CORE_REV_2_92a)) {
- + valid = 0;
- + }
- + if (valid == 0) {
- + if (dwc_otg_param_initialized(core_if->core_params->reload_ctl)) {
- + DWC_ERROR("%d invalid for parameter reload_ctl."
- + "Check HW configuration.\n", val);
- + }
- + retval = -DWC_E_INVALID;
- + val = 0;
- + }
- + core_if->core_params->reload_ctl = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_reload_ctl(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->reload_ctl;
- +}
- +
- +int dwc_otg_set_param_dev_out_nak(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + int retval = 0;
- + int valid = 1;
- +
- + if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
- + DWC_WARN("`%d' invalid for parameter `dev_out_nak'\n", val);
- + DWC_WARN("dev_out_nak must be 0 or 1\n");
- + return -DWC_E_INVALID;
- + }
- +
- + if ((val == 1) && ((core_if->snpsid < OTG_CORE_REV_2_93a) ||
- + !(core_if->core_params->dma_desc_enable))) {
- + valid = 0;
- + }
- + if (valid == 0) {
- + if (dwc_otg_param_initialized(core_if->core_params->dev_out_nak)) {
- + DWC_ERROR("%d invalid for parameter dev_out_nak."
- + "Check HW configuration.\n", val);
- + }
- + retval = -DWC_E_INVALID;
- + val = 0;
- + }
- + core_if->core_params->dev_out_nak = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_dev_out_nak(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->dev_out_nak;
- +}
- +
- +int dwc_otg_set_param_cont_on_bna(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + int retval = 0;
- + int valid = 1;
- +
- + if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
- + DWC_WARN("`%d' invalid for parameter `cont_on_bna'\n", val);
- + DWC_WARN("cont_on_bna must be 0 or 1\n");
- + return -DWC_E_INVALID;
- + }
- +
- + if ((val == 1) && ((core_if->snpsid < OTG_CORE_REV_2_94a) ||
- + !(core_if->core_params->dma_desc_enable))) {
- + valid = 0;
- + }
- + if (valid == 0) {
- + if (dwc_otg_param_initialized(core_if->core_params->cont_on_bna)) {
- + DWC_ERROR("%d invalid for parameter cont_on_bna."
- + "Check HW configuration.\n", val);
- + }
- + retval = -DWC_E_INVALID;
- + val = 0;
- + }
- + core_if->core_params->cont_on_bna = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_cont_on_bna(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->cont_on_bna;
- +}
- +
- +int dwc_otg_set_param_ahb_single(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + int retval = 0;
- + int valid = 1;
- +
- + if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
- + DWC_WARN("`%d' invalid for parameter `ahb_single'\n", val);
- + DWC_WARN("ahb_single must be 0 or 1\n");
- + return -DWC_E_INVALID;
- + }
- +
- + if ((val == 1) && (core_if->snpsid < OTG_CORE_REV_2_94a)) {
- + valid = 0;
- + }
- + if (valid == 0) {
- + if (dwc_otg_param_initialized(core_if->core_params->ahb_single)) {
- + DWC_ERROR("%d invalid for parameter ahb_single."
- + "Check HW configuration.\n", val);
- + }
- + retval = -DWC_E_INVALID;
- + val = 0;
- + }
- + core_if->core_params->ahb_single = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_ahb_single(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->ahb_single;
- +}
- +
- +int dwc_otg_set_param_otg_ver(dwc_otg_core_if_t * core_if, int32_t val)
- +{
- + int retval = 0;
- +
- + if (DWC_OTG_PARAM_TEST(val, 0, 1)) {
- + DWC_WARN("`%d' invalid for parameter `otg_ver'\n", val);
- + DWC_WARN
- + ("otg_ver must be 0(for OTG 1.3 support) or 1(for OTG 2.0 support)\n");
- + return -DWC_E_INVALID;
- + }
- +
- + core_if->core_params->otg_ver = val;
- + return retval;
- +}
- +
- +int32_t dwc_otg_get_param_otg_ver(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->core_params->otg_ver;
- +}
- +
- +uint32_t dwc_otg_get_hnpstatus(dwc_otg_core_if_t * core_if)
- +{
- + gotgctl_data_t otgctl;
- + otgctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->gotgctl);
- + return otgctl.b.hstnegscs;
- +}
- +
- +uint32_t dwc_otg_get_srpstatus(dwc_otg_core_if_t * core_if)
- +{
- + gotgctl_data_t otgctl;
- + otgctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->gotgctl);
- + return otgctl.b.sesreqscs;
- +}
- +
- +void dwc_otg_set_hnpreq(dwc_otg_core_if_t * core_if, uint32_t val)
- +{
- + if(core_if->otg_ver == 0) {
- + gotgctl_data_t otgctl;
- + otgctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->gotgctl);
- + otgctl.b.hnpreq = val;
- + DWC_WRITE_REG32(&core_if->core_global_regs->gotgctl, otgctl.d32);
- + } else {
- + core_if->otg_sts = val;
- + }
- +}
- +
- +uint32_t dwc_otg_get_gsnpsid(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->snpsid;
- +}
- +
- +uint32_t dwc_otg_get_mode(dwc_otg_core_if_t * core_if)
- +{
- + gintsts_data_t gintsts;
- + gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
- + return gintsts.b.curmode;
- +}
- +
- +uint32_t dwc_otg_get_hnpcapable(dwc_otg_core_if_t * core_if)
- +{
- + gusbcfg_data_t usbcfg;
- + usbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
- + return usbcfg.b.hnpcap;
- +}
- +
- +void dwc_otg_set_hnpcapable(dwc_otg_core_if_t * core_if, uint32_t val)
- +{
- + gusbcfg_data_t usbcfg;
- + usbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
- + usbcfg.b.hnpcap = val;
- + DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, usbcfg.d32);
- +}
- +
- +uint32_t dwc_otg_get_srpcapable(dwc_otg_core_if_t * core_if)
- +{
- + gusbcfg_data_t usbcfg;
- + usbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
- + return usbcfg.b.srpcap;
- +}
- +
- +void dwc_otg_set_srpcapable(dwc_otg_core_if_t * core_if, uint32_t val)
- +{
- + gusbcfg_data_t usbcfg;
- + usbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
- + usbcfg.b.srpcap = val;
- + DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, usbcfg.d32);
- +}
- +
- +uint32_t dwc_otg_get_devspeed(dwc_otg_core_if_t * core_if)
- +{
- + dcfg_data_t dcfg;
- + /* originally: dcfg.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg); */
- +
- + dcfg.d32 = -1; //GRAYG
- + DWC_DEBUGPL(DBG_CILV, "%s - core_if(%p)\n", __func__, core_if);
- + if (NULL == core_if)
- + DWC_ERROR("reg request with NULL core_if\n");
- + DWC_DEBUGPL(DBG_CILV, "%s - core_if(%p)->dev_if(%p)\n", __func__,
- + core_if, core_if->dev_if);
- + if (NULL == core_if->dev_if)
- + DWC_ERROR("reg request with NULL dev_if\n");
- + DWC_DEBUGPL(DBG_CILV, "%s - core_if(%p)->dev_if(%p)->"
- + "dev_global_regs(%p)\n", __func__,
- + core_if, core_if->dev_if,
- + core_if->dev_if->dev_global_regs);
- + if (NULL == core_if->dev_if->dev_global_regs)
- + DWC_ERROR("reg request with NULL dev_global_regs\n");
- + else {
- + DWC_DEBUGPL(DBG_CILV, "%s - &core_if(%p)->dev_if(%p)->"
- + "dev_global_regs(%p)->dcfg = %p\n", __func__,
- + core_if, core_if->dev_if,
- + core_if->dev_if->dev_global_regs,
- + &core_if->dev_if->dev_global_regs->dcfg);
- + dcfg.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg);
- + }
- + return dcfg.b.devspd;
- +}
- +
- +void dwc_otg_set_devspeed(dwc_otg_core_if_t * core_if, uint32_t val)
- +{
- + dcfg_data_t dcfg;
- + dcfg.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg);
- + dcfg.b.devspd = val;
- + DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dcfg, dcfg.d32);
- +}
- +
- +uint32_t dwc_otg_get_busconnected(dwc_otg_core_if_t * core_if)
- +{
- + hprt0_data_t hprt0;
- + hprt0.d32 = DWC_READ_REG32(core_if->host_if->hprt0);
- + return hprt0.b.prtconnsts;
- +}
- +
- +uint32_t dwc_otg_get_enumspeed(dwc_otg_core_if_t * core_if)
- +{
- + dsts_data_t dsts;
- + dsts.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
- + return dsts.b.enumspd;
- +}
- +
- +uint32_t dwc_otg_get_prtpower(dwc_otg_core_if_t * core_if)
- +{
- + hprt0_data_t hprt0;
- + hprt0.d32 = DWC_READ_REG32(core_if->host_if->hprt0);
- + return hprt0.b.prtpwr;
- +
- +}
- +
- +uint32_t dwc_otg_get_core_state(dwc_otg_core_if_t * core_if)
- +{
- + return core_if->hibernation_suspend;
- +}
- +
- +void dwc_otg_set_prtpower(dwc_otg_core_if_t * core_if, uint32_t val)
- +{
- + hprt0_data_t hprt0;
- + hprt0.d32 = dwc_otg_read_hprt0(core_if);
- + hprt0.b.prtpwr = val;
- + DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
- +}
- +
- +uint32_t dwc_otg_get_prtsuspend(dwc_otg_core_if_t * core_if)
- +{
- + hprt0_data_t hprt0;
- + hprt0.d32 = DWC_READ_REG32(core_if->host_if->hprt0);
- + return hprt0.b.prtsusp;
- +
- +}
- +
- +void dwc_otg_set_prtsuspend(dwc_otg_core_if_t * core_if, uint32_t val)
- +{
- + hprt0_data_t hprt0;
- + hprt0.d32 = dwc_otg_read_hprt0(core_if);
- + hprt0.b.prtsusp = val;
- + DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
- +}
- +
- +uint32_t dwc_otg_get_fr_interval(dwc_otg_core_if_t * core_if)
- +{
- + hfir_data_t hfir;
- + hfir.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->hfir);
- + return hfir.b.frint;
- +
- +}
- +
- +void dwc_otg_set_fr_interval(dwc_otg_core_if_t * core_if, uint32_t val)
- +{
- + hfir_data_t hfir;
- + uint32_t fram_int;
- + fram_int = calc_frame_interval(core_if);
- + hfir.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->hfir);
- + if (!core_if->core_params->reload_ctl) {
- + DWC_WARN("\nCannot reload HFIR register.HFIR.HFIRRldCtrl bit is"
- + "not set to 1.\nShould load driver with reload_ctl=1"
- + " module parameter\n");
- + return;
- + }
- + switch (fram_int) {
- + case 3750:
- + if ((val < 3350) || (val > 4150)) {
- + DWC_WARN("HFIR interval for HS core and 30 MHz"
- + "clock freq should be from 3350 to 4150\n");
- + return;
- + }
- + break;
- + case 30000:
- + if ((val < 26820) || (val > 33180)) {
- + DWC_WARN("HFIR interval for FS/LS core and 30 MHz"
- + "clock freq should be from 26820 to 33180\n");
- + return;
- + }
- + break;
- + case 6000:
- + if ((val < 5360) || (val > 6640)) {
- + DWC_WARN("HFIR interval for HS core and 48 MHz"
- + "clock freq should be from 5360 to 6640\n");
- + return;
- + }
- + break;
- + case 48000:
- + if ((val < 42912) || (val > 53088)) {
- + DWC_WARN("HFIR interval for FS/LS core and 48 MHz"
- + "clock freq should be from 42912 to 53088\n");
- + return;
- + }
- + break;
- + case 7500:
- + if ((val < 6700) || (val > 8300)) {
- + DWC_WARN("HFIR interval for HS core and 60 MHz"
- + "clock freq should be from 6700 to 8300\n");
- + return;
- + }
- + break;
- + case 60000:
- + if ((val < 53640) || (val > 65536)) {
- + DWC_WARN("HFIR interval for FS/LS core and 60 MHz"
- + "clock freq should be from 53640 to 65536\n");
- + return;
- + }
- + break;
- + default:
- + DWC_WARN("Unknown frame interval\n");
- + return;
- + break;
- +
- + }
- + hfir.b.frint = val;
- + DWC_WRITE_REG32(&core_if->host_if->host_global_regs->hfir, hfir.d32);
- +}
- +
- +uint32_t dwc_otg_get_mode_ch_tim(dwc_otg_core_if_t * core_if)
- +{
- + hcfg_data_t hcfg;
- + hcfg.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->hcfg);
- + return hcfg.b.modechtimen;
- +
- +}
- +
- +void dwc_otg_set_mode_ch_tim(dwc_otg_core_if_t * core_if, uint32_t val)
- +{
- + hcfg_data_t hcfg;
- + hcfg.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->hcfg);
- + hcfg.b.modechtimen = val;
- + DWC_WRITE_REG32(&core_if->host_if->host_global_regs->hcfg, hcfg.d32);
- +}
- +
- +void dwc_otg_set_prtresume(dwc_otg_core_if_t * core_if, uint32_t val)
- +{
- + hprt0_data_t hprt0;
- + hprt0.d32 = dwc_otg_read_hprt0(core_if);
- + hprt0.b.prtres = val;
- + DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
- +}
- +
- +uint32_t dwc_otg_get_remotewakesig(dwc_otg_core_if_t * core_if)
- +{
- + dctl_data_t dctl;
- + dctl.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dctl);
- + return dctl.b.rmtwkupsig;
- +}
- +
- +uint32_t dwc_otg_get_lpm_portsleepstatus(dwc_otg_core_if_t * core_if)
- +{
- + glpmcfg_data_t lpmcfg;
- + lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
- +
- + DWC_ASSERT(!
- + ((core_if->lx_state == DWC_OTG_L1) ^ lpmcfg.b.prt_sleep_sts),
- + "lx_state = %d, lmpcfg.prt_sleep_sts = %d\n",
- + core_if->lx_state, lpmcfg.b.prt_sleep_sts);
- +
- + return lpmcfg.b.prt_sleep_sts;
- +}
- +
- +uint32_t dwc_otg_get_lpm_remotewakeenabled(dwc_otg_core_if_t * core_if)
- +{
- + glpmcfg_data_t lpmcfg;
- + lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
- + return lpmcfg.b.rem_wkup_en;
- +}
- +
- +uint32_t dwc_otg_get_lpmresponse(dwc_otg_core_if_t * core_if)
- +{
- + glpmcfg_data_t lpmcfg;
- + lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
- + return lpmcfg.b.appl_resp;
- +}
- +
- +void dwc_otg_set_lpmresponse(dwc_otg_core_if_t * core_if, uint32_t val)
- +{
- + glpmcfg_data_t lpmcfg;
- + lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
- + lpmcfg.b.appl_resp = val;
- + DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg, lpmcfg.d32);
- +}
- +
- +uint32_t dwc_otg_get_hsic_connect(dwc_otg_core_if_t * core_if)
- +{
- + glpmcfg_data_t lpmcfg;
- + lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
- + return lpmcfg.b.hsic_connect;
- +}
- +
- +void dwc_otg_set_hsic_connect(dwc_otg_core_if_t * core_if, uint32_t val)
- +{
- + glpmcfg_data_t lpmcfg;
- + lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
- + lpmcfg.b.hsic_connect = val;
- + DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg, lpmcfg.d32);
- +}
- +
- +uint32_t dwc_otg_get_inv_sel_hsic(dwc_otg_core_if_t * core_if)
- +{
- + glpmcfg_data_t lpmcfg;
- + lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
- + return lpmcfg.b.inv_sel_hsic;
- +
- +}
- +
- +void dwc_otg_set_inv_sel_hsic(dwc_otg_core_if_t * core_if, uint32_t val)
- +{
- + glpmcfg_data_t lpmcfg;
- + lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
- + lpmcfg.b.inv_sel_hsic = val;
- + DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg, lpmcfg.d32);
- +}
- +
- +uint32_t dwc_otg_get_gotgctl(dwc_otg_core_if_t * core_if)
- +{
- + return DWC_READ_REG32(&core_if->core_global_regs->gotgctl);
- +}
- +
- +void dwc_otg_set_gotgctl(dwc_otg_core_if_t * core_if, uint32_t val)
- +{
- + DWC_WRITE_REG32(&core_if->core_global_regs->gotgctl, val);
- +}
- +
- +uint32_t dwc_otg_get_gusbcfg(dwc_otg_core_if_t * core_if)
- +{
- + return DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
- +}
- +
- +void dwc_otg_set_gusbcfg(dwc_otg_core_if_t * core_if, uint32_t val)
- +{
- + DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, val);
- +}
- +
- +uint32_t dwc_otg_get_grxfsiz(dwc_otg_core_if_t * core_if)
- +{
- + return DWC_READ_REG32(&core_if->core_global_regs->grxfsiz);
- +}
- +
- +void dwc_otg_set_grxfsiz(dwc_otg_core_if_t * core_if, uint32_t val)
- +{
- + DWC_WRITE_REG32(&core_if->core_global_regs->grxfsiz, val);
- +}
- +
- +uint32_t dwc_otg_get_gnptxfsiz(dwc_otg_core_if_t * core_if)
- +{
- + return DWC_READ_REG32(&core_if->core_global_regs->gnptxfsiz);
- +}
- +
- +void dwc_otg_set_gnptxfsiz(dwc_otg_core_if_t * core_if, uint32_t val)
- +{
- + DWC_WRITE_REG32(&core_if->core_global_regs->gnptxfsiz, val);
- +}
- +
- +uint32_t dwc_otg_get_gpvndctl(dwc_otg_core_if_t * core_if)
- +{
- + return DWC_READ_REG32(&core_if->core_global_regs->gpvndctl);
- +}
- +
- +void dwc_otg_set_gpvndctl(dwc_otg_core_if_t * core_if, uint32_t val)
- +{
- + DWC_WRITE_REG32(&core_if->core_global_regs->gpvndctl, val);
- +}
- +
- +uint32_t dwc_otg_get_ggpio(dwc_otg_core_if_t * core_if)
- +{
- + return DWC_READ_REG32(&core_if->core_global_regs->ggpio);
- +}
- +
- +void dwc_otg_set_ggpio(dwc_otg_core_if_t * core_if, uint32_t val)
- +{
- + DWC_WRITE_REG32(&core_if->core_global_regs->ggpio, val);
- +}
- +
- +uint32_t dwc_otg_get_hprt0(dwc_otg_core_if_t * core_if)
- +{
- + return DWC_READ_REG32(core_if->host_if->hprt0);
- +
- +}
- +
- +void dwc_otg_set_hprt0(dwc_otg_core_if_t * core_if, uint32_t val)
- +{
- + DWC_WRITE_REG32(core_if->host_if->hprt0, val);
- +}
- +
- +uint32_t dwc_otg_get_guid(dwc_otg_core_if_t * core_if)
- +{
- + return DWC_READ_REG32(&core_if->core_global_regs->guid);
- +}
- +
- +void dwc_otg_set_guid(dwc_otg_core_if_t * core_if, uint32_t val)
- +{
- + DWC_WRITE_REG32(&core_if->core_global_regs->guid, val);
- +}
- +
- +uint32_t dwc_otg_get_hptxfsiz(dwc_otg_core_if_t * core_if)
- +{
- + return DWC_READ_REG32(&core_if->core_global_regs->hptxfsiz);
- +}
- +
- +uint16_t dwc_otg_get_otg_version(dwc_otg_core_if_t * core_if)
- +{
- + return ((core_if->otg_ver == 1) ? (uint16_t)0x0200 : (uint16_t)0x0103);
- +}
- +
- +/**
- + * Start the SRP timer to detect when the SRP does not complete within
- + * 6 seconds.
- + *
- + * @param core_if the pointer to core_if strucure.
- + */
- +void dwc_otg_pcd_start_srp_timer(dwc_otg_core_if_t * core_if)
- +{
- + core_if->srp_timer_started = 1;
- + DWC_TIMER_SCHEDULE(core_if->srp_timer, 6000 /* 6 secs */ );
- +}
- +
- +void dwc_otg_initiate_srp(dwc_otg_core_if_t * core_if)
- +{
- + uint32_t *addr = (uint32_t *) & (core_if->core_global_regs->gotgctl);
- + gotgctl_data_t mem;
- + gotgctl_data_t val;
- +
- + val.d32 = DWC_READ_REG32(addr);
- + if (val.b.sesreq) {
- + DWC_ERROR("Session Request Already active!\n");
- + return;
- + }
- +
- + DWC_INFO("Session Request Initated\n"); //NOTICE
- + mem.d32 = DWC_READ_REG32(addr);
- + mem.b.sesreq = 1;
- + DWC_WRITE_REG32(addr, mem.d32);
- +
- + /* Start the SRP timer */
- + dwc_otg_pcd_start_srp_timer(core_if);
- + return;
- +}
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_cil.h
- @@ -0,0 +1,1464 @@
- +/* ==========================================================================
- + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_cil.h $
- + * $Revision: #123 $
- + * $Date: 2012/08/10 $
- + * $Change: 2047372 $
- + *
- + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
- + * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
- + * otherwise expressly agreed to in writing between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product under
- + * any End User Software License Agreement or Agreement for Licensed Product
- + * with Synopsys or any supplement thereto. You are permitted to use and
- + * redistribute this Software in source and binary forms, with or without
- + * modification, provided that redistributions of source code must retain this
- + * notice. You may not view, use, disclose, copy or distribute this file or
- + * any information contained herein except pursuant to this license grant from
- + * Synopsys. If you do not agree with this notice, including the disclaimer
- + * below, then you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
- + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- + * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
- + * ========================================================================== */
- +
- +#if !defined(__DWC_CIL_H__)
- +#define __DWC_CIL_H__
- +
- +#include "dwc_list.h"
- +#include "dwc_otg_dbg.h"
- +#include "dwc_otg_regs.h"
- +
- +#include "dwc_otg_core_if.h"
- +#include "dwc_otg_adp.h"
- +
- +/**
- + * @file
- + * This file contains the interface to the Core Interface Layer.
- + */
- +
- +#ifdef DWC_UTE_CFI
- +
- +#define MAX_DMA_DESCS_PER_EP 256
- +
- +/**
- + * Enumeration for the data buffer mode
- + */
- +typedef enum _data_buffer_mode {
- + BM_STANDARD = 0, /* data buffer is in normal mode */
- + BM_SG = 1, /* data buffer uses the scatter/gather mode */
- + BM_CONCAT = 2, /* data buffer uses the concatenation mode */
- + BM_CIRCULAR = 3, /* data buffer uses the circular DMA mode */
- + BM_ALIGN = 4 /* data buffer is in buffer alignment mode */
- +} data_buffer_mode_e;
- +#endif //DWC_UTE_CFI
- +
- +/** Macros defined for DWC OTG HW Release version */
- +
- +#define OTG_CORE_REV_2_60a 0x4F54260A
- +#define OTG_CORE_REV_2_71a 0x4F54271A
- +#define OTG_CORE_REV_2_72a 0x4F54272A
- +#define OTG_CORE_REV_2_80a 0x4F54280A
- +#define OTG_CORE_REV_2_81a 0x4F54281A
- +#define OTG_CORE_REV_2_90a 0x4F54290A
- +#define OTG_CORE_REV_2_91a 0x4F54291A
- +#define OTG_CORE_REV_2_92a 0x4F54292A
- +#define OTG_CORE_REV_2_93a 0x4F54293A
- +#define OTG_CORE_REV_2_94a 0x4F54294A
- +#define OTG_CORE_REV_3_00a 0x4F54300A
- +
- +/**
- + * Information for each ISOC packet.
- + */
- +typedef struct iso_pkt_info {
- + uint32_t offset;
- + uint32_t length;
- + int32_t status;
- +} iso_pkt_info_t;
- +
- +/**
- + * The <code>dwc_ep</code> structure represents the state of a single
- + * endpoint when acting in device mode. It contains the data items
- + * needed for an endpoint to be activated and transfer packets.
- + */
- +typedef struct dwc_ep {
- + /** EP number used for register address lookup */
- + uint8_t num;
- + /** EP direction 0 = OUT */
- + unsigned is_in:1;
- + /** EP active. */
- + unsigned active:1;
- +
- + /**
- + * Periodic Tx FIFO # for IN EPs For INTR EP set to 0 to use non-periodic
- + * Tx FIFO. If dedicated Tx FIFOs are enabled Tx FIFO # FOR IN EPs*/
- + unsigned tx_fifo_num:4;
- + /** EP type: 0 - Control, 1 - ISOC, 2 - BULK, 3 - INTR */
- + unsigned type:2;
- +#define DWC_OTG_EP_TYPE_CONTROL 0
- +#define DWC_OTG_EP_TYPE_ISOC 1
- +#define DWC_OTG_EP_TYPE_BULK 2
- +#define DWC_OTG_EP_TYPE_INTR 3
- +
- + /** DATA start PID for INTR and BULK EP */
- + unsigned data_pid_start:1;
- + /** Frame (even/odd) for ISOC EP */
- + unsigned even_odd_frame:1;
- + /** Max Packet bytes */
- + unsigned maxpacket:11;
- +
- + /** Max Transfer size */
- + uint32_t maxxfer;
- +
- + /** @name Transfer state */
- + /** @{ */
- +
- + /**
- + * Pointer to the beginning of the transfer buffer -- do not modify
- + * during transfer.
- + */
- +
- + dwc_dma_t dma_addr;
- +
- + dwc_dma_t dma_desc_addr;
- + dwc_otg_dev_dma_desc_t *desc_addr;
- +
- + uint8_t *start_xfer_buff;
- + /** pointer to the transfer buffer */
- + uint8_t *xfer_buff;
- + /** Number of bytes to transfer */
- + unsigned xfer_len:19;
- + /** Number of bytes transferred. */
- + unsigned xfer_count:19;
- + /** Sent ZLP */
- + unsigned sent_zlp:1;
- + /** Total len for control transfer */
- + unsigned total_len:19;
- +
- + /** stall clear flag */
- + unsigned stall_clear_flag:1;
- +
- + /** SETUP pkt cnt rollover flag for EP0 out*/
- + unsigned stp_rollover;
- +
- +#ifdef DWC_UTE_CFI
- + /* The buffer mode */
- + data_buffer_mode_e buff_mode;
- +
- + /* The chain of DMA descriptors.
- + * MAX_DMA_DESCS_PER_EP will be allocated for each active EP.
- + */
- + dwc_otg_dma_desc_t *descs;
- +
- + /* The DMA address of the descriptors chain start */
- + dma_addr_t descs_dma_addr;
- + /** This variable stores the length of the last enqueued request */
- + uint32_t cfi_req_len;
- +#endif //DWC_UTE_CFI
- +
- +/** Max DMA Descriptor count for any EP */
- +#define MAX_DMA_DESC_CNT 256
- + /** Allocated DMA Desc count */
- + uint32_t desc_cnt;
- +
- + /** bInterval */
- + uint32_t bInterval;
- + /** Next frame num to setup next ISOC transfer */
- + uint32_t frame_num;
- + /** Indicates SOF number overrun in DSTS */
- + uint8_t frm_overrun;
- +
- +#ifdef DWC_UTE_PER_IO
- + /** Next frame num for which will be setup DMA Desc */
- + uint32_t xiso_frame_num;
- + /** bInterval */
- + uint32_t xiso_bInterval;
- + /** Count of currently active transfers - shall be either 0 or 1 */
- + int xiso_active_xfers;
- + int xiso_queued_xfers;
- +#endif
- +#ifdef DWC_EN_ISOC
- + /**
- + * Variables specific for ISOC EPs
- + *
- + */
- + /** DMA addresses of ISOC buffers */
- + dwc_dma_t dma_addr0;
- + dwc_dma_t dma_addr1;
- +
- + dwc_dma_t iso_dma_desc_addr;
- + dwc_otg_dev_dma_desc_t *iso_desc_addr;
- +
- + /** pointer to the transfer buffers */
- + uint8_t *xfer_buff0;
- + uint8_t *xfer_buff1;
- +
- + /** number of ISOC Buffer is processing */
- + uint32_t proc_buf_num;
- + /** Interval of ISOC Buffer processing */
- + uint32_t buf_proc_intrvl;
- + /** Data size for regular frame */
- + uint32_t data_per_frame;
- +
- + /* todo - pattern data support is to be implemented in the future */
- + /** Data size for pattern frame */
- + uint32_t data_pattern_frame;
- + /** Frame number of pattern data */
- + uint32_t sync_frame;
- +
- + /** bInterval */
- + uint32_t bInterval;
- + /** ISO Packet number per frame */
- + uint32_t pkt_per_frm;
- + /** Next frame num for which will be setup DMA Desc */
- + uint32_t next_frame;
- + /** Number of packets per buffer processing */
- + uint32_t pkt_cnt;
- + /** Info for all isoc packets */
- + iso_pkt_info_t *pkt_info;
- + /** current pkt number */
- + uint32_t cur_pkt;
- + /** current pkt number */
- + uint8_t *cur_pkt_addr;
- + /** current pkt number */
- + uint32_t cur_pkt_dma_addr;
- +#endif /* DWC_EN_ISOC */
- +
- +/** @} */
- +} dwc_ep_t;
- +
- +/*
- + * Reasons for halting a host channel.
- + */
- +typedef enum dwc_otg_halt_status {
- + DWC_OTG_HC_XFER_NO_HALT_STATUS,
- + DWC_OTG_HC_XFER_COMPLETE,
- + DWC_OTG_HC_XFER_URB_COMPLETE,
- + DWC_OTG_HC_XFER_ACK,
- + DWC_OTG_HC_XFER_NAK,
- + DWC_OTG_HC_XFER_NYET,
- + DWC_OTG_HC_XFER_STALL,
- + DWC_OTG_HC_XFER_XACT_ERR,
- + DWC_OTG_HC_XFER_FRAME_OVERRUN,
- + DWC_OTG_HC_XFER_BABBLE_ERR,
- + DWC_OTG_HC_XFER_DATA_TOGGLE_ERR,
- + DWC_OTG_HC_XFER_AHB_ERR,
- + DWC_OTG_HC_XFER_PERIODIC_INCOMPLETE,
- + DWC_OTG_HC_XFER_URB_DEQUEUE
- +} dwc_otg_halt_status_e;
- +
- +/**
- + * Host channel descriptor. This structure represents the state of a single
- + * host channel when acting in host mode. It contains the data items needed to
- + * transfer packets to an endpoint via a host channel.
- + */
- +typedef struct dwc_hc {
- + /** Host channel number used for register address lookup */
- + uint8_t hc_num;
- +
- + /** Device to access */
- + unsigned dev_addr:7;
- +
- + /** EP to access */
- + unsigned ep_num:4;
- +
- + /** EP direction. 0: OUT, 1: IN */
- + unsigned ep_is_in:1;
- +
- + /**
- + * EP speed.
- + * One of the following values:
- + * - DWC_OTG_EP_SPEED_LOW
- + * - DWC_OTG_EP_SPEED_FULL
- + * - DWC_OTG_EP_SPEED_HIGH
- + */
- + unsigned speed:2;
- +#define DWC_OTG_EP_SPEED_LOW 0
- +#define DWC_OTG_EP_SPEED_FULL 1
- +#define DWC_OTG_EP_SPEED_HIGH 2
- +
- + /**
- + * Endpoint type.
- + * One of the following values:
- + * - DWC_OTG_EP_TYPE_CONTROL: 0
- + * - DWC_OTG_EP_TYPE_ISOC: 1
- + * - DWC_OTG_EP_TYPE_BULK: 2
- + * - DWC_OTG_EP_TYPE_INTR: 3
- + */
- + unsigned ep_type:2;
- +
- + /** Max packet size in bytes */
- + unsigned max_packet:11;
- +
- + /**
- + * PID for initial transaction.
- + * 0: DATA0,<br>
- + * 1: DATA2,<br>
- + * 2: DATA1,<br>
- + * 3: MDATA (non-Control EP),
- + * SETUP (Control EP)
- + */
- + unsigned data_pid_start:2;
- +#define DWC_OTG_HC_PID_DATA0 0
- +#define DWC_OTG_HC_PID_DATA2 1
- +#define DWC_OTG_HC_PID_DATA1 2
- +#define DWC_OTG_HC_PID_MDATA 3
- +#define DWC_OTG_HC_PID_SETUP 3
- +
- + /** Number of periodic transactions per (micro)frame */
- + unsigned multi_count:2;
- +
- + /** @name Transfer State */
- + /** @{ */
- +
- + /** Pointer to the current transfer buffer position. */
- + uint8_t *xfer_buff;
- + /**
- + * In Buffer DMA mode this buffer will be used
- + * if xfer_buff is not DWORD aligned.
- + */
- + dwc_dma_t align_buff;
- + /** Total number of bytes to transfer. */
- + uint32_t xfer_len;
- + /** Number of bytes transferred so far. */
- + uint32_t xfer_count;
- + /** Packet count at start of transfer.*/
- + uint16_t start_pkt_count;
- +
- + /**
- + * Flag to indicate whether the transfer has been started. Set to 1 if
- + * it has been started, 0 otherwise.
- + */
- + uint8_t xfer_started;
- +
- + /**
- + * Set to 1 to indicate that a PING request should be issued on this
- + * channel. If 0, process normally.
- + */
- + uint8_t do_ping;
- +
- + /**
- + * Set to 1 to indicate that the error count for this transaction is
- + * non-zero. Set to 0 if the error count is 0.
- + */
- + uint8_t error_state;
- +
- + /**
- + * Set to 1 to indicate that this channel should be halted the next
- + * time a request is queued for the channel. This is necessary in
- + * slave mode if no request queue space is available when an attempt
- + * is made to halt the channel.
- + */
- + uint8_t halt_on_queue;
- +
- + /**
- + * Set to 1 if the host channel has been halted, but the core is not
- + * finished flushing queued requests. Otherwise 0.
- + */
- + uint8_t halt_pending;
- +
- + /**
- + * Reason for halting the host channel.
- + */
- + dwc_otg_halt_status_e halt_status;
- +
- + /*
- + * Split settings for the host channel
- + */
- + uint8_t do_split; /**< Enable split for the channel */
- + uint8_t complete_split; /**< Enable complete split */
- + uint8_t hub_addr; /**< Address of high speed hub */
- +
- + uint8_t port_addr; /**< Port of the low/full speed device */
- + /** Split transaction position
- + * One of the following values:
- + * - DWC_HCSPLIT_XACTPOS_MID
- + * - DWC_HCSPLIT_XACTPOS_BEGIN
- + * - DWC_HCSPLIT_XACTPOS_END
- + * - DWC_HCSPLIT_XACTPOS_ALL */
- + uint8_t xact_pos;
- +
- + /** Set when the host channel does a short read. */
- + uint8_t short_read;
- +
- + /**
- + * Number of requests issued for this channel since it was assigned to
- + * the current transfer (not counting PINGs).
- + */
- + uint8_t requests;
- +
- + /**
- + * Queue Head for the transfer being processed by this channel.
- + */
- + struct dwc_otg_qh *qh;
- +
- + /** @} */
- +
- + /** Entry in list of host channels. */
- + DWC_CIRCLEQ_ENTRY(dwc_hc) hc_list_entry;
- +
- + /** @name Descriptor DMA support */
- + /** @{ */
- +
- + /** Number of Transfer Descriptors */
- + uint16_t ntd;
- +
- + /** Descriptor List DMA address */
- + dwc_dma_t desc_list_addr;
- +
- + /** Scheduling micro-frame bitmap. */
- + uint8_t schinfo;
- +
- + /** @} */
- +} dwc_hc_t;
- +
- +/**
- + * The following parameters may be specified when starting the module. These
- + * parameters define how the DWC_otg controller should be configured.
- + */
- +typedef struct dwc_otg_core_params {
- + int32_t opt;
- +
- + /**
- + * Specifies the OTG capabilities. The driver will automatically
- + * detect the value for this parameter if none is specified.
- + * 0 - HNP and SRP capable (default)
- + * 1 - SRP Only capable
- + * 2 - No HNP/SRP capable
- + */
- + int32_t otg_cap;
- +
- + /**
- + * Specifies whether to use slave or DMA mode for accessing the data
- + * FIFOs. The driver will automatically detect the value for this
- + * parameter if none is specified.
- + * 0 - Slave
- + * 1 - DMA (default, if available)
- + */
- + int32_t dma_enable;
- +
- + /**
- + * When DMA mode is enabled specifies whether to use address DMA or DMA
- + * Descriptor mode for accessing the data FIFOs in device mode. The driver
- + * will automatically detect the value for this if none is specified.
- + * 0 - address DMA
- + * 1 - DMA Descriptor(default, if available)
- + */
- + int32_t dma_desc_enable;
- + /** The DMA Burst size (applicable only for External DMA
- + * Mode). 1, 4, 8 16, 32, 64, 128, 256 (default 32)
- + */
- + int32_t dma_burst_size; /* Translate this to GAHBCFG values */
- +
- + /**
- + * Specifies the maximum speed of operation in host and device mode.
- + * The actual speed depends on the speed of the attached device and
- + * the value of phy_type. The actual speed depends on the speed of the
- + * attached device.
- + * 0 - High Speed (default)
- + * 1 - Full Speed
- + */
- + int32_t speed;
- + /** Specifies whether low power mode is supported when attached
- + * to a Full Speed or Low Speed device in host mode.
- + * 0 - Don't support low power mode (default)
- + * 1 - Support low power mode
- + */
- + int32_t host_support_fs_ls_low_power;
- +
- + /** Specifies the PHY clock rate in low power mode when connected to a
- + * Low Speed device in host mode. This parameter is applicable only if
- + * HOST_SUPPORT_FS_LS_LOW_POWER is enabled. If PHY_TYPE is set to FS
- + * then defaults to 6 MHZ otherwise 48 MHZ.
- + *
- + * 0 - 48 MHz
- + * 1 - 6 MHz
- + */
- + int32_t host_ls_low_power_phy_clk;
- +
- + /**
- + * 0 - Use cC FIFO size parameters
- + * 1 - Allow dynamic FIFO sizing (default)
- + */
- + int32_t enable_dynamic_fifo;
- +
- + /** Total number of 4-byte words in the data FIFO memory. This
- + * memory includes the Rx FIFO, non-periodic Tx FIFO, and periodic
- + * Tx FIFOs.
- + * 32 to 32768 (default 8192)
- + * Note: The total FIFO memory depth in the FPGA configuration is 8192.
- + */
- + int32_t data_fifo_size;
- +
- + /** Number of 4-byte words in the Rx FIFO in device mode when dynamic
- + * FIFO sizing is enabled.
- + * 16 to 32768 (default 1064)
- + */
- + int32_t dev_rx_fifo_size;
- +
- + /** Number of 4-byte words in the non-periodic Tx FIFO in device mode
- + * when dynamic FIFO sizing is enabled.
- + * 16 to 32768 (default 1024)
- + */
- + int32_t dev_nperio_tx_fifo_size;
- +
- + /** Number of 4-byte words in each of the periodic Tx FIFOs in device
- + * mode when dynamic FIFO sizing is enabled.
- + * 4 to 768 (default 256)
- + */
- + uint32_t dev_perio_tx_fifo_size[MAX_PERIO_FIFOS];
- +
- + /** Number of 4-byte words in the Rx FIFO in host mode when dynamic
- + * FIFO sizing is enabled.
- + * 16 to 32768 (default 1024)
- + */
- + int32_t host_rx_fifo_size;
- +
- + /** Number of 4-byte words in the non-periodic Tx FIFO in host mode
- + * when Dynamic FIFO sizing is enabled in the core.
- + * 16 to 32768 (default 1024)
- + */
- + int32_t host_nperio_tx_fifo_size;
- +
- + /** Number of 4-byte words in the host periodic Tx FIFO when dynamic
- + * FIFO sizing is enabled.
- + * 16 to 32768 (default 1024)
- + */
- + int32_t host_perio_tx_fifo_size;
- +
- + /** The maximum transfer size supported in bytes.
- + * 2047 to 65,535 (default 65,535)
- + */
- + int32_t max_transfer_size;
- +
- + /** The maximum number of packets in a transfer.
- + * 15 to 511 (default 511)
- + */
- + int32_t max_packet_count;
- +
- + /** The number of host channel registers to use.
- + * 1 to 16 (default 12)
- + * Note: The FPGA configuration supports a maximum of 12 host channels.
- + */
- + int32_t host_channels;
- +
- + /** The number of endpoints in addition to EP0 available for device
- + * mode operations.
- + * 1 to 15 (default 6 IN and OUT)
- + * Note: The FPGA configuration supports a maximum of 6 IN and OUT
- + * endpoints in addition to EP0.
- + */
- + int32_t dev_endpoints;
- +
- + /**
- + * Specifies the type of PHY interface to use. By default, the driver
- + * will automatically detect the phy_type.
- + *
- + * 0 - Full Speed PHY
- + * 1 - UTMI+ (default)
- + * 2 - ULPI
- + */
- + int32_t phy_type;
- +
- + /**
- + * Specifies the UTMI+ Data Width. This parameter is
- + * applicable for a PHY_TYPE of UTMI+ or ULPI. (For a ULPI
- + * PHY_TYPE, this parameter indicates the data width between
- + * the MAC and the ULPI Wrapper.) Also, this parameter is
- + * applicable only if the OTG_HSPHY_WIDTH cC parameter was set
- + * to "8 and 16 bits", meaning that the core has been
- + * configured to work at either data path width.
- + *
- + * 8 or 16 bits (default 16)
- + */
- + int32_t phy_utmi_width;
- +
- + /**
- + * Specifies whether the ULPI operates at double or single
- + * data rate. This parameter is only applicable if PHY_TYPE is
- + * ULPI.
- + *
- + * 0 - single data rate ULPI interface with 8 bit wide data
- + * bus (default)
- + * 1 - double data rate ULPI interface with 4 bit wide data
- + * bus
- + */
- + int32_t phy_ulpi_ddr;
- +
- + /**
- + * Specifies whether to use the internal or external supply to
- + * drive the vbus with a ULPI phy.
- + */
- + int32_t phy_ulpi_ext_vbus;
- +
- + /**
- + * Specifies whether to use the I2Cinterface for full speed PHY. This
- + * parameter is only applicable if PHY_TYPE is FS.
- + * 0 - No (default)
- + * 1 - Yes
- + */
- + int32_t i2c_enable;
- +
- + int32_t ulpi_fs_ls;
- +
- + int32_t ts_dline;
- +
- + /**
- + * Specifies whether dedicated transmit FIFOs are
- + * enabled for non periodic IN endpoints in device mode
- + * 0 - No
- + * 1 - Yes
- + */
- + int32_t en_multiple_tx_fifo;
- +
- + /** Number of 4-byte words in each of the Tx FIFOs in device
- + * mode when dynamic FIFO sizing is enabled.
- + * 4 to 768 (default 256)
- + */
- + uint32_t dev_tx_fifo_size[MAX_TX_FIFOS];
- +
- + /** Thresholding enable flag-
- + * bit 0 - enable non-ISO Tx thresholding
- + * bit 1 - enable ISO Tx thresholding
- + * bit 2 - enable Rx thresholding
- + */
- + uint32_t thr_ctl;
- +
- + /** Thresholding length for Tx
- + * FIFOs in 32 bit DWORDs
- + */
- + uint32_t tx_thr_length;
- +
- + /** Thresholding length for Rx
- + * FIFOs in 32 bit DWORDs
- + */
- + uint32_t rx_thr_length;
- +
- + /**
- + * Specifies whether LPM (Link Power Management) support is enabled
- + */
- + int32_t lpm_enable;
- +
- + /** Per Transfer Interrupt
- + * mode enable flag
- + * 1 - Enabled
- + * 0 - Disabled
- + */
- + int32_t pti_enable;
- +
- + /** Multi Processor Interrupt
- + * mode enable flag
- + * 1 - Enabled
- + * 0 - Disabled
- + */
- + int32_t mpi_enable;
- +
- + /** IS_USB Capability
- + * 1 - Enabled
- + * 0 - Disabled
- + */
- + int32_t ic_usb_cap;
- +
- + /** AHB Threshold Ratio
- + * 2'b00 AHB Threshold = MAC Threshold
- + * 2'b01 AHB Threshold = 1/2 MAC Threshold
- + * 2'b10 AHB Threshold = 1/4 MAC Threshold
- + * 2'b11 AHB Threshold = 1/8 MAC Threshold
- + */
- + int32_t ahb_thr_ratio;
- +
- + /** ADP Support
- + * 1 - Enabled
- + * 0 - Disabled
- + */
- + int32_t adp_supp_enable;
- +
- + /** HFIR Reload Control
- + * 0 - The HFIR cannot be reloaded dynamically.
- + * 1 - Allow dynamic reloading of the HFIR register during runtime.
- + */
- + int32_t reload_ctl;
- +
- + /** DCFG: Enable device Out NAK
- + * 0 - The core does not set NAK after Bulk Out transfer complete.
- + * 1 - The core sets NAK after Bulk OUT transfer complete.
- + */
- + int32_t dev_out_nak;
- +
- + /** DCFG: Enable Continue on BNA
- + * After receiving BNA interrupt the core disables the endpoint,when the
- + * endpoint is re-enabled by the application the core starts processing
- + * 0 - from the DOEPDMA descriptor
- + * 1 - from the descriptor which received the BNA.
- + */
- + int32_t cont_on_bna;
- +
- + /** GAHBCFG: AHB Single Support
- + * This bit when programmed supports SINGLE transfers for remainder
- + * data in a transfer for DMA mode of operation.
- + * 0 - in this case the remainder data will be sent using INCR burst size.
- + * 1 - in this case the remainder data will be sent using SINGLE burst size.
- + */
- + int32_t ahb_single;
- +
- + /** Core Power down mode
- + * 0 - No Power Down is enabled
- + * 1 - Reserved
- + * 2 - Complete Power Down (Hibernation)
- + */
- + int32_t power_down;
- +
- + /** OTG revision supported
- + * 0 - OTG 1.3 revision
- + * 1 - OTG 2.0 revision
- + */
- + int32_t otg_ver;
- +
- +} dwc_otg_core_params_t;
- +
- +#ifdef DEBUG
- +struct dwc_otg_core_if;
- +typedef struct hc_xfer_info {
- + struct dwc_otg_core_if *core_if;
- + dwc_hc_t *hc;
- +} hc_xfer_info_t;
- +#endif
- +
- +typedef struct ep_xfer_info {
- + struct dwc_otg_core_if *core_if;
- + dwc_ep_t *ep;
- + uint8_t state;
- +} ep_xfer_info_t;
- +/*
- + * Device States
- + */
- +typedef enum dwc_otg_lx_state {
- + /** On state */
- + DWC_OTG_L0,
- + /** LPM sleep state*/
- + DWC_OTG_L1,
- + /** USB suspend state*/
- + DWC_OTG_L2,
- + /** Off state*/
- + DWC_OTG_L3
- +} dwc_otg_lx_state_e;
- +
- +struct dwc_otg_global_regs_backup {
- + uint32_t gotgctl_local;
- + uint32_t gintmsk_local;
- + uint32_t gahbcfg_local;
- + uint32_t gusbcfg_local;
- + uint32_t grxfsiz_local;
- + uint32_t gnptxfsiz_local;
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- + uint32_t glpmcfg_local;
- +#endif
- + uint32_t gi2cctl_local;
- + uint32_t hptxfsiz_local;
- + uint32_t pcgcctl_local;
- + uint32_t gdfifocfg_local;
- + uint32_t dtxfsiz_local[MAX_EPS_CHANNELS];
- + uint32_t gpwrdn_local;
- + uint32_t xhib_pcgcctl;
- + uint32_t xhib_gpwrdn;
- +};
- +
- +struct dwc_otg_host_regs_backup {
- + uint32_t hcfg_local;
- + uint32_t haintmsk_local;
- + uint32_t hcintmsk_local[MAX_EPS_CHANNELS];
- + uint32_t hprt0_local;
- + uint32_t hfir_local;
- +};
- +
- +struct dwc_otg_dev_regs_backup {
- + uint32_t dcfg;
- + uint32_t dctl;
- + uint32_t daintmsk;
- + uint32_t diepmsk;
- + uint32_t doepmsk;
- + uint32_t diepctl[MAX_EPS_CHANNELS];
- + uint32_t dieptsiz[MAX_EPS_CHANNELS];
- + uint32_t diepdma[MAX_EPS_CHANNELS];
- +};
- +/**
- + * The <code>dwc_otg_core_if</code> structure contains information needed to manage
- + * the DWC_otg controller acting in either host or device mode. It
- + * represents the programming view of the controller as a whole.
- + */
- +struct dwc_otg_core_if {
- + /** Parameters that define how the core should be configured.*/
- + dwc_otg_core_params_t *core_params;
- +
- + /** Core Global registers starting at offset 000h. */
- + dwc_otg_core_global_regs_t *core_global_regs;
- +
- + /** Device-specific information */
- + dwc_otg_dev_if_t *dev_if;
- + /** Host-specific information */
- + dwc_otg_host_if_t *host_if;
- +
- + /** Value from SNPSID register */
- + uint32_t snpsid;
- +
- + /*
- + * Set to 1 if the core PHY interface bits in USBCFG have been
- + * initialized.
- + */
- + uint8_t phy_init_done;
- +
- + /*
- + * SRP Success flag, set by srp success interrupt in FS I2C mode
- + */
- + uint8_t srp_success;
- + uint8_t srp_timer_started;
- + /** Timer for SRP. If it expires before SRP is successful
- + * clear the SRP. */
- + dwc_timer_t *srp_timer;
- +
- +#ifdef DWC_DEV_SRPCAP
- + /* This timer is needed to power on the hibernated host core if SRP is not
- + * initiated on connected SRP capable device for limited period of time
- + */
- + uint8_t pwron_timer_started;
- + dwc_timer_t *pwron_timer;
- +#endif
- + /* Common configuration information */
- + /** Power and Clock Gating Control Register */
- + volatile uint32_t *pcgcctl;
- +#define DWC_OTG_PCGCCTL_OFFSET 0xE00
- +
- + /** Push/pop addresses for endpoints or host channels.*/
- + uint32_t *data_fifo[MAX_EPS_CHANNELS];
- +#define DWC_OTG_DATA_FIFO_OFFSET 0x1000
- +#define DWC_OTG_DATA_FIFO_SIZE 0x1000
- +
- + /** Total RAM for FIFOs (Bytes) */
- + uint16_t total_fifo_size;
- + /** Size of Rx FIFO (Bytes) */
- + uint16_t rx_fifo_size;
- + /** Size of Non-periodic Tx FIFO (Bytes) */
- + uint16_t nperio_tx_fifo_size;
- +
- + /** 1 if DMA is enabled, 0 otherwise. */
- + uint8_t dma_enable;
- +
- + /** 1 if DMA descriptor is enabled, 0 otherwise. */
- + uint8_t dma_desc_enable;
- +
- + /** 1 if PTI Enhancement mode is enabled, 0 otherwise. */
- + uint8_t pti_enh_enable;
- +
- + /** 1 if MPI Enhancement mode is enabled, 0 otherwise. */
- + uint8_t multiproc_int_enable;
- +
- + /** 1 if dedicated Tx FIFOs are enabled, 0 otherwise. */
- + uint8_t en_multiple_tx_fifo;
- +
- + /** Set to 1 if multiple packets of a high-bandwidth transfer is in
- + * process of being queued */
- + uint8_t queuing_high_bandwidth;
- +
- + /** Hardware Configuration -- stored here for convenience.*/
- + hwcfg1_data_t hwcfg1;
- + hwcfg2_data_t hwcfg2;
- + hwcfg3_data_t hwcfg3;
- + hwcfg4_data_t hwcfg4;
- + fifosize_data_t hptxfsiz;
- +
- + /** Host and Device Configuration -- stored here for convenience.*/
- + hcfg_data_t hcfg;
- + dcfg_data_t dcfg;
- +
- + /** The operational State, during transations
- + * (a_host>>a_peripherial and b_device=>b_host) this may not
- + * match the core but allows the software to determine
- + * transitions.
- + */
- + uint8_t op_state;
- +
- + /**
- + * Set to 1 if the HCD needs to be restarted on a session request
- + * interrupt. This is required if no connector ID status change has
- + * occurred since the HCD was last disconnected.
- + */
- + uint8_t restart_hcd_on_session_req;
- +
- + /** HCD callbacks */
- + /** A-Device is a_host */
- +#define A_HOST (1)
- + /** A-Device is a_suspend */
- +#define A_SUSPEND (2)
- + /** A-Device is a_peripherial */
- +#define A_PERIPHERAL (3)
- + /** B-Device is operating as a Peripheral. */
- +#define B_PERIPHERAL (4)
- + /** B-Device is operating as a Host. */
- +#define B_HOST (5)
- +
- + /** HCD callbacks */
- + struct dwc_otg_cil_callbacks *hcd_cb;
- + /** PCD callbacks */
- + struct dwc_otg_cil_callbacks *pcd_cb;
- +
- + /** Device mode Periodic Tx FIFO Mask */
- + uint32_t p_tx_msk;
- + /** Device mode Periodic Tx FIFO Mask */
- + uint32_t tx_msk;
- +
- + /** Workqueue object used for handling several interrupts */
- + dwc_workq_t *wq_otg;
- +
- + /** Timer object used for handling "Wakeup Detected" Interrupt */
- + dwc_timer_t *wkp_timer;
- + /** This arrays used for debug purposes for DEV OUT NAK enhancement */
- + uint32_t start_doeptsiz_val[MAX_EPS_CHANNELS];
- + ep_xfer_info_t ep_xfer_info[MAX_EPS_CHANNELS];
- + dwc_timer_t *ep_xfer_timer[MAX_EPS_CHANNELS];
- +#ifdef DEBUG
- + uint32_t start_hcchar_val[MAX_EPS_CHANNELS];
- +
- + hc_xfer_info_t hc_xfer_info[MAX_EPS_CHANNELS];
- + dwc_timer_t *hc_xfer_timer[MAX_EPS_CHANNELS];
- +
- + uint32_t hfnum_7_samples;
- + uint64_t hfnum_7_frrem_accum;
- + uint32_t hfnum_0_samples;
- + uint64_t hfnum_0_frrem_accum;
- + uint32_t hfnum_other_samples;
- + uint64_t hfnum_other_frrem_accum;
- +#endif
- +
- +#ifdef DWC_UTE_CFI
- + uint16_t pwron_rxfsiz;
- + uint16_t pwron_gnptxfsiz;
- + uint16_t pwron_txfsiz[15];
- +
- + uint16_t init_rxfsiz;
- + uint16_t init_gnptxfsiz;
- + uint16_t init_txfsiz[15];
- +#endif
- +
- + /** Lx state of device */
- + dwc_otg_lx_state_e lx_state;
- +
- + /** Saved Core Global registers */
- + struct dwc_otg_global_regs_backup *gr_backup;
- + /** Saved Host registers */
- + struct dwc_otg_host_regs_backup *hr_backup;
- + /** Saved Device registers */
- + struct dwc_otg_dev_regs_backup *dr_backup;
- +
- + /** Power Down Enable */
- + uint32_t power_down;
- +
- + /** ADP support Enable */
- + uint32_t adp_enable;
- +
- + /** ADP structure object */
- + dwc_otg_adp_t adp;
- +
- + /** hibernation/suspend flag */
- + int hibernation_suspend;
- +
- + /** Device mode extended hibernation flag */
- + int xhib;
- +
- + /** OTG revision supported */
- + uint32_t otg_ver;
- +
- + /** OTG status flag used for HNP polling */
- + uint8_t otg_sts;
- +
- + /** Pointer to either hcd->lock or pcd->lock */
- + dwc_spinlock_t *lock;
- +
- + /** Start predict NextEP based on Learning Queue if equal 1,
- + * also used as counter of disabled NP IN EP's */
- + uint8_t start_predict;
- +
- + /** NextEp sequence, including EP0: nextep_seq[] = EP if non-periodic and
- + * active, 0xff otherwise */
- + uint8_t nextep_seq[MAX_EPS_CHANNELS];
- +
- + /** Index of fisrt EP in nextep_seq array which should be re-enabled **/
- + uint8_t first_in_nextep_seq;
- +
- + /** Frame number while entering to ISR - needed for ISOCs **/
- + uint32_t frame_num;
- +
- +};
- +
- +#ifdef DEBUG
- +/*
- + * This function is called when transfer is timed out.
- + */
- +extern void hc_xfer_timeout(void *ptr);
- +#endif
- +
- +/*
- + * This function is called when transfer is timed out on endpoint.
- + */
- +extern void ep_xfer_timeout(void *ptr);
- +
- +/*
- + * The following functions are functions for works
- + * using during handling some interrupts
- + */
- +extern void w_conn_id_status_change(void *p);
- +
- +extern void w_wakeup_detected(void *p);
- +
- +/** Saves global register values into system memory. */
- +extern int dwc_otg_save_global_regs(dwc_otg_core_if_t * core_if);
- +/** Saves device register values into system memory. */
- +extern int dwc_otg_save_dev_regs(dwc_otg_core_if_t * core_if);
- +/** Saves host register values into system memory. */
- +extern int dwc_otg_save_host_regs(dwc_otg_core_if_t * core_if);
- +/** Restore global register values. */
- +extern int dwc_otg_restore_global_regs(dwc_otg_core_if_t * core_if);
- +/** Restore host register values. */
- +extern int dwc_otg_restore_host_regs(dwc_otg_core_if_t * core_if, int reset);
- +/** Restore device register values. */
- +extern int dwc_otg_restore_dev_regs(dwc_otg_core_if_t * core_if,
- + int rem_wakeup);
- +extern int restore_lpm_i2c_regs(dwc_otg_core_if_t * core_if);
- +extern int restore_essential_regs(dwc_otg_core_if_t * core_if, int rmode,
- + int is_host);
- +
- +extern int dwc_otg_host_hibernation_restore(dwc_otg_core_if_t * core_if,
- + int restore_mode, int reset);
- +extern int dwc_otg_device_hibernation_restore(dwc_otg_core_if_t * core_if,
- + int rem_wakeup, int reset);
- +
- +/*
- + * The following functions support initialization of the CIL driver component
- + * and the DWC_otg controller.
- + */
- +extern void dwc_otg_core_host_init(dwc_otg_core_if_t * _core_if);
- +extern void dwc_otg_core_dev_init(dwc_otg_core_if_t * _core_if);
- +
- +/** @name Device CIL Functions
- + * The following functions support managing the DWC_otg controller in device
- + * mode.
- + */
- +/**@{*/
- +extern void dwc_otg_wakeup(dwc_otg_core_if_t * _core_if);
- +extern void dwc_otg_read_setup_packet(dwc_otg_core_if_t * _core_if,
- + uint32_t * _dest);
- +extern uint32_t dwc_otg_get_frame_number(dwc_otg_core_if_t * _core_if);
- +extern void dwc_otg_ep0_activate(dwc_otg_core_if_t * _core_if, dwc_ep_t * _ep);
- +extern void dwc_otg_ep_activate(dwc_otg_core_if_t * _core_if, dwc_ep_t * _ep);
- +extern void dwc_otg_ep_deactivate(dwc_otg_core_if_t * _core_if, dwc_ep_t * _ep);
- +extern void dwc_otg_ep_start_transfer(dwc_otg_core_if_t * _core_if,
- + dwc_ep_t * _ep);
- +extern void dwc_otg_ep_start_zl_transfer(dwc_otg_core_if_t * _core_if,
- + dwc_ep_t * _ep);
- +extern void dwc_otg_ep0_start_transfer(dwc_otg_core_if_t * _core_if,
- + dwc_ep_t * _ep);
- +extern void dwc_otg_ep0_continue_transfer(dwc_otg_core_if_t * _core_if,
- + dwc_ep_t * _ep);
- +extern void dwc_otg_ep_write_packet(dwc_otg_core_if_t * _core_if,
- + dwc_ep_t * _ep, int _dma);
- +extern void dwc_otg_ep_set_stall(dwc_otg_core_if_t * _core_if, dwc_ep_t * _ep);
- +extern void dwc_otg_ep_clear_stall(dwc_otg_core_if_t * _core_if,
- + dwc_ep_t * _ep);
- +extern void dwc_otg_enable_device_interrupts(dwc_otg_core_if_t * _core_if);
- +
- +#ifdef DWC_EN_ISOC
- +extern void dwc_otg_iso_ep_start_frm_transfer(dwc_otg_core_if_t * core_if,
- + dwc_ep_t * ep);
- +extern void dwc_otg_iso_ep_start_buf_transfer(dwc_otg_core_if_t * core_if,
- + dwc_ep_t * ep);
- +#endif /* DWC_EN_ISOC */
- +/**@}*/
- +
- +/** @name Host CIL Functions
- + * The following functions support managing the DWC_otg controller in host
- + * mode.
- + */
- +/**@{*/
- +extern void dwc_otg_hc_init(dwc_otg_core_if_t * _core_if, dwc_hc_t * _hc);
- +extern void dwc_otg_hc_halt(dwc_otg_core_if_t * _core_if,
- + dwc_hc_t * _hc, dwc_otg_halt_status_e _halt_status);
- +extern void dwc_otg_hc_cleanup(dwc_otg_core_if_t * _core_if, dwc_hc_t * _hc);
- +extern void dwc_otg_hc_start_transfer(dwc_otg_core_if_t * _core_if,
- + dwc_hc_t * _hc);
- +extern int dwc_otg_hc_continue_transfer(dwc_otg_core_if_t * _core_if,
- + dwc_hc_t * _hc);
- +extern void dwc_otg_hc_do_ping(dwc_otg_core_if_t * _core_if, dwc_hc_t * _hc);
- +extern void dwc_otg_hc_write_packet(dwc_otg_core_if_t * _core_if,
- + dwc_hc_t * _hc);
- +extern void dwc_otg_enable_host_interrupts(dwc_otg_core_if_t * _core_if);
- +extern void dwc_otg_disable_host_interrupts(dwc_otg_core_if_t * _core_if);
- +
- +extern void dwc_otg_hc_start_transfer_ddma(dwc_otg_core_if_t * core_if,
- + dwc_hc_t * hc);
- +
- +extern uint32_t calc_frame_interval(dwc_otg_core_if_t * core_if);
- +
- +/* Macro used to clear one channel interrupt */
- +#define clear_hc_int(_hc_regs_, _intr_) \
- +do { \
- + hcint_data_t hcint_clear = {.d32 = 0}; \
- + hcint_clear.b._intr_ = 1; \
- + DWC_WRITE_REG32(&(_hc_regs_)->hcint, hcint_clear.d32); \
- +} while (0)
- +
- +/*
- + * Macro used to disable one channel interrupt. Channel interrupts are
- + * disabled when the channel is halted or released by the interrupt handler.
- + * There is no need to handle further interrupts of that type until the
- + * channel is re-assigned. In fact, subsequent handling may cause crashes
- + * because the channel structures are cleaned up when the channel is released.
- + */
- +#define disable_hc_int(_hc_regs_, _intr_) \
- +do { \
- + hcintmsk_data_t hcintmsk = {.d32 = 0}; \
- + hcintmsk.b._intr_ = 1; \
- + DWC_MODIFY_REG32(&(_hc_regs_)->hcintmsk, hcintmsk.d32, 0); \
- +} while (0)
- +
- +/**
- + * This function Reads HPRT0 in preparation to modify. It keeps the
- + * WC bits 0 so that if they are read as 1, they won't clear when you
- + * write it back
- + */
- +static inline uint32_t dwc_otg_read_hprt0(dwc_otg_core_if_t * _core_if)
- +{
- + hprt0_data_t hprt0;
- + hprt0.d32 = DWC_READ_REG32(_core_if->host_if->hprt0);
- + hprt0.b.prtena = 0;
- + hprt0.b.prtconndet = 0;
- + hprt0.b.prtenchng = 0;
- + hprt0.b.prtovrcurrchng = 0;
- + return hprt0.d32;
- +}
- +
- +/**@}*/
- +
- +/** @name Common CIL Functions
- + * The following functions support managing the DWC_otg controller in either
- + * device or host mode.
- + */
- +/**@{*/
- +
- +extern void dwc_otg_read_packet(dwc_otg_core_if_t * core_if,
- + uint8_t * dest, uint16_t bytes);
- +
- +extern void dwc_otg_flush_tx_fifo(dwc_otg_core_if_t * _core_if, const int _num);
- +extern void dwc_otg_flush_rx_fifo(dwc_otg_core_if_t * _core_if);
- +extern void dwc_otg_core_reset(dwc_otg_core_if_t * _core_if);
- +
- +/**
- + * This function returns the Core Interrupt register.
- + */
- +static inline uint32_t dwc_otg_read_core_intr(dwc_otg_core_if_t * core_if)
- +{
- + return (DWC_READ_REG32(&core_if->core_global_regs->gintsts) &
- + DWC_READ_REG32(&core_if->core_global_regs->gintmsk));
- +}
- +
- +/**
- + * This function returns the OTG Interrupt register.
- + */
- +static inline uint32_t dwc_otg_read_otg_intr(dwc_otg_core_if_t * core_if)
- +{
- + return (DWC_READ_REG32(&core_if->core_global_regs->gotgint));
- +}
- +
- +/**
- + * This function reads the Device All Endpoints Interrupt register and
- + * returns the IN endpoint interrupt bits.
- + */
- +static inline uint32_t dwc_otg_read_dev_all_in_ep_intr(dwc_otg_core_if_t *
- + core_if)
- +{
- +
- + uint32_t v;
- +
- + if (core_if->multiproc_int_enable) {
- + v = DWC_READ_REG32(&core_if->dev_if->
- + dev_global_regs->deachint) &
- + DWC_READ_REG32(&core_if->
- + dev_if->dev_global_regs->deachintmsk);
- + } else {
- + v = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->daint) &
- + DWC_READ_REG32(&core_if->dev_if->dev_global_regs->daintmsk);
- + }
- + return (v & 0xffff);
- +}
- +
- +/**
- + * This function reads the Device All Endpoints Interrupt register and
- + * returns the OUT endpoint interrupt bits.
- + */
- +static inline uint32_t dwc_otg_read_dev_all_out_ep_intr(dwc_otg_core_if_t *
- + core_if)
- +{
- + uint32_t v;
- +
- + if (core_if->multiproc_int_enable) {
- + v = DWC_READ_REG32(&core_if->dev_if->
- + dev_global_regs->deachint) &
- + DWC_READ_REG32(&core_if->
- + dev_if->dev_global_regs->deachintmsk);
- + } else {
- + v = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->daint) &
- + DWC_READ_REG32(&core_if->dev_if->dev_global_regs->daintmsk);
- + }
- +
- + return ((v & 0xffff0000) >> 16);
- +}
- +
- +/**
- + * This function returns the Device IN EP Interrupt register
- + */
- +static inline uint32_t dwc_otg_read_dev_in_ep_intr(dwc_otg_core_if_t * core_if,
- + dwc_ep_t * ep)
- +{
- + dwc_otg_dev_if_t *dev_if = core_if->dev_if;
- + uint32_t v, msk, emp;
- +
- + if (core_if->multiproc_int_enable) {
- + msk =
- + DWC_READ_REG32(&dev_if->
- + dev_global_regs->diepeachintmsk[ep->num]);
- + emp =
- + DWC_READ_REG32(&dev_if->
- + dev_global_regs->dtknqr4_fifoemptymsk);
- + msk |= ((emp >> ep->num) & 0x1) << 7;
- + v = DWC_READ_REG32(&dev_if->in_ep_regs[ep->num]->diepint) & msk;
- + } else {
- + msk = DWC_READ_REG32(&dev_if->dev_global_regs->diepmsk);
- + emp =
- + DWC_READ_REG32(&dev_if->
- + dev_global_regs->dtknqr4_fifoemptymsk);
- + msk |= ((emp >> ep->num) & 0x1) << 7;
- + v = DWC_READ_REG32(&dev_if->in_ep_regs[ep->num]->diepint) & msk;
- + }
- +
- + return v;
- +}
- +
- +/**
- + * This function returns the Device OUT EP Interrupt register
- + */
- +static inline uint32_t dwc_otg_read_dev_out_ep_intr(dwc_otg_core_if_t *
- + _core_if, dwc_ep_t * _ep)
- +{
- + dwc_otg_dev_if_t *dev_if = _core_if->dev_if;
- + uint32_t v;
- + doepmsk_data_t msk = {.d32 = 0 };
- +
- + if (_core_if->multiproc_int_enable) {
- + msk.d32 =
- + DWC_READ_REG32(&dev_if->
- + dev_global_regs->doepeachintmsk[_ep->num]);
- + if (_core_if->pti_enh_enable) {
- + msk.b.pktdrpsts = 1;
- + }
- + v = DWC_READ_REG32(&dev_if->
- + out_ep_regs[_ep->num]->doepint) & msk.d32;
- + } else {
- + msk.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->doepmsk);
- + if (_core_if->pti_enh_enable) {
- + msk.b.pktdrpsts = 1;
- + }
- + v = DWC_READ_REG32(&dev_if->
- + out_ep_regs[_ep->num]->doepint) & msk.d32;
- + }
- + return v;
- +}
- +
- +/**
- + * This function returns the Host All Channel Interrupt register
- + */
- +static inline uint32_t dwc_otg_read_host_all_channels_intr(dwc_otg_core_if_t *
- + _core_if)
- +{
- + return (DWC_READ_REG32(&_core_if->host_if->host_global_regs->haint));
- +}
- +
- +static inline uint32_t dwc_otg_read_host_channel_intr(dwc_otg_core_if_t *
- + _core_if, dwc_hc_t * _hc)
- +{
- + return (DWC_READ_REG32
- + (&_core_if->host_if->hc_regs[_hc->hc_num]->hcint));
- +}
- +
- +/**
- + * This function returns the mode of the operation, host or device.
- + *
- + * @return 0 - Device Mode, 1 - Host Mode
- + */
- +static inline uint32_t dwc_otg_mode(dwc_otg_core_if_t * _core_if)
- +{
- + return (DWC_READ_REG32(&_core_if->core_global_regs->gintsts) & 0x1);
- +}
- +
- +/**@}*/
- +
- +/**
- + * DWC_otg CIL callback structure. This structure allows the HCD and
- + * PCD to register functions used for starting and stopping the PCD
- + * and HCD for role change on for a DRD.
- + */
- +typedef struct dwc_otg_cil_callbacks {
- + /** Start function for role change */
- + int (*start) (void *_p);
- + /** Stop Function for role change */
- + int (*stop) (void *_p);
- + /** Disconnect Function for role change */
- + int (*disconnect) (void *_p);
- + /** Resume/Remote wakeup Function */
- + int (*resume_wakeup) (void *_p);
- + /** Suspend function */
- + int (*suspend) (void *_p);
- + /** Session Start (SRP) */
- + int (*session_start) (void *_p);
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- + /** Sleep (switch to L0 state) */
- + int (*sleep) (void *_p);
- +#endif
- + /** Pointer passed to start() and stop() */
- + void *p;
- +} dwc_otg_cil_callbacks_t;
- +
- +extern void dwc_otg_cil_register_pcd_callbacks(dwc_otg_core_if_t * _core_if,
- + dwc_otg_cil_callbacks_t * _cb,
- + void *_p);
- +extern void dwc_otg_cil_register_hcd_callbacks(dwc_otg_core_if_t * _core_if,
- + dwc_otg_cil_callbacks_t * _cb,
- + void *_p);
- +
- +void dwc_otg_initiate_srp(dwc_otg_core_if_t * core_if);
- +
- +//////////////////////////////////////////////////////////////////////
- +/** Start the HCD. Helper function for using the HCD callbacks.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + */
- +static inline void cil_hcd_start(dwc_otg_core_if_t * core_if)
- +{
- + if (core_if->hcd_cb && core_if->hcd_cb->start) {
- + core_if->hcd_cb->start(core_if->hcd_cb->p);
- + }
- +}
- +
- +/** Stop the HCD. Helper function for using the HCD callbacks.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + */
- +static inline void cil_hcd_stop(dwc_otg_core_if_t * core_if)
- +{
- + if (core_if->hcd_cb && core_if->hcd_cb->stop) {
- + core_if->hcd_cb->stop(core_if->hcd_cb->p);
- + }
- +}
- +
- +/** Disconnect the HCD. Helper function for using the HCD callbacks.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + */
- +static inline void cil_hcd_disconnect(dwc_otg_core_if_t * core_if)
- +{
- + if (core_if->hcd_cb && core_if->hcd_cb->disconnect) {
- + core_if->hcd_cb->disconnect(core_if->hcd_cb->p);
- + }
- +}
- +
- +/** Inform the HCD the a New Session has begun. Helper function for
- + * using the HCD callbacks.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + */
- +static inline void cil_hcd_session_start(dwc_otg_core_if_t * core_if)
- +{
- + if (core_if->hcd_cb && core_if->hcd_cb->session_start) {
- + core_if->hcd_cb->session_start(core_if->hcd_cb->p);
- + }
- +}
- +
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- +/**
- + * Inform the HCD about LPM sleep.
- + * Helper function for using the HCD callbacks.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + */
- +static inline void cil_hcd_sleep(dwc_otg_core_if_t * core_if)
- +{
- + if (core_if->hcd_cb && core_if->hcd_cb->sleep) {
- + core_if->hcd_cb->sleep(core_if->hcd_cb->p);
- + }
- +}
- +#endif
- +
- +/** Resume the HCD. Helper function for using the HCD callbacks.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + */
- +static inline void cil_hcd_resume(dwc_otg_core_if_t * core_if)
- +{
- + if (core_if->hcd_cb && core_if->hcd_cb->resume_wakeup) {
- + core_if->hcd_cb->resume_wakeup(core_if->hcd_cb->p);
- + }
- +}
- +
- +/** Start the PCD. Helper function for using the PCD callbacks.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + */
- +static inline void cil_pcd_start(dwc_otg_core_if_t * core_if)
- +{
- + if (core_if->pcd_cb && core_if->pcd_cb->start) {
- + core_if->pcd_cb->start(core_if->pcd_cb->p);
- + }
- +}
- +
- +/** Stop the PCD. Helper function for using the PCD callbacks.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + */
- +static inline void cil_pcd_stop(dwc_otg_core_if_t * core_if)
- +{
- + if (core_if->pcd_cb && core_if->pcd_cb->stop) {
- + core_if->pcd_cb->stop(core_if->pcd_cb->p);
- + }
- +}
- +
- +/** Suspend the PCD. Helper function for using the PCD callbacks.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + */
- +static inline void cil_pcd_suspend(dwc_otg_core_if_t * core_if)
- +{
- + if (core_if->pcd_cb && core_if->pcd_cb->suspend) {
- + core_if->pcd_cb->suspend(core_if->pcd_cb->p);
- + }
- +}
- +
- +/** Resume the PCD. Helper function for using the PCD callbacks.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + */
- +static inline void cil_pcd_resume(dwc_otg_core_if_t * core_if)
- +{
- + if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) {
- + core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p);
- + }
- +}
- +
- +//////////////////////////////////////////////////////////////////////
- +
- +#endif
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c
- @@ -0,0 +1,1601 @@
- +/* ==========================================================================
- + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_cil_intr.c $
- + * $Revision: #32 $
- + * $Date: 2012/08/10 $
- + * $Change: 2047372 $
- + *
- + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
- + * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
- + * otherwise expressly agreed to in writing between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product under
- + * any End User Software License Agreement or Agreement for Licensed Product
- + * with Synopsys or any supplement thereto. You are permitted to use and
- + * redistribute this Software in source and binary forms, with or without
- + * modification, provided that redistributions of source code must retain this
- + * notice. You may not view, use, disclose, copy or distribute this file or
- + * any information contained herein except pursuant to this license grant from
- + * Synopsys. If you do not agree with this notice, including the disclaimer
- + * below, then you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
- + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- + * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
- + * ========================================================================== */
- +
- +/** @file
- + *
- + * The Core Interface Layer provides basic services for accessing and
- + * managing the DWC_otg hardware. These services are used by both the
- + * Host Controller Driver and the Peripheral Controller Driver.
- + *
- + * This file contains the Common Interrupt handlers.
- + */
- +#include "dwc_os.h"
- +#include "dwc_otg_regs.h"
- +#include "dwc_otg_cil.h"
- +#include "dwc_otg_driver.h"
- +#include "dwc_otg_pcd.h"
- +#include "dwc_otg_hcd.h"
- +
- +#ifdef DEBUG
- +inline const char *op_state_str(dwc_otg_core_if_t * core_if)
- +{
- + return (core_if->op_state == A_HOST ? "a_host" :
- + (core_if->op_state == A_SUSPEND ? "a_suspend" :
- + (core_if->op_state == A_PERIPHERAL ? "a_peripheral" :
- + (core_if->op_state == B_PERIPHERAL ? "b_peripheral" :
- + (core_if->op_state == B_HOST ? "b_host" : "unknown")))));
- +}
- +#endif
- +
- +/** This function will log a debug message
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + */
- +int32_t dwc_otg_handle_mode_mismatch_intr(dwc_otg_core_if_t * core_if)
- +{
- + gintsts_data_t gintsts;
- + DWC_WARN("Mode Mismatch Interrupt: currently in %s mode\n",
- + dwc_otg_mode(core_if) ? "Host" : "Device");
- +
- + /* Clear interrupt */
- + gintsts.d32 = 0;
- + gintsts.b.modemismatch = 1;
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
- + return 1;
- +}
- +
- +/**
- + * This function handles the OTG Interrupts. It reads the OTG
- + * Interrupt Register (GOTGINT) to determine what interrupt has
- + * occurred.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + */
- +int32_t dwc_otg_handle_otg_intr(dwc_otg_core_if_t * core_if)
- +{
- + dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
- + gotgint_data_t gotgint;
- + gotgctl_data_t gotgctl;
- + gintmsk_data_t gintmsk;
- + gpwrdn_data_t gpwrdn;
- +
- + gotgint.d32 = DWC_READ_REG32(&global_regs->gotgint);
- + gotgctl.d32 = DWC_READ_REG32(&global_regs->gotgctl);
- + DWC_DEBUGPL(DBG_CIL, "++OTG Interrupt gotgint=%0x [%s]\n", gotgint.d32,
- + op_state_str(core_if));
- +
- + if (gotgint.b.sesenddet) {
- + DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
- + "Session End Detected++ (%s)\n",
- + op_state_str(core_if));
- + gotgctl.d32 = DWC_READ_REG32(&global_regs->gotgctl);
- +
- + if (core_if->op_state == B_HOST) {
- + cil_pcd_start(core_if);
- + core_if->op_state = B_PERIPHERAL;
- + } else {
- + /* If not B_HOST and Device HNP still set. HNP
- + * Did not succeed!*/
- + if (gotgctl.b.devhnpen) {
- + DWC_DEBUGPL(DBG_ANY, "Session End Detected\n");
- + __DWC_ERROR("Device Not Connected/Responding!\n");
- + }
- +
- + /* If Session End Detected the B-Cable has
- + * been disconnected. */
- + /* Reset PCD and Gadget driver to a
- + * clean state. */
- + core_if->lx_state = DWC_OTG_L0;
- + DWC_SPINUNLOCK(core_if->lock);
- + cil_pcd_stop(core_if);
- + DWC_SPINLOCK(core_if->lock);
- +
- + if (core_if->adp_enable) {
- + if (core_if->power_down == 2) {
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pwrdnswtch = 1;
- + DWC_MODIFY_REG32(&core_if->
- + core_global_regs->
- + gpwrdn, gpwrdn.d32, 0);
- + }
- +
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pmuintsel = 1;
- + gpwrdn.b.pmuactv = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->
- + gpwrdn, 0, gpwrdn.d32);
- +
- + dwc_otg_adp_sense_start(core_if);
- + }
- + }
- +
- + gotgctl.d32 = 0;
- + gotgctl.b.devhnpen = 1;
- + DWC_MODIFY_REG32(&global_regs->gotgctl, gotgctl.d32, 0);
- + }
- + if (gotgint.b.sesreqsucstschng) {
- + DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
- + "Session Reqeust Success Status Change++\n");
- + gotgctl.d32 = DWC_READ_REG32(&global_regs->gotgctl);
- + if (gotgctl.b.sesreqscs) {
- +
- + if ((core_if->core_params->phy_type ==
- + DWC_PHY_TYPE_PARAM_FS) && (core_if->core_params->i2c_enable)) {
- + core_if->srp_success = 1;
- + } else {
- + DWC_SPINUNLOCK(core_if->lock);
- + cil_pcd_resume(core_if);
- + DWC_SPINLOCK(core_if->lock);
- + /* Clear Session Request */
- + gotgctl.d32 = 0;
- + gotgctl.b.sesreq = 1;
- + DWC_MODIFY_REG32(&global_regs->gotgctl,
- + gotgctl.d32, 0);
- + }
- + }
- + }
- + if (gotgint.b.hstnegsucstschng) {
- + /* Print statements during the HNP interrupt handling
- + * can cause it to fail.*/
- + gotgctl.d32 = DWC_READ_REG32(&global_regs->gotgctl);
- + /* WA for 3.00a- HW is not setting cur_mode, even sometimes
- + * this does not help*/
- + if (core_if->snpsid >= OTG_CORE_REV_3_00a)
- + dwc_udelay(100);
- + if (gotgctl.b.hstnegscs) {
- + if (dwc_otg_is_host_mode(core_if)) {
- + core_if->op_state = B_HOST;
- + /*
- + * Need to disable SOF interrupt immediately.
- + * When switching from device to host, the PCD
- + * interrupt handler won't handle the
- + * interrupt if host mode is already set. The
- + * HCD interrupt handler won't get called if
- + * the HCD state is HALT. This means that the
- + * interrupt does not get handled and Linux
- + * complains loudly.
- + */
- + gintmsk.d32 = 0;
- + gintmsk.b.sofintr = 1;
- + DWC_MODIFY_REG32(&global_regs->gintmsk,
- + gintmsk.d32, 0);
- + /* Call callback function with spin lock released */
- + DWC_SPINUNLOCK(core_if->lock);
- + cil_pcd_stop(core_if);
- + /*
- + * Initialize the Core for Host mode.
- + */
- + cil_hcd_start(core_if);
- + DWC_SPINLOCK(core_if->lock);
- + core_if->op_state = B_HOST;
- + }
- + } else {
- + gotgctl.d32 = 0;
- + gotgctl.b.hnpreq = 1;
- + gotgctl.b.devhnpen = 1;
- + DWC_MODIFY_REG32(&global_regs->gotgctl, gotgctl.d32, 0);
- + DWC_DEBUGPL(DBG_ANY, "HNP Failed\n");
- + __DWC_ERROR("Device Not Connected/Responding\n");
- + }
- + }
- + if (gotgint.b.hstnegdet) {
- + /* The disconnect interrupt is set at the same time as
- + * Host Negotiation Detected. During the mode
- + * switch all interrupts are cleared so the disconnect
- + * interrupt handler will not get executed.
- + */
- + DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
- + "Host Negotiation Detected++ (%s)\n",
- + (dwc_otg_is_host_mode(core_if) ? "Host" :
- + "Device"));
- + if (dwc_otg_is_device_mode(core_if)) {
- + DWC_DEBUGPL(DBG_ANY, "a_suspend->a_peripheral (%d)\n",
- + core_if->op_state);
- + DWC_SPINUNLOCK(core_if->lock);
- + cil_hcd_disconnect(core_if);
- + cil_pcd_start(core_if);
- + DWC_SPINLOCK(core_if->lock);
- + core_if->op_state = A_PERIPHERAL;
- + } else {
- + /*
- + * Need to disable SOF interrupt immediately. When
- + * switching from device to host, the PCD interrupt
- + * handler won't handle the interrupt if host mode is
- + * already set. The HCD interrupt handler won't get
- + * called if the HCD state is HALT. This means that
- + * the interrupt does not get handled and Linux
- + * complains loudly.
- + */
- + gintmsk.d32 = 0;
- + gintmsk.b.sofintr = 1;
- + DWC_MODIFY_REG32(&global_regs->gintmsk, gintmsk.d32, 0);
- + DWC_SPINUNLOCK(core_if->lock);
- + cil_pcd_stop(core_if);
- + cil_hcd_start(core_if);
- + DWC_SPINLOCK(core_if->lock);
- + core_if->op_state = A_HOST;
- + }
- + }
- + if (gotgint.b.adevtoutchng) {
- + DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
- + "A-Device Timeout Change++\n");
- + }
- + if (gotgint.b.debdone) {
- + DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " "Debounce Done++\n");
- + }
- +
- + /* Clear GOTGINT */
- + DWC_WRITE_REG32(&core_if->core_global_regs->gotgint, gotgint.d32);
- +
- + return 1;
- +}
- +
- +void w_conn_id_status_change(void *p)
- +{
- + dwc_otg_core_if_t *core_if = p;
- + uint32_t count = 0;
- + gotgctl_data_t gotgctl = {.d32 = 0 };
- +
- + gotgctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->gotgctl);
- + DWC_DEBUGPL(DBG_CIL, "gotgctl=%0x\n", gotgctl.d32);
- + DWC_DEBUGPL(DBG_CIL, "gotgctl.b.conidsts=%d\n", gotgctl.b.conidsts);
- +
- + /* B-Device connector (Device Mode) */
- + if (gotgctl.b.conidsts) {
- + /* Wait for switch to device mode. */
- + while (!dwc_otg_is_device_mode(core_if)) {
- + DWC_PRINTF("Waiting for Peripheral Mode, Mode=%s\n",
- + (dwc_otg_is_host_mode(core_if) ? "Host" :
- + "Peripheral"));
- + dwc_mdelay(100);
- + if (++count > 10000)
- + break;
- + }
- + DWC_ASSERT(++count < 10000,
- + "Connection id status change timed out");
- + core_if->op_state = B_PERIPHERAL;
- + dwc_otg_core_init(core_if);
- + dwc_otg_enable_global_interrupts(core_if);
- + cil_pcd_start(core_if);
- + } else {
- + /* A-Device connector (Host Mode) */
- + while (!dwc_otg_is_host_mode(core_if)) {
- + DWC_PRINTF("Waiting for Host Mode, Mode=%s\n",
- + (dwc_otg_is_host_mode(core_if) ? "Host" :
- + "Peripheral"));
- + dwc_mdelay(100);
- + if (++count > 10000)
- + break;
- + }
- + DWC_ASSERT(++count < 10000,
- + "Connection id status change timed out");
- + core_if->op_state = A_HOST;
- + /*
- + * Initialize the Core for Host mode.
- + */
- + dwc_otg_core_init(core_if);
- + dwc_otg_enable_global_interrupts(core_if);
- + cil_hcd_start(core_if);
- + }
- +}
- +
- +/**
- + * This function handles the Connector ID Status Change Interrupt. It
- + * reads the OTG Interrupt Register (GOTCTL) to determine whether this
- + * is a Device to Host Mode transition or a Host Mode to Device
- + * Transition.
- + *
- + * This only occurs when the cable is connected/removed from the PHY
- + * connector.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + */
- +int32_t dwc_otg_handle_conn_id_status_change_intr(dwc_otg_core_if_t * core_if)
- +{
- +
- + /*
- + * Need to disable SOF interrupt immediately. If switching from device
- + * to host, the PCD interrupt handler won't handle the interrupt if
- + * host mode is already set. The HCD interrupt handler won't get
- + * called if the HCD state is HALT. This means that the interrupt does
- + * not get handled and Linux complains loudly.
- + */
- + gintmsk_data_t gintmsk = {.d32 = 0 };
- + gintsts_data_t gintsts = {.d32 = 0 };
- +
- + gintmsk.b.sofintr = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, gintmsk.d32, 0);
- +
- + DWC_DEBUGPL(DBG_CIL,
- + " ++Connector ID Status Change Interrupt++ (%s)\n",
- + (dwc_otg_is_host_mode(core_if) ? "Host" : "Device"));
- +
- + DWC_SPINUNLOCK(core_if->lock);
- +
- + /*
- + * Need to schedule a work, as there are possible DELAY function calls
- + * Release lock before scheduling workq as it holds spinlock during scheduling
- + */
- +
- + DWC_WORKQ_SCHEDULE(core_if->wq_otg, w_conn_id_status_change,
- + core_if, "connection id status change");
- + DWC_SPINLOCK(core_if->lock);
- +
- + /* Set flag and clear interrupt */
- + gintsts.b.conidstschng = 1;
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
- +
- + return 1;
- +}
- +
- +/**
- + * This interrupt indicates that a device is initiating the Session
- + * Request Protocol to request the host to turn on bus power so a new
- + * session can begin. The handler responds by turning on bus power. If
- + * the DWC_otg controller is in low power mode, the handler brings the
- + * controller out of low power mode before turning on bus power.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + */
- +int32_t dwc_otg_handle_session_req_intr(dwc_otg_core_if_t * core_if)
- +{
- + gintsts_data_t gintsts;
- +
- +#ifndef DWC_HOST_ONLY
- + DWC_DEBUGPL(DBG_ANY, "++Session Request Interrupt++\n");
- +
- + if (dwc_otg_is_device_mode(core_if)) {
- + DWC_PRINTF("SRP: Device mode\n");
- + } else {
- + hprt0_data_t hprt0;
- + DWC_PRINTF("SRP: Host mode\n");
- +
- + /* Turn on the port power bit. */
- + hprt0.d32 = dwc_otg_read_hprt0(core_if);
- + hprt0.b.prtpwr = 1;
- + DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
- +
- + /* Start the Connection timer. So a message can be displayed
- + * if connect does not occur within 10 seconds. */
- + cil_hcd_session_start(core_if);
- + }
- +#endif
- +
- + /* Clear interrupt */
- + gintsts.d32 = 0;
- + gintsts.b.sessreqintr = 1;
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
- +
- + return 1;
- +}
- +
- +void w_wakeup_detected(void *p)
- +{
- + dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *) p;
- + /*
- + * Clear the Resume after 70ms. (Need 20 ms minimum. Use 70 ms
- + * so that OPT tests pass with all PHYs).
- + */
- + hprt0_data_t hprt0 = {.d32 = 0 };
- +#if 0
- + pcgcctl_data_t pcgcctl = {.d32 = 0 };
- + /* Restart the Phy Clock */
- + pcgcctl.b.stoppclk = 1;
- + DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0);
- + dwc_udelay(10);
- +#endif //0
- + hprt0.d32 = dwc_otg_read_hprt0(core_if);
- + DWC_DEBUGPL(DBG_ANY, "Resume: HPRT0=%0x\n", hprt0.d32);
- +// dwc_mdelay(70);
- + hprt0.b.prtres = 0; /* Resume */
- + DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
- + DWC_DEBUGPL(DBG_ANY, "Clear Resume: HPRT0=%0x\n",
- + DWC_READ_REG32(core_if->host_if->hprt0));
- +
- + cil_hcd_resume(core_if);
- +
- + /** Change to L0 state*/
- + core_if->lx_state = DWC_OTG_L0;
- +}
- +
- +/**
- + * This interrupt indicates that the DWC_otg controller has detected a
- + * resume or remote wakeup sequence. If the DWC_otg controller is in
- + * low power mode, the handler must brings the controller out of low
- + * power mode. The controller automatically begins resume
- + * signaling. The handler schedules a time to stop resume signaling.
- + */
- +int32_t dwc_otg_handle_wakeup_detected_intr(dwc_otg_core_if_t * core_if)
- +{
- + gintsts_data_t gintsts;
- +
- + DWC_DEBUGPL(DBG_ANY,
- + "++Resume and Remote Wakeup Detected Interrupt++\n");
- +
- + DWC_PRINTF("%s lxstate = %d\n", __func__, core_if->lx_state);
- +
- + if (dwc_otg_is_device_mode(core_if)) {
- + dctl_data_t dctl = {.d32 = 0 };
- + DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n",
- + DWC_READ_REG32(&core_if->dev_if->dev_global_regs->
- + dsts));
- + if (core_if->lx_state == DWC_OTG_L2) {
- +#ifdef PARTIAL_POWER_DOWN
- + if (core_if->hwcfg4.b.power_optimiz) {
- + pcgcctl_data_t power = {.d32 = 0 };
- +
- + power.d32 = DWC_READ_REG32(core_if->pcgcctl);
- + DWC_DEBUGPL(DBG_CIL, "PCGCCTL=%0x\n",
- + power.d32);
- +
- + power.b.stoppclk = 0;
- + DWC_WRITE_REG32(core_if->pcgcctl, power.d32);
- +
- + power.b.pwrclmp = 0;
- + DWC_WRITE_REG32(core_if->pcgcctl, power.d32);
- +
- + power.b.rstpdwnmodule = 0;
- + DWC_WRITE_REG32(core_if->pcgcctl, power.d32);
- + }
- +#endif
- + /* Clear the Remote Wakeup Signaling */
- + dctl.b.rmtwkupsig = 1;
- + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->
- + dctl, dctl.d32, 0);
- +
- + DWC_SPINUNLOCK(core_if->lock);
- + if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) {
- + core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p);
- + }
- + DWC_SPINLOCK(core_if->lock);
- + } else {
- + glpmcfg_data_t lpmcfg;
- + lpmcfg.d32 =
- + DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
- + lpmcfg.b.hird_thres &= (~(1 << 4));
- + DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg,
- + lpmcfg.d32);
- + }
- + /** Change to L0 state*/
- + core_if->lx_state = DWC_OTG_L0;
- + } else {
- + if (core_if->lx_state != DWC_OTG_L1) {
- + pcgcctl_data_t pcgcctl = {.d32 = 0 };
- +
- + /* Restart the Phy Clock */
- + pcgcctl.b.stoppclk = 1;
- + DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0);
- + DWC_TIMER_SCHEDULE(core_if->wkp_timer, 71);
- + } else {
- + /** Change to L0 state*/
- + core_if->lx_state = DWC_OTG_L0;
- + }
- + }
- +
- + /* Clear interrupt */
- + gintsts.d32 = 0;
- + gintsts.b.wkupintr = 1;
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
- +
- + return 1;
- +}
- +
- +/**
- + * This interrupt indicates that the Wakeup Logic has detected a
- + * Device disconnect.
- + */
- +static int32_t dwc_otg_handle_pwrdn_disconnect_intr(dwc_otg_core_if_t *core_if)
- +{
- + gpwrdn_data_t gpwrdn = { .d32 = 0 };
- + gpwrdn_data_t gpwrdn_temp = { .d32 = 0 };
- + gpwrdn_temp.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
- +
- + DWC_PRINTF("%s called\n", __FUNCTION__);
- +
- + if (!core_if->hibernation_suspend) {
- + DWC_PRINTF("Already exited from Hibernation\n");
- + return 1;
- + }
- +
- + /* Switch on the voltage to the core */
- + gpwrdn.b.pwrdnswtch = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- + dwc_udelay(10);
- +
- + /* Reset the core */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pwrdnrstn = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- + dwc_udelay(10);
- +
- + /* Disable power clamps*/
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pwrdnclmp = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- +
- + /* Remove reset the core signal */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pwrdnrstn = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
- + dwc_udelay(10);
- +
- + /* Disable PMU interrupt */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pmuintsel = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- +
- + core_if->hibernation_suspend = 0;
- +
- + /* Disable PMU */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pmuactv = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- + dwc_udelay(10);
- +
- + if (gpwrdn_temp.b.idsts) {
- + core_if->op_state = B_PERIPHERAL;
- + dwc_otg_core_init(core_if);
- + dwc_otg_enable_global_interrupts(core_if);
- + cil_pcd_start(core_if);
- + } else {
- + core_if->op_state = A_HOST;
- + dwc_otg_core_init(core_if);
- + dwc_otg_enable_global_interrupts(core_if);
- + cil_hcd_start(core_if);
- + }
- +
- + return 1;
- +}
- +
- +/**
- + * This interrupt indicates that the Wakeup Logic has detected a
- + * remote wakeup sequence.
- + */
- +static int32_t dwc_otg_handle_pwrdn_wakeup_detected_intr(dwc_otg_core_if_t * core_if)
- +{
- + gpwrdn_data_t gpwrdn = {.d32 = 0 };
- + DWC_DEBUGPL(DBG_ANY,
- + "++Powerdown Remote Wakeup Detected Interrupt++\n");
- +
- + if (!core_if->hibernation_suspend) {
- + DWC_PRINTF("Already exited from Hibernation\n");
- + return 1;
- + }
- +
- + gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
- + if (gpwrdn.b.idsts) { // Device Mode
- + if ((core_if->power_down == 2)
- + && (core_if->hibernation_suspend == 1)) {
- + dwc_otg_device_hibernation_restore(core_if, 0, 0);
- + }
- + } else {
- + if ((core_if->power_down == 2)
- + && (core_if->hibernation_suspend == 1)) {
- + dwc_otg_host_hibernation_restore(core_if, 1, 0);
- + }
- + }
- + return 1;
- +}
- +
- +static int32_t dwc_otg_handle_pwrdn_idsts_change(dwc_otg_device_t *otg_dev)
- +{
- + gpwrdn_data_t gpwrdn = {.d32 = 0 };
- + gpwrdn_data_t gpwrdn_temp = {.d32 = 0 };
- + dwc_otg_core_if_t *core_if = otg_dev->core_if;
- +
- + DWC_DEBUGPL(DBG_ANY, "%s called\n", __FUNCTION__);
- + gpwrdn_temp.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
- + if (core_if->power_down == 2) {
- + if (!core_if->hibernation_suspend) {
- + DWC_PRINTF("Already exited from Hibernation\n");
- + return 1;
- + }
- + DWC_DEBUGPL(DBG_ANY, "Exit from hibernation on ID sts change\n");
- + /* Switch on the voltage to the core */
- + gpwrdn.b.pwrdnswtch = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- + dwc_udelay(10);
- +
- + /* Reset the core */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pwrdnrstn = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- + dwc_udelay(10);
- +
- + /* Disable power clamps */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pwrdnclmp = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- +
- + /* Remove reset the core signal */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pwrdnrstn = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
- + dwc_udelay(10);
- +
- + /* Disable PMU interrupt */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pmuintsel = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- +
- + /*Indicates that we are exiting from hibernation */
- + core_if->hibernation_suspend = 0;
- +
- + /* Disable PMU */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pmuactv = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- + dwc_udelay(10);
- +
- + gpwrdn.d32 = core_if->gr_backup->gpwrdn_local;
- + if (gpwrdn.b.dis_vbus == 1) {
- + gpwrdn.d32 = 0;
- + gpwrdn.b.dis_vbus = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- + }
- +
- + if (gpwrdn_temp.b.idsts) {
- + core_if->op_state = B_PERIPHERAL;
- + dwc_otg_core_init(core_if);
- + dwc_otg_enable_global_interrupts(core_if);
- + cil_pcd_start(core_if);
- + } else {
- + core_if->op_state = A_HOST;
- + dwc_otg_core_init(core_if);
- + dwc_otg_enable_global_interrupts(core_if);
- + cil_hcd_start(core_if);
- + }
- + }
- +
- + if (core_if->adp_enable) {
- + uint8_t is_host = 0;
- + DWC_SPINUNLOCK(core_if->lock);
- + /* Change the core_if's lock to hcd/pcd lock depend on mode? */
- +#ifndef DWC_HOST_ONLY
- + if (gpwrdn_temp.b.idsts)
- + core_if->lock = otg_dev->pcd->lock;
- +#endif
- +#ifndef DWC_DEVICE_ONLY
- + if (!gpwrdn_temp.b.idsts) {
- + core_if->lock = otg_dev->hcd->lock;
- + is_host = 1;
- + }
- +#endif
- + DWC_PRINTF("RESTART ADP\n");
- + if (core_if->adp.probe_enabled)
- + dwc_otg_adp_probe_stop(core_if);
- + if (core_if->adp.sense_enabled)
- + dwc_otg_adp_sense_stop(core_if);
- + if (core_if->adp.sense_timer_started)
- + DWC_TIMER_CANCEL(core_if->adp.sense_timer);
- + if (core_if->adp.vbuson_timer_started)
- + DWC_TIMER_CANCEL(core_if->adp.vbuson_timer);
- + core_if->adp.probe_timer_values[0] = -1;
- + core_if->adp.probe_timer_values[1] = -1;
- + core_if->adp.sense_timer_started = 0;
- + core_if->adp.vbuson_timer_started = 0;
- + core_if->adp.probe_counter = 0;
- + core_if->adp.gpwrdn = 0;
- +
- + /* Disable PMU and restart ADP */
- + gpwrdn_temp.d32 = 0;
- + gpwrdn_temp.b.pmuactv = 1;
- + gpwrdn_temp.b.pmuintsel = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- + DWC_PRINTF("Check point 1\n");
- + dwc_mdelay(110);
- + dwc_otg_adp_start(core_if, is_host);
- + DWC_SPINLOCK(core_if->lock);
- + }
- +
- +
- + return 1;
- +}
- +
- +static int32_t dwc_otg_handle_pwrdn_session_change(dwc_otg_core_if_t * core_if)
- +{
- + gpwrdn_data_t gpwrdn = {.d32 = 0 };
- + int32_t otg_cap_param = core_if->core_params->otg_cap;
- + DWC_DEBUGPL(DBG_ANY, "%s called\n", __FUNCTION__);
- +
- + gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
- + if (core_if->power_down == 2) {
- + if (!core_if->hibernation_suspend) {
- + DWC_PRINTF("Already exited from Hibernation\n");
- + return 1;
- + }
- +
- + if ((otg_cap_param != DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE ||
- + otg_cap_param != DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE) &&
- + gpwrdn.b.bsessvld == 0) {
- + /* Save gpwrdn register for further usage if stschng interrupt */
- + core_if->gr_backup->gpwrdn_local =
- + DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
- + /*Exit from ISR and wait for stschng interrupt with bsessvld = 1 */
- + return 1;
- + }
- +
- + /* Switch on the voltage to the core */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pwrdnswtch = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- + dwc_udelay(10);
- +
- + /* Reset the core */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pwrdnrstn = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- + dwc_udelay(10);
- +
- + /* Disable power clamps */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pwrdnclmp = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- +
- + /* Remove reset the core signal */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pwrdnrstn = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
- + dwc_udelay(10);
- +
- + /* Disable PMU interrupt */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pmuintsel = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- + dwc_udelay(10);
- +
- + /*Indicates that we are exiting from hibernation */
- + core_if->hibernation_suspend = 0;
- +
- + /* Disable PMU */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pmuactv = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- + dwc_udelay(10);
- +
- + core_if->op_state = B_PERIPHERAL;
- + dwc_otg_core_init(core_if);
- + dwc_otg_enable_global_interrupts(core_if);
- + cil_pcd_start(core_if);
- +
- + if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE ||
- + otg_cap_param == DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE) {
- + /*
- + * Initiate SRP after initial ADP probe.
- + */
- + dwc_otg_initiate_srp(core_if);
- + }
- + }
- +
- + return 1;
- +}
- +/**
- + * This interrupt indicates that the Wakeup Logic has detected a
- + * status change either on IDDIG or BSessVld.
- + */
- +static uint32_t dwc_otg_handle_pwrdn_stschng_intr(dwc_otg_device_t *otg_dev)
- +{
- + uint32_t retval = 0;
- + gpwrdn_data_t gpwrdn = {.d32 = 0 };
- + gpwrdn_data_t gpwrdn_temp = {.d32 = 0 };
- + dwc_otg_core_if_t *core_if = otg_dev->core_if;
- +
- + DWC_PRINTF("%s called\n", __FUNCTION__);
- +
- + if (core_if->power_down == 2) {
- + if (core_if->hibernation_suspend <= 0) {
- + DWC_PRINTF("Already exited from Hibernation\n");
- + return 1;
- + } else
- + gpwrdn_temp.d32 = core_if->gr_backup->gpwrdn_local;
- +
- + } else {
- + gpwrdn_temp.d32 = core_if->adp.gpwrdn;
- + }
- +
- + gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
- +
- + if (gpwrdn.b.idsts ^ gpwrdn_temp.b.idsts) {
- + retval = dwc_otg_handle_pwrdn_idsts_change(otg_dev);
- + } else if (gpwrdn.b.bsessvld ^ gpwrdn_temp.b.bsessvld) {
- + retval = dwc_otg_handle_pwrdn_session_change(core_if);
- + }
- +
- + return retval;
- +}
- +
- +/**
- + * This interrupt indicates that the Wakeup Logic has detected a
- + * SRP.
- + */
- +static int32_t dwc_otg_handle_pwrdn_srp_intr(dwc_otg_core_if_t * core_if)
- +{
- + gpwrdn_data_t gpwrdn = {.d32 = 0 };
- +
- + DWC_PRINTF("%s called\n", __FUNCTION__);
- +
- + if (!core_if->hibernation_suspend) {
- + DWC_PRINTF("Already exited from Hibernation\n");
- + return 1;
- + }
- +#ifdef DWC_DEV_SRPCAP
- + if (core_if->pwron_timer_started) {
- + core_if->pwron_timer_started = 0;
- + DWC_TIMER_CANCEL(core_if->pwron_timer);
- + }
- +#endif
- +
- + /* Switch on the voltage to the core */
- + gpwrdn.b.pwrdnswtch = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- + dwc_udelay(10);
- +
- + /* Reset the core */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pwrdnrstn = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- + dwc_udelay(10);
- +
- + /* Disable power clamps */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pwrdnclmp = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- +
- + /* Remove reset the core signal */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pwrdnrstn = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
- + dwc_udelay(10);
- +
- + /* Disable PMU interrupt */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pmuintsel = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- +
- + /* Indicates that we are exiting from hibernation */
- + core_if->hibernation_suspend = 0;
- +
- + /* Disable PMU */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pmuactv = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- + dwc_udelay(10);
- +
- + /* Programm Disable VBUS to 0 */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.dis_vbus = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- +
- + /*Initialize the core as Host */
- + core_if->op_state = A_HOST;
- + dwc_otg_core_init(core_if);
- + dwc_otg_enable_global_interrupts(core_if);
- + cil_hcd_start(core_if);
- +
- + return 1;
- +}
- +
- +/** This interrupt indicates that restore command after Hibernation
- + * was completed by the core. */
- +int32_t dwc_otg_handle_restore_done_intr(dwc_otg_core_if_t * core_if)
- +{
- + pcgcctl_data_t pcgcctl;
- + DWC_DEBUGPL(DBG_ANY, "++Restore Done Interrupt++\n");
- +
- + //TODO De-assert restore signal. 8.a
- + pcgcctl.d32 = DWC_READ_REG32(core_if->pcgcctl);
- + if (pcgcctl.b.restoremode == 1) {
- + gintmsk_data_t gintmsk = {.d32 = 0 };
- + /*
- + * If restore mode is Remote Wakeup,
- + * unmask Remote Wakeup interrupt.
- + */
- + gintmsk.b.wkupintr = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk,
- + 0, gintmsk.d32);
- + }
- +
- + return 1;
- +}
- +
- +/**
- + * This interrupt indicates that a device has been disconnected from
- + * the root port.
- + */
- +int32_t dwc_otg_handle_disconnect_intr(dwc_otg_core_if_t * core_if)
- +{
- + gintsts_data_t gintsts;
- +
- + DWC_DEBUGPL(DBG_ANY, "++Disconnect Detected Interrupt++ (%s) %s\n",
- + (dwc_otg_is_host_mode(core_if) ? "Host" : "Device"),
- + op_state_str(core_if));
- +
- +/** @todo Consolidate this if statement. */
- +#ifndef DWC_HOST_ONLY
- + if (core_if->op_state == B_HOST) {
- + /* If in device mode Disconnect and stop the HCD, then
- + * start the PCD. */
- + DWC_SPINUNLOCK(core_if->lock);
- + cil_hcd_disconnect(core_if);
- + cil_pcd_start(core_if);
- + DWC_SPINLOCK(core_if->lock);
- + core_if->op_state = B_PERIPHERAL;
- + } else if (dwc_otg_is_device_mode(core_if)) {
- + gotgctl_data_t gotgctl = {.d32 = 0 };
- + gotgctl.d32 =
- + DWC_READ_REG32(&core_if->core_global_regs->gotgctl);
- + if (gotgctl.b.hstsethnpen == 1) {
- + /* Do nothing, if HNP in process the OTG
- + * interrupt "Host Negotiation Detected"
- + * interrupt will do the mode switch.
- + */
- + } else if (gotgctl.b.devhnpen == 0) {
- + /* If in device mode Disconnect and stop the HCD, then
- + * start the PCD. */
- + DWC_SPINUNLOCK(core_if->lock);
- + cil_hcd_disconnect(core_if);
- + cil_pcd_start(core_if);
- + DWC_SPINLOCK(core_if->lock);
- + core_if->op_state = B_PERIPHERAL;
- + } else {
- + DWC_DEBUGPL(DBG_ANY, "!a_peripheral && !devhnpen\n");
- + }
- + } else {
- + if (core_if->op_state == A_HOST) {
- + /* A-Cable still connected but device disconnected. */
- + DWC_SPINUNLOCK(core_if->lock);
- + cil_hcd_disconnect(core_if);
- + DWC_SPINLOCK(core_if->lock);
- + if (core_if->adp_enable) {
- + gpwrdn_data_t gpwrdn = { .d32 = 0 };
- + cil_hcd_stop(core_if);
- + /* Enable Power Down Logic */
- + gpwrdn.b.pmuintsel = 1;
- + gpwrdn.b.pmuactv = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->
- + gpwrdn, 0, gpwrdn.d32);
- + dwc_otg_adp_probe_start(core_if);
- +
- + /* Power off the core */
- + if (core_if->power_down == 2) {
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pwrdnswtch = 1;
- + DWC_MODIFY_REG32
- + (&core_if->core_global_regs->gpwrdn,
- + gpwrdn.d32, 0);
- + }
- + }
- + }
- + }
- +#endif
- + /* Change to L3(OFF) state */
- + core_if->lx_state = DWC_OTG_L3;
- +
- + gintsts.d32 = 0;
- + gintsts.b.disconnect = 1;
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
- + return 1;
- +}
- +
- +/**
- + * This interrupt indicates that SUSPEND state has been detected on
- + * the USB.
- + *
- + * For HNP the USB Suspend interrupt signals the change from
- + * "a_peripheral" to "a_host".
- + *
- + * When power management is enabled the core will be put in low power
- + * mode.
- + */
- +int32_t dwc_otg_handle_usb_suspend_intr(dwc_otg_core_if_t * core_if)
- +{
- + dsts_data_t dsts;
- + gintsts_data_t gintsts;
- + dcfg_data_t dcfg;
- +
- + DWC_DEBUGPL(DBG_ANY, "USB SUSPEND\n");
- +
- + if (dwc_otg_is_device_mode(core_if)) {
- + /* Check the Device status register to determine if the Suspend
- + * state is active. */
- + dsts.d32 =
- + DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
- + DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n", dsts.d32);
- + DWC_DEBUGPL(DBG_PCD, "DSTS.Suspend Status=%d "
- + "HWCFG4.power Optimize=%d\n",
- + dsts.b.suspsts, core_if->hwcfg4.b.power_optimiz);
- +
- +#ifdef PARTIAL_POWER_DOWN
- +/** @todo Add a module parameter for power management. */
- +
- + if (dsts.b.suspsts && core_if->hwcfg4.b.power_optimiz) {
- + pcgcctl_data_t power = {.d32 = 0 };
- + DWC_DEBUGPL(DBG_CIL, "suspend\n");
- +
- + power.b.pwrclmp = 1;
- + DWC_WRITE_REG32(core_if->pcgcctl, power.d32);
- +
- + power.b.rstpdwnmodule = 1;
- + DWC_MODIFY_REG32(core_if->pcgcctl, 0, power.d32);
- +
- + power.b.stoppclk = 1;
- + DWC_MODIFY_REG32(core_if->pcgcctl, 0, power.d32);
- +
- + } else {
- + DWC_DEBUGPL(DBG_ANY, "disconnect?\n");
- + }
- +#endif
- + /* PCD callback for suspend. Release the lock inside of callback function */
- + cil_pcd_suspend(core_if);
- + if (core_if->power_down == 2)
- + {
- + dcfg.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg);
- + DWC_DEBUGPL(DBG_ANY,"lx_state = %08x\n",core_if->lx_state);
- + DWC_DEBUGPL(DBG_ANY," device address = %08d\n",dcfg.b.devaddr);
- +
- + if (core_if->lx_state != DWC_OTG_L3 && dcfg.b.devaddr) {
- + pcgcctl_data_t pcgcctl = {.d32 = 0 };
- + gpwrdn_data_t gpwrdn = {.d32 = 0 };
- + gusbcfg_data_t gusbcfg = {.d32 = 0 };
- +
- + /* Change to L2(suspend) state */
- + core_if->lx_state = DWC_OTG_L2;
- +
- + /* Clear interrupt in gintsts */
- + gintsts.d32 = 0;
- + gintsts.b.usbsuspend = 1;
- + DWC_WRITE_REG32(&core_if->core_global_regs->
- + gintsts, gintsts.d32);
- + DWC_PRINTF("Start of hibernation completed\n");
- + dwc_otg_save_global_regs(core_if);
- + dwc_otg_save_dev_regs(core_if);
- +
- + gusbcfg.d32 =
- + DWC_READ_REG32(&core_if->core_global_regs->
- + gusbcfg);
- + if (gusbcfg.b.ulpi_utmi_sel == 1) {
- + /* ULPI interface */
- + /* Suspend the Phy Clock */
- + pcgcctl.d32 = 0;
- + pcgcctl.b.stoppclk = 1;
- + DWC_MODIFY_REG32(core_if->pcgcctl, 0,
- + pcgcctl.d32);
- + dwc_udelay(10);
- + gpwrdn.b.pmuactv = 1;
- + DWC_MODIFY_REG32(&core_if->
- + core_global_regs->
- + gpwrdn, 0, gpwrdn.d32);
- + } else {
- + /* UTMI+ Interface */
- + gpwrdn.b.pmuactv = 1;
- + DWC_MODIFY_REG32(&core_if->
- + core_global_regs->
- + gpwrdn, 0, gpwrdn.d32);
- + dwc_udelay(10);
- + pcgcctl.b.stoppclk = 1;
- + DWC_MODIFY_REG32(core_if->pcgcctl, 0,
- + pcgcctl.d32);
- + dwc_udelay(10);
- + }
- +
- + /* Set flag to indicate that we are in hibernation */
- + core_if->hibernation_suspend = 1;
- + /* Enable interrupts from wake up logic */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pmuintsel = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->
- + gpwrdn, 0, gpwrdn.d32);
- + dwc_udelay(10);
- +
- + /* Unmask device mode interrupts in GPWRDN */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.rst_det_msk = 1;
- + gpwrdn.b.lnstchng_msk = 1;
- + gpwrdn.b.sts_chngint_msk = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->
- + gpwrdn, 0, gpwrdn.d32);
- + dwc_udelay(10);
- +
- + /* Enable Power Down Clamp */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pwrdnclmp = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->
- + gpwrdn, 0, gpwrdn.d32);
- + dwc_udelay(10);
- +
- + /* Switch off VDD */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pwrdnswtch = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->
- + gpwrdn, 0, gpwrdn.d32);
- +
- + /* Save gpwrdn register for further usage if stschng interrupt */
- + core_if->gr_backup->gpwrdn_local =
- + DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
- + DWC_PRINTF("Hibernation completed\n");
- +
- + return 1;
- + }
- + } else if (core_if->power_down == 3) {
- + pcgcctl_data_t pcgcctl = {.d32 = 0 };
- + dcfg.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dcfg);
- + DWC_DEBUGPL(DBG_ANY, "lx_state = %08x\n",core_if->lx_state);
- + DWC_DEBUGPL(DBG_ANY, " device address = %08d\n",dcfg.b.devaddr);
- +
- + if (core_if->lx_state != DWC_OTG_L3 && dcfg.b.devaddr) {
- + DWC_DEBUGPL(DBG_ANY, "Start entering to extended hibernation\n");
- + core_if->xhib = 1;
- +
- + /* Clear interrupt in gintsts */
- + gintsts.d32 = 0;
- + gintsts.b.usbsuspend = 1;
- + DWC_WRITE_REG32(&core_if->core_global_regs->
- + gintsts, gintsts.d32);
- +
- + dwc_otg_save_global_regs(core_if);
- + dwc_otg_save_dev_regs(core_if);
- +
- + /* Wait for 10 PHY clocks */
- + dwc_udelay(10);
- +
- + /* Program GPIO register while entering to xHib */
- + DWC_WRITE_REG32(&core_if->core_global_regs->ggpio, 0x1);
- +
- + pcgcctl.b.enbl_extnd_hiber = 1;
- + DWC_MODIFY_REG32(core_if->pcgcctl, 0, pcgcctl.d32);
- + DWC_MODIFY_REG32(core_if->pcgcctl, 0, pcgcctl.d32);
- +
- + pcgcctl.d32 = 0;
- + pcgcctl.b.extnd_hiber_pwrclmp = 1;
- + DWC_MODIFY_REG32(core_if->pcgcctl, 0, pcgcctl.d32);
- +
- + pcgcctl.d32 = 0;
- + pcgcctl.b.extnd_hiber_switch = 1;
- + core_if->gr_backup->xhib_gpwrdn = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
- + core_if->gr_backup->xhib_pcgcctl = DWC_READ_REG32(core_if->pcgcctl) | pcgcctl.d32;
- + DWC_MODIFY_REG32(core_if->pcgcctl, 0, pcgcctl.d32);
- +
- + DWC_DEBUGPL(DBG_ANY, "Finished entering to extended hibernation\n");
- +
- + return 1;
- + }
- + }
- + } else {
- + if (core_if->op_state == A_PERIPHERAL) {
- + DWC_DEBUGPL(DBG_ANY, "a_peripheral->a_host\n");
- + /* Clear the a_peripheral flag, back to a_host. */
- + DWC_SPINUNLOCK(core_if->lock);
- + cil_pcd_stop(core_if);
- + cil_hcd_start(core_if);
- + DWC_SPINLOCK(core_if->lock);
- + core_if->op_state = A_HOST;
- + }
- + }
- +
- + /* Change to L2(suspend) state */
- + core_if->lx_state = DWC_OTG_L2;
- +
- + /* Clear interrupt */
- + gintsts.d32 = 0;
- + gintsts.b.usbsuspend = 1;
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
- +
- + return 1;
- +}
- +
- +static int32_t dwc_otg_handle_xhib_exit_intr(dwc_otg_core_if_t * core_if)
- +{
- + gpwrdn_data_t gpwrdn = {.d32 = 0 };
- + pcgcctl_data_t pcgcctl = {.d32 = 0 };
- + gahbcfg_data_t gahbcfg = {.d32 = 0 };
- +
- + dwc_udelay(10);
- +
- + /* Program GPIO register while entering to xHib */
- + DWC_WRITE_REG32(&core_if->core_global_regs->ggpio, 0x0);
- +
- + pcgcctl.d32 = core_if->gr_backup->xhib_pcgcctl;
- + pcgcctl.b.extnd_hiber_pwrclmp = 0;
- + DWC_WRITE_REG32(core_if->pcgcctl, pcgcctl.d32);
- + dwc_udelay(10);
- +
- + gpwrdn.d32 = core_if->gr_backup->xhib_gpwrdn;
- + gpwrdn.b.restore = 1;
- + DWC_WRITE_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32);
- + dwc_udelay(10);
- +
- + restore_lpm_i2c_regs(core_if);
- +
- + pcgcctl.d32 = core_if->gr_backup->pcgcctl_local & (0x3FFFF << 14);
- + pcgcctl.b.max_xcvrselect = 1;
- + pcgcctl.b.ess_reg_restored = 0;
- + pcgcctl.b.extnd_hiber_switch = 0;
- + pcgcctl.b.extnd_hiber_pwrclmp = 0;
- + pcgcctl.b.enbl_extnd_hiber = 1;
- + DWC_WRITE_REG32(core_if->pcgcctl, pcgcctl.d32);
- +
- + gahbcfg.d32 = core_if->gr_backup->gahbcfg_local;
- + gahbcfg.b.glblintrmsk = 1;
- + DWC_WRITE_REG32(&core_if->core_global_regs->gahbcfg, gahbcfg.d32);
- +
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, 0x1 << 16);
- +
- + DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg,
- + core_if->gr_backup->gusbcfg_local);
- + DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dcfg,
- + core_if->dr_backup->dcfg);
- +
- + pcgcctl.d32 = 0;
- + pcgcctl.d32 = core_if->gr_backup->pcgcctl_local & (0x3FFFF << 14);
- + pcgcctl.b.max_xcvrselect = 1;
- + pcgcctl.d32 |= 0x608;
- + DWC_WRITE_REG32(core_if->pcgcctl, pcgcctl.d32);
- + dwc_udelay(10);
- +
- + pcgcctl.d32 = 0;
- + pcgcctl.d32 = core_if->gr_backup->pcgcctl_local & (0x3FFFF << 14);
- + pcgcctl.b.max_xcvrselect = 1;
- + pcgcctl.b.ess_reg_restored = 1;
- + pcgcctl.b.enbl_extnd_hiber = 1;
- + pcgcctl.b.rstpdwnmodule = 1;
- + pcgcctl.b.restoremode = 1;
- + DWC_WRITE_REG32(core_if->pcgcctl, pcgcctl.d32);
- +
- + DWC_DEBUGPL(DBG_ANY, "%s called\n", __FUNCTION__);
- +
- + return 1;
- +}
- +
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- +/**
- + * This function hadles LPM transaction received interrupt.
- + */
- +static int32_t dwc_otg_handle_lpm_intr(dwc_otg_core_if_t * core_if)
- +{
- + glpmcfg_data_t lpmcfg;
- + gintsts_data_t gintsts;
- +
- + if (!core_if->core_params->lpm_enable) {
- + DWC_PRINTF("Unexpected LPM interrupt\n");
- + }
- +
- + lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
- + DWC_PRINTF("LPM config register = 0x%08x\n", lpmcfg.d32);
- +
- + if (dwc_otg_is_host_mode(core_if)) {
- + cil_hcd_sleep(core_if);
- + } else {
- + lpmcfg.b.hird_thres |= (1 << 4);
- + DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg,
- + lpmcfg.d32);
- + }
- +
- + /* Examine prt_sleep_sts after TL1TokenTetry period max (10 us) */
- + dwc_udelay(10);
- + lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
- + if (lpmcfg.b.prt_sleep_sts) {
- + /* Save the current state */
- + core_if->lx_state = DWC_OTG_L1;
- + }
- +
- + /* Clear interrupt */
- + gintsts.d32 = 0;
- + gintsts.b.lpmtranrcvd = 1;
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
- + return 1;
- +}
- +#endif /* CONFIG_USB_DWC_OTG_LPM */
- +
- +/**
- + * 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, dwc_otg_hcd_t *hcd)
- +{
- + gahbcfg_data_t gahbcfg = {.d32 = 0 };
- + gintsts_data_t gintsts;
- + gintmsk_data_t gintmsk;
- + gintmsk_data_t gintmsk_common = {.d32 = 0 };
- + gintmsk_common.b.wkupintr = 1;
- + gintmsk_common.b.sessreqintr = 1;
- + gintmsk_common.b.conidstschng = 1;
- + gintmsk_common.b.otgintr = 1;
- + gintmsk_common.b.modemismatch = 1;
- + gintmsk_common.b.disconnect = 1;
- + gintmsk_common.b.usbsuspend = 1;
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- + gintmsk_common.b.lpmtranrcvd = 1;
- +#endif
- + gintmsk_common.b.restoredone = 1;
- + if(dwc_otg_is_device_mode(core_if))
- + {
- + /** @todo: The port interrupt occurs while in device
- + * mode. Added code to CIL to clear the interrupt for now!
- + */
- + gintmsk_common.b.portintr = 1;
- + }
- + if(fiq_enable) {
- + local_fiq_disable();
- + fiq_fsm_spin_lock(&hcd->fiq_state->lock);
- + gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
- + gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
- + /* Pull in the interrupts that the FIQ has masked */
- + gintmsk.d32 |= ~(hcd->fiq_state->gintmsk_saved.d32);
- + gintmsk.d32 |= gintmsk_common.d32;
- + /* for the upstairs function to reenable - have to read it here in case FIQ triggers again */
- + reenable_gintmsk->d32 = gintmsk.d32;
- + fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
- + local_fiq_enable();
- + } else {
- + gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
- + gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
- + }
- +
- + gahbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gahbcfg);
- +
- +#ifdef DEBUG
- + /* if any common interrupts set */
- + if (gintsts.d32 & gintmsk_common.d32) {
- + DWC_DEBUGPL(DBG_ANY, "common_intr: gintsts=%08x gintmsk=%08x\n",
- + gintsts.d32, gintmsk.d32);
- + }
- +#endif
- + if (!fiq_enable){
- + if (gahbcfg.b.glblintrmsk)
- + return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32);
- + else
- + return 0;
- + } 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);
- + }
- +
- +}
- +
- +/* MACRO for clearing interupt bits in GPWRDN register */
- +#define CLEAR_GPWRDN_INTR(__core_if,__intr) \
- +do { \
- + gpwrdn_data_t gpwrdn = {.d32=0}; \
- + gpwrdn.b.__intr = 1; \
- + DWC_MODIFY_REG32(&__core_if->core_global_regs->gpwrdn, \
- + 0, gpwrdn.d32); \
- +} while (0)
- +
- +/**
- + * Common interrupt handler.
- + *
- + * The common interrupts are those that occur in both Host and Device mode.
- + * This handler handles the following interrupts:
- + * - Mode Mismatch Interrupt
- + * - Disconnect Interrupt
- + * - OTG Interrupt
- + * - Connector ID Status Change Interrupt
- + * - Session Request Interrupt.
- + * - Resume / Remote Wakeup Detected Interrupt.
- + * - LPM Transaction Received Interrupt
- + * - ADP Transaction Received Interrupt
- + *
- + */
- +int32_t dwc_otg_handle_common_intr(void *dev)
- +{
- + int retval = 0;
- + gintsts_data_t gintsts;
- + 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;
- + gpwrdn.d32 = DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
- + if (dwc_otg_is_device_mode(core_if))
- + core_if->frame_num = dwc_otg_get_frame_number(core_if);
- +
- + if (core_if->lock)
- + DWC_SPINLOCK(core_if->lock);
- +
- + if (core_if->power_down == 3 && core_if->xhib == 1) {
- + DWC_DEBUGPL(DBG_ANY, "Exiting from xHIB state\n");
- + retval |= dwc_otg_handle_xhib_exit_intr(core_if);
- + core_if->xhib = 2;
- + if (core_if->lock)
- + DWC_SPINUNLOCK(core_if->lock);
- +
- + return retval;
- + }
- +
- + if (core_if->hibernation_suspend <= 0) {
- + /* 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);
- + }
- + if (gintsts.b.otgintr) {
- + retval |= dwc_otg_handle_otg_intr(core_if);
- + }
- + if (gintsts.b.conidstschng) {
- + retval |=
- + dwc_otg_handle_conn_id_status_change_intr(core_if);
- + }
- + if (gintsts.b.disconnect) {
- + retval |= dwc_otg_handle_disconnect_intr(core_if);
- + }
- + if (gintsts.b.sessreqintr) {
- + retval |= dwc_otg_handle_session_req_intr(core_if);
- + }
- + if (gintsts.b.wkupintr) {
- + retval |= dwc_otg_handle_wakeup_detected_intr(core_if);
- + }
- + if (gintsts.b.usbsuspend) {
- + retval |= dwc_otg_handle_usb_suspend_intr(core_if);
- + }
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- + if (gintsts.b.lpmtranrcvd) {
- + retval |= dwc_otg_handle_lpm_intr(core_if);
- + }
- +#endif
- + if (gintsts.b.restoredone) {
- + gintsts.d32 = 0;
- + if (core_if->power_down == 2)
- + core_if->hibernation_suspend = -1;
- + else if (core_if->power_down == 3 && core_if->xhib == 2) {
- + gpwrdn_data_t gpwrdn = {.d32 = 0 };
- + pcgcctl_data_t pcgcctl = {.d32 = 0 };
- + dctl_data_t dctl = {.d32 = 0 };
- +
- + DWC_WRITE_REG32(&core_if->core_global_regs->
- + gintsts, 0xFFFFFFFF);
- +
- + DWC_DEBUGPL(DBG_ANY,
- + "RESTORE DONE generated\n");
- +
- + gpwrdn.b.restore = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- + dwc_udelay(10);
- +
- + pcgcctl.b.rstpdwnmodule = 1;
- + DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0);
- +
- + DWC_WRITE_REG32(&core_if->core_global_regs->gusbcfg, core_if->gr_backup->gusbcfg_local);
- + DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dcfg, core_if->dr_backup->dcfg);
- + DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl, core_if->dr_backup->dctl);
- + dwc_udelay(50);
- +
- + dctl.b.pwronprgdone = 1;
- + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, 0, dctl.d32);
- + dwc_udelay(10);
- +
- + dwc_otg_restore_global_regs(core_if);
- + dwc_otg_restore_dev_regs(core_if, 0);
- +
- + dctl.d32 = 0;
- + dctl.b.pwronprgdone = 1;
- + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32, 0);
- + dwc_udelay(10);
- +
- + pcgcctl.d32 = 0;
- + pcgcctl.b.enbl_extnd_hiber = 1;
- + DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0);
- +
- + /* The core will be in ON STATE */
- + core_if->lx_state = DWC_OTG_L0;
- + core_if->xhib = 0;
- +
- + DWC_SPINUNLOCK(core_if->lock);
- + if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) {
- + core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p);
- + }
- + DWC_SPINLOCK(core_if->lock);
- +
- + }
- +
- + gintsts.b.restoredone = 1;
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintsts,gintsts.d32);
- + DWC_PRINTF(" --Restore done interrupt received-- \n");
- + retval |= 1;
- + }
- + if (gintsts.b.portintr && dwc_otg_is_device_mode(core_if)) {
- + /* The port interrupt occurs while in device mode with HPRT0
- + * Port Enable/Disable.
- + */
- + gintsts.d32 = 0;
- + gintsts.b.portintr = 1;
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintsts,gintsts.d32);
- + retval |= 1;
- + gintmsk_reenable.b.portintr = 1;
- +
- + }
- + /* 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 && fiq_enable) {
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, gintmsk_reenable.d32);
- + }
- +
- + } else {
- + DWC_DEBUGPL(DBG_ANY, "gpwrdn=%08x\n", gpwrdn.d32);
- +
- + if (gpwrdn.b.disconn_det && gpwrdn.b.disconn_det_msk) {
- + CLEAR_GPWRDN_INTR(core_if, disconn_det);
- + if (gpwrdn.b.linestate == 0) {
- + dwc_otg_handle_pwrdn_disconnect_intr(core_if);
- + } else {
- + DWC_PRINTF("Disconnect detected while linestate is not 0\n");
- + }
- +
- + retval |= 1;
- + }
- + if (gpwrdn.b.lnstschng && gpwrdn.b.lnstchng_msk) {
- + CLEAR_GPWRDN_INTR(core_if, lnstschng);
- + /* remote wakeup from hibernation */
- + if (gpwrdn.b.linestate == 2 || gpwrdn.b.linestate == 1) {
- + dwc_otg_handle_pwrdn_wakeup_detected_intr(core_if);
- + } else {
- + DWC_PRINTF("gpwrdn.linestate = %d\n", gpwrdn.b.linestate);
- + }
- + retval |= 1;
- + }
- + if (gpwrdn.b.rst_det && gpwrdn.b.rst_det_msk) {
- + CLEAR_GPWRDN_INTR(core_if, rst_det);
- + if (gpwrdn.b.linestate == 0) {
- + DWC_PRINTF("Reset detected\n");
- + retval |= dwc_otg_device_hibernation_restore(core_if, 0, 1);
- + }
- + }
- + if (gpwrdn.b.srp_det && gpwrdn.b.srp_det_msk) {
- + CLEAR_GPWRDN_INTR(core_if, srp_det);
- + dwc_otg_handle_pwrdn_srp_intr(core_if);
- + retval |= 1;
- + }
- + }
- + /* Handle ADP interrupt here */
- + if (gpwrdn.b.adp_int) {
- + DWC_PRINTF("ADP interrupt\n");
- + CLEAR_GPWRDN_INTR(core_if, adp_int);
- + dwc_otg_adp_handle_intr(core_if);
- + retval |= 1;
- + }
- + if (gpwrdn.b.sts_chngint && gpwrdn.b.sts_chngint_msk) {
- + DWC_PRINTF("STS CHNG interrupt asserted\n");
- + CLEAR_GPWRDN_INTR(core_if, sts_chngint);
- + dwc_otg_handle_pwrdn_stschng_intr(otg_dev);
- +
- + retval |= 1;
- + }
- + if (core_if->lock)
- + DWC_SPINUNLOCK(core_if->lock);
- + return retval;
- +}
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_core_if.h
- @@ -0,0 +1,705 @@
- +/* ==========================================================================
- + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_core_if.h $
- + * $Revision: #13 $
- + * $Date: 2012/08/10 $
- + * $Change: 2047372 $
- + *
- + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
- + * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
- + * otherwise expressly agreed to in writing between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product under
- + * any End User Software License Agreement or Agreement for Licensed Product
- + * with Synopsys or any supplement thereto. You are permitted to use and
- + * redistribute this Software in source and binary forms, with or without
- + * modification, provided that redistributions of source code must retain this
- + * notice. You may not view, use, disclose, copy or distribute this file or
- + * any information contained herein except pursuant to this license grant from
- + * Synopsys. If you do not agree with this notice, including the disclaimer
- + * below, then you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
- + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- + * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
- + * ========================================================================== */
- +#if !defined(__DWC_CORE_IF_H__)
- +#define __DWC_CORE_IF_H__
- +
- +#include "dwc_os.h"
- +
- +/** @file
- + * This file defines DWC_OTG Core API
- + */
- +
- +struct dwc_otg_core_if;
- +typedef struct dwc_otg_core_if dwc_otg_core_if_t;
- +
- +/** Maximum number of Periodic FIFOs */
- +#define MAX_PERIO_FIFOS 15
- +/** Maximum number of Periodic FIFOs */
- +#define MAX_TX_FIFOS 15
- +
- +/** Maximum number of Endpoints/HostChannels */
- +#define MAX_EPS_CHANNELS 16
- +
- +extern dwc_otg_core_if_t *dwc_otg_cil_init(const uint32_t * _reg_base_addr);
- +extern void dwc_otg_core_init(dwc_otg_core_if_t * _core_if);
- +extern void dwc_otg_cil_remove(dwc_otg_core_if_t * _core_if);
- +
- +extern void dwc_otg_enable_global_interrupts(dwc_otg_core_if_t * _core_if);
- +extern void dwc_otg_disable_global_interrupts(dwc_otg_core_if_t * _core_if);
- +
- +extern uint8_t dwc_otg_is_device_mode(dwc_otg_core_if_t * _core_if);
- +extern uint8_t dwc_otg_is_host_mode(dwc_otg_core_if_t * _core_if);
- +
- +extern uint8_t dwc_otg_is_dma_enable(dwc_otg_core_if_t * core_if);
- +
- +/** This function should be called on every hardware interrupt. */
- +extern int32_t dwc_otg_handle_common_intr(void *otg_dev);
- +
- +/** @name OTG Core Parameters */
- +/** @{ */
- +
- +/**
- + * Specifies the OTG capabilities. The driver will automatically
- + * detect the value for this parameter if none is specified.
- + * 0 - HNP and SRP capable (default)
- + * 1 - SRP Only capable
- + * 2 - No HNP/SRP capable
- + */
- +extern int dwc_otg_set_param_otg_cap(dwc_otg_core_if_t * core_if, int32_t val);
- +extern int32_t dwc_otg_get_param_otg_cap(dwc_otg_core_if_t * core_if);
- +#define DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE 0
- +#define DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE 1
- +#define DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE 2
- +#define dwc_param_otg_cap_default DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE
- +
- +extern int dwc_otg_set_param_opt(dwc_otg_core_if_t * core_if, int32_t val);
- +extern int32_t dwc_otg_get_param_opt(dwc_otg_core_if_t * core_if);
- +#define dwc_param_opt_default 1
- +
- +/**
- + * Specifies whether to use slave or DMA mode for accessing the data
- + * FIFOs. The driver will automatically detect the value for this
- + * parameter if none is specified.
- + * 0 - Slave
- + * 1 - DMA (default, if available)
- + */
- +extern int dwc_otg_set_param_dma_enable(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_param_dma_enable(dwc_otg_core_if_t * core_if);
- +#define dwc_param_dma_enable_default 1
- +
- +/**
- + * When DMA mode is enabled specifies whether to use
- + * address DMA or DMA Descritor mode for accessing the data
- + * FIFOs in device mode. The driver will automatically detect
- + * the value for this parameter if none is specified.
- + * 0 - address DMA
- + * 1 - DMA Descriptor(default, if available)
- + */
- +extern int dwc_otg_set_param_dma_desc_enable(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_param_dma_desc_enable(dwc_otg_core_if_t * core_if);
- +//#define dwc_param_dma_desc_enable_default 1
- +#define dwc_param_dma_desc_enable_default 0 // Broadcom BCM2708
- +
- +/** The DMA Burst size (applicable only for External DMA
- + * Mode). 1, 4, 8 16, 32, 64, 128, 256 (default 32)
- + */
- +extern int dwc_otg_set_param_dma_burst_size(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_param_dma_burst_size(dwc_otg_core_if_t * core_if);
- +#define dwc_param_dma_burst_size_default 32
- +
- +/**
- + * Specifies the maximum speed of operation in host and device mode.
- + * The actual speed depends on the speed of the attached device and
- + * the value of phy_type. The actual speed depends on the speed of the
- + * attached device.
- + * 0 - High Speed (default)
- + * 1 - Full Speed
- + */
- +extern int dwc_otg_set_param_speed(dwc_otg_core_if_t * core_if, int32_t val);
- +extern int32_t dwc_otg_get_param_speed(dwc_otg_core_if_t * core_if);
- +#define dwc_param_speed_default 0
- +#define DWC_SPEED_PARAM_HIGH 0
- +#define DWC_SPEED_PARAM_FULL 1
- +
- +/** Specifies whether low power mode is supported when attached
- + * to a Full Speed or Low Speed device in host mode.
- + * 0 - Don't support low power mode (default)
- + * 1 - Support low power mode
- + */
- +extern int dwc_otg_set_param_host_support_fs_ls_low_power(dwc_otg_core_if_t *
- + core_if, int32_t val);
- +extern int32_t dwc_otg_get_param_host_support_fs_ls_low_power(dwc_otg_core_if_t
- + * core_if);
- +#define dwc_param_host_support_fs_ls_low_power_default 0
- +
- +/** Specifies the PHY clock rate in low power mode when connected to a
- + * Low Speed device in host mode. This parameter is applicable only if
- + * HOST_SUPPORT_FS_LS_LOW_POWER is enabled. If PHY_TYPE is set to FS
- + * then defaults to 6 MHZ otherwise 48 MHZ.
- + *
- + * 0 - 48 MHz
- + * 1 - 6 MHz
- + */
- +extern int dwc_otg_set_param_host_ls_low_power_phy_clk(dwc_otg_core_if_t *
- + core_if, int32_t val);
- +extern int32_t dwc_otg_get_param_host_ls_low_power_phy_clk(dwc_otg_core_if_t *
- + core_if);
- +#define dwc_param_host_ls_low_power_phy_clk_default 0
- +#define DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ 0
- +#define DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ 1
- +
- +/**
- + * 0 - Use cC FIFO size parameters
- + * 1 - Allow dynamic FIFO sizing (default)
- + */
- +extern int dwc_otg_set_param_enable_dynamic_fifo(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_param_enable_dynamic_fifo(dwc_otg_core_if_t *
- + core_if);
- +#define dwc_param_enable_dynamic_fifo_default 1
- +
- +/** Total number of 4-byte words in the data FIFO memory. This
- + * memory includes the Rx FIFO, non-periodic Tx FIFO, and periodic
- + * Tx FIFOs.
- + * 32 to 32768 (default 8192)
- + * Note: The total FIFO memory depth in the FPGA configuration is 8192.
- + */
- +extern int dwc_otg_set_param_data_fifo_size(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_param_data_fifo_size(dwc_otg_core_if_t * core_if);
- +//#define dwc_param_data_fifo_size_default 8192
- +#define dwc_param_data_fifo_size_default 0xFF0 // Broadcom BCM2708
- +
- +/** Number of 4-byte words in the Rx FIFO in device mode when dynamic
- + * FIFO sizing is enabled.
- + * 16 to 32768 (default 1064)
- + */
- +extern int dwc_otg_set_param_dev_rx_fifo_size(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_param_dev_rx_fifo_size(dwc_otg_core_if_t * core_if);
- +#define dwc_param_dev_rx_fifo_size_default 1064
- +
- +/** Number of 4-byte words in the non-periodic Tx FIFO in device mode
- + * when dynamic FIFO sizing is enabled.
- + * 16 to 32768 (default 1024)
- + */
- +extern int dwc_otg_set_param_dev_nperio_tx_fifo_size(dwc_otg_core_if_t *
- + core_if, int32_t val);
- +extern int32_t dwc_otg_get_param_dev_nperio_tx_fifo_size(dwc_otg_core_if_t *
- + core_if);
- +#define dwc_param_dev_nperio_tx_fifo_size_default 1024
- +
- +/** Number of 4-byte words in each of the periodic Tx FIFOs in device
- + * mode when dynamic FIFO sizing is enabled.
- + * 4 to 768 (default 256)
- + */
- +extern int dwc_otg_set_param_dev_perio_tx_fifo_size(dwc_otg_core_if_t * core_if,
- + int32_t val, int fifo_num);
- +extern int32_t dwc_otg_get_param_dev_perio_tx_fifo_size(dwc_otg_core_if_t *
- + core_if, int fifo_num);
- +#define dwc_param_dev_perio_tx_fifo_size_default 256
- +
- +/** Number of 4-byte words in the Rx FIFO in host mode when dynamic
- + * FIFO sizing is enabled.
- + * 16 to 32768 (default 1024)
- + */
- +extern int dwc_otg_set_param_host_rx_fifo_size(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_param_host_rx_fifo_size(dwc_otg_core_if_t * core_if);
- +//#define dwc_param_host_rx_fifo_size_default 1024
- +#define dwc_param_host_rx_fifo_size_default 774 // Broadcom BCM2708
- +
- +/** Number of 4-byte words in the non-periodic Tx FIFO in host mode
- + * when Dynamic FIFO sizing is enabled in the core.
- + * 16 to 32768 (default 1024)
- + */
- +extern int dwc_otg_set_param_host_nperio_tx_fifo_size(dwc_otg_core_if_t *
- + core_if, int32_t val);
- +extern int32_t dwc_otg_get_param_host_nperio_tx_fifo_size(dwc_otg_core_if_t *
- + core_if);
- +//#define dwc_param_host_nperio_tx_fifo_size_default 1024
- +#define dwc_param_host_nperio_tx_fifo_size_default 0x100 // Broadcom BCM2708
- +
- +/** Number of 4-byte words in the host periodic Tx FIFO when dynamic
- + * FIFO sizing is enabled.
- + * 16 to 32768 (default 1024)
- + */
- +extern int dwc_otg_set_param_host_perio_tx_fifo_size(dwc_otg_core_if_t *
- + core_if, int32_t val);
- +extern int32_t dwc_otg_get_param_host_perio_tx_fifo_size(dwc_otg_core_if_t *
- + core_if);
- +//#define dwc_param_host_perio_tx_fifo_size_default 1024
- +#define dwc_param_host_perio_tx_fifo_size_default 0x200 // Broadcom BCM2708
- +
- +/** The maximum transfer size supported in bytes.
- + * 2047 to 65,535 (default 65,535)
- + */
- +extern int dwc_otg_set_param_max_transfer_size(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_param_max_transfer_size(dwc_otg_core_if_t * core_if);
- +#define dwc_param_max_transfer_size_default 65535
- +
- +/** The maximum number of packets in a transfer.
- + * 15 to 511 (default 511)
- + */
- +extern int dwc_otg_set_param_max_packet_count(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_param_max_packet_count(dwc_otg_core_if_t * core_if);
- +#define dwc_param_max_packet_count_default 511
- +
- +/** The number of host channel registers to use.
- + * 1 to 16 (default 12)
- + * Note: The FPGA configuration supports a maximum of 12 host channels.
- + */
- +extern int dwc_otg_set_param_host_channels(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_param_host_channels(dwc_otg_core_if_t * core_if);
- +//#define dwc_param_host_channels_default 12
- +#define dwc_param_host_channels_default 8 // Broadcom BCM2708
- +
- +/** The number of endpoints in addition to EP0 available for device
- + * mode operations.
- + * 1 to 15 (default 6 IN and OUT)
- + * Note: The FPGA configuration supports a maximum of 6 IN and OUT
- + * endpoints in addition to EP0.
- + */
- +extern int dwc_otg_set_param_dev_endpoints(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_param_dev_endpoints(dwc_otg_core_if_t * core_if);
- +#define dwc_param_dev_endpoints_default 6
- +
- +/**
- + * Specifies the type of PHY interface to use. By default, the driver
- + * will automatically detect the phy_type.
- + *
- + * 0 - Full Speed PHY
- + * 1 - UTMI+ (default)
- + * 2 - ULPI
- + */
- +extern int dwc_otg_set_param_phy_type(dwc_otg_core_if_t * core_if, int32_t val);
- +extern int32_t dwc_otg_get_param_phy_type(dwc_otg_core_if_t * core_if);
- +#define DWC_PHY_TYPE_PARAM_FS 0
- +#define DWC_PHY_TYPE_PARAM_UTMI 1
- +#define DWC_PHY_TYPE_PARAM_ULPI 2
- +#define dwc_param_phy_type_default DWC_PHY_TYPE_PARAM_UTMI
- +
- +/**
- + * Specifies the UTMI+ Data Width. This parameter is
- + * applicable for a PHY_TYPE of UTMI+ or ULPI. (For a ULPI
- + * PHY_TYPE, this parameter indicates the data width between
- + * the MAC and the ULPI Wrapper.) Also, this parameter is
- + * applicable only if the OTG_HSPHY_WIDTH cC parameter was set
- + * to "8 and 16 bits", meaning that the core has been
- + * configured to work at either data path width.
- + *
- + * 8 or 16 bits (default 16)
- + */
- +extern int dwc_otg_set_param_phy_utmi_width(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_param_phy_utmi_width(dwc_otg_core_if_t * core_if);
- +//#define dwc_param_phy_utmi_width_default 16
- +#define dwc_param_phy_utmi_width_default 8 // Broadcom BCM2708
- +
- +/**
- + * Specifies whether the ULPI operates at double or single
- + * data rate. This parameter is only applicable if PHY_TYPE is
- + * ULPI.
- + *
- + * 0 - single data rate ULPI interface with 8 bit wide data
- + * bus (default)
- + * 1 - double data rate ULPI interface with 4 bit wide data
- + * bus
- + */
- +extern int dwc_otg_set_param_phy_ulpi_ddr(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_param_phy_ulpi_ddr(dwc_otg_core_if_t * core_if);
- +#define dwc_param_phy_ulpi_ddr_default 0
- +
- +/**
- + * Specifies whether to use the internal or external supply to
- + * drive the vbus with a ULPI phy.
- + */
- +extern int dwc_otg_set_param_phy_ulpi_ext_vbus(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_param_phy_ulpi_ext_vbus(dwc_otg_core_if_t * core_if);
- +#define DWC_PHY_ULPI_INTERNAL_VBUS 0
- +#define DWC_PHY_ULPI_EXTERNAL_VBUS 1
- +#define dwc_param_phy_ulpi_ext_vbus_default DWC_PHY_ULPI_INTERNAL_VBUS
- +
- +/**
- + * Specifies whether to use the I2Cinterface for full speed PHY. This
- + * parameter is only applicable if PHY_TYPE is FS.
- + * 0 - No (default)
- + * 1 - Yes
- + */
- +extern int dwc_otg_set_param_i2c_enable(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_param_i2c_enable(dwc_otg_core_if_t * core_if);
- +#define dwc_param_i2c_enable_default 0
- +
- +extern int dwc_otg_set_param_ulpi_fs_ls(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_param_ulpi_fs_ls(dwc_otg_core_if_t * core_if);
- +#define dwc_param_ulpi_fs_ls_default 0
- +
- +extern int dwc_otg_set_param_ts_dline(dwc_otg_core_if_t * core_if, int32_t val);
- +extern int32_t dwc_otg_get_param_ts_dline(dwc_otg_core_if_t * core_if);
- +#define dwc_param_ts_dline_default 0
- +
- +/**
- + * Specifies whether dedicated transmit FIFOs are
- + * enabled for non periodic IN endpoints in device mode
- + * 0 - No
- + * 1 - Yes
- + */
- +extern int dwc_otg_set_param_en_multiple_tx_fifo(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_param_en_multiple_tx_fifo(dwc_otg_core_if_t *
- + core_if);
- +#define dwc_param_en_multiple_tx_fifo_default 1
- +
- +/** Number of 4-byte words in each of the Tx FIFOs in device
- + * mode when dynamic FIFO sizing is enabled.
- + * 4 to 768 (default 256)
- + */
- +extern int dwc_otg_set_param_dev_tx_fifo_size(dwc_otg_core_if_t * core_if,
- + int fifo_num, int32_t val);
- +extern int32_t dwc_otg_get_param_dev_tx_fifo_size(dwc_otg_core_if_t * core_if,
- + int fifo_num);
- +#define dwc_param_dev_tx_fifo_size_default 768
- +
- +/** Thresholding enable flag-
- + * bit 0 - enable non-ISO Tx thresholding
- + * bit 1 - enable ISO Tx thresholding
- + * bit 2 - enable Rx thresholding
- + */
- +extern int dwc_otg_set_param_thr_ctl(dwc_otg_core_if_t * core_if, int32_t val);
- +extern int32_t dwc_otg_get_thr_ctl(dwc_otg_core_if_t * core_if, int fifo_num);
- +#define dwc_param_thr_ctl_default 0
- +
- +/** Thresholding length for Tx
- + * FIFOs in 32 bit DWORDs
- + */
- +extern int dwc_otg_set_param_tx_thr_length(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_tx_thr_length(dwc_otg_core_if_t * core_if);
- +#define dwc_param_tx_thr_length_default 64
- +
- +/** Thresholding length for Rx
- + * FIFOs in 32 bit DWORDs
- + */
- +extern int dwc_otg_set_param_rx_thr_length(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_rx_thr_length(dwc_otg_core_if_t * core_if);
- +#define dwc_param_rx_thr_length_default 64
- +
- +/**
- + * Specifies whether LPM (Link Power Management) support is enabled
- + */
- +extern int dwc_otg_set_param_lpm_enable(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_param_lpm_enable(dwc_otg_core_if_t * core_if);
- +#define dwc_param_lpm_enable_default 1
- +
- +/**
- + * Specifies whether PTI enhancement is enabled
- + */
- +extern int dwc_otg_set_param_pti_enable(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_param_pti_enable(dwc_otg_core_if_t * core_if);
- +#define dwc_param_pti_enable_default 0
- +
- +/**
- + * Specifies whether MPI enhancement is enabled
- + */
- +extern int dwc_otg_set_param_mpi_enable(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_param_mpi_enable(dwc_otg_core_if_t * core_if);
- +#define dwc_param_mpi_enable_default 0
- +
- +/**
- + * Specifies whether ADP capability is enabled
- + */
- +extern int dwc_otg_set_param_adp_enable(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_param_adp_enable(dwc_otg_core_if_t * core_if);
- +#define dwc_param_adp_enable_default 0
- +
- +/**
- + * Specifies whether IC_USB capability is enabled
- + */
- +
- +extern int dwc_otg_set_param_ic_usb_cap(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_param_ic_usb_cap(dwc_otg_core_if_t * core_if);
- +#define dwc_param_ic_usb_cap_default 0
- +
- +extern int dwc_otg_set_param_ahb_thr_ratio(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_param_ahb_thr_ratio(dwc_otg_core_if_t * core_if);
- +#define dwc_param_ahb_thr_ratio_default 0
- +
- +extern int dwc_otg_set_param_power_down(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_param_power_down(dwc_otg_core_if_t * core_if);
- +#define dwc_param_power_down_default 0
- +
- +extern int dwc_otg_set_param_reload_ctl(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_param_reload_ctl(dwc_otg_core_if_t * core_if);
- +#define dwc_param_reload_ctl_default 0
- +
- +extern int dwc_otg_set_param_dev_out_nak(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_param_dev_out_nak(dwc_otg_core_if_t * core_if);
- +#define dwc_param_dev_out_nak_default 0
- +
- +extern int dwc_otg_set_param_cont_on_bna(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_param_cont_on_bna(dwc_otg_core_if_t * core_if);
- +#define dwc_param_cont_on_bna_default 0
- +
- +extern int dwc_otg_set_param_ahb_single(dwc_otg_core_if_t * core_if,
- + int32_t val);
- +extern int32_t dwc_otg_get_param_ahb_single(dwc_otg_core_if_t * core_if);
- +#define dwc_param_ahb_single_default 0
- +
- +extern int dwc_otg_set_param_otg_ver(dwc_otg_core_if_t * core_if, int32_t val);
- +extern int32_t dwc_otg_get_param_otg_ver(dwc_otg_core_if_t * core_if);
- +#define dwc_param_otg_ver_default 0
- +
- +/** @} */
- +
- +/** @name Access to registers and bit-fields */
- +
- +/**
- + * Dump core registers and SPRAM
- + */
- +extern void dwc_otg_dump_dev_registers(dwc_otg_core_if_t * _core_if);
- +extern void dwc_otg_dump_spram(dwc_otg_core_if_t * _core_if);
- +extern void dwc_otg_dump_host_registers(dwc_otg_core_if_t * _core_if);
- +extern void dwc_otg_dump_global_registers(dwc_otg_core_if_t * _core_if);
- +
- +/**
- + * Get host negotiation status.
- + */
- +extern uint32_t dwc_otg_get_hnpstatus(dwc_otg_core_if_t * core_if);
- +
- +/**
- + * Get srp status
- + */
- +extern uint32_t dwc_otg_get_srpstatus(dwc_otg_core_if_t * core_if);
- +
- +/**
- + * Set hnpreq bit in the GOTGCTL register.
- + */
- +extern void dwc_otg_set_hnpreq(dwc_otg_core_if_t * core_if, uint32_t val);
- +
- +/**
- + * Get Content of SNPSID register.
- + */
- +extern uint32_t dwc_otg_get_gsnpsid(dwc_otg_core_if_t * core_if);
- +
- +/**
- + * Get current mode.
- + * Returns 0 if in device mode, and 1 if in host mode.
- + */
- +extern uint32_t dwc_otg_get_mode(dwc_otg_core_if_t * core_if);
- +
- +/**
- + * Get value of hnpcapable field in the GUSBCFG register
- + */
- +extern uint32_t dwc_otg_get_hnpcapable(dwc_otg_core_if_t * core_if);
- +/**
- + * Set value of hnpcapable field in the GUSBCFG register
- + */
- +extern void dwc_otg_set_hnpcapable(dwc_otg_core_if_t * core_if, uint32_t val);
- +
- +/**
- + * Get value of srpcapable field in the GUSBCFG register
- + */
- +extern uint32_t dwc_otg_get_srpcapable(dwc_otg_core_if_t * core_if);
- +/**
- + * Set value of srpcapable field in the GUSBCFG register
- + */
- +extern void dwc_otg_set_srpcapable(dwc_otg_core_if_t * core_if, uint32_t val);
- +
- +/**
- + * Get value of devspeed field in the DCFG register
- + */
- +extern uint32_t dwc_otg_get_devspeed(dwc_otg_core_if_t * core_if);
- +/**
- + * Set value of devspeed field in the DCFG register
- + */
- +extern void dwc_otg_set_devspeed(dwc_otg_core_if_t * core_if, uint32_t val);
- +
- +/**
- + * Get the value of busconnected field from the HPRT0 register
- + */
- +extern uint32_t dwc_otg_get_busconnected(dwc_otg_core_if_t * core_if);
- +
- +/**
- + * Gets the device enumeration Speed.
- + */
- +extern uint32_t dwc_otg_get_enumspeed(dwc_otg_core_if_t * core_if);
- +
- +/**
- + * Get value of prtpwr field from the HPRT0 register
- + */
- +extern uint32_t dwc_otg_get_prtpower(dwc_otg_core_if_t * core_if);
- +
- +/**
- + * Get value of flag indicating core state - hibernated or not
- + */
- +extern uint32_t dwc_otg_get_core_state(dwc_otg_core_if_t * core_if);
- +
- +/**
- + * Set value of prtpwr field from the HPRT0 register
- + */
- +extern void dwc_otg_set_prtpower(dwc_otg_core_if_t * core_if, uint32_t val);
- +
- +/**
- + * Get value of prtsusp field from the HPRT0 regsiter
- + */
- +extern uint32_t dwc_otg_get_prtsuspend(dwc_otg_core_if_t * core_if);
- +/**
- + * Set value of prtpwr field from the HPRT0 register
- + */
- +extern void dwc_otg_set_prtsuspend(dwc_otg_core_if_t * core_if, uint32_t val);
- +
- +/**
- + * Get value of ModeChTimEn field from the HCFG regsiter
- + */
- +extern uint32_t dwc_otg_get_mode_ch_tim(dwc_otg_core_if_t * core_if);
- +/**
- + * Set value of ModeChTimEn field from the HCFG regsiter
- + */
- +extern void dwc_otg_set_mode_ch_tim(dwc_otg_core_if_t * core_if, uint32_t val);
- +
- +/**
- + * Get value of Fram Interval field from the HFIR regsiter
- + */
- +extern uint32_t dwc_otg_get_fr_interval(dwc_otg_core_if_t * core_if);
- +/**
- + * Set value of Frame Interval field from the HFIR regsiter
- + */
- +extern void dwc_otg_set_fr_interval(dwc_otg_core_if_t * core_if, uint32_t val);
- +
- +/**
- + * Set value of prtres field from the HPRT0 register
- + *FIXME Remove?
- + */
- +extern void dwc_otg_set_prtresume(dwc_otg_core_if_t * core_if, uint32_t val);
- +
- +/**
- + * Get value of rmtwkupsig bit in DCTL register
- + */
- +extern uint32_t dwc_otg_get_remotewakesig(dwc_otg_core_if_t * core_if);
- +
- +/**
- + * Get value of prt_sleep_sts field from the GLPMCFG register
- + */
- +extern uint32_t dwc_otg_get_lpm_portsleepstatus(dwc_otg_core_if_t * core_if);
- +
- +/**
- + * Get value of rem_wkup_en field from the GLPMCFG register
- + */
- +extern uint32_t dwc_otg_get_lpm_remotewakeenabled(dwc_otg_core_if_t * core_if);
- +
- +/**
- + * Get value of appl_resp field from the GLPMCFG register
- + */
- +extern uint32_t dwc_otg_get_lpmresponse(dwc_otg_core_if_t * core_if);
- +/**
- + * Set value of appl_resp field from the GLPMCFG register
- + */
- +extern void dwc_otg_set_lpmresponse(dwc_otg_core_if_t * core_if, uint32_t val);
- +
- +/**
- + * Get value of hsic_connect field from the GLPMCFG register
- + */
- +extern uint32_t dwc_otg_get_hsic_connect(dwc_otg_core_if_t * core_if);
- +/**
- + * Set value of hsic_connect field from the GLPMCFG register
- + */
- +extern void dwc_otg_set_hsic_connect(dwc_otg_core_if_t * core_if, uint32_t val);
- +
- +/**
- + * Get value of inv_sel_hsic field from the GLPMCFG register.
- + */
- +extern uint32_t dwc_otg_get_inv_sel_hsic(dwc_otg_core_if_t * core_if);
- +/**
- + * Set value of inv_sel_hsic field from the GLPMFG register.
- + */
- +extern void dwc_otg_set_inv_sel_hsic(dwc_otg_core_if_t * core_if, uint32_t val);
- +
- +/*
- + * Some functions for accessing registers
- + */
- +
- +/**
- + * GOTGCTL register
- + */
- +extern uint32_t dwc_otg_get_gotgctl(dwc_otg_core_if_t * core_if);
- +extern void dwc_otg_set_gotgctl(dwc_otg_core_if_t * core_if, uint32_t val);
- +
- +/**
- + * GUSBCFG register
- + */
- +extern uint32_t dwc_otg_get_gusbcfg(dwc_otg_core_if_t * core_if);
- +extern void dwc_otg_set_gusbcfg(dwc_otg_core_if_t * core_if, uint32_t val);
- +
- +/**
- + * GRXFSIZ register
- + */
- +extern uint32_t dwc_otg_get_grxfsiz(dwc_otg_core_if_t * core_if);
- +extern void dwc_otg_set_grxfsiz(dwc_otg_core_if_t * core_if, uint32_t val);
- +
- +/**
- + * GNPTXFSIZ register
- + */
- +extern uint32_t dwc_otg_get_gnptxfsiz(dwc_otg_core_if_t * core_if);
- +extern void dwc_otg_set_gnptxfsiz(dwc_otg_core_if_t * core_if, uint32_t val);
- +
- +extern uint32_t dwc_otg_get_gpvndctl(dwc_otg_core_if_t * core_if);
- +extern void dwc_otg_set_gpvndctl(dwc_otg_core_if_t * core_if, uint32_t val);
- +
- +/**
- + * GGPIO register
- + */
- +extern uint32_t dwc_otg_get_ggpio(dwc_otg_core_if_t * core_if);
- +extern void dwc_otg_set_ggpio(dwc_otg_core_if_t * core_if, uint32_t val);
- +
- +/**
- + * GUID register
- + */
- +extern uint32_t dwc_otg_get_guid(dwc_otg_core_if_t * core_if);
- +extern void dwc_otg_set_guid(dwc_otg_core_if_t * core_if, uint32_t val);
- +
- +/**
- + * HPRT0 register
- + */
- +extern uint32_t dwc_otg_get_hprt0(dwc_otg_core_if_t * core_if);
- +extern void dwc_otg_set_hprt0(dwc_otg_core_if_t * core_if, uint32_t val);
- +
- +/**
- + * GHPTXFSIZE
- + */
- +extern uint32_t dwc_otg_get_hptxfsiz(dwc_otg_core_if_t * core_if);
- +
- +/** @} */
- +
- +#endif /* __DWC_CORE_IF_H__ */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_dbg.h
- @@ -0,0 +1,117 @@
- +/* ==========================================================================
- + *
- + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
- + * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
- + * otherwise expressly agreed to in writing between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product under
- + * any End User Software License Agreement or Agreement for Licensed Product
- + * with Synopsys or any supplement thereto. You are permitted to use and
- + * redistribute this Software in source and binary forms, with or without
- + * modification, provided that redistributions of source code must retain this
- + * notice. You may not view, use, disclose, copy or distribute this file or
- + * any information contained herein except pursuant to this license grant from
- + * Synopsys. If you do not agree with this notice, including the disclaimer
- + * below, then you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
- + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- + * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
- + * ========================================================================== */
- +
- +#ifndef __DWC_OTG_DBG_H__
- +#define __DWC_OTG_DBG_H__
- +
- +/** @file
- + * This file defines debug levels.
- + * Debugging support vanishes in non-debug builds.
- + */
- +
- +/**
- + * The Debug Level bit-mask variable.
- + */
- +extern uint32_t g_dbg_lvl;
- +/**
- + * Set the Debug Level variable.
- + */
- +static inline uint32_t SET_DEBUG_LEVEL(const uint32_t new)
- +{
- + uint32_t old = g_dbg_lvl;
- + g_dbg_lvl = new;
- + return old;
- +}
- +
- +#define DBG_USER (0x1)
- +/** When debug level has the DBG_CIL bit set, display CIL Debug messages. */
- +#define DBG_CIL (0x2)
- +/** When debug level has the DBG_CILV bit set, display CIL Verbose debug
- + * messages */
- +#define DBG_CILV (0x20)
- +/** When debug level has the DBG_PCD bit set, display PCD (Device) debug
- + * messages */
- +#define DBG_PCD (0x4)
- +/** When debug level has the DBG_PCDV set, display PCD (Device) Verbose debug
- + * messages */
- +#define DBG_PCDV (0x40)
- +/** When debug level has the DBG_HCD bit set, display Host debug messages */
- +#define DBG_HCD (0x8)
- +/** When debug level has the DBG_HCDV bit set, display Verbose Host debug
- + * messages */
- +#define DBG_HCDV (0x80)
- +/** When debug level has the DBG_HCD_URB bit set, display enqueued URBs in host
- + * mode. */
- +#define DBG_HCD_URB (0x800)
- +/** When debug level has the DBG_HCDI bit set, display host interrupt
- + * messages. */
- +#define DBG_HCDI (0x1000)
- +
- +/** When debug level has any bit set, display debug messages */
- +#define DBG_ANY (0xFF)
- +
- +/** All debug messages off */
- +#define DBG_OFF 0
- +
- +/** Prefix string for DWC_DEBUG print macros. */
- +#define USB_DWC "DWC_otg: "
- +
- +/**
- + * Print a debug message when the Global debug level variable contains
- + * the bit defined in <code>lvl</code>.
- + *
- + * @param[in] lvl - Debug level, use one of the DBG_ constants above.
- + * @param[in] x - like printf
- + *
- + * Example:<p>
- + * <code>
- + * DWC_DEBUGPL( DBG_ANY, "%s(%p)\n", __func__, _reg_base_addr);
- + * </code>
- + * <br>
- + * results in:<br>
- + * <code>
- + * usb-DWC_otg: dwc_otg_cil_init(ca867000)
- + * </code>
- + */
- +#ifdef DEBUG
- +
- +# define DWC_DEBUGPL(lvl, x...) do{ if ((lvl)&g_dbg_lvl)__DWC_DEBUG(USB_DWC x ); }while(0)
- +# define DWC_DEBUGP(x...) DWC_DEBUGPL(DBG_ANY, x )
- +
- +# define CHK_DEBUG_LEVEL(level) ((level) & g_dbg_lvl)
- +
- +#else
- +
- +# define DWC_DEBUGPL(lvl, x...) do{}while(0)
- +# define DWC_DEBUGP(x...)
- +
- +# define CHK_DEBUG_LEVEL(level) (0)
- +
- +#endif /*DEBUG*/
- +#endif
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_driver.c
- @@ -0,0 +1,1772 @@
- +/* ==========================================================================
- + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_driver.c $
- + * $Revision: #92 $
- + * $Date: 2012/08/10 $
- + * $Change: 2047372 $
- + *
- + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
- + * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
- + * otherwise expressly agreed to in writing between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product under
- + * any End User Software License Agreement or Agreement for Licensed Product
- + * with Synopsys or any supplement thereto. You are permitted to use and
- + * redistribute this Software in source and binary forms, with or without
- + * modification, provided that redistributions of source code must retain this
- + * notice. You may not view, use, disclose, copy or distribute this file or
- + * any information contained herein except pursuant to this license grant from
- + * Synopsys. If you do not agree with this notice, including the disclaimer
- + * below, then you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
- + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- + * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
- + * ========================================================================== */
- +
- +/** @file
- + * The dwc_otg_driver module provides the initialization and cleanup entry
- + * points for the DWC_otg driver. This module will be dynamically installed
- + * after Linux is booted using the insmod command. When the module is
- + * installed, the dwc_otg_driver_init function is called. When the module is
- + * removed (using rmmod), the dwc_otg_driver_cleanup function is called.
- + *
- + * This module also defines a data structure for the dwc_otg_driver, which is
- + * used in conjunction with the standard ARM lm_device structure. These
- + * structures allow the OTG driver to comply with the standard Linux driver
- + * model in which devices and drivers are registered with a bus driver. This
- + * has the benefit that Linux can expose attributes of the driver and device
- + * in its special sysfs file system. Users can then read or write files in
- + * this file system to perform diagnostics on the driver components or the
- + * device.
- + */
- +
- +#include "dwc_otg_os_dep.h"
- +#include "dwc_os.h"
- +#include "dwc_otg_dbg.h"
- +#include "dwc_otg_driver.h"
- +#include "dwc_otg_attr.h"
- +#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"
- +
- +bool microframe_schedule=true;
- +
- +static const char dwc_driver_name[] = "dwc_otg";
- +
- +
- +extern int pcd_init(
- +#ifdef LM_INTERFACE
- + struct lm_device *_dev
- +#elif defined(PCI_INTERFACE)
- + struct pci_dev *_dev
- +#elif defined(PLATFORM_INTERFACE)
- + struct platform_device *dev
- +#endif
- + );
- +extern int hcd_init(
- +#ifdef LM_INTERFACE
- + struct lm_device *_dev
- +#elif defined(PCI_INTERFACE)
- + struct pci_dev *_dev
- +#elif defined(PLATFORM_INTERFACE)
- + struct platform_device *dev
- +#endif
- + );
- +
- +extern int pcd_remove(
- +#ifdef LM_INTERFACE
- + struct lm_device *_dev
- +#elif defined(PCI_INTERFACE)
- + struct pci_dev *_dev
- +#elif defined(PLATFORM_INTERFACE)
- + struct platform_device *_dev
- +#endif
- + );
- +
- +extern void hcd_remove(
- +#ifdef LM_INTERFACE
- + struct lm_device *_dev
- +#elif defined(PCI_INTERFACE)
- + struct pci_dev *_dev
- +#elif defined(PLATFORM_INTERFACE)
- + struct platform_device *_dev
- +#endif
- + );
- +
- +extern void dwc_otg_adp_start(dwc_otg_core_if_t * core_if, uint8_t is_host);
- +
- +/*-------------------------------------------------------------------------*/
- +/* Encapsulate the module parameter settings */
- +
- +struct dwc_otg_driver_module_params {
- + int32_t opt;
- + int32_t otg_cap;
- + int32_t dma_enable;
- + int32_t dma_desc_enable;
- + int32_t dma_burst_size;
- + int32_t speed;
- + int32_t host_support_fs_ls_low_power;
- + int32_t host_ls_low_power_phy_clk;
- + int32_t enable_dynamic_fifo;
- + int32_t data_fifo_size;
- + int32_t dev_rx_fifo_size;
- + int32_t dev_nperio_tx_fifo_size;
- + uint32_t dev_perio_tx_fifo_size[MAX_PERIO_FIFOS];
- + int32_t host_rx_fifo_size;
- + int32_t host_nperio_tx_fifo_size;
- + int32_t host_perio_tx_fifo_size;
- + int32_t max_transfer_size;
- + int32_t max_packet_count;
- + int32_t host_channels;
- + int32_t dev_endpoints;
- + int32_t phy_type;
- + int32_t phy_utmi_width;
- + int32_t phy_ulpi_ddr;
- + int32_t phy_ulpi_ext_vbus;
- + int32_t i2c_enable;
- + int32_t ulpi_fs_ls;
- + int32_t ts_dline;
- + int32_t en_multiple_tx_fifo;
- + uint32_t dev_tx_fifo_size[MAX_TX_FIFOS];
- + uint32_t thr_ctl;
- + uint32_t tx_thr_length;
- + uint32_t rx_thr_length;
- + int32_t pti_enable;
- + int32_t mpi_enable;
- + int32_t lpm_enable;
- + int32_t ic_usb_cap;
- + int32_t ahb_thr_ratio;
- + int32_t power_down;
- + int32_t reload_ctl;
- + int32_t dev_out_nak;
- + int32_t cont_on_bna;
- + int32_t ahb_single;
- + int32_t otg_ver;
- + int32_t adp_enable;
- +};
- +
- +static struct dwc_otg_driver_module_params dwc_otg_module_params = {
- + .opt = -1,
- + .otg_cap = -1,
- + .dma_enable = -1,
- + .dma_desc_enable = -1,
- + .dma_burst_size = -1,
- + .speed = -1,
- + .host_support_fs_ls_low_power = -1,
- + .host_ls_low_power_phy_clk = -1,
- + .enable_dynamic_fifo = -1,
- + .data_fifo_size = -1,
- + .dev_rx_fifo_size = -1,
- + .dev_nperio_tx_fifo_size = -1,
- + .dev_perio_tx_fifo_size = {
- + /* dev_perio_tx_fifo_size_1 */
- + -1,
- + -1,
- + -1,
- + -1,
- + -1,
- + -1,
- + -1,
- + -1,
- + -1,
- + -1,
- + -1,
- + -1,
- + -1,
- + -1,
- + -1
- + /* 15 */
- + },
- + .host_rx_fifo_size = -1,
- + .host_nperio_tx_fifo_size = -1,
- + .host_perio_tx_fifo_size = -1,
- + .max_transfer_size = -1,
- + .max_packet_count = -1,
- + .host_channels = -1,
- + .dev_endpoints = -1,
- + .phy_type = -1,
- + .phy_utmi_width = -1,
- + .phy_ulpi_ddr = -1,
- + .phy_ulpi_ext_vbus = -1,
- + .i2c_enable = -1,
- + .ulpi_fs_ls = -1,
- + .ts_dline = -1,
- + .en_multiple_tx_fifo = -1,
- + .dev_tx_fifo_size = {
- + /* dev_tx_fifo_size */
- + -1,
- + -1,
- + -1,
- + -1,
- + -1,
- + -1,
- + -1,
- + -1,
- + -1,
- + -1,
- + -1,
- + -1,
- + -1,
- + -1,
- + -1
- + /* 15 */
- + },
- + .thr_ctl = -1,
- + .tx_thr_length = -1,
- + .rx_thr_length = -1,
- + .pti_enable = -1,
- + .mpi_enable = -1,
- + .lpm_enable = 0,
- + .ic_usb_cap = -1,
- + .ahb_thr_ratio = -1,
- + .power_down = -1,
- + .reload_ctl = -1,
- + .dev_out_nak = -1,
- + .cont_on_bna = -1,
- + .ahb_single = -1,
- + .otg_ver = -1,
- + .adp_enable = -1,
- +};
- +
- +//Global variable to switch the fiq fix on or off
- +bool fiq_enable = 1;
- +// Global variable to enable the split transaction fix
- +bool fiq_fsm_enable = true;
- +//Bulk split-transaction NAK holdoff in microframes
- +uint16_t nak_holdoff = 8;
- +
- +//Force host mode during CIL re-init
- +bool cil_force_host = true;
- +
- +unsigned short fiq_fsm_mask = 0x0F;
- +
- +unsigned short int_ep_interval_min = 0;
- +/**
- + * This function shows the Driver Version.
- + */
- +static ssize_t version_show(struct device_driver *dev, char *buf)
- +{
- + return snprintf(buf, sizeof(DWC_DRIVER_VERSION) + 2, "%s\n",
- + DWC_DRIVER_VERSION);
- +}
- +
- +static DRIVER_ATTR_RO(version);
- +
- +/**
- + * Global Debug Level Mask.
- + */
- +uint32_t g_dbg_lvl = 0; /* OFF */
- +
- +/**
- + * This function shows the driver Debug Level.
- + */
- +static ssize_t debuglevel_show(struct device_driver *drv, char *buf)
- +{
- + return sprintf(buf, "0x%0x\n", g_dbg_lvl);
- +}
- +
- +/**
- + * This function stores the driver Debug Level.
- + */
- +static ssize_t debuglevel_store(struct device_driver *drv, const char *buf,
- + size_t count)
- +{
- + g_dbg_lvl = simple_strtoul(buf, NULL, 16);
- + return count;
- +}
- +
- +static DRIVER_ATTR_RW(debuglevel);
- +
- +/**
- + * This function is called during module intialization
- + * to pass module parameters to the DWC_OTG CORE.
- + */
- +static int set_parameters(dwc_otg_core_if_t * core_if)
- +{
- + int retval = 0;
- + int i;
- +
- + if (dwc_otg_module_params.otg_cap != -1) {
- + retval +=
- + dwc_otg_set_param_otg_cap(core_if,
- + dwc_otg_module_params.otg_cap);
- + }
- + if (dwc_otg_module_params.dma_enable != -1) {
- + retval +=
- + dwc_otg_set_param_dma_enable(core_if,
- + dwc_otg_module_params.
- + dma_enable);
- + }
- + if (dwc_otg_module_params.dma_desc_enable != -1) {
- + retval +=
- + dwc_otg_set_param_dma_desc_enable(core_if,
- + dwc_otg_module_params.
- + dma_desc_enable);
- + }
- + if (dwc_otg_module_params.opt != -1) {
- + retval +=
- + dwc_otg_set_param_opt(core_if, dwc_otg_module_params.opt);
- + }
- + if (dwc_otg_module_params.dma_burst_size != -1) {
- + retval +=
- + dwc_otg_set_param_dma_burst_size(core_if,
- + dwc_otg_module_params.
- + dma_burst_size);
- + }
- + if (dwc_otg_module_params.host_support_fs_ls_low_power != -1) {
- + retval +=
- + dwc_otg_set_param_host_support_fs_ls_low_power(core_if,
- + dwc_otg_module_params.
- + host_support_fs_ls_low_power);
- + }
- + if (dwc_otg_module_params.enable_dynamic_fifo != -1) {
- + retval +=
- + dwc_otg_set_param_enable_dynamic_fifo(core_if,
- + dwc_otg_module_params.
- + enable_dynamic_fifo);
- + }
- + if (dwc_otg_module_params.data_fifo_size != -1) {
- + retval +=
- + dwc_otg_set_param_data_fifo_size(core_if,
- + dwc_otg_module_params.
- + data_fifo_size);
- + }
- + if (dwc_otg_module_params.dev_rx_fifo_size != -1) {
- + retval +=
- + dwc_otg_set_param_dev_rx_fifo_size(core_if,
- + dwc_otg_module_params.
- + dev_rx_fifo_size);
- + }
- + if (dwc_otg_module_params.dev_nperio_tx_fifo_size != -1) {
- + retval +=
- + dwc_otg_set_param_dev_nperio_tx_fifo_size(core_if,
- + dwc_otg_module_params.
- + dev_nperio_tx_fifo_size);
- + }
- + if (dwc_otg_module_params.host_rx_fifo_size != -1) {
- + retval +=
- + dwc_otg_set_param_host_rx_fifo_size(core_if,
- + dwc_otg_module_params.host_rx_fifo_size);
- + }
- + if (dwc_otg_module_params.host_nperio_tx_fifo_size != -1) {
- + retval +=
- + dwc_otg_set_param_host_nperio_tx_fifo_size(core_if,
- + dwc_otg_module_params.
- + host_nperio_tx_fifo_size);
- + }
- + if (dwc_otg_module_params.host_perio_tx_fifo_size != -1) {
- + retval +=
- + dwc_otg_set_param_host_perio_tx_fifo_size(core_if,
- + dwc_otg_module_params.
- + host_perio_tx_fifo_size);
- + }
- + if (dwc_otg_module_params.max_transfer_size != -1) {
- + retval +=
- + dwc_otg_set_param_max_transfer_size(core_if,
- + dwc_otg_module_params.
- + max_transfer_size);
- + }
- + if (dwc_otg_module_params.max_packet_count != -1) {
- + retval +=
- + dwc_otg_set_param_max_packet_count(core_if,
- + dwc_otg_module_params.
- + max_packet_count);
- + }
- + if (dwc_otg_module_params.host_channels != -1) {
- + retval +=
- + dwc_otg_set_param_host_channels(core_if,
- + dwc_otg_module_params.
- + host_channels);
- + }
- + if (dwc_otg_module_params.dev_endpoints != -1) {
- + retval +=
- + dwc_otg_set_param_dev_endpoints(core_if,
- + dwc_otg_module_params.
- + dev_endpoints);
- + }
- + if (dwc_otg_module_params.phy_type != -1) {
- + retval +=
- + dwc_otg_set_param_phy_type(core_if,
- + dwc_otg_module_params.phy_type);
- + }
- + if (dwc_otg_module_params.speed != -1) {
- + retval +=
- + dwc_otg_set_param_speed(core_if,
- + dwc_otg_module_params.speed);
- + }
- + if (dwc_otg_module_params.host_ls_low_power_phy_clk != -1) {
- + retval +=
- + dwc_otg_set_param_host_ls_low_power_phy_clk(core_if,
- + dwc_otg_module_params.
- + host_ls_low_power_phy_clk);
- + }
- + if (dwc_otg_module_params.phy_ulpi_ddr != -1) {
- + retval +=
- + dwc_otg_set_param_phy_ulpi_ddr(core_if,
- + dwc_otg_module_params.
- + phy_ulpi_ddr);
- + }
- + if (dwc_otg_module_params.phy_ulpi_ext_vbus != -1) {
- + retval +=
- + dwc_otg_set_param_phy_ulpi_ext_vbus(core_if,
- + dwc_otg_module_params.
- + phy_ulpi_ext_vbus);
- + }
- + if (dwc_otg_module_params.phy_utmi_width != -1) {
- + retval +=
- + dwc_otg_set_param_phy_utmi_width(core_if,
- + dwc_otg_module_params.
- + phy_utmi_width);
- + }
- + if (dwc_otg_module_params.ulpi_fs_ls != -1) {
- + retval +=
- + dwc_otg_set_param_ulpi_fs_ls(core_if,
- + dwc_otg_module_params.ulpi_fs_ls);
- + }
- + if (dwc_otg_module_params.ts_dline != -1) {
- + retval +=
- + dwc_otg_set_param_ts_dline(core_if,
- + dwc_otg_module_params.ts_dline);
- + }
- + if (dwc_otg_module_params.i2c_enable != -1) {
- + retval +=
- + dwc_otg_set_param_i2c_enable(core_if,
- + dwc_otg_module_params.
- + i2c_enable);
- + }
- + if (dwc_otg_module_params.en_multiple_tx_fifo != -1) {
- + retval +=
- + dwc_otg_set_param_en_multiple_tx_fifo(core_if,
- + dwc_otg_module_params.
- + en_multiple_tx_fifo);
- + }
- + for (i = 0; i < 15; i++) {
- + if (dwc_otg_module_params.dev_perio_tx_fifo_size[i] != -1) {
- + retval +=
- + dwc_otg_set_param_dev_perio_tx_fifo_size(core_if,
- + dwc_otg_module_params.
- + dev_perio_tx_fifo_size
- + [i], i);
- + }
- + }
- +
- + for (i = 0; i < 15; i++) {
- + if (dwc_otg_module_params.dev_tx_fifo_size[i] != -1) {
- + retval += dwc_otg_set_param_dev_tx_fifo_size(core_if,
- + dwc_otg_module_params.
- + dev_tx_fifo_size
- + [i], i);
- + }
- + }
- + if (dwc_otg_module_params.thr_ctl != -1) {
- + retval +=
- + dwc_otg_set_param_thr_ctl(core_if,
- + dwc_otg_module_params.thr_ctl);
- + }
- + if (dwc_otg_module_params.mpi_enable != -1) {
- + retval +=
- + dwc_otg_set_param_mpi_enable(core_if,
- + dwc_otg_module_params.
- + mpi_enable);
- + }
- + if (dwc_otg_module_params.pti_enable != -1) {
- + retval +=
- + dwc_otg_set_param_pti_enable(core_if,
- + dwc_otg_module_params.
- + pti_enable);
- + }
- + if (dwc_otg_module_params.lpm_enable != -1) {
- + retval +=
- + dwc_otg_set_param_lpm_enable(core_if,
- + dwc_otg_module_params.
- + lpm_enable);
- + }
- + if (dwc_otg_module_params.ic_usb_cap != -1) {
- + retval +=
- + dwc_otg_set_param_ic_usb_cap(core_if,
- + dwc_otg_module_params.
- + ic_usb_cap);
- + }
- + if (dwc_otg_module_params.tx_thr_length != -1) {
- + retval +=
- + dwc_otg_set_param_tx_thr_length(core_if,
- + dwc_otg_module_params.tx_thr_length);
- + }
- + if (dwc_otg_module_params.rx_thr_length != -1) {
- + retval +=
- + dwc_otg_set_param_rx_thr_length(core_if,
- + dwc_otg_module_params.
- + rx_thr_length);
- + }
- + if (dwc_otg_module_params.ahb_thr_ratio != -1) {
- + retval +=
- + dwc_otg_set_param_ahb_thr_ratio(core_if,
- + dwc_otg_module_params.ahb_thr_ratio);
- + }
- + if (dwc_otg_module_params.power_down != -1) {
- + retval +=
- + dwc_otg_set_param_power_down(core_if,
- + dwc_otg_module_params.power_down);
- + }
- + if (dwc_otg_module_params.reload_ctl != -1) {
- + retval +=
- + dwc_otg_set_param_reload_ctl(core_if,
- + dwc_otg_module_params.reload_ctl);
- + }
- +
- + if (dwc_otg_module_params.dev_out_nak != -1) {
- + retval +=
- + dwc_otg_set_param_dev_out_nak(core_if,
- + dwc_otg_module_params.dev_out_nak);
- + }
- +
- + if (dwc_otg_module_params.cont_on_bna != -1) {
- + retval +=
- + dwc_otg_set_param_cont_on_bna(core_if,
- + dwc_otg_module_params.cont_on_bna);
- + }
- +
- + if (dwc_otg_module_params.ahb_single != -1) {
- + retval +=
- + dwc_otg_set_param_ahb_single(core_if,
- + dwc_otg_module_params.ahb_single);
- + }
- +
- + if (dwc_otg_module_params.otg_ver != -1) {
- + retval +=
- + dwc_otg_set_param_otg_ver(core_if,
- + dwc_otg_module_params.otg_ver);
- + }
- + if (dwc_otg_module_params.adp_enable != -1) {
- + retval +=
- + dwc_otg_set_param_adp_enable(core_if,
- + dwc_otg_module_params.
- + adp_enable);
- + }
- + return retval;
- +}
- +
- +/**
- + * This function is the top level interrupt handler for the Common
- + * (Device and host modes) interrupts.
- + */
- +static irqreturn_t dwc_otg_common_irq(int irq, void *dev)
- +{
- + int32_t retval = IRQ_NONE;
- +
- + retval = dwc_otg_handle_common_intr(dev);
- + if (retval != 0) {
- + S3C2410X_CLEAR_EINTPEND();
- + }
- + return IRQ_RETVAL(retval);
- +}
- +
- +/**
- + * This function is called when a lm_device is unregistered with the
- + * dwc_otg_driver. This happens, for example, when the rmmod command is
- + * executed. The device may or may not be electrically present. If it is
- + * present, the driver stops device processing. Any resources used on behalf
- + * of this device are freed.
- + *
- + * @param _dev
- + */
- +#ifdef LM_INTERFACE
- +#define REM_RETVAL(n)
- +static void dwc_otg_driver_remove( struct lm_device *_dev )
- +{ dwc_otg_device_t *otg_dev = lm_get_drvdata(_dev);
- +#elif defined(PCI_INTERFACE)
- +#define REM_RETVAL(n)
- +static void dwc_otg_driver_remove( struct pci_dev *_dev )
- +{ dwc_otg_device_t *otg_dev = pci_get_drvdata(_dev);
- +#elif defined(PLATFORM_INTERFACE)
- +#define REM_RETVAL(n) n
- +static int dwc_otg_driver_remove( struct platform_device *_dev )
- +{ dwc_otg_device_t *otg_dev = platform_get_drvdata(_dev);
- +#endif
- +
- + DWC_DEBUGPL(DBG_ANY, "%s(%p) otg_dev %p\n", __func__, _dev, otg_dev);
- +
- + if (!otg_dev) {
- + /* Memory allocation for the dwc_otg_device failed. */
- + DWC_DEBUGPL(DBG_ANY, "%s: otg_dev NULL!\n", __func__);
- + return REM_RETVAL(-ENOMEM);
- + }
- +#ifndef DWC_DEVICE_ONLY
- + if (otg_dev->hcd) {
- + hcd_remove(_dev);
- + } else {
- + DWC_DEBUGPL(DBG_ANY, "%s: otg_dev->hcd NULL!\n", __func__);
- + return REM_RETVAL(-EINVAL);
- + }
- +#endif
- +
- +#ifndef DWC_HOST_ONLY
- + if (otg_dev->pcd) {
- + pcd_remove(_dev);
- + } else {
- + DWC_DEBUGPL(DBG_ANY, "%s: otg_dev->pcd NULL!\n", __func__);
- + return REM_RETVAL(-EINVAL);
- + }
- +#endif
- + /*
- + * Free the IRQ
- + */
- + if (otg_dev->common_irq_installed) {
- + free_irq(otg_dev->os_dep.irq_num, otg_dev);
- + } else {
- + DWC_DEBUGPL(DBG_ANY, "%s: There is no installed irq!\n", __func__);
- + return REM_RETVAL(-ENXIO);
- + }
- +
- + if (otg_dev->core_if) {
- + dwc_otg_cil_remove(otg_dev->core_if);
- + } else {
- + DWC_DEBUGPL(DBG_ANY, "%s: otg_dev->core_if NULL!\n", __func__);
- + return REM_RETVAL(-ENXIO);
- + }
- +
- + /*
- + * Remove the device attributes
- + */
- + dwc_otg_attr_remove(_dev);
- +
- + /*
- + * Return the memory.
- + */
- + if (otg_dev->os_dep.base) {
- + iounmap(otg_dev->os_dep.base);
- + }
- + DWC_FREE(otg_dev);
- +
- + /*
- + * Clear the drvdata pointer.
- + */
- +#ifdef LM_INTERFACE
- + lm_set_drvdata(_dev, 0);
- +#elif defined(PCI_INTERFACE)
- + release_mem_region(otg_dev->os_dep.rsrc_start,
- + otg_dev->os_dep.rsrc_len);
- + pci_set_drvdata(_dev, 0);
- +#elif defined(PLATFORM_INTERFACE)
- + platform_set_drvdata(_dev, 0);
- +#endif
- + return REM_RETVAL(0);
- +}
- +
- +/**
- + * This function is called when an lm_device is bound to a
- + * dwc_otg_driver. It creates the driver components required to
- + * control the device (CIL, HCD, and PCD) and it initializes the
- + * device. The driver components are stored in a dwc_otg_device
- + * structure. A reference to the dwc_otg_device is saved in the
- + * lm_device. This allows the driver to access the dwc_otg_device
- + * structure on subsequent calls to driver methods for this device.
- + *
- + * @param _dev Bus device
- + */
- +static int dwc_otg_driver_probe(
- +#ifdef LM_INTERFACE
- + struct lm_device *_dev
- +#elif defined(PCI_INTERFACE)
- + struct pci_dev *_dev,
- + const struct pci_device_id *id
- +#elif defined(PLATFORM_INTERFACE)
- + struct platform_device *_dev
- +#endif
- + )
- +{
- + int retval = 0;
- + dwc_otg_device_t *dwc_otg_device;
- + int devirq;
- +
- + dev_dbg(&_dev->dev, "dwc_otg_driver_probe(%p)\n", _dev);
- +#ifdef LM_INTERFACE
- + dev_dbg(&_dev->dev, "start=0x%08x\n", (unsigned)_dev->resource.start);
- +#elif defined(PCI_INTERFACE)
- + if (!id) {
- + DWC_ERROR("Invalid pci_device_id %p", id);
- + return -EINVAL;
- + }
- +
- + if (!_dev || (pci_enable_device(_dev) < 0)) {
- + DWC_ERROR("Invalid pci_device %p", _dev);
- + return -ENODEV;
- + }
- + dev_dbg(&_dev->dev, "start=0x%08x\n", (unsigned)pci_resource_start(_dev,0));
- + /* other stuff needed as well? */
- +
- +#elif defined(PLATFORM_INTERFACE)
- + dev_dbg(&_dev->dev, "start=0x%08x (len 0x%x)\n",
- + (unsigned)_dev->resource->start,
- + (unsigned)(_dev->resource->end - _dev->resource->start));
- +#endif
- +
- + dwc_otg_device = DWC_ALLOC(sizeof(dwc_otg_device_t));
- +
- + if (!dwc_otg_device) {
- + dev_err(&_dev->dev, "kmalloc of dwc_otg_device failed\n");
- + return -ENOMEM;
- + }
- +
- + memset(dwc_otg_device, 0, sizeof(*dwc_otg_device));
- + dwc_otg_device->os_dep.reg_offset = 0xFFFFFFFF;
- + dwc_otg_device->os_dep.platformdev = _dev;
- +
- + /*
- + * Map the DWC_otg Core memory into virtual address space.
- + */
- +#ifdef LM_INTERFACE
- + dwc_otg_device->os_dep.base = ioremap(_dev->resource.start, SZ_256K);
- +
- + if (!dwc_otg_device->os_dep.base) {
- + dev_err(&_dev->dev, "ioremap() failed\n");
- + DWC_FREE(dwc_otg_device);
- + return -ENOMEM;
- + }
- + dev_dbg(&_dev->dev, "base=0x%08x\n",
- + (unsigned)dwc_otg_device->os_dep.base);
- +#elif defined(PCI_INTERFACE)
- + _dev->current_state = PCI_D0;
- + _dev->dev.power.power_state = PMSG_ON;
- +
- + if (!_dev->irq) {
- + DWC_ERROR("Found HC with no IRQ. Check BIOS/PCI %s setup!",
- + pci_name(_dev));
- + iounmap(dwc_otg_device->os_dep.base);
- + DWC_FREE(dwc_otg_device);
- + return -ENODEV;
- + }
- +
- + dwc_otg_device->os_dep.rsrc_start = pci_resource_start(_dev, 0);
- + dwc_otg_device->os_dep.rsrc_len = pci_resource_len(_dev, 0);
- + DWC_DEBUGPL(DBG_ANY, "PCI resource: start=%08x, len=%08x\n",
- + (unsigned)dwc_otg_device->os_dep.rsrc_start,
- + (unsigned)dwc_otg_device->os_dep.rsrc_len);
- + if (!request_mem_region
- + (dwc_otg_device->os_dep.rsrc_start, dwc_otg_device->os_dep.rsrc_len,
- + "dwc_otg")) {
- + dev_dbg(&_dev->dev, "error requesting memory\n");
- + iounmap(dwc_otg_device->os_dep.base);
- + DWC_FREE(dwc_otg_device);
- + return -EFAULT;
- + }
- +
- + dwc_otg_device->os_dep.base =
- + ioremap(dwc_otg_device->os_dep.rsrc_start,
- + dwc_otg_device->os_dep.rsrc_len);
- + if (dwc_otg_device->os_dep.base == NULL) {
- + dev_dbg(&_dev->dev, "error mapping memory\n");
- + release_mem_region(dwc_otg_device->os_dep.rsrc_start,
- + dwc_otg_device->os_dep.rsrc_len);
- + iounmap(dwc_otg_device->os_dep.base);
- + DWC_FREE(dwc_otg_device);
- + return -EFAULT;
- + }
- + dev_dbg(&_dev->dev, "base=0x%p (before adjust) \n",
- + dwc_otg_device->os_dep.base);
- + dwc_otg_device->os_dep.base = (char *)dwc_otg_device->os_dep.base;
- + dev_dbg(&_dev->dev, "base=0x%p (after adjust) \n",
- + dwc_otg_device->os_dep.base);
- + dev_dbg(&_dev->dev, "%s: mapped PA 0x%x to VA 0x%p\n", __func__,
- + (unsigned)dwc_otg_device->os_dep.rsrc_start,
- + dwc_otg_device->os_dep.base);
- +
- + pci_set_master(_dev);
- + pci_set_drvdata(_dev, dwc_otg_device);
- +#elif defined(PLATFORM_INTERFACE)
- + DWC_DEBUGPL(DBG_ANY,"Platform resource: start=%08x, len=%08x\n",
- + _dev->resource->start,
- + _dev->resource->end - _dev->resource->start + 1);
- +#if 1
- + if (!request_mem_region(_dev->resource[0].start,
- + _dev->resource[0].end - _dev->resource[0].start + 1,
- + "dwc_otg")) {
- + dev_dbg(&_dev->dev, "error reserving mapped memory\n");
- + retval = -EFAULT;
- + goto fail;
- + }
- +
- + dwc_otg_device->os_dep.base = ioremap(_dev->resource[0].start,
- + _dev->resource[0].end -
- + _dev->resource[0].start+1);
- + if (fiq_enable)
- + {
- + if (!request_mem_region(_dev->resource[1].start,
- + _dev->resource[1].end - _dev->resource[1].start + 1,
- + "dwc_otg")) {
- + dev_dbg(&_dev->dev, "error reserving mapped memory\n");
- + retval = -EFAULT;
- + goto fail;
- + }
- +
- + dwc_otg_device->os_dep.mphi_base = ioremap(_dev->resource[1].start,
- + _dev->resource[1].end -
- + _dev->resource[1].start + 1);
- + dwc_otg_device->os_dep.use_swirq = (_dev->resource[1].end - _dev->resource[1].start) == 0x200;
- + }
- +
- +#else
- + {
- + struct map_desc desc = {
- + .virtual = IO_ADDRESS((unsigned)_dev->resource->start),
- + .pfn = __phys_to_pfn((unsigned)_dev->resource->start),
- + .length = SZ_128K,
- + .type = MT_DEVICE
- + };
- + iotable_init(&desc, 1);
- + dwc_otg_device->os_dep.base = (void *)desc.virtual;
- + }
- +#endif
- + if (!dwc_otg_device->os_dep.base) {
- + dev_err(&_dev->dev, "ioremap() failed\n");
- + retval = -ENOMEM;
- + goto fail;
- + }
- +#endif
- +
- + /*
- + * Initialize driver data to point to the global DWC_otg
- + * Device structure.
- + */
- +#ifdef LM_INTERFACE
- + lm_set_drvdata(_dev, dwc_otg_device);
- +#elif defined(PLATFORM_INTERFACE)
- + platform_set_drvdata(_dev, dwc_otg_device);
- +#endif
- + dev_dbg(&_dev->dev, "dwc_otg_device=0x%p\n", dwc_otg_device);
- +
- + dwc_otg_device->core_if = dwc_otg_cil_init(dwc_otg_device->os_dep.base);
- + DWC_DEBUGPL(DBG_HCDV, "probe of device %p given core_if %p\n",
- + dwc_otg_device, dwc_otg_device->core_if);//GRAYG
- +
- + if (!dwc_otg_device->core_if) {
- + dev_err(&_dev->dev, "CIL initialization failed!\n");
- + retval = -ENOMEM;
- + goto fail;
- + }
- +
- + dev_dbg(&_dev->dev, "Calling get_gsnpsid\n");
- + /*
- + * Attempt to ensure this device is really a DWC_otg Controller.
- + * Read and verify the SNPSID register contents. The value should be
- + * 0x45F42XXX or 0x45F42XXX, which corresponds to either "OT2" or "OTG3",
- + * as in "OTG version 2.XX" or "OTG version 3.XX".
- + */
- +
- + if (((dwc_otg_get_gsnpsid(dwc_otg_device->core_if) & 0xFFFFF000) != 0x4F542000) &&
- + ((dwc_otg_get_gsnpsid(dwc_otg_device->core_if) & 0xFFFFF000) != 0x4F543000)) {
- + dev_err(&_dev->dev, "Bad value for SNPSID: 0x%08x\n",
- + dwc_otg_get_gsnpsid(dwc_otg_device->core_if));
- + retval = -EINVAL;
- + goto fail;
- + }
- +
- + /*
- + * Validate parameter values.
- + */
- + dev_dbg(&_dev->dev, "Calling set_parameters\n");
- + if (set_parameters(dwc_otg_device->core_if)) {
- + retval = -EINVAL;
- + goto fail;
- + }
- +
- + /*
- + * Create Device Attributes in sysfs
- + */
- + dev_dbg(&_dev->dev, "Calling attr_create\n");
- + dwc_otg_attr_create(_dev);
- +
- + /*
- + * Disable the global interrupt until all the interrupt
- + * handlers are installed.
- + */
- + dev_dbg(&_dev->dev, "Calling disable_global_interrupts\n");
- + dwc_otg_disable_global_interrupts(dwc_otg_device->core_if);
- +
- + /*
- + * Install the interrupt handler for the common interrupts before
- + * enabling common interrupts in core_init below.
- + */
- +
- +#if defined(PLATFORM_INTERFACE)
- + devirq = platform_get_irq_byname(_dev, fiq_enable ? "soft" : "usb");
- + if (devirq < 0)
- + devirq = platform_get_irq(_dev, fiq_enable ? 0 : 1);
- +#else
- + devirq = _dev->irq;
- +#endif
- + DWC_DEBUGPL(DBG_CIL, "registering (common) handler for irq%d\n",
- + devirq);
- + dev_dbg(&_dev->dev, "Calling request_irq(%d)\n", devirq);
- + retval = request_irq(devirq, dwc_otg_common_irq,
- + IRQF_SHARED,
- + "dwc_otg", dwc_otg_device);
- + if (retval) {
- + DWC_ERROR("request of irq%d failed\n", devirq);
- + retval = -EBUSY;
- + goto fail;
- + } else {
- + dwc_otg_device->common_irq_installed = 1;
- + }
- + dwc_otg_device->os_dep.irq_num = devirq;
- + dwc_otg_device->os_dep.fiq_num = -EINVAL;
- + if (fiq_enable) {
- + int devfiq = platform_get_irq_byname(_dev, "usb");
- + if (devfiq < 0)
- + devfiq = platform_get_irq(_dev, 1);
- + dwc_otg_device->os_dep.fiq_num = devfiq;
- + }
- +
- +#ifndef IRQF_TRIGGER_LOW
- +#if defined(LM_INTERFACE) || defined(PLATFORM_INTERFACE)
- + dev_dbg(&_dev->dev, "Calling set_irq_type\n");
- + set_irq_type(devirq,
- +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30))
- + IRQT_LOW
- +#else
- + IRQ_TYPE_LEVEL_LOW
- +#endif
- + );
- +#endif
- +#endif /*IRQF_TRIGGER_LOW*/
- +
- + /*
- + * Initialize the DWC_otg core.
- + */
- + dev_dbg(&_dev->dev, "Calling dwc_otg_core_init\n");
- + dwc_otg_core_init(dwc_otg_device->core_if);
- +
- +#ifndef DWC_HOST_ONLY
- + /*
- + * Initialize the PCD
- + */
- + dev_dbg(&_dev->dev, "Calling pcd_init\n");
- + retval = pcd_init(_dev);
- + if (retval != 0) {
- + DWC_ERROR("pcd_init failed\n");
- + dwc_otg_device->pcd = NULL;
- + goto fail;
- + }
- +#endif
- +#ifndef DWC_DEVICE_ONLY
- + /*
- + * Initialize the HCD
- + */
- + dev_dbg(&_dev->dev, "Calling hcd_init\n");
- + retval = hcd_init(_dev);
- + if (retval != 0) {
- + DWC_ERROR("hcd_init failed\n");
- + dwc_otg_device->hcd = NULL;
- + goto fail;
- + }
- +#endif
- + /* Recover from drvdata having been overwritten by hcd_init() */
- +#ifdef LM_INTERFACE
- + lm_set_drvdata(_dev, dwc_otg_device);
- +#elif defined(PLATFORM_INTERFACE)
- + platform_set_drvdata(_dev, dwc_otg_device);
- +#elif defined(PCI_INTERFACE)
- + pci_set_drvdata(_dev, dwc_otg_device);
- + dwc_otg_device->os_dep.pcidev = _dev;
- +#endif
- +
- + /*
- + * Enable the global interrupt after all the interrupt
- + * handlers are installed if there is no ADP support else
- + * perform initial actions required for Internal ADP logic.
- + */
- + if (!dwc_otg_get_param_adp_enable(dwc_otg_device->core_if)) {
- + dev_dbg(&_dev->dev, "Calling enable_global_interrupts\n");
- + dwc_otg_enable_global_interrupts(dwc_otg_device->core_if);
- + dev_dbg(&_dev->dev, "Done\n");
- + } else
- + dwc_otg_adp_start(dwc_otg_device->core_if,
- + dwc_otg_is_host_mode(dwc_otg_device->core_if));
- +
- + return 0;
- +
- +fail:
- + dwc_otg_driver_remove(_dev);
- + return retval;
- +}
- +
- +/**
- + * This structure defines the methods to be called by a bus driver
- + * during the lifecycle of a device on that bus. Both drivers and
- + * devices are registered with a bus driver. The bus driver matches
- + * devices to drivers based on information in the device and driver
- + * structures.
- + *
- + * The probe function is called when the bus driver matches a device
- + * to this driver. The remove function is called when a device is
- + * unregistered with the bus driver.
- + */
- +#ifdef LM_INTERFACE
- +static struct lm_driver dwc_otg_driver = {
- + .drv = {.name = (char *)dwc_driver_name,},
- + .probe = dwc_otg_driver_probe,
- + .remove = dwc_otg_driver_remove,
- + // 'suspend' and 'resume' absent
- +};
- +#elif defined(PCI_INTERFACE)
- +static const struct pci_device_id pci_ids[] = { {
- + PCI_DEVICE(0x16c3, 0xabcd),
- + .driver_data =
- + (unsigned long)0xdeadbeef,
- + }, { /* end: all zeroes */ }
- +};
- +
- +MODULE_DEVICE_TABLE(pci, pci_ids);
- +
- +/* pci driver glue; this is a "new style" PCI driver module */
- +static struct pci_driver dwc_otg_driver = {
- + .name = "dwc_otg",
- + .id_table = pci_ids,
- +
- + .probe = dwc_otg_driver_probe,
- + .remove = dwc_otg_driver_remove,
- +
- + .driver = {
- + .name = (char *)dwc_driver_name,
- + },
- +};
- +#elif defined(PLATFORM_INTERFACE)
- +static struct platform_device_id platform_ids[] = {
- + {
- + .name = "bcm2708_usb",
- + .driver_data = (kernel_ulong_t) 0xdeadbeef,
- + },
- + { /* end: all zeroes */ }
- +};
- +MODULE_DEVICE_TABLE(platform, platform_ids);
- +
- +static const struct of_device_id dwc_otg_of_match_table[] = {
- + { .compatible = "brcm,bcm2708-usb", },
- + {},
- +};
- +MODULE_DEVICE_TABLE(of, dwc_otg_of_match_table);
- +
- +static struct platform_driver dwc_otg_driver = {
- + .driver = {
- + .name = (char *)dwc_driver_name,
- + .of_match_table = dwc_otg_of_match_table,
- + },
- + .id_table = platform_ids,
- +
- + .probe = dwc_otg_driver_probe,
- + .remove = dwc_otg_driver_remove,
- + // no 'shutdown', 'suspend', 'resume', 'suspend_late' or 'resume_early'
- +};
- +#endif
- +
- +/**
- + * This function is called when the dwc_otg_driver is installed with the
- + * insmod command. It registers the dwc_otg_driver structure with the
- + * appropriate bus driver. This will cause the dwc_otg_driver_probe function
- + * to be called. In addition, the bus driver will automatically expose
- + * attributes defined for the device and driver in the special sysfs file
- + * system.
- + *
- + * @return
- + */
- +static int __init dwc_otg_driver_init(void)
- +{
- + int retval = 0;
- + int error;
- + struct device_driver *drv;
- +
- + 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,
- + DWC_DRIVER_VERSION,
- +#ifdef LM_INTERFACE
- + "logicmodule");
- + retval = lm_driver_register(&dwc_otg_driver);
- + drv = &dwc_otg_driver.drv;
- +#elif defined(PCI_INTERFACE)
- + "pci");
- + retval = pci_register_driver(&dwc_otg_driver);
- + drv = &dwc_otg_driver.driver;
- +#elif defined(PLATFORM_INTERFACE)
- + "platform");
- + retval = platform_driver_register(&dwc_otg_driver);
- + drv = &dwc_otg_driver.driver;
- +#endif
- + if (retval < 0) {
- + printk(KERN_ERR "%s retval=%d\n", __func__, retval);
- + return retval;
- + }
- + 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
- + error = driver_create_file(drv, &driver_attr_debuglevel);
- +#endif
- + return retval;
- +}
- +
- +module_init(dwc_otg_driver_init);
- +
- +/**
- + * This function is called when the driver is removed from the kernel
- + * with the rmmod command. The driver unregisters itself with its bus
- + * driver.
- + *
- + */
- +static void __exit dwc_otg_driver_cleanup(void)
- +{
- + printk(KERN_DEBUG "dwc_otg_driver_cleanup()\n");
- +
- +#ifdef LM_INTERFACE
- + driver_remove_file(&dwc_otg_driver.drv, &driver_attr_debuglevel);
- + driver_remove_file(&dwc_otg_driver.drv, &driver_attr_version);
- + lm_driver_unregister(&dwc_otg_driver);
- +#elif defined(PCI_INTERFACE)
- + driver_remove_file(&dwc_otg_driver.driver, &driver_attr_debuglevel);
- + driver_remove_file(&dwc_otg_driver.driver, &driver_attr_version);
- + pci_unregister_driver(&dwc_otg_driver);
- +#elif defined(PLATFORM_INTERFACE)
- + driver_remove_file(&dwc_otg_driver.driver, &driver_attr_debuglevel);
- + driver_remove_file(&dwc_otg_driver.driver, &driver_attr_version);
- + platform_driver_unregister(&dwc_otg_driver);
- +#endif
- +
- + printk(KERN_INFO "%s module removed\n", dwc_driver_name);
- +}
- +
- +module_exit(dwc_otg_driver_cleanup);
- +
- +MODULE_DESCRIPTION(DWC_DRIVER_DESC);
- +MODULE_AUTHOR("Synopsys Inc.");
- +MODULE_LICENSE("GPL");
- +
- +module_param_named(otg_cap, dwc_otg_module_params.otg_cap, int, 0444);
- +MODULE_PARM_DESC(otg_cap, "OTG Capabilities 0=HNP&SRP 1=SRP Only 2=None");
- +module_param_named(opt, dwc_otg_module_params.opt, int, 0444);
- +MODULE_PARM_DESC(opt, "OPT Mode");
- +module_param_named(dma_enable, dwc_otg_module_params.dma_enable, int, 0444);
- +MODULE_PARM_DESC(dma_enable, "DMA Mode 0=Slave 1=DMA enabled");
- +
- +module_param_named(dma_desc_enable, dwc_otg_module_params.dma_desc_enable, int,
- + 0444);
- +MODULE_PARM_DESC(dma_desc_enable,
- + "DMA Desc Mode 0=Address DMA 1=DMA Descriptor enabled");
- +
- +module_param_named(dma_burst_size, dwc_otg_module_params.dma_burst_size, int,
- + 0444);
- +MODULE_PARM_DESC(dma_burst_size,
- + "DMA Burst Size 1, 4, 8, 16, 32, 64, 128, 256");
- +module_param_named(speed, dwc_otg_module_params.speed, int, 0444);
- +MODULE_PARM_DESC(speed, "Speed 0=High Speed 1=Full Speed");
- +module_param_named(host_support_fs_ls_low_power,
- + dwc_otg_module_params.host_support_fs_ls_low_power, int,
- + 0444);
- +MODULE_PARM_DESC(host_support_fs_ls_low_power,
- + "Support Low Power w/FS or LS 0=Support 1=Don't Support");
- +module_param_named(host_ls_low_power_phy_clk,
- + dwc_otg_module_params.host_ls_low_power_phy_clk, int, 0444);
- +MODULE_PARM_DESC(host_ls_low_power_phy_clk,
- + "Low Speed Low Power Clock 0=48Mhz 1=6Mhz");
- +module_param_named(enable_dynamic_fifo,
- + dwc_otg_module_params.enable_dynamic_fifo, int, 0444);
- +MODULE_PARM_DESC(enable_dynamic_fifo, "0=cC Setting 1=Allow Dynamic Sizing");
- +module_param_named(data_fifo_size, dwc_otg_module_params.data_fifo_size, int,
- + 0444);
- +MODULE_PARM_DESC(data_fifo_size,
- + "Total number of words in the data FIFO memory 32-32768");
- +module_param_named(dev_rx_fifo_size, dwc_otg_module_params.dev_rx_fifo_size,
- + int, 0444);
- +MODULE_PARM_DESC(dev_rx_fifo_size, "Number of words in the Rx FIFO 16-32768");
- +module_param_named(dev_nperio_tx_fifo_size,
- + dwc_otg_module_params.dev_nperio_tx_fifo_size, int, 0444);
- +MODULE_PARM_DESC(dev_nperio_tx_fifo_size,
- + "Number of words in the non-periodic Tx FIFO 16-32768");
- +module_param_named(dev_perio_tx_fifo_size_1,
- + dwc_otg_module_params.dev_perio_tx_fifo_size[0], int, 0444);
- +MODULE_PARM_DESC(dev_perio_tx_fifo_size_1,
- + "Number of words in the periodic Tx FIFO 4-768");
- +module_param_named(dev_perio_tx_fifo_size_2,
- + dwc_otg_module_params.dev_perio_tx_fifo_size[1], int, 0444);
- +MODULE_PARM_DESC(dev_perio_tx_fifo_size_2,
- + "Number of words in the periodic Tx FIFO 4-768");
- +module_param_named(dev_perio_tx_fifo_size_3,
- + dwc_otg_module_params.dev_perio_tx_fifo_size[2], int, 0444);
- +MODULE_PARM_DESC(dev_perio_tx_fifo_size_3,
- + "Number of words in the periodic Tx FIFO 4-768");
- +module_param_named(dev_perio_tx_fifo_size_4,
- + dwc_otg_module_params.dev_perio_tx_fifo_size[3], int, 0444);
- +MODULE_PARM_DESC(dev_perio_tx_fifo_size_4,
- + "Number of words in the periodic Tx FIFO 4-768");
- +module_param_named(dev_perio_tx_fifo_size_5,
- + dwc_otg_module_params.dev_perio_tx_fifo_size[4], int, 0444);
- +MODULE_PARM_DESC(dev_perio_tx_fifo_size_5,
- + "Number of words in the periodic Tx FIFO 4-768");
- +module_param_named(dev_perio_tx_fifo_size_6,
- + dwc_otg_module_params.dev_perio_tx_fifo_size[5], int, 0444);
- +MODULE_PARM_DESC(dev_perio_tx_fifo_size_6,
- + "Number of words in the periodic Tx FIFO 4-768");
- +module_param_named(dev_perio_tx_fifo_size_7,
- + dwc_otg_module_params.dev_perio_tx_fifo_size[6], int, 0444);
- +MODULE_PARM_DESC(dev_perio_tx_fifo_size_7,
- + "Number of words in the periodic Tx FIFO 4-768");
- +module_param_named(dev_perio_tx_fifo_size_8,
- + dwc_otg_module_params.dev_perio_tx_fifo_size[7], int, 0444);
- +MODULE_PARM_DESC(dev_perio_tx_fifo_size_8,
- + "Number of words in the periodic Tx FIFO 4-768");
- +module_param_named(dev_perio_tx_fifo_size_9,
- + dwc_otg_module_params.dev_perio_tx_fifo_size[8], int, 0444);
- +MODULE_PARM_DESC(dev_perio_tx_fifo_size_9,
- + "Number of words in the periodic Tx FIFO 4-768");
- +module_param_named(dev_perio_tx_fifo_size_10,
- + dwc_otg_module_params.dev_perio_tx_fifo_size[9], int, 0444);
- +MODULE_PARM_DESC(dev_perio_tx_fifo_size_10,
- + "Number of words in the periodic Tx FIFO 4-768");
- +module_param_named(dev_perio_tx_fifo_size_11,
- + dwc_otg_module_params.dev_perio_tx_fifo_size[10], int, 0444);
- +MODULE_PARM_DESC(dev_perio_tx_fifo_size_11,
- + "Number of words in the periodic Tx FIFO 4-768");
- +module_param_named(dev_perio_tx_fifo_size_12,
- + dwc_otg_module_params.dev_perio_tx_fifo_size[11], int, 0444);
- +MODULE_PARM_DESC(dev_perio_tx_fifo_size_12,
- + "Number of words in the periodic Tx FIFO 4-768");
- +module_param_named(dev_perio_tx_fifo_size_13,
- + dwc_otg_module_params.dev_perio_tx_fifo_size[12], int, 0444);
- +MODULE_PARM_DESC(dev_perio_tx_fifo_size_13,
- + "Number of words in the periodic Tx FIFO 4-768");
- +module_param_named(dev_perio_tx_fifo_size_14,
- + dwc_otg_module_params.dev_perio_tx_fifo_size[13], int, 0444);
- +MODULE_PARM_DESC(dev_perio_tx_fifo_size_14,
- + "Number of words in the periodic Tx FIFO 4-768");
- +module_param_named(dev_perio_tx_fifo_size_15,
- + dwc_otg_module_params.dev_perio_tx_fifo_size[14], int, 0444);
- +MODULE_PARM_DESC(dev_perio_tx_fifo_size_15,
- + "Number of words in the periodic Tx FIFO 4-768");
- +module_param_named(host_rx_fifo_size, dwc_otg_module_params.host_rx_fifo_size,
- + int, 0444);
- +MODULE_PARM_DESC(host_rx_fifo_size, "Number of words in the Rx FIFO 16-32768");
- +module_param_named(host_nperio_tx_fifo_size,
- + dwc_otg_module_params.host_nperio_tx_fifo_size, int, 0444);
- +MODULE_PARM_DESC(host_nperio_tx_fifo_size,
- + "Number of words in the non-periodic Tx FIFO 16-32768");
- +module_param_named(host_perio_tx_fifo_size,
- + dwc_otg_module_params.host_perio_tx_fifo_size, int, 0444);
- +MODULE_PARM_DESC(host_perio_tx_fifo_size,
- + "Number of words in the host periodic Tx FIFO 16-32768");
- +module_param_named(max_transfer_size, dwc_otg_module_params.max_transfer_size,
- + int, 0444);
- +/** @todo Set the max to 512K, modify checks */
- +MODULE_PARM_DESC(max_transfer_size,
- + "The maximum transfer size supported in bytes 2047-65535");
- +module_param_named(max_packet_count, dwc_otg_module_params.max_packet_count,
- + int, 0444);
- +MODULE_PARM_DESC(max_packet_count,
- + "The maximum number of packets in a transfer 15-511");
- +module_param_named(host_channels, dwc_otg_module_params.host_channels, int,
- + 0444);
- +MODULE_PARM_DESC(host_channels,
- + "The number of host channel registers to use 1-16");
- +module_param_named(dev_endpoints, dwc_otg_module_params.dev_endpoints, int,
- + 0444);
- +MODULE_PARM_DESC(dev_endpoints,
- + "The number of endpoints in addition to EP0 available for device mode 1-15");
- +module_param_named(phy_type, dwc_otg_module_params.phy_type, int, 0444);
- +MODULE_PARM_DESC(phy_type, "0=Reserved 1=UTMI+ 2=ULPI");
- +module_param_named(phy_utmi_width, dwc_otg_module_params.phy_utmi_width, int,
- + 0444);
- +MODULE_PARM_DESC(phy_utmi_width, "Specifies the UTMI+ Data Width 8 or 16 bits");
- +module_param_named(phy_ulpi_ddr, dwc_otg_module_params.phy_ulpi_ddr, int, 0444);
- +MODULE_PARM_DESC(phy_ulpi_ddr,
- + "ULPI at double or single data rate 0=Single 1=Double");
- +module_param_named(phy_ulpi_ext_vbus, dwc_otg_module_params.phy_ulpi_ext_vbus,
- + int, 0444);
- +MODULE_PARM_DESC(phy_ulpi_ext_vbus,
- + "ULPI PHY using internal or external vbus 0=Internal");
- +module_param_named(i2c_enable, dwc_otg_module_params.i2c_enable, int, 0444);
- +MODULE_PARM_DESC(i2c_enable, "FS PHY Interface");
- +module_param_named(ulpi_fs_ls, dwc_otg_module_params.ulpi_fs_ls, int, 0444);
- +MODULE_PARM_DESC(ulpi_fs_ls, "ULPI PHY FS/LS mode only");
- +module_param_named(ts_dline, dwc_otg_module_params.ts_dline, int, 0444);
- +MODULE_PARM_DESC(ts_dline, "Term select Dline pulsing for all PHYs");
- +module_param_named(debug, g_dbg_lvl, int, 0444);
- +MODULE_PARM_DESC(debug, "");
- +
- +module_param_named(en_multiple_tx_fifo,
- + dwc_otg_module_params.en_multiple_tx_fifo, int, 0444);
- +MODULE_PARM_DESC(en_multiple_tx_fifo,
- + "Dedicated Non Periodic Tx FIFOs 0=disabled 1=enabled");
- +module_param_named(dev_tx_fifo_size_1,
- + dwc_otg_module_params.dev_tx_fifo_size[0], int, 0444);
- +MODULE_PARM_DESC(dev_tx_fifo_size_1, "Number of words in the Tx FIFO 4-768");
- +module_param_named(dev_tx_fifo_size_2,
- + dwc_otg_module_params.dev_tx_fifo_size[1], int, 0444);
- +MODULE_PARM_DESC(dev_tx_fifo_size_2, "Number of words in the Tx FIFO 4-768");
- +module_param_named(dev_tx_fifo_size_3,
- + dwc_otg_module_params.dev_tx_fifo_size[2], int, 0444);
- +MODULE_PARM_DESC(dev_tx_fifo_size_3, "Number of words in the Tx FIFO 4-768");
- +module_param_named(dev_tx_fifo_size_4,
- + dwc_otg_module_params.dev_tx_fifo_size[3], int, 0444);
- +MODULE_PARM_DESC(dev_tx_fifo_size_4, "Number of words in the Tx FIFO 4-768");
- +module_param_named(dev_tx_fifo_size_5,
- + dwc_otg_module_params.dev_tx_fifo_size[4], int, 0444);
- +MODULE_PARM_DESC(dev_tx_fifo_size_5, "Number of words in the Tx FIFO 4-768");
- +module_param_named(dev_tx_fifo_size_6,
- + dwc_otg_module_params.dev_tx_fifo_size[5], int, 0444);
- +MODULE_PARM_DESC(dev_tx_fifo_size_6, "Number of words in the Tx FIFO 4-768");
- +module_param_named(dev_tx_fifo_size_7,
- + dwc_otg_module_params.dev_tx_fifo_size[6], int, 0444);
- +MODULE_PARM_DESC(dev_tx_fifo_size_7, "Number of words in the Tx FIFO 4-768");
- +module_param_named(dev_tx_fifo_size_8,
- + dwc_otg_module_params.dev_tx_fifo_size[7], int, 0444);
- +MODULE_PARM_DESC(dev_tx_fifo_size_8, "Number of words in the Tx FIFO 4-768");
- +module_param_named(dev_tx_fifo_size_9,
- + dwc_otg_module_params.dev_tx_fifo_size[8], int, 0444);
- +MODULE_PARM_DESC(dev_tx_fifo_size_9, "Number of words in the Tx FIFO 4-768");
- +module_param_named(dev_tx_fifo_size_10,
- + dwc_otg_module_params.dev_tx_fifo_size[9], int, 0444);
- +MODULE_PARM_DESC(dev_tx_fifo_size_10, "Number of words in the Tx FIFO 4-768");
- +module_param_named(dev_tx_fifo_size_11,
- + dwc_otg_module_params.dev_tx_fifo_size[10], int, 0444);
- +MODULE_PARM_DESC(dev_tx_fifo_size_11, "Number of words in the Tx FIFO 4-768");
- +module_param_named(dev_tx_fifo_size_12,
- + dwc_otg_module_params.dev_tx_fifo_size[11], int, 0444);
- +MODULE_PARM_DESC(dev_tx_fifo_size_12, "Number of words in the Tx FIFO 4-768");
- +module_param_named(dev_tx_fifo_size_13,
- + dwc_otg_module_params.dev_tx_fifo_size[12], int, 0444);
- +MODULE_PARM_DESC(dev_tx_fifo_size_13, "Number of words in the Tx FIFO 4-768");
- +module_param_named(dev_tx_fifo_size_14,
- + dwc_otg_module_params.dev_tx_fifo_size[13], int, 0444);
- +MODULE_PARM_DESC(dev_tx_fifo_size_14, "Number of words in the Tx FIFO 4-768");
- +module_param_named(dev_tx_fifo_size_15,
- + dwc_otg_module_params.dev_tx_fifo_size[14], int, 0444);
- +MODULE_PARM_DESC(dev_tx_fifo_size_15, "Number of words in the Tx FIFO 4-768");
- +
- +module_param_named(thr_ctl, dwc_otg_module_params.thr_ctl, int, 0444);
- +MODULE_PARM_DESC(thr_ctl,
- + "Thresholding enable flag bit 0 - non ISO Tx thr., 1 - ISO Tx thr., 2 - Rx thr.- bit 0=disabled 1=enabled");
- +module_param_named(tx_thr_length, dwc_otg_module_params.tx_thr_length, int,
- + 0444);
- +MODULE_PARM_DESC(tx_thr_length, "Tx Threshold length in 32 bit DWORDs");
- +module_param_named(rx_thr_length, dwc_otg_module_params.rx_thr_length, int,
- + 0444);
- +MODULE_PARM_DESC(rx_thr_length, "Rx Threshold length in 32 bit DWORDs");
- +
- +module_param_named(pti_enable, dwc_otg_module_params.pti_enable, int, 0444);
- +module_param_named(mpi_enable, dwc_otg_module_params.mpi_enable, int, 0444);
- +module_param_named(lpm_enable, dwc_otg_module_params.lpm_enable, int, 0444);
- +MODULE_PARM_DESC(lpm_enable, "LPM Enable 0=LPM Disabled 1=LPM Enabled");
- +module_param_named(ic_usb_cap, dwc_otg_module_params.ic_usb_cap, int, 0444);
- +MODULE_PARM_DESC(ic_usb_cap,
- + "IC_USB Capability 0=IC_USB Disabled 1=IC_USB Enabled");
- +module_param_named(ahb_thr_ratio, dwc_otg_module_params.ahb_thr_ratio, int,
- + 0444);
- +MODULE_PARM_DESC(ahb_thr_ratio, "AHB Threshold Ratio");
- +module_param_named(power_down, dwc_otg_module_params.power_down, int, 0444);
- +MODULE_PARM_DESC(power_down, "Power Down Mode");
- +module_param_named(reload_ctl, dwc_otg_module_params.reload_ctl, int, 0444);
- +MODULE_PARM_DESC(reload_ctl, "HFIR Reload Control");
- +module_param_named(dev_out_nak, dwc_otg_module_params.dev_out_nak, int, 0444);
- +MODULE_PARM_DESC(dev_out_nak, "Enable Device OUT NAK");
- +module_param_named(cont_on_bna, dwc_otg_module_params.cont_on_bna, int, 0444);
- +MODULE_PARM_DESC(cont_on_bna, "Enable Enable Continue on BNA");
- +module_param_named(ahb_single, dwc_otg_module_params.ahb_single, int, 0444);
- +MODULE_PARM_DESC(ahb_single, "Enable AHB Single Support");
- +module_param_named(adp_enable, dwc_otg_module_params.adp_enable, int, 0444);
- +MODULE_PARM_DESC(adp_enable, "ADP Enable 0=ADP Disabled 1=ADP Enabled");
- +module_param_named(otg_ver, dwc_otg_module_params.otg_ver, int, 0444);
- +MODULE_PARM_DESC(otg_ver, "OTG revision supported 0=OTG 1.3 1=OTG 2.0");
- +module_param(microframe_schedule, bool, 0444);
- +MODULE_PARM_DESC(microframe_schedule, "Enable the microframe scheduler");
- +
- +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.");
- +module_param(int_ep_interval_min, ushort, 0644);
- +MODULE_PARM_DESC(int_ep_interval_min, "Clamp high-speed Interrupt endpoints to a minimum polling interval.\n"
- + "0..1 = Use endpoint default\n"
- + "2..n = Minimum interval n microframes. Use powers of 2.\n");
- +
- +module_param(cil_force_host, bool, 0644);
- +MODULE_PARM_DESC(cil_force_host, "On a connector-ID status change, "
- + "force Host Mode regardless of OTG state.");
- +
- +/** @page "Module Parameters"
- + *
- + * The following parameters may be specified when starting the module.
- + * These parameters define how the DWC_otg controller should be
- + * configured. Parameter values are passed to the CIL initialization
- + * function dwc_otg_cil_init
- + *
- + * Example: <code>modprobe dwc_otg speed=1 otg_cap=1</code>
- + *
- +
- + <table>
- + <tr><td>Parameter Name</td><td>Meaning</td></tr>
- +
- + <tr>
- + <td>otg_cap</td>
- + <td>Specifies the OTG capabilities. The driver will automatically detect the
- + value for this parameter if none is specified.
- + - 0: HNP and SRP capable (default, if available)
- + - 1: SRP Only capable
- + - 2: No HNP/SRP capable
- + </td></tr>
- +
- + <tr>
- + <td>dma_enable</td>
- + <td>Specifies whether to use slave or DMA mode for accessing the data FIFOs.
- + The driver will automatically detect the value for this parameter if none is
- + specified.
- + - 0: Slave
- + - 1: DMA (default, if available)
- + </td></tr>
- +
- + <tr>
- + <td>dma_burst_size</td>
- + <td>The DMA Burst size (applicable only for External DMA Mode).
- + - Values: 1, 4, 8 16, 32, 64, 128, 256 (default 32)
- + </td></tr>
- +
- + <tr>
- + <td>speed</td>
- + <td>Specifies the maximum speed of operation in host and device mode. The
- + actual speed depends on the speed of the attached device and the value of
- + phy_type.
- + - 0: High Speed (default)
- + - 1: Full Speed
- + </td></tr>
- +
- + <tr>
- + <td>host_support_fs_ls_low_power</td>
- + <td>Specifies whether low power mode is supported when attached to a Full
- + Speed or Low Speed device in host mode.
- + - 0: Don't support low power mode (default)
- + - 1: Support low power mode
- + </td></tr>
- +
- + <tr>
- + <td>host_ls_low_power_phy_clk</td>
- + <td>Specifies the PHY clock rate in low power mode when connected to a Low
- + Speed device in host mode. This parameter is applicable only if
- + HOST_SUPPORT_FS_LS_LOW_POWER is enabled.
- + - 0: 48 MHz (default)
- + - 1: 6 MHz
- + </td></tr>
- +
- + <tr>
- + <td>enable_dynamic_fifo</td>
- + <td> Specifies whether FIFOs may be resized by the driver software.
- + - 0: Use cC FIFO size parameters
- + - 1: Allow dynamic FIFO sizing (default)
- + </td></tr>
- +
- + <tr>
- + <td>data_fifo_size</td>
- + <td>Total number of 4-byte words in the data FIFO memory. This memory
- + includes the Rx FIFO, non-periodic Tx FIFO, and periodic Tx FIFOs.
- + - Values: 32 to 32768 (default 8192)
- +
- + Note: The total FIFO memory depth in the FPGA configuration is 8192.
- + </td></tr>
- +
- + <tr>
- + <td>dev_rx_fifo_size</td>
- + <td>Number of 4-byte words in the Rx FIFO in device mode when dynamic
- + FIFO sizing is enabled.
- + - Values: 16 to 32768 (default 1064)
- + </td></tr>
- +
- + <tr>
- + <td>dev_nperio_tx_fifo_size</td>
- + <td>Number of 4-byte words in the non-periodic Tx FIFO in device mode when
- + dynamic FIFO sizing is enabled.
- + - Values: 16 to 32768 (default 1024)
- + </td></tr>
- +
- + <tr>
- + <td>dev_perio_tx_fifo_size_n (n = 1 to 15)</td>
- + <td>Number of 4-byte words in each of the periodic Tx FIFOs in device mode
- + when dynamic FIFO sizing is enabled.
- + - Values: 4 to 768 (default 256)
- + </td></tr>
- +
- + <tr>
- + <td>host_rx_fifo_size</td>
- + <td>Number of 4-byte words in the Rx FIFO in host mode when dynamic FIFO
- + sizing is enabled.
- + - Values: 16 to 32768 (default 1024)
- + </td></tr>
- +
- + <tr>
- + <td>host_nperio_tx_fifo_size</td>
- + <td>Number of 4-byte words in the non-periodic Tx FIFO in host mode when
- + dynamic FIFO sizing is enabled in the core.
- + - Values: 16 to 32768 (default 1024)
- + </td></tr>
- +
- + <tr>
- + <td>host_perio_tx_fifo_size</td>
- + <td>Number of 4-byte words in the host periodic Tx FIFO when dynamic FIFO
- + sizing is enabled.
- + - Values: 16 to 32768 (default 1024)
- + </td></tr>
- +
- + <tr>
- + <td>max_transfer_size</td>
- + <td>The maximum transfer size supported in bytes.
- + - Values: 2047 to 65,535 (default 65,535)
- + </td></tr>
- +
- + <tr>
- + <td>max_packet_count</td>
- + <td>The maximum number of packets in a transfer.
- + - Values: 15 to 511 (default 511)
- + </td></tr>
- +
- + <tr>
- + <td>host_channels</td>
- + <td>The number of host channel registers to use.
- + - Values: 1 to 16 (default 12)
- +
- + Note: The FPGA configuration supports a maximum of 12 host channels.
- + </td></tr>
- +
- + <tr>
- + <td>dev_endpoints</td>
- + <td>The number of endpoints in addition to EP0 available for device mode
- + operations.
- + - Values: 1 to 15 (default 6 IN and OUT)
- +
- + Note: The FPGA configuration supports a maximum of 6 IN and OUT endpoints in
- + addition to EP0.
- + </td></tr>
- +
- + <tr>
- + <td>phy_type</td>
- + <td>Specifies the type of PHY interface to use. By default, the driver will
- + automatically detect the phy_type.
- + - 0: Full Speed
- + - 1: UTMI+ (default, if available)
- + - 2: ULPI
- + </td></tr>
- +
- + <tr>
- + <td>phy_utmi_width</td>
- + <td>Specifies the UTMI+ Data Width. This parameter is applicable for a
- + phy_type of UTMI+. Also, this parameter is applicable only if the
- + OTG_HSPHY_WIDTH cC parameter was set to "8 and 16 bits", meaning that the
- + core has been configured to work at either data path width.
- + - Values: 8 or 16 bits (default 16)
- + </td></tr>
- +
- + <tr>
- + <td>phy_ulpi_ddr</td>
- + <td>Specifies whether the ULPI operates at double or single data rate. This
- + parameter is only applicable if phy_type is ULPI.
- + - 0: single data rate ULPI interface with 8 bit wide data bus (default)
- + - 1: double data rate ULPI interface with 4 bit wide data bus
- + </td></tr>
- +
- + <tr>
- + <td>i2c_enable</td>
- + <td>Specifies whether to use the I2C interface for full speed PHY. This
- + parameter is only applicable if PHY_TYPE is FS.
- + - 0: Disabled (default)
- + - 1: Enabled
- + </td></tr>
- +
- + <tr>
- + <td>ulpi_fs_ls</td>
- + <td>Specifies whether to use ULPI FS/LS mode only.
- + - 0: Disabled (default)
- + - 1: Enabled
- + </td></tr>
- +
- + <tr>
- + <td>ts_dline</td>
- + <td>Specifies whether term select D-Line pulsing for all PHYs is enabled.
- + - 0: Disabled (default)
- + - 1: Enabled
- + </td></tr>
- +
- + <tr>
- + <td>en_multiple_tx_fifo</td>
- + <td>Specifies whether dedicatedto tx fifos are enabled for non periodic IN EPs.
- + The driver will automatically detect the value for this parameter if none is
- + specified.
- + - 0: Disabled
- + - 1: Enabled (default, if available)
- + </td></tr>
- +
- + <tr>
- + <td>dev_tx_fifo_size_n (n = 1 to 15)</td>
- + <td>Number of 4-byte words in each of the Tx FIFOs in device mode
- + when dynamic FIFO sizing is enabled.
- + - Values: 4 to 768 (default 256)
- + </td></tr>
- +
- + <tr>
- + <td>tx_thr_length</td>
- + <td>Transmit Threshold length in 32 bit double words
- + - Values: 8 to 128 (default 64)
- + </td></tr>
- +
- + <tr>
- + <td>rx_thr_length</td>
- + <td>Receive Threshold length in 32 bit double words
- + - Values: 8 to 128 (default 64)
- + </td></tr>
- +
- +<tr>
- + <td>thr_ctl</td>
- + <td>Specifies whether to enable Thresholding for Device mode. Bits 0, 1, 2 of
- + this parmater specifies if thresholding is enabled for non-Iso Tx, Iso Tx and
- + Rx transfers accordingly.
- + The driver will automatically detect the value for this parameter if none is
- + specified.
- + - Values: 0 to 7 (default 0)
- + Bit values indicate:
- + - 0: Thresholding disabled
- + - 1: Thresholding enabled
- + </td></tr>
- +
- +<tr>
- + <td>dma_desc_enable</td>
- + <td>Specifies whether to enable Descriptor DMA mode.
- + The driver will automatically detect the value for this parameter if none is
- + specified.
- + - 0: Descriptor DMA disabled
- + - 1: Descriptor DMA (default, if available)
- + </td></tr>
- +
- +<tr>
- + <td>mpi_enable</td>
- + <td>Specifies whether to enable MPI enhancement mode.
- + The driver will automatically detect the value for this parameter if none is
- + specified.
- + - 0: MPI disabled (default)
- + - 1: MPI enable
- + </td></tr>
- +
- +<tr>
- + <td>pti_enable</td>
- + <td>Specifies whether to enable PTI enhancement support.
- + The driver will automatically detect the value for this parameter if none is
- + specified.
- + - 0: PTI disabled (default)
- + - 1: PTI enable
- + </td></tr>
- +
- +<tr>
- + <td>lpm_enable</td>
- + <td>Specifies whether to enable LPM support.
- + The driver will automatically detect the value for this parameter if none is
- + specified.
- + - 0: LPM disabled
- + - 1: LPM enable (default, if available)
- + </td></tr>
- +
- +<tr>
- + <td>ic_usb_cap</td>
- + <td>Specifies whether to enable IC_USB capability.
- + The driver will automatically detect the value for this parameter if none is
- + specified.
- + - 0: IC_USB disabled (default, if available)
- + - 1: IC_USB enable
- + </td></tr>
- +
- +<tr>
- + <td>ahb_thr_ratio</td>
- + <td>Specifies AHB Threshold ratio.
- + - Values: 0 to 3 (default 0)
- + </td></tr>
- +
- +<tr>
- + <td>power_down</td>
- + <td>Specifies Power Down(Hibernation) Mode.
- + The driver will automatically detect the value for this parameter if none is
- + specified.
- + - 0: Power Down disabled (default)
- + - 2: Power Down enabled
- + </td></tr>
- +
- + <tr>
- + <td>reload_ctl</td>
- + <td>Specifies whether dynamic reloading of the HFIR register is allowed during
- + run time. The driver will automatically detect the value for this parameter if
- + none is specified. In case the HFIR value is reloaded when HFIR.RldCtrl == 1'b0
- + the core might misbehave.
- + - 0: Reload Control disabled (default)
- + - 1: Reload Control enabled
- + </td></tr>
- +
- + <tr>
- + <td>dev_out_nak</td>
- + <td>Specifies whether Device OUT NAK enhancement enabled or no.
- + The driver will automatically detect the value for this parameter if
- + none is specified. This parameter is valid only when OTG_EN_DESC_DMA == 1b1.
- + - 0: The core does not set NAK after Bulk OUT transfer complete (default)
- + - 1: The core sets NAK after Bulk OUT transfer complete
- + </td></tr>
- +
- + <tr>
- + <td>cont_on_bna</td>
- + <td>Specifies whether Enable Continue on BNA enabled or no.
- + After receiving BNA interrupt the core disables the endpoint,when the
- + endpoint is re-enabled by the application the
- + - 0: Core starts processing from the DOEPDMA descriptor (default)
- + - 1: Core starts processing from the descriptor which received the BNA.
- + This parameter is valid only when OTG_EN_DESC_DMA == 1b1.
- + </td></tr>
- +
- + <tr>
- + <td>ahb_single</td>
- + <td>This bit when programmed supports SINGLE transfers for remainder data
- + in a transfer for DMA mode of operation.
- + - 0: The remainder data will be sent using INCR burst size (default)
- + - 1: The remainder data will be sent using SINGLE burst size.
- + </td></tr>
- +
- +<tr>
- + <td>adp_enable</td>
- + <td>Specifies whether ADP feature is enabled.
- + The driver will automatically detect the value for this parameter if none is
- + specified.
- + - 0: ADP feature disabled (default)
- + - 1: ADP feature enabled
- + </td></tr>
- +
- + <tr>
- + <td>otg_ver</td>
- + <td>Specifies whether OTG is performing as USB OTG Revision 2.0 or Revision 1.3
- + USB OTG device.
- + - 0: OTG 2.0 support disabled (default)
- + - 1: OTG 2.0 support enabled
- + </td></tr>
- +
- +*/
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_driver.h
- @@ -0,0 +1,86 @@
- +/* ==========================================================================
- + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_driver.h $
- + * $Revision: #19 $
- + * $Date: 2010/11/15 $
- + * $Change: 1627671 $
- + *
- + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
- + * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
- + * otherwise expressly agreed to in writing between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product under
- + * any End User Software License Agreement or Agreement for Licensed Product
- + * with Synopsys or any supplement thereto. You are permitted to use and
- + * redistribute this Software in source and binary forms, with or without
- + * modification, provided that redistributions of source code must retain this
- + * notice. You may not view, use, disclose, copy or distribute this file or
- + * any information contained herein except pursuant to this license grant from
- + * Synopsys. If you do not agree with this notice, including the disclaimer
- + * below, then you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
- + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- + * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
- + * ========================================================================== */
- +
- +#ifndef __DWC_OTG_DRIVER_H__
- +#define __DWC_OTG_DRIVER_H__
- +
- +/** @file
- + * This file contains the interface to the Linux driver.
- + */
- +#include "dwc_otg_os_dep.h"
- +#include "dwc_otg_core_if.h"
- +
- +/* Type declarations */
- +struct dwc_otg_pcd;
- +struct dwc_otg_hcd;
- +
- +/**
- + * This structure is a wrapper that encapsulates the driver components used to
- + * manage a single DWC_otg controller.
- + */
- +typedef struct dwc_otg_device {
- + /** Structure containing OS-dependent stuff. KEEP THIS STRUCT AT THE
- + * VERY BEGINNING OF THE DEVICE STRUCT. OSes such as FreeBSD and NetBSD
- + * require this. */
- + struct os_dependent os_dep;
- +
- + /** Pointer to the core interface structure. */
- + dwc_otg_core_if_t *core_if;
- +
- + /** Pointer to the PCD structure. */
- + struct dwc_otg_pcd *pcd;
- +
- + /** Pointer to the HCD structure. */
- + struct dwc_otg_hcd *hcd;
- +
- + /** Flag to indicate whether the common IRQ handler is installed. */
- + uint8_t common_irq_installed;
- +
- +} dwc_otg_device_t;
- +
- +/*We must clear S3C24XX_EINTPEND external interrupt register
- + * because after clearing in this register trigerred IRQ from
- + * H/W core in kernel interrupt can be occured again before OTG
- + * handlers clear all IRQ sources of Core registers because of
- + * timing latencies and Low Level IRQ Type.
- + */
- +#ifdef CONFIG_MACH_IPMATE
- +#define S3C2410X_CLEAR_EINTPEND() \
- +do { \
- + __raw_writel(1UL << 11,S3C24XX_EINTPEND); \
- +} while (0)
- +#else
- +#define S3C2410X_CLEAR_EINTPEND() do { } while (0)
- +#endif
- +
- +#endif
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c
- @@ -0,0 +1,1433 @@
- +/*
- + * dwc_otg_fiq_fsm.c - The finite state machine FIQ
- + *
- + * Copyright (c) 2013 Raspberry Pi Foundation
- + *
- + * Author: Jonathan Bell <[email protected]>
- + * 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);
- + }
- +}
- +
- +
- +#ifdef CONFIG_ARM64
- +
- +inline void fiq_fsm_spin_lock(fiq_lock_t *lock)
- +{
- + spin_lock((spinlock_t *)lock);
- +}
- +
- +inline void fiq_fsm_spin_unlock(fiq_lock_t *lock)
- +{
- + spin_unlock((spinlock_t *)lock);
- +}
- +
- +#else
- +
- +/**
- + * fiq_fsm_spin_lock() - ARMv6+ bare bones spinlock
- + * Must be called with local interrupts and FIQ disabled.
- + */
- +#if defined(CONFIG_ARCH_BCM2835) && defined(CONFIG_SMP)
- +inline void fiq_fsm_spin_lock(fiq_lock_t *lock)
- +{
- + unsigned long tmp;
- + uint32_t newval;
- + fiq_lock_t lockval;
- + /* Nested locking, yay. If we are on the same CPU as the fiq, then the disable
- + * will be sufficient. If we are on a different CPU, then the lock protects us. */
- + prefetchw(&lock->slock);
- + asm volatile (
- + "1: ldrex %0, [%3]\n"
- + " add %1, %0, %4\n"
- + " strex %2, %1, [%3]\n"
- + " teq %2, #0\n"
- + " bne 1b"
- + : "=&r" (lockval), "=&r" (newval), "=&r" (tmp)
- + : "r" (&lock->slock), "I" (1 << 16)
- + : "cc");
- +
- + while (lockval.tickets.next != lockval.tickets.owner) {
- + wfe();
- + lockval.tickets.owner = READ_ONCE(lock->tickets.owner);
- + }
- + smp_mb();
- +}
- +#else
- +inline void fiq_fsm_spin_lock(fiq_lock_t *lock) { }
- +#endif
- +
- +/**
- + * fiq_fsm_spin_unlock() - ARMv6+ bare bones spinunlock
- + */
- +#if defined(CONFIG_ARCH_BCM2835) && defined(CONFIG_SMP)
- +inline void fiq_fsm_spin_unlock(fiq_lock_t *lock)
- +{
- + smp_mb();
- + lock->tickets.owner++;
- + dsb_sev();
- +}
- +#else
- +inline void fiq_fsm_spin_unlock(fiq_lock_t *lock) { }
- +#endif
- +
- +#endif
- +
- +/**
- + * 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();
- +}
- +
- +/**
- + * fiq_fsm_restart_np_pending() - Restart a single non-periodic contended transfer
- + * @st: Pointer to the channel's state
- + * @num_channels: Total number of host channels
- + * @orig_channel: Channel index of completed transfer
- + *
- + * In the case where an IN and OUT transfer are simultaneously scheduled to the
- + * same device/EP, inadequate hub implementations will misbehave. Once the first
- + * transfer is complete, a pending non-periodic split can then be issued.
- + */
- +static void notrace fiq_fsm_restart_np_pending(struct fiq_state *st, int num_channels, int orig_channel)
- +{
- + int i;
- + int dev_addr = st->channel[orig_channel].hcchar_copy.b.devaddr;
- + int ep_num = st->channel[orig_channel].hcchar_copy.b.epnum;
- + for (i = 0; i < num_channels; i++) {
- + if (st->channel[i].fsm == FIQ_NP_SSPLIT_PENDING &&
- + st->channel[i].hcchar_copy.b.devaddr == dev_addr &&
- + st->channel[i].hcchar_copy.b.epnum == ep_num) {
- + st->channel[i].fsm = FIQ_NP_SSPLIT_STARTED;
- + fiq_fsm_restart_channel(st, i, 0);
- + break;
- + }
- + }
- +}
- +
- +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 *)(uintptr_t)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 = (u32)(uintptr_t)&blob->channel[n].index[i].buf[0];
- + FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HC_DMA, 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_fsm_reload_hcdma() - for OUT transactions, rewind DMA pointer
- + */
- +static void notrace fiq_fsm_reload_hcdma(struct fiq_state *st, int n)
- +{
- + hcdma_data_t hcdma = st->channel[n].hcdma_copy;
- + FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HC_DMA, hcdma.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 *)(uintptr_t)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 = (u32)(uintptr_t)blob->channel[n].index[i].buf;
- + //hcdma.d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HC_DMA);
- + //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_START + (HC_OFFSET * n) + HC_DMA, 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 ");
- + if (st->channel[n].nrpackets == 1)
- + st->channel[n].fsm = FIQ_PER_ISO_OUT_LAST;
- + else
- + 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_START + (HC_OFFSET * n) + HC_DMA, 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 {
- + 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_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:
- + /* Is it time to wake this channel yet? */
- + if (--state->channel[n].uframe_sleeps == 0) {
- + 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, state, "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);
- + /* In some random cases we can get a NAK interrupt coincident with a Xacterr
- + * interrupt, after the device has disappeared.
- + */
- + if (!hcint.b.xacterr)
- + 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.
- + fiq_fsm_reload_hcdma(state, n);
- + 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++;
- + if(st->hcchar_copy.b.epdir == 0)
- + fiq_fsm_reload_hcdma(state, n);
- + 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;
- + }
- + }
- + if (st->fsm != FIQ_NP_IN_CSPLIT_RETRY) {
- + fiq_fsm_restart_np_pending(state, num_channels, n);
- + }
- + 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;
- + }
- + }
- + if (st->fsm != FIQ_NP_OUT_CSPLIT_RETRY) {
- + fiq_fsm_restart_np_pending(state, num_channels, n);
- + }
- + 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;
- + fiq_print(FIQDBG_INT, state, "HSISO M ");
- + /* For strided transfers, put ourselves to sleep */
- + if (st->hs_isoc_info.stride > 1) {
- + st->uframe_sleeps = st->hs_isoc_info.stride - 1;
- + st->fsm = FIQ_HS_ISOC_SLEEPING;
- + } else {
- + restart = 1;
- + }
- + } 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;
- +
- + /* Ensure peripheral reads issued prior to FIQ entry are complete */
- + dsb(sy);
- +
- + gintsts_handled.d32 = 0;
- + haint_handled.d32 = 0;
- +
- + fiq_fsm_spin_lock(&state->lock);
- + 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++;
- + if (state->mphi_regs.swirq_set) {
- + FIQ_WRITE(state->mphi_regs.swirq_set, 1);
- + } else {
- + FIQ_WRITE(state->mphi_regs.outdda, state->dummy_send_dma);
- + FIQ_WRITE(state->mphi_regs.outddb, (1<<29));
- + }
- +
- + }
- + state->fiq_done++;
- + mb();
- + fiq_fsm_spin_unlock(&state->lock);
- +}
- +
- +
- +/**
- + * 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;
- +
- + /* Ensure peripheral reads issued prior to FIQ entry are complete */
- + dsb(sy);
- +
- + fiq_fsm_spin_lock(&state->lock);
- + 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);
- + if (state->mphi_regs.swirq_set) {
- + FIQ_WRITE(state->mphi_regs.swirq_set, 1);
- + } else {
- + /* Force a clear before another dummy send */
- + FIQ_WRITE(state->mphi_regs.intstat, (1<<29));
- + FIQ_WRITE(state->mphi_regs.outdda, state->dummy_send_dma);
- + FIQ_WRITE(state->mphi_regs.outddb, (1<<29));
- + }
- + }
- + state->fiq_done++;
- + mb();
- + fiq_fsm_spin_unlock(&state->lock);
- +}
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h
- @@ -0,0 +1,399 @@
- +/*
- + * dwc_otg_fiq_fsm.h - Finite state machine FIQ header definitions
- + *
- + * Copyright (c) 2013 Raspberry Pi Foundation
- + *
- + * Author: Jonathan Bell <[email protected]>
- + * 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 0x14
- +
- +#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;
- + volatile void* swirq_set;
- + volatile void* swirq_clr;
- +} mphi_regs_t;
- +
- +enum fiq_debug_level {
- + FIQDBG_SCHED = (1 << 0),
- + FIQDBG_INT = (1 << 1),
- + FIQDBG_ERR = (1 << 2),
- + FIQDBG_PORTHUB = (1 << 3),
- +};
- +
- +#ifdef CONFIG_ARM64
- +
- +typedef spinlock_t fiq_lock_t;
- +
- +#else
- +
- +typedef struct {
- + union {
- + uint32_t slock;
- + struct _tickets {
- + uint16_t owner;
- + uint16_t next;
- + } tickets;
- + };
- +} fiq_lock_t;
- +
- +#endif
- +
- +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,
- + /* TT contention - working around hub bugs */
- + FIQ_NP_SSPLIT_PENDING = 33,
- + 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 fiq_split_dma_slot {
- + u8 buf[188];
- +} __attribute__((packed));
- +
- +struct fiq_dma_channel {
- + struct fiq_split_dma_slot index[6];
- +} __attribute__((packed));
- +
- +struct fiq_dma_blob {
- + struct fiq_dma_channel channel[0];
- +} __attribute__((packed));
- +
- +/**
- + * 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)
- + * @stride: Interval in uframes between HS isoc transactions
- + */
- +struct fiq_hs_isoc_info {
- + struct dwc_otg_hcd_iso_packet_desc *iso_desc;
- + unsigned int nrframes;
- + unsigned int index;
- + unsigned int stride;
- +};
- +
- +/**
- + * 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;
- + /* number of uframes remaining (for interval > 1 HS isoc transfers) before next transfer */
- + unsigned int uframe_sleeps;
- + /* 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 {
- + fiq_lock_t lock;
- + mphi_regs_t mphi_regs;
- + void *dwc_regs_base;
- + dma_addr_t dma_base;
- + struct fiq_dma_blob *fiq_dmab;
- + void *dummy_send;
- + dma_addr_t dummy_send_dma;
- + 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];
- +};
- +
- +#ifdef CONFIG_ARM64
- +
- +#ifdef local_fiq_enable
- +#undef local_fiq_enable
- +#endif
- +
- +#ifdef local_fiq_disable
- +#undef local_fiq_disable
- +#endif
- +
- +extern void local_fiq_enable(void);
- +
- +extern void local_fiq_disable(void);
- +
- +#endif
- +
- +extern void fiq_fsm_spin_lock(fiq_lock_t *lock);
- +
- +extern void fiq_fsm_spin_unlock(fiq_lock_t *lock);
- +
- +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 <[email protected]>
- + * 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)
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
- @@ -0,0 +1,4363 @@
- +
- +/* ==========================================================================
- + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd.c $
- + * $Revision: #104 $
- + * $Date: 2011/10/24 $
- + * $Change: 1871159 $
- + *
- + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
- + * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
- + * otherwise expressly agreed to in writing between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product under
- + * any End User Software License Agreement or Agreement for Licensed Product
- + * with Synopsys or any supplement thereto. You are permitted to use and
- + * redistribute this Software in source and binary forms, with or without
- + * modification, provided that redistributions of source code must retain this
- + * notice. You may not view, use, disclose, copy or distribute this file or
- + * any information contained herein except pursuant to this license grant from
- + * Synopsys. If you do not agree with this notice, including the disclaimer
- + * below, then you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
- + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- + * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
- + * ========================================================================== */
- +#ifndef DWC_DEVICE_ONLY
- +
- +/** @file
- + * This file implements HCD Core. All code in this file is portable and doesn't
- + * use any OS specific functions.
- + * Interface provided by HCD Core is defined in <code><hcd_if.h></code>
- + * header file.
- + */
- +
- +#include <linux/usb.h>
- +#include <linux/usb/hcd.h>
- +
- +#include "dwc_otg_hcd.h"
- +#include "dwc_otg_regs.h"
- +#include "dwc_otg_fiq_fsm.h"
- +
- +extern bool microframe_schedule;
- +extern uint16_t fiq_fsm_mask, nak_holdoff;
- +
- +//#define DEBUG_HOST_CHANNELS
- +#ifdef DEBUG_HOST_CHANNELS
- +static int last_sel_trans_num_per_scheduled = 0;
- +static int last_sel_trans_num_nonper_scheduled = 0;
- +static int last_sel_trans_num_avail_hc_at_start = 0;
- +static int last_sel_trans_num_avail_hc_at_end = 0;
- +#endif /* DEBUG_HOST_CHANNELS */
- +
- +
- +dwc_otg_hcd_t *dwc_otg_hcd_alloc_hcd(void)
- +{
- + return DWC_ALLOC(sizeof(dwc_otg_hcd_t));
- +}
- +
- +/**
- + * Connection timeout function. An OTG host is required to display a
- + * message if the device does not connect within 10 seconds.
- + */
- +void dwc_otg_hcd_connect_timeout(void *ptr)
- +{
- + DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, ptr);
- + DWC_PRINTF("Connect Timeout\n");
- + __DWC_ERROR("Device Not Connected/Responding\n");
- +}
- +
- +#if defined(DEBUG)
- +static void dump_channel_info(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
- +{
- + if (qh->channel != NULL) {
- + dwc_hc_t *hc = qh->channel;
- + dwc_list_link_t *item;
- + dwc_otg_qh_t *qh_item;
- + int num_channels = hcd->core_if->core_params->host_channels;
- + int i;
- +
- + dwc_otg_hc_regs_t *hc_regs;
- + hcchar_data_t hcchar;
- + hcsplt_data_t hcsplt;
- + hctsiz_data_t hctsiz;
- + uint32_t hcdma;
- +
- + hc_regs = hcd->core_if->host_if->hc_regs[hc->hc_num];
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- + hcsplt.d32 = DWC_READ_REG32(&hc_regs->hcsplt);
- + hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz);
- + hcdma = DWC_READ_REG32(&hc_regs->hcdma);
- +
- + DWC_PRINTF(" Assigned to channel %p:\n", hc);
- + DWC_PRINTF(" hcchar 0x%08x, hcsplt 0x%08x\n", hcchar.d32,
- + hcsplt.d32);
- + DWC_PRINTF(" hctsiz 0x%08x, hcdma 0x%08x\n", hctsiz.d32,
- + hcdma);
- + DWC_PRINTF(" dev_addr: %d, ep_num: %d, ep_is_in: %d\n",
- + hc->dev_addr, hc->ep_num, hc->ep_is_in);
- + DWC_PRINTF(" ep_type: %d\n", hc->ep_type);
- + DWC_PRINTF(" max_packet: %d\n", hc->max_packet);
- + DWC_PRINTF(" data_pid_start: %d\n", hc->data_pid_start);
- + DWC_PRINTF(" xfer_started: %d\n", hc->xfer_started);
- + DWC_PRINTF(" halt_status: %d\n", hc->halt_status);
- + DWC_PRINTF(" xfer_buff: %p\n", hc->xfer_buff);
- + DWC_PRINTF(" xfer_len: %d\n", hc->xfer_len);
- + DWC_PRINTF(" qh: %p\n", hc->qh);
- + DWC_PRINTF(" NP inactive sched:\n");
- + DWC_LIST_FOREACH(item, &hcd->non_periodic_sched_inactive) {
- + qh_item =
- + DWC_LIST_ENTRY(item, dwc_otg_qh_t, qh_list_entry);
- + DWC_PRINTF(" %p\n", qh_item);
- + }
- + DWC_PRINTF(" NP active sched:\n");
- + DWC_LIST_FOREACH(item, &hcd->non_periodic_sched_active) {
- + qh_item =
- + DWC_LIST_ENTRY(item, dwc_otg_qh_t, qh_list_entry);
- + DWC_PRINTF(" %p\n", qh_item);
- + }
- + DWC_PRINTF(" Channels: \n");
- + for (i = 0; i < num_channels; i++) {
- + dwc_hc_t *hc = hcd->hc_ptr_array[i];
- + DWC_PRINTF(" %2d: %p\n", i, hc);
- + }
- + }
- +}
- +#else
- +#define dump_channel_info(hcd, qh)
- +#endif /* DEBUG */
- +
- +/**
- + * Work queue function for starting the HCD when A-Cable is connected.
- + * The hcd_start() must be called in a process context.
- + */
- +static void hcd_start_func(void *_vp)
- +{
- + dwc_otg_hcd_t *hcd = (dwc_otg_hcd_t *) _vp;
- +
- + DWC_DEBUGPL(DBG_HCDV, "%s() %p\n", __func__, hcd);
- + if (hcd) {
- + hcd->fops->start(hcd);
- + }
- +}
- +
- +static void del_xfer_timers(dwc_otg_hcd_t * hcd)
- +{
- +#ifdef DEBUG
- + int i;
- + int num_channels = hcd->core_if->core_params->host_channels;
- + for (i = 0; i < num_channels; i++) {
- + DWC_TIMER_CANCEL(hcd->core_if->hc_xfer_timer[i]);
- + }
- +#endif
- +}
- +
- +static void del_timers(dwc_otg_hcd_t * hcd)
- +{
- + del_xfer_timers(hcd);
- + DWC_TIMER_CANCEL(hcd->conn_timer);
- +}
- +
- +/**
- + * Processes all the URBs in a single list of QHs. Completes them with
- + * -ESHUTDOWN and frees the QTD.
- + */
- +static void kill_urbs_in_qh_list(dwc_otg_hcd_t * hcd, dwc_list_link_t * qh_list)
- +{
- + dwc_list_link_t *qh_item, *qh_tmp;
- + dwc_otg_qh_t *qh;
- + dwc_otg_qtd_t *qtd, *qtd_tmp;
- + int quiesced = 0;
- +
- + DWC_LIST_FOREACH_SAFE(qh_item, qh_tmp, qh_list) {
- + qh = DWC_LIST_ENTRY(qh_item, dwc_otg_qh_t, qh_list_entry);
- + DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp,
- + &qh->qtd_list, qtd_list_entry) {
- + qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list);
- + if (qtd->urb != NULL) {
- + hcd->fops->complete(hcd, qtd->urb->priv,
- + qtd->urb, -DWC_E_SHUTDOWN);
- + dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh);
- + }
- +
- + }
- + if(qh->channel) {
- + int n = qh->channel->hc_num;
- + /* Using hcchar.chen == 1 is not a reliable test.
- + * It is possible that the channel has already halted
- + * but not yet been through the IRQ handler.
- + */
- + if (fiq_fsm_enable && (hcd->fiq_state->channel[qh->channel->hc_num].fsm != FIQ_PASSTHROUGH)) {
- + qh->channel->halt_status = DWC_OTG_HC_XFER_URB_DEQUEUE;
- + qh->channel->halt_pending = 1;
- + if (hcd->fiq_state->channel[n].fsm == FIQ_HS_ISOC_TURBO ||
- + hcd->fiq_state->channel[n].fsm == FIQ_HS_ISOC_SLEEPING)
- + hcd->fiq_state->channel[n].fsm = FIQ_HS_ISOC_ABORTED;
- + /* We're called from disconnect callback or in the middle of freeing the HCD here,
- + * so FIQ is disabled, top-level interrupts masked and we're holding the spinlock.
- + * No further URBs will be submitted, but wait 1 microframe for any previously
- + * submitted periodic DMA to finish.
- + */
- + if (!quiesced) {
- + udelay(125);
- + quiesced = 1;
- + }
- + } else {
- + dwc_otg_hc_halt(hcd->core_if, qh->channel,
- + DWC_OTG_HC_XFER_URB_DEQUEUE);
- + }
- + qh->channel = NULL;
- + }
- + dwc_otg_hcd_qh_remove(hcd, qh);
- + }
- +}
- +
- +/**
- + * Responds with an error status of ESHUTDOWN to all URBs in the non-periodic
- + * and periodic schedules. The QTD associated with each URB is removed from
- + * the schedule and freed. This function may be called when a disconnect is
- + * detected or when the HCD is being stopped.
- + */
- +static void kill_all_urbs(dwc_otg_hcd_t * hcd)
- +{
- + kill_urbs_in_qh_list(hcd, &hcd->non_periodic_sched_inactive);
- + kill_urbs_in_qh_list(hcd, &hcd->non_periodic_sched_active);
- + kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_inactive);
- + kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_ready);
- + kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_assigned);
- + kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_queued);
- +}
- +
- +/**
- + * Start the connection timer. An OTG host is required to display a
- + * message if the device does not connect within 10 seconds. The
- + * timer is deleted if a port connect interrupt occurs before the
- + * timer expires.
- + */
- +static void dwc_otg_hcd_start_connect_timer(dwc_otg_hcd_t * hcd)
- +{
- + DWC_TIMER_SCHEDULE(hcd->conn_timer, 10000 /* 10 secs */ );
- +}
- +
- +/**
- + * HCD Callback function for disconnect of the HCD.
- + *
- + * @param p void pointer to the <code>struct usb_hcd</code>
- + */
- +static int32_t dwc_otg_hcd_session_start_cb(void *p)
- +{
- + dwc_otg_hcd_t *dwc_otg_hcd;
- + DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, p);
- + dwc_otg_hcd = p;
- + dwc_otg_hcd_start_connect_timer(dwc_otg_hcd);
- + return 1;
- +}
- +
- +/**
- + * HCD Callback function for starting the HCD when A-Cable is
- + * connected.
- + *
- + * @param p void pointer to the <code>struct usb_hcd</code>
- + */
- +static int32_t dwc_otg_hcd_start_cb(void *p)
- +{
- + dwc_otg_hcd_t *dwc_otg_hcd = p;
- + dwc_otg_core_if_t *core_if;
- + hprt0_data_t hprt0;
- +
- + core_if = dwc_otg_hcd->core_if;
- +
- + if (core_if->op_state == B_HOST) {
- + /*
- + * Reset the port. During a HNP mode switch the reset
- + * needs to occur within 1ms and have a duration of at
- + * least 50ms.
- + */
- + hprt0.d32 = dwc_otg_read_hprt0(core_if);
- + hprt0.b.prtrst = 1;
- + DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
- + }
- + DWC_WORKQ_SCHEDULE_DELAYED(core_if->wq_otg,
- + hcd_start_func, dwc_otg_hcd, 50,
- + "start hcd");
- +
- + return 1;
- +}
- +
- +/**
- + * HCD Callback function for disconnect of the HCD.
- + *
- + * @param p void pointer to the <code>struct usb_hcd</code>
- + */
- +static int32_t dwc_otg_hcd_disconnect_cb(void *p)
- +{
- + gintsts_data_t intr;
- + dwc_otg_hcd_t *dwc_otg_hcd = p;
- +
- + DWC_SPINLOCK(dwc_otg_hcd->lock);
- + /*
- + * Set status flags for the hub driver.
- + */
- + dwc_otg_hcd->flags.b.port_connect_status_change = 1;
- + dwc_otg_hcd->flags.b.port_connect_status = 0;
- + if(fiq_enable) {
- + local_fiq_disable();
- + fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock);
- + }
- + /*
- + * Shutdown any transfers in process by clearing the Tx FIFO Empty
- + * interrupt mask and status bits and disabling subsequent host
- + * channel interrupts.
- + */
- + intr.d32 = 0;
- + intr.b.nptxfempty = 1;
- + intr.b.ptxfempty = 1;
- + intr.b.hcintr = 1;
- + DWC_MODIFY_REG32(&dwc_otg_hcd->core_if->core_global_regs->gintmsk,
- + intr.d32, 0);
- + DWC_MODIFY_REG32(&dwc_otg_hcd->core_if->core_global_regs->gintsts,
- + intr.d32, 0);
- +
- + del_timers(dwc_otg_hcd);
- +
- + /*
- + * Turn off the vbus power only if the core has transitioned to device
- + * mode. If still in host mode, need to keep power on to detect a
- + * reconnection.
- + */
- + if (dwc_otg_is_device_mode(dwc_otg_hcd->core_if)) {
- + if (dwc_otg_hcd->core_if->op_state != A_SUSPEND) {
- + hprt0_data_t hprt0 = {.d32 = 0 };
- + DWC_PRINTF("Disconnect: PortPower off\n");
- + hprt0.b.prtpwr = 0;
- + DWC_WRITE_REG32(dwc_otg_hcd->core_if->host_if->hprt0,
- + hprt0.d32);
- + }
- +
- + dwc_otg_disable_host_interrupts(dwc_otg_hcd->core_if);
- + }
- +
- + /* Respond with an error status to all URBs in the schedule. */
- + kill_all_urbs(dwc_otg_hcd);
- +
- + if (dwc_otg_is_host_mode(dwc_otg_hcd->core_if)) {
- + /* Clean up any host channels that were in use. */
- + int num_channels;
- + int i;
- + dwc_hc_t *channel;
- + dwc_otg_hc_regs_t *hc_regs;
- + hcchar_data_t hcchar;
- +
- + num_channels = dwc_otg_hcd->core_if->core_params->host_channels;
- +
- + if (!dwc_otg_hcd->core_if->dma_enable) {
- + /* Flush out any channel requests in slave mode. */
- + for (i = 0; i < num_channels; i++) {
- + channel = dwc_otg_hcd->hc_ptr_array[i];
- + if (DWC_CIRCLEQ_EMPTY_ENTRY
- + (channel, hc_list_entry)) {
- + hc_regs =
- + dwc_otg_hcd->core_if->
- + host_if->hc_regs[i];
- + hcchar.d32 =
- + DWC_READ_REG32(&hc_regs->hcchar);
- + if (hcchar.b.chen) {
- + hcchar.b.chen = 0;
- + hcchar.b.chdis = 1;
- + hcchar.b.epdir = 0;
- + DWC_WRITE_REG32
- + (&hc_regs->hcchar,
- + hcchar.d32);
- + }
- + }
- + }
- + }
- +
- + if(fiq_fsm_enable) {
- + for(i=0; i < 128; i++) {
- + dwc_otg_hcd->hub_port[i] = 0;
- + }
- + }
- + }
- +
- + if(fiq_enable) {
- + fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock);
- + local_fiq_enable();
- + }
- +
- + if (dwc_otg_hcd->fops->disconnect) {
- + dwc_otg_hcd->fops->disconnect(dwc_otg_hcd);
- + }
- +
- + DWC_SPINUNLOCK(dwc_otg_hcd->lock);
- + return 1;
- +}
- +
- +/**
- + * HCD Callback function for stopping the HCD.
- + *
- + * @param p void pointer to the <code>struct usb_hcd</code>
- + */
- +static int32_t dwc_otg_hcd_stop_cb(void *p)
- +{
- + dwc_otg_hcd_t *dwc_otg_hcd = p;
- +
- + DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, p);
- + dwc_otg_hcd_stop(dwc_otg_hcd);
- + return 1;
- +}
- +
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- +/**
- + * HCD Callback function for sleep of HCD.
- + *
- + * @param p void pointer to the <code>struct usb_hcd</code>
- + */
- +static int dwc_otg_hcd_sleep_cb(void *p)
- +{
- + dwc_otg_hcd_t *hcd = p;
- +
- + dwc_otg_hcd_free_hc_from_lpm(hcd);
- +
- + return 0;
- +}
- +#endif
- +
- +
- +/**
- + * HCD Callback function for Remote Wakeup.
- + *
- + * @param p void pointer to the <code>struct usb_hcd</code>
- + */
- +static int dwc_otg_hcd_rem_wakeup_cb(void *p)
- +{
- + dwc_otg_hcd_t *hcd = p;
- +
- + if (hcd->core_if->lx_state == DWC_OTG_L2) {
- + hcd->flags.b.port_suspend_change = 1;
- + }
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- + else {
- + hcd->flags.b.port_l1_change = 1;
- + }
- +#endif
- + return 0;
- +}
- +
- +/**
- + * Halts the DWC_otg host mode operations in a clean manner. USB transfers are
- + * stopped.
- + */
- +void dwc_otg_hcd_stop(dwc_otg_hcd_t * hcd)
- +{
- + hprt0_data_t hprt0 = {.d32 = 0 };
- +
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD STOP\n");
- +
- + /*
- + * The root hub should be disconnected before this function is called.
- + * The disconnect will clear the QTD lists (via ..._hcd_urb_dequeue)
- + * and the QH lists (via ..._hcd_endpoint_disable).
- + */
- +
- + /* Turn off all host-specific interrupts. */
- + dwc_otg_disable_host_interrupts(hcd->core_if);
- +
- + /* Turn off the vbus power */
- + DWC_PRINTF("PortPower off\n");
- + hprt0.b.prtpwr = 0;
- + DWC_WRITE_REG32(hcd->core_if->host_if->hprt0, hprt0.d32);
- + dwc_mdelay(1);
- +}
- +
- +int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_t * hcd,
- + dwc_otg_hcd_urb_t * dwc_otg_urb, void **ep_handle,
- + int atomic_alloc)
- +{
- + int retval = 0;
- + uint8_t needs_scheduling = 0;
- + dwc_otg_transaction_type_e tr_type;
- + dwc_otg_qtd_t *qtd;
- + gintmsk_data_t intr_mask = {.d32 = 0 };
- + hprt0_data_t hprt0 = { .d32 = 0 };
- +
- +#ifdef DEBUG /* integrity checks (Broadcom) */
- + if (NULL == hcd->core_if) {
- + DWC_ERROR("**** DWC OTG HCD URB Enqueue - HCD has NULL core_if\n");
- + /* No longer connected. */
- + return -DWC_E_INVALID;
- + }
- +#endif
- + if (!hcd->flags.b.port_connect_status) {
- + /* No longer connected. */
- + DWC_ERROR("Not connected\n");
- + return -DWC_E_NO_DEVICE;
- + }
- +
- + /* Some core configurations cannot support LS traffic on a FS root port */
- + if ((hcd->fops->speed(hcd, dwc_otg_urb->priv) == USB_SPEED_LOW) &&
- + (hcd->core_if->hwcfg2.b.fs_phy_type == 1) &&
- + (hcd->core_if->hwcfg2.b.hs_phy_type == 1)) {
- + hprt0.d32 = DWC_READ_REG32(hcd->core_if->host_if->hprt0);
- + if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_FULL_SPEED) {
- + return -DWC_E_NO_DEVICE;
- + }
- + }
- +
- + qtd = dwc_otg_hcd_qtd_create(dwc_otg_urb, atomic_alloc);
- + if (qtd == NULL) {
- + DWC_ERROR("DWC OTG HCD URB Enqueue failed creating QTD\n");
- + return -DWC_E_NO_MEMORY;
- + }
- +#ifdef DEBUG /* integrity checks (Broadcom) */
- + if (qtd->urb == NULL) {
- + DWC_ERROR("**** DWC OTG HCD URB Enqueue created QTD with no URBs\n");
- + return -DWC_E_NO_MEMORY;
- + }
- + if (qtd->urb->priv == NULL) {
- + DWC_ERROR("**** DWC OTG HCD URB Enqueue created QTD URB with no URB handle\n");
- + return -DWC_E_NO_MEMORY;
- + }
- +#endif
- + intr_mask.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->gintmsk);
- + 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;
- +
- + retval = dwc_otg_hcd_qtd_add(qtd, hcd, (dwc_otg_qh_t **) ep_handle, atomic_alloc);
- + // creates a new queue in ep_handle if it doesn't exist already
- + if (retval < 0) {
- + DWC_ERROR("DWC OTG HCD URB Enqueue failed adding QTD. "
- + "Error status %d\n", retval);
- + dwc_otg_hcd_qtd_free(qtd);
- + return retval;
- + }
- +
- + if(needs_scheduling) {
- + tr_type = dwc_otg_hcd_select_transactions(hcd);
- + if (tr_type != DWC_OTG_TRANSACTION_NONE) {
- + dwc_otg_hcd_queue_transactions(hcd, tr_type);
- + }
- + }
- + return retval;
- +}
- +
- +int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_t * hcd,
- + dwc_otg_hcd_urb_t * dwc_otg_urb)
- +{
- + dwc_otg_qh_t *qh;
- + dwc_otg_qtd_t *urb_qtd;
- + BUG_ON(!hcd);
- + BUG_ON(!dwc_otg_urb);
- +
- +#ifdef DEBUG /* integrity checks (Broadcom) */
- +
- + if (hcd == NULL) {
- + DWC_ERROR("**** DWC OTG HCD URB Dequeue has NULL HCD\n");
- + return -DWC_E_INVALID;
- + }
- + if (dwc_otg_urb == NULL) {
- + DWC_ERROR("**** DWC OTG HCD URB Dequeue has NULL URB\n");
- + return -DWC_E_INVALID;
- + }
- + if (dwc_otg_urb->qtd == NULL) {
- + DWC_ERROR("**** DWC OTG HCD URB Dequeue with NULL QTD\n");
- + return -DWC_E_INVALID;
- + }
- + urb_qtd = dwc_otg_urb->qtd;
- + BUG_ON(!urb_qtd);
- + if (urb_qtd->qh == NULL) {
- + DWC_ERROR("**** DWC OTG HCD URB Dequeue with QTD with NULL Q handler\n");
- + return -DWC_E_INVALID;
- + }
- +#else
- + urb_qtd = dwc_otg_urb->qtd;
- + BUG_ON(!urb_qtd);
- +#endif
- + qh = urb_qtd->qh;
- + BUG_ON(!qh);
- + if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) {
- + if (urb_qtd->in_process) {
- + dump_channel_info(hcd, qh);
- + }
- + }
- +#ifdef DEBUG /* integrity checks (Broadcom) */
- + if (hcd->core_if == NULL) {
- + DWC_ERROR("**** DWC OTG HCD URB Dequeue HCD has NULL core_if\n");
- + return -DWC_E_INVALID;
- + }
- +#endif
- + 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
- + * no longer connected, the host registers can't be
- + * written to halt the channel since the core is in
- + * device mode.
- + */
- + /* 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)) {
- + int retries = 3;
- + int running = 0;
- + enum fiq_fsm_state state;
- +
- + local_fiq_disable();
- + fiq_fsm_spin_lock(&hcd->fiq_state->lock);
- + qh->channel->halt_status = DWC_OTG_HC_XFER_URB_DEQUEUE;
- + qh->channel->halt_pending = 1;
- + if (hcd->fiq_state->channel[n].fsm == FIQ_HS_ISOC_TURBO ||
- + hcd->fiq_state->channel[n].fsm == FIQ_HS_ISOC_SLEEPING)
- + hcd->fiq_state->channel[n].fsm = FIQ_HS_ISOC_ABORTED;
- + fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
- + local_fiq_enable();
- +
- + if (dwc_qh_is_non_per(qh)) {
- + do {
- + state = READ_ONCE(hcd->fiq_state->channel[n].fsm);
- + running = (state != FIQ_NP_SPLIT_DONE) &&
- + (state != FIQ_NP_SPLIT_LS_ABORTED) &&
- + (state != FIQ_NP_SPLIT_HS_ABORTED);
- + if (!running)
- + break;
- + udelay(125);
- + } while(--retries);
- + if (!retries)
- + DWC_WARN("Timed out waiting for FSM NP transfer to complete on %d",
- + qh->channel->hc_num);
- + }
- + } else {
- + dwc_otg_hc_halt(hcd->core_if, qh->channel,
- + DWC_OTG_HC_XFER_URB_DEQUEUE);
- + }
- + }
- + }
- +
- + /*
- + * Free the QTD and clean up the associated QH. Leave the QH in the
- + * schedule if it has any remaining QTDs.
- + */
- +
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD URB Dequeue - "
- + "delete %sQueue handler\n",
- + hcd->core_if->dma_desc_enable?"DMA ":"");
- + if (!hcd->core_if->dma_desc_enable) {
- + uint8_t b = urb_qtd->in_process;
- + if (nak_holdoff && qh->do_split && dwc_qh_is_non_per(qh))
- + qh->nak_frame = 0xFFFF;
- + dwc_otg_hcd_qtd_remove_and_free(hcd, urb_qtd, qh);
- + if (b) {
- + dwc_otg_hcd_qh_deactivate(hcd, qh, 0);
- + qh->channel = NULL;
- + } else if (DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) {
- + dwc_otg_hcd_qh_remove(hcd, qh);
- + }
- + } else {
- + dwc_otg_hcd_qtd_remove_and_free(hcd, urb_qtd, qh);
- + }
- + return 0;
- +}
- +
- +int dwc_otg_hcd_endpoint_disable(dwc_otg_hcd_t * hcd, void *ep_handle,
- + int retry)
- +{
- + dwc_otg_qh_t *qh = (dwc_otg_qh_t *) ep_handle;
- + int retval = 0;
- + dwc_irqflags_t flags;
- +
- + if (retry < 0) {
- + retval = -DWC_E_INVALID;
- + goto done;
- + }
- +
- + if (!qh) {
- + retval = -DWC_E_INVALID;
- + goto done;
- + }
- +
- + DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
- +
- + while (!DWC_CIRCLEQ_EMPTY(&qh->qtd_list) && retry) {
- + DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
- + retry--;
- + dwc_msleep(5);
- + DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
- + }
- +
- + dwc_otg_hcd_qh_remove(hcd, qh);
- +
- + DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
- + /*
- + * Split dwc_otg_hcd_qh_remove_and_free() into qh_remove
- + * and qh_free to prevent stack dump on DWC_DMA_FREE() with
- + * irq_disabled (spinlock_irqsave) in dwc_otg_hcd_desc_list_free()
- + * and dwc_otg_hcd_frame_list_alloc().
- + */
- + dwc_otg_hcd_qh_free(hcd, qh);
- +
- +done:
- + return retval;
- +}
- +
- +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
- +int dwc_otg_hcd_endpoint_reset(dwc_otg_hcd_t * hcd, void *ep_handle)
- +{
- + int retval = 0;
- + dwc_otg_qh_t *qh = (dwc_otg_qh_t *) ep_handle;
- + if (!qh)
- + return -DWC_E_INVALID;
- +
- + qh->data_toggle = DWC_OTG_HC_PID_DATA0;
- + return retval;
- +}
- +#endif
- +
- +/**
- + * HCD Callback structure for handling mode switching.
- + */
- +static dwc_otg_cil_callbacks_t hcd_cil_callbacks = {
- + .start = dwc_otg_hcd_start_cb,
- + .stop = dwc_otg_hcd_stop_cb,
- + .disconnect = dwc_otg_hcd_disconnect_cb,
- + .session_start = dwc_otg_hcd_session_start_cb,
- + .resume_wakeup = dwc_otg_hcd_rem_wakeup_cb,
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- + .sleep = dwc_otg_hcd_sleep_cb,
- +#endif
- + .p = 0,
- +};
- +
- +/**
- + * Reset tasklet function
- + */
- +static void reset_tasklet_func(void *data)
- +{
- + dwc_otg_hcd_t *dwc_otg_hcd = (dwc_otg_hcd_t *) data;
- + dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if;
- + hprt0_data_t hprt0;
- +
- + DWC_DEBUGPL(DBG_HCDV, "USB RESET tasklet called\n");
- +
- + hprt0.d32 = dwc_otg_read_hprt0(core_if);
- + hprt0.b.prtrst = 1;
- + DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
- + dwc_mdelay(60);
- +
- + hprt0.b.prtrst = 0;
- + DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
- + dwc_otg_hcd->flags.b.port_reset_change = 1;
- +}
- +
- +static void completion_tasklet_func(void *ptr)
- +{
- + dwc_otg_hcd_t *hcd = (dwc_otg_hcd_t *) ptr;
- + struct urb *urb;
- + urb_tq_entry_t *item;
- + dwc_irqflags_t flags;
- +
- + /* This could just be spin_lock_irq */
- + DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
- + while (!DWC_TAILQ_EMPTY(&hcd->completed_urb_list)) {
- + item = DWC_TAILQ_FIRST(&hcd->completed_urb_list);
- + urb = item->urb;
- + DWC_TAILQ_REMOVE(&hcd->completed_urb_list, item,
- + urb_tq_entries);
- + DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
- + DWC_FREE(item);
- +
- + usb_hcd_giveback_urb(hcd->priv, urb, urb->status);
- +
- +
- + DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
- + }
- + DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
- + return;
- +}
- +
- +static void qh_list_free(dwc_otg_hcd_t * hcd, dwc_list_link_t * qh_list)
- +{
- + dwc_list_link_t *item;
- + dwc_otg_qh_t *qh;
- + dwc_irqflags_t flags;
- +
- + if (!qh_list->next) {
- + /* The list hasn't been initialized yet. */
- + return;
- + }
- + /*
- + * Hold spinlock here. Not needed in that case if bellow
- + * function is being called from ISR
- + */
- + DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
- + /* Ensure there are no QTDs or URBs left. */
- + kill_urbs_in_qh_list(hcd, qh_list);
- + DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
- +
- + DWC_LIST_FOREACH(item, qh_list) {
- + qh = DWC_LIST_ENTRY(item, dwc_otg_qh_t, qh_list_entry);
- + dwc_otg_hcd_qh_remove_and_free(hcd, qh);
- + }
- +}
- +
- +/**
- + * Exit from Hibernation if Host did not detect SRP from connected SRP capable
- + * Device during SRP time by host power up.
- + */
- +void dwc_otg_hcd_power_up(void *ptr)
- +{
- + gpwrdn_data_t gpwrdn = {.d32 = 0 };
- + dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *) ptr;
- +
- + DWC_PRINTF("%s called\n", __FUNCTION__);
- +
- + if (!core_if->hibernation_suspend) {
- + DWC_PRINTF("Already exited from Hibernation\n");
- + return;
- + }
- +
- + /* Switch on the voltage to the core */
- + gpwrdn.b.pwrdnswtch = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- + dwc_udelay(10);
- +
- + /* Reset the core */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pwrdnrstn = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- + dwc_udelay(10);
- +
- + /* Disable power clamps */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pwrdnclmp = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- +
- + /* Remove reset the core signal */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pwrdnrstn = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0, gpwrdn.d32);
- + dwc_udelay(10);
- +
- + /* Disable PMU interrupt */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pmuintsel = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- +
- + core_if->hibernation_suspend = 0;
- +
- + /* Disable PMU */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pmuactv = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- + dwc_udelay(10);
- +
- + /* Enable VBUS */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.dis_vbus = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, gpwrdn.d32, 0);
- +
- + core_if->op_state = A_HOST;
- + dwc_otg_core_init(core_if);
- + dwc_otg_enable_global_interrupts(core_if);
- + 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.
- + */
- +static void dwc_otg_hcd_free(dwc_otg_hcd_t * dwc_otg_hcd)
- +{
- + struct device *dev = dwc_otg_hcd_to_dev(dwc_otg_hcd);
- + int i;
- +
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD FREE\n");
- +
- + del_timers(dwc_otg_hcd);
- +
- + /* Free memory for QH/QTD lists */
- + qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->non_periodic_sched_inactive);
- + qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->non_periodic_sched_active);
- + qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_inactive);
- + qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_ready);
- + qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_assigned);
- + qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_queued);
- +
- + /* Free memory for the host channels. */
- + for (i = 0; i < MAX_EPS_CHANNELS; i++) {
- + dwc_hc_t *hc = dwc_otg_hcd->hc_ptr_array[i];
- +
- +#ifdef DEBUG
- + if (dwc_otg_hcd->core_if->hc_xfer_timer[i]) {
- + DWC_TIMER_FREE(dwc_otg_hcd->core_if->hc_xfer_timer[i]);
- + }
- +#endif
- + if (hc != NULL) {
- + DWC_DEBUGPL(DBG_HCDV, "HCD Free channel #%i, hc=%p\n",
- + i, hc);
- + DWC_FREE(hc);
- + }
- + }
- +
- + if (dwc_otg_hcd->core_if->dma_enable) {
- + if (dwc_otg_hcd->status_buf_dma) {
- + DWC_DMA_FREE(dev, DWC_OTG_HCD_STATUS_BUF_SIZE,
- + dwc_otg_hcd->status_buf,
- + dwc_otg_hcd->status_buf_dma);
- + }
- + } else if (dwc_otg_hcd->status_buf != NULL) {
- + DWC_FREE(dwc_otg_hcd->status_buf);
- + }
- + DWC_SPINLOCK_FREE(dwc_otg_hcd->lock);
- + /* Set core_if's lock pointer to NULL */
- + dwc_otg_hcd->core_if->lock = NULL;
- +
- + 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_DMA_FREE(dev, 16, dwc_otg_hcd->fiq_state->dummy_send,
- + dwc_otg_hcd->fiq_state->dummy_send_dma);
- + DWC_FREE(dwc_otg_hcd->fiq_state);
- +
- +#ifdef DWC_DEV_SRPCAP
- + if (dwc_otg_hcd->core_if->power_down == 2 &&
- + dwc_otg_hcd->core_if->pwron_timer) {
- + DWC_TIMER_FREE(dwc_otg_hcd->core_if->pwron_timer);
- + }
- +#endif
- + DWC_FREE(dwc_otg_hcd);
- +}
- +
- +int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if)
- +{
- + struct device *dev = dwc_otg_hcd_to_dev(hcd);
- + int retval = 0;
- + int num_channels;
- + int i;
- + dwc_hc_t *channel;
- +
- +#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_SPINLOCK))
- + DWC_SPINLOCK_ALLOC_LINUX_DEBUG(hcd->lock);
- +#else
- + hcd->lock = DWC_SPINLOCK_ALLOC();
- +#endif
- + DWC_DEBUGPL(DBG_HCDV, "init of HCD %p given core_if %p\n",
- + hcd, core_if);
- + if (!hcd->lock) {
- + DWC_ERROR("Could not allocate lock for pcd");
- + DWC_FREE(hcd);
- + retval = -DWC_E_NO_MEMORY;
- + goto out;
- + }
- + hcd->core_if = core_if;
- +
- + /* Register the HCD CIL Callbacks */
- + dwc_otg_cil_register_hcd_callbacks(hcd->core_if,
- + &hcd_cil_callbacks, hcd);
- +
- + /* Initialize the non-periodic schedule. */
- + DWC_LIST_INIT(&hcd->non_periodic_sched_inactive);
- + DWC_LIST_INIT(&hcd->non_periodic_sched_active);
- +
- + /* Initialize the periodic schedule. */
- + DWC_LIST_INIT(&hcd->periodic_sched_inactive);
- + DWC_LIST_INIT(&hcd->periodic_sched_ready);
- + DWC_LIST_INIT(&hcd->periodic_sched_assigned);
- + DWC_LIST_INIT(&hcd->periodic_sched_queued);
- + DWC_TAILQ_INIT(&hcd->completed_urb_list);
- + /*
- + * Create a host channel descriptor for each host channel implemented
- + * in the controller. Initialize the channel descriptor array.
- + */
- + DWC_CIRCLEQ_INIT(&hcd->free_hc_list);
- + num_channels = hcd->core_if->core_params->host_channels;
- + DWC_MEMSET(hcd->hc_ptr_array, 0, sizeof(hcd->hc_ptr_array));
- + for (i = 0; i < num_channels; i++) {
- + channel = DWC_ALLOC(sizeof(dwc_hc_t));
- + if (channel == NULL) {
- + retval = -DWC_E_NO_MEMORY;
- + DWC_ERROR("%s: host channel allocation failed\n",
- + __func__);
- + dwc_otg_hcd_free(hcd);
- + goto out;
- + }
- + channel->hc_num = i;
- + hcd->hc_ptr_array[i] = channel;
- +#ifdef DEBUG
- + hcd->core_if->hc_xfer_timer[i] =
- + DWC_TIMER_ALLOC("hc timer", hc_xfer_timeout,
- + &hcd->core_if->hc_xfer_info[i]);
- +#endif
- + DWC_DEBUGPL(DBG_HCDV, "HCD Added channel #%d, hc=%p\n", i,
- + 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)));
- +
- +#ifdef CONFIG_ARM64
- + spin_lock_init(&hcd->fiq_state->lock);
- +#endif
- +
- + for (i = 0; i < num_channels; i++) {
- + hcd->fiq_state->channel[i].fsm = FIQ_PASSTHROUGH;
- + }
- + hcd->fiq_state->dummy_send = DWC_DMA_ALLOC_ATOMIC(dev, 16,
- + &hcd->fiq_state->dummy_send_dma);
- +
- + 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(dev, (sizeof(struct fiq_dma_channel) * num_channels), &hcd->fiq_state->dma_base);
- + DWC_WARN("FIQ DMA bounce buffers: virt = %px dma = %pad len=%zu",
- + hcd->fiq_dmab, &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%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" : "",
- + (fiq_fsm_mask & 0x8) ? "Interrupt/Control Split Transaction hack enabled\n" : "");
- + }
- + }
- +
- + /* Initialize the Connection timeout timer. */
- + hcd->conn_timer = DWC_TIMER_ALLOC("Connection timer",
- + dwc_otg_hcd_connect_timeout, 0);
- +
- + printk(KERN_DEBUG "dwc_otg: Microframe scheduler %s\n", microframe_schedule ? "enabled":"disabled");
- + if (microframe_schedule)
- + init_hcd_usecs(hcd);
- +
- + /* Initialize reset tasklet. */
- + hcd->reset_tasklet = DWC_TASK_ALLOC("reset_tasklet", reset_tasklet_func, hcd);
- +
- + hcd->completion_tasklet = DWC_TASK_ALLOC("completion_tasklet",
- + completion_tasklet_func, hcd);
- +#ifdef DWC_DEV_SRPCAP
- + if (hcd->core_if->power_down == 2) {
- + /* Initialize Power on timer for Host power up in case hibernation */
- + hcd->core_if->pwron_timer = DWC_TIMER_ALLOC("PWRON TIMER",
- + dwc_otg_hcd_power_up, core_if);
- + }
- +#endif
- +
- + /*
- + * Allocate space for storing data on status transactions. Normally no
- + * data is sent, but this space acts as a bit bucket. This must be
- + * done after usb_add_hcd since that function allocates the DMA buffer
- + * pool.
- + */
- + if (hcd->core_if->dma_enable) {
- + hcd->status_buf =
- + DWC_DMA_ALLOC(dev, DWC_OTG_HCD_STATUS_BUF_SIZE,
- + &hcd->status_buf_dma);
- + } else {
- + hcd->status_buf = DWC_ALLOC(DWC_OTG_HCD_STATUS_BUF_SIZE);
- + }
- + if (!hcd->status_buf) {
- + retval = -DWC_E_NO_MEMORY;
- + DWC_ERROR("%s: status_buf allocation failed\n", __func__);
- + dwc_otg_hcd_free(hcd);
- + goto out;
- + }
- +
- + hcd->otg_port = 1;
- + hcd->frame_list = NULL;
- + hcd->frame_list_dma = 0;
- + hcd->periodic_qh_count = 0;
- +
- + DWC_MEMSET(hcd->hub_port, 0, sizeof(hcd->hub_port));
- +#ifdef FIQ_DEBUG
- + DWC_MEMSET(hcd->hub_port_alloc, -1, sizeof(hcd->hub_port_alloc));
- +#endif
- +
- +out:
- + return retval;
- +}
- +
- +void dwc_otg_hcd_remove(dwc_otg_hcd_t * hcd)
- +{
- + /* Turn off all host-specific interrupts. */
- + dwc_otg_disable_host_interrupts(hcd->core_if);
- +
- + dwc_otg_hcd_free(hcd);
- +}
- +
- +/**
- + * Initializes dynamic portions of the DWC_otg HCD state.
- + */
- +static void dwc_otg_hcd_reinit(dwc_otg_hcd_t * hcd)
- +{
- + int num_channels;
- + int i;
- + dwc_hc_t *channel;
- + dwc_hc_t *channel_tmp;
- +
- + hcd->flags.d32 = 0;
- +
- + hcd->non_periodic_qh_ptr = &hcd->non_periodic_sched_active;
- + if (!microframe_schedule) {
- + hcd->non_periodic_channels = 0;
- + hcd->periodic_channels = 0;
- + } else {
- + hcd->available_host_channels = hcd->core_if->core_params->host_channels;
- + }
- + /*
- + * Put all channels in the free channel list and clean up channel
- + * states.
- + */
- + DWC_CIRCLEQ_FOREACH_SAFE(channel, channel_tmp,
- + &hcd->free_hc_list, hc_list_entry) {
- + DWC_CIRCLEQ_REMOVE(&hcd->free_hc_list, channel, hc_list_entry);
- + }
- +
- + num_channels = hcd->core_if->core_params->host_channels;
- + for (i = 0; i < num_channels; i++) {
- + channel = hcd->hc_ptr_array[i];
- + DWC_CIRCLEQ_INSERT_TAIL(&hcd->free_hc_list, channel,
- + hc_list_entry);
- + dwc_otg_hc_cleanup(hcd->core_if, channel);
- + }
- +
- + /* Initialize the DWC core for host mode operation. */
- + dwc_otg_core_host_init(hcd->core_if);
- +
- + /* Set core_if's lock pointer to the hcd->lock */
- + hcd->core_if->lock = hcd->lock;
- +}
- +
- +/**
- + * Assigns transactions from a QTD to a free host channel and initializes the
- + * host channel to perform the transactions. The host channel is removed from
- + * the free list.
- + *
- + * @param hcd The HCD state structure.
- + * @param qh Transactions from the first QTD for this QH are selected and
- + * assigned to a free host channel.
- + */
- +static void assign_and_init_hc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
- +{
- + dwc_hc_t *hc;
- + dwc_otg_qtd_t *qtd;
- + dwc_otg_hcd_urb_t *urb;
- + void* ptr = NULL;
- + uint16_t wLength;
- + uint32_t intr_enable;
- + unsigned long flags;
- + gintmsk_data_t gintmsk = { .d32 = 0, };
- + struct device *dev = dwc_otg_hcd_to_dev(hcd);
- +
- + qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list);
- +
- + urb = qtd->urb;
- +
- + DWC_DEBUGPL(DBG_HCDV, "%s(%p,%p) - urb %x, actual_length %d\n", __func__, hcd, qh, (unsigned int)urb, urb->actual_length);
- +
- + if (((urb->actual_length < 0) || (urb->actual_length > urb->length)) && !dwc_otg_hcd_is_pipe_in(&urb->pipe_info))
- + urb->actual_length = urb->length;
- +
- +
- + hc = DWC_CIRCLEQ_FIRST(&hcd->free_hc_list);
- +
- + /* Remove the host channel from the free list. */
- + DWC_CIRCLEQ_REMOVE_INIT(&hcd->free_hc_list, hc, hc_list_entry);
- +
- + qh->channel = hc;
- +
- + qtd->in_process = 1;
- +
- + /*
- + * Use usb_pipedevice to determine device address. This address is
- + * 0 before the SET_ADDRESS command and the correct address afterward.
- + */
- + hc->dev_addr = dwc_otg_hcd_get_dev_addr(&urb->pipe_info);
- + hc->ep_num = dwc_otg_hcd_get_ep_num(&urb->pipe_info);
- + hc->speed = qh->dev_speed;
- + hc->max_packet = dwc_max_packet(qh->maxp);
- +
- + hc->xfer_started = 0;
- + hc->halt_status = DWC_OTG_HC_XFER_NO_HALT_STATUS;
- + hc->error_state = (qtd->error_count > 0);
- + hc->halt_on_queue = 0;
- + hc->halt_pending = 0;
- + hc->requests = 0;
- +
- + /*
- + * The following values may be modified in the transfer type section
- + * below. The xfer_len value may be reduced when the transfer is
- + * started to accommodate the max widths of the XferSize and PktCnt
- + * fields in the HCTSIZn register.
- + */
- +
- + hc->ep_is_in = (dwc_otg_hcd_is_pipe_in(&urb->pipe_info) != 0);
- + if (hc->ep_is_in) {
- + hc->do_ping = 0;
- + } else {
- + hc->do_ping = qh->ping_state;
- + }
- +
- + hc->data_pid_start = qh->data_toggle;
- + hc->multi_count = 1;
- +
- + if (hcd->core_if->dma_enable) {
- + hc->xfer_buff =
- + (uint8_t *)(uintptr_t)urb->dma + urb->actual_length;
- +
- + /* For non-dword aligned case */
- + if (((unsigned long)hc->xfer_buff & 0x3)
- + && !hcd->core_if->dma_desc_enable) {
- + ptr = (uint8_t *) urb->buf + urb->actual_length;
- + }
- + } else {
- + hc->xfer_buff = (uint8_t *) urb->buf + urb->actual_length;
- + }
- + hc->xfer_len = urb->length - urb->actual_length;
- + hc->xfer_count = 0;
- +
- + /*
- + * Set the split attributes
- + */
- + hc->do_split = 0;
- + if (qh->do_split) {
- + uint32_t hub_addr, port_addr;
- + hc->do_split = 1;
- + hc->start_pkt_count = 1;
- + hc->xact_pos = qtd->isoc_split_pos;
- + /* We don't need to do complete splits anymore */
- +// if(fiq_fsm_enable)
- + if (0)
- + hc->complete_split = qtd->complete_split = 0;
- + else
- + hc->complete_split = qtd->complete_split;
- +
- + hcd->fops->hub_info(hcd, urb->priv, &hub_addr, &port_addr);
- + hc->hub_addr = (uint8_t) hub_addr;
- + hc->port_addr = (uint8_t) port_addr;
- + }
- +
- + switch (dwc_otg_hcd_get_pipe_type(&urb->pipe_info)) {
- + case UE_CONTROL:
- + hc->ep_type = DWC_OTG_EP_TYPE_CONTROL;
- + switch (qtd->control_phase) {
- + case DWC_OTG_CONTROL_SETUP:
- + DWC_DEBUGPL(DBG_HCDV, " Control setup transaction\n");
- + hc->do_ping = 0;
- + hc->ep_is_in = 0;
- + hc->data_pid_start = DWC_OTG_HC_PID_SETUP;
- + if (hcd->core_if->dma_enable) {
- + hc->xfer_buff =
- + (uint8_t *)(uintptr_t)urb->setup_dma;
- + } else {
- + hc->xfer_buff = (uint8_t *) urb->setup_packet;
- + }
- + hc->xfer_len = 8;
- + ptr = NULL;
- + break;
- + case DWC_OTG_CONTROL_DATA:
- + DWC_DEBUGPL(DBG_HCDV, " Control data transaction\n");
- + /*
- + * Hardware bug: small IN packets with length < 4
- + * cause a 4-byte write to memory. We can only catch
- + * the case where we know a short packet is going to be
- + * returned in a control transfer, as the length is
- + * specified in the setup packet. This is only an issue
- + * for drivers that insist on packing a device's various
- + * properties into a struct and querying them one at a
- + * time (uvcvideo).
- + * Force the use of align_buf so that the subsequent
- + * memcpy puts the right number of bytes in the URB's
- + * buffer.
- + */
- + wLength = ((uint16_t *)urb->setup_packet)[3];
- + if (hc->ep_is_in && wLength < 4)
- + ptr = hc->xfer_buff;
- +
- + hc->data_pid_start = qtd->data_toggle;
- + break;
- + case DWC_OTG_CONTROL_STATUS:
- + /*
- + * Direction is opposite of data direction or IN if no
- + * data.
- + */
- + DWC_DEBUGPL(DBG_HCDV, " Control status transaction\n");
- + if (urb->length == 0) {
- + hc->ep_is_in = 1;
- + } else {
- + hc->ep_is_in =
- + dwc_otg_hcd_is_pipe_out(&urb->pipe_info);
- + }
- + if (hc->ep_is_in) {
- + hc->do_ping = 0;
- + }
- +
- + hc->data_pid_start = DWC_OTG_HC_PID_DATA1;
- +
- + hc->xfer_len = 0;
- + if (hcd->core_if->dma_enable) {
- + hc->xfer_buff = (uint8_t *)
- + (uintptr_t)hcd->status_buf_dma;
- + } else {
- + hc->xfer_buff = (uint8_t *) hcd->status_buf;
- + }
- + ptr = NULL;
- + break;
- + }
- + break;
- + case UE_BULK:
- + hc->ep_type = DWC_OTG_EP_TYPE_BULK;
- + break;
- + case UE_INTERRUPT:
- + hc->ep_type = DWC_OTG_EP_TYPE_INTR;
- + break;
- + case UE_ISOCHRONOUS:
- + {
- + struct dwc_otg_hcd_iso_packet_desc *frame_desc;
- +
- + hc->ep_type = DWC_OTG_EP_TYPE_ISOC;
- +
- + if (hcd->core_if->dma_desc_enable)
- + break;
- +
- + frame_desc = &urb->iso_descs[qtd->isoc_frame_index];
- +
- + frame_desc->status = 0;
- +
- + if (hcd->core_if->dma_enable) {
- + hc->xfer_buff = (uint8_t *)(uintptr_t)urb->dma;
- + } else {
- + hc->xfer_buff = (uint8_t *) urb->buf;
- + }
- + hc->xfer_buff +=
- + frame_desc->offset + qtd->isoc_split_offset;
- + hc->xfer_len =
- + frame_desc->length - qtd->isoc_split_offset;
- +
- + /* For non-dword aligned buffers */
- + if (((unsigned long)hc->xfer_buff & 0x3)
- + && hcd->core_if->dma_enable) {
- + ptr =
- + (uint8_t *) urb->buf + frame_desc->offset +
- + qtd->isoc_split_offset;
- + } else
- + ptr = NULL;
- +
- + if (hc->xact_pos == DWC_HCSPLIT_XACTPOS_ALL) {
- + if (hc->xfer_len <= 188) {
- + hc->xact_pos = DWC_HCSPLIT_XACTPOS_ALL;
- + } else {
- + hc->xact_pos =
- + DWC_HCSPLIT_XACTPOS_BEGIN;
- + }
- + }
- + }
- + break;
- + }
- + /* non DWORD-aligned buffer case */
- + if (ptr) {
- + uint32_t buf_size;
- + if (hc->ep_type != DWC_OTG_EP_TYPE_ISOC) {
- + buf_size = hcd->core_if->core_params->max_transfer_size;
- + } else {
- + buf_size = 4096;
- + }
- + if (!qh->dw_align_buf) {
- + qh->dw_align_buf = DWC_DMA_ALLOC_ATOMIC(dev, buf_size,
- + &qh->dw_align_buf_dma);
- + if (!qh->dw_align_buf) {
- + DWC_ERROR
- + ("%s: Failed to allocate memory to handle "
- + "non-dword aligned buffer case\n",
- + __func__);
- + return;
- + }
- + }
- + if (!hc->ep_is_in) {
- + dwc_memcpy(qh->dw_align_buf, ptr, hc->xfer_len);
- + }
- + hc->align_buff = qh->dw_align_buf_dma;
- + } else {
- + hc->align_buff = 0;
- + }
- +
- + if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
- + hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
- + /*
- + * This value may be modified when the transfer is started to
- + * reflect the actual transfer length.
- + */
- + hc->multi_count = dwc_hb_mult(qh->maxp);
- + }
- +
- + if (hcd->core_if->dma_desc_enable)
- + hc->desc_list_addr = qh->desc_list_dma;
- +
- + dwc_otg_hc_init(hcd->core_if, hc);
- +
- + local_irq_save(flags);
- +
- + if (fiq_enable) {
- + local_fiq_disable();
- + fiq_fsm_spin_lock(&hcd->fiq_state->lock);
- + }
- +
- + /* Enable the top level host channel interrupt. */
- + intr_enable = (1 << hc->hc_num);
- + DWC_MODIFY_REG32(&hcd->core_if->host_if->host_global_regs->haintmsk, 0, intr_enable);
- +
- + /* Make sure host channel interrupts are enabled. */
- + gintmsk.b.hcintr = 1;
- + DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, 0, gintmsk.d32);
- +
- + if (fiq_enable) {
- + fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
- + local_fiq_enable();
- + }
- +
- + local_irq_restore(flags);
- + hc->qh = qh;
- +}
- +
- +
- +/**
- + * fiq_fsm_transaction_suitable() - Test a QH for compatibility with the FIQ
- + * @hcd: Pointer to the dwc_otg_hcd struct
- + * @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_hcd_t *hcd, dwc_otg_qh_t *qh)
- +{
- + 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)) {
- + /* 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;
- + dwc_dma_t ptr;
- + int i;
- +
- + if (urb->packet_count < 2)
- + return 0;
- + for (i = 0; i < urb->packet_count; i++) {
- + ptr = urb->dma + urb->iso_descs[i].offset;
- + if (ptr & 0x3)
- + return 0;
- + }
- + return 1;
- + }
- + }
- + 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;
- +
- + 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.
- + * Pointer arithmetic on hcd->fiq_state->dma_base (a dma_addr_t)
- + * to point it to the correct offset in the allocated buffers.
- + */
- + blob = (struct fiq_dma_blob *)
- + (uintptr_t)hcd->fiq_state->dma_base;
- + st->hcdma_copy.d32 =(u32)(uintptr_t)
- + blob->channel[hc->hc_num].index[0].buf;
- +
- + /* 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) {
- +
- + dwc_otg_qtd_t *qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list);
- +
- + frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index];
- + frame_length = frame_desc->length;
- +
- + /* 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
- + *
- + * Pointer arithmetic on hcd->fiq_state->dma_base (a
- + * dma_addr_t) to point it to the correct offset in the
- + * allocated buffers.
- + */
- + blob = (struct fiq_dma_blob *)
- + (uintptr_t)hcd->fiq_state->dma_base;
- + st->hcdma_copy.d32 = (u32)(uintptr_t)
- + blob->channel[hc->hc_num].index[0].buf;
- +
- + /* 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;
- + }
- + }
- +}
- +
- +/**
- + * fiq_fsm_np_tt_contended() - Avoid performing contended non-periodic transfers
- + * @hcd: Pointer to the dwc_otg_hcd struct
- + * @qh: Pointer to the endpoint's queue head
- + *
- + * Certain hub chips don't differentiate between IN and OUT non-periodic pipes
- + * with the same endpoint number. If transfers get completed out of order
- + * (disregarding the direction token) then the hub can lock up
- + * or return erroneous responses.
- + *
- + * Returns 1 if initiating the transfer would cause contention, 0 otherwise.
- + */
- +int fiq_fsm_np_tt_contended(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
- +{
- + int i;
- + struct fiq_channel_state *st;
- + int dev_addr = qh->channel->dev_addr;
- + int ep_num = qh->channel->ep_num;
- + for (i = 0; i < hcd->core_if->core_params->host_channels; i++) {
- + if (i == qh->channel->hc_num)
- + continue;
- + st = &hcd->fiq_state->channel[i];
- + switch (st->fsm) {
- + case FIQ_NP_SSPLIT_STARTED:
- + case FIQ_NP_SSPLIT_RETRY:
- + case FIQ_NP_SSPLIT_PENDING:
- + case FIQ_NP_OUT_CSPLIT_RETRY:
- + case FIQ_NP_IN_CSPLIT_RETRY:
- + if (st->hcchar_copy.b.devaddr == dev_addr &&
- + st->hcchar_copy.b.epnum == ep_num)
- + return 1;
- + break;
- + default:
- + break;
- + }
- + }
- + return 0;
- +}
- +
- +/*
- + * 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;
- + }
- + }
- +
- + st->hs_isoc_info.stride = qh->interval;
- + st->uframe_sleeps = 0;
- +
- + 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();
- + fiq_fsm_spin_lock(&hcd->fiq_state->lock);
- + 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. SOF interrupt handler
- + * will wake this channel at the next interrupt.
- + */
- + st->fsm = FIQ_HS_ISOC_SLEEPING;
- + st->uframe_sleeps = 1;
- + } 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;
- + fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
- + local_fiq_enable();
- + return 0;
- +}
- +
- +
- +/**
- + * 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)
- +{
- + 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];
- +
- + /*
- + * Non-periodic channel assignments stay in the non_periodic_active queue.
- + * Therefore we get repeatedly called until the FIQ's done processing this channel.
- + */
- + if (qh->channel->xfer_started == 1)
- + return 0;
- +
- + if (st->fsm != FIQ_PASSTHROUGH) {
- + pr_warn_ratelimited("%s:%d: Queue called for an active channel\n", __func__, __LINE__);
- + return 0;
- + }
- +
- + qh->channel->xfer_started = 1;
- +
- + st->nr_errors = 0;
- +
- + st->hcchar_copy.d32 = 0;
- + st->hcchar_copy.b.mps = min_t(uint32_t, hc->xfer_len, 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. */
- +
- + 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 = min_t(uint32_t, 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;
- +
- + /* Hack courtesy of FreeBSD: apparently forcing Interrupt Split transactions
- + * as Control puts the transfer into the non-periodic request queue and the
- + * non-periodic handler in the hub. Makes things lots easier.
- + */
- + if ((fiq_fsm_mask & 0x8) && hc->ep_type == UE_INTERRUPT) {
- + st->hcchar_copy.b.multicnt = 0;
- + st->hcchar_copy.b.oddfrm = 0;
- + st->hcchar_copy.b.eptype = UE_CONTROL;
- + if (hc->align_buff) {
- + st->hcdma_copy.d32 = hc->align_buff;
- + } else {
- + st->hcdma_copy.d32 = ((unsigned long) hc->xfer_buff & 0xFFFFFFFF);
- + }
- + }
- + 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();
- + fiq_fsm_spin_lock(&hcd->fiq_state->lock);
- +
- + 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;
- + }
- + }
- + }
- + if ((fiq_fsm_mask & 0x8) && hc->ep_type == UE_INTERRUPT)
- + start_immediate = 1;
- +
- + 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:
- + if (fiq_fsm_np_tt_contended(hcd, qh)) {
- + st->fsm = FIQ_NP_SSPLIT_PENDING;
- + start_immediate = 0;
- + } else {
- + 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 (fiq_fsm_mask & 0x8) {
- + if (fiq_fsm_np_tt_contended(hcd, qh)) {
- + st->fsm = FIQ_NP_SSPLIT_PENDING;
- + start_immediate = 0;
- + } else {
- + st->fsm = FIQ_NP_SSPLIT_STARTED;
- + }
- + } else 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();
- + fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
- + local_fiq_enable();
- + return 0;
- +}
- +
- +
- +/**
- + * This function selects transactions from the HCD transfer schedule and
- + * assigns them to available host channels. It is called from HCD interrupt
- + * handler functions.
- + *
- + * @param hcd The HCD state structure.
- + *
- + * @return The types of new transactions that were assigned to host channels.
- + */
- +dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd)
- +{
- + dwc_list_link_t *qh_ptr;
- + dwc_otg_qh_t *qh;
- + int num_channels;
- + dwc_otg_transaction_type_e ret_val = DWC_OTG_TRANSACTION_NONE;
- +
- +#ifdef DEBUG_HOST_CHANNELS
- + last_sel_trans_num_per_scheduled = 0;
- + last_sel_trans_num_nonper_scheduled = 0;
- + last_sel_trans_num_avail_hc_at_start = hcd->available_host_channels;
- +#endif /* DEBUG_HOST_CHANNELS */
- +
- + /* Process entries in the periodic ready list. */
- + qh_ptr = DWC_LIST_FIRST(&hcd->periodic_sched_ready);
- +
- + while (qh_ptr != &hcd->periodic_sched_ready &&
- + !DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) {
- +
- + qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry);
- +
- + if (microframe_schedule) {
- + // Make sure we leave one channel for non periodic transactions.
- + if (hcd->available_host_channels <= 1) {
- + break;
- + }
- + hcd->available_host_channels--;
- +#ifdef DEBUG_HOST_CHANNELS
- + last_sel_trans_num_per_scheduled++;
- +#endif /* DEBUG_HOST_CHANNELS */
- + }
- + qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry);
- + assign_and_init_hc(hcd, qh);
- +
- + /*
- + * Move the QH from the periodic ready schedule to the
- + * periodic assigned schedule.
- + */
- + qh_ptr = DWC_LIST_NEXT(qh_ptr);
- + DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_assigned,
- + &qh->qh_list_entry);
- + }
- +
- + /*
- + * Process entries in the inactive portion of the non-periodic
- + * schedule. Some free host channels may not be used if they are
- + * reserved for periodic transfers.
- + */
- + qh_ptr = hcd->non_periodic_sched_inactive.next;
- + num_channels = hcd->core_if->core_params->host_channels;
- + while (qh_ptr != &hcd->non_periodic_sched_inactive &&
- + (microframe_schedule || hcd->non_periodic_channels <
- + num_channels - hcd->periodic_channels) &&
- + !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 (fiq_enable && 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;
- + }
- + }
- + }
- +
- + if (microframe_schedule) {
- + if (hcd->available_host_channels < 1) {
- + break;
- + }
- + hcd->available_host_channels--;
- +#ifdef DEBUG_HOST_CHANNELS
- + last_sel_trans_num_nonper_scheduled++;
- +#endif /* DEBUG_HOST_CHANNELS */
- + }
- +
- + assign_and_init_hc(hcd, qh);
- +
- + /*
- + * Move the QH from the non-periodic inactive schedule to the
- + * non-periodic active schedule.
- + */
- + qh_ptr = DWC_LIST_NEXT(qh_ptr);
- + DWC_LIST_MOVE_HEAD(&hcd->non_periodic_sched_active,
- + &qh->qh_list_entry);
- +
- + 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;
- +
- + if(!DWC_LIST_EMPTY(&hcd->non_periodic_sched_active))
- + ret_val |= DWC_OTG_TRANSACTION_NON_PERIODIC;
- +
- +
- +#ifdef DEBUG_HOST_CHANNELS
- + last_sel_trans_num_avail_hc_at_end = hcd->available_host_channels;
- +#endif /* DEBUG_HOST_CHANNELS */
- + return ret_val;
- +}
- +
- +/**
- + * Attempts to queue a single transaction request for a host channel
- + * associated with either a periodic or non-periodic transfer. This function
- + * assumes that there is space available in the appropriate request queue. For
- + * an OUT transfer or SETUP transaction in Slave mode, it checks whether space
- + * is available in the appropriate Tx FIFO.
- + *
- + * @param hcd The HCD state structure.
- + * @param hc Host channel descriptor associated with either a periodic or
- + * non-periodic transfer.
- + * @param fifo_dwords_avail Number of DWORDs available in the periodic Tx
- + * FIFO for periodic transfers or the non-periodic Tx FIFO for non-periodic
- + * transfers.
- + *
- + * @return 1 if a request is queued and more requests may be needed to
- + * complete the transfer, 0 if no more requests are required for this
- + * transfer, -1 if there is insufficient space in the Tx FIFO.
- + */
- +static int queue_transaction(dwc_otg_hcd_t * hcd,
- + dwc_hc_t * hc, uint16_t fifo_dwords_avail)
- +{
- + int retval;
- +
- + if (hcd->core_if->dma_enable) {
- + if (hcd->core_if->dma_desc_enable) {
- + if (!hc->xfer_started
- + || (hc->ep_type == DWC_OTG_EP_TYPE_ISOC)) {
- + dwc_otg_hcd_start_xfer_ddma(hcd, hc->qh);
- + 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;
- + }
- + retval = 0;
- + } else if (hc->halt_pending) {
- + /* Don't queue a request if the channel has been halted. */
- + retval = 0;
- + } else if (hc->halt_on_queue) {
- + dwc_otg_hc_halt(hcd->core_if, hc, hc->halt_status);
- + retval = 0;
- + } else if (hc->do_ping) {
- + if (!hc->xfer_started) {
- + dwc_otg_hc_start_transfer(hcd->core_if, hc);
- + }
- + retval = 0;
- + } else if (!hc->ep_is_in || hc->data_pid_start == DWC_OTG_HC_PID_SETUP) {
- + if ((fifo_dwords_avail * 4) >= hc->max_packet) {
- + if (!hc->xfer_started) {
- + dwc_otg_hc_start_transfer(hcd->core_if, hc);
- + retval = 1;
- + } else {
- + retval =
- + dwc_otg_hc_continue_transfer(hcd->core_if,
- + hc);
- + }
- + } else {
- + retval = -1;
- + }
- + } else {
- + if (!hc->xfer_started) {
- + dwc_otg_hc_start_transfer(hcd->core_if, hc);
- + retval = 1;
- + } else {
- + retval = dwc_otg_hc_continue_transfer(hcd->core_if, hc);
- + }
- + }
- +
- + return retval;
- +}
- +
- +/**
- + * Processes periodic channels for the next frame and queues transactions for
- + * these channels to the DWC_otg controller. After queueing transactions, the
- + * Periodic Tx FIFO Empty interrupt is enabled if there are more transactions
- + * to queue as Periodic Tx FIFO or request queue space becomes available.
- + * Otherwise, the Periodic Tx FIFO Empty interrupt is disabled.
- + */
- +static void process_periodic_channels(dwc_otg_hcd_t * hcd)
- +{
- + hptxsts_data_t tx_status;
- + dwc_list_link_t *qh_ptr;
- + dwc_otg_qh_t *qh;
- + int status = 0;
- + int no_queue_space = 0;
- + int no_fifo_space = 0;
- +
- + dwc_otg_host_global_regs_t *host_regs;
- + host_regs = hcd->core_if->host_if->host_global_regs;
- +
- + DWC_DEBUGPL(DBG_HCDV, "Queue periodic transactions\n");
- +#ifdef DEBUG
- + tx_status.d32 = DWC_READ_REG32(&host_regs->hptxsts);
- + DWC_DEBUGPL(DBG_HCDV,
- + " P Tx Req Queue Space Avail (before queue): %d\n",
- + tx_status.b.ptxqspcavail);
- + DWC_DEBUGPL(DBG_HCDV, " P Tx FIFO Space Avail (before queue): %d\n",
- + tx_status.b.ptxfspcavail);
- +#endif
- +
- + qh_ptr = hcd->periodic_sched_assigned.next;
- + while (qh_ptr != &hcd->periodic_sched_assigned) {
- + tx_status.d32 = DWC_READ_REG32(&host_regs->hptxsts);
- + if (tx_status.b.ptxqspcavail == 0) {
- + no_queue_space = 1;
- + break;
- + }
- +
- + qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry);
- +
- + // 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_fsm_enable && qh->do_split && ((dwc_otg_hcd_get_frame_number(hcd) + 1) & 7) > 6)
- + {
- + qh_ptr = qh_ptr->next;
- + hcd->fiq_state->next_sched_frame = dwc_otg_hcd_get_frame_number(hcd) | 7;
- + continue;
- + }
- +
- + if (fiq_fsm_enable && fiq_fsm_transaction_suitable(hcd, 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;
- + }
- + }
- +
- + /*
- + * In Slave mode, stay on the current transfer until there is
- + * nothing more to do or the high-bandwidth request count is
- + * reached. In DMA mode, only need to queue one request. The
- + * controller automatically handles multiple packets for
- + * high-bandwidth transfers.
- + */
- + if (hcd->core_if->dma_enable || status == 0 ||
- + qh->channel->requests == qh->channel->multi_count) {
- + qh_ptr = qh_ptr->next;
- + /*
- + * Move the QH from the periodic assigned schedule to
- + * the periodic queued schedule.
- + */
- + DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_queued,
- + &qh->qh_list_entry);
- +
- + /* done queuing high bandwidth */
- + hcd->core_if->queuing_high_bandwidth = 0;
- + }
- + }
- +
- + if (!hcd->core_if->dma_enable) {
- + dwc_otg_core_global_regs_t *global_regs;
- + gintmsk_data_t intr_mask = {.d32 = 0 };
- +
- + global_regs = hcd->core_if->core_global_regs;
- + intr_mask.b.ptxfempty = 1;
- +#ifdef DEBUG
- + tx_status.d32 = DWC_READ_REG32(&host_regs->hptxsts);
- + DWC_DEBUGPL(DBG_HCDV,
- + " P Tx Req Queue Space Avail (after queue): %d\n",
- + tx_status.b.ptxqspcavail);
- + DWC_DEBUGPL(DBG_HCDV,
- + " P Tx FIFO Space Avail (after queue): %d\n",
- + tx_status.b.ptxfspcavail);
- +#endif
- + if (!DWC_LIST_EMPTY(&hcd->periodic_sched_assigned) ||
- + no_queue_space || no_fifo_space) {
- + /*
- + * May need to queue more transactions as the request
- + * queue or Tx FIFO empties. Enable the periodic Tx
- + * FIFO empty interrupt. (Always use the half-empty
- + * level to ensure that new requests are loaded as
- + * soon as possible.)
- + */
- + DWC_MODIFY_REG32(&global_regs->gintmsk, 0,
- + intr_mask.d32);
- + } else {
- + /*
- + * Disable the Tx FIFO empty interrupt since there are
- + * no more transactions that need to be queued right
- + * now. This function is called from interrupt
- + * handlers to queue more transactions as transfer
- + * states change.
- + */
- + DWC_MODIFY_REG32(&global_regs->gintmsk, intr_mask.d32,
- + 0);
- + }
- + }
- +}
- +
- +/**
- + * Processes active non-periodic channels and queues transactions for these
- + * channels to the DWC_otg controller. After queueing transactions, the NP Tx
- + * FIFO Empty interrupt is enabled if there are more transactions to queue as
- + * NP Tx FIFO or request queue space becomes available. Otherwise, the NP Tx
- + * FIFO Empty interrupt is disabled.
- + */
- +static void process_non_periodic_channels(dwc_otg_hcd_t * hcd)
- +{
- + gnptxsts_data_t tx_status;
- + dwc_list_link_t *orig_qh_ptr;
- + dwc_otg_qh_t *qh;
- + int status;
- + int no_queue_space = 0;
- + int no_fifo_space = 0;
- + int more_to_do = 0;
- +
- + dwc_otg_core_global_regs_t *global_regs =
- + hcd->core_if->core_global_regs;
- +
- + DWC_DEBUGPL(DBG_HCDV, "Queue non-periodic transactions\n");
- +#ifdef DEBUG
- + tx_status.d32 = DWC_READ_REG32(&global_regs->gnptxsts);
- + DWC_DEBUGPL(DBG_HCDV,
- + " NP Tx Req Queue Space Avail (before queue): %d\n",
- + tx_status.b.nptxqspcavail);
- + DWC_DEBUGPL(DBG_HCDV, " NP Tx FIFO Space Avail (before queue): %d\n",
- + tx_status.b.nptxfspcavail);
- +#endif
- + /*
- + * Keep track of the starting point. Skip over the start-of-list
- + * entry.
- + */
- + if (hcd->non_periodic_qh_ptr == &hcd->non_periodic_sched_active) {
- + hcd->non_periodic_qh_ptr = hcd->non_periodic_qh_ptr->next;
- + }
- + orig_qh_ptr = hcd->non_periodic_qh_ptr;
- +
- + /*
- + * Process once through the active list or until no more space is
- + * available in the request queue or the Tx FIFO.
- + */
- + do {
- + tx_status.d32 = DWC_READ_REG32(&global_regs->gnptxsts);
- + if (!hcd->core_if->dma_enable && tx_status.b.nptxqspcavail == 0) {
- + no_queue_space = 1;
- + break;
- + }
- +
- + qh = DWC_LIST_ENTRY(hcd->non_periodic_qh_ptr, dwc_otg_qh_t,
- + qh_list_entry);
- +
- + if(fiq_fsm_enable && fiq_fsm_transaction_suitable(hcd, qh)) {
- + fiq_fsm_queue_split_transaction(hcd, qh);
- + } else {
- + 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;
- + }
- + }
- + /* 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) {
- + hcd->non_periodic_qh_ptr =
- + hcd->non_periodic_qh_ptr->next;
- + }
- +
- + } while (hcd->non_periodic_qh_ptr != orig_qh_ptr);
- +
- + if (!hcd->core_if->dma_enable) {
- + gintmsk_data_t intr_mask = {.d32 = 0 };
- + intr_mask.b.nptxfempty = 1;
- +
- +#ifdef DEBUG
- + tx_status.d32 = DWC_READ_REG32(&global_regs->gnptxsts);
- + DWC_DEBUGPL(DBG_HCDV,
- + " NP Tx Req Queue Space Avail (after queue): %d\n",
- + tx_status.b.nptxqspcavail);
- + DWC_DEBUGPL(DBG_HCDV,
- + " NP Tx FIFO Space Avail (after queue): %d\n",
- + tx_status.b.nptxfspcavail);
- +#endif
- + if (more_to_do || no_queue_space || no_fifo_space) {
- + /*
- + * May need to queue more transactions as the request
- + * queue or Tx FIFO empties. Enable the non-periodic
- + * Tx FIFO empty interrupt. (Always use the half-empty
- + * level to ensure that new requests are loaded as
- + * soon as possible.)
- + */
- + DWC_MODIFY_REG32(&global_regs->gintmsk, 0,
- + intr_mask.d32);
- + } else {
- + /*
- + * Disable the Tx FIFO empty interrupt since there are
- + * no more transactions that need to be queued right
- + * now. This function is called from interrupt
- + * handlers to queue more transactions as transfer
- + * states change.
- + */
- + DWC_MODIFY_REG32(&global_regs->gintmsk, intr_mask.d32,
- + 0);
- + }
- + }
- +}
- +
- +/**
- + * This function processes the currently active host channels and queues
- + * transactions for these channels to the DWC_otg controller. It is called
- + * from HCD interrupt handler functions.
- + *
- + * @param hcd The HCD state structure.
- + * @param tr_type The type(s) of transactions to queue (non-periodic,
- + * periodic, or both).
- + */
- +void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t * hcd,
- + dwc_otg_transaction_type_e tr_type)
- +{
- +#ifdef DEBUG_SOF
- + DWC_DEBUGPL(DBG_HCD, "Queue Transactions\n");
- +#endif
- + /* Process host channels associated with periodic transfers. */
- + if ((tr_type == DWC_OTG_TRANSACTION_PERIODIC ||
- + tr_type == DWC_OTG_TRANSACTION_ALL) &&
- + !DWC_LIST_EMPTY(&hcd->periodic_sched_assigned)) {
- +
- + process_periodic_channels(hcd);
- + }
- +
- + /* Process host channels associated with non-periodic transfers. */
- + if (tr_type == DWC_OTG_TRANSACTION_NON_PERIODIC ||
- + tr_type == DWC_OTG_TRANSACTION_ALL) {
- + if (!DWC_LIST_EMPTY(&hcd->non_periodic_sched_active)) {
- + process_non_periodic_channels(hcd);
- + } else {
- + /*
- + * Ensure NP Tx FIFO empty interrupt is disabled when
- + * there are no non-periodic transfers to process.
- + */
- + gintmsk_data_t gintmsk = {.d32 = 0 };
- + gintmsk.b.nptxfempty = 1;
- +
- + if (fiq_enable) {
- + local_fiq_disable();
- + fiq_fsm_spin_lock(&hcd->fiq_state->lock);
- + DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, gintmsk.d32, 0);
- + fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
- + local_fiq_enable();
- + } else {
- + DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, gintmsk.d32, 0);
- + }
- + }
- + }
- +}
- +
- +#ifdef DWC_HS_ELECT_TST
- +/*
- + * Quick and dirty hack to implement the HS Electrical Test
- + * SINGLE_STEP_GET_DEVICE_DESCRIPTOR feature.
- + *
- + * This code was copied from our userspace app "hset". It sends a
- + * Get Device Descriptor control sequence in two parts, first the
- + * Setup packet by itself, followed some time later by the In and
- + * Ack packets. Rather than trying to figure out how to add this
- + * functionality to the normal driver code, we just hijack the
- + * hardware, using these two function to drive the hardware
- + * directly.
- + */
- +
- +static dwc_otg_core_global_regs_t *global_regs;
- +static dwc_otg_host_global_regs_t *hc_global_regs;
- +static dwc_otg_hc_regs_t *hc_regs;
- +static uint32_t *data_fifo;
- +
- +static void do_setup(void)
- +{
- + gintsts_data_t gintsts;
- + hctsiz_data_t hctsiz;
- + hcchar_data_t hcchar;
- + haint_data_t haint;
- + hcint_data_t hcint;
- +
- + /* Enable HAINTs */
- + DWC_WRITE_REG32(&hc_global_regs->haintmsk, 0x0001);
- +
- + /* Enable HCINTs */
- + DWC_WRITE_REG32(&hc_regs->hcintmsk, 0x04a3);
- +
- + /* Read GINTSTS */
- + gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
- +
- + /* Read HAINT */
- + haint.d32 = DWC_READ_REG32(&hc_global_regs->haint);
- +
- + /* Read HCINT */
- + hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
- +
- + /* Read HCCHAR */
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- +
- + /* Clear HCINT */
- + DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32);
- +
- + /* Clear HAINT */
- + DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32);
- +
- + /* Clear GINTSTS */
- + DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
- +
- + /* Read GINTSTS */
- + gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
- +
- + /*
- + * Send Setup packet (Get Device Descriptor)
- + */
- +
- + /* Make sure channel is disabled */
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- + if (hcchar.b.chen) {
- + hcchar.b.chdis = 1;
- +// hcchar.b.chen = 1;
- + DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
- + //sleep(1);
- + dwc_mdelay(1000);
- +
- + /* Read GINTSTS */
- + gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
- +
- + /* Read HAINT */
- + haint.d32 = DWC_READ_REG32(&hc_global_regs->haint);
- +
- + /* Read HCINT */
- + hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
- +
- + /* Read HCCHAR */
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- +
- + /* Clear HCINT */
- + DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32);
- +
- + /* Clear HAINT */
- + DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32);
- +
- + /* Clear GINTSTS */
- + DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
- +
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- + }
- +
- + /* Set HCTSIZ */
- + hctsiz.d32 = 0;
- + hctsiz.b.xfersize = 8;
- + hctsiz.b.pktcnt = 1;
- + hctsiz.b.pid = DWC_OTG_HC_PID_SETUP;
- + DWC_WRITE_REG32(&hc_regs->hctsiz, hctsiz.d32);
- +
- + /* Set HCCHAR */
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- + hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL;
- + hcchar.b.epdir = 0;
- + hcchar.b.epnum = 0;
- + hcchar.b.mps = 8;
- + hcchar.b.chen = 1;
- + DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
- +
- + /* Fill FIFO with Setup data for Get Device Descriptor */
- + data_fifo = (uint32_t *) ((char *)global_regs + 0x1000);
- + DWC_WRITE_REG32(data_fifo++, 0x01000680);
- + DWC_WRITE_REG32(data_fifo++, 0x00080000);
- +
- + gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
- +
- + /* Wait for host channel interrupt */
- + do {
- + gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
- + } while (gintsts.b.hcintr == 0);
- +
- + /* Disable HCINTs */
- + DWC_WRITE_REG32(&hc_regs->hcintmsk, 0x0000);
- +
- + /* Disable HAINTs */
- + DWC_WRITE_REG32(&hc_global_regs->haintmsk, 0x0000);
- +
- + /* Read HAINT */
- + haint.d32 = DWC_READ_REG32(&hc_global_regs->haint);
- +
- + /* Read HCINT */
- + hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
- +
- + /* Read HCCHAR */
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- +
- + /* Clear HCINT */
- + DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32);
- +
- + /* Clear HAINT */
- + DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32);
- +
- + /* Clear GINTSTS */
- + DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
- +
- + /* Read GINTSTS */
- + gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
- +}
- +
- +static void do_in_ack(void)
- +{
- + gintsts_data_t gintsts;
- + hctsiz_data_t hctsiz;
- + hcchar_data_t hcchar;
- + haint_data_t haint;
- + hcint_data_t hcint;
- + host_grxsts_data_t grxsts;
- +
- + /* Enable HAINTs */
- + DWC_WRITE_REG32(&hc_global_regs->haintmsk, 0x0001);
- +
- + /* Enable HCINTs */
- + DWC_WRITE_REG32(&hc_regs->hcintmsk, 0x04a3);
- +
- + /* Read GINTSTS */
- + gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
- +
- + /* Read HAINT */
- + haint.d32 = DWC_READ_REG32(&hc_global_regs->haint);
- +
- + /* Read HCINT */
- + hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
- +
- + /* Read HCCHAR */
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- +
- + /* Clear HCINT */
- + DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32);
- +
- + /* Clear HAINT */
- + DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32);
- +
- + /* Clear GINTSTS */
- + DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
- +
- + /* Read GINTSTS */
- + gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
- +
- + /*
- + * Receive Control In packet
- + */
- +
- + /* Make sure channel is disabled */
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- + if (hcchar.b.chen) {
- + hcchar.b.chdis = 1;
- + hcchar.b.chen = 1;
- + DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
- + //sleep(1);
- + dwc_mdelay(1000);
- +
- + /* Read GINTSTS */
- + gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
- +
- + /* Read HAINT */
- + haint.d32 = DWC_READ_REG32(&hc_global_regs->haint);
- +
- + /* Read HCINT */
- + hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
- +
- + /* Read HCCHAR */
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- +
- + /* Clear HCINT */
- + DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32);
- +
- + /* Clear HAINT */
- + DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32);
- +
- + /* Clear GINTSTS */
- + DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
- +
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- + }
- +
- + /* Set HCTSIZ */
- + hctsiz.d32 = 0;
- + hctsiz.b.xfersize = 8;
- + hctsiz.b.pktcnt = 1;
- + hctsiz.b.pid = DWC_OTG_HC_PID_DATA1;
- + DWC_WRITE_REG32(&hc_regs->hctsiz, hctsiz.d32);
- +
- + /* Set HCCHAR */
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- + hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL;
- + hcchar.b.epdir = 1;
- + hcchar.b.epnum = 0;
- + hcchar.b.mps = 8;
- + hcchar.b.chen = 1;
- + DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
- +
- + gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
- +
- + /* Wait for receive status queue interrupt */
- + do {
- + gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
- + } while (gintsts.b.rxstsqlvl == 0);
- +
- + /* Read RXSTS */
- + grxsts.d32 = DWC_READ_REG32(&global_regs->grxstsp);
- +
- + /* Clear RXSTSQLVL in GINTSTS */
- + gintsts.d32 = 0;
- + gintsts.b.rxstsqlvl = 1;
- + DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
- +
- + switch (grxsts.b.pktsts) {
- + case DWC_GRXSTS_PKTSTS_IN:
- + /* Read the data into the host buffer */
- + if (grxsts.b.bcnt > 0) {
- + int i;
- + int word_count = (grxsts.b.bcnt + 3) / 4;
- +
- + data_fifo = (uint32_t *) ((char *)global_regs + 0x1000);
- +
- + for (i = 0; i < word_count; i++) {
- + (void)DWC_READ_REG32(data_fifo++);
- + }
- + }
- + break;
- +
- + default:
- + break;
- + }
- +
- + gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
- +
- + /* Wait for receive status queue interrupt */
- + do {
- + gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
- + } while (gintsts.b.rxstsqlvl == 0);
- +
- + /* Read RXSTS */
- + grxsts.d32 = DWC_READ_REG32(&global_regs->grxstsp);
- +
- + /* Clear RXSTSQLVL in GINTSTS */
- + gintsts.d32 = 0;
- + gintsts.b.rxstsqlvl = 1;
- + DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
- +
- + switch (grxsts.b.pktsts) {
- + case DWC_GRXSTS_PKTSTS_IN_XFER_COMP:
- + break;
- +
- + default:
- + break;
- + }
- +
- + gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
- +
- + /* Wait for host channel interrupt */
- + do {
- + gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
- + } while (gintsts.b.hcintr == 0);
- +
- + /* Read HAINT */
- + haint.d32 = DWC_READ_REG32(&hc_global_regs->haint);
- +
- + /* Read HCINT */
- + hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
- +
- + /* Read HCCHAR */
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- +
- + /* Clear HCINT */
- + DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32);
- +
- + /* Clear HAINT */
- + DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32);
- +
- + /* Clear GINTSTS */
- + DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
- +
- + /* Read GINTSTS */
- + gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
- +
- +// usleep(100000);
- +// mdelay(100);
- + dwc_mdelay(1);
- +
- + /*
- + * Send handshake packet
- + */
- +
- + /* Read HAINT */
- + haint.d32 = DWC_READ_REG32(&hc_global_regs->haint);
- +
- + /* Read HCINT */
- + hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
- +
- + /* Read HCCHAR */
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- +
- + /* Clear HCINT */
- + DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32);
- +
- + /* Clear HAINT */
- + DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32);
- +
- + /* Clear GINTSTS */
- + DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
- +
- + /* Read GINTSTS */
- + gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
- +
- + /* Make sure channel is disabled */
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- + if (hcchar.b.chen) {
- + hcchar.b.chdis = 1;
- + hcchar.b.chen = 1;
- + DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
- + //sleep(1);
- + dwc_mdelay(1000);
- +
- + /* Read GINTSTS */
- + gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
- +
- + /* Read HAINT */
- + haint.d32 = DWC_READ_REG32(&hc_global_regs->haint);
- +
- + /* Read HCINT */
- + hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
- +
- + /* Read HCCHAR */
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- +
- + /* Clear HCINT */
- + DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32);
- +
- + /* Clear HAINT */
- + DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32);
- +
- + /* Clear GINTSTS */
- + DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
- +
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- + }
- +
- + /* Set HCTSIZ */
- + hctsiz.d32 = 0;
- + hctsiz.b.xfersize = 0;
- + hctsiz.b.pktcnt = 1;
- + hctsiz.b.pid = DWC_OTG_HC_PID_DATA1;
- + DWC_WRITE_REG32(&hc_regs->hctsiz, hctsiz.d32);
- +
- + /* Set HCCHAR */
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- + hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL;
- + hcchar.b.epdir = 0;
- + hcchar.b.epnum = 0;
- + hcchar.b.mps = 8;
- + hcchar.b.chen = 1;
- + DWC_WRITE_REG32(&hc_regs->hcchar, hcchar.d32);
- +
- + gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
- +
- + /* Wait for host channel interrupt */
- + do {
- + gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
- + } while (gintsts.b.hcintr == 0);
- +
- + /* Disable HCINTs */
- + DWC_WRITE_REG32(&hc_regs->hcintmsk, 0x0000);
- +
- + /* Disable HAINTs */
- + DWC_WRITE_REG32(&hc_global_regs->haintmsk, 0x0000);
- +
- + /* Read HAINT */
- + haint.d32 = DWC_READ_REG32(&hc_global_regs->haint);
- +
- + /* Read HCINT */
- + hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
- +
- + /* Read HCCHAR */
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- +
- + /* Clear HCINT */
- + DWC_WRITE_REG32(&hc_regs->hcint, hcint.d32);
- +
- + /* Clear HAINT */
- + DWC_WRITE_REG32(&hc_global_regs->haint, haint.d32);
- +
- + /* Clear GINTSTS */
- + DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
- +
- + /* Read GINTSTS */
- + gintsts.d32 = DWC_READ_REG32(&global_regs->gintsts);
- +}
- +#endif
- +
- +/** Handles hub class-specific requests. */
- +int dwc_otg_hcd_hub_control(dwc_otg_hcd_t * dwc_otg_hcd,
- + uint16_t typeReq,
- + uint16_t wValue,
- + uint16_t wIndex, uint8_t * buf, uint16_t wLength)
- +{
- + int retval = 0;
- +
- + dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if;
- + usb_hub_descriptor_t *hub_desc;
- + hprt0_data_t hprt0 = {.d32 = 0 };
- +
- + uint32_t port_status;
- +
- + switch (typeReq) {
- + case UCR_CLEAR_HUB_FEATURE:
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
- + "ClearHubFeature 0x%x\n", wValue);
- + switch (wValue) {
- + case UHF_C_HUB_LOCAL_POWER:
- + case UHF_C_HUB_OVER_CURRENT:
- + /* Nothing required here */
- + break;
- + default:
- + retval = -DWC_E_INVALID;
- + DWC_ERROR("DWC OTG HCD - "
- + "ClearHubFeature request %xh unknown\n",
- + wValue);
- + }
- + break;
- + case UCR_CLEAR_PORT_FEATURE:
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- + if (wValue != UHF_PORT_L1)
- +#endif
- + if (!wIndex || wIndex > 1)
- + goto error;
- +
- + switch (wValue) {
- + case UHF_PORT_ENABLE:
- + DWC_DEBUGPL(DBG_ANY, "DWC OTG HCD HUB CONTROL - "
- + "ClearPortFeature USB_PORT_FEAT_ENABLE\n");
- + hprt0.d32 = dwc_otg_read_hprt0(core_if);
- + hprt0.b.prtena = 1;
- + DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
- + break;
- + case UHF_PORT_SUSPEND:
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
- + "ClearPortFeature USB_PORT_FEAT_SUSPEND\n");
- +
- + if (core_if->power_down == 2) {
- + dwc_otg_host_hibernation_restore(core_if, 0, 0);
- + } else {
- + DWC_WRITE_REG32(core_if->pcgcctl, 0);
- + dwc_mdelay(5);
- +
- + hprt0.d32 = dwc_otg_read_hprt0(core_if);
- + hprt0.b.prtres = 1;
- + DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
- + hprt0.b.prtsusp = 0;
- + /* Clear Resume bit */
- + dwc_mdelay(100);
- + hprt0.b.prtres = 0;
- + DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
- + }
- + break;
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- + case UHF_PORT_L1:
- + {
- + pcgcctl_data_t pcgcctl = {.d32 = 0 };
- + glpmcfg_data_t lpmcfg = {.d32 = 0 };
- +
- + lpmcfg.d32 =
- + DWC_READ_REG32(&core_if->
- + core_global_regs->glpmcfg);
- + lpmcfg.b.en_utmi_sleep = 0;
- + lpmcfg.b.hird_thres &= (~(1 << 4));
- + lpmcfg.b.prt_sleep_sts = 1;
- + DWC_WRITE_REG32(&core_if->
- + core_global_regs->glpmcfg,
- + lpmcfg.d32);
- +
- + /* Clear Enbl_L1Gating bit. */
- + pcgcctl.b.enbl_sleep_gating = 1;
- + DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32,
- + 0);
- +
- + dwc_mdelay(5);
- +
- + hprt0.d32 = dwc_otg_read_hprt0(core_if);
- + hprt0.b.prtres = 1;
- + DWC_WRITE_REG32(core_if->host_if->hprt0,
- + hprt0.d32);
- + /* This bit will be cleared in wakeup interrupt handle */
- + break;
- + }
- +#endif
- + case UHF_PORT_POWER:
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
- + "ClearPortFeature USB_PORT_FEAT_POWER\n");
- + hprt0.d32 = dwc_otg_read_hprt0(core_if);
- + hprt0.b.prtpwr = 0;
- + DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
- + break;
- + case UHF_PORT_INDICATOR:
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
- + "ClearPortFeature USB_PORT_FEAT_INDICATOR\n");
- + /* Port inidicator not supported */
- + break;
- + case UHF_C_PORT_CONNECTION:
- + /* Clears drivers internal connect status change
- + * flag */
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
- + "ClearPortFeature USB_PORT_FEAT_C_CONNECTION\n");
- + dwc_otg_hcd->flags.b.port_connect_status_change = 0;
- + break;
- + case UHF_C_PORT_RESET:
- + /* Clears the driver's internal Port Reset Change
- + * flag */
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
- + "ClearPortFeature USB_PORT_FEAT_C_RESET\n");
- + dwc_otg_hcd->flags.b.port_reset_change = 0;
- + break;
- + case UHF_C_PORT_ENABLE:
- + /* Clears the driver's internal Port
- + * Enable/Disable Change flag */
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
- + "ClearPortFeature USB_PORT_FEAT_C_ENABLE\n");
- + dwc_otg_hcd->flags.b.port_enable_change = 0;
- + break;
- + case UHF_C_PORT_SUSPEND:
- + /* Clears the driver's internal Port Suspend
- + * Change flag, which is set when resume signaling on
- + * the host port is complete */
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
- + "ClearPortFeature USB_PORT_FEAT_C_SUSPEND\n");
- + dwc_otg_hcd->flags.b.port_suspend_change = 0;
- + break;
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- + case UHF_C_PORT_L1:
- + dwc_otg_hcd->flags.b.port_l1_change = 0;
- + break;
- +#endif
- + case UHF_C_PORT_OVER_CURRENT:
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
- + "ClearPortFeature USB_PORT_FEAT_C_OVER_CURRENT\n");
- + dwc_otg_hcd->flags.b.port_over_current_change = 0;
- + break;
- + default:
- + retval = -DWC_E_INVALID;
- + DWC_ERROR("DWC OTG HCD - "
- + "ClearPortFeature request %xh "
- + "unknown or unsupported\n", wValue);
- + }
- + break;
- + case UCR_GET_HUB_DESCRIPTOR:
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
- + "GetHubDescriptor\n");
- + hub_desc = (usb_hub_descriptor_t *) buf;
- + hub_desc->bDescLength = 9;
- + hub_desc->bDescriptorType = 0x29;
- + hub_desc->bNbrPorts = 1;
- + USETW(hub_desc->wHubCharacteristics, 0x08);
- + hub_desc->bPwrOn2PwrGood = 1;
- + hub_desc->bHubContrCurrent = 0;
- + hub_desc->DeviceRemovable[0] = 0;
- + hub_desc->DeviceRemovable[1] = 0xff;
- + break;
- + case UCR_GET_HUB_STATUS:
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
- + "GetHubStatus\n");
- + DWC_MEMSET(buf, 0, 4);
- + break;
- + case UCR_GET_PORT_STATUS:
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
- + "GetPortStatus wIndex = 0x%04x FLAGS=0x%08x\n",
- + wIndex, dwc_otg_hcd->flags.d32);
- + if (!wIndex || wIndex > 1)
- + goto error;
- +
- + port_status = 0;
- +
- + if (dwc_otg_hcd->flags.b.port_connect_status_change)
- + port_status |= (1 << UHF_C_PORT_CONNECTION);
- +
- + if (dwc_otg_hcd->flags.b.port_enable_change)
- + port_status |= (1 << UHF_C_PORT_ENABLE);
- +
- + if (dwc_otg_hcd->flags.b.port_suspend_change)
- + port_status |= (1 << UHF_C_PORT_SUSPEND);
- +
- + if (dwc_otg_hcd->flags.b.port_l1_change)
- + port_status |= (1 << UHF_C_PORT_L1);
- +
- + if (dwc_otg_hcd->flags.b.port_reset_change) {
- + port_status |= (1 << UHF_C_PORT_RESET);
- + }
- +
- + if (dwc_otg_hcd->flags.b.port_over_current_change) {
- + DWC_WARN("Overcurrent change detected\n");
- + port_status |= (1 << UHF_C_PORT_OVER_CURRENT);
- + }
- +
- + if (!dwc_otg_hcd->flags.b.port_connect_status) {
- + /*
- + * The port is disconnected, which means the core is
- + * either in device mode or it soon will be. Just
- + * return 0's for the remainder of the port status
- + * since the port register can't be read if the core
- + * is in device mode.
- + */
- + *((__le32 *) buf) = dwc_cpu_to_le32(&port_status);
- + break;
- + }
- +
- + hprt0.d32 = DWC_READ_REG32(core_if->host_if->hprt0);
- + DWC_DEBUGPL(DBG_HCDV, " HPRT0: 0x%08x\n", hprt0.d32);
- +
- + if (hprt0.b.prtconnsts)
- + port_status |= (1 << UHF_PORT_CONNECTION);
- +
- + if (hprt0.b.prtena)
- + port_status |= (1 << UHF_PORT_ENABLE);
- +
- + if (hprt0.b.prtsusp)
- + port_status |= (1 << UHF_PORT_SUSPEND);
- +
- + if (hprt0.b.prtovrcurract)
- + port_status |= (1 << UHF_PORT_OVER_CURRENT);
- +
- + if (hprt0.b.prtrst)
- + port_status |= (1 << UHF_PORT_RESET);
- +
- + if (hprt0.b.prtpwr)
- + port_status |= (1 << UHF_PORT_POWER);
- +
- + if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED)
- + port_status |= (1 << UHF_PORT_HIGH_SPEED);
- + else if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_LOW_SPEED)
- + port_status |= (1 << UHF_PORT_LOW_SPEED);
- +
- + if (hprt0.b.prttstctl)
- + port_status |= (1 << UHF_PORT_TEST);
- + if (dwc_otg_get_lpm_portsleepstatus(dwc_otg_hcd->core_if)) {
- + port_status |= (1 << UHF_PORT_L1);
- + }
- + /*
- + For Synopsys HW emulation of Power down wkup_control asserts the
- + hreset_n and prst_n on suspned. This causes the HPRT0 to be zero.
- + We intentionally tell the software that port is in L2Suspend state.
- + Only for STE.
- + */
- + if ((core_if->power_down == 2)
- + && (core_if->hibernation_suspend == 1)) {
- + port_status |= (1 << UHF_PORT_SUSPEND);
- + }
- + /* USB_PORT_FEAT_INDICATOR unsupported always 0 */
- +
- + *((__le32 *) buf) = dwc_cpu_to_le32(&port_status);
- +
- + break;
- + case UCR_SET_HUB_FEATURE:
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
- + "SetHubFeature\n");
- + /* No HUB features supported */
- + break;
- + case UCR_SET_PORT_FEATURE:
- + if (wValue != UHF_PORT_TEST && (!wIndex || wIndex > 1))
- + goto error;
- +
- + if (!dwc_otg_hcd->flags.b.port_connect_status) {
- + /*
- + * The port is disconnected, which means the core is
- + * either in device mode or it soon will be. Just
- + * return without doing anything since the port
- + * register can't be written if the core is in device
- + * mode.
- + */
- + break;
- + }
- +
- + switch (wValue) {
- + case UHF_PORT_SUSPEND:
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
- + "SetPortFeature - USB_PORT_FEAT_SUSPEND\n");
- + if (dwc_otg_hcd_otg_port(dwc_otg_hcd) != wIndex) {
- + goto error;
- + }
- + if (core_if->power_down == 2) {
- + int timeout = 300;
- + dwc_irqflags_t flags;
- + pcgcctl_data_t pcgcctl = {.d32 = 0 };
- + gpwrdn_data_t gpwrdn = {.d32 = 0 };
- + gusbcfg_data_t gusbcfg = {.d32 = 0 };
- +#ifdef DWC_DEV_SRPCAP
- + int32_t otg_cap_param = core_if->core_params->otg_cap;
- +#endif
- + DWC_PRINTF("Preparing for complete power-off\n");
- +
- + /* Save registers before hibernation */
- + dwc_otg_save_global_regs(core_if);
- + dwc_otg_save_host_regs(core_if);
- +
- + hprt0.d32 = dwc_otg_read_hprt0(core_if);
- + hprt0.b.prtsusp = 1;
- + hprt0.b.prtena = 0;
- + DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
- + /* Spin hprt0.b.prtsusp to became 1 */
- + do {
- + hprt0.d32 = dwc_otg_read_hprt0(core_if);
- + if (hprt0.b.prtsusp) {
- + break;
- + }
- + dwc_mdelay(1);
- + } while (--timeout);
- + if (!timeout) {
- + DWC_WARN("Suspend wasn't genereted\n");
- + }
- + dwc_udelay(10);
- +
- + /*
- + * We need to disable interrupts to prevent servicing of any IRQ
- + * during going to hibernation
- + */
- + DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &flags);
- + core_if->lx_state = DWC_OTG_L2;
- +#ifdef DWC_DEV_SRPCAP
- + hprt0.d32 = dwc_otg_read_hprt0(core_if);
- + hprt0.b.prtpwr = 0;
- + hprt0.b.prtena = 0;
- + DWC_WRITE_REG32(core_if->host_if->hprt0,
- + hprt0.d32);
- +#endif
- + gusbcfg.d32 =
- + DWC_READ_REG32(&core_if->core_global_regs->
- + gusbcfg);
- + if (gusbcfg.b.ulpi_utmi_sel == 1) {
- + /* ULPI interface */
- + /* Suspend the Phy Clock */
- + pcgcctl.d32 = 0;
- + pcgcctl.b.stoppclk = 1;
- + DWC_MODIFY_REG32(core_if->pcgcctl, 0,
- + pcgcctl.d32);
- + dwc_udelay(10);
- + gpwrdn.b.pmuactv = 1;
- + DWC_MODIFY_REG32(&core_if->
- + core_global_regs->
- + gpwrdn, 0, gpwrdn.d32);
- + } else {
- + /* UTMI+ Interface */
- + gpwrdn.b.pmuactv = 1;
- + DWC_MODIFY_REG32(&core_if->
- + core_global_regs->
- + gpwrdn, 0, gpwrdn.d32);
- + dwc_udelay(10);
- + pcgcctl.b.stoppclk = 1;
- + DWC_MODIFY_REG32(core_if->pcgcctl, 0, pcgcctl.d32);
- + dwc_udelay(10);
- + }
- +#ifdef DWC_DEV_SRPCAP
- + gpwrdn.d32 = 0;
- + gpwrdn.b.dis_vbus = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->
- + gpwrdn, 0, gpwrdn.d32);
- +#endif
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pmuintsel = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->
- + gpwrdn, 0, gpwrdn.d32);
- + dwc_udelay(10);
- +
- + gpwrdn.d32 = 0;
- +#ifdef DWC_DEV_SRPCAP
- + gpwrdn.b.srp_det_msk = 1;
- +#endif
- + gpwrdn.b.disconn_det_msk = 1;
- + gpwrdn.b.lnstchng_msk = 1;
- + gpwrdn.b.sts_chngint_msk = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->
- + gpwrdn, 0, gpwrdn.d32);
- + dwc_udelay(10);
- +
- + /* Enable Power Down Clamp and all interrupts in GPWRDN */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pwrdnclmp = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->
- + gpwrdn, 0, gpwrdn.d32);
- + dwc_udelay(10);
- +
- + /* Switch off VDD */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pwrdnswtch = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->
- + gpwrdn, 0, gpwrdn.d32);
- +
- +#ifdef DWC_DEV_SRPCAP
- + if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE)
- + {
- + core_if->pwron_timer_started = 1;
- + DWC_TIMER_SCHEDULE(core_if->pwron_timer, 6000 /* 6 secs */ );
- + }
- +#endif
- + /* Save gpwrdn register for further usage if stschng interrupt */
- + core_if->gr_backup->gpwrdn_local =
- + DWC_READ_REG32(&core_if->core_global_regs->gpwrdn);
- +
- + /* Set flag to indicate that we are in hibernation */
- + core_if->hibernation_suspend = 1;
- + DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock,flags);
- +
- + DWC_PRINTF("Host hibernation completed\n");
- + // Exit from case statement
- + break;
- +
- + }
- + if (dwc_otg_hcd_otg_port(dwc_otg_hcd) == wIndex &&
- + dwc_otg_hcd->fops->get_b_hnp_enable(dwc_otg_hcd)) {
- + gotgctl_data_t gotgctl = {.d32 = 0 };
- + gotgctl.b.hstsethnpen = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->
- + gotgctl, 0, gotgctl.d32);
- + core_if->op_state = A_SUSPEND;
- + }
- + hprt0.d32 = dwc_otg_read_hprt0(core_if);
- + hprt0.b.prtsusp = 1;
- + DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
- + {
- + dwc_irqflags_t flags;
- + /* Update lx_state */
- + DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &flags);
- + core_if->lx_state = DWC_OTG_L2;
- + DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, flags);
- + }
- + /* Suspend the Phy Clock */
- + {
- + pcgcctl_data_t pcgcctl = {.d32 = 0 };
- + pcgcctl.b.stoppclk = 1;
- + DWC_MODIFY_REG32(core_if->pcgcctl, 0,
- + pcgcctl.d32);
- + dwc_udelay(10);
- + }
- +
- + /* For HNP the bus must be suspended for at least 200ms. */
- + if (dwc_otg_hcd->fops->get_b_hnp_enable(dwc_otg_hcd)) {
- + pcgcctl_data_t pcgcctl = {.d32 = 0 };
- + pcgcctl.b.stoppclk = 1;
- + DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0);
- + dwc_mdelay(200);
- + }
- +
- + /** @todo - check how sw can wait for 1 sec to check asesvld??? */
- +#if 0 //vahrama !!!!!!!!!!!!!!!!!!
- + if (core_if->adp_enable) {
- + gotgctl_data_t gotgctl = {.d32 = 0 };
- + gpwrdn_data_t gpwrdn;
- +
- + while (gotgctl.b.asesvld == 1) {
- + gotgctl.d32 =
- + DWC_READ_REG32(&core_if->
- + core_global_regs->
- + gotgctl);
- + dwc_mdelay(100);
- + }
- +
- + /* Enable Power Down Logic */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pmuactv = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->
- + gpwrdn, 0, gpwrdn.d32);
- +
- + /* Unmask SRP detected interrupt from Power Down Logic */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.srp_det_msk = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->
- + gpwrdn, 0, gpwrdn.d32);
- +
- + dwc_otg_adp_probe_start(core_if);
- + }
- +#endif
- + break;
- + case UHF_PORT_POWER:
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
- + "SetPortFeature - USB_PORT_FEAT_POWER\n");
- + hprt0.d32 = dwc_otg_read_hprt0(core_if);
- + hprt0.b.prtpwr = 1;
- + DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
- + break;
- + case UHF_PORT_RESET:
- + if ((core_if->power_down == 2)
- + && (core_if->hibernation_suspend == 1)) {
- + /* If we are going to exit from Hibernated
- + * state via USB RESET.
- + */
- + dwc_otg_host_hibernation_restore(core_if, 0, 1);
- + } else {
- + hprt0.d32 = dwc_otg_read_hprt0(core_if);
- +
- + DWC_DEBUGPL(DBG_HCD,
- + "DWC OTG HCD HUB CONTROL - "
- + "SetPortFeature - USB_PORT_FEAT_RESET\n");
- + {
- + pcgcctl_data_t pcgcctl = {.d32 = 0 };
- + pcgcctl.b.enbl_sleep_gating = 1;
- + pcgcctl.b.stoppclk = 1;
- + DWC_MODIFY_REG32(core_if->pcgcctl, pcgcctl.d32, 0);
- + DWC_WRITE_REG32(core_if->pcgcctl, 0);
- + }
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- + {
- + glpmcfg_data_t lpmcfg;
- + lpmcfg.d32 =
- + DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
- + if (lpmcfg.b.prt_sleep_sts) {
- + lpmcfg.b.en_utmi_sleep = 0;
- + lpmcfg.b.hird_thres &= (~(1 << 4));
- + DWC_WRITE_REG32
- + (&core_if->core_global_regs->glpmcfg,
- + lpmcfg.d32);
- + dwc_mdelay(1);
- + }
- + }
- +#endif
- + hprt0.d32 = dwc_otg_read_hprt0(core_if);
- + /* Clear suspend bit if resetting from suspended state. */
- + hprt0.b.prtsusp = 0;
- + /* When B-Host the Port reset bit is set in
- + * the Start HCD Callback function, so that
- + * the reset is started within 1ms of the HNP
- + * success interrupt. */
- + if (!dwc_otg_hcd_is_b_host(dwc_otg_hcd)) {
- + hprt0.b.prtpwr = 1;
- + hprt0.b.prtrst = 1;
- + DWC_PRINTF("Indeed it is in host mode hprt0 = %08x\n",hprt0.d32);
- + DWC_WRITE_REG32(core_if->host_if->hprt0,
- + hprt0.d32);
- + }
- + /* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */
- + dwc_mdelay(60);
- + hprt0.b.prtrst = 0;
- + DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
- + core_if->lx_state = DWC_OTG_L0; /* Now back to the on state */
- + }
- + break;
- +#ifdef DWC_HS_ELECT_TST
- + case UHF_PORT_TEST:
- + {
- + uint32_t t;
- + gintmsk_data_t gintmsk;
- +
- + t = (wIndex >> 8); /* MSB wIndex USB */
- + DWC_DEBUGPL(DBG_HCD,
- + "DWC OTG HCD HUB CONTROL - "
- + "SetPortFeature - USB_PORT_FEAT_TEST %d\n",
- + t);
- + DWC_WARN("USB_PORT_FEAT_TEST %d\n", t);
- + if (t < 6) {
- + hprt0.d32 = dwc_otg_read_hprt0(core_if);
- + hprt0.b.prttstctl = t;
- + DWC_WRITE_REG32(core_if->host_if->hprt0,
- + hprt0.d32);
- + } else {
- + /* Setup global vars with reg addresses (quick and
- + * dirty hack, should be cleaned up)
- + */
- + global_regs = core_if->core_global_regs;
- + hc_global_regs =
- + core_if->host_if->host_global_regs;
- + hc_regs =
- + (dwc_otg_hc_regs_t *) ((char *)
- + global_regs +
- + 0x500);
- + data_fifo =
- + (uint32_t *) ((char *)global_regs +
- + 0x1000);
- +
- + if (t == 6) { /* HS_HOST_PORT_SUSPEND_RESUME */
- + /* Save current interrupt mask */
- + gintmsk.d32 =
- + DWC_READ_REG32
- + (&global_regs->gintmsk);
- +
- + /* Disable all interrupts while we muck with
- + * the hardware directly
- + */
- + DWC_WRITE_REG32(&global_regs->gintmsk, 0);
- +
- + /* 15 second delay per the test spec */
- + dwc_mdelay(15000);
- +
- + /* Drive suspend on the root port */
- + hprt0.d32 =
- + dwc_otg_read_hprt0(core_if);
- + hprt0.b.prtsusp = 1;
- + hprt0.b.prtres = 0;
- + DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
- +
- + /* 15 second delay per the test spec */
- + dwc_mdelay(15000);
- +
- + /* Drive resume on the root port */
- + hprt0.d32 =
- + dwc_otg_read_hprt0(core_if);
- + hprt0.b.prtsusp = 0;
- + hprt0.b.prtres = 1;
- + DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
- + dwc_mdelay(100);
- +
- + /* Clear the resume bit */
- + hprt0.b.prtres = 0;
- + DWC_WRITE_REG32(core_if->host_if->hprt0, hprt0.d32);
- +
- + /* Restore interrupts */
- + DWC_WRITE_REG32(&global_regs->gintmsk, gintmsk.d32);
- + } else if (t == 7) { /* SINGLE_STEP_GET_DEVICE_DESCRIPTOR setup */
- + /* Save current interrupt mask */
- + gintmsk.d32 =
- + DWC_READ_REG32
- + (&global_regs->gintmsk);
- +
- + /* Disable all interrupts while we muck with
- + * the hardware directly
- + */
- + DWC_WRITE_REG32(&global_regs->gintmsk, 0);
- +
- + /* 15 second delay per the test spec */
- + dwc_mdelay(15000);
- +
- + /* Send the Setup packet */
- + do_setup();
- +
- + /* 15 second delay so nothing else happens for awhile */
- + dwc_mdelay(15000);
- +
- + /* Restore interrupts */
- + DWC_WRITE_REG32(&global_regs->gintmsk, gintmsk.d32);
- + } else if (t == 8) { /* SINGLE_STEP_GET_DEVICE_DESCRIPTOR execute */
- + /* Save current interrupt mask */
- + gintmsk.d32 =
- + DWC_READ_REG32
- + (&global_regs->gintmsk);
- +
- + /* Disable all interrupts while we muck with
- + * the hardware directly
- + */
- + DWC_WRITE_REG32(&global_regs->gintmsk, 0);
- +
- + /* Send the Setup packet */
- + do_setup();
- +
- + /* 15 second delay so nothing else happens for awhile */
- + dwc_mdelay(15000);
- +
- + /* Send the In and Ack packets */
- + do_in_ack();
- +
- + /* 15 second delay so nothing else happens for awhile */
- + dwc_mdelay(15000);
- +
- + /* Restore interrupts */
- + DWC_WRITE_REG32(&global_regs->gintmsk, gintmsk.d32);
- + }
- + }
- + break;
- + }
- +#endif /* DWC_HS_ELECT_TST */
- +
- + case UHF_PORT_INDICATOR:
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - "
- + "SetPortFeature - USB_PORT_FEAT_INDICATOR\n");
- + /* Not supported */
- + break;
- + default:
- + retval = -DWC_E_INVALID;
- + DWC_ERROR("DWC OTG HCD - "
- + "SetPortFeature request %xh "
- + "unknown or unsupported\n", wValue);
- + break;
- + }
- + break;
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- + case UCR_SET_AND_TEST_PORT_FEATURE:
- + if (wValue != UHF_PORT_L1) {
- + goto error;
- + }
- + {
- + int portnum, hird, devaddr, remwake;
- + glpmcfg_data_t lpmcfg;
- + uint32_t time_usecs;
- + gintsts_data_t gintsts;
- + gintmsk_data_t gintmsk;
- +
- + if (!dwc_otg_get_param_lpm_enable(core_if)) {
- + goto error;
- + }
- + if (wValue != UHF_PORT_L1 || wLength != 1) {
- + goto error;
- + }
- + /* Check if the port currently is in SLEEP state */
- + lpmcfg.d32 =
- + DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
- + if (lpmcfg.b.prt_sleep_sts) {
- + DWC_INFO("Port is already in sleep mode\n");
- + buf[0] = 0; /* Return success */
- + break;
- + }
- +
- + portnum = wIndex & 0xf;
- + hird = (wIndex >> 4) & 0xf;
- + devaddr = (wIndex >> 8) & 0x7f;
- + remwake = (wIndex >> 15);
- +
- + if (portnum != 1) {
- + retval = -DWC_E_INVALID;
- + DWC_WARN
- + ("Wrong port number(%d) in SetandTestPortFeature request\n",
- + portnum);
- + break;
- + }
- +
- + DWC_PRINTF
- + ("SetandTestPortFeature request: portnum = %d, hird = %d, devaddr = %d, rewake = %d\n",
- + portnum, hird, devaddr, remwake);
- + /* Disable LPM interrupt */
- + gintmsk.d32 = 0;
- + gintmsk.b.lpmtranrcvd = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk,
- + gintmsk.d32, 0);
- +
- + if (dwc_otg_hcd_send_lpm
- + (dwc_otg_hcd, devaddr, hird, remwake)) {
- + retval = -DWC_E_INVALID;
- + break;
- + }
- +
- + time_usecs = 10 * (lpmcfg.b.retry_count + 1);
- + /* We will consider timeout if time_usecs microseconds pass,
- + * and we don't receive LPM transaction status.
- + * After receiving non-error responce(ACK/NYET/STALL) from device,
- + * core will set lpmtranrcvd bit.
- + */
- + do {
- + gintsts.d32 =
- + DWC_READ_REG32(&core_if->core_global_regs->gintsts);
- + if (gintsts.b.lpmtranrcvd) {
- + break;
- + }
- + dwc_udelay(1);
- + } while (--time_usecs);
- + /* lpm_int bit will be cleared in LPM interrupt handler */
- +
- + /* Now fill status
- + * 0x00 - Success
- + * 0x10 - NYET
- + * 0x11 - Timeout
- + */
- + if (!gintsts.b.lpmtranrcvd) {
- + buf[0] = 0x3; /* Completion code is Timeout */
- + dwc_otg_hcd_free_hc_from_lpm(dwc_otg_hcd);
- + } else {
- + lpmcfg.d32 =
- + DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
- + if (lpmcfg.b.lpm_resp == 0x3) {
- + /* ACK responce from the device */
- + buf[0] = 0x00; /* Success */
- + } else if (lpmcfg.b.lpm_resp == 0x2) {
- + /* NYET responce from the device */
- + buf[0] = 0x2;
- + } else {
- + /* Otherwise responce with Timeout */
- + buf[0] = 0x3;
- + }
- + }
- + DWC_PRINTF("Device responce to LPM trans is %x\n",
- + lpmcfg.b.lpm_resp);
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0,
- + gintmsk.d32);
- +
- + break;
- + }
- +#endif /* CONFIG_USB_DWC_OTG_LPM */
- + default:
- +error:
- + retval = -DWC_E_INVALID;
- + DWC_WARN("DWC OTG HCD - "
- + "Unknown hub control request type or invalid typeReq: %xh wIndex: %xh wValue: %xh\n",
- + typeReq, wIndex, wValue);
- + break;
- + }
- +
- + return retval;
- +}
- +
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- +/** Returns index of host channel to perform LPM transaction. */
- +int dwc_otg_hcd_get_hc_for_lpm_tran(dwc_otg_hcd_t * hcd, uint8_t devaddr)
- +{
- + dwc_otg_core_if_t *core_if = hcd->core_if;
- + dwc_hc_t *hc;
- + hcchar_data_t hcchar;
- + gintmsk_data_t gintmsk = {.d32 = 0 };
- +
- + if (DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) {
- + DWC_PRINTF("No free channel to select for LPM transaction\n");
- + return -1;
- + }
- +
- + hc = DWC_CIRCLEQ_FIRST(&hcd->free_hc_list);
- +
- + /* Mask host channel interrupts. */
- + gintmsk.b.hcintr = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, gintmsk.d32, 0);
- +
- + /* Fill fields that core needs for LPM transaction */
- + hcchar.b.devaddr = devaddr;
- + hcchar.b.epnum = 0;
- + hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL;
- + hcchar.b.mps = 64;
- + hcchar.b.lspddev = (hc->speed == DWC_OTG_EP_SPEED_LOW);
- + hcchar.b.epdir = 0; /* OUT */
- + DWC_WRITE_REG32(&core_if->host_if->hc_regs[hc->hc_num]->hcchar,
- + hcchar.d32);
- +
- + /* Remove the host channel from the free list. */
- + DWC_CIRCLEQ_REMOVE_INIT(&hcd->free_hc_list, hc, hc_list_entry);
- +
- + DWC_PRINTF("hcnum = %d devaddr = %d\n", hc->hc_num, devaddr);
- +
- + return hc->hc_num;
- +}
- +
- +/** Release hc after performing LPM transaction */
- +void dwc_otg_hcd_free_hc_from_lpm(dwc_otg_hcd_t * hcd)
- +{
- + dwc_hc_t *hc;
- + glpmcfg_data_t lpmcfg;
- + uint8_t hc_num;
- +
- + lpmcfg.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->glpmcfg);
- + hc_num = lpmcfg.b.lpm_chan_index;
- +
- + hc = hcd->hc_ptr_array[hc_num];
- +
- + DWC_PRINTF("Freeing channel %d after LPM\n", hc_num);
- + /* Return host channel to free list */
- + DWC_CIRCLEQ_INSERT_TAIL(&hcd->free_hc_list, hc, hc_list_entry);
- +}
- +
- +int dwc_otg_hcd_send_lpm(dwc_otg_hcd_t * hcd, uint8_t devaddr, uint8_t hird,
- + uint8_t bRemoteWake)
- +{
- + glpmcfg_data_t lpmcfg;
- + pcgcctl_data_t pcgcctl = {.d32 = 0 };
- + int channel;
- +
- + channel = dwc_otg_hcd_get_hc_for_lpm_tran(hcd, devaddr);
- + if (channel < 0) {
- + return channel;
- + }
- +
- + pcgcctl.b.enbl_sleep_gating = 1;
- + DWC_MODIFY_REG32(hcd->core_if->pcgcctl, 0, pcgcctl.d32);
- +
- + /* Read LPM config register */
- + lpmcfg.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->glpmcfg);
- +
- + /* Program LPM transaction fields */
- + lpmcfg.b.rem_wkup_en = bRemoteWake;
- + lpmcfg.b.hird = hird;
- + lpmcfg.b.hird_thres = 0x1c;
- + lpmcfg.b.lpm_chan_index = channel;
- + lpmcfg.b.en_utmi_sleep = 1;
- + /* Program LPM config register */
- + DWC_WRITE_REG32(&hcd->core_if->core_global_regs->glpmcfg, lpmcfg.d32);
- +
- + /* Send LPM transaction */
- + lpmcfg.b.send_lpm = 1;
- + DWC_WRITE_REG32(&hcd->core_if->core_global_regs->glpmcfg, lpmcfg.d32);
- +
- + return 0;
- +}
- +
- +#endif /* CONFIG_USB_DWC_OTG_LPM */
- +
- +int dwc_otg_hcd_is_status_changed(dwc_otg_hcd_t * hcd, int port)
- +{
- + int retval;
- +
- + if (port != 1) {
- + return -DWC_E_INVALID;
- + }
- +
- + retval = (hcd->flags.b.port_connect_status_change ||
- + hcd->flags.b.port_reset_change ||
- + hcd->flags.b.port_enable_change ||
- + hcd->flags.b.port_suspend_change ||
- + hcd->flags.b.port_over_current_change);
- +#ifdef DEBUG
- + if (retval) {
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB STATUS DATA:"
- + " Root port status changed\n");
- + DWC_DEBUGPL(DBG_HCDV, " port_connect_status_change: %d\n",
- + hcd->flags.b.port_connect_status_change);
- + DWC_DEBUGPL(DBG_HCDV, " port_reset_change: %d\n",
- + hcd->flags.b.port_reset_change);
- + DWC_DEBUGPL(DBG_HCDV, " port_enable_change: %d\n",
- + hcd->flags.b.port_enable_change);
- + DWC_DEBUGPL(DBG_HCDV, " port_suspend_change: %d\n",
- + hcd->flags.b.port_suspend_change);
- + DWC_DEBUGPL(DBG_HCDV, " port_over_current_change: %d\n",
- + hcd->flags.b.port_over_current_change);
- + }
- +#endif
- + return retval;
- +}
- +
- +int dwc_otg_hcd_get_frame_number(dwc_otg_hcd_t * dwc_otg_hcd)
- +{
- + hfnum_data_t hfnum;
- + hfnum.d32 =
- + DWC_READ_REG32(&dwc_otg_hcd->core_if->host_if->host_global_regs->
- + hfnum);
- +
- +#ifdef DEBUG_SOF
- + DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD GET FRAME NUMBER %d\n",
- + hfnum.b.frnum);
- +#endif
- + return hfnum.b.frnum;
- +}
- +
- +int dwc_otg_hcd_start(dwc_otg_hcd_t * hcd,
- + struct dwc_otg_hcd_function_ops *fops)
- +{
- + int retval = 0;
- +
- + hcd->fops = fops;
- + if (!dwc_otg_is_device_mode(hcd->core_if) &&
- + (!hcd->core_if->adp_enable || hcd->core_if->adp.adp_started)) {
- + dwc_otg_hcd_reinit(hcd);
- + } else {
- + retval = -DWC_E_NO_DEVICE;
- + }
- +
- + return retval;
- +}
- +
- +void *dwc_otg_hcd_get_priv_data(dwc_otg_hcd_t * hcd)
- +{
- + return hcd->priv;
- +}
- +
- +void dwc_otg_hcd_set_priv_data(dwc_otg_hcd_t * hcd, void *priv_data)
- +{
- + hcd->priv = priv_data;
- +}
- +
- +uint32_t dwc_otg_hcd_otg_port(dwc_otg_hcd_t * hcd)
- +{
- + return hcd->otg_port;
- +}
- +
- +uint32_t dwc_otg_hcd_is_b_host(dwc_otg_hcd_t * hcd)
- +{
- + uint32_t is_b_host;
- + if (hcd->core_if->op_state == B_HOST) {
- + is_b_host = 1;
- + } else {
- + is_b_host = 0;
- + }
- +
- + return is_b_host;
- +}
- +
- +dwc_otg_hcd_urb_t *dwc_otg_hcd_urb_alloc(dwc_otg_hcd_t * hcd,
- + int iso_desc_count, int atomic_alloc)
- +{
- + dwc_otg_hcd_urb_t *dwc_otg_urb;
- + uint32_t size;
- +
- + size =
- + sizeof(*dwc_otg_urb) +
- + iso_desc_count * sizeof(struct dwc_otg_hcd_iso_packet_desc);
- + if (atomic_alloc)
- + dwc_otg_urb = DWC_ALLOC_ATOMIC(size);
- + else
- + dwc_otg_urb = DWC_ALLOC(size);
- +
- + if (dwc_otg_urb)
- + dwc_otg_urb->packet_count = iso_desc_count;
- + else {
- + DWC_ERROR("**** DWC OTG HCD URB alloc - "
- + "%salloc of %db failed\n",
- + atomic_alloc?"atomic ":"", size);
- + }
- + return dwc_otg_urb;
- +}
- +
- +void dwc_otg_hcd_urb_set_pipeinfo(dwc_otg_hcd_urb_t * dwc_otg_urb,
- + uint8_t dev_addr, uint8_t ep_num,
- + uint8_t ep_type, uint8_t ep_dir, uint16_t mps)
- +{
- + dwc_otg_hcd_fill_pipe(&dwc_otg_urb->pipe_info, dev_addr, ep_num,
- + ep_type, ep_dir, mps);
- +#if 0
- + DWC_PRINTF
- + ("addr = %d, ep_num = %d, ep_dir = 0x%x, ep_type = 0x%x, mps = %d\n",
- + dev_addr, ep_num, ep_dir, ep_type, mps);
- +#endif
- +}
- +
- +void dwc_otg_hcd_urb_set_params(dwc_otg_hcd_urb_t * dwc_otg_urb,
- + void *urb_handle, void *buf, dwc_dma_t dma,
- + uint32_t buflen, void *setup_packet,
- + dwc_dma_t setup_dma, uint32_t flags,
- + uint16_t interval)
- +{
- + dwc_otg_urb->priv = urb_handle;
- + dwc_otg_urb->buf = buf;
- + dwc_otg_urb->dma = dma;
- + dwc_otg_urb->length = buflen;
- + dwc_otg_urb->setup_packet = setup_packet;
- + dwc_otg_urb->setup_dma = setup_dma;
- + dwc_otg_urb->flags = flags;
- + dwc_otg_urb->interval = interval;
- + dwc_otg_urb->status = -DWC_E_IN_PROGRESS;
- +}
- +
- +uint32_t dwc_otg_hcd_urb_get_status(dwc_otg_hcd_urb_t * dwc_otg_urb)
- +{
- + return dwc_otg_urb->status;
- +}
- +
- +uint32_t dwc_otg_hcd_urb_get_actual_length(dwc_otg_hcd_urb_t * dwc_otg_urb)
- +{
- + return dwc_otg_urb->actual_length;
- +}
- +
- +uint32_t dwc_otg_hcd_urb_get_error_count(dwc_otg_hcd_urb_t * dwc_otg_urb)
- +{
- + return dwc_otg_urb->error_count;
- +}
- +
- +void dwc_otg_hcd_urb_set_iso_desc_params(dwc_otg_hcd_urb_t * dwc_otg_urb,
- + int desc_num, uint32_t offset,
- + uint32_t length)
- +{
- + dwc_otg_urb->iso_descs[desc_num].offset = offset;
- + dwc_otg_urb->iso_descs[desc_num].length = length;
- +}
- +
- +uint32_t dwc_otg_hcd_urb_get_iso_desc_status(dwc_otg_hcd_urb_t * dwc_otg_urb,
- + int desc_num)
- +{
- + return dwc_otg_urb->iso_descs[desc_num].status;
- +}
- +
- +uint32_t dwc_otg_hcd_urb_get_iso_desc_actual_length(dwc_otg_hcd_urb_t *
- + dwc_otg_urb, int desc_num)
- +{
- + return dwc_otg_urb->iso_descs[desc_num].actual_length;
- +}
- +
- +int dwc_otg_hcd_is_bandwidth_allocated(dwc_otg_hcd_t * hcd, void *ep_handle)
- +{
- + int allocated = 0;
- + dwc_otg_qh_t *qh = (dwc_otg_qh_t *) ep_handle;
- +
- + if (qh) {
- + if (!DWC_LIST_EMPTY(&qh->qh_list_entry)) {
- + allocated = 1;
- + }
- + }
- + return allocated;
- +}
- +
- +int dwc_otg_hcd_is_bandwidth_freed(dwc_otg_hcd_t * hcd, void *ep_handle)
- +{
- + dwc_otg_qh_t *qh = (dwc_otg_qh_t *) ep_handle;
- + int freed = 0;
- + DWC_ASSERT(qh, "qh is not allocated\n");
- +
- + if (DWC_LIST_EMPTY(&qh->qh_list_entry)) {
- + freed = 1;
- + }
- +
- + return freed;
- +}
- +
- +uint8_t dwc_otg_hcd_get_ep_bandwidth(dwc_otg_hcd_t * hcd, void *ep_handle)
- +{
- + dwc_otg_qh_t *qh = (dwc_otg_qh_t *) ep_handle;
- + DWC_ASSERT(qh, "qh is not allocated\n");
- + return qh->usecs;
- +}
- +
- +void dwc_otg_hcd_dump_state(dwc_otg_hcd_t * hcd)
- +{
- +#ifdef DEBUG
- + int num_channels;
- + int i;
- + gnptxsts_data_t np_tx_status;
- + hptxsts_data_t p_tx_status;
- +
- + num_channels = hcd->core_if->core_params->host_channels;
- + DWC_PRINTF("\n");
- + DWC_PRINTF
- + ("************************************************************\n");
- + DWC_PRINTF("HCD State:\n");
- + DWC_PRINTF(" Num channels: %d\n", num_channels);
- + for (i = 0; i < num_channels; i++) {
- + dwc_hc_t *hc = hcd->hc_ptr_array[i];
- + DWC_PRINTF(" Channel %d:\n", i);
- + DWC_PRINTF(" dev_addr: %d, ep_num: %d, ep_is_in: %d\n",
- + hc->dev_addr, hc->ep_num, hc->ep_is_in);
- + DWC_PRINTF(" speed: %d\n", hc->speed);
- + DWC_PRINTF(" ep_type: %d\n", hc->ep_type);
- + DWC_PRINTF(" max_packet: %d\n", hc->max_packet);
- + DWC_PRINTF(" data_pid_start: %d\n", hc->data_pid_start);
- + DWC_PRINTF(" multi_count: %d\n", hc->multi_count);
- + DWC_PRINTF(" xfer_started: %d\n", hc->xfer_started);
- + DWC_PRINTF(" xfer_buff: %p\n", hc->xfer_buff);
- + DWC_PRINTF(" xfer_len: %d\n", hc->xfer_len);
- + DWC_PRINTF(" xfer_count: %d\n", hc->xfer_count);
- + DWC_PRINTF(" halt_on_queue: %d\n", hc->halt_on_queue);
- + DWC_PRINTF(" halt_pending: %d\n", hc->halt_pending);
- + DWC_PRINTF(" halt_status: %d\n", hc->halt_status);
- + DWC_PRINTF(" do_split: %d\n", hc->do_split);
- + DWC_PRINTF(" complete_split: %d\n", hc->complete_split);
- + DWC_PRINTF(" hub_addr: %d\n", hc->hub_addr);
- + DWC_PRINTF(" port_addr: %d\n", hc->port_addr);
- + DWC_PRINTF(" xact_pos: %d\n", hc->xact_pos);
- + DWC_PRINTF(" requests: %d\n", hc->requests);
- + DWC_PRINTF(" qh: %p\n", hc->qh);
- + if (hc->xfer_started) {
- + hfnum_data_t hfnum;
- + hcchar_data_t hcchar;
- + hctsiz_data_t hctsiz;
- + hcint_data_t hcint;
- + hcintmsk_data_t hcintmsk;
- + hfnum.d32 =
- + DWC_READ_REG32(&hcd->core_if->
- + host_if->host_global_regs->hfnum);
- + hcchar.d32 =
- + DWC_READ_REG32(&hcd->core_if->host_if->
- + hc_regs[i]->hcchar);
- + hctsiz.d32 =
- + DWC_READ_REG32(&hcd->core_if->host_if->
- + hc_regs[i]->hctsiz);
- + hcint.d32 =
- + DWC_READ_REG32(&hcd->core_if->host_if->
- + hc_regs[i]->hcint);
- + hcintmsk.d32 =
- + DWC_READ_REG32(&hcd->core_if->host_if->
- + hc_regs[i]->hcintmsk);
- + DWC_PRINTF(" hfnum: 0x%08x\n", hfnum.d32);
- + DWC_PRINTF(" hcchar: 0x%08x\n", hcchar.d32);
- + DWC_PRINTF(" hctsiz: 0x%08x\n", hctsiz.d32);
- + DWC_PRINTF(" hcint: 0x%08x\n", hcint.d32);
- + DWC_PRINTF(" hcintmsk: 0x%08x\n", hcintmsk.d32);
- + }
- + if (hc->xfer_started && hc->qh) {
- + dwc_otg_qtd_t *qtd;
- + dwc_otg_hcd_urb_t *urb;
- +
- + DWC_CIRCLEQ_FOREACH(qtd, &hc->qh->qtd_list, qtd_list_entry) {
- + if (!qtd->in_process)
- + break;
- +
- + urb = qtd->urb;
- + DWC_PRINTF(" URB Info:\n");
- + DWC_PRINTF(" qtd: %p, urb: %p\n", qtd, urb);
- + if (urb) {
- + DWC_PRINTF(" Dev: %d, EP: %d %s\n",
- + dwc_otg_hcd_get_dev_addr(&urb->
- + pipe_info),
- + dwc_otg_hcd_get_ep_num(&urb->
- + pipe_info),
- + dwc_otg_hcd_is_pipe_in(&urb->
- + pipe_info) ?
- + "IN" : "OUT");
- + DWC_PRINTF(" Max packet size: %d\n",
- + dwc_otg_hcd_get_mps(&urb->
- + pipe_info));
- + DWC_PRINTF(" transfer_buffer: %p\n",
- + urb->buf);
- + DWC_PRINTF(" transfer_dma: %p\n",
- + (void *)urb->dma);
- + DWC_PRINTF(" transfer_buffer_length: %d\n",
- + urb->length);
- + DWC_PRINTF(" actual_length: %d\n",
- + urb->actual_length);
- + }
- + }
- + }
- + }
- + DWC_PRINTF(" non_periodic_channels: %d\n", hcd->non_periodic_channels);
- + DWC_PRINTF(" periodic_channels: %d\n", hcd->periodic_channels);
- + DWC_PRINTF(" periodic_usecs: %d\n", hcd->periodic_usecs);
- + np_tx_status.d32 =
- + DWC_READ_REG32(&hcd->core_if->core_global_regs->gnptxsts);
- + DWC_PRINTF(" NP Tx Req Queue Space Avail: %d\n",
- + np_tx_status.b.nptxqspcavail);
- + DWC_PRINTF(" NP Tx FIFO Space Avail: %d\n",
- + np_tx_status.b.nptxfspcavail);
- + p_tx_status.d32 =
- + DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hptxsts);
- + DWC_PRINTF(" P Tx Req Queue Space Avail: %d\n",
- + p_tx_status.b.ptxqspcavail);
- + DWC_PRINTF(" P Tx FIFO Space Avail: %d\n", p_tx_status.b.ptxfspcavail);
- + dwc_otg_hcd_dump_frrem(hcd);
- + dwc_otg_dump_global_registers(hcd->core_if);
- + dwc_otg_dump_host_registers(hcd->core_if);
- + DWC_PRINTF
- + ("************************************************************\n");
- + DWC_PRINTF("\n");
- +#endif
- +}
- +
- +#ifdef DEBUG
- +void dwc_print_setup_data(uint8_t * setup)
- +{
- + int i;
- + if (CHK_DEBUG_LEVEL(DBG_HCD)) {
- + DWC_PRINTF("Setup Data = MSB ");
- + for (i = 7; i >= 0; i--)
- + DWC_PRINTF("%02x ", setup[i]);
- + DWC_PRINTF("\n");
- + DWC_PRINTF(" bmRequestType Tranfer = %s\n",
- + (setup[0] & 0x80) ? "Device-to-Host" :
- + "Host-to-Device");
- + DWC_PRINTF(" bmRequestType Type = ");
- + switch ((setup[0] & 0x60) >> 5) {
- + case 0:
- + DWC_PRINTF("Standard\n");
- + break;
- + case 1:
- + DWC_PRINTF("Class\n");
- + break;
- + case 2:
- + DWC_PRINTF("Vendor\n");
- + break;
- + case 3:
- + DWC_PRINTF("Reserved\n");
- + break;
- + }
- + DWC_PRINTF(" bmRequestType Recipient = ");
- + switch (setup[0] & 0x1f) {
- + case 0:
- + DWC_PRINTF("Device\n");
- + break;
- + case 1:
- + DWC_PRINTF("Interface\n");
- + break;
- + case 2:
- + DWC_PRINTF("Endpoint\n");
- + break;
- + case 3:
- + DWC_PRINTF("Other\n");
- + break;
- + default:
- + DWC_PRINTF("Reserved\n");
- + break;
- + }
- + DWC_PRINTF(" bRequest = 0x%0x\n", setup[1]);
- + DWC_PRINTF(" wValue = 0x%0x\n", *((uint16_t *) & setup[2]));
- + DWC_PRINTF(" wIndex = 0x%0x\n", *((uint16_t *) & setup[4]));
- + DWC_PRINTF(" wLength = 0x%0x\n\n", *((uint16_t *) & setup[6]));
- + }
- +}
- +#endif
- +
- +void dwc_otg_hcd_dump_frrem(dwc_otg_hcd_t * hcd)
- +{
- +#if 0
- + DWC_PRINTF("Frame remaining at SOF:\n");
- + DWC_PRINTF(" samples %u, accum %llu, avg %llu\n",
- + hcd->frrem_samples, hcd->frrem_accum,
- + (hcd->frrem_samples > 0) ?
- + hcd->frrem_accum / hcd->frrem_samples : 0);
- +
- + DWC_PRINTF("\n");
- + DWC_PRINTF("Frame remaining at start_transfer (uframe 7):\n");
- + DWC_PRINTF(" samples %u, accum %llu, avg %llu\n",
- + hcd->core_if->hfnum_7_samples,
- + hcd->core_if->hfnum_7_frrem_accum,
- + (hcd->core_if->hfnum_7_samples >
- + 0) ? hcd->core_if->hfnum_7_frrem_accum /
- + hcd->core_if->hfnum_7_samples : 0);
- + DWC_PRINTF("Frame remaining at start_transfer (uframe 0):\n");
- + DWC_PRINTF(" samples %u, accum %llu, avg %llu\n",
- + hcd->core_if->hfnum_0_samples,
- + hcd->core_if->hfnum_0_frrem_accum,
- + (hcd->core_if->hfnum_0_samples >
- + 0) ? hcd->core_if->hfnum_0_frrem_accum /
- + hcd->core_if->hfnum_0_samples : 0);
- + DWC_PRINTF("Frame remaining at start_transfer (uframe 1-6):\n");
- + DWC_PRINTF(" samples %u, accum %llu, avg %llu\n",
- + hcd->core_if->hfnum_other_samples,
- + hcd->core_if->hfnum_other_frrem_accum,
- + (hcd->core_if->hfnum_other_samples >
- + 0) ? hcd->core_if->hfnum_other_frrem_accum /
- + hcd->core_if->hfnum_other_samples : 0);
- +
- + DWC_PRINTF("\n");
- + DWC_PRINTF("Frame remaining at sample point A (uframe 7):\n");
- + DWC_PRINTF(" samples %u, accum %llu, avg %llu\n",
- + hcd->hfnum_7_samples_a, hcd->hfnum_7_frrem_accum_a,
- + (hcd->hfnum_7_samples_a > 0) ?
- + hcd->hfnum_7_frrem_accum_a / hcd->hfnum_7_samples_a : 0);
- + DWC_PRINTF("Frame remaining at sample point A (uframe 0):\n");
- + DWC_PRINTF(" samples %u, accum %llu, avg %llu\n",
- + hcd->hfnum_0_samples_a, hcd->hfnum_0_frrem_accum_a,
- + (hcd->hfnum_0_samples_a > 0) ?
- + hcd->hfnum_0_frrem_accum_a / hcd->hfnum_0_samples_a : 0);
- + DWC_PRINTF("Frame remaining at sample point A (uframe 1-6):\n");
- + DWC_PRINTF(" samples %u, accum %llu, avg %llu\n",
- + hcd->hfnum_other_samples_a, hcd->hfnum_other_frrem_accum_a,
- + (hcd->hfnum_other_samples_a > 0) ?
- + hcd->hfnum_other_frrem_accum_a /
- + hcd->hfnum_other_samples_a : 0);
- +
- + DWC_PRINTF("\n");
- + DWC_PRINTF("Frame remaining at sample point B (uframe 7):\n");
- + DWC_PRINTF(" samples %u, accum %llu, avg %llu\n",
- + hcd->hfnum_7_samples_b, hcd->hfnum_7_frrem_accum_b,
- + (hcd->hfnum_7_samples_b > 0) ?
- + hcd->hfnum_7_frrem_accum_b / hcd->hfnum_7_samples_b : 0);
- + DWC_PRINTF("Frame remaining at sample point B (uframe 0):\n");
- + DWC_PRINTF(" samples %u, accum %llu, avg %llu\n",
- + hcd->hfnum_0_samples_b, hcd->hfnum_0_frrem_accum_b,
- + (hcd->hfnum_0_samples_b > 0) ?
- + hcd->hfnum_0_frrem_accum_b / hcd->hfnum_0_samples_b : 0);
- + DWC_PRINTF("Frame remaining at sample point B (uframe 1-6):\n");
- + DWC_PRINTF(" samples %u, accum %llu, avg %llu\n",
- + hcd->hfnum_other_samples_b, hcd->hfnum_other_frrem_accum_b,
- + (hcd->hfnum_other_samples_b > 0) ?
- + hcd->hfnum_other_frrem_accum_b /
- + hcd->hfnum_other_samples_b : 0);
- +#endif
- +}
- +
- +#endif /* DWC_DEVICE_ONLY */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.h
- @@ -0,0 +1,870 @@
- +/* ==========================================================================
- + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd.h $
- + * $Revision: #58 $
- + * $Date: 2011/09/15 $
- + * $Change: 1846647 $
- + *
- + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
- + * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
- + * otherwise expressly agreed to in writing between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product under
- + * any End User Software License Agreement or Agreement for Licensed Product
- + * with Synopsys or any supplement thereto. You are permitted to use and
- + * redistribute this Software in source and binary forms, with or without
- + * modification, provided that redistributions of source code must retain this
- + * notice. You may not view, use, disclose, copy or distribute this file or
- + * any information contained herein except pursuant to this license grant from
- + * Synopsys. If you do not agree with this notice, including the disclaimer
- + * below, then you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
- + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- + * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
- + * ========================================================================== */
- +#ifndef DWC_DEVICE_ONLY
- +#ifndef __DWC_HCD_H__
- +#define __DWC_HCD_H__
- +
- +#include "dwc_otg_os_dep.h"
- +#include "usb.h"
- +#include "dwc_otg_hcd_if.h"
- +#include "dwc_otg_core_if.h"
- +#include "dwc_list.h"
- +#include "dwc_otg_cil.h"
- +#include "dwc_otg_fiq_fsm.h"
- +#include "dwc_otg_driver.h"
- +
- +
- +/**
- + * @file
- + *
- + * This file contains the structures, constants, and interfaces for
- + * the Host Contoller Driver (HCD).
- + *
- + * The Host Controller Driver (HCD) is responsible for translating requests
- + * from the USB Driver into the appropriate actions on the DWC_otg controller.
- + * It isolates the USBD from the specifics of the controller by providing an
- + * API to the USBD.
- + */
- +
- +struct dwc_otg_hcd_pipe_info {
- + uint8_t dev_addr;
- + uint8_t ep_num;
- + uint8_t pipe_type;
- + uint8_t pipe_dir;
- + uint16_t mps;
- +};
- +
- +struct dwc_otg_hcd_iso_packet_desc {
- + uint32_t offset;
- + uint32_t length;
- + uint32_t actual_length;
- + uint32_t status;
- +};
- +
- +struct dwc_otg_qtd;
- +
- +struct dwc_otg_hcd_urb {
- + void *priv;
- + struct dwc_otg_qtd *qtd;
- + void *buf;
- + dwc_dma_t dma;
- + void *setup_packet;
- + dwc_dma_t setup_dma;
- + uint32_t length;
- + uint32_t actual_length;
- + uint32_t status;
- + uint32_t error_count;
- + uint32_t packet_count;
- + uint32_t flags;
- + uint16_t interval;
- + struct dwc_otg_hcd_pipe_info pipe_info;
- + struct dwc_otg_hcd_iso_packet_desc iso_descs[0];
- +};
- +
- +static inline uint8_t dwc_otg_hcd_get_ep_num(struct dwc_otg_hcd_pipe_info *pipe)
- +{
- + return pipe->ep_num;
- +}
- +
- +static inline uint8_t dwc_otg_hcd_get_pipe_type(struct dwc_otg_hcd_pipe_info
- + *pipe)
- +{
- + return pipe->pipe_type;
- +}
- +
- +static inline uint16_t dwc_otg_hcd_get_mps(struct dwc_otg_hcd_pipe_info *pipe)
- +{
- + return pipe->mps;
- +}
- +
- +static inline uint8_t dwc_otg_hcd_get_dev_addr(struct dwc_otg_hcd_pipe_info
- + *pipe)
- +{
- + return pipe->dev_addr;
- +}
- +
- +static inline uint8_t dwc_otg_hcd_is_pipe_isoc(struct dwc_otg_hcd_pipe_info
- + *pipe)
- +{
- + return (pipe->pipe_type == UE_ISOCHRONOUS);
- +}
- +
- +static inline uint8_t dwc_otg_hcd_is_pipe_int(struct dwc_otg_hcd_pipe_info
- + *pipe)
- +{
- + return (pipe->pipe_type == UE_INTERRUPT);
- +}
- +
- +static inline uint8_t dwc_otg_hcd_is_pipe_bulk(struct dwc_otg_hcd_pipe_info
- + *pipe)
- +{
- + return (pipe->pipe_type == UE_BULK);
- +}
- +
- +static inline uint8_t dwc_otg_hcd_is_pipe_control(struct dwc_otg_hcd_pipe_info
- + *pipe)
- +{
- + return (pipe->pipe_type == UE_CONTROL);
- +}
- +
- +static inline uint8_t dwc_otg_hcd_is_pipe_in(struct dwc_otg_hcd_pipe_info *pipe)
- +{
- + return (pipe->pipe_dir == UE_DIR_IN);
- +}
- +
- +static inline uint8_t dwc_otg_hcd_is_pipe_out(struct dwc_otg_hcd_pipe_info
- + *pipe)
- +{
- + return (!dwc_otg_hcd_is_pipe_in(pipe));
- +}
- +
- +static inline void dwc_otg_hcd_fill_pipe(struct dwc_otg_hcd_pipe_info *pipe,
- + uint8_t devaddr, uint8_t ep_num,
- + uint8_t pipe_type, uint8_t pipe_dir,
- + uint16_t mps)
- +{
- + pipe->dev_addr = devaddr;
- + pipe->ep_num = ep_num;
- + pipe->pipe_type = pipe_type;
- + pipe->pipe_dir = pipe_dir;
- + pipe->mps = mps;
- +}
- +
- +/**
- + * Phases for control transfers.
- + */
- +typedef enum dwc_otg_control_phase {
- + DWC_OTG_CONTROL_SETUP,
- + DWC_OTG_CONTROL_DATA,
- + DWC_OTG_CONTROL_STATUS
- +} dwc_otg_control_phase_e;
- +
- +/** Transaction types. */
- +typedef enum dwc_otg_transaction_type {
- + DWC_OTG_TRANSACTION_NONE = 0,
- + DWC_OTG_TRANSACTION_PERIODIC = 1,
- + DWC_OTG_TRANSACTION_NON_PERIODIC = 2,
- + DWC_OTG_TRANSACTION_ALL = DWC_OTG_TRANSACTION_PERIODIC + DWC_OTG_TRANSACTION_NON_PERIODIC
- +} dwc_otg_transaction_type_e;
- +
- +struct dwc_otg_qh;
- +
- +/**
- + * A Queue Transfer Descriptor (QTD) holds the state of a bulk, control,
- + * interrupt, or isochronous transfer. A single QTD is created for each URB
- + * (of one of these types) submitted to the HCD. The transfer associated with
- + * a QTD may require one or multiple transactions.
- + *
- + * A QTD is linked to a Queue Head, which is entered in either the
- + * non-periodic or periodic schedule for execution. When a QTD is chosen for
- + * execution, some or all of its transactions may be executed. After
- + * execution, the state of the QTD is updated. The QTD may be retired if all
- + * its transactions are complete or if an error occurred. Otherwise, it
- + * remains in the schedule so more transactions can be executed later.
- + */
- +typedef struct dwc_otg_qtd {
- + /**
- + * Determines the PID of the next data packet for the data phase of
- + * control transfers. Ignored for other transfer types.<br>
- + * One of the following values:
- + * - DWC_OTG_HC_PID_DATA0
- + * - DWC_OTG_HC_PID_DATA1
- + */
- + uint8_t data_toggle;
- +
- + /** Current phase for control transfers (Setup, Data, or Status). */
- + dwc_otg_control_phase_e control_phase;
- +
- + /** Keep track of the current split type
- + * for FS/LS endpoints on a HS Hub */
- + uint8_t complete_split;
- +
- + /** How many bytes transferred during SSPLIT OUT */
- + uint32_t ssplit_out_xfer_count;
- +
- + /**
- + * Holds the number of bus errors that have occurred for a transaction
- + * within this transfer.
- + */
- + uint8_t error_count;
- +
- + /**
- + * Index of the next frame descriptor for an isochronous transfer. A
- + * frame descriptor describes the buffer position and length of the
- + * data to be transferred in the next scheduled (micro)frame of an
- + * isochronous transfer. It also holds status for that transaction.
- + * The frame index starts at 0.
- + */
- + uint16_t isoc_frame_index;
- +
- + /** Position of the ISOC split on full/low speed */
- + uint8_t isoc_split_pos;
- +
- + /** Position of the ISOC split in the buffer for the current frame */
- + uint16_t isoc_split_offset;
- +
- + /** URB for this transfer */
- + struct dwc_otg_hcd_urb *urb;
- +
- + struct dwc_otg_qh *qh;
- +
- + /** This list of QTDs */
- + DWC_CIRCLEQ_ENTRY(dwc_otg_qtd) qtd_list_entry;
- +
- + /** Indicates if this QTD is currently processed by HW. */
- + uint8_t in_process;
- +
- + /** Number of DMA descriptors for this QTD */
- + uint8_t n_desc;
- +
- + /**
- + * Last activated frame(packet) index.
- + * Used in Descriptor DMA mode only.
- + */
- + uint16_t isoc_frame_index_last;
- +
- +} dwc_otg_qtd_t;
- +
- +DWC_CIRCLEQ_HEAD(dwc_otg_qtd_list, dwc_otg_qtd);
- +
- +/**
- + * A Queue Head (QH) holds the static characteristics of an endpoint and
- + * maintains a list of transfers (QTDs) for that endpoint. A QH structure may
- + * be entered in either the non-periodic or periodic schedule.
- + */
- +typedef struct dwc_otg_qh {
- + /**
- + * Endpoint type.
- + * One of the following values:
- + * - UE_CONTROL
- + * - UE_BULK
- + * - UE_INTERRUPT
- + * - UE_ISOCHRONOUS
- + */
- + uint8_t ep_type;
- + uint8_t ep_is_in;
- +
- + /** wMaxPacketSize Field of Endpoint Descriptor. */
- + uint16_t maxp;
- +
- + /**
- + * Device speed.
- + * One of the following values:
- + * - DWC_OTG_EP_SPEED_LOW
- + * - DWC_OTG_EP_SPEED_FULL
- + * - DWC_OTG_EP_SPEED_HIGH
- + */
- + uint8_t dev_speed;
- +
- + /**
- + * Determines the PID of the next data packet for non-control
- + * transfers. Ignored for control transfers.<br>
- + * One of the following values:
- + * - DWC_OTG_HC_PID_DATA0
- + * - DWC_OTG_HC_PID_DATA1
- + */
- + uint8_t data_toggle;
- +
- + /** Ping state if 1. */
- + uint8_t ping_state;
- +
- + /**
- + * List of QTDs for this QH.
- + */
- + struct dwc_otg_qtd_list qtd_list;
- +
- + /** Host channel currently processing transfers for this QH. */
- + struct dwc_hc *channel;
- +
- + /** Full/low speed endpoint on high-speed hub requires split. */
- + uint8_t do_split;
- +
- + /** @name Periodic schedule information */
- + /** @{ */
- +
- + /** Bandwidth in microseconds per (micro)frame. */
- + uint16_t usecs;
- +
- + /** Interval between transfers in (micro)frames. */
- + uint16_t interval;
- +
- + /**
- + * (micro)frame to initialize a periodic transfer. The transfer
- + * executes in the following (micro)frame.
- + */
- + uint16_t sched_frame;
- +
- + /*
- + ** Frame a NAK was received on this queue head, used to minimise NAK retransmission
- + */
- + uint16_t nak_frame;
- +
- + /** (micro)frame at which last start split was initialized. */
- + uint16_t start_split_frame;
- +
- + /** @} */
- +
- + /**
- + * Used instead of original buffer if
- + * it(physical address) is not dword-aligned.
- + */
- + uint8_t *dw_align_buf;
- + dwc_dma_t dw_align_buf_dma;
- +
- + /** Entry for QH in either the periodic or non-periodic schedule. */
- + dwc_list_link_t qh_list_entry;
- +
- + /** @name Descriptor DMA support */
- + /** @{ */
- +
- + /** Descriptor List. */
- + dwc_otg_host_dma_desc_t *desc_list;
- +
- + /** Descriptor List physical address. */
- + dwc_dma_t desc_list_dma;
- +
- + /**
- + * Xfer Bytes array.
- + * Each element corresponds to a descriptor and indicates
- + * original XferSize size value for the descriptor.
- + */
- + uint32_t *n_bytes;
- +
- + /** Actual number of transfer descriptors in a list. */
- + uint16_t ntd;
- +
- + /** First activated isochronous transfer descriptor index. */
- + uint8_t td_first;
- + /** Last activated isochronous transfer descriptor index. */
- + uint8_t td_last;
- +
- + /** @} */
- +
- +
- + uint16_t speed;
- + uint16_t frame_usecs[8];
- +
- + uint32_t skip_count;
- +} dwc_otg_qh_t;
- +
- +DWC_CIRCLEQ_HEAD(hc_list, dwc_hc);
- +
- +typedef struct urb_tq_entry {
- + struct urb *urb;
- + DWC_TAILQ_ENTRY(urb_tq_entry) urb_tq_entries;
- +} urb_tq_entry_t;
- +
- +DWC_TAILQ_HEAD(urb_list, urb_tq_entry);
- +
- +/**
- + * This structure holds the state of the HCD, including the non-periodic and
- + * periodic schedules.
- + */
- +struct dwc_otg_hcd {
- + /** The DWC otg device pointer */
- + struct dwc_otg_device *otg_dev;
- + /** DWC OTG Core Interface Layer */
- + dwc_otg_core_if_t *core_if;
- +
- + /** Function HCD driver callbacks */
- + struct dwc_otg_hcd_function_ops *fops;
- +
- + /** Internal DWC HCD Flags */
- + volatile union dwc_otg_hcd_internal_flags {
- + uint32_t d32;
- + struct {
- + unsigned port_connect_status_change:1;
- + unsigned port_connect_status:1;
- + unsigned port_reset_change:1;
- + unsigned port_enable_change:1;
- + unsigned port_suspend_change:1;
- + unsigned port_over_current_change:1;
- + unsigned port_l1_change:1;
- + unsigned port_speed:2;
- + unsigned reserved:24;
- + } b;
- + } flags;
- +
- + /**
- + * Inactive items in the non-periodic schedule. This is a list of
- + * Queue Heads. Transfers associated with these Queue Heads are not
- + * currently assigned to a host channel.
- + */
- + dwc_list_link_t non_periodic_sched_inactive;
- +
- + /**
- + * Active items in the non-periodic schedule. This is a list of
- + * Queue Heads. Transfers associated with these Queue Heads are
- + * currently assigned to a host channel.
- + */
- + dwc_list_link_t non_periodic_sched_active;
- +
- + /**
- + * Pointer to the next Queue Head to process in the active
- + * non-periodic schedule.
- + */
- + dwc_list_link_t *non_periodic_qh_ptr;
- +
- + /**
- + * Inactive items in the periodic schedule. This is a list of QHs for
- + * periodic transfers that are _not_ scheduled for the next frame.
- + * Each QH in the list has an interval counter that determines when it
- + * needs to be scheduled for execution. This scheduling mechanism
- + * allows only a simple calculation for periodic bandwidth used (i.e.
- + * must assume that all periodic transfers may need to execute in the
- + * same frame). However, it greatly simplifies scheduling and should
- + * be sufficient for the vast majority of OTG hosts, which need to
- + * connect to a small number of peripherals at one time.
- + *
- + * Items move from this list to periodic_sched_ready when the QH
- + * interval counter is 0 at SOF.
- + */
- + dwc_list_link_t periodic_sched_inactive;
- +
- + /**
- + * List of periodic QHs that are ready for execution in the next
- + * frame, but have not yet been assigned to host channels.
- + *
- + * Items move from this list to periodic_sched_assigned as host
- + * channels become available during the current frame.
- + */
- + dwc_list_link_t periodic_sched_ready;
- +
- + /**
- + * List of periodic QHs to be executed in the next frame that are
- + * assigned to host channels.
- + *
- + * Items move from this list to periodic_sched_queued as the
- + * transactions for the QH are queued to the DWC_otg controller.
- + */
- + dwc_list_link_t periodic_sched_assigned;
- +
- + /**
- + * List of periodic QHs that have been queued for execution.
- + *
- + * Items move from this list to either periodic_sched_inactive or
- + * periodic_sched_ready when the channel associated with the transfer
- + * is released. If the interval for the QH is 1, the item moves to
- + * periodic_sched_ready because it must be rescheduled for the next
- + * frame. Otherwise, the item moves to periodic_sched_inactive.
- + */
- + dwc_list_link_t periodic_sched_queued;
- +
- + /**
- + * Total bandwidth claimed so far for periodic transfers. This value
- + * is in microseconds per (micro)frame. The assumption is that all
- + * periodic transfers may occur in the same (micro)frame.
- + */
- + uint16_t periodic_usecs;
- +
- + /**
- + * Total bandwidth claimed so far for all periodic transfers
- + * in a frame.
- + * This will include a mixture of HS and FS transfers.
- + * Units are microseconds per (micro)frame.
- + * We have a budget per frame and have to schedule
- + * transactions accordingly.
- + * Watch out for the fact that things are actually scheduled for the
- + * "next frame".
- + */
- + uint16_t frame_usecs[8];
- +
- +
- + /**
- + * Frame number read from the core at SOF. The value ranges from 0 to
- + * DWC_HFNUM_MAX_FRNUM.
- + */
- + uint16_t frame_number;
- +
- + /**
- + * Count of periodic QHs, if using several eps. For SOF enable/disable.
- + */
- + uint16_t periodic_qh_count;
- +
- + /**
- + * Free host channels in the controller. This is a list of
- + * dwc_hc_t items.
- + */
- + struct hc_list free_hc_list;
- + /**
- + * Number of host channels assigned to periodic transfers. Currently
- + * assuming that there is a dedicated host channel for each periodic
- + * transaction and at least one host channel available for
- + * non-periodic transactions.
- + */
- + int periodic_channels; /* microframe_schedule==0 */
- +
- + /**
- + * Number of host channels assigned to non-periodic transfers.
- + */
- + int non_periodic_channels; /* microframe_schedule==0 */
- +
- + /**
- + * Number of host channels assigned to non-periodic transfers.
- + */
- + int available_host_channels;
- +
- + /**
- + * Array of pointers to the host channel descriptors. Allows accessing
- + * a host channel descriptor given the host channel number. This is
- + * useful in interrupt handlers.
- + */
- + struct dwc_hc *hc_ptr_array[MAX_EPS_CHANNELS];
- +
- + /**
- + * Buffer to use for any data received during the status phase of a
- + * control transfer. Normally no data is transferred during the status
- + * phase. This buffer is used as a bit bucket.
- + */
- + uint8_t *status_buf;
- +
- + /**
- + * DMA address for status_buf.
- + */
- + dma_addr_t status_buf_dma;
- +#define DWC_OTG_HCD_STATUS_BUF_SIZE 64
- +
- + /**
- + * Connection timer. An OTG host must display a message if the device
- + * does not connect. Started when the VBus power is turned on via
- + * sysfs attribute "buspower".
- + */
- + dwc_timer_t *conn_timer;
- +
- + /* Tasket to do a reset */
- + dwc_tasklet_t *reset_tasklet;
- +
- + dwc_tasklet_t *completion_tasklet;
- + struct urb_list completed_urb_list;
- +
- + /* */
- + dwc_spinlock_t *lock;
- + /**
- + * Private data that could be used by OS wrapper.
- + */
- + void *priv;
- +
- + uint8_t otg_port;
- +
- + /** Frame List */
- + uint32_t *frame_list;
- +
- + /** Hub - Port assignment */
- + int hub_port[128];
- +#ifdef FIQ_DEBUG
- + int hub_port_alloc[2048];
- +#endif
- +
- + /** 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;
- +
- + uint32_t hfnum_7_samples_a;
- + uint64_t hfnum_7_frrem_accum_a;
- + uint32_t hfnum_0_samples_a;
- + uint64_t hfnum_0_frrem_accum_a;
- + uint32_t hfnum_other_samples_a;
- + uint64_t hfnum_other_frrem_accum_a;
- +
- + uint32_t hfnum_7_samples_b;
- + uint64_t hfnum_7_frrem_accum_b;
- + uint32_t hfnum_0_samples_b;
- + uint64_t hfnum_0_frrem_accum_b;
- + uint32_t hfnum_other_samples_b;
- + uint64_t hfnum_other_frrem_accum_b;
- +#endif
- +};
- +
- +static inline struct device *dwc_otg_hcd_to_dev(struct dwc_otg_hcd *hcd)
- +{
- + return &hcd->otg_dev->os_dep.platformdev->dev;
- +}
- +
- +/** @name Transaction Execution Functions */
- +/** @{ */
- +extern dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t
- + * hcd);
- +extern void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t * hcd,
- + dwc_otg_transaction_type_e tr_type);
- +
- +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_hcd_t *hcd, dwc_otg_qh_t *qh);
- +extern void dwc_otg_cleanup_fiq_channel(dwc_otg_hcd_t *hcd, uint32_t num);
- +
- +/** @} */
- +
- +/** @name Interrupt Handler Functions */
- +/** @{ */
- +extern int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd);
- +extern int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * dwc_otg_hcd);
- +extern int32_t dwc_otg_hcd_handle_rx_status_q_level_intr(dwc_otg_hcd_t *
- + dwc_otg_hcd);
- +extern int32_t dwc_otg_hcd_handle_np_tx_fifo_empty_intr(dwc_otg_hcd_t *
- + dwc_otg_hcd);
- +extern int32_t dwc_otg_hcd_handle_perio_tx_fifo_empty_intr(dwc_otg_hcd_t *
- + dwc_otg_hcd);
- +extern int32_t dwc_otg_hcd_handle_incomplete_periodic_intr(dwc_otg_hcd_t *
- + dwc_otg_hcd);
- +extern int32_t dwc_otg_hcd_handle_port_intr(dwc_otg_hcd_t * dwc_otg_hcd);
- +extern int32_t dwc_otg_hcd_handle_conn_id_status_change_intr(dwc_otg_hcd_t *
- + dwc_otg_hcd);
- +extern int32_t dwc_otg_hcd_handle_disconnect_intr(dwc_otg_hcd_t * dwc_otg_hcd);
- +extern int32_t dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd_t * dwc_otg_hcd);
- +extern int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd,
- + uint32_t num);
- +extern int32_t dwc_otg_hcd_handle_session_req_intr(dwc_otg_hcd_t * dwc_otg_hcd);
- +extern int32_t dwc_otg_hcd_handle_wakeup_detected_intr(dwc_otg_hcd_t *
- + dwc_otg_hcd);
- +/** @} */
- +
- +/** @name Schedule Queue Functions */
- +/** @{ */
- +
- +/* Implemented in dwc_otg_hcd_queue.c */
- +extern dwc_otg_qh_t *dwc_otg_hcd_qh_create(dwc_otg_hcd_t * hcd,
- + dwc_otg_hcd_urb_t * urb, int atomic_alloc);
- +extern void dwc_otg_hcd_qh_free(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh);
- +extern int dwc_otg_hcd_qh_add(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh);
- +extern void dwc_otg_hcd_qh_remove(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh);
- +extern void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh,
- + int sched_csplit);
- +
- +/** Remove and free a QH */
- +static inline void dwc_otg_hcd_qh_remove_and_free(dwc_otg_hcd_t * hcd,
- + dwc_otg_qh_t * qh)
- +{
- + dwc_irqflags_t flags;
- + DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
- + dwc_otg_hcd_qh_remove(hcd, qh);
- + DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
- + dwc_otg_hcd_qh_free(hcd, qh);
- +}
- +
- +/** Allocates memory for a QH structure.
- + * @return Returns the memory allocate or NULL on error. */
- +static inline dwc_otg_qh_t *dwc_otg_hcd_qh_alloc(int atomic_alloc)
- +{
- + if (atomic_alloc)
- + return (dwc_otg_qh_t *) DWC_ALLOC_ATOMIC(sizeof(dwc_otg_qh_t));
- + else
- + return (dwc_otg_qh_t *) DWC_ALLOC(sizeof(dwc_otg_qh_t));
- +}
- +
- +extern dwc_otg_qtd_t *dwc_otg_hcd_qtd_create(dwc_otg_hcd_urb_t * urb,
- + int atomic_alloc);
- +extern void dwc_otg_hcd_qtd_init(dwc_otg_qtd_t * qtd, dwc_otg_hcd_urb_t * urb);
- +extern int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t * qtd, dwc_otg_hcd_t * dwc_otg_hcd,
- + dwc_otg_qh_t ** qh, int atomic_alloc);
- +
- +/** Allocates memory for a QTD structure.
- + * @return Returns the memory allocate or NULL on error. */
- +static inline dwc_otg_qtd_t *dwc_otg_hcd_qtd_alloc(int atomic_alloc)
- +{
- + if (atomic_alloc)
- + return (dwc_otg_qtd_t *) DWC_ALLOC_ATOMIC(sizeof(dwc_otg_qtd_t));
- + else
- + return (dwc_otg_qtd_t *) DWC_ALLOC(sizeof(dwc_otg_qtd_t));
- +}
- +
- +/** Frees the memory for a QTD structure. QTD should already be removed from
- + * list.
- + * @param qtd QTD to free.*/
- +static inline void dwc_otg_hcd_qtd_free(dwc_otg_qtd_t * qtd)
- +{
- + DWC_FREE(qtd);
- +}
- +
- +/** Removes a QTD from list.
- + * @param hcd HCD instance.
- + * @param qtd QTD to remove from list.
- + * @param qh QTD belongs to.
- + */
- +static inline void dwc_otg_hcd_qtd_remove(dwc_otg_hcd_t * hcd,
- + dwc_otg_qtd_t * qtd,
- + dwc_otg_qh_t * qh)
- +{
- + DWC_CIRCLEQ_REMOVE(&qh->qtd_list, qtd, qtd_list_entry);
- +}
- +
- +/** Remove and free a QTD
- + * Need to disable IRQ and hold hcd lock while calling this function out of
- + * interrupt servicing chain */
- +static inline void dwc_otg_hcd_qtd_remove_and_free(dwc_otg_hcd_t * hcd,
- + dwc_otg_qtd_t * qtd,
- + dwc_otg_qh_t * qh)
- +{
- + dwc_otg_hcd_qtd_remove(hcd, qtd, qh);
- + dwc_otg_hcd_qtd_free(qtd);
- +}
- +
- +/** @} */
- +
- +/** @name Descriptor DMA Supporting Functions */
- +/** @{ */
- +
- +extern void dwc_otg_hcd_start_xfer_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh);
- +extern void dwc_otg_hcd_complete_xfer_ddma(dwc_otg_hcd_t * hcd,
- + dwc_hc_t * hc,
- + dwc_otg_hc_regs_t * hc_regs,
- + dwc_otg_halt_status_e halt_status);
- +
- +extern int dwc_otg_hcd_qh_init_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh);
- +extern void dwc_otg_hcd_qh_free_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh);
- +
- +/** @} */
- +
- +/** @name Internal Functions */
- +/** @{ */
- +dwc_otg_qh_t *dwc_urb_to_qh(dwc_otg_hcd_urb_t * urb);
- +/** @} */
- +
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- +extern int dwc_otg_hcd_get_hc_for_lpm_tran(dwc_otg_hcd_t * hcd,
- + uint8_t devaddr);
- +extern void dwc_otg_hcd_free_hc_from_lpm(dwc_otg_hcd_t * hcd);
- +#endif
- +
- +/** Gets the QH that contains the list_head */
- +#define dwc_list_to_qh(_list_head_ptr_) container_of(_list_head_ptr_, dwc_otg_qh_t, qh_list_entry)
- +
- +/** Gets the QTD that contains the list_head */
- +#define dwc_list_to_qtd(_list_head_ptr_) container_of(_list_head_ptr_, dwc_otg_qtd_t, qtd_list_entry)
- +
- +/** Check if QH is non-periodic */
- +#define dwc_qh_is_non_per(_qh_ptr_) ((_qh_ptr_->ep_type == UE_BULK) || \
- + (_qh_ptr_->ep_type == UE_CONTROL))
- +
- +/** High bandwidth multiplier as encoded in highspeed endpoint descriptors */
- +#define dwc_hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
- +
- +/** Packet size for any kind of endpoint descriptor */
- +#define dwc_max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff)
- +
- +/**
- + * Returns true if _frame1 is less than or equal to _frame2. The comparison is
- + * done modulo DWC_HFNUM_MAX_FRNUM. This accounts for the rollover of the
- + * frame number when the max frame number is reached.
- + */
- +static inline int dwc_frame_num_le(uint16_t frame1, uint16_t frame2)
- +{
- + return ((frame2 - frame1) & DWC_HFNUM_MAX_FRNUM) <=
- + (DWC_HFNUM_MAX_FRNUM >> 1);
- +}
- +
- +/**
- + * Returns true if _frame1 is greater than _frame2. The comparison is done
- + * modulo DWC_HFNUM_MAX_FRNUM. This accounts for the rollover of the frame
- + * number when the max frame number is reached.
- + */
- +static inline int dwc_frame_num_gt(uint16_t frame1, uint16_t frame2)
- +{
- + return (frame1 != frame2) &&
- + (((frame1 - frame2) & DWC_HFNUM_MAX_FRNUM) <
- + (DWC_HFNUM_MAX_FRNUM >> 1));
- +}
- +
- +/**
- + * Increments _frame by the amount specified by _inc. The addition is done
- + * modulo DWC_HFNUM_MAX_FRNUM. Returns the incremented value.
- + */
- +static inline uint16_t dwc_frame_num_inc(uint16_t frame, uint16_t inc)
- +{
- + return (frame + inc) & DWC_HFNUM_MAX_FRNUM;
- +}
- +
- +static inline uint16_t dwc_full_frame_num(uint16_t frame)
- +{
- + return (frame & DWC_HFNUM_MAX_FRNUM) >> 3;
- +}
- +
- +static inline uint16_t dwc_micro_frame_num(uint16_t frame)
- +{
- + return frame & 0x7;
- +}
- +
- +extern void init_hcd_usecs(dwc_otg_hcd_t *_hcd);
- +
- +void dwc_otg_hcd_save_data_toggle(dwc_hc_t * hc,
- + dwc_otg_hc_regs_t * hc_regs,
- + dwc_otg_qtd_t * qtd);
- +
- +#ifdef DEBUG
- +/**
- + * Macro to sample the remaining PHY clocks left in the current frame. This
- + * may be used during debugging to determine the average time it takes to
- + * execute sections of code. There are two possible sample points, "a" and
- + * "b", so the _letter argument must be one of these values.
- + *
- + * To dump the average sample times, read the "hcd_frrem" sysfs attribute. For
- + * example, "cat /sys/devices/lm0/hcd_frrem".
- + */
- +#define dwc_sample_frrem(_hcd, _qh, _letter) \
- +{ \
- + hfnum_data_t hfnum; \
- + dwc_otg_qtd_t *qtd; \
- + qtd = list_entry(_qh->qtd_list.next, dwc_otg_qtd_t, qtd_list_entry); \
- + if (usb_pipeint(qtd->urb->pipe) && _qh->start_split_frame != 0 && !qtd->complete_split) { \
- + hfnum.d32 = DWC_READ_REG32(&_hcd->core_if->host_if->host_global_regs->hfnum); \
- + switch (hfnum.b.frnum & 0x7) { \
- + case 7: \
- + _hcd->hfnum_7_samples_##_letter++; \
- + _hcd->hfnum_7_frrem_accum_##_letter += hfnum.b.frrem; \
- + break; \
- + case 0: \
- + _hcd->hfnum_0_samples_##_letter++; \
- + _hcd->hfnum_0_frrem_accum_##_letter += hfnum.b.frrem; \
- + break; \
- + default: \
- + _hcd->hfnum_other_samples_##_letter++; \
- + _hcd->hfnum_other_frrem_accum_##_letter += hfnum.b.frrem; \
- + break; \
- + } \
- + } \
- +}
- +#else
- +#define dwc_sample_frrem(_hcd, _qh, _letter)
- +#endif
- +#endif
- +#endif /* DWC_DEVICE_ONLY */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c
- @@ -0,0 +1,1135 @@
- +/*==========================================================================
- + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_ddma.c $
- + * $Revision: #10 $
- + * $Date: 2011/10/20 $
- + * $Change: 1869464 $
- + *
- + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
- + * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
- + * otherwise expressly agreed to in writing between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product under
- + * any End User Software License Agreement or Agreement for Licensed Product
- + * with Synopsys or any supplement thereto. You are permitted to use and
- + * redistribute this Software in source and binary forms, with or without
- + * modification, provided that redistributions of source code must retain this
- + * notice. You may not view, use, disclose, copy or distribute this file or
- + * any information contained herein except pursuant to this license grant from
- + * Synopsys. If you do not agree with this notice, including the disclaimer
- + * below, then you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
- + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- + * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
- + * ========================================================================== */
- +#ifndef DWC_DEVICE_ONLY
- +
- +/** @file
- + * This file contains Descriptor DMA support implementation for host mode.
- + */
- +
- +#include "dwc_otg_hcd.h"
- +#include "dwc_otg_regs.h"
- +
- +extern bool microframe_schedule;
- +
- +static inline uint8_t frame_list_idx(uint16_t frame)
- +{
- + return (frame & (MAX_FRLIST_EN_NUM - 1));
- +}
- +
- +static inline uint16_t desclist_idx_inc(uint16_t idx, uint16_t inc, uint8_t speed)
- +{
- + return (idx + inc) &
- + (((speed ==
- + DWC_OTG_EP_SPEED_HIGH) ? MAX_DMA_DESC_NUM_HS_ISOC :
- + MAX_DMA_DESC_NUM_GENERIC) - 1);
- +}
- +
- +static inline uint16_t desclist_idx_dec(uint16_t idx, uint16_t inc, uint8_t speed)
- +{
- + return (idx - inc) &
- + (((speed ==
- + DWC_OTG_EP_SPEED_HIGH) ? MAX_DMA_DESC_NUM_HS_ISOC :
- + MAX_DMA_DESC_NUM_GENERIC) - 1);
- +}
- +
- +static inline uint16_t max_desc_num(dwc_otg_qh_t * qh)
- +{
- + return (((qh->ep_type == UE_ISOCHRONOUS)
- + && (qh->dev_speed == DWC_OTG_EP_SPEED_HIGH))
- + ? MAX_DMA_DESC_NUM_HS_ISOC : MAX_DMA_DESC_NUM_GENERIC);
- +}
- +static inline uint16_t frame_incr_val(dwc_otg_qh_t * qh)
- +{
- + return ((qh->dev_speed == DWC_OTG_EP_SPEED_HIGH)
- + ? ((qh->interval + 8 - 1) / 8)
- + : qh->interval);
- +}
- +
- +static int desc_list_alloc(struct device *dev, dwc_otg_qh_t * qh)
- +{
- + int retval = 0;
- +
- + qh->desc_list = (dwc_otg_host_dma_desc_t *)
- + DWC_DMA_ALLOC(dev, sizeof(dwc_otg_host_dma_desc_t) * max_desc_num(qh),
- + &qh->desc_list_dma);
- +
- + if (!qh->desc_list) {
- + retval = -DWC_E_NO_MEMORY;
- + DWC_ERROR("%s: DMA descriptor list allocation failed\n", __func__);
- +
- + }
- +
- + dwc_memset(qh->desc_list, 0x00,
- + sizeof(dwc_otg_host_dma_desc_t) * max_desc_num(qh));
- +
- + qh->n_bytes =
- + (uint32_t *) DWC_ALLOC(sizeof(uint32_t) * max_desc_num(qh));
- +
- + if (!qh->n_bytes) {
- + retval = -DWC_E_NO_MEMORY;
- + DWC_ERROR
- + ("%s: Failed to allocate array for descriptors' size actual values\n",
- + __func__);
- +
- + }
- + return retval;
- +
- +}
- +
- +static void desc_list_free(struct device *dev, dwc_otg_qh_t * qh)
- +{
- + if (qh->desc_list) {
- + DWC_DMA_FREE(dev, max_desc_num(qh), qh->desc_list,
- + qh->desc_list_dma);
- + qh->desc_list = NULL;
- + }
- +
- + if (qh->n_bytes) {
- + DWC_FREE(qh->n_bytes);
- + qh->n_bytes = NULL;
- + }
- +}
- +
- +static int frame_list_alloc(dwc_otg_hcd_t * hcd)
- +{
- + struct device *dev = dwc_otg_hcd_to_dev(hcd);
- + int retval = 0;
- +
- + if (hcd->frame_list)
- + return 0;
- +
- + hcd->frame_list = DWC_DMA_ALLOC(dev, 4 * MAX_FRLIST_EN_NUM,
- + &hcd->frame_list_dma);
- + if (!hcd->frame_list) {
- + retval = -DWC_E_NO_MEMORY;
- + DWC_ERROR("%s: Frame List allocation failed\n", __func__);
- + }
- +
- + dwc_memset(hcd->frame_list, 0x00, 4 * MAX_FRLIST_EN_NUM);
- +
- + return retval;
- +}
- +
- +static void frame_list_free(dwc_otg_hcd_t * hcd)
- +{
- + struct device *dev = dwc_otg_hcd_to_dev(hcd);
- +
- + if (!hcd->frame_list)
- + return;
- +
- + DWC_DMA_FREE(dev, 4 * MAX_FRLIST_EN_NUM, hcd->frame_list, hcd->frame_list_dma);
- + hcd->frame_list = NULL;
- +}
- +
- +static void per_sched_enable(dwc_otg_hcd_t * hcd, uint16_t fr_list_en)
- +{
- +
- + hcfg_data_t hcfg;
- +
- + hcfg.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hcfg);
- +
- + if (hcfg.b.perschedena) {
- + /* already enabled */
- + return;
- + }
- +
- + DWC_WRITE_REG32(&hcd->core_if->host_if->host_global_regs->hflbaddr,
- + hcd->frame_list_dma);
- +
- + switch (fr_list_en) {
- + case 64:
- + hcfg.b.frlisten = 3;
- + break;
- + case 32:
- + hcfg.b.frlisten = 2;
- + break;
- + case 16:
- + hcfg.b.frlisten = 1;
- + break;
- + case 8:
- + hcfg.b.frlisten = 0;
- + break;
- + default:
- + break;
- + }
- +
- + hcfg.b.perschedena = 1;
- +
- + DWC_DEBUGPL(DBG_HCD, "Enabling Periodic schedule\n");
- + DWC_WRITE_REG32(&hcd->core_if->host_if->host_global_regs->hcfg, hcfg.d32);
- +
- +}
- +
- +static void per_sched_disable(dwc_otg_hcd_t * hcd)
- +{
- + hcfg_data_t hcfg;
- +
- + hcfg.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hcfg);
- +
- + if (!hcfg.b.perschedena) {
- + /* already disabled */
- + return;
- + }
- + hcfg.b.perschedena = 0;
- +
- + DWC_DEBUGPL(DBG_HCD, "Disabling Periodic schedule\n");
- + DWC_WRITE_REG32(&hcd->core_if->host_if->host_global_regs->hcfg, hcfg.d32);
- +}
- +
- +/*
- + * Activates/Deactivates FrameList entries for the channel
- + * based on endpoint servicing period.
- + */
- +void update_frame_list(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, uint8_t enable)
- +{
- + uint16_t i, j, inc;
- + dwc_hc_t *hc = NULL;
- +
- + if (!qh->channel) {
- + DWC_ERROR("qh->channel = %p", qh->channel);
- + return;
- + }
- +
- + if (!hcd) {
- + DWC_ERROR("------hcd = %p", hcd);
- + return;
- + }
- +
- + if (!hcd->frame_list) {
- + DWC_ERROR("-------hcd->frame_list = %p", hcd->frame_list);
- + return;
- + }
- +
- + hc = qh->channel;
- + inc = frame_incr_val(qh);
- + if (qh->ep_type == UE_ISOCHRONOUS)
- + i = frame_list_idx(qh->sched_frame);
- + else
- + i = 0;
- +
- + j = i;
- + do {
- + if (enable)
- + hcd->frame_list[j] |= (1 << hc->hc_num);
- + else
- + hcd->frame_list[j] &= ~(1 << hc->hc_num);
- + j = (j + inc) & (MAX_FRLIST_EN_NUM - 1);
- + }
- + while (j != i);
- + if (!enable)
- + return;
- + hc->schinfo = 0;
- + if (qh->channel->speed == DWC_OTG_EP_SPEED_HIGH) {
- + j = 1;
- + /* TODO - check this */
- + inc = (8 + qh->interval - 1) / qh->interval;
- + for (i = 0; i < inc; i++) {
- + hc->schinfo |= j;
- + j = j << qh->interval;
- + }
- + } else {
- + hc->schinfo = 0xff;
- + }
- +}
- +
- +#if 1
- +void dump_frame_list(dwc_otg_hcd_t * hcd)
- +{
- + int i = 0;
- + DWC_PRINTF("--FRAME LIST (hex) --\n");
- + for (i = 0; i < MAX_FRLIST_EN_NUM; i++) {
- + DWC_PRINTF("%x\t", hcd->frame_list[i]);
- + if (!(i % 8) && i)
- + DWC_PRINTF("\n");
- + }
- + DWC_PRINTF("\n----\n");
- +
- +}
- +#endif
- +
- +static void release_channel_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
- +{
- + dwc_hc_t *hc = qh->channel;
- + if (dwc_qh_is_non_per(qh)) {
- + if (!microframe_schedule)
- + hcd->non_periodic_channels--;
- + else
- + hcd->available_host_channels++;
- + } else
- + update_frame_list(hcd, qh, 0);
- +
- + /*
- + * The condition is added to prevent double cleanup try in case of device
- + * disconnect. See channel cleanup in dwc_otg_hcd_disconnect_cb().
- + */
- + if (hc->qh) {
- + dwc_otg_hc_cleanup(hcd->core_if, hc);
- + DWC_CIRCLEQ_INSERT_TAIL(&hcd->free_hc_list, hc, hc_list_entry);
- + hc->qh = NULL;
- + }
- +
- + qh->channel = NULL;
- + qh->ntd = 0;
- +
- + if (qh->desc_list) {
- + dwc_memset(qh->desc_list, 0x00,
- + sizeof(dwc_otg_host_dma_desc_t) * max_desc_num(qh));
- + }
- +}
- +
- +/**
- + * Initializes a QH structure's Descriptor DMA related members.
- + * Allocates memory for descriptor list.
- + * On first periodic QH, allocates memory for FrameList
- + * and enables periodic scheduling.
- + *
- + * @param hcd The HCD state structure for the DWC OTG controller.
- + * @param qh The QH to init.
- + *
- + * @return 0 if successful, negative error code otherwise.
- + */
- +int dwc_otg_hcd_qh_init_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
- +{
- + struct device *dev = dwc_otg_hcd_to_dev(hcd);
- + int retval = 0;
- +
- + if (qh->do_split) {
- + DWC_ERROR("SPLIT Transfers are not supported in Descriptor DMA.\n");
- + return -1;
- + }
- +
- + retval = desc_list_alloc(dev, qh);
- +
- + if ((retval == 0)
- + && (qh->ep_type == UE_ISOCHRONOUS || qh->ep_type == UE_INTERRUPT)) {
- + if (!hcd->frame_list) {
- + retval = frame_list_alloc(hcd);
- + /* Enable periodic schedule on first periodic QH */
- + if (retval == 0)
- + per_sched_enable(hcd, MAX_FRLIST_EN_NUM);
- + }
- + }
- +
- + qh->ntd = 0;
- +
- + return retval;
- +}
- +
- +/**
- + * Frees descriptor list memory associated with the QH.
- + * If QH is periodic and the last, frees FrameList memory
- + * and disables periodic scheduling.
- + *
- + * @param hcd The HCD state structure for the DWC OTG controller.
- + * @param qh The QH to init.
- + */
- +void dwc_otg_hcd_qh_free_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
- +{
- + struct device *dev = dwc_otg_hcd_to_dev(hcd);
- +
- + desc_list_free(dev, qh);
- +
- + /*
- + * Channel still assigned due to some reasons.
- + * Seen on Isoc URB dequeue. Channel halted but no subsequent
- + * ChHalted interrupt to release the channel. Afterwards
- + * when it comes here from endpoint disable routine
- + * channel remains assigned.
- + */
- + if (qh->channel)
- + release_channel_ddma(hcd, qh);
- +
- + if ((qh->ep_type == UE_ISOCHRONOUS || qh->ep_type == UE_INTERRUPT)
- + && (microframe_schedule || !hcd->periodic_channels) && hcd->frame_list) {
- +
- + per_sched_disable(hcd);
- + frame_list_free(hcd);
- + }
- +}
- +
- +static uint8_t frame_to_desc_idx(dwc_otg_qh_t * qh, uint16_t frame_idx)
- +{
- + if (qh->dev_speed == DWC_OTG_EP_SPEED_HIGH) {
- + /*
- + * Descriptor set(8 descriptors) index
- + * which is 8-aligned.
- + */
- + return (frame_idx & ((MAX_DMA_DESC_NUM_HS_ISOC / 8) - 1)) * 8;
- + } else {
- + return (frame_idx & (MAX_DMA_DESC_NUM_GENERIC - 1));
- + }
- +}
- +
- +/*
- + * Determine starting frame for Isochronous transfer.
- + * Few frames skipped to prevent race condition with HC.
- + */
- +static uint8_t calc_starting_frame(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh,
- + uint8_t * skip_frames)
- +{
- + uint16_t frame = 0;
- + hcd->frame_number = dwc_otg_hcd_get_frame_number(hcd);
- +
- + /* sched_frame is always frame number(not uFrame) both in FS and HS !! */
- +
- + /*
- + * skip_frames is used to limit activated descriptors number
- + * to avoid the situation when HC services the last activated
- + * descriptor firstly.
- + * Example for FS:
- + * Current frame is 1, scheduled frame is 3. Since HC always fetches the descriptor
- + * corresponding to curr_frame+1, the descriptor corresponding to frame 2
- + * will be fetched. If the number of descriptors is max=64 (or greather) the
- + * list will be fully programmed with Active descriptors and it is possible
- + * case(rare) that the latest descriptor(considering rollback) corresponding
- + * to frame 2 will be serviced first. HS case is more probable because, in fact,
- + * up to 11 uframes(16 in the code) may be skipped.
- + */
- + if (qh->dev_speed == DWC_OTG_EP_SPEED_HIGH) {
- + /*
- + * Consider uframe counter also, to start xfer asap.
- + * If half of the frame elapsed skip 2 frames otherwise
- + * just 1 frame.
- + * Starting descriptor index must be 8-aligned, so
- + * if the current frame is near to complete the next one
- + * is skipped as well.
- + */
- +
- + if (dwc_micro_frame_num(hcd->frame_number) >= 5) {
- + *skip_frames = 2 * 8;
- + frame = dwc_frame_num_inc(hcd->frame_number, *skip_frames);
- + } else {
- + *skip_frames = 1 * 8;
- + frame = dwc_frame_num_inc(hcd->frame_number, *skip_frames);
- + }
- +
- + frame = dwc_full_frame_num(frame);
- + } else {
- + /*
- + * Two frames are skipped for FS - the current and the next.
- + * But for descriptor programming, 1 frame(descriptor) is enough,
- + * see example above.
- + */
- + *skip_frames = 1;
- + frame = dwc_frame_num_inc(hcd->frame_number, 2);
- + }
- +
- + return frame;
- +}
- +
- +/*
- + * Calculate initial descriptor index for isochronous transfer
- + * based on scheduled frame.
- + */
- +static uint8_t recalc_initial_desc_idx(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
- +{
- + uint16_t frame = 0, fr_idx, fr_idx_tmp;
- + uint8_t skip_frames = 0;
- + /*
- + * With current ISOC processing algorithm the channel is being
- + * released when no more QTDs in the list(qh->ntd == 0).
- + * Thus this function is called only when qh->ntd == 0 and qh->channel == 0.
- + *
- + * So qh->channel != NULL branch is not used and just not removed from the
- + * source file. It is required for another possible approach which is,
- + * do not disable and release the channel when ISOC session completed,
- + * just move QH to inactive schedule until new QTD arrives.
- + * On new QTD, the QH moved back to 'ready' schedule,
- + * starting frame and therefore starting desc_index are recalculated.
- + * In this case channel is released only on ep_disable.
- + */
- +
- + /* Calculate starting descriptor index. For INTERRUPT endpoint it is always 0. */
- + if (qh->channel) {
- + frame = calc_starting_frame(hcd, qh, &skip_frames);
- + /*
- + * Calculate initial descriptor index based on FrameList current bitmap
- + * and servicing period.
- + */
- + fr_idx_tmp = frame_list_idx(frame);
- + fr_idx =
- + (MAX_FRLIST_EN_NUM + frame_list_idx(qh->sched_frame) -
- + fr_idx_tmp)
- + % frame_incr_val(qh);
- + fr_idx = (fr_idx + fr_idx_tmp) % MAX_FRLIST_EN_NUM;
- + } else {
- + qh->sched_frame = calc_starting_frame(hcd, qh, &skip_frames);
- + fr_idx = frame_list_idx(qh->sched_frame);
- + }
- +
- + qh->td_first = qh->td_last = frame_to_desc_idx(qh, fr_idx);
- +
- + return skip_frames;
- +}
- +
- +#define ISOC_URB_GIVEBACK_ASAP
- +
- +#define MAX_ISOC_XFER_SIZE_FS 1023
- +#define MAX_ISOC_XFER_SIZE_HS 3072
- +#define DESCNUM_THRESHOLD 4
- +
- +static void init_isoc_dma_desc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh,
- + uint8_t skip_frames)
- +{
- + struct dwc_otg_hcd_iso_packet_desc *frame_desc;
- + dwc_otg_qtd_t *qtd;
- + dwc_otg_host_dma_desc_t *dma_desc;
- + uint16_t idx, inc, n_desc, ntd_max, max_xfer_size;
- +
- + idx = qh->td_last;
- + inc = qh->interval;
- + n_desc = 0;
- +
- + ntd_max = (max_desc_num(qh) + qh->interval - 1) / qh->interval;
- + if (skip_frames && !qh->channel)
- + ntd_max = ntd_max - skip_frames / qh->interval;
- +
- + max_xfer_size =
- + (qh->dev_speed ==
- + DWC_OTG_EP_SPEED_HIGH) ? MAX_ISOC_XFER_SIZE_HS :
- + MAX_ISOC_XFER_SIZE_FS;
- +
- + DWC_CIRCLEQ_FOREACH(qtd, &qh->qtd_list, qtd_list_entry) {
- + while ((qh->ntd < ntd_max)
- + && (qtd->isoc_frame_index_last <
- + qtd->urb->packet_count)) {
- +
- + dma_desc = &qh->desc_list[idx];
- + dwc_memset(dma_desc, 0x00, sizeof(dwc_otg_host_dma_desc_t));
- +
- + frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index_last];
- +
- + if (frame_desc->length > max_xfer_size)
- + qh->n_bytes[idx] = max_xfer_size;
- + else
- + qh->n_bytes[idx] = frame_desc->length;
- + dma_desc->status.b_isoc.n_bytes = qh->n_bytes[idx];
- + dma_desc->status.b_isoc.a = 1;
- + dma_desc->status.b_isoc.sts = 0;
- +
- + dma_desc->buf = qtd->urb->dma + frame_desc->offset;
- +
- + qh->ntd++;
- +
- + qtd->isoc_frame_index_last++;
- +
- +#ifdef ISOC_URB_GIVEBACK_ASAP
- + /*
- + * Set IOC for each descriptor corresponding to the
- + * last frame of the URB.
- + */
- + if (qtd->isoc_frame_index_last ==
- + qtd->urb->packet_count)
- + dma_desc->status.b_isoc.ioc = 1;
- +
- +#endif
- + idx = desclist_idx_inc(idx, inc, qh->dev_speed);
- + n_desc++;
- +
- + }
- + qtd->in_process = 1;
- + }
- +
- + qh->td_last = idx;
- +
- +#ifdef ISOC_URB_GIVEBACK_ASAP
- + /* Set IOC for the last descriptor if descriptor list is full */
- + if (qh->ntd == ntd_max) {
- + idx = desclist_idx_dec(qh->td_last, inc, qh->dev_speed);
- + qh->desc_list[idx].status.b_isoc.ioc = 1;
- + }
- +#else
- + /*
- + * Set IOC bit only for one descriptor.
- + * Always try to be ahead of HW processing,
- + * i.e. on IOC generation driver activates next descriptors but
- + * core continues to process descriptors followed the one with IOC set.
- + */
- +
- + if (n_desc > DESCNUM_THRESHOLD) {
- + /*
- + * Move IOC "up". Required even if there is only one QTD
- + * in the list, cause QTDs migth continue to be queued,
- + * but during the activation it was only one queued.
- + * Actually more than one QTD might be in the list if this function called
- + * from XferCompletion - QTDs was queued during HW processing of the previous
- + * descriptor chunk.
- + */
- + idx = dwc_desclist_idx_dec(idx, inc * ((qh->ntd + 1) / 2), qh->dev_speed);
- + } else {
- + /*
- + * Set the IOC for the latest descriptor
- + * if either number of descriptor is not greather than threshold
- + * or no more new descriptors activated.
- + */
- + idx = dwc_desclist_idx_dec(qh->td_last, inc, qh->dev_speed);
- + }
- +
- + qh->desc_list[idx].status.b_isoc.ioc = 1;
- +#endif
- +}
- +
- +static void init_non_isoc_dma_desc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
- +{
- +
- + dwc_hc_t *hc;
- + dwc_otg_host_dma_desc_t *dma_desc;
- + dwc_otg_qtd_t *qtd;
- + int num_packets, len, n_desc = 0;
- +
- + hc = qh->channel;
- +
- + /*
- + * Start with hc->xfer_buff initialized in
- + * assign_and_init_hc(), then if SG transfer consists of multiple URBs,
- + * this pointer re-assigned to the buffer of the currently processed QTD.
- + * For non-SG request there is always one QTD active.
- + */
- +
- + DWC_CIRCLEQ_FOREACH(qtd, &qh->qtd_list, qtd_list_entry) {
- +
- + if (n_desc) {
- + /* SG request - more than 1 QTDs */
- + hc->xfer_buff = (uint8_t *)(uintptr_t)qtd->urb->dma +
- + qtd->urb->actual_length;
- + hc->xfer_len = qtd->urb->length - qtd->urb->actual_length;
- + }
- +
- + qtd->n_desc = 0;
- +
- + do {
- + dma_desc = &qh->desc_list[n_desc];
- + len = hc->xfer_len;
- +
- + if (len > MAX_DMA_DESC_SIZE)
- + len = MAX_DMA_DESC_SIZE - hc->max_packet + 1;
- +
- + if (hc->ep_is_in) {
- + if (len > 0) {
- + num_packets = (len + hc->max_packet - 1) / hc->max_packet;
- + } else {
- + /* Need 1 packet for transfer length of 0. */
- + num_packets = 1;
- + }
- + /* Always program an integral # of max packets for IN transfers. */
- + len = num_packets * hc->max_packet;
- + }
- +
- + dma_desc->status.b.n_bytes = len;
- +
- + qh->n_bytes[n_desc] = len;
- +
- + if ((qh->ep_type == UE_CONTROL)
- + && (qtd->control_phase == DWC_OTG_CONTROL_SETUP))
- + dma_desc->status.b.sup = 1; /* Setup Packet */
- +
- + dma_desc->status.b.a = 1; /* Active descriptor */
- + dma_desc->status.b.sts = 0;
- +
- + dma_desc->buf =
- + ((unsigned long)hc->xfer_buff & 0xffffffff);
- +
- + /*
- + * Last descriptor(or single) of IN transfer
- + * with actual size less than MaxPacket.
- + */
- + if (len > hc->xfer_len) {
- + hc->xfer_len = 0;
- + } else {
- + hc->xfer_buff += len;
- + hc->xfer_len -= len;
- + }
- +
- + qtd->n_desc++;
- + n_desc++;
- + }
- + while ((hc->xfer_len > 0) && (n_desc != MAX_DMA_DESC_NUM_GENERIC));
- +
- +
- + qtd->in_process = 1;
- +
- + if (qh->ep_type == UE_CONTROL)
- + break;
- +
- + if (n_desc == MAX_DMA_DESC_NUM_GENERIC)
- + break;
- + }
- +
- + if (n_desc) {
- + /* Request Transfer Complete interrupt for the last descriptor */
- + qh->desc_list[n_desc - 1].status.b.ioc = 1;
- + /* End of List indicator */
- + qh->desc_list[n_desc - 1].status.b.eol = 1;
- +
- + hc->ntd = n_desc;
- + }
- +}
- +
- +/**
- + * For Control and Bulk endpoints initializes descriptor list
- + * and starts the transfer.
- + *
- + * For Interrupt and Isochronous endpoints initializes descriptor list
- + * then updates FrameList, marking appropriate entries as active.
- + * In case of Isochronous, the starting descriptor index is calculated based
- + * on the scheduled frame, but only on the first transfer descriptor within a session.
- + * Then starts the transfer via enabling the channel.
- + * For Isochronous endpoint the channel is not halted on XferComplete
- + * interrupt so remains assigned to the endpoint(QH) until session is done.
- + *
- + * @param hcd The HCD state structure for the DWC OTG controller.
- + * @param qh The QH to init.
- + *
- + * @return 0 if successful, negative error code otherwise.
- + */
- +void dwc_otg_hcd_start_xfer_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
- +{
- + /* Channel is already assigned */
- + dwc_hc_t *hc = qh->channel;
- + uint8_t skip_frames = 0;
- +
- + switch (hc->ep_type) {
- + case DWC_OTG_EP_TYPE_CONTROL:
- + case DWC_OTG_EP_TYPE_BULK:
- + init_non_isoc_dma_desc(hcd, qh);
- +
- + dwc_otg_hc_start_transfer_ddma(hcd->core_if, hc);
- + break;
- + case DWC_OTG_EP_TYPE_INTR:
- + init_non_isoc_dma_desc(hcd, qh);
- +
- + update_frame_list(hcd, qh, 1);
- +
- + dwc_otg_hc_start_transfer_ddma(hcd->core_if, hc);
- + break;
- + case DWC_OTG_EP_TYPE_ISOC:
- +
- + if (!qh->ntd)
- + skip_frames = recalc_initial_desc_idx(hcd, qh);
- +
- + init_isoc_dma_desc(hcd, qh, skip_frames);
- +
- + if (!hc->xfer_started) {
- +
- + update_frame_list(hcd, qh, 1);
- +
- + /*
- + * Always set to max, instead of actual size.
- + * Otherwise ntd will be changed with
- + * channel being enabled. Not recommended.
- + *
- + */
- + hc->ntd = max_desc_num(qh);
- + /* Enable channel only once for ISOC */
- + dwc_otg_hc_start_transfer_ddma(hcd->core_if, hc);
- + }
- +
- + break;
- + default:
- +
- + break;
- + }
- +}
- +
- +static void complete_isoc_xfer_ddma(dwc_otg_hcd_t * hcd,
- + dwc_hc_t * hc,
- + dwc_otg_hc_regs_t * hc_regs,
- + dwc_otg_halt_status_e halt_status)
- +{
- + struct dwc_otg_hcd_iso_packet_desc *frame_desc;
- + dwc_otg_qtd_t *qtd, *qtd_tmp;
- + dwc_otg_qh_t *qh;
- + dwc_otg_host_dma_desc_t *dma_desc;
- + uint16_t idx, remain;
- + uint8_t urb_compl;
- +
- + qh = hc->qh;
- + idx = qh->td_first;
- +
- + if (hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) {
- + DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &hc->qh->qtd_list, qtd_list_entry)
- + qtd->in_process = 0;
- + return;
- + } else if ((halt_status == DWC_OTG_HC_XFER_AHB_ERR) ||
- + (halt_status == DWC_OTG_HC_XFER_BABBLE_ERR)) {
- + /*
- + * Channel is halted in these error cases.
- + * Considered as serious issues.
- + * Complete all URBs marking all frames as failed,
- + * irrespective whether some of the descriptors(frames) succeeded or no.
- + * Pass error code to completion routine as well, to
- + * update urb->status, some of class drivers might use it to stop
- + * queing transfer requests.
- + */
- + int err = (halt_status == DWC_OTG_HC_XFER_AHB_ERR)
- + ? (-DWC_E_IO)
- + : (-DWC_E_OVERFLOW);
- +
- + DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &hc->qh->qtd_list, qtd_list_entry) {
- + for (idx = 0; idx < qtd->urb->packet_count; idx++) {
- + frame_desc = &qtd->urb->iso_descs[idx];
- + frame_desc->status = err;
- + }
- + hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, err);
- + dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh);
- + }
- + return;
- + }
- +
- + DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &hc->qh->qtd_list, qtd_list_entry) {
- +
- + if (!qtd->in_process)
- + break;
- +
- + urb_compl = 0;
- +
- + do {
- +
- + dma_desc = &qh->desc_list[idx];
- +
- + frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index];
- + remain = hc->ep_is_in ? dma_desc->status.b_isoc.n_bytes : 0;
- +
- + if (dma_desc->status.b_isoc.sts == DMA_DESC_STS_PKTERR) {
- + /*
- + * XactError or, unable to complete all the transactions
- + * in the scheduled micro-frame/frame,
- + * both indicated by DMA_DESC_STS_PKTERR.
- + */
- + qtd->urb->error_count++;
- + frame_desc->actual_length = qh->n_bytes[idx] - remain;
- + frame_desc->status = -DWC_E_PROTOCOL;
- + } else {
- + /* Success */
- +
- + frame_desc->actual_length = qh->n_bytes[idx] - remain;
- + frame_desc->status = 0;
- + }
- +
- + if (++qtd->isoc_frame_index == qtd->urb->packet_count) {
- + /*
- + * urb->status is not used for isoc transfers here.
- + * The individual frame_desc status are used instead.
- + */
- +
- + hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0);
- + dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh);
- +
- + /*
- + * This check is necessary because urb_dequeue can be called
- + * from urb complete callback(sound driver example).
- + * All pending URBs are dequeued there, so no need for
- + * further processing.
- + */
- + if (hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) {
- + return;
- + }
- +
- + urb_compl = 1;
- +
- + }
- +
- + qh->ntd--;
- +
- + /* Stop if IOC requested descriptor reached */
- + if (dma_desc->status.b_isoc.ioc) {
- + idx = desclist_idx_inc(idx, qh->interval, hc->speed);
- + goto stop_scan;
- + }
- +
- + idx = desclist_idx_inc(idx, qh->interval, hc->speed);
- +
- + if (urb_compl)
- + break;
- + }
- + while (idx != qh->td_first);
- + }
- +stop_scan:
- + qh->td_first = idx;
- +}
- +
- +uint8_t update_non_isoc_urb_state_ddma(dwc_otg_hcd_t * hcd,
- + dwc_hc_t * hc,
- + dwc_otg_qtd_t * qtd,
- + dwc_otg_host_dma_desc_t * dma_desc,
- + dwc_otg_halt_status_e halt_status,
- + uint32_t n_bytes, uint8_t * xfer_done)
- +{
- +
- + uint16_t remain = hc->ep_is_in ? dma_desc->status.b.n_bytes : 0;
- + dwc_otg_hcd_urb_t *urb = qtd->urb;
- +
- + if (halt_status == DWC_OTG_HC_XFER_AHB_ERR) {
- + urb->status = -DWC_E_IO;
- + return 1;
- + }
- + if (dma_desc->status.b.sts == DMA_DESC_STS_PKTERR) {
- + switch (halt_status) {
- + case DWC_OTG_HC_XFER_STALL:
- + urb->status = -DWC_E_PIPE;
- + break;
- + case DWC_OTG_HC_XFER_BABBLE_ERR:
- + urb->status = -DWC_E_OVERFLOW;
- + break;
- + case DWC_OTG_HC_XFER_XACT_ERR:
- + urb->status = -DWC_E_PROTOCOL;
- + break;
- + default:
- + DWC_ERROR("%s: Unhandled descriptor error status (%d)\n", __func__,
- + halt_status);
- + break;
- + }
- + return 1;
- + }
- +
- + if (dma_desc->status.b.a == 1) {
- + DWC_DEBUGPL(DBG_HCDV,
- + "Active descriptor encountered on channel %d\n",
- + hc->hc_num);
- + return 0;
- + }
- +
- + if (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL) {
- + if (qtd->control_phase == DWC_OTG_CONTROL_DATA) {
- + urb->actual_length += n_bytes - remain;
- + if (remain || urb->actual_length == urb->length) {
- + /*
- + * For Control Data stage do not set urb->status=0 to prevent
- + * URB callback. Set it when Status phase done. See below.
- + */
- + *xfer_done = 1;
- + }
- +
- + } else if (qtd->control_phase == DWC_OTG_CONTROL_STATUS) {
- + urb->status = 0;
- + *xfer_done = 1;
- + }
- + /* No handling for SETUP stage */
- + } else {
- + /* BULK and INTR */
- + urb->actual_length += n_bytes - remain;
- + if (remain || urb->actual_length == urb->length) {
- + urb->status = 0;
- + *xfer_done = 1;
- + }
- + }
- +
- + return 0;
- +}
- +
- +static void complete_non_isoc_xfer_ddma(dwc_otg_hcd_t * hcd,
- + dwc_hc_t * hc,
- + dwc_otg_hc_regs_t * hc_regs,
- + dwc_otg_halt_status_e halt_status)
- +{
- + dwc_otg_hcd_urb_t *urb = NULL;
- + dwc_otg_qtd_t *qtd, *qtd_tmp;
- + dwc_otg_qh_t *qh;
- + dwc_otg_host_dma_desc_t *dma_desc;
- + uint32_t n_bytes, n_desc, i;
- + uint8_t failed = 0, xfer_done;
- +
- + n_desc = 0;
- +
- + qh = hc->qh;
- +
- + if (hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) {
- + DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &hc->qh->qtd_list, qtd_list_entry) {
- + qtd->in_process = 0;
- + }
- + return;
- + }
- +
- + DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &qh->qtd_list, qtd_list_entry) {
- +
- + urb = qtd->urb;
- +
- + n_bytes = 0;
- + xfer_done = 0;
- +
- + for (i = 0; i < qtd->n_desc; i++) {
- + dma_desc = &qh->desc_list[n_desc];
- +
- + n_bytes = qh->n_bytes[n_desc];
- +
- + failed =
- + update_non_isoc_urb_state_ddma(hcd, hc, qtd,
- + dma_desc,
- + halt_status, n_bytes,
- + &xfer_done);
- +
- + if (failed
- + || (xfer_done
- + && (urb->status != -DWC_E_IN_PROGRESS))) {
- +
- + hcd->fops->complete(hcd, urb->priv, urb,
- + urb->status);
- + dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh);
- +
- + if (failed)
- + goto stop_scan;
- + } else if (qh->ep_type == UE_CONTROL) {
- + if (qtd->control_phase == DWC_OTG_CONTROL_SETUP) {
- + if (urb->length > 0) {
- + qtd->control_phase = DWC_OTG_CONTROL_DATA;
- + } else {
- + qtd->control_phase = DWC_OTG_CONTROL_STATUS;
- + }
- + DWC_DEBUGPL(DBG_HCDV, " Control setup transaction done\n");
- + } else if (qtd->control_phase == DWC_OTG_CONTROL_DATA) {
- + if (xfer_done) {
- + qtd->control_phase = DWC_OTG_CONTROL_STATUS;
- + DWC_DEBUGPL(DBG_HCDV, " Control data transfer done\n");
- + } else if (i + 1 == qtd->n_desc) {
- + /*
- + * Last descriptor for Control data stage which is
- + * not completed yet.
- + */
- + dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
- + }
- + }
- + }
- +
- + n_desc++;
- + }
- +
- + }
- +
- +stop_scan:
- +
- + if (qh->ep_type != UE_CONTROL) {
- + /*
- + * Resetting the data toggle for bulk
- + * and interrupt endpoints in case of stall. See handle_hc_stall_intr()
- + */
- + if (halt_status == DWC_OTG_HC_XFER_STALL)
- + qh->data_toggle = DWC_OTG_HC_PID_DATA0;
- + else
- + dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
- + }
- +
- + if (halt_status == DWC_OTG_HC_XFER_COMPLETE) {
- + hcint_data_t hcint;
- + hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
- + if (hcint.b.nyet) {
- + /*
- + * Got a NYET on the last transaction of the transfer. It
- + * means that the endpoint should be in the PING state at the
- + * beginning of the next transfer.
- + */
- + qh->ping_state = 1;
- + clear_hc_int(hc_regs, nyet);
- + }
- +
- + }
- +
- +}
- +
- +/**
- + * This function is called from interrupt handlers.
- + * Scans the descriptor list, updates URB's status and
- + * calls completion routine for the URB if it's done.
- + * Releases the channel to be used by other transfers.
- + * In case of Isochronous endpoint the channel is not halted until
- + * the end of the session, i.e. QTD list is empty.
- + * If periodic channel released the FrameList is updated accordingly.
- + *
- + * Calls transaction selection routines to activate pending transfers.
- + *
- + * @param hcd The HCD state structure for the DWC OTG controller.
- + * @param hc Host channel, the transfer is completed on.
- + * @param hc_regs Host channel registers.
- + * @param halt_status Reason the channel is being halted,
- + * or just XferComplete for isochronous transfer
- + */
- +void dwc_otg_hcd_complete_xfer_ddma(dwc_otg_hcd_t * hcd,
- + dwc_hc_t * hc,
- + dwc_otg_hc_regs_t * hc_regs,
- + dwc_otg_halt_status_e halt_status)
- +{
- + uint8_t continue_isoc_xfer = 0;
- + dwc_otg_transaction_type_e tr_type;
- + dwc_otg_qh_t *qh = hc->qh;
- +
- + if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
- +
- + complete_isoc_xfer_ddma(hcd, hc, hc_regs, halt_status);
- +
- + /* Release the channel if halted or session completed */
- + if (halt_status != DWC_OTG_HC_XFER_COMPLETE ||
- + DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) {
- +
- + /* Halt the channel if session completed */
- + if (halt_status == DWC_OTG_HC_XFER_COMPLETE) {
- + dwc_otg_hc_halt(hcd->core_if, hc, halt_status);
- + }
- +
- + release_channel_ddma(hcd, qh);
- + dwc_otg_hcd_qh_remove(hcd, qh);
- + } else {
- + /* Keep in assigned schedule to continue transfer */
- + DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_assigned,
- + &qh->qh_list_entry);
- + continue_isoc_xfer = 1;
- +
- + }
- + /** @todo Consider the case when period exceeds FrameList size.
- + * Frame Rollover interrupt should be used.
- + */
- + } else {
- + /* Scan descriptor list to complete the URB(s), then release the channel */
- + complete_non_isoc_xfer_ddma(hcd, hc, hc_regs, halt_status);
- +
- + release_channel_ddma(hcd, qh);
- + dwc_otg_hcd_qh_remove(hcd, qh);
- +
- + if (!DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) {
- + /* Add back to inactive non-periodic schedule on normal completion */
- + dwc_otg_hcd_qh_add(hcd, qh);
- + }
- +
- + }
- + tr_type = dwc_otg_hcd_select_transactions(hcd);
- + if (tr_type != DWC_OTG_TRANSACTION_NONE || continue_isoc_xfer) {
- + if (continue_isoc_xfer) {
- + if (tr_type == DWC_OTG_TRANSACTION_NONE) {
- + tr_type = DWC_OTG_TRANSACTION_PERIODIC;
- + } else if (tr_type == DWC_OTG_TRANSACTION_NON_PERIODIC) {
- + tr_type = DWC_OTG_TRANSACTION_ALL;
- + }
- + }
- + dwc_otg_hcd_queue_transactions(hcd, tr_type);
- + }
- +}
- +
- +#endif /* DWC_DEVICE_ONLY */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h
- @@ -0,0 +1,421 @@
- +/* ==========================================================================
- + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_if.h $
- + * $Revision: #12 $
- + * $Date: 2011/10/26 $
- + * $Change: 1873028 $
- + *
- + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
- + * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
- + * otherwise expressly agreed to in writing between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product under
- + * any End User Software License Agreement or Agreement for Licensed Product
- + * with Synopsys or any supplement thereto. You are permitted to use and
- + * redistribute this Software in source and binary forms, with or without
- + * modification, provided that redistributions of source code must retain this
- + * notice. You may not view, use, disclose, copy or distribute this file or
- + * any information contained herein except pursuant to this license grant from
- + * Synopsys. If you do not agree with this notice, including the disclaimer
- + * below, then you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
- + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- + * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
- + * ========================================================================== */
- +#ifndef DWC_DEVICE_ONLY
- +#ifndef __DWC_HCD_IF_H__
- +#define __DWC_HCD_IF_H__
- +
- +#include "dwc_otg_core_if.h"
- +
- +/** @file
- + * This file defines DWC_OTG HCD Core API.
- + */
- +
- +struct dwc_otg_hcd;
- +typedef struct dwc_otg_hcd dwc_otg_hcd_t;
- +
- +struct dwc_otg_hcd_urb;
- +typedef struct dwc_otg_hcd_urb dwc_otg_hcd_urb_t;
- +
- +/** @name HCD Function Driver Callbacks */
- +/** @{ */
- +
- +/** This function is called whenever core switches to host mode. */
- +typedef int (*dwc_otg_hcd_start_cb_t) (dwc_otg_hcd_t * hcd);
- +
- +/** This function is called when device has been disconnected */
- +typedef int (*dwc_otg_hcd_disconnect_cb_t) (dwc_otg_hcd_t * hcd);
- +
- +/** Wrapper provides this function to HCD to core, so it can get hub information to which device is connected */
- +typedef int (*dwc_otg_hcd_hub_info_from_urb_cb_t) (dwc_otg_hcd_t * hcd,
- + void *urb_handle,
- + uint32_t * hub_addr,
- + uint32_t * port_addr);
- +/** Via this function HCD core gets device speed */
- +typedef int (*dwc_otg_hcd_speed_from_urb_cb_t) (dwc_otg_hcd_t * hcd,
- + void *urb_handle);
- +
- +/** This function is called when urb is completed */
- +typedef int (*dwc_otg_hcd_complete_urb_cb_t) (dwc_otg_hcd_t * hcd,
- + void *urb_handle,
- + dwc_otg_hcd_urb_t * dwc_otg_urb,
- + int32_t status);
- +
- +/** Via this function HCD core gets b_hnp_enable parameter */
- +typedef int (*dwc_otg_hcd_get_b_hnp_enable) (dwc_otg_hcd_t * hcd);
- +
- +struct dwc_otg_hcd_function_ops {
- + dwc_otg_hcd_start_cb_t start;
- + dwc_otg_hcd_disconnect_cb_t disconnect;
- + dwc_otg_hcd_hub_info_from_urb_cb_t hub_info;
- + dwc_otg_hcd_speed_from_urb_cb_t speed;
- + dwc_otg_hcd_complete_urb_cb_t complete;
- + dwc_otg_hcd_get_b_hnp_enable get_b_hnp_enable;
- +};
- +/** @} */
- +
- +/** @name HCD Core API */
- +/** @{ */
- +/** This function allocates dwc_otg_hcd structure and returns pointer on it. */
- +extern dwc_otg_hcd_t *dwc_otg_hcd_alloc_hcd(void);
- +
- +/** This function should be called to initiate HCD Core.
- + *
- + * @param hcd The HCD
- + * @param core_if The DWC_OTG Core
- + *
- + * Returns -DWC_E_NO_MEMORY if no enough memory.
- + * Returns 0 on success
- + */
- +extern int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if);
- +
- +/** Frees HCD
- + *
- + * @param hcd The HCD
- + */
- +extern void dwc_otg_hcd_remove(dwc_otg_hcd_t * hcd);
- +
- +/** This function should be called on every hardware interrupt.
- + *
- + * @param dwc_otg_hcd The HCD
- + *
- + * Returns non zero if interrupt is handled
- + * Return 0 if interrupt is not handled
- + */
- +extern int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd);
- +
- +/** This function is used to handle the fast interrupt
- + *
- + */
- +#ifdef CONFIG_ARM64
- +extern void dwc_otg_hcd_handle_fiq(void);
- +#else
- +extern void __attribute__ ((naked)) dwc_otg_hcd_handle_fiq(void);
- +#endif
- +
- +/**
- + * Returns private data set by
- + * dwc_otg_hcd_set_priv_data function.
- + *
- + * @param hcd The HCD
- + */
- +extern void *dwc_otg_hcd_get_priv_data(dwc_otg_hcd_t * hcd);
- +
- +/**
- + * Set private data.
- + *
- + * @param hcd The HCD
- + * @param priv_data pointer to be stored in private data
- + */
- +extern void dwc_otg_hcd_set_priv_data(dwc_otg_hcd_t * hcd, void *priv_data);
- +
- +/**
- + * This function initializes the HCD Core.
- + *
- + * @param hcd The HCD
- + * @param fops The Function Driver Operations data structure containing pointers to all callbacks.
- + *
- + * Returns -DWC_E_NO_DEVICE if Core is currently is in device mode.
- + * Returns 0 on success
- + */
- +extern int dwc_otg_hcd_start(dwc_otg_hcd_t * hcd,
- + struct dwc_otg_hcd_function_ops *fops);
- +
- +/**
- + * Halts the DWC_otg host mode operations in a clean manner. USB transfers are
- + * stopped.
- + *
- + * @param hcd The HCD
- + */
- +extern void dwc_otg_hcd_stop(dwc_otg_hcd_t * hcd);
- +
- +/**
- + * Handles hub class-specific requests.
- + *
- + * @param dwc_otg_hcd The HCD
- + * @param typeReq Request Type
- + * @param wValue wValue from control request
- + * @param wIndex wIndex from control request
- + * @param buf data buffer
- + * @param wLength data buffer length
- + *
- + * Returns -DWC_E_INVALID if invalid argument is passed
- + * Returns 0 on success
- + */
- +extern int dwc_otg_hcd_hub_control(dwc_otg_hcd_t * dwc_otg_hcd,
- + uint16_t typeReq, uint16_t wValue,
- + uint16_t wIndex, uint8_t * buf,
- + uint16_t wLength);
- +
- +/**
- + * Returns otg port number.
- + *
- + * @param hcd The HCD
- + */
- +extern uint32_t dwc_otg_hcd_otg_port(dwc_otg_hcd_t * hcd);
- +
- +/**
- + * Returns OTG version - either 1.3 or 2.0.
- + *
- + * @param core_if The core_if structure pointer
- + */
- +extern uint16_t dwc_otg_get_otg_version(dwc_otg_core_if_t * core_if);
- +
- +/**
- + * Returns 1 if currently core is acting as B host, and 0 otherwise.
- + *
- + * @param hcd The HCD
- + */
- +extern uint32_t dwc_otg_hcd_is_b_host(dwc_otg_hcd_t * hcd);
- +
- +/**
- + * Returns current frame number.
- + *
- + * @param hcd The HCD
- + */
- +extern int dwc_otg_hcd_get_frame_number(dwc_otg_hcd_t * hcd);
- +
- +/**
- + * Dumps hcd state.
- + *
- + * @param hcd The HCD
- + */
- +extern void dwc_otg_hcd_dump_state(dwc_otg_hcd_t * hcd);
- +
- +/**
- + * Dump the average frame remaining at SOF. This can be used to
- + * determine average interrupt latency. Frame remaining is also shown for
- + * start transfer and two additional sample points.
- + * Currently this function is not implemented.
- + *
- + * @param hcd The HCD
- + */
- +extern void dwc_otg_hcd_dump_frrem(dwc_otg_hcd_t * hcd);
- +
- +/**
- + * Sends LPM transaction to the local device.
- + *
- + * @param hcd The HCD
- + * @param devaddr Device Address
- + * @param hird Host initiated resume duration
- + * @param bRemoteWake Value of bRemoteWake field in LPM transaction
- + *
- + * Returns negative value if sending LPM transaction was not succeeded.
- + * Returns 0 on success.
- + */
- +extern int dwc_otg_hcd_send_lpm(dwc_otg_hcd_t * hcd, uint8_t devaddr,
- + uint8_t hird, uint8_t bRemoteWake);
- +
- +/* URB interface */
- +
- +/**
- + * Allocates memory for dwc_otg_hcd_urb structure.
- + * Allocated memory should be freed by call of DWC_FREE.
- + *
- + * @param hcd The HCD
- + * @param iso_desc_count Count of ISOC descriptors
- + * @param atomic_alloc Specefies whether to perform atomic allocation.
- + */
- +extern dwc_otg_hcd_urb_t *dwc_otg_hcd_urb_alloc(dwc_otg_hcd_t * hcd,
- + int iso_desc_count,
- + int atomic_alloc);
- +
- +/**
- + * Set pipe information in URB.
- + *
- + * @param hcd_urb DWC_OTG URB
- + * @param devaddr Device Address
- + * @param ep_num Endpoint Number
- + * @param ep_type Endpoint Type
- + * @param ep_dir Endpoint Direction
- + * @param mps Max Packet Size
- + */
- +extern void dwc_otg_hcd_urb_set_pipeinfo(dwc_otg_hcd_urb_t * hcd_urb,
- + uint8_t devaddr, uint8_t ep_num,
- + uint8_t ep_type, uint8_t ep_dir,
- + uint16_t mps);
- +
- +/* Transfer flags */
- +#define URB_GIVEBACK_ASAP 0x1
- +#define URB_SEND_ZERO_PACKET 0x2
- +
- +/**
- + * Sets dwc_otg_hcd_urb parameters.
- + *
- + * @param urb DWC_OTG URB allocated by dwc_otg_hcd_urb_alloc function.
- + * @param urb_handle Unique handle for request, this will be passed back
- + * to function driver in completion callback.
- + * @param buf The buffer for the data
- + * @param dma The DMA buffer for the data
- + * @param buflen Transfer length
- + * @param sp Buffer for setup data
- + * @param sp_dma DMA address of setup data buffer
- + * @param flags Transfer flags
- + * @param interval Polling interval for interrupt or isochronous transfers.
- + */
- +extern void dwc_otg_hcd_urb_set_params(dwc_otg_hcd_urb_t * urb,
- + void *urb_handle, void *buf,
- + dwc_dma_t dma, uint32_t buflen, void *sp,
- + dwc_dma_t sp_dma, uint32_t flags,
- + uint16_t interval);
- +
- +/** Gets status from dwc_otg_hcd_urb
- + *
- + * @param dwc_otg_urb DWC_OTG URB
- + */
- +extern uint32_t dwc_otg_hcd_urb_get_status(dwc_otg_hcd_urb_t * dwc_otg_urb);
- +
- +/** Gets actual length from dwc_otg_hcd_urb
- + *
- + * @param dwc_otg_urb DWC_OTG URB
- + */
- +extern uint32_t dwc_otg_hcd_urb_get_actual_length(dwc_otg_hcd_urb_t *
- + dwc_otg_urb);
- +
- +/** Gets error count from dwc_otg_hcd_urb. Only for ISOC URBs
- + *
- + * @param dwc_otg_urb DWC_OTG URB
- + */
- +extern uint32_t dwc_otg_hcd_urb_get_error_count(dwc_otg_hcd_urb_t *
- + dwc_otg_urb);
- +
- +/** Set ISOC descriptor offset and length
- + *
- + * @param dwc_otg_urb DWC_OTG URB
- + * @param desc_num ISOC descriptor number
- + * @param offset Offset from beginig of buffer.
- + * @param length Transaction length
- + */
- +extern void dwc_otg_hcd_urb_set_iso_desc_params(dwc_otg_hcd_urb_t * dwc_otg_urb,
- + int desc_num, uint32_t offset,
- + uint32_t length);
- +
- +/** Get status of ISOC descriptor, specified by desc_num
- + *
- + * @param dwc_otg_urb DWC_OTG URB
- + * @param desc_num ISOC descriptor number
- + */
- +extern uint32_t dwc_otg_hcd_urb_get_iso_desc_status(dwc_otg_hcd_urb_t *
- + dwc_otg_urb, int desc_num);
- +
- +/** Get actual length of ISOC descriptor, specified by desc_num
- + *
- + * @param dwc_otg_urb DWC_OTG URB
- + * @param desc_num ISOC descriptor number
- + */
- +extern uint32_t dwc_otg_hcd_urb_get_iso_desc_actual_length(dwc_otg_hcd_urb_t *
- + dwc_otg_urb,
- + int desc_num);
- +
- +/** Queue URB. After transfer is completes, the complete callback will be called with the URB status
- + *
- + * @param dwc_otg_hcd The HCD
- + * @param dwc_otg_urb DWC_OTG URB
- + * @param ep_handle Out parameter for returning endpoint handle
- + * @param atomic_alloc Flag to do atomic allocation if needed
- + *
- + * Returns -DWC_E_NO_DEVICE if no device is connected.
- + * Returns -DWC_E_NO_MEMORY if there is no enough memory.
- + * Returns 0 on success.
- + */
- +extern int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_t * dwc_otg_hcd,
- + dwc_otg_hcd_urb_t * dwc_otg_urb,
- + void **ep_handle, int atomic_alloc);
- +
- +/** De-queue the specified URB
- + *
- + * @param dwc_otg_hcd The HCD
- + * @param dwc_otg_urb DWC_OTG URB
- + */
- +extern int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_t * dwc_otg_hcd,
- + dwc_otg_hcd_urb_t * dwc_otg_urb);
- +
- +/** Frees resources in the DWC_otg controller related to a given endpoint.
- + * Any URBs for the endpoint must already be dequeued.
- + *
- + * @param hcd The HCD
- + * @param ep_handle Endpoint handle, returned by dwc_otg_hcd_urb_enqueue function
- + * @param retry Number of retries if there are queued transfers.
- + *
- + * Returns -DWC_E_INVALID if invalid arguments are passed.
- + * Returns 0 on success
- + */
- +extern int dwc_otg_hcd_endpoint_disable(dwc_otg_hcd_t * hcd, void *ep_handle,
- + int retry);
- +
- +/* Resets the data toggle in qh structure. This function can be called from
- + * usb_clear_halt routine.
- + *
- + * @param hcd The HCD
- + * @param ep_handle Endpoint handle, returned by dwc_otg_hcd_urb_enqueue function
- + *
- + * Returns -DWC_E_INVALID if invalid arguments are passed.
- + * Returns 0 on success
- + */
- +extern int dwc_otg_hcd_endpoint_reset(dwc_otg_hcd_t * hcd, void *ep_handle);
- +
- +/** Returns 1 if status of specified port is changed and 0 otherwise.
- + *
- + * @param hcd The HCD
- + * @param port Port number
- + */
- +extern int dwc_otg_hcd_is_status_changed(dwc_otg_hcd_t * hcd, int port);
- +
- +/** Call this function to check if bandwidth was allocated for specified endpoint.
- + * Only for ISOC and INTERRUPT endpoints.
- + *
- + * @param hcd The HCD
- + * @param ep_handle Endpoint handle
- + */
- +extern int dwc_otg_hcd_is_bandwidth_allocated(dwc_otg_hcd_t * hcd,
- + void *ep_handle);
- +
- +/** Call this function to check if bandwidth was freed for specified endpoint.
- + *
- + * @param hcd The HCD
- + * @param ep_handle Endpoint handle
- + */
- +extern int dwc_otg_hcd_is_bandwidth_freed(dwc_otg_hcd_t * hcd, void *ep_handle);
- +
- +/** Returns bandwidth allocated for specified endpoint in microseconds.
- + * Only for ISOC and INTERRUPT endpoints.
- + *
- + * @param hcd The HCD
- + * @param ep_handle Endpoint handle
- + */
- +extern uint8_t dwc_otg_hcd_get_ep_bandwidth(dwc_otg_hcd_t * hcd,
- + void *ep_handle);
- +
- +/** @} */
- +
- +#endif /* __DWC_HCD_IF_H__ */
- +#endif /* DWC_DEVICE_ONLY */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
- @@ -0,0 +1,2757 @@
- +/* ==========================================================================
- + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_intr.c $
- + * $Revision: #89 $
- + * $Date: 2011/10/20 $
- + * $Change: 1869487 $
- + *
- + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
- + * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
- + * otherwise expressly agreed to in writing between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product under
- + * any End User Software License Agreement or Agreement for Licensed Product
- + * with Synopsys or any supplement thereto. You are permitted to use and
- + * redistribute this Software in source and binary forms, with or without
- + * modification, provided that redistributions of source code must retain this
- + * notice. You may not view, use, disclose, copy or distribute this file or
- + * any information contained herein except pursuant to this license grant from
- + * Synopsys. If you do not agree with this notice, including the disclaimer
- + * below, then you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
- + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- + * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
- + * ========================================================================== */
- +#ifndef DWC_DEVICE_ONLY
- +
- +#include "dwc_otg_hcd.h"
- +#include "dwc_otg_regs.h"
- +
- +#include <linux/jiffies.h>
- +#ifdef CONFIG_ARM
- +#include <asm/fiq.h>
- +#endif
- +
- +extern bool microframe_schedule;
- +
- +/** @file
- + * This file contains the implementation of the HCD Interrupt handlers.
- + */
- +
- +int fiq_done, int_done;
- +
- +#ifdef FIQ_DEBUG
- +char buffer[1000*16];
- +int wptr;
- +void notrace _fiq_print(FIQDBG_T dbg_lvl, char *fmt, ...)
- +{
- + FIQDBG_T dbg_lvl_req = FIQDBG_PORTHUB;
- + va_list args;
- + char text[17];
- + hfnum_data_t hfnum = { .d32 = FIQ_READ(dwc_regs_base + 0x408) };
- +
- + 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);
- + va_end(args);
- +
- + memcpy(buffer + wptr, text, 16);
- + wptr = (wptr + 16) % sizeof(buffer);
- + local_fiq_enable();
- + }
- +}
- +#endif
- +
- +/** 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;
- +
- +#endif
- +
- + gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
- + gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
- +
- + /* Exit from ISR if core is hibernated */
- + if (core_if->hibernation_suspend == 1) {
- + goto exit_handler_routine;
- + }
- + DWC_SPINLOCK(dwc_otg_hcd->lock);
- + /* Check if HOST Mode */
- + if (dwc_otg_is_host_mode(core_if)) {
- + if (fiq_enable) {
- + local_fiq_disable();
- + fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock);
- + /* 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) {
- + fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock);
- + local_fiq_enable();
- + }
- +
- + if (!gintsts.d32) {
- + goto exit_handler_routine;
- + }
- +
- +#ifdef DEBUG
- + // We should be OK doing this because the common interrupts should already have been serviced
- + /* Don't print debug message in the interrupt handler on SOF */
- +#ifndef DEBUG_SOF
- + if (gintsts.d32 != DWC_SOF_INTR_MASK)
- +#endif
- + DWC_DEBUGPL(DBG_HCDI, "\n");
- +#endif
- +
- +#ifdef DEBUG
- +#ifndef DEBUG_SOF
- + if (gintsts.d32 != DWC_SOF_INTR_MASK)
- +#endif
- + DWC_DEBUGPL(DBG_HCDI,
- + "DWC OTG HCD Interrupt Detected gintsts&gintmsk=0x%08x core_if=%p\n",
- + 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) {
- + retval |= dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd);
- + }
- +
- + if (gintsts.b.rxstsqlvl) {
- + retval |=
- + dwc_otg_hcd_handle_rx_status_q_level_intr
- + (dwc_otg_hcd);
- + }
- + if (gintsts.b.nptxfempty) {
- + retval |=
- + dwc_otg_hcd_handle_np_tx_fifo_empty_intr
- + (dwc_otg_hcd);
- + }
- + if (gintsts.b.i2cintr) {
- + /** @todo Implement i2cintr handler. */
- + }
- + if (gintsts.b.portintr) {
- +
- + gintmsk_data_t gintmsk = { .b.portintr = 1};
- + retval |= dwc_otg_hcd_handle_port_intr(dwc_otg_hcd);
- + if (fiq_enable) {
- + local_fiq_disable();
- + fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock);
- + DWC_MODIFY_REG32(&dwc_otg_hcd->core_if->core_global_regs->gintmsk, 0, gintmsk.d32);
- + fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock);
- + local_fiq_enable();
- + } else {
- + DWC_MODIFY_REG32(&dwc_otg_hcd->core_if->core_global_regs->gintmsk, 0, gintmsk.d32);
- + }
- + }
- + if (gintsts.b.hcintr) {
- + retval |= dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd);
- + }
- + if (gintsts.b.ptxfempty) {
- + retval |=
- + dwc_otg_hcd_handle_perio_tx_fifo_empty_intr
- + (dwc_otg_hcd);
- + }
- +#ifdef DEBUG
- +#ifndef DEBUG_SOF
- + if (gintsts.d32 != DWC_SOF_INTR_MASK)
- +#endif
- + {
- + DWC_DEBUGPL(DBG_HCDI,
- + "DWC OTG HCD Finished Servicing Interrupts\n");
- + DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD gintsts=0x%08x\n",
- + DWC_READ_REG32(&global_regs->gintsts));
- + DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD gintmsk=0x%08x\n",
- + DWC_READ_REG32(&global_regs->gintmsk));
- + }
- +#endif
- +
- +#ifdef DEBUG
- +#ifndef DEBUG_SOF
- + if (gintsts.d32 != DWC_SOF_INTR_MASK)
- +#endif
- + DWC_DEBUGPL(DBG_HCDI, "\n");
- +#endif
- +
- + }
- +
- +exit_handler_routine:
- + if (fiq_enable) {
- + gintmsk_data_t gintmsk_new;
- + haintmsk_data_t haintmsk_new;
- + local_fiq_disable();
- + fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock);
- + 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)) {
- + if (dwc_otg_hcd->fiq_state->mphi_regs.swirq_clr) {
- + DWC_WRITE_REG32(dwc_otg_hcd->fiq_state->mphi_regs.swirq_clr, 1);
- + } else {
- + 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);
- + fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock);
- + local_fiq_enable();
- +
- + 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_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");
- + }
- + }
- +
- + DWC_SPINUNLOCK(dwc_otg_hcd->lock);
- + return retval;
- +}
- +
- +#ifdef DWC_TRACK_MISSED_SOFS
- +
- +#warning Compiling code to track missed SOFs
- +#define FRAME_NUM_ARRAY_SIZE 1000
- +/**
- + * This function is for debug only.
- + */
- +static inline void track_missed_sofs(uint16_t curr_frame_number)
- +{
- + static uint16_t frame_num_array[FRAME_NUM_ARRAY_SIZE];
- + static uint16_t last_frame_num_array[FRAME_NUM_ARRAY_SIZE];
- + static int frame_num_idx = 0;
- + static uint16_t last_frame_num = DWC_HFNUM_MAX_FRNUM;
- + static int dumped_frame_num_array = 0;
- +
- + if (frame_num_idx < FRAME_NUM_ARRAY_SIZE) {
- + if (((last_frame_num + 1) & DWC_HFNUM_MAX_FRNUM) !=
- + curr_frame_number) {
- + frame_num_array[frame_num_idx] = curr_frame_number;
- + last_frame_num_array[frame_num_idx++] = last_frame_num;
- + }
- + } else if (!dumped_frame_num_array) {
- + int i;
- + DWC_PRINTF("Frame Last Frame\n");
- + DWC_PRINTF("----- ----------\n");
- + for (i = 0; i < FRAME_NUM_ARRAY_SIZE; i++) {
- + DWC_PRINTF("0x%04x 0x%04x\n",
- + frame_num_array[i], last_frame_num_array[i]);
- + }
- + dumped_frame_num_array = 1;
- + }
- + last_frame_num = curr_frame_number;
- +}
- +#endif
- +
- +/**
- + * Handles the start-of-frame interrupt in host mode. Non-periodic
- + * transactions may be queued to the DWC_otg controller for the current
- + * (micro)frame. Periodic transactions may be queued to the controller for the
- + * next (micro)frame.
- + */
- +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;
- + int did_something = 0;
- + int32_t next_sched_frame = -1;
- +
- + hfnum.d32 =
- + DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum);
- +
- +#ifdef DEBUG_SOF
- + DWC_DEBUGPL(DBG_HCD, "--Start of Frame Interrupt--\n");
- +#endif
- + hcd->frame_number = hfnum.b.frnum;
- +
- +#ifdef DEBUG
- + hcd->frrem_accum += hfnum.b.frrem;
- + hcd->frrem_samples++;
- +#endif
- +
- +#ifdef DWC_TRACK_MISSED_SOFS
- + track_missed_sofs(hcd->frame_number);
- +#endif
- + /* Determine whether any periodic QHs should be executed. */
- + qh_entry = DWC_LIST_FIRST(&hcd->periodic_sched_inactive);
- + while (qh_entry != &hcd->periodic_sched_inactive) {
- + qh = DWC_LIST_ENTRY(qh_entry, dwc_otg_qh_t, qh_list_entry);
- + qh_entry = qh_entry->next;
- + if (dwc_frame_num_le(qh->sched_frame, hcd->frame_number)) {
- +
- + /*
- + * Move QH to the ready list to be executed next
- + * (micro)frame.
- + */
- + DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready,
- + &qh->qh_list_entry);
- +
- + did_something = 1;
- + }
- + else
- + {
- + if(next_sched_frame < 0 || dwc_frame_num_le(qh->sched_frame, next_sched_frame))
- + {
- + next_sched_frame = qh->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) {
- + dwc_otg_hcd_queue_transactions(hcd, tr_type);
- + did_something = 1;
- + }
- +
- + /* 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;
- +}
- +
- +/** Handles the Rx Status Queue Level Interrupt, which indicates that there is at
- + * least one packet in the Rx FIFO. The packets are moved from the FIFO to
- + * memory if the DWC_otg controller is operating in Slave mode. */
- +int32_t dwc_otg_hcd_handle_rx_status_q_level_intr(dwc_otg_hcd_t * dwc_otg_hcd)
- +{
- + host_grxsts_data_t grxsts;
- + dwc_hc_t *hc = NULL;
- +
- + DWC_DEBUGPL(DBG_HCD, "--RxStsQ Level Interrupt--\n");
- +
- + grxsts.d32 =
- + DWC_READ_REG32(&dwc_otg_hcd->core_if->core_global_regs->grxstsp);
- +
- + hc = dwc_otg_hcd->hc_ptr_array[grxsts.b.chnum];
- + if (!hc) {
- + DWC_ERROR("Unable to get corresponding channel\n");
- + return 0;
- + }
- +
- + /* Packet Status */
- + DWC_DEBUGPL(DBG_HCDV, " Ch num = %d\n", grxsts.b.chnum);
- + DWC_DEBUGPL(DBG_HCDV, " Count = %d\n", grxsts.b.bcnt);
- + DWC_DEBUGPL(DBG_HCDV, " DPID = %d, hc.dpid = %d\n", grxsts.b.dpid,
- + hc->data_pid_start);
- + DWC_DEBUGPL(DBG_HCDV, " PStatus = %d\n", grxsts.b.pktsts);
- +
- + switch (grxsts.b.pktsts) {
- + case DWC_GRXSTS_PKTSTS_IN:
- + /* Read the data into the host buffer. */
- + if (grxsts.b.bcnt > 0) {
- + dwc_otg_read_packet(dwc_otg_hcd->core_if,
- + hc->xfer_buff, grxsts.b.bcnt);
- +
- + /* Update the HC fields for the next packet received. */
- + hc->xfer_count += grxsts.b.bcnt;
- + hc->xfer_buff += grxsts.b.bcnt;
- + }
- +
- + case DWC_GRXSTS_PKTSTS_IN_XFER_COMP:
- + case DWC_GRXSTS_PKTSTS_DATA_TOGGLE_ERR:
- + case DWC_GRXSTS_PKTSTS_CH_HALTED:
- + /* Handled in interrupt, just ignore data */
- + break;
- + default:
- + DWC_ERROR("RX_STS_Q Interrupt: Unknown status %d\n",
- + grxsts.b.pktsts);
- + break;
- + }
- +
- + return 1;
- +}
- +
- +/** This interrupt occurs when the non-periodic Tx FIFO is half-empty. More
- + * data packets may be written to the FIFO for OUT transfers. More requests
- + * may be written to the non-periodic request queue for IN transfers. This
- + * interrupt is enabled only in Slave mode. */
- +int32_t dwc_otg_hcd_handle_np_tx_fifo_empty_intr(dwc_otg_hcd_t * dwc_otg_hcd)
- +{
- + DWC_DEBUGPL(DBG_HCD, "--Non-Periodic TxFIFO Empty Interrupt--\n");
- + dwc_otg_hcd_queue_transactions(dwc_otg_hcd,
- + DWC_OTG_TRANSACTION_NON_PERIODIC);
- + return 1;
- +}
- +
- +/** This interrupt occurs when the periodic Tx FIFO is half-empty. More data
- + * packets may be written to the FIFO for OUT transfers. More requests may be
- + * written to the periodic request queue for IN transfers. This interrupt is
- + * enabled only in Slave mode. */
- +int32_t dwc_otg_hcd_handle_perio_tx_fifo_empty_intr(dwc_otg_hcd_t * dwc_otg_hcd)
- +{
- + DWC_DEBUGPL(DBG_HCD, "--Periodic TxFIFO Empty Interrupt--\n");
- + dwc_otg_hcd_queue_transactions(dwc_otg_hcd,
- + DWC_OTG_TRANSACTION_PERIODIC);
- + return 1;
- +}
- +
- +/** There are multiple conditions that can cause a port interrupt. This function
- + * determines which interrupt conditions have occurred and handles them
- + * appropriately. */
- +int32_t dwc_otg_hcd_handle_port_intr(dwc_otg_hcd_t * dwc_otg_hcd)
- +{
- + int retval = 0;
- + hprt0_data_t hprt0;
- + hprt0_data_t hprt0_modify;
- +
- + hprt0.d32 = DWC_READ_REG32(dwc_otg_hcd->core_if->host_if->hprt0);
- + hprt0_modify.d32 = DWC_READ_REG32(dwc_otg_hcd->core_if->host_if->hprt0);
- +
- + /* Clear appropriate bits in HPRT0 to clear the interrupt bit in
- + * GINTSTS */
- +
- + hprt0_modify.b.prtena = 0;
- + hprt0_modify.b.prtconndet = 0;
- + hprt0_modify.b.prtenchng = 0;
- + hprt0_modify.b.prtovrcurrchng = 0;
- +
- + /* Port Connect Detected
- + * Set flag and clear if detected */
- + if (dwc_otg_hcd->core_if->hibernation_suspend == 1) {
- + // Dont modify port status if we are in hibernation state
- + hprt0_modify.b.prtconndet = 1;
- + hprt0_modify.b.prtenchng = 1;
- + DWC_WRITE_REG32(dwc_otg_hcd->core_if->host_if->hprt0, hprt0_modify.d32);
- + hprt0.d32 = DWC_READ_REG32(dwc_otg_hcd->core_if->host_if->hprt0);
- + return retval;
- + }
- +
- + if (hprt0.b.prtconndet) {
- + /** @todo - check if steps performed in 'else' block should be perfromed regardles adp */
- + if (dwc_otg_hcd->core_if->adp_enable &&
- + dwc_otg_hcd->core_if->adp.vbuson_timer_started == 1) {
- + DWC_PRINTF("PORT CONNECT DETECTED ----------------\n");
- + DWC_TIMER_CANCEL(dwc_otg_hcd->core_if->adp.vbuson_timer);
- + dwc_otg_hcd->core_if->adp.vbuson_timer_started = 0;
- + /* TODO - check if this is required, as
- + * host initialization was already performed
- + * after initial ADP probing
- + */
- + /*dwc_otg_hcd->core_if->adp.vbuson_timer_started = 0;
- + dwc_otg_core_init(dwc_otg_hcd->core_if);
- + dwc_otg_enable_global_interrupts(dwc_otg_hcd->core_if);
- + cil_hcd_start(dwc_otg_hcd->core_if);*/
- + } else {
- +
- + DWC_DEBUGPL(DBG_HCD, "--Port Interrupt HPRT0=0x%08x "
- + "Port Connect Detected--\n", hprt0.d32);
- + dwc_otg_hcd->flags.b.port_connect_status_change = 1;
- + dwc_otg_hcd->flags.b.port_connect_status = 1;
- + hprt0_modify.b.prtconndet = 1;
- +
- + /* B-Device has connected, Delete the connection timer. */
- + DWC_TIMER_CANCEL(dwc_otg_hcd->conn_timer);
- + }
- + /* The Hub driver asserts a reset when it sees port connect
- + * status change flag */
- + retval |= 1;
- + }
- +
- + /* Port Enable Changed
- + * Clear if detected - Set internal flag if disabled */
- + if (hprt0.b.prtenchng) {
- + DWC_DEBUGPL(DBG_HCD, " --Port Interrupt HPRT0=0x%08x "
- + "Port Enable Changed--\n", hprt0.d32);
- + hprt0_modify.b.prtenchng = 1;
- + if (hprt0.b.prtena == 1) {
- + hfir_data_t hfir;
- + int do_reset = 0;
- + dwc_otg_core_params_t *params =
- + dwc_otg_hcd->core_if->core_params;
- + dwc_otg_core_global_regs_t *global_regs =
- + dwc_otg_hcd->core_if->core_global_regs;
- + dwc_otg_host_if_t *host_if =
- + dwc_otg_hcd->core_if->host_if;
- +
- + dwc_otg_hcd->flags.b.port_speed = hprt0.b.prtspd;
- + if (microframe_schedule)
- + init_hcd_usecs(dwc_otg_hcd);
- +
- + /* Every time when port enables calculate
- + * HFIR.FrInterval
- + */
- + hfir.d32 = DWC_READ_REG32(&host_if->host_global_regs->hfir);
- + hfir.b.frint = calc_frame_interval(dwc_otg_hcd->core_if);
- + DWC_WRITE_REG32(&host_if->host_global_regs->hfir, hfir.d32);
- +
- + /* Check if we need to adjust the PHY clock speed for
- + * low power and adjust it */
- + if (params->host_support_fs_ls_low_power) {
- + gusbcfg_data_t usbcfg;
- +
- + usbcfg.d32 =
- + DWC_READ_REG32(&global_regs->gusbcfg);
- +
- + if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_LOW_SPEED
- + || hprt0.b.prtspd ==
- + DWC_HPRT0_PRTSPD_FULL_SPEED) {
- + /*
- + * Low power
- + */
- + hcfg_data_t hcfg;
- + if (usbcfg.b.phylpwrclksel == 0) {
- + /* Set PHY low power clock select for FS/LS devices */
- + usbcfg.b.phylpwrclksel = 1;
- + DWC_WRITE_REG32
- + (&global_regs->gusbcfg,
- + usbcfg.d32);
- + do_reset = 1;
- + }
- +
- + hcfg.d32 =
- + DWC_READ_REG32
- + (&host_if->host_global_regs->hcfg);
- +
- + if (hprt0.b.prtspd ==
- + DWC_HPRT0_PRTSPD_LOW_SPEED
- + && params->host_ls_low_power_phy_clk
- + ==
- + DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ)
- + {
- + /* 6 MHZ */
- + DWC_DEBUGPL(DBG_CIL,
- + "FS_PHY programming HCFG to 6 MHz (Low Power)\n");
- + if (hcfg.b.fslspclksel !=
- + DWC_HCFG_6_MHZ) {
- + hcfg.b.fslspclksel =
- + DWC_HCFG_6_MHZ;
- + DWC_WRITE_REG32
- + (&host_if->host_global_regs->hcfg,
- + hcfg.d32);
- + do_reset = 1;
- + }
- + } else {
- + /* 48 MHZ */
- + DWC_DEBUGPL(DBG_CIL,
- + "FS_PHY programming HCFG to 48 MHz ()\n");
- + if (hcfg.b.fslspclksel !=
- + DWC_HCFG_48_MHZ) {
- + hcfg.b.fslspclksel =
- + DWC_HCFG_48_MHZ;
- + DWC_WRITE_REG32
- + (&host_if->host_global_regs->hcfg,
- + hcfg.d32);
- + do_reset = 1;
- + }
- + }
- + } else {
- + /*
- + * Not low power
- + */
- + if (usbcfg.b.phylpwrclksel == 1) {
- + usbcfg.b.phylpwrclksel = 0;
- + DWC_WRITE_REG32
- + (&global_regs->gusbcfg,
- + usbcfg.d32);
- + do_reset = 1;
- + }
- + }
- +
- + if (do_reset) {
- + DWC_TASK_SCHEDULE(dwc_otg_hcd->reset_tasklet);
- + }
- + }
- +
- + if (!do_reset) {
- + /* Port has been enabled set the reset change flag */
- + dwc_otg_hcd->flags.b.port_reset_change = 1;
- + }
- + } else {
- + dwc_otg_hcd->flags.b.port_enable_change = 1;
- + }
- + retval |= 1;
- + }
- +
- + /** Overcurrent Change Interrupt */
- + if (hprt0.b.prtovrcurrchng) {
- + DWC_DEBUGPL(DBG_HCD, " --Port Interrupt HPRT0=0x%08x "
- + "Port Overcurrent Changed--\n", hprt0.d32);
- + dwc_otg_hcd->flags.b.port_over_current_change = 1;
- + hprt0_modify.b.prtovrcurrchng = 1;
- + retval |= 1;
- + }
- +
- + /* Clear Port Interrupts */
- + DWC_WRITE_REG32(dwc_otg_hcd->core_if->host_if->hprt0, hprt0_modify.d32);
- +
- + return retval;
- +}
- +
- +/** This interrupt indicates that one or more host channels has a pending
- + * interrupt. There are multiple conditions that can cause each host channel
- + * interrupt. This function determines which conditions have occurred for each
- + * host channel interrupt and handles them appropriately. */
- +int32_t dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd_t * dwc_otg_hcd)
- +{
- + int i;
- + int retval = 0;
- + haint_data_t haint = { .d32 = 0 } ;
- +
- + /* Clear appropriate bits in HCINTn to clear the interrupt bit in
- + * GINTSTS */
- +
- + 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_fsm_enable)
- + {
- + /* check the mask? */
- + local_fiq_disable();
- + fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock);
- + haint.b2.chint |= ~(dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint);
- + dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint = ~0;
- + fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock);
- + local_fiq_enable();
- + }
- +
- + for (i = 0; i < dwc_otg_hcd->core_if->core_params->host_channels; i++) {
- + if (haint.b2.chint & (1 << i)) {
- + retval |= dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd, i);
- + }
- + }
- +
- + return retval;
- +}
- +
- +/**
- + * Gets the actual length of a transfer after the transfer halts. _halt_status
- + * holds the reason for the halt.
- + *
- + * For IN transfers where halt_status is DWC_OTG_HC_XFER_COMPLETE,
- + * *short_read is set to 1 upon return if less than the requested
- + * number of bytes were transferred. Otherwise, *short_read is set to 0 upon
- + * return. short_read may also be NULL on entry, in which case it remains
- + * unchanged.
- + */
- +static uint32_t get_actual_xfer_length(dwc_hc_t * hc,
- + dwc_otg_hc_regs_t * hc_regs,
- + dwc_otg_qtd_t * qtd,
- + dwc_otg_halt_status_e halt_status,
- + int *short_read)
- +{
- + hctsiz_data_t hctsiz;
- + uint32_t length;
- +
- + if (short_read != NULL) {
- + *short_read = 0;
- + }
- + hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz);
- +
- + if (halt_status == DWC_OTG_HC_XFER_COMPLETE) {
- + if (hc->ep_is_in) {
- + length = hc->xfer_len - hctsiz.b.xfersize;
- + if (short_read != NULL) {
- + *short_read = (hctsiz.b.xfersize != 0);
- + }
- + } else if (hc->qh->do_split) {
- + //length = split_out_xfersize[hc->hc_num];
- + length = qtd->ssplit_out_xfer_count;
- + } else {
- + length = hc->xfer_len;
- + }
- + } else {
- + /*
- + * Must use the hctsiz.pktcnt field to determine how much data
- + * has been transferred. This field reflects the number of
- + * packets that have been transferred via the USB. This is
- + * always an integral number of packets if the transfer was
- + * halted before its normal completion. (Can't use the
- + * hctsiz.xfersize field because that reflects the number of
- + * bytes transferred via the AHB, not the USB).
- + */
- + length =
- + (hc->start_pkt_count - hctsiz.b.pktcnt) * hc->max_packet;
- + }
- +
- + return length;
- +}
- +
- +/**
- + * Updates the state of the URB after a Transfer Complete interrupt on the
- + * host channel. Updates the actual_length field of the URB based on the
- + * number of bytes transferred via the host channel. Sets the URB status
- + * if the data transfer is finished.
- + *
- + * @return 1 if the data transfer specified by the URB is completely finished,
- + * 0 otherwise.
- + */
- +static int update_urb_state_xfer_comp(dwc_hc_t * hc,
- + dwc_otg_hc_regs_t * hc_regs,
- + dwc_otg_hcd_urb_t * urb,
- + dwc_otg_qtd_t * qtd)
- +{
- + int xfer_done = 0;
- + int short_read = 0;
- +
- + int xfer_length;
- +
- + xfer_length = get_actual_xfer_length(hc, hc_regs, qtd,
- + DWC_OTG_HC_XFER_COMPLETE,
- + &short_read);
- +
- + if (urb->actual_length + xfer_length > urb->length) {
- + printk_once(KERN_DEBUG "dwc_otg: DEVICE:%03d : %s:%d:trimming xfer length\n",
- + hc->dev_addr, __func__, __LINE__);
- + xfer_length = urb->length - urb->actual_length;
- + }
- +
- + /* non DWORD-aligned buffer case handling. */
- + if (hc->align_buff && xfer_length && hc->ep_is_in) {
- + dwc_memcpy(urb->buf + urb->actual_length, hc->qh->dw_align_buf,
- + xfer_length);
- + }
- +
- + urb->actual_length += xfer_length;
- +
- + if (xfer_length && (hc->ep_type == DWC_OTG_EP_TYPE_BULK) &&
- + (urb->flags & URB_SEND_ZERO_PACKET)
- + && (urb->actual_length == urb->length)
- + && !(urb->length % hc->max_packet)) {
- + xfer_done = 0;
- + } else if (short_read || urb->actual_length >= urb->length) {
- + xfer_done = 1;
- + urb->status = 0;
- + }
- +
- +#ifdef DEBUG
- + {
- + hctsiz_data_t hctsiz;
- + hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz);
- + DWC_DEBUGPL(DBG_HCDV, "DWC_otg: %s: %s, channel %d\n",
- + __func__, (hc->ep_is_in ? "IN" : "OUT"),
- + hc->hc_num);
- + DWC_DEBUGPL(DBG_HCDV, " hc->xfer_len %d\n", hc->xfer_len);
- + DWC_DEBUGPL(DBG_HCDV, " hctsiz.xfersize %d\n",
- + hctsiz.b.xfersize);
- + DWC_DEBUGPL(DBG_HCDV, " urb->transfer_buffer_length %d\n",
- + urb->length);
- + DWC_DEBUGPL(DBG_HCDV, " urb->actual_length %d\n",
- + urb->actual_length);
- + DWC_DEBUGPL(DBG_HCDV, " short_read %d, xfer_done %d\n",
- + short_read, xfer_done);
- + }
- +#endif
- +
- + return xfer_done;
- +}
- +
- +/*
- + * Save the starting data toggle for the next transfer. The data toggle is
- + * saved in the QH for non-control transfers and it's saved in the QTD for
- + * control transfers.
- + */
- +void dwc_otg_hcd_save_data_toggle(dwc_hc_t * hc,
- + dwc_otg_hc_regs_t * hc_regs, dwc_otg_qtd_t * qtd)
- +{
- + hctsiz_data_t hctsiz;
- + hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz);
- +
- + if (hc->ep_type != DWC_OTG_EP_TYPE_CONTROL) {
- + dwc_otg_qh_t *qh = hc->qh;
- + if (hctsiz.b.pid == DWC_HCTSIZ_DATA0) {
- + qh->data_toggle = DWC_OTG_HC_PID_DATA0;
- + } else {
- + qh->data_toggle = DWC_OTG_HC_PID_DATA1;
- + }
- + } else {
- + if (hctsiz.b.pid == DWC_HCTSIZ_DATA0) {
- + qtd->data_toggle = DWC_OTG_HC_PID_DATA0;
- + } else {
- + qtd->data_toggle = DWC_OTG_HC_PID_DATA1;
- + }
- + }
- +}
- +
- +/**
- + * Updates the state of an Isochronous URB when the transfer is stopped for
- + * any reason. The fields of the current entry in the frame descriptor array
- + * are set based on the transfer state and the input _halt_status. Completes
- + * the Isochronous URB if all the URB frames have been completed.
- + *
- + * @return DWC_OTG_HC_XFER_COMPLETE if there are more frames remaining to be
- + * transferred in the URB. Otherwise return DWC_OTG_HC_XFER_URB_COMPLETE.
- + */
- +static dwc_otg_halt_status_e
- +update_isoc_urb_state(dwc_otg_hcd_t * hcd,
- + dwc_hc_t * hc,
- + dwc_otg_hc_regs_t * hc_regs,
- + dwc_otg_qtd_t * qtd, dwc_otg_halt_status_e halt_status)
- +{
- + dwc_otg_hcd_urb_t *urb = qtd->urb;
- + dwc_otg_halt_status_e ret_val = halt_status;
- + struct dwc_otg_hcd_iso_packet_desc *frame_desc;
- +
- + frame_desc = &urb->iso_descs[qtd->isoc_frame_index];
- + switch (halt_status) {
- + case DWC_OTG_HC_XFER_COMPLETE:
- + frame_desc->status = 0;
- + frame_desc->actual_length =
- + get_actual_xfer_length(hc, hc_regs, qtd, halt_status, NULL);
- +
- + /* non DWORD-aligned buffer case handling. */
- + if (hc->align_buff && frame_desc->actual_length && hc->ep_is_in) {
- + dwc_memcpy(urb->buf + frame_desc->offset + qtd->isoc_split_offset,
- + hc->qh->dw_align_buf, frame_desc->actual_length);
- + }
- +
- + break;
- + case DWC_OTG_HC_XFER_FRAME_OVERRUN:
- + urb->error_count++;
- + if (hc->ep_is_in) {
- + frame_desc->status = -DWC_E_NO_STREAM_RES;
- + } else {
- + frame_desc->status = -DWC_E_COMMUNICATION;
- + }
- + frame_desc->actual_length = 0;
- + break;
- + case DWC_OTG_HC_XFER_BABBLE_ERR:
- + urb->error_count++;
- + frame_desc->status = -DWC_E_OVERFLOW;
- + /* Don't need to update actual_length in this case. */
- + break;
- + case DWC_OTG_HC_XFER_XACT_ERR:
- + urb->error_count++;
- + frame_desc->status = -DWC_E_PROTOCOL;
- + frame_desc->actual_length =
- + get_actual_xfer_length(hc, hc_regs, qtd, halt_status, NULL);
- +
- + /* non DWORD-aligned buffer case handling. */
- + if (hc->align_buff && frame_desc->actual_length && hc->ep_is_in) {
- + dwc_memcpy(urb->buf + frame_desc->offset + qtd->isoc_split_offset,
- + hc->qh->dw_align_buf, frame_desc->actual_length);
- + }
- + /* Skip whole frame */
- + if (hc->qh->do_split && (hc->ep_type == DWC_OTG_EP_TYPE_ISOC) &&
- + hc->ep_is_in && hcd->core_if->dma_enable) {
- + qtd->complete_split = 0;
- + qtd->isoc_split_offset = 0;
- + }
- +
- + break;
- + default:
- + DWC_ASSERT(1, "Unhandled _halt_status (%d)\n", halt_status);
- + break;
- + }
- + if (++qtd->isoc_frame_index == urb->packet_count) {
- + /*
- + * urb->status is not used for isoc transfers.
- + * The individual frame_desc statuses are used instead.
- + */
- + hcd->fops->complete(hcd, urb->priv, urb, 0);
- + ret_val = DWC_OTG_HC_XFER_URB_COMPLETE;
- + } else {
- + ret_val = DWC_OTG_HC_XFER_COMPLETE;
- + }
- + return ret_val;
- +}
- +
- +/**
- + * Frees the first QTD in the QH's list if free_qtd is 1. For non-periodic
- + * QHs, removes the QH from the active non-periodic schedule. If any QTDs are
- + * still linked to the QH, the QH is added to the end of the inactive
- + * non-periodic schedule. For periodic QHs, removes the QH from the periodic
- + * schedule if no more QTDs are linked to the QH.
- + */
- +static void deactivate_qh(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, int free_qtd)
- +{
- + int continue_split = 0;
- + dwc_otg_qtd_t *qtd;
- +
- + DWC_DEBUGPL(DBG_HCDV, " %s(%p,%p,%d)\n", __func__, hcd, qh, free_qtd);
- +
- + qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list);
- +
- + if (qtd->complete_split) {
- + continue_split = 1;
- + } else if (qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_MID ||
- + qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_END) {
- + continue_split = 1;
- + }
- +
- + if (free_qtd) {
- + dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh);
- + continue_split = 0;
- + }
- +
- + qh->channel = NULL;
- + dwc_otg_hcd_qh_deactivate(hcd, qh, continue_split);
- +}
- +
- +/**
- + * Releases a host channel for use by other transfers. Attempts to select and
- + * queue more transactions since at least one host channel is available.
- + *
- + * @param hcd The HCD state structure.
- + * @param hc The host channel to release.
- + * @param qtd The QTD associated with the host channel. This QTD may be freed
- + * if the transfer is complete or an error has occurred.
- + * @param halt_status Reason the channel is being released. This status
- + * determines the actions taken by this function.
- + */
- +static void release_channel(dwc_otg_hcd_t * hcd,
- + dwc_hc_t * hc,
- + dwc_otg_qtd_t * qtd,
- + dwc_otg_halt_status_e halt_status)
- +{
- + dwc_otg_transaction_type_e tr_type;
- + int free_qtd;
- +
- + 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_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 = 0;
- + }
- + }
- + }
- +
- + switch (halt_status) {
- + case DWC_OTG_HC_XFER_URB_COMPLETE:
- + free_qtd = 1;
- + break;
- + case DWC_OTG_HC_XFER_AHB_ERR:
- + case DWC_OTG_HC_XFER_STALL:
- + case DWC_OTG_HC_XFER_BABBLE_ERR:
- + free_qtd = 1;
- + break;
- + case DWC_OTG_HC_XFER_XACT_ERR:
- + if (qtd->error_count >= 3) {
- + DWC_DEBUGPL(DBG_HCDV,
- + " Complete URB with transaction error\n");
- + free_qtd = 1;
- + qtd->urb->status = -DWC_E_PROTOCOL;
- + hcd->fops->complete(hcd, qtd->urb->priv,
- + qtd->urb, -DWC_E_PROTOCOL);
- + } else {
- + free_qtd = 0;
- + }
- + break;
- + case DWC_OTG_HC_XFER_URB_DEQUEUE:
- + /*
- + * The QTD has already been removed and the QH has been
- + * deactivated. Don't want to do anything except release the
- + * host channel and try to queue more transfers.
- + */
- + goto cleanup;
- + case DWC_OTG_HC_XFER_NO_HALT_STATUS:
- + free_qtd = 0;
- + break;
- + case DWC_OTG_HC_XFER_PERIODIC_INCOMPLETE:
- + DWC_DEBUGPL(DBG_HCDV,
- + " Complete URB with I/O error\n");
- + free_qtd = 1;
- + qtd->urb->status = -DWC_E_IO;
- + hcd->fops->complete(hcd, qtd->urb->priv,
- + qtd->urb, -DWC_E_IO);
- + break;
- + default:
- + free_qtd = 0;
- + break;
- + }
- +
- + deactivate_qh(hcd, hc->qh, free_qtd);
- +
- +cleanup:
- + /*
- + * Release the host channel for use by other transfers. The 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);
- +
- + if (!microframe_schedule) {
- + switch (hc->ep_type) {
- + case DWC_OTG_EP_TYPE_CONTROL:
- + case DWC_OTG_EP_TYPE_BULK:
- + hcd->non_periodic_channels--;
- + break;
- +
- + default:
- + /*
- + * Don't release reservations for periodic channels here.
- + * That's done when a periodic transfer is descheduled (i.e.
- + * when the QH is removed from the periodic schedule).
- + */
- + break;
- + }
- + } else {
- + hcd->available_host_channels++;
- + fiq_print(FIQDBG_INT, hcd->fiq_state, "AHC = %d ", hcd->available_host_channels);
- + }
- +
- + /* 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) {
- + dwc_otg_hcd_queue_transactions(hcd, tr_type);
- + }
- +}
- +
- +/**
- + * Halts a host channel. If the channel cannot be halted immediately because
- + * the request queue is full, this function ensures that the FIFO empty
- + * interrupt for the appropriate queue is enabled so that the halt request can
- + * be queued when there is space in the request queue.
- + *
- + * This function may also be called in DMA mode. In that case, the channel is
- + * simply released since the core always halts the channel automatically in
- + * DMA mode.
- + */
- +static void halt_channel(dwc_otg_hcd_t * hcd,
- + dwc_hc_t * hc,
- + dwc_otg_qtd_t * qtd, dwc_otg_halt_status_e halt_status)
- +{
- + if (hcd->core_if->dma_enable) {
- + release_channel(hcd, hc, qtd, halt_status);
- + return;
- + }
- +
- + /* Slave mode processing... */
- + dwc_otg_hc_halt(hcd->core_if, hc, halt_status);
- +
- + if (hc->halt_on_queue) {
- + gintmsk_data_t gintmsk = {.d32 = 0 };
- + dwc_otg_core_global_regs_t *global_regs;
- + global_regs = hcd->core_if->core_global_regs;
- +
- + if (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL ||
- + hc->ep_type == DWC_OTG_EP_TYPE_BULK) {
- + /*
- + * Make sure the Non-periodic Tx FIFO empty interrupt
- + * is enabled so that the non-periodic schedule will
- + * be processed.
- + */
- + gintmsk.b.nptxfempty = 1;
- + if (fiq_enable) {
- + local_fiq_disable();
- + fiq_fsm_spin_lock(&hcd->fiq_state->lock);
- + DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32);
- + fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
- + local_fiq_enable();
- + } else {
- + DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32);
- + }
- + } else {
- + /*
- + * Move the QH from the periodic queued schedule to
- + * the periodic assigned schedule. This allows the
- + * halt to be queued when the periodic schedule is
- + * processed.
- + */
- + DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_assigned,
- + &hc->qh->qh_list_entry);
- +
- + /*
- + * Make sure the Periodic Tx FIFO Empty interrupt is
- + * enabled so that the periodic schedule will be
- + * processed.
- + */
- + gintmsk.b.ptxfempty = 1;
- + if (fiq_enable) {
- + local_fiq_disable();
- + fiq_fsm_spin_lock(&hcd->fiq_state->lock);
- + DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32);
- + fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
- + local_fiq_enable();
- + } else {
- + DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32);
- + }
- + }
- + }
- +}
- +
- +/**
- + * Performs common cleanup for non-periodic transfers after a Transfer
- + * Complete interrupt. This function should be called after any endpoint type
- + * specific handling is finished to release the host channel.
- + */
- +static void complete_non_periodic_xfer(dwc_otg_hcd_t * hcd,
- + dwc_hc_t * hc,
- + dwc_otg_hc_regs_t * hc_regs,
- + dwc_otg_qtd_t * qtd,
- + dwc_otg_halt_status_e halt_status)
- +{
- + hcint_data_t hcint;
- +
- + qtd->error_count = 0;
- +
- + hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
- + if (hcint.b.nyet) {
- + /*
- + * Got a NYET on the last transaction of the transfer. This
- + * means that the endpoint should be in the PING state at the
- + * beginning of the next transfer.
- + */
- + hc->qh->ping_state = 1;
- + clear_hc_int(hc_regs, nyet);
- + }
- +
- + /*
- + * Always halt and release the host channel to make it available for
- + * more transfers. There may still be more phases for a control
- + * transfer or more data packets for a bulk transfer at this point,
- + * but the host channel is still halted. A channel will be reassigned
- + * to the transfer when the non-periodic schedule is processed after
- + * the channel is released. This allows transactions to be queued
- + * properly via dwc_otg_hcd_queue_transactions, which also enables the
- + * Tx FIFO Empty interrupt if necessary.
- + */
- + if (hc->ep_is_in) {
- + /*
- + * IN transfers in Slave mode require an explicit disable to
- + * halt the channel. (In DMA mode, this call simply releases
- + * the channel.)
- + */
- + halt_channel(hcd, hc, qtd, halt_status);
- + } else {
- + /*
- + * The channel is automatically disabled by the core for OUT
- + * transfers in Slave mode.
- + */
- + release_channel(hcd, hc, qtd, halt_status);
- + }
- +}
- +
- +/**
- + * Performs common cleanup for periodic transfers after a Transfer Complete
- + * interrupt. This function should be called after any endpoint type specific
- + * handling is finished to release the host channel.
- + */
- +static void complete_periodic_xfer(dwc_otg_hcd_t * hcd,
- + dwc_hc_t * hc,
- + dwc_otg_hc_regs_t * hc_regs,
- + dwc_otg_qtd_t * qtd,
- + dwc_otg_halt_status_e halt_status)
- +{
- + hctsiz_data_t hctsiz;
- + qtd->error_count = 0;
- +
- + hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz);
- + if (!hc->ep_is_in || hctsiz.b.pktcnt == 0) {
- + /* Core halts channel in these cases. */
- + release_channel(hcd, hc, qtd, halt_status);
- + } else {
- + /* Flush any outstanding requests from the Tx queue. */
- + halt_channel(hcd, hc, qtd, halt_status);
- + }
- +}
- +
- +static int32_t handle_xfercomp_isoc_split_in(dwc_otg_hcd_t * hcd,
- + dwc_hc_t * hc,
- + dwc_otg_hc_regs_t * hc_regs,
- + dwc_otg_qtd_t * qtd)
- +{
- + uint32_t len;
- + struct dwc_otg_hcd_iso_packet_desc *frame_desc;
- + frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index];
- +
- + len = get_actual_xfer_length(hc, hc_regs, qtd,
- + DWC_OTG_HC_XFER_COMPLETE, NULL);
- +
- + if (!len) {
- + qtd->complete_split = 0;
- + qtd->isoc_split_offset = 0;
- + return 0;
- + }
- + frame_desc->actual_length += len;
- +
- + if (hc->align_buff && len)
- + dwc_memcpy(qtd->urb->buf + frame_desc->offset +
- + qtd->isoc_split_offset, hc->qh->dw_align_buf, len);
- + qtd->isoc_split_offset += len;
- +
- + if (frame_desc->length == frame_desc->actual_length) {
- + frame_desc->status = 0;
- + qtd->isoc_frame_index++;
- + qtd->complete_split = 0;
- + 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_NO_HALT_STATUS);
- + }
- +
- + return 1; /* Indicates that channel released */
- +}
- +
- +/**
- + * Handles a host channel Transfer Complete interrupt. This handler may be
- + * called in either DMA mode or Slave mode.
- + */
- +static int32_t handle_hc_xfercomp_intr(dwc_otg_hcd_t * hcd,
- + dwc_hc_t * hc,
- + dwc_otg_hc_regs_t * hc_regs,
- + dwc_otg_qtd_t * qtd)
- +{
- + int urb_xfer_done;
- + dwc_otg_halt_status_e halt_status = DWC_OTG_HC_XFER_COMPLETE;
- + dwc_otg_hcd_urb_t *urb = qtd->urb;
- + int pipe_type = dwc_otg_hcd_get_pipe_type(&urb->pipe_info);
- +
- + DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: "
- + "Transfer Complete--\n", hc->hc_num);
- +
- + if (hcd->core_if->dma_desc_enable) {
- + dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs, halt_status);
- + if (pipe_type == UE_ISOCHRONOUS) {
- + /* Do not disable the interrupt, just clear it */
- + clear_hc_int(hc_regs, xfercomp);
- + return 1;
- + }
- + goto handle_xfercomp_done;
- + }
- +
- + /*
- + * Handle xfer complete on CSPLIT.
- + */
- +
- + if (hc->qh->do_split) {
- + if ((hc->ep_type == DWC_OTG_EP_TYPE_ISOC) && hc->ep_is_in
- + && hcd->core_if->dma_enable) {
- + if (qtd->complete_split
- + && handle_xfercomp_isoc_split_in(hcd, hc, hc_regs,
- + qtd))
- + goto handle_xfercomp_done;
- + } else {
- + qtd->complete_split = 0;
- + }
- + }
- +
- + /* Update the QTD and URB states. */
- + switch (pipe_type) {
- + case UE_CONTROL:
- + switch (qtd->control_phase) {
- + case DWC_OTG_CONTROL_SETUP:
- + if (urb->length > 0) {
- + qtd->control_phase = DWC_OTG_CONTROL_DATA;
- + } else {
- + qtd->control_phase = DWC_OTG_CONTROL_STATUS;
- + }
- + DWC_DEBUGPL(DBG_HCDV,
- + " Control setup transaction done\n");
- + halt_status = DWC_OTG_HC_XFER_COMPLETE;
- + break;
- + case DWC_OTG_CONTROL_DATA:{
- + urb_xfer_done =
- + update_urb_state_xfer_comp(hc, hc_regs, urb,
- + qtd);
- + if (urb_xfer_done) {
- + qtd->control_phase =
- + DWC_OTG_CONTROL_STATUS;
- + DWC_DEBUGPL(DBG_HCDV,
- + " Control data transfer done\n");
- + } else {
- + dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
- + }
- + halt_status = DWC_OTG_HC_XFER_COMPLETE;
- + break;
- + }
- + case DWC_OTG_CONTROL_STATUS:
- + DWC_DEBUGPL(DBG_HCDV, " Control transfer complete\n");
- + if (urb->status == -DWC_E_IN_PROGRESS) {
- + urb->status = 0;
- + }
- + hcd->fops->complete(hcd, urb->priv, urb, urb->status);
- + halt_status = DWC_OTG_HC_XFER_URB_COMPLETE;
- + break;
- + }
- +
- + complete_non_periodic_xfer(hcd, hc, hc_regs, qtd, halt_status);
- + break;
- + case UE_BULK:
- + DWC_DEBUGPL(DBG_HCDV, " Bulk transfer complete\n");
- + urb_xfer_done =
- + update_urb_state_xfer_comp(hc, hc_regs, urb, qtd);
- + if (urb_xfer_done) {
- + hcd->fops->complete(hcd, urb->priv, urb, urb->status);
- + halt_status = DWC_OTG_HC_XFER_URB_COMPLETE;
- + } else {
- + halt_status = DWC_OTG_HC_XFER_COMPLETE;
- + }
- +
- + dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
- + complete_non_periodic_xfer(hcd, hc, hc_regs, qtd, halt_status);
- + break;
- + case UE_INTERRUPT:
- + DWC_DEBUGPL(DBG_HCDV, " Interrupt transfer complete\n");
- + urb_xfer_done =
- + update_urb_state_xfer_comp(hc, hc_regs, urb, qtd);
- +
- + /*
- + * Interrupt URB is done on the first transfer complete
- + * interrupt.
- + */
- + if (urb_xfer_done) {
- + hcd->fops->complete(hcd, urb->priv, urb, urb->status);
- + halt_status = DWC_OTG_HC_XFER_URB_COMPLETE;
- + } else {
- + halt_status = DWC_OTG_HC_XFER_COMPLETE;
- + }
- +
- + dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
- + complete_periodic_xfer(hcd, hc, hc_regs, qtd, halt_status);
- + break;
- + case UE_ISOCHRONOUS:
- + DWC_DEBUGPL(DBG_HCDV, " Isochronous transfer complete\n");
- + if (qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_ALL) {
- + halt_status =
- + update_isoc_urb_state(hcd, hc, hc_regs, qtd,
- + DWC_OTG_HC_XFER_COMPLETE);
- + }
- + complete_periodic_xfer(hcd, hc, hc_regs, qtd, halt_status);
- + break;
- + }
- +
- +handle_xfercomp_done:
- + disable_hc_int(hc_regs, xfercompl);
- +
- + return 1;
- +}
- +
- +/**
- + * Handles a host channel STALL interrupt. This handler may be called in
- + * either DMA mode or Slave mode.
- + */
- +static int32_t handle_hc_stall_intr(dwc_otg_hcd_t * hcd,
- + dwc_hc_t * hc,
- + dwc_otg_hc_regs_t * hc_regs,
- + dwc_otg_qtd_t * qtd)
- +{
- + dwc_otg_hcd_urb_t *urb = qtd->urb;
- + int pipe_type = dwc_otg_hcd_get_pipe_type(&urb->pipe_info);
- +
- + DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: "
- + "STALL Received--\n", hc->hc_num);
- +
- + if (hcd->core_if->dma_desc_enable) {
- + dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs, DWC_OTG_HC_XFER_STALL);
- + goto handle_stall_done;
- + }
- +
- + if (pipe_type == UE_CONTROL) {
- + hcd->fops->complete(hcd, urb->priv, urb, -DWC_E_PIPE);
- + }
- +
- + if (pipe_type == UE_BULK || pipe_type == UE_INTERRUPT) {
- + hcd->fops->complete(hcd, urb->priv, urb, -DWC_E_PIPE);
- + /*
- + * USB protocol requires resetting the data toggle for bulk
- + * and interrupt endpoints when a CLEAR_FEATURE(ENDPOINT_HALT)
- + * setup command is issued to the endpoint. Anticipate the
- + * CLEAR_FEATURE command since a STALL has occurred and reset
- + * the data toggle now.
- + */
- + hc->qh->data_toggle = 0;
- + }
- +
- + halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_STALL);
- +
- +handle_stall_done:
- + disable_hc_int(hc_regs, stall);
- +
- + return 1;
- +}
- +
- +/*
- + * Updates the state of the URB when a transfer has been stopped due to an
- + * abnormal condition before the transfer completes. Modifies the
- + * actual_length field of the URB to reflect the number of bytes that have
- + * actually been transferred via the host channel.
- + */
- +static void update_urb_state_xfer_intr(dwc_hc_t * hc,
- + dwc_otg_hc_regs_t * hc_regs,
- + dwc_otg_hcd_urb_t * urb,
- + dwc_otg_qtd_t * qtd,
- + dwc_otg_halt_status_e halt_status)
- +{
- + uint32_t bytes_transferred = get_actual_xfer_length(hc, hc_regs, qtd,
- + halt_status, NULL);
- +
- + if (urb->actual_length + bytes_transferred > urb->length) {
- + printk_once(KERN_DEBUG "dwc_otg: DEVICE:%03d : %s:%d:trimming xfer length\n",
- + hc->dev_addr, __func__, __LINE__);
- + bytes_transferred = urb->length - urb->actual_length;
- + }
- +
- + /* non DWORD-aligned buffer case handling. */
- + if (hc->align_buff && bytes_transferred && hc->ep_is_in) {
- + dwc_memcpy(urb->buf + urb->actual_length, hc->qh->dw_align_buf,
- + bytes_transferred);
- + }
- +
- + urb->actual_length += bytes_transferred;
- +
- +#ifdef DEBUG
- + {
- + hctsiz_data_t hctsiz;
- + hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz);
- + DWC_DEBUGPL(DBG_HCDV, "DWC_otg: %s: %s, channel %d\n",
- + __func__, (hc->ep_is_in ? "IN" : "OUT"),
- + hc->hc_num);
- + DWC_DEBUGPL(DBG_HCDV, " hc->start_pkt_count %d\n",
- + hc->start_pkt_count);
- + DWC_DEBUGPL(DBG_HCDV, " hctsiz.pktcnt %d\n", hctsiz.b.pktcnt);
- + DWC_DEBUGPL(DBG_HCDV, " hc->max_packet %d\n", hc->max_packet);
- + DWC_DEBUGPL(DBG_HCDV, " bytes_transferred %d\n",
- + bytes_transferred);
- + DWC_DEBUGPL(DBG_HCDV, " urb->actual_length %d\n",
- + urb->actual_length);
- + DWC_DEBUGPL(DBG_HCDV, " urb->transfer_buffer_length %d\n",
- + urb->length);
- + }
- +#endif
- +}
- +
- +/**
- + * Handles a host channel NAK interrupt. This handler may be called in either
- + * DMA mode or Slave mode.
- + */
- +static int32_t handle_hc_nak_intr(dwc_otg_hcd_t * hcd,
- + dwc_hc_t * hc,
- + dwc_otg_hc_regs_t * hc_regs,
- + dwc_otg_qtd_t * qtd)
- +{
- + DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: "
- + "NAK Received--\n", hc->hc_num);
- +
- + /*
- + * When we get bulk NAKs then remember this so we holdoff on this qh until
- + * the beginning of the next frame
- + */
- + switch(dwc_otg_hcd_get_pipe_type(&qtd->urb->pipe_info)) {
- + case UE_BULK:
- + case UE_CONTROL:
- + if (nak_holdoff && qtd->qh->do_split)
- + hc->qh->nak_frame = dwc_otg_hcd_get_frame_number(hcd);
- + }
- +
- + /*
- + * Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and
- + * interrupt. Re-start the SSPLIT transfer.
- + */
- + if (hc->do_split) {
- + if (hc->complete_split) {
- + qtd->error_count = 0;
- + }
- + qtd->complete_split = 0;
- + halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NAK);
- + goto handle_nak_done;
- + }
- +
- + switch (dwc_otg_hcd_get_pipe_type(&qtd->urb->pipe_info)) {
- + case UE_CONTROL:
- + case UE_BULK:
- + if (hcd->core_if->dma_enable && hc->ep_is_in) {
- + /*
- + * NAK interrupts are enabled on bulk/control IN
- + * transfers in DMA mode for the sole purpose of
- + * resetting the error count after a transaction error
- + * occurs. The core will continue transferring data.
- + * Disable other interrupts unmasked for the same
- + * reason.
- + */
- + disable_hc_int(hc_regs, datatglerr);
- + disable_hc_int(hc_regs, ack);
- + qtd->error_count = 0;
- + goto handle_nak_done;
- + }
- +
- + /*
- + * NAK interrupts normally occur during OUT transfers in DMA
- + * or Slave mode. For IN transfers, more requests will be
- + * queued as request queue space is available.
- + */
- + qtd->error_count = 0;
- +
- + if (!hc->qh->ping_state) {
- + update_urb_state_xfer_intr(hc, hc_regs,
- + qtd->urb, qtd,
- + DWC_OTG_HC_XFER_NAK);
- + dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
- +
- + if (hc->speed == DWC_OTG_EP_SPEED_HIGH)
- + hc->qh->ping_state = 1;
- + }
- +
- + /*
- + * Halt the channel so the transfer can be re-started from
- + * the appropriate point or the PING protocol will
- + * start/continue.
- + */
- + halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NAK);
- + break;
- + case UE_INTERRUPT:
- + qtd->error_count = 0;
- + halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NAK);
- + break;
- + case UE_ISOCHRONOUS:
- + /* Should never get called for isochronous transfers. */
- + DWC_ASSERT(1, "NACK interrupt for ISOC transfer\n");
- + break;
- + }
- +
- +handle_nak_done:
- + disable_hc_int(hc_regs, nak);
- +
- + return 1;
- +}
- +
- +/**
- + * Handles a host channel ACK interrupt. This interrupt is enabled when
- + * performing the PING protocol in Slave mode, when errors occur during
- + * either Slave mode or DMA mode, and during Start Split transactions.
- + */
- +static int32_t handle_hc_ack_intr(dwc_otg_hcd_t * hcd,
- + dwc_hc_t * hc,
- + dwc_otg_hc_regs_t * hc_regs,
- + dwc_otg_qtd_t * qtd)
- +{
- + DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: "
- + "ACK Received--\n", hc->hc_num);
- +
- + if (hc->do_split) {
- + /*
- + * Handle ACK on SSPLIT.
- + * ACK should not occur in CSPLIT.
- + */
- + if (!hc->ep_is_in && hc->data_pid_start != DWC_OTG_HC_PID_SETUP) {
- + qtd->ssplit_out_xfer_count = hc->xfer_len;
- + }
- + if (!(hc->ep_type == DWC_OTG_EP_TYPE_ISOC && !hc->ep_is_in)) {
- + /* Don't need complete for isochronous out transfers. */
- + qtd->complete_split = 1;
- + }
- +
- + /* ISOC OUT */
- + if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC && !hc->ep_is_in) {
- + switch (hc->xact_pos) {
- + case DWC_HCSPLIT_XACTPOS_ALL:
- + break;
- + case DWC_HCSPLIT_XACTPOS_END:
- + qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_ALL;
- + qtd->isoc_split_offset = 0;
- + break;
- + case DWC_HCSPLIT_XACTPOS_BEGIN:
- + case DWC_HCSPLIT_XACTPOS_MID:
- + /*
- + * For BEGIN or MID, calculate the length for
- + * the next microframe to determine the correct
- + * SSPLIT token, either MID or END.
- + */
- + {
- + struct dwc_otg_hcd_iso_packet_desc
- + *frame_desc;
- +
- + frame_desc =
- + &qtd->urb->
- + iso_descs[qtd->isoc_frame_index];
- + qtd->isoc_split_offset += 188;
- +
- + if ((frame_desc->length -
- + qtd->isoc_split_offset) <= 188) {
- + qtd->isoc_split_pos =
- + DWC_HCSPLIT_XACTPOS_END;
- + } else {
- + qtd->isoc_split_pos =
- + DWC_HCSPLIT_XACTPOS_MID;
- + }
- +
- + }
- + break;
- + }
- + } else {
- + halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_ACK);
- + }
- + } else {
- + /*
- + * An unmasked ACK on a non-split DMA transaction is
- + * for the sole purpose of resetting error counts. Disable other
- + * interrupts unmasked for the same reason.
- + */
- + if(hcd->core_if->dma_enable) {
- + disable_hc_int(hc_regs, datatglerr);
- + disable_hc_int(hc_regs, nak);
- + }
- + qtd->error_count = 0;
- +
- + if (hc->qh->ping_state) {
- + hc->qh->ping_state = 0;
- + /*
- + * Halt the channel so the transfer can be re-started
- + * from the appropriate point. This only happens in
- + * Slave mode. In DMA mode, the ping_state is cleared
- + * when the transfer is started because the core
- + * automatically executes the PING, then the transfer.
- + */
- + halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_ACK);
- + }
- + }
- +
- + /*
- + * If the ACK occurred when _not_ in the PING state, let the channel
- + * continue transferring data after clearing the error count.
- + */
- +
- + disable_hc_int(hc_regs, ack);
- +
- + return 1;
- +}
- +
- +/**
- + * Handles a host channel NYET interrupt. This interrupt should only occur on
- + * Bulk and Control OUT endpoints and for complete split transactions. If a
- + * NYET occurs at the same time as a Transfer Complete interrupt, it is
- + * handled in the xfercomp interrupt handler, not here. This handler may be
- + * called in either DMA mode or Slave mode.
- + */
- +static int32_t handle_hc_nyet_intr(dwc_otg_hcd_t * hcd,
- + dwc_hc_t * hc,
- + dwc_otg_hc_regs_t * hc_regs,
- + dwc_otg_qtd_t * qtd)
- +{
- + DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: "
- + "NYET Received--\n", hc->hc_num);
- +
- + /*
- + * NYET on CSPLIT
- + * re-do the CSPLIT immediately on non-periodic
- + */
- + if (hc->do_split && hc->complete_split) {
- + if (hc->ep_is_in && (hc->ep_type == DWC_OTG_EP_TYPE_ISOC)
- + && hcd->core_if->dma_enable) {
- + qtd->complete_split = 0;
- + 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_NO_HALT_STATUS);
- + goto handle_nyet_done;
- + }
- +
- + if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
- + hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
- + int frnum = dwc_otg_hcd_get_frame_number(hcd);
- +
- + // 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_fsm_enable) {
- + /*
- + * No longer in the same full speed frame.
- + * Treat this as a transaction error.
- + */
- +#if 0
- + /** @todo Fix system performance so this can
- + * be treated as an error. Right now complete
- + * splits cannot be scheduled precisely enough
- + * due to other system activity, so this error
- + * occurs regularly in Slave mode.
- + */
- + qtd->error_count++;
- +#endif
- + qtd->complete_split = 0;
- + halt_channel(hcd, hc, qtd,
- + DWC_OTG_HC_XFER_XACT_ERR);
- + /** @todo add support for isoc release */
- + goto handle_nyet_done;
- + }
- + }
- +
- + halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NYET);
- + goto handle_nyet_done;
- + }
- +
- + hc->qh->ping_state = 1;
- + qtd->error_count = 0;
- +
- + update_urb_state_xfer_intr(hc, hc_regs, qtd->urb, qtd,
- + DWC_OTG_HC_XFER_NYET);
- + dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
- +
- + /*
- + * Halt the channel and re-start the transfer so the PING
- + * protocol will start.
- + */
- + halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NYET);
- +
- +handle_nyet_done:
- + disable_hc_int(hc_regs, nyet);
- + return 1;
- +}
- +
- +/**
- + * Handles a host channel babble interrupt. This handler may be called in
- + * either DMA mode or Slave mode.
- + */
- +static int32_t handle_hc_babble_intr(dwc_otg_hcd_t * hcd,
- + dwc_hc_t * hc,
- + dwc_otg_hc_regs_t * hc_regs,
- + dwc_otg_qtd_t * qtd)
- +{
- + DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: "
- + "Babble Error--\n", hc->hc_num);
- +
- + if (hcd->core_if->dma_desc_enable) {
- + dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs,
- + DWC_OTG_HC_XFER_BABBLE_ERR);
- + goto handle_babble_done;
- + }
- +
- + if (hc->ep_type != DWC_OTG_EP_TYPE_ISOC) {
- + hcd->fops->complete(hcd, qtd->urb->priv,
- + qtd->urb, -DWC_E_OVERFLOW);
- + halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_BABBLE_ERR);
- + } else {
- + dwc_otg_halt_status_e halt_status;
- + halt_status = update_isoc_urb_state(hcd, hc, hc_regs, qtd,
- + DWC_OTG_HC_XFER_BABBLE_ERR);
- + halt_channel(hcd, hc, qtd, halt_status);
- + }
- +
- +handle_babble_done:
- + disable_hc_int(hc_regs, bblerr);
- + return 1;
- +}
- +
- +/**
- + * Handles a host channel AHB error interrupt. This handler is only called in
- + * DMA mode.
- + */
- +static int32_t handle_hc_ahberr_intr(dwc_otg_hcd_t * hcd,
- + dwc_hc_t * hc,
- + dwc_otg_hc_regs_t * hc_regs,
- + dwc_otg_qtd_t * qtd)
- +{
- + hcchar_data_t hcchar;
- + hcsplt_data_t hcsplt;
- + hctsiz_data_t hctsiz;
- + uint32_t hcdma;
- + char *pipetype, *speed;
- +
- + dwc_otg_hcd_urb_t *urb = qtd->urb;
- +
- + DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: "
- + "AHB Error--\n", hc->hc_num);
- +
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- + hcsplt.d32 = DWC_READ_REG32(&hc_regs->hcsplt);
- + hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz);
- + hcdma = DWC_READ_REG32(&hc_regs->hcdma);
- +
- + DWC_ERROR("AHB ERROR, Channel %d\n", hc->hc_num);
- + DWC_ERROR(" hcchar 0x%08x, hcsplt 0x%08x\n", hcchar.d32, hcsplt.d32);
- + DWC_ERROR(" hctsiz 0x%08x, hcdma 0x%08x\n", hctsiz.d32, hcdma);
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD URB Enqueue\n");
- + DWC_ERROR(" Device address: %d\n",
- + dwc_otg_hcd_get_dev_addr(&urb->pipe_info));
- + DWC_ERROR(" Endpoint: %d, %s\n",
- + dwc_otg_hcd_get_ep_num(&urb->pipe_info),
- + (dwc_otg_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT"));
- +
- + switch (dwc_otg_hcd_get_pipe_type(&urb->pipe_info)) {
- + case UE_CONTROL:
- + pipetype = "CONTROL";
- + break;
- + case UE_BULK:
- + pipetype = "BULK";
- + break;
- + case UE_INTERRUPT:
- + pipetype = "INTERRUPT";
- + break;
- + case UE_ISOCHRONOUS:
- + pipetype = "ISOCHRONOUS";
- + break;
- + default:
- + pipetype = "UNKNOWN";
- + break;
- + }
- +
- + DWC_ERROR(" Endpoint type: %s\n", pipetype);
- +
- + switch (hc->speed) {
- + case DWC_OTG_EP_SPEED_HIGH:
- + speed = "HIGH";
- + break;
- + case DWC_OTG_EP_SPEED_FULL:
- + speed = "FULL";
- + break;
- + case DWC_OTG_EP_SPEED_LOW:
- + speed = "LOW";
- + break;
- + default:
- + speed = "UNKNOWN";
- + break;
- + };
- +
- + DWC_ERROR(" Speed: %s\n", speed);
- +
- + DWC_ERROR(" Max packet size: %d\n",
- + dwc_otg_hcd_get_mps(&urb->pipe_info));
- + DWC_ERROR(" Data buffer length: %d\n", urb->length);
- + DWC_ERROR(" Transfer buffer: %p, Transfer DMA: %pad\n",
- + urb->buf, &urb->dma);
- + DWC_ERROR(" Setup buffer: %p, Setup DMA: %pad\n",
- + urb->setup_packet, &urb->setup_dma);
- + DWC_ERROR(" Interval: %d\n", urb->interval);
- +
- + /* Core haltes the channel for Descriptor DMA mode */
- + if (hcd->core_if->dma_desc_enable) {
- + dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs,
- + DWC_OTG_HC_XFER_AHB_ERR);
- + goto handle_ahberr_done;
- + }
- +
- + hcd->fops->complete(hcd, urb->priv, urb, -DWC_E_IO);
- +
- + /*
- + * Force a channel halt. Don't call halt_channel because that won't
- + * write to the HCCHARn register in DMA mode to force the halt.
- + */
- + dwc_otg_hc_halt(hcd->core_if, hc, DWC_OTG_HC_XFER_AHB_ERR);
- +handle_ahberr_done:
- + disable_hc_int(hc_regs, ahberr);
- + return 1;
- +}
- +
- +/**
- + * Handles a host channel transaction error interrupt. This handler may be
- + * called in either DMA mode or Slave mode.
- + */
- +static int32_t handle_hc_xacterr_intr(dwc_otg_hcd_t * hcd,
- + dwc_hc_t * hc,
- + dwc_otg_hc_regs_t * hc_regs,
- + dwc_otg_qtd_t * qtd)
- +{
- + DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: "
- + "Transaction Error--\n", hc->hc_num);
- +
- + if (hcd->core_if->dma_desc_enable) {
- + dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs,
- + DWC_OTG_HC_XFER_XACT_ERR);
- + goto handle_xacterr_done;
- + }
- +
- + switch (dwc_otg_hcd_get_pipe_type(&qtd->urb->pipe_info)) {
- + case UE_CONTROL:
- + case UE_BULK:
- + qtd->error_count++;
- + if (!hc->qh->ping_state) {
- +
- + update_urb_state_xfer_intr(hc, hc_regs,
- + qtd->urb, qtd,
- + DWC_OTG_HC_XFER_XACT_ERR);
- + dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
- + if (!hc->ep_is_in && hc->speed == DWC_OTG_EP_SPEED_HIGH) {
- + hc->qh->ping_state = 1;
- + }
- + }
- +
- + /*
- + * Halt the channel so the transfer can be re-started from
- + * the appropriate point or the PING protocol will start.
- + */
- + halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR);
- + break;
- + case UE_INTERRUPT:
- + qtd->error_count++;
- + if (hc->do_split && hc->complete_split) {
- + qtd->complete_split = 0;
- + }
- + halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR);
- + break;
- + case UE_ISOCHRONOUS:
- + {
- + dwc_otg_halt_status_e halt_status;
- + halt_status =
- + update_isoc_urb_state(hcd, hc, hc_regs, qtd,
- + DWC_OTG_HC_XFER_XACT_ERR);
- +
- + halt_channel(hcd, hc, qtd, halt_status);
- + }
- + break;
- + }
- +handle_xacterr_done:
- + disable_hc_int(hc_regs, xacterr);
- +
- + return 1;
- +}
- +
- +/**
- + * Handles a host channel frame overrun interrupt. This handler may be called
- + * in either DMA mode or Slave mode.
- + */
- +static int32_t handle_hc_frmovrun_intr(dwc_otg_hcd_t * hcd,
- + dwc_hc_t * hc,
- + dwc_otg_hc_regs_t * hc_regs,
- + dwc_otg_qtd_t * qtd)
- +{
- + DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: "
- + "Frame Overrun--\n", hc->hc_num);
- +
- + switch (dwc_otg_hcd_get_pipe_type(&qtd->urb->pipe_info)) {
- + case UE_CONTROL:
- + case UE_BULK:
- + break;
- + case UE_INTERRUPT:
- + halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_FRAME_OVERRUN);
- + break;
- + case UE_ISOCHRONOUS:
- + {
- + dwc_otg_halt_status_e halt_status;
- + halt_status =
- + update_isoc_urb_state(hcd, hc, hc_regs, qtd,
- + DWC_OTG_HC_XFER_FRAME_OVERRUN);
- +
- + halt_channel(hcd, hc, qtd, halt_status);
- + }
- + break;
- + }
- +
- + disable_hc_int(hc_regs, frmovrun);
- +
- + return 1;
- +}
- +
- +/**
- + * Handles a host channel data toggle error interrupt. This handler may be
- + * called in either DMA mode or Slave mode.
- + */
- +static int32_t handle_hc_datatglerr_intr(dwc_otg_hcd_t * hcd,
- + dwc_hc_t * hc,
- + dwc_otg_hc_regs_t * hc_regs,
- + dwc_otg_qtd_t * qtd)
- +{
- + DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: "
- + "Data Toggle Error on %s transfer--\n",
- + hc->hc_num, (hc->ep_is_in ? "IN" : "OUT"));
- +
- + /* Data toggles on split transactions cause the hc to halt.
- + * restart transfer */
- + if(hc->qh->do_split)
- + {
- + qtd->error_count++;
- + dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
- + update_urb_state_xfer_intr(hc, hc_regs,
- + qtd->urb, qtd, DWC_OTG_HC_XFER_XACT_ERR);
- + halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR);
- + } else if (hc->ep_is_in) {
- + /* An unmasked data toggle error on a non-split DMA transaction is
- + * for the sole purpose of resetting error counts. Disable other
- + * interrupts unmasked for the same reason.
- + */
- + if(hcd->core_if->dma_enable) {
- + disable_hc_int(hc_regs, ack);
- + disable_hc_int(hc_regs, nak);
- + }
- + qtd->error_count = 0;
- + }
- +
- + disable_hc_int(hc_regs, datatglerr);
- +
- + return 1;
- +}
- +
- +#ifdef DEBUG
- +/**
- + * This function is for debug only. It checks that a valid halt status is set
- + * and that HCCHARn.chdis is clear. If there's a problem, corrective action is
- + * taken and a warning is issued.
- + * @return 1 if halt status is ok, 0 otherwise.
- + */
- +static inline int halt_status_ok(dwc_otg_hcd_t * hcd,
- + dwc_hc_t * hc,
- + dwc_otg_hc_regs_t * hc_regs,
- + dwc_otg_qtd_t * qtd)
- +{
- + hcchar_data_t hcchar;
- + hctsiz_data_t hctsiz;
- + hcint_data_t hcint;
- + hcintmsk_data_t hcintmsk;
- + hcsplt_data_t hcsplt;
- +
- + if (hc->halt_status == DWC_OTG_HC_XFER_NO_HALT_STATUS) {
- + /*
- + * This code is here only as a check. This condition should
- + * never happen. Ignore the halt if it does occur.
- + */
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- + hctsiz.d32 = DWC_READ_REG32(&hc_regs->hctsiz);
- + hcint.d32 = DWC_READ_REG32(&hc_regs->hcint);
- + hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk);
- + hcsplt.d32 = DWC_READ_REG32(&hc_regs->hcsplt);
- + DWC_WARN
- + ("%s: hc->halt_status == DWC_OTG_HC_XFER_NO_HALT_STATUS, "
- + "channel %d, hcchar 0x%08x, hctsiz 0x%08x, "
- + "hcint 0x%08x, hcintmsk 0x%08x, "
- + "hcsplt 0x%08x, qtd->complete_split %d\n", __func__,
- + hc->hc_num, hcchar.d32, hctsiz.d32, hcint.d32,
- + hcintmsk.d32, hcsplt.d32, qtd->complete_split);
- +
- + DWC_WARN("%s: no halt status, channel %d, ignoring interrupt\n",
- + __func__, hc->hc_num);
- + DWC_WARN("\n");
- + clear_hc_int(hc_regs, chhltd);
- + return 0;
- + }
- +
- + /*
- + * This code is here only as a check. hcchar.chdis should
- + * never be set when the halt interrupt occurs. Halt the
- + * channel again if it does occur.
- + */
- + hcchar.d32 = DWC_READ_REG32(&hc_regs->hcchar);
- + if (hcchar.b.chdis) {
- + DWC_WARN("%s: hcchar.chdis set unexpectedly, "
- + "hcchar 0x%08x, trying to halt again\n",
- + __func__, hcchar.d32);
- + clear_hc_int(hc_regs, chhltd);
- + hc->halt_pending = 0;
- + halt_channel(hcd, hc, qtd, hc->halt_status);
- + return 0;
- + }
- +
- + return 1;
- +}
- +#endif
- +
- +/**
- + * Handles a host Channel Halted interrupt in DMA mode. This handler
- + * determines the reason the channel halted and proceeds accordingly.
- + */
- +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)
- +{
- + 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.
- + */
- + if (hcd->core_if->snpsid >= OTG_CORE_REV_2_71a) {
- + if (hc->speed == DWC_OTG_EP_SPEED_HIGH && !hc->ep_is_in &&
- + (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL ||
- + hc->ep_type == DWC_OTG_EP_TYPE_BULK)) {
- + out_nak_enh = 1;
- + }
- + }
- +
- + if (hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE ||
- + (hc->halt_status == DWC_OTG_HC_XFER_AHB_ERR
- + && !hcd->core_if->dma_desc_enable)) {
- + /*
- + * Just release the channel. A dequeue can happen on a
- + * transfer timeout. In the case of an AHB Error, the channel
- + * was forced to halt because there's no way to gracefully
- + * recover.
- + */
- + if (hcd->core_if->dma_desc_enable)
- + dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs,
- + hc->halt_status);
- + else
- + release_channel(hcd, hc, qtd, hc->halt_status);
- + return;
- + }
- +
- + /* Read the HCINTn register to determine the cause for the halt. */
- +
- + 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
- + * says that on SPLIT-ISOC OUT transfers in DMA mode that a HALT
- + * interrupt w/ACK bit set should occur, but I only see the
- + * XFERCOMP bit, even with it masked out. This is a workaround
- + * for that behavior. Should fix this when hardware is fixed.
- + */
- + if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC && !hc->ep_is_in) {
- + handle_hc_ack_intr(hcd, hc, hc_regs, qtd);
- + }
- + handle_hc_xfercomp_intr(hcd, hc, hc_regs, qtd);
- + } else if (hcint.b.stall) {
- + handle_hc_stall_intr(hcd, hc, hc_regs, qtd);
- + } else if (hcint.b.xacterr && !hcd->core_if->dma_desc_enable) {
- + if (out_nak_enh) {
- + if (hcint.b.nyet || hcint.b.nak || hcint.b.ack) {
- + DWC_DEBUGPL(DBG_HCD, "XactErr with NYET/NAK/ACK\n");
- + qtd->error_count = 0;
- + } else {
- + DWC_DEBUGPL(DBG_HCD, "XactErr without NYET/NAK/ACK\n");
- + }
- + }
- +
- + /*
- + * Must handle xacterr before nak or ack. Could get a xacterr
- + * at the same time as either of these on a BULK/CONTROL OUT
- + * that started with a PING. The xacterr takes precedence.
- + */
- + handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd);
- + } else if (hcint.b.xcs_xact && hcd->core_if->dma_desc_enable) {
- + handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd);
- + } else if (hcint.b.ahberr && hcd->core_if->dma_desc_enable) {
- + handle_hc_ahberr_intr(hcd, hc, hc_regs, qtd);
- + } else if (hcint.b.bblerr) {
- + handle_hc_babble_intr(hcd, hc, hc_regs, qtd);
- + } else if (hcint.b.frmovrun) {
- + handle_hc_frmovrun_intr(hcd, hc, hc_regs, qtd);
- + } else if (hcint.b.datatglerr) {
- + handle_hc_datatglerr_intr(hcd, hc, hc_regs, qtd);
- + } else if (!out_nak_enh) {
- + if (hcint.b.nyet) {
- + /*
- + * Must handle nyet before nak or ack. Could get a nyet at the
- + * same time as either of those on a BULK/CONTROL OUT that
- + * started with a PING. The nyet takes precedence.
- + */
- + handle_hc_nyet_intr(hcd, hc, hc_regs, qtd);
- + } else if (hcint.b.nak && !hcintmsk.b.nak) {
- + /*
- + * If nak is not masked, it's because a non-split IN transfer
- + * is in an error state. In that case, the nak is handled by
- + * the nak interrupt handler, not here. Handle nak here for
- + * BULK/CONTROL OUT transfers, which halt on a NAK to allow
- + * rewinding the buffer pointer.
- + */
- + handle_hc_nak_intr(hcd, hc, hc_regs, qtd);
- + } else if (hcint.b.ack && !hcintmsk.b.ack) {
- + /*
- + * If ack is not masked, it's because a non-split IN transfer
- + * is in an error state. In that case, the ack is handled by
- + * the ack interrupt handler, not here. Handle ack here for
- + * split transfers. Start splits halt on ACK.
- + */
- + handle_hc_ack_intr(hcd, hc, hc_regs, qtd);
- + } else {
- + if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
- + hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
- + /*
- + * A periodic transfer halted with no other channel
- + * interrupts set. Assume it was halted by the core
- + * because it could not be completed in its scheduled
- + * (micro)frame.
- + */
- +#ifdef DEBUG
- + DWC_PRINTF
- + ("%s: Halt channel %d (assume incomplete periodic transfer)\n",
- + __func__, hc->hc_num);
- +#endif
- + halt_channel(hcd, hc, qtd,
- + DWC_OTG_HC_XFER_PERIODIC_INCOMPLETE);
- + } else {
- + DWC_ERROR
- + ("%s: Channel %d, DMA Mode -- ChHltd set, but reason "
- + "for halting is unknown, hcint 0x%08x, intsts 0x%08x\n",
- + __func__, hc->hc_num, hcint.d32,
- + DWC_READ_REG32(&hcd->
- + core_if->core_global_regs->
- + gintsts));
- + /* Failthrough: use 3-strikes rule */
- + qtd->error_count++;
- + dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
- + update_urb_state_xfer_intr(hc, hc_regs,
- + qtd->urb, qtd, DWC_OTG_HC_XFER_XACT_ERR);
- + halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR);
- + }
- +
- + }
- + } else {
- + DWC_PRINTF("NYET/NAK/ACK/other in non-error case, 0x%08x\n",
- + hcint.d32);
- + /* Failthrough: use 3-strikes rule */
- + qtd->error_count++;
- + dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd);
- + update_urb_state_xfer_intr(hc, hc_regs,
- + qtd->urb, qtd, DWC_OTG_HC_XFER_XACT_ERR);
- + halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR);
- + }
- +}
- +
- +/**
- + * Handles a host channel Channel Halted interrupt.
- + *
- + * In slave mode, this handler is called only when the driver specifically
- + * requests a halt. This occurs during handling other host channel interrupts
- + * (e.g. nak, xacterr, stall, nyet, etc.).
- + *
- + * In DMA mode, this is the interrupt that occurs when the core has finished
- + * processing a transfer on a channel. Other host channel interrupts (except
- + * ahberr) are disabled in DMA mode.
- + */
- +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)
- +{
- + 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);
- + } else {
- +#ifdef DEBUG
- + if (!halt_status_ok(hcd, hc, hc_regs, qtd)) {
- + return 1;
- + }
- +#endif
- + release_channel(hcd, hc, qtd, hc->halt_status);
- + }
- +
- + 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++;
- + }
- + }
- + qh->sched_frame = dwc_frame_num_inc(qh->sched_frame, qh->interval * (nr_frames - 1));
- +
- + //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);
- +}
- +
- +/**
- + * 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.
- + */
- +void 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_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;
- + hctsiz_data_t hctsiz = hcd->fiq_state->channel[num].hctsiz_copy;
- + int hostchannels = 0;
- + fiq_print(FIQDBG_INT, hcd->fiq_state, "OUT %01d %01d ", num , st->fsm);
- +
- + hostchannels = hcd->available_host_channels;
- + if (hc->halt_pending) {
- + /* Dequeue: The FIQ was allowed to complete the transfer but state has been cleared. */
- + if (hc->qh && st->fsm == FIQ_NP_SPLIT_DONE &&
- + hcint.b.xfercomp && hc->qh->ep_type == UE_BULK) {
- + if (hctsiz.b.pid == DWC_HCTSIZ_DATA0) {
- + hc->qh->data_toggle = DWC_OTG_HC_PID_DATA1;
- + } else {
- + hc->qh->data_toggle = DWC_OTG_HC_PID_DATA0;
- + }
- + }
- + release_channel(hcd, hc, NULL, hc->halt_status);
- + return;
- + }
- +
- + qtd = DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list);
- + switch (st->fsm) {
- + case FIQ_TEST:
- + break;
- +
- + case FIQ_DEQUEUE_ISSUED:
- + /* Handled above, but keep for posterity */
- + release_channel(hcd, hc, NULL, hc->halt_status);
- + 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);
- + } else {
- + DWC_WARN("Unexpected IRQ state on FSM transaction:"
- + "dev_addr=%d ep=%d fsm=%d, hcint=0x%08x\n",
- + hc->dev_addr, hc->ep_num, st->fsm, hcint.d32);
- + release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS);
- + }
- + 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 {
- + DWC_WARN("Unexpected IRQ state on FSM transaction:"
- + "dev_addr=%d ep=%d fsm=%d, hcint=0x%08x\n",
- + hc->dev_addr, hc->ep_num, st->fsm, hcint.d32);
- + release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS);
- + }
- + 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 {
- + DWC_WARN("Unexpected IRQ state on FSM transaction:"
- + "dev_addr=%d ep=%d fsm=%d, hcint=0x%08x\n",
- + hc->dev_addr, hc->ep_num, st->fsm, hcint.d32);
- + release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS);
- + }
- + 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, hc->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, hc->qh, qtd, num);
- + hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0);
- + release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE);
- + 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:
- + DWC_WARN("Unexpected state received on hc=%d fsm=%d on transfer to device %d ep 0x%x",
- + hc->hc_num, st->fsm, hc->dev_addr, hc->ep_num);
- + qtd->error_count++;
- + release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS);
- + }
- + return;
- +}
- +
- +/** 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;
- + hcintmsk_data_t hcintmsk;
- + dwc_hc_t *hc;
- + dwc_otg_hc_regs_t *hc_regs;
- + dwc_otg_qtd_t *qtd;
- +
- + DWC_DEBUGPL(DBG_HCDV, "--Host Channel Interrupt--, Channel %d\n", num);
- +
- + hc = dwc_otg_hcd->hc_ptr_array[num];
- + hc_regs = dwc_otg_hcd->core_if->host_if->hc_regs[num];
- + if(hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) {
- + /* A dequeue was issued for this transfer. Our QTD has gone away
- + * but in the case of a FIQ transfer, the transfer would have run
- + * to completion.
- + */
- + if (fiq_fsm_enable && dwc_otg_hcd->fiq_state->channel[num].fsm != FIQ_PASSTHROUGH) {
- + dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd, num);
- + } else {
- + release_channel(dwc_otg_hcd, hc, NULL, hc->halt_status);
- + }
- + return 1;
- + }
- + 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);
- + hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk);
- + hcint.d32 = hcint.d32 & hcintmsk.d32;
- + if (!dwc_otg_hcd->core_if->dma_enable) {
- + if (hcint.b.chhltd && hcint.d32 != 0x2) {
- + hcint.b.chhltd = 0;
- + }
- + }
- +
- + if (hcint.b.xfercomp) {
- + retval |=
- + handle_hc_xfercomp_intr(dwc_otg_hcd, hc, hc_regs, qtd);
- + /*
- + * If NYET occurred at same time as Xfer Complete, the NYET is
- + * handled by the Xfer Complete interrupt handler. Don't want
- + * to call the NYET interrupt handler in this case.
- + */
- + hcint.b.nyet = 0;
- + }
- + if (hcint.b.chhltd) {
- + 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);
- + }
- + if (hcint.b.stall) {
- + retval |= handle_hc_stall_intr(dwc_otg_hcd, hc, hc_regs, qtd);
- + }
- + if (hcint.b.nak) {
- + retval |= handle_hc_nak_intr(dwc_otg_hcd, hc, hc_regs, qtd);
- + }
- + if (hcint.b.ack) {
- + if(!hcint.b.chhltd)
- + retval |= handle_hc_ack_intr(dwc_otg_hcd, hc, hc_regs, qtd);
- + }
- + if (hcint.b.nyet) {
- + retval |= handle_hc_nyet_intr(dwc_otg_hcd, hc, hc_regs, qtd);
- + }
- + if (hcint.b.xacterr) {
- + retval |= handle_hc_xacterr_intr(dwc_otg_hcd, hc, hc_regs, qtd);
- + }
- + if (hcint.b.bblerr) {
- + retval |= handle_hc_babble_intr(dwc_otg_hcd, hc, hc_regs, qtd);
- + }
- + if (hcint.b.frmovrun) {
- + retval |=
- + handle_hc_frmovrun_intr(dwc_otg_hcd, hc, hc_regs, qtd);
- + }
- + if (hcint.b.datatglerr) {
- + retval |=
- + handle_hc_datatglerr_intr(dwc_otg_hcd, hc, hc_regs, qtd);
- + }
- +
- + return retval;
- +}
- +#endif /* DWC_DEVICE_ONLY */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c
- @@ -0,0 +1,1086 @@
- +
- +/* ==========================================================================
- + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_linux.c $
- + * $Revision: #20 $
- + * $Date: 2011/10/26 $
- + * $Change: 1872981 $
- + *
- + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
- + * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
- + * otherwise expressly agreed to in writing between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product under
- + * any End User Software License Agreement or Agreement for Licensed Product
- + * with Synopsys or any supplement thereto. You are permitted to use and
- + * redistribute this Software in source and binary forms, with or without
- + * modification, provided that redistributions of source code must retain this
- + * notice. You may not view, use, disclose, copy or distribute this file or
- + * any information contained herein except pursuant to this license grant from
- + * Synopsys. If you do not agree with this notice, including the disclaimer
- + * below, then you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
- + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- + * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
- + * ========================================================================== */
- +#ifndef DWC_DEVICE_ONLY
- +
- +/**
- + * @file
- + *
- + * This file contains the implementation of the HCD. In Linux, the HCD
- + * implements the hc_driver API.
- + */
- +#include <linux/kernel.h>
- +#include <linux/module.h>
- +#include <linux/moduleparam.h>
- +#include <linux/init.h>
- +#include <linux/device.h>
- +#include <linux/errno.h>
- +#include <linux/list.h>
- +#include <linux/interrupt.h>
- +#include <linux/string.h>
- +#include <linux/dma-mapping.h>
- +#include <linux/version.h>
- +#include <asm/io.h>
- +#ifdef CONFIG_ARM
- +#include <asm/fiq.h>
- +#endif
- +#include <linux/usb.h>
- +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
- +#include <../drivers/usb/core/hcd.h>
- +#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
- +#else
- +#define USB_URB_EP_LINKING 0
- +#endif
- +
- +#include "dwc_otg_hcd_if.h"
- +#include "dwc_otg_dbg.h"
- +#include "dwc_otg_driver.h"
- +#include "dwc_otg_hcd.h"
- +
- +#ifndef __virt_to_bus
- +#define __virt_to_bus __virt_to_phys
- +#define __bus_to_virt __phys_to_virt
- +#define __pfn_to_bus(x) __pfn_to_phys(x)
- +#define __bus_to_pfn(x) __phys_to_pfn(x)
- +#endif
- +
- +extern unsigned char _dwc_otg_fiq_stub, _dwc_otg_fiq_stub_end;
- +
- +/**
- + * Gets the endpoint number from a _bEndpointAddress argument. The endpoint is
- + * qualified with its direction (possible 32 endpoints per device).
- + */
- +#define dwc_ep_addr_to_endpoint(_bEndpointAddress_) ((_bEndpointAddress_ & USB_ENDPOINT_NUMBER_MASK) | \
- + ((_bEndpointAddress_ & USB_DIR_IN) != 0) << 4)
- +
- +static const char dwc_otg_hcd_name[] = "dwc_otg_hcd";
- +
- +extern bool fiq_enable;
- +
- +/** @name Linux HC Driver API Functions */
- +/** @{ */
- +/* manage i/o requests, device state */
- +static int dwc_otg_urb_enqueue(struct usb_hcd *hcd,
- +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
- + struct usb_host_endpoint *ep,
- +#endif
- + struct urb *urb, gfp_t mem_flags);
- +
- +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)
- +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
- +static int dwc_otg_urb_dequeue(struct usb_hcd *hcd, struct urb *urb);
- +#endif
- +#else /* kernels at or post 2.6.30 */
- +static int dwc_otg_urb_dequeue(struct usb_hcd *hcd,
- + struct urb *urb, int status);
- +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30) */
- +
- +static void endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep);
- +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
- +static void endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep);
- +#endif
- +static irqreturn_t dwc_otg_hcd_irq(struct usb_hcd *hcd);
- +extern int hcd_start(struct usb_hcd *hcd);
- +extern void hcd_stop(struct usb_hcd *hcd);
- +static int get_frame_number(struct usb_hcd *hcd);
- +extern int hub_status_data(struct usb_hcd *hcd, char *buf);
- +extern int hub_control(struct usb_hcd *hcd,
- + u16 typeReq,
- + u16 wValue, u16 wIndex, char *buf, u16 wLength);
- +
- +struct wrapper_priv_data {
- + dwc_otg_hcd_t *dwc_otg_hcd;
- +};
- +
- +/** @} */
- +
- +static struct hc_driver dwc_otg_hc_driver = {
- +
- + .description = dwc_otg_hcd_name,
- + .product_desc = "DWC OTG Controller",
- + .hcd_priv_size = sizeof(struct wrapper_priv_data),
- +
- + .irq = dwc_otg_hcd_irq,
- +
- + .flags = HCD_MEMORY | HCD_DMA | HCD_USB2,
- +
- + //.reset =
- + .start = hcd_start,
- + //.suspend =
- + //.resume =
- + .stop = hcd_stop,
- +
- + .urb_enqueue = dwc_otg_urb_enqueue,
- + .urb_dequeue = dwc_otg_urb_dequeue,
- + .endpoint_disable = endpoint_disable,
- +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
- + .endpoint_reset = endpoint_reset,
- +#endif
- + .get_frame_number = get_frame_number,
- +
- + .hub_status_data = hub_status_data,
- + .hub_control = hub_control,
- + //.bus_suspend =
- + //.bus_resume =
- +};
- +
- +/** Gets the dwc_otg_hcd from a struct usb_hcd */
- +static inline dwc_otg_hcd_t *hcd_to_dwc_otg_hcd(struct usb_hcd *hcd)
- +{
- + struct wrapper_priv_data *p;
- + p = (struct wrapper_priv_data *)(hcd->hcd_priv);
- + return p->dwc_otg_hcd;
- +}
- +
- +/** Gets the struct usb_hcd that contains a dwc_otg_hcd_t. */
- +static inline struct usb_hcd *dwc_otg_hcd_to_hcd(dwc_otg_hcd_t * dwc_otg_hcd)
- +{
- + return dwc_otg_hcd_get_priv_data(dwc_otg_hcd);
- +}
- +
- +/** Gets the usb_host_endpoint associated with an URB. */
- +inline struct usb_host_endpoint *dwc_urb_to_endpoint(struct urb *urb)
- +{
- + struct usb_device *dev = urb->dev;
- + int ep_num = usb_pipeendpoint(urb->pipe);
- +
- + if (usb_pipein(urb->pipe))
- + return dev->ep_in[ep_num];
- + else
- + return dev->ep_out[ep_num];
- +}
- +
- +static int _disconnect(dwc_otg_hcd_t * hcd)
- +{
- + struct usb_hcd *usb_hcd = dwc_otg_hcd_to_hcd(hcd);
- +
- + usb_hcd->self.is_b_host = 0;
- + return 0;
- +}
- +
- +static int _start(dwc_otg_hcd_t * hcd)
- +{
- + struct usb_hcd *usb_hcd = dwc_otg_hcd_to_hcd(hcd);
- +
- + usb_hcd->self.is_b_host = dwc_otg_hcd_is_b_host(hcd);
- + hcd_start(usb_hcd);
- +
- + return 0;
- +}
- +
- +static int _hub_info(dwc_otg_hcd_t * hcd, void *urb_handle, uint32_t * hub_addr,
- + uint32_t * port_addr)
- +{
- + struct urb *urb = (struct urb *)urb_handle;
- + struct usb_bus *bus;
- +#if 1 //GRAYG - temporary
- + if (NULL == urb_handle)
- + DWC_ERROR("**** %s - NULL URB handle\n", __func__);//GRAYG
- + if (NULL == urb->dev)
- + DWC_ERROR("**** %s - URB has no device\n", __func__);//GRAYG
- + if (NULL == port_addr)
- + DWC_ERROR("**** %s - NULL port_address\n", __func__);//GRAYG
- +#endif
- + if (urb->dev->tt) {
- + if (NULL == urb->dev->tt->hub) {
- + DWC_ERROR("**** %s - (URB's transactor has no TT - giving no hub)\n",
- + __func__); //GRAYG
- + //*hub_addr = (u8)usb_pipedevice(urb->pipe); //GRAYG
- + *hub_addr = 0; //GRAYG
- + // we probably shouldn't have a transaction translator if
- + // there's no associated hub?
- + } else {
- + bus = hcd_to_bus(dwc_otg_hcd_to_hcd(hcd));
- + if (urb->dev->tt->hub == bus->root_hub)
- + *hub_addr = 0;
- + else
- + *hub_addr = urb->dev->tt->hub->devnum;
- + }
- + *port_addr = urb->dev->ttport;
- + } else {
- + *hub_addr = 0;
- + *port_addr = urb->dev->ttport;
- + }
- + return 0;
- +}
- +
- +static int _speed(dwc_otg_hcd_t * hcd, void *urb_handle)
- +{
- + struct urb *urb = (struct urb *)urb_handle;
- + return urb->dev->speed;
- +}
- +
- +static int _get_b_hnp_enable(dwc_otg_hcd_t * hcd)
- +{
- + struct usb_hcd *usb_hcd = dwc_otg_hcd_to_hcd(hcd);
- + return usb_hcd->self.b_hnp_enable;
- +}
- +
- +static void allocate_bus_bandwidth(struct usb_hcd *hcd, uint32_t bw,
- + struct urb *urb)
- +{
- + hcd_to_bus(hcd)->bandwidth_allocated += bw / urb->interval;
- + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
- + hcd_to_bus(hcd)->bandwidth_isoc_reqs++;
- + } else {
- + hcd_to_bus(hcd)->bandwidth_int_reqs++;
- + }
- +}
- +
- +static void free_bus_bandwidth(struct usb_hcd *hcd, uint32_t bw,
- + struct urb *urb)
- +{
- + hcd_to_bus(hcd)->bandwidth_allocated -= bw / urb->interval;
- + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
- + hcd_to_bus(hcd)->bandwidth_isoc_reqs--;
- + } else {
- + hcd_to_bus(hcd)->bandwidth_int_reqs--;
- + }
- +}
- +
- +/**
- + * Sets the final status of an URB and returns it to the device driver. Any
- + * required cleanup of the URB is performed. The HCD lock should be held on
- + * entry.
- + */
- +static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle,
- + dwc_otg_hcd_urb_t * dwc_otg_urb, int32_t status)
- +{
- + struct urb *urb = (struct urb *)urb_handle;
- + urb_tq_entry_t *new_entry;
- + int rc = 0;
- + if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) {
- + DWC_PRINTF("%s: urb %p, device %d, ep %d %s, status=%d\n",
- + __func__, urb, usb_pipedevice(urb->pipe),
- + usb_pipeendpoint(urb->pipe),
- + usb_pipein(urb->pipe) ? "IN" : "OUT", status);
- + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
- + int i;
- + for (i = 0; i < urb->number_of_packets; i++) {
- + DWC_PRINTF(" ISO Desc %d status: %d\n",
- + i, urb->iso_frame_desc[i].status);
- + }
- + }
- + }
- + new_entry = DWC_ALLOC_ATOMIC(sizeof(urb_tq_entry_t));
- + urb->actual_length = dwc_otg_hcd_urb_get_actual_length(dwc_otg_urb);
- + /* Convert status value. */
- + switch (status) {
- + case -DWC_E_PROTOCOL:
- + status = -EPROTO;
- + break;
- + case -DWC_E_IN_PROGRESS:
- + status = -EINPROGRESS;
- + break;
- + case -DWC_E_PIPE:
- + status = -EPIPE;
- + break;
- + case -DWC_E_IO:
- + status = -EIO;
- + break;
- + case -DWC_E_TIMEOUT:
- + status = -ETIMEDOUT;
- + break;
- + case -DWC_E_OVERFLOW:
- + status = -EOVERFLOW;
- + break;
- + case -DWC_E_SHUTDOWN:
- + status = -ESHUTDOWN;
- + break;
- + default:
- + if (status) {
- + DWC_PRINTF("Uknown urb status %d\n", status);
- +
- + }
- + }
- +
- + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
- + int i;
- +
- + urb->error_count = dwc_otg_hcd_urb_get_error_count(dwc_otg_urb);
- + urb->actual_length = 0;
- + for (i = 0; i < urb->number_of_packets; ++i) {
- + urb->iso_frame_desc[i].actual_length =
- + dwc_otg_hcd_urb_get_iso_desc_actual_length
- + (dwc_otg_urb, i);
- + urb->actual_length += urb->iso_frame_desc[i].actual_length;
- + urb->iso_frame_desc[i].status =
- + dwc_otg_hcd_urb_get_iso_desc_status(dwc_otg_urb, i);
- + }
- + }
- +
- + urb->status = status;
- + urb->hcpriv = NULL;
- + if (!status) {
- + if ((urb->transfer_flags & URB_SHORT_NOT_OK) &&
- + (urb->actual_length < urb->transfer_buffer_length)) {
- + urb->status = -EREMOTEIO;
- + }
- + }
- +
- + if ((usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) ||
- + (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) {
- + struct usb_host_endpoint *ep = dwc_urb_to_endpoint(urb);
- + if (ep) {
- + free_bus_bandwidth(dwc_otg_hcd_to_hcd(hcd),
- + dwc_otg_hcd_get_ep_bandwidth(hcd,
- + ep->hcpriv),
- + urb);
- + }
- + }
- + DWC_FREE(dwc_otg_urb);
- + if (!new_entry) {
- + DWC_ERROR("dwc_otg_hcd: complete: cannot allocate URB TQ entry\n");
- + urb->status = -EPROTO;
- + /* don't schedule the tasklet -
- + * directly return the packet here with error. */
- +#if USB_URB_EP_LINKING
- + usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb);
- +#endif
- +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
- + usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb);
- +#else
- + usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, urb->status);
- +#endif
- + } else {
- + new_entry->urb = urb;
- +#if USB_URB_EP_LINKING
- + rc = usb_hcd_check_unlink_urb(dwc_otg_hcd_to_hcd(hcd), urb, urb->status);
- + if(0 == rc) {
- + usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb);
- + }
- +#endif
- + if(0 == rc) {
- + DWC_TAILQ_INSERT_TAIL(&hcd->completed_urb_list, new_entry,
- + urb_tq_entries);
- + DWC_TASK_HI_SCHEDULE(hcd->completion_tasklet);
- + }
- + }
- + return 0;
- +}
- +
- +static struct dwc_otg_hcd_function_ops hcd_fops = {
- + .start = _start,
- + .disconnect = _disconnect,
- + .hub_info = _hub_info,
- + .speed = _speed,
- + .complete = _complete,
- + .get_b_hnp_enable = _get_b_hnp_enable,
- +};
- +
- +#ifdef CONFIG_ARM64
- +
- +static int simfiq_irq = -1;
- +
- +void local_fiq_enable(void)
- +{
- + if (simfiq_irq >= 0)
- + enable_irq(simfiq_irq);
- +}
- +
- +void local_fiq_disable(void)
- +{
- + if (simfiq_irq >= 0)
- + disable_irq(simfiq_irq);
- +}
- +
- +irqreturn_t fiq_irq_handler(int irq, void *dev_id)
- +{
- + dwc_otg_hcd_t *dwc_otg_hcd = (dwc_otg_hcd_t *)dev_id;
- +
- + if (fiq_fsm_enable)
- + dwc_otg_fiq_fsm(dwc_otg_hcd->fiq_state, dwc_otg_hcd->core_if->core_params->host_channels);
- + else
- + dwc_otg_fiq_nop(dwc_otg_hcd->fiq_state);
- +
- + return IRQ_HANDLED;
- +}
- +
- +#else
- +static struct fiq_handler fh = {
- + .name = "usb_fiq",
- +};
- +
- +#endif
- +
- +static void hcd_init_fiq(void *cookie)
- +{
- + dwc_otg_device_t *otg_dev = cookie;
- + dwc_otg_hcd_t *dwc_otg_hcd = otg_dev->hcd;
- +#ifdef CONFIG_ARM64
- + int retval = 0;
- + int irq;
- +#else
- + struct pt_regs regs;
- + int irq;
- +
- + if (claim_fiq(&fh)) {
- + DWC_ERROR("Can't claim FIQ");
- + BUG();
- + }
- + DWC_WARN("FIQ on core %d", smp_processor_id());
- + DWC_WARN("FIQ ASM at %px length %d", &_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);
- +#endif
- +
- + dwc_otg_hcd->fiq_state->dwc_regs_base = otg_dev->os_dep.base;
- + //Set the mphi periph to the required registers
- + dwc_otg_hcd->fiq_state->mphi_regs.base = otg_dev->os_dep.mphi_base;
- + if (otg_dev->os_dep.use_swirq) {
- + dwc_otg_hcd->fiq_state->mphi_regs.swirq_set =
- + otg_dev->os_dep.mphi_base + 0x1f0;
- + dwc_otg_hcd->fiq_state->mphi_regs.swirq_clr =
- + otg_dev->os_dep.mphi_base + 0x1f4;
- + DWC_WARN("Fake MPHI regs_base at %px",
- + dwc_otg_hcd->fiq_state->mphi_regs.base);
- + } else {
- + 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_WARN("MPHI regs_base at %px",
- + 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
- +#ifdef CONFIG_ARM64
- + irq = otg_dev->os_dep.fiq_num;
- +
- + if (irq < 0) {
- + DWC_ERROR("Can't get SIM-FIQ irq");
- + return;
- + }
- +
- + retval = request_irq(irq, fiq_irq_handler, 0, "dwc_otg_sim-fiq", dwc_otg_hcd);
- +
- + if (retval < 0) {
- + DWC_ERROR("Unable to request SIM-FIQ irq\n");
- + return;
- + }
- +
- + simfiq_irq = irq;
- +#else
- +#ifdef CONFIG_GENERIC_IRQ_MULTI_HANDLER
- + irq = otg_dev->os_dep.fiq_num;
- +#else
- + irq = INTERRUPT_VC_USB;
- +#endif
- + if (irq < 0) {
- + DWC_ERROR("Can't get FIQ irq");
- + return;
- + }
- + /*
- + * We could take an interrupt immediately after enabling the FIQ.
- + * Ensure coherency of hcd->fiq_state.
- + */
- + smp_mb();
- + enable_fiq(irq);
- + local_fiq_enable();
- +#endif
- +
- +}
- +
- +/**
- + * 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
- + * USB bus with the core and calls the hc_driver->start() function. It returns
- + * a negative error on failure.
- + */
- +int hcd_init(dwc_bus_dev_t *_dev)
- +{
- + struct usb_hcd *hcd = NULL;
- + dwc_otg_hcd_t *dwc_otg_hcd = NULL;
- + dwc_otg_device_t *otg_dev = DWC_OTG_BUSDRVDATA(_dev);
- + int retval = 0;
- + u64 dmamask;
- +
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD INIT otg_dev=%p\n", otg_dev);
- +
- + /* Set device flags indicating whether the HCD supports DMA. */
- + if (dwc_otg_is_dma_enable(otg_dev->core_if))
- + dmamask = DMA_BIT_MASK(32);
- + else
- + dmamask = 0;
- +
- +#if defined(LM_INTERFACE) || defined(PLATFORM_INTERFACE)
- + dma_set_mask(&_dev->dev, dmamask);
- + dma_set_coherent_mask(&_dev->dev, dmamask);
- +#elif defined(PCI_INTERFACE)
- + pci_set_dma_mask(_dev, dmamask);
- + pci_set_consistent_dma_mask(_dev, dmamask);
- +#endif
- +
- + /*
- + * Allocate memory for the base HCD plus the DWC OTG HCD.
- + * Initialize the base HCD.
- + */
- +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)
- + hcd = usb_create_hcd(&dwc_otg_hc_driver, &_dev->dev, _dev->dev.bus_id);
- +#else
- + hcd = usb_create_hcd(&dwc_otg_hc_driver, &_dev->dev, dev_name(&_dev->dev));
- + hcd->has_tt = 1;
- +// hcd->uses_new_polling = 1;
- +// hcd->poll_rh = 0;
- +#endif
- + if (!hcd) {
- + retval = -ENOMEM;
- + goto error1;
- + }
- +
- + hcd->regs = otg_dev->os_dep.base;
- +
- +
- + /* Initialize the DWC OTG HCD. */
- + dwc_otg_hcd = dwc_otg_hcd_alloc_hcd();
- + if (!dwc_otg_hcd) {
- + goto error2;
- + }
- + ((struct wrapper_priv_data *)(hcd->hcd_priv))->dwc_otg_hcd =
- + dwc_otg_hcd;
- + otg_dev->hcd = dwc_otg_hcd;
- + otg_dev->hcd->otg_dev = otg_dev;
- +
- +#ifdef CONFIG_ARM64
- + if (dwc_otg_hcd_init(dwc_otg_hcd, otg_dev->core_if))
- + goto error2;
- +
- + if (fiq_enable)
- + hcd_init_fiq(otg_dev);
- +#else
- + if (dwc_otg_hcd_init(dwc_otg_hcd, otg_dev->core_if)) {
- + goto error2;
- + }
- +
- + if (fiq_enable) {
- + if (num_online_cpus() > 1) {
- + /*
- + * bcm2709: can run the FIQ on a separate core to IRQs.
- + * Ensure driver state is visible to other cores before setting up the FIQ.
- + */
- + smp_mb();
- + smp_call_function_single(1, hcd_init_fiq, otg_dev, 1);
- + } else {
- + smp_call_function_single(0, hcd_init_fiq, otg_dev, 1);
- + }
- + }
- +#endif
- +
- + 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)
- +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) //version field absent later
- + hcd->self.otg_version = dwc_otg_get_otg_version(otg_dev->core_if);
- +#endif
- + /* Don't support SG list at this point */
- + hcd->self.sg_tablesize = 0;
- +#endif
- + /*
- + * Finish generic HCD initialization and start the HCD. This function
- + * allocates the DMA buffer pool, registers the USB bus, requests the
- + * IRQ line, and calls hcd_start method.
- + */
- + retval = usb_add_hcd(hcd, otg_dev->os_dep.irq_num, IRQF_SHARED);
- + if (retval < 0) {
- + goto error2;
- + }
- +
- + dwc_otg_hcd_set_priv_data(dwc_otg_hcd, hcd);
- + return 0;
- +
- +error2:
- + usb_put_hcd(hcd);
- +error1:
- + return retval;
- +}
- +
- +/**
- + * Removes the HCD.
- + * Frees memory and resources associated with the HCD and deregisters the bus.
- + */
- +void hcd_remove(dwc_bus_dev_t *_dev)
- +{
- + dwc_otg_device_t *otg_dev = DWC_OTG_BUSDRVDATA(_dev);
- + dwc_otg_hcd_t *dwc_otg_hcd;
- + struct usb_hcd *hcd;
- +
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD REMOVE otg_dev=%p\n", otg_dev);
- +
- + if (!otg_dev) {
- + DWC_DEBUGPL(DBG_ANY, "%s: otg_dev NULL!\n", __func__);
- + return;
- + }
- +
- + dwc_otg_hcd = otg_dev->hcd;
- +
- + if (!dwc_otg_hcd) {
- + DWC_DEBUGPL(DBG_ANY, "%s: otg_dev->hcd NULL!\n", __func__);
- + return;
- + }
- +
- + hcd = dwc_otg_hcd_to_hcd(dwc_otg_hcd);
- +
- + if (!hcd) {
- + DWC_DEBUGPL(DBG_ANY,
- + "%s: dwc_otg_hcd_to_hcd(dwc_otg_hcd) NULL!\n",
- + __func__);
- + return;
- + }
- + usb_remove_hcd(hcd);
- + dwc_otg_hcd_set_priv_data(dwc_otg_hcd, NULL);
- + dwc_otg_hcd_remove(dwc_otg_hcd);
- + usb_put_hcd(hcd);
- +}
- +
- +/* =========================================================================
- + * Linux HC Driver Functions
- + * ========================================================================= */
- +
- +/** Initializes the DWC_otg controller and its root hub and prepares it for host
- + * mode operation. Activates the root port. Returns 0 on success and a negative
- + * error code on failure. */
- +int hcd_start(struct usb_hcd *hcd)
- +{
- + dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
- + struct usb_bus *bus;
- +
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD START\n");
- + bus = hcd_to_bus(hcd);
- +
- + hcd->state = HC_STATE_RUNNING;
- + if (dwc_otg_hcd_start(dwc_otg_hcd, &hcd_fops)) {
- + return 0;
- + }
- +
- + /* Initialize and connect root hub if one is not already attached */
- + if (bus->root_hub) {
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Has Root Hub\n");
- + /* Inform the HUB driver to resume. */
- + usb_hcd_resume_root_hub(hcd);
- + }
- +
- + return 0;
- +}
- +
- +/**
- + * Halts the DWC_otg host mode operations in a clean manner. USB transfers are
- + * stopped.
- + */
- +void hcd_stop(struct usb_hcd *hcd)
- +{
- + dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
- +
- + dwc_otg_hcd_stop(dwc_otg_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);
- + 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
- +static void dump_urb_info(struct urb *urb, char *fn_name)
- +{
- + DWC_PRINTF("%s, urb %p\n", fn_name, urb);
- + DWC_PRINTF(" Device address: %d\n", usb_pipedevice(urb->pipe));
- + DWC_PRINTF(" Endpoint: %d, %s\n", usb_pipeendpoint(urb->pipe),
- + (usb_pipein(urb->pipe) ? "IN" : "OUT"));
- + DWC_PRINTF(" Endpoint type: %s\n", ( {
- + char *pipetype;
- + switch (usb_pipetype(urb->pipe)) {
- +case PIPE_CONTROL:
- +pipetype = "CONTROL"; break; case PIPE_BULK:
- +pipetype = "BULK"; break; case PIPE_INTERRUPT:
- +pipetype = "INTERRUPT"; break; case PIPE_ISOCHRONOUS:
- +pipetype = "ISOCHRONOUS"; break; default:
- + pipetype = "UNKNOWN"; break;};
- + pipetype;}
- + )) ;
- + DWC_PRINTF(" Speed: %s\n", ( {
- + char *speed; switch (urb->dev->speed) {
- +case USB_SPEED_HIGH:
- +speed = "HIGH"; break; case USB_SPEED_FULL:
- +speed = "FULL"; break; case USB_SPEED_LOW:
- +speed = "LOW"; break; default:
- + speed = "UNKNOWN"; break;};
- + speed;}
- + )) ;
- + DWC_PRINTF(" Max packet size: %d\n",
- + usb_maxpacket(urb->dev, urb->pipe);
- + DWC_PRINTF(" Data buffer length: %d\n", urb->transfer_buffer_length);
- + DWC_PRINTF(" Transfer buffer: %p, Transfer DMA: %p\n",
- + urb->transfer_buffer, (void *)urb->transfer_dma);
- + DWC_PRINTF(" Setup buffer: %p, Setup DMA: %p\n",
- + urb->setup_packet, (void *)urb->setup_dma);
- + DWC_PRINTF(" Interval: %d\n", urb->interval);
- + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
- + int i;
- + for (i = 0; i < urb->number_of_packets; i++) {
- + DWC_PRINTF(" ISO Desc %d:\n", i);
- + DWC_PRINTF(" offset: %d, length %d\n",
- + urb->iso_frame_desc[i].offset,
- + urb->iso_frame_desc[i].length);
- + }
- + }
- +}
- +#endif
- +
- +/** Starts processing a USB transfer request specified by a USB Request Block
- + * (URB). mem_flags indicates the type of memory allocation to use while
- + * processing this URB. */
- +static int dwc_otg_urb_enqueue(struct usb_hcd *hcd,
- +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
- + struct usb_host_endpoint *ep,
- +#endif
- + struct urb *urb, gfp_t mem_flags)
- +{
- + int retval = 0;
- +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
- + struct usb_host_endpoint *ep = urb->ep;
- +#endif
- + dwc_irqflags_t irqflags;
- + void **ref_ep_hcpriv = &ep->hcpriv;
- + dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
- + dwc_otg_hcd_urb_t *dwc_otg_urb;
- + int i;
- + int alloc_bandwidth = 0;
- + uint8_t ep_type = 0;
- + uint32_t flags = 0;
- + void *buf;
- +
- +#ifdef DEBUG
- + if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) {
- + dump_urb_info(urb, "dwc_otg_urb_enqueue");
- + }
- +#endif
- + if ((usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS)
- + || (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) {
- + if (!dwc_otg_hcd_is_bandwidth_allocated
- + (dwc_otg_hcd, ref_ep_hcpriv)) {
- + alloc_bandwidth = 1;
- + }
- + }
- +
- + switch (usb_pipetype(urb->pipe)) {
- + case PIPE_CONTROL:
- + ep_type = USB_ENDPOINT_XFER_CONTROL;
- + break;
- + case PIPE_ISOCHRONOUS:
- + ep_type = USB_ENDPOINT_XFER_ISOC;
- + break;
- + case PIPE_BULK:
- + ep_type = USB_ENDPOINT_XFER_BULK;
- + break;
- + case PIPE_INTERRUPT:
- + ep_type = USB_ENDPOINT_XFER_INT;
- + break;
- + default:
- + DWC_WARN("Wrong EP type - %d\n", usb_pipetype(urb->pipe));
- + }
- +
- + /* # of packets is often 0 - do we really need to call this then? */
- + dwc_otg_urb = dwc_otg_hcd_urb_alloc(dwc_otg_hcd,
- + urb->number_of_packets,
- + mem_flags == GFP_ATOMIC ? 1 : 0);
- +
- + if(dwc_otg_urb == NULL)
- + return -ENOMEM;
- +
- + if (!dwc_otg_urb && urb->number_of_packets)
- + return -ENOMEM;
- +
- + dwc_otg_hcd_urb_set_pipeinfo(dwc_otg_urb, usb_pipedevice(urb->pipe),
- + usb_pipeendpoint(urb->pipe), ep_type,
- + usb_pipein(urb->pipe),
- + usb_maxpacket(urb->dev, urb->pipe));
- +
- + buf = urb->transfer_buffer;
- + if (hcd_uses_dma(hcd) && !buf && urb->transfer_buffer_length) {
- + /*
- + * Calculate virtual address from physical address,
- + * because some class driver may not fill transfer_buffer.
- + * In Buffer DMA mode virual address is used,
- + * when handling non DWORD aligned buffers.
- + */
- + buf = (void *)__bus_to_virt((unsigned long)urb->transfer_dma);
- + dev_warn_once(&urb->dev->dev,
- + "USB transfer_buffer was NULL, will use __bus_to_virt(%pad)=%p\n",
- + &urb->transfer_dma, buf);
- + }
- +
- + if (!buf && urb->transfer_buffer_length) {
- + DWC_FREE(dwc_otg_urb);
- + DWC_ERROR("transfer_buffer is NULL in PIO mode or both "
- + "transfer_buffer and transfer_dma are NULL in DMA mode\n");
- + return -EINVAL;
- + }
- +
- + if (!(urb->transfer_flags & URB_NO_INTERRUPT))
- + flags |= URB_GIVEBACK_ASAP;
- + if (urb->transfer_flags & URB_ZERO_PACKET)
- + flags |= URB_SEND_ZERO_PACKET;
- +
- + dwc_otg_hcd_urb_set_params(dwc_otg_urb, urb, buf,
- + urb->transfer_dma,
- + urb->transfer_buffer_length,
- + urb->setup_packet,
- + urb->setup_dma, flags, urb->interval);
- +
- + for (i = 0; i < urb->number_of_packets; ++i) {
- + dwc_otg_hcd_urb_set_iso_desc_params(dwc_otg_urb, i,
- + urb->
- + iso_frame_desc[i].offset,
- + urb->
- + iso_frame_desc[i].length);
- + }
- +
- + DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &irqflags);
- + urb->hcpriv = dwc_otg_urb;
- +#if USB_URB_EP_LINKING
- + retval = usb_hcd_link_urb_to_ep(hcd, urb);
- + if (0 == retval)
- +#endif
- + {
- + retval = dwc_otg_hcd_urb_enqueue(dwc_otg_hcd, dwc_otg_urb,
- + /*(dwc_otg_qh_t **)*/
- + ref_ep_hcpriv, 1);
- + if (0 == retval) {
- + if (alloc_bandwidth) {
- + allocate_bus_bandwidth(hcd,
- + dwc_otg_hcd_get_ep_bandwidth(
- + dwc_otg_hcd, *ref_ep_hcpriv),
- + urb);
- + }
- + } else {
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG dwc_otg_hcd_urb_enqueue failed rc %d\n", retval);
- +#if USB_URB_EP_LINKING
- + usb_hcd_unlink_urb_from_ep(hcd, urb);
- +#endif
- + DWC_FREE(dwc_otg_urb);
- + urb->hcpriv = NULL;
- + if (retval == -DWC_E_NO_DEVICE)
- + retval = -ENODEV;
- + }
- + }
- +#if USB_URB_EP_LINKING
- + else
- + {
- + DWC_FREE(dwc_otg_urb);
- + urb->hcpriv = NULL;
- + }
- +#endif
- + DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, irqflags);
- + return retval;
- +}
- +
- +/** Aborts/cancels a USB transfer request. Always returns 0 to indicate
- + * success. */
- +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
- +static int dwc_otg_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
- +#else
- +static int dwc_otg_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
- +#endif
- +{
- + dwc_irqflags_t flags;
- + dwc_otg_hcd_t *dwc_otg_hcd;
- + int rc;
- +
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD URB Dequeue\n");
- +
- + dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
- +
- +#ifdef DEBUG
- + if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) {
- + dump_urb_info(urb, "dwc_otg_urb_dequeue");
- + }
- +#endif
- +
- + DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &flags);
- + rc = usb_hcd_check_unlink_urb(hcd, urb, status);
- + if (0 == rc) {
- + if(urb->hcpriv != NULL) {
- + dwc_otg_hcd_urb_dequeue(dwc_otg_hcd,
- + (dwc_otg_hcd_urb_t *)urb->hcpriv);
- +
- + DWC_FREE(urb->hcpriv);
- + urb->hcpriv = NULL;
- + }
- + }
- +
- + if (0 == rc) {
- + /* Higher layer software sets URB status. */
- +#if USB_URB_EP_LINKING
- + usb_hcd_unlink_urb_from_ep(hcd, urb);
- +#endif
- + DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, flags);
- +
- +
- +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
- + usb_hcd_giveback_urb(hcd, urb);
- +#else
- + usb_hcd_giveback_urb(hcd, urb, status);
- +#endif
- + if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) {
- + DWC_PRINTF("Called usb_hcd_giveback_urb() \n");
- + DWC_PRINTF(" 1urb->status = %d\n", urb->status);
- + }
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD URB Dequeue OK\n");
- + } else {
- + DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, flags);
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD URB Dequeue failed - rc %d\n",
- + rc);
- + }
- +
- + return rc;
- +}
- +
- +/* Frees resources in the DWC_otg controller related to a given endpoint. Also
- + * clears state in the HCD related to the endpoint. Any URBs for the endpoint
- + * must already be dequeued. */
- +static void endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
- +{
- + dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
- +
- + DWC_DEBUGPL(DBG_HCD,
- + "DWC OTG HCD EP DISABLE: _bEndpointAddress=0x%02x, "
- + "endpoint=%d\n", ep->desc.bEndpointAddress,
- + dwc_ep_addr_to_endpoint(ep->desc.bEndpointAddress));
- + dwc_otg_hcd_endpoint_disable(dwc_otg_hcd, ep->hcpriv, 250);
- + ep->hcpriv = NULL;
- +}
- +
- +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
- +/* Resets endpoint specific parameter values, in current version used to reset
- + * the data toggle(as a WA). This function can be called from usb_clear_halt routine */
- +static void endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
- +{
- + dwc_irqflags_t flags;
- + dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
- +
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD EP RESET: Endpoint Num=0x%02d\n",
- + ep->desc.bEndpointAddress);
- +
- + DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &flags);
- + if (ep->hcpriv) {
- + dwc_otg_hcd_endpoint_reset(dwc_otg_hcd, ep->hcpriv);
- + }
- + DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, flags);
- +}
- +#endif
- +
- +/** Handles host mode interrupts for the DWC_otg controller. Returns IRQ_NONE if
- + * there was no interrupt to handle. Returns IRQ_HANDLED if there was a valid
- + * interrupt.
- + *
- + * This function is called by the USB core when an interrupt occurs */
- +static irqreturn_t dwc_otg_hcd_irq(struct usb_hcd *hcd)
- +{
- + dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
- + int32_t retval = dwc_otg_hcd_handle_intr(dwc_otg_hcd);
- + if (retval != 0) {
- + S3C2410X_CLEAR_EINTPEND();
- + }
- + return IRQ_RETVAL(retval);
- +}
- +
- +/** Creates Status Change bitmap for the root hub and root port. The bitmap is
- + * returned in buf. Bit 0 is the status change indicator for the root hub. Bit 1
- + * is the status change indicator for the single root port. Returns 1 if either
- + * change indicator is 1, otherwise returns 0. */
- +int hub_status_data(struct usb_hcd *hcd, char *buf)
- +{
- + dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd);
- +
- + buf[0] = 0;
- + buf[0] |= (dwc_otg_hcd_is_status_changed(dwc_otg_hcd, 1)) << 1;
- +
- + return (buf[0] != 0);
- +}
- +
- +/** Handles hub class-specific requests. */
- +int hub_control(struct usb_hcd *hcd,
- + u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength)
- +{
- + int retval;
- +
- + retval = dwc_otg_hcd_hub_control(hcd_to_dwc_otg_hcd(hcd),
- + typeReq, wValue, wIndex, buf, wLength);
- +
- + switch (retval) {
- + case -DWC_E_INVALID:
- + retval = -EINVAL;
- + break;
- + }
- +
- + return retval;
- +}
- +
- +#endif /* DWC_DEVICE_ONLY */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c
- @@ -0,0 +1,974 @@
- +/* ==========================================================================
- + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_queue.c $
- + * $Revision: #44 $
- + * $Date: 2011/10/26 $
- + * $Change: 1873028 $
- + *
- + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
- + * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
- + * otherwise expressly agreed to in writing between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product under
- + * any End User Software License Agreement or Agreement for Licensed Product
- + * with Synopsys or any supplement thereto. You are permitted to use and
- + * redistribute this Software in source and binary forms, with or without
- + * modification, provided that redistributions of source code must retain this
- + * notice. You may not view, use, disclose, copy or distribute this file or
- + * any information contained herein except pursuant to this license grant from
- + * Synopsys. If you do not agree with this notice, including the disclaimer
- + * below, then you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
- + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- + * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
- + * ========================================================================== */
- +#ifndef DWC_DEVICE_ONLY
- +
- +/**
- + * @file
- + *
- + * This file contains the functions to manage Queue Heads and Queue
- + * Transfer Descriptors.
- + */
- +
- +#include "dwc_otg_hcd.h"
- +#include "dwc_otg_regs.h"
- +
- +extern bool microframe_schedule;
- +extern unsigned short int_ep_interval_min;
- +
- +/**
- + * Free each QTD in the QH's QTD-list then free the QH. QH should already be
- + * removed from a list. QTD list should already be empty if called from URB
- + * Dequeue.
- + *
- + * @param hcd HCD instance.
- + * @param qh The QH to free.
- + */
- +void dwc_otg_hcd_qh_free(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
- +{
- + dwc_otg_qtd_t *qtd, *qtd_tmp;
- + dwc_irqflags_t flags;
- + uint32_t buf_size = 0;
- + uint8_t *align_buf_virt = NULL;
- + dwc_dma_t align_buf_dma;
- + struct device *dev = dwc_otg_hcd_to_dev(hcd);
- +
- + /* Free each QTD in the QTD list */
- + DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
- + DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &qh->qtd_list, qtd_list_entry) {
- + DWC_CIRCLEQ_REMOVE(&qh->qtd_list, qtd, qtd_list_entry);
- + dwc_otg_hcd_qtd_free(qtd);
- + }
- +
- + if (hcd->core_if->dma_desc_enable) {
- + dwc_otg_hcd_qh_free_ddma(hcd, qh);
- + } else if (qh->dw_align_buf) {
- + if (qh->ep_type == UE_ISOCHRONOUS) {
- + buf_size = 4096;
- + } else {
- + buf_size = hcd->core_if->core_params->max_transfer_size;
- + }
- + align_buf_virt = qh->dw_align_buf;
- + align_buf_dma = qh->dw_align_buf_dma;
- + }
- +
- + DWC_FREE(qh);
- + DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
- + if (align_buf_virt)
- + DWC_DMA_FREE(dev, buf_size, align_buf_virt, align_buf_dma);
- + return;
- +}
- +
- +#define BitStuffTime(bytecount) ((8 * 7* bytecount) / 6)
- +#define HS_HOST_DELAY 5 /* nanoseconds */
- +#define FS_LS_HOST_DELAY 1000 /* nanoseconds */
- +#define HUB_LS_SETUP 333 /* nanoseconds */
- +#define NS_TO_US(ns) ((ns + 500) / 1000)
- + /* convert & round nanoseconds to microseconds */
- +
- +static uint32_t calc_bus_time(int speed, int is_in, int is_isoc, int bytecount)
- +{
- + unsigned long retval;
- +
- + switch (speed) {
- + case USB_SPEED_HIGH:
- + if (is_isoc) {
- + retval =
- + ((38 * 8 * 2083) +
- + (2083 * (3 + BitStuffTime(bytecount)))) / 1000 +
- + HS_HOST_DELAY;
- + } else {
- + retval =
- + ((55 * 8 * 2083) +
- + (2083 * (3 + BitStuffTime(bytecount)))) / 1000 +
- + HS_HOST_DELAY;
- + }
- + break;
- + case USB_SPEED_FULL:
- + if (is_isoc) {
- + retval =
- + (8354 * (31 + 10 * BitStuffTime(bytecount))) / 1000;
- + if (is_in) {
- + retval = 7268 + FS_LS_HOST_DELAY + retval;
- + } else {
- + retval = 6265 + FS_LS_HOST_DELAY + retval;
- + }
- + } else {
- + retval =
- + (8354 * (31 + 10 * BitStuffTime(bytecount))) / 1000;
- + retval = 9107 + FS_LS_HOST_DELAY + retval;
- + }
- + break;
- + case USB_SPEED_LOW:
- + if (is_in) {
- + retval =
- + (67667 * (31 + 10 * BitStuffTime(bytecount))) /
- + 1000;
- + retval =
- + 64060 + (2 * HUB_LS_SETUP) + FS_LS_HOST_DELAY +
- + retval;
- + } else {
- + retval =
- + (66700 * (31 + 10 * BitStuffTime(bytecount))) /
- + 1000;
- + retval =
- + 64107 + (2 * HUB_LS_SETUP) + FS_LS_HOST_DELAY +
- + retval;
- + }
- + break;
- + default:
- + DWC_WARN("Unknown device speed\n");
- + retval = -1;
- + }
- +
- + return NS_TO_US(retval);
- +}
- +
- +/**
- + * Initializes a QH structure.
- + *
- + * @param hcd The HCD state structure for the DWC OTG controller.
- + * @param qh The QH to init.
- + * @param urb Holds the information about the device/endpoint that we need
- + * to initialize the QH.
- + */
- +#define SCHEDULE_SLOP 10
- +void qh_init(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, dwc_otg_hcd_urb_t * urb)
- +{
- + char *speed, *type;
- + int dev_speed;
- + uint32_t hub_addr, hub_port;
- + hprt0_data_t hprt;
- +
- + dwc_memset(qh, 0, sizeof(dwc_otg_qh_t));
- + hprt.d32 = DWC_READ_REG32(hcd->core_if->host_if->hprt0);
- +
- + /* Initialize QH */
- + qh->ep_type = dwc_otg_hcd_get_pipe_type(&urb->pipe_info);
- + qh->ep_is_in = dwc_otg_hcd_is_pipe_in(&urb->pipe_info) ? 1 : 0;
- +
- + qh->data_toggle = DWC_OTG_HC_PID_DATA0;
- + qh->maxp = dwc_otg_hcd_get_mps(&urb->pipe_info);
- + DWC_CIRCLEQ_INIT(&qh->qtd_list);
- + DWC_LIST_INIT(&qh->qh_list_entry);
- + qh->channel = NULL;
- +
- + /* FS/LS Enpoint on HS Hub
- + * NOT virtual root hub */
- + dev_speed = hcd->fops->speed(hcd, urb->priv);
- +
- + hcd->fops->hub_info(hcd, urb->priv, &hub_addr, &hub_port);
- + qh->do_split = 0;
- + if (microframe_schedule)
- + qh->speed = dev_speed;
- +
- + qh->nak_frame = 0xffff;
- +
- + if (hprt.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED &&
- + dev_speed != USB_SPEED_HIGH) {
- + DWC_DEBUGPL(DBG_HCD,
- + "QH init: EP %d: TT found at hub addr %d, for port %d\n",
- + dwc_otg_hcd_get_ep_num(&urb->pipe_info), hub_addr,
- + hub_port);
- + qh->do_split = 1;
- + qh->skip_count = 0;
- + }
- +
- + if (qh->ep_type == UE_INTERRUPT || qh->ep_type == UE_ISOCHRONOUS) {
- + /* Compute scheduling parameters once and save them. */
- +
- + /** @todo Account for split transfers in the bus time. */
- + int bytecount =
- + dwc_hb_mult(qh->maxp) * dwc_max_packet(qh->maxp);
- +
- + qh->usecs =
- + calc_bus_time((qh->do_split ? USB_SPEED_HIGH : dev_speed),
- + qh->ep_is_in, (qh->ep_type == UE_ISOCHRONOUS),
- + bytecount);
- + /* Start in a slightly future (micro)frame. */
- + qh->sched_frame = dwc_frame_num_inc(hcd->frame_number,
- + SCHEDULE_SLOP);
- + qh->interval = urb->interval;
- +
- + if (hprt.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED) {
- + if (dev_speed == USB_SPEED_LOW ||
- + dev_speed == USB_SPEED_FULL) {
- + qh->interval *= 8;
- + qh->sched_frame |= 0x7;
- + qh->start_split_frame = qh->sched_frame;
- + } else if (int_ep_interval_min >= 2 &&
- + qh->interval < int_ep_interval_min &&
- + qh->ep_type == UE_INTERRUPT) {
- + qh->interval = int_ep_interval_min;
- + }
- + }
- + }
- +
- + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD QH Initialized\n");
- + DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - qh = %p\n", qh);
- + DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Device Address = %d\n",
- + dwc_otg_hcd_get_dev_addr(&urb->pipe_info));
- + DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Endpoint %d, %s\n",
- + dwc_otg_hcd_get_ep_num(&urb->pipe_info),
- + dwc_otg_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT");
- + switch (dev_speed) {
- + case USB_SPEED_LOW:
- + qh->dev_speed = DWC_OTG_EP_SPEED_LOW;
- + speed = "low";
- + break;
- + case USB_SPEED_FULL:
- + qh->dev_speed = DWC_OTG_EP_SPEED_FULL;
- + speed = "full";
- + break;
- + case USB_SPEED_HIGH:
- + qh->dev_speed = DWC_OTG_EP_SPEED_HIGH;
- + speed = "high";
- + break;
- + default:
- + speed = "?";
- + break;
- + }
- + DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Speed = %s\n", speed);
- +
- + switch (qh->ep_type) {
- + case UE_ISOCHRONOUS:
- + type = "isochronous";
- + break;
- + case UE_INTERRUPT:
- + type = "interrupt";
- + break;
- + case UE_CONTROL:
- + type = "control";
- + break;
- + case UE_BULK:
- + type = "bulk";
- + break;
- + default:
- + type = "?";
- + break;
- + }
- +
- + DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Type = %s\n", type);
- +
- +#ifdef DEBUG
- + if (qh->ep_type == UE_INTERRUPT) {
- + DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - usecs = %d\n",
- + qh->usecs);
- + DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - interval = %d\n",
- + qh->interval);
- + }
- +#endif
- +
- +}
- +
- +/**
- + * This function allocates and initializes a QH.
- + *
- + * @param hcd The HCD state structure for the DWC OTG controller.
- + * @param urb Holds the information about the device/endpoint that we need
- + * to initialize the QH.
- + * @param atomic_alloc Flag to do atomic allocation if needed
- + *
- + * @return Returns pointer to the newly allocated QH, or NULL on error. */
- +dwc_otg_qh_t *dwc_otg_hcd_qh_create(dwc_otg_hcd_t * hcd,
- + dwc_otg_hcd_urb_t * urb, int atomic_alloc)
- +{
- + dwc_otg_qh_t *qh;
- +
- + /* Allocate memory */
- + /** @todo add memflags argument */
- + qh = dwc_otg_hcd_qh_alloc(atomic_alloc);
- + if (qh == NULL) {
- + DWC_ERROR("qh allocation failed");
- + return NULL;
- + }
- +
- + qh_init(hcd, qh, urb);
- +
- + if (hcd->core_if->dma_desc_enable
- + && (dwc_otg_hcd_qh_init_ddma(hcd, qh) < 0)) {
- + dwc_otg_hcd_qh_free(hcd, qh);
- + return NULL;
- + }
- +
- + return qh;
- +}
- +
- +/* microframe_schedule=0 start */
- +
- +/**
- + * Checks that a channel is available for a periodic transfer.
- + *
- + * @return 0 if successful, negative error code otherise.
- + */
- +static int periodic_channel_available(dwc_otg_hcd_t * hcd)
- +{
- + /*
- + * Currently assuming that there is a dedicated host channnel for each
- + * periodic transaction plus at least one host channel for
- + * non-periodic transactions.
- + */
- + int status;
- + int num_channels;
- +
- + num_channels = hcd->core_if->core_params->host_channels;
- + if ((hcd->periodic_channels + hcd->non_periodic_channels < num_channels)
- + && (hcd->periodic_channels < num_channels - 1)) {
- + status = 0;
- + } else {
- + DWC_INFO("%s: Total channels: %d, Periodic: %d, Non-periodic: %d\n",
- + __func__, num_channels, hcd->periodic_channels, hcd->non_periodic_channels); //NOTICE
- + status = -DWC_E_NO_SPACE;
- + }
- +
- + return status;
- +}
- +
- +/**
- + * Checks that there is sufficient bandwidth for the specified QH in the
- + * periodic schedule. For simplicity, this calculation assumes that all the
- + * transfers in the periodic schedule may occur in the same (micro)frame.
- + *
- + * @param hcd The HCD state structure for the DWC OTG controller.
- + * @param qh QH containing periodic bandwidth required.
- + *
- + * @return 0 if successful, negative error code otherwise.
- + */
- +static int check_periodic_bandwidth(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
- +{
- + int status;
- + int16_t max_claimed_usecs;
- +
- + status = 0;
- +
- + if ((qh->dev_speed == DWC_OTG_EP_SPEED_HIGH) || qh->do_split) {
- + /*
- + * High speed mode.
- + * Max periodic usecs is 80% x 125 usec = 100 usec.
- + */
- +
- + max_claimed_usecs = 100 - qh->usecs;
- + } else {
- + /*
- + * Full speed mode.
- + * Max periodic usecs is 90% x 1000 usec = 900 usec.
- + */
- + max_claimed_usecs = 900 - qh->usecs;
- + }
- +
- + if (hcd->periodic_usecs > max_claimed_usecs) {
- + DWC_INFO("%s: already claimed usecs %d, required usecs %d\n", __func__, hcd->periodic_usecs, qh->usecs); //NOTICE
- + status = -DWC_E_NO_SPACE;
- + }
- +
- + return status;
- +}
- +
- +/* microframe_schedule=0 end */
- +
- +/**
- + * Microframe scheduler
- + * track the total use in hcd->frame_usecs
- + * keep each qh use in qh->frame_usecs
- + * when surrendering the qh then donate the time back
- + */
- +const unsigned short max_uframe_usecs[]={ 100, 100, 100, 100, 100, 100, 30, 0 };
- +
- +/*
- + * called from dwc_otg_hcd.c:dwc_otg_hcd_init
- + */
- +void init_hcd_usecs(dwc_otg_hcd_t *_hcd)
- +{
- + int i;
- + if (_hcd->flags.b.port_speed == DWC_HPRT0_PRTSPD_FULL_SPEED) {
- + _hcd->frame_usecs[0] = 900;
- + for (i = 1; i < 8; i++)
- + _hcd->frame_usecs[i] = 0;
- + } else {
- + for (i = 0; i < 8; i++)
- + _hcd->frame_usecs[i] = max_uframe_usecs[i];
- + }
- +}
- +
- +static int find_single_uframe(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh)
- +{
- + int i;
- + unsigned short utime;
- + int t_left;
- + int ret;
- + int done;
- +
- + ret = -1;
- + utime = _qh->usecs;
- + t_left = utime;
- + i = 0;
- + done = 0;
- + while (done == 0) {
- + /* At the start _hcd->frame_usecs[i] = max_uframe_usecs[i]; */
- + if (utime <= _hcd->frame_usecs[i]) {
- + _hcd->frame_usecs[i] -= utime;
- + _qh->frame_usecs[i] += utime;
- + t_left -= utime;
- + ret = i;
- + done = 1;
- + return ret;
- + } else {
- + i++;
- + if (i == 8) {
- + done = 1;
- + ret = -1;
- + }
- + }
- + }
- + return ret;
- + }
- +
- +/*
- + * use this for FS apps that can span multiple uframes
- + */
- +static int find_multi_uframe(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh)
- +{
- + int i;
- + int j;
- + unsigned short utime;
- + int t_left;
- + int ret;
- + int done;
- + unsigned short xtime;
- +
- + ret = -1;
- + utime = _qh->usecs;
- + t_left = utime;
- + i = 0;
- + done = 0;
- +loop:
- + while (done == 0) {
- + if(_hcd->frame_usecs[i] <= 0) {
- + i++;
- + if (i == 8) {
- + done = 1;
- + ret = -1;
- + }
- + goto loop;
- + }
- +
- + /*
- + * we need n consecutive slots
- + * so use j as a start slot j plus j+1 must be enough time (for now)
- + */
- + xtime= _hcd->frame_usecs[i];
- + for (j = i+1 ; j < 8 ; j++ ) {
- + /*
- + * if we add this frame remaining time to xtime we may
- + * be OK, if not we need to test j for a complete frame
- + */
- + if ((xtime+_hcd->frame_usecs[j]) < utime) {
- + if (_hcd->frame_usecs[j] < max_uframe_usecs[j]) {
- + j = 8;
- + ret = -1;
- + continue;
- + }
- + }
- + if (xtime >= utime) {
- + ret = i;
- + j = 8; /* stop loop with a good value ret */
- + continue;
- + }
- + /* add the frame time to x time */
- + xtime += _hcd->frame_usecs[j];
- + /* we must have a fully available next frame or break */
- + if ((xtime < utime)
- + && (_hcd->frame_usecs[j] == max_uframe_usecs[j])) {
- + ret = -1;
- + j = 8; /* stop loop with a bad value ret */
- + continue;
- + }
- + }
- + if (ret >= 0) {
- + t_left = utime;
- + for (j = i; (t_left>0) && (j < 8); j++ ) {
- + t_left -= _hcd->frame_usecs[j];
- + if ( t_left <= 0 ) {
- + _qh->frame_usecs[j] += _hcd->frame_usecs[j] + t_left;
- + _hcd->frame_usecs[j]= -t_left;
- + ret = i;
- + done = 1;
- + } else {
- + _qh->frame_usecs[j] += _hcd->frame_usecs[j];
- + _hcd->frame_usecs[j] = 0;
- + }
- + }
- + } else {
- + i++;
- + if (i == 8) {
- + done = 1;
- + ret = -1;
- + }
- + }
- + }
- + return ret;
- +}
- +
- +static int find_uframe(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh)
- +{
- + int ret;
- + ret = -1;
- +
- + if (_qh->speed == USB_SPEED_HIGH ||
- + _hcd->flags.b.port_speed == DWC_HPRT0_PRTSPD_FULL_SPEED) {
- + /* if this is a hs transaction we need a full frame - or account for FS usecs */
- + ret = find_single_uframe(_hcd, _qh);
- + } else {
- + /* if this is a fs transaction we may need a sequence of frames */
- + ret = find_multi_uframe(_hcd, _qh);
- + }
- + return ret;
- +}
- +
- +/**
- + * Checks that the max transfer size allowed in a host channel is large enough
- + * to handle the maximum data transfer in a single (micro)frame for a periodic
- + * transfer.
- + *
- + * @param hcd The HCD state structure for the DWC OTG controller.
- + * @param qh QH for a periodic endpoint.
- + *
- + * @return 0 if successful, negative error code otherwise.
- + */
- +static int check_max_xfer_size(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
- +{
- + int status;
- + uint32_t max_xfer_size;
- + uint32_t max_channel_xfer_size;
- +
- + status = 0;
- +
- + max_xfer_size = dwc_max_packet(qh->maxp) * dwc_hb_mult(qh->maxp);
- + max_channel_xfer_size = hcd->core_if->core_params->max_transfer_size;
- +
- + if (max_xfer_size > max_channel_xfer_size) {
- + DWC_INFO("%s: Periodic xfer length %d > " "max xfer length for channel %d\n",
- + __func__, max_xfer_size, max_channel_xfer_size); //NOTICE
- + status = -DWC_E_NO_SPACE;
- + }
- +
- + return status;
- +}
- +
- +
- +
- +/**
- + * Schedules an interrupt or isochronous transfer in the periodic schedule.
- + *
- + * @param hcd The HCD state structure for the DWC OTG controller.
- + * @param qh QH for the periodic transfer. The QH should already contain the
- + * scheduling information.
- + *
- + * @return 0 if successful, negative error code otherwise.
- + */
- +static int schedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
- +{
- + int status = 0;
- +
- + if (microframe_schedule) {
- + int frame;
- + status = find_uframe(hcd, qh);
- + frame = -1;
- + if (status == 0) {
- + frame = 7;
- + } else {
- + if (status > 0 )
- + frame = status-1;
- + }
- +
- + /* Set the new frame up */
- + if (frame > -1) {
- + qh->sched_frame &= ~0x7;
- + qh->sched_frame |= (frame & 7);
- + }
- +
- + if (status != -1)
- + status = 0;
- + } else {
- + status = periodic_channel_available(hcd);
- + if (status) {
- + DWC_INFO("%s: No host channel available for periodic " "transfer.\n", __func__); //NOTICE
- + return status;
- + }
- +
- + status = check_periodic_bandwidth(hcd, qh);
- + }
- + if (status) {
- + DWC_INFO("%s: Insufficient periodic bandwidth for "
- + "periodic transfer.\n", __func__);
- + return -DWC_E_NO_SPACE;
- + }
- + status = check_max_xfer_size(hcd, qh);
- + if (status) {
- + DWC_INFO("%s: Channel max transfer size too small "
- + "for periodic transfer.\n", __func__);
- + return status;
- + }
- +
- + if (hcd->core_if->dma_desc_enable) {
- + /* Don't rely on SOF and start in ready schedule */
- + DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_ready, &qh->qh_list_entry);
- + }
- + else {
- + if(fiq_enable && (DWC_LIST_EMPTY(&hcd->periodic_sched_inactive) || dwc_frame_num_le(qh->sched_frame, hcd->fiq_state->next_sched_frame)))
- + {
- + hcd->fiq_state->next_sched_frame = qh->sched_frame;
- +
- + }
- + /* Always start in the inactive schedule. */
- + DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_inactive, &qh->qh_list_entry);
- + }
- +
- + if (!microframe_schedule) {
- + /* Reserve the periodic channel. */
- + hcd->periodic_channels++;
- + }
- +
- + /* Update claimed usecs per (micro)frame. */
- + hcd->periodic_usecs += qh->usecs;
- +
- + return status;
- +}
- +
- +
- +/**
- + * This function adds a QH to either the non periodic or periodic schedule if
- + * it is not already in the schedule. If the QH is already in the schedule, no
- + * action is taken.
- + *
- + * @return 0 if successful, negative error code otherwise.
- + */
- +int dwc_otg_hcd_qh_add(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
- +{
- + int status = 0;
- + gintmsk_data_t intr_mask = {.d32 = 0 };
- +
- + if (!DWC_LIST_EMPTY(&qh->qh_list_entry)) {
- + /* QH already in a schedule. */
- + return status;
- + }
- +
- + /* Add the new QH to the appropriate schedule */
- + if (dwc_qh_is_non_per(qh)) {
- + /* Always start in the inactive schedule. */
- + DWC_LIST_INSERT_TAIL(&hcd->non_periodic_sched_inactive,
- + &qh->qh_list_entry);
- + //hcd->fiq_state->kick_np_queues = 1;
- + } else {
- + /* If the QH wasn't in a schedule, then sched_frame is stale. */
- + qh->sched_frame = dwc_frame_num_inc(dwc_otg_hcd_get_frame_number(hcd),
- + max_t(uint32_t, qh->interval, SCHEDULE_SLOP));
- + status = schedule_periodic(hcd, qh);
- + qh->start_split_frame = qh->sched_frame;
- + if ( !hcd->periodic_qh_count ) {
- + intr_mask.b.sofintr = 1;
- + if (fiq_enable) {
- + local_fiq_disable();
- + fiq_fsm_spin_lock(&hcd->fiq_state->lock);
- + DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, intr_mask.d32);
- + fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
- + local_fiq_enable();
- + } else {
- + DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, intr_mask.d32);
- + }
- + }
- + hcd->periodic_qh_count++;
- + }
- +
- + return status;
- +}
- +
- +/**
- + * Removes an interrupt or isochronous transfer from the periodic schedule.
- + *
- + * @param hcd The HCD state structure for the DWC OTG controller.
- + * @param qh QH for the periodic transfer.
- + */
- +static void deschedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
- +{
- + int i;
- + DWC_LIST_REMOVE_INIT(&qh->qh_list_entry);
- +
- + /* Update claimed usecs per (micro)frame. */
- + hcd->periodic_usecs -= qh->usecs;
- +
- + if (!microframe_schedule) {
- + /* Release the periodic channel reservation. */
- + hcd->periodic_channels--;
- + } else {
- + for (i = 0; i < 8; i++) {
- + hcd->frame_usecs[i] += qh->frame_usecs[i];
- + qh->frame_usecs[i] = 0;
- + }
- + }
- +}
- +
- +/**
- + * Removes a QH from either the non-periodic or periodic schedule. Memory is
- + * not freed.
- + *
- + * @param hcd The HCD state structure.
- + * @param qh QH to remove from schedule. */
- +void dwc_otg_hcd_qh_remove(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
- +{
- + gintmsk_data_t intr_mask = {.d32 = 0 };
- +
- + if (DWC_LIST_EMPTY(&qh->qh_list_entry)) {
- + /* QH is not in a schedule. */
- + return;
- + }
- +
- + if (dwc_qh_is_non_per(qh)) {
- + if (hcd->non_periodic_qh_ptr == &qh->qh_list_entry) {
- + hcd->non_periodic_qh_ptr =
- + hcd->non_periodic_qh_ptr->next;
- + }
- + DWC_LIST_REMOVE_INIT(&qh->qh_list_entry);
- + //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 && !fiq_fsm_enable ) {
- + intr_mask.b.sofintr = 1;
- + if (fiq_enable) {
- + local_fiq_disable();
- + fiq_fsm_spin_lock(&hcd->fiq_state->lock);
- + DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, 0);
- + fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
- + local_fiq_enable();
- + } else {
- + DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, 0);
- + }
- + }
- + }
- +}
- +
- +/**
- + * Deactivates a QH. For non-periodic QHs, removes the QH from the active
- + * non-periodic schedule. The QH is added to the inactive non-periodic
- + * schedule if any QTDs are still attached to the QH.
- + *
- + * For periodic QHs, the QH is removed from the periodic queued schedule. If
- + * there are any QTDs still attached to the QH, the QH is added to either the
- + * periodic inactive schedule or the periodic ready schedule and its next
- + * scheduled frame is calculated. The QH is placed in the ready schedule if
- + * the scheduled frame has been reached already. Otherwise it's placed in the
- + * inactive schedule. If there are no QTDs attached to the QH, the QH is
- + * completely removed from the periodic schedule.
- + */
- +void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh,
- + int sched_next_periodic_split)
- +{
- + if (dwc_qh_is_non_per(qh)) {
- + 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 {
- + if(nak_holdoff && qh->do_split) {
- + qh->nak_frame = 0xFFFF;
- + }
- + }
- + } else {
- + uint16_t frame_number = dwc_otg_hcd_get_frame_number(hcd);
- +
- + if (qh->do_split) {
- + /* Schedule the next continuing periodic split transfer */
- + if (sched_next_periodic_split) {
- +
- + qh->sched_frame = frame_number;
- +
- + if (dwc_frame_num_le(frame_number,
- + dwc_frame_num_inc
- + (qh->start_split_frame,
- + 1))) {
- + /*
- + * Allow one frame to elapse after start
- + * split microframe before scheduling
- + * complete split, but DONT if we are
- + * doing the next start split in the
- + * same frame for an ISOC out.
- + */
- + if ((qh->ep_type != UE_ISOCHRONOUS) ||
- + (qh->ep_is_in != 0)) {
- + qh->sched_frame =
- + dwc_frame_num_inc(qh->sched_frame, 1);
- + }
- + }
- + } else {
- + qh->sched_frame =
- + dwc_frame_num_inc(qh->start_split_frame,
- + qh->interval);
- + if (dwc_frame_num_le
- + (qh->sched_frame, frame_number)) {
- + qh->sched_frame = frame_number;
- + }
- + qh->sched_frame |= 0x7;
- + qh->start_split_frame = qh->sched_frame;
- + }
- + } else {
- + qh->sched_frame =
- + dwc_frame_num_inc(qh->sched_frame, qh->interval);
- + if (dwc_frame_num_le(qh->sched_frame, frame_number)) {
- + qh->sched_frame = frame_number;
- + }
- + }
- +
- + if (DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) {
- + dwc_otg_hcd_qh_remove(hcd, qh);
- + } else {
- + /*
- + * Remove from periodic_sched_queued and move to
- + * appropriate queue.
- + */
- + if ((microframe_schedule && dwc_frame_num_le(qh->sched_frame, frame_number)) ||
- + (!microframe_schedule && qh->sched_frame == frame_number)) {
- + DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready,
- + &qh->qh_list_entry);
- + } else {
- + if(fiq_enable && !dwc_frame_num_le(hcd->fiq_state->next_sched_frame, qh->sched_frame))
- + {
- + hcd->fiq_state->next_sched_frame = qh->sched_frame;
- + }
- +
- + DWC_LIST_MOVE_HEAD
- + (&hcd->periodic_sched_inactive,
- + &qh->qh_list_entry);
- + }
- + }
- + }
- +}
- +
- +/**
- + * This function allocates and initializes a QTD.
- + *
- + * @param urb The URB to create a QTD from. Each URB-QTD pair will end up
- + * pointing to each other so each pair should have a unique correlation.
- + * @param atomic_alloc Flag to do atomic alloc if needed
- + *
- + * @return Returns pointer to the newly allocated QTD, or NULL on error. */
- +dwc_otg_qtd_t *dwc_otg_hcd_qtd_create(dwc_otg_hcd_urb_t * urb, int atomic_alloc)
- +{
- + dwc_otg_qtd_t *qtd;
- +
- + qtd = dwc_otg_hcd_qtd_alloc(atomic_alloc);
- + if (qtd == NULL) {
- + return NULL;
- + }
- +
- + dwc_otg_hcd_qtd_init(qtd, urb);
- + return qtd;
- +}
- +
- +/**
- + * Initializes a QTD structure.
- + *
- + * @param qtd The QTD to initialize.
- + * @param urb The URB to use for initialization. */
- +void dwc_otg_hcd_qtd_init(dwc_otg_qtd_t * qtd, dwc_otg_hcd_urb_t * urb)
- +{
- + dwc_memset(qtd, 0, sizeof(dwc_otg_qtd_t));
- + qtd->urb = urb;
- + if (dwc_otg_hcd_get_pipe_type(&urb->pipe_info) == UE_CONTROL) {
- + /*
- + * The only time the QTD data toggle is used is on the data
- + * phase of control transfers. This phase always starts with
- + * DATA1.
- + */
- + qtd->data_toggle = DWC_OTG_HC_PID_DATA1;
- + qtd->control_phase = DWC_OTG_CONTROL_SETUP;
- + }
- +
- + /* start split */
- + qtd->complete_split = 0;
- + qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_ALL;
- + qtd->isoc_split_offset = 0;
- + qtd->in_process = 0;
- +
- + /* Store the qtd ptr in the urb to reference what QTD. */
- + urb->qtd = qtd;
- + return;
- +}
- +
- +/**
- + * This function adds a QTD to the QTD-list of a QH. It will find the correct
- + * QH to place the QTD into. If it does not find a QH, then it will create a
- + * new QH. If the QH to which the QTD is added is not currently scheduled, it
- + * is placed into the proper schedule based on its EP type.
- + * HCD lock must be held and interrupts must be disabled on entry
- + *
- + * @param[in] qtd The QTD to add
- + * @param[in] hcd The DWC HCD structure
- + * @param[out] qh out parameter to return queue head
- + * @param atomic_alloc Flag to do atomic alloc if needed
- + *
- + * @return 0 if successful, negative error code otherwise.
- + */
- +int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t * qtd,
- + dwc_otg_hcd_t * hcd, dwc_otg_qh_t ** qh, int atomic_alloc)
- +{
- + int retval = 0;
- + dwc_otg_hcd_urb_t *urb = qtd->urb;
- +
- + /*
- + * Get the QH which holds the QTD-list to insert to. Create QH if it
- + * doesn't exist.
- + */
- + if (*qh == NULL) {
- + *qh = dwc_otg_hcd_qh_create(hcd, urb, atomic_alloc);
- + 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);
- + if (retval == 0) {
- + DWC_CIRCLEQ_INSERT_TAIL(&((*qh)->qtd_list), qtd,
- + qtd_list_entry);
- + qtd->qh = *qh;
- + }
- +done:
- +
- + return retval;
- +}
- +
- +#endif /* DWC_DEVICE_ONLY */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_os_dep.h
- @@ -0,0 +1,200 @@
- +#ifndef _DWC_OS_DEP_H_
- +#define _DWC_OS_DEP_H_
- +
- +/**
- + * @file
- + *
- + * This file contains OS dependent structures.
- + *
- + */
- +
- +#include <linux/kernel.h>
- +#include <linux/module.h>
- +#include <linux/moduleparam.h>
- +#include <linux/init.h>
- +#include <linux/device.h>
- +#include <linux/errno.h>
- +#include <linux/types.h>
- +#include <linux/slab.h>
- +#include <linux/list.h>
- +#include <linux/interrupt.h>
- +#include <linux/ctype.h>
- +#include <linux/string.h>
- +#include <linux/dma-mapping.h>
- +#include <linux/jiffies.h>
- +#include <linux/delay.h>
- +#include <linux/timer.h>
- +#include <linux/workqueue.h>
- +#include <linux/stat.h>
- +#include <linux/pci.h>
- +#include <linux/compiler.h>
- +
- +#include <linux/version.h>
- +
- +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
- +# include <linux/irq.h>
- +#endif
- +
- +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
- +# include <linux/usb/ch9.h>
- +#else
- +# include <linux/usb_ch9.h>
- +#endif
- +
- +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
- +# include <linux/usb/gadget.h>
- +#else
- +# include <linux/usb_gadget.h>
- +#endif
- +
- +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
- +# include <asm/irq.h>
- +#endif
- +
- +#ifdef PCI_INTERFACE
- +# include <asm/io.h>
- +#endif
- +
- +#ifdef LM_INTERFACE
- +# include <asm/unaligned.h>
- +# include <asm/sizes.h>
- +# include <asm/param.h>
- +# include <asm/io.h>
- +# if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30))
- +# include <asm/arch/hardware.h>
- +# include <asm/arch/lm.h>
- +# include <asm/arch/irqs.h>
- +# include <asm/arch/regs-irq.h>
- +# else
- +/* in 2.6.31, at least, we seem to have lost the generic LM infrastructure -
- + here we assume that the machine architecture provides definitions
- + in its own header
- +*/
- +# include <mach/lm.h>
- +# include <mach/hardware.h>
- +# endif
- +#endif
- +
- +#ifdef PLATFORM_INTERFACE
- +#include <linux/platform_device.h>
- +#ifdef CONFIG_ARM
- +#include <asm/mach/map.h>
- +#endif
- +#endif
- +
- +/** The OS page size */
- +#define DWC_OS_PAGE_SIZE PAGE_SIZE
- +
- +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
- +typedef int gfp_t;
- +#endif
- +
- +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
- +# define IRQF_SHARED SA_SHIRQ
- +#endif
- +
- +typedef struct os_dependent {
- + /** Base address returned from ioremap() */
- + void *base;
- +
- + /** Register offset for Diagnostic API */
- + uint32_t reg_offset;
- +
- + /** Base address for MPHI peripheral */
- + void *mphi_base;
- +
- + /** mphi_base actually points to the SWIRQ block */
- + bool use_swirq;
- +
- + /** IRQ number (<0 if not valid) */
- + int irq_num;
- +
- + /** FIQ number (<0 if not valid) */
- + int fiq_num;
- +
- +#ifdef LM_INTERFACE
- + struct lm_device *lmdev;
- +#elif defined(PCI_INTERFACE)
- + struct pci_dev *pcidev;
- +
- + /** Start address of a PCI region */
- + resource_size_t rsrc_start;
- +
- + /** Length address of a PCI region */
- + resource_size_t rsrc_len;
- +#elif defined(PLATFORM_INTERFACE)
- + struct platform_device *platformdev;
- +#endif
- +
- +} os_dependent_t;
- +
- +#ifdef __cplusplus
- +}
- +#endif
- +
- +
- +
- +/* Type for the our device on the chosen bus */
- +#if defined(LM_INTERFACE)
- +typedef struct lm_device dwc_bus_dev_t;
- +#elif defined(PCI_INTERFACE)
- +typedef struct pci_dev dwc_bus_dev_t;
- +#elif defined(PLATFORM_INTERFACE)
- +typedef struct platform_device dwc_bus_dev_t;
- +#endif
- +
- +/* Helper macro to retrieve drvdata from the device on the chosen bus */
- +#if defined(LM_INTERFACE)
- +#define DWC_OTG_BUSDRVDATA(_dev) lm_get_drvdata(_dev)
- +#elif defined(PCI_INTERFACE)
- +#define DWC_OTG_BUSDRVDATA(_dev) pci_get_drvdata(_dev)
- +#elif defined(PLATFORM_INTERFACE)
- +#define DWC_OTG_BUSDRVDATA(_dev) platform_get_drvdata(_dev)
- +#endif
- +
- +/**
- + * Helper macro returning the otg_device structure of a given struct device
- + *
- + * c.f. static dwc_otg_device_t *dwc_otg_drvdev(struct device *_dev)
- + */
- +#ifdef LM_INTERFACE
- +#define DWC_OTG_GETDRVDEV(_var, _dev) do { \
- + struct lm_device *lm_dev = \
- + container_of(_dev, struct lm_device, dev); \
- + _var = lm_get_drvdata(lm_dev); \
- + } while (0)
- +
- +#elif defined(PCI_INTERFACE)
- +#define DWC_OTG_GETDRVDEV(_var, _dev) do { \
- + _var = dev_get_drvdata(_dev); \
- + } while (0)
- +
- +#elif defined(PLATFORM_INTERFACE)
- +#define DWC_OTG_GETDRVDEV(_var, _dev) do { \
- + struct platform_device *platform_dev = \
- + container_of(_dev, struct platform_device, dev); \
- + _var = platform_get_drvdata(platform_dev); \
- + } while (0)
- +#endif
- +
- +
- +/**
- + * Helper macro returning the struct dev of the given struct os_dependent
- + *
- + * c.f. static struct device *dwc_otg_getdev(struct os_dependent *osdep)
- + */
- +#ifdef LM_INTERFACE
- +#define DWC_OTG_OS_GETDEV(_osdep) \
- + ((_osdep).lmdev == NULL? NULL: &(_osdep).lmdev->dev)
- +#elif defined(PCI_INTERFACE)
- +#define DWC_OTG_OS_GETDEV(_osdep) \
- + ((_osdep).pci_dev == NULL? NULL: &(_osdep).pci_dev->dev)
- +#elif defined(PLATFORM_INTERFACE)
- +#define DWC_OTG_OS_GETDEV(_osdep) \
- + ((_osdep).platformdev == NULL? NULL: &(_osdep).platformdev->dev)
- +#endif
- +
- +
- +
- +
- +#endif /* _DWC_OS_DEP_H_ */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_pcd.c
- @@ -0,0 +1,2725 @@
- +/* ==========================================================================
- + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd.c $
- + * $Revision: #101 $
- + * $Date: 2012/08/10 $
- + * $Change: 2047372 $
- + *
- + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
- + * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
- + * otherwise expressly agreed to in writing between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product under
- + * any End User Software License Agreement or Agreement for Licensed Product
- + * with Synopsys or any supplement thereto. You are permitted to use and
- + * redistribute this Software in source and binary forms, with or without
- + * modification, provided that redistributions of source code must retain this
- + * notice. You may not view, use, disclose, copy or distribute this file or
- + * any information contained herein except pursuant to this license grant from
- + * Synopsys. If you do not agree with this notice, including the disclaimer
- + * below, then you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
- + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- + * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
- + * ========================================================================== */
- +#ifndef DWC_HOST_ONLY
- +
- +/** @file
- + * This file implements PCD Core. All code in this file is portable and doesn't
- + * use any OS specific functions.
- + * PCD Core provides Interface, defined in <code><dwc_otg_pcd_if.h></code>
- + * header file, which can be used to implement OS specific PCD interface.
- + *
- + * An important function of the PCD is managing interrupts generated
- + * by the DWC_otg controller. The implementation of the DWC_otg device
- + * mode interrupt service routines is in dwc_otg_pcd_intr.c.
- + *
- + * @todo Add Device Mode test modes (Test J mode, Test K mode, etc).
- + * @todo Does it work when the request size is greater than DEPTSIZ
- + * transfer size
- + *
- + */
- +
- +#include "dwc_otg_pcd.h"
- +
- +#ifdef DWC_UTE_CFI
- +#include "dwc_otg_cfi.h"
- +
- +extern int init_cfi(cfiobject_t * cfiobj);
- +#endif
- +
- +/**
- + * Choose endpoint from ep arrays using usb_ep structure.
- + */
- +static dwc_otg_pcd_ep_t *get_ep_from_handle(dwc_otg_pcd_t * pcd, void *handle)
- +{
- + int i;
- + if (pcd->ep0.priv == handle) {
- + return &pcd->ep0;
- + }
- + for (i = 0; i < MAX_EPS_CHANNELS - 1; i++) {
- + if (pcd->in_ep[i].priv == handle)
- + return &pcd->in_ep[i];
- + if (pcd->out_ep[i].priv == handle)
- + return &pcd->out_ep[i];
- + }
- +
- + return NULL;
- +}
- +
- +/**
- + * This function completes a request. It call's the request call back.
- + */
- +void dwc_otg_request_done(dwc_otg_pcd_ep_t * ep, dwc_otg_pcd_request_t * req,
- + int32_t status)
- +{
- + unsigned stopped = ep->stopped;
- +
- + DWC_DEBUGPL(DBG_PCDV, "%s(ep %p req %p)\n", __func__, ep, req);
- + DWC_CIRCLEQ_REMOVE_INIT(&ep->queue, req, queue_entry);
- +
- + /* don't modify queue heads during completion callback */
- + ep->stopped = 1;
- + /* spin_unlock/spin_lock now done in fops->complete() */
- + ep->pcd->fops->complete(ep->pcd, ep->priv, req->priv, status,
- + req->actual);
- +
- + if (ep->pcd->request_pending > 0) {
- + --ep->pcd->request_pending;
- + }
- +
- + ep->stopped = stopped;
- + DWC_FREE(req);
- +}
- +
- +/**
- + * This function terminates all the requsts in the EP request queue.
- + */
- +void dwc_otg_request_nuke(dwc_otg_pcd_ep_t * ep)
- +{
- + dwc_otg_pcd_request_t *req;
- +
- + ep->stopped = 1;
- +
- + /* called with irqs blocked?? */
- + while (!DWC_CIRCLEQ_EMPTY(&ep->queue)) {
- + req = DWC_CIRCLEQ_FIRST(&ep->queue);
- + dwc_otg_request_done(ep, req, -DWC_E_SHUTDOWN);
- + }
- +}
- +
- +void dwc_otg_pcd_start(dwc_otg_pcd_t * pcd,
- + const struct dwc_otg_pcd_function_ops *fops)
- +{
- + pcd->fops = fops;
- +}
- +
- +/**
- + * PCD Callback function for initializing the PCD when switching to
- + * device mode.
- + *
- + * @param p void pointer to the <code>dwc_otg_pcd_t</code>
- + */
- +static int32_t dwc_otg_pcd_start_cb(void *p)
- +{
- + dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) p;
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
- +
- + /*
- + * Initialized the Core for Device mode.
- + */
- + if (dwc_otg_is_device_mode(core_if)) {
- + dwc_otg_core_dev_init(core_if);
- + /* Set core_if's lock pointer to the pcd->lock */
- + core_if->lock = pcd->lock;
- + }
- + return 1;
- +}
- +
- +/** CFI-specific buffer allocation function for EP */
- +#ifdef DWC_UTE_CFI
- +uint8_t *cfiw_ep_alloc_buffer(dwc_otg_pcd_t * pcd, void *pep, dwc_dma_t * addr,
- + size_t buflen, int flags)
- +{
- + dwc_otg_pcd_ep_t *ep;
- + ep = get_ep_from_handle(pcd, pep);
- + if (!ep) {
- + DWC_WARN("bad ep\n");
- + return -DWC_E_INVALID;
- + }
- +
- + return pcd->cfi->ops.ep_alloc_buf(pcd->cfi, pcd, ep, addr, buflen,
- + flags);
- +}
- +#else
- +uint8_t *cfiw_ep_alloc_buffer(dwc_otg_pcd_t * pcd, void *pep, dwc_dma_t * addr,
- + size_t buflen, int flags);
- +#endif
- +
- +/**
- + * PCD Callback function for notifying the PCD when resuming from
- + * suspend.
- + *
- + * @param p void pointer to the <code>dwc_otg_pcd_t</code>
- + */
- +static int32_t dwc_otg_pcd_resume_cb(void *p)
- +{
- + dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) p;
- +
- + if (pcd->fops->resume) {
- + pcd->fops->resume(pcd);
- + }
- +
- + /* Stop the SRP timeout timer. */
- + if ((GET_CORE_IF(pcd)->core_params->phy_type != DWC_PHY_TYPE_PARAM_FS)
- + || (!GET_CORE_IF(pcd)->core_params->i2c_enable)) {
- + if (GET_CORE_IF(pcd)->srp_timer_started) {
- + GET_CORE_IF(pcd)->srp_timer_started = 0;
- + DWC_TIMER_CANCEL(GET_CORE_IF(pcd)->srp_timer);
- + }
- + }
- + return 1;
- +}
- +
- +/**
- + * PCD Callback function for notifying the PCD device is suspended.
- + *
- + * @param p void pointer to the <code>dwc_otg_pcd_t</code>
- + */
- +static int32_t dwc_otg_pcd_suspend_cb(void *p)
- +{
- + dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) p;
- +
- + if (pcd->fops->suspend) {
- + DWC_SPINUNLOCK(pcd->lock);
- + pcd->fops->suspend(pcd);
- + DWC_SPINLOCK(pcd->lock);
- + }
- +
- + return 1;
- +}
- +
- +/**
- + * PCD Callback function for stopping the PCD when switching to Host
- + * mode.
- + *
- + * @param p void pointer to the <code>dwc_otg_pcd_t</code>
- + */
- +static int32_t dwc_otg_pcd_stop_cb(void *p)
- +{
- + dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) p;
- + extern void dwc_otg_pcd_stop(dwc_otg_pcd_t * _pcd);
- +
- + dwc_otg_pcd_stop(pcd);
- + return 1;
- +}
- +
- +/**
- + * PCD Callback structure for handling mode switching.
- + */
- +static dwc_otg_cil_callbacks_t pcd_callbacks = {
- + .start = dwc_otg_pcd_start_cb,
- + .stop = dwc_otg_pcd_stop_cb,
- + .suspend = dwc_otg_pcd_suspend_cb,
- + .resume_wakeup = dwc_otg_pcd_resume_cb,
- + .p = 0, /* Set at registration */
- +};
- +
- +/**
- + * This function allocates a DMA Descriptor chain for the Endpoint
- + * buffer to be used for a transfer to/from the specified endpoint.
- + */
- +dwc_otg_dev_dma_desc_t *dwc_otg_ep_alloc_desc_chain(struct device *dev,
- + dwc_dma_t * dma_desc_addr,
- + uint32_t count)
- +{
- + return DWC_DMA_ALLOC_ATOMIC(dev, count * sizeof(dwc_otg_dev_dma_desc_t),
- + dma_desc_addr);
- +}
- +
- +/**
- + * This function frees a DMA Descriptor chain that was allocated by ep_alloc_desc.
- + */
- +void dwc_otg_ep_free_desc_chain(struct device *dev,
- + dwc_otg_dev_dma_desc_t * desc_addr,
- + uint32_t dma_desc_addr, uint32_t count)
- +{
- + DWC_DMA_FREE(dev, count * sizeof(dwc_otg_dev_dma_desc_t), desc_addr,
- + dma_desc_addr);
- +}
- +
- +#ifdef DWC_EN_ISOC
- +
- +/**
- + * This function initializes a descriptor chain for Isochronous transfer
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param dwc_ep The EP to start the transfer on.
- + *
- + */
- +void dwc_otg_iso_ep_start_ddma_transfer(dwc_otg_core_if_t * core_if,
- + dwc_ep_t * dwc_ep)
- +{
- +
- + dsts_data_t dsts = {.d32 = 0 };
- + depctl_data_t depctl = {.d32 = 0 };
- + volatile uint32_t *addr;
- + int i, j;
- + uint32_t len;
- +
- + if (dwc_ep->is_in)
- + dwc_ep->desc_cnt = dwc_ep->buf_proc_intrvl / dwc_ep->bInterval;
- + else
- + dwc_ep->desc_cnt =
- + dwc_ep->buf_proc_intrvl * dwc_ep->pkt_per_frm /
- + dwc_ep->bInterval;
- +
- + /** Allocate descriptors for double buffering */
- + dwc_ep->iso_desc_addr =
- + dwc_otg_ep_alloc_desc_chain(&dwc_ep->iso_dma_desc_addr,
- + dwc_ep->desc_cnt * 2);
- + if (dwc_ep->desc_addr) {
- + DWC_WARN("%s, can't allocate DMA descriptor chain\n", __func__);
- + return;
- + }
- +
- + dsts.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
- +
- + /** ISO OUT EP */
- + if (dwc_ep->is_in == 0) {
- + dev_dma_desc_sts_t sts = {.d32 = 0 };
- + dwc_otg_dev_dma_desc_t *dma_desc = dwc_ep->iso_desc_addr;
- + dma_addr_t dma_ad;
- + uint32_t data_per_desc;
- + dwc_otg_dev_out_ep_regs_t *out_regs =
- + core_if->dev_if->out_ep_regs[dwc_ep->num];
- + int offset;
- +
- + addr = &core_if->dev_if->out_ep_regs[dwc_ep->num]->doepctl;
- + dma_ad = (dma_addr_t) DWC_READ_REG32(&(out_regs->doepdma));
- +
- + /** Buffer 0 descriptors setup */
- + dma_ad = dwc_ep->dma_addr0;
- +
- + sts.b_iso_out.bs = BS_HOST_READY;
- + sts.b_iso_out.rxsts = 0;
- + sts.b_iso_out.l = 0;
- + sts.b_iso_out.sp = 0;
- + sts.b_iso_out.ioc = 0;
- + sts.b_iso_out.pid = 0;
- + sts.b_iso_out.framenum = 0;
- +
- + offset = 0;
- + for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm;
- + i += dwc_ep->pkt_per_frm) {
- +
- + for (j = 0; j < dwc_ep->pkt_per_frm; ++j) {
- + uint32_t len = (j + 1) * dwc_ep->maxpacket;
- + if (len > dwc_ep->data_per_frame)
- + data_per_desc =
- + dwc_ep->data_per_frame -
- + j * dwc_ep->maxpacket;
- + else
- + data_per_desc = dwc_ep->maxpacket;
- + len = data_per_desc % 4;
- + if (len)
- + data_per_desc += 4 - len;
- +
- + sts.b_iso_out.rxbytes = data_per_desc;
- + dma_desc->buf = dma_ad;
- + dma_desc->status.d32 = sts.d32;
- +
- + offset += data_per_desc;
- + dma_desc++;
- + dma_ad += data_per_desc;
- + }
- + }
- +
- + for (j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) {
- + uint32_t len = (j + 1) * dwc_ep->maxpacket;
- + if (len > dwc_ep->data_per_frame)
- + data_per_desc =
- + dwc_ep->data_per_frame -
- + j * dwc_ep->maxpacket;
- + else
- + data_per_desc = dwc_ep->maxpacket;
- + len = data_per_desc % 4;
- + if (len)
- + data_per_desc += 4 - len;
- + sts.b_iso_out.rxbytes = data_per_desc;
- + dma_desc->buf = dma_ad;
- + dma_desc->status.d32 = sts.d32;
- +
- + offset += data_per_desc;
- + dma_desc++;
- + dma_ad += data_per_desc;
- + }
- +
- + sts.b_iso_out.ioc = 1;
- + len = (j + 1) * dwc_ep->maxpacket;
- + if (len > dwc_ep->data_per_frame)
- + data_per_desc =
- + dwc_ep->data_per_frame - j * dwc_ep->maxpacket;
- + else
- + data_per_desc = dwc_ep->maxpacket;
- + len = data_per_desc % 4;
- + if (len)
- + data_per_desc += 4 - len;
- + sts.b_iso_out.rxbytes = data_per_desc;
- +
- + dma_desc->buf = dma_ad;
- + dma_desc->status.d32 = sts.d32;
- + dma_desc++;
- +
- + /** Buffer 1 descriptors setup */
- + sts.b_iso_out.ioc = 0;
- + dma_ad = dwc_ep->dma_addr1;
- +
- + offset = 0;
- + for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm;
- + i += dwc_ep->pkt_per_frm) {
- + for (j = 0; j < dwc_ep->pkt_per_frm; ++j) {
- + uint32_t len = (j + 1) * dwc_ep->maxpacket;
- + if (len > dwc_ep->data_per_frame)
- + data_per_desc =
- + dwc_ep->data_per_frame -
- + j * dwc_ep->maxpacket;
- + else
- + data_per_desc = dwc_ep->maxpacket;
- + len = data_per_desc % 4;
- + if (len)
- + data_per_desc += 4 - len;
- +
- + data_per_desc =
- + sts.b_iso_out.rxbytes = data_per_desc;
- + dma_desc->buf = dma_ad;
- + dma_desc->status.d32 = sts.d32;
- +
- + offset += data_per_desc;
- + dma_desc++;
- + dma_ad += data_per_desc;
- + }
- + }
- + for (j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) {
- + data_per_desc =
- + ((j + 1) * dwc_ep->maxpacket >
- + dwc_ep->data_per_frame) ? dwc_ep->data_per_frame -
- + j * dwc_ep->maxpacket : dwc_ep->maxpacket;
- + data_per_desc +=
- + (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0;
- + sts.b_iso_out.rxbytes = data_per_desc;
- + dma_desc->buf = dma_ad;
- + dma_desc->status.d32 = sts.d32;
- +
- + offset += data_per_desc;
- + dma_desc++;
- + dma_ad += data_per_desc;
- + }
- +
- + sts.b_iso_out.ioc = 1;
- + sts.b_iso_out.l = 1;
- + data_per_desc =
- + ((j + 1) * dwc_ep->maxpacket >
- + dwc_ep->data_per_frame) ? dwc_ep->data_per_frame -
- + j * dwc_ep->maxpacket : dwc_ep->maxpacket;
- + data_per_desc +=
- + (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0;
- + sts.b_iso_out.rxbytes = data_per_desc;
- +
- + dma_desc->buf = dma_ad;
- + dma_desc->status.d32 = sts.d32;
- +
- + dwc_ep->next_frame = 0;
- +
- + /** Write dma_ad into DOEPDMA register */
- + DWC_WRITE_REG32(&(out_regs->doepdma),
- + (uint32_t) dwc_ep->iso_dma_desc_addr);
- +
- + }
- + /** ISO IN EP */
- + else {
- + dev_dma_desc_sts_t sts = {.d32 = 0 };
- + dwc_otg_dev_dma_desc_t *dma_desc = dwc_ep->iso_desc_addr;
- + dma_addr_t dma_ad;
- + dwc_otg_dev_in_ep_regs_t *in_regs =
- + core_if->dev_if->in_ep_regs[dwc_ep->num];
- + unsigned int frmnumber;
- + fifosize_data_t txfifosize, rxfifosize;
- +
- + txfifosize.d32 =
- + DWC_READ_REG32(&core_if->dev_if->in_ep_regs[dwc_ep->num]->
- + dtxfsts);
- + rxfifosize.d32 =
- + DWC_READ_REG32(&core_if->core_global_regs->grxfsiz);
- +
- + addr = &core_if->dev_if->in_ep_regs[dwc_ep->num]->diepctl;
- +
- + dma_ad = dwc_ep->dma_addr0;
- +
- + dsts.d32 =
- + DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
- +
- + sts.b_iso_in.bs = BS_HOST_READY;
- + sts.b_iso_in.txsts = 0;
- + sts.b_iso_in.sp =
- + (dwc_ep->data_per_frame % dwc_ep->maxpacket) ? 1 : 0;
- + sts.b_iso_in.ioc = 0;
- + sts.b_iso_in.pid = dwc_ep->pkt_per_frm;
- +
- + frmnumber = dwc_ep->next_frame;
- +
- + sts.b_iso_in.framenum = frmnumber;
- + sts.b_iso_in.txbytes = dwc_ep->data_per_frame;
- + sts.b_iso_in.l = 0;
- +
- + /** Buffer 0 descriptors setup */
- + for (i = 0; i < dwc_ep->desc_cnt - 1; i++) {
- + dma_desc->buf = dma_ad;
- + dma_desc->status.d32 = sts.d32;
- + dma_desc++;
- +
- + dma_ad += dwc_ep->data_per_frame;
- + sts.b_iso_in.framenum += dwc_ep->bInterval;
- + }
- +
- + sts.b_iso_in.ioc = 1;
- + dma_desc->buf = dma_ad;
- + dma_desc->status.d32 = sts.d32;
- + ++dma_desc;
- +
- + /** Buffer 1 descriptors setup */
- + sts.b_iso_in.ioc = 0;
- + dma_ad = dwc_ep->dma_addr1;
- +
- + for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm;
- + i += dwc_ep->pkt_per_frm) {
- + dma_desc->buf = dma_ad;
- + dma_desc->status.d32 = sts.d32;
- + dma_desc++;
- +
- + dma_ad += dwc_ep->data_per_frame;
- + sts.b_iso_in.framenum += dwc_ep->bInterval;
- +
- + sts.b_iso_in.ioc = 0;
- + }
- + sts.b_iso_in.ioc = 1;
- + sts.b_iso_in.l = 1;
- +
- + dma_desc->buf = dma_ad;
- + dma_desc->status.d32 = sts.d32;
- +
- + dwc_ep->next_frame = sts.b_iso_in.framenum + dwc_ep->bInterval;
- +
- + /** Write dma_ad into diepdma register */
- + DWC_WRITE_REG32(&(in_regs->diepdma),
- + (uint32_t) dwc_ep->iso_dma_desc_addr);
- + }
- + /** Enable endpoint, clear nak */
- + depctl.d32 = 0;
- + depctl.b.epena = 1;
- + depctl.b.usbactep = 1;
- + depctl.b.cnak = 1;
- +
- + DWC_MODIFY_REG32(addr, depctl.d32, depctl.d32);
- + depctl.d32 = DWC_READ_REG32(addr);
- +}
- +
- +/**
- + * This function initializes a descriptor chain for Isochronous transfer
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param ep The EP to start the transfer on.
- + *
- + */
- +void dwc_otg_iso_ep_start_buf_transfer(dwc_otg_core_if_t * core_if,
- + dwc_ep_t * ep)
- +{
- + depctl_data_t depctl = {.d32 = 0 };
- + volatile uint32_t *addr;
- +
- + if (ep->is_in) {
- + addr = &core_if->dev_if->in_ep_regs[ep->num]->diepctl;
- + } else {
- + addr = &core_if->dev_if->out_ep_regs[ep->num]->doepctl;
- + }
- +
- + if (core_if->dma_enable == 0 || core_if->dma_desc_enable != 0) {
- + return;
- + } else {
- + deptsiz_data_t deptsiz = {.d32 = 0 };
- +
- + ep->xfer_len =
- + ep->data_per_frame * ep->buf_proc_intrvl / ep->bInterval;
- + ep->pkt_cnt =
- + (ep->xfer_len - 1 + ep->maxpacket) / ep->maxpacket;
- + ep->xfer_count = 0;
- + ep->xfer_buff =
- + (ep->proc_buf_num) ? ep->xfer_buff1 : ep->xfer_buff0;
- + ep->dma_addr =
- + (ep->proc_buf_num) ? ep->dma_addr1 : ep->dma_addr0;
- +
- + if (ep->is_in) {
- + /* Program the transfer size and packet count
- + * as follows: xfersize = N * maxpacket +
- + * short_packet pktcnt = N + (short_packet
- + * exist ? 1 : 0)
- + */
- + deptsiz.b.mc = ep->pkt_per_frm;
- + deptsiz.b.xfersize = ep->xfer_len;
- + deptsiz.b.pktcnt =
- + (ep->xfer_len - 1 + ep->maxpacket) / ep->maxpacket;
- + DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[ep->num]->
- + dieptsiz, deptsiz.d32);
- +
- + /* Write the DMA register */
- + DWC_WRITE_REG32(&
- + (core_if->dev_if->in_ep_regs[ep->num]->
- + diepdma), (uint32_t) ep->dma_addr);
- +
- + } else {
- + deptsiz.b.pktcnt =
- + (ep->xfer_len + (ep->maxpacket - 1)) /
- + ep->maxpacket;
- + deptsiz.b.xfersize = deptsiz.b.pktcnt * ep->maxpacket;
- +
- + DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[ep->num]->
- + doeptsiz, deptsiz.d32);
- +
- + /* Write the DMA register */
- + DWC_WRITE_REG32(&
- + (core_if->dev_if->out_ep_regs[ep->num]->
- + doepdma), (uint32_t) ep->dma_addr);
- +
- + }
- + /** Enable endpoint, clear nak */
- + depctl.d32 = 0;
- + depctl.b.epena = 1;
- + depctl.b.cnak = 1;
- +
- + DWC_MODIFY_REG32(addr, depctl.d32, depctl.d32);
- + }
- +}
- +
- +/**
- + * This function does the setup for a data transfer for an EP and
- + * starts the transfer. For an IN transfer, the packets will be
- + * loaded into the appropriate Tx FIFO in the ISR. For OUT transfers,
- + * the packets are unloaded from the Rx FIFO in the ISR.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param ep The EP to start the transfer on.
- + */
- +
- +static void dwc_otg_iso_ep_start_transfer(dwc_otg_core_if_t * core_if,
- + dwc_ep_t * ep)
- +{
- + if (core_if->dma_enable) {
- + if (core_if->dma_desc_enable) {
- + if (ep->is_in) {
- + ep->desc_cnt = ep->pkt_cnt / ep->pkt_per_frm;
- + } else {
- + ep->desc_cnt = ep->pkt_cnt;
- + }
- + dwc_otg_iso_ep_start_ddma_transfer(core_if, ep);
- + } else {
- + if (core_if->pti_enh_enable) {
- + dwc_otg_iso_ep_start_buf_transfer(core_if, ep);
- + } else {
- + ep->cur_pkt_addr =
- + (ep->proc_buf_num) ? ep->xfer_buff1 : ep->
- + xfer_buff0;
- + ep->cur_pkt_dma_addr =
- + (ep->proc_buf_num) ? ep->dma_addr1 : ep->
- + dma_addr0;
- + dwc_otg_iso_ep_start_frm_transfer(core_if, ep);
- + }
- + }
- + } else {
- + ep->cur_pkt_addr =
- + (ep->proc_buf_num) ? ep->xfer_buff1 : ep->xfer_buff0;
- + ep->cur_pkt_dma_addr =
- + (ep->proc_buf_num) ? ep->dma_addr1 : ep->dma_addr0;
- + dwc_otg_iso_ep_start_frm_transfer(core_if, ep);
- + }
- +}
- +
- +/**
- + * This function stops transfer for an EP and
- + * resets the ep's variables.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param ep The EP to start the transfer on.
- + */
- +
- +void dwc_otg_iso_ep_stop_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
- +{
- + depctl_data_t depctl = {.d32 = 0 };
- + volatile uint32_t *addr;
- +
- + if (ep->is_in == 1) {
- + addr = &core_if->dev_if->in_ep_regs[ep->num]->diepctl;
- + } else {
- + addr = &core_if->dev_if->out_ep_regs[ep->num]->doepctl;
- + }
- +
- + /* disable the ep */
- + depctl.d32 = DWC_READ_REG32(addr);
- +
- + depctl.b.epdis = 1;
- + depctl.b.snak = 1;
- +
- + DWC_WRITE_REG32(addr, depctl.d32);
- +
- + if (core_if->dma_desc_enable &&
- + ep->iso_desc_addr && ep->iso_dma_desc_addr) {
- + dwc_otg_ep_free_desc_chain(ep->iso_desc_addr,
- + ep->iso_dma_desc_addr,
- + ep->desc_cnt * 2);
- + }
- +
- + /* reset varibales */
- + ep->dma_addr0 = 0;
- + ep->dma_addr1 = 0;
- + ep->xfer_buff0 = 0;
- + ep->xfer_buff1 = 0;
- + ep->data_per_frame = 0;
- + ep->data_pattern_frame = 0;
- + ep->sync_frame = 0;
- + ep->buf_proc_intrvl = 0;
- + ep->bInterval = 0;
- + ep->proc_buf_num = 0;
- + ep->pkt_per_frm = 0;
- + ep->pkt_per_frm = 0;
- + ep->desc_cnt = 0;
- + ep->iso_desc_addr = 0;
- + ep->iso_dma_desc_addr = 0;
- +}
- +
- +int dwc_otg_pcd_iso_ep_start(dwc_otg_pcd_t * pcd, void *ep_handle,
- + uint8_t * buf0, uint8_t * buf1, dwc_dma_t dma0,
- + dwc_dma_t dma1, int sync_frame, int dp_frame,
- + int data_per_frame, int start_frame,
- + int buf_proc_intrvl, void *req_handle,
- + int atomic_alloc)
- +{
- + dwc_otg_pcd_ep_t *ep;
- + dwc_irqflags_t flags = 0;
- + dwc_ep_t *dwc_ep;
- + int32_t frm_data;
- + dsts_data_t dsts;
- + dwc_otg_core_if_t *core_if;
- +
- + ep = get_ep_from_handle(pcd, ep_handle);
- +
- + if (!ep || !ep->desc || ep->dwc_ep.num == 0) {
- + DWC_WARN("bad ep\n");
- + return -DWC_E_INVALID;
- + }
- +
- + DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
- + core_if = GET_CORE_IF(pcd);
- + dwc_ep = &ep->dwc_ep;
- +
- + if (ep->iso_req_handle) {
- + DWC_WARN("ISO request in progress\n");
- + }
- +
- + dwc_ep->dma_addr0 = dma0;
- + dwc_ep->dma_addr1 = dma1;
- +
- + dwc_ep->xfer_buff0 = buf0;
- + dwc_ep->xfer_buff1 = buf1;
- +
- + dwc_ep->data_per_frame = data_per_frame;
- +
- + /** @todo - pattern data support is to be implemented in the future */
- + dwc_ep->data_pattern_frame = dp_frame;
- + dwc_ep->sync_frame = sync_frame;
- +
- + dwc_ep->buf_proc_intrvl = buf_proc_intrvl;
- +
- + dwc_ep->bInterval = 1 << (ep->desc->bInterval - 1);
- +
- + dwc_ep->proc_buf_num = 0;
- +
- + dwc_ep->pkt_per_frm = 0;
- + frm_data = ep->dwc_ep.data_per_frame;
- + while (frm_data > 0) {
- + dwc_ep->pkt_per_frm++;
- + frm_data -= ep->dwc_ep.maxpacket;
- + }
- +
- + dsts.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
- +
- + if (start_frame == -1) {
- + dwc_ep->next_frame = dsts.b.soffn + 1;
- + if (dwc_ep->bInterval != 1) {
- + dwc_ep->next_frame =
- + dwc_ep->next_frame + (dwc_ep->bInterval - 1 -
- + dwc_ep->next_frame %
- + dwc_ep->bInterval);
- + }
- + } else {
- + dwc_ep->next_frame = start_frame;
- + }
- +
- + if (!core_if->pti_enh_enable) {
- + dwc_ep->pkt_cnt =
- + dwc_ep->buf_proc_intrvl * dwc_ep->pkt_per_frm /
- + dwc_ep->bInterval;
- + } else {
- + dwc_ep->pkt_cnt =
- + (dwc_ep->data_per_frame *
- + (dwc_ep->buf_proc_intrvl / dwc_ep->bInterval)
- + - 1 + dwc_ep->maxpacket) / dwc_ep->maxpacket;
- + }
- +
- + if (core_if->dma_desc_enable) {
- + dwc_ep->desc_cnt =
- + dwc_ep->buf_proc_intrvl * dwc_ep->pkt_per_frm /
- + dwc_ep->bInterval;
- + }
- +
- + if (atomic_alloc) {
- + dwc_ep->pkt_info =
- + DWC_ALLOC_ATOMIC(sizeof(iso_pkt_info_t) * dwc_ep->pkt_cnt);
- + } else {
- + dwc_ep->pkt_info =
- + DWC_ALLOC(sizeof(iso_pkt_info_t) * dwc_ep->pkt_cnt);
- + }
- + if (!dwc_ep->pkt_info) {
- + DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
- + return -DWC_E_NO_MEMORY;
- + }
- + if (core_if->pti_enh_enable) {
- + dwc_memset(dwc_ep->pkt_info, 0,
- + sizeof(iso_pkt_info_t) * dwc_ep->pkt_cnt);
- + }
- +
- + dwc_ep->cur_pkt = 0;
- + ep->iso_req_handle = req_handle;
- +
- + DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
- + dwc_otg_iso_ep_start_transfer(core_if, dwc_ep);
- + return 0;
- +}
- +
- +int dwc_otg_pcd_iso_ep_stop(dwc_otg_pcd_t * pcd, void *ep_handle,
- + void *req_handle)
- +{
- + dwc_irqflags_t flags = 0;
- + dwc_otg_pcd_ep_t *ep;
- + dwc_ep_t *dwc_ep;
- +
- + ep = get_ep_from_handle(pcd, ep_handle);
- + if (!ep || !ep->desc || ep->dwc_ep.num == 0) {
- + DWC_WARN("bad ep\n");
- + return -DWC_E_INVALID;
- + }
- + dwc_ep = &ep->dwc_ep;
- +
- + dwc_otg_iso_ep_stop_transfer(GET_CORE_IF(pcd), dwc_ep);
- +
- + DWC_FREE(dwc_ep->pkt_info);
- + DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
- + if (ep->iso_req_handle != req_handle) {
- + DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
- + return -DWC_E_INVALID;
- + }
- +
- + DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
- +
- + ep->iso_req_handle = 0;
- + return 0;
- +}
- +
- +/**
- + * This function is used for perodical data exchnage between PCD and gadget drivers.
- + * for Isochronous EPs
- + *
- + * - Every time a sync period completes this function is called to
- + * perform data exchange between PCD and gadget
- + */
- +void dwc_otg_iso_buffer_done(dwc_otg_pcd_t * pcd, dwc_otg_pcd_ep_t * ep,
- + void *req_handle)
- +{
- + int i;
- + dwc_ep_t *dwc_ep;
- +
- + dwc_ep = &ep->dwc_ep;
- +
- + DWC_SPINUNLOCK(ep->pcd->lock);
- + pcd->fops->isoc_complete(pcd, ep->priv, ep->iso_req_handle,
- + dwc_ep->proc_buf_num ^ 0x1);
- + DWC_SPINLOCK(ep->pcd->lock);
- +
- + for (i = 0; i < dwc_ep->pkt_cnt; ++i) {
- + dwc_ep->pkt_info[i].status = 0;
- + dwc_ep->pkt_info[i].offset = 0;
- + dwc_ep->pkt_info[i].length = 0;
- + }
- +}
- +
- +int dwc_otg_pcd_get_iso_packet_count(dwc_otg_pcd_t * pcd, void *ep_handle,
- + void *iso_req_handle)
- +{
- + dwc_otg_pcd_ep_t *ep;
- + dwc_ep_t *dwc_ep;
- +
- + ep = get_ep_from_handle(pcd, ep_handle);
- + if (!ep->desc || ep->dwc_ep.num == 0) {
- + DWC_WARN("bad ep\n");
- + return -DWC_E_INVALID;
- + }
- + dwc_ep = &ep->dwc_ep;
- +
- + return dwc_ep->pkt_cnt;
- +}
- +
- +void dwc_otg_pcd_get_iso_packet_params(dwc_otg_pcd_t * pcd, void *ep_handle,
- + void *iso_req_handle, int packet,
- + int *status, int *actual, int *offset)
- +{
- + dwc_otg_pcd_ep_t *ep;
- + dwc_ep_t *dwc_ep;
- +
- + ep = get_ep_from_handle(pcd, ep_handle);
- + if (!ep)
- + DWC_WARN("bad ep\n");
- +
- + dwc_ep = &ep->dwc_ep;
- +
- + *status = dwc_ep->pkt_info[packet].status;
- + *actual = dwc_ep->pkt_info[packet].length;
- + *offset = dwc_ep->pkt_info[packet].offset;
- +}
- +
- +#endif /* DWC_EN_ISOC */
- +
- +static void dwc_otg_pcd_init_ep(dwc_otg_pcd_t * pcd, dwc_otg_pcd_ep_t * pcd_ep,
- + uint32_t is_in, uint32_t ep_num)
- +{
- + /* Init EP structure */
- + pcd_ep->desc = 0;
- + pcd_ep->pcd = pcd;
- + pcd_ep->stopped = 1;
- + pcd_ep->queue_sof = 0;
- +
- + /* Init DWC ep structure */
- + pcd_ep->dwc_ep.is_in = is_in;
- + pcd_ep->dwc_ep.num = ep_num;
- + pcd_ep->dwc_ep.active = 0;
- + pcd_ep->dwc_ep.tx_fifo_num = 0;
- + /* Control until ep is actvated */
- + pcd_ep->dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL;
- + pcd_ep->dwc_ep.maxpacket = MAX_PACKET_SIZE;
- + pcd_ep->dwc_ep.dma_addr = 0;
- + pcd_ep->dwc_ep.start_xfer_buff = 0;
- + pcd_ep->dwc_ep.xfer_buff = 0;
- + pcd_ep->dwc_ep.xfer_len = 0;
- + pcd_ep->dwc_ep.xfer_count = 0;
- + pcd_ep->dwc_ep.sent_zlp = 0;
- + pcd_ep->dwc_ep.total_len = 0;
- + pcd_ep->dwc_ep.desc_addr = 0;
- + pcd_ep->dwc_ep.dma_desc_addr = 0;
- + DWC_CIRCLEQ_INIT(&pcd_ep->queue);
- +}
- +
- +/**
- + * Initialize ep's
- + */
- +static void dwc_otg_pcd_reinit(dwc_otg_pcd_t * pcd)
- +{
- + int i;
- + uint32_t hwcfg1;
- + dwc_otg_pcd_ep_t *ep;
- + int in_ep_cntr, out_ep_cntr;
- + uint32_t num_in_eps = (GET_CORE_IF(pcd))->dev_if->num_in_eps;
- + uint32_t num_out_eps = (GET_CORE_IF(pcd))->dev_if->num_out_eps;
- +
- + /**
- + * Initialize the EP0 structure.
- + */
- + ep = &pcd->ep0;
- + dwc_otg_pcd_init_ep(pcd, ep, 0, 0);
- +
- + in_ep_cntr = 0;
- + hwcfg1 = (GET_CORE_IF(pcd))->hwcfg1.d32 >> 3;
- + for (i = 1; in_ep_cntr < num_in_eps; i++) {
- + if ((hwcfg1 & 0x1) == 0) {
- + dwc_otg_pcd_ep_t *ep = &pcd->in_ep[in_ep_cntr];
- + in_ep_cntr++;
- + /**
- + * @todo NGS: Add direction to EP, based on contents
- + * of HWCFG1. Need a copy of HWCFG1 in pcd structure?
- + * sprintf(";r
- + */
- + dwc_otg_pcd_init_ep(pcd, ep, 1 /* IN */ , i);
- +
- + DWC_CIRCLEQ_INIT(&ep->queue);
- + }
- + hwcfg1 >>= 2;
- + }
- +
- + out_ep_cntr = 0;
- + hwcfg1 = (GET_CORE_IF(pcd))->hwcfg1.d32 >> 2;
- + for (i = 1; out_ep_cntr < num_out_eps; i++) {
- + if ((hwcfg1 & 0x1) == 0) {
- + dwc_otg_pcd_ep_t *ep = &pcd->out_ep[out_ep_cntr];
- + out_ep_cntr++;
- + /**
- + * @todo NGS: Add direction to EP, based on contents
- + * of HWCFG1. Need a copy of HWCFG1 in pcd structure?
- + * sprintf(";r
- + */
- + dwc_otg_pcd_init_ep(pcd, ep, 0 /* OUT */ , i);
- + DWC_CIRCLEQ_INIT(&ep->queue);
- + }
- + hwcfg1 >>= 2;
- + }
- +
- + pcd->ep0state = EP0_DISCONNECT;
- + pcd->ep0.dwc_ep.maxpacket = MAX_EP0_SIZE;
- + pcd->ep0.dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL;
- +}
- +
- +/**
- + * This function is called when the SRP timer expires. The SRP should
- + * complete within 6 seconds.
- + */
- +static void srp_timeout(void *ptr)
- +{
- + gotgctl_data_t gotgctl;
- + dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *) ptr;
- + volatile uint32_t *addr = &core_if->core_global_regs->gotgctl;
- +
- + gotgctl.d32 = DWC_READ_REG32(addr);
- +
- + core_if->srp_timer_started = 0;
- +
- + if (core_if->adp_enable) {
- + if (gotgctl.b.bsesvld == 0) {
- + gpwrdn_data_t gpwrdn = {.d32 = 0 };
- + DWC_PRINTF("SRP Timeout BSESSVLD = 0\n");
- + /* Power off the core */
- + if (core_if->power_down == 2) {
- + gpwrdn.b.pwrdnswtch = 1;
- + DWC_MODIFY_REG32(&core_if->
- + core_global_regs->gpwrdn,
- + gpwrdn.d32, 0);
- + }
- +
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pmuintsel = 1;
- + gpwrdn.b.pmuactv = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gpwrdn, 0,
- + gpwrdn.d32);
- + dwc_otg_adp_probe_start(core_if);
- + } else {
- + DWC_PRINTF("SRP Timeout BSESSVLD = 1\n");
- + core_if->op_state = B_PERIPHERAL;
- + dwc_otg_core_init(core_if);
- + dwc_otg_enable_global_interrupts(core_if);
- + cil_pcd_start(core_if);
- + }
- + }
- +
- + if ((core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS) &&
- + (core_if->core_params->i2c_enable)) {
- + DWC_PRINTF("SRP Timeout\n");
- +
- + if ((core_if->srp_success) && (gotgctl.b.bsesvld)) {
- + if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) {
- + core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p);
- + }
- +
- + /* Clear Session Request */
- + gotgctl.d32 = 0;
- + gotgctl.b.sesreq = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gotgctl,
- + gotgctl.d32, 0);
- +
- + core_if->srp_success = 0;
- + } else {
- + __DWC_ERROR("Device not connected/responding\n");
- + gotgctl.b.sesreq = 0;
- + DWC_WRITE_REG32(addr, gotgctl.d32);
- + }
- + } else if (gotgctl.b.sesreq) {
- + DWC_PRINTF("SRP Timeout\n");
- +
- + __DWC_ERROR("Device not connected/responding\n");
- + gotgctl.b.sesreq = 0;
- + DWC_WRITE_REG32(addr, gotgctl.d32);
- + } else {
- + DWC_PRINTF(" SRP GOTGCTL=%0x\n", gotgctl.d32);
- + }
- +}
- +
- +/**
- + * Tasklet
- + *
- + */
- +extern void start_next_request(dwc_otg_pcd_ep_t * ep);
- +
- +static void start_xfer_tasklet_func(void *data)
- +{
- + dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) data;
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
- +
- + int i;
- + depctl_data_t diepctl;
- +
- + DWC_DEBUGPL(DBG_PCDV, "Start xfer tasklet\n");
- +
- + diepctl.d32 = DWC_READ_REG32(&core_if->dev_if->in_ep_regs[0]->diepctl);
- +
- + if (pcd->ep0.queue_sof) {
- + pcd->ep0.queue_sof = 0;
- + start_next_request(&pcd->ep0);
- + // break;
- + }
- +
- + for (i = 0; i < core_if->dev_if->num_in_eps; i++) {
- + depctl_data_t diepctl;
- + diepctl.d32 =
- + DWC_READ_REG32(&core_if->dev_if->in_ep_regs[i]->diepctl);
- +
- + if (pcd->in_ep[i].queue_sof) {
- + pcd->in_ep[i].queue_sof = 0;
- + start_next_request(&pcd->in_ep[i]);
- + // break;
- + }
- + }
- +
- + return;
- +}
- +
- +/**
- + * This function initialized the PCD portion of the driver.
- + *
- + */
- +dwc_otg_pcd_t *dwc_otg_pcd_init(dwc_otg_device_t *otg_dev)
- +{
- + struct device *dev = &otg_dev->os_dep.platformdev->dev;
- + dwc_otg_core_if_t *core_if = otg_dev->core_if;
- + dwc_otg_pcd_t *pcd = NULL;
- + dwc_otg_dev_if_t *dev_if;
- + int i;
- +
- + /*
- + * Allocate PCD structure
- + */
- + pcd = DWC_ALLOC(sizeof(dwc_otg_pcd_t));
- +
- + if (pcd == NULL) {
- + return NULL;
- + }
- +
- +#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_SPINLOCK))
- + DWC_SPINLOCK_ALLOC_LINUX_DEBUG(pcd->lock);
- +#else
- + pcd->lock = DWC_SPINLOCK_ALLOC();
- +#endif
- + DWC_DEBUGPL(DBG_HCDV, "Init of PCD %p given core_if %p\n",
- + pcd, core_if);//GRAYG
- + if (!pcd->lock) {
- + DWC_ERROR("Could not allocate lock for pcd");
- + DWC_FREE(pcd);
- + return NULL;
- + }
- + /* Set core_if's lock pointer to hcd->lock */
- + core_if->lock = pcd->lock;
- + pcd->core_if = core_if;
- +
- + dev_if = core_if->dev_if;
- + dev_if->isoc_ep = NULL;
- +
- + if (core_if->hwcfg4.b.ded_fifo_en) {
- + DWC_PRINTF("Dedicated Tx FIFOs mode\n");
- + } else {
- + DWC_PRINTF("Shared Tx FIFO mode\n");
- + }
- +
- + /*
- + * Initialized the Core for Device mode here if there is nod ADP support.
- + * Otherwise it will be done later in dwc_otg_adp_start routine.
- + */
- + if (dwc_otg_is_device_mode(core_if) /*&& !core_if->adp_enable*/) {
- + dwc_otg_core_dev_init(core_if);
- + }
- +
- + /*
- + * Register the PCD Callbacks.
- + */
- + dwc_otg_cil_register_pcd_callbacks(core_if, &pcd_callbacks, pcd);
- +
- + /*
- + * Initialize the DMA buffer for SETUP packets
- + */
- + if (GET_CORE_IF(pcd)->dma_enable) {
- + pcd->setup_pkt =
- + DWC_DMA_ALLOC(dev, sizeof(*pcd->setup_pkt) * 5,
- + &pcd->setup_pkt_dma_handle);
- + if (pcd->setup_pkt == NULL) {
- + DWC_FREE(pcd);
- + return NULL;
- + }
- +
- + pcd->status_buf =
- + DWC_DMA_ALLOC(dev, sizeof(uint16_t),
- + &pcd->status_buf_dma_handle);
- + if (pcd->status_buf == NULL) {
- + DWC_DMA_FREE(dev, sizeof(*pcd->setup_pkt) * 5,
- + pcd->setup_pkt, pcd->setup_pkt_dma_handle);
- + DWC_FREE(pcd);
- + return NULL;
- + }
- +
- + if (GET_CORE_IF(pcd)->dma_desc_enable) {
- + dev_if->setup_desc_addr[0] =
- + dwc_otg_ep_alloc_desc_chain(dev,
- + &dev_if->dma_setup_desc_addr[0], 1);
- + dev_if->setup_desc_addr[1] =
- + dwc_otg_ep_alloc_desc_chain(dev,
- + &dev_if->dma_setup_desc_addr[1], 1);
- + dev_if->in_desc_addr =
- + dwc_otg_ep_alloc_desc_chain(dev,
- + &dev_if->dma_in_desc_addr, 1);
- + dev_if->out_desc_addr =
- + dwc_otg_ep_alloc_desc_chain(dev,
- + &dev_if->dma_out_desc_addr, 1);
- + pcd->data_terminated = 0;
- +
- + if (dev_if->setup_desc_addr[0] == 0
- + || dev_if->setup_desc_addr[1] == 0
- + || dev_if->in_desc_addr == 0
- + || dev_if->out_desc_addr == 0) {
- +
- + if (dev_if->out_desc_addr)
- + dwc_otg_ep_free_desc_chain(dev,
- + dev_if->out_desc_addr,
- + dev_if->dma_out_desc_addr, 1);
- + if (dev_if->in_desc_addr)
- + dwc_otg_ep_free_desc_chain(dev,
- + dev_if->in_desc_addr,
- + dev_if->dma_in_desc_addr, 1);
- + if (dev_if->setup_desc_addr[1])
- + dwc_otg_ep_free_desc_chain(dev,
- + dev_if->setup_desc_addr[1],
- + dev_if->dma_setup_desc_addr[1], 1);
- + if (dev_if->setup_desc_addr[0])
- + dwc_otg_ep_free_desc_chain(dev,
- + dev_if->setup_desc_addr[0],
- + dev_if->dma_setup_desc_addr[0], 1);
- +
- + DWC_DMA_FREE(dev, sizeof(*pcd->setup_pkt) * 5,
- + pcd->setup_pkt,
- + pcd->setup_pkt_dma_handle);
- + DWC_DMA_FREE(dev, sizeof(*pcd->status_buf),
- + pcd->status_buf,
- + pcd->status_buf_dma_handle);
- +
- + DWC_FREE(pcd);
- +
- + return NULL;
- + }
- + }
- + } else {
- + pcd->setup_pkt = DWC_ALLOC(sizeof(*pcd->setup_pkt) * 5);
- + if (pcd->setup_pkt == NULL) {
- + DWC_FREE(pcd);
- + return NULL;
- + }
- +
- + pcd->status_buf = DWC_ALLOC(sizeof(uint16_t));
- + if (pcd->status_buf == NULL) {
- + DWC_FREE(pcd->setup_pkt);
- + DWC_FREE(pcd);
- + return NULL;
- + }
- + }
- +
- + dwc_otg_pcd_reinit(pcd);
- +
- + /* Allocate the cfi object for the PCD */
- +#ifdef DWC_UTE_CFI
- + pcd->cfi = DWC_ALLOC(sizeof(cfiobject_t));
- + if (NULL == pcd->cfi)
- + goto fail;
- + if (init_cfi(pcd->cfi)) {
- + CFI_INFO("%s: Failed to init the CFI object\n", __func__);
- + goto fail;
- + }
- +#endif
- +
- + /* Initialize tasklets */
- + pcd->start_xfer_tasklet = DWC_TASK_ALLOC("xfer_tasklet",
- + start_xfer_tasklet_func, pcd);
- + pcd->test_mode_tasklet = DWC_TASK_ALLOC("test_mode_tasklet",
- + do_test_mode, pcd);
- +
- + /* Initialize SRP timer */
- + core_if->srp_timer = DWC_TIMER_ALLOC("SRP TIMER", srp_timeout, core_if);
- +
- + if (core_if->core_params->dev_out_nak) {
- + /**
- + * Initialize xfer timeout timer. Implemented for
- + * 2.93a feature "Device DDMA OUT NAK Enhancement"
- + */
- + for(i = 0; i < MAX_EPS_CHANNELS; i++) {
- + pcd->core_if->ep_xfer_timer[i] =
- + DWC_TIMER_ALLOC("ep timer", ep_xfer_timeout,
- + &pcd->core_if->ep_xfer_info[i]);
- + }
- + }
- +
- + return pcd;
- +#ifdef DWC_UTE_CFI
- +fail:
- +#endif
- + if (pcd->setup_pkt)
- + DWC_FREE(pcd->setup_pkt);
- + if (pcd->status_buf)
- + DWC_FREE(pcd->status_buf);
- +#ifdef DWC_UTE_CFI
- + if (pcd->cfi)
- + DWC_FREE(pcd->cfi);
- +#endif
- + if (pcd)
- + DWC_FREE(pcd);
- + return NULL;
- +
- +}
- +
- +/**
- + * Remove PCD specific data
- + */
- +void dwc_otg_pcd_remove(dwc_otg_pcd_t * pcd)
- +{
- + dwc_otg_dev_if_t *dev_if = GET_CORE_IF(pcd)->dev_if;
- + struct device *dev = dwc_otg_pcd_to_dev(pcd);
- + int i;
- +
- + if (pcd->core_if->core_params->dev_out_nak) {
- + for (i = 0; i < MAX_EPS_CHANNELS; i++) {
- + DWC_TIMER_CANCEL(pcd->core_if->ep_xfer_timer[i]);
- + pcd->core_if->ep_xfer_info[i].state = 0;
- + }
- + }
- +
- + if (GET_CORE_IF(pcd)->dma_enable) {
- + DWC_DMA_FREE(dev, sizeof(*pcd->setup_pkt) * 5, pcd->setup_pkt,
- + pcd->setup_pkt_dma_handle);
- + DWC_DMA_FREE(dev, sizeof(uint16_t), pcd->status_buf,
- + pcd->status_buf_dma_handle);
- + if (GET_CORE_IF(pcd)->dma_desc_enable) {
- + dwc_otg_ep_free_desc_chain(dev,
- + dev_if->setup_desc_addr[0],
- + dev_if->dma_setup_desc_addr
- + [0], 1);
- + dwc_otg_ep_free_desc_chain(dev,
- + dev_if->setup_desc_addr[1],
- + dev_if->dma_setup_desc_addr
- + [1], 1);
- + dwc_otg_ep_free_desc_chain(dev,
- + dev_if->in_desc_addr,
- + dev_if->dma_in_desc_addr, 1);
- + dwc_otg_ep_free_desc_chain(dev,
- + dev_if->out_desc_addr,
- + dev_if->dma_out_desc_addr,
- + 1);
- + }
- + } else {
- + DWC_FREE(pcd->setup_pkt);
- + DWC_FREE(pcd->status_buf);
- + }
- + DWC_SPINLOCK_FREE(pcd->lock);
- + /* Set core_if's lock pointer to NULL */
- + pcd->core_if->lock = NULL;
- +
- + DWC_TASK_FREE(pcd->start_xfer_tasklet);
- + DWC_TASK_FREE(pcd->test_mode_tasklet);
- + if (pcd->core_if->core_params->dev_out_nak) {
- + for (i = 0; i < MAX_EPS_CHANNELS; i++) {
- + if (pcd->core_if->ep_xfer_timer[i]) {
- + DWC_TIMER_FREE(pcd->core_if->ep_xfer_timer[i]);
- + }
- + }
- + }
- +
- +/* Release the CFI object's dynamic memory */
- +#ifdef DWC_UTE_CFI
- + if (pcd->cfi->ops.release) {
- + pcd->cfi->ops.release(pcd->cfi);
- + }
- +#endif
- +
- + DWC_FREE(pcd);
- +}
- +
- +/**
- + * Returns whether registered pcd is dual speed or not
- + */
- +uint32_t dwc_otg_pcd_is_dualspeed(dwc_otg_pcd_t * pcd)
- +{
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
- +
- + if ((core_if->core_params->speed == DWC_SPEED_PARAM_FULL) ||
- + ((core_if->hwcfg2.b.hs_phy_type == 2) &&
- + (core_if->hwcfg2.b.fs_phy_type == 1) &&
- + (core_if->core_params->ulpi_fs_ls))) {
- + return 0;
- + }
- +
- + return 1;
- +}
- +
- +/**
- + * Returns whether registered pcd is OTG capable or not
- + */
- +uint32_t dwc_otg_pcd_is_otg(dwc_otg_pcd_t * pcd)
- +{
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
- + gusbcfg_data_t usbcfg = {.d32 = 0 };
- +
- + usbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gusbcfg);
- + if (!usbcfg.b.srpcap || !usbcfg.b.hnpcap) {
- + return 0;
- + }
- +
- + return 1;
- +}
- +
- +/**
- + * This function assigns periodic Tx FIFO to an periodic EP
- + * in shared Tx FIFO mode
- + */
- +static uint32_t assign_tx_fifo(dwc_otg_core_if_t * core_if)
- +{
- + uint32_t TxMsk = 1;
- + int i;
- +
- + for (i = 0; i < core_if->hwcfg4.b.num_in_eps; ++i) {
- + if ((TxMsk & core_if->tx_msk) == 0) {
- + core_if->tx_msk |= TxMsk;
- + return i + 1;
- + }
- + TxMsk <<= 1;
- + }
- + return 0;
- +}
- +
- +/**
- + * This function assigns periodic Tx FIFO to an periodic EP
- + * in shared Tx FIFO mode
- + */
- +static uint32_t assign_perio_tx_fifo(dwc_otg_core_if_t * core_if)
- +{
- + uint32_t PerTxMsk = 1;
- + int i;
- + for (i = 0; i < core_if->hwcfg4.b.num_dev_perio_in_ep; ++i) {
- + if ((PerTxMsk & core_if->p_tx_msk) == 0) {
- + core_if->p_tx_msk |= PerTxMsk;
- + return i + 1;
- + }
- + PerTxMsk <<= 1;
- + }
- + return 0;
- +}
- +
- +/**
- + * This function releases periodic Tx FIFO
- + * in shared Tx FIFO mode
- + */
- +static void release_perio_tx_fifo(dwc_otg_core_if_t * core_if,
- + uint32_t fifo_num)
- +{
- + core_if->p_tx_msk =
- + (core_if->p_tx_msk & (1 << (fifo_num - 1))) ^ core_if->p_tx_msk;
- +}
- +
- +/**
- + * This function releases periodic Tx FIFO
- + * in shared Tx FIFO mode
- + */
- +static void release_tx_fifo(dwc_otg_core_if_t * core_if, uint32_t fifo_num)
- +{
- + core_if->tx_msk =
- + (core_if->tx_msk & (1 << (fifo_num - 1))) ^ core_if->tx_msk;
- +}
- +
- +/**
- + * This function is being called from gadget
- + * to enable PCD endpoint.
- + */
- +int dwc_otg_pcd_ep_enable(dwc_otg_pcd_t * pcd,
- + const uint8_t * ep_desc, void *usb_ep)
- +{
- + int num, dir;
- + dwc_otg_pcd_ep_t *ep = NULL;
- + const usb_endpoint_descriptor_t *desc;
- + dwc_irqflags_t flags;
- + fifosize_data_t dptxfsiz = {.d32 = 0 };
- + gdfifocfg_data_t gdfifocfg = {.d32 = 0 };
- + gdfifocfg_data_t gdfifocfgbase = {.d32 = 0 };
- + int retval = 0;
- + int i, epcount;
- + struct device *dev = dwc_otg_pcd_to_dev(pcd);
- +
- + desc = (const usb_endpoint_descriptor_t *)ep_desc;
- +
- + if (!desc) {
- + pcd->ep0.priv = usb_ep;
- + ep = &pcd->ep0;
- + retval = -DWC_E_INVALID;
- + goto out;
- + }
- +
- + num = UE_GET_ADDR(desc->bEndpointAddress);
- + dir = UE_GET_DIR(desc->bEndpointAddress);
- +
- + if (!UGETW(desc->wMaxPacketSize)) {
- + DWC_WARN("bad maxpacketsize\n");
- + retval = -DWC_E_INVALID;
- + goto out;
- + }
- +
- + if (dir == UE_DIR_IN) {
- + epcount = pcd->core_if->dev_if->num_in_eps;
- + for (i = 0; i < epcount; i++) {
- + if (num == pcd->in_ep[i].dwc_ep.num) {
- + ep = &pcd->in_ep[i];
- + break;
- + }
- + }
- + } else {
- + epcount = pcd->core_if->dev_if->num_out_eps;
- + for (i = 0; i < epcount; i++) {
- + if (num == pcd->out_ep[i].dwc_ep.num) {
- + ep = &pcd->out_ep[i];
- + break;
- + }
- + }
- + }
- +
- + if (!ep) {
- + DWC_WARN("bad address\n");
- + retval = -DWC_E_INVALID;
- + goto out;
- + }
- +
- + DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
- +
- + ep->desc = desc;
- + ep->priv = usb_ep;
- +
- + /*
- + * Activate the EP
- + */
- + ep->stopped = 0;
- +
- + ep->dwc_ep.is_in = (dir == UE_DIR_IN);
- + ep->dwc_ep.maxpacket = UGETW(desc->wMaxPacketSize);
- +
- + ep->dwc_ep.type = desc->bmAttributes & UE_XFERTYPE;
- +
- + if (ep->dwc_ep.is_in) {
- + if (!GET_CORE_IF(pcd)->en_multiple_tx_fifo) {
- + ep->dwc_ep.tx_fifo_num = 0;
- +
- + if (ep->dwc_ep.type == UE_ISOCHRONOUS) {
- + /*
- + * if ISOC EP then assign a Periodic Tx FIFO.
- + */
- + ep->dwc_ep.tx_fifo_num =
- + assign_perio_tx_fifo(GET_CORE_IF(pcd));
- + }
- + } else {
- + /*
- + * if Dedicated FIFOs mode is on then assign a Tx FIFO.
- + */
- + ep->dwc_ep.tx_fifo_num =
- + assign_tx_fifo(GET_CORE_IF(pcd));
- + }
- +
- + /* Calculating EP info controller base address */
- + if (ep->dwc_ep.tx_fifo_num
- + && GET_CORE_IF(pcd)->en_multiple_tx_fifo) {
- + gdfifocfg.d32 =
- + DWC_READ_REG32(&GET_CORE_IF(pcd)->
- + core_global_regs->gdfifocfg);
- + gdfifocfgbase.d32 = gdfifocfg.d32 >> 16;
- + dptxfsiz.d32 =
- + (DWC_READ_REG32
- + (&GET_CORE_IF(pcd)->core_global_regs->
- + dtxfsiz[ep->dwc_ep.tx_fifo_num - 1]) >> 16);
- + gdfifocfg.b.epinfobase =
- + gdfifocfgbase.d32 + dptxfsiz.d32;
- + if (GET_CORE_IF(pcd)->snpsid <= OTG_CORE_REV_2_94a) {
- + DWC_WRITE_REG32(&GET_CORE_IF(pcd)->
- + core_global_regs->gdfifocfg,
- + gdfifocfg.d32);
- + }
- + }
- + }
- + /* Set initial data PID. */
- + if (ep->dwc_ep.type == UE_BULK) {
- + ep->dwc_ep.data_pid_start = 0;
- + }
- +
- + /* Alloc DMA Descriptors */
- + if (GET_CORE_IF(pcd)->dma_desc_enable) {
- +#ifndef DWC_UTE_PER_IO
- + if (ep->dwc_ep.type != UE_ISOCHRONOUS) {
- +#endif
- + ep->dwc_ep.desc_addr =
- + dwc_otg_ep_alloc_desc_chain(dev,
- + &ep->dwc_ep.dma_desc_addr,
- + MAX_DMA_DESC_CNT);
- + if (!ep->dwc_ep.desc_addr) {
- + DWC_WARN("%s, can't allocate DMA descriptor\n",
- + __func__);
- + retval = -DWC_E_SHUTDOWN;
- + DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
- + goto out;
- + }
- +#ifndef DWC_UTE_PER_IO
- + }
- +#endif
- + }
- +
- + DWC_DEBUGPL(DBG_PCD, "Activate %s: type=%d, mps=%d desc=%p\n",
- + (ep->dwc_ep.is_in ? "IN" : "OUT"),
- + ep->dwc_ep.type, ep->dwc_ep.maxpacket, ep->desc);
- +#ifdef DWC_UTE_PER_IO
- + ep->dwc_ep.xiso_bInterval = 1 << (ep->desc->bInterval - 1);
- +#endif
- + if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) {
- + ep->dwc_ep.bInterval = 1 << (ep->desc->bInterval - 1);
- + ep->dwc_ep.frame_num = 0xFFFFFFFF;
- + }
- +
- + dwc_otg_ep_activate(GET_CORE_IF(pcd), &ep->dwc_ep);
- +
- +#ifdef DWC_UTE_CFI
- + if (pcd->cfi->ops.ep_enable) {
- + pcd->cfi->ops.ep_enable(pcd->cfi, pcd, ep);
- + }
- +#endif
- +
- + DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
- +
- +out:
- + return retval;
- +}
- +
- +/**
- + * This function is being called from gadget
- + * to disable PCD endpoint.
- + */
- +int dwc_otg_pcd_ep_disable(dwc_otg_pcd_t * pcd, void *ep_handle)
- +{
- + dwc_otg_pcd_ep_t *ep;
- + dwc_irqflags_t flags;
- + dwc_otg_dev_dma_desc_t *desc_addr;
- + dwc_dma_t dma_desc_addr;
- + gdfifocfg_data_t gdfifocfgbase = {.d32 = 0 };
- + gdfifocfg_data_t gdfifocfg = {.d32 = 0 };
- + fifosize_data_t dptxfsiz = {.d32 = 0 };
- + struct device *dev = dwc_otg_pcd_to_dev(pcd);
- +
- + ep = get_ep_from_handle(pcd, ep_handle);
- +
- + if (!ep || !ep->desc) {
- + DWC_DEBUGPL(DBG_PCD, "bad ep address\n");
- + return -DWC_E_INVALID;
- + }
- +
- + DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
- +
- + dwc_otg_request_nuke(ep);
- +
- + dwc_otg_ep_deactivate(GET_CORE_IF(pcd), &ep->dwc_ep);
- + if (pcd->core_if->core_params->dev_out_nak) {
- + DWC_TIMER_CANCEL(pcd->core_if->ep_xfer_timer[ep->dwc_ep.num]);
- + pcd->core_if->ep_xfer_info[ep->dwc_ep.num].state = 0;
- + }
- + ep->desc = NULL;
- + ep->stopped = 1;
- +
- + gdfifocfg.d32 =
- + DWC_READ_REG32(&GET_CORE_IF(pcd)->core_global_regs->gdfifocfg);
- + gdfifocfgbase.d32 = gdfifocfg.d32 >> 16;
- +
- + if (ep->dwc_ep.is_in) {
- + if (GET_CORE_IF(pcd)->en_multiple_tx_fifo) {
- + /* Flush the Tx FIFO */
- + dwc_otg_flush_tx_fifo(GET_CORE_IF(pcd),
- + ep->dwc_ep.tx_fifo_num);
- + }
- + release_perio_tx_fifo(GET_CORE_IF(pcd), ep->dwc_ep.tx_fifo_num);
- + release_tx_fifo(GET_CORE_IF(pcd), ep->dwc_ep.tx_fifo_num);
- + if (GET_CORE_IF(pcd)->en_multiple_tx_fifo) {
- + /* Decreasing EPinfo Base Addr */
- + dptxfsiz.d32 =
- + (DWC_READ_REG32
- + (&GET_CORE_IF(pcd)->
- + core_global_regs->dtxfsiz[ep->dwc_ep.tx_fifo_num-1]) >> 16);
- + gdfifocfg.b.epinfobase = gdfifocfgbase.d32 - dptxfsiz.d32;
- + if (GET_CORE_IF(pcd)->snpsid <= OTG_CORE_REV_2_94a) {
- + DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gdfifocfg,
- + gdfifocfg.d32);
- + }
- + }
- + }
- +
- + /* Free DMA Descriptors */
- + if (GET_CORE_IF(pcd)->dma_desc_enable) {
- + if (ep->dwc_ep.type != UE_ISOCHRONOUS) {
- + desc_addr = ep->dwc_ep.desc_addr;
- + dma_desc_addr = ep->dwc_ep.dma_desc_addr;
- +
- + /* Cannot call dma_free_coherent() with IRQs disabled */
- + DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
- + dwc_otg_ep_free_desc_chain(dev, desc_addr, dma_desc_addr,
- + MAX_DMA_DESC_CNT);
- +
- + goto out_unlocked;
- + }
- + }
- + DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
- +
- +out_unlocked:
- + DWC_DEBUGPL(DBG_PCD, "%d %s disabled\n", ep->dwc_ep.num,
- + ep->dwc_ep.is_in ? "IN" : "OUT");
- + return 0;
- +
- +}
- +
- +/******************************************************************************/
- +#ifdef DWC_UTE_PER_IO
- +
- +/**
- + * Free the request and its extended parts
- + *
- + */
- +void dwc_pcd_xiso_ereq_free(dwc_otg_pcd_ep_t * ep, dwc_otg_pcd_request_t * req)
- +{
- + DWC_FREE(req->ext_req.per_io_frame_descs);
- + DWC_FREE(req);
- +}
- +
- +/**
- + * Start the next request in the endpoint's queue.
- + *
- + */
- +int dwc_otg_pcd_xiso_start_next_request(dwc_otg_pcd_t * pcd,
- + dwc_otg_pcd_ep_t * ep)
- +{
- + int i;
- + dwc_otg_pcd_request_t *req = NULL;
- + dwc_ep_t *dwcep = NULL;
- + struct dwc_iso_xreq_port *ereq = NULL;
- + struct dwc_iso_pkt_desc_port *ddesc_iso;
- + uint16_t nat;
- + depctl_data_t diepctl;
- +
- + dwcep = &ep->dwc_ep;
- +
- + if (dwcep->xiso_active_xfers > 0) {
- +#if 0 //Disable this to decrease s/w overhead that is crucial for Isoc transfers
- + DWC_WARN("There are currently active transfers for EP%d \
- + (active=%d; queued=%d)", dwcep->num, dwcep->xiso_active_xfers,
- + dwcep->xiso_queued_xfers);
- +#endif
- + return 0;
- + }
- +
- + nat = UGETW(ep->desc->wMaxPacketSize);
- + nat = (nat >> 11) & 0x03;
- +
- + if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) {
- + req = DWC_CIRCLEQ_FIRST(&ep->queue);
- + ereq = &req->ext_req;
- + ep->stopped = 0;
- +
- + /* Get the frame number */
- + dwcep->xiso_frame_num =
- + dwc_otg_get_frame_number(GET_CORE_IF(pcd));
- + DWC_DEBUG("FRM_NUM=%d", dwcep->xiso_frame_num);
- +
- + ddesc_iso = ereq->per_io_frame_descs;
- +
- + if (dwcep->is_in) {
- + /* Setup DMA Descriptor chain for IN Isoc request */
- + for (i = 0; i < ereq->pio_pkt_count; i++) {
- + //if ((i % (nat + 1)) == 0)
- + if ( i > 0 )
- + dwcep->xiso_frame_num =
- + (dwcep->xiso_bInterval +
- + dwcep->xiso_frame_num) & 0x3FFF;
- + dwcep->desc_addr[i].buf =
- + req->dma + ddesc_iso[i].offset;
- + dwcep->desc_addr[i].status.b_iso_in.txbytes =
- + ddesc_iso[i].length;
- + dwcep->desc_addr[i].status.b_iso_in.framenum =
- + dwcep->xiso_frame_num;
- + dwcep->desc_addr[i].status.b_iso_in.bs =
- + BS_HOST_READY;
- + dwcep->desc_addr[i].status.b_iso_in.txsts = 0;
- + dwcep->desc_addr[i].status.b_iso_in.sp =
- + (ddesc_iso[i].length %
- + dwcep->maxpacket) ? 1 : 0;
- + dwcep->desc_addr[i].status.b_iso_in.ioc = 0;
- + dwcep->desc_addr[i].status.b_iso_in.pid = nat + 1;
- + dwcep->desc_addr[i].status.b_iso_in.l = 0;
- +
- + /* Process the last descriptor */
- + if (i == ereq->pio_pkt_count - 1) {
- + dwcep->desc_addr[i].status.b_iso_in.ioc = 1;
- + dwcep->desc_addr[i].status.b_iso_in.l = 1;
- + }
- + }
- +
- + /* Setup and start the transfer for this endpoint */
- + dwcep->xiso_active_xfers++;
- + DWC_WRITE_REG32(&GET_CORE_IF(pcd)->dev_if->
- + in_ep_regs[dwcep->num]->diepdma,
- + dwcep->dma_desc_addr);
- + diepctl.d32 = 0;
- + diepctl.b.epena = 1;
- + diepctl.b.cnak = 1;
- + DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->dev_if->
- + in_ep_regs[dwcep->num]->diepctl, 0,
- + diepctl.d32);
- + } else {
- + /* Setup DMA Descriptor chain for OUT Isoc request */
- + for (i = 0; i < ereq->pio_pkt_count; i++) {
- + //if ((i % (nat + 1)) == 0)
- + dwcep->xiso_frame_num = (dwcep->xiso_bInterval +
- + dwcep->xiso_frame_num) & 0x3FFF;
- + dwcep->desc_addr[i].buf =
- + req->dma + ddesc_iso[i].offset;
- + dwcep->desc_addr[i].status.b_iso_out.rxbytes =
- + ddesc_iso[i].length;
- + dwcep->desc_addr[i].status.b_iso_out.framenum =
- + dwcep->xiso_frame_num;
- + dwcep->desc_addr[i].status.b_iso_out.bs =
- + BS_HOST_READY;
- + dwcep->desc_addr[i].status.b_iso_out.rxsts = 0;
- + dwcep->desc_addr[i].status.b_iso_out.sp =
- + (ddesc_iso[i].length %
- + dwcep->maxpacket) ? 1 : 0;
- + dwcep->desc_addr[i].status.b_iso_out.ioc = 0;
- + dwcep->desc_addr[i].status.b_iso_out.pid = nat + 1;
- + dwcep->desc_addr[i].status.b_iso_out.l = 0;
- +
- + /* Process the last descriptor */
- + if (i == ereq->pio_pkt_count - 1) {
- + dwcep->desc_addr[i].status.b_iso_out.ioc = 1;
- + dwcep->desc_addr[i].status.b_iso_out.l = 1;
- + }
- + }
- +
- + /* Setup and start the transfer for this endpoint */
- + dwcep->xiso_active_xfers++;
- + DWC_WRITE_REG32(&GET_CORE_IF(pcd)->
- + dev_if->out_ep_regs[dwcep->num]->
- + doepdma, dwcep->dma_desc_addr);
- + diepctl.d32 = 0;
- + diepctl.b.epena = 1;
- + diepctl.b.cnak = 1;
- + DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->
- + dev_if->out_ep_regs[dwcep->num]->
- + doepctl, 0, diepctl.d32);
- + }
- +
- + } else {
- + ep->stopped = 1;
- + }
- +
- + return 0;
- +}
- +
- +/**
- + * - Remove the request from the queue
- + */
- +void complete_xiso_ep(dwc_otg_pcd_ep_t * ep)
- +{
- + dwc_otg_pcd_request_t *req = NULL;
- + struct dwc_iso_xreq_port *ereq = NULL;
- + struct dwc_iso_pkt_desc_port *ddesc_iso = NULL;
- + dwc_ep_t *dwcep = NULL;
- + int i;
- +
- + //DWC_DEBUG();
- + dwcep = &ep->dwc_ep;
- +
- + /* Get the first pending request from the queue */
- + if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) {
- + req = DWC_CIRCLEQ_FIRST(&ep->queue);
- + if (!req) {
- + DWC_PRINTF("complete_ep 0x%p, req = NULL!\n", ep);
- + return;
- + }
- + dwcep->xiso_active_xfers--;
- + dwcep->xiso_queued_xfers--;
- + /* Remove this request from the queue */
- + DWC_CIRCLEQ_REMOVE_INIT(&ep->queue, req, queue_entry);
- + } else {
- + DWC_PRINTF("complete_ep 0x%p, ep->queue empty!\n", ep);
- + return;
- + }
- +
- + ep->stopped = 1;
- + ereq = &req->ext_req;
- + ddesc_iso = ereq->per_io_frame_descs;
- +
- + if (dwcep->xiso_active_xfers < 0) {
- + DWC_WARN("EP#%d (xiso_active_xfers=%d)", dwcep->num,
- + dwcep->xiso_active_xfers);
- + }
- +
- + /* Fill the Isoc descs of portable extended req from dma descriptors */
- + for (i = 0; i < ereq->pio_pkt_count; i++) {
- + if (dwcep->is_in) { /* IN endpoints */
- + ddesc_iso[i].actual_length = ddesc_iso[i].length -
- + dwcep->desc_addr[i].status.b_iso_in.txbytes;
- + ddesc_iso[i].status =
- + dwcep->desc_addr[i].status.b_iso_in.txsts;
- + } else { /* OUT endpoints */
- + ddesc_iso[i].actual_length = ddesc_iso[i].length -
- + dwcep->desc_addr[i].status.b_iso_out.rxbytes;
- + ddesc_iso[i].status =
- + dwcep->desc_addr[i].status.b_iso_out.rxsts;
- + }
- + }
- +
- + DWC_SPINUNLOCK(ep->pcd->lock);
- +
- + /* Call the completion function in the non-portable logic */
- + ep->pcd->fops->xisoc_complete(ep->pcd, ep->priv, req->priv, 0,
- + &req->ext_req);
- +
- + DWC_SPINLOCK(ep->pcd->lock);
- +
- + /* Free the request - specific freeing needed for extended request object */
- + dwc_pcd_xiso_ereq_free(ep, req);
- +
- + /* Start the next request */
- + dwc_otg_pcd_xiso_start_next_request(ep->pcd, ep);
- +
- + return;
- +}
- +
- +/**
- + * Create and initialize the Isoc pkt descriptors of the extended request.
- + *
- + */
- +static int dwc_otg_pcd_xiso_create_pkt_descs(dwc_otg_pcd_request_t * req,
- + void *ereq_nonport,
- + int atomic_alloc)
- +{
- + struct dwc_iso_xreq_port *ereq = NULL;
- + struct dwc_iso_xreq_port *req_mapped = NULL;
- + struct dwc_iso_pkt_desc_port *ipds = NULL; /* To be created in this function */
- + uint32_t pkt_count;
- + int i;
- +
- + ereq = &req->ext_req;
- + req_mapped = (struct dwc_iso_xreq_port *)ereq_nonport;
- + pkt_count = req_mapped->pio_pkt_count;
- +
- + /* Create the isoc descs */
- + if (atomic_alloc) {
- + ipds = DWC_ALLOC_ATOMIC(sizeof(*ipds) * pkt_count);
- + } else {
- + ipds = DWC_ALLOC(sizeof(*ipds) * pkt_count);
- + }
- +
- + if (!ipds) {
- + DWC_ERROR("Failed to allocate isoc descriptors");
- + return -DWC_E_NO_MEMORY;
- + }
- +
- + /* Initialize the extended request fields */
- + ereq->per_io_frame_descs = ipds;
- + ereq->error_count = 0;
- + ereq->pio_alloc_pkt_count = pkt_count;
- + ereq->pio_pkt_count = pkt_count;
- + ereq->tr_sub_flags = req_mapped->tr_sub_flags;
- +
- + /* Init the Isoc descriptors */
- + for (i = 0; i < pkt_count; i++) {
- + ipds[i].length = req_mapped->per_io_frame_descs[i].length;
- + ipds[i].offset = req_mapped->per_io_frame_descs[i].offset;
- + ipds[i].status = req_mapped->per_io_frame_descs[i].status; /* 0 */
- + ipds[i].actual_length =
- + req_mapped->per_io_frame_descs[i].actual_length;
- + }
- +
- + return 0;
- +}
- +
- +static void prn_ext_request(struct dwc_iso_xreq_port *ereq)
- +{
- + struct dwc_iso_pkt_desc_port *xfd = NULL;
- + int i;
- +
- + DWC_DEBUG("per_io_frame_descs=%p", ereq->per_io_frame_descs);
- + DWC_DEBUG("tr_sub_flags=%d", ereq->tr_sub_flags);
- + DWC_DEBUG("error_count=%d", ereq->error_count);
- + DWC_DEBUG("pio_alloc_pkt_count=%d", ereq->pio_alloc_pkt_count);
- + DWC_DEBUG("pio_pkt_count=%d", ereq->pio_pkt_count);
- + DWC_DEBUG("res=%d", ereq->res);
- +
- + for (i = 0; i < ereq->pio_pkt_count; i++) {
- + xfd = &ereq->per_io_frame_descs[0];
- + DWC_DEBUG("FD #%d", i);
- +
- + DWC_DEBUG("xfd->actual_length=%d", xfd->actual_length);
- + DWC_DEBUG("xfd->length=%d", xfd->length);
- + DWC_DEBUG("xfd->offset=%d", xfd->offset);
- + DWC_DEBUG("xfd->status=%d", xfd->status);
- + }
- +}
- +
- +/**
- + *
- + */
- +int dwc_otg_pcd_xiso_ep_queue(dwc_otg_pcd_t * pcd, void *ep_handle,
- + uint8_t * buf, dwc_dma_t dma_buf, uint32_t buflen,
- + int zero, void *req_handle, int atomic_alloc,
- + void *ereq_nonport)
- +{
- + dwc_otg_pcd_request_t *req = NULL;
- + dwc_otg_pcd_ep_t *ep;
- + dwc_irqflags_t flags;
- + int res;
- +
- + ep = get_ep_from_handle(pcd, ep_handle);
- + if (!ep) {
- + DWC_WARN("bad ep\n");
- + return -DWC_E_INVALID;
- + }
- +
- + /* We support this extension only for DDMA mode */
- + if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC)
- + if (!GET_CORE_IF(pcd)->dma_desc_enable)
- + return -DWC_E_INVALID;
- +
- + /* Create a dwc_otg_pcd_request_t object */
- + if (atomic_alloc) {
- + req = DWC_ALLOC_ATOMIC(sizeof(*req));
- + } else {
- + req = DWC_ALLOC(sizeof(*req));
- + }
- +
- + if (!req) {
- + return -DWC_E_NO_MEMORY;
- + }
- +
- + /* Create the Isoc descs for this request which shall be the exact match
- + * of the structure sent to us from the non-portable logic */
- + res =
- + dwc_otg_pcd_xiso_create_pkt_descs(req, ereq_nonport, atomic_alloc);
- + if (res) {
- + DWC_WARN("Failed to init the Isoc descriptors");
- + DWC_FREE(req);
- + return res;
- + }
- +
- + DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
- +
- + DWC_CIRCLEQ_INIT_ENTRY(req, queue_entry);
- + req->buf = buf;
- + req->dma = dma_buf;
- + req->length = buflen;
- + req->sent_zlp = zero;
- + req->priv = req_handle;
- +
- + //DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
- + ep->dwc_ep.dma_addr = dma_buf;
- + ep->dwc_ep.start_xfer_buff = buf;
- + ep->dwc_ep.xfer_buff = buf;
- + ep->dwc_ep.xfer_len = 0;
- + ep->dwc_ep.xfer_count = 0;
- + ep->dwc_ep.sent_zlp = 0;
- + ep->dwc_ep.total_len = buflen;
- +
- + /* Add this request to the tail */
- + DWC_CIRCLEQ_INSERT_TAIL(&ep->queue, req, queue_entry);
- + ep->dwc_ep.xiso_queued_xfers++;
- +
- +//DWC_DEBUG("CP_0");
- +//DWC_DEBUG("req->ext_req.tr_sub_flags=%d", req->ext_req.tr_sub_flags);
- +//prn_ext_request((struct dwc_iso_xreq_port *) ereq_nonport);
- +//prn_ext_request(&req->ext_req);
- +
- + //DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
- +
- + /* If the req->status == ASAP then check if there is any active transfer
- + * for this endpoint. If no active transfers, then get the first entry
- + * from the queue and start that transfer
- + */
- + if (req->ext_req.tr_sub_flags == DWC_EREQ_TF_ASAP) {
- + res = dwc_otg_pcd_xiso_start_next_request(pcd, ep);
- + if (res) {
- + DWC_WARN("Failed to start the next Isoc transfer");
- + DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
- + DWC_FREE(req);
- + return res;
- + }
- + }
- +
- + DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
- + return 0;
- +}
- +
- +#endif
- +/* END ifdef DWC_UTE_PER_IO ***************************************************/
- +int dwc_otg_pcd_ep_queue(dwc_otg_pcd_t * pcd, void *ep_handle,
- + uint8_t * buf, dwc_dma_t dma_buf, uint32_t buflen,
- + int zero, void *req_handle, int atomic_alloc)
- +{
- + struct device *dev = dwc_otg_pcd_to_dev(pcd);
- + dwc_irqflags_t flags;
- + dwc_otg_pcd_request_t *req;
- + dwc_otg_pcd_ep_t *ep;
- + uint32_t max_transfer;
- +
- + ep = get_ep_from_handle(pcd, ep_handle);
- + if (!ep || (!ep->desc && ep->dwc_ep.num != 0)) {
- + DWC_WARN("bad ep\n");
- + return -DWC_E_INVALID;
- + }
- +
- + if (atomic_alloc) {
- + req = DWC_ALLOC_ATOMIC(sizeof(*req));
- + } else {
- + req = DWC_ALLOC(sizeof(*req));
- + }
- +
- + if (!req) {
- + return -DWC_E_NO_MEMORY;
- + }
- + DWC_CIRCLEQ_INIT_ENTRY(req, queue_entry);
- + if (!GET_CORE_IF(pcd)->core_params->opt) {
- + if (ep->dwc_ep.num != 0) {
- + DWC_ERROR("queue req %p, len %d buf %p\n",
- + req_handle, buflen, buf);
- + }
- + }
- +
- + req->buf = buf;
- + req->dma = dma_buf;
- + req->length = buflen;
- + req->sent_zlp = zero;
- + req->priv = req_handle;
- + req->dw_align_buf = NULL;
- + if ((dma_buf & 0x3) && GET_CORE_IF(pcd)->dma_enable
- + && !GET_CORE_IF(pcd)->dma_desc_enable)
- + req->dw_align_buf = DWC_DMA_ALLOC(dev, buflen,
- + &req->dw_align_buf_dma);
- + DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
- +
- + /*
- + * After adding request to the queue for IN ISOC wait for In Token Received
- + * when TX FIFO is empty interrupt and for OUT ISOC wait for OUT Token
- + * Received when EP is disabled interrupt to obtain starting microframe
- + * (odd/even) start transfer
- + */
- + if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) {
- + if (req != 0) {
- + depctl_data_t depctl = {.d32 =
- + DWC_READ_REG32(&pcd->core_if->dev_if->
- + in_ep_regs[ep->dwc_ep.num]->
- + diepctl) };
- + ++pcd->request_pending;
- +
- + DWC_CIRCLEQ_INSERT_TAIL(&ep->queue, req, queue_entry);
- + if (ep->dwc_ep.is_in) {
- + depctl.b.cnak = 1;
- + DWC_WRITE_REG32(&pcd->core_if->dev_if->
- + in_ep_regs[ep->dwc_ep.num]->
- + diepctl, depctl.d32);
- + }
- +
- + DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
- + }
- + return 0;
- + }
- +
- + /*
- + * For EP0 IN without premature status, zlp is required?
- + */
- + if (ep->dwc_ep.num == 0 && ep->dwc_ep.is_in) {
- + DWC_DEBUGPL(DBG_PCDV, "%d-OUT ZLP\n", ep->dwc_ep.num);
- + //_req->zero = 1;
- + }
- +
- + /* Start the transfer */
- + if (DWC_CIRCLEQ_EMPTY(&ep->queue) && !ep->stopped) {
- + /* EP0 Transfer? */
- + if (ep->dwc_ep.num == 0) {
- + switch (pcd->ep0state) {
- + case EP0_IN_DATA_PHASE:
- + DWC_DEBUGPL(DBG_PCD,
- + "%s ep0: EP0_IN_DATA_PHASE\n",
- + __func__);
- + break;
- +
- + case EP0_OUT_DATA_PHASE:
- + DWC_DEBUGPL(DBG_PCD,
- + "%s ep0: EP0_OUT_DATA_PHASE\n",
- + __func__);
- + if (pcd->request_config) {
- + /* Complete STATUS PHASE */
- + ep->dwc_ep.is_in = 1;
- + pcd->ep0state = EP0_IN_STATUS_PHASE;
- + }
- + break;
- +
- + case EP0_IN_STATUS_PHASE:
- + DWC_DEBUGPL(DBG_PCD,
- + "%s ep0: EP0_IN_STATUS_PHASE\n",
- + __func__);
- + break;
- +
- + default:
- + DWC_DEBUGPL(DBG_ANY, "ep0: odd state %d\n",
- + pcd->ep0state);
- + DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
- + return -DWC_E_SHUTDOWN;
- + }
- +
- + ep->dwc_ep.dma_addr = dma_buf;
- + ep->dwc_ep.start_xfer_buff = buf;
- + ep->dwc_ep.xfer_buff = buf;
- + ep->dwc_ep.xfer_len = buflen;
- + ep->dwc_ep.xfer_count = 0;
- + ep->dwc_ep.sent_zlp = 0;
- + ep->dwc_ep.total_len = ep->dwc_ep.xfer_len;
- +
- + if (zero) {
- + if ((ep->dwc_ep.xfer_len %
- + ep->dwc_ep.maxpacket == 0)
- + && (ep->dwc_ep.xfer_len != 0)) {
- + ep->dwc_ep.sent_zlp = 1;
- + }
- +
- + }
- +
- + dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd),
- + &ep->dwc_ep);
- + } // non-ep0 endpoints
- + else {
- +#ifdef DWC_UTE_CFI
- + if (ep->dwc_ep.buff_mode != BM_STANDARD) {
- + /* store the request length */
- + ep->dwc_ep.cfi_req_len = buflen;
- + pcd->cfi->ops.build_descriptors(pcd->cfi, pcd,
- + ep, req);
- + } else {
- +#endif
- + max_transfer =
- + GET_CORE_IF(ep->pcd)->core_params->
- + max_transfer_size;
- +
- + /* Setup and start the Transfer */
- + if (req->dw_align_buf){
- + if (ep->dwc_ep.is_in)
- + dwc_memcpy(req->dw_align_buf,
- + buf, buflen);
- + ep->dwc_ep.dma_addr =
- + req->dw_align_buf_dma;
- + ep->dwc_ep.start_xfer_buff =
- + req->dw_align_buf;
- + ep->dwc_ep.xfer_buff =
- + req->dw_align_buf;
- + } else {
- + ep->dwc_ep.dma_addr = dma_buf;
- + ep->dwc_ep.start_xfer_buff = buf;
- + ep->dwc_ep.xfer_buff = buf;
- + }
- + ep->dwc_ep.xfer_len = 0;
- + ep->dwc_ep.xfer_count = 0;
- + ep->dwc_ep.sent_zlp = 0;
- + ep->dwc_ep.total_len = buflen;
- +
- + ep->dwc_ep.maxxfer = max_transfer;
- + if (GET_CORE_IF(pcd)->dma_desc_enable) {
- + uint32_t out_max_xfer =
- + DDMA_MAX_TRANSFER_SIZE -
- + (DDMA_MAX_TRANSFER_SIZE % 4);
- + if (ep->dwc_ep.is_in) {
- + if (ep->dwc_ep.maxxfer >
- + DDMA_MAX_TRANSFER_SIZE) {
- + ep->dwc_ep.maxxfer =
- + DDMA_MAX_TRANSFER_SIZE;
- + }
- + } else {
- + if (ep->dwc_ep.maxxfer >
- + out_max_xfer) {
- + ep->dwc_ep.maxxfer =
- + out_max_xfer;
- + }
- + }
- + }
- + if (ep->dwc_ep.maxxfer < ep->dwc_ep.total_len) {
- + ep->dwc_ep.maxxfer -=
- + (ep->dwc_ep.maxxfer %
- + ep->dwc_ep.maxpacket);
- + }
- +
- + if (zero) {
- + if ((ep->dwc_ep.total_len %
- + ep->dwc_ep.maxpacket == 0)
- + && (ep->dwc_ep.total_len != 0)) {
- + ep->dwc_ep.sent_zlp = 1;
- + }
- + }
- +#ifdef DWC_UTE_CFI
- + }
- +#endif
- + dwc_otg_ep_start_transfer(GET_CORE_IF(pcd),
- + &ep->dwc_ep);
- + }
- + }
- +
- + if (req != 0) {
- + ++pcd->request_pending;
- + DWC_CIRCLEQ_INSERT_TAIL(&ep->queue, req, queue_entry);
- + if (ep->dwc_ep.is_in && ep->stopped
- + && !(GET_CORE_IF(pcd)->dma_enable)) {
- + /** @todo NGS Create a function for this. */
- + diepmsk_data_t diepmsk = {.d32 = 0 };
- + diepmsk.b.intktxfemp = 1;
- + if (GET_CORE_IF(pcd)->multiproc_int_enable) {
- + DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->
- + dev_if->dev_global_regs->diepeachintmsk
- + [ep->dwc_ep.num], 0,
- + diepmsk.d32);
- + } else {
- + DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->
- + dev_if->dev_global_regs->
- + diepmsk, 0, diepmsk.d32);
- + }
- +
- + }
- + }
- + DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
- +
- + return 0;
- +}
- +
- +int dwc_otg_pcd_ep_dequeue(dwc_otg_pcd_t * pcd, void *ep_handle,
- + void *req_handle)
- +{
- + dwc_irqflags_t flags;
- + dwc_otg_pcd_request_t *req;
- + dwc_otg_pcd_ep_t *ep;
- +
- + ep = get_ep_from_handle(pcd, ep_handle);
- + if (!ep || (!ep->desc && ep->dwc_ep.num != 0)) {
- + DWC_WARN("bad argument\n");
- + return -DWC_E_INVALID;
- + }
- +
- + DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
- +
- + /* make sure it's actually queued on this endpoint */
- + DWC_CIRCLEQ_FOREACH(req, &ep->queue, queue_entry) {
- + if (req->priv == (void *)req_handle) {
- + break;
- + }
- + }
- +
- + if (req->priv != (void *)req_handle) {
- + DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
- + return -DWC_E_INVALID;
- + }
- +
- + if (!DWC_CIRCLEQ_EMPTY_ENTRY(req, queue_entry)) {
- + dwc_otg_request_done(ep, req, -DWC_E_RESTART);
- + } else {
- + req = NULL;
- + }
- +
- + DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
- +
- + return req ? 0 : -DWC_E_SHUTDOWN;
- +
- +}
- +
- +/**
- + * dwc_otg_pcd_ep_wedge - sets the halt feature and ignores clear requests
- + *
- + * Use this to stall an endpoint and ignore CLEAR_FEATURE(HALT_ENDPOINT)
- + * requests. If the gadget driver clears the halt status, it will
- + * automatically unwedge the endpoint.
- + *
- + * Returns zero on success, else negative DWC error code.
- + */
- +int dwc_otg_pcd_ep_wedge(dwc_otg_pcd_t * pcd, void *ep_handle)
- +{
- + dwc_otg_pcd_ep_t *ep;
- + dwc_irqflags_t flags;
- + int retval = 0;
- +
- + ep = get_ep_from_handle(pcd, ep_handle);
- +
- + if ((!ep->desc && ep != &pcd->ep0) ||
- + (ep->desc && (ep->desc->bmAttributes == UE_ISOCHRONOUS))) {
- + DWC_WARN("%s, bad ep\n", __func__);
- + return -DWC_E_INVALID;
- + }
- +
- + DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
- + if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) {
- + DWC_WARN("%d %s XFer In process\n", ep->dwc_ep.num,
- + ep->dwc_ep.is_in ? "IN" : "OUT");
- + retval = -DWC_E_AGAIN;
- + } else {
- + /* This code needs to be reviewed */
- + if (ep->dwc_ep.is_in == 1 && GET_CORE_IF(pcd)->dma_desc_enable) {
- + dtxfsts_data_t txstatus;
- + fifosize_data_t txfifosize;
- +
- + txfifosize.d32 =
- + DWC_READ_REG32(&GET_CORE_IF(pcd)->
- + core_global_regs->dtxfsiz[ep->dwc_ep.
- + tx_fifo_num]);
- + txstatus.d32 =
- + DWC_READ_REG32(&GET_CORE_IF(pcd)->
- + dev_if->in_ep_regs[ep->dwc_ep.num]->
- + dtxfsts);
- +
- + if (txstatus.b.txfspcavail < txfifosize.b.depth) {
- + DWC_WARN("%s() Data In Tx Fifo\n", __func__);
- + retval = -DWC_E_AGAIN;
- + } else {
- + if (ep->dwc_ep.num == 0) {
- + pcd->ep0state = EP0_STALL;
- + }
- +
- + ep->stopped = 1;
- + dwc_otg_ep_set_stall(GET_CORE_IF(pcd),
- + &ep->dwc_ep);
- + }
- + } else {
- + if (ep->dwc_ep.num == 0) {
- + pcd->ep0state = EP0_STALL;
- + }
- +
- + ep->stopped = 1;
- + dwc_otg_ep_set_stall(GET_CORE_IF(pcd), &ep->dwc_ep);
- + }
- + }
- +
- + DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
- +
- + return retval;
- +}
- +
- +int dwc_otg_pcd_ep_halt(dwc_otg_pcd_t * pcd, void *ep_handle, int value)
- +{
- + dwc_otg_pcd_ep_t *ep;
- + dwc_irqflags_t flags;
- + int retval = 0;
- +
- + ep = get_ep_from_handle(pcd, ep_handle);
- +
- + if (!ep || (!ep->desc && ep != &pcd->ep0) ||
- + (ep->desc && (ep->desc->bmAttributes == UE_ISOCHRONOUS))) {
- + DWC_WARN("%s, bad ep\n", __func__);
- + return -DWC_E_INVALID;
- + }
- +
- + DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
- + if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) {
- + DWC_WARN("%d %s XFer In process\n", ep->dwc_ep.num,
- + ep->dwc_ep.is_in ? "IN" : "OUT");
- + retval = -DWC_E_AGAIN;
- + } else if (value == 0) {
- + dwc_otg_ep_clear_stall(GET_CORE_IF(pcd), &ep->dwc_ep);
- + } else if (value == 1) {
- + if (ep->dwc_ep.is_in == 1 && GET_CORE_IF(pcd)->dma_desc_enable) {
- + dtxfsts_data_t txstatus;
- + fifosize_data_t txfifosize;
- +
- + txfifosize.d32 =
- + DWC_READ_REG32(&GET_CORE_IF(pcd)->core_global_regs->
- + dtxfsiz[ep->dwc_ep.tx_fifo_num]);
- + txstatus.d32 =
- + DWC_READ_REG32(&GET_CORE_IF(pcd)->dev_if->
- + in_ep_regs[ep->dwc_ep.num]->dtxfsts);
- +
- + if (txstatus.b.txfspcavail < txfifosize.b.depth) {
- + DWC_WARN("%s() Data In Tx Fifo\n", __func__);
- + retval = -DWC_E_AGAIN;
- + } else {
- + if (ep->dwc_ep.num == 0) {
- + pcd->ep0state = EP0_STALL;
- + }
- +
- + ep->stopped = 1;
- + dwc_otg_ep_set_stall(GET_CORE_IF(pcd),
- + &ep->dwc_ep);
- + }
- + } else {
- + if (ep->dwc_ep.num == 0) {
- + pcd->ep0state = EP0_STALL;
- + }
- +
- + ep->stopped = 1;
- + dwc_otg_ep_set_stall(GET_CORE_IF(pcd), &ep->dwc_ep);
- + }
- + } else if (value == 2) {
- + ep->dwc_ep.stall_clear_flag = 0;
- + } else if (value == 3) {
- + ep->dwc_ep.stall_clear_flag = 1;
- + }
- +
- + DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
- +
- + return retval;
- +}
- +
- +/**
- + * This function initiates remote wakeup of the host from suspend state.
- + */
- +void dwc_otg_pcd_rem_wkup_from_suspend(dwc_otg_pcd_t * pcd, int set)
- +{
- + dctl_data_t dctl = { 0 };
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
- + dsts_data_t dsts;
- +
- + dsts.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
- + if (!dsts.b.suspsts) {
- + DWC_WARN("Remote wakeup while is not in suspend state\n");
- + }
- + /* Check if DEVICE_REMOTE_WAKEUP feature enabled */
- + if (pcd->remote_wakeup_enable) {
- + if (set) {
- +
- + if (core_if->adp_enable) {
- + gpwrdn_data_t gpwrdn;
- +
- + dwc_otg_adp_probe_stop(core_if);
- +
- + /* Mask SRP detected interrupt from Power Down Logic */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.srp_det_msk = 1;
- + DWC_MODIFY_REG32(&core_if->
- + core_global_regs->gpwrdn,
- + gpwrdn.d32, 0);
- +
- + /* Disable Power Down Logic */
- + gpwrdn.d32 = 0;
- + gpwrdn.b.pmuactv = 1;
- + DWC_MODIFY_REG32(&core_if->
- + core_global_regs->gpwrdn,
- + gpwrdn.d32, 0);
- +
- + /*
- + * Initialize the Core for Device mode.
- + */
- + core_if->op_state = B_PERIPHERAL;
- + dwc_otg_core_init(core_if);
- + dwc_otg_enable_global_interrupts(core_if);
- + cil_pcd_start(core_if);
- +
- + dwc_otg_initiate_srp(core_if);
- + }
- +
- + dctl.b.rmtwkupsig = 1;
- + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->
- + dctl, 0, dctl.d32);
- + DWC_DEBUGPL(DBG_PCD, "Set Remote Wakeup\n");
- +
- + dwc_mdelay(2);
- + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->
- + dctl, dctl.d32, 0);
- + DWC_DEBUGPL(DBG_PCD, "Clear Remote Wakeup\n");
- + }
- + } else {
- + DWC_DEBUGPL(DBG_PCD, "Remote Wakeup is disabled\n");
- + }
- +}
- +
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- +/**
- + * This function initiates remote wakeup of the host from L1 sleep state.
- + */
- +void dwc_otg_pcd_rem_wkup_from_sleep(dwc_otg_pcd_t * pcd, int set)
- +{
- + glpmcfg_data_t lpmcfg;
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
- +
- + lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
- +
- + /* Check if we are in L1 state */
- + if (!lpmcfg.b.prt_sleep_sts) {
- + DWC_DEBUGPL(DBG_PCD, "Device is not in sleep state\n");
- + return;
- + }
- +
- + /* Check if host allows remote wakeup */
- + if (!lpmcfg.b.rem_wkup_en) {
- + DWC_DEBUGPL(DBG_PCD, "Host does not allow remote wakeup\n");
- + return;
- + }
- +
- + /* Check if Resume OK */
- + if (!lpmcfg.b.sleep_state_resumeok) {
- + DWC_DEBUGPL(DBG_PCD, "Sleep state resume is not OK\n");
- + return;
- + }
- +
- + lpmcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->glpmcfg);
- + lpmcfg.b.en_utmi_sleep = 0;
- + lpmcfg.b.hird_thres &= (~(1 << 4));
- + DWC_WRITE_REG32(&core_if->core_global_regs->glpmcfg, lpmcfg.d32);
- +
- + if (set) {
- + dctl_data_t dctl = {.d32 = 0 };
- + dctl.b.rmtwkupsig = 1;
- + /* Set RmtWkUpSig bit to start remote wakup signaling.
- + * Hardware will automatically clear this bit.
- + */
- + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl,
- + 0, dctl.d32);
- + DWC_DEBUGPL(DBG_PCD, "Set Remote Wakeup\n");
- + }
- +
- +}
- +#endif
- +
- +/**
- + * Performs remote wakeup.
- + */
- +void dwc_otg_pcd_remote_wakeup(dwc_otg_pcd_t * pcd, int set)
- +{
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
- + dwc_irqflags_t flags;
- + if (dwc_otg_is_device_mode(core_if)) {
- + DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- + if (core_if->lx_state == DWC_OTG_L1) {
- + dwc_otg_pcd_rem_wkup_from_sleep(pcd, set);
- + } else {
- +#endif
- + dwc_otg_pcd_rem_wkup_from_suspend(pcd, set);
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- + }
- +#endif
- + DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
- + }
- + return;
- +}
- +
- +void dwc_otg_pcd_disconnect_us(dwc_otg_pcd_t * pcd, int no_of_usecs)
- +{
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
- + dctl_data_t dctl = { 0 };
- +
- + if (dwc_otg_is_device_mode(core_if)) {
- + dctl.b.sftdiscon = 1;
- + DWC_PRINTF("Soft disconnect for %d useconds\n",no_of_usecs);
- + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, 0, dctl.d32);
- + dwc_udelay(no_of_usecs);
- + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32,0);
- +
- + } else{
- + DWC_PRINTF("NOT SUPPORTED IN HOST MODE\n");
- + }
- + return;
- +
- +}
- +
- +int dwc_otg_pcd_wakeup(dwc_otg_pcd_t * pcd)
- +{
- + dsts_data_t dsts;
- + gotgctl_data_t gotgctl;
- +
- + /*
- + * This function starts the Protocol if no session is in progress. If
- + * a session is already in progress, but the device is suspended,
- + * remote wakeup signaling is started.
- + */
- +
- + /* Check if valid session */
- + gotgctl.d32 =
- + DWC_READ_REG32(&(GET_CORE_IF(pcd)->core_global_regs->gotgctl));
- + if (gotgctl.b.bsesvld) {
- + /* Check if suspend state */
- + dsts.d32 =
- + DWC_READ_REG32(&
- + (GET_CORE_IF(pcd)->dev_if->
- + dev_global_regs->dsts));
- + if (dsts.b.suspsts) {
- + dwc_otg_pcd_remote_wakeup(pcd, 1);
- + }
- + } else {
- + dwc_otg_pcd_initiate_srp(pcd);
- + }
- +
- + return 0;
- +
- +}
- +
- +/**
- + * Start the SRP timer to detect when the SRP does not complete within
- + * 6 seconds.
- + *
- + * @param pcd the pcd structure.
- + */
- +void dwc_otg_pcd_initiate_srp(dwc_otg_pcd_t * pcd)
- +{
- + dwc_irqflags_t flags;
- + DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags);
- + dwc_otg_initiate_srp(GET_CORE_IF(pcd));
- + DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags);
- +}
- +
- +int dwc_otg_pcd_get_frame_number(dwc_otg_pcd_t * pcd)
- +{
- + return dwc_otg_get_frame_number(GET_CORE_IF(pcd));
- +}
- +
- +int dwc_otg_pcd_is_lpm_enabled(dwc_otg_pcd_t * pcd)
- +{
- + return GET_CORE_IF(pcd)->core_params->lpm_enable;
- +}
- +
- +uint32_t get_b_hnp_enable(dwc_otg_pcd_t * pcd)
- +{
- + return pcd->b_hnp_enable;
- +}
- +
- +uint32_t get_a_hnp_support(dwc_otg_pcd_t * pcd)
- +{
- + return pcd->a_hnp_support;
- +}
- +
- +uint32_t get_a_alt_hnp_support(dwc_otg_pcd_t * pcd)
- +{
- + return pcd->a_alt_hnp_support;
- +}
- +
- +int dwc_otg_pcd_get_rmwkup_enable(dwc_otg_pcd_t * pcd)
- +{
- + return pcd->remote_wakeup_enable;
- +}
- +
- +#endif /* DWC_HOST_ONLY */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_pcd.h
- @@ -0,0 +1,273 @@
- +/* ==========================================================================
- + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd.h $
- + * $Revision: #48 $
- + * $Date: 2012/08/10 $
- + * $Change: 2047372 $
- + *
- + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
- + * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
- + * otherwise expressly agreed to in writing between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product under
- + * any End User Software License Agreement or Agreement for Licensed Product
- + * with Synopsys or any supplement thereto. You are permitted to use and
- + * redistribute this Software in source and binary forms, with or without
- + * modification, provided that redistributions of source code must retain this
- + * notice. You may not view, use, disclose, copy or distribute this file or
- + * any information contained herein except pursuant to this license grant from
- + * Synopsys. If you do not agree with this notice, including the disclaimer
- + * below, then you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
- + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- + * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
- + * ========================================================================== */
- +#ifndef DWC_HOST_ONLY
- +#if !defined(__DWC_PCD_H__)
- +#define __DWC_PCD_H__
- +
- +#include "dwc_otg_os_dep.h"
- +#include "usb.h"
- +#include "dwc_otg_cil.h"
- +#include "dwc_otg_pcd_if.h"
- +#include "dwc_otg_driver.h"
- +
- +struct cfiobject;
- +
- +/**
- + * @file
- + *
- + * This file contains the structures, constants, and interfaces for
- + * the Perpherial Contoller Driver (PCD).
- + *
- + * The Peripheral Controller Driver (PCD) for Linux will implement the
- + * Gadget API, so that the existing Gadget drivers can be used. For
- + * the Mass Storage Function driver the File-backed USB Storage Gadget
- + * (FBS) driver will be used. The FBS driver supports the
- + * Control-Bulk (CB), Control-Bulk-Interrupt (CBI), and Bulk-Only
- + * transports.
- + *
- + */
- +
- +/** Invalid DMA Address */
- +#define DWC_DMA_ADDR_INVALID (~(dwc_dma_t)0)
- +
- +/** Max Transfer size for any EP */
- +#define DDMA_MAX_TRANSFER_SIZE 65535
- +
- +/**
- + * Get the pointer to the core_if from the pcd pointer.
- + */
- +#define GET_CORE_IF( _pcd ) (_pcd->core_if)
- +
- +/**
- + * States of EP0.
- + */
- +typedef enum ep0_state {
- + EP0_DISCONNECT, /* no host */
- + EP0_IDLE,
- + EP0_IN_DATA_PHASE,
- + EP0_OUT_DATA_PHASE,
- + EP0_IN_STATUS_PHASE,
- + EP0_OUT_STATUS_PHASE,
- + EP0_STALL,
- +} ep0state_e;
- +
- +/** Fordward declaration.*/
- +struct dwc_otg_pcd;
- +
- +/** DWC_otg iso request structure.
- + *
- + */
- +typedef struct usb_iso_request dwc_otg_pcd_iso_request_t;
- +
- +#ifdef DWC_UTE_PER_IO
- +
- +/**
- + * This shall be the exact analogy of the same type structure defined in the
- + * usb_gadget.h. Each descriptor contains
- + */
- +struct dwc_iso_pkt_desc_port {
- + uint32_t offset;
- + uint32_t length; /* expected length */
- + uint32_t actual_length;
- + uint32_t status;
- +};
- +
- +struct dwc_iso_xreq_port {
- + /** transfer/submission flag */
- + uint32_t tr_sub_flags;
- + /** Start the request ASAP */
- +#define DWC_EREQ_TF_ASAP 0x00000002
- + /** Just enqueue the request w/o initiating a transfer */
- +#define DWC_EREQ_TF_ENQUEUE 0x00000004
- +
- + /**
- + * count of ISO packets attached to this request - shall
- + * not exceed the pio_alloc_pkt_count
- + */
- + uint32_t pio_pkt_count;
- + /** count of ISO packets allocated for this request */
- + uint32_t pio_alloc_pkt_count;
- + /** number of ISO packet errors */
- + uint32_t error_count;
- + /** reserved for future extension */
- + uint32_t res;
- + /** Will be allocated and freed in the UTE gadget and based on the CFC value */
- + struct dwc_iso_pkt_desc_port *per_io_frame_descs;
- +};
- +#endif
- +/** DWC_otg request structure.
- + * This structure is a list of requests.
- + */
- +typedef struct dwc_otg_pcd_request {
- + void *priv;
- + void *buf;
- + dwc_dma_t dma;
- + uint32_t length;
- + uint32_t actual;
- + unsigned sent_zlp:1;
- + /**
- + * Used instead of original buffer if
- + * it(physical address) is not dword-aligned.
- + **/
- + uint8_t *dw_align_buf;
- + dwc_dma_t dw_align_buf_dma;
- +
- + DWC_CIRCLEQ_ENTRY(dwc_otg_pcd_request) queue_entry;
- +#ifdef DWC_UTE_PER_IO
- + struct dwc_iso_xreq_port ext_req;
- + //void *priv_ereq_nport; /* */
- +#endif
- +} dwc_otg_pcd_request_t;
- +
- +DWC_CIRCLEQ_HEAD(req_list, dwc_otg_pcd_request);
- +
- +/** PCD EP structure.
- + * This structure describes an EP, there is an array of EPs in the PCD
- + * structure.
- + */
- +typedef struct dwc_otg_pcd_ep {
- + /** USB EP Descriptor */
- + const usb_endpoint_descriptor_t *desc;
- +
- + /** queue of dwc_otg_pcd_requests. */
- + struct req_list queue;
- + unsigned stopped:1;
- + unsigned disabling:1;
- + unsigned dma:1;
- + unsigned queue_sof:1;
- +
- +#ifdef DWC_EN_ISOC
- + /** ISOC req handle passed */
- + void *iso_req_handle;
- +#endif //_EN_ISOC_
- +
- + /** DWC_otg ep data. */
- + dwc_ep_t dwc_ep;
- +
- + /** Pointer to PCD */
- + struct dwc_otg_pcd *pcd;
- +
- + void *priv;
- +} dwc_otg_pcd_ep_t;
- +
- +/** DWC_otg PCD Structure.
- + * This structure encapsulates the data for the dwc_otg PCD.
- + */
- +struct dwc_otg_pcd {
- + const struct dwc_otg_pcd_function_ops *fops;
- + /** The DWC otg device pointer */
- + struct dwc_otg_device *otg_dev;
- + /** Core Interface */
- + dwc_otg_core_if_t *core_if;
- + /** State of EP0 */
- + ep0state_e ep0state;
- + /** EP0 Request is pending */
- + unsigned ep0_pending:1;
- + /** Indicates when SET CONFIGURATION Request is in process */
- + unsigned request_config:1;
- + /** The state of the Remote Wakeup Enable. */
- + unsigned remote_wakeup_enable:1;
- + /** The state of the B-Device HNP Enable. */
- + unsigned b_hnp_enable:1;
- + /** The state of A-Device HNP Support. */
- + unsigned a_hnp_support:1;
- + /** The state of the A-Device Alt HNP support. */
- + unsigned a_alt_hnp_support:1;
- + /** Count of pending Requests */
- + unsigned request_pending;
- +
- + /** SETUP packet for EP0
- + * This structure is allocated as a DMA buffer on PCD initialization
- + * with enough space for up to 3 setup packets.
- + */
- + union {
- + usb_device_request_t req;
- + uint32_t d32[2];
- + } *setup_pkt;
- +
- + dwc_dma_t setup_pkt_dma_handle;
- +
- + /* Additional buffer and flag for CTRL_WR premature case */
- + uint8_t *backup_buf;
- + unsigned data_terminated;
- +
- + /** 2-byte dma buffer used to return status from GET_STATUS */
- + uint16_t *status_buf;
- + dwc_dma_t status_buf_dma_handle;
- +
- + /** EP0 */
- + dwc_otg_pcd_ep_t ep0;
- +
- + /** Array of IN EPs. */
- + dwc_otg_pcd_ep_t in_ep[MAX_EPS_CHANNELS - 1];
- + /** Array of OUT EPs. */
- + dwc_otg_pcd_ep_t out_ep[MAX_EPS_CHANNELS - 1];
- + /** number of valid EPs in the above array. */
- +// unsigned num_eps : 4;
- + dwc_spinlock_t *lock;
- +
- + /** Tasklet to defer starting of TEST mode transmissions until
- + * Status Phase has been completed.
- + */
- + dwc_tasklet_t *test_mode_tasklet;
- +
- + /** Tasklet to delay starting of xfer in DMA mode */
- + dwc_tasklet_t *start_xfer_tasklet;
- +
- + /** The test mode to enter when the tasklet is executed. */
- + unsigned test_mode;
- + /** The cfi_api structure that implements most of the CFI API
- + * and OTG specific core configuration functionality
- + */
- +#ifdef DWC_UTE_CFI
- + struct cfiobject *cfi;
- +#endif
- +
- +};
- +
- +static inline struct device *dwc_otg_pcd_to_dev(struct dwc_otg_pcd *pcd)
- +{
- + return &pcd->otg_dev->os_dep.platformdev->dev;
- +}
- +
- +//FIXME this functions should be static, and this prototypes should be removed
- +extern void dwc_otg_request_nuke(dwc_otg_pcd_ep_t * ep);
- +extern void dwc_otg_request_done(dwc_otg_pcd_ep_t * ep,
- + dwc_otg_pcd_request_t * req, int32_t status);
- +
- +void dwc_otg_iso_buffer_done(dwc_otg_pcd_t * pcd, dwc_otg_pcd_ep_t * ep,
- + void *req_handle);
- +
- +extern void do_test_mode(void *data);
- +#endif
- +#endif /* DWC_HOST_ONLY */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_pcd_if.h
- @@ -0,0 +1,361 @@
- +/* ==========================================================================
- + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd_if.h $
- + * $Revision: #11 $
- + * $Date: 2011/10/26 $
- + * $Change: 1873028 $
- + *
- + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
- + * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
- + * otherwise expressly agreed to in writing between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product under
- + * any End User Software License Agreement or Agreement for Licensed Product
- + * with Synopsys or any supplement thereto. You are permitted to use and
- + * redistribute this Software in source and binary forms, with or without
- + * modification, provided that redistributions of source code must retain this
- + * notice. You may not view, use, disclose, copy or distribute this file or
- + * any information contained herein except pursuant to this license grant from
- + * Synopsys. If you do not agree with this notice, including the disclaimer
- + * below, then you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
- + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- + * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
- + * ========================================================================== */
- +#ifndef DWC_HOST_ONLY
- +
- +#if !defined(__DWC_PCD_IF_H__)
- +#define __DWC_PCD_IF_H__
- +
- +//#include "dwc_os.h"
- +#include "dwc_otg_core_if.h"
- +#include "dwc_otg_driver.h"
- +
- +/** @file
- + * This file defines DWC_OTG PCD Core API.
- + */
- +
- +struct dwc_otg_pcd;
- +typedef struct dwc_otg_pcd dwc_otg_pcd_t;
- +
- +/** Maxpacket size for EP0 */
- +#define MAX_EP0_SIZE 64
- +/** Maxpacket size for any EP */
- +#define MAX_PACKET_SIZE 1024
- +
- +/** @name Function Driver Callbacks */
- +/** @{ */
- +
- +/** This function will be called whenever a previously queued request has
- + * completed. The status value will be set to -DWC_E_SHUTDOWN to indicated a
- + * failed or aborted transfer, or -DWC_E_RESTART to indicate the device was reset,
- + * or -DWC_E_TIMEOUT to indicate it timed out, or -DWC_E_INVALID to indicate invalid
- + * parameters. */
- +typedef int (*dwc_completion_cb_t) (dwc_otg_pcd_t * pcd, void *ep_handle,
- + void *req_handle, int32_t status,
- + uint32_t actual);
- +/**
- + * This function will be called whenever a previousle queued ISOC request has
- + * completed. Count of ISOC packets could be read using dwc_otg_pcd_get_iso_packet_count
- + * function.
- + * The status of each ISOC packet could be read using dwc_otg_pcd_get_iso_packet_*
- + * functions.
- + */
- +typedef int (*dwc_isoc_completion_cb_t) (dwc_otg_pcd_t * pcd, void *ep_handle,
- + void *req_handle, int proc_buf_num);
- +/** This function should handle any SETUP request that cannot be handled by the
- + * PCD Core. This includes most GET_DESCRIPTORs, SET_CONFIGS, Any
- + * class-specific requests, etc. The function must non-blocking.
- + *
- + * Returns 0 on success.
- + * Returns -DWC_E_NOT_SUPPORTED if the request is not supported.
- + * Returns -DWC_E_INVALID if the setup request had invalid parameters or bytes.
- + * Returns -DWC_E_SHUTDOWN on any other error. */
- +typedef int (*dwc_setup_cb_t) (dwc_otg_pcd_t * pcd, uint8_t * bytes);
- +/** This is called whenever the device has been disconnected. The function
- + * driver should take appropriate action to clean up all pending requests in the
- + * PCD Core, remove all endpoints (except ep0), and initialize back to reset
- + * state. */
- +typedef int (*dwc_disconnect_cb_t) (dwc_otg_pcd_t * pcd);
- +/** This function is called when device has been connected. */
- +typedef int (*dwc_connect_cb_t) (dwc_otg_pcd_t * pcd, int speed);
- +/** This function is called when device has been suspended */
- +typedef int (*dwc_suspend_cb_t) (dwc_otg_pcd_t * pcd);
- +/** This function is called when device has received LPM tokens, i.e.
- + * device has been sent to sleep state. */
- +typedef int (*dwc_sleep_cb_t) (dwc_otg_pcd_t * pcd);
- +/** This function is called when device has been resumed
- + * from suspend(L2) or L1 sleep state. */
- +typedef int (*dwc_resume_cb_t) (dwc_otg_pcd_t * pcd);
- +/** This function is called whenever hnp params has been changed.
- + * User can call get_b_hnp_enable, get_a_hnp_support, get_a_alt_hnp_support functions
- + * to get hnp parameters. */
- +typedef int (*dwc_hnp_params_changed_cb_t) (dwc_otg_pcd_t * pcd);
- +/** This function is called whenever USB RESET is detected. */
- +typedef int (*dwc_reset_cb_t) (dwc_otg_pcd_t * pcd);
- +
- +typedef int (*cfi_setup_cb_t) (dwc_otg_pcd_t * pcd, void *ctrl_req_bytes);
- +
- +/**
- + *
- + * @param ep_handle Void pointer to the usb_ep structure
- + * @param ereq_port Pointer to the extended request structure created in the
- + * portable part.
- + */
- +typedef int (*xiso_completion_cb_t) (dwc_otg_pcd_t * pcd, void *ep_handle,
- + void *req_handle, int32_t status,
- + void *ereq_port);
- +/** Function Driver Ops Data Structure */
- +struct dwc_otg_pcd_function_ops {
- + dwc_connect_cb_t connect;
- + dwc_disconnect_cb_t disconnect;
- + dwc_setup_cb_t setup;
- + dwc_completion_cb_t complete;
- + dwc_isoc_completion_cb_t isoc_complete;
- + dwc_suspend_cb_t suspend;
- + dwc_sleep_cb_t sleep;
- + dwc_resume_cb_t resume;
- + dwc_reset_cb_t reset;
- + dwc_hnp_params_changed_cb_t hnp_changed;
- + cfi_setup_cb_t cfi_setup;
- +#ifdef DWC_UTE_PER_IO
- + xiso_completion_cb_t xisoc_complete;
- +#endif
- +};
- +/** @} */
- +
- +/** @name Function Driver Functions */
- +/** @{ */
- +
- +/** Call this function to get pointer on dwc_otg_pcd_t,
- + * this pointer will be used for all PCD API functions.
- + *
- + * @param core_if The DWC_OTG Core
- + */
- +extern dwc_otg_pcd_t *dwc_otg_pcd_init(dwc_otg_device_t *otg_dev);
- +
- +/** Frees PCD allocated by dwc_otg_pcd_init
- + *
- + * @param pcd The PCD
- + */
- +extern void dwc_otg_pcd_remove(dwc_otg_pcd_t * pcd);
- +
- +/** Call this to bind the function driver to the PCD Core.
- + *
- + * @param pcd Pointer on dwc_otg_pcd_t returned by dwc_otg_pcd_init function.
- + * @param fops The Function Driver Ops data structure containing pointers to all callbacks.
- + */
- +extern void dwc_otg_pcd_start(dwc_otg_pcd_t * pcd,
- + const struct dwc_otg_pcd_function_ops *fops);
- +
- +/** Enables an endpoint for use. This function enables an endpoint in
- + * the PCD. The endpoint is described by the ep_desc which has the
- + * same format as a USB ep descriptor. The ep_handle parameter is used to refer
- + * to the endpoint from other API functions and in callbacks. Normally this
- + * should be called after a SET_CONFIGURATION/SET_INTERFACE to configure the
- + * core for that interface.
- + *
- + * Returns -DWC_E_INVALID if invalid parameters were passed.
- + * Returns -DWC_E_SHUTDOWN if any other error ocurred.
- + * Returns 0 on success.
- + *
- + * @param pcd The PCD
- + * @param ep_desc Endpoint descriptor
- + * @param usb_ep Handle on endpoint, that will be used to identify endpoint.
- + */
- +extern int dwc_otg_pcd_ep_enable(dwc_otg_pcd_t * pcd,
- + const uint8_t * ep_desc, void *usb_ep);
- +
- +/** Disable the endpoint referenced by ep_handle.
- + *
- + * Returns -DWC_E_INVALID if invalid parameters were passed.
- + * Returns -DWC_E_SHUTDOWN if any other error occurred.
- + * Returns 0 on success. */
- +extern int dwc_otg_pcd_ep_disable(dwc_otg_pcd_t * pcd, void *ep_handle);
- +
- +/** Queue a data transfer request on the endpoint referenced by ep_handle.
- + * After the transfer is completes, the complete callback will be called with
- + * the request status.
- + *
- + * @param pcd The PCD
- + * @param ep_handle The handle of the endpoint
- + * @param buf The buffer for the data
- + * @param dma_buf The DMA buffer for the data
- + * @param buflen The length of the data transfer
- + * @param zero Specifies whether to send zero length last packet.
- + * @param req_handle Set this handle to any value to use to reference this
- + * request in the ep_dequeue function or from the complete callback
- + * @param atomic_alloc If driver need to perform atomic allocations
- + * for internal data structures.
- + *
- + * Returns -DWC_E_INVALID if invalid parameters were passed.
- + * Returns -DWC_E_SHUTDOWN if any other error ocurred.
- + * Returns 0 on success. */
- +extern int dwc_otg_pcd_ep_queue(dwc_otg_pcd_t * pcd, void *ep_handle,
- + uint8_t * buf, dwc_dma_t dma_buf,
- + uint32_t buflen, int zero, void *req_handle,
- + int atomic_alloc);
- +#ifdef DWC_UTE_PER_IO
- +/**
- + *
- + * @param ereq_nonport Pointer to the extended request part of the
- + * usb_request structure defined in usb_gadget.h file.
- + */
- +extern int dwc_otg_pcd_xiso_ep_queue(dwc_otg_pcd_t * pcd, void *ep_handle,
- + uint8_t * buf, dwc_dma_t dma_buf,
- + uint32_t buflen, int zero,
- + void *req_handle, int atomic_alloc,
- + void *ereq_nonport);
- +
- +#endif
- +
- +/** De-queue the specified data transfer that has not yet completed.
- + *
- + * Returns -DWC_E_INVALID if invalid parameters were passed.
- + * Returns -DWC_E_SHUTDOWN if any other error ocurred.
- + * Returns 0 on success. */
- +extern int dwc_otg_pcd_ep_dequeue(dwc_otg_pcd_t * pcd, void *ep_handle,
- + void *req_handle);
- +
- +/** Halt (STALL) an endpoint or clear it.
- + *
- + * Returns -DWC_E_INVALID if invalid parameters were passed.
- + * Returns -DWC_E_SHUTDOWN if any other error ocurred.
- + * Returns -DWC_E_AGAIN if the STALL cannot be sent and must be tried again later
- + * Returns 0 on success. */
- +extern int dwc_otg_pcd_ep_halt(dwc_otg_pcd_t * pcd, void *ep_handle, int value);
- +
- +/** This function */
- +extern int dwc_otg_pcd_ep_wedge(dwc_otg_pcd_t * pcd, void *ep_handle);
- +
- +/** This function should be called on every hardware interrupt */
- +extern int32_t dwc_otg_pcd_handle_intr(dwc_otg_pcd_t * pcd);
- +
- +/** This function returns current frame number */
- +extern int dwc_otg_pcd_get_frame_number(dwc_otg_pcd_t * pcd);
- +
- +/**
- + * Start isochronous transfers on the endpoint referenced by ep_handle.
- + * For isochronous transfers duble buffering is used.
- + * After processing each of buffers comlete callback will be called with
- + * status for each transaction.
- + *
- + * @param pcd The PCD
- + * @param ep_handle The handle of the endpoint
- + * @param buf0 The virtual address of first data buffer
- + * @param buf1 The virtual address of second data buffer
- + * @param dma0 The DMA address of first data buffer
- + * @param dma1 The DMA address of second data buffer
- + * @param sync_frame Data pattern frame number
- + * @param dp_frame Data size for pattern frame
- + * @param data_per_frame Data size for regular frame
- + * @param start_frame Frame number to start transfers, if -1 then start transfers ASAP.
- + * @param buf_proc_intrvl Interval of ISOC Buffer processing
- + * @param req_handle Handle of ISOC request
- + * @param atomic_alloc Specefies whether to perform atomic allocation for
- + * internal data structures.
- + *
- + * Returns -DWC_E_NO_MEMORY if there is no enough memory.
- + * Returns -DWC_E_INVALID if incorrect arguments are passed to the function.
- + * Returns -DW_E_SHUTDOWN for any other error.
- + * Returns 0 on success
- + */
- +extern int dwc_otg_pcd_iso_ep_start(dwc_otg_pcd_t * pcd, void *ep_handle,
- + uint8_t * buf0, uint8_t * buf1,
- + dwc_dma_t dma0, dwc_dma_t dma1,
- + int sync_frame, int dp_frame,
- + int data_per_frame, int start_frame,
- + int buf_proc_intrvl, void *req_handle,
- + int atomic_alloc);
- +
- +/** Stop ISOC transfers on endpoint referenced by ep_handle.
- + *
- + * @param pcd The PCD
- + * @param ep_handle The handle of the endpoint
- + * @param req_handle Handle of ISOC request
- + *
- + * Returns -DWC_E_INVALID if incorrect arguments are passed to the function
- + * Returns 0 on success
- + */
- +int dwc_otg_pcd_iso_ep_stop(dwc_otg_pcd_t * pcd, void *ep_handle,
- + void *req_handle);
- +
- +/** Get ISOC packet status.
- + *
- + * @param pcd The PCD
- + * @param ep_handle The handle of the endpoint
- + * @param iso_req_handle Isochronoush request handle
- + * @param packet Number of packet
- + * @param status Out parameter for returning status
- + * @param actual Out parameter for returning actual length
- + * @param offset Out parameter for returning offset
- + *
- + */
- +extern void dwc_otg_pcd_get_iso_packet_params(dwc_otg_pcd_t * pcd,
- + void *ep_handle,
- + void *iso_req_handle, int packet,
- + int *status, int *actual,
- + int *offset);
- +
- +/** Get ISOC packet count.
- + *
- + * @param pcd The PCD
- + * @param ep_handle The handle of the endpoint
- + * @param iso_req_handle
- + */
- +extern int dwc_otg_pcd_get_iso_packet_count(dwc_otg_pcd_t * pcd,
- + void *ep_handle,
- + void *iso_req_handle);
- +
- +/** This function starts the SRP Protocol if no session is in progress. If
- + * a session is already in progress, but the device is suspended,
- + * remote wakeup signaling is started.
- + */
- +extern int dwc_otg_pcd_wakeup(dwc_otg_pcd_t * pcd);
- +
- +/** This function returns 1 if LPM support is enabled, and 0 otherwise. */
- +extern int dwc_otg_pcd_is_lpm_enabled(dwc_otg_pcd_t * pcd);
- +
- +/** This function returns 1 if remote wakeup is allowed and 0, otherwise. */
- +extern int dwc_otg_pcd_get_rmwkup_enable(dwc_otg_pcd_t * pcd);
- +
- +/** Initiate SRP */
- +extern void dwc_otg_pcd_initiate_srp(dwc_otg_pcd_t * pcd);
- +
- +/** Starts remote wakeup signaling. */
- +extern void dwc_otg_pcd_remote_wakeup(dwc_otg_pcd_t * pcd, int set);
- +
- +/** Starts micorsecond soft disconnect. */
- +extern void dwc_otg_pcd_disconnect_us(dwc_otg_pcd_t * pcd, int no_of_usecs);
- +/** This function returns whether device is dualspeed.*/
- +extern uint32_t dwc_otg_pcd_is_dualspeed(dwc_otg_pcd_t * pcd);
- +
- +/** This function returns whether device is otg. */
- +extern uint32_t dwc_otg_pcd_is_otg(dwc_otg_pcd_t * pcd);
- +
- +/** These functions allow to get hnp parameters */
- +extern uint32_t get_b_hnp_enable(dwc_otg_pcd_t * pcd);
- +extern uint32_t get_a_hnp_support(dwc_otg_pcd_t * pcd);
- +extern uint32_t get_a_alt_hnp_support(dwc_otg_pcd_t * pcd);
- +
- +/** CFI specific Interface functions */
- +/** Allocate a cfi buffer */
- +extern uint8_t *cfiw_ep_alloc_buffer(dwc_otg_pcd_t * pcd, void *pep,
- + dwc_dma_t * addr, size_t buflen,
- + int flags);
- +
- +/******************************************************************************/
- +
- +/** @} */
- +
- +#endif /* __DWC_PCD_IF_H__ */
- +
- +#endif /* DWC_HOST_ONLY */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c
- @@ -0,0 +1,5148 @@
- +/* ==========================================================================
- + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd_intr.c $
- + * $Revision: #116 $
- + * $Date: 2012/08/10 $
- + * $Change: 2047372 $
- + *
- + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
- + * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
- + * otherwise expressly agreed to in writing between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product under
- + * any End User Software License Agreement or Agreement for Licensed Product
- + * with Synopsys or any supplement thereto. You are permitted to use and
- + * redistribute this Software in source and binary forms, with or without
- + * modification, provided that redistributions of source code must retain this
- + * notice. You may not view, use, disclose, copy or distribute this file or
- + * any information contained herein except pursuant to this license grant from
- + * Synopsys. If you do not agree with this notice, including the disclaimer
- + * below, then you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
- + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- + * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
- + * ========================================================================== */
- +#ifndef DWC_HOST_ONLY
- +
- +#include "dwc_otg_pcd.h"
- +
- +#ifdef DWC_UTE_CFI
- +#include "dwc_otg_cfi.h"
- +#endif
- +
- +#ifdef DWC_UTE_PER_IO
- +extern void complete_xiso_ep(dwc_otg_pcd_ep_t * ep);
- +#endif
- +//#define PRINT_CFI_DMA_DESCS
- +
- +#define DEBUG_EP0
- +
- +/**
- + * This function updates OTG.
- + */
- +static void dwc_otg_pcd_update_otg(dwc_otg_pcd_t * pcd, const unsigned reset)
- +{
- +
- + if (reset) {
- + pcd->b_hnp_enable = 0;
- + pcd->a_hnp_support = 0;
- + pcd->a_alt_hnp_support = 0;
- + }
- +
- + if (pcd->fops->hnp_changed) {
- + pcd->fops->hnp_changed(pcd);
- + }
- +}
- +
- +/** @file
- + * This file contains the implementation of the PCD Interrupt handlers.
- + *
- + * The PCD handles the device interrupts. Many conditions can cause a
- + * device interrupt. When an interrupt occurs, the device interrupt
- + * service routine determines the cause of the interrupt and
- + * dispatches handling to the appropriate function. These interrupt
- + * handling functions are described below.
- + * All interrupt registers are processed from LSB to MSB.
- + */
- +
- +/**
- + * This function prints the ep0 state for debug purposes.
- + */
- +static inline void print_ep0_state(dwc_otg_pcd_t * pcd)
- +{
- +#ifdef DEBUG
- + char str[40];
- +
- + switch (pcd->ep0state) {
- + case EP0_DISCONNECT:
- + dwc_strcpy(str, "EP0_DISCONNECT");
- + break;
- + case EP0_IDLE:
- + dwc_strcpy(str, "EP0_IDLE");
- + break;
- + case EP0_IN_DATA_PHASE:
- + dwc_strcpy(str, "EP0_IN_DATA_PHASE");
- + break;
- + case EP0_OUT_DATA_PHASE:
- + dwc_strcpy(str, "EP0_OUT_DATA_PHASE");
- + break;
- + case EP0_IN_STATUS_PHASE:
- + dwc_strcpy(str, "EP0_IN_STATUS_PHASE");
- + break;
- + case EP0_OUT_STATUS_PHASE:
- + dwc_strcpy(str, "EP0_OUT_STATUS_PHASE");
- + break;
- + case EP0_STALL:
- + dwc_strcpy(str, "EP0_STALL");
- + break;
- + default:
- + dwc_strcpy(str, "EP0_INVALID");
- + }
- +
- + DWC_DEBUGPL(DBG_ANY, "%s(%d)\n", str, pcd->ep0state);
- +#endif
- +}
- +
- +/**
- + * This function calculate the size of the payload in the memory
- + * for out endpoints and prints size for debug purposes(used in
- + * 2.93a DevOutNak feature).
- + */
- +static inline void print_memory_payload(dwc_otg_pcd_t * pcd, dwc_ep_t * ep)
- +{
- +#ifdef DEBUG
- + deptsiz_data_t deptsiz_init = {.d32 = 0 };
- + deptsiz_data_t deptsiz_updt = {.d32 = 0 };
- + int pack_num;
- + unsigned payload;
- +
- + deptsiz_init.d32 = pcd->core_if->start_doeptsiz_val[ep->num];
- + deptsiz_updt.d32 =
- + DWC_READ_REG32(&pcd->core_if->dev_if->
- + out_ep_regs[ep->num]->doeptsiz);
- + /* Payload will be */
- + payload = deptsiz_init.b.xfersize - deptsiz_updt.b.xfersize;
- + /* Packet count is decremented every time a packet
- + * is written to the RxFIFO not in to the external memory
- + * So, if payload == 0, then it means no packet was sent to ext memory*/
- + pack_num = (!payload) ? 0 : (deptsiz_init.b.pktcnt - deptsiz_updt.b.pktcnt);
- + DWC_DEBUGPL(DBG_PCDV,
- + "Payload for EP%d-%s\n",
- + ep->num, (ep->is_in ? "IN" : "OUT"));
- + DWC_DEBUGPL(DBG_PCDV,
- + "Number of transfered bytes = 0x%08x\n", payload);
- + DWC_DEBUGPL(DBG_PCDV,
- + "Number of transfered packets = %d\n", pack_num);
- +#endif
- +}
- +
- +
- +#ifdef DWC_UTE_CFI
- +static inline void print_desc(struct dwc_otg_dma_desc *ddesc,
- + const uint8_t * epname, int descnum)
- +{
- + CFI_INFO
- + ("%s DMA_DESC(%d) buf=0x%08x bytes=0x%04x; sp=0x%x; l=0x%x; sts=0x%02x; bs=0x%02x\n",
- + epname, descnum, ddesc->buf, ddesc->status.b.bytes,
- + ddesc->status.b.sp, ddesc->status.b.l, ddesc->status.b.sts,
- + ddesc->status.b.bs);
- +}
- +#endif
- +
- +/**
- + * This function returns pointer to in ep struct with number ep_num
- + */
- +static inline dwc_otg_pcd_ep_t *get_in_ep(dwc_otg_pcd_t * pcd, uint32_t ep_num)
- +{
- + int i;
- + int num_in_eps = GET_CORE_IF(pcd)->dev_if->num_in_eps;
- + if (ep_num == 0) {
- + return &pcd->ep0;
- + } else {
- + for (i = 0; i < num_in_eps; ++i) {
- + if (pcd->in_ep[i].dwc_ep.num == ep_num)
- + return &pcd->in_ep[i];
- + }
- + return 0;
- + }
- +}
- +
- +/**
- + * This function returns pointer to out ep struct with number ep_num
- + */
- +static inline dwc_otg_pcd_ep_t *get_out_ep(dwc_otg_pcd_t * pcd, uint32_t ep_num)
- +{
- + int i;
- + int num_out_eps = GET_CORE_IF(pcd)->dev_if->num_out_eps;
- + if (ep_num == 0) {
- + return &pcd->ep0;
- + } else {
- + for (i = 0; i < num_out_eps; ++i) {
- + if (pcd->out_ep[i].dwc_ep.num == ep_num)
- + return &pcd->out_ep[i];
- + }
- + return 0;
- + }
- +}
- +
- +/**
- + * This functions gets a pointer to an EP from the wIndex address
- + * value of the control request.
- + */
- +dwc_otg_pcd_ep_t *get_ep_by_addr(dwc_otg_pcd_t * pcd, u16 wIndex)
- +{
- + dwc_otg_pcd_ep_t *ep;
- + uint32_t ep_num = UE_GET_ADDR(wIndex);
- +
- + if (ep_num == 0) {
- + ep = &pcd->ep0;
- + } else if (UE_GET_DIR(wIndex) == UE_DIR_IN) { /* in ep */
- + ep = &pcd->in_ep[ep_num - 1];
- + } else {
- + ep = &pcd->out_ep[ep_num - 1];
- + }
- +
- + return ep;
- +}
- +
- +/**
- + * This function checks the EP request queue, if the queue is not
- + * empty the next request is started.
- + */
- +void start_next_request(dwc_otg_pcd_ep_t * ep)
- +{
- + dwc_otg_pcd_request_t *req = 0;
- + uint32_t max_transfer =
- + GET_CORE_IF(ep->pcd)->core_params->max_transfer_size;
- +
- +#ifdef DWC_UTE_CFI
- + struct dwc_otg_pcd *pcd;
- + pcd = ep->pcd;
- +#endif
- +
- + if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) {
- + req = DWC_CIRCLEQ_FIRST(&ep->queue);
- +
- +#ifdef DWC_UTE_CFI
- + if (ep->dwc_ep.buff_mode != BM_STANDARD) {
- + ep->dwc_ep.cfi_req_len = req->length;
- + pcd->cfi->ops.build_descriptors(pcd->cfi, pcd, ep, req);
- + } else {
- +#endif
- + /* Setup and start the Transfer */
- + if (req->dw_align_buf) {
- + ep->dwc_ep.dma_addr = req->dw_align_buf_dma;
- + ep->dwc_ep.start_xfer_buff = req->dw_align_buf;
- + ep->dwc_ep.xfer_buff = req->dw_align_buf;
- + } else {
- + ep->dwc_ep.dma_addr = req->dma;
- + ep->dwc_ep.start_xfer_buff = req->buf;
- + ep->dwc_ep.xfer_buff = req->buf;
- + }
- + ep->dwc_ep.sent_zlp = 0;
- + ep->dwc_ep.total_len = req->length;
- + ep->dwc_ep.xfer_len = 0;
- + ep->dwc_ep.xfer_count = 0;
- +
- + ep->dwc_ep.maxxfer = max_transfer;
- + if (GET_CORE_IF(ep->pcd)->dma_desc_enable) {
- + uint32_t out_max_xfer = DDMA_MAX_TRANSFER_SIZE
- + - (DDMA_MAX_TRANSFER_SIZE % 4);
- + if (ep->dwc_ep.is_in) {
- + if (ep->dwc_ep.maxxfer >
- + DDMA_MAX_TRANSFER_SIZE) {
- + ep->dwc_ep.maxxfer =
- + DDMA_MAX_TRANSFER_SIZE;
- + }
- + } else {
- + if (ep->dwc_ep.maxxfer > out_max_xfer) {
- + ep->dwc_ep.maxxfer =
- + out_max_xfer;
- + }
- + }
- + }
- + if (ep->dwc_ep.maxxfer < ep->dwc_ep.total_len) {
- + ep->dwc_ep.maxxfer -=
- + (ep->dwc_ep.maxxfer % ep->dwc_ep.maxpacket);
- + }
- + if (req->sent_zlp) {
- + if ((ep->dwc_ep.total_len %
- + ep->dwc_ep.maxpacket == 0)
- + && (ep->dwc_ep.total_len != 0)) {
- + ep->dwc_ep.sent_zlp = 1;
- + }
- +
- + }
- +#ifdef DWC_UTE_CFI
- + }
- +#endif
- + dwc_otg_ep_start_transfer(GET_CORE_IF(ep->pcd), &ep->dwc_ep);
- + } else if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) {
- + DWC_PRINTF("There are no more ISOC requests \n");
- + ep->dwc_ep.frame_num = 0xFFFFFFFF;
- + }
- +}
- +
- +/**
- + * This function handles the SOF Interrupts. At this time the SOF
- + * Interrupt is disabled.
- + */
- +int32_t dwc_otg_pcd_handle_sof_intr(dwc_otg_pcd_t * pcd)
- +{
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
- +
- + gintsts_data_t gintsts;
- +
- + DWC_DEBUGPL(DBG_PCD, "SOF\n");
- +
- + /* Clear interrupt */
- + gintsts.d32 = 0;
- + gintsts.b.sofintr = 1;
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
- +
- + return 1;
- +}
- +
- +/**
- + * This function handles the Rx Status Queue Level Interrupt, which
- + * indicates that there is a least one packet in the Rx FIFO. The
- + * packets are moved from the FIFO to memory, where they will be
- + * processed when the Endpoint Interrupt Register indicates Transfer
- + * Complete or SETUP Phase Done.
- + *
- + * Repeat the following until the Rx Status Queue is empty:
- + * -# Read the Receive Status Pop Register (GRXSTSP) to get Packet
- + * info
- + * -# If Receive FIFO is empty then skip to step Clear the interrupt
- + * and exit
- + * -# If SETUP Packet call dwc_otg_read_setup_packet to copy the
- + * SETUP data to the buffer
- + * -# If OUT Data Packet call dwc_otg_read_packet to copy the data
- + * to the destination buffer
- + */
- +int32_t dwc_otg_pcd_handle_rx_status_q_level_intr(dwc_otg_pcd_t * pcd)
- +{
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
- + dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
- + gintmsk_data_t gintmask = {.d32 = 0 };
- + device_grxsts_data_t status;
- + dwc_otg_pcd_ep_t *ep;
- + gintsts_data_t gintsts;
- +#ifdef DEBUG
- + static char *dpid_str[] = { "D0", "D2", "D1", "MDATA" };
- +#endif
- +
- + //DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _pcd);
- + /* Disable the Rx Status Queue Level interrupt */
- + gintmask.b.rxstsqlvl = 1;
- + DWC_MODIFY_REG32(&global_regs->gintmsk, gintmask.d32, 0);
- +
- + /* Get the Status from the top of the FIFO */
- + status.d32 = DWC_READ_REG32(&global_regs->grxstsp);
- +
- + DWC_DEBUGPL(DBG_PCD, "EP:%d BCnt:%d DPID:%s "
- + "pktsts:%x Frame:%d(0x%0x)\n",
- + status.b.epnum, status.b.bcnt,
- + dpid_str[status.b.dpid],
- + status.b.pktsts, status.b.fn, status.b.fn);
- + /* Get pointer to EP structure */
- + ep = get_out_ep(pcd, status.b.epnum);
- +
- + switch (status.b.pktsts) {
- + case DWC_DSTS_GOUT_NAK:
- + DWC_DEBUGPL(DBG_PCDV, "Global OUT NAK\n");
- + break;
- + case DWC_STS_DATA_UPDT:
- + DWC_DEBUGPL(DBG_PCDV, "OUT Data Packet\n");
- + if (status.b.bcnt && ep->dwc_ep.xfer_buff) {
- + /** @todo NGS Check for buffer overflow? */
- + dwc_otg_read_packet(core_if,
- + ep->dwc_ep.xfer_buff,
- + status.b.bcnt);
- + ep->dwc_ep.xfer_count += status.b.bcnt;
- + ep->dwc_ep.xfer_buff += status.b.bcnt;
- + }
- + break;
- + case DWC_STS_XFER_COMP:
- + DWC_DEBUGPL(DBG_PCDV, "OUT Complete\n");
- + break;
- + case DWC_DSTS_SETUP_COMP:
- +#ifdef DEBUG_EP0
- + DWC_DEBUGPL(DBG_PCDV, "Setup Complete\n");
- +#endif
- + break;
- + case DWC_DSTS_SETUP_UPDT:
- + dwc_otg_read_setup_packet(core_if, pcd->setup_pkt->d32);
- +#ifdef DEBUG_EP0
- + DWC_DEBUGPL(DBG_PCD,
- + "SETUP PKT: %02x.%02x v%04x i%04x l%04x\n",
- + pcd->setup_pkt->req.bmRequestType,
- + pcd->setup_pkt->req.bRequest,
- + UGETW(pcd->setup_pkt->req.wValue),
- + UGETW(pcd->setup_pkt->req.wIndex),
- + UGETW(pcd->setup_pkt->req.wLength));
- +#endif
- + ep->dwc_ep.xfer_count += status.b.bcnt;
- + break;
- + default:
- + DWC_DEBUGPL(DBG_PCDV, "Invalid Packet Status (0x%0x)\n",
- + status.b.pktsts);
- + break;
- + }
- +
- + /* Enable the Rx Status Queue Level interrupt */
- + DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmask.d32);
- + /* Clear interrupt */
- + gintsts.d32 = 0;
- + gintsts.b.rxstsqlvl = 1;
- + DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
- +
- + //DWC_DEBUGPL(DBG_PCDV, "EXIT: %s\n", __func__);
- + return 1;
- +}
- +
- +/**
- + * This function examines the Device IN Token Learning Queue to
- + * determine the EP number of the last IN token received. This
- + * implementation is for the Mass Storage device where there are only
- + * 2 IN EPs (Control-IN and BULK-IN).
- + *
- + * The EP numbers for the first six IN Tokens are in DTKNQR1 and there
- + * are 8 EP Numbers in each of the other possible DTKNQ Registers.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + *
- + */
- +static inline int get_ep_of_last_in_token(dwc_otg_core_if_t * core_if)
- +{
- + dwc_otg_device_global_regs_t *dev_global_regs =
- + core_if->dev_if->dev_global_regs;
- + const uint32_t TOKEN_Q_DEPTH = core_if->hwcfg2.b.dev_token_q_depth;
- + /* Number of Token Queue Registers */
- + const int DTKNQ_REG_CNT = (TOKEN_Q_DEPTH + 7) / 8;
- + dtknq1_data_t dtknqr1;
- + uint32_t in_tkn_epnums[4];
- + int ndx = 0;
- + int i = 0;
- + volatile uint32_t *addr = &dev_global_regs->dtknqr1;
- + int epnum = 0;
- +
- + //DWC_DEBUGPL(DBG_PCD,"dev_token_q_depth=%d\n",TOKEN_Q_DEPTH);
- +
- + /* Read the DTKNQ Registers */
- + for (i = 0; i < DTKNQ_REG_CNT; i++) {
- + in_tkn_epnums[i] = DWC_READ_REG32(addr);
- + DWC_DEBUGPL(DBG_PCDV, "DTKNQR%d=0x%08x\n", i + 1,
- + in_tkn_epnums[i]);
- + if (addr == &dev_global_regs->dvbusdis) {
- + addr = &dev_global_regs->dtknqr3_dthrctl;
- + } else {
- + ++addr;
- + }
- +
- + }
- +
- + /* Copy the DTKNQR1 data to the bit field. */
- + dtknqr1.d32 = in_tkn_epnums[0];
- + /* Get the EP numbers */
- + in_tkn_epnums[0] = dtknqr1.b.epnums0_5;
- + ndx = dtknqr1.b.intknwptr - 1;
- +
- + //DWC_DEBUGPL(DBG_PCDV,"ndx=%d\n",ndx);
- + if (ndx == -1) {
- + /** @todo Find a simpler way to calculate the max
- + * queue position.*/
- + int cnt = TOKEN_Q_DEPTH;
- + if (TOKEN_Q_DEPTH <= 6) {
- + cnt = TOKEN_Q_DEPTH - 1;
- + } else if (TOKEN_Q_DEPTH <= 14) {
- + cnt = TOKEN_Q_DEPTH - 7;
- + } else if (TOKEN_Q_DEPTH <= 22) {
- + cnt = TOKEN_Q_DEPTH - 15;
- + } else {
- + cnt = TOKEN_Q_DEPTH - 23;
- + }
- + epnum = (in_tkn_epnums[DTKNQ_REG_CNT - 1] >> (cnt * 4)) & 0xF;
- + } else {
- + if (ndx <= 5) {
- + epnum = (in_tkn_epnums[0] >> (ndx * 4)) & 0xF;
- + } else if (ndx <= 13) {
- + ndx -= 6;
- + epnum = (in_tkn_epnums[1] >> (ndx * 4)) & 0xF;
- + } else if (ndx <= 21) {
- + ndx -= 14;
- + epnum = (in_tkn_epnums[2] >> (ndx * 4)) & 0xF;
- + } else if (ndx <= 29) {
- + ndx -= 22;
- + epnum = (in_tkn_epnums[3] >> (ndx * 4)) & 0xF;
- + }
- + }
- + //DWC_DEBUGPL(DBG_PCD,"epnum=%d\n",epnum);
- + return epnum;
- +}
- +
- +/**
- + * This interrupt occurs when the non-periodic Tx FIFO is half-empty.
- + * The active request is checked for the next packet to be loaded into
- + * the non-periodic Tx FIFO.
- + */
- +int32_t dwc_otg_pcd_handle_np_tx_fifo_empty_intr(dwc_otg_pcd_t * pcd)
- +{
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
- + dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
- + dwc_otg_dev_in_ep_regs_t *ep_regs;
- + gnptxsts_data_t txstatus = {.d32 = 0 };
- + gintsts_data_t gintsts;
- +
- + int epnum = 0;
- + dwc_otg_pcd_ep_t *ep = 0;
- + uint32_t len = 0;
- + int dwords;
- +
- + /* Get the epnum from the IN Token Learning Queue. */
- + epnum = get_ep_of_last_in_token(core_if);
- + ep = get_in_ep(pcd, epnum);
- +
- + DWC_DEBUGPL(DBG_PCD, "NP TxFifo Empty: %d \n", epnum);
- +
- + ep_regs = core_if->dev_if->in_ep_regs[epnum];
- +
- + len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
- + if (len > ep->dwc_ep.maxpacket) {
- + len = ep->dwc_ep.maxpacket;
- + }
- + dwords = (len + 3) / 4;
- +
- + /* While there is space in the queue and space in the FIFO and
- + * More data to tranfer, Write packets to the Tx FIFO */
- + txstatus.d32 = DWC_READ_REG32(&global_regs->gnptxsts);
- + DWC_DEBUGPL(DBG_PCDV, "b4 GNPTXSTS=0x%08x\n", txstatus.d32);
- +
- + while (txstatus.b.nptxqspcavail > 0 &&
- + txstatus.b.nptxfspcavail > dwords &&
- + ep->dwc_ep.xfer_count < ep->dwc_ep.xfer_len) {
- + /* Write the FIFO */
- + dwc_otg_ep_write_packet(core_if, &ep->dwc_ep, 0);
- + len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
- +
- + if (len > ep->dwc_ep.maxpacket) {
- + len = ep->dwc_ep.maxpacket;
- + }
- +
- + dwords = (len + 3) / 4;
- + txstatus.d32 = DWC_READ_REG32(&global_regs->gnptxsts);
- + DWC_DEBUGPL(DBG_PCDV, "GNPTXSTS=0x%08x\n", txstatus.d32);
- + }
- +
- + DWC_DEBUGPL(DBG_PCDV, "GNPTXSTS=0x%08x\n",
- + DWC_READ_REG32(&global_regs->gnptxsts));
- +
- + /* Clear interrupt */
- + gintsts.d32 = 0;
- + gintsts.b.nptxfempty = 1;
- + DWC_WRITE_REG32(&global_regs->gintsts, gintsts.d32);
- +
- + return 1;
- +}
- +
- +/**
- + * This function is called when dedicated Tx FIFO Empty interrupt occurs.
- + * The active request is checked for the next packet to be loaded into
- + * apropriate Tx FIFO.
- + */
- +static int32_t write_empty_tx_fifo(dwc_otg_pcd_t * pcd, uint32_t epnum)
- +{
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
- + dwc_otg_dev_if_t *dev_if = core_if->dev_if;
- + dwc_otg_dev_in_ep_regs_t *ep_regs;
- + dtxfsts_data_t txstatus = {.d32 = 0 };
- + dwc_otg_pcd_ep_t *ep = 0;
- + uint32_t len = 0;
- + int dwords;
- +
- + ep = get_in_ep(pcd, epnum);
- +
- + DWC_DEBUGPL(DBG_PCD, "Dedicated TxFifo Empty: %d \n", epnum);
- +
- + ep_regs = core_if->dev_if->in_ep_regs[epnum];
- +
- + len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
- +
- + if (len > ep->dwc_ep.maxpacket) {
- + len = ep->dwc_ep.maxpacket;
- + }
- +
- + dwords = (len + 3) / 4;
- +
- + /* While there is space in the queue and space in the FIFO and
- + * More data to tranfer, Write packets to the Tx FIFO */
- + txstatus.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dtxfsts);
- + DWC_DEBUGPL(DBG_PCDV, "b4 dtxfsts[%d]=0x%08x\n", epnum, txstatus.d32);
- +
- + while (txstatus.b.txfspcavail > dwords &&
- + ep->dwc_ep.xfer_count < ep->dwc_ep.xfer_len &&
- + ep->dwc_ep.xfer_len != 0) {
- + /* Write the FIFO */
- + dwc_otg_ep_write_packet(core_if, &ep->dwc_ep, 0);
- +
- + len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;
- + if (len > ep->dwc_ep.maxpacket) {
- + len = ep->dwc_ep.maxpacket;
- + }
- +
- + dwords = (len + 3) / 4;
- + txstatus.d32 =
- + DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dtxfsts);
- + DWC_DEBUGPL(DBG_PCDV, "dtxfsts[%d]=0x%08x\n", epnum,
- + txstatus.d32);
- + }
- +
- + DWC_DEBUGPL(DBG_PCDV, "b4 dtxfsts[%d]=0x%08x\n", epnum,
- + DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dtxfsts));
- +
- + return 1;
- +}
- +
- +/**
- + * This function is called when the Device is disconnected. It stops
- + * any active requests and informs the Gadget driver of the
- + * disconnect.
- + */
- +void dwc_otg_pcd_stop(dwc_otg_pcd_t * pcd)
- +{
- + int i, num_in_eps, num_out_eps;
- + dwc_otg_pcd_ep_t *ep;
- +
- + gintmsk_data_t intr_mask = {.d32 = 0 };
- +
- + DWC_SPINLOCK(pcd->lock);
- +
- + num_in_eps = GET_CORE_IF(pcd)->dev_if->num_in_eps;
- + num_out_eps = GET_CORE_IF(pcd)->dev_if->num_out_eps;
- +
- + DWC_DEBUGPL(DBG_PCDV, "%s() \n", __func__);
- + /* don't disconnect drivers more than once */
- + if (pcd->ep0state == EP0_DISCONNECT) {
- + DWC_DEBUGPL(DBG_ANY, "%s() Already Disconnected\n", __func__);
- + DWC_SPINUNLOCK(pcd->lock);
- + return;
- + }
- + pcd->ep0state = EP0_DISCONNECT;
- +
- + /* Reset the OTG state. */
- + dwc_otg_pcd_update_otg(pcd, 1);
- +
- + /* Disable the NP Tx Fifo Empty Interrupt. */
- + intr_mask.b.nptxfempty = 1;
- + DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
- + intr_mask.d32, 0);
- +
- + /* Flush the FIFOs */
- + /**@todo NGS Flush Periodic FIFOs */
- + dwc_otg_flush_tx_fifo(GET_CORE_IF(pcd), 0x10);
- + dwc_otg_flush_rx_fifo(GET_CORE_IF(pcd));
- +
- + /* prevent new request submissions, kill any outstanding requests */
- + ep = &pcd->ep0;
- + dwc_otg_request_nuke(ep);
- + /* prevent new request submissions, kill any outstanding requests */
- + for (i = 0; i < num_in_eps; i++) {
- + dwc_otg_pcd_ep_t *ep = &pcd->in_ep[i];
- + dwc_otg_request_nuke(ep);
- + }
- + /* prevent new request submissions, kill any outstanding requests */
- + for (i = 0; i < num_out_eps; i++) {
- + dwc_otg_pcd_ep_t *ep = &pcd->out_ep[i];
- + dwc_otg_request_nuke(ep);
- + }
- +
- + /* report disconnect; the driver is already quiesced */
- + if (pcd->fops->disconnect) {
- + DWC_SPINUNLOCK(pcd->lock);
- + pcd->fops->disconnect(pcd);
- + DWC_SPINLOCK(pcd->lock);
- + }
- + DWC_SPINUNLOCK(pcd->lock);
- +}
- +
- +/**
- + * This interrupt indicates that ...
- + */
- +int32_t dwc_otg_pcd_handle_i2c_intr(dwc_otg_pcd_t * pcd)
- +{
- + gintmsk_data_t intr_mask = {.d32 = 0 };
- + gintsts_data_t gintsts;
- +
- + DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", "i2cintr");
- + intr_mask.b.i2cintr = 1;
- + DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
- + intr_mask.d32, 0);
- +
- + /* Clear interrupt */
- + gintsts.d32 = 0;
- + gintsts.b.i2cintr = 1;
- + DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
- + gintsts.d32);
- + return 1;
- +}
- +
- +/**
- + * This interrupt indicates that ...
- + */
- +int32_t dwc_otg_pcd_handle_early_suspend_intr(dwc_otg_pcd_t * pcd)
- +{
- + gintsts_data_t gintsts;
- +#if defined(VERBOSE)
- + DWC_PRINTF("Early Suspend Detected\n");
- +#endif
- +
- + /* Clear interrupt */
- + gintsts.d32 = 0;
- + gintsts.b.erlysuspend = 1;
- + DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
- + gintsts.d32);
- + return 1;
- +}
- +
- +/**
- + * This function configures EPO to receive SETUP packets.
- + *
- + * @todo NGS: Update the comments from the HW FS.
- + *
- + * -# Program the following fields in the endpoint specific registers
- + * for Control OUT EP 0, in order to receive a setup packet
- + * - DOEPTSIZ0.Packet Count = 3 (To receive up to 3 back to back
- + * setup packets)
- + * - DOEPTSIZE0.Transfer Size = 24 Bytes (To receive up to 3 back
- + * to back setup packets)
- + * - In DMA mode, DOEPDMA0 Register with a memory address to
- + * store any setup packets received
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param pcd Programming view of the PCD.
- + */
- +static inline void ep0_out_start(dwc_otg_core_if_t * core_if,
- + dwc_otg_pcd_t * pcd)
- +{
- + dwc_otg_dev_if_t *dev_if = core_if->dev_if;
- + deptsiz0_data_t doeptsize0 = {.d32 = 0 };
- + dwc_otg_dev_dma_desc_t *dma_desc;
- + depctl_data_t doepctl = {.d32 = 0 };
- +
- +#ifdef VERBOSE
- + DWC_DEBUGPL(DBG_PCDV, "%s() doepctl0=%0x\n", __func__,
- + DWC_READ_REG32(&dev_if->out_ep_regs[0]->doepctl));
- +#endif
- + if (core_if->snpsid >= OTG_CORE_REV_3_00a) {
- + doepctl.d32 = DWC_READ_REG32(&dev_if->out_ep_regs[0]->doepctl);
- + if (doepctl.b.epena) {
- + return;
- + }
- + }
- +
- + doeptsize0.b.supcnt = 3;
- + doeptsize0.b.pktcnt = 1;
- + doeptsize0.b.xfersize = 8 * 3;
- +
- + if (core_if->dma_enable) {
- + if (!core_if->dma_desc_enable) {
- + /** put here as for Hermes mode deptisz register should not be written */
- + DWC_WRITE_REG32(&dev_if->out_ep_regs[0]->doeptsiz,
- + doeptsize0.d32);
- +
- + /** @todo dma needs to handle multiple setup packets (up to 3) */
- + DWC_WRITE_REG32(&dev_if->out_ep_regs[0]->doepdma,
- + pcd->setup_pkt_dma_handle);
- + } else {
- + dev_if->setup_desc_index =
- + (dev_if->setup_desc_index + 1) & 1;
- + dma_desc =
- + dev_if->setup_desc_addr[dev_if->setup_desc_index];
- +
- + /** DMA Descriptor Setup */
- + dma_desc->status.b.bs = BS_HOST_BUSY;
- + if (core_if->snpsid >= OTG_CORE_REV_3_00a) {
- + dma_desc->status.b.sr = 0;
- + dma_desc->status.b.mtrf = 0;
- + }
- + dma_desc->status.b.l = 1;
- + dma_desc->status.b.ioc = 1;
- + dma_desc->status.b.bytes = pcd->ep0.dwc_ep.maxpacket;
- + dma_desc->buf = pcd->setup_pkt_dma_handle;
- + dma_desc->status.b.sts = 0;
- + dma_desc->status.b.bs = BS_HOST_READY;
- +
- + /** DOEPDMA0 Register write */
- + DWC_WRITE_REG32(&dev_if->out_ep_regs[0]->doepdma,
- + dev_if->dma_setup_desc_addr
- + [dev_if->setup_desc_index]);
- + }
- +
- + } else {
- + /** put here as for Hermes mode deptisz register should not be written */
- + DWC_WRITE_REG32(&dev_if->out_ep_regs[0]->doeptsiz,
- + doeptsize0.d32);
- + }
- +
- + /** DOEPCTL0 Register write cnak will be set after setup interrupt */
- + doepctl.d32 = 0;
- + doepctl.b.epena = 1;
- + if (core_if->snpsid <= OTG_CORE_REV_2_94a) {
- + doepctl.b.cnak = 1;
- + DWC_WRITE_REG32(&dev_if->out_ep_regs[0]->doepctl, doepctl.d32);
- + } else {
- + DWC_MODIFY_REG32(&dev_if->out_ep_regs[0]->doepctl, 0, doepctl.d32);
- + }
- +
- +#ifdef VERBOSE
- + DWC_DEBUGPL(DBG_PCDV, "doepctl0=%0x\n",
- + DWC_READ_REG32(&dev_if->out_ep_regs[0]->doepctl));
- + DWC_DEBUGPL(DBG_PCDV, "diepctl0=%0x\n",
- + DWC_READ_REG32(&dev_if->in_ep_regs[0]->diepctl));
- +#endif
- +}
- +
- +/**
- + * This interrupt occurs when a USB Reset is detected. When the USB
- + * Reset Interrupt occurs the device state is set to DEFAULT and the
- + * EP0 state is set to IDLE.
- + * -# Set the NAK bit for all OUT endpoints (DOEPCTLn.SNAK = 1)
- + * -# Unmask the following interrupt bits
- + * - DAINTMSK.INEP0 = 1 (Control 0 IN endpoint)
- + * - DAINTMSK.OUTEP0 = 1 (Control 0 OUT endpoint)
- + * - DOEPMSK.SETUP = 1
- + * - DOEPMSK.XferCompl = 1
- + * - DIEPMSK.XferCompl = 1
- + * - DIEPMSK.TimeOut = 1
- + * -# Program the following fields in the endpoint specific registers
- + * for Control OUT EP 0, in order to receive a setup packet
- + * - DOEPTSIZ0.Packet Count = 3 (To receive up to 3 back to back
- + * setup packets)
- + * - DOEPTSIZE0.Transfer Size = 24 Bytes (To receive up to 3 back
- + * to back setup packets)
- + * - In DMA mode, DOEPDMA0 Register with a memory address to
- + * store any setup packets received
- + * At this point, all the required initialization, except for enabling
- + * the control 0 OUT endpoint is done, for receiving SETUP packets.
- + */
- +int32_t dwc_otg_pcd_handle_usb_reset_intr(dwc_otg_pcd_t * pcd)
- +{
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
- + dwc_otg_dev_if_t *dev_if = core_if->dev_if;
- + depctl_data_t doepctl = {.d32 = 0 };
- + depctl_data_t diepctl = {.d32 = 0 };
- + daint_data_t daintmsk = {.d32 = 0 };
- + doepmsk_data_t doepmsk = {.d32 = 0 };
- + diepmsk_data_t diepmsk = {.d32 = 0 };
- + dcfg_data_t dcfg = {.d32 = 0 };
- + grstctl_t resetctl = {.d32 = 0 };
- + dctl_data_t dctl = {.d32 = 0 };
- + int i = 0;
- + gintsts_data_t gintsts;
- + pcgcctl_data_t power = {.d32 = 0 };
- +
- + power.d32 = DWC_READ_REG32(core_if->pcgcctl);
- + if (power.b.stoppclk) {
- + power.d32 = 0;
- + power.b.stoppclk = 1;
- + DWC_MODIFY_REG32(core_if->pcgcctl, power.d32, 0);
- +
- + power.b.pwrclmp = 1;
- + DWC_MODIFY_REG32(core_if->pcgcctl, power.d32, 0);
- +
- + power.b.rstpdwnmodule = 1;
- + DWC_MODIFY_REG32(core_if->pcgcctl, power.d32, 0);
- + }
- +
- + core_if->lx_state = DWC_OTG_L0;
- +
- + DWC_PRINTF("USB RESET\n");
- +#ifdef DWC_EN_ISOC
- + for (i = 1; i < 16; ++i) {
- + dwc_otg_pcd_ep_t *ep;
- + dwc_ep_t *dwc_ep;
- + ep = get_in_ep(pcd, i);
- + if (ep != 0) {
- + dwc_ep = &ep->dwc_ep;
- + dwc_ep->next_frame = 0xffffffff;
- + }
- + }
- +#endif /* DWC_EN_ISOC */
- +
- + /* reset the HNP settings */
- + dwc_otg_pcd_update_otg(pcd, 1);
- +
- + /* Clear the Remote Wakeup Signalling */
- + dctl.b.rmtwkupsig = 1;
- + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32, 0);
- +
- + /* Set NAK for all OUT EPs */
- + doepctl.b.snak = 1;
- + for (i = 0; i <= dev_if->num_out_eps; i++) {
- + DWC_WRITE_REG32(&dev_if->out_ep_regs[i]->doepctl, doepctl.d32);
- + }
- +
- + /* Flush the NP Tx FIFO */
- + dwc_otg_flush_tx_fifo(core_if, 0x10);
- + /* Flush the Learning Queue */
- + resetctl.b.intknqflsh = 1;
- + DWC_WRITE_REG32(&core_if->core_global_regs->grstctl, resetctl.d32);
- +
- + if (!core_if->core_params->en_multiple_tx_fifo && core_if->dma_enable) {
- + core_if->start_predict = 0;
- + for (i = 0; i<= core_if->dev_if->num_in_eps; ++i) {
- + core_if->nextep_seq[i] = 0xff; // 0xff - EP not active
- + }
- + core_if->nextep_seq[0] = 0;
- + core_if->first_in_nextep_seq = 0;
- + diepctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[0]->diepctl);
- + diepctl.b.nextep = 0;
- + DWC_WRITE_REG32(&dev_if->in_ep_regs[0]->diepctl, diepctl.d32);
- +
- + /* Update IN Endpoint Mismatch Count by active IN NP EP count + 1 */
- + dcfg.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dcfg);
- + dcfg.b.epmscnt = 2;
- + DWC_WRITE_REG32(&dev_if->dev_global_regs->dcfg, dcfg.d32);
- +
- + DWC_DEBUGPL(DBG_PCDV,
- + "%s first_in_nextep_seq= %2d; nextep_seq[]:\n",
- + __func__, core_if->first_in_nextep_seq);
- + for (i=0; i <= core_if->dev_if->num_in_eps; i++) {
- + DWC_DEBUGPL(DBG_PCDV, "%2d\n", core_if->nextep_seq[i]);
- + }
- + }
- +
- + if (core_if->multiproc_int_enable) {
- + daintmsk.b.inep0 = 1;
- + daintmsk.b.outep0 = 1;
- + DWC_WRITE_REG32(&dev_if->dev_global_regs->deachintmsk,
- + daintmsk.d32);
- +
- + doepmsk.b.setup = 1;
- + doepmsk.b.xfercompl = 1;
- + doepmsk.b.ahberr = 1;
- + doepmsk.b.epdisabled = 1;
- +
- + if ((core_if->dma_desc_enable) ||
- + (core_if->dma_enable
- + && core_if->snpsid >= OTG_CORE_REV_3_00a)) {
- + doepmsk.b.stsphsercvd = 1;
- + }
- + if (core_if->dma_desc_enable)
- + doepmsk.b.bna = 1;
- +/*
- + doepmsk.b.babble = 1;
- + doepmsk.b.nyet = 1;
- +
- + if (core_if->dma_enable) {
- + doepmsk.b.nak = 1;
- + }
- +*/
- + DWC_WRITE_REG32(&dev_if->dev_global_regs->doepeachintmsk[0],
- + doepmsk.d32);
- +
- + diepmsk.b.xfercompl = 1;
- + diepmsk.b.timeout = 1;
- + diepmsk.b.epdisabled = 1;
- + diepmsk.b.ahberr = 1;
- + diepmsk.b.intknepmis = 1;
- + if (!core_if->en_multiple_tx_fifo && core_if->dma_enable)
- + diepmsk.b.intknepmis = 0;
- +
- +/* if (core_if->dma_desc_enable) {
- + diepmsk.b.bna = 1;
- + }
- +*/
- +/*
- + if (core_if->dma_enable) {
- + diepmsk.b.nak = 1;
- + }
- +*/
- + DWC_WRITE_REG32(&dev_if->dev_global_regs->diepeachintmsk[0],
- + diepmsk.d32);
- + } else {
- + daintmsk.b.inep0 = 1;
- + daintmsk.b.outep0 = 1;
- + DWC_WRITE_REG32(&dev_if->dev_global_regs->daintmsk,
- + daintmsk.d32);
- +
- + doepmsk.b.setup = 1;
- + doepmsk.b.xfercompl = 1;
- + doepmsk.b.ahberr = 1;
- + doepmsk.b.epdisabled = 1;
- +
- + if ((core_if->dma_desc_enable) ||
- + (core_if->dma_enable
- + && core_if->snpsid >= OTG_CORE_REV_3_00a)) {
- + doepmsk.b.stsphsercvd = 1;
- + }
- + if (core_if->dma_desc_enable)
- + doepmsk.b.bna = 1;
- + DWC_WRITE_REG32(&dev_if->dev_global_regs->doepmsk, doepmsk.d32);
- +
- + diepmsk.b.xfercompl = 1;
- + diepmsk.b.timeout = 1;
- + diepmsk.b.epdisabled = 1;
- + diepmsk.b.ahberr = 1;
- + if (!core_if->en_multiple_tx_fifo && core_if->dma_enable)
- + diepmsk.b.intknepmis = 0;
- +/*
- + if (core_if->dma_desc_enable) {
- + diepmsk.b.bna = 1;
- + }
- +*/
- +
- + DWC_WRITE_REG32(&dev_if->dev_global_regs->diepmsk, diepmsk.d32);
- + }
- +
- + /* Reset Device Address */
- + dcfg.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dcfg);
- + dcfg.b.devaddr = 0;
- + DWC_WRITE_REG32(&dev_if->dev_global_regs->dcfg, dcfg.d32);
- +
- + /* setup EP0 to receive SETUP packets */
- + if (core_if->snpsid <= OTG_CORE_REV_2_94a)
- + ep0_out_start(core_if, pcd);
- +
- + /* Clear interrupt */
- + gintsts.d32 = 0;
- + gintsts.b.usbreset = 1;
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
- +
- + return 1;
- +}
- +
- +/**
- + * Get the device speed from the device status register and convert it
- + * to USB speed constant.
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + */
- +static int get_device_speed(dwc_otg_core_if_t * core_if)
- +{
- + dsts_data_t dsts;
- + int speed = 0;
- + dsts.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dsts);
- +
- + switch (dsts.b.enumspd) {
- + case DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ:
- + speed = USB_SPEED_HIGH;
- + break;
- + case DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ:
- + case DWC_DSTS_ENUMSPD_FS_PHY_48MHZ:
- + speed = USB_SPEED_FULL;
- + break;
- +
- + case DWC_DSTS_ENUMSPD_LS_PHY_6MHZ:
- + speed = USB_SPEED_LOW;
- + break;
- + }
- +
- + return speed;
- +}
- +
- +/**
- + * Read the device status register and set the device speed in the
- + * data structure.
- + * Set up EP0 to receive SETUP packets by calling dwc_ep0_activate.
- + */
- +int32_t dwc_otg_pcd_handle_enum_done_intr(dwc_otg_pcd_t * pcd)
- +{
- + dwc_otg_pcd_ep_t *ep0 = &pcd->ep0;
- + gintsts_data_t gintsts;
- + gusbcfg_data_t gusbcfg;
- + dwc_otg_core_global_regs_t *global_regs =
- + GET_CORE_IF(pcd)->core_global_regs;
- + uint8_t utmi16b, utmi8b;
- + int speed;
- + DWC_DEBUGPL(DBG_PCD, "SPEED ENUM\n");
- +
- + if (GET_CORE_IF(pcd)->snpsid >= OTG_CORE_REV_2_60a) {
- + utmi16b = 6; //vahrama old value was 6;
- + utmi8b = 9;
- + } else {
- + utmi16b = 4;
- + utmi8b = 8;
- + }
- + dwc_otg_ep0_activate(GET_CORE_IF(pcd), &ep0->dwc_ep);
- + if (GET_CORE_IF(pcd)->snpsid >= OTG_CORE_REV_3_00a) {
- + ep0_out_start(GET_CORE_IF(pcd), pcd);
- + }
- +
- +#ifdef DEBUG_EP0
- + print_ep0_state(pcd);
- +#endif
- +
- + if (pcd->ep0state == EP0_DISCONNECT) {
- + pcd->ep0state = EP0_IDLE;
- + } else if (pcd->ep0state == EP0_STALL) {
- + pcd->ep0state = EP0_IDLE;
- + }
- +
- + pcd->ep0state = EP0_IDLE;
- +
- + ep0->stopped = 0;
- +
- + speed = get_device_speed(GET_CORE_IF(pcd));
- + pcd->fops->connect(pcd, speed);
- +
- + /* Set USB turnaround time based on device speed and PHY interface. */
- + gusbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg);
- + if (speed == USB_SPEED_HIGH) {
- + if (GET_CORE_IF(pcd)->hwcfg2.b.hs_phy_type ==
- + DWC_HWCFG2_HS_PHY_TYPE_ULPI) {
- + /* ULPI interface */
- + gusbcfg.b.usbtrdtim = 9;
- + }
- + if (GET_CORE_IF(pcd)->hwcfg2.b.hs_phy_type ==
- + DWC_HWCFG2_HS_PHY_TYPE_UTMI) {
- + /* UTMI+ interface */
- + if (GET_CORE_IF(pcd)->hwcfg4.b.utmi_phy_data_width == 0) {
- + gusbcfg.b.usbtrdtim = utmi8b;
- + } else if (GET_CORE_IF(pcd)->hwcfg4.
- + b.utmi_phy_data_width == 1) {
- + gusbcfg.b.usbtrdtim = utmi16b;
- + } else if (GET_CORE_IF(pcd)->
- + core_params->phy_utmi_width == 8) {
- + gusbcfg.b.usbtrdtim = utmi8b;
- + } else {
- + gusbcfg.b.usbtrdtim = utmi16b;
- + }
- + }
- + if (GET_CORE_IF(pcd)->hwcfg2.b.hs_phy_type ==
- + DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI) {
- + /* UTMI+ OR ULPI interface */
- + if (gusbcfg.b.ulpi_utmi_sel == 1) {
- + /* ULPI interface */
- + gusbcfg.b.usbtrdtim = 9;
- + } else {
- + /* UTMI+ interface */
- + if (GET_CORE_IF(pcd)->
- + core_params->phy_utmi_width == 16) {
- + gusbcfg.b.usbtrdtim = utmi16b;
- + } else {
- + gusbcfg.b.usbtrdtim = utmi8b;
- + }
- + }
- + }
- + } else {
- + /* Full or low speed */
- + gusbcfg.b.usbtrdtim = 9;
- + }
- + DWC_WRITE_REG32(&global_regs->gusbcfg, gusbcfg.d32);
- +
- + /* Clear interrupt */
- + gintsts.d32 = 0;
- + gintsts.b.enumdone = 1;
- + DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
- + gintsts.d32);
- + return 1;
- +}
- +
- +/**
- + * This interrupt indicates that the ISO OUT Packet was dropped due to
- + * Rx FIFO full or Rx Status Queue Full. If this interrupt occurs
- + * read all the data from the Rx FIFO.
- + */
- +int32_t dwc_otg_pcd_handle_isoc_out_packet_dropped_intr(dwc_otg_pcd_t * pcd)
- +{
- + gintmsk_data_t intr_mask = {.d32 = 0 };
- + gintsts_data_t gintsts;
- +
- + DWC_WARN("INTERRUPT Handler not implemented for %s\n",
- + "ISOC Out Dropped");
- +
- + intr_mask.b.isooutdrop = 1;
- + DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
- + intr_mask.d32, 0);
- +
- + /* Clear interrupt */
- + gintsts.d32 = 0;
- + gintsts.b.isooutdrop = 1;
- + DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
- + gintsts.d32);
- +
- + return 1;
- +}
- +
- +/**
- + * This interrupt indicates the end of the portion of the micro-frame
- + * for periodic transactions. If there is a periodic transaction for
- + * the next frame, load the packets into the EP periodic Tx FIFO.
- + */
- +int32_t dwc_otg_pcd_handle_end_periodic_frame_intr(dwc_otg_pcd_t * pcd)
- +{
- + gintmsk_data_t intr_mask = {.d32 = 0 };
- + gintsts_data_t gintsts;
- + DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", "EOP");
- +
- + intr_mask.b.eopframe = 1;
- + DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
- + intr_mask.d32, 0);
- +
- + /* Clear interrupt */
- + gintsts.d32 = 0;
- + gintsts.b.eopframe = 1;
- + DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
- + gintsts.d32);
- +
- + return 1;
- +}
- +
- +/**
- + * This interrupt indicates that EP of the packet on the top of the
- + * non-periodic Tx FIFO does not match EP of the IN Token received.
- + *
- + * The "Device IN Token Queue" Registers are read to determine the
- + * order the IN Tokens have been received. The non-periodic Tx FIFO
- + * is flushed, so it can be reloaded in the order seen in the IN Token
- + * Queue.
- + */
- +int32_t dwc_otg_pcd_handle_ep_mismatch_intr(dwc_otg_pcd_t * pcd)
- +{
- + gintsts_data_t gintsts;
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
- + dctl_data_t dctl;
- + gintmsk_data_t intr_mask = {.d32 = 0 };
- +
- + if (!core_if->en_multiple_tx_fifo && core_if->dma_enable) {
- + core_if->start_predict = 1;
- +
- + DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, core_if);
- +
- + gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
- + if (!gintsts.b.ginnakeff) {
- + /* Disable EP Mismatch interrupt */
- + intr_mask.d32 = 0;
- + intr_mask.b.epmismatch = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, intr_mask.d32, 0);
- + /* Enable the Global IN NAK Effective Interrupt */
- + intr_mask.d32 = 0;
- + intr_mask.b.ginnakeff = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0, intr_mask.d32);
- + /* Set the global non-periodic IN NAK handshake */
- + dctl.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dctl);
- + dctl.b.sgnpinnak = 1;
- + DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32);
- + } else {
- + DWC_PRINTF("gintsts.b.ginnakeff = 1! dctl.b.sgnpinnak not set\n");
- + }
- + /* Disabling of all EP's will be done in dwc_otg_pcd_handle_in_nak_effective()
- + * handler after Global IN NAK Effective interrupt will be asserted */
- + }
- + /* Clear interrupt */
- + gintsts.d32 = 0;
- + gintsts.b.epmismatch = 1;
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
- +
- + return 1;
- +}
- +
- +/**
- + * This interrupt is valid only in DMA mode. This interrupt indicates that the
- + * core has stopped fetching data for IN endpoints due to the unavailability of
- + * TxFIFO space or Request Queue space. This interrupt is used by the
- + * application for an endpoint mismatch algorithm.
- + *
- + * @param pcd The PCD
- + */
- +int32_t dwc_otg_pcd_handle_ep_fetsusp_intr(dwc_otg_pcd_t * pcd)
- +{
- + gintsts_data_t gintsts;
- + gintmsk_data_t gintmsk_data;
- + dctl_data_t dctl;
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
- + DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, core_if);
- +
- + /* Clear the global non-periodic IN NAK handshake */
- + dctl.d32 = 0;
- + dctl.b.cgnpinnak = 1;
- + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32);
- +
- + /* Mask GINTSTS.FETSUSP interrupt */
- + gintmsk_data.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
- + gintmsk_data.b.fetsusp = 0;
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, gintmsk_data.d32);
- +
- + /* Clear interrupt */
- + gintsts.d32 = 0;
- + gintsts.b.fetsusp = 1;
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32);
- +
- + return 1;
- +}
- +/**
- + * This funcion stalls EP0.
- + */
- +static inline void ep0_do_stall(dwc_otg_pcd_t * pcd, const int err_val)
- +{
- + dwc_otg_pcd_ep_t *ep0 = &pcd->ep0;
- + usb_device_request_t *ctrl = &pcd->setup_pkt->req;
- + DWC_WARN("req %02x.%02x protocol STALL; err %d\n",
- + ctrl->bmRequestType, ctrl->bRequest, err_val);
- +
- + ep0->dwc_ep.is_in = 1;
- + dwc_otg_ep_set_stall(GET_CORE_IF(pcd), &ep0->dwc_ep);
- + pcd->ep0.stopped = 1;
- + pcd->ep0state = EP0_IDLE;
- + ep0_out_start(GET_CORE_IF(pcd), pcd);
- +}
- +
- +/**
- + * This functions delegates the setup command to the gadget driver.
- + */
- +static inline void do_gadget_setup(dwc_otg_pcd_t * pcd,
- + usb_device_request_t * ctrl)
- +{
- + int ret = 0;
- + DWC_SPINUNLOCK(pcd->lock);
- + ret = pcd->fops->setup(pcd, (uint8_t *) ctrl);
- + DWC_SPINLOCK(pcd->lock);
- + if (ret < 0) {
- + ep0_do_stall(pcd, ret);
- + }
- +
- + /** @todo This is a g_file_storage gadget driver specific
- + * workaround: a DELAYED_STATUS result from the fsg_setup
- + * routine will result in the gadget queueing a EP0 IN status
- + * phase for a two-stage control transfer. Exactly the same as
- + * a SET_CONFIGURATION/SET_INTERFACE except that this is a class
- + * specific request. Need a generic way to know when the gadget
- + * driver will queue the status phase. Can we assume when we
- + * call the gadget driver setup() function that it will always
- + * queue and require the following flag? Need to look into
- + * this.
- + */
- +
- + if (ret == 256 + 999) {
- + pcd->request_config = 1;
- + }
- +}
- +
- +#ifdef DWC_UTE_CFI
- +/**
- + * This functions delegates the CFI setup commands to the gadget driver.
- + * This function will return a negative value to indicate a failure.
- + */
- +static inline int cfi_gadget_setup(dwc_otg_pcd_t * pcd,
- + struct cfi_usb_ctrlrequest *ctrl_req)
- +{
- + int ret = 0;
- +
- + if (pcd->fops && pcd->fops->cfi_setup) {
- + DWC_SPINUNLOCK(pcd->lock);
- + ret = pcd->fops->cfi_setup(pcd, ctrl_req);
- + DWC_SPINLOCK(pcd->lock);
- + if (ret < 0) {
- + ep0_do_stall(pcd, ret);
- + return ret;
- + }
- + }
- +
- + return ret;
- +}
- +#endif
- +
- +/**
- + * This function starts the Zero-Length Packet for the IN status phase
- + * of a 2 stage control transfer.
- + */
- +static inline void do_setup_in_status_phase(dwc_otg_pcd_t * pcd)
- +{
- + dwc_otg_pcd_ep_t *ep0 = &pcd->ep0;
- + if (pcd->ep0state == EP0_STALL) {
- + return;
- + }
- +
- + pcd->ep0state = EP0_IN_STATUS_PHASE;
- +
- + /* Prepare for more SETUP Packets */
- + DWC_DEBUGPL(DBG_PCD, "EP0 IN ZLP\n");
- + if ((GET_CORE_IF(pcd)->snpsid >= OTG_CORE_REV_3_00a)
- + && (pcd->core_if->dma_desc_enable)
- + && (ep0->dwc_ep.xfer_count < ep0->dwc_ep.total_len)) {
- + DWC_DEBUGPL(DBG_PCDV,
- + "Data terminated wait next packet in out_desc_addr\n");
- + pcd->backup_buf = phys_to_virt(ep0->dwc_ep.dma_addr);
- + pcd->data_terminated = 1;
- + }
- + ep0->dwc_ep.xfer_len = 0;
- + ep0->dwc_ep.xfer_count = 0;
- + ep0->dwc_ep.is_in = 1;
- + ep0->dwc_ep.dma_addr = pcd->setup_pkt_dma_handle;
- + dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep);
- +
- + /* Prepare for more SETUP Packets */
- + //ep0_out_start(GET_CORE_IF(pcd), pcd);
- +}
- +
- +/**
- + * This function starts the Zero-Length Packet for the OUT status phase
- + * of a 2 stage control transfer.
- + */
- +static inline void do_setup_out_status_phase(dwc_otg_pcd_t * pcd)
- +{
- + dwc_otg_pcd_ep_t *ep0 = &pcd->ep0;
- + if (pcd->ep0state == EP0_STALL) {
- + DWC_DEBUGPL(DBG_PCD, "EP0 STALLED\n");
- + return;
- + }
- + pcd->ep0state = EP0_OUT_STATUS_PHASE;
- +
- + DWC_DEBUGPL(DBG_PCD, "EP0 OUT ZLP\n");
- + ep0->dwc_ep.xfer_len = 0;
- + ep0->dwc_ep.xfer_count = 0;
- + ep0->dwc_ep.is_in = 0;
- + ep0->dwc_ep.dma_addr = pcd->setup_pkt_dma_handle;
- + dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep);
- +
- + /* Prepare for more SETUP Packets */
- + if (GET_CORE_IF(pcd)->dma_enable == 0) {
- + ep0_out_start(GET_CORE_IF(pcd), pcd);
- + }
- +}
- +
- +/**
- + * Clear the EP halt (STALL) and if pending requests start the
- + * transfer.
- + */
- +static inline void pcd_clear_halt(dwc_otg_pcd_t * pcd, dwc_otg_pcd_ep_t * ep)
- +{
- + if (ep->dwc_ep.stall_clear_flag == 0)
- + dwc_otg_ep_clear_stall(GET_CORE_IF(pcd), &ep->dwc_ep);
- +
- + /* Reactive the EP */
- + dwc_otg_ep_activate(GET_CORE_IF(pcd), &ep->dwc_ep);
- + if (ep->stopped) {
- + ep->stopped = 0;
- + /* If there is a request in the EP queue start it */
- +
- + /** @todo FIXME: this causes an EP mismatch in DMA mode.
- + * epmismatch not yet implemented. */
- +
- + /*
- + * Above fixme is solved by implmenting a tasklet to call the
- + * start_next_request(), outside of interrupt context at some
- + * time after the current time, after a clear-halt setup packet.
- + * Still need to implement ep mismatch in the future if a gadget
- + * ever uses more than one endpoint at once
- + */
- + ep->queue_sof = 1;
- + DWC_TASK_SCHEDULE(pcd->start_xfer_tasklet);
- + }
- + /* Start Control Status Phase */
- + do_setup_in_status_phase(pcd);
- +}
- +
- +/**
- + * This function is called when the SET_FEATURE TEST_MODE Setup packet
- + * is sent from the host. The Device Control register is written with
- + * the Test Mode bits set to the specified Test Mode. This is done as
- + * a tasklet so that the "Status" phase of the control transfer
- + * completes before transmitting the TEST packets.
- + *
- + * @todo This has not been tested since the tasklet struct was put
- + * into the PCD struct!
- + *
- + */
- +void do_test_mode(void *data)
- +{
- + dctl_data_t dctl;
- + dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) data;
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
- + int test_mode = pcd->test_mode;
- +
- +// DWC_WARN("%s() has not been tested since being rewritten!\n", __func__);
- +
- + dctl.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dctl);
- + switch (test_mode) {
- + case 1: // TEST_J
- + dctl.b.tstctl = 1;
- + break;
- +
- + case 2: // TEST_K
- + dctl.b.tstctl = 2;
- + break;
- +
- + case 3: // TEST_SE0_NAK
- + dctl.b.tstctl = 3;
- + break;
- +
- + case 4: // TEST_PACKET
- + dctl.b.tstctl = 4;
- + break;
- +
- + case 5: // TEST_FORCE_ENABLE
- + dctl.b.tstctl = 5;
- + break;
- + }
- + DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32);
- +}
- +
- +/**
- + * This function process the GET_STATUS Setup Commands.
- + */
- +static inline void do_get_status(dwc_otg_pcd_t * pcd)
- +{
- + usb_device_request_t ctrl = pcd->setup_pkt->req;
- + dwc_otg_pcd_ep_t *ep;
- + dwc_otg_pcd_ep_t *ep0 = &pcd->ep0;
- + uint16_t *status = pcd->status_buf;
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
- +
- +#ifdef DEBUG_EP0
- + DWC_DEBUGPL(DBG_PCD,
- + "GET_STATUS %02x.%02x v%04x i%04x l%04x\n",
- + ctrl.bmRequestType, ctrl.bRequest,
- + UGETW(ctrl.wValue), UGETW(ctrl.wIndex),
- + UGETW(ctrl.wLength));
- +#endif
- +
- + switch (UT_GET_RECIPIENT(ctrl.bmRequestType)) {
- + case UT_DEVICE:
- + if(UGETW(ctrl.wIndex) == 0xF000) { /* OTG Status selector */
- + DWC_PRINTF("wIndex - %d\n", UGETW(ctrl.wIndex));
- + DWC_PRINTF("OTG VERSION - %d\n", core_if->otg_ver);
- + DWC_PRINTF("OTG CAP - %d, %d\n",
- + core_if->core_params->otg_cap,
- + DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE);
- + if (core_if->otg_ver == 1
- + && core_if->core_params->otg_cap ==
- + DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) {
- + uint8_t *otgsts = (uint8_t*)pcd->status_buf;
- + *otgsts = (core_if->otg_sts & 0x1);
- + pcd->ep0_pending = 1;
- + ep0->dwc_ep.start_xfer_buff =
- + (uint8_t *) otgsts;
- + ep0->dwc_ep.xfer_buff = (uint8_t *) otgsts;
- + ep0->dwc_ep.dma_addr =
- + pcd->status_buf_dma_handle;
- + ep0->dwc_ep.xfer_len = 1;
- + ep0->dwc_ep.xfer_count = 0;
- + ep0->dwc_ep.total_len = ep0->dwc_ep.xfer_len;
- + dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd),
- + &ep0->dwc_ep);
- + return;
- + } else {
- + ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED);
- + return;
- + }
- + break;
- + } else {
- + *status = 0x1; /* Self powered */
- + *status |= pcd->remote_wakeup_enable << 1;
- + break;
- + }
- + case UT_INTERFACE:
- + *status = 0;
- + break;
- +
- + case UT_ENDPOINT:
- + ep = get_ep_by_addr(pcd, UGETW(ctrl.wIndex));
- + if (ep == 0 || UGETW(ctrl.wLength) > 2) {
- + ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED);
- + return;
- + }
- + /** @todo check for EP stall */
- + *status = ep->stopped;
- + break;
- + }
- + pcd->ep0_pending = 1;
- + ep0->dwc_ep.start_xfer_buff = (uint8_t *) status;
- + ep0->dwc_ep.xfer_buff = (uint8_t *) status;
- + ep0->dwc_ep.dma_addr = pcd->status_buf_dma_handle;
- + ep0->dwc_ep.xfer_len = 2;
- + ep0->dwc_ep.xfer_count = 0;
- + ep0->dwc_ep.total_len = ep0->dwc_ep.xfer_len;
- + dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep);
- +}
- +
- +/**
- + * This function process the SET_FEATURE Setup Commands.
- + */
- +static inline void do_set_feature(dwc_otg_pcd_t * pcd)
- +{
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
- + dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
- + usb_device_request_t ctrl = pcd->setup_pkt->req;
- + dwc_otg_pcd_ep_t *ep = 0;
- + int32_t otg_cap_param = core_if->core_params->otg_cap;
- + gotgctl_data_t gotgctl = {.d32 = 0 };
- +
- + DWC_DEBUGPL(DBG_PCD, "SET_FEATURE:%02x.%02x v%04x i%04x l%04x\n",
- + ctrl.bmRequestType, ctrl.bRequest,
- + UGETW(ctrl.wValue), UGETW(ctrl.wIndex),
- + UGETW(ctrl.wLength));
- + DWC_DEBUGPL(DBG_PCD, "otg_cap=%d\n", otg_cap_param);
- +
- + switch (UT_GET_RECIPIENT(ctrl.bmRequestType)) {
- + case UT_DEVICE:
- + switch (UGETW(ctrl.wValue)) {
- + case UF_DEVICE_REMOTE_WAKEUP:
- + pcd->remote_wakeup_enable = 1;
- + break;
- +
- + case UF_TEST_MODE:
- + /* Setup the Test Mode tasklet to do the Test
- + * Packet generation after the SETUP Status
- + * phase has completed. */
- +
- + /** @todo This has not been tested since the
- + * tasklet struct was put into the PCD
- + * struct! */
- + pcd->test_mode = UGETW(ctrl.wIndex) >> 8;
- + DWC_TASK_SCHEDULE(pcd->test_mode_tasklet);
- + break;
- +
- + case UF_DEVICE_B_HNP_ENABLE:
- + DWC_DEBUGPL(DBG_PCDV,
- + "SET_FEATURE: USB_DEVICE_B_HNP_ENABLE\n");
- +
- + /* dev may initiate HNP */
- + if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) {
- + pcd->b_hnp_enable = 1;
- + dwc_otg_pcd_update_otg(pcd, 0);
- + DWC_DEBUGPL(DBG_PCD, "Request B HNP\n");
- + /**@todo Is the gotgctl.devhnpen cleared
- + * by a USB Reset? */
- + gotgctl.b.devhnpen = 1;
- + gotgctl.b.hnpreq = 1;
- + DWC_WRITE_REG32(&global_regs->gotgctl,
- + gotgctl.d32);
- + } else {
- + ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED);
- + return;
- + }
- + break;
- +
- + case UF_DEVICE_A_HNP_SUPPORT:
- + /* RH port supports HNP */
- + DWC_DEBUGPL(DBG_PCDV,
- + "SET_FEATURE: USB_DEVICE_A_HNP_SUPPORT\n");
- + if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) {
- + pcd->a_hnp_support = 1;
- + dwc_otg_pcd_update_otg(pcd, 0);
- + } else {
- + ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED);
- + return;
- + }
- + break;
- +
- + case UF_DEVICE_A_ALT_HNP_SUPPORT:
- + /* other RH port does */
- + DWC_DEBUGPL(DBG_PCDV,
- + "SET_FEATURE: USB_DEVICE_A_ALT_HNP_SUPPORT\n");
- + if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) {
- + pcd->a_alt_hnp_support = 1;
- + dwc_otg_pcd_update_otg(pcd, 0);
- + } else {
- + ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED);
- + return;
- + }
- + break;
- +
- + default:
- + ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED);
- + return;
- +
- + }
- + do_setup_in_status_phase(pcd);
- + break;
- +
- + case UT_INTERFACE:
- + do_gadget_setup(pcd, &ctrl);
- + break;
- +
- + case UT_ENDPOINT:
- + if (UGETW(ctrl.wValue) == UF_ENDPOINT_HALT) {
- + ep = get_ep_by_addr(pcd, UGETW(ctrl.wIndex));
- + if (ep == 0) {
- + ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED);
- + return;
- + }
- + ep->stopped = 1;
- + dwc_otg_ep_set_stall(core_if, &ep->dwc_ep);
- + }
- + do_setup_in_status_phase(pcd);
- + break;
- + }
- +}
- +
- +/**
- + * This function process the CLEAR_FEATURE Setup Commands.
- + */
- +static inline void do_clear_feature(dwc_otg_pcd_t * pcd)
- +{
- + usb_device_request_t ctrl = pcd->setup_pkt->req;
- + dwc_otg_pcd_ep_t *ep = 0;
- +
- + DWC_DEBUGPL(DBG_PCD,
- + "CLEAR_FEATURE:%02x.%02x v%04x i%04x l%04x\n",
- + ctrl.bmRequestType, ctrl.bRequest,
- + UGETW(ctrl.wValue), UGETW(ctrl.wIndex),
- + UGETW(ctrl.wLength));
- +
- + switch (UT_GET_RECIPIENT(ctrl.bmRequestType)) {
- + case UT_DEVICE:
- + switch (UGETW(ctrl.wValue)) {
- + case UF_DEVICE_REMOTE_WAKEUP:
- + pcd->remote_wakeup_enable = 0;
- + break;
- +
- + case UF_TEST_MODE:
- + /** @todo Add CLEAR_FEATURE for TEST modes. */
- + break;
- +
- + default:
- + ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED);
- + return;
- + }
- + do_setup_in_status_phase(pcd);
- + break;
- +
- + case UT_ENDPOINT:
- + ep = get_ep_by_addr(pcd, UGETW(ctrl.wIndex));
- + if (ep == 0) {
- + ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED);
- + return;
- + }
- +
- + pcd_clear_halt(pcd, ep);
- +
- + break;
- + }
- +}
- +
- +/**
- + * This function process the SET_ADDRESS Setup Commands.
- + */
- +static inline void do_set_address(dwc_otg_pcd_t * pcd)
- +{
- + dwc_otg_dev_if_t *dev_if = GET_CORE_IF(pcd)->dev_if;
- + usb_device_request_t ctrl = pcd->setup_pkt->req;
- +
- + if (ctrl.bmRequestType == UT_DEVICE) {
- + dcfg_data_t dcfg = {.d32 = 0 };
- +
- +#ifdef DEBUG_EP0
- +// DWC_DEBUGPL(DBG_PCDV, "SET_ADDRESS:%d\n", ctrl.wValue);
- +#endif
- + dcfg.b.devaddr = UGETW(ctrl.wValue);
- + DWC_MODIFY_REG32(&dev_if->dev_global_regs->dcfg, 0, dcfg.d32);
- + do_setup_in_status_phase(pcd);
- + }
- +}
- +
- +/**
- + * This function processes SETUP commands. In Linux, the USB Command
- + * processing is done in two places - the first being the PCD and the
- + * second in the Gadget Driver (for example, the File-Backed Storage
- + * Gadget Driver).
- + *
- + * <table>
- + * <tr><td>Command </td><td>Driver </td><td>Description</td></tr>
- + *
- + * <tr><td>GET_STATUS </td><td>PCD </td><td>Command is processed as
- + * defined in chapter 9 of the USB 2.0 Specification chapter 9
- + * </td></tr>
- + *
- + * <tr><td>CLEAR_FEATURE </td><td>PCD </td><td>The Device and Endpoint
- + * requests are the ENDPOINT_HALT feature is procesed, all others the
- + * interface requests are ignored.</td></tr>
- + *
- + * <tr><td>SET_FEATURE </td><td>PCD </td><td>The Device and Endpoint
- + * requests are processed by the PCD. Interface requests are passed
- + * to the Gadget Driver.</td></tr>
- + *
- + * <tr><td>SET_ADDRESS </td><td>PCD </td><td>Program the DCFG reg,
- + * with device address received </td></tr>
- + *
- + * <tr><td>GET_DESCRIPTOR </td><td>Gadget Driver </td><td>Return the
- + * requested descriptor</td></tr>
- + *
- + * <tr><td>SET_DESCRIPTOR </td><td>Gadget Driver </td><td>Optional -
- + * not implemented by any of the existing Gadget Drivers.</td></tr>
- + *
- + * <tr><td>SET_CONFIGURATION </td><td>Gadget Driver </td><td>Disable
- + * all EPs and enable EPs for new configuration.</td></tr>
- + *
- + * <tr><td>GET_CONFIGURATION </td><td>Gadget Driver </td><td>Return
- + * the current configuration</td></tr>
- + *
- + * <tr><td>SET_INTERFACE </td><td>Gadget Driver </td><td>Disable all
- + * EPs and enable EPs for new configuration.</td></tr>
- + *
- + * <tr><td>GET_INTERFACE </td><td>Gadget Driver </td><td>Return the
- + * current interface.</td></tr>
- + *
- + * <tr><td>SYNC_FRAME </td><td>PCD </td><td>Display debug
- + * message.</td></tr>
- + * </table>
- + *
- + * When the SETUP Phase Done interrupt occurs, the PCD SETUP commands are
- + * processed by pcd_setup. Calling the Function Driver's setup function from
- + * pcd_setup processes the gadget SETUP commands.
- + */
- +static inline void pcd_setup(dwc_otg_pcd_t * pcd)
- +{
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
- + dwc_otg_dev_if_t *dev_if = core_if->dev_if;
- + usb_device_request_t ctrl = pcd->setup_pkt->req;
- + dwc_otg_pcd_ep_t *ep0 = &pcd->ep0;
- +
- + deptsiz0_data_t doeptsize0 = {.d32 = 0 };
- +
- +#ifdef DWC_UTE_CFI
- + int retval = 0;
- + struct cfi_usb_ctrlrequest cfi_req;
- +#endif
- +
- + doeptsize0.d32 = DWC_READ_REG32(&dev_if->out_ep_regs[0]->doeptsiz);
- +
- + /** In BDMA more then 1 setup packet is not supported till 3.00a */
- + if (core_if->dma_enable && core_if->dma_desc_enable == 0
- + && (doeptsize0.b.supcnt < 2)
- + && (core_if->snpsid < OTG_CORE_REV_2_94a)) {
- + DWC_ERROR
- + ("\n\n----------- CANNOT handle > 1 setup packet in DMA mode\n\n");
- + }
- + if ((core_if->snpsid >= OTG_CORE_REV_3_00a)
- + && (core_if->dma_enable == 1) && (core_if->dma_desc_enable == 0)) {
- + ctrl =
- + (pcd->setup_pkt +
- + (3 - doeptsize0.b.supcnt - 1 +
- + ep0->dwc_ep.stp_rollover))->req;
- + }
- +#ifdef DEBUG_EP0
- + DWC_DEBUGPL(DBG_PCD, "SETUP %02x.%02x v%04x i%04x l%04x\n",
- + ctrl.bmRequestType, ctrl.bRequest,
- + UGETW(ctrl.wValue), UGETW(ctrl.wIndex),
- + UGETW(ctrl.wLength));
- +#endif
- +
- + /* Clean up the request queue */
- + dwc_otg_request_nuke(ep0);
- + ep0->stopped = 0;
- +
- + if (ctrl.bmRequestType & UE_DIR_IN) {
- + ep0->dwc_ep.is_in = 1;
- + pcd->ep0state = EP0_IN_DATA_PHASE;
- + } else {
- + ep0->dwc_ep.is_in = 0;
- + pcd->ep0state = EP0_OUT_DATA_PHASE;
- + }
- +
- + if (UGETW(ctrl.wLength) == 0) {
- + ep0->dwc_ep.is_in = 1;
- + pcd->ep0state = EP0_IN_STATUS_PHASE;
- + }
- +
- + if (UT_GET_TYPE(ctrl.bmRequestType) != UT_STANDARD) {
- +
- +#ifdef DWC_UTE_CFI
- + DWC_MEMCPY(&cfi_req, &ctrl, sizeof(usb_device_request_t));
- +
- + //printk(KERN_ALERT "CFI: req_type=0x%02x; req=0x%02x\n",
- + ctrl.bRequestType, ctrl.bRequest);
- + if (UT_GET_TYPE(cfi_req.bRequestType) == UT_VENDOR) {
- + if (cfi_req.bRequest > 0xB0 && cfi_req.bRequest < 0xBF) {
- + retval = cfi_setup(pcd, &cfi_req);
- + if (retval < 0) {
- + ep0_do_stall(pcd, retval);
- + pcd->ep0_pending = 0;
- + return;
- + }
- +
- + /* if need gadget setup then call it and check the retval */
- + if (pcd->cfi->need_gadget_att) {
- + retval =
- + cfi_gadget_setup(pcd,
- + &pcd->
- + cfi->ctrl_req);
- + if (retval < 0) {
- + pcd->ep0_pending = 0;
- + return;
- + }
- + }
- +
- + if (pcd->cfi->need_status_in_complete) {
- + do_setup_in_status_phase(pcd);
- + }
- + return;
- + }
- + }
- +#endif
- +
- + /* handle non-standard (class/vendor) requests in the gadget driver */
- + do_gadget_setup(pcd, &ctrl);
- + return;
- + }
- +
- + /** @todo NGS: Handle bad setup packet? */
- +
- +///////////////////////////////////////////
- +//// --- Standard Request handling --- ////
- +
- + switch (ctrl.bRequest) {
- + case UR_GET_STATUS:
- + do_get_status(pcd);
- + break;
- +
- + case UR_CLEAR_FEATURE:
- + do_clear_feature(pcd);
- + break;
- +
- + case UR_SET_FEATURE:
- + do_set_feature(pcd);
- + break;
- +
- + case UR_SET_ADDRESS:
- + do_set_address(pcd);
- + break;
- +
- + case UR_SET_INTERFACE:
- + case UR_SET_CONFIG:
- +// _pcd->request_config = 1; /* Configuration changed */
- + do_gadget_setup(pcd, &ctrl);
- + break;
- +
- + case UR_SYNCH_FRAME:
- + do_gadget_setup(pcd, &ctrl);
- + break;
- +
- + default:
- + /* Call the Gadget Driver's setup functions */
- + do_gadget_setup(pcd, &ctrl);
- + break;
- + }
- +}
- +
- +/**
- + * This function completes the ep0 control transfer.
- + */
- +static int32_t ep0_complete_request(dwc_otg_pcd_ep_t * ep)
- +{
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(ep->pcd);
- + dwc_otg_dev_if_t *dev_if = core_if->dev_if;
- + dwc_otg_dev_in_ep_regs_t *in_ep_regs =
- + dev_if->in_ep_regs[ep->dwc_ep.num];
- +#ifdef DEBUG_EP0
- + dwc_otg_dev_out_ep_regs_t *out_ep_regs =
- + dev_if->out_ep_regs[ep->dwc_ep.num];
- +#endif
- + deptsiz0_data_t deptsiz;
- + dev_dma_desc_sts_t desc_sts;
- + dwc_otg_pcd_request_t *req;
- + int is_last = 0;
- + dwc_otg_pcd_t *pcd = ep->pcd;
- +
- +#ifdef DWC_UTE_CFI
- + struct cfi_usb_ctrlrequest *ctrlreq;
- + int retval = -DWC_E_NOT_SUPPORTED;
- +#endif
- +
- + desc_sts.b.bytes = 0;
- +
- + if (pcd->ep0_pending && DWC_CIRCLEQ_EMPTY(&ep->queue)) {
- + if (ep->dwc_ep.is_in) {
- +#ifdef DEBUG_EP0
- + DWC_DEBUGPL(DBG_PCDV, "Do setup OUT status phase\n");
- +#endif
- + do_setup_out_status_phase(pcd);
- + } else {
- +#ifdef DEBUG_EP0
- + DWC_DEBUGPL(DBG_PCDV, "Do setup IN status phase\n");
- +#endif
- +
- +#ifdef DWC_UTE_CFI
- + ctrlreq = &pcd->cfi->ctrl_req;
- +
- + if (UT_GET_TYPE(ctrlreq->bRequestType) == UT_VENDOR) {
- + if (ctrlreq->bRequest > 0xB0
- + && ctrlreq->bRequest < 0xBF) {
- +
- + /* Return if the PCD failed to handle the request */
- + if ((retval =
- + pcd->cfi->ops.
- + ctrl_write_complete(pcd->cfi,
- + pcd)) < 0) {
- + CFI_INFO
- + ("ERROR setting a new value in the PCD(%d)\n",
- + retval);
- + ep0_do_stall(pcd, retval);
- + pcd->ep0_pending = 0;
- + return 0;
- + }
- +
- + /* If the gadget needs to be notified on the request */
- + if (pcd->cfi->need_gadget_att == 1) {
- + //retval = do_gadget_setup(pcd, &pcd->cfi->ctrl_req);
- + retval =
- + cfi_gadget_setup(pcd,
- + &pcd->cfi->
- + ctrl_req);
- +
- + /* Return from the function if the gadget failed to process
- + * the request properly - this should never happen !!!
- + */
- + if (retval < 0) {
- + CFI_INFO
- + ("ERROR setting a new value in the gadget(%d)\n",
- + retval);
- + pcd->ep0_pending = 0;
- + return 0;
- + }
- + }
- +
- + CFI_INFO("%s: RETVAL=%d\n", __func__,
- + retval);
- + /* If we hit here then the PCD and the gadget has properly
- + * handled the request - so send the ZLP IN to the host.
- + */
- + /* @todo: MAS - decide whether we need to start the setup
- + * stage based on the need_setup value of the cfi object
- + */
- + do_setup_in_status_phase(pcd);
- + pcd->ep0_pending = 0;
- + return 1;
- + }
- + }
- +#endif
- +
- + do_setup_in_status_phase(pcd);
- + }
- + pcd->ep0_pending = 0;
- + return 1;
- + }
- +
- + if (DWC_CIRCLEQ_EMPTY(&ep->queue)) {
- + return 0;
- + }
- + req = DWC_CIRCLEQ_FIRST(&ep->queue);
- +
- + if (pcd->ep0state == EP0_OUT_STATUS_PHASE
- + || pcd->ep0state == EP0_IN_STATUS_PHASE) {
- + is_last = 1;
- + } else if (ep->dwc_ep.is_in) {
- + deptsiz.d32 = DWC_READ_REG32(&in_ep_regs->dieptsiz);
- + if (core_if->dma_desc_enable != 0)
- + desc_sts = dev_if->in_desc_addr->status;
- +#ifdef DEBUG_EP0
- + DWC_DEBUGPL(DBG_PCDV, "%d len=%d xfersize=%d pktcnt=%d\n",
- + ep->dwc_ep.num, ep->dwc_ep.xfer_len,
- + deptsiz.b.xfersize, deptsiz.b.pktcnt);
- +#endif
- +
- + if (((core_if->dma_desc_enable == 0)
- + && (deptsiz.b.xfersize == 0))
- + || ((core_if->dma_desc_enable != 0)
- + && (desc_sts.b.bytes == 0))) {
- + req->actual = ep->dwc_ep.xfer_count;
- + /* Is a Zero Len Packet needed? */
- + if (req->sent_zlp) {
- +#ifdef DEBUG_EP0
- + DWC_DEBUGPL(DBG_PCD, "Setup Rx ZLP\n");
- +#endif
- + req->sent_zlp = 0;
- + }
- + do_setup_out_status_phase(pcd);
- + }
- + } else {
- + /* ep0-OUT */
- +#ifdef DEBUG_EP0
- + deptsiz.d32 = DWC_READ_REG32(&out_ep_regs->doeptsiz);
- + DWC_DEBUGPL(DBG_PCDV, "%d len=%d xsize=%d pktcnt=%d\n",
- + ep->dwc_ep.num, ep->dwc_ep.xfer_len,
- + deptsiz.b.xfersize, deptsiz.b.pktcnt);
- +#endif
- + req->actual = ep->dwc_ep.xfer_count;
- +
- + /* Is a Zero Len Packet needed? */
- + if (req->sent_zlp) {
- +#ifdef DEBUG_EP0
- + DWC_DEBUGPL(DBG_PCDV, "Setup Tx ZLP\n");
- +#endif
- + req->sent_zlp = 0;
- + }
- + /* For older cores do setup in status phase in Slave/BDMA modes,
- + * starting from 3.00 do that only in slave, and for DMA modes
- + * just re-enable ep 0 OUT here*/
- + if (core_if->dma_enable == 0
- + || (core_if->dma_desc_enable == 0
- + && core_if->snpsid <= OTG_CORE_REV_2_94a)) {
- + do_setup_in_status_phase(pcd);
- + } else if (core_if->snpsid >= OTG_CORE_REV_3_00a) {
- + DWC_DEBUGPL(DBG_PCDV,
- + "Enable out ep before in status phase\n");
- + ep0_out_start(core_if, pcd);
- + }
- + }
- +
- + /* Complete the request */
- + if (is_last) {
- + dwc_otg_request_done(ep, req, 0);
- + ep->dwc_ep.start_xfer_buff = 0;
- + ep->dwc_ep.xfer_buff = 0;
- + ep->dwc_ep.xfer_len = 0;
- + return 1;
- + }
- + return 0;
- +}
- +
- +#ifdef DWC_UTE_CFI
- +/**
- + * This function calculates traverses all the CFI DMA descriptors and
- + * and accumulates the bytes that are left to be transfered.
- + *
- + * @return The total bytes left to transfered, or a negative value as failure
- + */
- +static inline int cfi_calc_desc_residue(dwc_otg_pcd_ep_t * ep)
- +{
- + int32_t ret = 0;
- + int i;
- + struct dwc_otg_dma_desc *ddesc = NULL;
- + struct cfi_ep *cfiep;
- +
- + /* See if the pcd_ep has its respective cfi_ep mapped */
- + cfiep = get_cfi_ep_by_pcd_ep(ep->pcd->cfi, ep);
- + if (!cfiep) {
- + CFI_INFO("%s: Failed to find ep\n", __func__);
- + return -1;
- + }
- +
- + ddesc = ep->dwc_ep.descs;
- +
- + for (i = 0; (i < cfiep->desc_count) && (i < MAX_DMA_DESCS_PER_EP); i++) {
- +
- +#if defined(PRINT_CFI_DMA_DESCS)
- + print_desc(ddesc, ep->ep.name, i);
- +#endif
- + ret += ddesc->status.b.bytes;
- + ddesc++;
- + }
- +
- + if (ret)
- + CFI_INFO("!!!!!!!!!! WARNING (%s) - residue=%d\n", __func__,
- + ret);
- +
- + return ret;
- +}
- +#endif
- +
- +/**
- + * This function completes the request for the EP. If there are
- + * additional requests for the EP in the queue they will be started.
- + */
- +static void complete_ep(dwc_otg_pcd_ep_t * ep)
- +{
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(ep->pcd);
- + struct device *dev = dwc_otg_pcd_to_dev(ep->pcd);
- + dwc_otg_dev_if_t *dev_if = core_if->dev_if;
- + dwc_otg_dev_in_ep_regs_t *in_ep_regs =
- + dev_if->in_ep_regs[ep->dwc_ep.num];
- + deptsiz_data_t deptsiz;
- + dev_dma_desc_sts_t desc_sts;
- + dwc_otg_pcd_request_t *req = 0;
- + dwc_otg_dev_dma_desc_t *dma_desc;
- + uint32_t byte_count = 0;
- + int is_last = 0;
- + int i;
- +
- + DWC_DEBUGPL(DBG_PCDV, "%s() %d-%s\n", __func__, ep->dwc_ep.num,
- + (ep->dwc_ep.is_in ? "IN" : "OUT"));
- +
- + /* Get any pending requests */
- + if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) {
- + req = DWC_CIRCLEQ_FIRST(&ep->queue);
- + if (!req) {
- + DWC_PRINTF("complete_ep 0x%p, req = NULL!\n", ep);
- + return;
- + }
- + } else {
- + DWC_PRINTF("complete_ep 0x%p, ep->queue empty!\n", ep);
- + return;
- + }
- +
- + DWC_DEBUGPL(DBG_PCD, "Requests %d\n", ep->pcd->request_pending);
- +
- + if (ep->dwc_ep.is_in) {
- + deptsiz.d32 = DWC_READ_REG32(&in_ep_regs->dieptsiz);
- +
- + if (core_if->dma_enable) {
- + if (core_if->dma_desc_enable == 0) {
- + if (deptsiz.b.xfersize == 0
- + && deptsiz.b.pktcnt == 0) {
- + byte_count =
- + ep->dwc_ep.xfer_len -
- + ep->dwc_ep.xfer_count;
- +
- + ep->dwc_ep.xfer_buff += byte_count;
- + ep->dwc_ep.dma_addr += byte_count;
- + ep->dwc_ep.xfer_count += byte_count;
- +
- + DWC_DEBUGPL(DBG_PCDV,
- + "%d-%s len=%d xfersize=%d pktcnt=%d\n",
- + ep->dwc_ep.num,
- + (ep->dwc_ep.
- + is_in ? "IN" : "OUT"),
- + ep->dwc_ep.xfer_len,
- + deptsiz.b.xfersize,
- + deptsiz.b.pktcnt);
- +
- + if (ep->dwc_ep.xfer_len <
- + ep->dwc_ep.total_len) {
- + dwc_otg_ep_start_transfer
- + (core_if, &ep->dwc_ep);
- + } else if (ep->dwc_ep.sent_zlp) {
- + /*
- + * This fragment of code should initiate 0
- + * length transfer in case if it is queued
- + * a transfer with size divisible to EPs max
- + * packet size and with usb_request zero field
- + * is set, which means that after data is transfered,
- + * it is also should be transfered
- + * a 0 length packet at the end. For Slave and
- + * Buffer DMA modes in this case SW has
- + * to initiate 2 transfers one with transfer size,
- + * and the second with 0 size. For Descriptor
- + * DMA mode SW is able to initiate a transfer,
- + * which will handle all the packets including
- + * the last 0 length.
- + */
- + ep->dwc_ep.sent_zlp = 0;
- + dwc_otg_ep_start_zl_transfer
- + (core_if, &ep->dwc_ep);
- + } else {
- + is_last = 1;
- + }
- + } else {
- + if (ep->dwc_ep.type ==
- + DWC_OTG_EP_TYPE_ISOC) {
- + req->actual = 0;
- + dwc_otg_request_done(ep, req, 0);
- +
- + ep->dwc_ep.start_xfer_buff = 0;
- + ep->dwc_ep.xfer_buff = 0;
- + ep->dwc_ep.xfer_len = 0;
- +
- + /* If there is a request in the queue start it. */
- + start_next_request(ep);
- + } else
- + DWC_WARN
- + ("Incomplete transfer (%d - %s [siz=%d pkt=%d])\n",
- + ep->dwc_ep.num,
- + (ep->dwc_ep.is_in ? "IN" : "OUT"),
- + deptsiz.b.xfersize,
- + deptsiz.b.pktcnt);
- + }
- + } else {
- + dma_desc = ep->dwc_ep.desc_addr;
- + byte_count = 0;
- + ep->dwc_ep.sent_zlp = 0;
- +
- +#ifdef DWC_UTE_CFI
- + CFI_INFO("%s: BUFFER_MODE=%d\n", __func__,
- + ep->dwc_ep.buff_mode);
- + if (ep->dwc_ep.buff_mode != BM_STANDARD) {
- + int residue;
- +
- + residue = cfi_calc_desc_residue(ep);
- + if (residue < 0)
- + return;
- +
- + byte_count = residue;
- + } else {
- +#endif
- + for (i = 0; i < ep->dwc_ep.desc_cnt;
- + ++i) {
- + desc_sts = dma_desc->status;
- + byte_count += desc_sts.b.bytes;
- + dma_desc++;
- + }
- +#ifdef DWC_UTE_CFI
- + }
- +#endif
- + if (byte_count == 0) {
- + ep->dwc_ep.xfer_count =
- + ep->dwc_ep.total_len;
- + is_last = 1;
- + } else {
- + DWC_WARN("Incomplete transfer\n");
- + }
- + }
- + } else {
- + if (deptsiz.b.xfersize == 0 && deptsiz.b.pktcnt == 0) {
- + DWC_DEBUGPL(DBG_PCDV,
- + "%d-%s len=%d xfersize=%d pktcnt=%d\n",
- + ep->dwc_ep.num,
- + ep->dwc_ep.is_in ? "IN" : "OUT",
- + ep->dwc_ep.xfer_len,
- + deptsiz.b.xfersize,
- + deptsiz.b.pktcnt);
- +
- + /* Check if the whole transfer was completed,
- + * if no, setup transfer for next portion of data
- + */
- + if (ep->dwc_ep.xfer_len < ep->dwc_ep.total_len) {
- + dwc_otg_ep_start_transfer(core_if,
- + &ep->dwc_ep);
- + } else if (ep->dwc_ep.sent_zlp) {
- + /*
- + * This fragment of code should initiate 0
- + * length trasfer in case if it is queued
- + * a trasfer with size divisible to EPs max
- + * packet size and with usb_request zero field
- + * is set, which means that after data is transfered,
- + * it is also should be transfered
- + * a 0 length packet at the end. For Slave and
- + * Buffer DMA modes in this case SW has
- + * to initiate 2 transfers one with transfer size,
- + * and the second with 0 size. For Desriptor
- + * DMA mode SW is able to initiate a transfer,
- + * which will handle all the packets including
- + * the last 0 legth.
- + */
- + ep->dwc_ep.sent_zlp = 0;
- + dwc_otg_ep_start_zl_transfer(core_if,
- + &ep->dwc_ep);
- + } else {
- + is_last = 1;
- + }
- + } else {
- + DWC_WARN
- + ("Incomplete transfer (%d-%s [siz=%d pkt=%d])\n",
- + ep->dwc_ep.num,
- + (ep->dwc_ep.is_in ? "IN" : "OUT"),
- + deptsiz.b.xfersize, deptsiz.b.pktcnt);
- + }
- + }
- + } else {
- + dwc_otg_dev_out_ep_regs_t *out_ep_regs =
- + dev_if->out_ep_regs[ep->dwc_ep.num];
- + desc_sts.d32 = 0;
- + if (core_if->dma_enable) {
- + if (core_if->dma_desc_enable) {
- + dma_desc = ep->dwc_ep.desc_addr;
- + byte_count = 0;
- + ep->dwc_ep.sent_zlp = 0;
- +
- +#ifdef DWC_UTE_CFI
- + CFI_INFO("%s: BUFFER_MODE=%d\n", __func__,
- + ep->dwc_ep.buff_mode);
- + if (ep->dwc_ep.buff_mode != BM_STANDARD) {
- + int residue;
- + residue = cfi_calc_desc_residue(ep);
- + if (residue < 0)
- + return;
- + byte_count = residue;
- + } else {
- +#endif
- +
- + for (i = 0; i < ep->dwc_ep.desc_cnt;
- + ++i) {
- + desc_sts = dma_desc->status;
- + byte_count += desc_sts.b.bytes;
- + dma_desc++;
- + }
- +
- +#ifdef DWC_UTE_CFI
- + }
- +#endif
- + /* Checking for interrupt Out transfers with not
- + * dword aligned mps sizes
- + */
- + if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_INTR &&
- + (ep->dwc_ep.maxpacket%4)) {
- + ep->dwc_ep.xfer_count =
- + ep->dwc_ep.total_len - byte_count;
- + if ((ep->dwc_ep.xfer_len %
- + ep->dwc_ep.maxpacket)
- + && (ep->dwc_ep.xfer_len /
- + ep->dwc_ep.maxpacket <
- + MAX_DMA_DESC_CNT))
- + ep->dwc_ep.xfer_len -=
- + (ep->dwc_ep.desc_cnt -
- + 1) * ep->dwc_ep.maxpacket +
- + ep->dwc_ep.xfer_len %
- + ep->dwc_ep.maxpacket;
- + else
- + ep->dwc_ep.xfer_len -=
- + ep->dwc_ep.desc_cnt *
- + ep->dwc_ep.maxpacket;
- + if (ep->dwc_ep.xfer_len > 0) {
- + dwc_otg_ep_start_transfer
- + (core_if, &ep->dwc_ep);
- + } else {
- + is_last = 1;
- + }
- + } else {
- + ep->dwc_ep.xfer_count =
- + ep->dwc_ep.total_len - byte_count +
- + ((4 -
- + (ep->dwc_ep.
- + total_len & 0x3)) & 0x3);
- + is_last = 1;
- + }
- + } else {
- + deptsiz.d32 = 0;
- + deptsiz.d32 =
- + DWC_READ_REG32(&out_ep_regs->doeptsiz);
- +
- + byte_count = (ep->dwc_ep.xfer_len -
- + ep->dwc_ep.xfer_count -
- + deptsiz.b.xfersize);
- + ep->dwc_ep.xfer_buff += byte_count;
- + ep->dwc_ep.dma_addr += byte_count;
- + ep->dwc_ep.xfer_count += byte_count;
- +
- + /* Check if the whole transfer was completed,
- + * if no, setup transfer for next portion of data
- + */
- + if (ep->dwc_ep.xfer_len < ep->dwc_ep.total_len) {
- + dwc_otg_ep_start_transfer(core_if,
- + &ep->dwc_ep);
- + } else if (ep->dwc_ep.sent_zlp) {
- + /*
- + * This fragment of code should initiate 0
- + * length trasfer in case if it is queued
- + * a trasfer with size divisible to EPs max
- + * packet size and with usb_request zero field
- + * is set, which means that after data is transfered,
- + * it is also should be transfered
- + * a 0 length packet at the end. For Slave and
- + * Buffer DMA modes in this case SW has
- + * to initiate 2 transfers one with transfer size,
- + * and the second with 0 size. For Desriptor
- + * DMA mode SW is able to initiate a transfer,
- + * which will handle all the packets including
- + * the last 0 legth.
- + */
- + ep->dwc_ep.sent_zlp = 0;
- + dwc_otg_ep_start_zl_transfer(core_if,
- + &ep->dwc_ep);
- + } else {
- + is_last = 1;
- + }
- + }
- + } else {
- + /* Check if the whole transfer was completed,
- + * if no, setup transfer for next portion of data
- + */
- + if (ep->dwc_ep.xfer_len < ep->dwc_ep.total_len) {
- + dwc_otg_ep_start_transfer(core_if, &ep->dwc_ep);
- + } else if (ep->dwc_ep.sent_zlp) {
- + /*
- + * This fragment of code should initiate 0
- + * length transfer in case if it is queued
- + * a transfer with size divisible to EPs max
- + * packet size and with usb_request zero field
- + * is set, which means that after data is transfered,
- + * it is also should be transfered
- + * a 0 length packet at the end. For Slave and
- + * Buffer DMA modes in this case SW has
- + * to initiate 2 transfers one with transfer size,
- + * and the second with 0 size. For Descriptor
- + * DMA mode SW is able to initiate a transfer,
- + * which will handle all the packets including
- + * the last 0 length.
- + */
- + ep->dwc_ep.sent_zlp = 0;
- + dwc_otg_ep_start_zl_transfer(core_if,
- + &ep->dwc_ep);
- + } else {
- + is_last = 1;
- + }
- + }
- +
- + DWC_DEBUGPL(DBG_PCDV,
- + "addr %p, %d-%s len=%d cnt=%d xsize=%d pktcnt=%d\n",
- + &out_ep_regs->doeptsiz, ep->dwc_ep.num,
- + ep->dwc_ep.is_in ? "IN" : "OUT",
- + ep->dwc_ep.xfer_len, ep->dwc_ep.xfer_count,
- + deptsiz.b.xfersize, deptsiz.b.pktcnt);
- + }
- +
- + /* Complete the request */
- + if (is_last) {
- +#ifdef DWC_UTE_CFI
- + if (ep->dwc_ep.buff_mode != BM_STANDARD) {
- + req->actual = ep->dwc_ep.cfi_req_len - byte_count;
- + } else {
- +#endif
- + req->actual = ep->dwc_ep.xfer_count;
- +#ifdef DWC_UTE_CFI
- + }
- +#endif
- + if (req->dw_align_buf) {
- + if (!ep->dwc_ep.is_in) {
- + dwc_memcpy(req->buf, req->dw_align_buf, req->length);
- + }
- + DWC_DMA_FREE(dev, req->length, req->dw_align_buf,
- + req->dw_align_buf_dma);
- + }
- +
- + dwc_otg_request_done(ep, req, 0);
- +
- + ep->dwc_ep.start_xfer_buff = 0;
- + ep->dwc_ep.xfer_buff = 0;
- + ep->dwc_ep.xfer_len = 0;
- +
- + /* If there is a request in the queue start it. */
- + start_next_request(ep);
- + }
- +}
- +
- +#ifdef DWC_EN_ISOC
- +
- +/**
- + * This function BNA interrupt for Isochronous EPs
- + *
- + */
- +static void dwc_otg_pcd_handle_iso_bna(dwc_otg_pcd_ep_t * ep)
- +{
- + dwc_ep_t *dwc_ep = &ep->dwc_ep;
- + volatile uint32_t *addr;
- + depctl_data_t depctl = {.d32 = 0 };
- + dwc_otg_pcd_t *pcd = ep->pcd;
- + dwc_otg_dev_dma_desc_t *dma_desc;
- + int i;
- +
- + dma_desc =
- + dwc_ep->iso_desc_addr + dwc_ep->desc_cnt * (dwc_ep->proc_buf_num);
- +
- + if (dwc_ep->is_in) {
- + dev_dma_desc_sts_t sts = {.d32 = 0 };
- + for (i = 0; i < dwc_ep->desc_cnt; ++i, ++dma_desc) {
- + sts.d32 = dma_desc->status.d32;
- + sts.b_iso_in.bs = BS_HOST_READY;
- + dma_desc->status.d32 = sts.d32;
- + }
- + } else {
- + dev_dma_desc_sts_t sts = {.d32 = 0 };
- + for (i = 0; i < dwc_ep->desc_cnt; ++i, ++dma_desc) {
- + sts.d32 = dma_desc->status.d32;
- + sts.b_iso_out.bs = BS_HOST_READY;
- + dma_desc->status.d32 = sts.d32;
- + }
- + }
- +
- + if (dwc_ep->is_in == 0) {
- + addr =
- + &GET_CORE_IF(pcd)->dev_if->out_ep_regs[dwc_ep->
- + num]->doepctl;
- + } else {
- + addr =
- + &GET_CORE_IF(pcd)->dev_if->in_ep_regs[dwc_ep->num]->diepctl;
- + }
- + depctl.b.epena = 1;
- + DWC_MODIFY_REG32(addr, depctl.d32, depctl.d32);
- +}
- +
- +/**
- + * This function sets latest iso packet information(non-PTI mode)
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param ep The EP to start the transfer on.
- + *
- + */
- +void set_current_pkt_info(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
- +{
- + deptsiz_data_t deptsiz = {.d32 = 0 };
- + dma_addr_t dma_addr;
- + uint32_t offset;
- +
- + if (ep->proc_buf_num)
- + dma_addr = ep->dma_addr1;
- + else
- + dma_addr = ep->dma_addr0;
- +
- + if (ep->is_in) {
- + deptsiz.d32 =
- + DWC_READ_REG32(&core_if->dev_if->
- + in_ep_regs[ep->num]->dieptsiz);
- + offset = ep->data_per_frame;
- + } else {
- + deptsiz.d32 =
- + DWC_READ_REG32(&core_if->dev_if->
- + out_ep_regs[ep->num]->doeptsiz);
- + offset =
- + ep->data_per_frame +
- + (0x4 & (0x4 - (ep->data_per_frame & 0x3)));
- + }
- +
- + if (!deptsiz.b.xfersize) {
- + ep->pkt_info[ep->cur_pkt].length = ep->data_per_frame;
- + ep->pkt_info[ep->cur_pkt].offset =
- + ep->cur_pkt_dma_addr - dma_addr;
- + ep->pkt_info[ep->cur_pkt].status = 0;
- + } else {
- + ep->pkt_info[ep->cur_pkt].length = ep->data_per_frame;
- + ep->pkt_info[ep->cur_pkt].offset =
- + ep->cur_pkt_dma_addr - dma_addr;
- + ep->pkt_info[ep->cur_pkt].status = -DWC_E_NO_DATA;
- + }
- + ep->cur_pkt_addr += offset;
- + ep->cur_pkt_dma_addr += offset;
- + ep->cur_pkt++;
- +}
- +
- +/**
- + * This function sets latest iso packet information(DDMA mode)
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param dwc_ep The EP to start the transfer on.
- + *
- + */
- +static void set_ddma_iso_pkts_info(dwc_otg_core_if_t * core_if,
- + dwc_ep_t * dwc_ep)
- +{
- + dwc_otg_dev_dma_desc_t *dma_desc;
- + dev_dma_desc_sts_t sts = {.d32 = 0 };
- + iso_pkt_info_t *iso_packet;
- + uint32_t data_per_desc;
- + uint32_t offset;
- + int i, j;
- +
- + iso_packet = dwc_ep->pkt_info;
- +
- + /** Reinit closed DMA Descriptors*/
- + /** ISO OUT EP */
- + if (dwc_ep->is_in == 0) {
- + dma_desc =
- + dwc_ep->iso_desc_addr +
- + dwc_ep->desc_cnt * dwc_ep->proc_buf_num;
- + offset = 0;
- +
- + for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm;
- + i += dwc_ep->pkt_per_frm) {
- + for (j = 0; j < dwc_ep->pkt_per_frm; ++j) {
- + data_per_desc =
- + ((j + 1) * dwc_ep->maxpacket >
- + dwc_ep->
- + data_per_frame) ? dwc_ep->data_per_frame -
- + j * dwc_ep->maxpacket : dwc_ep->maxpacket;
- + data_per_desc +=
- + (data_per_desc % 4) ? (4 -
- + data_per_desc %
- + 4) : 0;
- +
- + sts.d32 = dma_desc->status.d32;
- +
- + /* Write status in iso_packet_decsriptor */
- + iso_packet->status =
- + sts.b_iso_out.rxsts +
- + (sts.b_iso_out.bs ^ BS_DMA_DONE);
- + if (iso_packet->status) {
- + iso_packet->status = -DWC_E_NO_DATA;
- + }
- +
- + /* Received data length */
- + if (!sts.b_iso_out.rxbytes) {
- + iso_packet->length =
- + data_per_desc -
- + sts.b_iso_out.rxbytes;
- + } else {
- + iso_packet->length =
- + data_per_desc -
- + sts.b_iso_out.rxbytes + (4 -
- + dwc_ep->data_per_frame
- + % 4);
- + }
- +
- + iso_packet->offset = offset;
- +
- + offset += data_per_desc;
- + dma_desc++;
- + iso_packet++;
- + }
- + }
- +
- + for (j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) {
- + data_per_desc =
- + ((j + 1) * dwc_ep->maxpacket >
- + dwc_ep->data_per_frame) ? dwc_ep->data_per_frame -
- + j * dwc_ep->maxpacket : dwc_ep->maxpacket;
- + data_per_desc +=
- + (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0;
- +
- + sts.d32 = dma_desc->status.d32;
- +
- + /* Write status in iso_packet_decsriptor */
- + iso_packet->status =
- + sts.b_iso_out.rxsts +
- + (sts.b_iso_out.bs ^ BS_DMA_DONE);
- + if (iso_packet->status) {
- + iso_packet->status = -DWC_E_NO_DATA;
- + }
- +
- + /* Received data length */
- + iso_packet->length =
- + dwc_ep->data_per_frame - sts.b_iso_out.rxbytes;
- +
- + iso_packet->offset = offset;
- +
- + offset += data_per_desc;
- + iso_packet++;
- + dma_desc++;
- + }
- +
- + sts.d32 = dma_desc->status.d32;
- +
- + /* Write status in iso_packet_decsriptor */
- + iso_packet->status =
- + sts.b_iso_out.rxsts + (sts.b_iso_out.bs ^ BS_DMA_DONE);
- + if (iso_packet->status) {
- + iso_packet->status = -DWC_E_NO_DATA;
- + }
- + /* Received data length */
- + if (!sts.b_iso_out.rxbytes) {
- + iso_packet->length =
- + dwc_ep->data_per_frame - sts.b_iso_out.rxbytes;
- + } else {
- + iso_packet->length =
- + dwc_ep->data_per_frame - sts.b_iso_out.rxbytes +
- + (4 - dwc_ep->data_per_frame % 4);
- + }
- +
- + iso_packet->offset = offset;
- + } else {
- +/** ISO IN EP */
- +
- + dma_desc =
- + dwc_ep->iso_desc_addr +
- + dwc_ep->desc_cnt * dwc_ep->proc_buf_num;
- +
- + for (i = 0; i < dwc_ep->desc_cnt - 1; i++) {
- + sts.d32 = dma_desc->status.d32;
- +
- + /* Write status in iso packet descriptor */
- + iso_packet->status =
- + sts.b_iso_in.txsts +
- + (sts.b_iso_in.bs ^ BS_DMA_DONE);
- + if (iso_packet->status != 0) {
- + iso_packet->status = -DWC_E_NO_DATA;
- +
- + }
- + /* Bytes has been transfered */
- + iso_packet->length =
- + dwc_ep->data_per_frame - sts.b_iso_in.txbytes;
- +
- + dma_desc++;
- + iso_packet++;
- + }
- +
- + sts.d32 = dma_desc->status.d32;
- + while (sts.b_iso_in.bs == BS_DMA_BUSY) {
- + sts.d32 = dma_desc->status.d32;
- + }
- +
- + /* Write status in iso packet descriptor ??? do be done with ERROR codes */
- + iso_packet->status =
- + sts.b_iso_in.txsts + (sts.b_iso_in.bs ^ BS_DMA_DONE);
- + if (iso_packet->status != 0) {
- + iso_packet->status = -DWC_E_NO_DATA;
- + }
- +
- + /* Bytes has been transfered */
- + iso_packet->length =
- + dwc_ep->data_per_frame - sts.b_iso_in.txbytes;
- + }
- +}
- +
- +/**
- + * This function reinitialize DMA Descriptors for Isochronous transfer
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param dwc_ep The EP to start the transfer on.
- + *
- + */
- +static void reinit_ddma_iso_xfer(dwc_otg_core_if_t * core_if, dwc_ep_t * dwc_ep)
- +{
- + int i, j;
- + dwc_otg_dev_dma_desc_t *dma_desc;
- + dma_addr_t dma_ad;
- + volatile uint32_t *addr;
- + dev_dma_desc_sts_t sts = {.d32 = 0 };
- + uint32_t data_per_desc;
- +
- + if (dwc_ep->is_in == 0) {
- + addr = &core_if->dev_if->out_ep_regs[dwc_ep->num]->doepctl;
- + } else {
- + addr = &core_if->dev_if->in_ep_regs[dwc_ep->num]->diepctl;
- + }
- +
- + if (dwc_ep->proc_buf_num == 0) {
- + /** Buffer 0 descriptors setup */
- + dma_ad = dwc_ep->dma_addr0;
- + } else {
- + /** Buffer 1 descriptors setup */
- + dma_ad = dwc_ep->dma_addr1;
- + }
- +
- + /** Reinit closed DMA Descriptors*/
- + /** ISO OUT EP */
- + if (dwc_ep->is_in == 0) {
- + dma_desc =
- + dwc_ep->iso_desc_addr +
- + dwc_ep->desc_cnt * dwc_ep->proc_buf_num;
- +
- + sts.b_iso_out.bs = BS_HOST_READY;
- + sts.b_iso_out.rxsts = 0;
- + sts.b_iso_out.l = 0;
- + sts.b_iso_out.sp = 0;
- + sts.b_iso_out.ioc = 0;
- + sts.b_iso_out.pid = 0;
- + sts.b_iso_out.framenum = 0;
- +
- + for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm;
- + i += dwc_ep->pkt_per_frm) {
- + for (j = 0; j < dwc_ep->pkt_per_frm; ++j) {
- + data_per_desc =
- + ((j + 1) * dwc_ep->maxpacket >
- + dwc_ep->
- + data_per_frame) ? dwc_ep->data_per_frame -
- + j * dwc_ep->maxpacket : dwc_ep->maxpacket;
- + data_per_desc +=
- + (data_per_desc % 4) ? (4 -
- + data_per_desc %
- + 4) : 0;
- + sts.b_iso_out.rxbytes = data_per_desc;
- + dma_desc->buf = dma_ad;
- + dma_desc->status.d32 = sts.d32;
- +
- + dma_ad += data_per_desc;
- + dma_desc++;
- + }
- + }
- +
- + for (j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) {
- +
- + data_per_desc =
- + ((j + 1) * dwc_ep->maxpacket >
- + dwc_ep->data_per_frame) ? dwc_ep->data_per_frame -
- + j * dwc_ep->maxpacket : dwc_ep->maxpacket;
- + data_per_desc +=
- + (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0;
- + sts.b_iso_out.rxbytes = data_per_desc;
- +
- + dma_desc->buf = dma_ad;
- + dma_desc->status.d32 = sts.d32;
- +
- + dma_desc++;
- + dma_ad += data_per_desc;
- + }
- +
- + sts.b_iso_out.ioc = 1;
- + sts.b_iso_out.l = dwc_ep->proc_buf_num;
- +
- + data_per_desc =
- + ((j + 1) * dwc_ep->maxpacket >
- + dwc_ep->data_per_frame) ? dwc_ep->data_per_frame -
- + j * dwc_ep->maxpacket : dwc_ep->maxpacket;
- + data_per_desc +=
- + (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0;
- + sts.b_iso_out.rxbytes = data_per_desc;
- +
- + dma_desc->buf = dma_ad;
- + dma_desc->status.d32 = sts.d32;
- + } else {
- +/** ISO IN EP */
- +
- + dma_desc =
- + dwc_ep->iso_desc_addr +
- + dwc_ep->desc_cnt * dwc_ep->proc_buf_num;
- +
- + sts.b_iso_in.bs = BS_HOST_READY;
- + sts.b_iso_in.txsts = 0;
- + sts.b_iso_in.sp = 0;
- + sts.b_iso_in.ioc = 0;
- + sts.b_iso_in.pid = dwc_ep->pkt_per_frm;
- + sts.b_iso_in.framenum = dwc_ep->next_frame;
- + sts.b_iso_in.txbytes = dwc_ep->data_per_frame;
- + sts.b_iso_in.l = 0;
- +
- + for (i = 0; i < dwc_ep->desc_cnt - 1; i++) {
- + dma_desc->buf = dma_ad;
- + dma_desc->status.d32 = sts.d32;
- +
- + sts.b_iso_in.framenum += dwc_ep->bInterval;
- + dma_ad += dwc_ep->data_per_frame;
- + dma_desc++;
- + }
- +
- + sts.b_iso_in.ioc = 1;
- + sts.b_iso_in.l = dwc_ep->proc_buf_num;
- +
- + dma_desc->buf = dma_ad;
- + dma_desc->status.d32 = sts.d32;
- +
- + dwc_ep->next_frame =
- + sts.b_iso_in.framenum + dwc_ep->bInterval * 1;
- + }
- + dwc_ep->proc_buf_num = (dwc_ep->proc_buf_num ^ 1) & 0x1;
- +}
- +
- +/**
- + * This function is to handle Iso EP transfer complete interrupt
- + * in case Iso out packet was dropped
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param dwc_ep The EP for wihich transfer complete was asserted
- + *
- + */
- +static uint32_t handle_iso_out_pkt_dropped(dwc_otg_core_if_t * core_if,
- + dwc_ep_t * dwc_ep)
- +{
- + uint32_t dma_addr;
- + uint32_t drp_pkt;
- + uint32_t drp_pkt_cnt;
- + deptsiz_data_t deptsiz = {.d32 = 0 };
- + depctl_data_t depctl = {.d32 = 0 };
- + int i;
- +
- + deptsiz.d32 =
- + DWC_READ_REG32(&core_if->dev_if->
- + out_ep_regs[dwc_ep->num]->doeptsiz);
- +
- + drp_pkt = dwc_ep->pkt_cnt - deptsiz.b.pktcnt;
- + drp_pkt_cnt = dwc_ep->pkt_per_frm - (drp_pkt % dwc_ep->pkt_per_frm);
- +
- + /* Setting dropped packets status */
- + for (i = 0; i < drp_pkt_cnt; ++i) {
- + dwc_ep->pkt_info[drp_pkt].status = -DWC_E_NO_DATA;
- + drp_pkt++;
- + deptsiz.b.pktcnt--;
- + }
- +
- + if (deptsiz.b.pktcnt > 0) {
- + deptsiz.b.xfersize =
- + dwc_ep->xfer_len - (dwc_ep->pkt_cnt -
- + deptsiz.b.pktcnt) * dwc_ep->maxpacket;
- + } else {
- + deptsiz.b.xfersize = 0;
- + deptsiz.b.pktcnt = 0;
- + }
- +
- + DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[dwc_ep->num]->doeptsiz,
- + deptsiz.d32);
- +
- + if (deptsiz.b.pktcnt > 0) {
- + if (dwc_ep->proc_buf_num) {
- + dma_addr =
- + dwc_ep->dma_addr1 + dwc_ep->xfer_len -
- + deptsiz.b.xfersize;
- + } else {
- + dma_addr =
- + dwc_ep->dma_addr0 + dwc_ep->xfer_len -
- + deptsiz.b.xfersize;;
- + }
- +
- + DWC_WRITE_REG32(&core_if->dev_if->
- + out_ep_regs[dwc_ep->num]->doepdma, dma_addr);
- +
- + /** Re-enable endpoint, clear nak */
- + depctl.d32 = 0;
- + depctl.b.epena = 1;
- + depctl.b.cnak = 1;
- +
- + DWC_MODIFY_REG32(&core_if->dev_if->
- + out_ep_regs[dwc_ep->num]->doepctl, depctl.d32,
- + depctl.d32);
- + return 0;
- + } else {
- + return 1;
- + }
- +}
- +
- +/**
- + * This function sets iso packets information(PTI mode)
- + *
- + * @param core_if Programming view of DWC_otg controller.
- + * @param ep The EP to start the transfer on.
- + *
- + */
- +static uint32_t set_iso_pkts_info(dwc_otg_core_if_t * core_if, dwc_ep_t * ep)
- +{
- + int i, j;
- + dma_addr_t dma_ad;
- + iso_pkt_info_t *packet_info = ep->pkt_info;
- + uint32_t offset;
- + uint32_t frame_data;
- + deptsiz_data_t deptsiz;
- +
- + if (ep->proc_buf_num == 0) {
- + /** Buffer 0 descriptors setup */
- + dma_ad = ep->dma_addr0;
- + } else {
- + /** Buffer 1 descriptors setup */
- + dma_ad = ep->dma_addr1;
- + }
- +
- + if (ep->is_in) {
- + deptsiz.d32 =
- + DWC_READ_REG32(&core_if->dev_if->in_ep_regs[ep->num]->
- + dieptsiz);
- + } else {
- + deptsiz.d32 =
- + DWC_READ_REG32(&core_if->dev_if->out_ep_regs[ep->num]->
- + doeptsiz);
- + }
- +
- + if (!deptsiz.b.xfersize) {
- + offset = 0;
- + for (i = 0; i < ep->pkt_cnt; i += ep->pkt_per_frm) {
- + frame_data = ep->data_per_frame;
- + for (j = 0; j < ep->pkt_per_frm; ++j) {
- +
- + /* Packet status - is not set as initially
- + * it is set to 0 and if packet was sent
- + successfully, status field will remain 0*/
- +
- + /* Bytes has been transfered */
- + packet_info->length =
- + (ep->maxpacket <
- + frame_data) ? ep->maxpacket : frame_data;
- +
- + /* Received packet offset */
- + packet_info->offset = offset;
- + offset += packet_info->length;
- + frame_data -= packet_info->length;
- +
- + packet_info++;
- + }
- + }
- + return 1;
- + } else {
- + /* This is a workaround for in case of Transfer Complete with
- + * PktDrpSts interrupts merging - in this case Transfer complete
- + * interrupt for Isoc Out Endpoint is asserted without PktDrpSts
- + * set and with DOEPTSIZ register non zero. Investigations showed,
- + * that this happens when Out packet is dropped, but because of
- + * interrupts merging during first interrupt handling PktDrpSts
- + * bit is cleared and for next merged interrupts it is not reset.
- + * In this case SW hadles the interrupt as if PktDrpSts bit is set.
- + */
- + if (ep->is_in) {
- + return 1;
- + } else {
- + return handle_iso_out_pkt_dropped(core_if, ep);
- + }
- + }
- +}
- +
- +/**
- + * This function is to handle Iso EP transfer complete interrupt
- + *
- + * @param pcd The PCD
- + * @param ep The EP for which transfer complete was asserted
- + *
- + */
- +static void complete_iso_ep(dwc_otg_pcd_t * pcd, dwc_otg_pcd_ep_t * ep)
- +{
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(ep->pcd);
- + dwc_ep_t *dwc_ep = &ep->dwc_ep;
- + uint8_t is_last = 0;
- +
- + if (ep->dwc_ep.next_frame == 0xffffffff) {
- + DWC_WARN("Next frame is not set!\n");
- + return;
- + }
- +
- + if (core_if->dma_enable) {
- + if (core_if->dma_desc_enable) {
- + set_ddma_iso_pkts_info(core_if, dwc_ep);
- + reinit_ddma_iso_xfer(core_if, dwc_ep);
- + is_last = 1;
- + } else {
- + if (core_if->pti_enh_enable) {
- + if (set_iso_pkts_info(core_if, dwc_ep)) {
- + dwc_ep->proc_buf_num =
- + (dwc_ep->proc_buf_num ^ 1) & 0x1;
- + dwc_otg_iso_ep_start_buf_transfer
- + (core_if, dwc_ep);
- + is_last = 1;
- + }
- + } else {
- + set_current_pkt_info(core_if, dwc_ep);
- + if (dwc_ep->cur_pkt >= dwc_ep->pkt_cnt) {
- + is_last = 1;
- + dwc_ep->cur_pkt = 0;
- + dwc_ep->proc_buf_num =
- + (dwc_ep->proc_buf_num ^ 1) & 0x1;
- + if (dwc_ep->proc_buf_num) {
- + dwc_ep->cur_pkt_addr =
- + dwc_ep->xfer_buff1;
- + dwc_ep->cur_pkt_dma_addr =
- + dwc_ep->dma_addr1;
- + } else {
- + dwc_ep->cur_pkt_addr =
- + dwc_ep->xfer_buff0;
- + dwc_ep->cur_pkt_dma_addr =
- + dwc_ep->dma_addr0;
- + }
- +
- + }
- + dwc_otg_iso_ep_start_frm_transfer(core_if,
- + dwc_ep);
- + }
- + }
- + } else {
- + set_current_pkt_info(core_if, dwc_ep);
- + if (dwc_ep->cur_pkt >= dwc_ep->pkt_cnt) {
- + is_last = 1;
- + dwc_ep->cur_pkt = 0;
- + dwc_ep->proc_buf_num = (dwc_ep->proc_buf_num ^ 1) & 0x1;
- + if (dwc_ep->proc_buf_num) {
- + dwc_ep->cur_pkt_addr = dwc_ep->xfer_buff1;
- + dwc_ep->cur_pkt_dma_addr = dwc_ep->dma_addr1;
- + } else {
- + dwc_ep->cur_pkt_addr = dwc_ep->xfer_buff0;
- + dwc_ep->cur_pkt_dma_addr = dwc_ep->dma_addr0;
- + }
- +
- + }
- + dwc_otg_iso_ep_start_frm_transfer(core_if, dwc_ep);
- + }
- + if (is_last)
- + dwc_otg_iso_buffer_done(pcd, ep, ep->iso_req_handle);
- +}
- +#endif /* DWC_EN_ISOC */
- +
- +/**
- + * This function handle BNA interrupt for Non Isochronous EPs
- + *
- + */
- +static void dwc_otg_pcd_handle_noniso_bna(dwc_otg_pcd_ep_t * ep)
- +{
- + dwc_ep_t *dwc_ep = &ep->dwc_ep;
- + volatile uint32_t *addr;
- + depctl_data_t depctl = {.d32 = 0 };
- + dwc_otg_pcd_t *pcd = ep->pcd;
- + dwc_otg_dev_dma_desc_t *dma_desc;
- + dev_dma_desc_sts_t sts = {.d32 = 0 };
- + dwc_otg_core_if_t *core_if = ep->pcd->core_if;
- + int i, start;
- +
- + if (!dwc_ep->desc_cnt)
- + DWC_WARN("Ep%d %s Descriptor count = %d \n", dwc_ep->num,
- + (dwc_ep->is_in ? "IN" : "OUT"), dwc_ep->desc_cnt);
- +
- + if (core_if->core_params->cont_on_bna && !dwc_ep->is_in
- + && dwc_ep->type != DWC_OTG_EP_TYPE_CONTROL) {
- + uint32_t doepdma;
- + dwc_otg_dev_out_ep_regs_t *out_regs =
- + core_if->dev_if->out_ep_regs[dwc_ep->num];
- + doepdma = DWC_READ_REG32(&(out_regs->doepdma));
- + start = (doepdma - dwc_ep->dma_desc_addr)/sizeof(dwc_otg_dev_dma_desc_t);
- + dma_desc = &(dwc_ep->desc_addr[start]);
- + } else {
- + start = 0;
- + dma_desc = dwc_ep->desc_addr;
- + }
- +
- +
- + for (i = start; i < dwc_ep->desc_cnt; ++i, ++dma_desc) {
- + sts.d32 = dma_desc->status.d32;
- + sts.b.bs = BS_HOST_READY;
- + dma_desc->status.d32 = sts.d32;
- + }
- +
- + if (dwc_ep->is_in == 0) {
- + addr =
- + &GET_CORE_IF(pcd)->dev_if->out_ep_regs[dwc_ep->num]->
- + doepctl;
- + } else {
- + addr =
- + &GET_CORE_IF(pcd)->dev_if->in_ep_regs[dwc_ep->num]->diepctl;
- + }
- + depctl.b.epena = 1;
- + depctl.b.cnak = 1;
- + DWC_MODIFY_REG32(addr, 0, depctl.d32);
- +}
- +
- +/**
- + * This function handles EP0 Control transfers.
- + *
- + * The state of the control transfers are tracked in
- + * <code>ep0state</code>.
- + */
- +static void handle_ep0(dwc_otg_pcd_t * pcd)
- +{
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
- + dwc_otg_pcd_ep_t *ep0 = &pcd->ep0;
- + dev_dma_desc_sts_t desc_sts;
- + deptsiz0_data_t deptsiz;
- + uint32_t byte_count;
- +
- +#ifdef DEBUG_EP0
- + DWC_DEBUGPL(DBG_PCDV, "%s()\n", __func__);
- + print_ep0_state(pcd);
- +#endif
- +
- +// DWC_PRINTF("HANDLE EP0\n");
- +
- + switch (pcd->ep0state) {
- + case EP0_DISCONNECT:
- + break;
- +
- + case EP0_IDLE:
- + pcd->request_config = 0;
- +
- + pcd_setup(pcd);
- + break;
- +
- + case EP0_IN_DATA_PHASE:
- +#ifdef DEBUG_EP0
- + DWC_DEBUGPL(DBG_PCD, "DATA_IN EP%d-%s: type=%d, mps=%d\n",
- + ep0->dwc_ep.num, (ep0->dwc_ep.is_in ? "IN" : "OUT"),
- + ep0->dwc_ep.type, ep0->dwc_ep.maxpacket);
- +#endif
- +
- + if (core_if->dma_enable != 0) {
- + /*
- + * For EP0 we can only program 1 packet at a time so we
- + * need to do the make calculations after each complete.
- + * Call write_packet to make the calculations, as in
- + * slave mode, and use those values to determine if we
- + * can complete.
- + */
- + if (core_if->dma_desc_enable == 0) {
- + deptsiz.d32 =
- + DWC_READ_REG32(&core_if->
- + dev_if->in_ep_regs[0]->
- + dieptsiz);
- + byte_count =
- + ep0->dwc_ep.xfer_len - deptsiz.b.xfersize;
- + } else {
- + desc_sts =
- + core_if->dev_if->in_desc_addr->status;
- + byte_count =
- + ep0->dwc_ep.xfer_len - desc_sts.b.bytes;
- + }
- + ep0->dwc_ep.xfer_count += byte_count;
- + ep0->dwc_ep.xfer_buff += byte_count;
- + ep0->dwc_ep.dma_addr += byte_count;
- + }
- + if (ep0->dwc_ep.xfer_count < ep0->dwc_ep.total_len) {
- + dwc_otg_ep0_continue_transfer(GET_CORE_IF(pcd),
- + &ep0->dwc_ep);
- + DWC_DEBUGPL(DBG_PCD, "CONTINUE TRANSFER\n");
- + } else if (ep0->dwc_ep.sent_zlp) {
- + dwc_otg_ep0_continue_transfer(GET_CORE_IF(pcd),
- + &ep0->dwc_ep);
- + ep0->dwc_ep.sent_zlp = 0;
- + DWC_DEBUGPL(DBG_PCD, "CONTINUE TRANSFER sent zlp\n");
- + } else {
- + ep0_complete_request(ep0);
- + DWC_DEBUGPL(DBG_PCD, "COMPLETE TRANSFER\n");
- + }
- + break;
- + case EP0_OUT_DATA_PHASE:
- +#ifdef DEBUG_EP0
- + DWC_DEBUGPL(DBG_PCD, "DATA_OUT EP%d-%s: type=%d, mps=%d\n",
- + ep0->dwc_ep.num, (ep0->dwc_ep.is_in ? "IN" : "OUT"),
- + ep0->dwc_ep.type, ep0->dwc_ep.maxpacket);
- +#endif
- + if (core_if->dma_enable != 0) {
- + if (core_if->dma_desc_enable == 0) {
- + deptsiz.d32 =
- + DWC_READ_REG32(&core_if->
- + dev_if->out_ep_regs[0]->
- + doeptsiz);
- + byte_count =
- + ep0->dwc_ep.maxpacket - deptsiz.b.xfersize;
- + } else {
- + desc_sts =
- + core_if->dev_if->out_desc_addr->status;
- + byte_count =
- + ep0->dwc_ep.maxpacket - desc_sts.b.bytes;
- + }
- + ep0->dwc_ep.xfer_count += byte_count;
- + ep0->dwc_ep.xfer_buff += byte_count;
- + ep0->dwc_ep.dma_addr += byte_count;
- + }
- + if (ep0->dwc_ep.xfer_count < ep0->dwc_ep.total_len) {
- + dwc_otg_ep0_continue_transfer(GET_CORE_IF(pcd),
- + &ep0->dwc_ep);
- + DWC_DEBUGPL(DBG_PCD, "CONTINUE TRANSFER\n");
- + } else if (ep0->dwc_ep.sent_zlp) {
- + dwc_otg_ep0_continue_transfer(GET_CORE_IF(pcd),
- + &ep0->dwc_ep);
- + ep0->dwc_ep.sent_zlp = 0;
- + DWC_DEBUGPL(DBG_PCD, "CONTINUE TRANSFER sent zlp\n");
- + } else {
- + ep0_complete_request(ep0);
- + DWC_DEBUGPL(DBG_PCD, "COMPLETE TRANSFER\n");
- + }
- + break;
- +
- + case EP0_IN_STATUS_PHASE:
- + case EP0_OUT_STATUS_PHASE:
- + DWC_DEBUGPL(DBG_PCD, "CASE: EP0_STATUS\n");
- + ep0_complete_request(ep0);
- + pcd->ep0state = EP0_IDLE;
- + ep0->stopped = 1;
- + ep0->dwc_ep.is_in = 0; /* OUT for next SETUP */
- +
- + /* Prepare for more SETUP Packets */
- + if (core_if->dma_enable) {
- + ep0_out_start(core_if, pcd);
- + }
- + break;
- +
- + case EP0_STALL:
- + DWC_ERROR("EP0 STALLed, should not get here pcd_setup()\n");
- + break;
- + }
- +#ifdef DEBUG_EP0
- + print_ep0_state(pcd);
- +#endif
- +}
- +
- +/**
- + * Restart transfer
- + */
- +static void restart_transfer(dwc_otg_pcd_t * pcd, const uint32_t epnum)
- +{
- + dwc_otg_core_if_t *core_if;
- + dwc_otg_dev_if_t *dev_if;
- + deptsiz_data_t dieptsiz = {.d32 = 0 };
- + dwc_otg_pcd_ep_t *ep;
- +
- + ep = get_in_ep(pcd, epnum);
- +
- +#ifdef DWC_EN_ISOC
- + if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) {
- + return;
- + }
- +#endif /* DWC_EN_ISOC */
- +
- + core_if = GET_CORE_IF(pcd);
- + dev_if = core_if->dev_if;
- +
- + dieptsiz.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dieptsiz);
- +
- + DWC_DEBUGPL(DBG_PCD, "xfer_buff=%p xfer_count=%0x xfer_len=%0x"
- + " stopped=%d\n", ep->dwc_ep.xfer_buff,
- + ep->dwc_ep.xfer_count, ep->dwc_ep.xfer_len, ep->stopped);
- + /*
- + * If xfersize is 0 and pktcnt in not 0, resend the last packet.
- + */
- + if (dieptsiz.b.pktcnt && dieptsiz.b.xfersize == 0 &&
- + ep->dwc_ep.start_xfer_buff != 0) {
- + if (ep->dwc_ep.total_len <= ep->dwc_ep.maxpacket) {
- + ep->dwc_ep.xfer_count = 0;
- + ep->dwc_ep.xfer_buff = ep->dwc_ep.start_xfer_buff;
- + ep->dwc_ep.xfer_len = ep->dwc_ep.xfer_count;
- + } else {
- + ep->dwc_ep.xfer_count -= ep->dwc_ep.maxpacket;
- + /* convert packet size to dwords. */
- + ep->dwc_ep.xfer_buff -= ep->dwc_ep.maxpacket;
- + ep->dwc_ep.xfer_len = ep->dwc_ep.xfer_count;
- + }
- + ep->stopped = 0;
- + DWC_DEBUGPL(DBG_PCD, "xfer_buff=%p xfer_count=%0x "
- + "xfer_len=%0x stopped=%d\n",
- + ep->dwc_ep.xfer_buff,
- + ep->dwc_ep.xfer_count, ep->dwc_ep.xfer_len,
- + ep->stopped);
- + if (epnum == 0) {
- + dwc_otg_ep0_start_transfer(core_if, &ep->dwc_ep);
- + } else {
- + dwc_otg_ep_start_transfer(core_if, &ep->dwc_ep);
- + }
- + }
- +}
- +
- +/*
- + * This function create new nextep sequnce based on Learn Queue.
- + *
- + * @param core_if Programming view of DWC_otg controller
- + */
- +void predict_nextep_seq( dwc_otg_core_if_t * core_if)
- +{
- + dwc_otg_device_global_regs_t *dev_global_regs =
- + core_if->dev_if->dev_global_regs;
- + const uint32_t TOKEN_Q_DEPTH = core_if->hwcfg2.b.dev_token_q_depth;
- + /* Number of Token Queue Registers */
- + const int DTKNQ_REG_CNT = (TOKEN_Q_DEPTH + 7) / 8;
- + dtknq1_data_t dtknqr1;
- + uint32_t in_tkn_epnums[4];
- + uint8_t seqnum[MAX_EPS_CHANNELS];
- + uint8_t intkn_seq[1 << 5];
- + grstctl_t resetctl = {.d32 = 0 };
- + uint8_t temp;
- + int ndx = 0;
- + int start = 0;
- + int end = 0;
- + int sort_done = 0;
- + int i = 0;
- + volatile uint32_t *addr = &dev_global_regs->dtknqr1;
- +
- +
- + DWC_DEBUGPL(DBG_PCD,"dev_token_q_depth=%d\n",TOKEN_Q_DEPTH);
- +
- + /* Read the DTKNQ Registers */
- + for (i = 0; i < DTKNQ_REG_CNT; i++) {
- + in_tkn_epnums[i] = DWC_READ_REG32(addr);
- + DWC_DEBUGPL(DBG_PCDV, "DTKNQR%d=0x%08x\n", i + 1,
- + in_tkn_epnums[i]);
- + if (addr == &dev_global_regs->dvbusdis) {
- + addr = &dev_global_regs->dtknqr3_dthrctl;
- + } else {
- + ++addr;
- + }
- +
- + }
- +
- + /* Copy the DTKNQR1 data to the bit field. */
- + dtknqr1.d32 = in_tkn_epnums[0];
- + if (dtknqr1.b.wrap_bit) {
- + ndx = dtknqr1.b.intknwptr;
- + end = ndx -1;
- + if (end < 0)
- + end = TOKEN_Q_DEPTH -1;
- + } else {
- + ndx = 0;
- + end = dtknqr1.b.intknwptr -1;
- + if (end < 0)
- + end = 0;
- + }
- + start = ndx;
- +
- + /* Fill seqnum[] by initial values: EP number + 31 */
- + for (i=0; i <= core_if->dev_if->num_in_eps; i++) {
- + seqnum[i] = i +31;
- + }
- +
- + /* Fill intkn_seq[] from in_tkn_epnums[0] */
- + for (i=0; i < 6; i++)
- + intkn_seq[i] = (in_tkn_epnums[0] >> ((7-i) * 4)) & 0xf;
- +
- + if (TOKEN_Q_DEPTH > 6) {
- + /* Fill intkn_seq[] from in_tkn_epnums[1] */
- + for (i=6; i < 14; i++)
- + intkn_seq[i] =
- + (in_tkn_epnums[1] >> ((7 - (i - 6)) * 4)) & 0xf;
- + }
- +
- + if (TOKEN_Q_DEPTH > 14) {
- + /* Fill intkn_seq[] from in_tkn_epnums[1] */
- + for (i=14; i < 22; i++)
- + intkn_seq[i] =
- + (in_tkn_epnums[2] >> ((7 - (i - 14)) * 4)) & 0xf;
- + }
- +
- + if (TOKEN_Q_DEPTH > 22) {
- + /* Fill intkn_seq[] from in_tkn_epnums[1] */
- + for (i=22; i < 30; i++)
- + intkn_seq[i] =
- + (in_tkn_epnums[3] >> ((7 - (i - 22)) * 4)) & 0xf;
- + }
- +
- + DWC_DEBUGPL(DBG_PCDV, "%s start=%d end=%d intkn_seq[]:\n", __func__,
- + start, end);
- + for (i=0; i<TOKEN_Q_DEPTH; i++)
- + DWC_DEBUGPL(DBG_PCDV,"%d\n", intkn_seq[i]);
- +
- + /* Update seqnum based on intkn_seq[] */
- + i = 0;
- + do {
- + seqnum[intkn_seq[ndx]] = i;
- + ndx++;
- + i++;
- + if (ndx == TOKEN_Q_DEPTH)
- + ndx = 0;
- + } while ( i < TOKEN_Q_DEPTH );
- +
- + /* Mark non active EP's in seqnum[] by 0xff */
- + for (i=0; i<=core_if->dev_if->num_in_eps; i++) {
- + if (core_if->nextep_seq[i] == 0xff )
- + seqnum[i] = 0xff;
- + }
- +
- + /* Sort seqnum[] */
- + sort_done = 0;
- + while (!sort_done) {
- + sort_done = 1;
- + for (i=0; i<core_if->dev_if->num_in_eps; i++) {
- + if (seqnum[i] > seqnum[i+1]) {
- + temp = seqnum[i];
- + seqnum[i] = seqnum[i+1];
- + seqnum[i+1] = temp;
- + sort_done = 0;
- + }
- + }
- + }
- +
- + ndx = start + seqnum[0];
- + if (ndx >= TOKEN_Q_DEPTH)
- + ndx = ndx % TOKEN_Q_DEPTH;
- + core_if->first_in_nextep_seq = intkn_seq[ndx];
- +
- + /* Update seqnum[] by EP numbers */
- + for (i=0; i<=core_if->dev_if->num_in_eps; i++) {
- + ndx = start + i;
- + if (seqnum[i] < 31) {
- + ndx = start + seqnum[i];
- + if (ndx >= TOKEN_Q_DEPTH)
- + ndx = ndx % TOKEN_Q_DEPTH;
- + seqnum[i] = intkn_seq[ndx];
- + } else {
- + if (seqnum[i] < 0xff) {
- + seqnum[i] = seqnum[i] - 31;
- + } else {
- + break;
- + }
- + }
- + }
- +
- + /* Update nextep_seq[] based on seqnum[] */
- + for (i=0; i<core_if->dev_if->num_in_eps; i++) {
- + if (seqnum[i] != 0xff) {
- + if (seqnum[i+1] != 0xff) {
- + core_if->nextep_seq[seqnum[i]] = seqnum[i+1];
- + } else {
- + core_if->nextep_seq[seqnum[i]] = core_if->first_in_nextep_seq;
- + break;
- + }
- + } else {
- + break;
- + }
- + }
- +
- + DWC_DEBUGPL(DBG_PCDV, "%s first_in_nextep_seq= %2d; nextep_seq[]:\n",
- + __func__, core_if->first_in_nextep_seq);
- + for (i=0; i <= core_if->dev_if->num_in_eps; i++) {
- + DWC_DEBUGPL(DBG_PCDV,"%2d\n", core_if->nextep_seq[i]);
- + }
- +
- + /* Flush the Learning Queue */
- + resetctl.d32 = DWC_READ_REG32(&core_if->core_global_regs->grstctl);
- + resetctl.b.intknqflsh = 1;
- + DWC_WRITE_REG32(&core_if->core_global_regs->grstctl, resetctl.d32);
- +
- +
- +}
- +
- +/**
- + * handle the IN EP disable interrupt.
- + */
- +static inline void handle_in_ep_disable_intr(dwc_otg_pcd_t * pcd,
- + const uint32_t epnum)
- +{
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
- + dwc_otg_dev_if_t *dev_if = core_if->dev_if;
- + deptsiz_data_t dieptsiz = {.d32 = 0 };
- + dctl_data_t dctl = {.d32 = 0 };
- + dwc_otg_pcd_ep_t *ep;
- + dwc_ep_t *dwc_ep;
- + gintmsk_data_t gintmsk_data;
- + depctl_data_t depctl;
- + uint32_t diepdma;
- + uint32_t remain_to_transfer = 0;
- + uint8_t i;
- + uint32_t xfer_size;
- +
- + ep = get_in_ep(pcd, epnum);
- + dwc_ep = &ep->dwc_ep;
- +
- + if (dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) {
- + dwc_otg_flush_tx_fifo(core_if, dwc_ep->tx_fifo_num);
- + complete_ep(ep);
- + return;
- + }
- +
- + DWC_DEBUGPL(DBG_PCD, "diepctl%d=%0x\n", epnum,
- + DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->diepctl));
- + dieptsiz.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dieptsiz);
- + depctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->diepctl);
- +
- + DWC_DEBUGPL(DBG_ANY, "pktcnt=%d size=%d\n",
- + dieptsiz.b.pktcnt, dieptsiz.b.xfersize);
- +
- + if ((core_if->start_predict == 0) || (depctl.b.eptype & 1)) {
- + if (ep->stopped) {
- + if (core_if->en_multiple_tx_fifo)
- + /* Flush the Tx FIFO */
- + dwc_otg_flush_tx_fifo(core_if, dwc_ep->tx_fifo_num);
- + /* Clear the Global IN NP NAK */
- + dctl.d32 = 0;
- + dctl.b.cgnpinnak = 1;
- + DWC_MODIFY_REG32(&dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32);
- + /* Restart the transaction */
- + if (dieptsiz.b.pktcnt != 0 || dieptsiz.b.xfersize != 0) {
- + restart_transfer(pcd, epnum);
- + }
- + } else {
- + /* Restart the transaction */
- + if (dieptsiz.b.pktcnt != 0 || dieptsiz.b.xfersize != 0) {
- + restart_transfer(pcd, epnum);
- + }
- + DWC_DEBUGPL(DBG_ANY, "STOPPED!!!\n");
- + }
- + return;
- + }
- +
- + if (core_if->start_predict > 2) { // NP IN EP
- + core_if->start_predict--;
- + return;
- + }
- +
- + core_if->start_predict--;
- +
- + if (core_if->start_predict == 1) { // All NP IN Ep's disabled now
- +
- + predict_nextep_seq(core_if);
- +
- + /* Update all active IN EP's NextEP field based of nextep_seq[] */
- + for ( i = 0; i <= core_if->dev_if->num_in_eps; i++) {
- + depctl.d32 =
- + DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl);
- + if (core_if->nextep_seq[i] != 0xff) { // Active NP IN EP
- + depctl.b.nextep = core_if->nextep_seq[i];
- + DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepctl, depctl.d32);
- + }
- + }
- + /* Flush Shared NP TxFIFO */
- + dwc_otg_flush_tx_fifo(core_if, 0);
- + /* Rewind buffers */
- + if (!core_if->dma_desc_enable) {
- + i = core_if->first_in_nextep_seq;
- + do {
- + ep = get_in_ep(pcd, i);
- + dieptsiz.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->dieptsiz);
- + xfer_size = ep->dwc_ep.total_len - ep->dwc_ep.xfer_count;
- + if (xfer_size > ep->dwc_ep.maxxfer)
- + xfer_size = ep->dwc_ep.maxxfer;
- + depctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl);
- + if (dieptsiz.b.pktcnt != 0) {
- + if (xfer_size == 0) {
- + remain_to_transfer = 0;
- + } else {
- + if ((xfer_size % ep->dwc_ep.maxpacket) == 0) {
- + remain_to_transfer =
- + dieptsiz.b.pktcnt * ep->dwc_ep.maxpacket;
- + } else {
- + remain_to_transfer = ((dieptsiz.b.pktcnt -1) * ep->dwc_ep.maxpacket)
- + + (xfer_size % ep->dwc_ep.maxpacket);
- + }
- + }
- + diepdma = DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepdma);
- + dieptsiz.b.xfersize = remain_to_transfer;
- + DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->dieptsiz, dieptsiz.d32);
- + diepdma = ep->dwc_ep.dma_addr + (xfer_size - remain_to_transfer);
- + DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepdma, diepdma);
- + }
- + i = core_if->nextep_seq[i];
- + } while (i != core_if->first_in_nextep_seq);
- + } else { // dma_desc_enable
- + DWC_PRINTF("%s Learning Queue not supported in DDMA\n", __func__);
- + }
- +
- + /* Restart transfers in predicted sequences */
- + i = core_if->first_in_nextep_seq;
- + do {
- + dieptsiz.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->dieptsiz);
- + depctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl);
- + if (dieptsiz.b.pktcnt != 0) {
- + depctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl);
- + depctl.b.epena = 1;
- + depctl.b.cnak = 1;
- + DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepctl, depctl.d32);
- + }
- + i = core_if->nextep_seq[i];
- + } while (i != core_if->first_in_nextep_seq);
- +
- + /* Clear the global non-periodic IN NAK handshake */
- + dctl.d32 = 0;
- + dctl.b.cgnpinnak = 1;
- + DWC_MODIFY_REG32(&dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32);
- +
- + /* Unmask EP Mismatch interrupt */
- + gintmsk_data.d32 = 0;
- + gintmsk_data.b.epmismatch = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0, gintmsk_data.d32);
- +
- + core_if->start_predict = 0;
- +
- + }
- +}
- +
- +/**
- + * Handler for the IN EP timeout handshake interrupt.
- + */
- +static inline void handle_in_ep_timeout_intr(dwc_otg_pcd_t * pcd,
- + const uint32_t epnum)
- +{
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
- + dwc_otg_dev_if_t *dev_if = core_if->dev_if;
- +
- +#ifdef DEBUG
- + deptsiz_data_t dieptsiz = {.d32 = 0 };
- + uint32_t num = 0;
- +#endif
- + dctl_data_t dctl = {.d32 = 0 };
- + dwc_otg_pcd_ep_t *ep;
- +
- + gintmsk_data_t intr_mask = {.d32 = 0 };
- +
- + ep = get_in_ep(pcd, epnum);
- +
- + /* Disable the NP Tx Fifo Empty Interrrupt */
- + if (!core_if->dma_enable) {
- + intr_mask.b.nptxfempty = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk,
- + intr_mask.d32, 0);
- + }
- + /** @todo NGS Check EP type.
- + * Implement for Periodic EPs */
- + /*
- + * Non-periodic EP
- + */
- + /* Enable the Global IN NAK Effective Interrupt */
- + intr_mask.b.ginnakeff = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0, intr_mask.d32);
- +
- + /* Set Global IN NAK */
- + dctl.b.sgnpinnak = 1;
- + DWC_MODIFY_REG32(&dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32);
- +
- + ep->stopped = 1;
- +
- +#ifdef DEBUG
- + dieptsiz.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[num]->dieptsiz);
- + DWC_DEBUGPL(DBG_ANY, "pktcnt=%d size=%d\n",
- + dieptsiz.b.pktcnt, dieptsiz.b.xfersize);
- +#endif
- +
- +#ifdef DISABLE_PERIODIC_EP
- + /*
- + * Set the NAK bit for this EP to
- + * start the disable process.
- + */
- + diepctl.d32 = 0;
- + diepctl.b.snak = 1;
- + DWC_MODIFY_REG32(&dev_if->in_ep_regs[num]->diepctl, diepctl.d32,
- + diepctl.d32);
- + ep->disabling = 1;
- + ep->stopped = 1;
- +#endif
- +}
- +
- +/**
- + * Handler for the IN EP NAK interrupt.
- + */
- +static inline int32_t handle_in_ep_nak_intr(dwc_otg_pcd_t * pcd,
- + const uint32_t epnum)
- +{
- + /** @todo implement ISR */
- + dwc_otg_core_if_t *core_if;
- + diepmsk_data_t intr_mask = {.d32 = 0 };
- +
- + DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", "IN EP NAK");
- + core_if = GET_CORE_IF(pcd);
- + intr_mask.b.nak = 1;
- +
- + if (core_if->multiproc_int_enable) {
- + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->
- + diepeachintmsk[epnum], intr_mask.d32, 0);
- + } else {
- + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->diepmsk,
- + intr_mask.d32, 0);
- + }
- +
- + return 1;
- +}
- +
- +/**
- + * Handler for the OUT EP Babble interrupt.
- + */
- +static inline int32_t handle_out_ep_babble_intr(dwc_otg_pcd_t * pcd,
- + const uint32_t epnum)
- +{
- + /** @todo implement ISR */
- + dwc_otg_core_if_t *core_if;
- + doepmsk_data_t intr_mask = {.d32 = 0 };
- +
- + DWC_PRINTF("INTERRUPT Handler not implemented for %s\n",
- + "OUT EP Babble");
- + core_if = GET_CORE_IF(pcd);
- + intr_mask.b.babble = 1;
- +
- + if (core_if->multiproc_int_enable) {
- + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->
- + doepeachintmsk[epnum], intr_mask.d32, 0);
- + } else {
- + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->doepmsk,
- + intr_mask.d32, 0);
- + }
- +
- + return 1;
- +}
- +
- +/**
- + * Handler for the OUT EP NAK interrupt.
- + */
- +static inline int32_t handle_out_ep_nak_intr(dwc_otg_pcd_t * pcd,
- + const uint32_t epnum)
- +{
- + /** @todo implement ISR */
- + dwc_otg_core_if_t *core_if;
- + doepmsk_data_t intr_mask = {.d32 = 0 };
- +
- + DWC_DEBUGPL(DBG_ANY, "INTERRUPT Handler not implemented for %s\n", "OUT EP NAK");
- + core_if = GET_CORE_IF(pcd);
- + intr_mask.b.nak = 1;
- +
- + if (core_if->multiproc_int_enable) {
- + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->
- + doepeachintmsk[epnum], intr_mask.d32, 0);
- + } else {
- + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->doepmsk,
- + intr_mask.d32, 0);
- + }
- +
- + return 1;
- +}
- +
- +/**
- + * Handler for the OUT EP NYET interrupt.
- + */
- +static inline int32_t handle_out_ep_nyet_intr(dwc_otg_pcd_t * pcd,
- + const uint32_t epnum)
- +{
- + /** @todo implement ISR */
- + dwc_otg_core_if_t *core_if;
- + doepmsk_data_t intr_mask = {.d32 = 0 };
- +
- + DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", "OUT EP NYET");
- + core_if = GET_CORE_IF(pcd);
- + intr_mask.b.nyet = 1;
- +
- + if (core_if->multiproc_int_enable) {
- + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->
- + doepeachintmsk[epnum], intr_mask.d32, 0);
- + } else {
- + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->doepmsk,
- + intr_mask.d32, 0);
- + }
- +
- + return 1;
- +}
- +
- +/**
- + * This interrupt indicates that an IN EP has a pending Interrupt.
- + * The sequence for handling the IN EP interrupt is shown below:
- + * -# Read the Device All Endpoint Interrupt register
- + * -# Repeat the following for each IN EP interrupt bit set (from
- + * LSB to MSB).
- + * -# Read the Device Endpoint Interrupt (DIEPINTn) register
- + * -# If "Transfer Complete" call the request complete function
- + * -# If "Endpoint Disabled" complete the EP disable procedure.
- + * -# If "AHB Error Interrupt" log error
- + * -# If "Time-out Handshake" log error
- + * -# If "IN Token Received when TxFIFO Empty" write packet to Tx
- + * FIFO.
- + * -# If "IN Token EP Mismatch" (disable, this is handled by EP
- + * Mismatch Interrupt)
- + */
- +static int32_t dwc_otg_pcd_handle_in_ep_intr(dwc_otg_pcd_t * pcd)
- +{
- +#define CLEAR_IN_EP_INTR(__core_if,__epnum,__intr) \
- +do { \
- + diepint_data_t diepint = {.d32=0}; \
- + diepint.b.__intr = 1; \
- + DWC_WRITE_REG32(&__core_if->dev_if->in_ep_regs[__epnum]->diepint, \
- + diepint.d32); \
- +} while (0)
- +
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
- + dwc_otg_dev_if_t *dev_if = core_if->dev_if;
- + diepint_data_t diepint = {.d32 = 0 };
- + depctl_data_t depctl = {.d32 = 0 };
- + uint32_t ep_intr;
- + uint32_t epnum = 0;
- + dwc_otg_pcd_ep_t *ep;
- + dwc_ep_t *dwc_ep;
- + gintmsk_data_t intr_mask = {.d32 = 0 };
- +
- + DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, pcd);
- +
- + /* Read in the device interrupt bits */
- + ep_intr = dwc_otg_read_dev_all_in_ep_intr(core_if);
- +
- + /* Service the Device IN interrupts for each endpoint */
- + while (ep_intr) {
- + if (ep_intr & 0x1) {
- + uint32_t empty_msk;
- + /* Get EP pointer */
- + ep = get_in_ep(pcd, epnum);
- + dwc_ep = &ep->dwc_ep;
- +
- + depctl.d32 =
- + DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->diepctl);
- + empty_msk =
- + DWC_READ_REG32(&dev_if->
- + dev_global_regs->dtknqr4_fifoemptymsk);
- +
- + DWC_DEBUGPL(DBG_PCDV,
- + "IN EP INTERRUPT - %d\nepmty_msk - %8x diepctl - %8x\n",
- + epnum, empty_msk, depctl.d32);
- +
- + DWC_DEBUGPL(DBG_PCD,
- + "EP%d-%s: type=%d, mps=%d\n",
- + dwc_ep->num, (dwc_ep->is_in ? "IN" : "OUT"),
- + dwc_ep->type, dwc_ep->maxpacket);
- +
- + diepint.d32 =
- + dwc_otg_read_dev_in_ep_intr(core_if, dwc_ep);
- +
- + DWC_DEBUGPL(DBG_PCDV,
- + "EP %d Interrupt Register - 0x%x\n", epnum,
- + diepint.d32);
- + /* Transfer complete */
- + if (diepint.b.xfercompl) {
- + /* Disable the NP Tx FIFO Empty
- + * Interrupt */
- + if (core_if->en_multiple_tx_fifo == 0) {
- + intr_mask.b.nptxfempty = 1;
- + DWC_MODIFY_REG32
- + (&core_if->core_global_regs->gintmsk,
- + intr_mask.d32, 0);
- + } else {
- + /* Disable the Tx FIFO Empty Interrupt for this EP */
- + uint32_t fifoemptymsk =
- + 0x1 << dwc_ep->num;
- + DWC_MODIFY_REG32(&core_if->
- + dev_if->dev_global_regs->dtknqr4_fifoemptymsk,
- + fifoemptymsk, 0);
- + }
- + /* Clear the bit in DIEPINTn for this interrupt */
- + CLEAR_IN_EP_INTR(core_if, epnum, xfercompl);
- +
- + /* Complete the transfer */
- + if (epnum == 0) {
- + handle_ep0(pcd);
- + }
- +#ifdef DWC_EN_ISOC
- + else if (dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) {
- + if (!ep->stopped)
- + complete_iso_ep(pcd, ep);
- + }
- +#endif /* DWC_EN_ISOC */
- +#ifdef DWC_UTE_PER_IO
- + else if (dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) {
- + if (!ep->stopped)
- + complete_xiso_ep(ep);
- + }
- +#endif /* DWC_UTE_PER_IO */
- + else {
- + if (dwc_ep->type == DWC_OTG_EP_TYPE_ISOC &&
- + dwc_ep->bInterval > 1) {
- + dwc_ep->frame_num += dwc_ep->bInterval;
- + if (dwc_ep->frame_num > 0x3FFF)
- + {
- + dwc_ep->frm_overrun = 1;
- + dwc_ep->frame_num &= 0x3FFF;
- + } else
- + dwc_ep->frm_overrun = 0;
- + }
- + complete_ep(ep);
- + if(diepint.b.nak)
- + CLEAR_IN_EP_INTR(core_if, epnum, nak);
- + }
- + }
- + /* Endpoint disable */
- + if (diepint.b.epdisabled) {
- + DWC_DEBUGPL(DBG_ANY, "EP%d IN disabled\n",
- + epnum);
- + handle_in_ep_disable_intr(pcd, epnum);
- +
- + /* Clear the bit in DIEPINTn for this interrupt */
- + CLEAR_IN_EP_INTR(core_if, epnum, epdisabled);
- + }
- + /* AHB Error */
- + if (diepint.b.ahberr) {
- + DWC_ERROR("EP%d IN AHB Error\n", epnum);
- + /* Clear the bit in DIEPINTn for this interrupt */
- + CLEAR_IN_EP_INTR(core_if, epnum, ahberr);
- + }
- + /* TimeOUT Handshake (non-ISOC IN EPs) */
- + if (diepint.b.timeout) {
- + DWC_ERROR("EP%d IN Time-out\n", epnum);
- + handle_in_ep_timeout_intr(pcd, epnum);
- +
- + CLEAR_IN_EP_INTR(core_if, epnum, timeout);
- + }
- + /** IN Token received with TxF Empty */
- + if (diepint.b.intktxfemp) {
- + DWC_DEBUGPL(DBG_ANY,
- + "EP%d IN TKN TxFifo Empty\n",
- + epnum);
- + if (!ep->stopped && epnum != 0) {
- +
- + diepmsk_data_t diepmsk = {.d32 = 0 };
- + diepmsk.b.intktxfemp = 1;
- +
- + if (core_if->multiproc_int_enable) {
- + DWC_MODIFY_REG32
- + (&dev_if->dev_global_regs->diepeachintmsk
- + [epnum], diepmsk.d32, 0);
- + } else {
- + DWC_MODIFY_REG32
- + (&dev_if->dev_global_regs->diepmsk,
- + diepmsk.d32, 0);
- + }
- + } else if (core_if->dma_desc_enable
- + && epnum == 0
- + && pcd->ep0state ==
- + EP0_OUT_STATUS_PHASE) {
- + // EP0 IN set STALL
- + depctl.d32 =
- + DWC_READ_REG32(&dev_if->in_ep_regs
- + [epnum]->diepctl);
- +
- + /* set the disable and stall bits */
- + if (depctl.b.epena) {
- + depctl.b.epdis = 1;
- + }
- + depctl.b.stall = 1;
- + DWC_WRITE_REG32(&dev_if->in_ep_regs
- + [epnum]->diepctl,
- + depctl.d32);
- + }
- + CLEAR_IN_EP_INTR(core_if, epnum, intktxfemp);
- + }
- + /** IN Token Received with EP mismatch */
- + if (diepint.b.intknepmis) {
- + DWC_DEBUGPL(DBG_ANY,
- + "EP%d IN TKN EP Mismatch\n", epnum);
- + CLEAR_IN_EP_INTR(core_if, epnum, intknepmis);
- + }
- + /** IN Endpoint NAK Effective */
- + if (diepint.b.inepnakeff) {
- + DWC_DEBUGPL(DBG_ANY,
- + "EP%d IN EP NAK Effective\n",
- + epnum);
- + /* Periodic EP */
- + if (ep->disabling) {
- + depctl.d32 = 0;
- + depctl.b.snak = 1;
- + depctl.b.epdis = 1;
- + DWC_MODIFY_REG32(&dev_if->in_ep_regs
- + [epnum]->diepctl,
- + depctl.d32,
- + depctl.d32);
- + }
- + CLEAR_IN_EP_INTR(core_if, epnum, inepnakeff);
- +
- + }
- +
- + /** IN EP Tx FIFO Empty Intr */
- + if (diepint.b.emptyintr) {
- + DWC_DEBUGPL(DBG_ANY,
- + "EP%d Tx FIFO Empty Intr \n",
- + epnum);
- + write_empty_tx_fifo(pcd, epnum);
- +
- + CLEAR_IN_EP_INTR(core_if, epnum, emptyintr);
- +
- + }
- +
- + /** IN EP BNA Intr */
- + if (diepint.b.bna) {
- + CLEAR_IN_EP_INTR(core_if, epnum, bna);
- + if (core_if->dma_desc_enable) {
- +#ifdef DWC_EN_ISOC
- + if (dwc_ep->type ==
- + DWC_OTG_EP_TYPE_ISOC) {
- + /*
- + * This checking is performed to prevent first "false" BNA
- + * handling occuring right after reconnect
- + */
- + if (dwc_ep->next_frame !=
- + 0xffffffff)
- + dwc_otg_pcd_handle_iso_bna(ep);
- + } else
- +#endif /* DWC_EN_ISOC */
- + {
- + dwc_otg_pcd_handle_noniso_bna(ep);
- + }
- + }
- + }
- + /* NAK Interrutp */
- + if (diepint.b.nak) {
- + DWC_DEBUGPL(DBG_ANY, "EP%d IN NAK Interrupt\n",
- + epnum);
- + if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) {
- + depctl_data_t depctl;
- + if (ep->dwc_ep.frame_num == 0xFFFFFFFF) {
- + ep->dwc_ep.frame_num = core_if->frame_num;
- + if (ep->dwc_ep.bInterval > 1) {
- + depctl.d32 = 0;
- + depctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->diepctl);
- + if (ep->dwc_ep.frame_num & 0x1) {
- + depctl.b.setd1pid = 1;
- + depctl.b.setd0pid = 0;
- + } else {
- + depctl.b.setd0pid = 1;
- + depctl.b.setd1pid = 0;
- + }
- + DWC_WRITE_REG32(&dev_if->in_ep_regs[epnum]->diepctl, depctl.d32);
- + }
- + start_next_request(ep);
- + }
- + ep->dwc_ep.frame_num += ep->dwc_ep.bInterval;
- + if (dwc_ep->frame_num > 0x3FFF) {
- + dwc_ep->frm_overrun = 1;
- + dwc_ep->frame_num &= 0x3FFF;
- + } else
- + dwc_ep->frm_overrun = 0;
- + }
- +
- + CLEAR_IN_EP_INTR(core_if, epnum, nak);
- + }
- + }
- + epnum++;
- + ep_intr >>= 1;
- + }
- +
- + return 1;
- +#undef CLEAR_IN_EP_INTR
- +}
- +
- +/**
- + * This interrupt indicates that an OUT EP has a pending Interrupt.
- + * The sequence for handling the OUT EP interrupt is shown below:
- + * -# Read the Device All Endpoint Interrupt register
- + * -# Repeat the following for each OUT EP interrupt bit set (from
- + * LSB to MSB).
- + * -# Read the Device Endpoint Interrupt (DOEPINTn) register
- + * -# If "Transfer Complete" call the request complete function
- + * -# If "Endpoint Disabled" complete the EP disable procedure.
- + * -# If "AHB Error Interrupt" log error
- + * -# If "Setup Phase Done" process Setup Packet (See Standard USB
- + * Command Processing)
- + */
- +static int32_t dwc_otg_pcd_handle_out_ep_intr(dwc_otg_pcd_t * pcd)
- +{
- +#define CLEAR_OUT_EP_INTR(__core_if,__epnum,__intr) \
- +do { \
- + doepint_data_t doepint = {.d32=0}; \
- + doepint.b.__intr = 1; \
- + DWC_WRITE_REG32(&__core_if->dev_if->out_ep_regs[__epnum]->doepint, \
- + doepint.d32); \
- +} while (0)
- +
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
- + uint32_t ep_intr;
- + doepint_data_t doepint = {.d32 = 0 };
- + uint32_t epnum = 0;
- + dwc_otg_pcd_ep_t *ep;
- + dwc_ep_t *dwc_ep;
- + dctl_data_t dctl = {.d32 = 0 };
- + gintmsk_data_t gintmsk = {.d32 = 0 };
- +
- +
- + DWC_DEBUGPL(DBG_PCDV, "%s()\n", __func__);
- +
- + /* Read in the device interrupt bits */
- + ep_intr = dwc_otg_read_dev_all_out_ep_intr(core_if);
- +
- + while (ep_intr) {
- + if (ep_intr & 0x1) {
- + /* Get EP pointer */
- + ep = get_out_ep(pcd, epnum);
- + dwc_ep = &ep->dwc_ep;
- +
- +#ifdef VERBOSE
- + DWC_DEBUGPL(DBG_PCDV,
- + "EP%d-%s: type=%d, mps=%d\n",
- + dwc_ep->num, (dwc_ep->is_in ? "IN" : "OUT"),
- + dwc_ep->type, dwc_ep->maxpacket);
- +#endif
- + doepint.d32 =
- + dwc_otg_read_dev_out_ep_intr(core_if, dwc_ep);
- + /* Moved this interrupt upper due to core deffect of asserting
- + * OUT EP 0 xfercompl along with stsphsrcvd in BDMA */
- + if (doepint.b.stsphsercvd) {
- + deptsiz0_data_t deptsiz;
- + CLEAR_OUT_EP_INTR(core_if, epnum, stsphsercvd);
- + deptsiz.d32 =
- + DWC_READ_REG32(&core_if->dev_if->
- + out_ep_regs[0]->doeptsiz);
- + if (core_if->snpsid >= OTG_CORE_REV_3_00a
- + && core_if->dma_enable
- + && core_if->dma_desc_enable == 0
- + && doepint.b.xfercompl
- + && deptsiz.b.xfersize == 24) {
- + CLEAR_OUT_EP_INTR(core_if, epnum,
- + xfercompl);
- + doepint.b.xfercompl = 0;
- + ep0_out_start(core_if, pcd);
- + }
- + if ((core_if->dma_desc_enable) ||
- + (core_if->dma_enable
- + && core_if->snpsid >=
- + OTG_CORE_REV_3_00a)) {
- + do_setup_in_status_phase(pcd);
- + }
- + }
- + /* Transfer complete */
- + if (doepint.b.xfercompl) {
- +
- + if (epnum == 0) {
- + /* Clear the bit in DOEPINTn for this interrupt */
- + CLEAR_OUT_EP_INTR(core_if, epnum, xfercompl);
- + if (core_if->snpsid >= OTG_CORE_REV_3_00a) {
- + DWC_DEBUGPL(DBG_PCDV, "DOEPINT=%x doepint=%x\n",
- + DWC_READ_REG32(&core_if->dev_if->out_ep_regs[0]->doepint),
- + doepint.d32);
- + DWC_DEBUGPL(DBG_PCDV, "DOEPCTL=%x \n",
- + DWC_READ_REG32(&core_if->dev_if->out_ep_regs[0]->doepctl));
- +
- + if (core_if->snpsid >= OTG_CORE_REV_3_00a
- + && core_if->dma_enable == 0) {
- + doepint_data_t doepint;
- + doepint.d32 = DWC_READ_REG32(&core_if->dev_if->
- + out_ep_regs[0]->doepint);
- + if (pcd->ep0state == EP0_IDLE && doepint.b.sr) {
- + CLEAR_OUT_EP_INTR(core_if, epnum, sr);
- + goto exit_xfercompl;
- + }
- + }
- + /* In case of DDMA look at SR bit to go to the Data Stage */
- + if (core_if->dma_desc_enable) {
- + dev_dma_desc_sts_t status = {.d32 = 0};
- + if (pcd->ep0state == EP0_IDLE) {
- + status.d32 = core_if->dev_if->setup_desc_addr[core_if->
- + dev_if->setup_desc_index]->status.d32;
- + if(pcd->data_terminated) {
- + pcd->data_terminated = 0;
- + status.d32 = core_if->dev_if->out_desc_addr->status.d32;
- + dwc_memcpy(&pcd->setup_pkt->req, pcd->backup_buf, 8);
- + }
- + if (status.b.sr) {
- + if (doepint.b.setup) {
- + DWC_DEBUGPL(DBG_PCDV, "DMA DESC EP0_IDLE SR=1 setup=1\n");
- + /* Already started data stage, clear setup */
- + CLEAR_OUT_EP_INTR(core_if, epnum, setup);
- + doepint.b.setup = 0;
- + handle_ep0(pcd);
- + /* Prepare for more setup packets */
- + if (pcd->ep0state == EP0_IN_STATUS_PHASE ||
- + pcd->ep0state == EP0_IN_DATA_PHASE) {
- + ep0_out_start(core_if, pcd);
- + }
- +
- + goto exit_xfercompl;
- + } else {
- + /* Prepare for more setup packets */
- + DWC_DEBUGPL(DBG_PCDV,
- + "EP0_IDLE SR=1 setup=0 new setup comes\n");
- + ep0_out_start(core_if, pcd);
- + }
- + }
- + } else {
- + dwc_otg_pcd_request_t *req;
- + dev_dma_desc_sts_t status = {.d32 = 0};
- + diepint_data_t diepint0;
- + diepint0.d32 = DWC_READ_REG32(&core_if->dev_if->
- + in_ep_regs[0]->diepint);
- +
- + if (pcd->ep0state == EP0_STALL || pcd->ep0state == EP0_DISCONNECT) {
- + DWC_ERROR("EP0 is stalled/disconnected\n");
- + }
- +
- + /* Clear IN xfercompl if set */
- + if (diepint0.b.xfercompl && (pcd->ep0state == EP0_IN_STATUS_PHASE
- + || pcd->ep0state == EP0_IN_DATA_PHASE)) {
- + DWC_WRITE_REG32(&core_if->dev_if->
- + in_ep_regs[0]->diepint, diepint0.d32);
- + }
- +
- + status.d32 = core_if->dev_if->setup_desc_addr[core_if->
- + dev_if->setup_desc_index]->status.d32;
- +
- + if (ep->dwc_ep.xfer_count != ep->dwc_ep.total_len
- + && (pcd->ep0state == EP0_OUT_DATA_PHASE))
- + status.d32 = core_if->dev_if->out_desc_addr->status.d32;
- + if (pcd->ep0state == EP0_OUT_STATUS_PHASE)
- + status.d32 = core_if->dev_if->
- + out_desc_addr->status.d32;
- +
- + if (status.b.sr) {
- + if (DWC_CIRCLEQ_EMPTY(&ep->queue)) {
- + DWC_DEBUGPL(DBG_PCDV, "Request queue empty!!\n");
- + } else {
- + DWC_DEBUGPL(DBG_PCDV, "complete req!!\n");
- + req = DWC_CIRCLEQ_FIRST(&ep->queue);
- + if (ep->dwc_ep.xfer_count != ep->dwc_ep.total_len &&
- + pcd->ep0state == EP0_OUT_DATA_PHASE) {
- + /* Read arrived setup packet from req->buf */
- + dwc_memcpy(&pcd->setup_pkt->req,
- + req->buf + ep->dwc_ep.xfer_count, 8);
- + }
- + req->actual = ep->dwc_ep.xfer_count;
- + dwc_otg_request_done(ep, req, -ECONNRESET);
- + ep->dwc_ep.start_xfer_buff = 0;
- + ep->dwc_ep.xfer_buff = 0;
- + ep->dwc_ep.xfer_len = 0;
- + }
- + pcd->ep0state = EP0_IDLE;
- + if (doepint.b.setup) {
- + DWC_DEBUGPL(DBG_PCDV, "EP0_IDLE SR=1 setup=1\n");
- + /* Data stage started, clear setup */
- + CLEAR_OUT_EP_INTR(core_if, epnum, setup);
- + doepint.b.setup = 0;
- + handle_ep0(pcd);
- + /* Prepare for setup packets if ep0in was enabled*/
- + if (pcd->ep0state == EP0_IN_STATUS_PHASE) {
- + ep0_out_start(core_if, pcd);
- + }
- +
- + goto exit_xfercompl;
- + } else {
- + /* Prepare for more setup packets */
- + DWC_DEBUGPL(DBG_PCDV,
- + "EP0_IDLE SR=1 setup=0 new setup comes 2\n");
- + ep0_out_start(core_if, pcd);
- + }
- + }
- + }
- + }
- + if (core_if->snpsid >= OTG_CORE_REV_2_94a && core_if->dma_enable
- + && core_if->dma_desc_enable == 0) {
- + doepint_data_t doepint_temp = {.d32 = 0};
- + deptsiz0_data_t doeptsize0 = {.d32 = 0 };
- + doepint_temp.d32 = DWC_READ_REG32(&core_if->dev_if->
- + out_ep_regs[ep->dwc_ep.num]->doepint);
- + doeptsize0.d32 = DWC_READ_REG32(&core_if->dev_if->
- + out_ep_regs[ep->dwc_ep.num]->doeptsiz);
- + if (pcd->ep0state == EP0_IDLE) {
- + if (doepint_temp.b.sr) {
- + CLEAR_OUT_EP_INTR(core_if, epnum, sr);
- + }
- + doepint.d32 = DWC_READ_REG32(&core_if->dev_if->
- + out_ep_regs[0]->doepint);
- + if (doeptsize0.b.supcnt == 3) {
- + DWC_DEBUGPL(DBG_ANY, "Rolling over!!!!!!!\n");
- + ep->dwc_ep.stp_rollover = 1;
- + }
- + if (doepint.b.setup) {
- +retry:
- + /* Already started data stage, clear setup */
- + CLEAR_OUT_EP_INTR(core_if, epnum, setup);
- + doepint.b.setup = 0;
- + handle_ep0(pcd);
- + ep->dwc_ep.stp_rollover = 0;
- + /* Prepare for more setup packets */
- + if (pcd->ep0state == EP0_IN_STATUS_PHASE ||
- + pcd->ep0state == EP0_IN_DATA_PHASE) {
- + ep0_out_start(core_if, pcd);
- + }
- + goto exit_xfercompl;
- + } else {
- + /* Prepare for more setup packets */
- + DWC_DEBUGPL(DBG_ANY,
- + "EP0_IDLE SR=1 setup=0 new setup comes\n");
- + doepint.d32 = DWC_READ_REG32(&core_if->dev_if->
- + out_ep_regs[0]->doepint);
- + if(doepint.b.setup)
- + goto retry;
- + ep0_out_start(core_if, pcd);
- + }
- + } else {
- + dwc_otg_pcd_request_t *req;
- + diepint_data_t diepint0 = {.d32 = 0};
- + doepint_data_t doepint_temp = {.d32 = 0};
- + depctl_data_t diepctl0;
- + diepint0.d32 = DWC_READ_REG32(&core_if->dev_if->
- + in_ep_regs[0]->diepint);
- + diepctl0.d32 = DWC_READ_REG32(&core_if->dev_if->
- + in_ep_regs[0]->diepctl);
- +
- + if (pcd->ep0state == EP0_IN_DATA_PHASE
- + || pcd->ep0state == EP0_IN_STATUS_PHASE) {
- + if (diepint0.b.xfercompl) {
- + DWC_WRITE_REG32(&core_if->dev_if->
- + in_ep_regs[0]->diepint, diepint0.d32);
- + }
- + if (diepctl0.b.epena) {
- + diepint_data_t diepint = {.d32 = 0};
- + diepctl0.b.snak = 1;
- + DWC_WRITE_REG32(&core_if->dev_if->
- + in_ep_regs[0]->diepctl, diepctl0.d32);
- + do {
- + dwc_udelay(10);
- + diepint.d32 = DWC_READ_REG32(&core_if->dev_if->
- + in_ep_regs[0]->diepint);
- + } while (!diepint.b.inepnakeff);
- + diepint.b.inepnakeff = 1;
- + DWC_WRITE_REG32(&core_if->dev_if->
- + in_ep_regs[0]->diepint, diepint.d32);
- + diepctl0.d32 = 0;
- + diepctl0.b.epdis = 1;
- + DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[0]->diepctl,
- + diepctl0.d32);
- + do {
- + dwc_udelay(10);
- + diepint.d32 = DWC_READ_REG32(&core_if->dev_if->
- + in_ep_regs[0]->diepint);
- + } while (!diepint.b.epdisabled);
- + diepint.b.epdisabled = 1;
- + DWC_WRITE_REG32(&core_if->dev_if->in_ep_regs[0]->diepint,
- + diepint.d32);
- + }
- + }
- + doepint_temp.d32 = DWC_READ_REG32(&core_if->dev_if->
- + out_ep_regs[ep->dwc_ep.num]->doepint);
- + if (doepint_temp.b.sr) {
- + CLEAR_OUT_EP_INTR(core_if, epnum, sr);
- + if (DWC_CIRCLEQ_EMPTY(&ep->queue)) {
- + DWC_DEBUGPL(DBG_PCDV, "Request queue empty!!\n");
- + } else {
- + DWC_DEBUGPL(DBG_PCDV, "complete req!!\n");
- + req = DWC_CIRCLEQ_FIRST(&ep->queue);
- + if (ep->dwc_ep.xfer_count != ep->dwc_ep.total_len &&
- + pcd->ep0state == EP0_OUT_DATA_PHASE) {
- + /* Read arrived setup packet from req->buf */
- + dwc_memcpy(&pcd->setup_pkt->req,
- + req->buf + ep->dwc_ep.xfer_count, 8);
- + }
- + req->actual = ep->dwc_ep.xfer_count;
- + dwc_otg_request_done(ep, req, -ECONNRESET);
- + ep->dwc_ep.start_xfer_buff = 0;
- + ep->dwc_ep.xfer_buff = 0;
- + ep->dwc_ep.xfer_len = 0;
- + }
- + pcd->ep0state = EP0_IDLE;
- + if (doepint.b.setup) {
- + DWC_DEBUGPL(DBG_PCDV, "EP0_IDLE SR=1 setup=1\n");
- + /* Data stage started, clear setup */
- + CLEAR_OUT_EP_INTR(core_if, epnum, setup);
- + doepint.b.setup = 0;
- + handle_ep0(pcd);
- + /* Prepare for setup packets if ep0in was enabled*/
- + if (pcd->ep0state == EP0_IN_STATUS_PHASE) {
- + ep0_out_start(core_if, pcd);
- + }
- + goto exit_xfercompl;
- + } else {
- + /* Prepare for more setup packets */
- + DWC_DEBUGPL(DBG_PCDV,
- + "EP0_IDLE SR=1 setup=0 new setup comes 2\n");
- + ep0_out_start(core_if, pcd);
- + }
- + }
- + }
- + }
- + if (core_if->dma_enable == 0 || pcd->ep0state != EP0_IDLE)
- + handle_ep0(pcd);
- +exit_xfercompl:
- + DWC_DEBUGPL(DBG_PCDV, "DOEPINT=%x doepint=%x\n",
- + dwc_otg_read_dev_out_ep_intr(core_if, dwc_ep), doepint.d32);
- + } else {
- + if (core_if->dma_desc_enable == 0
- + || pcd->ep0state != EP0_IDLE)
- + handle_ep0(pcd);
- + }
- +#ifdef DWC_EN_ISOC
- + } else if (dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) {
- + if (doepint.b.pktdrpsts == 0) {
- + /* Clear the bit in DOEPINTn for this interrupt */
- + CLEAR_OUT_EP_INTR(core_if,
- + epnum,
- + xfercompl);
- + complete_iso_ep(pcd, ep);
- + } else {
- +
- + doepint_data_t doepint = {.d32 = 0 };
- + doepint.b.xfercompl = 1;
- + doepint.b.pktdrpsts = 1;
- + DWC_WRITE_REG32
- + (&core_if->dev_if->out_ep_regs
- + [epnum]->doepint,
- + doepint.d32);
- + if (handle_iso_out_pkt_dropped
- + (core_if, dwc_ep)) {
- + complete_iso_ep(pcd,
- + ep);
- + }
- + }
- +#endif /* DWC_EN_ISOC */
- +#ifdef DWC_UTE_PER_IO
- + } else if (dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) {
- + CLEAR_OUT_EP_INTR(core_if, epnum, xfercompl);
- + if (!ep->stopped)
- + complete_xiso_ep(ep);
- +#endif /* DWC_UTE_PER_IO */
- + } else {
- + /* Clear the bit in DOEPINTn for this interrupt */
- + CLEAR_OUT_EP_INTR(core_if, epnum,
- + xfercompl);
- +
- + if (core_if->core_params->dev_out_nak) {
- + DWC_TIMER_CANCEL(pcd->core_if->ep_xfer_timer[epnum]);
- + pcd->core_if->ep_xfer_info[epnum].state = 0;
- +#ifdef DEBUG
- + print_memory_payload(pcd, dwc_ep);
- +#endif
- + }
- + complete_ep(ep);
- + }
- +
- + }
- +
- + /* Endpoint disable */
- + if (doepint.b.epdisabled) {
- +
- + /* Clear the bit in DOEPINTn for this interrupt */
- + CLEAR_OUT_EP_INTR(core_if, epnum, epdisabled);
- + if (core_if->core_params->dev_out_nak) {
- +#ifdef DEBUG
- + print_memory_payload(pcd, dwc_ep);
- +#endif
- + /* In case of timeout condition */
- + if (core_if->ep_xfer_info[epnum].state == 2) {
- + dctl.d32 = DWC_READ_REG32(&core_if->dev_if->
- + dev_global_regs->dctl);
- + dctl.b.cgoutnak = 1;
- + DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl,
- + dctl.d32);
- + /* Unmask goutnakeff interrupt which was masked
- + * during handle nak out interrupt */
- + gintmsk.b.goutnakeff = 1;
- + DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk,
- + 0, gintmsk.d32);
- +
- + complete_ep(ep);
- + }
- + }
- + if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC)
- + {
- + dctl_data_t dctl;
- + gintmsk_data_t intr_mask = {.d32 = 0};
- + dwc_otg_pcd_request_t *req = 0;
- +
- + dctl.d32 = DWC_READ_REG32(&core_if->dev_if->
- + dev_global_regs->dctl);
- + dctl.b.cgoutnak = 1;
- + DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl,
- + dctl.d32);
- +
- + intr_mask.d32 = 0;
- + intr_mask.b.incomplisoout = 1;
- +
- + /* Get any pending requests */
- + if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) {
- + req = DWC_CIRCLEQ_FIRST(&ep->queue);
- + if (!req) {
- + DWC_PRINTF("complete_ep 0x%p, req = NULL!\n", ep);
- + } else {
- + dwc_otg_request_done(ep, req, 0);
- + start_next_request(ep);
- + }
- + } else {
- + DWC_PRINTF("complete_ep 0x%p, ep->queue empty!\n", ep);
- + }
- + }
- + }
- + /* AHB Error */
- + if (doepint.b.ahberr) {
- + DWC_ERROR("EP%d OUT AHB Error\n", epnum);
- + DWC_ERROR("EP%d DEPDMA=0x%08x \n",
- + epnum, core_if->dev_if->out_ep_regs[epnum]->doepdma);
- + CLEAR_OUT_EP_INTR(core_if, epnum, ahberr);
- + }
- + /* Setup Phase Done (contorl EPs) */
- + if (doepint.b.setup) {
- +#ifdef DEBUG_EP0
- + DWC_DEBUGPL(DBG_PCD, "EP%d SETUP Done\n", epnum);
- +#endif
- + CLEAR_OUT_EP_INTR(core_if, epnum, setup);
- +
- + handle_ep0(pcd);
- + }
- +
- + /** OUT EP BNA Intr */
- + if (doepint.b.bna) {
- + CLEAR_OUT_EP_INTR(core_if, epnum, bna);
- + if (core_if->dma_desc_enable) {
- +#ifdef DWC_EN_ISOC
- + if (dwc_ep->type ==
- + DWC_OTG_EP_TYPE_ISOC) {
- + /*
- + * This checking is performed to prevent first "false" BNA
- + * handling occuring right after reconnect
- + */
- + if (dwc_ep->next_frame !=
- + 0xffffffff)
- + dwc_otg_pcd_handle_iso_bna(ep);
- + } else
- +#endif /* DWC_EN_ISOC */
- + {
- + dwc_otg_pcd_handle_noniso_bna(ep);
- + }
- + }
- + }
- + /* Babble Interrupt */
- + if (doepint.b.babble) {
- + DWC_DEBUGPL(DBG_ANY, "EP%d OUT Babble\n",
- + epnum);
- + handle_out_ep_babble_intr(pcd, epnum);
- +
- + CLEAR_OUT_EP_INTR(core_if, epnum, babble);
- + }
- + if (doepint.b.outtknepdis) {
- + DWC_DEBUGPL(DBG_ANY, "EP%d OUT Token received when EP is \
- + disabled\n",epnum);
- + if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) {
- + doepmsk_data_t doepmsk = {.d32 = 0};
- + ep->dwc_ep.frame_num = core_if->frame_num;
- + if (ep->dwc_ep.bInterval > 1) {
- + depctl_data_t depctl;
- + depctl.d32 = DWC_READ_REG32(&core_if->dev_if->
- + out_ep_regs[epnum]->doepctl);
- + if (ep->dwc_ep.frame_num & 0x1) {
- + depctl.b.setd1pid = 1;
- + depctl.b.setd0pid = 0;
- + } else {
- + depctl.b.setd0pid = 1;
- + depctl.b.setd1pid = 0;
- + }
- + DWC_WRITE_REG32(&core_if->dev_if->
- + out_ep_regs[epnum]->doepctl, depctl.d32);
- + }
- + start_next_request(ep);
- + doepmsk.b.outtknepdis = 1;
- + DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->doepmsk,
- + doepmsk.d32, 0);
- + }
- + CLEAR_OUT_EP_INTR(core_if, epnum, outtknepdis);
- + }
- +
- + /* NAK Interrutp */
- + if (doepint.b.nak) {
- + DWC_DEBUGPL(DBG_ANY, "EP%d OUT NAK\n", epnum);
- + handle_out_ep_nak_intr(pcd, epnum);
- +
- + CLEAR_OUT_EP_INTR(core_if, epnum, nak);
- + }
- + /* NYET Interrutp */
- + if (doepint.b.nyet) {
- + DWC_DEBUGPL(DBG_ANY, "EP%d OUT NYET\n", epnum);
- + handle_out_ep_nyet_intr(pcd, epnum);
- +
- + CLEAR_OUT_EP_INTR(core_if, epnum, nyet);
- + }
- + }
- +
- + epnum++;
- + ep_intr >>= 1;
- + }
- +
- + return 1;
- +
- +#undef CLEAR_OUT_EP_INTR
- +}
- +static int drop_transfer(uint32_t trgt_fr, uint32_t curr_fr, uint8_t frm_overrun)
- +{
- + int retval = 0;
- + if(!frm_overrun && curr_fr >= trgt_fr)
- + retval = 1;
- + else if (frm_overrun
- + && (curr_fr >= trgt_fr && ((curr_fr - trgt_fr) < 0x3FFF / 2)))
- + retval = 1;
- + return retval;
- +}
- +/**
- + * Incomplete ISO IN Transfer Interrupt.
- + * This interrupt indicates one of the following conditions occurred
- + * while transmitting an ISOC transaction.
- + * - Corrupted IN Token for ISOC EP.
- + * - Packet not complete in FIFO.
- + * The follow actions will be taken:
- + * -# Determine the EP
- + * -# Set incomplete flag in dwc_ep structure
- + * -# Disable EP; when "Endpoint Disabled" interrupt is received
- + * Flush FIFO
- + */
- +int32_t dwc_otg_pcd_handle_incomplete_isoc_in_intr(dwc_otg_pcd_t * pcd)
- +{
- + gintsts_data_t gintsts;
- +
- +#ifdef DWC_EN_ISOC
- + dwc_otg_dev_if_t *dev_if;
- + deptsiz_data_t deptsiz = {.d32 = 0 };
- + depctl_data_t depctl = {.d32 = 0 };
- + dsts_data_t dsts = {.d32 = 0 };
- + dwc_ep_t *dwc_ep;
- + int i;
- +
- + dev_if = GET_CORE_IF(pcd)->dev_if;
- +
- + for (i = 1; i <= dev_if->num_in_eps; ++i) {
- + dwc_ep = &pcd->in_ep[i].dwc_ep;
- + if (dwc_ep->active && dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) {
- + deptsiz.d32 =
- + DWC_READ_REG32(&dev_if->in_ep_regs[i]->dieptsiz);
- + depctl.d32 =
- + DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl);
- +
- + if (depctl.b.epdis && deptsiz.d32) {
- + set_current_pkt_info(GET_CORE_IF(pcd), dwc_ep);
- + if (dwc_ep->cur_pkt >= dwc_ep->pkt_cnt) {
- + dwc_ep->cur_pkt = 0;
- + dwc_ep->proc_buf_num =
- + (dwc_ep->proc_buf_num ^ 1) & 0x1;
- +
- + if (dwc_ep->proc_buf_num) {
- + dwc_ep->cur_pkt_addr =
- + dwc_ep->xfer_buff1;
- + dwc_ep->cur_pkt_dma_addr =
- + dwc_ep->dma_addr1;
- + } else {
- + dwc_ep->cur_pkt_addr =
- + dwc_ep->xfer_buff0;
- + dwc_ep->cur_pkt_dma_addr =
- + dwc_ep->dma_addr0;
- + }
- +
- + }
- +
- + dsts.d32 =
- + DWC_READ_REG32(&GET_CORE_IF(pcd)->dev_if->
- + dev_global_regs->dsts);
- + dwc_ep->next_frame = dsts.b.soffn;
- +
- + dwc_otg_iso_ep_start_frm_transfer(GET_CORE_IF
- + (pcd),
- + dwc_ep);
- + }
- + }
- + }
- +
- +#else
- + depctl_data_t depctl = {.d32 = 0 };
- + dwc_ep_t *dwc_ep;
- + dwc_otg_dev_if_t *dev_if;
- + int i;
- + dev_if = GET_CORE_IF(pcd)->dev_if;
- +
- + DWC_DEBUGPL(DBG_PCD,"Incomplete ISO IN \n");
- +
- + for (i = 1; i <= dev_if->num_in_eps; ++i) {
- + dwc_ep = &pcd->in_ep[i-1].dwc_ep;
- + depctl.d32 =
- + DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl);
- + if (depctl.b.epena && dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) {
- + if (drop_transfer(dwc_ep->frame_num, GET_CORE_IF(pcd)->frame_num,
- + dwc_ep->frm_overrun))
- + {
- + depctl.d32 =
- + DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl);
- + depctl.b.snak = 1;
- + depctl.b.epdis = 1;
- + DWC_MODIFY_REG32(&dev_if->in_ep_regs[i]->diepctl, depctl.d32, depctl.d32);
- + }
- + }
- + }
- +
- + /*intr_mask.b.incomplisoin = 1;
- + DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
- + intr_mask.d32, 0); */
- +#endif //DWC_EN_ISOC
- +
- + /* Clear interrupt */
- + gintsts.d32 = 0;
- + gintsts.b.incomplisoin = 1;
- + DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
- + gintsts.d32);
- +
- + return 1;
- +}
- +
- +/**
- + * Incomplete ISO OUT Transfer Interrupt.
- + *
- + * This interrupt indicates that the core has dropped an ISO OUT
- + * packet. The following conditions can be the cause:
- + * - FIFO Full, the entire packet would not fit in the FIFO.
- + * - CRC Error
- + * - Corrupted Token
- + * The follow actions will be taken:
- + * -# Determine the EP
- + * -# Set incomplete flag in dwc_ep structure
- + * -# Read any data from the FIFO
- + * -# Disable EP. When "Endpoint Disabled" interrupt is received
- + * re-enable EP.
- + */
- +int32_t dwc_otg_pcd_handle_incomplete_isoc_out_intr(dwc_otg_pcd_t * pcd)
- +{
- +
- + gintsts_data_t gintsts;
- +
- +#ifdef DWC_EN_ISOC
- + dwc_otg_dev_if_t *dev_if;
- + deptsiz_data_t deptsiz = {.d32 = 0 };
- + depctl_data_t depctl = {.d32 = 0 };
- + dsts_data_t dsts = {.d32 = 0 };
- + dwc_ep_t *dwc_ep;
- + int i;
- +
- + dev_if = GET_CORE_IF(pcd)->dev_if;
- +
- + for (i = 1; i <= dev_if->num_out_eps; ++i) {
- + dwc_ep = &pcd->in_ep[i].dwc_ep;
- + if (pcd->out_ep[i].dwc_ep.active &&
- + pcd->out_ep[i].dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) {
- + deptsiz.d32 =
- + DWC_READ_REG32(&dev_if->out_ep_regs[i]->doeptsiz);
- + depctl.d32 =
- + DWC_READ_REG32(&dev_if->out_ep_regs[i]->doepctl);
- +
- + if (depctl.b.epdis && deptsiz.d32) {
- + set_current_pkt_info(GET_CORE_IF(pcd),
- + &pcd->out_ep[i].dwc_ep);
- + if (dwc_ep->cur_pkt >= dwc_ep->pkt_cnt) {
- + dwc_ep->cur_pkt = 0;
- + dwc_ep->proc_buf_num =
- + (dwc_ep->proc_buf_num ^ 1) & 0x1;
- +
- + if (dwc_ep->proc_buf_num) {
- + dwc_ep->cur_pkt_addr =
- + dwc_ep->xfer_buff1;
- + dwc_ep->cur_pkt_dma_addr =
- + dwc_ep->dma_addr1;
- + } else {
- + dwc_ep->cur_pkt_addr =
- + dwc_ep->xfer_buff0;
- + dwc_ep->cur_pkt_dma_addr =
- + dwc_ep->dma_addr0;
- + }
- +
- + }
- +
- + dsts.d32 =
- + DWC_READ_REG32(&GET_CORE_IF(pcd)->dev_if->
- + dev_global_regs->dsts);
- + dwc_ep->next_frame = dsts.b.soffn;
- +
- + dwc_otg_iso_ep_start_frm_transfer(GET_CORE_IF
- + (pcd),
- + dwc_ep);
- + }
- + }
- + }
- +#else
- + /** @todo implement ISR */
- + gintmsk_data_t intr_mask = {.d32 = 0 };
- + dwc_otg_core_if_t *core_if;
- + deptsiz_data_t deptsiz = {.d32 = 0 };
- + depctl_data_t depctl = {.d32 = 0 };
- + dctl_data_t dctl = {.d32 = 0 };
- + dwc_ep_t *dwc_ep = NULL;
- + int i;
- + core_if = GET_CORE_IF(pcd);
- +
- + for (i = 0; i < core_if->dev_if->num_out_eps; ++i) {
- + dwc_ep = &pcd->out_ep[i].dwc_ep;
- + depctl.d32 =
- + DWC_READ_REG32(&core_if->dev_if->out_ep_regs[dwc_ep->num]->doepctl);
- + if (depctl.b.epena && depctl.b.dpid == (core_if->frame_num & 0x1)) {
- + core_if->dev_if->isoc_ep = dwc_ep;
- + deptsiz.d32 =
- + DWC_READ_REG32(&core_if->dev_if->out_ep_regs[dwc_ep->num]->doeptsiz);
- + break;
- + }
- + }
- + dctl.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dctl);
- + gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts);
- + intr_mask.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk);
- +
- + if (!intr_mask.b.goutnakeff) {
- + /* Unmask it */
- + intr_mask.b.goutnakeff = 1;
- + DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, intr_mask.d32);
- + }
- + if (!gintsts.b.goutnakeff) {
- + dctl.b.sgoutnak = 1;
- + }
- + DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32);
- +
- + depctl.d32 = DWC_READ_REG32(&core_if->dev_if->out_ep_regs[dwc_ep->num]->doepctl);
- + if (depctl.b.epena) {
- + depctl.b.epdis = 1;
- + depctl.b.snak = 1;
- + }
- + DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[dwc_ep->num]->doepctl, depctl.d32);
- +
- + intr_mask.d32 = 0;
- + intr_mask.b.incomplisoout = 1;
- +
- +#endif /* DWC_EN_ISOC */
- +
- + /* Clear interrupt */
- + gintsts.d32 = 0;
- + gintsts.b.incomplisoout = 1;
- + DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
- + gintsts.d32);
- +
- + return 1;
- +}
- +
- +/**
- + * This function handles the Global IN NAK Effective interrupt.
- + *
- + */
- +int32_t dwc_otg_pcd_handle_in_nak_effective(dwc_otg_pcd_t * pcd)
- +{
- + dwc_otg_dev_if_t *dev_if = GET_CORE_IF(pcd)->dev_if;
- + depctl_data_t diepctl = {.d32 = 0 };
- + gintmsk_data_t intr_mask = {.d32 = 0 };
- + gintsts_data_t gintsts;
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
- + int i;
- +
- + DWC_DEBUGPL(DBG_PCD, "Global IN NAK Effective\n");
- +
- + /* Disable all active IN EPs */
- + for (i = 0; i <= dev_if->num_in_eps; i++) {
- + diepctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[i]->diepctl);
- + if (!(diepctl.b.eptype & 1) && diepctl.b.epena) {
- + if (core_if->start_predict > 0)
- + core_if->start_predict++;
- + diepctl.b.epdis = 1;
- + diepctl.b.snak = 1;
- + DWC_WRITE_REG32(&dev_if->in_ep_regs[i]->diepctl, diepctl.d32);
- + }
- + }
- +
- +
- + /* Disable the Global IN NAK Effective Interrupt */
- + intr_mask.b.ginnakeff = 1;
- + DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
- + intr_mask.d32, 0);
- +
- + /* Clear interrupt */
- + gintsts.d32 = 0;
- + gintsts.b.ginnakeff = 1;
- + DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
- + gintsts.d32);
- +
- + return 1;
- +}
- +
- +/**
- + * OUT NAK Effective.
- + *
- + */
- +int32_t dwc_otg_pcd_handle_out_nak_effective(dwc_otg_pcd_t * pcd)
- +{
- + dwc_otg_dev_if_t *dev_if = GET_CORE_IF(pcd)->dev_if;
- + gintmsk_data_t intr_mask = {.d32 = 0 };
- + gintsts_data_t gintsts;
- + depctl_data_t doepctl;
- + int i;
- +
- + /* Disable the Global OUT NAK Effective Interrupt */
- + intr_mask.b.goutnakeff = 1;
- + DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk,
- + intr_mask.d32, 0);
- +
- + /* If DEV OUT NAK enabled*/
- + if (pcd->core_if->core_params->dev_out_nak) {
- + /* Run over all out endpoints to determine the ep number on
- + * which the timeout has happened
- + */
- + for (i = 0; i <= dev_if->num_out_eps; i++) {
- + if ( pcd->core_if->ep_xfer_info[i].state == 2 )
- + break;
- + }
- + if (i > dev_if->num_out_eps) {
- + dctl_data_t dctl;
- + dctl.d32 =
- + DWC_READ_REG32(&dev_if->dev_global_regs->dctl);
- + dctl.b.cgoutnak = 1;
- + DWC_WRITE_REG32(&dev_if->dev_global_regs->dctl,
- + dctl.d32);
- + goto out;
- + }
- +
- + /* Disable the endpoint */
- + doepctl.d32 = DWC_READ_REG32(&dev_if->out_ep_regs[i]->doepctl);
- + if (doepctl.b.epena) {
- + doepctl.b.epdis = 1;
- + doepctl.b.snak = 1;
- + }
- + DWC_WRITE_REG32(&dev_if->out_ep_regs[i]->doepctl, doepctl.d32);
- + return 1;
- + }
- + /* We come here from Incomplete ISO OUT handler */
- + if (dev_if->isoc_ep) {
- + dwc_ep_t *dwc_ep = (dwc_ep_t *)dev_if->isoc_ep;
- + uint32_t epnum = dwc_ep->num;
- + doepint_data_t doepint;
- + doepint.d32 =
- + DWC_READ_REG32(&dev_if->out_ep_regs[dwc_ep->num]->doepint);
- + dev_if->isoc_ep = NULL;
- + doepctl.d32 =
- + DWC_READ_REG32(&dev_if->out_ep_regs[epnum]->doepctl);
- + DWC_PRINTF("Before disable DOEPCTL = %08x\n", doepctl.d32);
- + if (doepctl.b.epena) {
- + doepctl.b.epdis = 1;
- + doepctl.b.snak = 1;
- + }
- + DWC_WRITE_REG32(&dev_if->out_ep_regs[epnum]->doepctl,
- + doepctl.d32);
- + return 1;
- + } else
- + DWC_PRINTF("INTERRUPT Handler not implemented for %s\n",
- + "Global OUT NAK Effective\n");
- +
- +out:
- + /* Clear interrupt */
- + gintsts.d32 = 0;
- + gintsts.b.goutnakeff = 1;
- + DWC_WRITE_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintsts,
- + gintsts.d32);
- +
- + return 1;
- +}
- +
- +/**
- + * PCD interrupt handler.
- + *
- + * The PCD handles the device interrupts. Many conditions can cause a
- + * device interrupt. When an interrupt occurs, the device interrupt
- + * service routine determines the cause of the interrupt and
- + * dispatches handling to the appropriate function. These interrupt
- + * handling functions are described below.
- + *
- + * All interrupt registers are processed from LSB to MSB.
- + *
- + */
- +int32_t dwc_otg_pcd_handle_intr(dwc_otg_pcd_t * pcd)
- +{
- + dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd);
- +#ifdef VERBOSE
- + dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
- +#endif
- + gintsts_data_t gintr_status;
- + int32_t retval = 0;
- +
- + /* Exit from ISR if core is hibernated */
- + if (core_if->hibernation_suspend == 1) {
- + return retval;
- + }
- +#ifdef VERBOSE
- + DWC_DEBUGPL(DBG_ANY, "%s() gintsts=%08x gintmsk=%08x\n",
- + __func__,
- + DWC_READ_REG32(&global_regs->gintsts),
- + DWC_READ_REG32(&global_regs->gintmsk));
- +#endif
- +
- + if (dwc_otg_is_device_mode(core_if)) {
- + DWC_SPINLOCK(pcd->lock);
- +#ifdef VERBOSE
- + DWC_DEBUGPL(DBG_PCDV, "%s() gintsts=%08x gintmsk=%08x\n",
- + __func__,
- + DWC_READ_REG32(&global_regs->gintsts),
- + DWC_READ_REG32(&global_regs->gintmsk));
- +#endif
- +
- + gintr_status.d32 = dwc_otg_read_core_intr(core_if);
- +
- + DWC_DEBUGPL(DBG_PCDV, "%s: gintsts&gintmsk=%08x\n",
- + __func__, gintr_status.d32);
- +
- + if (gintr_status.b.sofintr) {
- + retval |= dwc_otg_pcd_handle_sof_intr(pcd);
- + }
- + if (gintr_status.b.rxstsqlvl) {
- + retval |=
- + dwc_otg_pcd_handle_rx_status_q_level_intr(pcd);
- + }
- + if (gintr_status.b.nptxfempty) {
- + retval |= dwc_otg_pcd_handle_np_tx_fifo_empty_intr(pcd);
- + }
- + if (gintr_status.b.goutnakeff) {
- + retval |= dwc_otg_pcd_handle_out_nak_effective(pcd);
- + }
- + if (gintr_status.b.i2cintr) {
- + retval |= dwc_otg_pcd_handle_i2c_intr(pcd);
- + }
- + if (gintr_status.b.erlysuspend) {
- + retval |= dwc_otg_pcd_handle_early_suspend_intr(pcd);
- + }
- + if (gintr_status.b.usbreset) {
- + retval |= dwc_otg_pcd_handle_usb_reset_intr(pcd);
- + }
- + if (gintr_status.b.enumdone) {
- + retval |= dwc_otg_pcd_handle_enum_done_intr(pcd);
- + }
- + if (gintr_status.b.isooutdrop) {
- + retval |=
- + dwc_otg_pcd_handle_isoc_out_packet_dropped_intr
- + (pcd);
- + }
- + if (gintr_status.b.eopframe) {
- + retval |=
- + dwc_otg_pcd_handle_end_periodic_frame_intr(pcd);
- + }
- + if (gintr_status.b.inepint) {
- + if (!core_if->multiproc_int_enable) {
- + retval |= dwc_otg_pcd_handle_in_ep_intr(pcd);
- + }
- + }
- + if (gintr_status.b.outepintr) {
- + if (!core_if->multiproc_int_enable) {
- + retval |= dwc_otg_pcd_handle_out_ep_intr(pcd);
- + }
- + }
- + if (gintr_status.b.epmismatch) {
- + retval |= dwc_otg_pcd_handle_ep_mismatch_intr(pcd);
- + }
- + if (gintr_status.b.fetsusp) {
- + retval |= dwc_otg_pcd_handle_ep_fetsusp_intr(pcd);
- + }
- + if (gintr_status.b.ginnakeff) {
- + retval |= dwc_otg_pcd_handle_in_nak_effective(pcd);
- + }
- + if (gintr_status.b.incomplisoin) {
- + retval |=
- + dwc_otg_pcd_handle_incomplete_isoc_in_intr(pcd);
- + }
- + if (gintr_status.b.incomplisoout) {
- + retval |=
- + dwc_otg_pcd_handle_incomplete_isoc_out_intr(pcd);
- + }
- +
- + /* In MPI mode Device Endpoints interrupts are asserted
- + * without setting outepintr and inepint bits set, so these
- + * Interrupt handlers are called without checking these bit-fields
- + */
- + if (core_if->multiproc_int_enable) {
- + retval |= dwc_otg_pcd_handle_in_ep_intr(pcd);
- + retval |= dwc_otg_pcd_handle_out_ep_intr(pcd);
- + }
- +#ifdef VERBOSE
- + DWC_DEBUGPL(DBG_PCDV, "%s() gintsts=%0x\n", __func__,
- + DWC_READ_REG32(&global_regs->gintsts));
- +#endif
- + DWC_SPINUNLOCK(pcd->lock);
- + }
- + return retval;
- +}
- +
- +#endif /* DWC_HOST_ONLY */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c
- @@ -0,0 +1,1262 @@
- + /* ==========================================================================
- + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd_linux.c $
- + * $Revision: #21 $
- + * $Date: 2012/08/10 $
- + * $Change: 2047372 $
- + *
- + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
- + * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
- + * otherwise expressly agreed to in writing between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product under
- + * any End User Software License Agreement or Agreement for Licensed Product
- + * with Synopsys or any supplement thereto. You are permitted to use and
- + * redistribute this Software in source and binary forms, with or without
- + * modification, provided that redistributions of source code must retain this
- + * notice. You may not view, use, disclose, copy or distribute this file or
- + * any information contained herein except pursuant to this license grant from
- + * Synopsys. If you do not agree with this notice, including the disclaimer
- + * below, then you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
- + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- + * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
- + * ========================================================================== */
- +#ifndef DWC_HOST_ONLY
- +
- +/** @file
- + * This file implements the Peripheral Controller Driver.
- + *
- + * The Peripheral Controller Driver (PCD) is responsible for
- + * translating requests from the Function Driver into the appropriate
- + * actions on the DWC_otg controller. It isolates the Function Driver
- + * from the specifics of the controller by providing an API to the
- + * Function Driver.
- + *
- + * The Peripheral Controller Driver for Linux will implement the
- + * Gadget API, so that the existing Gadget drivers can be used.
- + * (Gadget Driver is the Linux terminology for a Function Driver.)
- + *
- + * The Linux Gadget API is defined in the header file
- + * <code><linux/usb_gadget.h></code>. The USB EP operations API is
- + * defined in the structure <code>usb_ep_ops</code> and the USB
- + * Controller API is defined in the structure
- + * <code>usb_gadget_ops</code>.
- + *
- + */
- +
- +#include "dwc_otg_os_dep.h"
- +#include "dwc_otg_pcd_if.h"
- +#include "dwc_otg_pcd.h"
- +#include "dwc_otg_driver.h"
- +#include "dwc_otg_dbg.h"
- +
- +extern bool fiq_enable;
- +
- +static struct gadget_wrapper {
- + dwc_otg_pcd_t *pcd;
- +
- + struct usb_gadget gadget;
- + struct usb_gadget_driver *driver;
- +
- + struct usb_ep ep0;
- + struct usb_ep in_ep[16];
- + struct usb_ep out_ep[16];
- +
- +} *gadget_wrapper;
- +
- +/* Display the contents of the buffer */
- +extern void dump_msg(const u8 * buf, unsigned int length);
- +/**
- + * Get the dwc_otg_pcd_ep_t* from usb_ep* pointer - NULL in case
- + * if the endpoint is not found
- + */
- +static struct dwc_otg_pcd_ep *ep_from_handle(dwc_otg_pcd_t * pcd, void *handle)
- +{
- + int i;
- + if (pcd->ep0.priv == handle) {
- + return &pcd->ep0;
- + }
- +
- + for (i = 0; i < MAX_EPS_CHANNELS - 1; i++) {
- + if (pcd->in_ep[i].priv == handle)
- + return &pcd->in_ep[i];
- + if (pcd->out_ep[i].priv == handle)
- + return &pcd->out_ep[i];
- + }
- +
- + return NULL;
- +}
- +
- +/* USB Endpoint Operations */
- +/*
- + * The following sections briefly describe the behavior of the Gadget
- + * API endpoint operations implemented in the DWC_otg driver
- + * software. Detailed descriptions of the generic behavior of each of
- + * these functions can be found in the Linux header file
- + * include/linux/usb_gadget.h.
- + *
- + * The Gadget API provides wrapper functions for each of the function
- + * pointers defined in usb_ep_ops. The Gadget Driver calls the wrapper
- + * function, which then calls the underlying PCD function. The
- + * following sections are named according to the wrapper
- + * functions. Within each section, the corresponding DWC_otg PCD
- + * function name is specified.
- + *
- + */
- +
- +/**
- + * This function is called by the Gadget Driver for each EP to be
- + * configured for the current configuration (SET_CONFIGURATION).
- + *
- + * This function initializes the dwc_otg_ep_t data structure, and then
- + * calls dwc_otg_ep_activate.
- + */
- +static int ep_enable(struct usb_ep *usb_ep,
- + const struct usb_endpoint_descriptor *ep_desc)
- +{
- + int retval;
- +
- + DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p)\n", __func__, usb_ep, ep_desc);
- +
- + if (!usb_ep || !ep_desc || ep_desc->bDescriptorType != USB_DT_ENDPOINT) {
- + DWC_WARN("%s, bad ep or descriptor\n", __func__);
- + return -EINVAL;
- + }
- + if (usb_ep == &gadget_wrapper->ep0) {
- + DWC_WARN("%s, bad ep(0)\n", __func__);
- + return -EINVAL;
- + }
- +
- + /* Check FIFO size? */
- + if (!ep_desc->wMaxPacketSize) {
- + DWC_WARN("%s, bad %s maxpacket\n", __func__, usb_ep->name);
- + return -ERANGE;
- + }
- +
- + if (!gadget_wrapper->driver ||
- + gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) {
- + DWC_WARN("%s, bogus device state\n", __func__);
- + return -ESHUTDOWN;
- + }
- +
- + /* Delete after check - MAS */
- +#if 0
- + nat = (uint32_t) ep_desc->wMaxPacketSize;
- + printk(KERN_ALERT "%s: nat (before) =%d\n", __func__, nat);
- + nat = (nat >> 11) & 0x03;
- + printk(KERN_ALERT "%s: nat (after) =%d\n", __func__, nat);
- +#endif
- + retval = dwc_otg_pcd_ep_enable(gadget_wrapper->pcd,
- + (const uint8_t *)ep_desc,
- + (void *)usb_ep);
- + if (retval) {
- + DWC_WARN("dwc_otg_pcd_ep_enable failed\n");
- + return -EINVAL;
- + }
- +
- + usb_ep->maxpacket = le16_to_cpu(ep_desc->wMaxPacketSize);
- +
- + return 0;
- +}
- +
- +/**
- + * This function is called when an EP is disabled due to disconnect or
- + * change in configuration. Any pending requests will terminate with a
- + * status of -ESHUTDOWN.
- + *
- + * This function modifies the dwc_otg_ep_t data structure for this EP,
- + * and then calls dwc_otg_ep_deactivate.
- + */
- +static int ep_disable(struct usb_ep *usb_ep)
- +{
- + int retval;
- +
- + DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, usb_ep);
- + if (!usb_ep) {
- + DWC_DEBUGPL(DBG_PCD, "%s, %s not enabled\n", __func__,
- + usb_ep ? usb_ep->name : NULL);
- + return -EINVAL;
- + }
- +
- + retval = dwc_otg_pcd_ep_disable(gadget_wrapper->pcd, usb_ep);
- + if (retval) {
- + retval = -EINVAL;
- + }
- +
- + return retval;
- +}
- +
- +/**
- + * This function allocates a request object to use with the specified
- + * endpoint.
- + *
- + * @param ep The endpoint to be used with with the request
- + * @param gfp_flags the GFP_* flags to use.
- + */
- +static struct usb_request *dwc_otg_pcd_alloc_request(struct usb_ep *ep,
- + gfp_t gfp_flags)
- +{
- + struct usb_request *usb_req;
- +
- + DWC_DEBUGPL(DBG_PCDV, "%s(%p,%d)\n", __func__, ep, gfp_flags);
- + if (0 == ep) {
- + DWC_WARN("%s() %s\n", __func__, "Invalid EP!\n");
- + return 0;
- + }
- + usb_req = kzalloc(sizeof(*usb_req), gfp_flags);
- + if (0 == usb_req) {
- + DWC_WARN("%s() %s\n", __func__, "request allocation failed!\n");
- + return 0;
- + }
- + usb_req->dma = DWC_DMA_ADDR_INVALID;
- +
- + return usb_req;
- +}
- +
- +/**
- + * This function frees a request object.
- + *
- + * @param ep The endpoint associated with the request
- + * @param req The request being freed
- + */
- +static void dwc_otg_pcd_free_request(struct usb_ep *ep, struct usb_request *req)
- +{
- + DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p)\n", __func__, ep, req);
- +
- + if (0 == ep || 0 == req) {
- + DWC_WARN("%s() %s\n", __func__,
- + "Invalid ep or req argument!\n");
- + return;
- + }
- +
- + kfree(req);
- +}
- +
- +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
- +/**
- + * This function allocates an I/O buffer to be used for a transfer
- + * to/from the specified endpoint.
- + *
- + * @param usb_ep The endpoint to be used with with the request
- + * @param bytes The desired number of bytes for the buffer
- + * @param dma Pointer to the buffer's DMA address; must be valid
- + * @param gfp_flags the GFP_* flags to use.
- + * @return address of a new buffer or null is buffer could not be allocated.
- + */
- +static void *dwc_otg_pcd_alloc_buffer(struct usb_ep *usb_ep, unsigned bytes,
- + dma_addr_t * dma, gfp_t gfp_flags)
- +{
- + void *buf;
- + dwc_otg_pcd_t *pcd = 0;
- +
- + pcd = gadget_wrapper->pcd;
- +
- + DWC_DEBUGPL(DBG_PCDV, "%s(%p,%d,%p,%0x)\n", __func__, usb_ep, bytes,
- + dma, gfp_flags);
- +
- + /* Check dword alignment */
- + if ((bytes & 0x3UL) != 0) {
- + DWC_WARN("%s() Buffer size is not a multiple of"
- + "DWORD size (%d)", __func__, bytes);
- + }
- +
- + buf = dma_alloc_coherent(NULL, bytes, dma, gfp_flags);
- + WARN_ON(!buf);
- +
- + /* Check dword alignment */
- + if (((int)buf & 0x3UL) != 0) {
- + DWC_WARN("%s() Buffer is not DWORD aligned (%p)",
- + __func__, buf);
- + }
- +
- + return buf;
- +}
- +
- +/**
- + * This function frees an I/O buffer that was allocated by alloc_buffer.
- + *
- + * @param usb_ep the endpoint associated with the buffer
- + * @param buf address of the buffer
- + * @param dma The buffer's DMA address
- + * @param bytes The number of bytes of the buffer
- + */
- +static void dwc_otg_pcd_free_buffer(struct usb_ep *usb_ep, void *buf,
- + dma_addr_t dma, unsigned bytes)
- +{
- + dwc_otg_pcd_t *pcd = 0;
- +
- + pcd = gadget_wrapper->pcd;
- +
- + DWC_DEBUGPL(DBG_PCDV, "%s(%p,%0x,%d)\n", __func__, buf, dma, bytes);
- +
- + dma_free_coherent(NULL, bytes, buf, dma);
- +}
- +#endif
- +
- +/**
- + * This function is used to submit an I/O Request to an EP.
- + *
- + * - When the request completes the request's completion callback
- + * is called to return the request to the driver.
- + * - An EP, except control EPs, may have multiple requests
- + * pending.
- + * - Once submitted the request cannot be examined or modified.
- + * - Each request is turned into one or more packets.
- + * - A BULK EP can queue any amount of data; the transfer is
- + * packetized.
- + * - Zero length Packets are specified with the request 'zero'
- + * flag.
- + */
- +static int ep_queue(struct usb_ep *usb_ep, struct usb_request *usb_req,
- + gfp_t gfp_flags)
- +{
- + dwc_otg_pcd_t *pcd;
- + struct dwc_otg_pcd_ep *ep = NULL;
- + int retval = 0, is_isoc_ep = 0;
- + dma_addr_t dma_addr = DWC_DMA_ADDR_INVALID;
- +
- + DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p,%d)\n",
- + __func__, usb_ep, usb_req, gfp_flags);
- +
- + if (!usb_req || !usb_req->complete || !usb_req->buf) {
- + DWC_WARN("bad params\n");
- + return -EINVAL;
- + }
- +
- + if (!usb_ep) {
- + DWC_WARN("bad ep\n");
- + return -EINVAL;
- + }
- +
- + pcd = gadget_wrapper->pcd;
- + if (!gadget_wrapper->driver ||
- + gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) {
- + DWC_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n",
- + gadget_wrapper->gadget.speed);
- + DWC_WARN("bogus device state\n");
- + return -ESHUTDOWN;
- + }
- +
- + DWC_DEBUGPL(DBG_PCD, "%s queue req %p, len %d buf %p\n",
- + usb_ep->name, usb_req, usb_req->length, usb_req->buf);
- +
- + usb_req->status = -EINPROGRESS;
- + usb_req->actual = 0;
- +
- + ep = ep_from_handle(pcd, usb_ep);
- + if (ep == NULL)
- + is_isoc_ep = 0;
- + else
- + is_isoc_ep = (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) ? 1 : 0;
- +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
- + dma_addr = usb_req->dma;
- +#else
- + if (GET_CORE_IF(pcd)->dma_enable) {
- + dwc_otg_device_t *otg_dev = gadget_wrapper->pcd->otg_dev;
- + struct device *dev = NULL;
- +
- + if (otg_dev != NULL)
- + dev = DWC_OTG_OS_GETDEV(otg_dev->os_dep);
- +
- + if (usb_req->length != 0 &&
- + usb_req->dma == DWC_DMA_ADDR_INVALID) {
- + dma_addr = dma_map_single(dev, usb_req->buf,
- + usb_req->length,
- + ep->dwc_ep.is_in ?
- + DMA_TO_DEVICE:
- + DMA_FROM_DEVICE);
- + }
- + }
- +#endif
- +
- +#ifdef DWC_UTE_PER_IO
- + if (is_isoc_ep == 1) {
- + retval = dwc_otg_pcd_xiso_ep_queue(pcd, usb_ep, usb_req->buf, dma_addr,
- + usb_req->length, usb_req->zero, usb_req,
- + gfp_flags == GFP_ATOMIC ? 1 : 0, &usb_req->ext_req);
- + if (retval)
- + return -EINVAL;
- +
- + return 0;
- + }
- +#endif
- + retval = dwc_otg_pcd_ep_queue(pcd, usb_ep, usb_req->buf, dma_addr,
- + usb_req->length, usb_req->zero, usb_req,
- + gfp_flags == GFP_ATOMIC ? 1 : 0);
- + if (retval) {
- + return -EINVAL;
- + }
- +
- + return 0;
- +}
- +
- +/**
- + * This function cancels an I/O request from an EP.
- + */
- +static int ep_dequeue(struct usb_ep *usb_ep, struct usb_request *usb_req)
- +{
- + DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p)\n", __func__, usb_ep, usb_req);
- +
- + if (!usb_ep || !usb_req) {
- + DWC_WARN("bad argument\n");
- + return -EINVAL;
- + }
- + if (!gadget_wrapper->driver ||
- + gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) {
- + DWC_WARN("bogus device state\n");
- + return -ESHUTDOWN;
- + }
- + if (dwc_otg_pcd_ep_dequeue(gadget_wrapper->pcd, usb_ep, usb_req)) {
- + return -EINVAL;
- + }
- +
- + return 0;
- +}
- +
- +/**
- + * usb_ep_set_halt stalls an endpoint.
- + *
- + * usb_ep_clear_halt clears an endpoint halt and resets its data
- + * toggle.
- + *
- + * Both of these functions are implemented with the same underlying
- + * function. The behavior depends on the value argument.
- + *
- + * @param[in] usb_ep the Endpoint to halt or clear halt.
- + * @param[in] value
- + * - 0 means clear_halt.
- + * - 1 means set_halt,
- + * - 2 means clear stall lock flag.
- + * - 3 means set stall lock flag.
- + */
- +static int ep_halt(struct usb_ep *usb_ep, int value)
- +{
- + int retval = 0;
- +
- + DWC_DEBUGPL(DBG_PCD, "HALT %s %d\n", usb_ep->name, value);
- +
- + if (!usb_ep) {
- + DWC_WARN("bad ep\n");
- + return -EINVAL;
- + }
- +
- + retval = dwc_otg_pcd_ep_halt(gadget_wrapper->pcd, usb_ep, value);
- + if (retval == -DWC_E_AGAIN) {
- + return -EAGAIN;
- + } else if (retval) {
- + retval = -EINVAL;
- + }
- +
- + return retval;
- +}
- +
- +//#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30))
- +#if 0
- +/**
- + * ep_wedge: sets the halt feature and ignores clear requests
- + *
- + * @usb_ep: the endpoint being wedged
- + *
- + * Use this to stall an endpoint and ignore CLEAR_FEATURE(HALT_ENDPOINT)
- + * requests. If the gadget driver clears the halt status, it will
- + * automatically unwedge the endpoint.
- + *
- + * Returns zero on success, else negative errno. *
- + * Check usb_ep_set_wedge() at "usb_gadget.h" for details
- + */
- +static int ep_wedge(struct usb_ep *usb_ep)
- +{
- + int retval = 0;
- +
- + DWC_DEBUGPL(DBG_PCD, "WEDGE %s\n", usb_ep->name);
- +
- + if (!usb_ep) {
- + DWC_WARN("bad ep\n");
- + return -EINVAL;
- + }
- +
- + retval = dwc_otg_pcd_ep_wedge(gadget_wrapper->pcd, usb_ep);
- + if (retval == -DWC_E_AGAIN) {
- + retval = -EAGAIN;
- + } else if (retval) {
- + retval = -EINVAL;
- + }
- +
- + return retval;
- +}
- +#endif
- +
- +#ifdef DWC_EN_ISOC
- +/**
- + * This function is used to submit an ISOC Transfer Request to an EP.
- + *
- + * - Every time a sync period completes the request's completion callback
- + * is called to provide data to the gadget driver.
- + * - Once submitted the request cannot be modified.
- + * - Each request is turned into periodic data packets untill ISO
- + * Transfer is stopped..
- + */
- +static int iso_ep_start(struct usb_ep *usb_ep, struct usb_iso_request *req,
- + gfp_t gfp_flags)
- +{
- + int retval = 0;
- +
- + if (!req || !req->process_buffer || !req->buf0 || !req->buf1) {
- + DWC_WARN("bad params\n");
- + return -EINVAL;
- + }
- +
- + if (!usb_ep) {
- + DWC_PRINTF("bad params\n");
- + return -EINVAL;
- + }
- +
- + req->status = -EINPROGRESS;
- +
- + retval =
- + dwc_otg_pcd_iso_ep_start(gadget_wrapper->pcd, usb_ep, req->buf0,
- + req->buf1, req->dma0, req->dma1,
- + req->sync_frame, req->data_pattern_frame,
- + req->data_per_frame,
- + req->
- + flags & USB_REQ_ISO_ASAP ? -1 :
- + req->start_frame, req->buf_proc_intrvl,
- + req, gfp_flags == GFP_ATOMIC ? 1 : 0);
- +
- + if (retval) {
- + return -EINVAL;
- + }
- +
- + return retval;
- +}
- +
- +/**
- + * This function stops ISO EP Periodic Data Transfer.
- + */
- +static int iso_ep_stop(struct usb_ep *usb_ep, struct usb_iso_request *req)
- +{
- + int retval = 0;
- + if (!usb_ep) {
- + DWC_WARN("bad ep\n");
- + }
- +
- + if (!gadget_wrapper->driver ||
- + gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) {
- + DWC_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n",
- + gadget_wrapper->gadget.speed);
- + DWC_WARN("bogus device state\n");
- + }
- +
- + dwc_otg_pcd_iso_ep_stop(gadget_wrapper->pcd, usb_ep, req);
- + if (retval) {
- + retval = -EINVAL;
- + }
- +
- + return retval;
- +}
- +
- +static struct usb_iso_request *alloc_iso_request(struct usb_ep *ep,
- + int packets, gfp_t gfp_flags)
- +{
- + struct usb_iso_request *pReq = NULL;
- + uint32_t req_size;
- +
- + req_size = sizeof(struct usb_iso_request);
- + req_size +=
- + (2 * packets * (sizeof(struct usb_gadget_iso_packet_descriptor)));
- +
- + pReq = kmalloc(req_size, gfp_flags);
- + if (!pReq) {
- + DWC_WARN("Can't allocate Iso Request\n");
- + return 0;
- + }
- + pReq->iso_packet_desc0 = (void *)(pReq + 1);
- +
- + pReq->iso_packet_desc1 = pReq->iso_packet_desc0 + packets;
- +
- + return pReq;
- +}
- +
- +static void free_iso_request(struct usb_ep *ep, struct usb_iso_request *req)
- +{
- + kfree(req);
- +}
- +
- +static struct usb_isoc_ep_ops dwc_otg_pcd_ep_ops = {
- + .ep_ops = {
- + .enable = ep_enable,
- + .disable = ep_disable,
- +
- + .alloc_request = dwc_otg_pcd_alloc_request,
- + .free_request = dwc_otg_pcd_free_request,
- +
- +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
- + .alloc_buffer = dwc_otg_pcd_alloc_buffer,
- + .free_buffer = dwc_otg_pcd_free_buffer,
- +#endif
- +
- + .queue = ep_queue,
- + .dequeue = ep_dequeue,
- +
- + .set_halt = ep_halt,
- + .fifo_status = 0,
- + .fifo_flush = 0,
- + },
- + .iso_ep_start = iso_ep_start,
- + .iso_ep_stop = iso_ep_stop,
- + .alloc_iso_request = alloc_iso_request,
- + .free_iso_request = free_iso_request,
- +};
- +
- +#else
- +
- + int (*enable) (struct usb_ep *ep,
- + const struct usb_endpoint_descriptor *desc);
- + int (*disable) (struct usb_ep *ep);
- +
- + struct usb_request *(*alloc_request) (struct usb_ep *ep,
- + gfp_t gfp_flags);
- + void (*free_request) (struct usb_ep *ep, struct usb_request *req);
- +
- + int (*queue) (struct usb_ep *ep, struct usb_request *req,
- + gfp_t gfp_flags);
- + int (*dequeue) (struct usb_ep *ep, struct usb_request *req);
- +
- + int (*set_halt) (struct usb_ep *ep, int value);
- + int (*set_wedge) (struct usb_ep *ep);
- +
- + int (*fifo_status) (struct usb_ep *ep);
- + void (*fifo_flush) (struct usb_ep *ep);
- +static struct usb_ep_ops dwc_otg_pcd_ep_ops = {
- + .enable = ep_enable,
- + .disable = ep_disable,
- +
- + .alloc_request = dwc_otg_pcd_alloc_request,
- + .free_request = dwc_otg_pcd_free_request,
- +
- +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
- + .alloc_buffer = dwc_otg_pcd_alloc_buffer,
- + .free_buffer = dwc_otg_pcd_free_buffer,
- +#else
- + /* .set_wedge = ep_wedge, */
- + .set_wedge = NULL, /* uses set_halt instead */
- +#endif
- +
- + .queue = ep_queue,
- + .dequeue = ep_dequeue,
- +
- + .set_halt = ep_halt,
- + .fifo_status = 0,
- + .fifo_flush = 0,
- +
- +};
- +
- +#endif /* _EN_ISOC_ */
- +/* Gadget Operations */
- +/**
- + * The following gadget operations will be implemented in the DWC_otg
- + * PCD. Functions in the API that are not described below are not
- + * implemented.
- + *
- + * The Gadget API provides wrapper functions for each of the function
- + * pointers defined in usb_gadget_ops. The Gadget Driver calls the
- + * wrapper function, which then calls the underlying PCD function. The
- + * following sections are named according to the wrapper functions
- + * (except for ioctl, which doesn't have a wrapper function). Within
- + * each section, the corresponding DWC_otg PCD function name is
- + * specified.
- + *
- + */
- +
- +/**
- + *Gets the USB Frame number of the last SOF.
- + */
- +static int get_frame_number(struct usb_gadget *gadget)
- +{
- + struct gadget_wrapper *d;
- +
- + DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, gadget);
- +
- + if (gadget == 0) {
- + return -ENODEV;
- + }
- +
- + d = container_of(gadget, struct gadget_wrapper, gadget);
- + return dwc_otg_pcd_get_frame_number(d->pcd);
- +}
- +
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- +static int test_lpm_enabled(struct usb_gadget *gadget)
- +{
- + struct gadget_wrapper *d;
- +
- + d = container_of(gadget, struct gadget_wrapper, gadget);
- +
- + return dwc_otg_pcd_is_lpm_enabled(d->pcd);
- +}
- +#endif
- +
- +/**
- + * Initiates Session Request Protocol (SRP) to wakeup the host if no
- + * session is in progress. If a session is already in progress, but
- + * the device is suspended, remote wakeup signaling is started.
- + *
- + */
- +static int wakeup(struct usb_gadget *gadget)
- +{
- + struct gadget_wrapper *d;
- +
- + DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, gadget);
- +
- + if (gadget == 0) {
- + return -ENODEV;
- + } else {
- + d = container_of(gadget, struct gadget_wrapper, gadget);
- + }
- + dwc_otg_pcd_wakeup(d->pcd);
- + return 0;
- +}
- +
- +static const struct usb_gadget_ops dwc_otg_pcd_ops = {
- + .get_frame = get_frame_number,
- + .wakeup = wakeup,
- +#ifdef CONFIG_USB_DWC_OTG_LPM
- + .lpm_support = test_lpm_enabled,
- +#endif
- + // current versions must always be self-powered
- +};
- +
- +static int _setup(dwc_otg_pcd_t * pcd, uint8_t * bytes)
- +{
- + int retval = -DWC_E_NOT_SUPPORTED;
- + if (gadget_wrapper->driver && gadget_wrapper->driver->setup) {
- + retval = gadget_wrapper->driver->setup(&gadget_wrapper->gadget,
- + (struct usb_ctrlrequest
- + *)bytes);
- + }
- +
- + if (retval == -ENOTSUPP) {
- + retval = -DWC_E_NOT_SUPPORTED;
- + } else if (retval < 0) {
- + retval = -DWC_E_INVALID;
- + }
- +
- + return retval;
- +}
- +
- +#ifdef DWC_EN_ISOC
- +static int _isoc_complete(dwc_otg_pcd_t * pcd, void *ep_handle,
- + void *req_handle, int proc_buf_num)
- +{
- + int i, packet_count;
- + struct usb_gadget_iso_packet_descriptor *iso_packet = 0;
- + struct usb_iso_request *iso_req = req_handle;
- +
- + if (proc_buf_num) {
- + iso_packet = iso_req->iso_packet_desc1;
- + } else {
- + iso_packet = iso_req->iso_packet_desc0;
- + }
- + packet_count =
- + dwc_otg_pcd_get_iso_packet_count(pcd, ep_handle, req_handle);
- + for (i = 0; i < packet_count; ++i) {
- + int status;
- + int actual;
- + int offset;
- + dwc_otg_pcd_get_iso_packet_params(pcd, ep_handle, req_handle,
- + i, &status, &actual, &offset);
- + switch (status) {
- + case -DWC_E_NO_DATA:
- + status = -ENODATA;
- + break;
- + default:
- + if (status) {
- + DWC_PRINTF("unknown status in isoc packet\n");
- + }
- +
- + }
- + iso_packet[i].status = status;
- + iso_packet[i].offset = offset;
- + iso_packet[i].actual_length = actual;
- + }
- +
- + iso_req->status = 0;
- + iso_req->process_buffer(ep_handle, iso_req);
- +
- + return 0;
- +}
- +#endif /* DWC_EN_ISOC */
- +
- +#ifdef DWC_UTE_PER_IO
- +/**
- + * Copy the contents of the extended request to the Linux usb_request's
- + * extended part and call the gadget's completion.
- + *
- + * @param pcd Pointer to the pcd structure
- + * @param ep_handle Void pointer to the usb_ep structure
- + * @param req_handle Void pointer to the usb_request structure
- + * @param status Request status returned from the portable logic
- + * @param ereq_port Void pointer to the extended request structure
- + * created in the the portable part that contains the
- + * results of the processed iso packets.
- + */
- +static int _xisoc_complete(dwc_otg_pcd_t * pcd, void *ep_handle,
- + void *req_handle, int32_t status, void *ereq_port)
- +{
- + struct dwc_ute_iso_req_ext *ereqorg = NULL;
- + struct dwc_iso_xreq_port *ereqport = NULL;
- + struct dwc_ute_iso_packet_descriptor *desc_org = NULL;
- + int i;
- + struct usb_request *req;
- + //struct dwc_ute_iso_packet_descriptor *
- + //int status = 0;
- +
- + req = (struct usb_request *)req_handle;
- + ereqorg = &req->ext_req;
- + ereqport = (struct dwc_iso_xreq_port *)ereq_port;
- + desc_org = ereqorg->per_io_frame_descs;
- +
- + if (req && req->complete) {
- + /* Copy the request data from the portable logic to our request */
- + for (i = 0; i < ereqport->pio_pkt_count; i++) {
- + desc_org[i].actual_length =
- + ereqport->per_io_frame_descs[i].actual_length;
- + desc_org[i].status =
- + ereqport->per_io_frame_descs[i].status;
- + }
- +
- + switch (status) {
- + case -DWC_E_SHUTDOWN:
- + req->status = -ESHUTDOWN;
- + break;
- + case -DWC_E_RESTART:
- + req->status = -ECONNRESET;
- + break;
- + case -DWC_E_INVALID:
- + req->status = -EINVAL;
- + break;
- + case -DWC_E_TIMEOUT:
- + req->status = -ETIMEDOUT;
- + break;
- + default:
- + req->status = status;
- + }
- +
- + /* And call the gadget's completion */
- + req->complete(ep_handle, req);
- + }
- +
- + return 0;
- +}
- +#endif /* DWC_UTE_PER_IO */
- +
- +static int _complete(dwc_otg_pcd_t * pcd, void *ep_handle,
- + void *req_handle, int32_t status, uint32_t actual)
- +{
- + struct usb_request *req = (struct usb_request *)req_handle;
- +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,27)
- + struct dwc_otg_pcd_ep *ep = NULL;
- +#endif
- +
- + if (req && req->complete) {
- + switch (status) {
- + case -DWC_E_SHUTDOWN:
- + req->status = -ESHUTDOWN;
- + break;
- + case -DWC_E_RESTART:
- + req->status = -ECONNRESET;
- + break;
- + case -DWC_E_INVALID:
- + req->status = -EINVAL;
- + break;
- + case -DWC_E_TIMEOUT:
- + req->status = -ETIMEDOUT;
- + break;
- + default:
- + req->status = status;
- +
- + }
- +
- + req->actual = actual;
- + DWC_SPINUNLOCK(pcd->lock);
- + req->complete(ep_handle, req);
- + DWC_SPINLOCK(pcd->lock);
- + }
- +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,27)
- + ep = ep_from_handle(pcd, ep_handle);
- + if (GET_CORE_IF(pcd)->dma_enable) {
- + if (req->length != 0) {
- + dwc_otg_device_t *otg_dev = gadget_wrapper->pcd->otg_dev;
- + struct device *dev = NULL;
- +
- + if (otg_dev != NULL)
- + dev = DWC_OTG_OS_GETDEV(otg_dev->os_dep);
- +
- + dma_unmap_single(dev, req->dma, req->length,
- + ep->dwc_ep.is_in ?
- + DMA_TO_DEVICE: DMA_FROM_DEVICE);
- + }
- + }
- +#endif
- +
- + return 0;
- +}
- +
- +static int _connect(dwc_otg_pcd_t * pcd, int speed)
- +{
- + gadget_wrapper->gadget.speed = speed;
- + return 0;
- +}
- +
- +static int _disconnect(dwc_otg_pcd_t * pcd)
- +{
- + if (gadget_wrapper->driver && gadget_wrapper->driver->disconnect) {
- + gadget_wrapper->driver->disconnect(&gadget_wrapper->gadget);
- + }
- + return 0;
- +}
- +
- +static int _resume(dwc_otg_pcd_t * pcd)
- +{
- + if (gadget_wrapper->driver && gadget_wrapper->driver->resume) {
- + gadget_wrapper->driver->resume(&gadget_wrapper->gadget);
- + }
- +
- + return 0;
- +}
- +
- +static int _suspend(dwc_otg_pcd_t * pcd)
- +{
- + if (gadget_wrapper->driver && gadget_wrapper->driver->suspend) {
- + gadget_wrapper->driver->suspend(&gadget_wrapper->gadget);
- + }
- + return 0;
- +}
- +
- +/**
- + * This function updates the otg values in the gadget structure.
- + */
- +static int _hnp_changed(dwc_otg_pcd_t * pcd)
- +{
- +
- + if (!gadget_wrapper->gadget.is_otg)
- + return 0;
- +
- + gadget_wrapper->gadget.b_hnp_enable = get_b_hnp_enable(pcd);
- + gadget_wrapper->gadget.a_hnp_support = get_a_hnp_support(pcd);
- + gadget_wrapper->gadget.a_alt_hnp_support = get_a_alt_hnp_support(pcd);
- + return 0;
- +}
- +
- +static int _reset(dwc_otg_pcd_t * pcd)
- +{
- + return 0;
- +}
- +
- +#ifdef DWC_UTE_CFI
- +static int _cfi_setup(dwc_otg_pcd_t * pcd, void *cfi_req)
- +{
- + int retval = -DWC_E_INVALID;
- + if (gadget_wrapper->driver->cfi_feature_setup) {
- + retval =
- + gadget_wrapper->driver->
- + cfi_feature_setup(&gadget_wrapper->gadget,
- + (struct cfi_usb_ctrlrequest *)cfi_req);
- + }
- +
- + return retval;
- +}
- +#endif
- +
- +static const struct dwc_otg_pcd_function_ops fops = {
- + .complete = _complete,
- +#ifdef DWC_EN_ISOC
- + .isoc_complete = _isoc_complete,
- +#endif
- + .setup = _setup,
- + .disconnect = _disconnect,
- + .connect = _connect,
- + .resume = _resume,
- + .suspend = _suspend,
- + .hnp_changed = _hnp_changed,
- + .reset = _reset,
- +#ifdef DWC_UTE_CFI
- + .cfi_setup = _cfi_setup,
- +#endif
- +#ifdef DWC_UTE_PER_IO
- + .xisoc_complete = _xisoc_complete,
- +#endif
- +};
- +
- +/**
- + * This function is the top level PCD interrupt handler.
- + */
- +static irqreturn_t dwc_otg_pcd_irq(int irq, void *dev)
- +{
- + dwc_otg_pcd_t *pcd = dev;
- + int32_t retval = IRQ_NONE;
- +
- + retval = dwc_otg_pcd_handle_intr(pcd);
- + if (retval != 0) {
- + S3C2410X_CLEAR_EINTPEND();
- + }
- + return IRQ_RETVAL(retval);
- +}
- +
- +/**
- + * This function initialized the usb_ep structures to there default
- + * state.
- + *
- + * @param d Pointer on gadget_wrapper.
- + */
- +void gadget_add_eps(struct gadget_wrapper *d)
- +{
- + static const char *names[] = {
- +
- + "ep0",
- + "ep1in",
- + "ep2in",
- + "ep3in",
- + "ep4in",
- + "ep5in",
- + "ep6in",
- + "ep7in",
- + "ep8in",
- + "ep9in",
- + "ep10in",
- + "ep11in",
- + "ep12in",
- + "ep13in",
- + "ep14in",
- + "ep15in",
- + "ep1out",
- + "ep2out",
- + "ep3out",
- + "ep4out",
- + "ep5out",
- + "ep6out",
- + "ep7out",
- + "ep8out",
- + "ep9out",
- + "ep10out",
- + "ep11out",
- + "ep12out",
- + "ep13out",
- + "ep14out",
- + "ep15out"
- + };
- +
- + int i;
- + struct usb_ep *ep;
- + int8_t dev_endpoints;
- +
- + DWC_DEBUGPL(DBG_PCDV, "%s\n", __func__);
- +
- + INIT_LIST_HEAD(&d->gadget.ep_list);
- + d->gadget.ep0 = &d->ep0;
- + d->gadget.speed = USB_SPEED_UNKNOWN;
- +
- + INIT_LIST_HEAD(&d->gadget.ep0->ep_list);
- +
- + /**
- + * Initialize the EP0 structure.
- + */
- + ep = &d->ep0;
- +
- + /* Init the usb_ep structure. */
- + ep->name = names[0];
- + ep->ops = (struct usb_ep_ops *)&dwc_otg_pcd_ep_ops;
- +
- + /**
- + * @todo NGS: What should the max packet size be set to
- + * here? Before EP type is set?
- + */
- + ep->maxpacket = MAX_PACKET_SIZE;
- + dwc_otg_pcd_ep_enable(d->pcd, NULL, ep);
- +
- + list_add_tail(&ep->ep_list, &d->gadget.ep_list);
- +
- + /**
- + * Initialize the EP structures.
- + */
- + dev_endpoints = d->pcd->core_if->dev_if->num_in_eps;
- +
- + for (i = 0; i < dev_endpoints; i++) {
- + ep = &d->in_ep[i];
- +
- + /* Init the usb_ep structure. */
- + ep->name = names[d->pcd->in_ep[i].dwc_ep.num];
- + ep->ops = (struct usb_ep_ops *)&dwc_otg_pcd_ep_ops;
- +
- + /**
- + * @todo NGS: What should the max packet size be set to
- + * here? Before EP type is set?
- + */
- + ep->maxpacket = MAX_PACKET_SIZE;
- + list_add_tail(&ep->ep_list, &d->gadget.ep_list);
- + }
- +
- + dev_endpoints = d->pcd->core_if->dev_if->num_out_eps;
- +
- + for (i = 0; i < dev_endpoints; i++) {
- + ep = &d->out_ep[i];
- +
- + /* Init the usb_ep structure. */
- + ep->name = names[15 + d->pcd->out_ep[i].dwc_ep.num];
- + ep->ops = (struct usb_ep_ops *)&dwc_otg_pcd_ep_ops;
- +
- + /**
- + * @todo NGS: What should the max packet size be set to
- + * here? Before EP type is set?
- + */
- + ep->maxpacket = MAX_PACKET_SIZE;
- +
- + list_add_tail(&ep->ep_list, &d->gadget.ep_list);
- + }
- +
- + /* remove ep0 from the list. There is a ep0 pointer. */
- + list_del_init(&d->ep0.ep_list);
- +
- + d->ep0.maxpacket = MAX_EP0_SIZE;
- +}
- +
- +/**
- + * This function releases the Gadget device.
- + * required by device_unregister().
- + *
- + * @todo Should this do something? Should it free the PCD?
- + */
- +static void dwc_otg_pcd_gadget_release(struct device *dev)
- +{
- + DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, dev);
- +}
- +
- +static struct gadget_wrapper *alloc_wrapper(dwc_bus_dev_t *_dev)
- +{
- + static char pcd_name[] = "dwc_otg_pcd";
- + dwc_otg_device_t *otg_dev = DWC_OTG_BUSDRVDATA(_dev);
- + struct gadget_wrapper *d;
- + int retval;
- +
- + d = DWC_ALLOC(sizeof(*d));
- + if (d == NULL) {
- + return NULL;
- + }
- +
- + memset(d, 0, sizeof(*d));
- +
- + d->gadget.name = pcd_name;
- + d->pcd = otg_dev->pcd;
- +
- +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)
- + strcpy(d->gadget.dev.bus_id, "gadget");
- +#else
- + dev_set_name(&d->gadget.dev, "%s", "gadget");
- +#endif
- +
- + d->gadget.dev.parent = &_dev->dev;
- + d->gadget.dev.release = dwc_otg_pcd_gadget_release;
- + d->gadget.ops = &dwc_otg_pcd_ops;
- + d->gadget.max_speed = dwc_otg_pcd_is_dualspeed(otg_dev->pcd) ? USB_SPEED_HIGH:USB_SPEED_FULL;
- + d->gadget.is_otg = dwc_otg_pcd_is_otg(otg_dev->pcd);
- +
- + d->driver = 0;
- + /* Register the gadget device */
- + retval = device_register(&d->gadget.dev);
- + if (retval != 0) {
- + DWC_ERROR("device_register failed\n");
- + DWC_FREE(d);
- + return NULL;
- + }
- +
- + return d;
- +}
- +
- +static void free_wrapper(struct gadget_wrapper *d)
- +{
- + if (d->driver) {
- + /* should have been done already by driver model core */
- + DWC_WARN("driver '%s' is still registered\n",
- + d->driver->driver.name);
- +#ifdef CONFIG_USB_GADGET
- + usb_gadget_unregister_driver(d->driver);
- +#endif
- + }
- +
- + device_unregister(&d->gadget.dev);
- + DWC_FREE(d);
- +}
- +
- +/**
- + * This function initialized the PCD portion of the driver.
- + *
- + */
- +int pcd_init(dwc_bus_dev_t *_dev)
- +{
- + dwc_otg_device_t *otg_dev = DWC_OTG_BUSDRVDATA(_dev);
- + int retval = 0;
- +
- + DWC_DEBUGPL(DBG_PCDV, "%s(%p) otg_dev=%p\n", __func__, _dev, otg_dev);
- +
- + otg_dev->pcd = dwc_otg_pcd_init(otg_dev);
- +
- + if (!otg_dev->pcd) {
- + DWC_ERROR("dwc_otg_pcd_init failed\n");
- + return -ENOMEM;
- + }
- +
- + otg_dev->pcd->otg_dev = otg_dev;
- + gadget_wrapper = alloc_wrapper(_dev);
- +
- + /*
- + * Initialize EP structures
- + */
- + gadget_add_eps(gadget_wrapper);
- + /*
- + * Setup interupt handler
- + */
- + DWC_DEBUGPL(DBG_ANY, "registering handler for irq%d\n",
- + otg_dev->os_dep.irq_num);
- + retval = request_irq(otg_dev->os_dep.irq_num, dwc_otg_pcd_irq,
- + IRQF_SHARED, gadget_wrapper->gadget.name,
- + otg_dev->pcd);
- + if (retval != 0) {
- + DWC_ERROR("request of irq%d failed\n", otg_dev->os_dep.irq_num);
- + free_wrapper(gadget_wrapper);
- + return -EBUSY;
- + }
- +
- + dwc_otg_pcd_start(gadget_wrapper->pcd, &fops);
- +
- + return retval;
- +}
- +
- +/**
- + * Cleanup the PCD.
- + */
- +void pcd_remove(dwc_bus_dev_t *_dev)
- +{
- + dwc_otg_device_t *otg_dev = DWC_OTG_BUSDRVDATA(_dev);
- + dwc_otg_pcd_t *pcd = otg_dev->pcd;
- +
- + DWC_DEBUGPL(DBG_PCDV, "%s(%p) otg_dev %p\n", __func__, _dev, otg_dev);
- +
- + /*
- + * Free the IRQ
- + */
- + free_irq(otg_dev->os_dep.irq_num, pcd);
- + dwc_otg_pcd_remove(otg_dev->pcd);
- + free_wrapper(gadget_wrapper);
- + otg_dev->pcd = 0;
- +}
- +
- +#endif /* DWC_HOST_ONLY */
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/dwc_otg_regs.h
- @@ -0,0 +1,2550 @@
- +/* ==========================================================================
- + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_regs.h $
- + * $Revision: #98 $
- + * $Date: 2012/08/10 $
- + * $Change: 2047372 $
- + *
- + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
- + * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
- + * otherwise expressly agreed to in writing between Synopsys and you.
- + *
- + * The Software IS NOT an item of Licensed Software or Licensed Product under
- + * any End User Software License Agreement or Agreement for Licensed Product
- + * with Synopsys or any supplement thereto. You are permitted to use and
- + * redistribute this Software in source and binary forms, with or without
- + * modification, provided that redistributions of source code must retain this
- + * notice. You may not view, use, disclose, copy or distribute this file or
- + * any information contained herein except pursuant to this license grant from
- + * Synopsys. If you do not agree with this notice, including the disclaimer
- + * below, then you are not authorized to use the Software.
- + *
- + * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
- + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- + * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
- + * ========================================================================== */
- +
- +#ifndef __DWC_OTG_REGS_H__
- +#define __DWC_OTG_REGS_H__
- +
- +#include "dwc_otg_core_if.h"
- +
- +/**
- + * @file
- + *
- + * This file contains the data structures for accessing the DWC_otg core registers.
- + *
- + * The application interfaces with the HS OTG core by reading from and
- + * writing to the Control and Status Register (CSR) space through the
- + * AHB Slave interface. These registers are 32 bits wide, and the
- + * addresses are 32-bit-block aligned.
- + * CSRs are classified as follows:
- + * - Core Global Registers
- + * - Device Mode Registers
- + * - Device Global Registers
- + * - Device Endpoint Specific Registers
- + * - Host Mode Registers
- + * - Host Global Registers
- + * - Host Port CSRs
- + * - Host Channel Specific Registers
- + *
- + * Only the Core Global registers can be accessed in both Device and
- + * Host modes. When the HS OTG core is operating in one mode, either
- + * Device or Host, the application must not access registers from the
- + * other mode. When the core switches from one mode to another, the
- + * registers in the new mode of operation must be reprogrammed as they
- + * would be after a power-on reset.
- + */
- +
- +/****************************************************************************/
- +/** DWC_otg Core registers .
- + * The dwc_otg_core_global_regs structure defines the size
- + * and relative field offsets for the Core Global registers.
- + */
- +typedef struct dwc_otg_core_global_regs {
- + /** OTG Control and Status Register. <i>Offset: 000h</i> */
- + volatile uint32_t gotgctl;
- + /** OTG Interrupt Register. <i>Offset: 004h</i> */
- + volatile uint32_t gotgint;
- + /**Core AHB Configuration Register. <i>Offset: 008h</i> */
- + volatile uint32_t gahbcfg;
- +
- +#define DWC_GLBINTRMASK 0x0001
- +#define DWC_DMAENABLE 0x0020
- +#define DWC_NPTXEMPTYLVL_EMPTY 0x0080
- +#define DWC_NPTXEMPTYLVL_HALFEMPTY 0x0000
- +#define DWC_PTXEMPTYLVL_EMPTY 0x0100
- +#define DWC_PTXEMPTYLVL_HALFEMPTY 0x0000
- +
- + /**Core USB Configuration Register. <i>Offset: 00Ch</i> */
- + volatile uint32_t gusbcfg;
- + /**Core Reset Register. <i>Offset: 010h</i> */
- + volatile uint32_t grstctl;
- + /**Core Interrupt Register. <i>Offset: 014h</i> */
- + volatile uint32_t gintsts;
- + /**Core Interrupt Mask Register. <i>Offset: 018h</i> */
- + volatile uint32_t gintmsk;
- + /**Receive Status Queue Read Register (Read Only). <i>Offset: 01Ch</i> */
- + volatile uint32_t grxstsr;
- + /**Receive Status Queue Read & POP Register (Read Only). <i>Offset: 020h</i>*/
- + volatile uint32_t grxstsp;
- + /**Receive FIFO Size Register. <i>Offset: 024h</i> */
- + volatile uint32_t grxfsiz;
- + /**Non Periodic Transmit FIFO Size Register. <i>Offset: 028h</i> */
- + volatile uint32_t gnptxfsiz;
- + /**Non Periodic Transmit FIFO/Queue Status Register (Read
- + * Only). <i>Offset: 02Ch</i> */
- + volatile uint32_t gnptxsts;
- + /**I2C Access Register. <i>Offset: 030h</i> */
- + volatile uint32_t gi2cctl;
- + /**PHY Vendor Control Register. <i>Offset: 034h</i> */
- + volatile uint32_t gpvndctl;
- + /**General Purpose Input/Output Register. <i>Offset: 038h</i> */
- + volatile uint32_t ggpio;
- + /**User ID Register. <i>Offset: 03Ch</i> */
- + volatile uint32_t guid;
- + /**Synopsys ID Register (Read Only). <i>Offset: 040h</i> */
- + volatile uint32_t gsnpsid;
- + /**User HW Config1 Register (Read Only). <i>Offset: 044h</i> */
- + volatile uint32_t ghwcfg1;
- + /**User HW Config2 Register (Read Only). <i>Offset: 048h</i> */
- + volatile uint32_t ghwcfg2;
- +#define DWC_SLAVE_ONLY_ARCH 0
- +#define DWC_EXT_DMA_ARCH 1
- +#define DWC_INT_DMA_ARCH 2
- +
- +#define DWC_MODE_HNP_SRP_CAPABLE 0
- +#define DWC_MODE_SRP_ONLY_CAPABLE 1
- +#define DWC_MODE_NO_HNP_SRP_CAPABLE 2
- +#define DWC_MODE_SRP_CAPABLE_DEVICE 3
- +#define DWC_MODE_NO_SRP_CAPABLE_DEVICE 4
- +#define DWC_MODE_SRP_CAPABLE_HOST 5
- +#define DWC_MODE_NO_SRP_CAPABLE_HOST 6
- +
- + /**User HW Config3 Register (Read Only). <i>Offset: 04Ch</i> */
- + volatile uint32_t ghwcfg3;
- + /**User HW Config4 Register (Read Only). <i>Offset: 050h</i>*/
- + volatile uint32_t ghwcfg4;
- + /** Core LPM Configuration register <i>Offset: 054h</i>*/
- + volatile uint32_t glpmcfg;
- + /** Global PowerDn Register <i>Offset: 058h</i> */
- + volatile uint32_t gpwrdn;
- + /** Global DFIFO SW Config Register <i>Offset: 05Ch</i> */
- + volatile uint32_t gdfifocfg;
- + /** ADP Control Register <i>Offset: 060h</i> */
- + volatile uint32_t adpctl;
- + /** Reserved <i>Offset: 064h-0FFh</i> */
- + volatile uint32_t reserved39[39];
- + /** Host Periodic Transmit FIFO Size Register. <i>Offset: 100h</i> */
- + volatile uint32_t hptxfsiz;
- + /** Device Periodic Transmit FIFO#n Register if dedicated fifos are disabled,
- + otherwise Device Transmit FIFO#n Register.
- + * <i>Offset: 104h + (FIFO_Number-1)*04h, 1 <= FIFO Number <= 15 (1<=n<=15).</i> */
- + volatile uint32_t dtxfsiz[15];
- +} dwc_otg_core_global_regs_t;
- +
- +/**
- + * This union represents the bit fields of the Core OTG Control
- + * and Status Register (GOTGCTL). Set the bits using the bit
- + * fields then write the <i>d32</i> value to the register.
- + */
- +typedef union gotgctl_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + unsigned sesreqscs:1;
- + unsigned sesreq:1;
- + unsigned vbvalidoven:1;
- + unsigned vbvalidovval:1;
- + unsigned avalidoven:1;
- + unsigned avalidovval:1;
- + unsigned bvalidoven:1;
- + unsigned bvalidovval:1;
- + unsigned hstnegscs:1;
- + unsigned hnpreq:1;
- + unsigned hstsethnpen:1;
- + unsigned devhnpen:1;
- + unsigned reserved12_15:4;
- + unsigned conidsts:1;
- + unsigned dbnctime:1;
- + unsigned asesvld:1;
- + unsigned bsesvld:1;
- + unsigned otgver:1;
- + unsigned reserved1:1;
- + unsigned multvalidbc:5;
- + unsigned chirpen:1;
- + unsigned reserved28_31:4;
- + } b;
- +} gotgctl_data_t;
- +
- +/**
- + * This union represents the bit fields of the Core OTG Interrupt Register
- + * (GOTGINT). Set/clear the bits using the bit fields then write the <i>d32</i>
- + * value to the register.
- + */
- +typedef union gotgint_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + /** Current Mode */
- + unsigned reserved0_1:2;
- +
- + /** Session End Detected */
- + unsigned sesenddet:1;
- +
- + unsigned reserved3_7:5;
- +
- + /** Session Request Success Status Change */
- + unsigned sesreqsucstschng:1;
- + /** Host Negotiation Success Status Change */
- + unsigned hstnegsucstschng:1;
- +
- + unsigned reserved10_16:7;
- +
- + /** Host Negotiation Detected */
- + unsigned hstnegdet:1;
- + /** A-Device Timeout Change */
- + unsigned adevtoutchng:1;
- + /** Debounce Done */
- + unsigned debdone:1;
- + /** Multi-Valued input changed */
- + unsigned mvic:1;
- +
- + unsigned reserved31_21:11;
- +
- + } b;
- +} gotgint_data_t;
- +
- +/**
- + * This union represents the bit fields of the Core AHB Configuration
- + * Register (GAHBCFG). Set/clear the bits using the bit fields then
- + * write the <i>d32</i> value to the register.
- + */
- +typedef union gahbcfg_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + unsigned glblintrmsk:1;
- +#define DWC_GAHBCFG_GLBINT_ENABLE 1
- +
- + unsigned hburstlen:4;
- +#define DWC_GAHBCFG_INT_DMA_BURST_SINGLE 0
- +#define DWC_GAHBCFG_INT_DMA_BURST_INCR 1
- +#define DWC_GAHBCFG_INT_DMA_BURST_INCR4 3
- +#define DWC_GAHBCFG_INT_DMA_BURST_INCR8 5
- +#define DWC_GAHBCFG_INT_DMA_BURST_INCR16 7
- +
- + unsigned dmaenable:1;
- +#define DWC_GAHBCFG_DMAENABLE 1
- + unsigned reserved:1;
- + unsigned nptxfemplvl_txfemplvl:1;
- + unsigned ptxfemplvl:1;
- +#define DWC_GAHBCFG_TXFEMPTYLVL_EMPTY 1
- +#define DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY 0
- + unsigned reserved9_20:12;
- + unsigned remmemsupp:1;
- + unsigned notialldmawrit:1;
- + unsigned ahbsingle:1;
- + unsigned reserved24_31:8;
- + } b;
- +} gahbcfg_data_t;
- +
- +/**
- + * This union represents the bit fields of the Core USB Configuration
- + * Register (GUSBCFG). Set the bits using the bit fields then write
- + * the <i>d32</i> value to the register.
- + */
- +typedef union gusbcfg_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + unsigned toutcal:3;
- + unsigned phyif:1;
- + unsigned ulpi_utmi_sel:1;
- + unsigned fsintf:1;
- + unsigned physel:1;
- + unsigned ddrsel:1;
- + unsigned srpcap:1;
- + unsigned hnpcap:1;
- + unsigned usbtrdtim:4;
- + unsigned reserved1:1;
- + unsigned phylpwrclksel:1;
- + unsigned otgutmifssel:1;
- + unsigned ulpi_fsls:1;
- + unsigned ulpi_auto_res:1;
- + unsigned ulpi_clk_sus_m:1;
- + unsigned ulpi_ext_vbus_drv:1;
- + unsigned ulpi_int_vbus_indicator:1;
- + unsigned term_sel_dl_pulse:1;
- + unsigned indicator_complement:1;
- + unsigned indicator_pass_through:1;
- + unsigned ulpi_int_prot_dis:1;
- + unsigned ic_usb_cap:1;
- + unsigned ic_traffic_pull_remove:1;
- + unsigned tx_end_delay:1;
- + unsigned force_host_mode:1;
- + unsigned force_dev_mode:1;
- + unsigned reserved31:1;
- + } b;
- +} gusbcfg_data_t;
- +
- +/**
- + * This union represents the bit fields of the Core Reset Register
- + * (GRSTCTL). Set/clear the bits using the bit fields then write the
- + * <i>d32</i> value to the register.
- + */
- +typedef union grstctl_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + /** Core Soft Reset (CSftRst) (Device and Host)
- + *
- + * The application can flush the control logic in the
- + * entire core using this bit. This bit resets the
- + * pipelines in the AHB Clock domain as well as the
- + * PHY Clock domain.
- + *
- + * The state machines are reset to an IDLE state, the
- + * control bits in the CSRs are cleared, all the
- + * transmit FIFOs and the receive FIFO are flushed.
- + *
- + * The status mask bits that control the generation of
- + * the interrupt, are cleared, to clear the
- + * interrupt. The interrupt status bits are not
- + * cleared, so the application can get the status of
- + * any events that occurred in the core after it has
- + * set this bit.
- + *
- + * Any transactions on the AHB are terminated as soon
- + * as possible following the protocol. Any
- + * transactions on the USB are terminated immediately.
- + *
- + * The configuration settings in the CSRs are
- + * unchanged, so the software doesn't have to
- + * reprogram these registers (Device
- + * Configuration/Host Configuration/Core System
- + * Configuration/Core PHY Configuration).
- + *
- + * The application can write to this bit, any time it
- + * wants to reset the core. This is a self clearing
- + * bit and the core clears this bit after all the
- + * necessary logic is reset in the core, which may
- + * take several clocks, depending on the current state
- + * of the core.
- + */
- + unsigned csftrst:1;
- + /** Hclk Soft Reset
- + *
- + * The application uses this bit to reset the control logic in
- + * the AHB clock domain. Only AHB clock domain pipelines are
- + * reset.
- + */
- + unsigned hsftrst:1;
- + /** Host Frame Counter Reset (Host Only)<br>
- + *
- + * The application can reset the (micro)frame number
- + * counter inside the core, using this bit. When the
- + * (micro)frame counter is reset, the subsequent SOF
- + * sent out by the core, will have a (micro)frame
- + * number of 0.
- + */
- + unsigned hstfrm:1;
- + /** In Token Sequence Learning Queue Flush
- + * (INTknQFlsh) (Device Only)
- + */
- + unsigned intknqflsh:1;
- + /** RxFIFO Flush (RxFFlsh) (Device and Host)
- + *
- + * The application can flush the entire Receive FIFO
- + * using this bit. The application must first
- + * ensure that the core is not in the middle of a
- + * transaction. The application should write into
- + * this bit, only after making sure that neither the
- + * DMA engine is reading from the RxFIFO nor the MAC
- + * is writing the data in to the FIFO. The
- + * application should wait until the bit is cleared
- + * before performing any other operations. This bit
- + * will takes 8 clocks (slowest of PHY or AHB clock)
- + * to clear.
- + */
- + unsigned rxfflsh:1;
- + /** TxFIFO Flush (TxFFlsh) (Device and Host).
- + *
- + * This bit is used to selectively flush a single or
- + * all transmit FIFOs. The application must first
- + * ensure that the core is not in the middle of a
- + * transaction. The application should write into
- + * this bit, only after making sure that neither the
- + * DMA engine is writing into the TxFIFO nor the MAC
- + * is reading the data out of the FIFO. The
- + * application should wait until the core clears this
- + * bit, before performing any operations. This bit
- + * will takes 8 clocks (slowest of PHY or AHB clock)
- + * to clear.
- + */
- + unsigned txfflsh:1;
- +
- + /** TxFIFO Number (TxFNum) (Device and Host).
- + *
- + * This is the FIFO number which needs to be flushed,
- + * using the TxFIFO Flush bit. This field should not
- + * be changed until the TxFIFO Flush bit is cleared by
- + * the core.
- + * - 0x0 : Non Periodic TxFIFO Flush
- + * - 0x1 : Periodic TxFIFO #1 Flush in device mode
- + * or Periodic TxFIFO in host mode
- + * - 0x2 : Periodic TxFIFO #2 Flush in device mode.
- + * - ...
- + * - 0xF : Periodic TxFIFO #15 Flush in device mode
- + * - 0x10: Flush all the Transmit NonPeriodic and
- + * Transmit Periodic FIFOs in the core
- + */
- + unsigned txfnum:5;
- + /** Reserved */
- + unsigned reserved11_29:19;
- + /** DMA Request Signal. Indicated DMA request is in
- + * probress. Used for debug purpose. */
- + unsigned dmareq:1;
- + /** AHB Master Idle. Indicates the AHB Master State
- + * Machine is in IDLE condition. */
- + unsigned ahbidle:1;
- + } b;
- +} grstctl_t;
- +
- +/**
- + * This union represents the bit fields of the Core Interrupt Mask
- + * Register (GINTMSK). Set/clear the bits using the bit fields then
- + * write the <i>d32</i> value to the register.
- + */
- +typedef union gintmsk_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + unsigned reserved0:1;
- + unsigned modemismatch:1;
- + unsigned otgintr:1;
- + unsigned sofintr:1;
- + unsigned rxstsqlvl:1;
- + unsigned nptxfempty:1;
- + unsigned ginnakeff:1;
- + unsigned goutnakeff:1;
- + unsigned ulpickint:1;
- + unsigned i2cintr:1;
- + unsigned erlysuspend:1;
- + unsigned usbsuspend:1;
- + unsigned usbreset:1;
- + unsigned enumdone:1;
- + unsigned isooutdrop:1;
- + unsigned eopframe:1;
- + unsigned restoredone:1;
- + unsigned epmismatch:1;
- + unsigned inepintr:1;
- + unsigned outepintr:1;
- + unsigned incomplisoin:1;
- + unsigned incomplisoout:1;
- + unsigned fetsusp:1;
- + unsigned resetdet:1;
- + unsigned portintr:1;
- + unsigned hcintr:1;
- + unsigned ptxfempty:1;
- + unsigned lpmtranrcvd:1;
- + unsigned conidstschng:1;
- + unsigned disconnect:1;
- + unsigned sessreqintr:1;
- + unsigned wkupintr:1;
- + } b;
- +} gintmsk_data_t;
- +/**
- + * This union represents the bit fields of the Core Interrupt Register
- + * (GINTSTS). Set/clear the bits using the bit fields then write the
- + * <i>d32</i> value to the register.
- + */
- +typedef union gintsts_data {
- + /** raw register data */
- + uint32_t d32;
- +#define DWC_SOF_INTR_MASK 0x0008
- + /** register bits */
- + struct {
- +#define DWC_HOST_MODE 1
- + unsigned curmode:1;
- + unsigned modemismatch:1;
- + unsigned otgintr:1;
- + unsigned sofintr:1;
- + unsigned rxstsqlvl:1;
- + unsigned nptxfempty:1;
- + unsigned ginnakeff:1;
- + unsigned goutnakeff:1;
- + unsigned ulpickint:1;
- + unsigned i2cintr:1;
- + unsigned erlysuspend:1;
- + unsigned usbsuspend:1;
- + unsigned usbreset:1;
- + unsigned enumdone:1;
- + unsigned isooutdrop:1;
- + unsigned eopframe:1;
- + unsigned restoredone:1;
- + unsigned epmismatch:1;
- + unsigned inepint:1;
- + unsigned outepintr:1;
- + unsigned incomplisoin:1;
- + unsigned incomplisoout:1;
- + unsigned fetsusp:1;
- + unsigned resetdet:1;
- + unsigned portintr:1;
- + unsigned hcintr:1;
- + unsigned ptxfempty:1;
- + unsigned lpmtranrcvd:1;
- + unsigned conidstschng:1;
- + unsigned disconnect:1;
- + unsigned sessreqintr:1;
- + unsigned wkupintr:1;
- + } b;
- +} gintsts_data_t;
- +
- +/**
- + * This union represents the bit fields in the Device Receive Status Read and
- + * Pop Registers (GRXSTSR, GRXSTSP) Read the register into the <i>d32</i>
- + * element then read out the bits using the <i>b</i>it elements.
- + */
- +typedef union device_grxsts_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + unsigned epnum:4;
- + unsigned bcnt:11;
- + unsigned dpid:2;
- +
- +#define DWC_STS_DATA_UPDT 0x2 // OUT Data Packet
- +#define DWC_STS_XFER_COMP 0x3 // OUT Data Transfer Complete
- +
- +#define DWC_DSTS_GOUT_NAK 0x1 // Global OUT NAK
- +#define DWC_DSTS_SETUP_COMP 0x4 // Setup Phase Complete
- +#define DWC_DSTS_SETUP_UPDT 0x6 // SETUP Packet
- + unsigned pktsts:4;
- + unsigned fn:4;
- + unsigned reserved25_31:7;
- + } b;
- +} device_grxsts_data_t;
- +
- +/**
- + * This union represents the bit fields in the Host Receive Status Read and
- + * Pop Registers (GRXSTSR, GRXSTSP) Read the register into the <i>d32</i>
- + * element then read out the bits using the <i>b</i>it elements.
- + */
- +typedef union host_grxsts_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + unsigned chnum:4;
- + unsigned bcnt:11;
- + unsigned dpid:2;
- +
- + unsigned pktsts:4;
- +#define DWC_GRXSTS_PKTSTS_IN 0x2
- +#define DWC_GRXSTS_PKTSTS_IN_XFER_COMP 0x3
- +#define DWC_GRXSTS_PKTSTS_DATA_TOGGLE_ERR 0x5
- +#define DWC_GRXSTS_PKTSTS_CH_HALTED 0x7
- +
- + unsigned reserved21_31:11;
- + } b;
- +} host_grxsts_data_t;
- +
- +/**
- + * This union represents the bit fields in the FIFO Size Registers (HPTXFSIZ,
- + * GNPTXFSIZ, DPTXFSIZn, DIEPTXFn). Read the register into the <i>d32</i> element
- + * then read out the bits using the <i>b</i>it elements.
- + */
- +typedef union fifosize_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + unsigned startaddr:16;
- + unsigned depth:16;
- + } b;
- +} fifosize_data_t;
- +
- +/**
- + * This union represents the bit fields in the Non-Periodic Transmit
- + * FIFO/Queue Status Register (GNPTXSTS). Read the register into the
- + * <i>d32</i> element then read out the bits using the <i>b</i>it
- + * elements.
- + */
- +typedef union gnptxsts_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + unsigned nptxfspcavail:16;
- + unsigned nptxqspcavail:8;
- + /** Top of the Non-Periodic Transmit Request Queue
- + * - bit 24 - Terminate (Last entry for the selected
- + * channel/EP)
- + * - bits 26:25 - Token Type
- + * - 2'b00 - IN/OUT
- + * - 2'b01 - Zero Length OUT
- + * - 2'b10 - PING/Complete Split
- + * - 2'b11 - Channel Halt
- + * - bits 30:27 - Channel/EP Number
- + */
- + unsigned nptxqtop_terminate:1;
- + unsigned nptxqtop_token:2;
- + unsigned nptxqtop_chnep:4;
- + unsigned reserved:1;
- + } b;
- +} gnptxsts_data_t;
- +
- +/**
- + * This union represents the bit fields in the Transmit
- + * FIFO Status Register (DTXFSTS). Read the register into the
- + * <i>d32</i> element then read out the bits using the <i>b</i>it
- + * elements.
- + */
- +typedef union dtxfsts_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + unsigned txfspcavail:16;
- + unsigned reserved:16;
- + } b;
- +} dtxfsts_data_t;
- +
- +/**
- + * This union represents the bit fields in the I2C Control Register
- + * (I2CCTL). Read the register into the <i>d32</i> element then read out the
- + * bits using the <i>b</i>it elements.
- + */
- +typedef union gi2cctl_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + unsigned rwdata:8;
- + unsigned regaddr:8;
- + unsigned addr:7;
- + unsigned i2cen:1;
- + unsigned ack:1;
- + unsigned i2csuspctl:1;
- + unsigned i2cdevaddr:2;
- + unsigned i2cdatse0:1;
- + unsigned reserved:1;
- + unsigned rw:1;
- + unsigned bsydne:1;
- + } b;
- +} gi2cctl_data_t;
- +
- +/**
- + * This union represents the bit fields in the PHY Vendor Control Register
- + * (GPVNDCTL). Read the register into the <i>d32</i> element then read out the
- + * bits using the <i>b</i>it elements.
- + */
- +typedef union gpvndctl_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + unsigned regdata:8;
- + unsigned vctrl:8;
- + unsigned regaddr16_21:6;
- + unsigned regwr:1;
- + unsigned reserved23_24:2;
- + unsigned newregreq:1;
- + unsigned vstsbsy:1;
- + unsigned vstsdone:1;
- + unsigned reserved28_30:3;
- + unsigned disulpidrvr:1;
- + } b;
- +} gpvndctl_data_t;
- +
- +/**
- + * This union represents the bit fields in the General Purpose
- + * Input/Output Register (GGPIO).
- + * Read the register into the <i>d32</i> element then read out the
- + * bits using the <i>b</i>it elements.
- + */
- +typedef union ggpio_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + unsigned gpi:16;
- + unsigned gpo:16;
- + } b;
- +} ggpio_data_t;
- +
- +/**
- + * This union represents the bit fields in the User ID Register
- + * (GUID). Read the register into the <i>d32</i> element then read out the
- + * bits using the <i>b</i>it elements.
- + */
- +typedef union guid_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + unsigned rwdata:32;
- + } b;
- +} guid_data_t;
- +
- +/**
- + * This union represents the bit fields in the Synopsys ID Register
- + * (GSNPSID). Read the register into the <i>d32</i> element then read out the
- + * bits using the <i>b</i>it elements.
- + */
- +typedef union gsnpsid_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + unsigned rwdata:32;
- + } b;
- +} gsnpsid_data_t;
- +
- +/**
- + * This union represents the bit fields in the User HW Config1
- + * Register. Read the register into the <i>d32</i> element then read
- + * out the bits using the <i>b</i>it elements.
- + */
- +typedef union hwcfg1_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + unsigned ep_dir0:2;
- + unsigned ep_dir1:2;
- + unsigned ep_dir2:2;
- + unsigned ep_dir3:2;
- + unsigned ep_dir4:2;
- + unsigned ep_dir5:2;
- + unsigned ep_dir6:2;
- + unsigned ep_dir7:2;
- + unsigned ep_dir8:2;
- + unsigned ep_dir9:2;
- + unsigned ep_dir10:2;
- + unsigned ep_dir11:2;
- + unsigned ep_dir12:2;
- + unsigned ep_dir13:2;
- + unsigned ep_dir14:2;
- + unsigned ep_dir15:2;
- + } b;
- +} hwcfg1_data_t;
- +
- +/**
- + * This union represents the bit fields in the User HW Config2
- + * Register. Read the register into the <i>d32</i> element then read
- + * out the bits using the <i>b</i>it elements.
- + */
- +typedef union hwcfg2_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + /* GHWCFG2 */
- + unsigned op_mode:3;
- +#define DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG 0
- +#define DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG 1
- +#define DWC_HWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE_OTG 2
- +#define DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE 3
- +#define DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE 4
- +#define DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST 5
- +#define DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST 6
- +
- + unsigned architecture:2;
- + unsigned point2point:1;
- + unsigned hs_phy_type:2;
- +#define DWC_HWCFG2_HS_PHY_TYPE_NOT_SUPPORTED 0
- +#define DWC_HWCFG2_HS_PHY_TYPE_UTMI 1
- +#define DWC_HWCFG2_HS_PHY_TYPE_ULPI 2
- +#define DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI 3
- +
- + unsigned fs_phy_type:2;
- + unsigned num_dev_ep:4;
- + unsigned num_host_chan:4;
- + unsigned perio_ep_supported:1;
- + unsigned dynamic_fifo:1;
- + unsigned multi_proc_int:1;
- + unsigned reserved21:1;
- + unsigned nonperio_tx_q_depth:2;
- + unsigned host_perio_tx_q_depth:2;
- + unsigned dev_token_q_depth:5;
- + unsigned otg_enable_ic_usb:1;
- + } b;
- +} hwcfg2_data_t;
- +
- +/**
- + * This union represents the bit fields in the User HW Config3
- + * Register. Read the register into the <i>d32</i> element then read
- + * out the bits using the <i>b</i>it elements.
- + */
- +typedef union hwcfg3_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + /* GHWCFG3 */
- + unsigned xfer_size_cntr_width:4;
- + unsigned packet_size_cntr_width:3;
- + unsigned otg_func:1;
- + unsigned i2c:1;
- + unsigned vendor_ctrl_if:1;
- + unsigned optional_features:1;
- + unsigned synch_reset_type:1;
- + unsigned adp_supp:1;
- + unsigned otg_enable_hsic:1;
- + unsigned bc_support:1;
- + unsigned otg_lpm_en:1;
- + unsigned dfifo_depth:16;
- + } b;
- +} hwcfg3_data_t;
- +
- +/**
- + * This union represents the bit fields in the User HW Config4
- + * Register. Read the register into the <i>d32</i> element then read
- + * out the bits using the <i>b</i>it elements.
- + */
- +typedef union hwcfg4_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + unsigned num_dev_perio_in_ep:4;
- + unsigned power_optimiz:1;
- + unsigned min_ahb_freq:1;
- + unsigned hiber:1;
- + unsigned xhiber:1;
- + unsigned reserved:6;
- + unsigned utmi_phy_data_width:2;
- + unsigned num_dev_mode_ctrl_ep:4;
- + unsigned iddig_filt_en:1;
- + unsigned vbus_valid_filt_en:1;
- + unsigned a_valid_filt_en:1;
- + unsigned b_valid_filt_en:1;
- + unsigned session_end_filt_en:1;
- + unsigned ded_fifo_en:1;
- + unsigned num_in_eps:4;
- + unsigned desc_dma:1;
- + unsigned desc_dma_dyn:1;
- + } b;
- +} hwcfg4_data_t;
- +
- +/**
- + * This union represents the bit fields of the Core LPM Configuration
- + * Register (GLPMCFG). Set the bits using bit fields then write
- + * the <i>d32</i> value to the register.
- + */
- +typedef union glpmctl_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + /** LPM-Capable (LPMCap) (Device and Host)
- + * The application uses this bit to control
- + * the DWC_otg core LPM capabilities.
- + */
- + unsigned lpm_cap_en:1;
- + /** LPM response programmed by application (AppL1Res) (Device)
- + * Handshake response to LPM token pre-programmed
- + * by device application software.
- + */
- + unsigned appl_resp:1;
- + /** Host Initiated Resume Duration (HIRD) (Device and Host)
- + * In Host mode this field indicates the value of HIRD
- + * to be sent in an LPM transaction.
- + * In Device mode this field is updated with the
- + * Received LPM Token HIRD bmAttribute
- + * when an ACK/NYET/STALL response is sent
- + * to an LPM transaction.
- + */
- + unsigned hird:4;
- + /** RemoteWakeEnable (bRemoteWake) (Device and Host)
- + * In Host mode this bit indicates the value of remote
- + * wake up to be sent in wIndex field of LPM transaction.
- + * In Device mode this field is updated with the
- + * Received LPM Token bRemoteWake bmAttribute
- + * when an ACK/NYET/STALL response is sent
- + * to an LPM transaction.
- + */
- + unsigned rem_wkup_en:1;
- + /** Enable utmi_sleep_n (EnblSlpM) (Device and Host)
- + * The application uses this bit to control
- + * the utmi_sleep_n assertion to the PHY when in L1 state.
- + */
- + unsigned en_utmi_sleep:1;
- + /** HIRD Threshold (HIRD_Thres) (Device and Host)
- + */
- + unsigned hird_thres:5;
- + /** LPM Response (CoreL1Res) (Device and Host)
- + * In Host mode this bit contains handsake response to
- + * LPM transaction.
- + * In Device mode the response of the core to
- + * LPM transaction received is reflected in these two bits.
- + - 0x0 : ERROR (No handshake response)
- + - 0x1 : STALL
- + - 0x2 : NYET
- + - 0x3 : ACK
- + */
- + unsigned lpm_resp:2;
- + /** Port Sleep Status (SlpSts) (Device and Host)
- + * This bit is set as long as a Sleep condition
- + * is present on the USB bus.
- + */
- + unsigned prt_sleep_sts:1;
- + /** Sleep State Resume OK (L1ResumeOK) (Device and Host)
- + * Indicates that the application or host
- + * can start resume from Sleep state.
- + */
- + unsigned sleep_state_resumeok:1;
- + /** LPM channel Index (LPM_Chnl_Indx) (Host)
- + * The channel number on which the LPM transaction
- + * has to be applied while sending
- + * an LPM transaction to the local device.
- + */
- + unsigned lpm_chan_index:4;
- + /** LPM Retry Count (LPM_Retry_Cnt) (Host)
- + * Number host retries that would be performed
- + * if the device response was not valid response.
- + */
- + unsigned retry_count:3;
- + /** Send LPM Transaction (SndLPM) (Host)
- + * When set by application software,
- + * an LPM transaction containing two tokens
- + * is sent.
- + */
- + unsigned send_lpm:1;
- + /** LPM Retry status (LPM_RetryCnt_Sts) (Host)
- + * Number of LPM Host Retries still remaining
- + * to be transmitted for the current LPM sequence
- + */
- + unsigned retry_count_sts:3;
- + unsigned reserved28_29:2;
- + /** In host mode once this bit is set, the host
- + * configures to drive the HSIC Idle state on the bus.
- + * It then waits for the device to initiate the Connect sequence.
- + * In device mode once this bit is set, the device waits for
- + * the HSIC Idle line state on the bus. Upon receving the Idle
- + * line state, it initiates the HSIC Connect sequence.
- + */
- + unsigned hsic_connect:1;
- + /** This bit overrides and functionally inverts
- + * the if_select_hsic input port signal.
- + */
- + unsigned inv_sel_hsic:1;
- + } b;
- +} glpmcfg_data_t;
- +
- +/**
- + * This union represents the bit fields of the Core ADP Timer, Control and
- + * Status Register (ADPTIMCTLSTS). Set the bits using bit fields then write
- + * the <i>d32</i> value to the register.
- + */
- +typedef union adpctl_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + /** Probe Discharge (PRB_DSCHG)
- + * These bits set the times for TADP_DSCHG.
- + * These bits are defined as follows:
- + * 2'b00 - 4 msec
- + * 2'b01 - 8 msec
- + * 2'b10 - 16 msec
- + * 2'b11 - 32 msec
- + */
- + unsigned prb_dschg:2;
- + /** Probe Delta (PRB_DELTA)
- + * These bits set the resolution for RTIM value.
- + * The bits are defined in units of 32 kHz clock cycles as follows:
- + * 2'b00 - 1 cycles
- + * 2'b01 - 2 cycles
- + * 2'b10 - 3 cycles
- + * 2'b11 - 4 cycles
- + * For example if this value is chosen to 2'b01, it means that RTIM
- + * increments for every 3(three) 32Khz clock cycles.
- + */
- + unsigned prb_delta:2;
- + /** Probe Period (PRB_PER)
- + * These bits sets the TADP_PRD as shown in Figure 4 as follows:
- + * 2'b00 - 0.625 to 0.925 sec (typical 0.775 sec)
- + * 2'b01 - 1.25 to 1.85 sec (typical 1.55 sec)
- + * 2'b10 - 1.9 to 2.6 sec (typical 2.275 sec)
- + * 2'b11 - Reserved
- + */
- + unsigned prb_per:2;
- + /** These bits capture the latest time it took for VBUS to ramp from
- + * VADP_SINK to VADP_PRB.
- + * 0x000 - 1 cycles
- + * 0x001 - 2 cycles
- + * 0x002 - 3 cycles
- + * etc
- + * 0x7FF - 2048 cycles
- + * A time of 1024 cycles at 32 kHz corresponds to a time of 32 msec.
- + */
- + unsigned rtim:11;
- + /** Enable Probe (EnaPrb)
- + * When programmed to 1'b1, the core performs a probe operation.
- + * This bit is valid only if OTG_Ver = 1'b1.
- + */
- + unsigned enaprb:1;
- + /** Enable Sense (EnaSns)
- + * When programmed to 1'b1, the core performs a Sense operation.
- + * This bit is valid only if OTG_Ver = 1'b1.
- + */
- + unsigned enasns:1;
- + /** ADP Reset (ADPRes)
- + * When set, ADP controller is reset.
- + * This bit is valid only if OTG_Ver = 1'b1.
- + */
- + unsigned adpres:1;
- + /** ADP Enable (ADPEn)
- + * When set, the core performs either ADP probing or sensing
- + * based on EnaPrb or EnaSns.
- + * This bit is valid only if OTG_Ver = 1'b1.
- + */
- + unsigned adpen:1;
- + /** ADP Probe Interrupt (ADP_PRB_INT)
- + * When this bit is set, it means that the VBUS
- + * voltage is greater than VADP_PRB or VADP_PRB is reached.
- + * This bit is valid only if OTG_Ver = 1'b1.
- + */
- + unsigned adp_prb_int:1;
- + /**
- + * ADP Sense Interrupt (ADP_SNS_INT)
- + * When this bit is set, it means that the VBUS voltage is greater than
- + * VADP_SNS value or VADP_SNS is reached.
- + * This bit is valid only if OTG_Ver = 1'b1.
- + */
- + unsigned adp_sns_int:1;
- + /** ADP Tomeout Interrupt (ADP_TMOUT_INT)
- + * This bit is relevant only for an ADP probe.
- + * When this bit is set, it means that the ramp time has
- + * completed ie ADPCTL.RTIM has reached its terminal value
- + * of 0x7FF. This is a debug feature that allows software
- + * to read the ramp time after each cycle.
- + * This bit is valid only if OTG_Ver = 1'b1.
- + */
- + unsigned adp_tmout_int:1;
- + /** ADP Probe Interrupt Mask (ADP_PRB_INT_MSK)
- + * When this bit is set, it unmasks the interrupt due to ADP_PRB_INT.
- + * This bit is valid only if OTG_Ver = 1'b1.
- + */
- + unsigned adp_prb_int_msk:1;
- + /** ADP Sense Interrupt Mask (ADP_SNS_INT_MSK)
- + * When this bit is set, it unmasks the interrupt due to ADP_SNS_INT.
- + * This bit is valid only if OTG_Ver = 1'b1.
- + */
- + unsigned adp_sns_int_msk:1;
- + /** ADP Timoeout Interrupt Mask (ADP_TMOUT_MSK)
- + * When this bit is set, it unmasks the interrupt due to ADP_TMOUT_INT.
- + * This bit is valid only if OTG_Ver = 1'b1.
- + */
- + unsigned adp_tmout_int_msk:1;
- + /** Access Request
- + * 2'b00 - Read/Write Valid (updated by the core)
- + * 2'b01 - Read
- + * 2'b00 - Write
- + * 2'b00 - Reserved
- + */
- + unsigned ar:2;
- + /** Reserved */
- + unsigned reserved29_31:3;
- + } b;
- +} adpctl_data_t;
- +
- +////////////////////////////////////////////
- +// Device Registers
- +/**
- + * Device Global Registers. <i>Offsets 800h-BFFh</i>
- + *
- + * The following structures define the size and relative field offsets
- + * for the Device Mode Registers.
- + *
- + * <i>These registers are visible only in Device mode and must not be
- + * accessed in Host mode, as the results are unknown.</i>
- + */
- +typedef struct dwc_otg_dev_global_regs {
- + /** Device Configuration Register. <i>Offset 800h</i> */
- + volatile uint32_t dcfg;
- + /** Device Control Register. <i>Offset: 804h</i> */
- + volatile uint32_t dctl;
- + /** Device Status Register (Read Only). <i>Offset: 808h</i> */
- + volatile uint32_t dsts;
- + /** Reserved. <i>Offset: 80Ch</i> */
- + uint32_t unused;
- + /** Device IN Endpoint Common Interrupt Mask
- + * Register. <i>Offset: 810h</i> */
- + volatile uint32_t diepmsk;
- + /** Device OUT Endpoint Common Interrupt Mask
- + * Register. <i>Offset: 814h</i> */
- + volatile uint32_t doepmsk;
- + /** Device All Endpoints Interrupt Register. <i>Offset: 818h</i> */
- + volatile uint32_t daint;
- + /** Device All Endpoints Interrupt Mask Register. <i>Offset:
- + * 81Ch</i> */
- + volatile uint32_t daintmsk;
- + /** Device IN Token Queue Read Register-1 (Read Only).
- + * <i>Offset: 820h</i> */
- + volatile uint32_t dtknqr1;
- + /** Device IN Token Queue Read Register-2 (Read Only).
- + * <i>Offset: 824h</i> */
- + volatile uint32_t dtknqr2;
- + /** Device VBUS discharge Register. <i>Offset: 828h</i> */
- + volatile uint32_t dvbusdis;
- + /** Device VBUS Pulse Register. <i>Offset: 82Ch</i> */
- + volatile uint32_t dvbuspulse;
- + /** Device IN Token Queue Read Register-3 (Read Only). /
- + * Device Thresholding control register (Read/Write)
- + * <i>Offset: 830h</i> */
- + volatile uint32_t dtknqr3_dthrctl;
- + /** Device IN Token Queue Read Register-4 (Read Only). /
- + * Device IN EPs empty Inr. Mask Register (Read/Write)
- + * <i>Offset: 834h</i> */
- + volatile uint32_t dtknqr4_fifoemptymsk;
- + /** Device Each Endpoint Interrupt Register (Read Only). /
- + * <i>Offset: 838h</i> */
- + volatile uint32_t deachint;
- + /** Device Each Endpoint Interrupt mask Register (Read/Write). /
- + * <i>Offset: 83Ch</i> */
- + volatile uint32_t deachintmsk;
- + /** Device Each In Endpoint Interrupt mask Register (Read/Write). /
- + * <i>Offset: 840h</i> */
- + volatile uint32_t diepeachintmsk[MAX_EPS_CHANNELS];
- + /** Device Each Out Endpoint Interrupt mask Register (Read/Write). /
- + * <i>Offset: 880h</i> */
- + volatile uint32_t doepeachintmsk[MAX_EPS_CHANNELS];
- +} dwc_otg_device_global_regs_t;
- +
- +/**
- + * This union represents the bit fields in the Device Configuration
- + * Register. Read the register into the <i>d32</i> member then
- + * set/clear the bits using the <i>b</i>it elements. Write the
- + * <i>d32</i> member to the dcfg register.
- + */
- +typedef union dcfg_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + /** Device Speed */
- + unsigned devspd:2;
- + /** Non Zero Length Status OUT Handshake */
- + unsigned nzstsouthshk:1;
- +#define DWC_DCFG_SEND_STALL 1
- +
- + unsigned ena32khzs:1;
- + /** Device Addresses */
- + unsigned devaddr:7;
- + /** Periodic Frame Interval */
- + unsigned perfrint:2;
- +#define DWC_DCFG_FRAME_INTERVAL_80 0
- +#define DWC_DCFG_FRAME_INTERVAL_85 1
- +#define DWC_DCFG_FRAME_INTERVAL_90 2
- +#define DWC_DCFG_FRAME_INTERVAL_95 3
- +
- + /** Enable Device OUT NAK for bulk in DDMA mode */
- + unsigned endevoutnak:1;
- +
- + unsigned reserved14_17:4;
- + /** In Endpoint Mis-match count */
- + unsigned epmscnt:5;
- + /** Enable Descriptor DMA in Device mode */
- + unsigned descdma:1;
- + unsigned perschintvl:2;
- + unsigned resvalid:6;
- + } b;
- +} dcfg_data_t;
- +
- +/**
- + * This union represents the bit fields in the Device Control
- + * Register. Read the register into the <i>d32</i> member then
- + * set/clear the bits using the <i>b</i>it elements.
- + */
- +typedef union dctl_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + /** Remote Wakeup */
- + unsigned rmtwkupsig:1;
- + /** Soft Disconnect */
- + unsigned sftdiscon:1;
- + /** Global Non-Periodic IN NAK Status */
- + unsigned gnpinnaksts:1;
- + /** Global OUT NAK Status */
- + unsigned goutnaksts:1;
- + /** Test Control */
- + unsigned tstctl:3;
- + /** Set Global Non-Periodic IN NAK */
- + unsigned sgnpinnak:1;
- + /** Clear Global Non-Periodic IN NAK */
- + unsigned cgnpinnak:1;
- + /** Set Global OUT NAK */
- + unsigned sgoutnak:1;
- + /** Clear Global OUT NAK */
- + unsigned cgoutnak:1;
- + /** Power-On Programming Done */
- + unsigned pwronprgdone:1;
- + /** Reserved */
- + unsigned reserved:1;
- + /** Global Multi Count */
- + unsigned gmc:2;
- + /** Ignore Frame Number for ISOC EPs */
- + unsigned ifrmnum:1;
- + /** NAK on Babble */
- + unsigned nakonbble:1;
- + /** Enable Continue on BNA */
- + unsigned encontonbna:1;
- +
- + unsigned reserved18_31:14;
- + } b;
- +} dctl_data_t;
- +
- +/**
- + * This union represents the bit fields in the Device Status
- + * Register. Read the register into the <i>d32</i> member then
- + * set/clear the bits using the <i>b</i>it elements.
- + */
- +typedef union dsts_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + /** Suspend Status */
- + unsigned suspsts:1;
- + /** Enumerated Speed */
- + unsigned enumspd:2;
- +#define DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ 0
- +#define DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ 1
- +#define DWC_DSTS_ENUMSPD_LS_PHY_6MHZ 2
- +#define DWC_DSTS_ENUMSPD_FS_PHY_48MHZ 3
- + /** Erratic Error */
- + unsigned errticerr:1;
- + unsigned reserved4_7:4;
- + /** Frame or Microframe Number of the received SOF */
- + unsigned soffn:14;
- + unsigned reserved22_31:10;
- + } b;
- +} dsts_data_t;
- +
- +/**
- + * This union represents the bit fields in the Device IN EP Interrupt
- + * Register and the Device IN EP Common Mask Register.
- + *
- + * - Read the register into the <i>d32</i> member then set/clear the
- + * bits using the <i>b</i>it elements.
- + */
- +typedef union diepint_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + /** Transfer complete mask */
- + unsigned xfercompl:1;
- + /** Endpoint disable mask */
- + unsigned epdisabled:1;
- + /** AHB Error mask */
- + unsigned ahberr:1;
- + /** TimeOUT Handshake mask (non-ISOC EPs) */
- + unsigned timeout:1;
- + /** IN Token received with TxF Empty mask */
- + unsigned intktxfemp:1;
- + /** IN Token Received with EP mismatch mask */
- + unsigned intknepmis:1;
- + /** IN Endpoint NAK Effective mask */
- + unsigned inepnakeff:1;
- + /** Reserved */
- + unsigned emptyintr:1;
- +
- + unsigned txfifoundrn:1;
- +
- + /** BNA Interrupt mask */
- + unsigned bna:1;
- +
- + unsigned reserved10_12:3;
- + /** BNA Interrupt mask */
- + unsigned nak:1;
- +
- + unsigned reserved14_31:18;
- + } b;
- +} diepint_data_t;
- +
- +/**
- + * This union represents the bit fields in the Device IN EP
- + * Common/Dedicated Interrupt Mask Register.
- + */
- +typedef union diepint_data diepmsk_data_t;
- +
- +/**
- + * This union represents the bit fields in the Device OUT EP Interrupt
- + * Registerand Device OUT EP Common Interrupt Mask Register.
- + *
- + * - Read the register into the <i>d32</i> member then set/clear the
- + * bits using the <i>b</i>it elements.
- + */
- +typedef union doepint_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + /** Transfer complete */
- + unsigned xfercompl:1;
- + /** Endpoint disable */
- + unsigned epdisabled:1;
- + /** AHB Error */
- + unsigned ahberr:1;
- + /** Setup Phase Done (contorl EPs) */
- + unsigned setup:1;
- + /** OUT Token Received when Endpoint Disabled */
- + unsigned outtknepdis:1;
- +
- + unsigned stsphsercvd:1;
- + /** Back-to-Back SETUP Packets Received */
- + unsigned back2backsetup:1;
- +
- + unsigned reserved7:1;
- + /** OUT packet Error */
- + unsigned outpkterr:1;
- + /** BNA Interrupt */
- + unsigned bna:1;
- +
- + unsigned reserved10:1;
- + /** Packet Drop Status */
- + unsigned pktdrpsts:1;
- + /** Babble Interrupt */
- + unsigned babble:1;
- + /** NAK Interrupt */
- + unsigned nak:1;
- + /** NYET Interrupt */
- + unsigned nyet:1;
- + /** Bit indicating setup packet received */
- + unsigned sr:1;
- +
- + unsigned reserved16_31:16;
- + } b;
- +} doepint_data_t;
- +
- +/**
- + * This union represents the bit fields in the Device OUT EP
- + * Common/Dedicated Interrupt Mask Register.
- + */
- +typedef union doepint_data doepmsk_data_t;
- +
- +/**
- + * This union represents the bit fields in the Device All EP Interrupt
- + * and Mask Registers.
- + * - Read the register into the <i>d32</i> member then set/clear the
- + * bits using the <i>b</i>it elements.
- + */
- +typedef union daint_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + /** IN Endpoint bits */
- + unsigned in:16;
- + /** OUT Endpoint bits */
- + unsigned out:16;
- + } ep;
- + struct {
- + /** IN Endpoint bits */
- + unsigned inep0:1;
- + unsigned inep1:1;
- + unsigned inep2:1;
- + unsigned inep3:1;
- + unsigned inep4:1;
- + unsigned inep5:1;
- + unsigned inep6:1;
- + unsigned inep7:1;
- + unsigned inep8:1;
- + unsigned inep9:1;
- + unsigned inep10:1;
- + unsigned inep11:1;
- + unsigned inep12:1;
- + unsigned inep13:1;
- + unsigned inep14:1;
- + unsigned inep15:1;
- + /** OUT Endpoint bits */
- + unsigned outep0:1;
- + unsigned outep1:1;
- + unsigned outep2:1;
- + unsigned outep3:1;
- + unsigned outep4:1;
- + unsigned outep5:1;
- + unsigned outep6:1;
- + unsigned outep7:1;
- + unsigned outep8:1;
- + unsigned outep9:1;
- + unsigned outep10:1;
- + unsigned outep11:1;
- + unsigned outep12:1;
- + unsigned outep13:1;
- + unsigned outep14:1;
- + unsigned outep15:1;
- + } b;
- +} daint_data_t;
- +
- +/**
- + * This union represents the bit fields in the Device IN Token Queue
- + * Read Registers.
- + * - Read the register into the <i>d32</i> member.
- + * - READ-ONLY Register
- + */
- +typedef union dtknq1_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + /** In Token Queue Write Pointer */
- + unsigned intknwptr:5;
- + /** Reserved */
- + unsigned reserved05_06:2;
- + /** write pointer has wrapped. */
- + unsigned wrap_bit:1;
- + /** EP Numbers of IN Tokens 0 ... 4 */
- + unsigned epnums0_5:24;
- + } b;
- +} dtknq1_data_t;
- +
- +/**
- + * This union represents Threshold control Register
- + * - Read and write the register into the <i>d32</i> member.
- + * - READ-WRITABLE Register
- + */
- +typedef union dthrctl_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + /** non ISO Tx Thr. Enable */
- + unsigned non_iso_thr_en:1;
- + /** ISO Tx Thr. Enable */
- + unsigned iso_thr_en:1;
- + /** Tx Thr. Length */
- + unsigned tx_thr_len:9;
- + /** AHB Threshold ratio */
- + unsigned ahb_thr_ratio:2;
- + /** Reserved */
- + unsigned reserved13_15:3;
- + /** Rx Thr. Enable */
- + unsigned rx_thr_en:1;
- + /** Rx Thr. Length */
- + unsigned rx_thr_len:9;
- + unsigned reserved26:1;
- + /** Arbiter Parking Enable*/
- + unsigned arbprken:1;
- + /** Reserved */
- + unsigned reserved28_31:4;
- + } b;
- +} dthrctl_data_t;
- +
- +/**
- + * Device Logical IN Endpoint-Specific Registers. <i>Offsets
- + * 900h-AFCh</i>
- + *
- + * There will be one set of endpoint registers per logical endpoint
- + * implemented.
- + *
- + * <i>These registers are visible only in Device mode and must not be
- + * accessed in Host mode, as the results are unknown.</i>
- + */
- +typedef struct dwc_otg_dev_in_ep_regs {
- + /** Device IN Endpoint Control Register. <i>Offset:900h +
- + * (ep_num * 20h) + 00h</i> */
- + volatile uint32_t diepctl;
- + /** Reserved. <i>Offset:900h + (ep_num * 20h) + 04h</i> */
- + uint32_t reserved04;
- + /** Device IN Endpoint Interrupt Register. <i>Offset:900h +
- + * (ep_num * 20h) + 08h</i> */
- + volatile uint32_t diepint;
- + /** Reserved. <i>Offset:900h + (ep_num * 20h) + 0Ch</i> */
- + uint32_t reserved0C;
- + /** Device IN Endpoint Transfer Size
- + * Register. <i>Offset:900h + (ep_num * 20h) + 10h</i> */
- + volatile uint32_t dieptsiz;
- + /** Device IN Endpoint DMA Address Register. <i>Offset:900h +
- + * (ep_num * 20h) + 14h</i> */
- + volatile uint32_t diepdma;
- + /** Device IN Endpoint Transmit FIFO Status Register. <i>Offset:900h +
- + * (ep_num * 20h) + 18h</i> */
- + volatile uint32_t dtxfsts;
- + /** Device IN Endpoint DMA Buffer Register. <i>Offset:900h +
- + * (ep_num * 20h) + 1Ch</i> */
- + volatile uint32_t diepdmab;
- +} dwc_otg_dev_in_ep_regs_t;
- +
- +/**
- + * Device Logical OUT Endpoint-Specific Registers. <i>Offsets:
- + * B00h-CFCh</i>
- + *
- + * There will be one set of endpoint registers per logical endpoint
- + * implemented.
- + *
- + * <i>These registers are visible only in Device mode and must not be
- + * accessed in Host mode, as the results are unknown.</i>
- + */
- +typedef struct dwc_otg_dev_out_ep_regs {
- + /** Device OUT Endpoint Control Register. <i>Offset:B00h +
- + * (ep_num * 20h) + 00h</i> */
- + volatile uint32_t doepctl;
- + /** Reserved. <i>Offset:B00h + (ep_num * 20h) + 04h</i> */
- + uint32_t reserved04;
- + /** Device OUT Endpoint Interrupt Register. <i>Offset:B00h +
- + * (ep_num * 20h) + 08h</i> */
- + volatile uint32_t doepint;
- + /** Reserved. <i>Offset:B00h + (ep_num * 20h) + 0Ch</i> */
- + uint32_t reserved0C;
- + /** Device OUT Endpoint Transfer Size Register. <i>Offset:
- + * B00h + (ep_num * 20h) + 10h</i> */
- + volatile uint32_t doeptsiz;
- + /** Device OUT Endpoint DMA Address Register. <i>Offset:B00h
- + * + (ep_num * 20h) + 14h</i> */
- + volatile uint32_t doepdma;
- + /** Reserved. <i>Offset:B00h + * (ep_num * 20h) + 18h</i> */
- + uint32_t unused;
- + /** Device OUT Endpoint DMA Buffer Register. <i>Offset:B00h
- + * + (ep_num * 20h) + 1Ch</i> */
- + uint32_t doepdmab;
- +} dwc_otg_dev_out_ep_regs_t;
- +
- +/**
- + * This union represents the bit fields in the Device EP Control
- + * Register. Read the register into the <i>d32</i> member then
- + * set/clear the bits using the <i>b</i>it elements.
- + */
- +typedef union depctl_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + /** Maximum Packet Size
- + * IN/OUT EPn
- + * IN/OUT EP0 - 2 bits
- + * 2'b00: 64 Bytes
- + * 2'b01: 32
- + * 2'b10: 16
- + * 2'b11: 8 */
- + unsigned mps:11;
- +#define DWC_DEP0CTL_MPS_64 0
- +#define DWC_DEP0CTL_MPS_32 1
- +#define DWC_DEP0CTL_MPS_16 2
- +#define DWC_DEP0CTL_MPS_8 3
- +
- + /** Next Endpoint
- + * IN EPn/IN EP0
- + * OUT EPn/OUT EP0 - reserved */
- + unsigned nextep:4;
- +
- + /** USB Active Endpoint */
- + unsigned usbactep:1;
- +
- + /** Endpoint DPID (INTR/Bulk IN and OUT endpoints)
- + * This field contains the PID of the packet going to
- + * be received or transmitted on this endpoint. The
- + * application should program the PID of the first
- + * packet going to be received or transmitted on this
- + * endpoint , after the endpoint is
- + * activated. Application use the SetD1PID and
- + * SetD0PID fields of this register to program either
- + * D0 or D1 PID.
- + *
- + * The encoding for this field is
- + * - 0: D0
- + * - 1: D1
- + */
- + unsigned dpid:1;
- +
- + /** NAK Status */
- + unsigned naksts:1;
- +
- + /** Endpoint Type
- + * 2'b00: Control
- + * 2'b01: Isochronous
- + * 2'b10: Bulk
- + * 2'b11: Interrupt */
- + unsigned eptype:2;
- +
- + /** Snoop Mode
- + * OUT EPn/OUT EP0
- + * IN EPn/IN EP0 - reserved */
- + unsigned snp:1;
- +
- + /** Stall Handshake */
- + unsigned stall:1;
- +
- + /** Tx Fifo Number
- + * IN EPn/IN EP0
- + * OUT EPn/OUT EP0 - reserved */
- + unsigned txfnum:4;
- +
- + /** Clear NAK */
- + unsigned cnak:1;
- + /** Set NAK */
- + unsigned snak:1;
- + /** Set DATA0 PID (INTR/Bulk IN and OUT endpoints)
- + * Writing to this field sets the Endpoint DPID (DPID)
- + * field in this register to DATA0. Set Even
- + * (micro)frame (SetEvenFr) (ISO IN and OUT Endpoints)
- + * Writing to this field sets the Even/Odd
- + * (micro)frame (EO_FrNum) field to even (micro)
- + * frame.
- + */
- + unsigned setd0pid:1;
- + /** Set DATA1 PID (INTR/Bulk IN and OUT endpoints)
- + * Writing to this field sets the Endpoint DPID (DPID)
- + * field in this register to DATA1 Set Odd
- + * (micro)frame (SetOddFr) (ISO IN and OUT Endpoints)
- + * Writing to this field sets the Even/Odd
- + * (micro)frame (EO_FrNum) field to odd (micro) frame.
- + */
- + unsigned setd1pid:1;
- +
- + /** Endpoint Disable */
- + unsigned epdis:1;
- + /** Endpoint Enable */
- + unsigned epena:1;
- + } b;
- +} depctl_data_t;
- +
- +/**
- + * This union represents the bit fields in the Device EP Transfer
- + * Size Register. Read the register into the <i>d32</i> member then
- + * set/clear the bits using the <i>b</i>it elements.
- + */
- +typedef union deptsiz_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + /** Transfer size */
- + unsigned xfersize:19;
- +/** Max packet count for EP (pow(2,10)-1) */
- +#define MAX_PKT_CNT 1023
- + /** Packet Count */
- + unsigned pktcnt:10;
- + /** Multi Count - Periodic IN endpoints */
- + unsigned mc:2;
- + unsigned reserved:1;
- + } b;
- +} deptsiz_data_t;
- +
- +/**
- + * This union represents the bit fields in the Device EP 0 Transfer
- + * Size Register. Read the register into the <i>d32</i> member then
- + * set/clear the bits using the <i>b</i>it elements.
- + */
- +typedef union deptsiz0_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + /** Transfer size */
- + unsigned xfersize:7;
- + /** Reserved */
- + unsigned reserved7_18:12;
- + /** Packet Count */
- + unsigned pktcnt:2;
- + /** Reserved */
- + unsigned reserved21_28:8;
- + /**Setup Packet Count (DOEPTSIZ0 Only) */
- + unsigned supcnt:2;
- + unsigned reserved31;
- + } b;
- +} deptsiz0_data_t;
- +
- +/////////////////////////////////////////////////
- +// DMA Descriptor Specific Structures
- +//
- +
- +/** Buffer status definitions */
- +
- +#define BS_HOST_READY 0x0
- +#define BS_DMA_BUSY 0x1
- +#define BS_DMA_DONE 0x2
- +#define BS_HOST_BUSY 0x3
- +
- +/** Receive/Transmit status definitions */
- +
- +#define RTS_SUCCESS 0x0
- +#define RTS_BUFFLUSH 0x1
- +#define RTS_RESERVED 0x2
- +#define RTS_BUFERR 0x3
- +
- +/**
- + * This union represents the bit fields in the DMA Descriptor
- + * status quadlet. Read the quadlet into the <i>d32</i> member then
- + * set/clear the bits using the <i>b</i>it, <i>b_iso_out</i> and
- + * <i>b_iso_in</i> elements.
- + */
- +typedef union dev_dma_desc_sts {
- + /** raw register data */
- + uint32_t d32;
- + /** quadlet bits */
- + struct {
- + /** Received number of bytes */
- + unsigned bytes:16;
- + /** NAK bit - only for OUT EPs */
- + unsigned nak:1;
- + unsigned reserved17_22:6;
- + /** Multiple Transfer - only for OUT EPs */
- + unsigned mtrf:1;
- + /** Setup Packet received - only for OUT EPs */
- + unsigned sr:1;
- + /** Interrupt On Complete */
- + unsigned ioc:1;
- + /** Short Packet */
- + unsigned sp:1;
- + /** Last */
- + unsigned l:1;
- + /** Receive Status */
- + unsigned sts:2;
- + /** Buffer Status */
- + unsigned bs:2;
- + } b;
- +
- +//#ifdef DWC_EN_ISOC
- + /** iso out quadlet bits */
- + struct {
- + /** Received number of bytes */
- + unsigned rxbytes:11;
- +
- + unsigned reserved11:1;
- + /** Frame Number */
- + unsigned framenum:11;
- + /** Received ISO Data PID */
- + unsigned pid:2;
- + /** Interrupt On Complete */
- + unsigned ioc:1;
- + /** Short Packet */
- + unsigned sp:1;
- + /** Last */
- + unsigned l:1;
- + /** Receive Status */
- + unsigned rxsts:2;
- + /** Buffer Status */
- + unsigned bs:2;
- + } b_iso_out;
- +
- + /** iso in quadlet bits */
- + struct {
- + /** Transmited number of bytes */
- + unsigned txbytes:12;
- + /** Frame Number */
- + unsigned framenum:11;
- + /** Transmited ISO Data PID */
- + unsigned pid:2;
- + /** Interrupt On Complete */
- + unsigned ioc:1;
- + /** Short Packet */
- + unsigned sp:1;
- + /** Last */
- + unsigned l:1;
- + /** Transmit Status */
- + unsigned txsts:2;
- + /** Buffer Status */
- + unsigned bs:2;
- + } b_iso_in;
- +//#endif /* DWC_EN_ISOC */
- +} dev_dma_desc_sts_t;
- +
- +/**
- + * DMA Descriptor structure
- + *
- + * DMA Descriptor structure contains two quadlets:
- + * Status quadlet and Data buffer pointer.
- + */
- +typedef struct dwc_otg_dev_dma_desc {
- + /** DMA Descriptor status quadlet */
- + dev_dma_desc_sts_t status;
- + /** DMA Descriptor data buffer pointer */
- + uint32_t buf;
- +} dwc_otg_dev_dma_desc_t;
- +
- +/**
- + * The dwc_otg_dev_if structure contains information needed to manage
- + * the DWC_otg controller acting in device mode. It represents the
- + * programming view of the device-specific aspects of the controller.
- + */
- +typedef struct dwc_otg_dev_if {
- + /** Pointer to device Global registers.
- + * Device Global Registers starting at offset 800h
- + */
- + dwc_otg_device_global_regs_t *dev_global_regs;
- +#define DWC_DEV_GLOBAL_REG_OFFSET 0x800
- +
- + /**
- + * Device Logical IN Endpoint-Specific Registers 900h-AFCh
- + */
- + dwc_otg_dev_in_ep_regs_t *in_ep_regs[MAX_EPS_CHANNELS];
- +#define DWC_DEV_IN_EP_REG_OFFSET 0x900
- +#define DWC_EP_REG_OFFSET 0x20
- +
- + /** Device Logical OUT Endpoint-Specific Registers B00h-CFCh */
- + dwc_otg_dev_out_ep_regs_t *out_ep_regs[MAX_EPS_CHANNELS];
- +#define DWC_DEV_OUT_EP_REG_OFFSET 0xB00
- +
- + /* Device configuration information */
- + uint8_t speed; /**< Device Speed 0: Unknown, 1: LS, 2:FS, 3: HS */
- + uint8_t num_in_eps; /**< Number # of Tx EP range: 0-15 exept ep0 */
- + uint8_t num_out_eps; /**< Number # of Rx EP range: 0-15 exept ep 0*/
- +
- + /** Size of periodic FIFOs (Bytes) */
- + uint16_t perio_tx_fifo_size[MAX_PERIO_FIFOS];
- +
- + /** Size of Tx FIFOs (Bytes) */
- + uint16_t tx_fifo_size[MAX_TX_FIFOS];
- +
- + /** Thresholding enable flags and length varaiables **/
- + uint16_t rx_thr_en;
- + uint16_t iso_tx_thr_en;
- + uint16_t non_iso_tx_thr_en;
- +
- + uint16_t rx_thr_length;
- + uint16_t tx_thr_length;
- +
- + /**
- + * Pointers to the DMA Descriptors for EP0 Control
- + * transfers (virtual and physical)
- + */
- +
- + /** 2 descriptors for SETUP packets */
- + dwc_dma_t dma_setup_desc_addr[2];
- + dwc_otg_dev_dma_desc_t *setup_desc_addr[2];
- +
- + /** Pointer to Descriptor with latest SETUP packet */
- + dwc_otg_dev_dma_desc_t *psetup;
- +
- + /** Index of current SETUP handler descriptor */
- + uint32_t setup_desc_index;
- +
- + /** Descriptor for Data In or Status In phases */
- + dwc_dma_t dma_in_desc_addr;
- + dwc_otg_dev_dma_desc_t *in_desc_addr;
- +
- + /** Descriptor for Data Out or Status Out phases */
- + dwc_dma_t dma_out_desc_addr;
- + dwc_otg_dev_dma_desc_t *out_desc_addr;
- +
- + /** Setup Packet Detected - if set clear NAK when queueing */
- + uint32_t spd;
- + /** Isoc ep pointer on which incomplete happens */
- + void *isoc_ep;
- +
- +} dwc_otg_dev_if_t;
- +
- +/////////////////////////////////////////////////
- +// Host Mode Register Structures
- +//
- +/**
- + * The Host Global Registers structure defines the size and relative
- + * field offsets for the Host Mode Global Registers. Host Global
- + * Registers offsets 400h-7FFh.
- +*/
- +typedef struct dwc_otg_host_global_regs {
- + /** Host Configuration Register. <i>Offset: 400h</i> */
- + volatile uint32_t hcfg;
- + /** Host Frame Interval Register. <i>Offset: 404h</i> */
- + volatile uint32_t hfir;
- + /** Host Frame Number / Frame Remaining Register. <i>Offset: 408h</i> */
- + volatile uint32_t hfnum;
- + /** Reserved. <i>Offset: 40Ch</i> */
- + uint32_t reserved40C;
- + /** Host Periodic Transmit FIFO/ Queue Status Register. <i>Offset: 410h</i> */
- + volatile uint32_t hptxsts;
- + /** Host All Channels Interrupt Register. <i>Offset: 414h</i> */
- + volatile uint32_t haint;
- + /** Host All Channels Interrupt Mask Register. <i>Offset: 418h</i> */
- + volatile uint32_t haintmsk;
- + /** Host Frame List Base Address Register . <i>Offset: 41Ch</i> */
- + volatile uint32_t hflbaddr;
- +} dwc_otg_host_global_regs_t;
- +
- +/**
- + * This union represents the bit fields in the Host Configuration Register.
- + * Read the register into the <i>d32</i> member then set/clear the bits using
- + * the <i>b</i>it elements. Write the <i>d32</i> member to the hcfg register.
- + */
- +typedef union hcfg_data {
- + /** raw register data */
- + uint32_t d32;
- +
- + /** register bits */
- + struct {
- + /** FS/LS Phy Clock Select */
- + unsigned fslspclksel:2;
- +#define DWC_HCFG_30_60_MHZ 0
- +#define DWC_HCFG_48_MHZ 1
- +#define DWC_HCFG_6_MHZ 2
- +
- + /** FS/LS Only Support */
- + unsigned fslssupp:1;
- + unsigned reserved3_6:4;
- + /** Enable 32-KHz Suspend Mode */
- + unsigned ena32khzs:1;
- + /** Resume Validation Periiod */
- + unsigned resvalid:8;
- + unsigned reserved16_22:7;
- + /** Enable Scatter/gather DMA in Host mode */
- + unsigned descdma:1;
- + /** Frame List Entries */
- + unsigned frlisten:2;
- + /** Enable Periodic Scheduling */
- + unsigned perschedena:1;
- + unsigned reserved27_30:4;
- + unsigned modechtimen:1;
- + } b;
- +} hcfg_data_t;
- +
- +/**
- + * This union represents the bit fields in the Host Frame Remaing/Number
- + * Register.
- + */
- +typedef union hfir_data {
- + /** raw register data */
- + uint32_t d32;
- +
- + /** register bits */
- + struct {
- + unsigned frint:16;
- + unsigned hfirrldctrl:1;
- + unsigned reserved:15;
- + } b;
- +} hfir_data_t;
- +
- +/**
- + * This union represents the bit fields in the Host Frame Remaing/Number
- + * Register.
- + */
- +typedef union hfnum_data {
- + /** raw register data */
- + uint32_t d32;
- +
- + /** register bits */
- + struct {
- + unsigned frnum:16;
- +#define DWC_HFNUM_MAX_FRNUM 0x3FFF
- + unsigned frrem:16;
- + } b;
- +} hfnum_data_t;
- +
- +typedef union hptxsts_data {
- + /** raw register data */
- + uint32_t d32;
- +
- + /** register bits */
- + struct {
- + unsigned ptxfspcavail:16;
- + unsigned ptxqspcavail:8;
- + /** Top of the Periodic Transmit Request Queue
- + * - bit 24 - Terminate (last entry for the selected channel)
- + * - bits 26:25 - Token Type
- + * - 2'b00 - Zero length
- + * - 2'b01 - Ping
- + * - 2'b10 - Disable
- + * - bits 30:27 - Channel Number
- + * - bit 31 - Odd/even microframe
- + */
- + unsigned ptxqtop_terminate:1;
- + unsigned ptxqtop_token:2;
- + unsigned ptxqtop_chnum:4;
- + unsigned ptxqtop_odd:1;
- + } b;
- +} hptxsts_data_t;
- +
- +/**
- + * This union represents the bit fields in the Host Port Control and Status
- + * Register. Read the register into the <i>d32</i> member then set/clear the
- + * bits using the <i>b</i>it elements. Write the <i>d32</i> member to the
- + * hprt0 register.
- + */
- +typedef union hprt0_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + unsigned prtconnsts:1;
- + unsigned prtconndet:1;
- + unsigned prtena:1;
- + unsigned prtenchng:1;
- + unsigned prtovrcurract:1;
- + unsigned prtovrcurrchng:1;
- + unsigned prtres:1;
- + unsigned prtsusp:1;
- + unsigned prtrst:1;
- + unsigned reserved9:1;
- + unsigned prtlnsts:2;
- + unsigned prtpwr:1;
- + unsigned prttstctl:4;
- + unsigned prtspd:2;
- +#define DWC_HPRT0_PRTSPD_HIGH_SPEED 0
- +#define DWC_HPRT0_PRTSPD_FULL_SPEED 1
- +#define DWC_HPRT0_PRTSPD_LOW_SPEED 2
- + unsigned reserved19_31:13;
- + } b;
- +} hprt0_data_t;
- +
- +/**
- + * This union represents the bit fields in the Host All Interrupt
- + * Register.
- + */
- +typedef union haint_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + unsigned ch0:1;
- + unsigned ch1:1;
- + unsigned ch2:1;
- + unsigned ch3:1;
- + unsigned ch4:1;
- + unsigned ch5:1;
- + unsigned ch6:1;
- + unsigned ch7:1;
- + unsigned ch8:1;
- + unsigned ch9:1;
- + unsigned ch10:1;
- + unsigned ch11:1;
- + unsigned ch12:1;
- + unsigned ch13:1;
- + unsigned ch14:1;
- + unsigned ch15:1;
- + unsigned reserved:16;
- + } b;
- +
- + struct {
- + unsigned chint:16;
- + unsigned reserved:16;
- + } b2;
- +} haint_data_t;
- +
- +/**
- + * This union represents the bit fields in the Host All Interrupt
- + * Register.
- + */
- +typedef union haintmsk_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + unsigned ch0:1;
- + unsigned ch1:1;
- + unsigned ch2:1;
- + unsigned ch3:1;
- + unsigned ch4:1;
- + unsigned ch5:1;
- + unsigned ch6:1;
- + unsigned ch7:1;
- + unsigned ch8:1;
- + unsigned ch9:1;
- + unsigned ch10:1;
- + unsigned ch11:1;
- + unsigned ch12:1;
- + unsigned ch13:1;
- + unsigned ch14:1;
- + unsigned ch15:1;
- + unsigned reserved:16;
- + } b;
- +
- + struct {
- + unsigned chint:16;
- + unsigned reserved:16;
- + } b2;
- +} haintmsk_data_t;
- +
- +/**
- + * Host Channel Specific Registers. <i>500h-5FCh</i>
- + */
- +typedef struct dwc_otg_hc_regs {
- + /** Host Channel 0 Characteristic Register. <i>Offset: 500h + (chan_num * 20h) + 00h</i> */
- + volatile uint32_t hcchar;
- + /** Host Channel 0 Split Control Register. <i>Offset: 500h + (chan_num * 20h) + 04h</i> */
- + volatile uint32_t hcsplt;
- + /** Host Channel 0 Interrupt Register. <i>Offset: 500h + (chan_num * 20h) + 08h</i> */
- + volatile uint32_t hcint;
- + /** Host Channel 0 Interrupt Mask Register. <i>Offset: 500h + (chan_num * 20h) + 0Ch</i> */
- + volatile uint32_t hcintmsk;
- + /** Host Channel 0 Transfer Size Register. <i>Offset: 500h + (chan_num * 20h) + 10h</i> */
- + volatile uint32_t hctsiz;
- + /** Host Channel 0 DMA Address Register. <i>Offset: 500h + (chan_num * 20h) + 14h</i> */
- + volatile uint32_t hcdma;
- + volatile uint32_t reserved;
- + /** Host Channel 0 DMA Buffer Address Register. <i>Offset: 500h + (chan_num * 20h) + 1Ch</i> */
- + volatile uint32_t hcdmab;
- +} dwc_otg_hc_regs_t;
- +
- +/**
- + * This union represents the bit fields in the Host Channel Characteristics
- + * Register. Read the register into the <i>d32</i> member then set/clear the
- + * bits using the <i>b</i>it elements. Write the <i>d32</i> member to the
- + * hcchar register.
- + */
- +typedef union hcchar_data {
- + /** raw register data */
- + uint32_t d32;
- +
- + /** register bits */
- + struct {
- + /** Maximum packet size in bytes */
- + unsigned mps:11;
- +
- + /** Endpoint number */
- + unsigned epnum:4;
- +
- + /** 0: OUT, 1: IN */
- + unsigned epdir:1;
- +
- + unsigned reserved:1;
- +
- + /** 0: Full/high speed device, 1: Low speed device */
- + unsigned lspddev:1;
- +
- + /** 0: Control, 1: Isoc, 2: Bulk, 3: Intr */
- + unsigned eptype:2;
- +
- + /** Packets per frame for periodic transfers. 0 is reserved. */
- + unsigned multicnt:2;
- +
- + /** Device address */
- + unsigned devaddr:7;
- +
- + /**
- + * Frame to transmit periodic transaction.
- + * 0: even, 1: odd
- + */
- + unsigned oddfrm:1;
- +
- + /** Channel disable */
- + unsigned chdis:1;
- +
- + /** Channel enable */
- + unsigned chen:1;
- + } b;
- +} hcchar_data_t;
- +
- +typedef union hcsplt_data {
- + /** raw register data */
- + uint32_t d32;
- +
- + /** register bits */
- + struct {
- + /** Port Address */
- + unsigned prtaddr:7;
- +
- + /** Hub Address */
- + unsigned hubaddr:7;
- +
- + /** Transaction Position */
- + unsigned xactpos:2;
- +#define DWC_HCSPLIT_XACTPOS_MID 0
- +#define DWC_HCSPLIT_XACTPOS_END 1
- +#define DWC_HCSPLIT_XACTPOS_BEGIN 2
- +#define DWC_HCSPLIT_XACTPOS_ALL 3
- +
- + /** Do Complete Split */
- + unsigned compsplt:1;
- +
- + /** Reserved */
- + unsigned reserved:14;
- +
- + /** Split Enble */
- + unsigned spltena:1;
- + } b;
- +} hcsplt_data_t;
- +
- +/**
- + * This union represents the bit fields in the Host All Interrupt
- + * Register.
- + */
- +typedef union hcint_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + /** Transfer Complete */
- + unsigned xfercomp:1;
- + /** Channel Halted */
- + unsigned chhltd:1;
- + /** AHB Error */
- + unsigned ahberr:1;
- + /** STALL Response Received */
- + unsigned stall:1;
- + /** NAK Response Received */
- + unsigned nak:1;
- + /** ACK Response Received */
- + unsigned ack:1;
- + /** NYET Response Received */
- + unsigned nyet:1;
- + /** Transaction Err */
- + unsigned xacterr:1;
- + /** Babble Error */
- + unsigned bblerr:1;
- + /** Frame Overrun */
- + unsigned frmovrun:1;
- + /** Data Toggle Error */
- + unsigned datatglerr:1;
- + /** Buffer Not Available (only for DDMA mode) */
- + unsigned bna:1;
- + /** Exessive transaction error (only for DDMA mode) */
- + unsigned xcs_xact:1;
- + /** Frame List Rollover interrupt */
- + unsigned frm_list_roll:1;
- + /** Reserved */
- + unsigned reserved14_31:18;
- + } b;
- +} hcint_data_t;
- +
- +/**
- + * This union represents the bit fields in the Host Channel Interrupt Mask
- + * Register. Read the register into the <i>d32</i> member then set/clear the
- + * bits using the <i>b</i>it elements. Write the <i>d32</i> member to the
- + * hcintmsk register.
- + */
- +typedef union hcintmsk_data {
- + /** raw register data */
- + uint32_t d32;
- +
- + /** register bits */
- + struct {
- + unsigned xfercompl:1;
- + unsigned chhltd:1;
- + unsigned ahberr:1;
- + unsigned stall:1;
- + unsigned nak:1;
- + unsigned ack:1;
- + unsigned nyet:1;
- + unsigned xacterr:1;
- + unsigned bblerr:1;
- + unsigned frmovrun:1;
- + unsigned datatglerr:1;
- + unsigned bna:1;
- + unsigned xcs_xact:1;
- + unsigned frm_list_roll:1;
- + unsigned reserved14_31:18;
- + } b;
- +} hcintmsk_data_t;
- +
- +/**
- + * This union represents the bit fields in the Host Channel Transfer Size
- + * Register. Read the register into the <i>d32</i> member then set/clear the
- + * bits using the <i>b</i>it elements. Write the <i>d32</i> member to the
- + * hcchar register.
- + */
- +
- +typedef union hctsiz_data {
- + /** raw register data */
- + uint32_t d32;
- +
- + /** register bits */
- + struct {
- + /** Total transfer size in bytes */
- + unsigned xfersize:19;
- +
- + /** Data packets to transfer */
- + unsigned pktcnt:10;
- +
- + /**
- + * Packet ID for next data packet
- + * 0: DATA0
- + * 1: DATA2
- + * 2: DATA1
- + * 3: MDATA (non-Control), SETUP (Control)
- + */
- + unsigned pid:2;
- +#define DWC_HCTSIZ_DATA0 0
- +#define DWC_HCTSIZ_DATA1 2
- +#define DWC_HCTSIZ_DATA2 1
- +#define DWC_HCTSIZ_MDATA 3
- +#define DWC_HCTSIZ_SETUP 3
- +
- + /** Do PING protocol when 1 */
- + unsigned dopng:1;
- + } b;
- +
- + /** register bits */
- + struct {
- + /** Scheduling information */
- + unsigned schinfo:8;
- +
- + /** Number of transfer descriptors.
- + * Max value:
- + * 64 in general,
- + * 256 only for HS isochronous endpoint.
- + */
- + unsigned ntd:8;
- +
- + /** Data packets to transfer */
- + unsigned reserved16_28:13;
- +
- + /**
- + * Packet ID for next data packet
- + * 0: DATA0
- + * 1: DATA2
- + * 2: DATA1
- + * 3: MDATA (non-Control)
- + */
- + unsigned pid:2;
- +
- + /** Do PING protocol when 1 */
- + unsigned dopng:1;
- + } b_ddma;
- +} hctsiz_data_t;
- +
- +/**
- + * This union represents the bit fields in the Host DMA Address
- + * Register used in Descriptor DMA mode.
- + */
- +typedef union hcdma_data {
- + /** raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + unsigned reserved0_2:3;
- + /** Current Transfer Descriptor. Not used for ISOC */
- + unsigned ctd:8;
- + /** Start Address of Descriptor List */
- + unsigned dma_addr:21;
- + } b;
- +} hcdma_data_t;
- +
- +/**
- + * This union represents the bit fields in the DMA Descriptor
- + * status quadlet for host mode. Read the quadlet into the <i>d32</i> member then
- + * set/clear the bits using the <i>b</i>it elements.
- + */
- +typedef union host_dma_desc_sts {
- + /** raw register data */
- + uint32_t d32;
- + /** quadlet bits */
- +
- + /* for non-isochronous */
- + struct {
- + /** Number of bytes */
- + unsigned n_bytes:17;
- + /** QTD offset to jump when Short Packet received - only for IN EPs */
- + unsigned qtd_offset:6;
- + /**
- + * Set to request the core to jump to alternate QTD if
- + * Short Packet received - only for IN EPs
- + */
- + unsigned a_qtd:1;
- + /**
- + * Setup Packet bit. When set indicates that buffer contains
- + * setup packet.
- + */
- + unsigned sup:1;
- + /** Interrupt On Complete */
- + unsigned ioc:1;
- + /** End of List */
- + unsigned eol:1;
- + unsigned reserved27:1;
- + /** Rx/Tx Status */
- + unsigned sts:2;
- +#define DMA_DESC_STS_PKTERR 1
- + unsigned reserved30:1;
- + /** Active Bit */
- + unsigned a:1;
- + } b;
- + /* for isochronous */
- + struct {
- + /** Number of bytes */
- + unsigned n_bytes:12;
- + unsigned reserved12_24:13;
- + /** Interrupt On Complete */
- + unsigned ioc:1;
- + unsigned reserved26_27:2;
- + /** Rx/Tx Status */
- + unsigned sts:2;
- + unsigned reserved30:1;
- + /** Active Bit */
- + unsigned a:1;
- + } b_isoc;
- +} host_dma_desc_sts_t;
- +
- +#define MAX_DMA_DESC_SIZE 131071
- +#define MAX_DMA_DESC_NUM_GENERIC 64
- +#define MAX_DMA_DESC_NUM_HS_ISOC 256
- +#define MAX_FRLIST_EN_NUM 64
- +/**
- + * Host-mode DMA Descriptor structure
- + *
- + * DMA Descriptor structure contains two quadlets:
- + * Status quadlet and Data buffer pointer.
- + */
- +typedef struct dwc_otg_host_dma_desc {
- + /** DMA Descriptor status quadlet */
- + host_dma_desc_sts_t status;
- + /** DMA Descriptor data buffer pointer */
- + uint32_t buf;
- +} dwc_otg_host_dma_desc_t;
- +
- +/** OTG Host Interface Structure.
- + *
- + * The OTG Host Interface Structure structure contains information
- + * needed to manage the DWC_otg controller acting in host mode. It
- + * represents the programming view of the host-specific aspects of the
- + * controller.
- + */
- +typedef struct dwc_otg_host_if {
- + /** Host Global Registers starting at offset 400h.*/
- + dwc_otg_host_global_regs_t *host_global_regs;
- +#define DWC_OTG_HOST_GLOBAL_REG_OFFSET 0x400
- +
- + /** Host Port 0 Control and Status Register */
- + volatile uint32_t *hprt0;
- +#define DWC_OTG_HOST_PORT_REGS_OFFSET 0x440
- +
- + /** Host Channel Specific Registers at offsets 500h-5FCh. */
- + dwc_otg_hc_regs_t *hc_regs[MAX_EPS_CHANNELS];
- +#define DWC_OTG_HOST_CHAN_REGS_OFFSET 0x500
- +#define DWC_OTG_CHAN_REGS_OFFSET 0x20
- +
- + /* Host configuration information */
- + /** Number of Host Channels (range: 1-16) */
- + uint8_t num_host_channels;
- + /** Periodic EPs supported (0: no, 1: yes) */
- + uint8_t perio_eps_supported;
- + /** Periodic Tx FIFO Size (Only 1 host periodic Tx FIFO) */
- + uint16_t perio_tx_fifo_size;
- +
- +} dwc_otg_host_if_t;
- +
- +/**
- + * This union represents the bit fields in the Power and Clock Gating Control
- + * Register. Read the register into the <i>d32</i> member then set/clear the
- + * bits using the <i>b</i>it elements.
- + */
- +typedef union pcgcctl_data {
- + /** raw register data */
- + uint32_t d32;
- +
- + /** register bits */
- + struct {
- + /** Stop Pclk */
- + unsigned stoppclk:1;
- + /** Gate Hclk */
- + unsigned gatehclk:1;
- + /** Power Clamp */
- + unsigned pwrclmp:1;
- + /** Reset Power Down Modules */
- + unsigned rstpdwnmodule:1;
- + /** Reserved */
- + unsigned reserved:1;
- + /** Enable Sleep Clock Gating (Enbl_L1Gating) */
- + unsigned enbl_sleep_gating:1;
- + /** PHY In Sleep (PhySleep) */
- + unsigned phy_in_sleep:1;
- + /** Deep Sleep*/
- + unsigned deep_sleep:1;
- + unsigned resetaftsusp:1;
- + unsigned restoremode:1;
- + unsigned enbl_extnd_hiber:1;
- + unsigned extnd_hiber_pwrclmp:1;
- + unsigned extnd_hiber_switch:1;
- + unsigned ess_reg_restored:1;
- + unsigned prt_clk_sel:2;
- + unsigned port_power:1;
- + unsigned max_xcvrselect:2;
- + unsigned max_termsel:1;
- + unsigned mac_dev_addr:7;
- + unsigned p2hd_dev_enum_spd:2;
- + unsigned p2hd_prt_spd:2;
- + unsigned if_dev_mode:1;
- + } b;
- +} pcgcctl_data_t;
- +
- +/**
- + * This union represents the bit fields in the Global Data FIFO Software
- + * Configuration Register. Read the register into the <i>d32</i> member then
- + * set/clear the bits using the <i>b</i>it elements.
- + */
- +typedef union gdfifocfg_data {
- + /* raw register data */
- + uint32_t d32;
- + /** register bits */
- + struct {
- + /** OTG Data FIFO depth */
- + unsigned gdfifocfg:16;
- + /** Start address of EP info controller */
- + unsigned epinfobase:16;
- + } b;
- +} gdfifocfg_data_t;
- +
- +/**
- + * This union represents the bit fields in the Global Power Down Register
- + * Register. Read the register into the <i>d32</i> member then set/clear the
- + * bits using the <i>b</i>it elements.
- + */
- +typedef union gpwrdn_data {
- + /* raw register data */
- + uint32_t d32;
- +
- + /** register bits */
- + struct {
- + /** PMU Interrupt Select */
- + unsigned pmuintsel:1;
- + /** PMU Active */
- + unsigned pmuactv:1;
- + /** Restore */
- + unsigned restore:1;
- + /** Power Down Clamp */
- + unsigned pwrdnclmp:1;
- + /** Power Down Reset */
- + unsigned pwrdnrstn:1;
- + /** Power Down Switch */
- + unsigned pwrdnswtch:1;
- + /** Disable VBUS */
- + unsigned dis_vbus:1;
- + /** Line State Change */
- + unsigned lnstschng:1;
- + /** Line state change mask */
- + unsigned lnstchng_msk:1;
- + /** Reset Detected */
- + unsigned rst_det:1;
- + /** Reset Detect mask */
- + unsigned rst_det_msk:1;
- + /** Disconnect Detected */
- + unsigned disconn_det:1;
- + /** Disconnect Detect mask */
- + unsigned disconn_det_msk:1;
- + /** Connect Detected*/
- + unsigned connect_det:1;
- + /** Connect Detected Mask*/
- + unsigned connect_det_msk:1;
- + /** SRP Detected */
- + unsigned srp_det:1;
- + /** SRP Detect mask */
- + unsigned srp_det_msk:1;
- + /** Status Change Interrupt */
- + unsigned sts_chngint:1;
- + /** Status Change Interrupt Mask */
- + unsigned sts_chngint_msk:1;
- + /** Line State */
- + unsigned linestate:2;
- + /** Indicates current mode(status of IDDIG signal) */
- + unsigned idsts:1;
- + /** B Session Valid signal status*/
- + unsigned bsessvld:1;
- + /** ADP Event Detected */
- + unsigned adp_int:1;
- + /** Multi Valued ID pin */
- + unsigned mult_val_id_bc:5;
- + /** Reserved 24_31 */
- + unsigned reserved29_31:3;
- + } b;
- +} gpwrdn_data_t;
- +
- +#endif
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/test/Makefile
- @@ -0,0 +1,16 @@
- +
- +PERL=/usr/bin/perl
- +PL_TESTS=test_sysfs.pl test_mod_param.pl
- +
- +.PHONY : test
- +test : perl_tests
- +
- +perl_tests :
- + @echo
- + @echo Running perl tests
- + @for test in $(PL_TESTS); do \
- + if $(PERL) ./$$test ; then \
- + echo "=======> $$test, PASSED" ; \
- + else echo "=======> $$test, FAILED" ; \
- + fi \
- + done
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/test/dwc_otg_test.pm
- @@ -0,0 +1,337 @@
- +package dwc_otg_test;
- +
- +use strict;
- +use Exporter ();
- +
- +use vars qw(@ISA @EXPORT
- +$sysfsdir $paramdir $errors $params
- +);
- +
- +@ISA = qw(Exporter);
- +
- +#
- +# Globals
- +#
- +$sysfsdir = "/sys/devices/lm0";
- +$paramdir = "/sys/module/dwc_otg";
- +$errors = 0;
- +
- +$params = [
- + {
- + NAME => "otg_cap",
- + DEFAULT => 0,
- + ENUM => [],
- + LOW => 0,
- + HIGH => 2
- + },
- + {
- + NAME => "dma_enable",
- + DEFAULT => 0,
- + ENUM => [],
- + LOW => 0,
- + HIGH => 1
- + },
- + {
- + NAME => "dma_burst_size",
- + DEFAULT => 32,
- + ENUM => [1, 4, 8, 16, 32, 64, 128, 256],
- + LOW => 1,
- + HIGH => 256
- + },
- + {
- + NAME => "host_speed",
- + DEFAULT => 0,
- + ENUM => [],
- + LOW => 0,
- + HIGH => 1
- + },
- + {
- + NAME => "host_support_fs_ls_low_power",
- + DEFAULT => 0,
- + ENUM => [],
- + LOW => 0,
- + HIGH => 1
- + },
- + {
- + NAME => "host_ls_low_power_phy_clk",
- + DEFAULT => 0,
- + ENUM => [],
- + LOW => 0,
- + HIGH => 1
- + },
- + {
- + NAME => "dev_speed",
- + DEFAULT => 0,
- + ENUM => [],
- + LOW => 0,
- + HIGH => 1
- + },
- + {
- + NAME => "enable_dynamic_fifo",
- + DEFAULT => 1,
- + ENUM => [],
- + LOW => 0,
- + HIGH => 1
- + },
- + {
- + NAME => "data_fifo_size",
- + DEFAULT => 8192,
- + ENUM => [],
- + LOW => 32,
- + HIGH => 32768
- + },
- + {
- + NAME => "dev_rx_fifo_size",
- + DEFAULT => 1064,
- + ENUM => [],
- + LOW => 16,
- + HIGH => 32768
- + },
- + {
- + NAME => "dev_nperio_tx_fifo_size",
- + DEFAULT => 1024,
- + ENUM => [],
- + LOW => 16,
- + HIGH => 32768
- + },
- + {
- + NAME => "dev_perio_tx_fifo_size_1",
- + DEFAULT => 256,
- + ENUM => [],
- + LOW => 4,
- + HIGH => 768
- + },
- + {
- + NAME => "dev_perio_tx_fifo_size_2",
- + DEFAULT => 256,
- + ENUM => [],
- + LOW => 4,
- + HIGH => 768
- + },
- + {
- + NAME => "dev_perio_tx_fifo_size_3",
- + DEFAULT => 256,
- + ENUM => [],
- + LOW => 4,
- + HIGH => 768
- + },
- + {
- + NAME => "dev_perio_tx_fifo_size_4",
- + DEFAULT => 256,
- + ENUM => [],
- + LOW => 4,
- + HIGH => 768
- + },
- + {
- + NAME => "dev_perio_tx_fifo_size_5",
- + DEFAULT => 256,
- + ENUM => [],
- + LOW => 4,
- + HIGH => 768
- + },
- + {
- + NAME => "dev_perio_tx_fifo_size_6",
- + DEFAULT => 256,
- + ENUM => [],
- + LOW => 4,
- + HIGH => 768
- + },
- + {
- + NAME => "dev_perio_tx_fifo_size_7",
- + DEFAULT => 256,
- + ENUM => [],
- + LOW => 4,
- + HIGH => 768
- + },
- + {
- + NAME => "dev_perio_tx_fifo_size_8",
- + DEFAULT => 256,
- + ENUM => [],
- + LOW => 4,
- + HIGH => 768
- + },
- + {
- + NAME => "dev_perio_tx_fifo_size_9",
- + DEFAULT => 256,
- + ENUM => [],
- + LOW => 4,
- + HIGH => 768
- + },
- + {
- + NAME => "dev_perio_tx_fifo_size_10",
- + DEFAULT => 256,
- + ENUM => [],
- + LOW => 4,
- + HIGH => 768
- + },
- + {
- + NAME => "dev_perio_tx_fifo_size_11",
- + DEFAULT => 256,
- + ENUM => [],
- + LOW => 4,
- + HIGH => 768
- + },
- + {
- + NAME => "dev_perio_tx_fifo_size_12",
- + DEFAULT => 256,
- + ENUM => [],
- + LOW => 4,
- + HIGH => 768
- + },
- + {
- + NAME => "dev_perio_tx_fifo_size_13",
- + DEFAULT => 256,
- + ENUM => [],
- + LOW => 4,
- + HIGH => 768
- + },
- + {
- + NAME => "dev_perio_tx_fifo_size_14",
- + DEFAULT => 256,
- + ENUM => [],
- + LOW => 4,
- + HIGH => 768
- + },
- + {
- + NAME => "dev_perio_tx_fifo_size_15",
- + DEFAULT => 256,
- + ENUM => [],
- + LOW => 4,
- + HIGH => 768
- + },
- + {
- + NAME => "host_rx_fifo_size",
- + DEFAULT => 1024,
- + ENUM => [],
- + LOW => 16,
- + HIGH => 32768
- + },
- + {
- + NAME => "host_nperio_tx_fifo_size",
- + DEFAULT => 1024,
- + ENUM => [],
- + LOW => 16,
- + HIGH => 32768
- + },
- + {
- + NAME => "host_perio_tx_fifo_size",
- + DEFAULT => 1024,
- + ENUM => [],
- + LOW => 16,
- + HIGH => 32768
- + },
- + {
- + NAME => "max_transfer_size",
- + DEFAULT => 65535,
- + ENUM => [],
- + LOW => 2047,
- + HIGH => 65535
- + },
- + {
- + NAME => "max_packet_count",
- + DEFAULT => 511,
- + ENUM => [],
- + LOW => 15,
- + HIGH => 511
- + },
- + {
- + NAME => "host_channels",
- + DEFAULT => 12,
- + ENUM => [],
- + LOW => 1,
- + HIGH => 16
- + },
- + {
- + NAME => "dev_endpoints",
- + DEFAULT => 6,
- + ENUM => [],
- + LOW => 1,
- + HIGH => 15
- + },
- + {
- + NAME => "phy_type",
- + DEFAULT => 1,
- + ENUM => [],
- + LOW => 0,
- + HIGH => 2
- + },
- + {
- + NAME => "phy_utmi_width",
- + DEFAULT => 16,
- + ENUM => [8, 16],
- + LOW => 8,
- + HIGH => 16
- + },
- + {
- + NAME => "phy_ulpi_ddr",
- + DEFAULT => 0,
- + ENUM => [],
- + LOW => 0,
- + HIGH => 1
- + },
- + ];
- +
- +
- +#
- +#
- +sub check_arch {
- + $_ = `uname -m`;
- + chomp;
- + unless (m/armv4tl/) {
- + warn "# \n# Can't execute on $_. Run on integrator platform.\n# \n";
- + return 0;
- + }
- + return 1;
- +}
- +
- +#
- +#
- +sub load_module {
- + my $params = shift;
- + print "\nRemoving Module\n";
- + system "rmmod dwc_otg";
- + print "Loading Module\n";
- + if ($params ne "") {
- + print "Module Parameters: $params\n";
- + }
- + if (system("modprobe dwc_otg $params")) {
- + warn "Unable to load module\n";
- + return 0;
- + }
- + return 1;
- +}
- +
- +#
- +#
- +sub test_status {
- + my $arg = shift;
- +
- + print "\n";
- +
- + if (defined $arg) {
- + warn "WARNING: $arg\n";
- + }
- +
- + if ($errors > 0) {
- + warn "TEST FAILED with $errors errors\n";
- + return 0;
- + } else {
- + print "TEST PASSED\n";
- + return 0 if (defined $arg);
- + }
- + return 1;
- +}
- +
- +#
- +#
- +@EXPORT = qw(
- +$sysfsdir
- +$paramdir
- +$params
- +$errors
- +check_arch
- +load_module
- +test_status
- +);
- +
- +1;
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/test/test_mod_param.pl
- @@ -0,0 +1,133 @@
- +#!/usr/bin/perl -w
- +#
- +# Run this program on the integrator.
- +#
- +# - Tests module parameter default values.
- +# - Tests setting of valid module parameter values via modprobe.
- +# - Tests invalid module parameter values.
- +# -----------------------------------------------------------------------------
- +use strict;
- +use dwc_otg_test;
- +
- +check_arch() or die;
- +
- +#
- +#
- +sub test {
- + my ($param,$expected) = @_;
- + my $value = get($param);
- +
- + if ($value == $expected) {
- + print "$param = $value, okay\n";
- + }
- +
- + else {
- + warn "ERROR: value of $param != $expected, $value\n";
- + $errors ++;
- + }
- +}
- +
- +#
- +#
- +sub get {
- + my $param = shift;
- + my $tmp = `cat $paramdir/$param`;
- + chomp $tmp;
- + return $tmp;
- +}
- +
- +#
- +#
- +sub test_main {
- +
- + print "\nTesting Module Parameters\n";
- +
- + load_module("") or die;
- +
- + # Test initial values
- + print "\nTesting Default Values\n";
- + foreach (@{$params}) {
- + test ($_->{NAME}, $_->{DEFAULT});
- + }
- +
- + # Test low value
- + print "\nTesting Low Value\n";
- + my $cmd_params = "";
- + foreach (@{$params}) {
- + $cmd_params = $cmd_params . "$_->{NAME}=$_->{LOW} ";
- + }
- + load_module($cmd_params) or die;
- +
- + foreach (@{$params}) {
- + test ($_->{NAME}, $_->{LOW});
- + }
- +
- + # Test high value
- + print "\nTesting High Value\n";
- + $cmd_params = "";
- + foreach (@{$params}) {
- + $cmd_params = $cmd_params . "$_->{NAME}=$_->{HIGH} ";
- + }
- + load_module($cmd_params) or die;
- +
- + foreach (@{$params}) {
- + test ($_->{NAME}, $_->{HIGH});
- + }
- +
- + # Test Enum
- + print "\nTesting Enumerated\n";
- + foreach (@{$params}) {
- + if (defined $_->{ENUM}) {
- + my $value;
- + foreach $value (@{$_->{ENUM}}) {
- + $cmd_params = "$_->{NAME}=$value";
- + load_module($cmd_params) or die;
- + test ($_->{NAME}, $value);
- + }
- + }
- + }
- +
- + # Test Invalid Values
- + print "\nTesting Invalid Values\n";
- + $cmd_params = "";
- + foreach (@{$params}) {
- + $cmd_params = $cmd_params . sprintf "$_->{NAME}=%d ", $_->{LOW}-1;
- + }
- + load_module($cmd_params) or die;
- +
- + foreach (@{$params}) {
- + test ($_->{NAME}, $_->{DEFAULT});
- + }
- +
- + $cmd_params = "";
- + foreach (@{$params}) {
- + $cmd_params = $cmd_params . sprintf "$_->{NAME}=%d ", $_->{HIGH}+1;
- + }
- + load_module($cmd_params) or die;
- +
- + foreach (@{$params}) {
- + test ($_->{NAME}, $_->{DEFAULT});
- + }
- +
- + print "\nTesting Enumerated\n";
- + foreach (@{$params}) {
- + if (defined $_->{ENUM}) {
- + my $value;
- + foreach $value (@{$_->{ENUM}}) {
- + $value = $value + 1;
- + $cmd_params = "$_->{NAME}=$value";
- + load_module($cmd_params) or die;
- + test ($_->{NAME}, $_->{DEFAULT});
- + $value = $value - 2;
- + $cmd_params = "$_->{NAME}=$value";
- + load_module($cmd_params) or die;
- + test ($_->{NAME}, $_->{DEFAULT});
- + }
- + }
- + }
- +
- + test_status() or die;
- +}
- +
- +test_main();
- +0;
- --- /dev/null
- +++ b/drivers/usb/host/dwc_otg/test/test_sysfs.pl
- @@ -0,0 +1,193 @@
- +#!/usr/bin/perl -w
- +#
- +# Run this program on the integrator
- +# - Tests select sysfs attributes.
- +# - Todo ... test more attributes, hnp/srp, buspower/bussuspend, etc.
- +# -----------------------------------------------------------------------------
- +use strict;
- +use dwc_otg_test;
- +
- +check_arch() or die;
- +
- +#
- +#
- +sub test {
- + my ($attr,$expected) = @_;
- + my $string = get($attr);
- +
- + if ($string eq $expected) {
- + printf("$attr = $string, okay\n");
- + }
- + else {
- + warn "ERROR: value of $attr != $expected, $string\n";
- + $errors ++;
- + }
- +}
- +
- +#
- +#
- +sub set {
- + my ($reg, $value) = @_;
- + system "echo $value > $sysfsdir/$reg";
- +}
- +
- +#
- +#
- +sub get {
- + my $attr = shift;
- + my $string = `cat $sysfsdir/$attr`;
- + chomp $string;
- + if ($string =~ m/\s\=\s/) {
- + my $tmp;
- + ($tmp, $string) = split /\s=\s/, $string;
- + }
- + return $string;
- +}
- +
- +#
- +#
- +sub test_main {
- + print("\nTesting Sysfs Attributes\n");
- +
- + load_module("") or die;
- +
- + # Test initial values of regoffset/regvalue/guid/gsnpsid
- + print("\nTesting Default Values\n");
- +
- + test("regoffset", "0xffffffff");
- + test("regvalue", "invalid offset");
- + test("guid", "0x12345678"); # this will fail if it has been changed
- + test("gsnpsid", "0x4f54200a");
- +
- + # Test operation of regoffset/regvalue
- + print("\nTesting regoffset\n");
- + set('regoffset', '5a5a5a5a');
- + test("regoffset", "0xffffffff");
- +
- + set('regoffset', '0');
- + test("regoffset", "0x00000000");
- +
- + set('regoffset', '40000');
- + test("regoffset", "0x00000000");
- +
- + set('regoffset', '3ffff');
- + test("regoffset", "0x0003ffff");
- +
- + set('regoffset', '1');
- + test("regoffset", "0x00000001");
- +
- + print("\nTesting regvalue\n");
- + set('regoffset', '3c');
- + test("regvalue", "0x12345678");
- + set('regvalue', '5a5a5a5a');
- + test("regvalue", "0x5a5a5a5a");
- + set('regvalue','a5a5a5a5');
- + test("regvalue", "0xa5a5a5a5");
- + set('guid','12345678');
- +
- + # Test HNP Capable
- + print("\nTesting HNP Capable bit\n");
- + set('hnpcapable', '1');
- + test("hnpcapable", "0x1");
- + set('hnpcapable','0');
- + test("hnpcapable", "0x0");
- +
- + set('regoffset','0c');
- +
- + my $old = get('gusbcfg');
- + print("setting hnpcapable\n");
- + set('hnpcapable', '1');
- + test("hnpcapable", "0x1");
- + test('gusbcfg', sprintf "0x%08x", (oct ($old) | (1<<9)));
- + test('regvalue', sprintf "0x%08x", (oct ($old) | (1<<9)));
- +
- + $old = get('gusbcfg');
- + print("clearing hnpcapable\n");
- + set('hnpcapable', '0');
- + test("hnpcapable", "0x0");
- + test ('gusbcfg', sprintf "0x%08x", oct ($old) & (~(1<<9)));
- + test ('regvalue', sprintf "0x%08x", oct ($old) & (~(1<<9)));
- +
- + # Test SRP Capable
- + print("\nTesting SRP Capable bit\n");
- + set('srpcapable', '1');
- + test("srpcapable", "0x1");
- + set('srpcapable','0');
- + test("srpcapable", "0x0");
- +
- + set('regoffset','0c');
- +
- + $old = get('gusbcfg');
- + print("setting srpcapable\n");
- + set('srpcapable', '1');
- + test("srpcapable", "0x1");
- + test('gusbcfg', sprintf "0x%08x", (oct ($old) | (1<<8)));
- + test('regvalue', sprintf "0x%08x", (oct ($old) | (1<<8)));
- +
- + $old = get('gusbcfg');
- + print("clearing srpcapable\n");
- + set('srpcapable', '0');
- + test("srpcapable", "0x0");
- + test('gusbcfg', sprintf "0x%08x", oct ($old) & (~(1<<8)));
- + test('regvalue', sprintf "0x%08x", oct ($old) & (~(1<<8)));
- +
- + # Test GGPIO
- + print("\nTesting GGPIO\n");
- + set('ggpio','5a5a5a5a');
- + test('ggpio','0x5a5a0000');
- + set('ggpio','a5a5a5a5');
- + test('ggpio','0xa5a50000');
- + set('ggpio','11110000');
- + test('ggpio','0x11110000');
- + set('ggpio','00001111');
- + test('ggpio','0x00000000');
- +
- + # Test DEVSPEED
- + print("\nTesting DEVSPEED\n");
- + set('regoffset','800');
- + $old = get('regvalue');
- + set('devspeed','0');
- + test('devspeed','0x0');
- + test('regvalue',sprintf("0x%08x", oct($old) & ~(0x3)));
- + set('devspeed','1');
- + test('devspeed','0x1');
- + test('regvalue',sprintf("0x%08x", oct($old) & ~(0x3) | 1));
- + set('devspeed','2');
- + test('devspeed','0x2');
- + test('regvalue',sprintf("0x%08x", oct($old) & ~(0x3) | 2));
- + set('devspeed','3');
- + test('devspeed','0x3');
- + test('regvalue',sprintf("0x%08x", oct($old) & ~(0x3) | 3));
- + set('devspeed','4');
- + test('devspeed','0x0');
- + test('regvalue',sprintf("0x%08x", oct($old) & ~(0x3)));
- + set('devspeed','5');
- + test('devspeed','0x1');
- + test('regvalue',sprintf("0x%08x", oct($old) & ~(0x3) | 1));
- +
- +
- + # mode Returns the current mode:0 for device mode1 for host mode Read
- + # hnp Initiate the Host Negotiation Protocol. Read returns the status. Read/Write
- + # srp Initiate the Session Request Protocol. Read returns the status. Read/Write
- + # buspower Get or Set the Power State of the bus (0 - Off or 1 - On) Read/Write
- + # bussuspend Suspend the USB bus. Read/Write
- + # busconnected Get the connection status of the bus Read
- +
- + # gotgctl Get or set the Core Control Status Register. Read/Write
- + ## gusbcfg Get or set the Core USB Configuration Register Read/Write
- + # grxfsiz Get or set the Receive FIFO Size Register Read/Write
- + # gnptxfsiz Get or set the non-periodic Transmit Size Register Read/Write
- + # gpvndctl Get or set the PHY Vendor Control Register Read/Write
- + ## ggpio Get the value in the lower 16-bits of the General Purpose IO Register or Set the upper 16 bits. Read/Write
- + ## guid Get or set the value of the User ID Register Read/Write
- + ## gsnpsid Get the value of the Synopsys ID Regester Read
- + ## devspeed Get or set the device speed setting in the DCFG register Read/Write
- + # enumspeed Gets the device enumeration Speed. Read
- + # hptxfsiz Get the value of the Host Periodic Transmit FIFO Read
- + # hprt0 Get or Set the value in the Host Port Control and Status Register Read/Write
- +
- + test_status("TEST NYI") or die;
- +}
- +
- +test_main();
- +0;
|