123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257132581325913260132611326213263132641326513266132671326813269132701327113272132731327413275132761327713278132791328013281132821328313284132851328613287132881328913290132911329213293132941329513296132971329813299133001330113302133031330413305133061330713308133091331013311133121331313314133151331613317133181331913320133211332213323133241332513326133271332813329133301333113332133331333413335133361333713338133391334013341133421334313344133451334613347133481334913350133511335213353133541335513356133571335813359133601336113362133631336413365133661336713368133691337013371133721337313374133751337613377133781337913380133811338213383133841338513386133871338813389133901339113392133931339413395133961339713398133991340013401134021340313404134051340613407134081340913410134111341213413134141341513416134171341813419134201342113422134231342413425134261342713428134291343013431134321343313434134351343613437134381343913440134411344213443134441344513446134471344813449134501345113452134531345413455134561345713458134591346013461134621346313464134651346613467134681346913470134711347213473134741347513476134771347813479134801348113482134831348413485134861348713488134891349013491134921349313494134951349613497134981349913500135011350213503135041350513506135071350813509135101351113512135131351413515135161351713518135191352013521135221352313524135251352613527135281352913530135311353213533135341353513536135371353813539135401354113542135431354413545135461354713548135491355013551135521355313554135551355613557135581355913560135611356213563135641356513566135671356813569135701357113572135731357413575135761357713578135791358013581135821358313584135851358613587135881358913590135911359213593135941359513596135971359813599136001360113602136031360413605136061360713608136091361013611136121361313614136151361613617136181361913620136211362213623136241362513626136271362813629136301363113632136331363413635136361363713638136391364013641136421364313644136451364613647136481364913650136511365213653136541365513656136571365813659136601366113662136631366413665136661366713668136691367013671136721367313674136751367613677136781367913680136811368213683136841368513686136871368813689136901369113692136931369413695136961369713698136991370013701137021370313704137051370613707137081370913710137111371213713137141371513716137171371813719137201372113722137231372413725137261372713728137291373013731137321373313734137351373613737137381373913740137411374213743137441374513746137471374813749137501375113752137531375413755137561375713758137591376013761137621376313764137651376613767137681376913770137711377213773137741377513776137771377813779137801378113782137831378413785137861378713788137891379013791137921379313794137951379613797137981379913800138011380213803138041380513806138071380813809138101381113812138131381413815138161381713818138191382013821138221382313824138251382613827138281382913830138311383213833138341383513836138371383813839138401384113842138431384413845138461384713848138491385013851138521385313854138551385613857138581385913860138611386213863138641386513866138671386813869138701387113872138731387413875138761387713878138791388013881138821388313884138851388613887138881388913890138911389213893138941389513896138971389813899139001390113902139031390413905139061390713908139091391013911139121391313914139151391613917139181391913920139211392213923139241392513926139271392813929139301393113932139331393413935139361393713938139391394013941139421394313944139451394613947139481394913950139511395213953139541395513956139571395813959139601396113962139631396413965139661396713968139691397013971139721397313974139751397613977139781397913980139811398213983139841398513986139871398813989139901399113992139931399413995139961399713998139991400014001140021400314004140051400614007140081400914010140111401214013140141401514016140171401814019140201402114022140231402414025140261402714028140291403014031140321403314034140351403614037140381403914040140411404214043140441404514046140471404814049140501405114052140531405414055140561405714058140591406014061140621406314064140651406614067140681406914070140711407214073140741407514076140771407814079140801408114082140831408414085140861408714088140891409014091140921409314094140951409614097140981409914100141011410214103141041410514106141071410814109141101411114112141131411414115141161411714118141191412014121141221412314124141251412614127141281412914130141311413214133141341413514136141371413814139141401414114142141431414414145141461414714148141491415014151141521415314154141551415614157141581415914160141611416214163141641416514166141671416814169141701417114172141731417414175141761417714178141791418014181141821418314184141851418614187141881418914190141911419214193141941419514196141971419814199142001420114202142031420414205142061420714208142091421014211142121421314214142151421614217142181421914220142211422214223142241422514226142271422814229142301423114232142331423414235142361423714238142391424014241142421424314244142451424614247142481424914250142511425214253142541425514256142571425814259142601426114262142631426414265142661426714268142691427014271142721427314274142751427614277142781427914280142811428214283142841428514286142871428814289142901429114292142931429414295142961429714298142991430014301143021430314304143051430614307143081430914310143111431214313143141431514316143171431814319143201432114322143231432414325143261432714328143291433014331143321433314334143351433614337143381433914340143411434214343143441434514346143471434814349143501435114352143531435414355143561435714358143591436014361143621436314364143651436614367143681436914370143711437214373143741437514376143771437814379143801438114382143831438414385143861438714388143891439014391143921439314394143951439614397143981439914400144011440214403144041440514406144071440814409144101441114412144131441414415144161441714418144191442014421144221442314424144251442614427144281442914430144311443214433144341443514436144371443814439144401444114442144431444414445144461444714448144491445014451144521445314454144551445614457144581445914460144611446214463144641446514466144671446814469144701447114472144731447414475144761447714478144791448014481144821448314484144851448614487144881448914490144911449214493144941449514496144971449814499145001450114502145031450414505145061450714508145091451014511145121451314514145151451614517145181451914520145211452214523145241452514526145271452814529145301453114532145331453414535145361453714538145391454014541145421454314544145451454614547145481454914550145511455214553145541455514556145571455814559145601456114562145631456414565145661456714568145691457014571145721457314574145751457614577145781457914580145811458214583145841458514586145871458814589145901459114592145931459414595145961459714598145991460014601146021460314604146051460614607146081460914610146111461214613146141461514616146171461814619146201462114622146231462414625146261462714628146291463014631146321463314634146351463614637146381463914640146411464214643146441464514646146471464814649146501465114652146531465414655146561465714658146591466014661146621466314664146651466614667146681466914670146711467214673146741467514676146771467814679146801468114682146831468414685146861468714688146891469014691146921469314694146951469614697146981469914700147011470214703147041470514706147071470814709147101471114712147131471414715147161471714718147191472014721147221472314724147251472614727147281472914730147311473214733147341473514736147371473814739147401474114742147431474414745147461474714748147491475014751147521475314754147551475614757147581475914760147611476214763147641476514766147671476814769147701477114772147731477414775147761477714778147791478014781147821478314784147851478614787147881478914790147911479214793147941479514796147971479814799148001480114802148031480414805148061480714808148091481014811148121481314814148151481614817148181481914820148211482214823148241482514826148271482814829148301483114832148331483414835148361483714838148391484014841148421484314844148451484614847148481484914850148511485214853148541485514856148571485814859148601486114862148631486414865148661486714868148691487014871148721487314874148751487614877148781487914880148811488214883148841488514886148871488814889148901489114892148931489414895148961489714898148991490014901149021490314904149051490614907149081490914910149111491214913149141491514916149171491814919149201492114922149231492414925149261492714928149291493014931149321493314934149351493614937149381493914940149411494214943149441494514946149471494814949149501495114952149531495414955149561495714958149591496014961149621496314964149651496614967149681496914970149711497214973149741497514976149771497814979149801498114982149831498414985149861498714988149891499014991149921499314994149951499614997149981499915000150011500215003150041500515006150071500815009150101501115012150131501415015150161501715018150191502015021150221502315024150251502615027150281502915030150311503215033150341503515036150371503815039150401504115042150431504415045150461504715048150491505015051150521505315054150551505615057150581505915060150611506215063150641506515066150671506815069150701507115072150731507415075150761507715078150791508015081150821508315084150851508615087150881508915090150911509215093150941509515096150971509815099151001510115102151031510415105151061510715108151091511015111151121511315114151151511615117151181511915120151211512215123151241512515126151271512815129151301513115132151331513415135151361513715138151391514015141151421514315144151451514615147151481514915150151511515215153151541515515156151571515815159151601516115162151631516415165151661516715168151691517015171151721517315174151751517615177151781517915180151811518215183151841518515186151871518815189151901519115192151931519415195151961519715198151991520015201152021520315204152051520615207152081520915210152111521215213152141521515216152171521815219152201522115222152231522415225152261522715228152291523015231152321523315234152351523615237152381523915240152411524215243152441524515246152471524815249152501525115252152531525415255152561525715258152591526015261152621526315264152651526615267152681526915270152711527215273152741527515276152771527815279152801528115282152831528415285152861528715288152891529015291152921529315294152951529615297152981529915300153011530215303153041530515306153071530815309153101531115312153131531415315153161531715318153191532015321153221532315324153251532615327153281532915330153311533215333153341533515336153371533815339153401534115342153431534415345153461534715348153491535015351153521535315354153551535615357153581535915360153611536215363153641536515366153671536815369153701537115372153731537415375153761537715378153791538015381153821538315384153851538615387153881538915390153911539215393153941539515396153971539815399154001540115402154031540415405154061540715408154091541015411154121541315414154151541615417154181541915420154211542215423154241542515426154271542815429154301543115432154331543415435154361543715438154391544015441154421544315444154451544615447154481544915450154511545215453154541545515456154571545815459154601546115462154631546415465154661546715468154691547015471154721547315474154751547615477154781547915480154811548215483154841548515486154871548815489154901549115492154931549415495154961549715498154991550015501155021550315504155051550615507155081550915510155111551215513155141551515516155171551815519155201552115522155231552415525155261552715528155291553015531155321553315534155351553615537155381553915540155411554215543155441554515546155471554815549155501555115552155531555415555155561555715558155591556015561155621556315564155651556615567155681556915570155711557215573155741557515576155771557815579155801558115582155831558415585155861558715588155891559015591155921559315594155951559615597155981559915600156011560215603156041560515606156071560815609156101561115612156131561415615156161561715618156191562015621156221562315624156251562615627156281562915630156311563215633156341563515636156371563815639156401564115642156431564415645156461564715648156491565015651156521565315654156551565615657156581565915660156611566215663156641566515666156671566815669156701567115672156731567415675156761567715678156791568015681156821568315684156851568615687156881568915690156911569215693156941569515696156971569815699157001570115702157031570415705157061570715708157091571015711157121571315714157151571615717157181571915720157211572215723157241572515726157271572815729157301573115732157331573415735157361573715738157391574015741157421574315744157451574615747157481574915750157511575215753157541575515756157571575815759157601576115762157631576415765157661576715768157691577015771157721577315774157751577615777157781577915780157811578215783157841578515786157871578815789157901579115792157931579415795157961579715798157991580015801158021580315804158051580615807158081580915810158111581215813158141581515816158171581815819158201582115822158231582415825158261582715828158291583015831158321583315834158351583615837158381583915840158411584215843158441584515846158471584815849158501585115852158531585415855158561585715858158591586015861158621586315864158651586615867158681586915870158711587215873158741587515876158771587815879158801588115882158831588415885158861588715888158891589015891158921589315894158951589615897158981589915900159011590215903159041590515906159071590815909159101591115912159131591415915159161591715918159191592015921159221592315924159251592615927159281592915930159311593215933159341593515936159371593815939159401594115942159431594415945159461594715948159491595015951159521595315954159551595615957159581595915960159611596215963159641596515966159671596815969159701597115972159731597415975159761597715978159791598015981159821598315984159851598615987159881598915990159911599215993159941599515996159971599815999160001600116002160031600416005160061600716008160091601016011160121601316014160151601616017160181601916020160211602216023160241602516026160271602816029160301603116032160331603416035160361603716038160391604016041160421604316044160451604616047160481604916050160511605216053160541605516056160571605816059160601606116062160631606416065160661606716068160691607016071160721607316074160751607616077160781607916080160811608216083160841608516086160871608816089160901609116092160931609416095160961609716098160991610016101161021610316104161051610616107161081610916110161111611216113161141611516116161171611816119161201612116122161231612416125161261612716128161291613016131161321613316134161351613616137161381613916140161411614216143161441614516146161471614816149161501615116152161531615416155161561615716158161591616016161161621616316164161651616616167161681616916170161711617216173161741617516176161771617816179161801618116182161831618416185161861618716188161891619016191161921619316194161951619616197161981619916200162011620216203162041620516206162071620816209162101621116212162131621416215162161621716218162191622016221162221622316224162251622616227162281622916230162311623216233162341623516236162371623816239162401624116242162431624416245162461624716248162491625016251162521625316254162551625616257162581625916260162611626216263162641626516266162671626816269162701627116272162731627416275162761627716278162791628016281162821628316284162851628616287162881628916290162911629216293162941629516296162971629816299163001630116302163031630416305163061630716308163091631016311163121631316314163151631616317163181631916320163211632216323163241632516326163271632816329163301633116332163331633416335163361633716338163391634016341163421634316344163451634616347163481634916350163511635216353163541635516356163571635816359163601636116362163631636416365163661636716368163691637016371163721637316374163751637616377163781637916380163811638216383163841638516386163871638816389163901639116392163931639416395163961639716398163991640016401164021640316404164051640616407164081640916410164111641216413164141641516416164171641816419164201642116422164231642416425164261642716428164291643016431164321643316434164351643616437164381643916440164411644216443164441644516446164471644816449164501645116452164531645416455164561645716458164591646016461164621646316464164651646616467164681646916470164711647216473164741647516476164771647816479164801648116482164831648416485164861648716488164891649016491164921649316494164951649616497164981649916500165011650216503165041650516506165071650816509165101651116512165131651416515165161651716518165191652016521165221652316524165251652616527165281652916530165311653216533165341653516536165371653816539165401654116542165431654416545165461654716548165491655016551165521655316554165551655616557165581655916560165611656216563165641656516566165671656816569165701657116572165731657416575165761657716578165791658016581165821658316584165851658616587165881658916590165911659216593165941659516596165971659816599166001660116602166031660416605166061660716608166091661016611166121661316614166151661616617166181661916620166211662216623166241662516626166271662816629166301663116632166331663416635166361663716638166391664016641166421664316644166451664616647166481664916650166511665216653166541665516656166571665816659166601666116662166631666416665166661666716668166691667016671166721667316674166751667616677166781667916680166811668216683166841668516686166871668816689166901669116692166931669416695166961669716698166991670016701167021670316704167051670616707167081670916710167111671216713167141671516716167171671816719167201672116722167231672416725167261672716728167291673016731167321673316734167351673616737167381673916740167411674216743167441674516746167471674816749167501675116752167531675416755167561675716758167591676016761167621676316764167651676616767167681676916770167711677216773167741677516776167771677816779167801678116782167831678416785167861678716788167891679016791167921679316794167951679616797167981679916800168011680216803168041680516806168071680816809168101681116812168131681416815168161681716818168191682016821168221682316824168251682616827168281682916830168311683216833168341683516836168371683816839168401684116842168431684416845168461684716848168491685016851168521685316854168551685616857168581685916860168611686216863168641686516866168671686816869168701687116872168731687416875168761687716878168791688016881168821688316884168851688616887168881688916890168911689216893168941689516896168971689816899169001690116902169031690416905169061690716908169091691016911169121691316914169151691616917169181691916920169211692216923169241692516926169271692816929169301693116932169331693416935169361693716938169391694016941169421694316944169451694616947169481694916950169511695216953169541695516956169571695816959169601696116962169631696416965169661696716968169691697016971169721697316974169751697616977169781697916980169811698216983169841698516986169871698816989169901699116992169931699416995169961699716998169991700017001170021700317004170051700617007170081700917010170111701217013170141701517016170171701817019170201702117022170231702417025170261702717028170291703017031170321703317034170351703617037170381703917040170411704217043170441704517046170471704817049170501705117052170531705417055170561705717058170591706017061170621706317064170651706617067170681706917070170711707217073170741707517076170771707817079170801708117082170831708417085170861708717088170891709017091170921709317094170951709617097170981709917100171011710217103171041710517106171071710817109171101711117112171131711417115171161711717118171191712017121171221712317124171251712617127171281712917130171311713217133171341713517136171371713817139171401714117142171431714417145171461714717148171491715017151171521715317154171551715617157171581715917160171611716217163171641716517166171671716817169171701717117172171731717417175171761717717178171791718017181171821718317184171851718617187171881718917190171911719217193171941719517196171971719817199172001720117202172031720417205172061720717208172091721017211172121721317214172151721617217172181721917220172211722217223172241722517226172271722817229172301723117232172331723417235172361723717238172391724017241172421724317244172451724617247172481724917250172511725217253172541725517256172571725817259172601726117262172631726417265172661726717268172691727017271172721727317274172751727617277172781727917280172811728217283172841728517286172871728817289172901729117292172931729417295172961729717298172991730017301173021730317304173051730617307173081730917310173111731217313173141731517316173171731817319173201732117322173231732417325173261732717328173291733017331173321733317334173351733617337173381733917340173411734217343173441734517346173471734817349173501735117352173531735417355173561735717358173591736017361173621736317364173651736617367173681736917370173711737217373173741737517376173771737817379173801738117382173831738417385173861738717388173891739017391173921739317394173951739617397173981739917400174011740217403174041740517406174071740817409174101741117412174131741417415174161741717418174191742017421174221742317424174251742617427174281742917430174311743217433174341743517436174371743817439174401744117442174431744417445174461744717448174491745017451174521745317454174551745617457174581745917460174611746217463174641746517466174671746817469174701747117472174731747417475174761747717478174791748017481174821748317484174851748617487174881748917490174911749217493174941749517496174971749817499175001750117502175031750417505175061750717508175091751017511175121751317514175151751617517175181751917520175211752217523175241752517526175271752817529175301753117532175331753417535175361753717538175391754017541175421754317544175451754617547175481754917550175511755217553175541755517556175571755817559175601756117562175631756417565175661756717568175691757017571175721757317574175751757617577175781757917580175811758217583175841758517586175871758817589175901759117592175931759417595175961759717598175991760017601176021760317604176051760617607176081760917610176111761217613176141761517616176171761817619176201762117622176231762417625176261762717628176291763017631176321763317634176351763617637176381763917640176411764217643176441764517646176471764817649176501765117652176531765417655176561765717658176591766017661176621766317664176651766617667176681766917670176711767217673176741767517676176771767817679176801768117682176831768417685176861768717688176891769017691176921769317694176951769617697176981769917700177011770217703177041770517706177071770817709177101771117712177131771417715177161771717718177191772017721177221772317724177251772617727177281772917730177311773217733177341773517736177371773817739177401774117742177431774417745177461774717748177491775017751177521775317754177551775617757177581775917760177611776217763177641776517766177671776817769177701777117772177731777417775177761777717778177791778017781177821778317784177851778617787177881778917790177911779217793177941779517796177971779817799178001780117802178031780417805178061780717808178091781017811178121781317814178151781617817178181781917820178211782217823178241782517826178271782817829178301783117832178331783417835178361783717838178391784017841178421784317844178451784617847178481784917850178511785217853178541785517856178571785817859178601786117862178631786417865178661786717868178691787017871178721787317874178751787617877178781787917880178811788217883178841788517886178871788817889178901789117892178931789417895178961789717898178991790017901179021790317904179051790617907179081790917910179111791217913179141791517916179171791817919179201792117922179231792417925179261792717928179291793017931179321793317934179351793617937179381793917940179411794217943179441794517946179471794817949179501795117952179531795417955179561795717958179591796017961179621796317964179651796617967179681796917970179711797217973179741797517976179771797817979179801798117982179831798417985179861798717988179891799017991179921799317994179951799617997179981799918000180011800218003180041800518006180071800818009180101801118012180131801418015180161801718018180191802018021180221802318024180251802618027180281802918030180311803218033180341803518036180371803818039180401804118042180431804418045180461804718048180491805018051180521805318054180551805618057180581805918060180611806218063180641806518066180671806818069180701807118072180731807418075180761807718078180791808018081180821808318084180851808618087180881808918090180911809218093180941809518096180971809818099181001810118102181031810418105181061810718108181091811018111181121811318114181151811618117181181811918120181211812218123181241812518126181271812818129181301813118132181331813418135181361813718138181391814018141181421814318144181451814618147181481814918150181511815218153181541815518156181571815818159181601816118162181631816418165181661816718168181691817018171181721817318174181751817618177181781817918180181811818218183181841818518186181871818818189181901819118192181931819418195181961819718198181991820018201182021820318204182051820618207182081820918210182111821218213182141821518216182171821818219182201822118222182231822418225182261822718228182291823018231182321823318234182351823618237182381823918240182411824218243182441824518246182471824818249182501825118252182531825418255182561825718258182591826018261182621826318264182651826618267182681826918270182711827218273182741827518276182771827818279182801828118282182831828418285182861828718288182891829018291182921829318294182951829618297182981829918300183011830218303183041830518306183071830818309183101831118312183131831418315183161831718318183191832018321183221832318324183251832618327183281832918330183311833218333183341833518336183371833818339183401834118342183431834418345183461834718348183491835018351183521835318354183551835618357183581835918360183611836218363183641836518366183671836818369183701837118372183731837418375183761837718378183791838018381183821838318384183851838618387183881838918390183911839218393183941839518396183971839818399184001840118402184031840418405184061840718408184091841018411184121841318414184151841618417184181841918420184211842218423184241842518426184271842818429184301843118432184331843418435184361843718438184391844018441184421844318444184451844618447184481844918450184511845218453184541845518456184571845818459184601846118462184631846418465184661846718468184691847018471184721847318474184751847618477184781847918480184811848218483184841848518486184871848818489184901849118492184931849418495184961849718498184991850018501185021850318504185051850618507185081850918510185111851218513185141851518516185171851818519185201852118522185231852418525185261852718528185291853018531185321853318534185351853618537185381853918540185411854218543185441854518546185471854818549185501855118552185531855418555185561855718558185591856018561185621856318564185651856618567185681856918570185711857218573185741857518576185771857818579185801858118582185831858418585185861858718588185891859018591185921859318594185951859618597185981859918600186011860218603186041860518606186071860818609186101861118612186131861418615186161861718618186191862018621186221862318624186251862618627186281862918630186311863218633186341863518636186371863818639186401864118642186431864418645186461864718648186491865018651186521865318654186551865618657186581865918660186611866218663186641866518666186671866818669186701867118672186731867418675186761867718678186791868018681186821868318684186851868618687186881868918690186911869218693186941869518696186971869818699187001870118702187031870418705187061870718708187091871018711187121871318714187151871618717187181871918720187211872218723187241872518726187271872818729187301873118732187331873418735187361873718738187391874018741187421874318744187451874618747187481874918750187511875218753187541875518756187571875818759187601876118762187631876418765187661876718768187691877018771187721877318774187751877618777187781877918780187811878218783187841878518786187871878818789187901879118792187931879418795187961879718798187991880018801188021880318804188051880618807188081880918810188111881218813188141881518816188171881818819188201882118822188231882418825188261882718828188291883018831188321883318834188351883618837188381883918840188411884218843188441884518846188471884818849188501885118852188531885418855188561885718858188591886018861188621886318864188651886618867188681886918870188711887218873188741887518876188771887818879188801888118882188831888418885188861888718888188891889018891188921889318894188951889618897188981889918900189011890218903189041890518906189071890818909189101891118912189131891418915189161891718918189191892018921189221892318924189251892618927189281892918930189311893218933189341893518936189371893818939189401894118942189431894418945189461894718948189491895018951189521895318954189551895618957189581895918960189611896218963189641896518966189671896818969189701897118972189731897418975189761897718978189791898018981189821898318984189851898618987189881898918990189911899218993189941899518996189971899818999190001900119002190031900419005190061900719008190091901019011190121901319014190151901619017190181901919020190211902219023190241902519026190271902819029190301903119032190331903419035190361903719038190391904019041190421904319044190451904619047190481904919050190511905219053190541905519056190571905819059190601906119062190631906419065190661906719068190691907019071190721907319074190751907619077190781907919080190811908219083190841908519086190871908819089190901909119092190931909419095190961909719098190991910019101191021910319104191051910619107191081910919110191111911219113191141911519116191171911819119191201912119122191231912419125191261912719128191291913019131191321913319134191351913619137191381913919140191411914219143191441914519146191471914819149191501915119152191531915419155191561915719158191591916019161191621916319164191651916619167191681916919170191711917219173191741917519176191771917819179191801918119182191831918419185191861918719188191891919019191191921919319194191951919619197191981919919200192011920219203192041920519206192071920819209192101921119212192131921419215192161921719218192191922019221192221922319224192251922619227192281922919230192311923219233192341923519236192371923819239192401924119242192431924419245192461924719248192491925019251192521925319254192551925619257192581925919260192611926219263192641926519266192671926819269192701927119272192731927419275192761927719278192791928019281192821928319284192851928619287192881928919290192911929219293192941929519296192971929819299193001930119302193031930419305193061930719308193091931019311193121931319314193151931619317193181931919320193211932219323193241932519326193271932819329193301933119332193331933419335193361933719338193391934019341193421934319344193451934619347193481934919350193511935219353193541935519356193571935819359193601936119362193631936419365193661936719368193691937019371193721937319374193751937619377193781937919380193811938219383193841938519386193871938819389193901939119392193931939419395193961939719398193991940019401194021940319404194051940619407194081940919410194111941219413194141941519416194171941819419194201942119422194231942419425194261942719428194291943019431194321943319434194351943619437194381943919440194411944219443194441944519446194471944819449194501945119452194531945419455194561945719458194591946019461194621946319464194651946619467194681946919470194711947219473194741947519476194771947819479194801948119482194831948419485194861948719488194891949019491194921949319494194951949619497194981949919500195011950219503195041950519506195071950819509195101951119512195131951419515195161951719518195191952019521195221952319524195251952619527195281952919530195311953219533195341953519536195371953819539195401954119542195431954419545195461954719548195491955019551195521955319554195551955619557195581955919560195611956219563195641956519566195671956819569195701957119572195731957419575195761957719578195791958019581195821958319584195851958619587195881958919590195911959219593195941959519596195971959819599196001960119602196031960419605196061960719608196091961019611196121961319614196151961619617196181961919620196211962219623196241962519626196271962819629196301963119632196331963419635196361963719638196391964019641196421964319644196451964619647196481964919650196511965219653196541965519656196571965819659196601966119662196631966419665196661966719668196691967019671196721967319674196751967619677196781967919680196811968219683196841968519686196871968819689196901969119692196931969419695196961969719698196991970019701197021970319704197051970619707197081970919710197111971219713197141971519716197171971819719197201972119722197231972419725197261972719728197291973019731197321973319734197351973619737197381973919740197411974219743197441974519746197471974819749197501975119752197531975419755197561975719758197591976019761197621976319764197651976619767197681976919770197711977219773197741977519776197771977819779197801978119782197831978419785197861978719788197891979019791197921979319794197951979619797197981979919800198011980219803198041980519806198071980819809198101981119812198131981419815198161981719818198191982019821198221982319824198251982619827198281982919830198311983219833198341983519836198371983819839198401984119842198431984419845198461984719848198491985019851198521985319854198551985619857198581985919860198611986219863198641986519866198671986819869198701987119872198731987419875198761987719878198791988019881198821988319884198851988619887198881988919890198911989219893198941989519896198971989819899199001990119902199031990419905199061990719908199091991019911199121991319914199151991619917199181991919920199211992219923199241992519926199271992819929199301993119932199331993419935199361993719938199391994019941199421994319944199451994619947199481994919950199511995219953199541995519956199571995819959199601996119962199631996419965199661996719968199691997019971199721997319974199751997619977199781997919980199811998219983199841998519986199871998819989199901999119992199931999419995199961999719998199992000020001200022000320004200052000620007200082000920010200112001220013200142001520016200172001820019200202002120022200232002420025200262002720028200292003020031200322003320034200352003620037200382003920040200412004220043200442004520046200472004820049200502005120052200532005420055200562005720058200592006020061200622006320064200652006620067200682006920070200712007220073200742007520076200772007820079200802008120082200832008420085200862008720088200892009020091200922009320094200952009620097200982009920100201012010220103201042010520106201072010820109201102011120112201132011420115201162011720118201192012020121201222012320124201252012620127201282012920130201312013220133201342013520136201372013820139201402014120142201432014420145201462014720148201492015020151201522015320154201552015620157201582015920160201612016220163201642016520166201672016820169201702017120172201732017420175201762017720178201792018020181201822018320184201852018620187201882018920190201912019220193201942019520196201972019820199202002020120202202032020420205202062020720208202092021020211202122021320214202152021620217202182021920220202212022220223202242022520226202272022820229202302023120232202332023420235202362023720238202392024020241202422024320244202452024620247202482024920250202512025220253202542025520256202572025820259202602026120262202632026420265202662026720268202692027020271202722027320274202752027620277202782027920280202812028220283202842028520286202872028820289202902029120292202932029420295202962029720298202992030020301203022030320304203052030620307203082030920310203112031220313203142031520316203172031820319203202032120322203232032420325203262032720328203292033020331203322033320334203352033620337203382033920340203412034220343203442034520346203472034820349203502035120352203532035420355203562035720358203592036020361203622036320364203652036620367203682036920370203712037220373203742037520376203772037820379203802038120382203832038420385203862038720388203892039020391203922039320394203952039620397203982039920400204012040220403204042040520406204072040820409204102041120412204132041420415204162041720418204192042020421204222042320424204252042620427204282042920430204312043220433204342043520436204372043820439204402044120442204432044420445204462044720448204492045020451204522045320454204552045620457204582045920460204612046220463204642046520466204672046820469204702047120472204732047420475204762047720478204792048020481204822048320484204852048620487204882048920490204912049220493204942049520496204972049820499205002050120502205032050420505205062050720508205092051020511205122051320514205152051620517205182051920520205212052220523205242052520526205272052820529205302053120532205332053420535205362053720538205392054020541205422054320544205452054620547205482054920550205512055220553205542055520556205572055820559205602056120562205632056420565205662056720568205692057020571205722057320574205752057620577205782057920580205812058220583205842058520586205872058820589205902059120592205932059420595205962059720598205992060020601206022060320604206052060620607206082060920610206112061220613206142061520616206172061820619206202062120622206232062420625206262062720628206292063020631206322063320634206352063620637206382063920640206412064220643206442064520646206472064820649206502065120652206532065420655206562065720658206592066020661206622066320664206652066620667206682066920670206712067220673206742067520676206772067820679206802068120682206832068420685206862068720688206892069020691206922069320694206952069620697206982069920700207012070220703207042070520706207072070820709207102071120712207132071420715207162071720718207192072020721207222072320724207252072620727207282072920730207312073220733207342073520736207372073820739207402074120742207432074420745207462074720748207492075020751207522075320754207552075620757207582075920760207612076220763207642076520766207672076820769207702077120772207732077420775207762077720778207792078020781207822078320784207852078620787207882078920790207912079220793207942079520796207972079820799208002080120802208032080420805208062080720808208092081020811208122081320814208152081620817208182081920820208212082220823208242082520826208272082820829208302083120832208332083420835208362083720838208392084020841208422084320844208452084620847208482084920850208512085220853208542085520856208572085820859208602086120862208632086420865208662086720868208692087020871208722087320874208752087620877208782087920880208812088220883208842088520886208872088820889208902089120892208932089420895208962089720898208992090020901209022090320904209052090620907209082090920910209112091220913209142091520916209172091820919209202092120922209232092420925209262092720928209292093020931209322093320934209352093620937209382093920940209412094220943209442094520946209472094820949209502095120952209532095420955209562095720958209592096020961209622096320964209652096620967209682096920970209712097220973209742097520976209772097820979209802098120982209832098420985209862098720988209892099020991209922099320994209952099620997209982099921000210012100221003210042100521006210072100821009210102101121012210132101421015210162101721018210192102021021210222102321024210252102621027210282102921030210312103221033210342103521036210372103821039210402104121042210432104421045210462104721048210492105021051210522105321054210552105621057210582105921060210612106221063210642106521066210672106821069210702107121072210732107421075210762107721078210792108021081210822108321084210852108621087210882108921090210912109221093210942109521096210972109821099211002110121102211032110421105211062110721108211092111021111211122111321114211152111621117211182111921120211212112221123211242112521126211272112821129211302113121132211332113421135211362113721138211392114021141211422114321144211452114621147211482114921150211512115221153211542115521156211572115821159211602116121162211632116421165211662116721168211692117021171211722117321174211752117621177211782117921180211812118221183211842118521186211872118821189211902119121192211932119421195211962119721198211992120021201212022120321204212052120621207212082120921210212112121221213212142121521216212172121821219212202122121222212232122421225212262122721228212292123021231212322123321234212352123621237212382123921240212412124221243212442124521246212472124821249212502125121252212532125421255212562125721258212592126021261212622126321264212652126621267212682126921270212712127221273212742127521276212772127821279212802128121282212832128421285212862128721288212892129021291212922129321294212952129621297212982129921300213012130221303213042130521306213072130821309213102131121312213132131421315213162131721318213192132021321213222132321324213252132621327213282132921330213312133221333213342133521336213372133821339213402134121342213432134421345213462134721348213492135021351213522135321354213552135621357213582135921360213612136221363213642136521366213672136821369213702137121372213732137421375213762137721378213792138021381213822138321384213852138621387213882138921390213912139221393213942139521396213972139821399214002140121402214032140421405214062140721408214092141021411214122141321414214152141621417214182141921420214212142221423214242142521426214272142821429214302143121432214332143421435214362143721438214392144021441214422144321444214452144621447214482144921450214512145221453214542145521456214572145821459214602146121462214632146421465214662146721468214692147021471214722147321474214752147621477214782147921480214812148221483214842148521486214872148821489214902149121492214932149421495214962149721498214992150021501215022150321504215052150621507215082150921510215112151221513215142151521516215172151821519215202152121522215232152421525215262152721528215292153021531215322153321534215352153621537215382153921540215412154221543215442154521546215472154821549215502155121552215532155421555215562155721558215592156021561215622156321564215652156621567215682156921570215712157221573215742157521576215772157821579215802158121582215832158421585215862158721588215892159021591215922159321594215952159621597215982159921600216012160221603216042160521606216072160821609216102161121612216132161421615216162161721618216192162021621216222162321624216252162621627216282162921630216312163221633216342163521636216372163821639216402164121642216432164421645216462164721648216492165021651216522165321654216552165621657216582165921660216612166221663216642166521666216672166821669216702167121672216732167421675216762167721678216792168021681216822168321684216852168621687216882168921690216912169221693216942169521696216972169821699217002170121702217032170421705217062170721708217092171021711217122171321714217152171621717217182171921720217212172221723217242172521726217272172821729217302173121732217332173421735217362173721738217392174021741217422174321744217452174621747217482174921750217512175221753217542175521756217572175821759217602176121762217632176421765217662176721768217692177021771217722177321774217752177621777217782177921780217812178221783217842178521786217872178821789217902179121792217932179421795217962179721798217992180021801218022180321804218052180621807218082180921810218112181221813218142181521816218172181821819218202182121822218232182421825218262182721828218292183021831218322183321834218352183621837218382183921840218412184221843218442184521846218472184821849218502185121852218532185421855218562185721858218592186021861218622186321864218652186621867218682186921870218712187221873218742187521876218772187821879218802188121882218832188421885218862188721888218892189021891218922189321894218952189621897218982189921900219012190221903219042190521906219072190821909219102191121912219132191421915219162191721918219192192021921219222192321924219252192621927219282192921930219312193221933219342193521936219372193821939219402194121942219432194421945219462194721948219492195021951219522195321954219552195621957219582195921960219612196221963219642196521966219672196821969219702197121972219732197421975219762197721978219792198021981219822198321984219852198621987219882198921990219912199221993219942199521996219972199821999220002200122002220032200422005220062200722008220092201022011220122201322014220152201622017220182201922020220212202222023220242202522026220272202822029220302203122032220332203422035220362203722038220392204022041220422204322044220452204622047220482204922050220512205222053220542205522056220572205822059220602206122062220632206422065220662206722068220692207022071220722207322074220752207622077220782207922080220812208222083220842208522086220872208822089220902209122092220932209422095220962209722098220992210022101221022210322104221052210622107221082210922110221112211222113221142211522116221172211822119221202212122122221232212422125221262212722128221292213022131221322213322134221352213622137221382213922140221412214222143221442214522146221472214822149221502215122152221532215422155221562215722158221592216022161221622216322164221652216622167221682216922170221712217222173221742217522176221772217822179221802218122182221832218422185221862218722188221892219022191221922219322194221952219622197221982219922200222012220222203222042220522206222072220822209222102221122212222132221422215222162221722218222192222022221222222222322224222252222622227222282222922230222312223222233222342223522236222372223822239222402224122242222432224422245222462224722248222492225022251222522225322254222552225622257222582225922260222612226222263222642226522266222672226822269222702227122272222732227422275222762227722278222792228022281222822228322284222852228622287222882228922290222912229222293222942229522296222972229822299223002230122302223032230422305223062230722308223092231022311223122231322314223152231622317223182231922320223212232222323223242232522326223272232822329223302233122332223332233422335223362233722338223392234022341223422234322344223452234622347223482234922350223512235222353223542235522356223572235822359223602236122362223632236422365223662236722368223692237022371223722237322374223752237622377223782237922380223812238222383223842238522386223872238822389223902239122392223932239422395223962239722398223992240022401224022240322404224052240622407224082240922410224112241222413224142241522416224172241822419224202242122422224232242422425224262242722428224292243022431224322243322434224352243622437224382243922440224412244222443224442244522446224472244822449224502245122452224532245422455224562245722458224592246022461224622246322464224652246622467224682246922470224712247222473224742247522476224772247822479224802248122482224832248422485224862248722488224892249022491224922249322494224952249622497224982249922500225012250222503225042250522506225072250822509225102251122512225132251422515225162251722518225192252022521225222252322524225252252622527225282252922530225312253222533225342253522536225372253822539225402254122542225432254422545225462254722548225492255022551225522255322554225552255622557225582255922560225612256222563225642256522566225672256822569225702257122572225732257422575225762257722578225792258022581225822258322584225852258622587225882258922590225912259222593225942259522596225972259822599226002260122602226032260422605226062260722608226092261022611226122261322614226152261622617226182261922620226212262222623226242262522626226272262822629226302263122632226332263422635226362263722638226392264022641226422264322644226452264622647226482264922650226512265222653226542265522656226572265822659226602266122662226632266422665226662266722668226692267022671226722267322674226752267622677226782267922680226812268222683226842268522686226872268822689226902269122692226932269422695226962269722698226992270022701227022270322704227052270622707227082270922710227112271222713227142271522716227172271822719227202272122722227232272422725227262272722728227292273022731227322273322734227352273622737227382273922740227412274222743227442274522746227472274822749227502275122752227532275422755227562275722758227592276022761227622276322764227652276622767227682276922770227712277222773227742277522776227772277822779227802278122782227832278422785227862278722788227892279022791227922279322794227952279622797227982279922800228012280222803228042280522806228072280822809228102281122812228132281422815228162281722818228192282022821228222282322824228252282622827228282282922830228312283222833228342283522836228372283822839228402284122842228432284422845228462284722848228492285022851228522285322854228552285622857228582285922860228612286222863228642286522866228672286822869228702287122872228732287422875228762287722878228792288022881228822288322884228852288622887228882288922890228912289222893228942289522896228972289822899229002290122902229032290422905229062290722908229092291022911229122291322914229152291622917229182291922920229212292222923229242292522926229272292822929229302293122932229332293422935229362293722938229392294022941229422294322944229452294622947229482294922950229512295222953229542295522956229572295822959229602296122962229632296422965229662296722968229692297022971229722297322974229752297622977229782297922980229812298222983229842298522986229872298822989229902299122992229932299422995229962299722998229992300023001230022300323004230052300623007230082300923010230112301223013230142301523016230172301823019230202302123022230232302423025230262302723028230292303023031230322303323034230352303623037230382303923040230412304223043230442304523046230472304823049230502305123052230532305423055230562305723058230592306023061230622306323064230652306623067230682306923070230712307223073230742307523076230772307823079230802308123082230832308423085230862308723088230892309023091230922309323094230952309623097230982309923100231012310223103231042310523106231072310823109231102311123112231132311423115231162311723118231192312023121231222312323124231252312623127231282312923130231312313223133231342313523136231372313823139231402314123142231432314423145231462314723148231492315023151231522315323154231552315623157231582315923160231612316223163231642316523166231672316823169231702317123172231732317423175231762317723178231792318023181231822318323184231852318623187231882318923190231912319223193231942319523196231972319823199232002320123202232032320423205232062320723208232092321023211232122321323214232152321623217232182321923220232212322223223232242322523226232272322823229232302323123232232332323423235232362323723238232392324023241232422324323244232452324623247232482324923250232512325223253232542325523256232572325823259232602326123262232632326423265232662326723268232692327023271232722327323274232752327623277232782327923280232812328223283232842328523286232872328823289232902329123292232932329423295232962329723298232992330023301233022330323304233052330623307233082330923310233112331223313233142331523316233172331823319233202332123322233232332423325233262332723328233292333023331233322333323334233352333623337233382333923340233412334223343233442334523346233472334823349233502335123352233532335423355233562335723358233592336023361233622336323364233652336623367233682336923370233712337223373233742337523376233772337823379233802338123382233832338423385233862338723388233892339023391233922339323394233952339623397233982339923400234012340223403234042340523406234072340823409234102341123412234132341423415234162341723418234192342023421234222342323424234252342623427234282342923430234312343223433234342343523436234372343823439234402344123442234432344423445234462344723448234492345023451234522345323454234552345623457234582345923460234612346223463234642346523466234672346823469234702347123472234732347423475234762347723478234792348023481234822348323484234852348623487234882348923490234912349223493234942349523496234972349823499235002350123502235032350423505235062350723508235092351023511235122351323514235152351623517235182351923520235212352223523235242352523526235272352823529235302353123532235332353423535235362353723538235392354023541235422354323544235452354623547235482354923550235512355223553235542355523556235572355823559235602356123562235632356423565235662356723568235692357023571235722357323574235752357623577235782357923580235812358223583235842358523586235872358823589235902359123592235932359423595235962359723598235992360023601236022360323604236052360623607236082360923610236112361223613236142361523616236172361823619236202362123622236232362423625236262362723628236292363023631236322363323634236352363623637236382363923640236412364223643236442364523646236472364823649236502365123652236532365423655236562365723658236592366023661236622366323664236652366623667236682366923670236712367223673236742367523676236772367823679236802368123682236832368423685236862368723688236892369023691236922369323694236952369623697236982369923700237012370223703237042370523706237072370823709237102371123712237132371423715237162371723718237192372023721237222372323724237252372623727237282372923730237312373223733237342373523736237372373823739237402374123742237432374423745237462374723748237492375023751237522375323754237552375623757237582375923760237612376223763237642376523766237672376823769237702377123772237732377423775237762377723778237792378023781237822378323784237852378623787237882378923790237912379223793237942379523796237972379823799238002380123802238032380423805238062380723808238092381023811238122381323814238152381623817238182381923820238212382223823238242382523826238272382823829238302383123832238332383423835238362383723838238392384023841238422384323844238452384623847238482384923850238512385223853238542385523856238572385823859238602386123862238632386423865238662386723868238692387023871238722387323874238752387623877238782387923880238812388223883238842388523886238872388823889238902389123892238932389423895238962389723898238992390023901239022390323904239052390623907239082390923910239112391223913239142391523916239172391823919239202392123922239232392423925239262392723928239292393023931239322393323934239352393623937239382393923940239412394223943239442394523946239472394823949239502395123952239532395423955239562395723958239592396023961239622396323964239652396623967239682396923970239712397223973239742397523976239772397823979239802398123982239832398423985239862398723988239892399023991239922399323994239952399623997239982399924000240012400224003240042400524006240072400824009240102401124012240132401424015240162401724018240192402024021240222402324024240252402624027240282402924030240312403224033240342403524036240372403824039240402404124042240432404424045240462404724048240492405024051240522405324054240552405624057240582405924060240612406224063240642406524066240672406824069240702407124072240732407424075240762407724078240792408024081240822408324084240852408624087240882408924090240912409224093240942409524096240972409824099241002410124102241032410424105241062410724108241092411024111241122411324114241152411624117241182411924120241212412224123241242412524126241272412824129241302413124132241332413424135241362413724138241392414024141241422414324144241452414624147241482414924150241512415224153241542415524156241572415824159241602416124162241632416424165241662416724168241692417024171241722417324174241752417624177241782417924180241812418224183241842418524186241872418824189241902419124192241932419424195241962419724198241992420024201242022420324204242052420624207242082420924210242112421224213242142421524216242172421824219242202422124222242232422424225242262422724228242292423024231242322423324234242352423624237242382423924240242412424224243242442424524246242472424824249242502425124252242532425424255242562425724258242592426024261242622426324264242652426624267242682426924270242712427224273242742427524276242772427824279242802428124282242832428424285242862428724288242892429024291242922429324294242952429624297242982429924300243012430224303243042430524306243072430824309243102431124312243132431424315243162431724318243192432024321243222432324324243252432624327243282432924330243312433224333243342433524336243372433824339243402434124342243432434424345243462434724348243492435024351243522435324354243552435624357243582435924360243612436224363243642436524366243672436824369243702437124372243732437424375243762437724378243792438024381243822438324384243852438624387243882438924390243912439224393243942439524396243972439824399244002440124402244032440424405244062440724408244092441024411244122441324414244152441624417244182441924420244212442224423244242442524426244272442824429244302443124432244332443424435244362443724438244392444024441244422444324444244452444624447244482444924450244512445224453244542445524456244572445824459244602446124462244632446424465244662446724468244692447024471244722447324474244752447624477244782447924480244812448224483244842448524486244872448824489244902449124492244932449424495244962449724498244992450024501245022450324504245052450624507245082450924510245112451224513245142451524516245172451824519245202452124522245232452424525245262452724528245292453024531245322453324534245352453624537245382453924540245412454224543245442454524546245472454824549245502455124552245532455424555245562455724558245592456024561245622456324564245652456624567245682456924570245712457224573245742457524576245772457824579245802458124582245832458424585245862458724588245892459024591245922459324594245952459624597245982459924600246012460224603246042460524606246072460824609246102461124612246132461424615246162461724618246192462024621246222462324624246252462624627246282462924630246312463224633246342463524636246372463824639246402464124642246432464424645246462464724648246492465024651246522465324654246552465624657246582465924660246612466224663246642466524666246672466824669246702467124672246732467424675246762467724678246792468024681246822468324684246852468624687246882468924690246912469224693246942469524696246972469824699247002470124702247032470424705247062470724708247092471024711247122471324714247152471624717247182471924720247212472224723247242472524726247272472824729247302473124732247332473424735247362473724738247392474024741247422474324744247452474624747247482474924750247512475224753247542475524756247572475824759247602476124762247632476424765247662476724768247692477024771247722477324774247752477624777247782477924780247812478224783247842478524786247872478824789247902479124792247932479424795247962479724798247992480024801248022480324804248052480624807248082480924810248112481224813248142481524816248172481824819248202482124822248232482424825248262482724828 |
- From f6f8ed4784936724154832ff9e4c5afe8caa63e4 Mon Sep 17 00:00:00 2001
- From: Zhao Qiang <qiang.zhao@nxp.com>
- Date: Mon, 11 Jul 2016 14:39:18 +0800
- Subject: [PATCH 17/70] fsl_qbman: add qbman driver
- The QMan and BMan are infrastructure components of dpaa, which are used
- by both software and hardware for queuing and memory allocation/deallocation.
- Signed-off-by: Roy Pledge <Roy.Pledge@freescale.com> Signed-off-by:
- Camelia Groza <camelia.groza@freescale.com> Signed-off-by: Geoff Thorpe
- <Geoff.Thorpe@freescale.com> Signed-off-by: Ahmed Mansour
- <Ahmed.Mansour@freescale.com> Signed-off-by: Alex Porosanu
- <alexandru.porosanu@nxp.com> Signed-off-by: Pan Jiafei
- <Jiafei.Pan@nxp.com> Signed-off-by: Haiying Wang
- <Haiying.wang@freescale.com>
- Signed-off-by: Xie Jianhua-B29408 <Jianhua.Xie@freescale.com>
- Signed-off-by: Zhao Qiang <qiang.zhao@nxp.com>
- ---
- arch/arm/Kconfig | 5 +
- arch/powerpc/Kconfig | 9 +-
- drivers/misc/Kconfig | 17 +
- drivers/staging/Kconfig | 2 +
- drivers/staging/Makefile | 1 +
- drivers/staging/fsl_qbman/Kconfig | 211 +
- drivers/staging/fsl_qbman/Makefile | 28 +
- drivers/staging/fsl_qbman/bman_config.c | 705 +++
- drivers/staging/fsl_qbman/bman_debugfs.c | 119 +
- drivers/staging/fsl_qbman/bman_driver.c | 574 +++
- drivers/staging/fsl_qbman/bman_high.c | 1141 +++++
- drivers/staging/fsl_qbman/bman_low.h | 559 +++
- drivers/staging/fsl_qbman/bman_private.h | 166 +
- drivers/staging/fsl_qbman/bman_test.c | 56 +
- drivers/staging/fsl_qbman/bman_test.h | 44 +
- drivers/staging/fsl_qbman/bman_test_high.c | 183 +
- drivers/staging/fsl_qbman/bman_test_thresh.c | 196 +
- drivers/staging/fsl_qbman/dpa_alloc.c | 706 +++
- drivers/staging/fsl_qbman/dpa_sys.h | 259 ++
- drivers/staging/fsl_qbman/dpa_sys_arm.h | 95 +
- drivers/staging/fsl_qbman/dpa_sys_arm64.h | 102 +
- drivers/staging/fsl_qbman/dpa_sys_ppc32.h | 70 +
- drivers/staging/fsl_qbman/dpa_sys_ppc64.h | 79 +
- drivers/staging/fsl_qbman/fsl_usdpaa.c | 1982 ++++++++
- drivers/staging/fsl_qbman/fsl_usdpaa_irq.c | 289 ++
- drivers/staging/fsl_qbman/qbman_driver.c | 88 +
- drivers/staging/fsl_qbman/qman_config.c | 1199 +++++
- drivers/staging/fsl_qbman/qman_debugfs.c | 1594 +++++++
- drivers/staging/fsl_qbman/qman_driver.c | 980 ++++
- drivers/staging/fsl_qbman/qman_high.c | 5568 +++++++++++++++++++++++
- drivers/staging/fsl_qbman/qman_low.h | 1407 ++++++
- drivers/staging/fsl_qbman/qman_private.h | 398 ++
- drivers/staging/fsl_qbman/qman_test.c | 57 +
- drivers/staging/fsl_qbman/qman_test.h | 45 +
- drivers/staging/fsl_qbman/qman_test_high.c | 216 +
- drivers/staging/fsl_qbman/qman_test_hotpotato.c | 499 ++
- drivers/staging/fsl_qbman/qman_utility.c | 129 +
- include/linux/fsl_bman.h | 532 +++
- include/linux/fsl_qman.h | 3889 ++++++++++++++++
- include/linux/fsl_usdpaa.h | 372 ++
- 40 files changed, 24569 insertions(+), 2 deletions(-)
- create mode 100644 drivers/staging/fsl_qbman/Kconfig
- create mode 100644 drivers/staging/fsl_qbman/Makefile
- create mode 100644 drivers/staging/fsl_qbman/bman_config.c
- create mode 100644 drivers/staging/fsl_qbman/bman_debugfs.c
- create mode 100644 drivers/staging/fsl_qbman/bman_driver.c
- create mode 100644 drivers/staging/fsl_qbman/bman_high.c
- create mode 100644 drivers/staging/fsl_qbman/bman_low.h
- create mode 100644 drivers/staging/fsl_qbman/bman_private.h
- create mode 100644 drivers/staging/fsl_qbman/bman_test.c
- create mode 100644 drivers/staging/fsl_qbman/bman_test.h
- create mode 100644 drivers/staging/fsl_qbman/bman_test_high.c
- create mode 100644 drivers/staging/fsl_qbman/bman_test_thresh.c
- create mode 100644 drivers/staging/fsl_qbman/dpa_alloc.c
- create mode 100644 drivers/staging/fsl_qbman/dpa_sys.h
- create mode 100644 drivers/staging/fsl_qbman/dpa_sys_arm.h
- create mode 100644 drivers/staging/fsl_qbman/dpa_sys_arm64.h
- create mode 100644 drivers/staging/fsl_qbman/dpa_sys_ppc32.h
- create mode 100644 drivers/staging/fsl_qbman/dpa_sys_ppc64.h
- create mode 100644 drivers/staging/fsl_qbman/fsl_usdpaa.c
- create mode 100644 drivers/staging/fsl_qbman/fsl_usdpaa_irq.c
- create mode 100644 drivers/staging/fsl_qbman/qbman_driver.c
- create mode 100644 drivers/staging/fsl_qbman/qman_config.c
- create mode 100644 drivers/staging/fsl_qbman/qman_debugfs.c
- create mode 100644 drivers/staging/fsl_qbman/qman_driver.c
- create mode 100644 drivers/staging/fsl_qbman/qman_high.c
- create mode 100644 drivers/staging/fsl_qbman/qman_low.h
- create mode 100644 drivers/staging/fsl_qbman/qman_private.h
- create mode 100644 drivers/staging/fsl_qbman/qman_test.c
- create mode 100644 drivers/staging/fsl_qbman/qman_test.h
- create mode 100644 drivers/staging/fsl_qbman/qman_test_high.c
- create mode 100644 drivers/staging/fsl_qbman/qman_test_hotpotato.c
- create mode 100644 drivers/staging/fsl_qbman/qman_utility.c
- create mode 100644 include/linux/fsl_bman.h
- create mode 100644 include/linux/fsl_qman.h
- create mode 100644 include/linux/fsl_usdpaa.h
- --- a/arch/arm/Kconfig
- +++ b/arch/arm/Kconfig
- @@ -1250,6 +1250,11 @@ source "arch/arm/common/Kconfig"
-
- menu "Bus support"
-
- +config HAS_FSL_QBMAN
- + bool "Datapath Acceleration Queue and Buffer management"
- + help
- + Datapath Acceleration Queue and Buffer management
- +
- config ISA
- bool
- help
- --- a/arch/powerpc/Kconfig
- +++ b/arch/powerpc/Kconfig
- @@ -792,6 +792,11 @@ config FSL_GTM
- help
- Freescale General-purpose Timers support
-
- +config HAS_FSL_QBMAN
- + bool "Datapath Acceleration Queue and Buffer management"
- + help
- + Datapath Acceleration Queue and Buffer management
- +
- # Yes MCA RS/6000s exist but Linux-PPC does not currently support any
- config MCA
- bool
- @@ -924,14 +929,14 @@ config DYNAMIC_MEMSTART
- select NONSTATIC_KERNEL
- help
- This option enables the kernel to be loaded at any page aligned
- - physical address. The kernel creates a mapping from KERNELBASE to
- + physical address. The kernel creates a mapping from KERNELBASE to
- the address where the kernel is loaded. The page size here implies
- the TLB page size of the mapping for kernel on the particular platform.
- Please refer to the init code for finding the TLB page size.
-
- DYNAMIC_MEMSTART is an easy way of implementing pseudo-RELOCATABLE
- kernel image, where the only restriction is the page aligned kernel
- - load address. When this option is enabled, the compile time physical
- + load address. When this option is enabled, the compile time physical
- address CONFIG_PHYSICAL_START is ignored.
-
- This option is overridden by CONFIG_RELOCATABLE
- --- a/drivers/misc/Kconfig
- +++ b/drivers/misc/Kconfig
- @@ -236,6 +236,23 @@ config SGI_XP
- this feature will allow for direct communication between SSIs
- based on a network adapter and DMA messaging.
-
- +config FSL_USDPAA
- + bool "Freescale USDPAA process driver"
- + depends on FSL_DPA
- + default y
- + help
- + This driver provides user-space access to kernel-managed
- + resource interfaces for USDPAA applications, on the assumption
- + that each process will open this device once. Specifically, this
- + device exposes functionality that would be awkward if exposed
- + via the portal devices - ie. this device exposes functionality
- + that is inherently process-wide rather than portal-specific.
- + This device is necessary for obtaining access to DMA memory and
- + for allocation of Qman and Bman resources. In short, if you wish
- + to use USDPAA applications, you need this.
- +
- + If unsure, say Y.
- +
- config CS5535_MFGPT
- tristate "CS5535/CS5536 Geode Multi-Function General Purpose Timer (MFGPT) support"
- depends on MFD_CS5535
- --- a/drivers/staging/Kconfig
- +++ b/drivers/staging/Kconfig
- @@ -106,6 +106,8 @@ source "drivers/staging/fbtft/Kconfig"
-
- source "drivers/staging/fsl-mc/Kconfig"
-
- +source "drivers/staging/fsl_qbman/Kconfig"
- +
- source "drivers/staging/wilc1000/Kconfig"
-
- source "drivers/staging/most/Kconfig"
- --- a/drivers/staging/Makefile
- +++ b/drivers/staging/Makefile
- @@ -45,5 +45,6 @@ obj-$(CONFIG_UNISYSSPAR) += unisys/
- obj-$(CONFIG_COMMON_CLK_XLNX_CLKWZRD) += clocking-wizard/
- obj-$(CONFIG_FB_TFT) += fbtft/
- obj-$(CONFIG_FSL_MC_BUS) += fsl-mc/
- +obj-$(CONFIG_FSL_DPA) += fsl_qbman/
- obj-$(CONFIG_WILC1000) += wilc1000/
- obj-$(CONFIG_MOST) += most/
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/Kconfig
- @@ -0,0 +1,211 @@
- +config FSL_DPA
- + bool "Freescale Datapath Queue and Buffer management"
- + depends on HAS_FSL_QBMAN
- + default y
- + select FSL_QMAN_FQ_LOOKUP if PPC64
- + select FSL_QMAN_FQ_LOOKUP if ARM64
- +
- +
- +menu "Freescale Datapath QMan/BMan options"
- + depends on FSL_DPA
- +
- +config FSL_DPA_CHECKING
- + bool "additional driver checking"
- + default n
- + ---help---
- + Compiles in additional checks to sanity-check the drivers and any
- + use of it by other code. Not recommended for performance.
- +
- +config FSL_DPA_CAN_WAIT
- + bool
- + default y
- +
- +config FSL_DPA_CAN_WAIT_SYNC
- + bool
- + default y
- +
- +config FSL_DPA_PIRQ_FAST
- + bool
- + default y
- +
- +config FSL_DPA_PIRQ_SLOW
- + bool
- + default y
- +
- +config FSL_DPA_PORTAL_SHARE
- + bool
- + default y
- +
- +config FSL_BMAN
- + bool "Freescale Buffer Manager (BMan) support"
- + default y
- +
- +if FSL_BMAN
- +
- +config FSL_BMAN_CONFIG
- + bool "BMan device management"
- + default y
- + ---help---
- + If this linux image is running natively, you need this option. If this
- + linux image is running as a guest OS under the hypervisor, only one
- + guest OS ("the control plane") needs this option.
- +
- +config FSL_BMAN_TEST
- + tristate "BMan self-tests"
- + default n
- + ---help---
- + This option compiles self-test code for BMan.
- +
- +config FSL_BMAN_TEST_HIGH
- + bool "BMan high-level self-test"
- + depends on FSL_BMAN_TEST
- + default y
- + ---help---
- + This requires the presence of cpu-affine portals, and performs
- + high-level API testing with them (whichever portal(s) are affine to
- + the cpu(s) the test executes on).
- +
- +config FSL_BMAN_TEST_THRESH
- + bool "BMan threshold test"
- + depends on FSL_BMAN_TEST
- + default y
- + ---help---
- + Multi-threaded (SMP) test of BMan pool depletion. A pool is seeded
- + before multiple threads (one per cpu) create pool objects to track
- + depletion state changes. The pool is then drained to empty by a
- + "drainer" thread, and the other threads that they observe exactly
- + the depletion state changes that are expected.
- +
- +config FSL_BMAN_DEBUGFS
- + tristate "BMan debugfs interface"
- + depends on DEBUG_FS
- + default y
- + ---help---
- + This option compiles debugfs code for BMan.
- +
- +endif # FSL_BMAN
- +
- +config FSL_QMAN
- + bool "Freescale Queue Manager (QMan) support"
- + default y
- +
- +if FSL_QMAN
- +
- +config FSL_QMAN_POLL_LIMIT
- + int
- + default 32
- +
- +config FSL_QMAN_CONFIG
- + bool "QMan device management"
- + default y
- + ---help---
- + If this linux image is running natively, you need this option. If this
- + linux image is running as a guest OS under the hypervisor, only one
- + guest OS ("the control plane") needs this option.
- +
- +config FSL_QMAN_TEST
- + tristate "QMan self-tests"
- + default n
- + ---help---
- + This option compiles self-test code for QMan.
- +
- +config FSL_QMAN_TEST_STASH_POTATO
- + bool "QMan 'hot potato' data-stashing self-test"
- + depends on FSL_QMAN_TEST
- + default y
- + ---help---
- + This performs a "hot potato" style test enqueuing/dequeuing a frame
- + across a series of FQs scheduled to different portals (and cpus), with
- + DQRR, data and context stashing always on.
- +
- +config FSL_QMAN_TEST_HIGH
- + bool "QMan high-level self-test"
- + depends on FSL_QMAN_TEST
- + default y
- + ---help---
- + This requires the presence of cpu-affine portals, and performs
- + high-level API testing with them (whichever portal(s) are affine to
- + the cpu(s) the test executes on).
- +
- +config FSL_QMAN_DEBUGFS
- + tristate "QMan debugfs interface"
- + depends on DEBUG_FS
- + default y
- + ---help---
- + This option compiles debugfs code for QMan.
- +
- +# H/w settings that can be hard-coded for now.
- +config FSL_QMAN_FQD_SZ
- + int "size of Frame Queue Descriptor region"
- + default 10
- + ---help---
- + This is the size of the FQD region defined as: PAGE_SIZE * (2^value)
- + ex: 10 => PAGE_SIZE * (2^10)
- + Note: Default device-trees now require minimum Kconfig setting of 10.
- +
- +config FSL_QMAN_PFDR_SZ
- + int "size of the PFDR pool"
- + default 13
- + ---help---
- + This is the size of the PFDR pool defined as: PAGE_SIZE * (2^value)
- + ex: 13 => PAGE_SIZE * (2^13)
- +
- +# Corenet initiator settings. Stash request queues are 4-deep to match cores'
- +# ability to snart. Stash priority is 3, other priorities are 2.
- +config FSL_QMAN_CI_SCHED_CFG_SRCCIV
- + int
- + depends on FSL_QMAN_CONFIG
- + default 4
- +config FSL_QMAN_CI_SCHED_CFG_SRQ_W
- + int
- + depends on FSL_QMAN_CONFIG
- + default 3
- +config FSL_QMAN_CI_SCHED_CFG_RW_W
- + int
- + depends on FSL_QMAN_CONFIG
- + default 2
- +config FSL_QMAN_CI_SCHED_CFG_BMAN_W
- + int
- + depends on FSL_QMAN_CONFIG
- + default 2
- +
- +# portal interrupt settings
- +config FSL_QMAN_PIRQ_DQRR_ITHRESH
- + int
- + default 12
- +config FSL_QMAN_PIRQ_MR_ITHRESH
- + int
- + default 4
- +config FSL_QMAN_PIRQ_IPERIOD
- + int
- + default 100
- +
- +# 64 bit kernel support
- +config FSL_QMAN_FQ_LOOKUP
- + bool
- + default n
- +
- +config QMAN_CEETM_UPDATE_PERIOD
- + int "Token update period for shaping, in nanoseconds"
- + default 1000
- + ---help---
- + Traffic shaping works by performing token calculations (using
- + credits) on shaper instances periodically. This update period
- + sets the granularity for how often those token rate credit
- + updates are performed, and thus determines the accuracy and
- + range of traffic rates that can be configured by users. The
- + reference manual recommends a 1 microsecond period as providing
- + a good balance between granularity and range.
- +
- + Unless you know what you are doing, leave this value at its default.
- +
- +config FSL_QMAN_INIT_TIMEOUT
- + int "timeout for qman init stage, in seconds"
- + default 10
- + ---help---
- + The timeout setting to quit the initialization loop for non-control
- + partition in case the control partition fails to boot-up.
- +
- +endif # FSL_QMAN
- +
- +endmenu
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/Makefile
- @@ -0,0 +1,28 @@
- +subdir-ccflags-y := -Werror
- +
- +# Common
- +obj-$(CONFIG_FSL_DPA) += dpa_alloc.o
- +obj-$(CONFIG_HAS_FSL_QBMAN) += qbman_driver.o
- +
- +# Bman
- +obj-$(CONFIG_FSL_BMAN) += bman_high.o
- +obj-$(CONFIG_FSL_BMAN_CONFIG) += bman_config.o bman_driver.o
- +obj-$(CONFIG_FSL_BMAN_TEST) += bman_tester.o
- +obj-$(CONFIG_FSL_BMAN_DEBUGFS) += bman_debugfs_interface.o
- +bman_tester-y = bman_test.o
- +bman_tester-$(CONFIG_FSL_BMAN_TEST_HIGH) += bman_test_high.o
- +bman_tester-$(CONFIG_FSL_BMAN_TEST_THRESH) += bman_test_thresh.o
- +bman_debugfs_interface-y = bman_debugfs.o
- +
- +# Qman
- +obj-$(CONFIG_FSL_QMAN) += qman_high.o qman_utility.o
- +obj-$(CONFIG_FSL_QMAN_CONFIG) += qman_config.o qman_driver.o
- +obj-$(CONFIG_FSL_QMAN_TEST) += qman_tester.o
- +qman_tester-y = qman_test.o
- +qman_tester-$(CONFIG_FSL_QMAN_TEST_STASH_POTATO) += qman_test_hotpotato.o
- +qman_tester-$(CONFIG_FSL_QMAN_TEST_HIGH) += qman_test_high.o
- +obj-$(CONFIG_FSL_QMAN_DEBUGFS) += qman_debugfs_interface.o
- +qman_debugfs_interface-y = qman_debugfs.o
- +
- +# USDPAA
- +obj-$(CONFIG_FSL_USDPAA) += fsl_usdpaa.o fsl_usdpaa_irq.o
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/bman_config.c
- @@ -0,0 +1,705 @@
- +/* Copyright (c) 2009-2012 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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/cacheflush.h>
- +#include "bman_private.h"
- +#include <linux/of_reserved_mem.h>
- +
- +/* Last updated for v00.79 of the BG */
- +
- +struct bman;
- +
- +/* Register offsets */
- +#define REG_POOL_SWDET(n) (0x0000 + ((n) * 0x04))
- +#define REG_POOL_HWDET(n) (0x0100 + ((n) * 0x04))
- +#define REG_POOL_SWDXT(n) (0x0200 + ((n) * 0x04))
- +#define REG_POOL_HWDXT(n) (0x0300 + ((n) * 0x04))
- +#define REG_POOL_CONTENT(n) (0x0600 + ((n) * 0x04))
- +#define REG_FBPR_FPC 0x0800
- +#define REG_STATE_IDLE 0x960
- +#define REG_STATE_STOP 0x964
- +#define REG_ECSR 0x0a00
- +#define REG_ECIR 0x0a04
- +#define REG_EADR 0x0a08
- +#define REG_EDATA(n) (0x0a10 + ((n) * 0x04))
- +#define REG_SBEC(n) (0x0a80 + ((n) * 0x04))
- +#define REG_IP_REV_1 0x0bf8
- +#define REG_IP_REV_2 0x0bfc
- +#define REG_FBPR_BARE 0x0c00
- +#define REG_FBPR_BAR 0x0c04
- +#define REG_FBPR_AR 0x0c10
- +#define REG_SRCIDR 0x0d04
- +#define REG_LIODNR 0x0d08
- +#define REG_ERR_ISR 0x0e00 /* + "enum bm_isr_reg" */
- +
- +/* Used by all error interrupt registers except 'inhibit' */
- +#define BM_EIRQ_IVCI 0x00000010 /* Invalid Command Verb */
- +#define BM_EIRQ_FLWI 0x00000008 /* FBPR Low Watermark */
- +#define BM_EIRQ_MBEI 0x00000004 /* Multi-bit ECC Error */
- +#define BM_EIRQ_SBEI 0x00000002 /* Single-bit ECC Error */
- +#define BM_EIRQ_BSCN 0x00000001 /* pool State Change Notification */
- +
- +/* BMAN_ECIR valid error bit */
- +#define PORTAL_ECSR_ERR (BM_EIRQ_IVCI)
- +
- +union bman_ecir {
- + u32 ecir_raw;
- + struct {
- + u32 __reserved1:4;
- + u32 portal_num:4;
- + u32 __reserved2:12;
- + u32 numb:4;
- + u32 __reserved3:2;
- + u32 pid:6;
- + } __packed info;
- +};
- +
- +union bman_eadr {
- + u32 eadr_raw;
- + struct {
- + u32 __reserved1:5;
- + u32 memid:3;
- + u32 __reserved2:14;
- + u32 eadr:10;
- + } __packed info;
- +};
- +
- +struct bman_hwerr_txt {
- + u32 mask;
- + const char *txt;
- +};
- +
- +#define BMAN_HWE_TXT(a, b) { .mask = BM_EIRQ_##a, .txt = b }
- +
- +static const struct bman_hwerr_txt bman_hwerr_txts[] = {
- + BMAN_HWE_TXT(IVCI, "Invalid Command Verb"),
- + BMAN_HWE_TXT(FLWI, "FBPR Low Watermark"),
- + BMAN_HWE_TXT(MBEI, "Multi-bit ECC Error"),
- + BMAN_HWE_TXT(SBEI, "Single-bit ECC Error"),
- + BMAN_HWE_TXT(BSCN, "Pool State Change Notification"),
- +};
- +#define BMAN_HWE_COUNT (sizeof(bman_hwerr_txts)/sizeof(struct bman_hwerr_txt))
- +
- +struct bman_error_info_mdata {
- + u16 addr_mask;
- + u16 bits;
- + const char *txt;
- +};
- +
- +#define BMAN_ERR_MDATA(a, b, c) { .addr_mask = a, .bits = b, .txt = c}
- +static const struct bman_error_info_mdata error_mdata[] = {
- + BMAN_ERR_MDATA(0x03FF, 192, "Stockpile memory"),
- + BMAN_ERR_MDATA(0x00FF, 256, "SW portal ring memory port 1"),
- + BMAN_ERR_MDATA(0x00FF, 256, "SW portal ring memory port 2"),
- +};
- +#define BMAN_ERR_MDATA_COUNT \
- + (sizeof(error_mdata)/sizeof(struct bman_error_info_mdata))
- +
- +/* Add this in Kconfig */
- +#define BMAN_ERRS_TO_UNENABLE (BM_EIRQ_FLWI)
- +
- +/**
- + * bm_err_isr_<reg>_<verb> - Manipulate global interrupt registers
- + * @v: for accessors that write values, this is the 32-bit value
- + *
- + * Manipulates BMAN_ERR_ISR, BMAN_ERR_IER, BMAN_ERR_ISDR, BMAN_ERR_IIR. All
- + * manipulations except bm_err_isr_[un]inhibit() use 32-bit masks composed of
- + * the BM_EIRQ_*** definitions. Note that "bm_err_isr_enable_write" means
- + * "write the enable register" rather than "enable the write register"!
- + */
- +#define bm_err_isr_status_read(bm) \
- + __bm_err_isr_read(bm, bm_isr_status)
- +#define bm_err_isr_status_clear(bm, m) \
- + __bm_err_isr_write(bm, bm_isr_status, m)
- +#define bm_err_isr_enable_read(bm) \
- + __bm_err_isr_read(bm, bm_isr_enable)
- +#define bm_err_isr_enable_write(bm, v) \
- + __bm_err_isr_write(bm, bm_isr_enable, v)
- +#define bm_err_isr_disable_read(bm) \
- + __bm_err_isr_read(bm, bm_isr_disable)
- +#define bm_err_isr_disable_write(bm, v) \
- + __bm_err_isr_write(bm, bm_isr_disable, v)
- +#define bm_err_isr_inhibit(bm) \
- + __bm_err_isr_write(bm, bm_isr_inhibit, 1)
- +#define bm_err_isr_uninhibit(bm) \
- + __bm_err_isr_write(bm, bm_isr_inhibit, 0)
- +
- +/*
- + * TODO: unimplemented registers
- + *
- + * BMAN_POOLk_SDCNT, BMAN_POOLk_HDCNT, BMAN_FULT,
- + * BMAN_VLDPL, BMAN_EECC, BMAN_SBET, BMAN_EINJ
- + */
- +
- +/* Encapsulate "struct bman *" as a cast of the register space address. */
- +
- +static struct bman *bm_create(void *regs)
- +{
- + return (struct bman *)regs;
- +}
- +
- +static inline u32 __bm_in(struct bman *bm, u32 offset)
- +{
- + return in_be32((void *)bm + offset);
- +}
- +static inline void __bm_out(struct bman *bm, u32 offset, u32 val)
- +{
- + out_be32((void *)bm + offset, val);
- +}
- +#define bm_in(reg) __bm_in(bm, REG_##reg)
- +#define bm_out(reg, val) __bm_out(bm, REG_##reg, val)
- +
- +static u32 __bm_err_isr_read(struct bman *bm, enum bm_isr_reg n)
- +{
- + return __bm_in(bm, REG_ERR_ISR + (n << 2));
- +}
- +
- +static void __bm_err_isr_write(struct bman *bm, enum bm_isr_reg n, u32 val)
- +{
- + __bm_out(bm, REG_ERR_ISR + (n << 2), val);
- +}
- +
- +static void bm_get_version(struct bman *bm, u16 *id, u8 *major, u8 *minor)
- +{
- + u32 v = bm_in(IP_REV_1);
- + *id = (v >> 16);
- + *major = (v >> 8) & 0xff;
- + *minor = v & 0xff;
- +}
- +
- +static u32 __generate_thresh(u32 val, int roundup)
- +{
- + u32 e = 0; /* co-efficient, exponent */
- + int oddbit = 0;
- + while (val > 0xff) {
- + oddbit = val & 1;
- + val >>= 1;
- + e++;
- + if (roundup && oddbit)
- + val++;
- + }
- + DPA_ASSERT(e < 0x10);
- + return val | (e << 8);
- +}
- +
- +static void bm_set_pool(struct bman *bm, u8 pool, u32 swdet, u32 swdxt,
- + u32 hwdet, u32 hwdxt)
- +{
- + DPA_ASSERT(pool < bman_pool_max);
- + bm_out(POOL_SWDET(pool), __generate_thresh(swdet, 0));
- + bm_out(POOL_SWDXT(pool), __generate_thresh(swdxt, 1));
- + bm_out(POOL_HWDET(pool), __generate_thresh(hwdet, 0));
- + bm_out(POOL_HWDXT(pool), __generate_thresh(hwdxt, 1));
- +}
- +
- +static void bm_set_memory(struct bman *bm, u64 ba, int prio, u32 size)
- +{
- + u32 exp = ilog2(size);
- + /* choke if size isn't within range */
- + DPA_ASSERT((size >= 4096) && (size <= 1073741824) &&
- + is_power_of_2(size));
- + /* choke if '[e]ba' has lower-alignment than 'size' */
- + DPA_ASSERT(!(ba & (size - 1)));
- + bm_out(FBPR_BARE, upper_32_bits(ba));
- + bm_out(FBPR_BAR, lower_32_bits(ba));
- + bm_out(FBPR_AR, (prio ? 0x40000000 : 0) | (exp - 1));
- +}
- +
- +/*****************/
- +/* Config driver */
- +/*****************/
- +
- +/* TODO: Kconfig these? */
- +#define DEFAULT_FBPR_SZ (PAGE_SIZE << 12)
- +
- +/* We support only one of these. */
- +static struct bman *bm;
- +static struct device_node *bm_node;
- +
- +/* And this state belongs to 'bm'. It is set during fsl_bman_init(), but used
- + * during bman_init_ccsr(). */
- +static dma_addr_t fbpr_a;
- +static size_t fbpr_sz = DEFAULT_FBPR_SZ;
- +
- +static int bman_fbpr(struct reserved_mem *rmem)
- +{
- + fbpr_a = rmem->base;
- + fbpr_sz = rmem->size;
- +
- + WARN_ON(!(fbpr_a && fbpr_sz));
- +
- + return 0;
- +}
- +RESERVEDMEM_OF_DECLARE(bman_fbpr, "fsl,bman-fbpr", bman_fbpr);
- +
- +static int __init fsl_bman_init(struct device_node *node)
- +{
- + struct resource res;
- + u32 __iomem *regs;
- + const char *s;
- + int ret, standby = 0;
- + u16 id;
- + u8 major, minor;
- +
- + ret = of_address_to_resource(node, 0, &res);
- + if (ret) {
- + pr_err("Can't get %s property 'reg'\n",
- + node->full_name);
- + return ret;
- + }
- + s = of_get_property(node, "fsl,hv-claimable", &ret);
- + if (s && !strcmp(s, "standby"))
- + standby = 1;
- + /* Global configuration */
- + regs = ioremap(res.start, res.end - res.start + 1);
- + bm = bm_create(regs);
- + BUG_ON(!bm);
- + bm_node = node;
- + bm_get_version(bm, &id, &major, &minor);
- + pr_info("Bman ver:%04x,%02x,%02x\n", id, major, minor);
- + if ((major == 1) && (minor == 0)) {
- + bman_ip_rev = BMAN_REV10;
- + bman_pool_max = 64;
- + } else if ((major == 2) && (minor == 0)) {
- + bman_ip_rev = BMAN_REV20;
- + bman_pool_max = 8;
- + } else if ((major == 2) && (minor == 1)) {
- + bman_ip_rev = BMAN_REV21;
- + bman_pool_max = 64;
- + } else {
- + pr_warn("unknown Bman version, default to rev1.0\n");
- + }
- +
- + if (standby) {
- + pr_info(" -> in standby mode\n");
- + return 0;
- + }
- + return 0;
- +}
- +
- +int bman_have_ccsr(void)
- +{
- + return bm ? 1 : 0;
- +}
- +
- +int bm_pool_set(u32 bpid, const u32 *thresholds)
- +{
- + if (!bm)
- + return -ENODEV;
- + bm_set_pool(bm, bpid, thresholds[0],
- + thresholds[1], thresholds[2],
- + thresholds[3]);
- + return 0;
- +}
- +EXPORT_SYMBOL(bm_pool_set);
- +
- +__init int bman_init_early(void)
- +{
- + struct device_node *dn;
- + int ret;
- +
- + for_each_compatible_node(dn, NULL, "fsl,bman") {
- + if (bm)
- + pr_err("%s: only one 'fsl,bman' allowed\n",
- + dn->full_name);
- + else {
- + if (!of_device_is_available(dn))
- + continue;
- +
- + ret = fsl_bman_init(dn);
- + BUG_ON(ret);
- + }
- + }
- + return 0;
- +}
- +postcore_initcall_sync(bman_init_early);
- +
- +
- +static void log_edata_bits(u32 bit_count)
- +{
- + u32 i, j, mask = 0xffffffff;
- +
- + pr_warn("Bman ErrInt, EDATA:\n");
- + i = bit_count/32;
- + if (bit_count%32) {
- + i++;
- + mask = ~(mask << bit_count%32);
- + }
- + j = 16-i;
- + pr_warn(" 0x%08x\n", bm_in(EDATA(j)) & mask);
- + j++;
- + for (; j < 16; j++)
- + pr_warn(" 0x%08x\n", bm_in(EDATA(j)));
- +}
- +
- +static void log_additional_error_info(u32 isr_val, u32 ecsr_val)
- +{
- + union bman_ecir ecir_val;
- + union bman_eadr eadr_val;
- +
- + ecir_val.ecir_raw = bm_in(ECIR);
- + /* Is portal info valid */
- + if (ecsr_val & PORTAL_ECSR_ERR) {
- + pr_warn("Bman ErrInt: SWP id %d, numb %d, pid %d\n",
- + ecir_val.info.portal_num, ecir_val.info.numb,
- + ecir_val.info.pid);
- + }
- + if (ecsr_val & (BM_EIRQ_SBEI|BM_EIRQ_MBEI)) {
- + eadr_val.eadr_raw = bm_in(EADR);
- + pr_warn("Bman ErrInt: EADR Memory: %s, 0x%x\n",
- + error_mdata[eadr_val.info.memid].txt,
- + error_mdata[eadr_val.info.memid].addr_mask
- + & eadr_val.info.eadr);
- + log_edata_bits(error_mdata[eadr_val.info.memid].bits);
- + }
- +}
- +
- +/* Bman interrupt handler */
- +static irqreturn_t bman_isr(int irq, void *ptr)
- +{
- + u32 isr_val, ier_val, ecsr_val, isr_mask, i;
- +
- + ier_val = bm_err_isr_enable_read(bm);
- + isr_val = bm_err_isr_status_read(bm);
- + ecsr_val = bm_in(ECSR);
- + isr_mask = isr_val & ier_val;
- +
- + if (!isr_mask)
- + return IRQ_NONE;
- + for (i = 0; i < BMAN_HWE_COUNT; i++) {
- + if (bman_hwerr_txts[i].mask & isr_mask) {
- + pr_warn("Bman ErrInt: %s\n", bman_hwerr_txts[i].txt);
- + if (bman_hwerr_txts[i].mask & ecsr_val) {
- + log_additional_error_info(isr_mask, ecsr_val);
- + /* Re-arm error capture registers */
- + bm_out(ECSR, ecsr_val);
- + }
- + if (bman_hwerr_txts[i].mask & BMAN_ERRS_TO_UNENABLE) {
- + pr_devel("Bman un-enabling error 0x%x\n",
- + bman_hwerr_txts[i].mask);
- + ier_val &= ~bman_hwerr_txts[i].mask;
- + bm_err_isr_enable_write(bm, ier_val);
- + }
- + }
- + }
- + bm_err_isr_status_clear(bm, isr_val);
- + return IRQ_HANDLED;
- +}
- +
- +static int __bind_irq(void)
- +{
- + int ret, err_irq;
- +
- + err_irq = of_irq_to_resource(bm_node, 0, NULL);
- + if (err_irq == 0) {
- + pr_info("Can't get %s property '%s'\n", bm_node->full_name,
- + "interrupts");
- + return -ENODEV;
- + }
- + ret = request_irq(err_irq, bman_isr, IRQF_SHARED, "bman-err", bm_node);
- + if (ret) {
- + pr_err("request_irq() failed %d for '%s'\n", ret,
- + bm_node->full_name);
- + return -ENODEV;
- + }
- + /* Disable Buffer Pool State Change */
- + bm_err_isr_disable_write(bm, BM_EIRQ_BSCN);
- + /* Write-to-clear any stale bits, (eg. starvation being asserted prior
- + * to resource allocation during driver init). */
- + bm_err_isr_status_clear(bm, 0xffffffff);
- + /* Enable Error Interrupts */
- + bm_err_isr_enable_write(bm, 0xffffffff);
- + return 0;
- +}
- +
- +int bman_init_ccsr(struct device_node *node)
- +{
- + int ret;
- + if (!bman_have_ccsr())
- + return 0;
- + if (node != bm_node)
- + return -EINVAL;
- + /* FBPR memory */
- + bm_set_memory(bm, fbpr_a, 0, fbpr_sz);
- + pr_info("bman-fbpr addr 0x%llx size 0x%zx\n",
- + (unsigned long long)fbpr_a, fbpr_sz);
- +
- + ret = __bind_irq();
- + if (ret)
- + return ret;
- + return 0;
- +}
- +
- +u32 bm_pool_free_buffers(u32 bpid)
- +{
- + return bm_in(POOL_CONTENT(bpid));
- +}
- +
- +#ifdef CONFIG_SYSFS
- +
- +#define DRV_NAME "fsl-bman"
- +#define SBEC_MAX_ID 1
- +#define SBEC_MIN_ID 0
- +
- +static ssize_t show_fbpr_fpc(struct device *dev,
- + struct device_attribute *dev_attr, char *buf)
- +{
- + return snprintf(buf, PAGE_SIZE, "%u\n", bm_in(FBPR_FPC));
- +};
- +
- +static ssize_t show_pool_count(struct device *dev,
- + struct device_attribute *dev_attr, char *buf)
- +{
- + u32 data;
- + int i;
- +
- + if (!sscanf(dev_attr->attr.name, "%d", &i) || (i >= bman_pool_max))
- + return -EINVAL;
- + data = bm_in(POOL_CONTENT(i));
- + return snprintf(buf, PAGE_SIZE, "%d\n", data);
- +};
- +
- +static ssize_t show_err_isr(struct device *dev,
- + struct device_attribute *dev_attr, char *buf)
- +{
- + return snprintf(buf, PAGE_SIZE, "0x%08x\n", bm_in(ERR_ISR));
- +};
- +
- +static ssize_t show_sbec(struct device *dev,
- + struct device_attribute *dev_attr, char *buf)
- +{
- + int i;
- +
- + if (!sscanf(dev_attr->attr.name, "sbec_%d", &i))
- + return -EINVAL;
- + if (i < SBEC_MIN_ID || i > SBEC_MAX_ID)
- + return -EINVAL;
- + return snprintf(buf, PAGE_SIZE, "%u\n", bm_in(SBEC(i)));
- +};
- +
- +static DEVICE_ATTR(err_isr, S_IRUSR, show_err_isr, NULL);
- +static DEVICE_ATTR(fbpr_fpc, S_IRUSR, show_fbpr_fpc, NULL);
- +
- +/* Didn't use DEVICE_ATTR as 64 of this would be required.
- + * Initialize them when needed. */
- +static char *name_attrs_pool_count; /* "xx" + null-terminator */
- +static struct device_attribute *dev_attr_buffer_pool_count;
- +
- +static DEVICE_ATTR(sbec_0, S_IRUSR, show_sbec, NULL);
- +static DEVICE_ATTR(sbec_1, S_IRUSR, show_sbec, NULL);
- +
- +static struct attribute *bman_dev_attributes[] = {
- + &dev_attr_fbpr_fpc.attr,
- + &dev_attr_err_isr.attr,
- + NULL
- +};
- +
- +static struct attribute *bman_dev_ecr_attributes[] = {
- + &dev_attr_sbec_0.attr,
- + &dev_attr_sbec_1.attr,
- + NULL
- +};
- +
- +static struct attribute **bman_dev_pool_count_attributes;
- +
- +
- +/* root level */
- +static const struct attribute_group bman_dev_attr_grp = {
- + .name = NULL,
- + .attrs = bman_dev_attributes
- +};
- +static const struct attribute_group bman_dev_ecr_grp = {
- + .name = "error_capture",
- + .attrs = bman_dev_ecr_attributes
- +};
- +static struct attribute_group bman_dev_pool_countent_grp = {
- + .name = "pool_count",
- +};
- +
- +static int of_fsl_bman_remove(struct platform_device *ofdev)
- +{
- + sysfs_remove_group(&ofdev->dev.kobj, &bman_dev_attr_grp);
- + return 0;
- +};
- +
- +static int of_fsl_bman_probe(struct platform_device *ofdev)
- +{
- + int ret, i;
- +
- + ret = sysfs_create_group(&ofdev->dev.kobj, &bman_dev_attr_grp);
- + if (ret)
- + goto done;
- + ret = sysfs_create_group(&ofdev->dev.kobj, &bman_dev_ecr_grp);
- + if (ret)
- + goto del_group_0;
- +
- + name_attrs_pool_count = kmalloc(sizeof(char) * bman_pool_max * 3,
- + GFP_KERNEL);
- + if (!name_attrs_pool_count) {
- + pr_err("Can't alloc name_attrs_pool_count\n");
- + goto del_group_1;
- + }
- +
- + dev_attr_buffer_pool_count = kmalloc(sizeof(struct device_attribute) *
- + bman_pool_max, GFP_KERNEL);
- + if (!dev_attr_buffer_pool_count) {
- + pr_err("Can't alloc dev_attr-buffer_pool_count\n");
- + goto del_group_2;
- + }
- +
- + bman_dev_pool_count_attributes = kmalloc(sizeof(struct attribute *) *
- + (bman_pool_max + 1), GFP_KERNEL);
- + if (!bman_dev_pool_count_attributes) {
- + pr_err("can't alloc bman_dev_pool_count_attributes\n");
- + goto del_group_3;
- + }
- +
- + for (i = 0; i < bman_pool_max; i++) {
- + ret = scnprintf((name_attrs_pool_count + i * 3), 3, "%d", i);
- + if (!ret)
- + goto del_group_4;
- + dev_attr_buffer_pool_count[i].attr.name =
- + (name_attrs_pool_count + i * 3);
- + dev_attr_buffer_pool_count[i].attr.mode = S_IRUSR;
- + dev_attr_buffer_pool_count[i].show = show_pool_count;
- + bman_dev_pool_count_attributes[i] =
- + &dev_attr_buffer_pool_count[i].attr;
- + sysfs_attr_init(bman_dev_pool_count_attributes[i]);
- + }
- + bman_dev_pool_count_attributes[bman_pool_max] = NULL;
- +
- + bman_dev_pool_countent_grp.attrs = bman_dev_pool_count_attributes;
- +
- + ret = sysfs_create_group(&ofdev->dev.kobj, &bman_dev_pool_countent_grp);
- + if (ret)
- + goto del_group_4;
- +
- + goto done;
- +
- +del_group_4:
- + kfree(bman_dev_pool_count_attributes);
- +del_group_3:
- + kfree(dev_attr_buffer_pool_count);
- +del_group_2:
- + kfree(name_attrs_pool_count);
- +del_group_1:
- + sysfs_remove_group(&ofdev->dev.kobj, &bman_dev_ecr_grp);
- +del_group_0:
- + sysfs_remove_group(&ofdev->dev.kobj, &bman_dev_attr_grp);
- +done:
- + if (ret)
- + dev_err(&ofdev->dev,
- + "Cannot create dev attributes ret=%d\n", ret);
- + return ret;
- +};
- +
- +static struct of_device_id of_fsl_bman_ids[] = {
- + {
- + .compatible = "fsl,bman",
- + },
- + {}
- +};
- +MODULE_DEVICE_TABLE(of, of_fsl_bman_ids);
- +
- +#ifdef CONFIG_SUSPEND
- +static u32 saved_isdr;
- +
- +static int bman_pm_suspend_noirq(struct device *dev)
- +{
- + uint32_t idle_state;
- +
- + suspend_unused_bportal();
- + /* save isdr, disable all, clear isr */
- + saved_isdr = bm_err_isr_disable_read(bm);
- + bm_err_isr_disable_write(bm, 0xffffffff);
- + bm_err_isr_status_clear(bm, 0xffffffff);
- +
- + if (bman_ip_rev < BMAN_REV21) {
- +#ifdef CONFIG_PM_DEBUG
- + pr_info("Bman version doesn't have STATE_IDLE\n");
- +#endif
- + return 0;
- + }
- + idle_state = bm_in(STATE_IDLE);
- + if (!(idle_state & 0x1)) {
- + pr_err("Bman not idle 0x%x aborting\n", idle_state);
- + bm_err_isr_disable_write(bm, saved_isdr);
- + resume_unused_bportal();
- + return -EBUSY;
- + }
- +#ifdef CONFIG_PM_DEBUG
- + pr_info("Bman suspend code, IDLE_STAT = 0x%x\n", idle_state);
- +#endif
- + return 0;
- +}
- +
- +static int bman_pm_resume_noirq(struct device *dev)
- +{
- + /* restore isdr */
- + bm_err_isr_disable_write(bm, saved_isdr);
- + resume_unused_bportal();
- + return 0;
- +}
- +#else
- +#define bman_pm_suspend_noirq NULL
- +#define bman_pm_resume_noirq NULL
- +#endif
- +
- +static const struct dev_pm_ops bman_pm_ops = {
- + .suspend_noirq = bman_pm_suspend_noirq,
- + .resume_noirq = bman_pm_resume_noirq,
- +};
- +
- +static struct platform_driver of_fsl_bman_driver = {
- + .driver = {
- + .owner = THIS_MODULE,
- + .name = DRV_NAME,
- + .of_match_table = of_fsl_bman_ids,
- + .pm = &bman_pm_ops,
- + },
- + .probe = of_fsl_bman_probe,
- + .remove = of_fsl_bman_remove,
- +};
- +
- +static int bman_ctrl_init(void)
- +{
- + return platform_driver_register(&of_fsl_bman_driver);
- +}
- +
- +static void bman_ctrl_exit(void)
- +{
- + platform_driver_unregister(&of_fsl_bman_driver);
- +}
- +
- +module_init(bman_ctrl_init);
- +module_exit(bman_ctrl_exit);
- +
- +#endif /* CONFIG_SYSFS */
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/bman_debugfs.c
- @@ -0,0 +1,119 @@
- +/* Copyright 2010-2011 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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 <linux/module.h>
- +#include <linux/fsl_bman.h>
- +#include <linux/debugfs.h>
- +#include <linux/seq_file.h>
- +#include <linux/uaccess.h>
- +
- +static struct dentry *dfs_root; /* debugfs root directory */
- +
- +/*******************************************************************************
- + * Query Buffer Pool State
- + ******************************************************************************/
- +static int query_bp_state_show(struct seq_file *file, void *offset)
- +{
- + int ret;
- + struct bm_pool_state state;
- + int i, j;
- + u32 mask;
- +
- + memset(&state, 0, sizeof(struct bm_pool_state));
- + ret = bman_query_pools(&state);
- + if (ret) {
- + seq_printf(file, "Error %d\n", ret);
- + return 0;
- + }
- + seq_puts(file, "bp_id free_buffers_avail bp_depleted\n");
- + for (i = 0; i < 2; i++) {
- + mask = 0x80000000;
- + for (j = 0; j < 32; j++) {
- + seq_printf(file,
- + " %-2u %-3s %-3s\n",
- + (i*32)+j,
- + (state.as.state.__state[i] & mask) ? "no" : "yes",
- + (state.ds.state.__state[i] & mask) ? "yes" : "no");
- + mask >>= 1;
- + }
- + }
- + return 0;
- +}
- +
- +static int query_bp_state_open(struct inode *inode, struct file *file)
- +{
- + return single_open(file, query_bp_state_show, NULL);
- +}
- +
- +static const struct file_operations query_bp_state_fops = {
- + .owner = THIS_MODULE,
- + .open = query_bp_state_open,
- + .read = seq_read,
- + .release = single_release,
- +};
- +
- +static int __init bman_debugfs_module_init(void)
- +{
- + int ret = 0;
- + struct dentry *d;
- +
- + dfs_root = debugfs_create_dir("bman", NULL);
- +
- + if (dfs_root == NULL) {
- + ret = -ENOMEM;
- + pr_err("Cannot create bman debugfs dir\n");
- + goto _return;
- + }
- + d = debugfs_create_file("query_bp_state",
- + S_IRUGO,
- + dfs_root,
- + NULL,
- + &query_bp_state_fops);
- + if (d == NULL) {
- + ret = -ENOMEM;
- + pr_err("Cannot create query_bp_state\n");
- + goto _return;
- + }
- + return 0;
- +
- +_return:
- + debugfs_remove_recursive(dfs_root);
- + return ret;
- +}
- +
- +static void __exit bman_debugfs_module_exit(void)
- +{
- + debugfs_remove_recursive(dfs_root);
- +}
- +
- +
- +module_init(bman_debugfs_module_init);
- +module_exit(bman_debugfs_module_exit);
- +MODULE_LICENSE("Dual BSD/GPL");
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/bman_driver.c
- @@ -0,0 +1,574 @@
- +/* Copyright 2008-2012 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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 "bman_low.h"
- +#ifdef CONFIG_HOTPLUG_CPU
- +#include <linux/cpu.h>
- +#endif
- +/*
- + * Global variables of the max portal/pool number this bman version supported
- + */
- +u16 bman_ip_rev;
- +EXPORT_SYMBOL(bman_ip_rev);
- +u16 bman_pool_max;
- +EXPORT_SYMBOL(bman_pool_max);
- +static u16 bman_portal_max;
- +
- +/* After initialising cpus that own shared portal configs, we cache the
- + * resulting portals (ie. not just the configs) in this array. Then we
- + * initialise slave cpus that don't have their own portals, redirecting them to
- + * portals from this cache in a round-robin assignment. */
- +static struct bman_portal *shared_portals[NR_CPUS];
- +static int num_shared_portals;
- +static int shared_portals_idx;
- +static LIST_HEAD(unused_pcfgs);
- +static DEFINE_SPINLOCK(unused_pcfgs_lock);
- +static void *affine_bportals[NR_CPUS];
- +
- +static int __init fsl_bpool_init(struct device_node *node)
- +{
- + int ret;
- + u32 *thresh, *bpid = (u32 *)of_get_property(node, "fsl,bpid", &ret);
- + if (!bpid || (ret != 4)) {
- + pr_err("Can't get %s property 'fsl,bpid'\n", node->full_name);
- + return -ENODEV;
- + }
- + thresh = (u32 *)of_get_property(node, "fsl,bpool-thresholds", &ret);
- + if (thresh) {
- + if (ret != 16) {
- + pr_err("Invalid %s property '%s'\n",
- + node->full_name, "fsl,bpool-thresholds");
- + return -ENODEV;
- + }
- + }
- + if (thresh) {
- +#ifdef CONFIG_FSL_BMAN_CONFIG
- + ret = bm_pool_set(be32_to_cpu(*bpid), thresh);
- + if (ret)
- + pr_err("No CCSR node for %s property '%s'\n",
- + node->full_name, "fsl,bpool-thresholds");
- + return ret;
- +#else
- + pr_err("Ignoring %s property '%s', no CCSR support\n",
- + node->full_name, "fsl,bpool-thresholds");
- +#endif
- + }
- + return 0;
- +}
- +
- +static int __init fsl_bpid_range_init(struct device_node *node)
- +{
- + int ret;
- + u32 *range = (u32 *)of_get_property(node, "fsl,bpid-range", &ret);
- + if (!range) {
- + pr_err("No 'fsl,bpid-range' property in node %s\n",
- + node->full_name);
- + return -EINVAL;
- + }
- + if (ret != 8) {
- + pr_err("'fsl,bpid-range' is not a 2-cell range in node %s\n",
- + node->full_name);
- + return -EINVAL;
- + }
- + bman_seed_bpid_range(be32_to_cpu(range[0]), be32_to_cpu(range[1]));
- + pr_info("Bman: BPID allocator includes range %d:%d\n",
- + be32_to_cpu(range[0]), be32_to_cpu(range[1]));
- + return 0;
- +}
- +
- +static struct bm_portal_config * __init parse_pcfg(struct device_node *node)
- +{
- + struct bm_portal_config *pcfg;
- + const u32 *index;
- + int irq, ret;
- + resource_size_t len;
- +
- + pcfg = kmalloc(sizeof(*pcfg), GFP_KERNEL);
- + if (!pcfg) {
- + pr_err("can't allocate portal config");
- + return NULL;
- + }
- +
- + if (of_device_is_compatible(node, "fsl,bman-portal-1.0") ||
- + of_device_is_compatible(node, "fsl,bman-portal-1.0.0")) {
- + bman_ip_rev = BMAN_REV10;
- + bman_pool_max = 64;
- + bman_portal_max = 10;
- + } else if (of_device_is_compatible(node, "fsl,bman-portal-2.0") ||
- + of_device_is_compatible(node, "fsl,bman-portal-2.0.8")) {
- + bman_ip_rev = BMAN_REV20;
- + bman_pool_max = 8;
- + bman_portal_max = 3;
- + } else if (of_device_is_compatible(node, "fsl,bman-portal-2.1.0")) {
- + bman_ip_rev = BMAN_REV21;
- + bman_pool_max = 64;
- + bman_portal_max = 50;
- + } else if (of_device_is_compatible(node, "fsl,bman-portal-2.1.1")) {
- + bman_ip_rev = BMAN_REV21;
- + bman_pool_max = 64;
- + bman_portal_max = 25;
- + } else if (of_device_is_compatible(node, "fsl,bman-portal-2.1.2")) {
- + bman_ip_rev = BMAN_REV21;
- + bman_pool_max = 64;
- + bman_portal_max = 18;
- + } else if (of_device_is_compatible(node, "fsl,bman-portal-2.1.3")) {
- + bman_ip_rev = BMAN_REV21;
- + bman_pool_max = 64;
- + bman_portal_max = 10;
- + } else {
- + pr_warn("unknown BMan version in portal node,"
- + "default to rev1.0\n");
- + bman_ip_rev = BMAN_REV10;
- + bman_pool_max = 64;
- + bman_portal_max = 10;
- + }
- +
- + ret = of_address_to_resource(node, DPA_PORTAL_CE,
- + &pcfg->addr_phys[DPA_PORTAL_CE]);
- + if (ret) {
- + pr_err("Can't get %s property 'reg::CE'\n", node->full_name);
- + goto err;
- + }
- + ret = of_address_to_resource(node, DPA_PORTAL_CI,
- + &pcfg->addr_phys[DPA_PORTAL_CI]);
- + if (ret) {
- + pr_err("Can't get %s property 'reg::CI'\n", node->full_name);
- + goto err;
- + }
- +
- + index = of_get_property(node, "cell-index", &ret);
- + if (!index || (ret != 4)) {
- + pr_err("Can't get %s property '%s'\n", node->full_name,
- + "cell-index");
- + goto err;
- + }
- + if (be32_to_cpu(*index) >= bman_portal_max) {
- + pr_err("BMan portal cell index %d out of range, max %d\n",
- + be32_to_cpu(*index), bman_portal_max);
- + goto err;
- + }
- +
- + pcfg->public_cfg.cpu = -1;
- +
- + irq = irq_of_parse_and_map(node, 0);
- + if (irq == 0) {
- + pr_err("Can't get %s property 'interrupts'\n", node->full_name);
- + goto err;
- + }
- + pcfg->public_cfg.irq = irq;
- + pcfg->public_cfg.index = be32_to_cpu(*index);
- + bman_depletion_fill(&pcfg->public_cfg.mask);
- +
- + len = resource_size(&pcfg->addr_phys[DPA_PORTAL_CE]);
- + if (len != (unsigned long)len)
- + goto err;
- +
- +#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
- + pcfg->addr_virt[DPA_PORTAL_CE] = ioremap_cache_ns(
- + pcfg->addr_phys[DPA_PORTAL_CE].start,
- + resource_size(&pcfg->addr_phys[DPA_PORTAL_CE]));
- + pcfg->addr_virt[DPA_PORTAL_CI] = ioremap(
- + pcfg->addr_phys[DPA_PORTAL_CI].start,
- + resource_size(&pcfg->addr_phys[DPA_PORTAL_CI]));
- +
- +#else
- + pcfg->addr_virt[DPA_PORTAL_CE] = ioremap_prot(
- + pcfg->addr_phys[DPA_PORTAL_CE].start,
- + (unsigned long)len,
- + 0);
- + pcfg->addr_virt[DPA_PORTAL_CI] = ioremap_prot(
- + pcfg->addr_phys[DPA_PORTAL_CI].start,
- + resource_size(&pcfg->addr_phys[DPA_PORTAL_CI]),
- + _PAGE_GUARDED | _PAGE_NO_CACHE);
- +#endif
- + /* disable bp depletion */
- + __raw_writel(0x0, pcfg->addr_virt[DPA_PORTAL_CI] + BM_REG_SCN(0));
- + __raw_writel(0x0, pcfg->addr_virt[DPA_PORTAL_CI] + BM_REG_SCN(1));
- + return pcfg;
- +err:
- + kfree(pcfg);
- + return NULL;
- +}
- +
- +static struct bm_portal_config *get_pcfg(struct list_head *list)
- +{
- + struct bm_portal_config *pcfg;
- + if (list_empty(list))
- + return NULL;
- + pcfg = list_entry(list->prev, struct bm_portal_config, list);
- + list_del(&pcfg->list);
- + return pcfg;
- +}
- +
- +static struct bm_portal_config *get_pcfg_idx(struct list_head *list,
- + uint32_t idx)
- +{
- + struct bm_portal_config *pcfg;
- + if (list_empty(list))
- + return NULL;
- + list_for_each_entry(pcfg, list, list) {
- + if (pcfg->public_cfg.index == idx) {
- + list_del(&pcfg->list);
- + return pcfg;
- + }
- + }
- + return NULL;
- +}
- +
- +struct bm_portal_config *bm_get_unused_portal(void)
- +{
- + return bm_get_unused_portal_idx(QBMAN_ANY_PORTAL_IDX);
- +}
- +
- +struct bm_portal_config *bm_get_unused_portal_idx(uint32_t idx)
- +{
- + struct bm_portal_config *ret;
- + spin_lock(&unused_pcfgs_lock);
- + if (idx == QBMAN_ANY_PORTAL_IDX)
- + ret = get_pcfg(&unused_pcfgs);
- + else
- + ret = get_pcfg_idx(&unused_pcfgs, idx);
- + spin_unlock(&unused_pcfgs_lock);
- + return ret;
- +}
- +
- +void bm_put_unused_portal(struct bm_portal_config *pcfg)
- +{
- + spin_lock(&unused_pcfgs_lock);
- + list_add(&pcfg->list, &unused_pcfgs);
- + spin_unlock(&unused_pcfgs_lock);
- +}
- +
- +static struct bman_portal *init_pcfg(struct bm_portal_config *pcfg)
- +{
- + struct bman_portal *p;
- + p = bman_create_affine_portal(pcfg);
- + if (p) {
- +#ifdef CONFIG_FSL_DPA_PIRQ_SLOW
- + bman_p_irqsource_add(p, BM_PIRQ_RCRI | BM_PIRQ_BSCN);
- +#endif
- + pr_info("Bman portal %sinitialised, cpu %d\n",
- + pcfg->public_cfg.is_shared ? "(shared) " : "",
- + pcfg->public_cfg.cpu);
- + affine_bportals[pcfg->public_cfg.cpu] = p;
- + } else
- + pr_crit("Bman portal failure on cpu %d\n",
- + pcfg->public_cfg.cpu);
- + return p;
- +}
- +
- +static void init_slave(int cpu)
- +{
- + struct bman_portal *p;
- + p = bman_create_affine_slave(shared_portals[shared_portals_idx++], cpu);
- + if (!p)
- + pr_err("Bman slave portal failure on cpu %d\n", cpu);
- + else
- + pr_info("Bman portal %sinitialised, cpu %d\n", "(slave) ", cpu);
- + if (shared_portals_idx >= num_shared_portals)
- + shared_portals_idx = 0;
- + affine_bportals[cpu] = p;
- +}
- +
- +/* Bootarg "bportals=[...]" has the same syntax as "qportals=", and so the
- + * parsing is in dpa_sys.h. The syntax is a comma-separated list of indexes
- + * and/or ranges of indexes, with each being optionally prefixed by "s" to
- + * explicitly mark it or them for sharing.
- + * Eg;
- + * bportals=s0,1-3,s4
- + * means that cpus 1,2,3 get "unshared" portals, cpus 0 and 4 get "shared"
- + * portals, and any remaining cpus share the portals that are assigned to cpus 0
- + * or 4, selected in a round-robin fashion. (In this example, cpu 5 would share
- + * cpu 0's portal, cpu 6 would share cpu4's portal, and cpu 7 would share cpu
- + * 0's portal.) */
- +static struct cpumask want_unshared __initdata; /* cpus requested without "s" */
- +static struct cpumask want_shared __initdata; /* cpus requested with "s" */
- +
- +static int __init parse_bportals(char *str)
- +{
- + return parse_portals_bootarg(str, &want_shared, &want_unshared,
- + "bportals");
- +}
- +__setup("bportals=", parse_bportals);
- +
- +static void bman_offline_cpu(unsigned int cpu)
- +{
- + struct bman_portal *p;
- + const struct bm_portal_config *pcfg;
- + p = (struct bman_portal *)affine_bportals[cpu];
- + if (p) {
- + pcfg = bman_get_bm_portal_config(p);
- + if (pcfg)
- + irq_set_affinity(pcfg->public_cfg.irq, cpumask_of(0));
- + }
- +}
- +
- +#ifdef CONFIG_HOTPLUG_CPU
- +static void bman_online_cpu(unsigned int cpu)
- +{
- + struct bman_portal *p;
- + const struct bm_portal_config *pcfg;
- + p = (struct bman_portal *)affine_bportals[cpu];
- + if (p) {
- + pcfg = bman_get_bm_portal_config(p);
- + if (pcfg)
- + irq_set_affinity(pcfg->public_cfg.irq, cpumask_of(cpu));
- + }
- +}
- +
- +static int bman_hotplug_cpu_callback(struct notifier_block *nfb,
- + unsigned long action, void *hcpu)
- +{
- + unsigned int cpu = (unsigned long)hcpu;
- +
- + switch (action) {
- + case CPU_ONLINE:
- + case CPU_ONLINE_FROZEN:
- + bman_online_cpu(cpu);
- + break;
- + case CPU_DOWN_PREPARE:
- + case CPU_DOWN_PREPARE_FROZEN:
- + bman_offline_cpu(cpu);
- + default:
- + break;
- + }
- + return NOTIFY_OK;
- +}
- +
- +static struct notifier_block bman_hotplug_cpu_notifier = {
- + .notifier_call = bman_hotplug_cpu_callback,
- +};
- +#endif /* CONFIG_HOTPLUG_CPU */
- +
- +/* Initialise the Bman driver. The meat of this function deals with portals. The
- + * following describes the flow of portal-handling, the code "steps" refer to
- + * this description;
- + * 1. Portal configs are parsed from the device-tree into 'unused_pcfgs', with
- + * ::cpu==-1. Regions and interrupts are mapped (but interrupts are not
- + * bound).
- + * 2. The "want_shared" and "want_unshared" lists (as filled by the
- + * "bportals=[...]" bootarg) are processed, allocating portals and assigning
- + * them to cpus, placing them in the relevant list and setting ::cpu as
- + * appropriate. If no "bportals" bootarg was present, the defaut is to try to
- + * assign portals to all online cpus at the time of driver initialisation.
- + * Any failure to allocate portals (when parsing the "want" lists or when
- + * using default behaviour) will be silently tolerated (the "fixup" logic in
- + * step 3 will determine what happens in this case).
- + * 3. Do fixups relative to cpu_online_mask(). If no portals are marked for
- + * sharing and sharing is required (because not all cpus have been assigned
- + * portals), then one portal will marked for sharing. Conversely if no
- + * sharing is required, any portals marked for sharing will not be shared. It
- + * may be that sharing occurs when it wasn't expected, if portal allocation
- + * failed to honour all the requested assignments (including the default
- + * assignments if no bootarg is present).
- + * 4. Unshared portals are initialised on their respective cpus.
- + * 5. Shared portals are initialised on their respective cpus.
- + * 6. Each remaining cpu is initialised to slave to one of the shared portals,
- + * which are selected in a round-robin fashion.
- + * Any portal configs left unused are available for USDPAA allocation.
- + */
- +__init int bman_init(void)
- +{
- + struct cpumask slave_cpus;
- + struct cpumask unshared_cpus = *cpu_none_mask;
- + struct cpumask shared_cpus = *cpu_none_mask;
- + LIST_HEAD(unshared_pcfgs);
- + LIST_HEAD(shared_pcfgs);
- + struct device_node *dn;
- + struct bm_portal_config *pcfg;
- + struct bman_portal *p;
- + int cpu, ret;
- + struct cpumask offline_cpus;
- +
- + /* Initialise the Bman (CCSR) device */
- + for_each_compatible_node(dn, NULL, "fsl,bman") {
- + if (!bman_init_ccsr(dn))
- + pr_info("Bman err interrupt handler present\n");
- + else
- + pr_err("Bman CCSR setup failed\n");
- + }
- + /* Initialise any declared buffer pools */
- + for_each_compatible_node(dn, NULL, "fsl,bpool") {
- + ret = fsl_bpool_init(dn);
- + if (ret)
- + return ret;
- + }
- + /* Step 1. See comments at the beginning of the file. */
- + for_each_compatible_node(dn, NULL, "fsl,bman-portal") {
- + if (!of_device_is_available(dn))
- + continue;
- + pcfg = parse_pcfg(dn);
- + if (pcfg)
- + list_add_tail(&pcfg->list, &unused_pcfgs);
- + }
- + /* Step 2. */
- + for_each_possible_cpu(cpu) {
- + if (cpumask_test_cpu(cpu, &want_shared)) {
- + pcfg = get_pcfg(&unused_pcfgs);
- + if (!pcfg)
- + break;
- + pcfg->public_cfg.cpu = cpu;
- + list_add_tail(&pcfg->list, &shared_pcfgs);
- + cpumask_set_cpu(cpu, &shared_cpus);
- + }
- + if (cpumask_test_cpu(cpu, &want_unshared)) {
- + if (cpumask_test_cpu(cpu, &shared_cpus))
- + continue;
- + pcfg = get_pcfg(&unused_pcfgs);
- + if (!pcfg)
- + break;
- + pcfg->public_cfg.cpu = cpu;
- + list_add_tail(&pcfg->list, &unshared_pcfgs);
- + cpumask_set_cpu(cpu, &unshared_cpus);
- + }
- + }
- + if (list_empty(&shared_pcfgs) && list_empty(&unshared_pcfgs)) {
- + /* Default, give an unshared portal to each online cpu */
- + for_each_online_cpu(cpu) {
- + pcfg = get_pcfg(&unused_pcfgs);
- + if (!pcfg)
- + break;
- + pcfg->public_cfg.cpu = cpu;
- + list_add_tail(&pcfg->list, &unshared_pcfgs);
- + cpumask_set_cpu(cpu, &unshared_cpus);
- + }
- + }
- + /* Step 3. */
- + cpumask_andnot(&slave_cpus, cpu_possible_mask, &shared_cpus);
- + cpumask_andnot(&slave_cpus, &slave_cpus, &unshared_cpus);
- + if (cpumask_empty(&slave_cpus)) {
- + /* No sharing required */
- + if (!list_empty(&shared_pcfgs)) {
- + /* Migrate "shared" to "unshared" */
- + cpumask_or(&unshared_cpus, &unshared_cpus,
- + &shared_cpus);
- + cpumask_clear(&shared_cpus);
- + list_splice_tail(&shared_pcfgs, &unshared_pcfgs);
- + INIT_LIST_HEAD(&shared_pcfgs);
- + }
- + } else {
- + /* Sharing required */
- + if (list_empty(&shared_pcfgs)) {
- + /* Migrate one "unshared" to "shared" */
- + pcfg = get_pcfg(&unshared_pcfgs);
- + if (!pcfg) {
- + pr_crit("No BMan portals available!\n");
- + return 0;
- + }
- + cpumask_clear_cpu(pcfg->public_cfg.cpu, &unshared_cpus);
- + cpumask_set_cpu(pcfg->public_cfg.cpu, &shared_cpus);
- + list_add_tail(&pcfg->list, &shared_pcfgs);
- + }
- + }
- + /* Step 4. */
- + list_for_each_entry(pcfg, &unshared_pcfgs, list) {
- + pcfg->public_cfg.is_shared = 0;
- + p = init_pcfg(pcfg);
- + if (!p) {
- + pr_crit("Unable to initialize bman portal\n");
- + return 0;
- + }
- + }
- + /* Step 5. */
- + list_for_each_entry(pcfg, &shared_pcfgs, list) {
- + pcfg->public_cfg.is_shared = 1;
- + p = init_pcfg(pcfg);
- + if (p)
- + shared_portals[num_shared_portals++] = p;
- + }
- + /* Step 6. */
- + if (!cpumask_empty(&slave_cpus))
- + for_each_cpu(cpu, &slave_cpus)
- + init_slave(cpu);
- + pr_info("Bman portals initialised\n");
- + cpumask_andnot(&offline_cpus, cpu_possible_mask, cpu_online_mask);
- + for_each_cpu(cpu, &offline_cpus)
- + bman_offline_cpu(cpu);
- +#ifdef CONFIG_HOTPLUG_CPU
- + register_hotcpu_notifier(&bman_hotplug_cpu_notifier);
- +#endif
- + return 0;
- +}
- +
- +__init int bman_resource_init(void)
- +{
- + struct device_node *dn;
- + int ret;
- +
- + /* Initialise BPID allocation ranges */
- + for_each_compatible_node(dn, NULL, "fsl,bpid-range") {
- + ret = fsl_bpid_range_init(dn);
- + if (ret)
- + return ret;
- + }
- + return 0;
- +}
- +
- +#ifdef CONFIG_SUSPEND
- +void suspend_unused_bportal(void)
- +{
- + struct bm_portal_config *pcfg;
- +
- + if (list_empty(&unused_pcfgs))
- + return;
- +
- + list_for_each_entry(pcfg, &unused_pcfgs, list) {
- +#ifdef CONFIG_PM_DEBUG
- + pr_info("Need to save bportal %d\n", pcfg->public_cfg.index);
- +#endif
- + /* save isdr, disable all via isdr, clear isr */
- + pcfg->saved_isdr =
- + __raw_readl(pcfg->addr_virt[DPA_PORTAL_CI] + 0xe08);
- + __raw_writel(0xffffffff, pcfg->addr_virt[DPA_PORTAL_CI] +
- + 0xe08);
- + __raw_writel(0xffffffff, pcfg->addr_virt[DPA_PORTAL_CI] +
- + 0xe00);
- + }
- + return;
- +}
- +
- +void resume_unused_bportal(void)
- +{
- + struct bm_portal_config *pcfg;
- +
- + if (list_empty(&unused_pcfgs))
- + return;
- +
- + list_for_each_entry(pcfg, &unused_pcfgs, list) {
- +#ifdef CONFIG_PM_DEBUG
- + pr_info("Need to resume bportal %d\n", pcfg->public_cfg.index);
- +#endif
- + /* restore isdr */
- + __raw_writel(pcfg->saved_isdr,
- + pcfg->addr_virt[DPA_PORTAL_CI] + 0xe08);
- + }
- + return;
- +}
- +#endif
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/bman_high.c
- @@ -0,0 +1,1141 @@
- +/* Copyright 2008-2012 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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 "bman_low.h"
- +
- +/* Compilation constants */
- +#define RCR_THRESH 2 /* reread h/w CI when running out of space */
- +#define IRQNAME "BMan portal %d"
- +#define MAX_IRQNAME 16 /* big enough for "BMan portal %d" */
- +
- +struct bman_portal {
- + struct bm_portal p;
- + /* 2-element array. pools[0] is mask, pools[1] is snapshot. */
- + struct bman_depletion *pools;
- + int thresh_set;
- + unsigned long irq_sources;
- + u32 slowpoll; /* only used when interrupts are off */
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC
- + struct bman_pool *rcri_owned; /* only 1 release WAIT_SYNC at a time */
- +#endif
- +#ifdef CONFIG_FSL_DPA_PORTAL_SHARE
- + raw_spinlock_t sharing_lock; /* only used if is_shared */
- + int is_shared;
- + struct bman_portal *sharing_redirect;
- +#endif
- + /* When the cpu-affine portal is activated, this is non-NULL */
- + const struct bm_portal_config *config;
- + /* This is needed for power management */
- + struct platform_device *pdev;
- + /* 64-entry hash-table of pool objects that are tracking depletion
- + * entry/exit (ie. BMAN_POOL_FLAG_DEPLETION). This isn't fast-path, so
- + * we're not fussy about cache-misses and so forth - whereas the above
- + * members should all fit in one cacheline.
- + * BTW, with 64 entries in the hash table and 64 buffer pools to track,
- + * you'll never guess the hash-function ... */
- + struct bman_pool *cb[64];
- + char irqname[MAX_IRQNAME];
- + /* Track if the portal was alloced by the driver */
- + u8 alloced;
- + /* power management data */
- + u32 save_isdr;
- +};
- +
- +/* For an explanation of the locking, redirection, or affine-portal logic,
- + * please consult the Qman driver for details. This is the same, only simpler
- + * (no fiddly Qman-specific bits.) */
- +#ifdef CONFIG_FSL_DPA_PORTAL_SHARE
- +#define PORTAL_IRQ_LOCK(p, irqflags) \
- + do { \
- + if ((p)->is_shared) \
- + raw_spin_lock_irqsave(&(p)->sharing_lock, irqflags); \
- + else \
- + local_irq_save(irqflags); \
- + } while (0)
- +#define PORTAL_IRQ_UNLOCK(p, irqflags) \
- + do { \
- + if ((p)->is_shared) \
- + raw_spin_unlock_irqrestore(&(p)->sharing_lock, \
- + irqflags); \
- + else \
- + local_irq_restore(irqflags); \
- + } while (0)
- +#else
- +#define PORTAL_IRQ_LOCK(p, irqflags) local_irq_save(irqflags)
- +#define PORTAL_IRQ_UNLOCK(p, irqflags) local_irq_restore(irqflags)
- +#endif
- +
- +static cpumask_t affine_mask;
- +static DEFINE_SPINLOCK(affine_mask_lock);
- +static DEFINE_PER_CPU(struct bman_portal, bman_affine_portal);
- +static inline struct bman_portal *get_raw_affine_portal(void)
- +{
- + return &get_cpu_var(bman_affine_portal);
- +}
- +#ifdef CONFIG_FSL_DPA_PORTAL_SHARE
- +static inline struct bman_portal *get_affine_portal(void)
- +{
- + struct bman_portal *p = get_raw_affine_portal();
- + if (p->sharing_redirect)
- + return p->sharing_redirect;
- + return p;
- +}
- +#else
- +#define get_affine_portal() get_raw_affine_portal()
- +#endif
- +static inline void put_affine_portal(void)
- +{
- + put_cpu_var(bman_affine_portal);
- +}
- +static inline struct bman_portal *get_poll_portal(void)
- +{
- + return &get_cpu_var(bman_affine_portal);
- +}
- +#define put_poll_portal()
- +
- +/* GOTCHA: this object type refers to a pool, it isn't *the* pool. There may be
- + * more than one such object per Bman buffer pool, eg. if different users of the
- + * pool are operating via different portals. */
- +struct bman_pool {
- + struct bman_pool_params params;
- + /* Used for hash-table admin when using depletion notifications. */
- + struct bman_portal *portal;
- + struct bman_pool *next;
- + /* stockpile state - NULL unless BMAN_POOL_FLAG_STOCKPILE is set */
- + struct bm_buffer *sp;
- + unsigned int sp_fill;
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + atomic_t in_use;
- +#endif
- +};
- +
- +/* (De)Registration of depletion notification callbacks */
- +static void depletion_link(struct bman_portal *portal, struct bman_pool *pool)
- +{
- + __maybe_unused unsigned long irqflags;
- + pool->portal = portal;
- + PORTAL_IRQ_LOCK(portal, irqflags);
- + pool->next = portal->cb[pool->params.bpid];
- + portal->cb[pool->params.bpid] = pool;
- + if (!pool->next)
- + /* First object for that bpid on this portal, enable the BSCN
- + * mask bit. */
- + bm_isr_bscn_mask(&portal->p, pool->params.bpid, 1);
- + PORTAL_IRQ_UNLOCK(portal, irqflags);
- +}
- +static void depletion_unlink(struct bman_pool *pool)
- +{
- + struct bman_pool *it, *last = NULL;
- + struct bman_pool **base = &pool->portal->cb[pool->params.bpid];
- + __maybe_unused unsigned long irqflags;
- + PORTAL_IRQ_LOCK(pool->portal, irqflags);
- + it = *base; /* <-- gotcha, don't do this prior to the irq_save */
- + while (it != pool) {
- + last = it;
- + it = it->next;
- + }
- + if (!last)
- + *base = pool->next;
- + else
- + last->next = pool->next;
- + if (!last && !pool->next) {
- + /* Last object for that bpid on this portal, disable the BSCN
- + * mask bit. */
- + bm_isr_bscn_mask(&pool->portal->p, pool->params.bpid, 0);
- + /* And "forget" that we last saw this pool as depleted */
- + bman_depletion_unset(&pool->portal->pools[1],
- + pool->params.bpid);
- + }
- + PORTAL_IRQ_UNLOCK(pool->portal, irqflags);
- +}
- +
- +/* In the case that the application's core loop calls qman_poll() and
- + * bman_poll(), we ought to balance how often we incur the overheads of the
- + * slow-path poll. We'll use two decrementer sources. The idle decrementer
- + * constant is used when the last slow-poll detected no work to do, and the busy
- + * decrementer constant when the last slow-poll had work to do. */
- +#define SLOW_POLL_IDLE 1000
- +#define SLOW_POLL_BUSY 10
- +static u32 __poll_portal_slow(struct bman_portal *p, u32 is);
- +
- +/* Portal interrupt handler */
- +static irqreturn_t portal_isr(__always_unused int irq, void *ptr)
- +{
- + struct bman_portal *p = ptr;
- + u32 clear = p->irq_sources;
- + u32 is = bm_isr_status_read(&p->p) & p->irq_sources;
- + clear |= __poll_portal_slow(p, is);
- + bm_isr_status_clear(&p->p, clear);
- + return IRQ_HANDLED;
- +}
- +
- +#ifdef CONFIG_SUSPEND
- +static int _bman_portal_suspend_noirq(struct device *dev)
- +{
- + struct bman_portal *p = (struct bman_portal *)dev->platform_data;
- +#ifdef CONFIG_PM_DEBUG
- + struct platform_device *pdev = to_platform_device(dev);
- +#endif
- + p->save_isdr = bm_isr_disable_read(&p->p);
- + bm_isr_disable_write(&p->p, 0xffffffff);
- + bm_isr_status_clear(&p->p, 0xffffffff);
- +#ifdef CONFIG_PM_DEBUG
- + pr_info("Suspend for %s\n", pdev->name);
- +#endif
- + return 0;
- +}
- +
- +static int _bman_portal_resume_noirq(struct device *dev)
- +{
- + struct bman_portal *p = (struct bman_portal *)dev->platform_data;
- +
- + /* restore isdr */
- + bm_isr_disable_write(&p->p, p->save_isdr);
- + return 0;
- +}
- +#else
- +#define _bman_portal_suspend_noirq NULL
- +#define _bman_portal_resume_noirq NULL
- +#endif
- +
- +struct dev_pm_domain bman_portal_device_pm_domain = {
- + .ops = {
- + USE_PLATFORM_PM_SLEEP_OPS
- + .suspend_noirq = _bman_portal_suspend_noirq,
- + .resume_noirq = _bman_portal_resume_noirq,
- + }
- +};
- +
- +struct bman_portal *bman_create_portal(
- + struct bman_portal *portal,
- + const struct bm_portal_config *config)
- +{
- + struct bm_portal *__p;
- + const struct bman_depletion *pools = &config->public_cfg.mask;
- + int ret;
- + u8 bpid = 0;
- + char buf[16];
- +
- + if (!portal) {
- + portal = kmalloc(sizeof(*portal), GFP_KERNEL);
- + if (!portal)
- + return portal;
- + portal->alloced = 1;
- + } else
- + portal->alloced = 0;
- +
- + __p = &portal->p;
- +
- + /* prep the low-level portal struct with the mapped addresses from the
- + * config, everything that follows depends on it and "config" is more
- + * for (de)reference... */
- + __p->addr.addr_ce = config->addr_virt[DPA_PORTAL_CE];
- + __p->addr.addr_ci = config->addr_virt[DPA_PORTAL_CI];
- + if (bm_rcr_init(__p, bm_rcr_pvb, bm_rcr_cce)) {
- + pr_err("Bman RCR initialisation failed\n");
- + goto fail_rcr;
- + }
- + if (bm_mc_init(__p)) {
- + pr_err("Bman MC initialisation failed\n");
- + goto fail_mc;
- + }
- + if (bm_isr_init(__p)) {
- + pr_err("Bman ISR initialisation failed\n");
- + goto fail_isr;
- + }
- + portal->pools = kmalloc(2 * sizeof(*pools), GFP_KERNEL);
- + if (!portal->pools)
- + goto fail_pools;
- + portal->pools[0] = *pools;
- + bman_depletion_init(portal->pools + 1);
- + while (bpid < bman_pool_max) {
- + /* Default to all BPIDs disabled, we enable as required at
- + * run-time. */
- + bm_isr_bscn_mask(__p, bpid, 0);
- + bpid++;
- + }
- + portal->slowpoll = 0;
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC
- + portal->rcri_owned = NULL;
- +#endif
- +#ifdef CONFIG_FSL_DPA_PORTAL_SHARE
- + raw_spin_lock_init(&portal->sharing_lock);
- + portal->is_shared = config->public_cfg.is_shared;
- + portal->sharing_redirect = NULL;
- +#endif
- + sprintf(buf, "bportal-%u", config->public_cfg.index);
- + portal->pdev = platform_device_alloc(buf, -1);
- + if (!portal->pdev)
- + goto fail_devalloc;
- + portal->pdev->dev.pm_domain = &bman_portal_device_pm_domain;
- + portal->pdev->dev.platform_data = portal;
- + ret = platform_device_add(portal->pdev);
- + if (ret)
- + goto fail_devadd;
- + memset(&portal->cb, 0, sizeof(portal->cb));
- + /* Write-to-clear any stale interrupt status bits */
- + bm_isr_disable_write(__p, 0xffffffff);
- + portal->irq_sources = 0;
- + bm_isr_enable_write(__p, portal->irq_sources);
- + bm_isr_status_clear(__p, 0xffffffff);
- + snprintf(portal->irqname, MAX_IRQNAME, IRQNAME, config->public_cfg.cpu);
- + if (request_irq(config->public_cfg.irq, portal_isr, 0, portal->irqname,
- + portal)) {
- + pr_err("request_irq() failed\n");
- + goto fail_irq;
- + }
- + if ((config->public_cfg.cpu != -1) &&
- + irq_can_set_affinity(config->public_cfg.irq) &&
- + irq_set_affinity(config->public_cfg.irq,
- + cpumask_of(config->public_cfg.cpu))) {
- + pr_err("irq_set_affinity() failed %s\n", portal->irqname);
- + goto fail_affinity;
- + }
- +
- + /* Need RCR to be empty before continuing */
- + ret = bm_rcr_get_fill(__p);
- + if (ret) {
- + pr_err("Bman RCR unclean\n");
- + goto fail_rcr_empty;
- + }
- + /* Success */
- + portal->config = config;
- +
- + bm_isr_disable_write(__p, 0);
- + bm_isr_uninhibit(__p);
- + return portal;
- +fail_rcr_empty:
- +fail_affinity:
- + free_irq(config->public_cfg.irq, portal);
- +fail_irq:
- + platform_device_del(portal->pdev);
- +fail_devadd:
- + platform_device_put(portal->pdev);
- +fail_devalloc:
- + kfree(portal->pools);
- +fail_pools:
- + bm_isr_finish(__p);
- +fail_isr:
- + bm_mc_finish(__p);
- +fail_mc:
- + bm_rcr_finish(__p);
- +fail_rcr:
- + if (portal->alloced)
- + kfree(portal);
- + return NULL;
- +}
- +
- +struct bman_portal *bman_create_affine_portal(
- + const struct bm_portal_config *config)
- +{
- + struct bman_portal *portal;
- +
- + portal = &per_cpu(bman_affine_portal, config->public_cfg.cpu);
- + portal = bman_create_portal(portal, config);
- + if (portal) {
- + spin_lock(&affine_mask_lock);
- + cpumask_set_cpu(config->public_cfg.cpu, &affine_mask);
- + spin_unlock(&affine_mask_lock);
- + }
- + return portal;
- +}
- +
- +
- +struct bman_portal *bman_create_affine_slave(struct bman_portal *redirect,
- + int cpu)
- +{
- +#ifdef CONFIG_FSL_DPA_PORTAL_SHARE
- + struct bman_portal *p;
- + p = &per_cpu(bman_affine_portal, cpu);
- + BUG_ON(p->config);
- + BUG_ON(p->is_shared);
- + BUG_ON(!redirect->config->public_cfg.is_shared);
- + p->irq_sources = 0;
- + p->sharing_redirect = redirect;
- + return p;
- +#else
- + BUG();
- + return NULL;
- +#endif
- +}
- +
- +void bman_destroy_portal(struct bman_portal *bm)
- +{
- + const struct bm_portal_config *pcfg;
- + pcfg = bm->config;
- + bm_rcr_cce_update(&bm->p);
- + bm_rcr_cce_update(&bm->p);
- +
- + free_irq(pcfg->public_cfg.irq, bm);
- +
- + kfree(bm->pools);
- + bm_isr_finish(&bm->p);
- + bm_mc_finish(&bm->p);
- + bm_rcr_finish(&bm->p);
- + bm->config = NULL;
- + if (bm->alloced)
- + kfree(bm);
- +}
- +
- +const struct bm_portal_config *bman_destroy_affine_portal(void)
- +{
- + struct bman_portal *bm = get_raw_affine_portal();
- + const struct bm_portal_config *pcfg;
- +#ifdef CONFIG_FSL_DPA_PORTAL_SHARE
- + if (bm->sharing_redirect) {
- + bm->sharing_redirect = NULL;
- + put_affine_portal();
- + return NULL;
- + }
- + bm->is_shared = 0;
- +#endif
- + pcfg = bm->config;
- + bman_destroy_portal(bm);
- + spin_lock(&affine_mask_lock);
- + cpumask_clear_cpu(pcfg->public_cfg.cpu, &affine_mask);
- + spin_unlock(&affine_mask_lock);
- + put_affine_portal();
- + return pcfg;
- +}
- +
- +/* When release logic waits on available RCR space, we need a global waitqueue
- + * in the case of "affine" use (as the waits wake on different cpus which means
- + * different portals - so we can't wait on any per-portal waitqueue). */
- +static DECLARE_WAIT_QUEUE_HEAD(affine_queue);
- +
- +static u32 __poll_portal_slow(struct bman_portal *p, u32 is)
- +{
- + struct bman_depletion tmp;
- + u32 ret = is;
- +
- + /* There is a gotcha to be aware of. If we do the query before clearing
- + * the status register, we may miss state changes that occur between the
- + * two. If we write to clear the status register before the query, the
- + * cache-enabled query command may overtake the status register write
- + * unless we use a heavyweight sync (which we don't want). Instead, we
- + * write-to-clear the status register then *read it back* before doing
- + * the query, hence the odd while loop with the 'is' accumulation. */
- + if (is & BM_PIRQ_BSCN) {
- + struct bm_mc_result *mcr;
- + __maybe_unused unsigned long irqflags;
- + unsigned int i, j;
- + u32 __is;
- + bm_isr_status_clear(&p->p, BM_PIRQ_BSCN);
- + while ((__is = bm_isr_status_read(&p->p)) & BM_PIRQ_BSCN) {
- + is |= __is;
- + bm_isr_status_clear(&p->p, BM_PIRQ_BSCN);
- + }
- + is &= ~BM_PIRQ_BSCN;
- + PORTAL_IRQ_LOCK(p, irqflags);
- + bm_mc_start(&p->p);
- + bm_mc_commit(&p->p, BM_MCC_VERB_CMD_QUERY);
- + while (!(mcr = bm_mc_result(&p->p)))
- + cpu_relax();
- + tmp = mcr->query.ds.state;
- + tmp.__state[0] = be32_to_cpu(tmp.__state[0]);
- + tmp.__state[1] = be32_to_cpu(tmp.__state[1]);
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + for (i = 0; i < 2; i++) {
- + int idx = i * 32;
- + /* tmp is a mask of currently-depleted pools.
- + * pools[0] is mask of those we care about.
- + * pools[1] is our previous view (we only want to
- + * be told about changes). */
- + tmp.__state[i] &= p->pools[0].__state[i];
- + if (tmp.__state[i] == p->pools[1].__state[i])
- + /* fast-path, nothing to see, move along */
- + continue;
- + for (j = 0; j <= 31; j++, idx++) {
- + struct bman_pool *pool = p->cb[idx];
- + int b4 = bman_depletion_get(&p->pools[1], idx);
- + int af = bman_depletion_get(&tmp, idx);
- + if (b4 == af)
- + continue;
- + while (pool) {
- + pool->params.cb(p, pool,
- + pool->params.cb_ctx, af);
- + pool = pool->next;
- + }
- + }
- + }
- + p->pools[1] = tmp;
- + }
- +
- + if (is & BM_PIRQ_RCRI) {
- + __maybe_unused unsigned long irqflags;
- + PORTAL_IRQ_LOCK(p, irqflags);
- + bm_rcr_cce_update(&p->p);
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC
- + /* If waiting for sync, we only cancel the interrupt threshold
- + * when the ring utilisation hits zero. */
- + if (p->rcri_owned) {
- + if (!bm_rcr_get_fill(&p->p)) {
- + p->rcri_owned = NULL;
- + bm_rcr_set_ithresh(&p->p, 0);
- + }
- + } else
- +#endif
- + bm_rcr_set_ithresh(&p->p, 0);
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + wake_up(&affine_queue);
- + bm_isr_status_clear(&p->p, BM_PIRQ_RCRI);
- + is &= ~BM_PIRQ_RCRI;
- + }
- +
- + /* There should be no status register bits left undefined */
- + DPA_ASSERT(!is);
- + return ret;
- +}
- +
- +const struct bman_portal_config *bman_get_portal_config(void)
- +{
- + struct bman_portal *p = get_affine_portal();
- + const struct bman_portal_config *ret = &p->config->public_cfg;
- + put_affine_portal();
- + return ret;
- +}
- +EXPORT_SYMBOL(bman_get_portal_config);
- +
- +u32 bman_irqsource_get(void)
- +{
- + struct bman_portal *p = get_raw_affine_portal();
- + u32 ret = p->irq_sources & BM_PIRQ_VISIBLE;
- + put_affine_portal();
- + return ret;
- +}
- +EXPORT_SYMBOL(bman_irqsource_get);
- +
- +int bman_p_irqsource_add(struct bman_portal *p, __maybe_unused u32 bits)
- +{
- +#ifdef CONFIG_FSL_DPA_PORTAL_SHARE
- + if (p->sharing_redirect)
- + return -EINVAL;
- + else
- +#endif
- + {
- + __maybe_unused unsigned long irqflags;
- + PORTAL_IRQ_LOCK(p, irqflags);
- + set_bits(bits & BM_PIRQ_VISIBLE, &p->irq_sources);
- + bm_isr_enable_write(&p->p, p->irq_sources);
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + }
- + return 0;
- +}
- +EXPORT_SYMBOL(bman_p_irqsource_add);
- +
- +int bman_irqsource_add(__maybe_unused u32 bits)
- +{
- + struct bman_portal *p = get_raw_affine_portal();
- + int ret = 0;
- + ret = bman_p_irqsource_add(p, bits);
- + put_affine_portal();
- + return ret;
- +}
- +EXPORT_SYMBOL(bman_irqsource_add);
- +
- +int bman_irqsource_remove(u32 bits)
- +{
- + struct bman_portal *p = get_raw_affine_portal();
- + __maybe_unused unsigned long irqflags;
- + u32 ier;
- +#ifdef CONFIG_FSL_DPA_PORTAL_SHARE
- + if (p->sharing_redirect) {
- + put_affine_portal();
- + return -EINVAL;
- + }
- +#endif
- + /* Our interrupt handler only processes+clears status register bits that
- + * are in p->irq_sources. As we're trimming that mask, if one of them
- + * were to assert in the status register just before we remove it from
- + * the enable register, there would be an interrupt-storm when we
- + * release the IRQ lock. So we wait for the enable register update to
- + * take effect in h/w (by reading it back) and then clear all other bits
- + * in the status register. Ie. we clear them from ISR once it's certain
- + * IER won't allow them to reassert. */
- + PORTAL_IRQ_LOCK(p, irqflags);
- + bits &= BM_PIRQ_VISIBLE;
- + clear_bits(bits, &p->irq_sources);
- + bm_isr_enable_write(&p->p, p->irq_sources);
- + ier = bm_isr_enable_read(&p->p);
- + /* Using "~ier" (rather than "bits" or "~p->irq_sources") creates a
- + * data-dependency, ie. to protect against re-ordering. */
- + bm_isr_status_clear(&p->p, ~ier);
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- + return 0;
- +}
- +EXPORT_SYMBOL(bman_irqsource_remove);
- +
- +const cpumask_t *bman_affine_cpus(void)
- +{
- + return &affine_mask;
- +}
- +EXPORT_SYMBOL(bman_affine_cpus);
- +
- +u32 bman_poll_slow(void)
- +{
- + struct bman_portal *p = get_poll_portal();
- + u32 ret;
- +#ifdef CONFIG_FSL_DPA_PORTAL_SHARE
- + if (unlikely(p->sharing_redirect))
- + ret = (u32)-1;
- + else
- +#endif
- + {
- + u32 is = bm_isr_status_read(&p->p) & ~p->irq_sources;
- + ret = __poll_portal_slow(p, is);
- + bm_isr_status_clear(&p->p, ret);
- + }
- + put_poll_portal();
- + return ret;
- +}
- +EXPORT_SYMBOL(bman_poll_slow);
- +
- +/* Legacy wrapper */
- +void bman_poll(void)
- +{
- + struct bman_portal *p = get_poll_portal();
- +#ifdef CONFIG_FSL_DPA_PORTAL_SHARE
- + if (unlikely(p->sharing_redirect))
- + goto done;
- +#endif
- + if (!(p->slowpoll--)) {
- + u32 is = bm_isr_status_read(&p->p) & ~p->irq_sources;
- + u32 active = __poll_portal_slow(p, is);
- + if (active)
- + p->slowpoll = SLOW_POLL_BUSY;
- + else
- + p->slowpoll = SLOW_POLL_IDLE;
- + }
- +#ifdef CONFIG_FSL_DPA_PORTAL_SHARE
- +done:
- +#endif
- + put_poll_portal();
- +}
- +EXPORT_SYMBOL(bman_poll);
- +
- +static const u32 zero_thresholds[4] = {0, 0, 0, 0};
- +
- +struct bman_pool *bman_new_pool(const struct bman_pool_params *params)
- +{
- + struct bman_pool *pool = NULL;
- + u32 bpid;
- +
- + if (params->flags & BMAN_POOL_FLAG_DYNAMIC_BPID) {
- + int ret = bman_alloc_bpid(&bpid);
- + if (ret)
- + return NULL;
- + } else {
- + if (params->bpid >= bman_pool_max)
- + return NULL;
- + bpid = params->bpid;
- + }
- +#ifdef CONFIG_FSL_BMAN_CONFIG
- + if (params->flags & BMAN_POOL_FLAG_THRESH) {
- + int ret = bm_pool_set(bpid, params->thresholds);
- + if (ret)
- + goto err;
- + }
- +#else
- + if (params->flags & BMAN_POOL_FLAG_THRESH)
- + goto err;
- +#endif
- + pool = kmalloc(sizeof(*pool), GFP_KERNEL);
- + if (!pool)
- + goto err;
- + pool->sp = NULL;
- + pool->sp_fill = 0;
- + pool->params = *params;
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + atomic_set(&pool->in_use, 1);
- +#endif
- + if (params->flags & BMAN_POOL_FLAG_DYNAMIC_BPID)
- + pool->params.bpid = bpid;
- + if (params->flags & BMAN_POOL_FLAG_STOCKPILE) {
- + pool->sp = kmalloc(sizeof(struct bm_buffer) * BMAN_STOCKPILE_SZ,
- + GFP_KERNEL);
- + if (!pool->sp)
- + goto err;
- + }
- + if (pool->params.flags & BMAN_POOL_FLAG_DEPLETION) {
- + struct bman_portal *p = get_affine_portal();
- + if (!p->pools || !bman_depletion_get(&p->pools[0], bpid)) {
- + pr_err("Depletion events disabled for bpid %d\n", bpid);
- + goto err;
- + }
- + depletion_link(p, pool);
- + put_affine_portal();
- + }
- + return pool;
- +err:
- +#ifdef CONFIG_FSL_BMAN_CONFIG
- + if (params->flags & BMAN_POOL_FLAG_THRESH)
- + bm_pool_set(bpid, zero_thresholds);
- +#endif
- + if (params->flags & BMAN_POOL_FLAG_DYNAMIC_BPID)
- + bman_release_bpid(bpid);
- + if (pool) {
- + kfree(pool->sp);
- + kfree(pool);
- + }
- + return NULL;
- +}
- +EXPORT_SYMBOL(bman_new_pool);
- +
- +void bman_free_pool(struct bman_pool *pool)
- +{
- +#ifdef CONFIG_FSL_BMAN_CONFIG
- + if (pool->params.flags & BMAN_POOL_FLAG_THRESH)
- + bm_pool_set(pool->params.bpid, zero_thresholds);
- +#endif
- + if (pool->params.flags & BMAN_POOL_FLAG_DEPLETION)
- + depletion_unlink(pool);
- + if (pool->params.flags & BMAN_POOL_FLAG_STOCKPILE) {
- + if (pool->sp_fill)
- + pr_err("Stockpile not flushed, has %u in bpid %u.\n",
- + pool->sp_fill, pool->params.bpid);
- + kfree(pool->sp);
- + pool->sp = NULL;
- + pool->params.flags ^= BMAN_POOL_FLAG_STOCKPILE;
- + }
- + if (pool->params.flags & BMAN_POOL_FLAG_DYNAMIC_BPID)
- + bman_release_bpid(pool->params.bpid);
- + kfree(pool);
- +}
- +EXPORT_SYMBOL(bman_free_pool);
- +
- +const struct bman_pool_params *bman_get_params(const struct bman_pool *pool)
- +{
- + return &pool->params;
- +}
- +EXPORT_SYMBOL(bman_get_params);
- +
- +static noinline void update_rcr_ci(struct bman_portal *p, u8 avail)
- +{
- + if (avail)
- + bm_rcr_cce_prefetch(&p->p);
- + else
- + bm_rcr_cce_update(&p->p);
- +}
- +
- +int bman_rcr_is_empty(void)
- +{
- + __maybe_unused unsigned long irqflags;
- + struct bman_portal *p = get_affine_portal();
- + u8 avail;
- +
- + PORTAL_IRQ_LOCK(p, irqflags);
- + update_rcr_ci(p, 0);
- + avail = bm_rcr_get_fill(&p->p);
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- + return avail == 0;
- +}
- +EXPORT_SYMBOL(bman_rcr_is_empty);
- +
- +static inline struct bm_rcr_entry *try_rel_start(struct bman_portal **p,
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT
- + __maybe_unused struct bman_pool *pool,
- +#endif
- + __maybe_unused unsigned long *irqflags,
- + __maybe_unused u32 flags)
- +{
- + struct bm_rcr_entry *r;
- + u8 avail;
- +
- + *p = get_affine_portal();
- + PORTAL_IRQ_LOCK(*p, (*irqflags));
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC
- + if (unlikely((flags & BMAN_RELEASE_FLAG_WAIT) &&
- + (flags & BMAN_RELEASE_FLAG_WAIT_SYNC))) {
- + if ((*p)->rcri_owned) {
- + PORTAL_IRQ_UNLOCK(*p, (*irqflags));
- + put_affine_portal();
- + return NULL;
- + }
- + (*p)->rcri_owned = pool;
- + }
- +#endif
- + avail = bm_rcr_get_avail(&(*p)->p);
- + if (avail < 2)
- + update_rcr_ci(*p, avail);
- + r = bm_rcr_start(&(*p)->p);
- + if (unlikely(!r)) {
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC
- + if (unlikely((flags & BMAN_RELEASE_FLAG_WAIT) &&
- + (flags & BMAN_RELEASE_FLAG_WAIT_SYNC)))
- + (*p)->rcri_owned = NULL;
- +#endif
- + PORTAL_IRQ_UNLOCK(*p, (*irqflags));
- + put_affine_portal();
- + }
- + return r;
- +}
- +
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT
- +static noinline struct bm_rcr_entry *__wait_rel_start(struct bman_portal **p,
- + struct bman_pool *pool,
- + __maybe_unused unsigned long *irqflags,
- + u32 flags)
- +{
- + struct bm_rcr_entry *rcr = try_rel_start(p, pool, irqflags, flags);
- + if (!rcr)
- + bm_rcr_set_ithresh(&(*p)->p, 1);
- + return rcr;
- +}
- +
- +static noinline struct bm_rcr_entry *wait_rel_start(struct bman_portal **p,
- + struct bman_pool *pool,
- + __maybe_unused unsigned long *irqflags,
- + u32 flags)
- +{
- + struct bm_rcr_entry *rcr;
- +#ifndef CONFIG_FSL_DPA_CAN_WAIT_SYNC
- + pool = NULL;
- +#endif
- + if (flags & BMAN_RELEASE_FLAG_WAIT_INT)
- + /* NB: return NULL if signal occurs before completion. Signal
- + * can occur during return. Caller must check for signal */
- + wait_event_interruptible(affine_queue,
- + (rcr = __wait_rel_start(p, pool, irqflags, flags)));
- + else
- + wait_event(affine_queue,
- + (rcr = __wait_rel_start(p, pool, irqflags, flags)));
- + return rcr;
- +}
- +#endif
- +
- +static inline int __bman_release(struct bman_pool *pool,
- + const struct bm_buffer *bufs, u8 num, u32 flags)
- +{
- + struct bman_portal *p;
- + struct bm_rcr_entry *r;
- + __maybe_unused unsigned long irqflags;
- + u32 i = num - 1;
- +
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT
- + if (flags & BMAN_RELEASE_FLAG_WAIT)
- + r = wait_rel_start(&p, pool, &irqflags, flags);
- + else
- + r = try_rel_start(&p, pool, &irqflags, flags);
- +#else
- + r = try_rel_start(&p, &irqflags, flags);
- +#endif
- + if (!r)
- + return -EBUSY;
- + /* We can copy all but the first entry, as this can trigger badness
- + * with the valid-bit. Use the overlay to mask the verb byte. */
- + r->bufs[0].opaque =
- + ((cpu_to_be64((bufs[0].opaque |
- + ((u64)pool->params.bpid<<48))
- + & 0x00ffffffffffffff)));
- + if (i) {
- + for (i = 1; i < num; i++)
- + r->bufs[i].opaque =
- + cpu_to_be64(bufs[i].opaque);
- + }
- +
- + bm_rcr_pvb_commit(&p->p, BM_RCR_VERB_CMD_BPID_SINGLE |
- + (num & BM_RCR_VERB_BUFCOUNT_MASK));
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC
- + /* if we wish to sync we need to set the threshold after h/w sees the
- + * new ring entry. As we're mixing cache-enabled and cache-inhibited
- + * accesses, this requires a heavy-weight sync. */
- + if (unlikely((flags & BMAN_RELEASE_FLAG_WAIT) &&
- + (flags & BMAN_RELEASE_FLAG_WAIT_SYNC))) {
- + hwsync();
- + bm_rcr_set_ithresh(&p->p, 1);
- + }
- +#endif
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC
- + if (unlikely((flags & BMAN_RELEASE_FLAG_WAIT) &&
- + (flags & BMAN_RELEASE_FLAG_WAIT_SYNC))) {
- + if (flags & BMAN_RELEASE_FLAG_WAIT_INT)
- + /* NB: return success even if signal occurs before
- + * condition is true. pvb_commit guarantees success */
- + wait_event_interruptible(affine_queue,
- + (p->rcri_owned != pool));
- + else
- + wait_event(affine_queue, (p->rcri_owned != pool));
- + }
- +#endif
- + return 0;
- +}
- +
- +int bman_release(struct bman_pool *pool, const struct bm_buffer *bufs, u8 num,
- + u32 flags)
- +{
- + int ret;
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + if (!num || (num > 8))
- + return -EINVAL;
- + if (pool->params.flags & BMAN_POOL_FLAG_NO_RELEASE)
- + return -EINVAL;
- +#endif
- + /* Without stockpile, this API is a pass-through to the h/w operation */
- + if (!(pool->params.flags & BMAN_POOL_FLAG_STOCKPILE))
- + return __bman_release(pool, bufs, num, flags);
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + if (!atomic_dec_and_test(&pool->in_use)) {
- + pr_crit("Parallel attempts to enter bman_released() detected.");
- + panic("only one instance of bman_released/acquired allowed");
- + }
- +#endif
- + /* Two movements of buffers are possible, and can occur in either order.
- + * A: moving buffers from the caller to the stockpile.
- + * B: moving buffers from the stockpile to hardware.
- + * Order 1: if there is already enough space in the stockpile for A
- + * then we want to do A first, and only do B if we trigger the
- + * stockpile-high threshold.
- + * Order 2: if there is not enough space in the stockpile for A, then
- + * we want to do B first, then do A if B had succeeded. However in this
- + * case B is dependent on how many buffers the user needs to release,
- + * not the stockpile-high threshold.
- + * Due to the different handling of B between the two cases, putting A
- + * and B in a while() loop would require quite obscure logic, so handle
- + * the different sequences explicitly. */
- + if ((pool->sp_fill + num) <= BMAN_STOCKPILE_SZ) {
- + /* Order 1: do A */
- + copy_words(pool->sp + pool->sp_fill, bufs,
- + sizeof(struct bm_buffer) * num);
- + pool->sp_fill += num;
- + /* do B relative to STOCKPILE_HIGH */
- + while (pool->sp_fill >= BMAN_STOCKPILE_HIGH) {
- + ret = __bman_release(pool,
- + pool->sp + (pool->sp_fill - 8), 8,
- + flags);
- + if (ret >= 0)
- + pool->sp_fill -= 8;
- + }
- + } else {
- + /* Order 2: do B relative to 'num' */
- + do {
- + ret = __bman_release(pool,
- + pool->sp + (pool->sp_fill - 8), 8,
- + flags);
- + if (ret < 0)
- + /* failure */
- + goto release_done;
- + pool->sp_fill -= 8;
- + } while ((pool->sp_fill + num) > BMAN_STOCKPILE_SZ);
- + /* do A */
- + copy_words(pool->sp + pool->sp_fill, bufs,
- + sizeof(struct bm_buffer) * num);
- + pool->sp_fill += num;
- + }
- + /* success */
- + ret = 0;
- +release_done:
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + atomic_inc(&pool->in_use);
- +#endif
- + return ret;
- +}
- +EXPORT_SYMBOL(bman_release);
- +
- +static inline int __bman_acquire(struct bman_pool *pool, struct bm_buffer *bufs,
- + u8 num)
- +{
- + struct bman_portal *p = get_affine_portal();
- + struct bm_mc_command *mcc;
- + struct bm_mc_result *mcr;
- + __maybe_unused unsigned long irqflags;
- + int ret, i;
- +
- + PORTAL_IRQ_LOCK(p, irqflags);
- + mcc = bm_mc_start(&p->p);
- + mcc->acquire.bpid = pool->params.bpid;
- + bm_mc_commit(&p->p, BM_MCC_VERB_CMD_ACQUIRE |
- + (num & BM_MCC_VERB_ACQUIRE_BUFCOUNT));
- + while (!(mcr = bm_mc_result(&p->p)))
- + cpu_relax();
- + ret = mcr->verb & BM_MCR_VERB_ACQUIRE_BUFCOUNT;
- + if (bufs) {
- + for (i = 0; i < num; i++)
- + bufs[i].opaque =
- + be64_to_cpu(mcr->acquire.bufs[i].opaque);
- + }
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- + if (ret != num)
- + ret = -ENOMEM;
- + return ret;
- +}
- +
- +int bman_acquire(struct bman_pool *pool, struct bm_buffer *bufs, u8 num,
- + u32 flags)
- +{
- + int ret;
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + if (!num || (num > 8))
- + return -EINVAL;
- + if (pool->params.flags & BMAN_POOL_FLAG_ONLY_RELEASE)
- + return -EINVAL;
- +#endif
- + /* Without stockpile, this API is a pass-through to the h/w operation */
- + if (!(pool->params.flags & BMAN_POOL_FLAG_STOCKPILE))
- + return __bman_acquire(pool, bufs, num);
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + if (!atomic_dec_and_test(&pool->in_use)) {
- + pr_crit("Parallel attempts to enter bman_acquire() detected.");
- + panic("only one instance of bman_released/acquired allowed");
- + }
- +#endif
- + /* Two movements of buffers are possible, and can occur in either order.
- + * A: moving buffers from stockpile to the caller.
- + * B: moving buffers from hardware to the stockpile.
- + * Order 1: if there are already enough buffers in the stockpile for A
- + * then we want to do A first, and only do B if we trigger the
- + * stockpile-low threshold.
- + * Order 2: if there are not enough buffers in the stockpile for A,
- + * then we want to do B first, then do A if B had succeeded. However in
- + * this case B is dependent on how many buffers the user needs, not the
- + * stockpile-low threshold.
- + * Due to the different handling of B between the two cases, putting A
- + * and B in a while() loop would require quite obscure logic, so handle
- + * the different sequences explicitly. */
- + if (num <= pool->sp_fill) {
- + /* Order 1: do A */
- + copy_words(bufs, pool->sp + (pool->sp_fill - num),
- + sizeof(struct bm_buffer) * num);
- + pool->sp_fill -= num;
- + /* do B relative to STOCKPILE_LOW */
- + while (pool->sp_fill <= BMAN_STOCKPILE_LOW) {
- + ret = __bman_acquire(pool, pool->sp + pool->sp_fill, 8);
- + if (ret < 0)
- + ret = __bman_acquire(pool,
- + pool->sp + pool->sp_fill, 1);
- + if (ret < 0)
- + break;
- + pool->sp_fill += ret;
- + }
- + } else {
- + /* Order 2: do B relative to 'num' */
- + do {
- + ret = __bman_acquire(pool, pool->sp + pool->sp_fill, 8);
- + if (ret < 0)
- + ret = __bman_acquire(pool,
- + pool->sp + pool->sp_fill, 1);
- + if (ret < 0)
- + /* failure */
- + goto acquire_done;
- + pool->sp_fill += ret;
- + } while (pool->sp_fill < num);
- + /* do A */
- + copy_words(bufs, pool->sp + (pool->sp_fill - num),
- + sizeof(struct bm_buffer) * num);
- + pool->sp_fill -= num;
- + }
- + /* success */
- + ret = num;
- +acquire_done:
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + atomic_inc(&pool->in_use);
- +#endif
- + return ret;
- +}
- +EXPORT_SYMBOL(bman_acquire);
- +
- +int bman_flush_stockpile(struct bman_pool *pool, u32 flags)
- +{
- + u8 num;
- + int ret;
- +
- + while (pool->sp_fill) {
- + num = ((pool->sp_fill > 8) ? 8 : pool->sp_fill);
- + ret = __bman_release(pool, pool->sp + (pool->sp_fill - num),
- + num, flags);
- + if (ret)
- + return ret;
- + pool->sp_fill -= num;
- + }
- + return 0;
- +}
- +EXPORT_SYMBOL(bman_flush_stockpile);
- +
- +int bman_query_pools(struct bm_pool_state *state)
- +{
- + struct bman_portal *p = get_affine_portal();
- + struct bm_mc_result *mcr;
- + __maybe_unused unsigned long irqflags;
- +
- + PORTAL_IRQ_LOCK(p, irqflags);
- + bm_mc_start(&p->p);
- + bm_mc_commit(&p->p, BM_MCC_VERB_CMD_QUERY);
- + while (!(mcr = bm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & BM_MCR_VERB_CMD_MASK) == BM_MCR_VERB_CMD_QUERY);
- + *state = mcr->query;
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- + return 0;
- +}
- +EXPORT_SYMBOL(bman_query_pools);
- +
- +#ifdef CONFIG_FSL_BMAN_CONFIG
- +u32 bman_query_free_buffers(struct bman_pool *pool)
- +{
- + return bm_pool_free_buffers(pool->params.bpid);
- +}
- +EXPORT_SYMBOL(bman_query_free_buffers);
- +
- +int bman_update_pool_thresholds(struct bman_pool *pool, const u32 *thresholds)
- +{
- + u32 bpid;
- +
- + bpid = bman_get_params(pool)->bpid;
- +
- + return bm_pool_set(bpid, thresholds);
- +}
- +EXPORT_SYMBOL(bman_update_pool_thresholds);
- +#endif
- +
- +int bman_shutdown_pool(u32 bpid)
- +{
- + struct bman_portal *p = get_affine_portal();
- + __maybe_unused unsigned long irqflags;
- + int ret;
- +
- + PORTAL_IRQ_LOCK(p, irqflags);
- + ret = bm_shutdown_pool(&p->p, bpid);
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- + return ret;
- +}
- +EXPORT_SYMBOL(bman_shutdown_pool);
- +
- +const struct bm_portal_config *bman_get_bm_portal_config(
- + struct bman_portal *portal)
- +{
- + return portal->sharing_redirect ? NULL : portal->config;
- +}
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/bman_low.h
- @@ -0,0 +1,559 @@
- +/* Copyright 2008-2011 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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 "bman_private.h"
- +
- +/***************************/
- +/* Portal register assists */
- +/***************************/
- +
- +#if defined(CONFIG_PPC32) || defined(CONFIG_PPC64)
- +
- +/* Cache-inhibited register offsets */
- +#define BM_REG_RCR_PI_CINH 0x0000
- +#define BM_REG_RCR_CI_CINH 0x0004
- +#define BM_REG_RCR_ITR 0x0008
- +#define BM_REG_CFG 0x0100
- +#define BM_REG_SCN(n) (0x0200 + ((n) << 2))
- +#define BM_REG_ISR 0x0e00
- +#define BM_REG_IIR 0x0e0c
- +
- +/* Cache-enabled register offsets */
- +#define BM_CL_CR 0x0000
- +#define BM_CL_RR0 0x0100
- +#define BM_CL_RR1 0x0140
- +#define BM_CL_RCR 0x1000
- +#define BM_CL_RCR_PI_CENA 0x3000
- +#define BM_CL_RCR_CI_CENA 0x3100
- +
- +#endif
- +
- +#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
- +
- +/* Cache-inhibited register offsets */
- +#define BM_REG_RCR_PI_CINH 0x3000
- +#define BM_REG_RCR_CI_CINH 0x3100
- +#define BM_REG_RCR_ITR 0x3200
- +#define BM_REG_CFG 0x3300
- +#define BM_REG_SCN(n) (0x3400 + ((n) << 6))
- +#define BM_REG_ISR 0x3e00
- +#define BM_REG_IIR 0x3ec0
- +
- +/* Cache-enabled register offsets */
- +#define BM_CL_CR 0x0000
- +#define BM_CL_RR0 0x0100
- +#define BM_CL_RR1 0x0140
- +#define BM_CL_RCR 0x1000
- +#define BM_CL_RCR_PI_CENA 0x3000
- +#define BM_CL_RCR_CI_CENA 0x3100
- +
- +#endif
- +
- +/* BTW, the drivers (and h/w programming model) already obtain the required
- + * synchronisation for portal accesses via lwsync(), hwsync(), and
- + * data-dependencies. Use of barrier()s or other order-preserving primitives
- + * simply degrade performance. Hence the use of the __raw_*() interfaces, which
- + * simply ensure that the compiler treats the portal registers as volatile (ie.
- + * non-coherent). */
- +
- +/* Cache-inhibited register access. */
- +#define __bm_in(bm, o) be32_to_cpu(__raw_readl((bm)->addr_ci + (o)))
- +#define __bm_out(bm, o, val) __raw_writel(cpu_to_be32(val), \
- + (bm)->addr_ci + (o));
- +#define bm_in(reg) __bm_in(&portal->addr, BM_REG_##reg)
- +#define bm_out(reg, val) __bm_out(&portal->addr, BM_REG_##reg, val)
- +
- +/* Cache-enabled (index) register access */
- +#define __bm_cl_touch_ro(bm, o) dcbt_ro((bm)->addr_ce + (o))
- +#define __bm_cl_touch_rw(bm, o) dcbt_rw((bm)->addr_ce + (o))
- +#define __bm_cl_in(bm, o) be32_to_cpu(__raw_readl((bm)->addr_ce + (o)))
- +#define __bm_cl_out(bm, o, val) \
- + do { \
- + u32 *__tmpclout = (bm)->addr_ce + (o); \
- + __raw_writel(cpu_to_be32(val), __tmpclout); \
- + dcbf(__tmpclout); \
- + } while (0)
- +#define __bm_cl_invalidate(bm, o) dcbi((bm)->addr_ce + (o))
- +#define bm_cl_touch_ro(reg) __bm_cl_touch_ro(&portal->addr, BM_CL_##reg##_CENA)
- +#define bm_cl_touch_rw(reg) __bm_cl_touch_rw(&portal->addr, BM_CL_##reg##_CENA)
- +#define bm_cl_in(reg) __bm_cl_in(&portal->addr, BM_CL_##reg##_CENA)
- +#define bm_cl_out(reg, val) __bm_cl_out(&portal->addr, BM_CL_##reg##_CENA, val)
- +#define bm_cl_invalidate(reg)\
- + __bm_cl_invalidate(&portal->addr, BM_CL_##reg##_CENA)
- +
- +/* Cyclic helper for rings. FIXME: once we are able to do fine-grain perf
- + * analysis, look at using the "extra" bit in the ring index registers to avoid
- + * cyclic issues. */
- +static inline u8 bm_cyc_diff(u8 ringsize, u8 first, u8 last)
- +{
- + /* 'first' is included, 'last' is excluded */
- + if (first <= last)
- + return last - first;
- + return ringsize + last - first;
- +}
- +
- +/* Portal modes.
- + * Enum types;
- + * pmode == production mode
- + * cmode == consumption mode,
- + * Enum values use 3 letter codes. First letter matches the portal mode,
- + * remaining two letters indicate;
- + * ci == cache-inhibited portal register
- + * ce == cache-enabled portal register
- + * vb == in-band valid-bit (cache-enabled)
- + */
- +enum bm_rcr_pmode { /* matches BCSP_CFG::RPM */
- + bm_rcr_pci = 0, /* PI index, cache-inhibited */
- + bm_rcr_pce = 1, /* PI index, cache-enabled */
- + bm_rcr_pvb = 2 /* valid-bit */
- +};
- +enum bm_rcr_cmode { /* s/w-only */
- + bm_rcr_cci, /* CI index, cache-inhibited */
- + bm_rcr_cce /* CI index, cache-enabled */
- +};
- +
- +
- +/* ------------------------- */
- +/* --- Portal structures --- */
- +
- +#define BM_RCR_SIZE 8
- +
- +struct bm_rcr {
- + struct bm_rcr_entry *ring, *cursor;
- + u8 ci, available, ithresh, vbit;
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + u32 busy;
- + enum bm_rcr_pmode pmode;
- + enum bm_rcr_cmode cmode;
- +#endif
- +};
- +
- +struct bm_mc {
- + struct bm_mc_command *cr;
- + struct bm_mc_result *rr;
- + u8 rridx, vbit;
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + enum {
- + /* Can only be _mc_start()ed */
- + mc_idle,
- + /* Can only be _mc_commit()ed or _mc_abort()ed */
- + mc_user,
- + /* Can only be _mc_retry()ed */
- + mc_hw
- + } state;
- +#endif
- +};
- +
- +struct bm_addr {
- + void __iomem *addr_ce; /* cache-enabled */
- + void __iomem *addr_ci; /* cache-inhibited */
- +};
- +
- +struct bm_portal {
- + struct bm_addr addr;
- + struct bm_rcr rcr;
- + struct bm_mc mc;
- + struct bm_portal_config config;
- +} ____cacheline_aligned;
- +
- +
- +/* --------------- */
- +/* --- RCR API --- */
- +
- +/* Bit-wise logic to wrap a ring pointer by clearing the "carry bit" */
- +#define RCR_CARRYCLEAR(p) \
- + (void *)((unsigned long)(p) & (~(unsigned long)(BM_RCR_SIZE << 6)))
- +
- +/* Bit-wise logic to convert a ring pointer to a ring index */
- +static inline u8 RCR_PTR2IDX(struct bm_rcr_entry *e)
- +{
- + return ((uintptr_t)e >> 6) & (BM_RCR_SIZE - 1);
- +}
- +
- +/* Increment the 'cursor' ring pointer, taking 'vbit' into account */
- +static inline void RCR_INC(struct bm_rcr *rcr)
- +{
- + /* NB: this is odd-looking, but experiments show that it generates
- + * fast code with essentially no branching overheads. We increment to
- + * the next RCR pointer and handle overflow and 'vbit'. */
- + struct bm_rcr_entry *partial = rcr->cursor + 1;
- + rcr->cursor = RCR_CARRYCLEAR(partial);
- + if (partial != rcr->cursor)
- + rcr->vbit ^= BM_RCR_VERB_VBIT;
- +}
- +
- +static inline int bm_rcr_init(struct bm_portal *portal, enum bm_rcr_pmode pmode,
- + __maybe_unused enum bm_rcr_cmode cmode)
- +{
- + /* This use of 'register', as well as all other occurrences, is because
- + * it has been observed to generate much faster code with gcc than is
- + * otherwise the case. */
- + register struct bm_rcr *rcr = &portal->rcr;
- + u32 cfg;
- + u8 pi;
- +
- + rcr->ring = portal->addr.addr_ce + BM_CL_RCR;
- + rcr->ci = bm_in(RCR_CI_CINH) & (BM_RCR_SIZE - 1);
- +
- + pi = bm_in(RCR_PI_CINH) & (BM_RCR_SIZE - 1);
- + rcr->cursor = rcr->ring + pi;
- + rcr->vbit = (bm_in(RCR_PI_CINH) & BM_RCR_SIZE) ? BM_RCR_VERB_VBIT : 0;
- + rcr->available = BM_RCR_SIZE - 1
- + - bm_cyc_diff(BM_RCR_SIZE, rcr->ci, pi);
- + rcr->ithresh = bm_in(RCR_ITR);
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + rcr->busy = 0;
- + rcr->pmode = pmode;
- + rcr->cmode = cmode;
- +#endif
- + cfg = (bm_in(CFG) & 0xffffffe0) | (pmode & 0x3); /* BCSP_CFG::RPM */
- + bm_out(CFG, cfg);
- + return 0;
- +}
- +
- +static inline void bm_rcr_finish(struct bm_portal *portal)
- +{
- + register struct bm_rcr *rcr = &portal->rcr;
- + u8 pi = bm_in(RCR_PI_CINH) & (BM_RCR_SIZE - 1);
- + u8 ci = bm_in(RCR_CI_CINH) & (BM_RCR_SIZE - 1);
- + DPA_ASSERT(!rcr->busy);
- + if (pi != RCR_PTR2IDX(rcr->cursor))
- + pr_crit("losing uncommited RCR entries\n");
- + if (ci != rcr->ci)
- + pr_crit("missing existing RCR completions\n");
- + if (rcr->ci != RCR_PTR2IDX(rcr->cursor))
- + pr_crit("RCR destroyed unquiesced\n");
- +}
- +
- +static inline struct bm_rcr_entry *bm_rcr_start(struct bm_portal *portal)
- +{
- + register struct bm_rcr *rcr = &portal->rcr;
- + DPA_ASSERT(!rcr->busy);
- + if (!rcr->available)
- + return NULL;
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + rcr->busy = 1;
- +#endif
- + dcbz_64(rcr->cursor);
- + return rcr->cursor;
- +}
- +
- +static inline void bm_rcr_abort(struct bm_portal *portal)
- +{
- + __maybe_unused register struct bm_rcr *rcr = &portal->rcr;
- + DPA_ASSERT(rcr->busy);
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + rcr->busy = 0;
- +#endif
- +}
- +
- +static inline struct bm_rcr_entry *bm_rcr_pend_and_next(
- + struct bm_portal *portal, u8 myverb)
- +{
- + register struct bm_rcr *rcr = &portal->rcr;
- + DPA_ASSERT(rcr->busy);
- + DPA_ASSERT(rcr->pmode != bm_rcr_pvb);
- + if (rcr->available == 1)
- + return NULL;
- + rcr->cursor->__dont_write_directly__verb = myverb | rcr->vbit;
- + dcbf_64(rcr->cursor);
- + RCR_INC(rcr);
- + rcr->available--;
- + dcbz_64(rcr->cursor);
- + return rcr->cursor;
- +}
- +
- +static inline void bm_rcr_pci_commit(struct bm_portal *portal, u8 myverb)
- +{
- + register struct bm_rcr *rcr = &portal->rcr;
- + DPA_ASSERT(rcr->busy);
- + DPA_ASSERT(rcr->pmode == bm_rcr_pci);
- + rcr->cursor->__dont_write_directly__verb = myverb | rcr->vbit;
- + RCR_INC(rcr);
- + rcr->available--;
- + hwsync();
- + bm_out(RCR_PI_CINH, RCR_PTR2IDX(rcr->cursor));
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + rcr->busy = 0;
- +#endif
- +}
- +
- +static inline void bm_rcr_pce_prefetch(struct bm_portal *portal)
- +{
- + __maybe_unused register struct bm_rcr *rcr = &portal->rcr;
- + DPA_ASSERT(rcr->pmode == bm_rcr_pce);
- + bm_cl_invalidate(RCR_PI);
- + bm_cl_touch_rw(RCR_PI);
- +}
- +
- +static inline void bm_rcr_pce_commit(struct bm_portal *portal, u8 myverb)
- +{
- + register struct bm_rcr *rcr = &portal->rcr;
- + DPA_ASSERT(rcr->busy);
- + DPA_ASSERT(rcr->pmode == bm_rcr_pce);
- + rcr->cursor->__dont_write_directly__verb = myverb | rcr->vbit;
- + RCR_INC(rcr);
- + rcr->available--;
- + lwsync();
- + bm_cl_out(RCR_PI, RCR_PTR2IDX(rcr->cursor));
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + rcr->busy = 0;
- +#endif
- +}
- +
- +static inline void bm_rcr_pvb_commit(struct bm_portal *portal, u8 myverb)
- +{
- + register struct bm_rcr *rcr = &portal->rcr;
- + struct bm_rcr_entry *rcursor;
- + DPA_ASSERT(rcr->busy);
- + DPA_ASSERT(rcr->pmode == bm_rcr_pvb);
- + lwsync();
- + rcursor = rcr->cursor;
- + rcursor->__dont_write_directly__verb = myverb | rcr->vbit;
- + dcbf_64(rcursor);
- + RCR_INC(rcr);
- + rcr->available--;
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + rcr->busy = 0;
- +#endif
- +}
- +
- +static inline u8 bm_rcr_cci_update(struct bm_portal *portal)
- +{
- + register struct bm_rcr *rcr = &portal->rcr;
- + u8 diff, old_ci = rcr->ci;
- + DPA_ASSERT(rcr->cmode == bm_rcr_cci);
- + rcr->ci = bm_in(RCR_CI_CINH) & (BM_RCR_SIZE - 1);
- + diff = bm_cyc_diff(BM_RCR_SIZE, old_ci, rcr->ci);
- + rcr->available += diff;
- + return diff;
- +}
- +
- +static inline void bm_rcr_cce_prefetch(struct bm_portal *portal)
- +{
- + __maybe_unused register struct bm_rcr *rcr = &portal->rcr;
- + DPA_ASSERT(rcr->cmode == bm_rcr_cce);
- + bm_cl_touch_ro(RCR_CI);
- +}
- +
- +static inline u8 bm_rcr_cce_update(struct bm_portal *portal)
- +{
- + register struct bm_rcr *rcr = &portal->rcr;
- + u8 diff, old_ci = rcr->ci;
- + DPA_ASSERT(rcr->cmode == bm_rcr_cce);
- + rcr->ci = bm_cl_in(RCR_CI) & (BM_RCR_SIZE - 1);
- + bm_cl_invalidate(RCR_CI);
- + diff = bm_cyc_diff(BM_RCR_SIZE, old_ci, rcr->ci);
- + rcr->available += diff;
- + return diff;
- +}
- +
- +static inline u8 bm_rcr_get_ithresh(struct bm_portal *portal)
- +{
- + register struct bm_rcr *rcr = &portal->rcr;
- + return rcr->ithresh;
- +}
- +
- +static inline void bm_rcr_set_ithresh(struct bm_portal *portal, u8 ithresh)
- +{
- + register struct bm_rcr *rcr = &portal->rcr;
- + rcr->ithresh = ithresh;
- + bm_out(RCR_ITR, ithresh);
- +}
- +
- +static inline u8 bm_rcr_get_avail(struct bm_portal *portal)
- +{
- + register struct bm_rcr *rcr = &portal->rcr;
- + return rcr->available;
- +}
- +
- +static inline u8 bm_rcr_get_fill(struct bm_portal *portal)
- +{
- + register struct bm_rcr *rcr = &portal->rcr;
- + return BM_RCR_SIZE - 1 - rcr->available;
- +}
- +
- +
- +/* ------------------------------ */
- +/* --- Management command API --- */
- +
- +static inline int bm_mc_init(struct bm_portal *portal)
- +{
- + register struct bm_mc *mc = &portal->mc;
- + mc->cr = portal->addr.addr_ce + BM_CL_CR;
- + mc->rr = portal->addr.addr_ce + BM_CL_RR0;
- + mc->rridx = (__raw_readb(&mc->cr->__dont_write_directly__verb) &
- + BM_MCC_VERB_VBIT) ? 0 : 1;
- + mc->vbit = mc->rridx ? BM_MCC_VERB_VBIT : 0;
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + mc->state = mc_idle;
- +#endif
- + return 0;
- +}
- +
- +static inline void bm_mc_finish(struct bm_portal *portal)
- +{
- + __maybe_unused register struct bm_mc *mc = &portal->mc;
- + DPA_ASSERT(mc->state == mc_idle);
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + if (mc->state != mc_idle)
- + pr_crit("Losing incomplete MC command\n");
- +#endif
- +}
- +
- +static inline struct bm_mc_command *bm_mc_start(struct bm_portal *portal)
- +{
- + register struct bm_mc *mc = &portal->mc;
- + DPA_ASSERT(mc->state == mc_idle);
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + mc->state = mc_user;
- +#endif
- + dcbz_64(mc->cr);
- + return mc->cr;
- +}
- +
- +static inline void bm_mc_abort(struct bm_portal *portal)
- +{
- + __maybe_unused register struct bm_mc *mc = &portal->mc;
- + DPA_ASSERT(mc->state == mc_user);
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + mc->state = mc_idle;
- +#endif
- +}
- +
- +static inline void bm_mc_commit(struct bm_portal *portal, u8 myverb)
- +{
- + register struct bm_mc *mc = &portal->mc;
- + struct bm_mc_result *rr = mc->rr + mc->rridx;
- + DPA_ASSERT(mc->state == mc_user);
- + lwsync();
- + mc->cr->__dont_write_directly__verb = myverb | mc->vbit;
- + dcbf(mc->cr);
- + dcbit_ro(rr);
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + mc->state = mc_hw;
- +#endif
- +}
- +
- +static inline struct bm_mc_result *bm_mc_result(struct bm_portal *portal)
- +{
- + register struct bm_mc *mc = &portal->mc;
- + struct bm_mc_result *rr = mc->rr + mc->rridx;
- + DPA_ASSERT(mc->state == mc_hw);
- + /* The inactive response register's verb byte always returns zero until
- + * its command is submitted and completed. This includes the valid-bit,
- + * in case you were wondering... */
- + if (!__raw_readb(&rr->verb)) {
- + dcbit_ro(rr);
- + return NULL;
- + }
- + mc->rridx ^= 1;
- + mc->vbit ^= BM_MCC_VERB_VBIT;
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + mc->state = mc_idle;
- +#endif
- + return rr;
- +}
- +
- +
- +/* ------------------------------------- */
- +/* --- Portal interrupt register API --- */
- +
- +static inline int bm_isr_init(__always_unused struct bm_portal *portal)
- +{
- + return 0;
- +}
- +
- +static inline void bm_isr_finish(__always_unused struct bm_portal *portal)
- +{
- +}
- +
- +#define SCN_REG(bpid) BM_REG_SCN((bpid) / 32)
- +#define SCN_BIT(bpid) (0x80000000 >> (bpid & 31))
- +static inline void bm_isr_bscn_mask(struct bm_portal *portal, u8 bpid,
- + int enable)
- +{
- + u32 val;
- + DPA_ASSERT(bpid < bman_pool_max);
- + /* REG_SCN for bpid=0..31, REG_SCN+4 for bpid=32..63 */
- + val = __bm_in(&portal->addr, SCN_REG(bpid));
- + if (enable)
- + val |= SCN_BIT(bpid);
- + else
- + val &= ~SCN_BIT(bpid);
- + __bm_out(&portal->addr, SCN_REG(bpid), val);
- +}
- +
- +static inline u32 __bm_isr_read(struct bm_portal *portal, enum bm_isr_reg n)
- +{
- +#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
- + return __bm_in(&portal->addr, BM_REG_ISR + (n << 6));
- +#else
- + return __bm_in(&portal->addr, BM_REG_ISR + (n << 2));
- +#endif
- +}
- +
- +static inline void __bm_isr_write(struct bm_portal *portal, enum bm_isr_reg n,
- + u32 val)
- +{
- +#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
- + __bm_out(&portal->addr, BM_REG_ISR + (n << 6), val);
- +#else
- + __bm_out(&portal->addr, BM_REG_ISR + (n << 2), val);
- +#endif
- +}
- +
- +/* Buffer Pool Cleanup */
- +static inline int bm_shutdown_pool(struct bm_portal *p, u32 bpid)
- +{
- + struct bm_mc_command *bm_cmd;
- + struct bm_mc_result *bm_res;
- +
- + int aq_count = 0;
- + bool stop = false;
- + while (!stop) {
- + /* Acquire buffers until empty */
- + bm_cmd = bm_mc_start(p);
- + bm_cmd->acquire.bpid = bpid;
- + bm_mc_commit(p, BM_MCC_VERB_CMD_ACQUIRE | 1);
- + while (!(bm_res = bm_mc_result(p)))
- + cpu_relax();
- + if (!(bm_res->verb & BM_MCR_VERB_ACQUIRE_BUFCOUNT)) {
- + /* Pool is empty */
- + /* TBD : Should we do a few extra iterations in
- + case some other some blocks keep buffers 'on deck',
- + which may also be problematic */
- + stop = true;
- + } else
- + ++aq_count;
- + }
- + return 0;
- +}
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/bman_private.h
- @@ -0,0 +1,166 @@
- +/* Copyright 2008-2012 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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 "dpa_sys.h"
- +#include <linux/fsl_bman.h>
- +
- +/* Revision info (for errata and feature handling) */
- +#define BMAN_REV10 0x0100
- +#define BMAN_REV20 0x0200
- +#define BMAN_REV21 0x0201
- +#define QBMAN_ANY_PORTAL_IDX 0xffffffff
- +extern u16 bman_ip_rev; /* 0 if uninitialised, otherwise QMAN_REVx */
- +
- +/*
- + * Global variables of the max portal/pool number this bman version supported
- + */
- +extern u16 bman_pool_max;
- +
- +/* used by CCSR and portal interrupt code */
- +enum bm_isr_reg {
- + bm_isr_status = 0,
- + bm_isr_enable = 1,
- + bm_isr_disable = 2,
- + bm_isr_inhibit = 3
- +};
- +
- +struct bm_portal_config {
- + /* Corenet portal addresses;
- + * [0]==cache-enabled, [1]==cache-inhibited. */
- + __iomem void *addr_virt[2];
- + struct resource addr_phys[2];
- + /* Allow these to be joined in lists */
- + struct list_head list;
- + /* User-visible portal configuration settings */
- + struct bman_portal_config public_cfg;
- + /* power management saved data */
- + u32 saved_isdr;
- +};
- +
- +#ifdef CONFIG_FSL_BMAN_CONFIG
- +/* Hooks from bman_driver.c to bman_config.c */
- +int bman_init_ccsr(struct device_node *node);
- +#endif
- +
- +/* Hooks from bman_driver.c in to bman_high.c */
- +struct bman_portal *bman_create_portal(
- + struct bman_portal *portal,
- + const struct bm_portal_config *config);
- +struct bman_portal *bman_create_affine_portal(
- + const struct bm_portal_config *config);
- +struct bman_portal *bman_create_affine_slave(struct bman_portal *redirect,
- + int cpu);
- +void bman_destroy_portal(struct bman_portal *bm);
- +
- +const struct bm_portal_config *bman_destroy_affine_portal(void);
- +
- +/* Hooks from fsl_usdpaa.c to bman_driver.c */
- +struct bm_portal_config *bm_get_unused_portal(void);
- +struct bm_portal_config *bm_get_unused_portal_idx(uint32_t idx);
- +void bm_put_unused_portal(struct bm_portal_config *pcfg);
- +void bm_set_liodns(struct bm_portal_config *pcfg);
- +
- +/* Pool logic in the portal driver, during initialisation, needs to know if
- + * there's access to CCSR or not (if not, it'll cripple the pool allocator). */
- +#ifdef CONFIG_FSL_BMAN_CONFIG
- +int bman_have_ccsr(void);
- +#else
- +#define bman_have_ccsr() 0
- +#endif
- +
- +/* Stockpile build constants. The _LOW value: when bman_acquire() is called and
- + * the stockpile fill-level is <= _LOW, an acquire is attempted from h/w but it
- + * might fail (if the buffer pool is depleted). So this value provides some
- + * "stagger" in that the bman_acquire() function will only fail if lots of bufs
- + * are requested at once or if h/w has been tested a couple of times without
- + * luck. The _HIGH value: when bman_release() is called and the stockpile
- + * fill-level is >= _HIGH, a release is attempted to h/w but it might fail (if
- + * the release ring is full). So this value provides some "stagger" so that
- + * ring-access is retried a couple of times prior to the API returning a
- + * failure. The following *must* be true;
- + * BMAN_STOCKPILE_HIGH-BMAN_STOCKPILE_LOW > 8
- + * (to avoid thrashing)
- + * BMAN_STOCKPILE_SZ >= 16
- + * (as the release logic expects to either send 8 buffers to hw prior to
- + * adding the given buffers to the stockpile or add the buffers to the
- + * stockpile before sending 8 to hw, as the API must be an all-or-nothing
- + * success/fail.)
- + */
- +#define BMAN_STOCKPILE_SZ 16u /* number of bufs in per-pool cache */
- +#define BMAN_STOCKPILE_LOW 2u /* when fill is <= this, acquire from hw */
- +#define BMAN_STOCKPILE_HIGH 14u /* when fill is >= this, release to hw */
- +
- +/*************************************************/
- +/* BMan s/w corenet portal, low-level i/face */
- +/*************************************************/
- +
- +/* Used by all portal interrupt registers except 'inhibit'
- + * This mask contains all the "irqsource" bits visible to API users
- + */
- +#define BM_PIRQ_VISIBLE (BM_PIRQ_RCRI | BM_PIRQ_BSCN)
- +
- +/* These are bm_<reg>_<verb>(). So for example, bm_disable_write() means "write
- + * the disable register" rather than "disable the ability to write". */
- +#define bm_isr_status_read(bm) __bm_isr_read(bm, bm_isr_status)
- +#define bm_isr_status_clear(bm, m) __bm_isr_write(bm, bm_isr_status, m)
- +#define bm_isr_enable_read(bm) __bm_isr_read(bm, bm_isr_enable)
- +#define bm_isr_enable_write(bm, v) __bm_isr_write(bm, bm_isr_enable, v)
- +#define bm_isr_disable_read(bm) __bm_isr_read(bm, bm_isr_disable)
- +#define bm_isr_disable_write(bm, v) __bm_isr_write(bm, bm_isr_disable, v)
- +#define bm_isr_inhibit(bm) __bm_isr_write(bm, bm_isr_inhibit, 1)
- +#define bm_isr_uninhibit(bm) __bm_isr_write(bm, bm_isr_inhibit, 0)
- +
- +#ifdef CONFIG_FSL_BMAN_CONFIG
- +/* Set depletion thresholds associated with a buffer pool. Requires that the
- + * operating system have access to Bman CCSR (ie. compiled in support and
- + * run-time access courtesy of the device-tree). */
- +int bm_pool_set(u32 bpid, const u32 *thresholds);
- +#define BM_POOL_THRESH_SW_ENTER 0
- +#define BM_POOL_THRESH_SW_EXIT 1
- +#define BM_POOL_THRESH_HW_ENTER 2
- +#define BM_POOL_THRESH_HW_EXIT 3
- +
- +/* Read the free buffer count for a given buffer */
- +u32 bm_pool_free_buffers(u32 bpid);
- +
- +__init int bman_init(void);
- +__init int bman_resource_init(void);
- +
- +const struct bm_portal_config *bman_get_bm_portal_config(
- + struct bman_portal *portal);
- +
- +/* power management */
- +#ifdef CONFIG_SUSPEND
- +void suspend_unused_bportal(void);
- +void resume_unused_bportal(void);
- +#endif
- +
- +#endif /* CONFIG_FSL_BMAN_CONFIG */
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/bman_test.c
- @@ -0,0 +1,56 @@
- +/* Copyright 2008-2011 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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 "bman_test.h"
- +
- +MODULE_AUTHOR("Geoff Thorpe");
- +MODULE_LICENSE("Dual BSD/GPL");
- +MODULE_DESCRIPTION("Bman testing");
- +
- +static int test_init(void)
- +{
- +#ifdef CONFIG_FSL_BMAN_TEST_HIGH
- + int loop = 1;
- + while (loop--)
- + bman_test_high();
- +#endif
- +#ifdef CONFIG_FSL_BMAN_TEST_THRESH
- + bman_test_thresh();
- +#endif
- + return 0;
- +}
- +
- +static void test_exit(void)
- +{
- +}
- +
- +module_init(test_init);
- +module_exit(test_exit);
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/bman_test.h
- @@ -0,0 +1,44 @@
- +/* Copyright 2008-2011 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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 <linux/kernel.h>
- +#include <linux/errno.h>
- +#include <linux/io.h>
- +#include <linux/slab.h>
- +#include <linux/module.h>
- +#include <linux/interrupt.h>
- +#include <linux/delay.h>
- +#include <linux/kthread.h>
- +
- +#include <linux/fsl_bman.h>
- +
- +void bman_test_high(void);
- +void bman_test_thresh(void);
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/bman_test_high.c
- @@ -0,0 +1,183 @@
- +/* Copyright 2008-2011 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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 "bman_test.h"
- +#include "bman_private.h"
- +
- +/*************/
- +/* constants */
- +/*************/
- +
- +#define PORTAL_OPAQUE ((void *)0xf00dbeef)
- +#define POOL_OPAQUE ((void *)0xdeadabba)
- +#define NUM_BUFS 93
- +#define LOOPS 3
- +#define BMAN_TOKEN_MASK 0x00FFFFFFFFFFLLU
- +
- +/***************/
- +/* global vars */
- +/***************/
- +
- +static struct bman_pool *pool;
- +static int depleted;
- +static struct bm_buffer bufs_in[NUM_BUFS] ____cacheline_aligned;
- +static struct bm_buffer bufs_out[NUM_BUFS] ____cacheline_aligned;
- +static int bufs_received;
- +
- +/* Predeclare the callback so we can instantiate pool parameters */
- +static void depletion_cb(struct bman_portal *, struct bman_pool *, void *, int);
- +
- +/**********************/
- +/* internal functions */
- +/**********************/
- +
- +static void bufs_init(void)
- +{
- + int i;
- + for (i = 0; i < NUM_BUFS; i++)
- + bm_buffer_set64(&bufs_in[i], 0xfedc01234567LLU * i);
- + bufs_received = 0;
- +}
- +
- +static inline int bufs_cmp(const struct bm_buffer *a, const struct bm_buffer *b)
- +{
- + if ((bman_ip_rev == BMAN_REV20) || (bman_ip_rev == BMAN_REV21)) {
- +
- + /* On SoCs with Bman revison 2.0, Bman only respects the 40
- + * LS-bits of buffer addresses, masking off the upper 8-bits on
- + * release commands. The API provides for 48-bit addresses
- + * because some SoCs support all 48-bits. When generating
- + * garbage addresses for testing, we either need to zero the
- + * upper 8-bits when releasing to Bman (otherwise we'll be
- + * disappointed when the buffers we acquire back from Bman
- + * don't match), or we need to mask the upper 8-bits off when
- + * comparing. We do the latter.
- + */
- + if ((bm_buffer_get64(a) & BMAN_TOKEN_MASK)
- + < (bm_buffer_get64(b) & BMAN_TOKEN_MASK))
- + return -1;
- + if ((bm_buffer_get64(a) & BMAN_TOKEN_MASK)
- + > (bm_buffer_get64(b) & BMAN_TOKEN_MASK))
- + return 1;
- + } else {
- + if (bm_buffer_get64(a) < bm_buffer_get64(b))
- + return -1;
- + if (bm_buffer_get64(a) > bm_buffer_get64(b))
- + return 1;
- + }
- +
- + return 0;
- +}
- +
- +static void bufs_confirm(void)
- +{
- + int i, j;
- + for (i = 0; i < NUM_BUFS; i++) {
- + int matches = 0;
- + for (j = 0; j < NUM_BUFS; j++)
- + if (!bufs_cmp(&bufs_in[i], &bufs_out[j]))
- + matches++;
- + BUG_ON(matches != 1);
- + }
- +}
- +
- +/********/
- +/* test */
- +/********/
- +
- +static void depletion_cb(struct bman_portal *__portal, struct bman_pool *__pool,
- + void *pool_ctx, int __depleted)
- +{
- + BUG_ON(__pool != pool);
- + BUG_ON(pool_ctx != POOL_OPAQUE);
- + depleted = __depleted;
- +}
- +
- +void bman_test_high(void)
- +{
- + struct bman_pool_params pparams = {
- + .flags = BMAN_POOL_FLAG_DEPLETION | BMAN_POOL_FLAG_DYNAMIC_BPID,
- + .cb = depletion_cb,
- + .cb_ctx = POOL_OPAQUE,
- + };
- + int i, loops = LOOPS;
- + struct bm_buffer tmp_buf;
- +
- + bufs_init();
- +
- + pr_info("BMAN: --- starting high-level test ---\n");
- +
- + pool = bman_new_pool(&pparams);
- + BUG_ON(!pool);
- +
- + /*******************/
- + /* Release buffers */
- + /*******************/
- +do_loop:
- + i = 0;
- + while (i < NUM_BUFS) {
- + u32 flags = BMAN_RELEASE_FLAG_WAIT;
- + int num = 8;
- + if ((i + num) > NUM_BUFS)
- + num = NUM_BUFS - i;
- + if ((i + num) == NUM_BUFS)
- + flags |= BMAN_RELEASE_FLAG_WAIT_SYNC;
- + if (bman_release(pool, bufs_in + i, num, flags))
- + panic("bman_release() failed\n");
- + i += num;
- + }
- +
- + /*******************/
- + /* Acquire buffers */
- + /*******************/
- + while (i > 0) {
- + int tmp, num = 8;
- + if (num > i)
- + num = i;
- + tmp = bman_acquire(pool, bufs_out + i - num, num, 0);
- + BUG_ON(tmp != num);
- + i -= num;
- + }
- +
- + i = bman_acquire(pool, &tmp_buf, 1, 0);
- + BUG_ON(i > 0);
- +
- + bufs_confirm();
- +
- + if (--loops)
- + goto do_loop;
- +
- + /************/
- + /* Clean up */
- + /************/
- + bman_free_pool(pool);
- + pr_info("BMAN: --- finished high-level test ---\n");
- +}
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/bman_test_thresh.c
- @@ -0,0 +1,196 @@
- +/* Copyright 2010-2011 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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 "bman_test.h"
- +
- +/* Test constants */
- +#define TEST_NUMBUFS 129728
- +#define TEST_EXIT 129536
- +#define TEST_ENTRY 129024
- +
- +struct affine_test_data {
- + struct task_struct *t;
- + int cpu;
- + int expect_affinity;
- + int drain;
- + int num_enter;
- + int num_exit;
- + struct list_head node;
- + struct completion wakethread;
- + struct completion wakeparent;
- +};
- +
- +static void cb_depletion(struct bman_portal *portal,
- + struct bman_pool *pool,
- + void *opaque,
- + int depleted)
- +{
- + struct affine_test_data *data = opaque;
- + int c = smp_processor_id();
- + pr_info("cb_depletion: bpid=%d, depleted=%d, cpu=%d, original=%d\n",
- + bman_get_params(pool)->bpid, !!depleted, c, data->cpu);
- + /* We should be executing on the CPU of the thread that owns the pool if
- + * and that CPU has an affine portal (ie. it isn't slaved). */
- + BUG_ON((c != data->cpu) && data->expect_affinity);
- + BUG_ON((c == data->cpu) && !data->expect_affinity);
- + if (depleted)
- + data->num_enter++;
- + else
- + data->num_exit++;
- +}
- +
- +/* Params used to set up a pool, this also dynamically allocates a BPID */
- +static const struct bman_pool_params params_nocb = {
- + .flags = BMAN_POOL_FLAG_DYNAMIC_BPID | BMAN_POOL_FLAG_THRESH,
- + .thresholds = { TEST_ENTRY, TEST_EXIT, 0, 0 }
- +};
- +
- +/* Params used to set up each cpu's pool with callbacks enabled */
- +static struct bman_pool_params params_cb = {
- + .bpid = 0, /* will be replaced to match pool_nocb */
- + .flags = BMAN_POOL_FLAG_DEPLETION,
- + .cb = cb_depletion
- +};
- +
- +static struct bman_pool *pool_nocb;
- +static LIST_HEAD(threads);
- +
- +static int affine_test(void *__data)
- +{
- + struct bman_pool *pool;
- + struct affine_test_data *data = __data;
- + struct bman_pool_params my_params = params_cb;
- +
- + pr_info("thread %d: starting\n", data->cpu);
- + /* create the pool */
- + my_params.cb_ctx = data;
- + pool = bman_new_pool(&my_params);
- + BUG_ON(!pool);
- + complete(&data->wakeparent);
- + wait_for_completion(&data->wakethread);
- + init_completion(&data->wakethread);
- +
- + /* if we're the drainer, we get signalled for that */
- + if (data->drain) {
- + struct bm_buffer buf;
- + int ret;
- + pr_info("thread %d: draining...\n", data->cpu);
- + do {
- + ret = bman_acquire(pool, &buf, 1, 0);
- + } while (ret > 0);
- + pr_info("thread %d: draining done.\n", data->cpu);
- + complete(&data->wakeparent);
- + wait_for_completion(&data->wakethread);
- + init_completion(&data->wakethread);
- + }
- +
- + /* cleanup */
- + bman_free_pool(pool);
- + while (!kthread_should_stop())
- + cpu_relax();
- + pr_info("thread %d: exiting\n", data->cpu);
- + return 0;
- +}
- +
- +static struct affine_test_data *start_affine_test(int cpu, int drain)
- +{
- + struct affine_test_data *data = kmalloc(sizeof(*data), GFP_KERNEL);
- +
- + if (!data)
- + return NULL;
- + data->cpu = cpu;
- + data->expect_affinity = cpumask_test_cpu(cpu, bman_affine_cpus());
- + data->drain = drain;
- + data->num_enter = 0;
- + data->num_exit = 0;
- + init_completion(&data->wakethread);
- + init_completion(&data->wakeparent);
- + list_add_tail(&data->node, &threads);
- + data->t = kthread_create(affine_test, data, "threshtest%d", cpu);
- + BUG_ON(IS_ERR(data->t));
- + kthread_bind(data->t, cpu);
- + wake_up_process(data->t);
- + return data;
- +}
- +
- +void bman_test_thresh(void)
- +{
- + int loop = TEST_NUMBUFS;
- + int ret, num_cpus = 0;
- + struct affine_test_data *data, *drainer = NULL;
- +
- + pr_info("bman_test_thresh: start\n");
- +
- + /* allocate a BPID and seed it */
- + pool_nocb = bman_new_pool(¶ms_nocb);
- + BUG_ON(!pool_nocb);
- + while (loop--) {
- + struct bm_buffer buf;
- + bm_buffer_set64(&buf, 0x0badbeef + loop);
- + ret = bman_release(pool_nocb, &buf, 1,
- + BMAN_RELEASE_FLAG_WAIT);
- + BUG_ON(ret);
- + }
- + while (!bman_rcr_is_empty())
- + cpu_relax();
- + pr_info("bman_test_thresh: buffers are in\n");
- +
- + /* create threads and wait for them to create pools */
- + params_cb.bpid = bman_get_params(pool_nocb)->bpid;
- + for_each_cpu(loop, cpu_online_mask) {
- + data = start_affine_test(loop, drainer ? 0 : 1);
- + BUG_ON(!data);
- + if (!drainer)
- + drainer = data;
- + num_cpus++;
- + wait_for_completion(&data->wakeparent);
- + }
- +
- + /* signal the drainer to start draining */
- + complete(&drainer->wakethread);
- + wait_for_completion(&drainer->wakeparent);
- + init_completion(&drainer->wakeparent);
- +
- + /* tear down */
- + list_for_each_entry_safe(data, drainer, &threads, node) {
- + complete(&data->wakethread);
- + ret = kthread_stop(data->t);
- + BUG_ON(ret);
- + list_del(&data->node);
- + /* check that we get the expected callbacks (and no others) */
- + BUG_ON(data->num_enter != 1);
- + BUG_ON(data->num_exit != 0);
- + kfree(data);
- + }
- + bman_free_pool(pool_nocb);
- +
- + pr_info("bman_test_thresh: done\n");
- +}
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/dpa_alloc.c
- @@ -0,0 +1,706 @@
- +/* Copyright 2009-2012 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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 "dpa_sys.h"
- +#include <linux/fsl_qman.h>
- +#include <linux/fsl_bman.h>
- +
- +/* Qman and Bman APIs are front-ends to the common code; */
- +
- +static DECLARE_DPA_ALLOC(bpalloc); /* BPID allocator */
- +static DECLARE_DPA_ALLOC(fqalloc); /* FQID allocator */
- +static DECLARE_DPA_ALLOC(qpalloc); /* pool-channel allocator */
- +static DECLARE_DPA_ALLOC(cgralloc); /* CGR ID allocator */
- +static DECLARE_DPA_ALLOC(ceetm0_challoc); /* CEETM Channel ID allocator */
- +static DECLARE_DPA_ALLOC(ceetm0_lfqidalloc); /* CEETM LFQID allocator */
- +static DECLARE_DPA_ALLOC(ceetm1_challoc); /* CEETM Channel ID allocator */
- +static DECLARE_DPA_ALLOC(ceetm1_lfqidalloc); /* CEETM LFQID allocator */
- +
- +/* This is a sort-of-conditional dpa_alloc_free() routine. Eg. when releasing
- + * FQIDs (probably from user-space), it can filter out those that aren't in the
- + * OOS state (better to leak a h/w resource than to crash). This function
- + * returns the number of invalid IDs that were not released. */
- +static u32 release_id_range(struct dpa_alloc *alloc, u32 id, u32 count,
- + int (*is_valid)(u32 id))
- +{
- + int valid_mode = 0;
- + u32 loop = id, total_invalid = 0;
- + while (loop < (id + count)) {
- + int isvalid = is_valid ? is_valid(loop) : 1;
- + if (!valid_mode) {
- + /* We're looking for a valid ID to terminate an invalid
- + * range */
- + if (isvalid) {
- + /* We finished a range of invalid IDs, a valid
- + * range is now underway */
- + valid_mode = 1;
- + count -= (loop - id);
- + id = loop;
- + } else
- + total_invalid++;
- + } else {
- + /* We're looking for an invalid ID to terminate a
- + * valid range */
- + if (!isvalid) {
- + /* Release the range of valid IDs, an unvalid
- + * range is now underway */
- + if (loop > id)
- + dpa_alloc_free(alloc, id, loop - id);
- + valid_mode = 0;
- + }
- + }
- + loop++;
- + }
- + /* Release any unterminated range of valid IDs */
- + if (valid_mode && count)
- + dpa_alloc_free(alloc, id, count);
- + return total_invalid;
- +}
- +
- +/* BPID allocator front-end */
- +
- +int bman_alloc_bpid_range(u32 *result, u32 count, u32 align, int partial)
- +{
- + return dpa_alloc_new(&bpalloc, result, count, align, partial);
- +}
- +EXPORT_SYMBOL(bman_alloc_bpid_range);
- +
- +static int bp_cleanup(u32 bpid)
- +{
- + return bman_shutdown_pool(bpid) == 0;
- +}
- +void bman_release_bpid_range(u32 bpid, u32 count)
- +{
- + u32 total_invalid = release_id_range(&bpalloc, bpid, count, bp_cleanup);
- + if (total_invalid)
- + pr_err("BPID range [%d..%d] (%d) had %d leaks\n",
- + bpid, bpid + count - 1, count, total_invalid);
- +}
- +EXPORT_SYMBOL(bman_release_bpid_range);
- +
- +void bman_seed_bpid_range(u32 bpid, u32 count)
- +{
- + dpa_alloc_seed(&bpalloc, bpid, count);
- +}
- +EXPORT_SYMBOL(bman_seed_bpid_range);
- +
- +int bman_reserve_bpid_range(u32 bpid, u32 count)
- +{
- + return dpa_alloc_reserve(&bpalloc, bpid, count);
- +}
- +EXPORT_SYMBOL(bman_reserve_bpid_range);
- +
- +
- +/* FQID allocator front-end */
- +
- +int qman_alloc_fqid_range(u32 *result, u32 count, u32 align, int partial)
- +{
- + return dpa_alloc_new(&fqalloc, result, count, align, partial);
- +}
- +EXPORT_SYMBOL(qman_alloc_fqid_range);
- +
- +static int fq_cleanup(u32 fqid)
- +{
- + return qman_shutdown_fq(fqid) == 0;
- +}
- +void qman_release_fqid_range(u32 fqid, u32 count)
- +{
- + u32 total_invalid = release_id_range(&fqalloc, fqid, count, fq_cleanup);
- + if (total_invalid)
- + pr_err("FQID range [%d..%d] (%d) had %d leaks\n",
- + fqid, fqid + count - 1, count, total_invalid);
- +}
- +EXPORT_SYMBOL(qman_release_fqid_range);
- +
- +int qman_reserve_fqid_range(u32 fqid, u32 count)
- +{
- + return dpa_alloc_reserve(&fqalloc, fqid, count);
- +}
- +EXPORT_SYMBOL(qman_reserve_fqid_range);
- +
- +void qman_seed_fqid_range(u32 fqid, u32 count)
- +{
- + dpa_alloc_seed(&fqalloc, fqid, count);
- +}
- +EXPORT_SYMBOL(qman_seed_fqid_range);
- +
- +/* Pool-channel allocator front-end */
- +
- +int qman_alloc_pool_range(u32 *result, u32 count, u32 align, int partial)
- +{
- + return dpa_alloc_new(&qpalloc, result, count, align, partial);
- +}
- +EXPORT_SYMBOL(qman_alloc_pool_range);
- +
- +static int qpool_cleanup(u32 qp)
- +{
- + /* We query all FQDs starting from
- + * FQID 1 until we get an "invalid FQID" error, looking for non-OOS FQDs
- + * whose destination channel is the pool-channel being released.
- + * When a non-OOS FQD is found we attempt to clean it up */
- + struct qman_fq fq = {
- + .fqid = 1
- + };
- + int err;
- + do {
- + struct qm_mcr_queryfq_np np;
- + err = qman_query_fq_np(&fq, &np);
- + if (err)
- + /* FQID range exceeded, found no problems */
- + return 1;
- + if ((np.state & QM_MCR_NP_STATE_MASK) != QM_MCR_NP_STATE_OOS) {
- + struct qm_fqd fqd;
- + err = qman_query_fq(&fq, &fqd);
- + BUG_ON(err);
- + if (fqd.dest.channel == qp) {
- + /* The channel is the FQ's target, clean it */
- + if (qman_shutdown_fq(fq.fqid) != 0)
- + /* Couldn't shut down the FQ
- + so the pool must be leaked */
- + return 0;
- + }
- + }
- + /* Move to the next FQID */
- + fq.fqid++;
- + } while (1);
- +}
- +void qman_release_pool_range(u32 qp, u32 count)
- +{
- + u32 total_invalid = release_id_range(&qpalloc, qp,
- + count, qpool_cleanup);
- + if (total_invalid) {
- + /* Pool channels are almost always used individually */
- + if (count == 1)
- + pr_err("Pool channel 0x%x had %d leaks\n",
- + qp, total_invalid);
- + else
- + pr_err("Pool channels [%d..%d] (%d) had %d leaks\n",
- + qp, qp + count - 1, count, total_invalid);
- + }
- +}
- +EXPORT_SYMBOL(qman_release_pool_range);
- +
- +
- +void qman_seed_pool_range(u32 poolid, u32 count)
- +{
- + dpa_alloc_seed(&qpalloc, poolid, count);
- +
- +}
- +EXPORT_SYMBOL(qman_seed_pool_range);
- +
- +int qman_reserve_pool_range(u32 poolid, u32 count)
- +{
- + return dpa_alloc_reserve(&qpalloc, poolid, count);
- +}
- +EXPORT_SYMBOL(qman_reserve_pool_range);
- +
- +
- +/* CGR ID allocator front-end */
- +
- +int qman_alloc_cgrid_range(u32 *result, u32 count, u32 align, int partial)
- +{
- + return dpa_alloc_new(&cgralloc, result, count, align, partial);
- +}
- +EXPORT_SYMBOL(qman_alloc_cgrid_range);
- +
- +static int cqr_cleanup(u32 cgrid)
- +{
- + /* We query all FQDs starting from
- + * FQID 1 until we get an "invalid FQID" error, looking for non-OOS FQDs
- + * whose CGR is the CGR being released.
- + */
- + struct qman_fq fq = {
- + .fqid = 1
- + };
- + int err;
- + do {
- + struct qm_mcr_queryfq_np np;
- + err = qman_query_fq_np(&fq, &np);
- + if (err)
- + /* FQID range exceeded, found no problems */
- + return 1;
- + if ((np.state & QM_MCR_NP_STATE_MASK) != QM_MCR_NP_STATE_OOS) {
- + struct qm_fqd fqd;
- + err = qman_query_fq(&fq, &fqd);
- + BUG_ON(err);
- + if ((fqd.fq_ctrl & QM_FQCTRL_CGE) &&
- + (fqd.cgid == cgrid)) {
- + pr_err("CRGID 0x%x is being used by FQID 0x%x,"
- + " CGR will be leaked\n",
- + cgrid, fq.fqid);
- + return 1;
- + }
- + }
- + /* Move to the next FQID */
- + fq.fqid++;
- + } while (1);
- +}
- +
- +void qman_release_cgrid_range(u32 cgrid, u32 count)
- +{
- + u32 total_invalid = release_id_range(&cgralloc, cgrid,
- + count, cqr_cleanup);
- + if (total_invalid)
- + pr_err("CGRID range [%d..%d] (%d) had %d leaks\n",
- + cgrid, cgrid + count - 1, count, total_invalid);
- +}
- +EXPORT_SYMBOL(qman_release_cgrid_range);
- +
- +void qman_seed_cgrid_range(u32 cgrid, u32 count)
- +{
- + dpa_alloc_seed(&cgralloc, cgrid, count);
- +
- +}
- +EXPORT_SYMBOL(qman_seed_cgrid_range);
- +
- +/* CEETM CHANNEL ID allocator front-end */
- +int qman_alloc_ceetm0_channel_range(u32 *result, u32 count, u32 align,
- + int partial)
- +{
- + return dpa_alloc_new(&ceetm0_challoc, result, count, align, partial);
- +}
- +EXPORT_SYMBOL(qman_alloc_ceetm0_channel_range);
- +
- +int qman_alloc_ceetm1_channel_range(u32 *result, u32 count, u32 align,
- + int partial)
- +{
- + return dpa_alloc_new(&ceetm1_challoc, result, count, align, partial);
- +}
- +EXPORT_SYMBOL(qman_alloc_ceetm1_channel_range);
- +
- +void qman_release_ceetm0_channel_range(u32 channelid, u32 count)
- +{
- + u32 total_invalid;
- +
- + total_invalid = release_id_range(&ceetm0_challoc, channelid, count,
- + NULL);
- + if (total_invalid)
- + pr_err("CEETM channel range [%d..%d] (%d) had %d leaks\n",
- + channelid, channelid + count - 1, count, total_invalid);
- +}
- +EXPORT_SYMBOL(qman_release_ceetm0_channel_range);
- +
- +void qman_seed_ceetm0_channel_range(u32 channelid, u32 count)
- +{
- + dpa_alloc_seed(&ceetm0_challoc, channelid, count);
- +
- +}
- +EXPORT_SYMBOL(qman_seed_ceetm0_channel_range);
- +
- +void qman_release_ceetm1_channel_range(u32 channelid, u32 count)
- +{
- + u32 total_invalid;
- + total_invalid = release_id_range(&ceetm1_challoc, channelid, count,
- + NULL);
- + if (total_invalid)
- + pr_err("CEETM channel range [%d..%d] (%d) had %d leaks\n",
- + channelid, channelid + count - 1, count, total_invalid);
- +}
- +EXPORT_SYMBOL(qman_release_ceetm1_channel_range);
- +
- +void qman_seed_ceetm1_channel_range(u32 channelid, u32 count)
- +{
- + dpa_alloc_seed(&ceetm1_challoc, channelid, count);
- +
- +}
- +EXPORT_SYMBOL(qman_seed_ceetm1_channel_range);
- +
- +/* CEETM LFQID allocator front-end */
- +int qman_alloc_ceetm0_lfqid_range(u32 *result, u32 count, u32 align,
- + int partial)
- +{
- + return dpa_alloc_new(&ceetm0_lfqidalloc, result, count, align, partial);
- +}
- +EXPORT_SYMBOL(qman_alloc_ceetm0_lfqid_range);
- +
- +int qman_alloc_ceetm1_lfqid_range(u32 *result, u32 count, u32 align,
- + int partial)
- +{
- + return dpa_alloc_new(&ceetm1_lfqidalloc, result, count, align, partial);
- +}
- +EXPORT_SYMBOL(qman_alloc_ceetm1_lfqid_range);
- +
- +void qman_release_ceetm0_lfqid_range(u32 lfqid, u32 count)
- +{
- + u32 total_invalid;
- +
- + total_invalid = release_id_range(&ceetm0_lfqidalloc, lfqid, count,
- + NULL);
- + if (total_invalid)
- + pr_err("CEETM LFQID range [0x%x..0x%x] (%d) had %d leaks\n",
- + lfqid, lfqid + count - 1, count, total_invalid);
- +}
- +EXPORT_SYMBOL(qman_release_ceetm0_lfqid_range);
- +
- +void qman_seed_ceetm0_lfqid_range(u32 lfqid, u32 count)
- +{
- + dpa_alloc_seed(&ceetm0_lfqidalloc, lfqid, count);
- +
- +}
- +EXPORT_SYMBOL(qman_seed_ceetm0_lfqid_range);
- +
- +void qman_release_ceetm1_lfqid_range(u32 lfqid, u32 count)
- +{
- + u32 total_invalid;
- +
- + total_invalid = release_id_range(&ceetm1_lfqidalloc, lfqid, count,
- + NULL);
- + if (total_invalid)
- + pr_err("CEETM LFQID range [0x%x..0x%x] (%d) had %d leaks\n",
- + lfqid, lfqid + count - 1, count, total_invalid);
- +}
- +EXPORT_SYMBOL(qman_release_ceetm1_lfqid_range);
- +
- +void qman_seed_ceetm1_lfqid_range(u32 lfqid, u32 count)
- +{
- + dpa_alloc_seed(&ceetm1_lfqidalloc, lfqid, count);
- +
- +}
- +EXPORT_SYMBOL(qman_seed_ceetm1_lfqid_range);
- +
- +
- +/* Everything else is the common backend to all the allocators */
- +
- +/* The allocator is a (possibly-empty) list of these; */
- +struct alloc_node {
- + struct list_head list;
- + u32 base;
- + u32 num;
- + /* refcount and is_alloced are only set
- + when the node is in the used list */
- + unsigned int refcount;
- + int is_alloced;
- +};
- +
- +/* #define DPA_ALLOC_DEBUG */
- +
- +#ifdef DPA_ALLOC_DEBUG
- +#define DPRINT pr_info
- +static void DUMP(struct dpa_alloc *alloc)
- +{
- + int off = 0;
- + char buf[256];
- + struct alloc_node *p;
- + pr_info("Free Nodes\n");
- + list_for_each_entry(p, &alloc->free, list) {
- + if (off < 255)
- + off += snprintf(buf + off, 255-off, "{%d,%d}",
- + p->base, p->base + p->num - 1);
- + }
- + pr_info("%s\n", buf);
- +
- + off = 0;
- + pr_info("Used Nodes\n");
- + list_for_each_entry(p, &alloc->used, list) {
- + if (off < 255)
- + off += snprintf(buf + off, 255-off, "{%d,%d}",
- + p->base, p->base + p->num - 1);
- + }
- + pr_info("%s\n", buf);
- +
- +
- +
- +}
- +#else
- +#define DPRINT(x...)
- +#define DUMP(a)
- +#endif
- +
- +int dpa_alloc_new(struct dpa_alloc *alloc, u32 *result, u32 count, u32 align,
- + int partial)
- +{
- + struct alloc_node *i = NULL, *next_best = NULL, *used_node = NULL;
- + u32 base, next_best_base = 0, num = 0, next_best_num = 0;
- + struct alloc_node *margin_left, *margin_right;
- +
- + *result = (u32)-1;
- + DPRINT("alloc_range(%d,%d,%d)\n", count, align, partial);
- + DUMP(alloc);
- + /* If 'align' is 0, it should behave as though it was 1 */
- + if (!align)
- + align = 1;
- + margin_left = kmalloc(sizeof(*margin_left), GFP_KERNEL);
- + if (!margin_left)
- + goto err;
- + margin_right = kmalloc(sizeof(*margin_right), GFP_KERNEL);
- + if (!margin_right) {
- + kfree(margin_left);
- + goto err;
- + }
- + spin_lock_irq(&alloc->lock);
- + list_for_each_entry(i, &alloc->free, list) {
- + base = (i->base + align - 1) / align;
- + base *= align;
- + if ((base - i->base) >= i->num)
- + /* alignment is impossible, regardless of count */
- + continue;
- + num = i->num - (base - i->base);
- + if (num >= count) {
- + /* this one will do nicely */
- + num = count;
- + goto done;
- + }
- + if (num > next_best_num) {
- + next_best = i;
- + next_best_base = base;
- + next_best_num = num;
- + }
- + }
- + if (partial && next_best) {
- + i = next_best;
- + base = next_best_base;
- + num = next_best_num;
- + } else
- + i = NULL;
- +done:
- + if (i) {
- + if (base != i->base) {
- + margin_left->base = i->base;
- + margin_left->num = base - i->base;
- + list_add_tail(&margin_left->list, &i->list);
- + } else
- + kfree(margin_left);
- + if ((base + num) < (i->base + i->num)) {
- + margin_right->base = base + num;
- + margin_right->num = (i->base + i->num) -
- + (base + num);
- + list_add(&margin_right->list, &i->list);
- + } else
- + kfree(margin_right);
- + list_del(&i->list);
- + kfree(i);
- + *result = base;
- + } else {
- + spin_unlock_irq(&alloc->lock);
- + kfree(margin_left);
- + kfree(margin_right);
- + }
- +
- +err:
- + DPRINT("returning %d\n", i ? num : -ENOMEM);
- + DUMP(alloc);
- + if (!i)
- + return -ENOMEM;
- +
- + /* Add the allocation to the used list with a refcount of 1 */
- + used_node = kmalloc(sizeof(*used_node), GFP_KERNEL);
- + if (!used_node) {
- + spin_unlock_irq(&alloc->lock);
- + return -ENOMEM;
- + }
- + used_node->base = *result;
- + used_node->num = num;
- + used_node->refcount = 1;
- + used_node->is_alloced = 1;
- + list_add_tail(&used_node->list, &alloc->used);
- + spin_unlock_irq(&alloc->lock);
- + return (int)num;
- +}
- +
- +/* Allocate the list node using GFP_ATOMIC, because we *really* want to avoid
- + * forcing error-handling on to users in the deallocation path. */
- +static void _dpa_alloc_free(struct dpa_alloc *alloc, u32 base_id, u32 count)
- +{
- + struct alloc_node *i, *node = kmalloc(sizeof(*node), GFP_ATOMIC);
- + BUG_ON(!node);
- + DPRINT("release_range(%d,%d)\n", base_id, count);
- + DUMP(alloc);
- + BUG_ON(!count);
- + spin_lock_irq(&alloc->lock);
- +
- +
- + node->base = base_id;
- + node->num = count;
- + list_for_each_entry(i, &alloc->free, list) {
- + if (i->base >= node->base) {
- + /* BUG_ON(any overlapping) */
- + BUG_ON(i->base < (node->base + node->num));
- + list_add_tail(&node->list, &i->list);
- + goto done;
- + }
- + }
- + list_add_tail(&node->list, &alloc->free);
- +done:
- + /* Merge to the left */
- + i = list_entry(node->list.prev, struct alloc_node, list);
- + if (node->list.prev != &alloc->free) {
- + BUG_ON((i->base + i->num) > node->base);
- + if ((i->base + i->num) == node->base) {
- + node->base = i->base;
- + node->num += i->num;
- + list_del(&i->list);
- + kfree(i);
- + }
- + }
- + /* Merge to the right */
- + i = list_entry(node->list.next, struct alloc_node, list);
- + if (node->list.next != &alloc->free) {
- + BUG_ON((node->base + node->num) > i->base);
- + if ((node->base + node->num) == i->base) {
- + node->num += i->num;
- + list_del(&i->list);
- + kfree(i);
- + }
- + }
- + spin_unlock_irq(&alloc->lock);
- + DUMP(alloc);
- +}
- +
- +
- +void dpa_alloc_free(struct dpa_alloc *alloc, u32 base_id, u32 count)
- +{
- + struct alloc_node *i = NULL;
- + spin_lock_irq(&alloc->lock);
- +
- + /* First find the node in the used list and decrement its ref count */
- + list_for_each_entry(i, &alloc->used, list) {
- + if (i->base == base_id && i->num == count) {
- + --i->refcount;
- + if (i->refcount == 0) {
- + list_del(&i->list);
- + spin_unlock_irq(&alloc->lock);
- + if (i->is_alloced)
- + _dpa_alloc_free(alloc, base_id, count);
- + kfree(i);
- + return;
- + }
- + spin_unlock_irq(&alloc->lock);
- + return;
- + }
- + }
- + /* Couldn't find the allocation */
- + pr_err("Attempt to free ID 0x%x COUNT %d that wasn't alloc'd or reserved\n",
- + base_id, count);
- + spin_unlock_irq(&alloc->lock);
- +}
- +
- +void dpa_alloc_seed(struct dpa_alloc *alloc, u32 base_id, u32 count)
- +{
- + /* Same as free but no previous allocation checking is needed */
- + _dpa_alloc_free(alloc, base_id, count);
- +}
- +
- +
- +int dpa_alloc_reserve(struct dpa_alloc *alloc, u32 base, u32 num)
- +{
- + struct alloc_node *i = NULL, *used_node;
- +
- + DPRINT("alloc_reserve(%d,%d)\n", base, num);
- + DUMP(alloc);
- +
- + spin_lock_irq(&alloc->lock);
- +
- + /* Check for the node in the used list.
- + If found, increase it's refcount */
- + list_for_each_entry(i, &alloc->used, list) {
- + if ((i->base == base) && (i->num == num)) {
- + ++i->refcount;
- + spin_unlock_irq(&alloc->lock);
- + return 0;
- + }
- + if ((base >= i->base) && (base < (i->base + i->num))) {
- + /* This is an attempt to reserve a region that was
- + already reserved or alloced with a different
- + base or num */
- + pr_err("Cannot reserve %d - %d, it overlaps with"
- + " existing reservation from %d - %d\n",
- + base, base + num - 1, i->base,
- + i->base + i->num - 1);
- + spin_unlock_irq(&alloc->lock);
- + return -1;
- + }
- + }
- + /* Check to make sure this ID isn't in the free list */
- + list_for_each_entry(i, &alloc->free, list) {
- + if ((base >= i->base) && (base < (i->base + i->num))) {
- + /* yep, the reservation is within this node */
- + pr_err("Cannot reserve %d - %d, it overlaps with"
- + " free range %d - %d and must be alloced\n",
- + base, base + num - 1,
- + i->base, i->base + i->num - 1);
- + spin_unlock_irq(&alloc->lock);
- + return -1;
- + }
- + }
- + /* Add the allocation to the used list with a refcount of 1 */
- + used_node = kmalloc(sizeof(*used_node), GFP_KERNEL);
- + if (!used_node) {
- + spin_unlock_irq(&alloc->lock);
- + return -ENOMEM;
- +
- + }
- + used_node->base = base;
- + used_node->num = num;
- + used_node->refcount = 1;
- + used_node->is_alloced = 0;
- + list_add_tail(&used_node->list, &alloc->used);
- + spin_unlock_irq(&alloc->lock);
- + return 0;
- +}
- +
- +
- +int dpa_alloc_pop(struct dpa_alloc *alloc, u32 *result, u32 *count)
- +{
- + struct alloc_node *i = NULL;
- + DPRINT("alloc_pop()\n");
- + DUMP(alloc);
- + spin_lock_irq(&alloc->lock);
- + if (!list_empty(&alloc->free)) {
- + i = list_entry(alloc->free.next, struct alloc_node, list);
- + list_del(&i->list);
- + }
- + spin_unlock_irq(&alloc->lock);
- + DPRINT("returning %d\n", i ? 0 : -ENOMEM);
- + DUMP(alloc);
- + if (!i)
- + return -ENOMEM;
- + *result = i->base;
- + *count = i->num;
- + kfree(i);
- + return 0;
- +}
- +
- +int dpa_alloc_check(struct dpa_alloc *list_head, u32 item)
- +{
- + struct alloc_node *i = NULL;
- + int res = 0;
- + DPRINT("alloc_check()\n");
- + spin_lock_irq(&list_head->lock);
- +
- + list_for_each_entry(i, &list_head->free, list) {
- + if ((item >= i->base) && (item < (i->base + i->num))) {
- + res = 1;
- + break;
- + }
- + }
- + spin_unlock_irq(&list_head->lock);
- + return res;
- +}
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/dpa_sys.h
- @@ -0,0 +1,259 @@
- +/* Copyright 2008-2012 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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 DPA_SYS_H
- +#define DPA_SYS_H
- +
- +#include <linux/kernel.h>
- +#include <linux/errno.h>
- +#include <linux/io.h>
- +#include <linux/dma-mapping.h>
- +#include <linux/bootmem.h>
- +#include <linux/slab.h>
- +#include <linux/module.h>
- +#include <linux/init.h>
- +#include <linux/interrupt.h>
- +#include <linux/delay.h>
- +#include <linux/of_platform.h>
- +#include <linux/of_address.h>
- +#include <linux/of_irq.h>
- +#include <linux/kthread.h>
- +#include <linux/memblock.h>
- +#include <linux/completion.h>
- +#include <linux/log2.h>
- +#include <linux/types.h>
- +#include <linux/ioctl.h>
- +#include <linux/miscdevice.h>
- +#include <linux/uaccess.h>
- +#include <linux/debugfs.h>
- +#include <linux/seq_file.h>
- +#include <linux/device.h>
- +#include <linux/uio_driver.h>
- +#include <linux/smp.h>
- +#include <linux/fsl_hypervisor.h>
- +#include <linux/vmalloc.h>
- +#include <linux/ctype.h>
- +#include <linux/math64.h>
- +#include <linux/bitops.h>
- +
- +#include <linux/fsl_usdpaa.h>
- +
- +/* When copying aligned words or shorts, try to avoid memcpy() */
- +#define CONFIG_TRY_BETTER_MEMCPY
- +
- +/* For 2-element tables related to cache-inhibited and cache-enabled mappings */
- +#define DPA_PORTAL_CE 0
- +#define DPA_PORTAL_CI 1
- +
- +/***********************/
- +/* Misc inline assists */
- +/***********************/
- +
- +#if defined CONFIG_PPC32
- +#include "dpa_sys_ppc32.h"
- +#elif defined CONFIG_PPC64
- +#include "dpa_sys_ppc64.h"
- +#elif defined CONFIG_ARM
- +#include "dpa_sys_arm.h"
- +#elif defined CONFIG_ARM64
- +#include "dpa_sys_arm64.h"
- +#endif
- +
- +
- +#ifdef CONFIG_FSL_DPA_CHECKING
- +#define DPA_ASSERT(x) \
- + do { \
- + if (!(x)) { \
- + pr_crit("ASSERT: (%s:%d) %s\n", __FILE__, __LINE__, \
- + __stringify_1(x)); \
- + dump_stack(); \
- + panic("assertion failure"); \
- + } \
- + } while (0)
- +#else
- +#define DPA_ASSERT(x)
- +#endif
- +
- +/* memcpy() stuff - when you know alignments in advance */
- +#ifdef CONFIG_TRY_BETTER_MEMCPY
- +static inline void copy_words(void *dest, const void *src, size_t sz)
- +{
- + u32 *__dest = dest;
- + const u32 *__src = src;
- + size_t __sz = sz >> 2;
- + BUG_ON((unsigned long)dest & 0x3);
- + BUG_ON((unsigned long)src & 0x3);
- + BUG_ON(sz & 0x3);
- + while (__sz--)
- + *(__dest++) = *(__src++);
- +}
- +static inline void copy_shorts(void *dest, const void *src, size_t sz)
- +{
- + u16 *__dest = dest;
- + const u16 *__src = src;
- + size_t __sz = sz >> 1;
- + BUG_ON((unsigned long)dest & 0x1);
- + BUG_ON((unsigned long)src & 0x1);
- + BUG_ON(sz & 0x1);
- + while (__sz--)
- + *(__dest++) = *(__src++);
- +}
- +static inline void copy_bytes(void *dest, const void *src, size_t sz)
- +{
- + u8 *__dest = dest;
- + const u8 *__src = src;
- + while (sz--)
- + *(__dest++) = *(__src++);
- +}
- +#else
- +#define copy_words memcpy
- +#define copy_shorts memcpy
- +#define copy_bytes memcpy
- +#endif
- +
- +/************/
- +/* RB-trees */
- +/************/
- +
- +/* We encapsulate RB-trees so that its easier to use non-linux forms in
- + * non-linux systems. This also encapsulates the extra plumbing that linux code
- + * usually provides when using RB-trees. This encapsulation assumes that the
- + * data type held by the tree is u32. */
- +
- +struct dpa_rbtree {
- + struct rb_root root;
- +};
- +#define DPA_RBTREE { .root = RB_ROOT }
- +
- +static inline void dpa_rbtree_init(struct dpa_rbtree *tree)
- +{
- + tree->root = RB_ROOT;
- +}
- +
- +#define IMPLEMENT_DPA_RBTREE(name, type, node_field, val_field) \
- +static inline int name##_push(struct dpa_rbtree *tree, type *obj) \
- +{ \
- + struct rb_node *parent = NULL, **p = &tree->root.rb_node; \
- + while (*p) { \
- + u32 item; \
- + parent = *p; \
- + item = rb_entry(parent, type, node_field)->val_field; \
- + if (obj->val_field < item) \
- + p = &parent->rb_left; \
- + else if (obj->val_field > item) \
- + p = &parent->rb_right; \
- + else \
- + return -EBUSY; \
- + } \
- + rb_link_node(&obj->node_field, parent, p); \
- + rb_insert_color(&obj->node_field, &tree->root); \
- + return 0; \
- +} \
- +static inline void name##_del(struct dpa_rbtree *tree, type *obj) \
- +{ \
- + rb_erase(&obj->node_field, &tree->root); \
- +} \
- +static inline type *name##_find(struct dpa_rbtree *tree, u32 val) \
- +{ \
- + type *ret; \
- + struct rb_node *p = tree->root.rb_node; \
- + while (p) { \
- + ret = rb_entry(p, type, node_field); \
- + if (val < ret->val_field) \
- + p = p->rb_left; \
- + else if (val > ret->val_field) \
- + p = p->rb_right; \
- + else \
- + return ret; \
- + } \
- + return NULL; \
- +}
- +
- +/************/
- +/* Bootargs */
- +/************/
- +
- +/* Qman has "qportals=" and Bman has "bportals=", they use the same syntax
- + * though; a comma-separated list of items, each item being a cpu index and/or a
- + * range of cpu indices, and each item optionally be prefixed by "s" to indicate
- + * that the portal associated with that cpu should be shared. See bman_driver.c
- + * for more specifics. */
- +static int __parse_portals_cpu(const char **s, unsigned int *cpu)
- +{
- + *cpu = 0;
- + if (!isdigit(**s))
- + return -EINVAL;
- + while (isdigit(**s))
- + *cpu = *cpu * 10 + (*((*s)++) - '0');
- + return 0;
- +}
- +static inline int parse_portals_bootarg(char *str, struct cpumask *want_shared,
- + struct cpumask *want_unshared,
- + const char *argname)
- +{
- + const char *s = str;
- + unsigned int shared, cpu1, cpu2, loop;
- +
- +keep_going:
- + if (*s == 's') {
- + shared = 1;
- + s++;
- + } else
- + shared = 0;
- + if (__parse_portals_cpu(&s, &cpu1))
- + goto err;
- + if (*s == '-') {
- + s++;
- + if (__parse_portals_cpu(&s, &cpu2))
- + goto err;
- + if (cpu2 < cpu1)
- + goto err;
- + } else
- + cpu2 = cpu1;
- + for (loop = cpu1; loop <= cpu2; loop++)
- + cpumask_set_cpu(loop, shared ? want_shared : want_unshared);
- + if (*s == ',') {
- + s++;
- + goto keep_going;
- + } else if ((*s == '\0') || isspace(*s))
- + return 0;
- +err:
- + pr_crit("Malformed %s argument: %s, offset: %lu\n", argname, str,
- + (unsigned long)s - (unsigned long)str);
- + return -EINVAL;
- +}
- +
- +/* Hooks from fsl_usdpaa_irq.c to fsl_usdpaa.c */
- +int usdpaa_get_portal_config(struct file *filp, void *cinh,
- + enum usdpaa_portal_type ptype, unsigned int *irq,
- + void **iir_reg);
- +
- +#endif /* DPA_SYS_H */
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/dpa_sys_arm.h
- @@ -0,0 +1,95 @@
- +/* Copyright 2016 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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 DPA_SYS_ARM_H
- +#define DPA_SYS_ARM_H
- +
- +#include <asm/cacheflush.h>
- +#include <asm/barrier.h>
- +
- +/* Implementation of ARM specific routines */
- +
- +/* TODO: NB, we currently assume that hwsync() and lwsync() imply compiler
- + * barriers and that dcb*() won't fall victim to compiler or execution
- + * reordering with respect to other code/instructions that manipulate the same
- + * cacheline. */
- +#define hwsync() { asm volatile("dmb st" : : : "memory"); }
- +#define lwsync() { asm volatile("dmb st" : : : "memory"); }
- +#define dcbf(p) { asm volatile("mcr p15, 0, %0, c7, c10, 1" : : "r" (p) : "memory"); }
- +#define dcbt_ro(p) { asm volatile("pld [%0, #64];": : "r" (p)); }
- +#define dcbt_rw(p) { asm volatile("pldw [%0, #64];": : "r" (p)); }
- +#define dcbi(p) { asm volatile("mcr p15, 0, %0, c7, c6, 1" : : "r" (p) : "memory"); }
- +
- +#define dcbz_64(p) { memset(p, 0, sizeof(*p)); }
- +
- +#define dcbf_64(p) \
- + do { \
- + dcbf((u32)p); \
- + } while (0)
- +/* Commonly used combo */
- +#define dcbit_ro(p) \
- + do { \
- + dcbi((u32)p); \
- + dcbt_ro((u32)p); \
- + } while (0)
- +
- +static inline u64 mfatb(void)
- +{
- + return get_cycles();
- +}
- +
- +static inline u32 in_be32(volatile void *addr)
- +{
- + return be32_to_cpu(*((volatile u32 *) addr));
- +}
- +
- +static inline void out_be32(void *addr, u32 val)
- +{
- + *((u32 *) addr) = cpu_to_be32(val);
- +}
- +
- +
- +static inline void set_bits(unsigned long mask, volatile unsigned long *p)
- +{
- + *p |= mask;
- +}
- +static inline void clear_bits(unsigned long mask, volatile unsigned long *p)
- +{
- + *p &= ~mask;
- +}
- +
- +static inline void flush_dcache_range(unsigned long start, unsigned long stop)
- +{
- + __cpuc_flush_dcache_area((void *) start, stop - start);
- +}
- +
- +#define hard_smp_processor_id() raw_smp_processor_id()
- +#endif
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/dpa_sys_arm64.h
- @@ -0,0 +1,102 @@
- +/* Copyright 2014 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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 DPA_SYS_ARM64_H
- +#define DPA_SYS_ARM64_H
- +
- +#include <asm/cacheflush.h>
- +#include <asm/barrier.h>
- +
- +/* Implementation of ARM 64 bit specific routines */
- +
- +/* TODO: NB, we currently assume that hwsync() and lwsync() imply compiler
- + * barriers and that dcb*() won't fall victim to compiler or execution
- + * reordering with respect to other code/instructions that manipulate the same
- + * cacheline. */
- +#define hwsync() { asm volatile("dmb st" : : : "memory"); }
- +#define lwsync() { asm volatile("dmb st" : : : "memory"); }
- +#define dcbf(p) { asm volatile("dc cvac, %0;" : : "r" (p) : "memory"); }
- +#define dcbt_ro(p) { asm volatile("prfm pldl1keep, [%0, #64]" : : "r" (p)); }
- +#define dcbt_rw(p) { asm volatile("prfm pldl1keep, [%0, #64]" : : "r" (p)); }
- +#define dcbi(p) { asm volatile("dc ivac, %0" : : "r"(p) : "memory"); }
- +#define dcbz(p) { asm volatile("dc zva, %0" : : "r" (p) : "memory"); }
- +
- +#define dcbz_64(p) \
- + do { \
- + dcbz(p); \
- + } while (0)
- +
- +#define dcbf_64(p) \
- + do { \
- + dcbf(p); \
- + } while (0)
- +/* Commonly used combo */
- +#define dcbit_ro(p) \
- + do { \
- + dcbi(p); \
- + dcbt_ro(p); \
- + } while (0)
- +
- +static inline u64 mfatb(void)
- +{
- + return get_cycles();
- +}
- +
- +static inline u32 in_be32(volatile void *addr)
- +{
- + return be32_to_cpu(*((volatile u32 *) addr));
- +}
- +
- +static inline void out_be32(void *addr, u32 val)
- +{
- + *((u32 *) addr) = cpu_to_be32(val);
- +}
- +
- +
- +static inline void set_bits(unsigned long mask, volatile unsigned long *p)
- +{
- + *p |= mask;
- +}
- +static inline void clear_bits(unsigned long mask, volatile unsigned long *p)
- +{
- + *p &= ~mask;
- +}
- +
- +static inline void flush_dcache_range(unsigned long start, unsigned long stop)
- +{
- + __flush_dcache_area((void *) start, stop - start);
- +}
- +
- +#define hard_smp_processor_id() raw_smp_processor_id()
- +
- +
- +
- +#endif
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/dpa_sys_ppc32.h
- @@ -0,0 +1,70 @@
- +/* Copyright 2014 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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 DPA_SYS_PPC32_H
- +#define DPA_SYS_PPC32_H
- +
- +/* Implementation of PowerPC 32 bit specific routines */
- +
- +/* TODO: NB, we currently assume that hwsync() and lwsync() imply compiler
- + * barriers and that dcb*() won't fall victim to compiler or execution
- + * reordering with respect to other code/instructions that manipulate the same
- + * cacheline. */
- +#define hwsync() __asm__ __volatile__ ("sync" : : : "memory")
- +#define lwsync() __asm__ __volatile__ (stringify_in_c(LWSYNC) : : : "memory")
- +#define dcbf(p) __asm__ __volatile__ ("dcbf 0,%0" : : "r" (p) : "memory")
- +#define dcbt_ro(p) __asm__ __volatile__ ("dcbt 0,%0" : : "r" (p))
- +#define dcbt_rw(p) __asm__ __volatile__ ("dcbtst 0,%0" : : "r" (p))
- +#define dcbi(p) dcbf(p)
- +
- +#define dcbzl(p) __asm__ __volatile__ ("dcbzl 0,%0" : : "r" (p))
- +#define dcbz_64(p) dcbzl(p)
- +#define dcbf_64(p) dcbf(p)
- +
- +/* Commonly used combo */
- +#define dcbit_ro(p) \
- + do { \
- + dcbi(p); \
- + dcbt_ro(p); \
- + } while (0)
- +
- +static inline u64 mfatb(void)
- +{
- + u32 hi, lo, chk;
- + do {
- + hi = mfspr(SPRN_ATBU);
- + lo = mfspr(SPRN_ATBL);
- + chk = mfspr(SPRN_ATBU);
- + } while (unlikely(hi != chk));
- + return ((u64)hi << 32) | (u64)lo;
- +}
- +
- +#endif
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/dpa_sys_ppc64.h
- @@ -0,0 +1,79 @@
- +/* Copyright 2014 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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 DPA_SYS_PPC64_H
- +#define DPA_SYS_PPC64_H
- +
- +/* Implementation of PowerPC 64 bit specific routines */
- +
- +/* TODO: NB, we currently assume that hwsync() and lwsync() imply compiler
- + * barriers and that dcb*() won't fall victim to compiler or execution
- + * reordering with respect to other code/instructions that manipulate the same
- + * cacheline. */
- +#define hwsync() __asm__ __volatile__ ("sync" : : : "memory")
- +#define lwsync() __asm__ __volatile__ (stringify_in_c(LWSYNC) : : : "memory")
- +#define dcbf(p) __asm__ __volatile__ ("dcbf 0,%0" : : "r" (p) : "memory")
- +#define dcbt_ro(p) __asm__ __volatile__ ("dcbt 0,%0" : : "r" (p))
- +#define dcbt_rw(p) __asm__ __volatile__ ("dcbtst 0,%0" : : "r" (p))
- +#define dcbi(p) dcbf(p)
- +
- +#define dcbz(p) __asm__ __volatile__ ("dcbz 0,%0" : : "r" (p))
- +#define dcbz_64(p) \
- + do { \
- + dcbz((void*)p + 32); \
- + dcbz(p); \
- + } while (0)
- +#define dcbf_64(p) \
- + do { \
- + dcbf((void*)p + 32); \
- + dcbf(p); \
- + } while (0)
- +/* Commonly used combo */
- +#define dcbit_ro(p) \
- + do { \
- + dcbi(p); \
- + dcbi((void*)p + 32); \
- + dcbt_ro(p); \
- + dcbt_ro((void*)p + 32); \
- + } while (0)
- +
- +static inline u64 mfatb(void)
- +{
- + u32 hi, lo, chk;
- + do {
- + hi = mfspr(SPRN_ATBU);
- + lo = mfspr(SPRN_ATBL);
- + chk = mfspr(SPRN_ATBU);
- + } while (unlikely(hi != chk));
- + return ((u64)hi << 32) | (u64)lo;
- +}
- +
- +#endif
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/fsl_usdpaa.c
- @@ -0,0 +1,1982 @@
- +/* Copyright (C) 2008-2012 Freescale Semiconductor, Inc.
- + * Authors: Andy Fleming <afleming@freescale.com>
- + * Timur Tabi <timur@freescale.com>
- + * Geoff Thorpe <Geoff.Thorpe@freescale.com>
- + *
- + * This file is licensed under the terms of the GNU General Public License
- + * version 2. This program is licensed "as is" without any warranty of any
- + * kind, whether express or implied.
- + */
- +
- +
- +#include <linux/miscdevice.h>
- +#include <linux/fs.h>
- +#include <linux/cdev.h>
- +#include <linux/mm.h>
- +#include <linux/of.h>
- +#include <linux/memblock.h>
- +#include <linux/slab.h>
- +#include <linux/mman.h>
- +#include <linux/of_reserved_mem.h>
- +
- +#if !(defined(CONFIG_ARM) || defined(CONFIG_ARM64))
- +#include <mm/mmu_decl.h>
- +#endif
- +
- +#include "dpa_sys.h"
- +#include <linux/fsl_usdpaa.h>
- +#include "bman_low.h"
- +#include "qman_low.h"
- +
- +/* Physical address range of the memory reservation, exported for mm/mem.c */
- +static u64 phys_start;
- +static u64 phys_size;
- +static u64 arg_phys_size;
- +
- +/* PFN versions of the above */
- +static unsigned long pfn_start;
- +static unsigned long pfn_size;
- +
- +/* Memory reservations are manipulated under this spinlock (which is why 'refs'
- + * isn't atomic_t). */
- +static DEFINE_SPINLOCK(mem_lock);
- +
- +/* The range of TLB1 indices */
- +static unsigned int first_tlb;
- +static unsigned int num_tlb = 1;
- +static unsigned int current_tlb; /* loops around for fault handling */
- +
- +/* Memory reservation is represented as a list of 'mem_fragment's, some of which
- + * may be mapped. Unmapped fragments are always merged where possible. */
- +static LIST_HEAD(mem_list);
- +
- +struct mem_mapping;
- +
- +/* Memory fragments are in 'mem_list'. */
- +struct mem_fragment {
- + u64 base;
- + u64 len;
- + unsigned long pfn_base; /* PFN version of 'base' */
- + unsigned long pfn_len; /* PFN version of 'len' */
- + unsigned int refs; /* zero if unmapped */
- + u64 root_len; /* Size of the orignal fragment */
- + unsigned long root_pfn; /* PFN of the orignal fragment */
- + struct list_head list;
- + /* if mapped, flags+name captured at creation time */
- + u32 flags;
- + char name[USDPAA_DMA_NAME_MAX];
- + u64 map_len;
- + /* support multi-process locks per-memory-fragment. */
- + int has_locking;
- + wait_queue_head_t wq;
- + struct mem_mapping *owner;
- +};
- +
- +/* Mappings of memory fragments in 'struct ctx'. These are created from
- + * ioctl(USDPAA_IOCTL_DMA_MAP), though the actual mapping then happens via a
- + * mmap(). */
- +struct mem_mapping {
- + struct mem_fragment *root_frag;
- + u32 frag_count;
- + u64 total_size;
- + struct list_head list;
- + int refs;
- + void *virt_addr;
- +};
- +
- +struct portal_mapping {
- + struct usdpaa_ioctl_portal_map user;
- + union {
- + struct qm_portal_config *qportal;
- + struct bm_portal_config *bportal;
- + };
- + /* Declare space for the portals in case the process
- + exits unexpectedly and needs to be cleaned by the kernel */
- + union {
- + struct qm_portal qman_portal_low;
- + struct bm_portal bman_portal_low;
- + };
- + struct list_head list;
- + struct resource *phys;
- + struct iommu_domain *iommu_domain;
- +};
- +
- +/* Track the DPAA resources the process is using */
- +struct active_resource {
- + struct list_head list;
- + u32 id;
- + u32 num;
- + unsigned int refcount;
- +};
- +
- +/* Per-FD state (which should also be per-process but we don't enforce that) */
- +struct ctx {
- + /* Lock to protect the context */
- + spinlock_t lock;
- + /* Allocated resources get put here for accounting */
- + struct list_head resources[usdpaa_id_max];
- + /* list of DMA maps */
- + struct list_head maps;
- + /* list of portal maps */
- + struct list_head portals;
- +};
- +
- +/* Different resource classes */
- +static const struct alloc_backend {
- + enum usdpaa_id_type id_type;
- + int (*alloc)(u32 *, u32, u32, int);
- + void (*release)(u32 base, unsigned int count);
- + int (*reserve)(u32 base, unsigned int count);
- + const char *acronym;
- +} alloc_backends[] = {
- + {
- + .id_type = usdpaa_id_fqid,
- + .alloc = qman_alloc_fqid_range,
- + .release = qman_release_fqid_range,
- + .reserve = qman_reserve_fqid_range,
- + .acronym = "FQID"
- + },
- + {
- + .id_type = usdpaa_id_bpid,
- + .alloc = bman_alloc_bpid_range,
- + .release = bman_release_bpid_range,
- + .reserve = bman_reserve_bpid_range,
- + .acronym = "BPID"
- + },
- + {
- + .id_type = usdpaa_id_qpool,
- + .alloc = qman_alloc_pool_range,
- + .release = qman_release_pool_range,
- + .reserve = qman_reserve_pool_range,
- + .acronym = "QPOOL"
- + },
- + {
- + .id_type = usdpaa_id_cgrid,
- + .alloc = qman_alloc_cgrid_range,
- + .release = qman_release_cgrid_range,
- + .acronym = "CGRID"
- + },
- + {
- + .id_type = usdpaa_id_ceetm0_lfqid,
- + .alloc = qman_alloc_ceetm0_lfqid_range,
- + .release = qman_release_ceetm0_lfqid_range,
- + .acronym = "CEETM0_LFQID"
- + },
- + {
- + .id_type = usdpaa_id_ceetm0_channelid,
- + .alloc = qman_alloc_ceetm0_channel_range,
- + .release = qman_release_ceetm0_channel_range,
- + .acronym = "CEETM0_LFQID"
- + },
- + {
- + .id_type = usdpaa_id_ceetm1_lfqid,
- + .alloc = qman_alloc_ceetm1_lfqid_range,
- + .release = qman_release_ceetm1_lfqid_range,
- + .acronym = "CEETM1_LFQID"
- + },
- + {
- + .id_type = usdpaa_id_ceetm1_channelid,
- + .alloc = qman_alloc_ceetm1_channel_range,
- + .release = qman_release_ceetm1_channel_range,
- + .acronym = "CEETM1_LFQID"
- + },
- + {
- + /* This terminates the array */
- + .id_type = usdpaa_id_max
- + }
- +};
- +
- +/* Determines the largest acceptable page size for a given size
- + The sizes are determined by what the TLB1 acceptable page sizes are */
- +static u32 largest_page_size(u32 size)
- +{
- + int shift = 30; /* Start at 1G size */
- + if (size < 4096)
- + return 0;
- + do {
- + if (size >= (1<<shift))
- + return 1<<shift;
- + shift -= 2;
- + } while (shift >= 12); /* Up to 4k */
- + return 0;
- +}
- +
- +/* Determine if value is power of 4 */
- +static inline bool is_power_of_4(u64 x)
- +{
- + if (x == 0 || ((x & (x - 1)) != 0))
- + return false;
- + return !!(x & 0x5555555555555555ull);
- +}
- +
- +/* Helper for ioctl_dma_map() when we have a larger fragment than we need. This
- + * splits the fragment into 4 and returns the upper-most. (The caller can loop
- + * until it has a suitable fragment size.) */
- +static struct mem_fragment *split_frag(struct mem_fragment *frag)
- +{
- + struct mem_fragment *x[3];
- +
- + x[0] = kmalloc(sizeof(struct mem_fragment), GFP_ATOMIC);
- + x[1] = kmalloc(sizeof(struct mem_fragment), GFP_ATOMIC);
- + x[2] = kmalloc(sizeof(struct mem_fragment), GFP_ATOMIC);
- + if (!x[0] || !x[1] || !x[2]) {
- + kfree(x[0]);
- + kfree(x[1]);
- + kfree(x[2]);
- + return NULL;
- + }
- + BUG_ON(frag->refs);
- + frag->len >>= 2;
- + frag->pfn_len >>= 2;
- + x[0]->base = frag->base + frag->len;
- + x[1]->base = x[0]->base + frag->len;
- + x[2]->base = x[1]->base + frag->len;
- + x[0]->len = x[1]->len = x[2]->len = frag->len;
- + x[0]->pfn_base = frag->pfn_base + frag->pfn_len;
- + x[1]->pfn_base = x[0]->pfn_base + frag->pfn_len;
- + x[2]->pfn_base = x[1]->pfn_base + frag->pfn_len;
- + x[0]->pfn_len = x[1]->pfn_len = x[2]->pfn_len = frag->pfn_len;
- + x[0]->refs = x[1]->refs = x[2]->refs = 0;
- + x[0]->root_len = x[1]->root_len = x[2]->root_len = frag->root_len;
- + x[0]->root_pfn = x[1]->root_pfn = x[2]->root_pfn = frag->root_pfn;
- + x[0]->name[0] = x[1]->name[0] = x[2]->name[0] = 0;
- + list_add_tail(&x[0]->list, &frag->list);
- + list_add_tail(&x[1]->list, &x[0]->list);
- + list_add_tail(&x[2]->list, &x[1]->list);
- + return x[2];
- +}
- +
- +static __maybe_unused void dump_frags(void)
- +{
- + struct mem_fragment *frag;
- + int i = 0;
- + list_for_each_entry(frag, &mem_list, list) {
- + pr_info("FRAG %d: base 0x%llx pfn_base 0x%lx len 0x%llx root_len 0x%llx root_pfn 0x%lx refs %d name %s\n",
- + i, frag->base, frag->pfn_base,
- + frag->len, frag->root_len, frag->root_pfn,
- + frag->refs, frag->name);
- + ++i;
- + }
- +}
- +
- +/* Walk the list of fragments and adjoin neighbouring segments if possible */
- +static void compress_frags(void)
- +{
- + /* Walk the fragment list and combine fragments */
- + struct mem_fragment *frag, *nxtfrag;
- + u64 len = 0;
- +
- + int i, numfrags;
- +
- +
- + frag = list_entry(mem_list.next, struct mem_fragment, list);
- +
- + while (&frag->list != &mem_list) {
- + /* Must combine consecutive fragemenst with
- + same root_pfn such that they are power of 4 */
- + if (frag->refs != 0) {
- + frag = list_entry(frag->list.next,
- + struct mem_fragment, list);
- + continue; /* Not this window */
- + }
- + len = frag->len;
- + numfrags = 0;
- + nxtfrag = list_entry(frag->list.next,
- + struct mem_fragment, list);
- + while (true) {
- + if (&nxtfrag->list == &mem_list) {
- + numfrags = 0;
- + break; /* End of list */
- + }
- + if (nxtfrag->refs) {
- + numfrags = 0;
- + break; /* In use still */
- + }
- + if (nxtfrag->root_pfn != frag->root_pfn) {
- + numfrags = 0;
- + break; /* Crosses root fragment boundary */
- + }
- + len += nxtfrag->len;
- + numfrags++;
- + if (is_power_of_4(len)) {
- + /* These fragments can be combined */
- + break;
- + }
- + nxtfrag = list_entry(nxtfrag->list.next,
- + struct mem_fragment, list);
- + }
- + if (numfrags == 0) {
- + frag = list_entry(frag->list.next,
- + struct mem_fragment, list);
- + continue; /* try the next window */
- + }
- + for (i = 0; i < numfrags; i++) {
- + struct mem_fragment *todel =
- + list_entry(nxtfrag->list.prev,
- + struct mem_fragment, list);
- + nxtfrag->len += todel->len;
- + nxtfrag->pfn_len += todel->pfn_len;
- + list_del(&todel->list);
- + }
- + /* Re evaluate the list, things may merge now */
- + frag = list_entry(mem_list.next, struct mem_fragment, list);
- + }
- +}
- +
- +/* Hook from arch/powerpc/mm/mem.c */
- +int usdpaa_test_fault(unsigned long pfn, u64 *phys_addr, u64 *size)
- +{
- + struct mem_fragment *frag;
- + int idx = -1;
- + if ((pfn < pfn_start) || (pfn >= (pfn_start + pfn_size)))
- + return -1;
- + /* It's in-range, we need to find the fragment */
- + spin_lock(&mem_lock);
- + list_for_each_entry(frag, &mem_list, list) {
- + if ((pfn >= frag->pfn_base) && (pfn < (frag->pfn_base +
- + frag->pfn_len))) {
- + *phys_addr = frag->base;
- + *size = frag->len;
- + idx = current_tlb++;
- + if (current_tlb >= (first_tlb + num_tlb))
- + current_tlb = first_tlb;
- + break;
- + }
- + }
- + spin_unlock(&mem_lock);
- + return idx;
- +}
- +
- +static int usdpaa_open(struct inode *inode, struct file *filp)
- +{
- + const struct alloc_backend *backend = &alloc_backends[0];
- + struct ctx *ctx = kmalloc(sizeof(struct ctx), GFP_KERNEL);
- + if (!ctx)
- + return -ENOMEM;
- + filp->private_data = ctx;
- +
- + while (backend->id_type != usdpaa_id_max) {
- + INIT_LIST_HEAD(&ctx->resources[backend->id_type]);
- + backend++;
- + }
- +
- + INIT_LIST_HEAD(&ctx->maps);
- + INIT_LIST_HEAD(&ctx->portals);
- + spin_lock_init(&ctx->lock);
- +
- + //filp->f_mapping->backing_dev_info = &directly_mappable_cdev_bdi;
- +
- + return 0;
- +}
- +
- +#define DQRR_MAXFILL 15
- +
- +/* Reset a QMan portal to its default state */
- +static int init_qm_portal(struct qm_portal_config *config,
- + struct qm_portal *portal)
- +{
- + const struct qm_dqrr_entry *dqrr = NULL;
- + int i;
- +
- + portal->addr.addr_ce = config->addr_virt[DPA_PORTAL_CE];
- + portal->addr.addr_ci = config->addr_virt[DPA_PORTAL_CI];
- +
- + /* Make sure interrupts are inhibited */
- + qm_out(IIR, 1);
- +
- + /* Initialize the DQRR. This will stop any dequeue
- + commands that are in progress */
- + if (qm_dqrr_init(portal, config, qm_dqrr_dpush, qm_dqrr_pvb,
- + qm_dqrr_cdc, DQRR_MAXFILL)) {
- + pr_err("qm_dqrr_init() failed when trying to"
- + " recover portal, portal will be leaked\n");
- + return 1;
- + }
- +
- + /* Discard any entries on the DQRR */
- + /* If we consume the ring twice something is wrong */
- + for (i = 0; i < DQRR_MAXFILL * 2; i++) {
- + qm_dqrr_pvb_update(portal);
- + dqrr = qm_dqrr_current(portal);
- + if (!dqrr)
- + break;
- + qm_dqrr_cdc_consume_1ptr(portal, dqrr, 0);
- + qm_dqrr_pvb_update(portal);
- + qm_dqrr_next(portal);
- + }
- + /* Initialize the EQCR */
- + if (qm_eqcr_init(portal, qm_eqcr_pvb,
- + qm_eqcr_get_ci_stashing(portal), 1)) {
- + pr_err("Qman EQCR initialisation failed\n");
- + return 1;
- + }
- + /* initialize the MR */
- + if (qm_mr_init(portal, qm_mr_pvb, qm_mr_cci)) {
- + pr_err("Qman MR initialisation failed\n");
- + return 1;
- + }
- + qm_mr_pvb_update(portal);
- + while (qm_mr_current(portal)) {
- + qm_mr_next(portal);
- + qm_mr_cci_consume_to_current(portal);
- + qm_mr_pvb_update(portal);
- + }
- +
- + if (qm_mc_init(portal)) {
- + pr_err("Qman MC initialisation failed\n");
- + return 1;
- + }
- + return 0;
- +}
- +
- +static int init_bm_portal(struct bm_portal_config *config,
- + struct bm_portal *portal)
- +{
- + portal->addr.addr_ce = config->addr_virt[DPA_PORTAL_CE];
- + portal->addr.addr_ci = config->addr_virt[DPA_PORTAL_CI];
- +
- + if (bm_rcr_init(portal, bm_rcr_pvb, bm_rcr_cce)) {
- + pr_err("Bman RCR initialisation failed\n");
- + return 1;
- + }
- + if (bm_mc_init(portal)) {
- + pr_err("Bman MC initialisation failed\n");
- + return 1;
- + }
- + return 0;
- +}
- +
- +/* Function that will scan all FQ's in the system. For each FQ that is not
- + OOS it will call the check_channel helper to determine if the FQ should
- + be torn down. If the check_channel helper returns true the FQ will be
- + transitioned to the OOS state */
- +static int qm_check_and_destroy_fqs(struct qm_portal *portal, void *ctx,
- + bool (*check_channel)(void*, u32))
- +{
- + u32 fq_id = 0;
- + while (1) {
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + u8 state;
- + u32 channel;
- +
- + /* Determine the channel for the FQID */
- + mcc = qm_mc_start(portal);
- + mcc->queryfq.fqid = fq_id;
- + qm_mc_commit(portal, QM_MCC_VERB_QUERYFQ);
- + while (!(mcr = qm_mc_result(portal)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK)
- + == QM_MCR_VERB_QUERYFQ);
- + if (mcr->result != QM_MCR_RESULT_OK)
- + break; /* End of valid FQIDs */
- +
- + channel = mcr->queryfq.fqd.dest.channel;
- + /* Determine the state of the FQID */
- + mcc = qm_mc_start(portal);
- + mcc->queryfq_np.fqid = fq_id;
- + qm_mc_commit(portal, QM_MCC_VERB_QUERYFQ_NP);
- + while (!(mcr = qm_mc_result(portal)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK)
- + == QM_MCR_VERB_QUERYFQ_NP);
- + state = mcr->queryfq_np.state & QM_MCR_NP_STATE_MASK;
- + if (state == QM_MCR_NP_STATE_OOS)
- + /* Already OOS, no need to do anymore checks */
- + goto next;
- +
- + if (check_channel(ctx, channel))
- + qm_shutdown_fq(&portal, 1, fq_id);
- + next:
- + ++fq_id;
- + }
- + return 0;
- +}
- +
- +static bool check_channel_device(void *_ctx, u32 channel)
- +{
- + struct ctx *ctx = _ctx;
- + struct portal_mapping *portal, *tmpportal;
- + struct active_resource *res;
- +
- + /* See if the FQ is destined for one of the portals we're cleaning up */
- + list_for_each_entry_safe(portal, tmpportal, &ctx->portals, list) {
- + if (portal->user.type == usdpaa_portal_qman) {
- + if (portal->qportal->public_cfg.channel == channel) {
- + /* This FQs destination is a portal
- + we're cleaning, send a retire */
- + return true;
- + }
- + }
- + }
- +
- + /* Check the pool channels that will be released as well */
- + list_for_each_entry(res, &ctx->resources[usdpaa_id_qpool], list) {
- + if ((res->id >= channel) &&
- + ((res->id + res->num - 1) <= channel))
- + return true;
- + }
- + return false;
- +}
- +
- +static bool check_portal_channel(void *ctx, u32 channel)
- +{
- + u32 portal_channel = *(u32 *)ctx;
- + if (portal_channel == channel) {
- + /* This FQs destination is a portal
- + we're cleaning, send a retire */
- + return true;
- + }
- + return false;
- +}
- +
- +
- +
- +
- +static int usdpaa_release(struct inode *inode, struct file *filp)
- +{
- + struct ctx *ctx = filp->private_data;
- + struct mem_mapping *map, *tmpmap;
- + struct portal_mapping *portal, *tmpportal;
- + const struct alloc_backend *backend = &alloc_backends[0];
- + struct active_resource *res;
- + struct qm_portal *qm_cleanup_portal = NULL;
- + struct bm_portal *bm_cleanup_portal = NULL;
- + struct qm_portal_config *qm_alloced_portal = NULL;
- + struct bm_portal_config *bm_alloced_portal = NULL;
- +
- + struct qm_portal *portal_array[qman_portal_max];
- + int portal_count = 0;
- +
- + /* Ensure the release operation cannot be migrated to another
- + CPU as CPU specific variables may be needed during cleanup */
- +#ifdef CONFIG_PREEMPT_RT_FULL
- + migrate_disable();
- +#endif
- + /* The following logic is used to recover resources that were not
- + correctly released by the process that is closing the FD.
- + Step 1: syncronize the HW with the qm_portal/bm_portal structures
- + in the kernel
- + */
- +
- + list_for_each_entry_safe(portal, tmpportal, &ctx->portals, list) {
- + /* Try to recover any portals that weren't shut down */
- + if (portal->user.type == usdpaa_portal_qman) {
- + portal_array[portal_count] = &portal->qman_portal_low;
- + ++portal_count;
- + init_qm_portal(portal->qportal,
- + &portal->qman_portal_low);
- + if (!qm_cleanup_portal) {
- + qm_cleanup_portal = &portal->qman_portal_low;
- + } else {
- + /* Clean FQs on the dedicated channel */
- + u32 chan = portal->qportal->public_cfg.channel;
- + qm_check_and_destroy_fqs(
- + &portal->qman_portal_low, &chan,
- + check_portal_channel);
- + }
- + } else {
- + /* BMAN */
- + init_bm_portal(portal->bportal,
- + &portal->bman_portal_low);
- + if (!bm_cleanup_portal)
- + bm_cleanup_portal = &portal->bman_portal_low;
- + }
- + }
- + /* If no portal was found, allocate one for cleanup */
- + if (!qm_cleanup_portal) {
- + qm_alloced_portal = qm_get_unused_portal();
- + if (!qm_alloced_portal) {
- + pr_crit("No QMan portal avalaible for cleanup\n");
- +#ifdef CONFIG_PREEMPT_RT_FULL
- + migrate_enable();
- +#endif
- + return -1;
- + }
- + qm_cleanup_portal = kmalloc(sizeof(struct qm_portal),
- + GFP_KERNEL);
- + if (!qm_cleanup_portal) {
- +#ifdef CONFIG_PREEMPT_RT_FULL
- + migrate_enable();
- +#endif
- + return -ENOMEM;
- + }
- + init_qm_portal(qm_alloced_portal, qm_cleanup_portal);
- + portal_array[portal_count] = qm_cleanup_portal;
- + ++portal_count;
- + }
- + if (!bm_cleanup_portal) {
- + bm_alloced_portal = bm_get_unused_portal();
- + if (!bm_alloced_portal) {
- + pr_crit("No BMan portal avalaible for cleanup\n");
- +#ifdef CONFIG_PREEMPT_RT_FULL
- + migrate_enable();
- +#endif
- + return -1;
- + }
- + bm_cleanup_portal = kmalloc(sizeof(struct bm_portal),
- + GFP_KERNEL);
- + if (!bm_cleanup_portal) {
- +#ifdef CONFIG_PREEMPT_RT_FULL
- + migrate_enable();
- +#endif
- + return -ENOMEM;
- + }
- + init_bm_portal(bm_alloced_portal, bm_cleanup_portal);
- + }
- +
- + /* OOS the FQs associated with this process */
- + qm_check_and_destroy_fqs(qm_cleanup_portal, ctx, check_channel_device);
- +
- + while (backend->id_type != usdpaa_id_max) {
- + int leaks = 0;
- + list_for_each_entry(res, &ctx->resources[backend->id_type],
- + list) {
- + if (backend->id_type == usdpaa_id_fqid) {
- + int i = 0;
- + for (; i < res->num; i++) {
- + /* Clean FQs with the cleanup portal */
- + qm_shutdown_fq(portal_array,
- + portal_count,
- + res->id + i);
- + }
- + }
- + leaks += res->num;
- + backend->release(res->id, res->num);
- + }
- + if (leaks)
- + pr_crit("USDPAA process leaking %d %s%s\n", leaks,
- + backend->acronym, (leaks > 1) ? "s" : "");
- + backend++;
- + }
- + /* Release any DMA regions */
- + spin_lock(&mem_lock);
- + list_for_each_entry_safe(map, tmpmap, &ctx->maps, list) {
- + struct mem_fragment *current_frag = map->root_frag;
- + int i;
- + if (map->root_frag->has_locking &&
- + (map->root_frag->owner == map)) {
- + map->root_frag->owner = NULL;
- + wake_up(&map->root_frag->wq);
- + }
- + /* Check each fragment and merge if the ref count is 0 */
- + for (i = 0; i < map->frag_count; i++) {
- + --current_frag->refs;
- + current_frag = list_entry(current_frag->list.prev,
- + struct mem_fragment, list);
- + }
- +
- + compress_frags();
- + list_del(&map->list);
- + kfree(map);
- + }
- + spin_unlock(&mem_lock);
- +
- + /* Return portals */
- + list_for_each_entry_safe(portal, tmpportal, &ctx->portals, list) {
- + if (portal->user.type == usdpaa_portal_qman) {
- + /* Give the portal back to the allocator */
- + init_qm_portal(portal->qportal,
- + &portal->qman_portal_low);
- + qm_put_unused_portal(portal->qportal);
- + } else {
- + init_bm_portal(portal->bportal,
- + &portal->bman_portal_low);
- + bm_put_unused_portal(portal->bportal);
- + }
- + list_del(&portal->list);
- + kfree(portal);
- + }
- + if (qm_alloced_portal) {
- + qm_put_unused_portal(qm_alloced_portal);
- + kfree(qm_cleanup_portal);
- + }
- + if (bm_alloced_portal) {
- + bm_put_unused_portal(bm_alloced_portal);
- + kfree(bm_cleanup_portal);
- + }
- +
- + kfree(ctx);
- +#ifdef CONFIG_PREEMPT_RT_FULL
- + migrate_enable();
- +#endif
- + return 0;
- +}
- +
- +static int check_mmap_dma(struct ctx *ctx, struct vm_area_struct *vma,
- + int *match, unsigned long *pfn)
- +{
- + struct mem_mapping *map;
- +
- + list_for_each_entry(map, &ctx->maps, list) {
- + int i;
- + struct mem_fragment *frag = map->root_frag;
- +
- + for (i = 0; i < map->frag_count; i++) {
- + if (frag->pfn_base == vma->vm_pgoff) {
- + *match = 1;
- + *pfn = frag->pfn_base;
- + return 0;
- + }
- + frag = list_entry(frag->list.next, struct mem_fragment,
- + list);
- + }
- + }
- + *match = 0;
- + return 0;
- +}
- +
- +static int check_mmap_resource(struct resource *res, struct vm_area_struct *vma,
- + int *match, unsigned long *pfn)
- +{
- + *pfn = res->start >> PAGE_SHIFT;
- + if (*pfn == vma->vm_pgoff) {
- + *match = 1;
- + if ((vma->vm_end - vma->vm_start) != resource_size(res))
- + return -EINVAL;
- + } else
- + *match = 0;
- + return 0;
- +}
- +
- +static int check_mmap_portal(struct ctx *ctx, struct vm_area_struct *vma,
- + int *match, unsigned long *pfn)
- +{
- + struct portal_mapping *portal;
- + int ret;
- +
- + list_for_each_entry(portal, &ctx->portals, list) {
- + ret = check_mmap_resource(&portal->phys[DPA_PORTAL_CE], vma,
- + match, pfn);
- + if (*match) {
- + vma->vm_page_prot =
- +#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
- + pgprot_cached_ns(vma->vm_page_prot);
- +#else
- + pgprot_cached_noncoherent(vma->vm_page_prot);
- +#endif
- + return ret;
- + }
- + ret = check_mmap_resource(&portal->phys[DPA_PORTAL_CI], vma,
- + match, pfn);
- + if (*match) {
- + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
- + return ret;
- + }
- + }
- + *match = 0;
- + return 0;
- +}
- +
- +static int usdpaa_mmap(struct file *filp, struct vm_area_struct *vma)
- +{
- + struct ctx *ctx = filp->private_data;
- + unsigned long pfn = 0;
- + int match, ret;
- +
- + spin_lock(&mem_lock);
- + ret = check_mmap_dma(ctx, vma, &match, &pfn);
- + if (!match)
- + ret = check_mmap_portal(ctx, vma, &match, &pfn);
- + spin_unlock(&mem_lock);
- + if (!match)
- + return -EINVAL;
- + if (!ret)
- + ret = remap_pfn_range(vma, vma->vm_start, pfn,
- + vma->vm_end - vma->vm_start,
- + vma->vm_page_prot);
- + return ret;
- +}
- +
- +/* Return the nearest rounded-up address >= 'addr' that is 'sz'-aligned. 'sz'
- + * must be a power of 2, but both 'addr' and 'sz' can be expressions. */
- +#define USDPAA_MEM_ROUNDUP(addr, sz) \
- + ({ \
- + unsigned long foo_align = (sz) - 1; \
- + ((addr) + foo_align) & ~foo_align; \
- + })
- +/* Searching for a size-aligned virtual address range starting from 'addr' */
- +static unsigned long usdpaa_get_unmapped_area(struct file *file,
- + unsigned long addr,
- + unsigned long len,
- + unsigned long pgoff,
- + unsigned long flags)
- +{
- + struct vm_area_struct *vma;
- +
- + if (len % PAGE_SIZE)
- + return -EINVAL;
- + if (!len)
- + return -EINVAL;
- +
- + /* Need to align the address to the largest pagesize of the mapping
- + * because the MMU requires the virtual address to have the same
- + * alignment as the physical address */
- + addr = USDPAA_MEM_ROUNDUP(addr, largest_page_size(len));
- + vma = find_vma(current->mm, addr);
- + /* Keep searching until we reach the end of currently-used virtual
- + * address-space or we find a big enough gap. */
- + while (vma) {
- + if ((addr + len) < vma->vm_start)
- + return addr;
- +
- + addr = USDPAA_MEM_ROUNDUP(vma->vm_end, largest_page_size(len));
- + vma = vma->vm_next;
- + }
- + if ((TASK_SIZE - len) < addr)
- + return -ENOMEM;
- + return addr;
- +}
- +
- +static long ioctl_id_alloc(struct ctx *ctx, void __user *arg)
- +{
- + struct usdpaa_ioctl_id_alloc i;
- + const struct alloc_backend *backend;
- + struct active_resource *res;
- + int ret = copy_from_user(&i, arg, sizeof(i));
- + if (ret)
- + return ret;
- + if ((i.id_type >= usdpaa_id_max) || !i.num)
- + return -EINVAL;
- + backend = &alloc_backends[i.id_type];
- + /* Allocate the required resource type */
- + ret = backend->alloc(&i.base, i.num, i.align, i.partial);
- + if (ret < 0)
- + return ret;
- + i.num = ret;
- + /* Copy the result to user-space */
- + ret = copy_to_user(arg, &i, sizeof(i));
- + if (ret) {
- + backend->release(i.base, i.num);
- + return ret;
- + }
- + /* Assign the allocated range to the FD accounting */
- + res = kmalloc(sizeof(*res), GFP_KERNEL);
- + if (!res) {
- + backend->release(i.base, i.num);
- + return -ENOMEM;
- + }
- + spin_lock(&ctx->lock);
- + res->id = i.base;
- + res->num = i.num;
- + res->refcount = 1;
- + list_add(&res->list, &ctx->resources[i.id_type]);
- + spin_unlock(&ctx->lock);
- + return 0;
- +}
- +
- +static long ioctl_id_release(struct ctx *ctx, void __user *arg)
- +{
- + struct usdpaa_ioctl_id_release i;
- + const struct alloc_backend *backend;
- + struct active_resource *tmp, *pos;
- +
- + int ret = copy_from_user(&i, arg, sizeof(i));
- + if (ret)
- + return ret;
- + if ((i.id_type >= usdpaa_id_max) || !i.num)
- + return -EINVAL;
- + backend = &alloc_backends[i.id_type];
- + /* Pull the range out of the FD accounting - the range is valid iff this
- + * succeeds. */
- + spin_lock(&ctx->lock);
- + list_for_each_entry_safe(pos, tmp, &ctx->resources[i.id_type], list) {
- + if (pos->id == i.base && pos->num == i.num) {
- + pos->refcount--;
- + if (pos->refcount) {
- + spin_unlock(&ctx->lock);
- + return 0; /* Still being used */
- + }
- + list_del(&pos->list);
- + kfree(pos);
- + spin_unlock(&ctx->lock);
- + goto found;
- + }
- + }
- + /* Failed to find the resource */
- + spin_unlock(&ctx->lock);
- + pr_err("Couldn't find resource type %d base 0x%x num %d\n",
- + i.id_type, i.base, i.num);
- + return -EINVAL;
- +found:
- + /* Release the resource to the backend */
- + backend->release(i.base, i.num);
- + return 0;
- +}
- +
- +static long ioctl_id_reserve(struct ctx *ctx, void __user *arg)
- +{
- + struct usdpaa_ioctl_id_reserve i;
- + const struct alloc_backend *backend;
- + struct active_resource *tmp, *pos;
- +
- + int ret = copy_from_user(&i, arg, sizeof(i));
- + if (ret)
- + return ret;
- + if ((i.id_type >= usdpaa_id_max) || !i.num)
- + return -EINVAL;
- + backend = &alloc_backends[i.id_type];
- + if (!backend->reserve)
- + return -EINVAL;
- + /* Pull the range out of the FD accounting - the range is valid iff this
- + * succeeds. */
- + spin_lock(&ctx->lock);
- + list_for_each_entry_safe(pos, tmp, &ctx->resources[i.id_type], list) {
- + if (pos->id == i.base && pos->num == i.num) {
- + pos->refcount++;
- + spin_unlock(&ctx->lock);
- + return 0;
- + }
- + }
- +
- + /* Failed to find the resource */
- + spin_unlock(&ctx->lock);
- +
- + /* Reserve the resource in the backend */
- + ret = backend->reserve(i.base, i.num);
- + if (ret)
- + return ret;
- + /* Assign the reserved range to the FD accounting */
- + pos = kmalloc(sizeof(*pos), GFP_KERNEL);
- + if (!pos) {
- + backend->release(i.base, i.num);
- + return -ENOMEM;
- + }
- + spin_lock(&ctx->lock);
- + pos->id = i.base;
- + pos->num = i.num;
- + pos->refcount = 1;
- + list_add(&pos->list, &ctx->resources[i.id_type]);
- + spin_unlock(&ctx->lock);
- + return 0;
- +}
- +
- +static long ioctl_dma_map(struct file *fp, struct ctx *ctx,
- + struct usdpaa_ioctl_dma_map *i)
- +{
- + struct mem_fragment *frag, *start_frag, *next_frag;
- + struct mem_mapping *map, *tmp;
- + int ret = 0;
- + u32 largest_page, so_far = 0;
- + int frag_count = 0;
- + unsigned long next_addr = PAGE_SIZE, populate;
- +
- + /* error checking to ensure values copied from user space are valid */
- + if (i->len % PAGE_SIZE)
- + return -EINVAL;
- +
- + map = kmalloc(sizeof(*map), GFP_KERNEL);
- + if (!map)
- + return -ENOMEM;
- +
- + spin_lock(&mem_lock);
- + if (i->flags & USDPAA_DMA_FLAG_SHARE) {
- + list_for_each_entry(frag, &mem_list, list) {
- + if (frag->refs && (frag->flags &
- + USDPAA_DMA_FLAG_SHARE) &&
- + !strncmp(i->name, frag->name,
- + USDPAA_DMA_NAME_MAX)) {
- + /* Matching entry */
- + if ((i->flags & USDPAA_DMA_FLAG_CREATE) &&
- + !(i->flags & USDPAA_DMA_FLAG_LAZY)) {
- + ret = -EBUSY;
- + goto out;
- + }
- +
- + /* Check to ensure size matches record */
- + if (i->len != frag->map_len && i->len) {
- + pr_err("ioctl_dma_map() Size requested does not match %s and is none zero. This usage will be disallowed in future release\n",
- + frag->name);
- + }
- +
- + /* Check if this has already been mapped
- + to this process */
- + list_for_each_entry(tmp, &ctx->maps, list)
- + if (tmp->root_frag == frag) {
- + /* Already mapped, just need to
- + inc ref count */
- + tmp->refs++;
- + kfree(map);
- + i->did_create = 0;
- + i->len = tmp->total_size;
- + i->phys_addr = frag->base;
- + i->ptr = tmp->virt_addr;
- + spin_unlock(&mem_lock);
- + return 0;
- + }
- + /* Matching entry - just need to map */
- + i->has_locking = frag->has_locking;
- + i->did_create = 0;
- + i->len = frag->map_len;
- + start_frag = frag;
- + goto do_map;
- + }
- + }
- + /* No matching entry */
- + if (!(i->flags & USDPAA_DMA_FLAG_CREATE)) {
- + pr_err("ioctl_dma_map() No matching entry\n");
- + ret = -ENOMEM;
- + goto out;
- + }
- + }
- + /* New fragment required, size must be provided. */
- + if (!i->len) {
- + ret = -EINVAL;
- + goto out;
- + }
- +
- + /* Find one of more contiguous fragments that satisfy the total length
- + trying to minimize the number of fragments
- + compute the largest page size that the allocation could use */
- + largest_page = largest_page_size(i->len);
- + start_frag = NULL;
- + while (largest_page &&
- + largest_page <= largest_page_size(phys_size) &&
- + start_frag == NULL) {
- + /* Search the list for a frag of that size */
- + list_for_each_entry(frag, &mem_list, list) {
- + if (!frag->refs && (frag->len == largest_page)) {
- + /* See if the next x fragments are free
- + and can accomidate the size */
- + u32 found_size = largest_page;
- + next_frag = list_entry(frag->list.prev,
- + struct mem_fragment,
- + list);
- + /* If the fragement is too small check
- + if the neighbours cab support it */
- + while (found_size < i->len) {
- + if (&mem_list == &next_frag->list)
- + break; /* End of list */
- + if (next_frag->refs != 0 ||
- + next_frag->len == 0)
- + break; /* not enough space */
- + found_size += next_frag->len;
- + next_frag = list_entry(
- + next_frag->list.prev,
- + struct mem_fragment,
- + list);
- + }
- + if (found_size >= i->len) {
- + /* Success! there is enough contigous
- + free space */
- + start_frag = frag;
- + break;
- + }
- + }
- + } /* next frag loop */
- + /* Couldn't statisfy the request with this
- + largest page size, try a smaller one */
- + largest_page <<= 2;
- + }
- + if (start_frag == NULL) {
- + /* Couldn't find proper amount of space */
- + ret = -ENOMEM;
- + goto out;
- + }
- + i->did_create = 1;
- +do_map:
- + /* Verify there is sufficient space to do the mapping */
- + down_write(¤t->mm->mmap_sem);
- + next_addr = usdpaa_get_unmapped_area(fp, next_addr, i->len, 0, 0);
- + up_write(¤t->mm->mmap_sem);
- +
- + if (next_addr & ~PAGE_MASK) {
- + ret = -ENOMEM;
- + goto out;
- + }
- +
- + /* We may need to divide the final fragment to accomidate the mapping */
- + next_frag = start_frag;
- + while (so_far != i->len) {
- + BUG_ON(next_frag->len == 0);
- + while ((next_frag->len + so_far) > i->len) {
- + /* Split frag until they match */
- + split_frag(next_frag);
- + }
- + so_far += next_frag->len;
- + next_frag->refs++;
- + ++frag_count;
- + next_frag = list_entry(next_frag->list.prev,
- + struct mem_fragment, list);
- + }
- + if (i->did_create) {
- + size_t name_len = 0;
- + start_frag->flags = i->flags;
- + strncpy(start_frag->name, i->name, USDPAA_DMA_NAME_MAX);
- + name_len = strnlen(start_frag->name, USDPAA_DMA_NAME_MAX);
- + if (name_len >= USDPAA_DMA_NAME_MAX) {
- + ret = -EFAULT;
- + goto out;
- + }
- + start_frag->map_len = i->len;
- + start_frag->has_locking = i->has_locking;
- + init_waitqueue_head(&start_frag->wq);
- + start_frag->owner = NULL;
- + }
- +
- + /* Setup the map entry */
- + map->root_frag = start_frag;
- + map->total_size = i->len;
- + map->frag_count = frag_count;
- + map->refs = 1;
- + list_add(&map->list, &ctx->maps);
- + i->phys_addr = start_frag->base;
- +out:
- + spin_unlock(&mem_lock);
- +
- + if (!ret) {
- + unsigned long longret;
- + down_write(¤t->mm->mmap_sem);
- + longret = do_mmap_pgoff(fp, next_addr, map->total_size,
- + PROT_READ |
- + (i->flags &
- + USDPAA_DMA_FLAG_RDONLY ? 0
- + : PROT_WRITE),
- + MAP_SHARED,
- + start_frag->pfn_base,
- + &populate);
- + up_write(¤t->mm->mmap_sem);
- + if (longret & ~PAGE_MASK) {
- + ret = (int)longret;
- + } else {
- + i->ptr = (void *)longret;
- + map->virt_addr = i->ptr;
- + }
- + } else
- + kfree(map);
- + return ret;
- +}
- +
- +static long ioctl_dma_unmap(struct ctx *ctx, void __user *arg)
- +{
- + struct mem_mapping *map;
- + struct vm_area_struct *vma;
- + int ret, i;
- + struct mem_fragment *current_frag;
- + size_t sz;
- + unsigned long base;
- + unsigned long vaddr;
- +
- + down_write(¤t->mm->mmap_sem);
- + vma = find_vma(current->mm, (unsigned long)arg);
- + if (!vma || (vma->vm_start > (unsigned long)arg)) {
- + up_write(¤t->mm->mmap_sem);
- + return -EFAULT;
- + }
- + spin_lock(&mem_lock);
- + list_for_each_entry(map, &ctx->maps, list) {
- + if (map->root_frag->pfn_base == vma->vm_pgoff) {
- + /* Drop the map lock if we hold it */
- + if (map->root_frag->has_locking &&
- + (map->root_frag->owner == map)) {
- + map->root_frag->owner = NULL;
- + wake_up(&map->root_frag->wq);
- + }
- + goto map_match;
- + }
- + }
- + /* Failed to find a matching mapping for this process */
- + ret = -EFAULT;
- + spin_unlock(&mem_lock);
- + goto out;
- +map_match:
- + map->refs--;
- + if (map->refs != 0) {
- + /* Another call the dma_map is referencing this */
- + ret = 0;
- + spin_unlock(&mem_lock);
- + goto out;
- + }
- +
- + current_frag = map->root_frag;
- + vaddr = (unsigned long) map->virt_addr;
- + for (i = 0; i < map->frag_count; i++) {
- + DPA_ASSERT(current_frag->refs > 0);
- + --current_frag->refs;
- +#if !(defined(CONFIG_ARM) || defined(CONFIG_ARM64))
- + /*
- + * Make sure we invalidate the TLB entry for
- + * this fragment, otherwise a remap of a different
- + * page to this vaddr would give acces to an
- + * incorrect piece of memory
- + */
- + cleartlbcam(vaddr, mfspr(SPRN_PID));
- +#endif
- + vaddr += current_frag->len;
- + current_frag = list_entry(current_frag->list.prev,
- + struct mem_fragment, list);
- + }
- + map->root_frag->name[0] = 0;
- + list_del(&map->list);
- + compress_frags();
- + spin_unlock(&mem_lock);
- +
- + base = vma->vm_start;
- + sz = vma->vm_end - vma->vm_start;
- + do_munmap(current->mm, base, sz);
- + ret = 0;
- + out:
- + up_write(¤t->mm->mmap_sem);
- + return ret;
- +}
- +
- +static long ioctl_dma_stats(struct ctx *ctx, void __user *arg)
- +{
- + struct mem_fragment *frag;
- + struct usdpaa_ioctl_dma_used result;
- +
- + result.free_bytes = 0;
- + result.total_bytes = phys_size;
- +
- + list_for_each_entry(frag, &mem_list, list) {
- + if (frag->refs == 0)
- + result.free_bytes += frag->len;
- + }
- +
- + return copy_to_user(arg, &result, sizeof(result)); }
- +
- +static int test_lock(struct mem_mapping *map)
- +{
- + int ret = 0;
- + spin_lock(&mem_lock);
- + if (!map->root_frag->owner) {
- + map->root_frag->owner = map;
- + ret = 1;
- + }
- + spin_unlock(&mem_lock);
- + return ret;
- +}
- +
- +static long ioctl_dma_lock(struct ctx *ctx, void __user *arg)
- +{
- + struct mem_mapping *map;
- + struct vm_area_struct *vma;
- +
- + down_read(¤t->mm->mmap_sem);
- + vma = find_vma(current->mm, (unsigned long)arg);
- + if (!vma || (vma->vm_start > (unsigned long)arg)) {
- + up_read(¤t->mm->mmap_sem);
- + return -EFAULT;
- + }
- + spin_lock(&mem_lock);
- + list_for_each_entry(map, &ctx->maps, list) {
- + if (map->root_frag->pfn_base == vma->vm_pgoff)
- + goto map_match;
- + }
- + map = NULL;
- +map_match:
- + spin_unlock(&mem_lock);
- + up_read(¤t->mm->mmap_sem);
- +
- + if (!map)
- + return -EFAULT;
- + if (!map->root_frag->has_locking)
- + return -ENODEV;
- + return wait_event_interruptible(map->root_frag->wq, test_lock(map));
- +}
- +
- +static long ioctl_dma_unlock(struct ctx *ctx, void __user *arg)
- +{
- + struct mem_mapping *map;
- + struct vm_area_struct *vma;
- + int ret;
- +
- + down_read(¤t->mm->mmap_sem);
- + vma = find_vma(current->mm, (unsigned long)arg);
- + if (!vma || (vma->vm_start > (unsigned long)arg))
- + ret = -EFAULT;
- + else {
- + spin_lock(&mem_lock);
- + list_for_each_entry(map, &ctx->maps, list) {
- + if (map->root_frag->pfn_base == vma->vm_pgoff) {
- + if (!map->root_frag->has_locking)
- + ret = -ENODEV;
- + else if (map->root_frag->owner == map) {
- + map->root_frag->owner = NULL;
- + wake_up(&map->root_frag->wq);
- + ret = 0;
- + } else
- + ret = -EBUSY;
- + goto map_match;
- + }
- + }
- + ret = -EINVAL;
- +map_match:
- + spin_unlock(&mem_lock);
- + }
- + up_read(¤t->mm->mmap_sem);
- + return ret;
- +}
- +
- +static int portal_mmap(struct file *fp, struct resource *res, void **ptr)
- +{
- + unsigned long longret = 0, populate;
- + resource_size_t len;
- +
- + down_write(¤t->mm->mmap_sem);
- + len = resource_size(res);
- + if (len != (unsigned long)len)
- + return -EINVAL;
- + longret = do_mmap_pgoff(fp, PAGE_SIZE, (unsigned long)len,
- + PROT_READ | PROT_WRITE, MAP_SHARED,
- + res->start >> PAGE_SHIFT, &populate);
- + up_write(¤t->mm->mmap_sem);
- +
- + if (longret & ~PAGE_MASK)
- + return (int)longret;
- +
- + *ptr = (void *) longret;
- + return 0;
- +}
- +
- +static void portal_munmap(struct resource *res, void *ptr)
- +{
- + down_write(¤t->mm->mmap_sem);
- + do_munmap(current->mm, (unsigned long)ptr, resource_size(res));
- + up_write(¤t->mm->mmap_sem);
- +}
- +
- +static long ioctl_portal_map(struct file *fp, struct ctx *ctx,
- + struct usdpaa_ioctl_portal_map *arg)
- +{
- + struct portal_mapping *mapping = kmalloc(sizeof(*mapping), GFP_KERNEL);
- + int ret;
- +
- + if (!mapping)
- + return -ENOMEM;
- +
- + mapping->user = *arg;
- + mapping->iommu_domain = NULL;
- +
- + if (mapping->user.type == usdpaa_portal_qman) {
- + mapping->qportal =
- + qm_get_unused_portal_idx(mapping->user.index);
- + if (!mapping->qportal) {
- + ret = -ENODEV;
- + goto err_get_portal;
- + }
- + mapping->phys = &mapping->qportal->addr_phys[0];
- + mapping->user.channel = mapping->qportal->public_cfg.channel;
- + mapping->user.pools = mapping->qportal->public_cfg.pools;
- + mapping->user.index = mapping->qportal->public_cfg.index;
- + } else if (mapping->user.type == usdpaa_portal_bman) {
- + mapping->bportal =
- + bm_get_unused_portal_idx(mapping->user.index);
- + if (!mapping->bportal) {
- + ret = -ENODEV;
- + goto err_get_portal;
- + }
- + mapping->phys = &mapping->bportal->addr_phys[0];
- + mapping->user.index = mapping->bportal->public_cfg.index;
- + } else {
- + ret = -EINVAL;
- + goto err_copy_from_user;
- + }
- + /* Need to put pcfg in ctx's list before the mmaps because the mmap
- + * handlers look it up. */
- + spin_lock(&mem_lock);
- + list_add(&mapping->list, &ctx->portals);
- + spin_unlock(&mem_lock);
- + ret = portal_mmap(fp, &mapping->phys[DPA_PORTAL_CE],
- + &mapping->user.addr.cena);
- + if (ret)
- + goto err_mmap_cena;
- + ret = portal_mmap(fp, &mapping->phys[DPA_PORTAL_CI],
- + &mapping->user.addr.cinh);
- + if (ret)
- + goto err_mmap_cinh;
- + *arg = mapping->user;
- + return ret;
- +
- +err_mmap_cinh:
- + portal_munmap(&mapping->phys[DPA_PORTAL_CE], mapping->user.addr.cena);
- +err_mmap_cena:
- + if ((mapping->user.type == usdpaa_portal_qman) && mapping->qportal)
- + qm_put_unused_portal(mapping->qportal);
- + else if ((mapping->user.type == usdpaa_portal_bman) && mapping->bportal)
- + bm_put_unused_portal(mapping->bportal);
- + spin_lock(&mem_lock);
- + list_del(&mapping->list);
- + spin_unlock(&mem_lock);
- +err_get_portal:
- +err_copy_from_user:
- + kfree(mapping);
- + return ret;
- +}
- +
- +static long ioctl_portal_unmap(struct ctx *ctx, struct usdpaa_portal_map *i)
- +{
- + struct portal_mapping *mapping;
- + struct vm_area_struct *vma;
- + unsigned long pfn;
- + u32 channel;
- +
- + /* Get the PFN corresponding to one of the virt addresses */
- + down_read(¤t->mm->mmap_sem);
- + vma = find_vma(current->mm, (unsigned long)i->cinh);
- + if (!vma || (vma->vm_start > (unsigned long)i->cinh)) {
- + up_read(¤t->mm->mmap_sem);
- + return -EFAULT;
- + }
- + pfn = vma->vm_pgoff;
- + up_read(¤t->mm->mmap_sem);
- +
- + /* Find the corresponding portal */
- + spin_lock(&mem_lock);
- + list_for_each_entry(mapping, &ctx->portals, list) {
- + if (pfn == (mapping->phys[DPA_PORTAL_CI].start >> PAGE_SHIFT))
- + goto found;
- + }
- + mapping = NULL;
- +found:
- + if (mapping)
- + list_del(&mapping->list);
- + spin_unlock(&mem_lock);
- + if (!mapping)
- + return -ENODEV;
- + portal_munmap(&mapping->phys[DPA_PORTAL_CI], mapping->user.addr.cinh);
- + portal_munmap(&mapping->phys[DPA_PORTAL_CE], mapping->user.addr.cena);
- + if (mapping->user.type == usdpaa_portal_qman) {
- + init_qm_portal(mapping->qportal,
- + &mapping->qman_portal_low);
- +
- + /* Tear down any FQs this portal is referencing */
- + channel = mapping->qportal->public_cfg.channel;
- + qm_check_and_destroy_fqs(&mapping->qman_portal_low,
- + &channel,
- + check_portal_channel);
- + qm_put_unused_portal(mapping->qportal);
- + } else if (mapping->user.type == usdpaa_portal_bman) {
- + init_bm_portal(mapping->bportal,
- + &mapping->bman_portal_low);
- + bm_put_unused_portal(mapping->bportal);
- + }
- + kfree(mapping);
- + return 0;
- +}
- +
- +static void portal_config_pamu(struct qm_portal_config *pcfg, uint8_t sdest,
- + uint32_t cpu, uint32_t cache, uint32_t window)
- +{
- +#ifdef CONFIG_FSL_PAMU
- + int ret;
- + int window_count = 1;
- + struct iommu_domain_geometry geom_attr;
- + struct pamu_stash_attribute stash_attr;
- +
- + pcfg->iommu_domain = iommu_domain_alloc(&platform_bus_type);
- + if (!pcfg->iommu_domain) {
- + pr_err(KBUILD_MODNAME ":%s(): iommu_domain_alloc() failed",
- + __func__);
- + goto _no_iommu;
- + }
- + geom_attr.aperture_start = 0;
- + geom_attr.aperture_end =
- + ((dma_addr_t)1 << min(8 * sizeof(dma_addr_t), (size_t)36)) - 1;
- + geom_attr.force_aperture = true;
- + ret = iommu_domain_set_attr(pcfg->iommu_domain, DOMAIN_ATTR_GEOMETRY,
- + &geom_attr);
- + if (ret < 0) {
- + pr_err(KBUILD_MODNAME ":%s(): iommu_domain_set_attr() = %d",
- + __func__, ret);
- + goto _iommu_domain_free;
- + }
- + ret = iommu_domain_set_attr(pcfg->iommu_domain, DOMAIN_ATTR_WINDOWS,
- + &window_count);
- + if (ret < 0) {
- + pr_err(KBUILD_MODNAME ":%s(): iommu_domain_set_attr() = %d",
- + __func__, ret);
- + goto _iommu_domain_free;
- + }
- + stash_attr.cpu = cpu;
- + stash_attr.cache = cache;
- + /* set stash information for the window */
- + stash_attr.window = 0;
- +
- + ret = iommu_domain_set_attr(pcfg->iommu_domain,
- + DOMAIN_ATTR_FSL_PAMU_STASH,
- + &stash_attr);
- + if (ret < 0) {
- + pr_err(KBUILD_MODNAME ":%s(): iommu_domain_set_attr() = %d",
- + __func__, ret);
- + goto _iommu_domain_free;
- + }
- + ret = iommu_domain_window_enable(pcfg->iommu_domain, 0, 0, 1ULL << 36,
- + IOMMU_READ | IOMMU_WRITE);
- + if (ret < 0) {
- + pr_err(KBUILD_MODNAME ":%s(): iommu_domain_window_enable() = %d",
- + __func__, ret);
- + goto _iommu_domain_free;
- + }
- + ret = iommu_attach_device(pcfg->iommu_domain, &pcfg->dev);
- + if (ret < 0) {
- + pr_err(KBUILD_MODNAME ":%s(): iommu_device_attach() = %d",
- + __func__, ret);
- + goto _iommu_domain_free;
- + }
- + ret = iommu_domain_set_attr(pcfg->iommu_domain,
- + DOMAIN_ATTR_FSL_PAMU_ENABLE,
- + &window_count);
- + if (ret < 0) {
- + pr_err(KBUILD_MODNAME ":%s(): iommu_domain_set_attr() = %d",
- + __func__, ret);
- + goto _iommu_detach_device;
- + }
- +_no_iommu:
- +#endif
- +
- +#ifdef CONFIG_FSL_QMAN_CONFIG
- + if (qman_set_sdest(pcfg->public_cfg.channel, sdest))
- +#endif
- + pr_warn("Failed to set QMan portal's stash request queue\n");
- +
- + return;
- +
- +#ifdef CONFIG_FSL_PAMU
- +_iommu_detach_device:
- + iommu_detach_device(pcfg->iommu_domain, NULL);
- +_iommu_domain_free:
- + iommu_domain_free(pcfg->iommu_domain);
- +#endif
- +}
- +
- +static long ioctl_allocate_raw_portal(struct file *fp, struct ctx *ctx,
- + struct usdpaa_ioctl_raw_portal *arg)
- +{
- + struct portal_mapping *mapping = kmalloc(sizeof(*mapping), GFP_KERNEL);
- + int ret;
- +
- + if (!mapping)
- + return -ENOMEM;
- +
- + mapping->user.type = arg->type;
- + mapping->iommu_domain = NULL;
- + if (arg->type == usdpaa_portal_qman) {
- + mapping->qportal = qm_get_unused_portal_idx(arg->index);
- + if (!mapping->qportal) {
- + ret = -ENODEV;
- + goto err;
- + }
- + mapping->phys = &mapping->qportal->addr_phys[0];
- + arg->index = mapping->qportal->public_cfg.index;
- + arg->cinh = mapping->qportal->addr_phys[DPA_PORTAL_CI].start;
- + arg->cena = mapping->qportal->addr_phys[DPA_PORTAL_CE].start;
- + if (arg->enable_stash) {
- + /* Setup the PAMU with the supplied parameters */
- + portal_config_pamu(mapping->qportal, arg->sdest,
- + arg->cpu, arg->cache, arg->window);
- + }
- + } else if (mapping->user.type == usdpaa_portal_bman) {
- + mapping->bportal =
- + bm_get_unused_portal_idx(arg->index);
- + if (!mapping->bportal) {
- + ret = -ENODEV;
- + goto err;
- + }
- + mapping->phys = &mapping->bportal->addr_phys[0];
- + arg->index = mapping->bportal->public_cfg.index;
- + arg->cinh = mapping->bportal->addr_phys[DPA_PORTAL_CI].start;
- + arg->cena = mapping->bportal->addr_phys[DPA_PORTAL_CE].start;
- + } else {
- + ret = -EINVAL;
- + goto err;
- + }
- + /* Need to put pcfg in ctx's list before the mmaps because the mmap
- + * handlers look it up. */
- + spin_lock(&mem_lock);
- + list_add(&mapping->list, &ctx->portals);
- + spin_unlock(&mem_lock);
- + return 0;
- +err:
- + kfree(mapping);
- + return ret;
- +}
- +
- +static long ioctl_free_raw_portal(struct file *fp, struct ctx *ctx,
- + struct usdpaa_ioctl_raw_portal *arg)
- +{
- + struct portal_mapping *mapping;
- + u32 channel;
- +
- + /* Find the corresponding portal */
- + spin_lock(&mem_lock);
- + list_for_each_entry(mapping, &ctx->portals, list) {
- + if (mapping->phys[DPA_PORTAL_CI].start == arg->cinh)
- + goto found;
- + }
- + mapping = NULL;
- +found:
- + if (mapping)
- + list_del(&mapping->list);
- + spin_unlock(&mem_lock);
- + if (!mapping)
- + return -ENODEV;
- + if (mapping->user.type == usdpaa_portal_qman) {
- + init_qm_portal(mapping->qportal,
- + &mapping->qman_portal_low);
- +
- + /* Tear down any FQs this portal is referencing */
- + channel = mapping->qportal->public_cfg.channel;
- + qm_check_and_destroy_fqs(&mapping->qman_portal_low,
- + &channel,
- + check_portal_channel);
- + qm_put_unused_portal(mapping->qportal);
- + } else if (mapping->user.type == usdpaa_portal_bman) {
- + init_bm_portal(mapping->bportal,
- + &mapping->bman_portal_low);
- + bm_put_unused_portal(mapping->bportal);
- + }
- + kfree(mapping);
- + return 0;
- +}
- +
- +static long usdpaa_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
- +{
- + struct ctx *ctx = fp->private_data;
- + void __user *a = (void __user *)arg;
- + switch (cmd) {
- + case USDPAA_IOCTL_ID_ALLOC:
- + return ioctl_id_alloc(ctx, a);
- + case USDPAA_IOCTL_ID_RELEASE:
- + return ioctl_id_release(ctx, a);
- + case USDPAA_IOCTL_ID_RESERVE:
- + return ioctl_id_reserve(ctx, a);
- + case USDPAA_IOCTL_DMA_MAP:
- + {
- + struct usdpaa_ioctl_dma_map input;
- + int ret;
- + if (copy_from_user(&input, a, sizeof(input)))
- + return -EFAULT;
- + ret = ioctl_dma_map(fp, ctx, &input);
- + if (copy_to_user(a, &input, sizeof(input)))
- + return -EFAULT;
- + return ret;
- + }
- + case USDPAA_IOCTL_DMA_UNMAP:
- + return ioctl_dma_unmap(ctx, a);
- + case USDPAA_IOCTL_DMA_LOCK:
- + return ioctl_dma_lock(ctx, a);
- + case USDPAA_IOCTL_DMA_UNLOCK:
- + return ioctl_dma_unlock(ctx, a);
- + case USDPAA_IOCTL_PORTAL_MAP:
- + {
- + struct usdpaa_ioctl_portal_map input;
- + int ret;
- + if (copy_from_user(&input, a, sizeof(input)))
- + return -EFAULT;
- + ret = ioctl_portal_map(fp, ctx, &input);
- + if (copy_to_user(a, &input, sizeof(input)))
- + return -EFAULT;
- + return ret;
- + }
- + case USDPAA_IOCTL_PORTAL_UNMAP:
- + {
- + struct usdpaa_portal_map input;
- + if (copy_from_user(&input, a, sizeof(input)))
- + return -EFAULT;
- + return ioctl_portal_unmap(ctx, &input);
- + }
- + case USDPAA_IOCTL_DMA_USED:
- + return ioctl_dma_stats(ctx, a);
- + case USDPAA_IOCTL_ALLOC_RAW_PORTAL:
- + {
- + struct usdpaa_ioctl_raw_portal input;
- + int ret;
- + if (copy_from_user(&input, a, sizeof(input)))
- + return -EFAULT;
- + ret = ioctl_allocate_raw_portal(fp, ctx, &input);
- + if (copy_to_user(a, &input, sizeof(input)))
- + return -EFAULT;
- + return ret;
- + }
- + case USDPAA_IOCTL_FREE_RAW_PORTAL:
- + {
- + struct usdpaa_ioctl_raw_portal input;
- + if (copy_from_user(&input, a, sizeof(input)))
- + return -EFAULT;
- + return ioctl_free_raw_portal(fp, ctx, &input);
- + }
- + }
- + return -EINVAL;
- +}
- +
- +static long usdpaa_ioctl_compat(struct file *fp, unsigned int cmd,
- + unsigned long arg)
- +{
- +#ifdef CONFIG_COMPAT
- + struct ctx *ctx = fp->private_data;
- + void __user *a = (void __user *)arg;
- +#endif
- + switch (cmd) {
- +#ifdef CONFIG_COMPAT
- + case USDPAA_IOCTL_DMA_MAP_COMPAT:
- + {
- + int ret;
- + struct usdpaa_ioctl_dma_map_compat input;
- + struct usdpaa_ioctl_dma_map converted;
- +
- + if (copy_from_user(&input, a, sizeof(input)))
- + return -EFAULT;
- +
- + converted.ptr = compat_ptr(input.ptr);
- + converted.phys_addr = input.phys_addr;
- + converted.len = input.len;
- + converted.flags = input.flags;
- + strncpy(converted.name, input.name, USDPAA_DMA_NAME_MAX);
- + converted.has_locking = input.has_locking;
- + converted.did_create = input.did_create;
- +
- + ret = ioctl_dma_map(fp, ctx, &converted);
- + input.ptr = ptr_to_compat(converted.ptr);
- + input.phys_addr = converted.phys_addr;
- + input.len = converted.len;
- + input.flags = converted.flags;
- + strncpy(input.name, converted.name, USDPAA_DMA_NAME_MAX);
- + input.has_locking = converted.has_locking;
- + input.did_create = converted.did_create;
- + if (copy_to_user(a, &input, sizeof(input)))
- + return -EFAULT;
- + return ret;
- + }
- + case USDPAA_IOCTL_PORTAL_MAP_COMPAT:
- + {
- + int ret;
- + struct compat_usdpaa_ioctl_portal_map input;
- + struct usdpaa_ioctl_portal_map converted;
- + if (copy_from_user(&input, a, sizeof(input)))
- + return -EFAULT;
- + converted.type = input.type;
- + converted.index = input.index;
- + ret = ioctl_portal_map(fp, ctx, &converted);
- + input.addr.cinh = ptr_to_compat(converted.addr.cinh);
- + input.addr.cena = ptr_to_compat(converted.addr.cena);
- + input.channel = converted.channel;
- + input.pools = converted.pools;
- + input.index = converted.index;
- + if (copy_to_user(a, &input, sizeof(input)))
- + return -EFAULT;
- + return ret;
- + }
- + case USDPAA_IOCTL_PORTAL_UNMAP_COMPAT:
- + {
- + struct usdpaa_portal_map_compat input;
- + struct usdpaa_portal_map converted;
- +
- + if (copy_from_user(&input, a, sizeof(input)))
- + return -EFAULT;
- + converted.cinh = compat_ptr(input.cinh);
- + converted.cena = compat_ptr(input.cena);
- + return ioctl_portal_unmap(ctx, &converted);
- + }
- + case USDPAA_IOCTL_ALLOC_RAW_PORTAL_COMPAT:
- + {
- + int ret;
- + struct usdpaa_ioctl_raw_portal converted;
- + struct compat_ioctl_raw_portal input;
- + if (copy_from_user(&input, a, sizeof(input)))
- + return -EFAULT;
- + converted.type = input.type;
- + converted.index = input.index;
- + converted.enable_stash = input.enable_stash;
- + converted.cpu = input.cpu;
- + converted.cache = input.cache;
- + converted.window = input.window;
- + converted.sdest = input.sdest;
- + ret = ioctl_allocate_raw_portal(fp, ctx, &converted);
- +
- + input.cinh = converted.cinh;
- + input.cena = converted.cena;
- + input.index = converted.index;
- +
- + if (copy_to_user(a, &input, sizeof(input)))
- + return -EFAULT;
- + return ret;
- + }
- + case USDPAA_IOCTL_FREE_RAW_PORTAL_COMPAT:
- + {
- + struct usdpaa_ioctl_raw_portal converted;
- + struct compat_ioctl_raw_portal input;
- + if (copy_from_user(&input, a, sizeof(input)))
- + return -EFAULT;
- + converted.type = input.type;
- + converted.index = input.index;
- + converted.cinh = input.cinh;
- + converted.cena = input.cena;
- + return ioctl_free_raw_portal(fp, ctx, &converted);
- + }
- +#endif
- + default:
- + return usdpaa_ioctl(fp, cmd, arg);
- + }
- + return -EINVAL;
- +}
- +
- +int usdpaa_get_portal_config(struct file *filp, void *cinh,
- + enum usdpaa_portal_type ptype, unsigned int *irq,
- + void **iir_reg)
- +{
- + /* Walk the list of portals for filp and return the config
- + for the portal that matches the hint */
- + struct ctx *context;
- + struct portal_mapping *portal;
- +
- + /* First sanitize the filp */
- + if (filp->f_op->open != usdpaa_open)
- + return -ENODEV;
- + context = filp->private_data;
- + spin_lock(&context->lock);
- + list_for_each_entry(portal, &context->portals, list) {
- + if (portal->user.type == ptype &&
- + portal->user.addr.cinh == cinh) {
- + if (ptype == usdpaa_portal_qman) {
- + *irq = portal->qportal->public_cfg.irq;
- + *iir_reg = portal->qportal->addr_virt[1] +
- + QM_REG_IIR;
- + } else {
- + *irq = portal->bportal->public_cfg.irq;
- + *iir_reg = portal->bportal->addr_virt[1] +
- + BM_REG_IIR;
- + }
- + spin_unlock(&context->lock);
- + return 0;
- + }
- + }
- + spin_unlock(&context->lock);
- + return -EINVAL;
- +}
- +
- +static const struct file_operations usdpaa_fops = {
- + .open = usdpaa_open,
- + .release = usdpaa_release,
- + .mmap = usdpaa_mmap,
- + .get_unmapped_area = usdpaa_get_unmapped_area,
- + .unlocked_ioctl = usdpaa_ioctl,
- + .compat_ioctl = usdpaa_ioctl_compat
- +};
- +
- +static struct miscdevice usdpaa_miscdev = {
- + .name = "fsl-usdpaa",
- + .fops = &usdpaa_fops,
- + .minor = MISC_DYNAMIC_MINOR,
- +};
- +
- +/* Early-boot memory allocation. The boot-arg "usdpaa_mem=<x>" is used to
- + * indicate how much memory (if any) to allocate during early boot. If the
- + * format "usdpaa_mem=<x>,<y>" is used, then <y> will be interpreted as the
- + * number of TLB1 entries to reserve (default is 1). If there are more mappings
- + * than there are TLB1 entries, fault-handling will occur. */
- +
- +static __init int usdpaa_mem(char *arg)
- +{
- + pr_warn("uspdaa_mem argument is depracated\n");
- + arg_phys_size = memparse(arg, &arg);
- + num_tlb = 1;
- + if (*arg == ',') {
- + unsigned long ul;
- + int err = kstrtoul(arg + 1, 0, &ul);
- + if (err < 0) {
- + num_tlb = 1;
- + pr_warn("ERROR, usdpaa_mem arg is invalid\n");
- + } else
- + num_tlb = (unsigned int)ul;
- + }
- + return 0;
- +}
- +early_param("usdpaa_mem", usdpaa_mem);
- +
- +static int usdpaa_mem_init(struct reserved_mem *rmem)
- +{
- + phys_start = rmem->base;
- + phys_size = rmem->size;
- +
- + WARN_ON(!(phys_start && phys_size));
- +
- + return 0;
- +}
- +RESERVEDMEM_OF_DECLARE(usdpaa_mem_init, "fsl,usdpaa-mem", usdpaa_mem_init);
- +
- +__init int fsl_usdpaa_init_early(void)
- +{
- + if (!phys_size || !phys_start) {
- + pr_info("No USDPAA memory, no 'fsl,usdpaa-mem' in device-tree\n");
- + return 0;
- + }
- + if (phys_size % PAGE_SIZE) {
- + pr_err("'fsl,usdpaa-mem' size must be a multiple of page size\n");
- + phys_size = 0;
- + return 0;
- + }
- + if (arg_phys_size && phys_size != arg_phys_size) {
- + pr_err("'usdpaa_mem argument size (0x%llx) does not match device tree size (0x%llx)\n",
- + arg_phys_size, phys_size);
- + phys_size = 0;
- + return 0;
- + }
- + pfn_start = phys_start >> PAGE_SHIFT;
- + pfn_size = phys_size >> PAGE_SHIFT;
- +#ifdef CONFIG_PPC
- + first_tlb = current_tlb = tlbcam_index;
- + tlbcam_index += num_tlb;
- +#endif
- + pr_info("USDPAA region at %llx:%llx(%lx:%lx), %d TLB1 entries)\n",
- + phys_start, phys_size, pfn_start, pfn_size, num_tlb);
- + return 0;
- +}
- +subsys_initcall(fsl_usdpaa_init_early);
- +
- +
- +static int __init usdpaa_init(void)
- +{
- + struct mem_fragment *frag;
- + int ret;
- + u64 tmp_size = phys_size;
- + u64 tmp_start = phys_start;
- + u64 tmp_pfn_size = pfn_size;
- + u64 tmp_pfn_start = pfn_start;
- +
- + pr_info("Freescale USDPAA process driver\n");
- + if (!phys_start) {
- + pr_warn("fsl-usdpaa: no region found\n");
- + return 0;
- + }
- +
- + while (tmp_size != 0) {
- + u32 frag_size = largest_page_size(tmp_size);
- + frag = kmalloc(sizeof(*frag), GFP_KERNEL);
- + if (!frag) {
- + pr_err("Failed to setup USDPAA memory accounting\n");
- + return -ENOMEM;
- + }
- + frag->base = tmp_start;
- + frag->len = frag->root_len = frag_size;
- + frag->root_pfn = tmp_pfn_start;
- + frag->pfn_base = tmp_pfn_start;
- + frag->pfn_len = frag_size / PAGE_SIZE;
- + frag->refs = 0;
- + init_waitqueue_head(&frag->wq);
- + frag->owner = NULL;
- + list_add(&frag->list, &mem_list);
- +
- + /* Adjust for this frag */
- + tmp_start += frag_size;
- + tmp_size -= frag_size;
- + tmp_pfn_start += frag_size / PAGE_SIZE;
- + tmp_pfn_size -= frag_size / PAGE_SIZE;
- + }
- + ret = misc_register(&usdpaa_miscdev);
- + if (ret)
- + pr_err("fsl-usdpaa: failed to register misc device\n");
- + return ret;
- +}
- +
- +static void __exit usdpaa_exit(void)
- +{
- + misc_deregister(&usdpaa_miscdev);
- +}
- +
- +module_init(usdpaa_init);
- +module_exit(usdpaa_exit);
- +
- +MODULE_LICENSE("GPL");
- +MODULE_AUTHOR("Freescale Semiconductor");
- +MODULE_DESCRIPTION("Freescale USDPAA process driver");
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/fsl_usdpaa_irq.c
- @@ -0,0 +1,289 @@
- +/* Copyright (c) 2013 Freescale Semiconductor, Inc.
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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.
- + */
- +
- +/* define a device that allows USPDAA processes to open a file
- + descriptor and specify which IRQ it wants to montior using an ioctl()
- + When an IRQ is received, the device becomes readable so that a process
- + can use read() or select() type calls to monitor for IRQs */
- +
- +#include <linux/miscdevice.h>
- +#include <linux/fs.h>
- +#include <linux/cdev.h>
- +#include <linux/slab.h>
- +#include <linux/interrupt.h>
- +#include <linux/poll.h>
- +#include <linux/uaccess.h>
- +#include <linux/fsl_usdpaa.h>
- +#include <linux/module.h>
- +#include <linux/fdtable.h>
- +#include <linux/file.h>
- +
- +#include "qman_low.h"
- +#include "bman_low.h"
- +
- +struct usdpaa_irq_ctx {
- + int irq_set; /* Set to true once the irq is set via ioctl */
- + unsigned int irq_num;
- + u32 last_irq_count; /* Last value returned from read */
- + u32 irq_count; /* Number of irqs since last read */
- + wait_queue_head_t wait_queue; /* Waiting processes */
- + spinlock_t lock;
- + void *inhibit_addr; /* inhibit register address */
- + struct file *usdpaa_filp;
- + char irq_name[128];
- +};
- +
- +static int usdpaa_irq_open(struct inode *inode, struct file *filp)
- +{
- + struct usdpaa_irq_ctx *ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
- + if (!ctx)
- + return -ENOMEM;
- + ctx->irq_set = 0;
- + ctx->irq_count = 0;
- + ctx->last_irq_count = 0;
- + init_waitqueue_head(&ctx->wait_queue);
- + spin_lock_init(&ctx->lock);
- + filp->private_data = ctx;
- + return 0;
- +}
- +
- +static int usdpaa_irq_release(struct inode *inode, struct file *filp)
- +{
- + struct usdpaa_irq_ctx *ctx = filp->private_data;
- + if (ctx->irq_set) {
- + /* Inhibit the IRQ */
- + out_be32(ctx->inhibit_addr, 0x1);
- + irq_set_affinity_hint(ctx->irq_num, NULL);
- + free_irq(ctx->irq_num, ctx);
- + ctx->irq_set = 0;
- + fput(ctx->usdpaa_filp);
- + }
- + kfree(filp->private_data);
- + return 0;
- +}
- +
- +static irqreturn_t usdpaa_irq_handler(int irq, void *_ctx)
- +{
- + unsigned long flags;
- + struct usdpaa_irq_ctx *ctx = _ctx;
- + spin_lock_irqsave(&ctx->lock, flags);
- + ++ctx->irq_count;
- + spin_unlock_irqrestore(&ctx->lock, flags);
- + wake_up_all(&ctx->wait_queue);
- + /* Set the inhibit register. This will be reenabled
- + once the USDPAA code handles the IRQ */
- + out_be32(ctx->inhibit_addr, 0x1);
- + pr_info("Inhibit at %p count %d", ctx->inhibit_addr, ctx->irq_count);
- + return IRQ_HANDLED;
- +}
- +
- +static int map_irq(struct file *fp, struct usdpaa_ioctl_irq_map *irq_map)
- +{
- + struct usdpaa_irq_ctx *ctx = fp->private_data;
- + int ret;
- +
- + if (ctx->irq_set) {
- + pr_debug("Setting USDPAA IRQ when it was already set!\n");
- + return -EBUSY;
- + }
- +
- + ctx->usdpaa_filp = fget(irq_map->fd);
- + if (!ctx->usdpaa_filp) {
- + pr_debug("USDPAA fget(%d) returned NULL\n", irq_map->fd);
- + return -EINVAL;
- + }
- +
- + ret = usdpaa_get_portal_config(ctx->usdpaa_filp, irq_map->portal_cinh,
- + irq_map->type, &ctx->irq_num,
- + &ctx->inhibit_addr);
- + if (ret) {
- + pr_debug("USDPAA IRQ couldn't identify portal\n");
- + fput(ctx->usdpaa_filp);
- + return ret;
- + }
- +
- + ctx->irq_set = 1;
- +
- + snprintf(ctx->irq_name, sizeof(ctx->irq_name),
- + "usdpaa_irq %d", ctx->irq_num);
- +
- + ret = request_irq(ctx->irq_num, usdpaa_irq_handler, 0,
- + ctx->irq_name, ctx);
- + if (ret) {
- + pr_err("USDPAA request_irq(%d) failed, ret= %d\n",
- + ctx->irq_num, ret);
- + ctx->irq_set = 0;
- + fput(ctx->usdpaa_filp);
- + return ret;
- + }
- + ret = irq_set_affinity(ctx->irq_num, tsk_cpus_allowed(current));
- + if (ret)
- + pr_err("USDPAA irq_set_affinity() failed, ret= %d\n", ret);
- +
- + ret = irq_set_affinity_hint(ctx->irq_num, tsk_cpus_allowed(current));
- + if (ret)
- + pr_err("USDPAA irq_set_affinity_hint() failed, ret= %d\n", ret);
- +
- + return 0;
- +}
- +
- +static long usdpaa_irq_ioctl(struct file *fp, unsigned int cmd,
- + unsigned long arg)
- +{
- + int ret;
- + struct usdpaa_ioctl_irq_map irq_map;
- +
- + if (cmd != USDPAA_IOCTL_PORTAL_IRQ_MAP) {
- + pr_debug("USDPAA IRQ unknown command 0x%x\n", cmd);
- + return -EINVAL;
- + }
- +
- + ret = copy_from_user(&irq_map, (void __user *)arg,
- + sizeof(irq_map));
- + if (ret)
- + return ret;
- + return map_irq(fp, &irq_map);
- +}
- +
- +static ssize_t usdpaa_irq_read(struct file *filp, char __user *buff,
- + size_t count, loff_t *offp)
- +{
- + struct usdpaa_irq_ctx *ctx = filp->private_data;
- + int ret;
- +
- + if (!ctx->irq_set) {
- + pr_debug("Reading USDPAA IRQ before it was set\n");
- + return -EINVAL;
- + }
- +
- + if (count < sizeof(ctx->irq_count)) {
- + pr_debug("USDPAA IRQ Read too small\n");
- + return -EINVAL;
- + }
- + if (ctx->irq_count == ctx->last_irq_count) {
- + if (filp->f_flags & O_NONBLOCK)
- + return -EAGAIN;
- +
- + ret = wait_event_interruptible(ctx->wait_queue,
- + ctx->irq_count != ctx->last_irq_count);
- + if (ret == -ERESTARTSYS)
- + return ret;
- + }
- +
- + ctx->last_irq_count = ctx->irq_count;
- +
- + if (copy_to_user(buff, &ctx->last_irq_count,
- + sizeof(ctx->last_irq_count)))
- + return -EFAULT;
- + return sizeof(ctx->irq_count);
- +}
- +
- +static unsigned int usdpaa_irq_poll(struct file *filp, poll_table *wait)
- +{
- + struct usdpaa_irq_ctx *ctx = filp->private_data;
- + unsigned int ret = 0;
- + unsigned long flags;
- +
- + if (!ctx->irq_set)
- + return POLLHUP;
- +
- + poll_wait(filp, &ctx->wait_queue, wait);
- +
- + spin_lock_irqsave(&ctx->lock, flags);
- + if (ctx->irq_count != ctx->last_irq_count)
- + ret |= POLLIN | POLLRDNORM;
- + spin_unlock_irqrestore(&ctx->lock, flags);
- + return ret;
- +}
- +
- +static long usdpaa_irq_ioctl_compat(struct file *fp, unsigned int cmd,
- + unsigned long arg)
- +{
- +#ifdef CONFIG_COMPAT
- + void __user *a = (void __user *)arg;
- +#endif
- + switch (cmd) {
- +#ifdef CONFIG_COMPAT
- + case USDPAA_IOCTL_PORTAL_IRQ_MAP_COMPAT:
- + {
- + struct compat_ioctl_irq_map input;
- + struct usdpaa_ioctl_irq_map converted;
- + if (copy_from_user(&input, a, sizeof(input)))
- + return -EFAULT;
- + converted.type = input.type;
- + converted.fd = input.fd;
- + converted.portal_cinh = compat_ptr(input.portal_cinh);
- + return map_irq(fp, &converted);
- + }
- +#endif
- + default:
- + return usdpaa_irq_ioctl(fp, cmd, arg);
- + }
- +}
- +
- +static const struct file_operations usdpaa_irq_fops = {
- + .open = usdpaa_irq_open,
- + .release = usdpaa_irq_release,
- + .unlocked_ioctl = usdpaa_irq_ioctl,
- + .compat_ioctl = usdpaa_irq_ioctl_compat,
- + .read = usdpaa_irq_read,
- + .poll = usdpaa_irq_poll
- +};
- +
- +static struct miscdevice usdpaa_miscdev = {
- + .name = "fsl-usdpaa-irq",
- + .fops = &usdpaa_irq_fops,
- + .minor = MISC_DYNAMIC_MINOR,
- +};
- +
- +static int __init usdpaa_irq_init(void)
- +{
- + int ret;
- +
- + pr_info("Freescale USDPAA process IRQ driver\n");
- + ret = misc_register(&usdpaa_miscdev);
- + if (ret)
- + pr_err("fsl-usdpaa-irq: failed to register misc device\n");
- + return ret;
- +}
- +
- +static void __exit usdpaa_irq_exit(void)
- +{
- + misc_deregister(&usdpaa_miscdev);
- +}
- +
- +module_init(usdpaa_irq_init);
- +module_exit(usdpaa_irq_exit);
- +
- +MODULE_LICENSE("GPL");
- +MODULE_AUTHOR("Freescale Semiconductor");
- +MODULE_DESCRIPTION("Freescale USDPAA process IRQ driver");
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/qbman_driver.c
- @@ -0,0 +1,88 @@
- +/* Copyright 2013 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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 <linux/time.h>
- +#include "qman_private.h"
- +#include "bman_private.h"
- +__init void qman_init_early(void);
- +__init void bman_init_early(void);
- +
- +static __init int qbman_init(void)
- +{
- + struct device_node *dn;
- + u32 is_portal_available;
- +
- + bman_init();
- + qman_init();
- +
- + is_portal_available = 0;
- + for_each_compatible_node(dn, NULL, "fsl,qman-portal") {
- + if (!of_device_is_available(dn))
- + continue;
- + else
- + is_portal_available = 1;
- + }
- +
- + if (!qman_have_ccsr() && is_portal_available) {
- + struct qman_fq fq = {
- + .fqid = 1
- + };
- + struct qm_mcr_queryfq_np np;
- + int err, retry = CONFIG_FSL_QMAN_INIT_TIMEOUT;
- + struct timespec nowts, diffts, startts = current_kernel_time();
- + /* Loop while querying given fqid succeeds or time out */
- + while (1) {
- + err = qman_query_fq_np(&fq, &np);
- + if (!err) {
- + /* success, control-plane has configured QMan */
- + break;
- + } else if (err != -ERANGE) {
- + pr_err("QMan: I/O error, continuing anyway\n");
- + break;
- + }
- + nowts = current_kernel_time();
- + diffts = timespec_sub(nowts, startts);
- + if (diffts.tv_sec > 0) {
- + if (!retry--) {
- + pr_err("QMan: time out, control-plane"
- + " dead?\n");
- + break;
- + }
- + pr_warn("QMan: polling for the control-plane"
- + " (%d)\n", retry);
- + }
- + }
- + }
- + bman_resource_init();
- + qman_resource_init();
- + return 0;
- +}
- +subsys_initcall(qbman_init);
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/qman_config.c
- @@ -0,0 +1,1199 @@
- +/* Copyright 2008-2012 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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/cacheflush.h>
- +#include "qman_private.h"
- +#include <linux/highmem.h>
- +#include <linux/of_reserved_mem.h>
- +
- +/* Last updated for v00.800 of the BG */
- +
- +/* Register offsets */
- +#define REG_QCSP_LIO_CFG(n) (0x0000 + ((n) * 0x10))
- +#define REG_QCSP_IO_CFG(n) (0x0004 + ((n) * 0x10))
- +#define REG_QCSP_DD_CFG(n) (0x000c + ((n) * 0x10))
- +#define REG_DD_CFG 0x0200
- +#define REG_DCP_CFG(n) (0x0300 + ((n) * 0x10))
- +#define REG_DCP_DD_CFG(n) (0x0304 + ((n) * 0x10))
- +#define REG_DCP_DLM_AVG(n) (0x030c + ((n) * 0x10))
- +#define REG_PFDR_FPC 0x0400
- +#define REG_PFDR_FP_HEAD 0x0404
- +#define REG_PFDR_FP_TAIL 0x0408
- +#define REG_PFDR_FP_LWIT 0x0410
- +#define REG_PFDR_CFG 0x0414
- +#define REG_SFDR_CFG 0x0500
- +#define REG_SFDR_IN_USE 0x0504
- +#define REG_WQ_CS_CFG(n) (0x0600 + ((n) * 0x04))
- +#define REG_WQ_DEF_ENC_WQID 0x0630
- +#define REG_WQ_SC_DD_CFG(n) (0x640 + ((n) * 0x04))
- +#define REG_WQ_PC_DD_CFG(n) (0x680 + ((n) * 0x04))
- +#define REG_WQ_DC0_DD_CFG(n) (0x6c0 + ((n) * 0x04))
- +#define REG_WQ_DC1_DD_CFG(n) (0x700 + ((n) * 0x04))
- +#define REG_WQ_DCn_DD_CFG(n) (0x6c0 + ((n) * 0x40)) /* n=2,3 */
- +#define REG_CM_CFG 0x0800
- +#define REG_ECSR 0x0a00
- +#define REG_ECIR 0x0a04
- +#define REG_EADR 0x0a08
- +#define REG_ECIR2 0x0a0c
- +#define REG_EDATA(n) (0x0a10 + ((n) * 0x04))
- +#define REG_SBEC(n) (0x0a80 + ((n) * 0x04))
- +#define REG_MCR 0x0b00
- +#define REG_MCP(n) (0x0b04 + ((n) * 0x04))
- +#define REG_MISC_CFG 0x0be0
- +#define REG_HID_CFG 0x0bf0
- +#define REG_IDLE_STAT 0x0bf4
- +#define REG_IP_REV_1 0x0bf8
- +#define REG_IP_REV_2 0x0bfc
- +#define REG_FQD_BARE 0x0c00
- +#define REG_PFDR_BARE 0x0c20
- +#define REG_offset_BAR 0x0004 /* relative to REG_[FQD|PFDR]_BARE */
- +#define REG_offset_AR 0x0010 /* relative to REG_[FQD|PFDR]_BARE */
- +#define REG_QCSP_BARE 0x0c80
- +#define REG_QCSP_BAR 0x0c84
- +#define REG_CI_SCHED_CFG 0x0d00
- +#define REG_SRCIDR 0x0d04
- +#define REG_LIODNR 0x0d08
- +#define REG_CI_RLM_AVG 0x0d14
- +#define REG_ERR_ISR 0x0e00 /* + "enum qm_isr_reg" */
- +#define REG_REV3_QCSP_LIO_CFG(n) (0x1000 + ((n) * 0x10))
- +#define REG_REV3_QCSP_IO_CFG(n) (0x1004 + ((n) * 0x10))
- +#define REG_REV3_QCSP_DD_CFG(n) (0x100c + ((n) * 0x10))
- +#define REG_CEETM_CFG_IDX 0x900
- +#define REG_CEETM_CFG_PRES 0x904
- +#define REG_CEETM_XSFDR_IN_USE 0x908
- +
- +/* Assists for QMAN_MCR */
- +#define MCR_INIT_PFDR 0x01000000
- +#define MCR_get_rslt(v) (u8)((v) >> 24)
- +#define MCR_rslt_idle(r) (!rslt || (rslt >= 0xf0))
- +#define MCR_rslt_ok(r) (rslt == 0xf0)
- +#define MCR_rslt_eaccess(r) (rslt == 0xf8)
- +#define MCR_rslt_inval(r) (rslt == 0xff)
- +
- +struct qman;
- +
- +/* Follows WQ_CS_CFG0-5 */
- +enum qm_wq_class {
- + qm_wq_portal = 0,
- + qm_wq_pool = 1,
- + qm_wq_fman0 = 2,
- + qm_wq_fman1 = 3,
- + qm_wq_caam = 4,
- + qm_wq_pme = 5,
- + qm_wq_first = qm_wq_portal,
- + qm_wq_last = qm_wq_pme
- +};
- +
- +/* Follows FQD_[BARE|BAR|AR] and PFDR_[BARE|BAR|AR] */
- +enum qm_memory {
- + qm_memory_fqd,
- + qm_memory_pfdr
- +};
- +
- +/* Used by all error interrupt registers except 'inhibit' */
- +#define QM_EIRQ_CIDE 0x20000000 /* Corenet Initiator Data Error */
- +#define QM_EIRQ_CTDE 0x10000000 /* Corenet Target Data Error */
- +#define QM_EIRQ_CITT 0x08000000 /* Corenet Invalid Target Transaction */
- +#define QM_EIRQ_PLWI 0x04000000 /* PFDR Low Watermark */
- +#define QM_EIRQ_MBEI 0x02000000 /* Multi-bit ECC Error */
- +#define QM_EIRQ_SBEI 0x01000000 /* Single-bit ECC Error */
- +#define QM_EIRQ_PEBI 0x00800000 /* PFDR Enqueues Blocked Interrupt */
- +#define QM_EIRQ_IFSI 0x00020000 /* Invalid FQ Flow Control State */
- +#define QM_EIRQ_ICVI 0x00010000 /* Invalid Command Verb */
- +#define QM_EIRQ_IDDI 0x00000800 /* Invalid Dequeue (Direct-connect) */
- +#define QM_EIRQ_IDFI 0x00000400 /* Invalid Dequeue FQ */
- +#define QM_EIRQ_IDSI 0x00000200 /* Invalid Dequeue Source */
- +#define QM_EIRQ_IDQI 0x00000100 /* Invalid Dequeue Queue */
- +#define QM_EIRQ_IECE 0x00000010 /* Invalid Enqueue Configuration */
- +#define QM_EIRQ_IEOI 0x00000008 /* Invalid Enqueue Overflow */
- +#define QM_EIRQ_IESI 0x00000004 /* Invalid Enqueue State */
- +#define QM_EIRQ_IECI 0x00000002 /* Invalid Enqueue Channel */
- +#define QM_EIRQ_IEQI 0x00000001 /* Invalid Enqueue Queue */
- +
- +/* QMAN_ECIR valid error bit */
- +#define PORTAL_ECSR_ERR (QM_EIRQ_IEQI | QM_EIRQ_IESI | QM_EIRQ_IEOI | \
- + QM_EIRQ_IDQI | QM_EIRQ_IDSI | QM_EIRQ_IDFI | \
- + QM_EIRQ_IDDI | QM_EIRQ_ICVI | QM_EIRQ_IFSI)
- +#define FQID_ECSR_ERR (QM_EIRQ_IEQI | QM_EIRQ_IECI | QM_EIRQ_IESI | \
- + QM_EIRQ_IEOI | QM_EIRQ_IDQI | QM_EIRQ_IDFI | \
- + QM_EIRQ_IFSI)
- +
- +union qman_ecir {
- + u32 ecir_raw;
- + struct {
- + u32 __reserved:2;
- + u32 portal_type:1;
- + u32 portal_num:5;
- + u32 fqid:24;
- + } __packed info;
- +};
- +
- +union qman_ecir2 {
- + u32 ecir2_raw;
- + struct {
- + u32 portal_type:1;
- + u32 __reserved:21;
- + u32 portal_num:10;
- + } __packed info;
- +};
- +
- +union qman_eadr {
- + u32 eadr_raw;
- + struct {
- + u32 __reserved1:4;
- + u32 memid:4;
- + u32 __reserved2:12;
- + u32 eadr:12;
- + } __packed info;
- + struct {
- + u32 __reserved1:3;
- + u32 memid:5;
- + u32 __reserved:8;
- + u32 eadr:16;
- + } __packed info_rev3;
- +};
- +
- +struct qman_hwerr_txt {
- + u32 mask;
- + const char *txt;
- +};
- +
- +#define QMAN_HWE_TXT(a, b) { .mask = QM_EIRQ_##a, .txt = b }
- +
- +static const struct qman_hwerr_txt qman_hwerr_txts[] = {
- + QMAN_HWE_TXT(CIDE, "Corenet Initiator Data Error"),
- + QMAN_HWE_TXT(CTDE, "Corenet Target Data Error"),
- + QMAN_HWE_TXT(CITT, "Corenet Invalid Target Transaction"),
- + QMAN_HWE_TXT(PLWI, "PFDR Low Watermark"),
- + QMAN_HWE_TXT(MBEI, "Multi-bit ECC Error"),
- + QMAN_HWE_TXT(SBEI, "Single-bit ECC Error"),
- + QMAN_HWE_TXT(PEBI, "PFDR Enqueues Blocked Interrupt"),
- + QMAN_HWE_TXT(ICVI, "Invalid Command Verb"),
- + QMAN_HWE_TXT(IFSI, "Invalid Flow Control State"),
- + QMAN_HWE_TXT(IDDI, "Invalid Dequeue (Direct-connect)"),
- + QMAN_HWE_TXT(IDFI, "Invalid Dequeue FQ"),
- + QMAN_HWE_TXT(IDSI, "Invalid Dequeue Source"),
- + QMAN_HWE_TXT(IDQI, "Invalid Dequeue Queue"),
- + QMAN_HWE_TXT(IECE, "Invalid Enqueue Configuration"),
- + QMAN_HWE_TXT(IEOI, "Invalid Enqueue Overflow"),
- + QMAN_HWE_TXT(IESI, "Invalid Enqueue State"),
- + QMAN_HWE_TXT(IECI, "Invalid Enqueue Channel"),
- + QMAN_HWE_TXT(IEQI, "Invalid Enqueue Queue")
- +};
- +#define QMAN_HWE_COUNT (sizeof(qman_hwerr_txts)/sizeof(struct qman_hwerr_txt))
- +
- +struct qman_error_info_mdata {
- + u16 addr_mask;
- + u16 bits;
- + const char *txt;
- +};
- +
- +#define QMAN_ERR_MDATA(a, b, c) { .addr_mask = a, .bits = b, .txt = c}
- +static const struct qman_error_info_mdata error_mdata[] = {
- + QMAN_ERR_MDATA(0x01FF, 24, "FQD cache tag memory 0"),
- + QMAN_ERR_MDATA(0x01FF, 24, "FQD cache tag memory 1"),
- + QMAN_ERR_MDATA(0x01FF, 24, "FQD cache tag memory 2"),
- + QMAN_ERR_MDATA(0x01FF, 24, "FQD cache tag memory 3"),
- + QMAN_ERR_MDATA(0x0FFF, 512, "FQD cache memory"),
- + QMAN_ERR_MDATA(0x07FF, 128, "SFDR memory"),
- + QMAN_ERR_MDATA(0x01FF, 72, "WQ context memory"),
- + QMAN_ERR_MDATA(0x00FF, 240, "CGR memory"),
- + QMAN_ERR_MDATA(0x00FF, 302, "Internal Order Restoration List memory"),
- + QMAN_ERR_MDATA(0x01FF, 256, "SW portal ring memory"),
- + QMAN_ERR_MDATA(0x07FF, 181, "CEETM class queue descriptor memory"),
- + QMAN_ERR_MDATA(0x0FFF, 140, "CEETM extended SFDR memory"),
- + QMAN_ERR_MDATA(0x0FFF, 25, "CEETM logical FQ mapping memory"),
- + QMAN_ERR_MDATA(0x0FFF, 96, "CEETM dequeue context memory"),
- + QMAN_ERR_MDATA(0x07FF, 396, "CEETM ccgr memory"),
- + QMAN_ERR_MDATA(0x00FF, 146, "CEETM CQ channel shaping memory"),
- + QMAN_ERR_MDATA(0x007F, 256, "CEETM CQ channel scheduling memory"),
- + QMAN_ERR_MDATA(0x01FF, 88, "CEETM dequeue statistics memory"),
- +};
- +#define QMAN_ERR_MDATA_COUNT \
- + (sizeof(error_mdata)/sizeof(struct qman_error_info_mdata))
- +
- +/* Add this in Kconfig */
- +#define QMAN_ERRS_TO_UNENABLE (QM_EIRQ_PLWI | QM_EIRQ_PEBI)
- +
- +/**
- + * qm_err_isr_<reg>_<verb> - Manipulate global interrupt registers
- + * @v: for accessors that write values, this is the 32-bit value
- + *
- + * Manipulates QMAN_ERR_ISR, QMAN_ERR_IER, QMAN_ERR_ISDR, QMAN_ERR_IIR. All
- + * manipulations except qm_err_isr_[un]inhibit() use 32-bit masks composed of
- + * the QM_EIRQ_*** definitions. Note that "qm_err_isr_enable_write" means
- + * "write the enable register" rather than "enable the write register"!
- + */
- +#define qm_err_isr_status_read(qm) \
- + __qm_err_isr_read(qm, qm_isr_status)
- +#define qm_err_isr_status_clear(qm, m) \
- + __qm_err_isr_write(qm, qm_isr_status, m)
- +#define qm_err_isr_enable_read(qm) \
- + __qm_err_isr_read(qm, qm_isr_enable)
- +#define qm_err_isr_enable_write(qm, v) \
- + __qm_err_isr_write(qm, qm_isr_enable, v)
- +#define qm_err_isr_disable_read(qm) \
- + __qm_err_isr_read(qm, qm_isr_disable)
- +#define qm_err_isr_disable_write(qm, v) \
- + __qm_err_isr_write(qm, qm_isr_disable, v)
- +#define qm_err_isr_inhibit(qm) \
- + __qm_err_isr_write(qm, qm_isr_inhibit, 1)
- +#define qm_err_isr_uninhibit(qm) \
- + __qm_err_isr_write(qm, qm_isr_inhibit, 0)
- +
- +/*
- + * TODO: unimplemented registers
- + *
- + * Keeping a list here of Qman registers I have not yet covered;
- + * QCSP_DD_IHRSR, QCSP_DD_IHRFR, QCSP_DD_HASR,
- + * DCP_DD_IHRSR, DCP_DD_IHRFR, DCP_DD_HASR, CM_CFG,
- + * QMAN_EECC, QMAN_SBET, QMAN_EINJ, QMAN_SBEC0-12
- + */
- +
- +/* Encapsulate "struct qman *" as a cast of the register space address. */
- +
- +static struct qman *qm_create(void *regs)
- +{
- + return (struct qman *)regs;
- +}
- +
- +static inline u32 __qm_in(struct qman *qm, u32 offset)
- +{
- + return in_be32((void *)qm + offset);
- +}
- +static inline void __qm_out(struct qman *qm, u32 offset, u32 val)
- +{
- + out_be32((void *)qm + offset, val);
- +}
- +#define qm_in(reg) __qm_in(qm, REG_##reg)
- +#define qm_out(reg, val) __qm_out(qm, REG_##reg, val)
- +
- +static u32 __qm_err_isr_read(struct qman *qm, enum qm_isr_reg n)
- +{
- + return __qm_in(qm, REG_ERR_ISR + (n << 2));
- +}
- +
- +static void __qm_err_isr_write(struct qman *qm, enum qm_isr_reg n, u32 val)
- +{
- + __qm_out(qm, REG_ERR_ISR + (n << 2), val);
- +}
- +
- +static void qm_set_dc(struct qman *qm, enum qm_dc_portal portal,
- + int ed, u8 sernd)
- +{
- + DPA_ASSERT(!ed || (portal == qm_dc_portal_fman0) ||
- + (portal == qm_dc_portal_fman1));
- + if ((qman_ip_rev & 0xFF00) >= QMAN_REV30)
- + qm_out(DCP_CFG(portal), (ed ? 0x1000 : 0) | (sernd & 0x3ff));
- + else
- + qm_out(DCP_CFG(portal), (ed ? 0x100 : 0) | (sernd & 0x1f));
- +}
- +
- +static void qm_set_wq_scheduling(struct qman *qm, enum qm_wq_class wq_class,
- + u8 cs_elev, u8 csw2, u8 csw3, u8 csw4, u8 csw5,
- + u8 csw6, u8 csw7)
- +{
- + qm_out(WQ_CS_CFG(wq_class), ((cs_elev & 0xff) << 24) |
- + ((csw2 & 0x7) << 20) | ((csw3 & 0x7) << 16) |
- + ((csw4 & 0x7) << 12) | ((csw5 & 0x7) << 8) |
- + ((csw6 & 0x7) << 4) | (csw7 & 0x7));
- +}
- +
- +static void qm_set_hid(struct qman *qm)
- +{
- + qm_out(HID_CFG, 0);
- +}
- +
- +static void qm_set_corenet_initiator(struct qman *qm)
- +{
- + qm_out(CI_SCHED_CFG,
- + 0x80000000 | /* write srcciv enable */
- + (CONFIG_FSL_QMAN_CI_SCHED_CFG_SRCCIV << 24) |
- + (CONFIG_FSL_QMAN_CI_SCHED_CFG_SRQ_W << 8) |
- + (CONFIG_FSL_QMAN_CI_SCHED_CFG_RW_W << 4) |
- + CONFIG_FSL_QMAN_CI_SCHED_CFG_BMAN_W);
- +}
- +
- +static void qm_get_version(struct qman *qm, u16 *id, u8 *major, u8 *minor,
- + u8 *cfg)
- +{
- + u32 v = qm_in(IP_REV_1);
- + u32 v2 = qm_in(IP_REV_2);
- + *id = (v >> 16);
- + *major = (v >> 8) & 0xff;
- + *minor = v & 0xff;
- + *cfg = v2 & 0xff;
- +}
- +
- +static void qm_set_memory(struct qman *qm, enum qm_memory memory, u64 ba,
- + int enable, int prio, int stash, u32 size)
- +{
- + u32 offset = (memory == qm_memory_fqd) ? REG_FQD_BARE : REG_PFDR_BARE;
- + u32 exp = ilog2(size);
- + /* choke if size isn't within range */
- + DPA_ASSERT((size >= 4096) && (size <= 1073741824) &&
- + is_power_of_2(size));
- + /* choke if 'ba' has lower-alignment than 'size' */
- + DPA_ASSERT(!(ba & (size - 1)));
- + __qm_out(qm, offset, upper_32_bits(ba));
- + __qm_out(qm, offset + REG_offset_BAR, lower_32_bits(ba));
- + __qm_out(qm, offset + REG_offset_AR,
- + (enable ? 0x80000000 : 0) |
- + (prio ? 0x40000000 : 0) |
- + (stash ? 0x20000000 : 0) |
- + (exp - 1));
- +}
- +
- +static void qm_set_pfdr_threshold(struct qman *qm, u32 th, u8 k)
- +{
- + qm_out(PFDR_FP_LWIT, th & 0xffffff);
- + qm_out(PFDR_CFG, k);
- +}
- +
- +static void qm_set_sfdr_threshold(struct qman *qm, u16 th)
- +{
- + qm_out(SFDR_CFG, th & 0x3ff);
- +}
- +
- +static int qm_init_pfdr(struct qman *qm, u32 pfdr_start, u32 num)
- +{
- + u8 rslt = MCR_get_rslt(qm_in(MCR));
- +
- + DPA_ASSERT(pfdr_start && !(pfdr_start & 7) && !(num & 7) && num);
- + /* Make sure the command interface is 'idle' */
- + if (!MCR_rslt_idle(rslt))
- + panic("QMAN_MCR isn't idle");
- +
- + /* Write the MCR command params then the verb */
- + qm_out(MCP(0), pfdr_start);
- + /* TODO: remove this - it's a workaround for a model bug that is
- + * corrected in more recent versions. We use the workaround until
- + * everyone has upgraded. */
- + qm_out(MCP(1), (pfdr_start + num - 16));
- + lwsync();
- + qm_out(MCR, MCR_INIT_PFDR);
- + /* Poll for the result */
- + do {
- + rslt = MCR_get_rslt(qm_in(MCR));
- + } while (!MCR_rslt_idle(rslt));
- + if (MCR_rslt_ok(rslt))
- + return 0;
- + if (MCR_rslt_eaccess(rslt))
- + return -EACCES;
- + if (MCR_rslt_inval(rslt))
- + return -EINVAL;
- + pr_crit("Unexpected result from MCR_INIT_PFDR: %02x\n", rslt);
- + return -ENOSYS;
- +}
- +
- +/*****************/
- +/* Config driver */
- +/*****************/
- +
- +#define DEFAULT_FQD_SZ (PAGE_SIZE << CONFIG_FSL_QMAN_FQD_SZ)
- +#define DEFAULT_PFDR_SZ (PAGE_SIZE << CONFIG_FSL_QMAN_PFDR_SZ)
- +
- +/* We support only one of these */
- +static struct qman *qm;
- +static struct device_node *qm_node;
- +
- +/* And this state belongs to 'qm'. It is set during fsl_qman_init(), but used
- + * during qman_init_ccsr(). */
- +static dma_addr_t fqd_a, pfdr_a;
- +static size_t fqd_sz = DEFAULT_FQD_SZ, pfdr_sz = DEFAULT_PFDR_SZ;
- +
- +static int qman_fqd(struct reserved_mem *rmem)
- +{
- + fqd_a = rmem->base;
- + fqd_sz = rmem->size;
- +
- + WARN_ON(!(fqd_a && fqd_sz));
- +
- + return 0;
- +}
- +RESERVEDMEM_OF_DECLARE(qman_fqd, "fsl,qman-fqd", qman_fqd);
- +
- +static int qman_pfdr(struct reserved_mem *rmem)
- +{
- + pfdr_a = rmem->base;
- + pfdr_sz = rmem->size;
- +
- + WARN_ON(!(pfdr_a && pfdr_sz));
- +
- + return 0;
- +}
- +RESERVEDMEM_OF_DECLARE(qman_fbpr, "fsl,qman-pfdr", qman_pfdr);
- +
- +size_t get_qman_fqd_size()
- +{
- + return fqd_sz;
- +}
- +
- +/* Parse the <name> property to extract the memory location and size and
- + * memblock_reserve() it. If it isn't supplied, memblock_alloc() the default
- + * size. Also flush this memory range from data cache so that QMAN originated
- + * transactions for this memory region could be marked non-coherent.
- + */
- +static __init int parse_mem_property(struct device_node *node, const char *name,
- + dma_addr_t *addr, size_t *sz, int zero)
- +{
- + int ret;
- +
- + /* If using a "zero-pma", don't try to zero it, even if you asked */
- + if (zero && of_find_property(node, "zero-pma", &ret)) {
- + pr_info(" it's a 'zero-pma', not zeroing from s/w\n");
- + zero = 0;
- + }
- +
- + if (zero) {
- + /* map as cacheable, non-guarded */
- +#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
- + void __iomem *tmpp = ioremap_cache(*addr, *sz);
- +#else
- + void __iomem *tmpp = ioremap(*addr, *sz);
- +#endif
- +
- + if (!tmpp)
- + return -ENOMEM;
- + memset_io(tmpp, 0, *sz);
- + flush_dcache_range((unsigned long)tmpp,
- + (unsigned long)tmpp + *sz);
- + iounmap(tmpp);
- + }
- +
- + return 0;
- +}
- +
- +/* TODO:
- + * - there is obviously no handling of errors,
- + * - the calls to qm_set_memory() hard-code the priority and CPC-stashing for
- + * both memory resources to zero.
- + */
- +static int __init fsl_qman_init(struct device_node *node)
- +{
- + struct resource res;
- + resource_size_t len;
- + u32 __iomem *regs;
- + const char *s;
- + int ret, standby = 0;
- + u16 id;
- + u8 major, minor, cfg;
- + ret = of_address_to_resource(node, 0, &res);
- + if (ret) {
- + pr_err("Can't get %s property '%s'\n", node->full_name, "reg");
- + return ret;
- + }
- + s = of_get_property(node, "fsl,hv-claimable", &ret);
- + if (s && !strcmp(s, "standby"))
- + standby = 1;
- + if (!standby) {
- + ret = parse_mem_property(node, "fsl,qman-fqd",
- + &fqd_a, &fqd_sz, 1);
- + pr_info("qman-fqd addr 0x%llx size 0x%zx\n",
- + (unsigned long long)fqd_a, fqd_sz);
- + BUG_ON(ret);
- + ret = parse_mem_property(node, "fsl,qman-pfdr",
- + &pfdr_a, &pfdr_sz, 0);
- + pr_info("qman-pfdr addr 0x%llx size 0x%zx\n",
- + (unsigned long long)pfdr_a, pfdr_sz);
- + BUG_ON(ret);
- + }
- + /* Global configuration */
- + len = resource_size(&res);
- + if (len != (unsigned long)len)
- + return -EINVAL;
- + regs = ioremap(res.start, (unsigned long)len);
- + qm = qm_create(regs);
- + qm_node = node;
- + qm_get_version(qm, &id, &major, &minor, &cfg);
- + pr_info("Qman ver:%04x,%02x,%02x,%02x\n", id, major, minor, cfg);
- + if (!qman_ip_rev) {
- + if ((major == 1) && (minor == 0)) {
- + pr_err("QMAN rev1.0 on P4080 rev1 is not supported!\n");
- + iounmap(regs);
- + return -ENODEV;
- + } else if ((major == 1) && (minor == 1))
- + qman_ip_rev = QMAN_REV11;
- + else if ((major == 1) && (minor == 2))
- + qman_ip_rev = QMAN_REV12;
- + else if ((major == 2) && (minor == 0))
- + qman_ip_rev = QMAN_REV20;
- + else if ((major == 3) && (minor == 0))
- + qman_ip_rev = QMAN_REV30;
- + else if ((major == 3) && (minor == 1))
- + qman_ip_rev = QMAN_REV31;
- + else if ((major == 3) && (minor == 2))
- + qman_ip_rev = QMAN_REV32;
- + else {
- + pr_warn("unknown Qman version, default to rev1.1\n");
- + qman_ip_rev = QMAN_REV11;
- + }
- + qman_ip_cfg = cfg;
- + }
- +
- + if (standby) {
- + pr_info(" -> in standby mode\n");
- + return 0;
- + }
- + return 0;
- +}
- +
- +int qman_have_ccsr(void)
- +{
- + return qm ? 1 : 0;
- +}
- +
- +__init int qman_init_early(void)
- +{
- + struct device_node *dn;
- + int ret;
- +
- + for_each_compatible_node(dn, NULL, "fsl,qman") {
- + if (qm)
- + pr_err("%s: only one 'fsl,qman' allowed\n",
- + dn->full_name);
- + else {
- + if (!of_device_is_available(dn))
- + continue;
- +
- + ret = fsl_qman_init(dn);
- + BUG_ON(ret);
- + }
- + }
- + return 0;
- +}
- +postcore_initcall_sync(qman_init_early);
- +
- +static void log_edata_bits(u32 bit_count)
- +{
- + u32 i, j, mask = 0xffffffff;
- +
- + pr_warn("Qman ErrInt, EDATA:\n");
- + i = bit_count/32;
- + if (bit_count%32) {
- + i++;
- + mask = ~(mask << bit_count%32);
- + }
- + j = 16-i;
- + pr_warn(" 0x%08x\n", qm_in(EDATA(j)) & mask);
- + j++;
- + for (; j < 16; j++)
- + pr_warn(" 0x%08x\n", qm_in(EDATA(j)));
- +}
- +
- +static void log_additional_error_info(u32 isr_val, u32 ecsr_val)
- +{
- + union qman_ecir ecir_val;
- + union qman_eadr eadr_val;
- +
- + ecir_val.ecir_raw = qm_in(ECIR);
- + /* Is portal info valid */
- + if ((qman_ip_rev & 0xFF00) >= QMAN_REV30) {
- + union qman_ecir2 ecir2_val;
- + ecir2_val.ecir2_raw = qm_in(ECIR2);
- + if (ecsr_val & PORTAL_ECSR_ERR) {
- + pr_warn("Qman ErrInt: %s id %d\n",
- + (ecir2_val.info.portal_type) ?
- + "DCP" : "SWP", ecir2_val.info.portal_num);
- + }
- + if (ecsr_val & (FQID_ECSR_ERR | QM_EIRQ_IECE)) {
- + pr_warn("Qman ErrInt: ecir.fqid 0x%x\n",
- + ecir_val.info.fqid);
- + }
- + if (ecsr_val & (QM_EIRQ_SBEI|QM_EIRQ_MBEI)) {
- + eadr_val.eadr_raw = qm_in(EADR);
- + pr_warn("Qman ErrInt: EADR Memory: %s, 0x%x\n",
- + error_mdata[eadr_val.info_rev3.memid].txt,
- + error_mdata[eadr_val.info_rev3.memid].addr_mask
- + & eadr_val.info_rev3.eadr);
- + log_edata_bits(
- + error_mdata[eadr_val.info_rev3.memid].bits);
- + }
- + } else {
- + if (ecsr_val & PORTAL_ECSR_ERR) {
- + pr_warn("Qman ErrInt: %s id %d\n",
- + (ecir_val.info.portal_type) ?
- + "DCP" : "SWP", ecir_val.info.portal_num);
- + }
- + if (ecsr_val & FQID_ECSR_ERR) {
- + pr_warn("Qman ErrInt: ecir.fqid 0x%x\n",
- + ecir_val.info.fqid);
- + }
- + if (ecsr_val & (QM_EIRQ_SBEI|QM_EIRQ_MBEI)) {
- + eadr_val.eadr_raw = qm_in(EADR);
- + pr_warn("Qman ErrInt: EADR Memory: %s, 0x%x\n",
- + error_mdata[eadr_val.info.memid].txt,
- + error_mdata[eadr_val.info.memid].addr_mask
- + & eadr_val.info.eadr);
- + log_edata_bits(error_mdata[eadr_val.info.memid].bits);
- + }
- + }
- +}
- +
- +/* Qman interrupt handler */
- +static irqreturn_t qman_isr(int irq, void *ptr)
- +{
- + u32 isr_val, ier_val, ecsr_val, isr_mask, i;
- +
- + ier_val = qm_err_isr_enable_read(qm);
- + isr_val = qm_err_isr_status_read(qm);
- + ecsr_val = qm_in(ECSR);
- + isr_mask = isr_val & ier_val;
- +
- + if (!isr_mask)
- + return IRQ_NONE;
- + for (i = 0; i < QMAN_HWE_COUNT; i++) {
- + if (qman_hwerr_txts[i].mask & isr_mask) {
- + pr_warn("Qman ErrInt: %s\n", qman_hwerr_txts[i].txt);
- + if (qman_hwerr_txts[i].mask & ecsr_val) {
- + log_additional_error_info(isr_mask, ecsr_val);
- + /* Re-arm error capture registers */
- + qm_out(ECSR, ecsr_val);
- + }
- + if (qman_hwerr_txts[i].mask & QMAN_ERRS_TO_UNENABLE) {
- + pr_devel("Qman un-enabling error 0x%x\n",
- + qman_hwerr_txts[i].mask);
- + ier_val &= ~qman_hwerr_txts[i].mask;
- + qm_err_isr_enable_write(qm, ier_val);
- + }
- + }
- + }
- + qm_err_isr_status_clear(qm, isr_val);
- + return IRQ_HANDLED;
- +}
- +
- +static int __bind_irq(void)
- +{
- + int ret, err_irq;
- +
- + err_irq = of_irq_to_resource(qm_node, 0, NULL);
- + if (err_irq == 0) {
- + pr_info("Can't get %s property '%s'\n", qm_node->full_name,
- + "interrupts");
- + return -ENODEV;
- + }
- + ret = request_irq(err_irq, qman_isr, IRQF_SHARED, "qman-err", qm_node);
- + if (ret) {
- + pr_err("request_irq() failed %d for '%s'\n", ret,
- + qm_node->full_name);
- + return -ENODEV;
- + }
- + /* Write-to-clear any stale bits, (eg. starvation being asserted prior
- + * to resource allocation during driver init). */
- + qm_err_isr_status_clear(qm, 0xffffffff);
- + /* Enable Error Interrupts */
- + qm_err_isr_enable_write(qm, 0xffffffff);
- + return 0;
- +}
- +
- +int qman_init_ccsr(struct device_node *node)
- +{
- + int ret;
- + if (!qman_have_ccsr())
- + return 0;
- + if (node != qm_node)
- + return -EINVAL;
- +#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
- + /* TEMP for LS1043 : should be done in uboot */
- + qm_out(QCSP_BARE, 0x5);
- + qm_out(QCSP_BAR, 0x0);
- +#endif
- + /* FQD memory */
- + qm_set_memory(qm, qm_memory_fqd, fqd_a, 1, 0, 0, fqd_sz);
- + /* PFDR memory */
- + qm_set_memory(qm, qm_memory_pfdr, pfdr_a, 1, 0, 0, pfdr_sz);
- + qm_init_pfdr(qm, 8, pfdr_sz / 64 - 8);
- + /* thresholds */
- + qm_set_pfdr_threshold(qm, 512, 64);
- + qm_set_sfdr_threshold(qm, 128);
- + /* clear stale PEBI bit from interrupt status register */
- + qm_err_isr_status_clear(qm, QM_EIRQ_PEBI);
- + /* corenet initiator settings */
- + qm_set_corenet_initiator(qm);
- + /* HID settings */
- + qm_set_hid(qm);
- + /* Set scheduling weights to defaults */
- + for (ret = qm_wq_first; ret <= qm_wq_last; ret++)
- + qm_set_wq_scheduling(qm, ret, 0, 0, 0, 0, 0, 0, 0);
- + /* We are not prepared to accept ERNs for hardware enqueues */
- + qm_set_dc(qm, qm_dc_portal_fman0, 1, 0);
- + qm_set_dc(qm, qm_dc_portal_fman1, 1, 0);
- + /* Initialise Error Interrupt Handler */
- + ret = __bind_irq();
- + if (ret)
- + return ret;
- + return 0;
- +}
- +
- +#define LIO_CFG_LIODN_MASK 0x0fff0000
- +void qman_liodn_fixup(u16 channel)
- +{
- + static int done;
- + static u32 liodn_offset;
- + u32 before, after;
- + int idx = channel - QM_CHANNEL_SWPORTAL0;
- +
- + if (!qman_have_ccsr())
- + return;
- + if ((qman_ip_rev & 0xFF00) >= QMAN_REV30)
- + before = qm_in(REV3_QCSP_LIO_CFG(idx));
- + else
- + before = qm_in(QCSP_LIO_CFG(idx));
- + if (!done) {
- + liodn_offset = before & LIO_CFG_LIODN_MASK;
- + done = 1;
- + return;
- + }
- + after = (before & (~LIO_CFG_LIODN_MASK)) | liodn_offset;
- + if ((qman_ip_rev & 0xFF00) >= QMAN_REV30)
- + qm_out(REV3_QCSP_LIO_CFG(idx), after);
- + else
- + qm_out(QCSP_LIO_CFG(idx), after);
- +}
- +
- +#define IO_CFG_SDEST_MASK 0x00ff0000
- +int qman_set_sdest(u16 channel, unsigned int cpu_idx)
- +{
- + int idx = channel - QM_CHANNEL_SWPORTAL0;
- + u32 before, after;
- +
- + if (!qman_have_ccsr())
- + return -ENODEV;
- + if ((qman_ip_rev & 0xFF00) == QMAN_REV31) {
- + /* LS1043A - only one L2 cache */
- + cpu_idx = 0;
- + }
- +
- + if ((qman_ip_rev & 0xFF00) >= QMAN_REV30) {
- + before = qm_in(REV3_QCSP_IO_CFG(idx));
- + /* Each pair of vcpu share the same SRQ(SDEST) */
- + cpu_idx /= 2;
- + after = (before & (~IO_CFG_SDEST_MASK)) | (cpu_idx << 16);
- + qm_out(REV3_QCSP_IO_CFG(idx), after);
- + } else {
- + before = qm_in(QCSP_IO_CFG(idx));
- + after = (before & (~IO_CFG_SDEST_MASK)) | (cpu_idx << 16);
- + qm_out(QCSP_IO_CFG(idx), after);
- + }
- + return 0;
- +}
- +
- +#define MISC_CFG_WPM_MASK 0x00000002
- +int qm_set_wpm(int wpm)
- +{
- + u32 before;
- + u32 after;
- +
- + if (!qman_have_ccsr())
- + return -ENODEV;
- +
- + before = qm_in(MISC_CFG);
- + after = (before & (~MISC_CFG_WPM_MASK)) | (wpm << 1);
- + qm_out(MISC_CFG, after);
- + return 0;
- +}
- +
- +int qm_get_wpm(int *wpm)
- +{
- + u32 before;
- +
- + if (!qman_have_ccsr())
- + return -ENODEV;
- +
- + before = qm_in(MISC_CFG);
- + *wpm = (before & MISC_CFG_WPM_MASK) >> 1;
- + return 0;
- +}
- +
- +/* CEETM_CFG_PRES register has PRES field which is calculated by:
- + * PRES = (2^22 / credit update reference period) * QMan clock period
- + * = (2^22 * 10^9)/ CONFIG_QMAN_CEETM_UPDATE_PERIOD) / qman_clk
- + */
- +
- +int qman_ceetm_set_prescaler(enum qm_dc_portal portal)
- +{
- + u64 temp;
- + u16 pres;
- +
- + if (!qman_have_ccsr())
- + return -ENODEV;
- +
- + temp = 0x400000 * 100;
- + do_div(temp, CONFIG_QMAN_CEETM_UPDATE_PERIOD);
- + temp *= 10000000;
- + do_div(temp, qman_clk);
- + pres = (u16) temp;
- + qm_out(CEETM_CFG_IDX, portal);
- + qm_out(CEETM_CFG_PRES, pres);
- + return 0;
- +}
- +
- +int qman_ceetm_get_prescaler(u16 *pres)
- +{
- + if (!qman_have_ccsr())
- + return -ENODEV;
- + *pres = (u16)qm_in(CEETM_CFG_PRES);
- + return 0;
- +}
- +
- +#define DCP_CFG_CEETME_MASK 0xFFFF0000
- +#define QM_SP_ENABLE_CEETM(n) (0x80000000 >> (n))
- +int qman_sp_enable_ceetm_mode(enum qm_dc_portal portal, u16 sub_portal)
- +{
- + u32 dcp_cfg;
- +
- + if (!qman_have_ccsr())
- + return -ENODEV;
- +
- + dcp_cfg = qm_in(DCP_CFG(portal));
- + dcp_cfg |= QM_SP_ENABLE_CEETM(sub_portal);
- + qm_out(DCP_CFG(portal), dcp_cfg);
- + return 0;
- +}
- +
- +int qman_sp_disable_ceetm_mode(enum qm_dc_portal portal, u16 sub_portal)
- +{
- + u32 dcp_cfg;
- +
- + if (!qman_have_ccsr())
- + return -ENODEV;
- + dcp_cfg = qm_in(DCP_CFG(portal));
- + dcp_cfg &= ~(QM_SP_ENABLE_CEETM(sub_portal));
- + qm_out(DCP_CFG(portal), dcp_cfg);
- + return 0;
- +}
- +
- +int qman_ceetm_get_xsfdr(enum qm_dc_portal portal, unsigned int *num)
- +{
- + if (!qman_have_ccsr())
- + return -ENODEV;
- + *num = qm_in(CEETM_XSFDR_IN_USE);
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_get_xsfdr);
- +
- +#ifdef CONFIG_SYSFS
- +
- +#define DRV_NAME "fsl-qman"
- +#define DCP_MAX_ID 3
- +#define DCP_MIN_ID 0
- +
- +static ssize_t show_pfdr_fpc(struct device *dev,
- + struct device_attribute *dev_attr, char *buf)
- +{
- + return snprintf(buf, PAGE_SIZE, "%u\n", qm_in(PFDR_FPC));
- +};
- +
- +static ssize_t show_dlm_avg(struct device *dev,
- + struct device_attribute *dev_attr, char *buf)
- +{
- + u32 data;
- + int i;
- +
- + if (!sscanf(dev_attr->attr.name, "dcp%d_dlm_avg", &i))
- + return -EINVAL;
- + if (i < DCP_MIN_ID || i > DCP_MAX_ID)
- + return -EINVAL;
- + data = qm_in(DCP_DLM_AVG(i));
- + return snprintf(buf, PAGE_SIZE, "%d.%08d\n", data>>8,
- + (data & 0x000000ff)*390625);
- +};
- +
- +static ssize_t set_dlm_avg(struct device *dev,
- + struct device_attribute *dev_attr, const char *buf, size_t count)
- +{
- + unsigned long val;
- + int i;
- +
- + if (!sscanf(dev_attr->attr.name, "dcp%d_dlm_avg", &i))
- + return -EINVAL;
- + if (i < DCP_MIN_ID || i > DCP_MAX_ID)
- + return -EINVAL;
- + if (kstrtoul(buf, 0, &val)) {
- + dev_dbg(dev, "invalid input %s\n", buf);
- + return -EINVAL;
- + }
- + qm_out(DCP_DLM_AVG(i), val);
- + return count;
- +};
- +
- +static ssize_t show_pfdr_cfg(struct device *dev,
- + struct device_attribute *dev_attr, char *buf)
- +{
- + return snprintf(buf, PAGE_SIZE, "%u\n", qm_in(PFDR_CFG));
- +};
- +
- +static ssize_t set_pfdr_cfg(struct device *dev,
- + struct device_attribute *dev_attr, const char *buf, size_t count)
- +{
- + unsigned long val;
- +
- + if (kstrtoul(buf, 0, &val)) {
- + dev_dbg(dev, "invalid input %s\n", buf);
- + return -EINVAL;
- + }
- + qm_out(PFDR_CFG, val);
- + return count;
- +};
- +
- +static ssize_t show_sfdr_in_use(struct device *dev,
- + struct device_attribute *dev_attr, char *buf)
- +{
- + return snprintf(buf, PAGE_SIZE, "%u\n", qm_in(SFDR_IN_USE));
- +};
- +
- +static ssize_t show_idle_stat(struct device *dev,
- + struct device_attribute *dev_attr, char *buf)
- +{
- + return snprintf(buf, PAGE_SIZE, "%u\n", qm_in(IDLE_STAT));
- +};
- +
- +static ssize_t show_ci_rlm_avg(struct device *dev,
- + struct device_attribute *dev_attr, char *buf)
- +{
- + u32 data = qm_in(CI_RLM_AVG);
- + return snprintf(buf, PAGE_SIZE, "%d.%08d\n", data>>8,
- + (data & 0x000000ff)*390625);
- +};
- +
- +static ssize_t set_ci_rlm_avg(struct device *dev,
- + struct device_attribute *dev_attr, const char *buf, size_t count)
- +{
- + unsigned long val;
- +
- + if (kstrtoul(buf, 0, &val)) {
- + dev_dbg(dev, "invalid input %s\n", buf);
- + return -EINVAL;
- + }
- + qm_out(CI_RLM_AVG, val);
- + return count;
- +};
- +
- +static ssize_t show_err_isr(struct device *dev,
- + struct device_attribute *dev_attr, char *buf)
- +{
- + return snprintf(buf, PAGE_SIZE, "0x%08x\n", qm_in(ERR_ISR));
- +};
- +
- +#define SBEC_MAX_ID 14
- +#define SBEC_MIN_ID 0
- +
- +static ssize_t show_sbec(struct device *dev,
- + struct device_attribute *dev_attr, char *buf)
- +{
- + int i;
- +
- + if (!sscanf(dev_attr->attr.name, "sbec_%d", &i))
- + return -EINVAL;
- + if (i < SBEC_MIN_ID || i > SBEC_MAX_ID)
- + return -EINVAL;
- + return snprintf(buf, PAGE_SIZE, "%u\n", qm_in(SBEC(i)));
- +};
- +
- +static DEVICE_ATTR(pfdr_fpc, S_IRUSR, show_pfdr_fpc, NULL);
- +static DEVICE_ATTR(pfdr_cfg, S_IRUSR, show_pfdr_cfg, set_pfdr_cfg);
- +static DEVICE_ATTR(idle_stat, S_IRUSR, show_idle_stat, NULL);
- +static DEVICE_ATTR(ci_rlm_avg, (S_IRUSR|S_IWUSR),
- + show_ci_rlm_avg, set_ci_rlm_avg);
- +static DEVICE_ATTR(err_isr, S_IRUSR, show_err_isr, NULL);
- +static DEVICE_ATTR(sfdr_in_use, S_IRUSR, show_sfdr_in_use, NULL);
- +
- +static DEVICE_ATTR(dcp0_dlm_avg, (S_IRUSR|S_IWUSR), show_dlm_avg, set_dlm_avg);
- +static DEVICE_ATTR(dcp1_dlm_avg, (S_IRUSR|S_IWUSR), show_dlm_avg, set_dlm_avg);
- +static DEVICE_ATTR(dcp2_dlm_avg, (S_IRUSR|S_IWUSR), show_dlm_avg, set_dlm_avg);
- +static DEVICE_ATTR(dcp3_dlm_avg, (S_IRUSR|S_IWUSR), show_dlm_avg, set_dlm_avg);
- +
- +static DEVICE_ATTR(sbec_0, S_IRUSR, show_sbec, NULL);
- +static DEVICE_ATTR(sbec_1, S_IRUSR, show_sbec, NULL);
- +static DEVICE_ATTR(sbec_2, S_IRUSR, show_sbec, NULL);
- +static DEVICE_ATTR(sbec_3, S_IRUSR, show_sbec, NULL);
- +static DEVICE_ATTR(sbec_4, S_IRUSR, show_sbec, NULL);
- +static DEVICE_ATTR(sbec_5, S_IRUSR, show_sbec, NULL);
- +static DEVICE_ATTR(sbec_6, S_IRUSR, show_sbec, NULL);
- +static DEVICE_ATTR(sbec_7, S_IRUSR, show_sbec, NULL);
- +static DEVICE_ATTR(sbec_8, S_IRUSR, show_sbec, NULL);
- +static DEVICE_ATTR(sbec_9, S_IRUSR, show_sbec, NULL);
- +static DEVICE_ATTR(sbec_10, S_IRUSR, show_sbec, NULL);
- +static DEVICE_ATTR(sbec_11, S_IRUSR, show_sbec, NULL);
- +static DEVICE_ATTR(sbec_12, S_IRUSR, show_sbec, NULL);
- +static DEVICE_ATTR(sbec_13, S_IRUSR, show_sbec, NULL);
- +static DEVICE_ATTR(sbec_14, S_IRUSR, show_sbec, NULL);
- +
- +static struct attribute *qman_dev_attributes[] = {
- + &dev_attr_pfdr_fpc.attr,
- + &dev_attr_pfdr_cfg.attr,
- + &dev_attr_idle_stat.attr,
- + &dev_attr_ci_rlm_avg.attr,
- + &dev_attr_err_isr.attr,
- + &dev_attr_dcp0_dlm_avg.attr,
- + &dev_attr_dcp1_dlm_avg.attr,
- + &dev_attr_dcp2_dlm_avg.attr,
- + &dev_attr_dcp3_dlm_avg.attr,
- + /* sfdr_in_use will be added if necessary */
- + NULL
- +};
- +
- +static struct attribute *qman_dev_ecr_attributes[] = {
- + &dev_attr_sbec_0.attr,
- + &dev_attr_sbec_1.attr,
- + &dev_attr_sbec_2.attr,
- + &dev_attr_sbec_3.attr,
- + &dev_attr_sbec_4.attr,
- + &dev_attr_sbec_5.attr,
- + &dev_attr_sbec_6.attr,
- + &dev_attr_sbec_7.attr,
- + &dev_attr_sbec_8.attr,
- + &dev_attr_sbec_9.attr,
- + &dev_attr_sbec_10.attr,
- + &dev_attr_sbec_11.attr,
- + &dev_attr_sbec_12.attr,
- + &dev_attr_sbec_13.attr,
- + &dev_attr_sbec_14.attr,
- + NULL
- +};
- +
- +/* root level */
- +static const struct attribute_group qman_dev_attr_grp = {
- + .name = NULL,
- + .attrs = qman_dev_attributes
- +};
- +static const struct attribute_group qman_dev_ecr_grp = {
- + .name = "error_capture",
- + .attrs = qman_dev_ecr_attributes
- +};
- +
- +static int of_fsl_qman_remove(struct platform_device *ofdev)
- +{
- + sysfs_remove_group(&ofdev->dev.kobj, &qman_dev_attr_grp);
- + return 0;
- +};
- +
- +static int of_fsl_qman_probe(struct platform_device *ofdev)
- +{
- + int ret;
- +
- + ret = sysfs_create_group(&ofdev->dev.kobj, &qman_dev_attr_grp);
- + if (ret)
- + goto done;
- + ret = sysfs_add_file_to_group(&ofdev->dev.kobj,
- + &dev_attr_sfdr_in_use.attr, qman_dev_attr_grp.name);
- + if (ret)
- + goto del_group_0;
- + ret = sysfs_create_group(&ofdev->dev.kobj, &qman_dev_ecr_grp);
- + if (ret)
- + goto del_group_0;
- +
- + goto done;
- +
- +del_group_0:
- + sysfs_remove_group(&ofdev->dev.kobj, &qman_dev_attr_grp);
- +done:
- + if (ret)
- + dev_err(&ofdev->dev,
- + "Cannot create dev attributes ret=%d\n", ret);
- + return ret;
- +};
- +
- +static struct of_device_id of_fsl_qman_ids[] = {
- + {
- + .compatible = "fsl,qman",
- + },
- + {}
- +};
- +MODULE_DEVICE_TABLE(of, of_fsl_qman_ids);
- +
- +#ifdef CONFIG_SUSPEND
- +
- +static u32 saved_isdr;
- +static int qman_pm_suspend_noirq(struct device *dev)
- +{
- + uint32_t idle_state;
- +
- + suspend_unused_qportal();
- + /* save isdr, disable all, clear isr */
- + saved_isdr = qm_err_isr_disable_read(qm);
- + qm_err_isr_disable_write(qm, 0xffffffff);
- + qm_err_isr_status_clear(qm, 0xffffffff);
- + idle_state = qm_in(IDLE_STAT);
- + if (!(idle_state & 0x1)) {
- + pr_err("Qman not idle 0x%x aborting\n", idle_state);
- + qm_err_isr_disable_write(qm, saved_isdr);
- + resume_unused_qportal();
- + return -EBUSY;
- + }
- +#ifdef CONFIG_PM_DEBUG
- + pr_info("Qman suspend code, IDLE_STAT = 0x%x\n", idle_state);
- +#endif
- + return 0;
- +}
- +
- +static int qman_pm_resume_noirq(struct device *dev)
- +{
- + /* restore isdr */
- + qm_err_isr_disable_write(qm, saved_isdr);
- + resume_unused_qportal();
- + return 0;
- +}
- +#else
- +#define qman_pm_suspend_noirq NULL
- +#define qman_pm_resume_noirq NULL
- +#endif
- +
- +static const struct dev_pm_ops qman_pm_ops = {
- + .suspend_noirq = qman_pm_suspend_noirq,
- + .resume_noirq = qman_pm_resume_noirq,
- +};
- +
- +static struct platform_driver of_fsl_qman_driver = {
- + .driver = {
- + .owner = THIS_MODULE,
- + .name = DRV_NAME,
- + .of_match_table = of_fsl_qman_ids,
- + .pm = &qman_pm_ops,
- + },
- + .probe = of_fsl_qman_probe,
- + .remove = of_fsl_qman_remove,
- +};
- +
- +static int qman_ctrl_init(void)
- +{
- + return platform_driver_register(&of_fsl_qman_driver);
- +}
- +
- +static void qman_ctrl_exit(void)
- +{
- + platform_driver_unregister(&of_fsl_qman_driver);
- +}
- +
- +module_init(qman_ctrl_init);
- +module_exit(qman_ctrl_exit);
- +
- +#endif /* CONFIG_SYSFS */
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/qman_debugfs.c
- @@ -0,0 +1,1594 @@
- +/* Copyright 2010-2011 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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 "qman_private.h"
- +
- +#define MAX_FQID (0x00ffffff)
- +#define QM_FQD_BLOCK_SIZE 64
- +#define QM_FQD_AR (0xC10)
- +
- +static u32 fqid_max;
- +static u64 qman_ccsr_start;
- +static u64 qman_ccsr_size;
- +
- +static const char * const state_txt[] = {
- + "Out of Service",
- + "Retired",
- + "Tentatively Scheduled",
- + "Truly Scheduled",
- + "Parked",
- + "Active, Active Held or Held Suspended",
- + "Unknown State 6",
- + "Unknown State 7",
- + NULL,
- +};
- +
- +static const u8 fqd_states[] = {
- + QM_MCR_NP_STATE_OOS, QM_MCR_NP_STATE_RETIRED, QM_MCR_NP_STATE_TEN_SCHED,
- + QM_MCR_NP_STATE_TRU_SCHED, QM_MCR_NP_STATE_PARKED,
- + QM_MCR_NP_STATE_ACTIVE};
- +
- +struct mask_to_text {
- + u16 mask;
- + const char *txt;
- +};
- +
- +struct mask_filter_s {
- + u16 mask;
- + u8 filter;
- +};
- +
- +static const struct mask_filter_s mask_filter[] = {
- + {QM_FQCTRL_PREFERINCACHE, 0},
- + {QM_FQCTRL_PREFERINCACHE, 1},
- + {QM_FQCTRL_HOLDACTIVE, 0},
- + {QM_FQCTRL_HOLDACTIVE, 1},
- + {QM_FQCTRL_AVOIDBLOCK, 0},
- + {QM_FQCTRL_AVOIDBLOCK, 1},
- + {QM_FQCTRL_FORCESFDR, 0},
- + {QM_FQCTRL_FORCESFDR, 1},
- + {QM_FQCTRL_CPCSTASH, 0},
- + {QM_FQCTRL_CPCSTASH, 1},
- + {QM_FQCTRL_CTXASTASHING, 0},
- + {QM_FQCTRL_CTXASTASHING, 1},
- + {QM_FQCTRL_ORP, 0},
- + {QM_FQCTRL_ORP, 1},
- + {QM_FQCTRL_TDE, 0},
- + {QM_FQCTRL_TDE, 1},
- + {QM_FQCTRL_CGE, 0},
- + {QM_FQCTRL_CGE, 1}
- +};
- +
- +static const struct mask_to_text fq_ctrl_text_list[] = {
- + {
- + .mask = QM_FQCTRL_PREFERINCACHE,
- + .txt = "Prefer in cache",
- + },
- + {
- + .mask = QM_FQCTRL_HOLDACTIVE,
- + .txt = "Hold active in portal",
- + },
- + {
- + .mask = QM_FQCTRL_AVOIDBLOCK,
- + .txt = "Avoid Blocking",
- + },
- + {
- + .mask = QM_FQCTRL_FORCESFDR,
- + .txt = "High-priority SFDRs",
- + },
- + {
- + .mask = QM_FQCTRL_CPCSTASH,
- + .txt = "CPC Stash Enable",
- + },
- + {
- + .mask = QM_FQCTRL_CTXASTASHING,
- + .txt = "Context-A stashing",
- + },
- + {
- + .mask = QM_FQCTRL_ORP,
- + .txt = "ORP Enable",
- + },
- + {
- + .mask = QM_FQCTRL_TDE,
- + .txt = "Tail-Drop Enable",
- + },
- + {
- + .mask = QM_FQCTRL_CGE,
- + .txt = "Congestion Group Enable",
- + },
- + {
- + .mask = 0,
- + .txt = NULL,
- + }
- +};
- +
- +static const char *get_fqd_ctrl_text(u16 mask)
- +{
- + int i = 0;
- +
- + while (fq_ctrl_text_list[i].txt != NULL) {
- + if (fq_ctrl_text_list[i].mask == mask)
- + return fq_ctrl_text_list[i].txt;
- + i++;
- + }
- + return NULL;
- +}
- +
- +static const struct mask_to_text stashing_text_list[] = {
- + {
- + .mask = QM_STASHING_EXCL_CTX,
- + .txt = "FQ Ctx Stash"
- + },
- + {
- + .mask = QM_STASHING_EXCL_DATA,
- + .txt = "Frame Data Stash",
- + },
- + {
- + .mask = QM_STASHING_EXCL_ANNOTATION,
- + .txt = "Frame Annotation Stash",
- + },
- + {
- + .mask = 0,
- + .txt = NULL,
- + },
- +};
- +
- +static int user_input_convert(const char __user *user_buf, size_t count,
- + unsigned long *val)
- +{
- + char buf[12];
- +
- + if (count > sizeof(buf) - 1)
- + return -EINVAL;
- + if (copy_from_user(buf, user_buf, count))
- + return -EFAULT;
- + buf[count] = '\0';
- + if (kstrtoul(buf, 0, val))
- + return -EINVAL;
- + return 0;
- +}
- +
- +struct line_buffer_fq {
- + u32 buf[8];
- + u32 buf_cnt;
- + int line_cnt;
- +};
- +
- +static void add_to_line_buffer(struct line_buffer_fq *line_buf, u32 fqid,
- + struct seq_file *file)
- +{
- + line_buf->buf[line_buf->buf_cnt] = fqid;
- + line_buf->buf_cnt++;
- + if (line_buf->buf_cnt == 8) {
- + /* Buffer is full, flush it */
- + if (line_buf->line_cnt != 0)
- + seq_puts(file, ",\n");
- + seq_printf(file, "0x%06x,0x%06x,0x%06x,0x%06x,0x%06x,"
- + "0x%06x,0x%06x,0x%06x",
- + line_buf->buf[0], line_buf->buf[1], line_buf->buf[2],
- + line_buf->buf[3], line_buf->buf[4], line_buf->buf[5],
- + line_buf->buf[6], line_buf->buf[7]);
- + line_buf->buf_cnt = 0;
- + line_buf->line_cnt++;
- + }
- +}
- +
- +static void flush_line_buffer(struct line_buffer_fq *line_buf,
- + struct seq_file *file)
- +{
- + if (line_buf->buf_cnt) {
- + int y = 0;
- + if (line_buf->line_cnt != 0)
- + seq_puts(file, ",\n");
- + while (y != line_buf->buf_cnt) {
- + if (y+1 == line_buf->buf_cnt)
- + seq_printf(file, "0x%06x", line_buf->buf[y]);
- + else
- + seq_printf(file, "0x%06x,", line_buf->buf[y]);
- + y++;
- + }
- + line_buf->line_cnt++;
- + }
- + if (line_buf->line_cnt)
- + seq_putc(file, '\n');
- +}
- +
- +static struct dentry *dfs_root; /* debugfs root directory */
- +
- +/*******************************************************************************
- + * Query Frame Queue Non Programmable Fields
- + ******************************************************************************/
- +struct query_fq_np_fields_data_s {
- + u32 fqid;
- +};
- +static struct query_fq_np_fields_data_s query_fq_np_fields_data = {
- + .fqid = 1,
- +};
- +
- +static int query_fq_np_fields_show(struct seq_file *file, void *offset)
- +{
- + int ret;
- + struct qm_mcr_queryfq_np np;
- + struct qman_fq fq;
- +
- + fq.fqid = query_fq_np_fields_data.fqid;
- + ret = qman_query_fq_np(&fq, &np);
- + if (ret)
- + return ret;
- + /* Print state */
- + seq_printf(file, "Query FQ Non Programmable Fields Result fqid 0x%x\n",
- + fq.fqid);
- + seq_printf(file, " force eligible pending: %s\n",
- + (np.state & QM_MCR_NP_STATE_FE) ? "yes" : "no");
- + seq_printf(file, " retirement pending: %s\n",
- + (np.state & QM_MCR_NP_STATE_R) ? "yes" : "no");
- + seq_printf(file, " state: %s\n",
- + state_txt[np.state & QM_MCR_NP_STATE_MASK]);
- + seq_printf(file, " fq_link: 0x%x\n", np.fqd_link);
- + seq_printf(file, " odp_seq: %u\n", np.odp_seq);
- + seq_printf(file, " orp_nesn: %u\n", np.orp_nesn);
- + seq_printf(file, " orp_ea_hseq: %u\n", np.orp_ea_hseq);
- + seq_printf(file, " orp_ea_tseq: %u\n", np.orp_ea_tseq);
- + seq_printf(file, " orp_ea_hptr: 0x%x\n", np.orp_ea_hptr);
- + seq_printf(file, " orp_ea_tptr: 0x%x\n", np.orp_ea_tptr);
- + seq_printf(file, " pfdr_hptr: 0x%x\n", np.pfdr_hptr);
- + seq_printf(file, " pfdr_tptr: 0x%x\n", np.pfdr_tptr);
- + seq_printf(file, " is: ics_surp contains a %s\n",
- + (np.is) ? "deficit" : "surplus");
- + seq_printf(file, " ics_surp: %u\n", np.ics_surp);
- + seq_printf(file, " byte_cnt: %u\n", np.byte_cnt);
- + seq_printf(file, " frm_cnt: %u\n", np.frm_cnt);
- + seq_printf(file, " ra1_sfdr: 0x%x\n", np.ra1_sfdr);
- + seq_printf(file, " ra2_sfdr: 0x%x\n", np.ra2_sfdr);
- + seq_printf(file, " od1_sfdr: 0x%x\n", np.od1_sfdr);
- + seq_printf(file, " od2_sfdr: 0x%x\n", np.od2_sfdr);
- + seq_printf(file, " od3_sfdr: 0x%x\n", np.od3_sfdr);
- + return 0;
- +}
- +
- +static int query_fq_np_fields_open(struct inode *inode,
- + struct file *file)
- +{
- + return single_open(file, query_fq_np_fields_show, NULL);
- +}
- +
- +static ssize_t query_fq_np_fields_write(struct file *f,
- + const char __user *buf, size_t count, loff_t *off)
- +{
- + int ret;
- + unsigned long val;
- +
- + ret = user_input_convert(buf, count, &val);
- + if (ret)
- + return ret;
- + if (val > MAX_FQID)
- + return -EINVAL;
- + query_fq_np_fields_data.fqid = (u32)val;
- + return count;
- +}
- +
- +static const struct file_operations query_fq_np_fields_fops = {
- + .owner = THIS_MODULE,
- + .open = query_fq_np_fields_open,
- + .read = seq_read,
- + .write = query_fq_np_fields_write,
- + .release = single_release,
- +};
- +
- +/*******************************************************************************
- + * Frame Queue Programmable Fields
- + ******************************************************************************/
- +struct query_fq_fields_data_s {
- + u32 fqid;
- +};
- +
- +static struct query_fq_fields_data_s query_fq_fields_data = {
- + .fqid = 1,
- +};
- +
- +static int query_fq_fields_show(struct seq_file *file, void *offset)
- +{
- + int ret;
- + struct qm_fqd fqd;
- + struct qman_fq fq;
- + int i = 0;
- +
- + memset(&fqd, 0, sizeof(struct qm_fqd));
- + fq.fqid = query_fq_fields_data.fqid;
- + ret = qman_query_fq(&fq, &fqd);
- + if (ret)
- + return ret;
- + seq_printf(file, "Query FQ Programmable Fields Result fqid 0x%x\n",
- + fq.fqid);
- + seq_printf(file, " orprws: %u\n", fqd.orprws);
- + seq_printf(file, " oa: %u\n", fqd.oa);
- + seq_printf(file, " olws: %u\n", fqd.olws);
- +
- + seq_printf(file, " cgid: %u\n", fqd.cgid);
- +
- + if ((fqd.fq_ctrl & QM_FQCTRL_MASK) == 0)
- + seq_puts(file, " fq_ctrl: None\n");
- + else {
- + i = 0;
- + seq_puts(file, " fq_ctrl:\n");
- + while (fq_ctrl_text_list[i].txt != NULL) {
- + if ((fqd.fq_ctrl & QM_FQCTRL_MASK) &
- + fq_ctrl_text_list[i].mask)
- + seq_printf(file, " %s\n",
- + fq_ctrl_text_list[i].txt);
- + i++;
- + }
- + }
- + seq_printf(file, " dest_channel: %u\n", fqd.dest.channel);
- + seq_printf(file, " dest_wq: %u\n", fqd.dest.wq);
- + seq_printf(file, " ics_cred: %u\n", fqd.ics_cred);
- + seq_printf(file, " td_mant: %u\n", fqd.td.mant);
- + seq_printf(file, " td_exp: %u\n", fqd.td.exp);
- +
- + seq_printf(file, " ctx_b: 0x%x\n", fqd.context_b);
- +
- + seq_printf(file, " ctx_a: 0x%llx\n", qm_fqd_stashing_get64(&fqd));
- + /* Any stashing configured */
- + if ((fqd.context_a.stashing.exclusive & 0x7) == 0)
- + seq_puts(file, " ctx_a_stash_exclusive: None\n");
- + else {
- + seq_puts(file, " ctx_a_stash_exclusive:\n");
- + i = 0;
- + while (stashing_text_list[i].txt != NULL) {
- + if ((fqd.fq_ctrl & 0x7) & stashing_text_list[i].mask)
- + seq_printf(file, " %s\n",
- + stashing_text_list[i].txt);
- + i++;
- + }
- + }
- + seq_printf(file, " ctx_a_stash_annotation_cl: %u\n",
- + fqd.context_a.stashing.annotation_cl);
- + seq_printf(file, " ctx_a_stash_data_cl: %u\n",
- + fqd.context_a.stashing.data_cl);
- + seq_printf(file, " ctx_a_stash_context_cl: %u\n",
- + fqd.context_a.stashing.context_cl);
- + return 0;
- +}
- +
- +static int query_fq_fields_open(struct inode *inode,
- + struct file *file)
- +{
- + return single_open(file, query_fq_fields_show, NULL);
- +}
- +
- +static ssize_t query_fq_fields_write(struct file *f,
- + const char __user *buf, size_t count, loff_t *off)
- +{
- + int ret;
- + unsigned long val;
- +
- + ret = user_input_convert(buf, count, &val);
- + if (ret)
- + return ret;
- + if (val > MAX_FQID)
- + return -EINVAL;
- + query_fq_fields_data.fqid = (u32)val;
- + return count;
- +}
- +
- +static const struct file_operations query_fq_fields_fops = {
- + .owner = THIS_MODULE,
- + .open = query_fq_fields_open,
- + .read = seq_read,
- + .write = query_fq_fields_write,
- + .release = single_release,
- +};
- +
- +/*******************************************************************************
- + * Query WQ lengths
- + ******************************************************************************/
- +struct query_wq_lengths_data_s {
- + union {
- + u16 channel_wq; /* ignores wq (3 lsbits) */
- + struct {
- + u16 id:13; /* qm_channel */
- + u16 __reserved:3;
- + } __packed channel;
- + };
- +};
- +static struct query_wq_lengths_data_s query_wq_lengths_data;
- +static int query_wq_lengths_show(struct seq_file *file, void *offset)
- +{
- + int ret;
- + struct qm_mcr_querywq wq;
- + int i;
- +
- + memset(&wq, 0, sizeof(struct qm_mcr_querywq));
- + wq.channel.id = query_wq_lengths_data.channel.id;
- + ret = qman_query_wq(0, &wq);
- + if (ret)
- + return ret;
- + seq_printf(file, "Query Result For Channel: 0x%x\n", wq.channel.id);
- + for (i = 0; i < 8; i++)
- + /* mask out upper 4 bits since they are not part of length */
- + seq_printf(file, " wq%d_len : %u\n", i, wq.wq_len[i] & 0x0fff);
- + return 0;
- +}
- +
- +static int query_wq_lengths_open(struct inode *inode,
- + struct file *file)
- +{
- + return single_open(file, query_wq_lengths_show, NULL);
- +}
- +
- +static ssize_t query_wq_lengths_write(struct file *f,
- + const char __user *buf, size_t count, loff_t *off)
- +{
- + int ret;
- + unsigned long val;
- +
- + ret = user_input_convert(buf, count, &val);
- + if (ret)
- + return ret;
- + if (val > 0xfff8)
- + return -EINVAL;
- + query_wq_lengths_data.channel.id = (u16)val;
- + return count;
- +}
- +
- +static const struct file_operations query_wq_lengths_fops = {
- + .owner = THIS_MODULE,
- + .open = query_wq_lengths_open,
- + .read = seq_read,
- + .write = query_wq_lengths_write,
- + .release = single_release,
- +};
- +
- +/*******************************************************************************
- + * Query CGR
- + ******************************************************************************/
- +struct query_cgr_s {
- + u8 cgid;
- +};
- +static struct query_cgr_s query_cgr_data;
- +
- +static int query_cgr_show(struct seq_file *file, void *offset)
- +{
- + int ret;
- + struct qm_mcr_querycgr cgrd;
- + struct qman_cgr cgr;
- + int i, j;
- + u32 mask;
- +
- + memset(&cgr, 0, sizeof(cgr));
- + memset(&cgrd, 0, sizeof(cgrd));
- + cgr.cgrid = query_cgr_data.cgid;
- + ret = qman_query_cgr(&cgr, &cgrd);
- + if (ret)
- + return ret;
- + seq_printf(file, "Query CGR id 0x%x\n", cgr.cgrid);
- + seq_printf(file, " wr_parm_g MA: %u, Mn: %u, SA: %u, Sn: %u, Pn: %u\n",
- + cgrd.cgr.wr_parm_g.MA, cgrd.cgr.wr_parm_g.Mn,
- + cgrd.cgr.wr_parm_g.SA, cgrd.cgr.wr_parm_g.Sn,
- + cgrd.cgr.wr_parm_g.Pn);
- +
- + seq_printf(file, " wr_parm_y MA: %u, Mn: %u, SA: %u, Sn: %u, Pn: %u\n",
- + cgrd.cgr.wr_parm_y.MA, cgrd.cgr.wr_parm_y.Mn,
- + cgrd.cgr.wr_parm_y.SA, cgrd.cgr.wr_parm_y.Sn,
- + cgrd.cgr.wr_parm_y.Pn);
- +
- + seq_printf(file, " wr_parm_r MA: %u, Mn: %u, SA: %u, Sn: %u, Pn: %u\n",
- + cgrd.cgr.wr_parm_r.MA, cgrd.cgr.wr_parm_r.Mn,
- + cgrd.cgr.wr_parm_r.SA, cgrd.cgr.wr_parm_r.Sn,
- + cgrd.cgr.wr_parm_r.Pn);
- +
- + seq_printf(file, " wr_en_g: %u, wr_en_y: %u, we_en_r: %u\n",
- + cgrd.cgr.wr_en_g, cgrd.cgr.wr_en_y, cgrd.cgr.wr_en_r);
- +
- + seq_printf(file, " cscn_en: %u\n", cgrd.cgr.cscn_en);
- + if ((qman_ip_rev & 0xFF00) >= QMAN_REV30) {
- + seq_puts(file, " cscn_targ_dcp:\n");
- + mask = 0x80000000;
- + for (i = 0; i < 32; i++) {
- + if (cgrd.cgr.cscn_targ & mask)
- + seq_printf(file, " send CSCN to dcp %u\n",
- + (31 - i));
- + mask >>= 1;
- + }
- +
- + seq_puts(file, " cscn_targ_swp:\n");
- + for (i = 0; i < 4; i++) {
- + mask = 0x80000000;
- + for (j = 0; j < 32; j++) {
- + if (cgrd.cscn_targ_swp[i] & mask)
- + seq_printf(file, " send CSCN to swp"
- + " %u\n", (127 - (i * 32) - j));
- + mask >>= 1;
- + }
- + }
- + } else {
- + seq_printf(file, " cscn_targ: %u\n", cgrd.cgr.cscn_targ);
- + }
- + seq_printf(file, " cstd_en: %u\n", cgrd.cgr.cstd_en);
- + seq_printf(file, " cs: %u\n", cgrd.cgr.cs);
- +
- + seq_printf(file, " cs_thresh_TA: %u, cs_thresh_Tn: %u\n",
- + cgrd.cgr.cs_thres.TA, cgrd.cgr.cs_thres.Tn);
- +
- + seq_printf(file, " mode: %s\n",
- + (cgrd.cgr.mode & QMAN_CGR_MODE_FRAME) ?
- + "frame count" : "byte count");
- + seq_printf(file, " i_bcnt: %llu\n", qm_mcr_querycgr_i_get64(&cgrd));
- + seq_printf(file, " a_bcnt: %llu\n", qm_mcr_querycgr_a_get64(&cgrd));
- +
- + return 0;
- +}
- +
- +static int query_cgr_open(struct inode *inode, struct file *file)
- +{
- + return single_open(file, query_cgr_show, NULL);
- +}
- +
- +static ssize_t query_cgr_write(struct file *f, const char __user *buf,
- + size_t count, loff_t *off)
- +{
- + int ret;
- + unsigned long val;
- +
- + ret = user_input_convert(buf, count, &val);
- + if (ret)
- + return ret;
- + if (val > 0xff)
- + return -EINVAL;
- + query_cgr_data.cgid = (u8)val;
- + return count;
- +}
- +
- +static const struct file_operations query_cgr_fops = {
- + .owner = THIS_MODULE,
- + .open = query_cgr_open,
- + .read = seq_read,
- + .write = query_cgr_write,
- + .release = single_release,
- +};
- +
- +/*******************************************************************************
- + * Test Write CGR
- + ******************************************************************************/
- +struct test_write_cgr_s {
- + u64 i_bcnt;
- + u8 cgid;
- +};
- +static struct test_write_cgr_s test_write_cgr_data;
- +
- +static int testwrite_cgr_show(struct seq_file *file, void *offset)
- +{
- + int ret;
- + struct qm_mcr_cgrtestwrite result;
- + struct qman_cgr cgr;
- + u64 i_bcnt;
- +
- + memset(&cgr, 0, sizeof(struct qman_cgr));
- + memset(&result, 0, sizeof(struct qm_mcr_cgrtestwrite));
- + cgr.cgrid = test_write_cgr_data.cgid;
- + i_bcnt = test_write_cgr_data.i_bcnt;
- + ret = qman_testwrite_cgr(&cgr, i_bcnt, &result);
- + if (ret)
- + return ret;
- + seq_printf(file, "CGR Test Write CGR id 0x%x\n", cgr.cgrid);
- + seq_printf(file, " wr_parm_g MA: %u, Mn: %u, SA: %u, Sn: %u, Pn: %u\n",
- + result.cgr.wr_parm_g.MA, result.cgr.wr_parm_g.Mn,
- + result.cgr.wr_parm_g.SA, result.cgr.wr_parm_g.Sn,
- + result.cgr.wr_parm_g.Pn);
- + seq_printf(file, " wr_parm_y MA: %u, Mn: %u, SA: %u, Sn: %u, Pn: %u\n",
- + result.cgr.wr_parm_y.MA, result.cgr.wr_parm_y.Mn,
- + result.cgr.wr_parm_y.SA, result.cgr.wr_parm_y.Sn,
- + result.cgr.wr_parm_y.Pn);
- + seq_printf(file, " wr_parm_r MA: %u, Mn: %u, SA: %u, Sn: %u, Pn: %u\n",
- + result.cgr.wr_parm_r.MA, result.cgr.wr_parm_r.Mn,
- + result.cgr.wr_parm_r.SA, result.cgr.wr_parm_r.Sn,
- + result.cgr.wr_parm_r.Pn);
- + seq_printf(file, " wr_en_g: %u, wr_en_y: %u, we_en_r: %u\n",
- + result.cgr.wr_en_g, result.cgr.wr_en_y, result.cgr.wr_en_r);
- + seq_printf(file, " cscn_en: %u\n", result.cgr.cscn_en);
- + seq_printf(file, " cscn_targ: %u\n", result.cgr.cscn_targ);
- + seq_printf(file, " cstd_en: %u\n", result.cgr.cstd_en);
- + seq_printf(file, " cs: %u\n", result.cgr.cs);
- + seq_printf(file, " cs_thresh_TA: %u, cs_thresh_Tn: %u\n",
- + result.cgr.cs_thres.TA, result.cgr.cs_thres.Tn);
- +
- + /* Add Mode for Si 2 */
- + seq_printf(file, " mode: %s\n",
- + (result.cgr.mode & QMAN_CGR_MODE_FRAME) ?
- + "frame count" : "byte count");
- +
- + seq_printf(file, " i_bcnt: %llu\n",
- + qm_mcr_cgrtestwrite_i_get64(&result));
- + seq_printf(file, " a_bcnt: %llu\n",
- + qm_mcr_cgrtestwrite_a_get64(&result));
- + seq_printf(file, " wr_prob_g: %u\n", result.wr_prob_g);
- + seq_printf(file, " wr_prob_y: %u\n", result.wr_prob_y);
- + seq_printf(file, " wr_prob_r: %u\n", result.wr_prob_r);
- + return 0;
- +}
- +
- +static int testwrite_cgr_open(struct inode *inode, struct file *file)
- +{
- + return single_open(file, testwrite_cgr_show, NULL);
- +}
- +
- +static const struct file_operations testwrite_cgr_fops = {
- + .owner = THIS_MODULE,
- + .open = testwrite_cgr_open,
- + .read = seq_read,
- + .release = single_release,
- +};
- +
- +
- +static int testwrite_cgr_ibcnt_show(struct seq_file *file, void *offset)
- +{
- + seq_printf(file, "i_bcnt: %llu\n", test_write_cgr_data.i_bcnt);
- + return 0;
- +}
- +static int testwrite_cgr_ibcnt_open(struct inode *inode, struct file *file)
- +{
- + return single_open(file, testwrite_cgr_ibcnt_show, NULL);
- +}
- +
- +static ssize_t testwrite_cgr_ibcnt_write(struct file *f, const char __user *buf,
- + size_t count, loff_t *off)
- +{
- + int ret;
- + unsigned long val;
- +
- + ret = user_input_convert(buf, count, &val);
- + if (ret)
- + return ret;
- + test_write_cgr_data.i_bcnt = val;
- + return count;
- +}
- +
- +static const struct file_operations teswrite_cgr_ibcnt_fops = {
- + .owner = THIS_MODULE,
- + .open = testwrite_cgr_ibcnt_open,
- + .read = seq_read,
- + .write = testwrite_cgr_ibcnt_write,
- + .release = single_release,
- +};
- +
- +static int testwrite_cgr_cgrid_show(struct seq_file *file, void *offset)
- +{
- + seq_printf(file, "cgrid: %u\n", (u32)test_write_cgr_data.cgid);
- + return 0;
- +}
- +static int testwrite_cgr_cgrid_open(struct inode *inode, struct file *file)
- +{
- + return single_open(file, testwrite_cgr_cgrid_show, NULL);
- +}
- +
- +static ssize_t testwrite_cgr_cgrid_write(struct file *f, const char __user *buf,
- + size_t count, loff_t *off)
- +{
- + int ret;
- + unsigned long val;
- +
- + ret = user_input_convert(buf, count, &val);
- + if (ret)
- + return ret;
- + if (val > 0xff)
- + return -EINVAL;
- + test_write_cgr_data.cgid = (u8)val;
- + return count;
- +}
- +
- +static const struct file_operations teswrite_cgr_cgrid_fops = {
- + .owner = THIS_MODULE,
- + .open = testwrite_cgr_cgrid_open,
- + .read = seq_read,
- + .write = testwrite_cgr_cgrid_write,
- + .release = single_release,
- +};
- +
- +/*******************************************************************************
- + * Query Congestion State
- + ******************************************************************************/
- +static int query_congestion_show(struct seq_file *file, void *offset)
- +{
- + int ret;
- + struct qm_mcr_querycongestion cs;
- + int i, j, in_cong = 0;
- + u32 mask;
- +
- + memset(&cs, 0, sizeof(struct qm_mcr_querycongestion));
- + ret = qman_query_congestion(&cs);
- + if (ret)
- + return ret;
- + seq_puts(file, "Query Congestion Result\n");
- + for (i = 0; i < 8; i++) {
- + mask = 0x80000000;
- + for (j = 0; j < 32; j++) {
- + if (cs.state.__state[i] & mask) {
- + in_cong = 1;
- + seq_printf(file, " cg %u: %s\n", (i*32)+j,
- + "in congestion");
- + }
- + mask >>= 1;
- + }
- + }
- + if (!in_cong)
- + seq_puts(file, " All congestion groups not congested.\n");
- + return 0;
- +}
- +
- +static int query_congestion_open(struct inode *inode, struct file *file)
- +{
- + return single_open(file, query_congestion_show, NULL);
- +}
- +
- +static const struct file_operations query_congestion_fops = {
- + .owner = THIS_MODULE,
- + .open = query_congestion_open,
- + .read = seq_read,
- + .release = single_release,
- +};
- +
- +/*******************************************************************************
- + * Query CCGR
- + ******************************************************************************/
- +struct query_ccgr_s {
- + u32 ccgid;
- +};
- +static struct query_ccgr_s query_ccgr_data;
- +
- +static int query_ccgr_show(struct seq_file *file, void *offset)
- +{
- + int ret;
- + struct qm_mcr_ceetm_ccgr_query ccgr_query;
- + struct qm_mcc_ceetm_ccgr_query query_opts;
- + int i, j;
- + u32 mask;
- +
- + memset(&ccgr_query, 0, sizeof(struct qm_mcr_ceetm_ccgr_query));
- + memset(&query_opts, 0, sizeof(struct qm_mcc_ceetm_ccgr_query));
- +
- + if ((qman_ip_rev & 0xFF00) < QMAN_REV30)
- + return -EINVAL;
- +
- + seq_printf(file, "Query CCGID %x\n", query_ccgr_data.ccgid);
- + query_opts.dcpid = ((query_ccgr_data.ccgid & 0xFF000000) >> 24);
- + query_opts.ccgrid = query_ccgr_data.ccgid & 0x000001FF;
- + ret = qman_ceetm_query_ccgr(&query_opts, &ccgr_query);
- + if (ret)
- + return ret;
- + seq_printf(file, "Query CCGR id %x in DCP %d\n", query_opts.ccgrid,
- + query_opts.dcpid);
- + seq_printf(file, " wr_parm_g MA: %u, Mn: %u, SA: %u, Sn: %u, Pn: %u\n",
- + ccgr_query.cm_query.wr_parm_g.MA,
- + ccgr_query.cm_query.wr_parm_g.Mn,
- + ccgr_query.cm_query.wr_parm_g.SA,
- + ccgr_query.cm_query.wr_parm_g.Sn,
- + ccgr_query.cm_query.wr_parm_g.Pn);
- +
- + seq_printf(file, " wr_parm_y MA: %u, Mn: %u, SA: %u, Sn: %u, Pn: %u\n",
- + ccgr_query.cm_query.wr_parm_y.MA,
- + ccgr_query.cm_query.wr_parm_y.Mn,
- + ccgr_query.cm_query.wr_parm_y.SA,
- + ccgr_query.cm_query.wr_parm_y.Sn,
- + ccgr_query.cm_query.wr_parm_y.Pn);
- +
- + seq_printf(file, " wr_parm_r MA: %u, Mn: %u, SA: %u, Sn: %u, Pn: %u\n",
- + ccgr_query.cm_query.wr_parm_r.MA,
- + ccgr_query.cm_query.wr_parm_r.Mn,
- + ccgr_query.cm_query.wr_parm_r.SA,
- + ccgr_query.cm_query.wr_parm_r.Sn,
- + ccgr_query.cm_query.wr_parm_r.Pn);
- +
- + seq_printf(file, " wr_en_g: %u, wr_en_y: %u, we_en_r: %u\n",
- + ccgr_query.cm_query.ctl_wr_en_g,
- + ccgr_query.cm_query.ctl_wr_en_y,
- + ccgr_query.cm_query.ctl_wr_en_r);
- +
- + seq_printf(file, " cscn_en: %u\n", ccgr_query.cm_query.ctl_cscn_en);
- + seq_puts(file, " cscn_targ_dcp:\n");
- + mask = 0x80000000;
- + for (i = 0; i < 32; i++) {
- + if (ccgr_query.cm_query.cscn_targ_dcp & mask)
- + seq_printf(file, " send CSCN to dcp %u\n", (31 - i));
- + mask >>= 1;
- + }
- +
- + seq_puts(file, " cscn_targ_swp:\n");
- + for (i = 0; i < 4; i++) {
- + mask = 0x80000000;
- + for (j = 0; j < 32; j++) {
- + if (ccgr_query.cm_query.cscn_targ_swp[i] & mask)
- + seq_printf(file, " send CSCN to swp"
- + "%u\n", (127 - (i * 32) - j));
- + mask >>= 1;
- + }
- + }
- +
- + seq_printf(file, " td_en: %u\n", ccgr_query.cm_query.ctl_td_en);
- +
- + seq_printf(file, " cs_thresh_in_TA: %u, cs_thresh_in_Tn: %u\n",
- + ccgr_query.cm_query.cs_thres.TA,
- + ccgr_query.cm_query.cs_thres.Tn);
- +
- + seq_printf(file, " cs_thresh_out_TA: %u, cs_thresh_out_Tn: %u\n",
- + ccgr_query.cm_query.cs_thres_x.TA,
- + ccgr_query.cm_query.cs_thres_x.Tn);
- +
- + seq_printf(file, " td_thresh_TA: %u, td_thresh_Tn: %u\n",
- + ccgr_query.cm_query.td_thres.TA,
- + ccgr_query.cm_query.td_thres.Tn);
- +
- + seq_printf(file, " mode: %s\n",
- + (ccgr_query.cm_query.ctl_mode &
- + QMAN_CGR_MODE_FRAME) ?
- + "frame count" : "byte count");
- + seq_printf(file, " i_cnt: %llu\n", (u64)ccgr_query.cm_query.i_cnt);
- + seq_printf(file, " a_cnt: %llu\n", (u64)ccgr_query.cm_query.a_cnt);
- +
- + return 0;
- +}
- +
- +static int query_ccgr_open(struct inode *inode, struct file *file)
- +{
- + return single_open(file, query_ccgr_show, NULL);
- +}
- +
- +static ssize_t query_ccgr_write(struct file *f, const char __user *buf,
- + size_t count, loff_t *off)
- +{
- + int ret;
- + unsigned long val;
- +
- + ret = user_input_convert(buf, count, &val);
- + if (ret)
- + return ret;
- + query_ccgr_data.ccgid = val;
- + return count;
- +}
- +
- +static const struct file_operations query_ccgr_fops = {
- + .owner = THIS_MODULE,
- + .open = query_ccgr_open,
- + .read = seq_read,
- + .write = query_ccgr_write,
- + .release = single_release,
- +};
- +/*******************************************************************************
- + * QMan register
- + ******************************************************************************/
- +struct qman_register_s {
- + u32 val;
- +};
- +static struct qman_register_s qman_register_data;
- +
- +static void init_ccsrmempeek(void)
- +{
- + struct device_node *dn;
- + const u32 *regaddr_p;
- +
- + dn = of_find_compatible_node(NULL, NULL, "fsl,qman");
- + if (!dn) {
- + pr_info("No fsl,qman node\n");
- + return;
- + }
- + regaddr_p = of_get_address(dn, 0, &qman_ccsr_size, NULL);
- + if (!regaddr_p) {
- + of_node_put(dn);
- + return;
- + }
- + qman_ccsr_start = of_translate_address(dn, regaddr_p);
- + of_node_put(dn);
- +}
- +/* This function provides access to QMan ccsr memory map */
- +static int qman_ccsrmempeek(u32 *val, u32 offset)
- +{
- + void __iomem *addr;
- + u64 phys_addr;
- +
- + if (!qman_ccsr_start)
- + return -EINVAL;
- +
- + if (offset > (qman_ccsr_size - sizeof(u32)))
- + return -EINVAL;
- +
- + phys_addr = qman_ccsr_start + offset;
- + addr = ioremap(phys_addr, sizeof(u32));
- + if (!addr) {
- + pr_err("ccsrmempeek, ioremap failed\n");
- + return -EINVAL;
- + }
- + *val = in_be32(addr);
- + iounmap(addr);
- + return 0;
- +}
- +
- +static int qman_ccsrmempeek_show(struct seq_file *file, void *offset)
- +{
- + u32 b;
- +
- + qman_ccsrmempeek(&b, qman_register_data.val);
- + seq_printf(file, "QMan register offset = 0x%x\n",
- + qman_register_data.val);
- + seq_printf(file, "value = 0x%08x\n", b);
- +
- + return 0;
- +}
- +
- +static int qman_ccsrmempeek_open(struct inode *inode, struct file *file)
- +{
- + return single_open(file, qman_ccsrmempeek_show, NULL);
- +}
- +
- +static ssize_t qman_ccsrmempeek_write(struct file *f, const char __user *buf,
- + size_t count, loff_t *off)
- +{
- + int ret;
- + unsigned long val;
- +
- + ret = user_input_convert(buf, count, &val);
- + if (ret)
- + return ret;
- + /* multiple of 4 */
- + if (val > (qman_ccsr_size - sizeof(u32))) {
- + pr_info("Input 0x%lx > 0x%llx\n",
- + val, (qman_ccsr_size - sizeof(u32)));
- + return -EINVAL;
- + }
- + if (val & 0x3) {
- + pr_info("Input 0x%lx not multiple of 4\n", val);
- + return -EINVAL;
- + }
- + qman_register_data.val = val;
- + return count;
- +}
- +
- +static const struct file_operations qman_ccsrmempeek_fops = {
- + .owner = THIS_MODULE,
- + .open = qman_ccsrmempeek_open,
- + .read = seq_read,
- + .write = qman_ccsrmempeek_write,
- +};
- +
- +/*******************************************************************************
- + * QMan state
- + ******************************************************************************/
- +static int qman_fqd_state_show(struct seq_file *file, void *offset)
- +{
- + struct qm_mcr_queryfq_np np;
- + struct qman_fq fq;
- + struct line_buffer_fq line_buf;
- + int ret, i;
- + u8 *state = file->private;
- + u32 qm_fq_state_cnt[ARRAY_SIZE(fqd_states)];
- +
- + memset(qm_fq_state_cnt, 0, sizeof(qm_fq_state_cnt));
- + memset(&line_buf, 0, sizeof(line_buf));
- +
- + seq_printf(file, "List of fq ids in state: %s\n", state_txt[*state]);
- +
- + for (i = 1; i < fqid_max; i++) {
- + fq.fqid = i;
- + ret = qman_query_fq_np(&fq, &np);
- + if (ret)
- + return ret;
- + if (*state == (np.state & QM_MCR_NP_STATE_MASK))
- + add_to_line_buffer(&line_buf, fq.fqid, file);
- + /* Keep a summary count of all states */
- + if ((np.state & QM_MCR_NP_STATE_MASK) < ARRAY_SIZE(fqd_states))
- + qm_fq_state_cnt[(np.state & QM_MCR_NP_STATE_MASK)]++;
- + }
- + flush_line_buffer(&line_buf, file);
- +
- + for (i = 0; i < ARRAY_SIZE(fqd_states); i++) {
- + seq_printf(file, "%s count = %u\n", state_txt[i],
- + qm_fq_state_cnt[i]);
- + }
- + return 0;
- +}
- +
- +static int qman_fqd_state_open(struct inode *inode, struct file *file)
- +{
- + return single_open(file, qman_fqd_state_show, inode->i_private);
- +}
- +
- +static const struct file_operations qman_fqd_state_fops = {
- + .owner = THIS_MODULE,
- + .open = qman_fqd_state_open,
- + .read = seq_read,
- +};
- +
- +static int qman_fqd_ctrl_show(struct seq_file *file, void *offset)
- +{
- + struct qm_fqd fqd;
- + struct qman_fq fq;
- + u32 fq_en_cnt = 0, fq_di_cnt = 0;
- + int ret, i;
- + struct mask_filter_s *data = file->private;
- + const char *ctrl_txt = get_fqd_ctrl_text(data->mask);
- + struct line_buffer_fq line_buf;
- +
- + memset(&line_buf, 0, sizeof(line_buf));
- + seq_printf(file, "List of fq ids with: %s :%s\n",
- + ctrl_txt, (data->filter) ? "enabled" : "disabled");
- + for (i = 1; i < fqid_max; i++) {
- + fq.fqid = i;
- + memset(&fqd, 0, sizeof(struct qm_fqd));
- + ret = qman_query_fq(&fq, &fqd);
- + if (ret)
- + return ret;
- + if (data->filter) {
- + if (fqd.fq_ctrl & data->mask)
- + add_to_line_buffer(&line_buf, fq.fqid, file);
- + } else {
- + if (!(fqd.fq_ctrl & data->mask))
- + add_to_line_buffer(&line_buf, fq.fqid, file);
- + }
- + if (fqd.fq_ctrl & data->mask)
- + fq_en_cnt++;
- + else
- + fq_di_cnt++;
- + }
- + flush_line_buffer(&line_buf, file);
- +
- + seq_printf(file, "Total FQD with: %s : enabled = %u\n",
- + ctrl_txt, fq_en_cnt);
- + seq_printf(file, "Total FQD with: %s : disabled = %u\n",
- + ctrl_txt, fq_di_cnt);
- + return 0;
- +}
- +
- +/*******************************************************************************
- + * QMan ctrl CGE, TDE, ORP, CTX, CPC, SFDR, BLOCK, HOLD, CACHE
- + ******************************************************************************/
- +static int qman_fqd_ctrl_open(struct inode *inode, struct file *file)
- +{
- + return single_open(file, qman_fqd_ctrl_show, inode->i_private);
- +}
- +
- +static const struct file_operations qman_fqd_ctrl_fops = {
- + .owner = THIS_MODULE,
- + .open = qman_fqd_ctrl_open,
- + .read = seq_read,
- +};
- +
- +/*******************************************************************************
- + * QMan ctrl summary
- + ******************************************************************************/
- +/*******************************************************************************
- + * QMan summary state
- + ******************************************************************************/
- +static int qman_fqd_non_prog_summary_show(struct seq_file *file, void *offset)
- +{
- + struct qm_mcr_queryfq_np np;
- + struct qman_fq fq;
- + int ret, i;
- + u32 qm_fq_state_cnt[ARRAY_SIZE(fqd_states)];
- +
- + memset(qm_fq_state_cnt, 0, sizeof(qm_fq_state_cnt));
- +
- + for (i = 1; i < fqid_max; i++) {
- + fq.fqid = i;
- + ret = qman_query_fq_np(&fq, &np);
- + if (ret)
- + return ret;
- + /* Keep a summary count of all states */
- + if ((np.state & QM_MCR_NP_STATE_MASK) < ARRAY_SIZE(fqd_states))
- + qm_fq_state_cnt[(np.state & QM_MCR_NP_STATE_MASK)]++;
- + }
- +
- + for (i = 0; i < ARRAY_SIZE(fqd_states); i++) {
- + seq_printf(file, "%s count = %u\n", state_txt[i],
- + qm_fq_state_cnt[i]);
- + }
- + return 0;
- +}
- +
- +static int qman_fqd_prog_summary_show(struct seq_file *file, void *offset)
- +{
- + struct qm_fqd fqd;
- + struct qman_fq fq;
- + int ret, i , j;
- + u32 qm_prog_cnt[ARRAY_SIZE(mask_filter)/2];
- +
- + memset(qm_prog_cnt, 0, sizeof(qm_prog_cnt));
- +
- + for (i = 1; i < fqid_max; i++) {
- + memset(&fqd, 0, sizeof(struct qm_fqd));
- + fq.fqid = i;
- + ret = qman_query_fq(&fq, &fqd);
- + if (ret)
- + return ret;
- + /* Keep a summary count of all states */
- + for (j = 0; j < ARRAY_SIZE(mask_filter); j += 2)
- + if ((fqd.fq_ctrl & QM_FQCTRL_MASK) &
- + mask_filter[j].mask)
- + qm_prog_cnt[j/2]++;
- + }
- + for (i = 0; i < ARRAY_SIZE(mask_filter) / 2; i++) {
- + seq_printf(file, "%s count = %u\n",
- + get_fqd_ctrl_text(mask_filter[i*2].mask),
- + qm_prog_cnt[i]);
- + }
- + return 0;
- +}
- +
- +static int qman_fqd_summary_show(struct seq_file *file, void *offset)
- +{
- + int ret;
- +
- + /* Display summary of non programmable fields */
- + ret = qman_fqd_non_prog_summary_show(file, offset);
- + if (ret)
- + return ret;
- + seq_puts(file, "-----------------------------------------\n");
- + /* Display programmable fields */
- + ret = qman_fqd_prog_summary_show(file, offset);
- + if (ret)
- + return ret;
- + return 0;
- +}
- +
- +static int qman_fqd_summary_open(struct inode *inode, struct file *file)
- +{
- + return single_open(file, qman_fqd_summary_show, NULL);
- +}
- +
- +static const struct file_operations qman_fqd_summary_fops = {
- + .owner = THIS_MODULE,
- + .open = qman_fqd_summary_open,
- + .read = seq_read,
- +};
- +
- +/*******************************************************************************
- + * QMan destination work queue
- + ******************************************************************************/
- +struct qman_dest_wq_s {
- + u16 wq_id;
- +};
- +static struct qman_dest_wq_s qman_dest_wq_data = {
- + .wq_id = 0,
- +};
- +
- +static int qman_fqd_dest_wq_show(struct seq_file *file, void *offset)
- +{
- + struct qm_fqd fqd;
- + struct qman_fq fq;
- + int ret, i;
- + u16 *wq, wq_id = qman_dest_wq_data.wq_id;
- + struct line_buffer_fq line_buf;
- +
- + memset(&line_buf, 0, sizeof(line_buf));
- + /* use vmalloc : need to allocate large memory region and don't
- + * require the memory to be physically contiguous. */
- + wq = vzalloc(sizeof(u16) * (0xFFFF+1));
- + if (!wq)
- + return -ENOMEM;
- +
- + seq_printf(file, "List of fq ids with destination work queue id"
- + " = 0x%x\n", wq_id);
- +
- + for (i = 1; i < fqid_max; i++) {
- + fq.fqid = i;
- + memset(&fqd, 0, sizeof(struct qm_fqd));
- + ret = qman_query_fq(&fq, &fqd);
- + if (ret) {
- + vfree(wq);
- + return ret;
- + }
- + if (wq_id == fqd.dest_wq)
- + add_to_line_buffer(&line_buf, fq.fqid, file);
- + wq[fqd.dest_wq]++;
- + }
- + flush_line_buffer(&line_buf, file);
- +
- + seq_puts(file, "Summary of all FQD destination work queue values\n");
- + for (i = 0; i < 0xFFFF; i++) {
- + if (wq[i])
- + seq_printf(file, "Channel: 0x%x WQ: 0x%x WQ_ID: 0x%x, "
- + "count = %u\n", i >> 3, i & 0x3, i, wq[i]);
- + }
- + vfree(wq);
- + return 0;
- +}
- +
- +static ssize_t qman_fqd_dest_wq_write(struct file *f, const char __user *buf,
- + size_t count, loff_t *off)
- +{
- + int ret;
- + unsigned long val;
- +
- + ret = user_input_convert(buf, count, &val);
- + if (ret)
- + return ret;
- + if (val > 0xFFFF)
- + return -EINVAL;
- + qman_dest_wq_data.wq_id = val;
- + return count;
- +}
- +
- +static int qman_fqd_dest_wq_open(struct inode *inode, struct file *file)
- +{
- + return single_open(file, qman_fqd_dest_wq_show, NULL);
- +}
- +
- +static const struct file_operations qman_fqd_dest_wq_fops = {
- + .owner = THIS_MODULE,
- + .open = qman_fqd_dest_wq_open,
- + .read = seq_read,
- + .write = qman_fqd_dest_wq_write,
- +};
- +
- +/*******************************************************************************
- + * QMan Intra-Class Scheduling Credit
- + ******************************************************************************/
- +static int qman_fqd_cred_show(struct seq_file *file, void *offset)
- +{
- + struct qm_fqd fqd;
- + struct qman_fq fq;
- + int ret, i;
- + u32 fq_cnt = 0;
- + struct line_buffer_fq line_buf;
- +
- + memset(&line_buf, 0, sizeof(line_buf));
- + seq_puts(file, "List of fq ids with Intra-Class Scheduling Credit > 0"
- + "\n");
- +
- + for (i = 1; i < fqid_max; i++) {
- + fq.fqid = i;
- + memset(&fqd, 0, sizeof(struct qm_fqd));
- + ret = qman_query_fq(&fq, &fqd);
- + if (ret)
- + return ret;
- + if (fqd.ics_cred > 0) {
- + add_to_line_buffer(&line_buf, fq.fqid, file);
- + fq_cnt++;
- + }
- + }
- + flush_line_buffer(&line_buf, file);
- +
- + seq_printf(file, "Total FQD with ics_cred > 0 = %d\n", fq_cnt);
- + return 0;
- +}
- +
- +static int qman_fqd_cred_open(struct inode *inode, struct file *file)
- +{
- + return single_open(file, qman_fqd_cred_show, NULL);
- +}
- +
- +static const struct file_operations qman_fqd_cred_fops = {
- + .owner = THIS_MODULE,
- + .open = qman_fqd_cred_open,
- + .read = seq_read,
- +};
- +
- +/*******************************************************************************
- + * Class Queue Fields
- + ******************************************************************************/
- +struct query_cq_fields_data_s {
- + u32 cqid;
- +};
- +
- +static struct query_cq_fields_data_s query_cq_fields_data = {
- + .cqid = 1,
- +};
- +
- +static int query_cq_fields_show(struct seq_file *file, void *offset)
- +{
- + int ret;
- + struct qm_mcr_ceetm_cq_query query_result;
- + unsigned int cqid;
- + unsigned int portal;
- +
- + if ((qman_ip_rev & 0xFF00) < QMAN_REV30)
- + return -EINVAL;
- +
- + cqid = query_cq_fields_data.cqid & 0x00FFFFFF;
- + portal = query_cq_fields_data.cqid >> 24;
- + if (portal > qm_dc_portal_fman1)
- + return -EINVAL;
- +
- + ret = qman_ceetm_query_cq(cqid, portal, &query_result);
- + if (ret)
- + return ret;
- + seq_printf(file, "Query CQ Fields Result cqid 0x%x on DCP %d\n",
- + cqid, portal);
- + seq_printf(file, " ccgid: %u\n", query_result.ccgid);
- + seq_printf(file, " state: %u\n", query_result.state);
- + seq_printf(file, " pfdr_hptr: %u\n", query_result.pfdr_hptr);
- + seq_printf(file, " pfdr_tptr: %u\n", query_result.pfdr_tptr);
- + seq_printf(file, " od1_xsfdr: %u\n", query_result.od1_xsfdr);
- + seq_printf(file, " od2_xsfdr: %u\n", query_result.od2_xsfdr);
- + seq_printf(file, " od3_xsfdr: %u\n", query_result.od3_xsfdr);
- + seq_printf(file, " od4_xsfdr: %u\n", query_result.od4_xsfdr);
- + seq_printf(file, " od5_xsfdr: %u\n", query_result.od5_xsfdr);
- + seq_printf(file, " od6_xsfdr: %u\n", query_result.od6_xsfdr);
- + seq_printf(file, " ra1_xsfdr: %u\n", query_result.ra1_xsfdr);
- + seq_printf(file, " ra2_xsfdr: %u\n", query_result.ra2_xsfdr);
- + seq_printf(file, " frame_count: %u\n", query_result.frm_cnt);
- +
- + return 0;
- +}
- +
- +static int query_cq_fields_open(struct inode *inode,
- + struct file *file)
- +{
- + return single_open(file, query_cq_fields_show, NULL);
- +}
- +
- +static ssize_t query_cq_fields_write(struct file *f,
- + const char __user *buf, size_t count, loff_t *off)
- +{
- + int ret;
- + unsigned long val;
- +
- + ret = user_input_convert(buf, count, &val);
- + if (ret)
- + return ret;
- + query_cq_fields_data.cqid = (u32)val;
- + return count;
- +}
- +
- +static const struct file_operations query_cq_fields_fops = {
- + .owner = THIS_MODULE,
- + .open = query_cq_fields_open,
- + .read = seq_read,
- + .write = query_cq_fields_write,
- + .release = single_release,
- +};
- +
- +/*******************************************************************************
- + * READ CEETM_XSFDR_IN_USE
- + ******************************************************************************/
- +struct query_ceetm_xsfdr_data_s {
- + enum qm_dc_portal dcp_portal;
- +};
- +
- +static struct query_ceetm_xsfdr_data_s query_ceetm_xsfdr_data;
- +
- +static int query_ceetm_xsfdr_show(struct seq_file *file, void *offset)
- +{
- + int ret;
- + unsigned int xsfdr_in_use;
- + enum qm_dc_portal portal;
- +
- +
- + if (qman_ip_rev < QMAN_REV31)
- + return -EINVAL;
- +
- + portal = query_ceetm_xsfdr_data.dcp_portal;
- + ret = qman_ceetm_get_xsfdr(portal, &xsfdr_in_use);
- + if (ret) {
- + seq_printf(file, "Read CEETM_XSFDR_IN_USE on DCP %d failed\n",
- + portal);
- + return ret;
- + }
- +
- + seq_printf(file, "DCP%d: CEETM_XSFDR_IN_USE number is %u\n", portal,
- + (xsfdr_in_use & 0x1FFF));
- + return 0;
- +}
- +
- +static int query_ceetm_xsfdr_open(struct inode *inode,
- + struct file *file)
- +{
- + return single_open(file, query_ceetm_xsfdr_show, NULL);
- +}
- +
- +static ssize_t query_ceetm_xsfdr_write(struct file *f,
- + const char __user *buf, size_t count, loff_t *off)
- +{
- + int ret;
- + unsigned long val;
- +
- + ret = user_input_convert(buf, count, &val);
- + if (ret)
- + return ret;
- + if (val > qm_dc_portal_fman1)
- + return -EINVAL;
- + query_ceetm_xsfdr_data.dcp_portal = (u32)val;
- + return count;
- +}
- +
- +static const struct file_operations query_ceetm_xsfdr_fops = {
- + .owner = THIS_MODULE,
- + .open = query_ceetm_xsfdr_open,
- + .read = seq_read,
- + .write = query_ceetm_xsfdr_write,
- + .release = single_release,
- +};
- +
- +/* helper macros used in qman_debugfs_module_init */
- +#define QMAN_DBGFS_ENTRY(name, mode, parent, data, fops) \
- + do { \
- + d = debugfs_create_file(name, \
- + mode, parent, \
- + data, \
- + fops); \
- + if (d == NULL) { \
- + ret = -ENOMEM; \
- + goto _return; \
- + } \
- + } while (0)
- +
- +/* dfs_root as parent */
- +#define QMAN_DBGFS_ENTRY_ROOT(name, mode, data, fops) \
- + QMAN_DBGFS_ENTRY(name, mode, dfs_root, data, fops)
- +
- +/* fqd_root as parent */
- +#define QMAN_DBGFS_ENTRY_FQDROOT(name, mode, data, fops) \
- + QMAN_DBGFS_ENTRY(name, mode, fqd_root, data, fops)
- +
- +/* fqd state */
- +#define QMAN_DBGFS_ENTRY_FQDSTATE(name, index) \
- + QMAN_DBGFS_ENTRY_FQDROOT(name, S_IRUGO, \
- + (void *)&mask_filter[index], &qman_fqd_ctrl_fops)
- +
- +static int __init qman_debugfs_module_init(void)
- +{
- + int ret = 0;
- + struct dentry *d, *fqd_root;
- + u32 reg;
- +
- + fqid_max = 0;
- + init_ccsrmempeek();
- + if (qman_ccsr_start) {
- + if (!qman_ccsrmempeek(®, QM_FQD_AR)) {
- + /* extract the size of the FQD window */
- + reg = reg & 0x3f;
- + /* calculate valid frame queue descriptor range */
- + fqid_max = (1 << (reg + 1)) / QM_FQD_BLOCK_SIZE;
- + }
- + }
- + dfs_root = debugfs_create_dir("qman", NULL);
- + fqd_root = debugfs_create_dir("fqd", dfs_root);
- + if (dfs_root == NULL || fqd_root == NULL) {
- + ret = -ENOMEM;
- + pr_err("Cannot create qman/fqd debugfs dir\n");
- + goto _return;
- + }
- + if (fqid_max) {
- + QMAN_DBGFS_ENTRY_ROOT("ccsrmempeek", S_IRUGO | S_IWUGO,
- + NULL, &qman_ccsrmempeek_fops);
- + }
- + QMAN_DBGFS_ENTRY_ROOT("query_fq_np_fields", S_IRUGO | S_IWUGO,
- + &query_fq_np_fields_data, &query_fq_np_fields_fops);
- +
- + QMAN_DBGFS_ENTRY_ROOT("query_fq_fields", S_IRUGO | S_IWUGO,
- + &query_fq_fields_data, &query_fq_fields_fops);
- +
- + QMAN_DBGFS_ENTRY_ROOT("query_wq_lengths", S_IRUGO | S_IWUGO,
- + &query_wq_lengths_data, &query_wq_lengths_fops);
- +
- + QMAN_DBGFS_ENTRY_ROOT("query_cgr", S_IRUGO | S_IWUGO,
- + &query_cgr_data, &query_cgr_fops);
- +
- + QMAN_DBGFS_ENTRY_ROOT("query_congestion", S_IRUGO,
- + NULL, &query_congestion_fops);
- +
- + QMAN_DBGFS_ENTRY_ROOT("testwrite_cgr", S_IRUGO,
- + NULL, &testwrite_cgr_fops);
- +
- + QMAN_DBGFS_ENTRY_ROOT("testwrite_cgr_cgrid", S_IRUGO | S_IWUGO,
- + NULL, &teswrite_cgr_cgrid_fops);
- +
- + QMAN_DBGFS_ENTRY_ROOT("testwrite_cgr_ibcnt", S_IRUGO | S_IWUGO,
- + NULL, &teswrite_cgr_ibcnt_fops);
- +
- + QMAN_DBGFS_ENTRY_ROOT("query_ceetm_ccgr", S_IRUGO | S_IWUGO,
- + &query_ccgr_data, &query_ccgr_fops);
- + /* Create files with fqd_root as parent */
- +
- + QMAN_DBGFS_ENTRY_FQDROOT("stateoos", S_IRUGO,
- + (void *)&fqd_states[QM_MCR_NP_STATE_OOS], &qman_fqd_state_fops);
- +
- + QMAN_DBGFS_ENTRY_FQDROOT("state_retired", S_IRUGO,
- + (void *)&fqd_states[QM_MCR_NP_STATE_RETIRED],
- + &qman_fqd_state_fops);
- +
- + QMAN_DBGFS_ENTRY_FQDROOT("state_tentatively_sched", S_IRUGO,
- + (void *)&fqd_states[QM_MCR_NP_STATE_TEN_SCHED],
- + &qman_fqd_state_fops);
- +
- + QMAN_DBGFS_ENTRY_FQDROOT("state_truly_sched", S_IRUGO,
- + (void *)&fqd_states[QM_MCR_NP_STATE_TRU_SCHED],
- + &qman_fqd_state_fops);
- +
- + QMAN_DBGFS_ENTRY_FQDROOT("state_parked", S_IRUGO,
- + (void *)&fqd_states[QM_MCR_NP_STATE_PARKED],
- + &qman_fqd_state_fops);
- +
- + QMAN_DBGFS_ENTRY_FQDROOT("state_active", S_IRUGO,
- + (void *)&fqd_states[QM_MCR_NP_STATE_ACTIVE],
- + &qman_fqd_state_fops);
- + QMAN_DBGFS_ENTRY_ROOT("query_cq_fields", S_IRUGO | S_IWUGO,
- + &query_cq_fields_data, &query_cq_fields_fops);
- + QMAN_DBGFS_ENTRY_ROOT("query_ceetm_xsfdr_in_use", S_IRUGO | S_IWUGO,
- + &query_ceetm_xsfdr_data, &query_ceetm_xsfdr_fops);
- +
- +
- + QMAN_DBGFS_ENTRY_FQDSTATE("cge_enable", 17);
- +
- + QMAN_DBGFS_ENTRY_FQDSTATE("cge_disable", 16);
- +
- + QMAN_DBGFS_ENTRY_FQDSTATE("tde_enable", 15);
- +
- + QMAN_DBGFS_ENTRY_FQDSTATE("tde_disable", 14);
- +
- + QMAN_DBGFS_ENTRY_FQDSTATE("orp_enable", 13);
- +
- + QMAN_DBGFS_ENTRY_FQDSTATE("orp_disable", 12);
- +
- + QMAN_DBGFS_ENTRY_FQDSTATE("ctx_a_stashing_enable", 11);
- +
- + QMAN_DBGFS_ENTRY_FQDSTATE("ctx_a_stashing_disable", 10);
- +
- + QMAN_DBGFS_ENTRY_FQDSTATE("cpc_enable", 9);
- +
- + QMAN_DBGFS_ENTRY_FQDSTATE("cpc_disable", 8);
- +
- + QMAN_DBGFS_ENTRY_FQDSTATE("sfdr_enable", 7);
- +
- + QMAN_DBGFS_ENTRY_FQDSTATE("sfdr_disable", 6);
- +
- + QMAN_DBGFS_ENTRY_FQDSTATE("avoid_blocking_enable", 5);
- +
- + QMAN_DBGFS_ENTRY_FQDSTATE("avoid_blocking_disable", 4);
- +
- + QMAN_DBGFS_ENTRY_FQDSTATE("hold_active_enable", 3);
- +
- + QMAN_DBGFS_ENTRY_FQDSTATE("hold_active_disable", 2);
- +
- + QMAN_DBGFS_ENTRY_FQDSTATE("prefer_in_cache_enable", 1);
- +
- + QMAN_DBGFS_ENTRY_FQDSTATE("prefer_in_cache_disable", 0);
- +
- + QMAN_DBGFS_ENTRY_FQDROOT("summary", S_IRUGO,
- + NULL, &qman_fqd_summary_fops);
- +
- + QMAN_DBGFS_ENTRY_FQDROOT("wq", S_IRUGO | S_IWUGO,
- + NULL, &qman_fqd_dest_wq_fops);
- +
- + QMAN_DBGFS_ENTRY_FQDROOT("cred", S_IRUGO,
- + NULL, &qman_fqd_cred_fops);
- +
- + return 0;
- +
- +_return:
- + debugfs_remove_recursive(dfs_root);
- + return ret;
- +}
- +
- +static void __exit qman_debugfs_module_exit(void)
- +{
- + debugfs_remove_recursive(dfs_root);
- +}
- +
- +module_init(qman_debugfs_module_init);
- +module_exit(qman_debugfs_module_exit);
- +MODULE_LICENSE("Dual BSD/GPL");
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/qman_driver.c
- @@ -0,0 +1,980 @@
- +/* Copyright 2008-2012 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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 "qman_private.h"
- +
- +#include <asm/smp.h> /* hard_smp_processor_id() if !CONFIG_SMP */
- +#ifdef CONFIG_HOTPLUG_CPU
- +#include <linux/cpu.h>
- +#endif
- +
- +/* Global variable containing revision id (even on non-control plane systems
- + * where CCSR isn't available) */
- +u16 qman_ip_rev;
- +EXPORT_SYMBOL(qman_ip_rev);
- +u8 qman_ip_cfg;
- +EXPORT_SYMBOL(qman_ip_cfg);
- +u16 qm_channel_pool1 = QMAN_CHANNEL_POOL1;
- +EXPORT_SYMBOL(qm_channel_pool1);
- +u16 qm_channel_caam = QMAN_CHANNEL_CAAM;
- +EXPORT_SYMBOL(qm_channel_caam);
- +u16 qm_channel_pme = QMAN_CHANNEL_PME;
- +EXPORT_SYMBOL(qm_channel_pme);
- +u16 qm_channel_dce = QMAN_CHANNEL_DCE;
- +EXPORT_SYMBOL(qm_channel_dce);
- +u16 qman_portal_max;
- +EXPORT_SYMBOL(qman_portal_max);
- +
- +u32 qman_clk;
- +struct qm_ceetm qman_ceetms[QMAN_CEETM_MAX];
- +/* the qman ceetm instances on the given SoC */
- +u8 num_ceetms;
- +
- +/* For these variables, and the portal-initialisation logic, the
- + * comments in bman_driver.c apply here so won't be repeated. */
- +static struct qman_portal *shared_portals[NR_CPUS];
- +static int num_shared_portals;
- +static int shared_portals_idx;
- +static LIST_HEAD(unused_pcfgs);
- +static DEFINE_SPINLOCK(unused_pcfgs_lock);
- +
- +/* A SDQCR mask comprising all the available/visible pool channels */
- +static u32 pools_sdqcr;
- +
- +#define STR_ERR_NOPROP "No '%s' property in node %s\n"
- +#define STR_ERR_CELL "'%s' is not a %d-cell range in node %s\n"
- +#define STR_FQID_RANGE "fsl,fqid-range"
- +#define STR_POOL_CHAN_RANGE "fsl,pool-channel-range"
- +#define STR_CGRID_RANGE "fsl,cgrid-range"
- +
- +/* A "fsl,fqid-range" node; release the given range to the allocator */
- +static __init int fsl_fqid_range_init(struct device_node *node)
- +{
- + int ret;
- + const u32 *range = of_get_property(node, STR_FQID_RANGE, &ret);
- + if (!range) {
- + pr_err(STR_ERR_NOPROP, STR_FQID_RANGE, node->full_name);
- + return -EINVAL;
- + }
- + if (ret != 8) {
- + pr_err(STR_ERR_CELL, STR_FQID_RANGE, 2, node->full_name);
- + return -EINVAL;
- + }
- + qman_seed_fqid_range(be32_to_cpu(range[0]), be32_to_cpu(range[1]));
- + pr_info("Qman: FQID allocator includes range %d:%d\n",
- + be32_to_cpu(range[0]), be32_to_cpu(range[1]));
- + return 0;
- +}
- +
- +/* A "fsl,pool-channel-range" node; add to the SDQCR mask only */
- +static __init int fsl_pool_channel_range_sdqcr(struct device_node *node)
- +{
- + int ret;
- + const u32 *chanid = of_get_property(node, STR_POOL_CHAN_RANGE, &ret);
- + if (!chanid) {
- + pr_err(STR_ERR_NOPROP, STR_POOL_CHAN_RANGE, node->full_name);
- + return -EINVAL;
- + }
- + if (ret != 8) {
- + pr_err(STR_ERR_CELL, STR_POOL_CHAN_RANGE, 1, node->full_name);
- + return -EINVAL;
- + }
- + for (ret = 0; ret < be32_to_cpu(chanid[1]); ret++)
- + pools_sdqcr |= QM_SDQCR_CHANNELS_POOL_CONV(be32_to_cpu(chanid[0]) + ret);
- + return 0;
- +}
- +
- +/* A "fsl,pool-channel-range" node; release the given range to the allocator */
- +static __init int fsl_pool_channel_range_init(struct device_node *node)
- +{
- + int ret;
- + const u32 *chanid = of_get_property(node, STR_POOL_CHAN_RANGE, &ret);
- + if (!chanid) {
- + pr_err(STR_ERR_NOPROP, STR_POOL_CHAN_RANGE, node->full_name);
- + return -EINVAL;
- + }
- + if (ret != 8) {
- + pr_err(STR_ERR_CELL, STR_POOL_CHAN_RANGE, 1, node->full_name);
- + return -EINVAL;
- + }
- + qman_seed_pool_range(be32_to_cpu(chanid[0]), be32_to_cpu(chanid[1]));
- + pr_info("Qman: pool channel allocator includes range %d:%d\n",
- + be32_to_cpu(chanid[0]), be32_to_cpu(chanid[1]));
- + return 0;
- +}
- +
- +/* A "fsl,cgrid-range" node; release the given range to the allocator */
- +static __init int fsl_cgrid_range_init(struct device_node *node)
- +{
- + struct qman_cgr cgr;
- + int ret, errors = 0;
- + const u32 *range = of_get_property(node, STR_CGRID_RANGE, &ret);
- + if (!range) {
- + pr_err(STR_ERR_NOPROP, STR_CGRID_RANGE, node->full_name);
- + return -EINVAL;
- + }
- + if (ret != 8) {
- + pr_err(STR_ERR_CELL, STR_CGRID_RANGE, 2, node->full_name);
- + return -EINVAL;
- + }
- + qman_seed_cgrid_range(be32_to_cpu(range[0]), be32_to_cpu(range[1]));
- + pr_info("Qman: CGRID allocator includes range %d:%d\n",
- + be32_to_cpu(range[0]), be32_to_cpu(range[1]));
- + for (cgr.cgrid = 0; cgr.cgrid < __CGR_NUM; cgr.cgrid++) {
- + ret = qman_modify_cgr(&cgr, QMAN_CGR_FLAG_USE_INIT, NULL);
- + if (ret)
- + errors++;
- + }
- + if (errors)
- + pr_err("Warning: %d error%s while initialising CGRs %d:%d\n",
- + errors, (errors > 1) ? "s" : "", range[0], range[1]);
- + return 0;
- +}
- +
- +static __init int fsl_ceetm_init(struct device_node *node)
- +{
- + enum qm_dc_portal dcp_portal;
- + struct qm_ceetm_sp *sp;
- + struct qm_ceetm_lni *lni;
- + int ret, i;
- + const u32 *range;
- +
- + /* Find LFQID range */
- + range = of_get_property(node, "fsl,ceetm-lfqid-range", &ret);
- + if (!range) {
- + pr_err("No fsl,ceetm-lfqid-range in node %s\n",
- + node->full_name);
- + return -EINVAL;
- + }
- + if (ret != 8) {
- + pr_err("fsl,ceetm-lfqid-range is not a 2-cell range in node"
- + " %s\n", node->full_name);
- + return -EINVAL;
- + }
- +
- + dcp_portal = (be32_to_cpu(range[0]) & 0x0F0000) >> 16;
- + if (dcp_portal > qm_dc_portal_fman1) {
- + pr_err("The DCP portal %d doesn't support CEETM\n", dcp_portal);
- + return -EINVAL;
- + }
- +
- + if (dcp_portal == qm_dc_portal_fman0)
- + qman_seed_ceetm0_lfqid_range(be32_to_cpu(range[0]), be32_to_cpu(range[1]));
- + if (dcp_portal == qm_dc_portal_fman1)
- + qman_seed_ceetm1_lfqid_range(be32_to_cpu(range[0]), be32_to_cpu(range[1]));
- + pr_debug("Qman: The lfqid allocator of CEETM %d includes range"
- + " 0x%x:0x%x\n", dcp_portal, be32_to_cpu(range[0]), be32_to_cpu(range[1]));
- +
- + qman_ceetms[dcp_portal].idx = dcp_portal;
- + INIT_LIST_HEAD(&qman_ceetms[dcp_portal].sub_portals);
- + INIT_LIST_HEAD(&qman_ceetms[dcp_portal].lnis);
- +
- + /* Find Sub-portal range */
- + range = of_get_property(node, "fsl,ceetm-sp-range", &ret);
- + if (!range) {
- + pr_err("No fsl,ceetm-sp-range in node %s\n", node->full_name);
- + return -EINVAL;
- + }
- + if (ret != 8) {
- + pr_err("fsl,ceetm-sp-range is not a 2-cell range in node %s\n",
- + node->full_name);
- + return -EINVAL;
- + }
- +
- + for (i = 0; i < be32_to_cpu(range[1]); i++) {
- + sp = kzalloc(sizeof(*sp), GFP_KERNEL);
- + if (!sp) {
- + pr_err("Can't alloc memory for sub-portal %d\n",
- + range[0] + i);
- + return -ENOMEM;
- + }
- + sp->idx = be32_to_cpu(range[0]) + i;
- + sp->dcp_idx = dcp_portal;
- + sp->is_claimed = 0;
- + list_add_tail(&sp->node, &qman_ceetms[dcp_portal].sub_portals);
- + sp++;
- + }
- + pr_debug("Qman: Reserve sub-portal %d:%d for CEETM %d\n",
- + be32_to_cpu(range[0]), be32_to_cpu(range[1]), dcp_portal);
- + qman_ceetms[dcp_portal].sp_range[0] = be32_to_cpu(range[0]);
- + qman_ceetms[dcp_portal].sp_range[1] = be32_to_cpu(range[1]);
- +
- + /* Find LNI range */
- + range = of_get_property(node, "fsl,ceetm-lni-range", &ret);
- + if (!range) {
- + pr_err("No fsl,ceetm-lni-range in node %s\n", node->full_name);
- + return -EINVAL;
- + }
- + if (ret != 8) {
- + pr_err("fsl,ceetm-lni-range is not a 2-cell range in node %s\n",
- + node->full_name);
- + return -EINVAL;
- + }
- +
- + for (i = 0; i < be32_to_cpu(range[1]); i++) {
- + lni = kzalloc(sizeof(*lni), GFP_KERNEL);
- + if (!lni) {
- + pr_err("Can't alloc memory for LNI %d\n",
- + range[0] + i);
- + return -ENOMEM;
- + }
- + lni->idx = be32_to_cpu(range[0]) + i;
- + lni->dcp_idx = dcp_portal;
- + lni->is_claimed = 0;
- + INIT_LIST_HEAD(&lni->channels);
- + list_add_tail(&lni->node, &qman_ceetms[dcp_portal].lnis);
- + lni++;
- + }
- + pr_debug("Qman: Reserve LNI %d:%d for CEETM %d\n",
- + be32_to_cpu(range[0]), be32_to_cpu(range[1]), dcp_portal);
- + qman_ceetms[dcp_portal].lni_range[0] = be32_to_cpu(range[0]);
- + qman_ceetms[dcp_portal].lni_range[1] = be32_to_cpu(range[1]);
- +
- + /* Find CEETM channel range */
- + range = of_get_property(node, "fsl,ceetm-channel-range", &ret);
- + if (!range) {
- + pr_err("No fsl,ceetm-channel-range in node %s\n",
- + node->full_name);
- + return -EINVAL;
- + }
- + if (ret != 8) {
- + pr_err("fsl,ceetm-channel-range is not a 2-cell range in node"
- + "%s\n", node->full_name);
- + return -EINVAL;
- + }
- +
- + if (dcp_portal == qm_dc_portal_fman0)
- + qman_seed_ceetm0_channel_range(be32_to_cpu(range[0]), be32_to_cpu(range[1]));
- + if (dcp_portal == qm_dc_portal_fman1)
- + qman_seed_ceetm1_channel_range(be32_to_cpu(range[0]), be32_to_cpu(range[1]));
- + pr_debug("Qman: The channel allocator of CEETM %d includes"
- + " range %d:%d\n", dcp_portal, be32_to_cpu(range[0]), be32_to_cpu(range[1]));
- +
- + /* Set CEETM PRES register */
- + ret = qman_ceetm_set_prescaler(dcp_portal);
- + if (ret)
- + return ret;
- + return 0;
- +}
- +
- +static void qman_get_ip_revision(struct device_node *dn)
- +{
- + u16 ip_rev = 0;
- + u8 ip_cfg = QMAN_REV_CFG_0;
- + for_each_compatible_node(dn, NULL, "fsl,qman-portal") {
- + if (!of_device_is_available(dn))
- + continue;
- + if (of_device_is_compatible(dn, "fsl,qman-portal-1.0") ||
- + of_device_is_compatible(dn, "fsl,qman-portal-1.0.0")) {
- + pr_err("QMAN rev1.0 on P4080 rev1 is not supported!\n");
- + BUG_ON(1);
- + } else if (of_device_is_compatible(dn, "fsl,qman-portal-1.1") ||
- + of_device_is_compatible(dn, "fsl,qman-portal-1.1.0")) {
- + ip_rev = QMAN_REV11;
- + qman_portal_max = 10;
- + } else if (of_device_is_compatible(dn, "fsl,qman-portal-1.2") ||
- + of_device_is_compatible(dn, "fsl,qman-portal-1.2.0")) {
- + ip_rev = QMAN_REV12;
- + qman_portal_max = 10;
- + } else if (of_device_is_compatible(dn, "fsl,qman-portal-2.0") ||
- + of_device_is_compatible(dn, "fsl,qman-portal-2.0.0")) {
- + ip_rev = QMAN_REV20;
- + qman_portal_max = 3;
- + } else if (of_device_is_compatible(dn,
- + "fsl,qman-portal-3.0.0")) {
- + ip_rev = QMAN_REV30;
- + qman_portal_max = 50;
- + } else if (of_device_is_compatible(dn,
- + "fsl,qman-portal-3.0.1")) {
- + ip_rev = QMAN_REV30;
- + qman_portal_max = 25;
- + ip_cfg = QMAN_REV_CFG_1;
- + } else if (of_device_is_compatible(dn,
- + "fsl,qman-portal-3.1.0")) {
- + ip_rev = QMAN_REV31;
- + qman_portal_max = 50;
- + } else if (of_device_is_compatible(dn,
- + "fsl,qman-portal-3.1.1")) {
- + ip_rev = QMAN_REV31;
- + qman_portal_max = 25;
- + ip_cfg = QMAN_REV_CFG_1;
- + } else if (of_device_is_compatible(dn,
- + "fsl,qman-portal-3.1.2")) {
- + ip_rev = QMAN_REV31;
- + qman_portal_max = 18;
- + ip_cfg = QMAN_REV_CFG_2;
- + } else if (of_device_is_compatible(dn,
- + "fsl,qman-portal-3.1.3")) {
- + ip_rev = QMAN_REV31;
- + qman_portal_max = 10;
- + ip_cfg = QMAN_REV_CFG_3;
- + } else if (of_device_is_compatible(dn,
- + "fsl,qman-portal-3.2.0")) {
- + ip_rev = QMAN_REV32;
- + qman_portal_max = 10;
- + ip_cfg = QMAN_REV_CFG_3; // TODO: Verify for ls1043
- + } else {
- + pr_warn("unknown QMan version in portal node,"
- + "default to rev1.1\n");
- + ip_rev = QMAN_REV11;
- + qman_portal_max = 10;
- + }
- +
- + if (!qman_ip_rev) {
- + if (ip_rev) {
- + qman_ip_rev = ip_rev;
- + qman_ip_cfg = ip_cfg;
- + } else {
- + pr_warn("unknown Qman version,"
- + " default to rev1.1\n");
- + qman_ip_rev = QMAN_REV11;
- + qman_ip_cfg = QMAN_REV_CFG_0;
- + }
- + } else if (ip_rev && (qman_ip_rev != ip_rev))
- + pr_warn("Revision=0x%04x, but portal '%s' has"
- + " 0x%04x\n",
- + qman_ip_rev, dn->full_name, ip_rev);
- + if (qman_ip_rev == ip_rev)
- + break;
- + }
- +}
- +
- +/* Parse a portal node, perform generic mapping duties and return the config. It
- + * is not known at this stage for what purpose (or even if) the portal will be
- + * used. */
- +static struct qm_portal_config * __init parse_pcfg(struct device_node *node)
- +{
- + struct qm_portal_config *pcfg;
- + const u32 *index_p, *channel_p;
- + u32 index, channel;
- + int irq, ret;
- + resource_size_t len;
- +
- + pcfg = kmalloc(sizeof(*pcfg), GFP_KERNEL);
- + if (!pcfg) {
- + pr_err("can't allocate portal config");
- + return NULL;
- + }
- +
- + /*
- + * This is a *horrible hack*, but the IOMMU/PAMU driver needs a
- + * 'struct device' in order to get the PAMU stashing setup and the QMan
- + * portal [driver] won't function at all without ring stashing
- + *
- + * Making the QMan portal driver nice and proper is part of the
- + * upstreaming effort
- + */
- + pcfg->dev.bus = &platform_bus_type;
- + pcfg->dev.of_node = node;
- +#ifdef CONFIG_FSL_PAMU
- + pcfg->dev.archdata.iommu_domain = NULL;
- +#endif
- +
- + ret = of_address_to_resource(node, DPA_PORTAL_CE,
- + &pcfg->addr_phys[DPA_PORTAL_CE]);
- + if (ret) {
- + pr_err("Can't get %s property '%s'\n", node->full_name,
- + "reg::CE");
- + goto err;
- + }
- + ret = of_address_to_resource(node, DPA_PORTAL_CI,
- + &pcfg->addr_phys[DPA_PORTAL_CI]);
- + if (ret) {
- + pr_err("Can't get %s property '%s'\n", node->full_name,
- + "reg::CI");
- + goto err;
- + }
- + index_p = of_get_property(node, "cell-index", &ret);
- + if (!index_p || (ret != 4)) {
- + pr_err("Can't get %s property '%s'\n", node->full_name,
- + "cell-index");
- + goto err;
- + }
- + index = be32_to_cpu(*index_p);
- + if (index >= qman_portal_max) {
- + pr_err("QMan portal index %d is beyond max (%d)\n",
- + index, qman_portal_max);
- + goto err;
- + }
- +
- + channel_p = of_get_property(node, "fsl,qman-channel-id", &ret);
- + if (!channel_p || (ret != 4)) {
- + pr_err("Can't get %s property '%s'\n", node->full_name,
- + "fsl,qman-channel-id");
- + goto err;
- + }
- + channel = be32_to_cpu(*channel_p);
- + if (channel != (index + QM_CHANNEL_SWPORTAL0))
- + pr_err("Warning: node %s has mismatched %s and %s\n",
- + node->full_name, "cell-index", "fsl,qman-channel-id");
- + pcfg->public_cfg.channel = channel;
- + pcfg->public_cfg.cpu = -1;
- + irq = irq_of_parse_and_map(node, 0);
- + if (irq == 0) {
- + pr_err("Can't get %s property '%s'\n", node->full_name,
- + "interrupts");
- + goto err;
- + }
- + pcfg->public_cfg.irq = irq;
- + pcfg->public_cfg.index = index;
- +#ifdef CONFIG_FSL_QMAN_CONFIG
- + /* We need the same LIODN offset for all portals */
- + qman_liodn_fixup(pcfg->public_cfg.channel);
- +#endif
- +
- + len = resource_size(&pcfg->addr_phys[DPA_PORTAL_CE]);
- + if (len != (unsigned long)len)
- + goto err;
- +
- +#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
- + pcfg->addr_virt[DPA_PORTAL_CE] = ioremap_cache_ns(
- + pcfg->addr_phys[DPA_PORTAL_CE].start,
- + resource_size(&pcfg->addr_phys[DPA_PORTAL_CE]));
- +
- + pcfg->addr_virt[DPA_PORTAL_CI] = ioremap(
- + pcfg->addr_phys[DPA_PORTAL_CI].start,
- + resource_size(&pcfg->addr_phys[DPA_PORTAL_CI]));
- +#else
- + pcfg->addr_virt[DPA_PORTAL_CE] = ioremap_prot(
- + pcfg->addr_phys[DPA_PORTAL_CE].start,
- + (unsigned long)len,
- + 0);
- + pcfg->addr_virt[DPA_PORTAL_CI] = ioremap_prot(
- + pcfg->addr_phys[DPA_PORTAL_CI].start,
- + resource_size(&pcfg->addr_phys[DPA_PORTAL_CI]),
- + _PAGE_GUARDED | _PAGE_NO_CACHE);
- +#endif
- + return pcfg;
- +err:
- + kfree(pcfg);
- + return NULL;
- +}
- +
- +static struct qm_portal_config *get_pcfg(struct list_head *list)
- +{
- + struct qm_portal_config *pcfg;
- + if (list_empty(list))
- + return NULL;
- + pcfg = list_entry(list->prev, struct qm_portal_config, list);
- + list_del(&pcfg->list);
- + return pcfg;
- +}
- +
- +static struct qm_portal_config *get_pcfg_idx(struct list_head *list, u32 idx)
- +{
- + struct qm_portal_config *pcfg;
- + if (list_empty(list))
- + return NULL;
- + list_for_each_entry(pcfg, list, list) {
- + if (pcfg->public_cfg.index == idx) {
- + list_del(&pcfg->list);
- + return pcfg;
- + }
- + }
- + return NULL;
- +}
- +
- +static void portal_set_cpu(struct qm_portal_config *pcfg, int cpu)
- +{
- +#ifdef CONFIG_FSL_PAMU
- + int ret;
- + int window_count = 1;
- + struct iommu_domain_geometry geom_attr;
- + struct pamu_stash_attribute stash_attr;
- +
- + pcfg->iommu_domain = iommu_domain_alloc(&platform_bus_type);
- + if (!pcfg->iommu_domain) {
- + pr_err(KBUILD_MODNAME ":%s(): iommu_domain_alloc() failed",
- + __func__);
- + goto _no_iommu;
- + }
- + geom_attr.aperture_start = 0;
- + geom_attr.aperture_end =
- + ((dma_addr_t)1 << min(8 * sizeof(dma_addr_t), (size_t)36)) - 1;
- + geom_attr.force_aperture = true;
- + ret = iommu_domain_set_attr(pcfg->iommu_domain, DOMAIN_ATTR_GEOMETRY,
- + &geom_attr);
- + if (ret < 0) {
- + pr_err(KBUILD_MODNAME ":%s(): iommu_domain_set_attr() = %d",
- + __func__, ret);
- + goto _iommu_domain_free;
- + }
- + ret = iommu_domain_set_attr(pcfg->iommu_domain, DOMAIN_ATTR_WINDOWS,
- + &window_count);
- + if (ret < 0) {
- + pr_err(KBUILD_MODNAME ":%s(): iommu_domain_set_attr() = %d",
- + __func__, ret);
- + goto _iommu_domain_free;
- + }
- + stash_attr.cpu = cpu;
- + stash_attr.cache = PAMU_ATTR_CACHE_L1;
- + /* set stash information for the window */
- + stash_attr.window = 0;
- + ret = iommu_domain_set_attr(pcfg->iommu_domain,
- + DOMAIN_ATTR_FSL_PAMU_STASH,
- + &stash_attr);
- + if (ret < 0) {
- + pr_err(KBUILD_MODNAME ":%s(): iommu_domain_set_attr() = %d",
- + __func__, ret);
- + goto _iommu_domain_free;
- + }
- + ret = iommu_domain_window_enable(pcfg->iommu_domain, 0, 0, 1ULL << 36,
- + IOMMU_READ | IOMMU_WRITE);
- + if (ret < 0) {
- + pr_err(KBUILD_MODNAME ":%s(): iommu_domain_window_enable() = %d",
- + __func__, ret);
- + goto _iommu_domain_free;
- + }
- + ret = iommu_attach_device(pcfg->iommu_domain, &pcfg->dev);
- + if (ret < 0) {
- + pr_err(KBUILD_MODNAME ":%s(): iommu_device_attach() = %d",
- + __func__, ret);
- + goto _iommu_domain_free;
- + }
- + ret = iommu_domain_set_attr(pcfg->iommu_domain,
- + DOMAIN_ATTR_FSL_PAMU_ENABLE,
- + &window_count);
- + if (ret < 0) {
- + pr_err(KBUILD_MODNAME ":%s(): iommu_domain_set_attr() = %d",
- + __func__, ret);
- + goto _iommu_detach_device;
- + }
- +
- +_no_iommu:
- +#endif
- +#ifdef CONFIG_FSL_QMAN_CONFIG
- + if (qman_set_sdest(pcfg->public_cfg.channel, cpu))
- +#endif
- + pr_warn("Failed to set QMan portal's stash request queue\n");
- +
- + return;
- +
- +#ifdef CONFIG_FSL_PAMU
- +_iommu_detach_device:
- + iommu_detach_device(pcfg->iommu_domain, NULL);
- +_iommu_domain_free:
- + iommu_domain_free(pcfg->iommu_domain);
- +#endif
- +}
- +
- +struct qm_portal_config *qm_get_unused_portal_idx(u32 idx)
- +{
- + struct qm_portal_config *ret;
- + spin_lock(&unused_pcfgs_lock);
- + if (idx == QBMAN_ANY_PORTAL_IDX)
- + ret = get_pcfg(&unused_pcfgs);
- + else
- + ret = get_pcfg_idx(&unused_pcfgs, idx);
- + spin_unlock(&unused_pcfgs_lock);
- + /* Bind stashing LIODNs to the CPU we are currently executing on, and
- + * set the portal to use the stashing request queue corresonding to the
- + * cpu as well. The user-space driver assumption is that the pthread has
- + * to already be affine to one cpu only before opening a portal. If that
- + * check is circumvented, the only risk is a performance degradation -
- + * stashing will go to whatever cpu they happened to be running on when
- + * opening the device file, and if that isn't the cpu they subsequently
- + * bind to and do their polling on, tough. */
- + if (ret)
- + portal_set_cpu(ret, hard_smp_processor_id());
- + return ret;
- +}
- +
- +struct qm_portal_config *qm_get_unused_portal(void)
- +{
- + return qm_get_unused_portal_idx(QBMAN_ANY_PORTAL_IDX);
- +}
- +
- +void qm_put_unused_portal(struct qm_portal_config *pcfg)
- +{
- + spin_lock(&unused_pcfgs_lock);
- + list_add(&pcfg->list, &unused_pcfgs);
- + spin_unlock(&unused_pcfgs_lock);
- +}
- +
- +static struct qman_portal *init_pcfg(struct qm_portal_config *pcfg)
- +{
- + struct qman_portal *p;
- +
- + pcfg->iommu_domain = NULL;
- + portal_set_cpu(pcfg, pcfg->public_cfg.cpu);
- + p = qman_create_affine_portal(pcfg, NULL);
- + if (p) {
- + u32 irq_sources = 0;
- + /* Determine what should be interrupt-vs-poll driven */
- +#ifdef CONFIG_FSL_DPA_PIRQ_SLOW
- + irq_sources |= QM_PIRQ_EQCI | QM_PIRQ_EQRI | QM_PIRQ_MRI |
- + QM_PIRQ_CSCI | QM_PIRQ_CCSCI;
- +#endif
- +#ifdef CONFIG_FSL_DPA_PIRQ_FAST
- + irq_sources |= QM_PIRQ_DQRI;
- +#endif
- + qman_p_irqsource_add(p, irq_sources);
- + pr_info("Qman portal %sinitialised, cpu %d\n",
- + pcfg->public_cfg.is_shared ? "(shared) " : "",
- + pcfg->public_cfg.cpu);
- + } else
- + pr_crit("Qman portal failure on cpu %d\n",
- + pcfg->public_cfg.cpu);
- + return p;
- +}
- +
- +static void init_slave(int cpu)
- +{
- + struct qman_portal *p;
- + struct cpumask oldmask = *tsk_cpus_allowed(current);
- + set_cpus_allowed_ptr(current, get_cpu_mask(cpu));
- + p = qman_create_affine_slave(shared_portals[shared_portals_idx++], cpu);
- + if (!p)
- + pr_err("Qman slave portal failure on cpu %d\n", cpu);
- + else
- + pr_info("Qman portal %sinitialised, cpu %d\n", "(slave) ", cpu);
- + set_cpus_allowed_ptr(current, &oldmask);
- + if (shared_portals_idx >= num_shared_portals)
- + shared_portals_idx = 0;
- +}
- +
- +static struct cpumask want_unshared __initdata;
- +static struct cpumask want_shared __initdata;
- +
- +static int __init parse_qportals(char *str)
- +{
- + return parse_portals_bootarg(str, &want_shared, &want_unshared,
- + "qportals");
- +}
- +__setup("qportals=", parse_qportals);
- +
- +static void qman_portal_update_sdest(const struct qm_portal_config *pcfg,
- + unsigned int cpu)
- +{
- +#ifdef CONFIG_FSL_PAMU
- + struct pamu_stash_attribute stash_attr;
- + int ret;
- +
- + if (pcfg->iommu_domain) {
- + stash_attr.cpu = cpu;
- + stash_attr.cache = PAMU_ATTR_CACHE_L1;
- + /* set stash information for the window */
- + stash_attr.window = 0;
- + ret = iommu_domain_set_attr(pcfg->iommu_domain,
- + DOMAIN_ATTR_FSL_PAMU_STASH, &stash_attr);
- + if (ret < 0) {
- + pr_err("Failed to update pamu stash setting\n");
- + return;
- + }
- + }
- +#endif
- +#ifdef CONFIG_FSL_QMAN_CONFIG
- + if (qman_set_sdest(pcfg->public_cfg.channel, cpu))
- + pr_warn("Failed to update portal's stash request queue\n");
- +#endif
- +}
- +
- +static void qman_offline_cpu(unsigned int cpu)
- +{
- + struct qman_portal *p;
- + const struct qm_portal_config *pcfg;
- + p = (struct qman_portal *)affine_portals[cpu];
- + if (p) {
- + pcfg = qman_get_qm_portal_config(p);
- + if (pcfg) {
- + irq_set_affinity(pcfg->public_cfg.irq, cpumask_of(0));
- + qman_portal_update_sdest(pcfg, 0);
- + }
- + }
- +}
- +
- +#ifdef CONFIG_HOTPLUG_CPU
- +static void qman_online_cpu(unsigned int cpu)
- +{
- + struct qman_portal *p;
- + const struct qm_portal_config *pcfg;
- + p = (struct qman_portal *)affine_portals[cpu];
- + if (p) {
- + pcfg = qman_get_qm_portal_config(p);
- + if (pcfg) {
- + irq_set_affinity(pcfg->public_cfg.irq, cpumask_of(cpu));
- + qman_portal_update_sdest(pcfg, cpu);
- + }
- + }
- +}
- +
- +static int qman_hotplug_cpu_callback(struct notifier_block *nfb,
- + unsigned long action, void *hcpu)
- +{
- + unsigned int cpu = (unsigned long)hcpu;
- +
- + switch (action) {
- + case CPU_ONLINE:
- + case CPU_ONLINE_FROZEN:
- + qman_online_cpu(cpu);
- + break;
- + case CPU_DOWN_PREPARE:
- + case CPU_DOWN_PREPARE_FROZEN:
- + qman_offline_cpu(cpu);
- + default:
- + break;
- + }
- + return NOTIFY_OK;
- +}
- +
- +static struct notifier_block qman_hotplug_cpu_notifier = {
- + .notifier_call = qman_hotplug_cpu_callback,
- +};
- +#endif /* CONFIG_HOTPLUG_CPU */
- +
- +__init int qman_init(void)
- +{
- + struct cpumask slave_cpus;
- + struct cpumask unshared_cpus = *cpu_none_mask;
- + struct cpumask shared_cpus = *cpu_none_mask;
- + LIST_HEAD(unshared_pcfgs);
- + LIST_HEAD(shared_pcfgs);
- + struct device_node *dn;
- + struct qm_portal_config *pcfg;
- + struct qman_portal *p;
- + int cpu, ret;
- + const u32 *clk;
- + struct cpumask offline_cpus;
- +
- + /* Initialise the Qman (CCSR) device */
- + for_each_compatible_node(dn, NULL, "fsl,qman") {
- + if (!qman_init_ccsr(dn))
- + pr_info("Qman err interrupt handler present\n");
- + else
- + pr_err("Qman CCSR setup failed\n");
- +
- + clk = of_get_property(dn, "clock-frequency", NULL);
- + if (!clk)
- + pr_warn("Can't find Qman clock frequency\n");
- + else
- + qman_clk = be32_to_cpu(*clk);
- + }
- +#ifdef CONFIG_FSL_QMAN_FQ_LOOKUP
- + /* Setup lookup table for FQ demux */
- + ret = qman_setup_fq_lookup_table(get_qman_fqd_size()/64);
- + if (ret)
- + return ret;
- +#endif
- +
- + /* Get qman ip revision */
- + qman_get_ip_revision(dn);
- + if ((qman_ip_rev & 0xff00) >= QMAN_REV30) {
- + qm_channel_pool1 = QMAN_CHANNEL_POOL1_REV3;
- + qm_channel_caam = QMAN_CHANNEL_CAAM_REV3;
- + qm_channel_pme = QMAN_CHANNEL_PME_REV3;
- + }
- +
- + if ((qman_ip_rev == QMAN_REV31) && (qman_ip_cfg == QMAN_REV_CFG_2))
- + qm_channel_dce = QMAN_CHANNEL_DCE_QMANREV312;
- +
- + /*
- + * Parse the ceetm node to get how many ceetm instances are supported
- + * on the current silicon. num_ceetms must be confirmed before portals
- + * are intiailized.
- + */
- + num_ceetms = 0;
- + for_each_compatible_node(dn, NULL, "fsl,qman-ceetm")
- + num_ceetms++;
- +
- + /* Parse pool channels into the SDQCR mask. (Must happen before portals
- + * are initialised.) */
- + for_each_compatible_node(dn, NULL, "fsl,pool-channel-range") {
- + ret = fsl_pool_channel_range_sdqcr(dn);
- + if (ret)
- + return ret;
- + }
- +
- + memset(affine_portals, 0, sizeof(void *) * num_possible_cpus());
- + /* Initialise portals. See bman_driver.c for comments */
- + for_each_compatible_node(dn, NULL, "fsl,qman-portal") {
- + if (!of_device_is_available(dn))
- + continue;
- + pcfg = parse_pcfg(dn);
- + if (pcfg) {
- + pcfg->public_cfg.pools = pools_sdqcr;
- + list_add_tail(&pcfg->list, &unused_pcfgs);
- + }
- + }
- + for_each_possible_cpu(cpu) {
- + if (cpumask_test_cpu(cpu, &want_shared)) {
- + pcfg = get_pcfg(&unused_pcfgs);
- + if (!pcfg)
- + break;
- + pcfg->public_cfg.cpu = cpu;
- + list_add_tail(&pcfg->list, &shared_pcfgs);
- + cpumask_set_cpu(cpu, &shared_cpus);
- + }
- + if (cpumask_test_cpu(cpu, &want_unshared)) {
- + if (cpumask_test_cpu(cpu, &shared_cpus))
- + continue;
- + pcfg = get_pcfg(&unused_pcfgs);
- + if (!pcfg)
- + break;
- + pcfg->public_cfg.cpu = cpu;
- + list_add_tail(&pcfg->list, &unshared_pcfgs);
- + cpumask_set_cpu(cpu, &unshared_cpus);
- + }
- + }
- + if (list_empty(&shared_pcfgs) && list_empty(&unshared_pcfgs)) {
- + for_each_online_cpu(cpu) {
- + pcfg = get_pcfg(&unused_pcfgs);
- + if (!pcfg)
- + break;
- + pcfg->public_cfg.cpu = cpu;
- + list_add_tail(&pcfg->list, &unshared_pcfgs);
- + cpumask_set_cpu(cpu, &unshared_cpus);
- + }
- + }
- + cpumask_andnot(&slave_cpus, cpu_possible_mask, &shared_cpus);
- + cpumask_andnot(&slave_cpus, &slave_cpus, &unshared_cpus);
- + if (cpumask_empty(&slave_cpus)) {
- + if (!list_empty(&shared_pcfgs)) {
- + cpumask_or(&unshared_cpus, &unshared_cpus,
- + &shared_cpus);
- + cpumask_clear(&shared_cpus);
- + list_splice_tail(&shared_pcfgs, &unshared_pcfgs);
- + INIT_LIST_HEAD(&shared_pcfgs);
- + }
- + } else {
- + if (list_empty(&shared_pcfgs)) {
- + pcfg = get_pcfg(&unshared_pcfgs);
- + if (!pcfg) {
- + pr_crit("No QMan portals available!\n");
- + return 0;
- + }
- + cpumask_clear_cpu(pcfg->public_cfg.cpu, &unshared_cpus);
- + cpumask_set_cpu(pcfg->public_cfg.cpu, &shared_cpus);
- + list_add_tail(&pcfg->list, &shared_pcfgs);
- + }
- + }
- + list_for_each_entry(pcfg, &unshared_pcfgs, list) {
- + pcfg->public_cfg.is_shared = 0;
- + p = init_pcfg(pcfg);
- + if (!p) {
- + pr_crit("Unable to configure portals\n");
- + return 0;
- + }
- + }
- + list_for_each_entry(pcfg, &shared_pcfgs, list) {
- + pcfg->public_cfg.is_shared = 1;
- + p = init_pcfg(pcfg);
- + if (p)
- + shared_portals[num_shared_portals++] = p;
- + }
- + if (!cpumask_empty(&slave_cpus))
- + for_each_cpu(cpu, &slave_cpus)
- + init_slave(cpu);
- + pr_info("Qman portals initialised\n");
- + cpumask_andnot(&offline_cpus, cpu_possible_mask, cpu_online_mask);
- + for_each_cpu(cpu, &offline_cpus)
- + qman_offline_cpu(cpu);
- +#ifdef CONFIG_HOTPLUG_CPU
- + register_hotcpu_notifier(&qman_hotplug_cpu_notifier);
- +#endif
- + return 0;
- +}
- +
- +__init int qman_resource_init(void)
- +{
- + struct device_node *dn;
- + int ret;
- +
- + /* Initialise FQID allocation ranges */
- + for_each_compatible_node(dn, NULL, "fsl,fqid-range") {
- + ret = fsl_fqid_range_init(dn);
- + if (ret)
- + return ret;
- + }
- + /* Initialise CGRID allocation ranges */
- + for_each_compatible_node(dn, NULL, "fsl,cgrid-range") {
- + ret = fsl_cgrid_range_init(dn);
- + if (ret)
- + return ret;
- + }
- + /* Parse pool channels into the allocator. (Must happen after portals
- + * are initialised.) */
- + for_each_compatible_node(dn, NULL, "fsl,pool-channel-range") {
- + ret = fsl_pool_channel_range_init(dn);
- + if (ret)
- + return ret;
- + }
- +
- + /* Parse CEETM */
- + for_each_compatible_node(dn, NULL, "fsl,qman-ceetm") {
- + ret = fsl_ceetm_init(dn);
- + if (ret)
- + return ret;
- + }
- + return 0;
- +}
- +
- +#ifdef CONFIG_SUSPEND
- +void suspend_unused_qportal(void)
- +{
- + struct qm_portal_config *pcfg;
- +
- + if (list_empty(&unused_pcfgs))
- + return;
- +
- + list_for_each_entry(pcfg, &unused_pcfgs, list) {
- +#ifdef CONFIG_PM_DEBUG
- + pr_info("Need to save qportal %d\n", pcfg->public_cfg.index);
- +#endif
- + /* save isdr, disable all via isdr, clear isr */
- + pcfg->saved_isdr =
- + __raw_readl(pcfg->addr_virt[DPA_PORTAL_CI] + 0xe08);
- + __raw_writel(0xffffffff, pcfg->addr_virt[DPA_PORTAL_CI] +
- + 0xe08);
- + __raw_writel(0xffffffff, pcfg->addr_virt[DPA_PORTAL_CI] +
- + 0xe00);
- + }
- + return;
- +}
- +
- +void resume_unused_qportal(void)
- +{
- + struct qm_portal_config *pcfg;
- +
- + if (list_empty(&unused_pcfgs))
- + return;
- +
- + list_for_each_entry(pcfg, &unused_pcfgs, list) {
- +#ifdef CONFIG_PM_DEBUG
- + pr_info("Need to resume qportal %d\n", pcfg->public_cfg.index);
- +#endif
- + /* restore isdr */
- + __raw_writel(pcfg->saved_isdr,
- + pcfg->addr_virt[DPA_PORTAL_CI] + 0xe08);
- + }
- + return;
- +}
- +#endif
- +
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/qman_high.c
- @@ -0,0 +1,5568 @@
- +/* Copyright 2008-2012 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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 "qman_low.h"
- +
- +/* Compilation constants */
- +#define DQRR_MAXFILL 15
- +#define EQCR_ITHRESH 4 /* if EQCR congests, interrupt threshold */
- +#define IRQNAME "QMan portal %d"
- +#define MAX_IRQNAME 16 /* big enough for "QMan portal %d" */
- +
- +/* Divide 'n' by 'd', rounding down if 'r' is negative, rounding up if it's
- + * positive, and rounding to the closest value if it's zero. NB, this macro
- + * implicitly upgrades parameters to unsigned 64-bit, so feed it with types
- + * that are compatible with this. NB, these arguments should not be expressions
- + * unless it is safe for them to be evaluated multiple times. Eg. do not pass
- + * in "some_value++" as a parameter to the macro! */
- +#define ROUNDING(n, d, r) \
- + (((r) < 0) ? div64_u64((n), (d)) : \
- + (((r) > 0) ? div64_u64(((n) + (d) - 1), (d)) : \
- + div64_u64(((n) + ((d) / 2)), (d))))
- +
- +/* Lock/unlock frame queues, subject to the "LOCKED" flag. This is about
- + * inter-processor locking only. Note, FQLOCK() is always called either under a
- + * local_irq_save() or from interrupt context - hence there's no need for irq
- + * protection (and indeed, attempting to nest irq-protection doesn't work, as
- + * the "irq en/disable" machinery isn't recursive...). */
- +#define FQLOCK(fq) \
- + do { \
- + struct qman_fq *__fq478 = (fq); \
- + if (fq_isset(__fq478, QMAN_FQ_FLAG_LOCKED)) \
- + spin_lock(&__fq478->fqlock); \
- + } while (0)
- +#define FQUNLOCK(fq) \
- + do { \
- + struct qman_fq *__fq478 = (fq); \
- + if (fq_isset(__fq478, QMAN_FQ_FLAG_LOCKED)) \
- + spin_unlock(&__fq478->fqlock); \
- + } while (0)
- +
- +static inline void fq_set(struct qman_fq *fq, u32 mask)
- +{
- + set_bits(mask, &fq->flags);
- +}
- +static inline void fq_clear(struct qman_fq *fq, u32 mask)
- +{
- + clear_bits(mask, &fq->flags);
- +}
- +static inline int fq_isset(struct qman_fq *fq, u32 mask)
- +{
- + return fq->flags & mask;
- +}
- +static inline int fq_isclear(struct qman_fq *fq, u32 mask)
- +{
- + return !(fq->flags & mask);
- +}
- +
- +struct qman_portal {
- + struct qm_portal p;
- + unsigned long bits; /* PORTAL_BITS_*** - dynamic, strictly internal */
- + unsigned long irq_sources;
- + u32 use_eqcr_ci_stashing;
- + u32 slowpoll; /* only used when interrupts are off */
- + struct qman_fq *vdqcr_owned; /* only 1 volatile dequeue at a time */
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC
- + struct qman_fq *eqci_owned; /* only 1 enqueue WAIT_SYNC at a time */
- +#endif
- +#ifdef CONFIG_FSL_DPA_PORTAL_SHARE
- + raw_spinlock_t sharing_lock; /* only used if is_shared */
- + int is_shared;
- + struct qman_portal *sharing_redirect;
- +#endif
- + u32 sdqcr;
- + int dqrr_disable_ref;
- + /* A portal-specific handler for DCP ERNs. If this is NULL, the global
- + * handler is called instead. */
- + qman_cb_dc_ern cb_dc_ern;
- + /* When the cpu-affine portal is activated, this is non-NULL */
- + const struct qm_portal_config *config;
- + /* This is needed for providing a non-NULL device to dma_map_***() */
- + struct platform_device *pdev;
- + struct dpa_rbtree retire_table;
- + char irqname[MAX_IRQNAME];
- + /* 2-element array. cgrs[0] is mask, cgrs[1] is snapshot. */
- + struct qman_cgrs *cgrs;
- + /* linked-list of CSCN handlers. */
- + struct list_head cgr_cbs;
- + /* list lock */
- + spinlock_t cgr_lock;
- + /* 2-element array. ccgrs[0] is mask, ccgrs[1] is snapshot. */
- + struct qman_ccgrs *ccgrs[QMAN_CEETM_MAX];
- + /* 256-element array, each is a linked-list of CCSCN handlers. */
- + struct list_head ccgr_cbs[QMAN_CEETM_MAX];
- + /* list lock */
- + spinlock_t ccgr_lock;
- + /* track if memory was allocated by the driver */
- + u8 alloced;
- + /* power management data */
- + u32 save_isdr;
- +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
- + /* Keep a shadow copy of the DQRR on LE systems
- + as the SW needs to do byteswaps of read only
- + memory. Must be aligned to the size of the
- + ring to ensure easy index calcualtions based
- + on address */
- + struct qm_dqrr_entry shadow_dqrr[QM_DQRR_SIZE]
- + __attribute__((aligned(512)));
- +#endif
- +};
- +
- +#ifdef CONFIG_FSL_DPA_PORTAL_SHARE
- +#define PORTAL_IRQ_LOCK(p, irqflags) \
- + do { \
- + if ((p)->is_shared) \
- + raw_spin_lock_irqsave(&(p)->sharing_lock, irqflags); \
- + else \
- + local_irq_save(irqflags); \
- + } while (0)
- +#define PORTAL_IRQ_UNLOCK(p, irqflags) \
- + do { \
- + if ((p)->is_shared) \
- + raw_spin_unlock_irqrestore(&(p)->sharing_lock, \
- + irqflags); \
- + else \
- + local_irq_restore(irqflags); \
- + } while (0)
- +#else
- +#define PORTAL_IRQ_LOCK(p, irqflags) local_irq_save(irqflags)
- +#define PORTAL_IRQ_UNLOCK(p, irqflags) local_irq_restore(irqflags)
- +#endif
- +
- +/* Global handler for DCP ERNs. Used when the portal receiving the message does
- + * not have a portal-specific handler. */
- +static qman_cb_dc_ern cb_dc_ern;
- +
- +static cpumask_t affine_mask;
- +static DEFINE_SPINLOCK(affine_mask_lock);
- +static u16 affine_channels[NR_CPUS];
- +static DEFINE_PER_CPU(struct qman_portal, qman_affine_portal);
- +void *affine_portals[NR_CPUS];
- +
- +/* "raw" gets the cpu-local struct whether it's a redirect or not. */
- +static inline struct qman_portal *get_raw_affine_portal(void)
- +{
- + return &get_cpu_var(qman_affine_portal);
- +}
- +/* For ops that can redirect, this obtains the portal to use */
- +#ifdef CONFIG_FSL_DPA_PORTAL_SHARE
- +static inline struct qman_portal *get_affine_portal(void)
- +{
- + struct qman_portal *p = get_raw_affine_portal();
- + if (p->sharing_redirect)
- + return p->sharing_redirect;
- + return p;
- +}
- +#else
- +#define get_affine_portal() get_raw_affine_portal()
- +#endif
- +/* For every "get", there must be a "put" */
- +static inline void put_affine_portal(void)
- +{
- + put_cpu_var(qman_affine_portal);
- +}
- +/* Exception: poll functions assume the caller is cpu-affine and in no risk of
- + * re-entrance, which are the two reasons we usually use the get/put_cpu_var()
- + * semantic - ie. to disable pre-emption. Some use-cases expect the execution
- + * context to remain as non-atomic during poll-triggered callbacks as it was
- + * when the poll API was first called (eg. NAPI), so we go out of our way in
- + * this case to not disable pre-emption. */
- +static inline struct qman_portal *get_poll_portal(void)
- +{
- + return &get_cpu_var(qman_affine_portal);
- +}
- +#define put_poll_portal()
- +
- +/* This gives a FQID->FQ lookup to cover the fact that we can't directly demux
- + * retirement notifications (the fact they are sometimes h/w-consumed means that
- + * contextB isn't always a s/w demux - and as we can't know which case it is
- + * when looking at the notification, we have to use the slow lookup for all of
- + * them). NB, it's possible to have multiple FQ objects refer to the same FQID
- + * (though at most one of them should be the consumer), so this table isn't for
- + * all FQs - FQs are added when retirement commands are issued, and removed when
- + * they complete, which also massively reduces the size of this table. */
- +IMPLEMENT_DPA_RBTREE(fqtree, struct qman_fq, node, fqid);
- +
- +/* This is what everything can wait on, even if it migrates to a different cpu
- + * to the one whose affine portal it is waiting on. */
- +static DECLARE_WAIT_QUEUE_HEAD(affine_queue);
- +
- +static inline int table_push_fq(struct qman_portal *p, struct qman_fq *fq)
- +{
- + int ret = fqtree_push(&p->retire_table, fq);
- + if (ret)
- + pr_err("ERROR: double FQ-retirement %d\n", fq->fqid);
- + return ret;
- +}
- +
- +static inline void table_del_fq(struct qman_portal *p, struct qman_fq *fq)
- +{
- + fqtree_del(&p->retire_table, fq);
- +}
- +
- +static inline struct qman_fq *table_find_fq(struct qman_portal *p, u32 fqid)
- +{
- + return fqtree_find(&p->retire_table, fqid);
- +}
- +
- +#ifdef CONFIG_FSL_QMAN_FQ_LOOKUP
- +static void **qman_fq_lookup_table;
- +static size_t qman_fq_lookup_table_size;
- +
- +int qman_setup_fq_lookup_table(size_t num_entries)
- +{
- + num_entries++;
- + /* Allocate 1 more entry since the first entry is not used */
- + qman_fq_lookup_table = vzalloc((num_entries * sizeof(void *)));
- + if (!qman_fq_lookup_table) {
- + pr_err("QMan: Could not allocate fq lookup table\n");
- + return -ENOMEM;
- + }
- + qman_fq_lookup_table_size = num_entries;
- + pr_info("QMan: Allocated lookup table at %p, entry count %lu\n",
- + qman_fq_lookup_table,
- + (unsigned long)qman_fq_lookup_table_size);
- + return 0;
- +}
- +
- +/* global structure that maintains fq object mapping */
- +static DEFINE_SPINLOCK(fq_hash_table_lock);
- +
- +static int find_empty_fq_table_entry(u32 *entry, struct qman_fq *fq)
- +{
- + u32 i;
- +
- + spin_lock(&fq_hash_table_lock);
- + /* Can't use index zero because this has special meaning
- + * in context_b field. */
- + for (i = 1; i < qman_fq_lookup_table_size; i++) {
- + if (qman_fq_lookup_table[i] == NULL) {
- + *entry = i;
- + qman_fq_lookup_table[i] = fq;
- + spin_unlock(&fq_hash_table_lock);
- + return 0;
- + }
- + }
- + spin_unlock(&fq_hash_table_lock);
- + return -ENOMEM;
- +}
- +
- +static void clear_fq_table_entry(u32 entry)
- +{
- + spin_lock(&fq_hash_table_lock);
- + BUG_ON(entry >= qman_fq_lookup_table_size);
- + qman_fq_lookup_table[entry] = NULL;
- + spin_unlock(&fq_hash_table_lock);
- +}
- +
- +static inline struct qman_fq *get_fq_table_entry(u32 entry)
- +{
- + BUG_ON(entry >= qman_fq_lookup_table_size);
- + return qman_fq_lookup_table[entry];
- +}
- +#endif
- +
- +static inline void cpu_to_hw_fqd(struct qm_fqd *fqd)
- +{
- + /* Byteswap the FQD to HW format */
- + fqd->fq_ctrl = cpu_to_be16(fqd->fq_ctrl);
- + fqd->dest_wq = cpu_to_be16(fqd->dest_wq);
- + fqd->ics_cred = cpu_to_be16(fqd->ics_cred);
- + fqd->context_b = cpu_to_be32(fqd->context_b);
- + fqd->context_a.opaque = cpu_to_be64(fqd->context_a.opaque);
- +}
- +
- +static inline void hw_fqd_to_cpu(struct qm_fqd *fqd)
- +{
- + /* Byteswap the FQD to CPU format */
- + fqd->fq_ctrl = be16_to_cpu(fqd->fq_ctrl);
- + fqd->dest_wq = be16_to_cpu(fqd->dest_wq);
- + fqd->ics_cred = be16_to_cpu(fqd->ics_cred);
- + fqd->context_b = be32_to_cpu(fqd->context_b);
- + fqd->context_a.opaque = be64_to_cpu(fqd->context_a.opaque);
- +}
- +
- +/* Swap a 40 bit address */
- +static inline u64 cpu_to_be40(u64 in)
- +{
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + return in;
- +#else
- + u64 out = 0;
- + u8 *p = (u8 *) &out;
- + p[0] = in >> 32;
- + p[1] = in >> 24;
- + p[2] = in >> 16;
- + p[3] = in >> 8;
- + p[4] = in >> 0;
- + return out;
- +#endif
- +}
- +static inline u64 be40_to_cpu(u64 in)
- +{
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + return in;
- +#else
- + u64 out = 0;
- + u8 *pout = (u8 *) &out;
- + u8 *pin = (u8 *) ∈
- + pout[0] = pin[4];
- + pout[1] = pin[3];
- + pout[2] = pin[2];
- + pout[3] = pin[1];
- + pout[4] = pin[0];
- + return out;
- +#endif
- +}
- +
- +/* Swap a 24 bit value */
- +static inline u32 cpu_to_be24(u32 in)
- +{
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + return in;
- +#else
- + u32 out = 0;
- + u8 *p = (u8 *) &out;
- + p[0] = in >> 16;
- + p[1] = in >> 8;
- + p[2] = in >> 0;
- + return out;
- +#endif
- +}
- +
- +static inline u32 be24_to_cpu(u32 in)
- +{
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + return in;
- +#else
- + u32 out = 0;
- + u8 *pout = (u8 *) &out;
- + u8 *pin = (u8 *) ∈
- + pout[0] = pin[2];
- + pout[1] = pin[1];
- + pout[2] = pin[0];
- + return out;
- +#endif
- +}
- +
- +static inline u64 be48_to_cpu(u64 in)
- +{
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + return in;
- +#else
- + u64 out = 0;
- + u8 *pout = (u8 *) &out;
- + u8 *pin = (u8 *) ∈
- +
- + pout[0] = pin[5];
- + pout[1] = pin[4];
- + pout[2] = pin[3];
- + pout[3] = pin[2];
- + pout[4] = pin[1];
- + pout[5] = pin[0];
- + return out;
- +#endif
- +}
- +static inline void cpu_to_hw_fd(struct qm_fd *fd)
- +{
- + fd->addr = cpu_to_be40(fd->addr);
- + fd->status = cpu_to_be32(fd->status);
- + fd->opaque = cpu_to_be32(fd->opaque);
- +}
- +
- +static inline void hw_fd_to_cpu(struct qm_fd *fd)
- +{
- + fd->addr = be40_to_cpu(fd->addr);
- + fd->status = be32_to_cpu(fd->status);
- + fd->opaque = be32_to_cpu(fd->opaque);
- +}
- +
- +static inline void hw_cq_query_to_cpu(struct qm_mcr_ceetm_cq_query *cq_query)
- +{
- + cq_query->ccgid = be16_to_cpu(cq_query->ccgid);
- + cq_query->state = be16_to_cpu(cq_query->state);
- + cq_query->pfdr_hptr = be24_to_cpu(cq_query->pfdr_hptr);
- + cq_query->pfdr_tptr = be24_to_cpu(cq_query->pfdr_tptr);
- + cq_query->od1_xsfdr = be16_to_cpu(cq_query->od1_xsfdr);
- + cq_query->od2_xsfdr = be16_to_cpu(cq_query->od2_xsfdr);
- + cq_query->od3_xsfdr = be16_to_cpu(cq_query->od3_xsfdr);
- + cq_query->od4_xsfdr = be16_to_cpu(cq_query->od4_xsfdr);
- + cq_query->od5_xsfdr = be16_to_cpu(cq_query->od5_xsfdr);
- + cq_query->od6_xsfdr = be16_to_cpu(cq_query->od6_xsfdr);
- + cq_query->ra1_xsfdr = be16_to_cpu(cq_query->ra1_xsfdr);
- + cq_query->ra2_xsfdr = be16_to_cpu(cq_query->ra2_xsfdr);
- + cq_query->frm_cnt = be24_to_cpu(cq_query->frm_cnt);
- +}
- +
- +static inline void hw_ccgr_query_to_cpu(struct qm_mcr_ceetm_ccgr_query *ccgr_q)
- +{
- + int i;
- +
- + ccgr_q->cm_query.cscn_targ_dcp =
- + be16_to_cpu(ccgr_q->cm_query.cscn_targ_dcp);
- + ccgr_q->cm_query.i_cnt = be40_to_cpu(ccgr_q->cm_query.i_cnt);
- + ccgr_q->cm_query.a_cnt = be40_to_cpu(ccgr_q->cm_query.a_cnt);
- + for (i = 0; i < ARRAY_SIZE(ccgr_q->cm_query.cscn_targ_swp); i++)
- + ccgr_q->cm_query.cscn_targ_swp[i] =
- + be32_to_cpu(ccgr_q->cm_query.cscn_targ_swp[i]);
- +}
- +
- +/* In the case that slow- and fast-path handling are both done by qman_poll()
- + * (ie. because there is no interrupt handling), we ought to balance how often
- + * we do the fast-path poll versus the slow-path poll. We'll use two decrementer
- + * sources, so we call the fast poll 'n' times before calling the slow poll
- + * once. The idle decrementer constant is used when the last slow-poll detected
- + * no work to do, and the busy decrementer constant when the last slow-poll had
- + * work to do. */
- +#define SLOW_POLL_IDLE 1000
- +#define SLOW_POLL_BUSY 10
- +static u32 __poll_portal_slow(struct qman_portal *p, u32 is);
- +static inline unsigned int __poll_portal_fast(struct qman_portal *p,
- + unsigned int poll_limit);
- +
- +/* Portal interrupt handler */
- +static irqreturn_t portal_isr(__always_unused int irq, void *ptr)
- +{
- + struct qman_portal *p = ptr;
- + /*
- + * The CSCI/CCSCI source is cleared inside __poll_portal_slow(), because
- + * it could race against a Query Congestion State command also given
- + * as part of the handling of this interrupt source. We mustn't
- + * clear it a second time in this top-level function.
- + */
- + u32 clear = QM_DQAVAIL_MASK | (p->irq_sources &
- + ~(QM_PIRQ_CSCI | QM_PIRQ_CCSCI));
- + u32 is = qm_isr_status_read(&p->p) & p->irq_sources;
- + /* DQRR-handling if it's interrupt-driven */
- + if (is & QM_PIRQ_DQRI)
- + __poll_portal_fast(p, CONFIG_FSL_QMAN_POLL_LIMIT);
- + /* Handling of anything else that's interrupt-driven */
- + clear |= __poll_portal_slow(p, is);
- + qm_isr_status_clear(&p->p, clear);
- + return IRQ_HANDLED;
- +}
- +
- +/* This inner version is used privately by qman_create_affine_portal(), as well
- + * as by the exported qman_stop_dequeues(). */
- +static inline void qman_stop_dequeues_ex(struct qman_portal *p)
- +{
- + unsigned long irqflags __maybe_unused;
- + PORTAL_IRQ_LOCK(p, irqflags);
- + if (!(p->dqrr_disable_ref++))
- + qm_dqrr_set_maxfill(&p->p, 0);
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- +}
- +
- +static int drain_mr(struct qm_portal *p)
- +{
- + const struct qm_mr_entry *msg;
- +loop:
- + msg = qm_mr_current(p);
- + if (!msg) {
- + /* if MR was full and h/w had other FQRNI entries to produce, we
- + * need to allow it time to produce those entries once the
- + * existing entries are consumed. A worst-case situation
- + * (fully-loaded system) means h/w sequencers may have to do 3-4
- + * other things before servicing the portal's MR pump, each of
- + * which (if slow) may take ~50 qman cycles (which is ~200
- + * processor cycles). So rounding up and then multiplying this
- + * worst-case estimate by a factor of 10, just to be
- + * ultra-paranoid, goes as high as 10,000 cycles. NB, we consume
- + * one entry at a time, so h/w has an opportunity to produce new
- + * entries well before the ring has been fully consumed, so
- + * we're being *really* paranoid here. */
- + u64 now, then = mfatb();
- + do {
- + now = mfatb();
- + } while ((then + 10000) > now);
- + msg = qm_mr_current(p);
- + if (!msg)
- + return 0;
- + }
- + qm_mr_next(p);
- + qm_mr_cci_consume(p, 1);
- + goto loop;
- +}
- +
- +#ifdef CONFIG_SUSPEND
- +static int _qman_portal_suspend_noirq(struct device *dev)
- +{
- + struct qman_portal *p = (struct qman_portal *)dev->platform_data;
- +#ifdef CONFIG_PM_DEBUG
- + struct platform_device *pdev = to_platform_device(dev);
- +#endif
- +
- + p->save_isdr = qm_isr_disable_read(&p->p);
- + qm_isr_disable_write(&p->p, 0xffffffff);
- + qm_isr_status_clear(&p->p, 0xffffffff);
- +#ifdef CONFIG_PM_DEBUG
- + pr_info("Suspend for %s\n", pdev->name);
- +#endif
- + return 0;
- +}
- +
- +static int _qman_portal_resume_noirq(struct device *dev)
- +{
- + struct qman_portal *p = (struct qman_portal *)dev->platform_data;
- +
- + /* restore isdr */
- + qm_isr_disable_write(&p->p, p->save_isdr);
- + return 0;
- +}
- +#else
- +#define _qman_portal_suspend_noirq NULL
- +#define _qman_portal_resume_noirq NULL
- +#endif
- +
- +struct dev_pm_domain qman_portal_device_pm_domain = {
- + .ops = {
- + USE_PLATFORM_PM_SLEEP_OPS
- + .suspend_noirq = _qman_portal_suspend_noirq,
- + .resume_noirq = _qman_portal_resume_noirq,
- + }
- +};
- +
- +struct qman_portal *qman_create_portal(
- + struct qman_portal *portal,
- + const struct qm_portal_config *config,
- + const struct qman_cgrs *cgrs)
- +{
- + struct qm_portal *__p;
- + char buf[16];
- + int ret;
- + u32 isdr;
- +
- + if (!portal) {
- + portal = kmalloc(sizeof(*portal), GFP_KERNEL);
- + if (!portal)
- + return portal;
- + portal->alloced = 1;
- + } else
- + portal->alloced = 0;
- +
- + __p = &portal->p;
- +
- +#ifdef CONFIG_FSL_PAMU
- + /* PAMU is required for stashing */
- + portal->use_eqcr_ci_stashing = ((qman_ip_rev >= QMAN_REV30) ?
- + 1 : 0);
- +#else
- + portal->use_eqcr_ci_stashing = 0;
- +#endif
- +
- + /* prep the low-level portal struct with the mapped addresses from the
- + * config, everything that follows depends on it and "config" is more
- + * for (de)reference... */
- + __p->addr.addr_ce = config->addr_virt[DPA_PORTAL_CE];
- + __p->addr.addr_ci = config->addr_virt[DPA_PORTAL_CI];
- + /*
- + * If CI-stashing is used, the current defaults use a threshold of 3,
- + * and stash with high-than-DQRR priority.
- + */
- + if (qm_eqcr_init(__p, qm_eqcr_pvb,
- + portal->use_eqcr_ci_stashing ? 3 : 0, 1)) {
- + pr_err("Qman EQCR initialisation failed\n");
- + goto fail_eqcr;
- + }
- + if (qm_dqrr_init(__p, config, qm_dqrr_dpush, qm_dqrr_pvb,
- + qm_dqrr_cdc, DQRR_MAXFILL)) {
- + pr_err("Qman DQRR initialisation failed\n");
- + goto fail_dqrr;
- + }
- + if (qm_mr_init(__p, qm_mr_pvb, qm_mr_cci)) {
- + pr_err("Qman MR initialisation failed\n");
- + goto fail_mr;
- + }
- + if (qm_mc_init(__p)) {
- + pr_err("Qman MC initialisation failed\n");
- + goto fail_mc;
- + }
- + if (qm_isr_init(__p)) {
- + pr_err("Qman ISR initialisation failed\n");
- + goto fail_isr;
- + }
- + /* static interrupt-gating controls */
- + qm_dqrr_set_ithresh(__p, CONFIG_FSL_QMAN_PIRQ_DQRR_ITHRESH);
- + qm_mr_set_ithresh(__p, CONFIG_FSL_QMAN_PIRQ_MR_ITHRESH);
- + qm_isr_set_iperiod(__p, CONFIG_FSL_QMAN_PIRQ_IPERIOD);
- + portal->cgrs = kmalloc(2 * sizeof(*cgrs), GFP_KERNEL);
- + if (!portal->cgrs)
- + goto fail_cgrs;
- + /* initial snapshot is no-depletion */
- + qman_cgrs_init(&portal->cgrs[1]);
- + if (cgrs)
- + portal->cgrs[0] = *cgrs;
- + else
- + /* if the given mask is NULL, assume all CGRs can be seen */
- + qman_cgrs_fill(&portal->cgrs[0]);
- + INIT_LIST_HEAD(&portal->cgr_cbs);
- + spin_lock_init(&portal->cgr_lock);
- + if (num_ceetms) {
- + for (ret = 0; ret < num_ceetms; ret++) {
- + portal->ccgrs[ret] = kmalloc(2 *
- + sizeof(struct qman_ccgrs), GFP_KERNEL);
- + if (!portal->ccgrs[ret])
- + goto fail_ccgrs;
- + qman_ccgrs_init(&portal->ccgrs[ret][1]);
- + qman_ccgrs_fill(&portal->ccgrs[ret][0]);
- + INIT_LIST_HEAD(&portal->ccgr_cbs[ret]);
- + }
- + }
- + spin_lock_init(&portal->ccgr_lock);
- + portal->bits = 0;
- + portal->slowpoll = 0;
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC
- + portal->eqci_owned = NULL;
- +#endif
- +#ifdef CONFIG_FSL_DPA_PORTAL_SHARE
- + raw_spin_lock_init(&portal->sharing_lock);
- + portal->is_shared = config->public_cfg.is_shared;
- + portal->sharing_redirect = NULL;
- +#endif
- + portal->sdqcr = QM_SDQCR_SOURCE_CHANNELS | QM_SDQCR_COUNT_UPTO3 |
- + QM_SDQCR_DEDICATED_PRECEDENCE | QM_SDQCR_TYPE_PRIO_QOS |
- + QM_SDQCR_TOKEN_SET(0xab) | QM_SDQCR_CHANNELS_DEDICATED;
- + portal->dqrr_disable_ref = 0;
- + portal->cb_dc_ern = NULL;
- + sprintf(buf, "qportal-%d", config->public_cfg.channel);
- + portal->pdev = platform_device_alloc(buf, -1);
- + if (!portal->pdev) {
- + pr_err("qman_portal - platform_device_alloc() failed\n");
- + goto fail_devalloc;
- + }
- +#ifdef CONFIG_ARM
- + portal->pdev->dev.coherent_dma_mask = DMA_BIT_MASK(40);
- + portal->pdev->dev.dma_mask = &portal->pdev->dev.coherent_dma_mask;
- +#else
- + if (dma_set_mask(&portal->pdev->dev, DMA_BIT_MASK(40))) {
- + pr_err("qman_portal - dma_set_mask() failed\n");
- + goto fail_devadd;
- + }
- +#endif
- + portal->pdev->dev.pm_domain = &qman_portal_device_pm_domain;
- + portal->pdev->dev.platform_data = portal;
- + ret = platform_device_add(portal->pdev);
- + if (ret) {
- + pr_err("qman_portal - platform_device_add() failed\n");
- + goto fail_devadd;
- + }
- + dpa_rbtree_init(&portal->retire_table);
- + isdr = 0xffffffff;
- + qm_isr_disable_write(__p, isdr);
- + portal->irq_sources = 0;
- + qm_isr_enable_write(__p, portal->irq_sources);
- + qm_isr_status_clear(__p, 0xffffffff);
- + snprintf(portal->irqname, MAX_IRQNAME, IRQNAME, config->public_cfg.cpu);
- + if (request_irq(config->public_cfg.irq, portal_isr, 0, portal->irqname,
- + portal)) {
- + pr_err("request_irq() failed\n");
- + goto fail_irq;
- + }
- + if ((config->public_cfg.cpu != -1) &&
- + irq_can_set_affinity(config->public_cfg.irq) &&
- + irq_set_affinity(config->public_cfg.irq,
- + cpumask_of(config->public_cfg.cpu))) {
- + pr_err("irq_set_affinity() failed\n");
- + goto fail_affinity;
- + }
- +
- + /* Need EQCR to be empty before continuing */
- + isdr ^= QM_PIRQ_EQCI;
- + qm_isr_disable_write(__p, isdr);
- + ret = qm_eqcr_get_fill(__p);
- + if (ret) {
- + pr_err("Qman EQCR unclean\n");
- + goto fail_eqcr_empty;
- + }
- + isdr ^= (QM_PIRQ_DQRI | QM_PIRQ_MRI);
- + qm_isr_disable_write(__p, isdr);
- + while (qm_dqrr_current(__p) != NULL)
- + qm_dqrr_cdc_consume_n(__p, 0xffff);
- + drain_mr(__p);
- + /* Success */
- + portal->config = config;
- + qm_isr_disable_write(__p, 0);
- + qm_isr_uninhibit(__p);
- + /* Write a sane SDQCR */
- + qm_dqrr_sdqcr_set(__p, portal->sdqcr);
- + return portal;
- +fail_eqcr_empty:
- +fail_affinity:
- + free_irq(config->public_cfg.irq, portal);
- +fail_irq:
- + platform_device_del(portal->pdev);
- +fail_devadd:
- + platform_device_put(portal->pdev);
- +fail_devalloc:
- + if (num_ceetms)
- + for (ret = 0; ret < num_ceetms; ret++)
- + kfree(portal->ccgrs[ret]);
- +fail_ccgrs:
- + kfree(portal->cgrs);
- +fail_cgrs:
- + qm_isr_finish(__p);
- +fail_isr:
- + qm_mc_finish(__p);
- +fail_mc:
- + qm_mr_finish(__p);
- +fail_mr:
- + qm_dqrr_finish(__p);
- +fail_dqrr:
- + qm_eqcr_finish(__p);
- +fail_eqcr:
- + if (portal->alloced)
- + kfree(portal);
- + return NULL;
- +}
- +
- +struct qman_portal *qman_create_affine_portal(
- + const struct qm_portal_config *config,
- + const struct qman_cgrs *cgrs)
- +{
- + struct qman_portal *res;
- + struct qman_portal *portal;
- +
- + portal = &per_cpu(qman_affine_portal, config->public_cfg.cpu);
- + res = qman_create_portal(portal, config, cgrs);
- + if (res) {
- + spin_lock(&affine_mask_lock);
- + cpumask_set_cpu(config->public_cfg.cpu, &affine_mask);
- + affine_channels[config->public_cfg.cpu] =
- + config->public_cfg.channel;
- + affine_portals[config->public_cfg.cpu] = portal;
- + spin_unlock(&affine_mask_lock);
- + }
- + return res;
- +}
- +
- +/* These checks are BUG_ON()s because the driver is already supposed to avoid
- + * these cases. */
- +struct qman_portal *qman_create_affine_slave(struct qman_portal *redirect,
- + int cpu)
- +{
- +#ifdef CONFIG_FSL_DPA_PORTAL_SHARE
- + struct qman_portal *p;
- + p = &per_cpu(qman_affine_portal, cpu);
- + /* Check that we don't already have our own portal */
- + BUG_ON(p->config);
- + /* Check that we aren't already slaving to another portal */
- + BUG_ON(p->is_shared);
- + /* Check that 'redirect' is prepared to have us */
- + BUG_ON(!redirect->config->public_cfg.is_shared);
- + /* These are the only elements to initialise when redirecting */
- + p->irq_sources = 0;
- + p->sharing_redirect = redirect;
- + affine_portals[cpu] = p;
- + return p;
- +#else
- + BUG();
- + return NULL;
- +#endif
- +}
- +
- +void qman_destroy_portal(struct qman_portal *qm)
- +{
- + const struct qm_portal_config *pcfg;
- + int i;
- +
- + /* Stop dequeues on the portal */
- + qm_dqrr_sdqcr_set(&qm->p, 0);
- +
- + /* NB we do this to "quiesce" EQCR. If we add enqueue-completions or
- + * something related to QM_PIRQ_EQCI, this may need fixing.
- + * Also, due to the prefetching model used for CI updates in the enqueue
- + * path, this update will only invalidate the CI cacheline *after*
- + * working on it, so we need to call this twice to ensure a full update
- + * irrespective of where the enqueue processing was at when the teardown
- + * began. */
- + qm_eqcr_cce_update(&qm->p);
- + qm_eqcr_cce_update(&qm->p);
- + pcfg = qm->config;
- +
- + free_irq(pcfg->public_cfg.irq, qm);
- +
- + kfree(qm->cgrs);
- + if (num_ceetms)
- + for (i = 0; i < num_ceetms; i++)
- + kfree(qm->ccgrs[i]);
- + qm_isr_finish(&qm->p);
- + qm_mc_finish(&qm->p);
- + qm_mr_finish(&qm->p);
- + qm_dqrr_finish(&qm->p);
- + qm_eqcr_finish(&qm->p);
- +
- + platform_device_del(qm->pdev);
- + platform_device_put(qm->pdev);
- +
- + qm->config = NULL;
- + if (qm->alloced)
- + kfree(qm);
- +}
- +
- +const struct qm_portal_config *qman_destroy_affine_portal(void)
- +{
- + /* We don't want to redirect if we're a slave, use "raw" */
- + struct qman_portal *qm = get_raw_affine_portal();
- + const struct qm_portal_config *pcfg;
- + int cpu;
- +#ifdef CONFIG_FSL_DPA_PORTAL_SHARE
- + if (qm->sharing_redirect) {
- + qm->sharing_redirect = NULL;
- + put_affine_portal();
- + return NULL;
- + }
- + qm->is_shared = 0;
- +#endif
- + pcfg = qm->config;
- + cpu = pcfg->public_cfg.cpu;
- +
- + qman_destroy_portal(qm);
- +
- + spin_lock(&affine_mask_lock);
- + cpumask_clear_cpu(cpu, &affine_mask);
- + spin_unlock(&affine_mask_lock);
- + put_affine_portal();
- + return pcfg;
- +}
- +
- +const struct qman_portal_config *qman_p_get_portal_config(struct qman_portal *p)
- +{
- + return &p->config->public_cfg;
- +}
- +EXPORT_SYMBOL(qman_p_get_portal_config);
- +
- +const struct qman_portal_config *qman_get_portal_config(void)
- +{
- + struct qman_portal *p = get_affine_portal();
- + const struct qman_portal_config *ret = qman_p_get_portal_config(p);
- + put_affine_portal();
- + return ret;
- +}
- +EXPORT_SYMBOL(qman_get_portal_config);
- +
- +/* Inline helper to reduce nesting in __poll_portal_slow() */
- +static inline void fq_state_change(struct qman_portal *p, struct qman_fq *fq,
- + const struct qm_mr_entry *msg, u8 verb)
- +{
- + FQLOCK(fq);
- + switch (verb) {
- + case QM_MR_VERB_FQRL:
- + DPA_ASSERT(fq_isset(fq, QMAN_FQ_STATE_ORL));
- + fq_clear(fq, QMAN_FQ_STATE_ORL);
- + table_del_fq(p, fq);
- + break;
- + case QM_MR_VERB_FQRN:
- + DPA_ASSERT((fq->state == qman_fq_state_parked) ||
- + (fq->state == qman_fq_state_sched));
- + DPA_ASSERT(fq_isset(fq, QMAN_FQ_STATE_CHANGING));
- + fq_clear(fq, QMAN_FQ_STATE_CHANGING);
- + if (msg->fq.fqs & QM_MR_FQS_NOTEMPTY)
- + fq_set(fq, QMAN_FQ_STATE_NE);
- + if (msg->fq.fqs & QM_MR_FQS_ORLPRESENT)
- + fq_set(fq, QMAN_FQ_STATE_ORL);
- + else
- + table_del_fq(p, fq);
- + fq->state = qman_fq_state_retired;
- + break;
- + case QM_MR_VERB_FQPN:
- + DPA_ASSERT(fq->state == qman_fq_state_sched);
- + DPA_ASSERT(fq_isclear(fq, QMAN_FQ_STATE_CHANGING));
- + fq->state = qman_fq_state_parked;
- + }
- + FQUNLOCK(fq);
- +}
- +
- +static u32 __poll_portal_slow(struct qman_portal *p, u32 is)
- +{
- + const struct qm_mr_entry *msg;
- +
- + if (is & QM_PIRQ_CSCI) {
- + struct qman_cgrs rr, c;
- + struct qm_mc_result *mcr;
- + struct qman_cgr *cgr;
- + unsigned long irqflags __maybe_unused;
- +
- + spin_lock_irqsave(&p->cgr_lock, irqflags);
- + /*
- + * The CSCI bit must be cleared _before_ issuing the
- + * Query Congestion State command, to ensure that a long
- + * CGR State Change callback cannot miss an intervening
- + * state change.
- + */
- + qm_isr_status_clear(&p->p, QM_PIRQ_CSCI);
- + qm_mc_start(&p->p);
- + qm_mc_commit(&p->p, QM_MCC_VERB_QUERYCONGESTION);
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + /* mask out the ones I'm not interested in */
- + qman_cgrs_and(&rr, (const struct qman_cgrs *)
- + &mcr->querycongestion.state, &p->cgrs[0]);
- + /* check previous snapshot for delta, enter/exit congestion */
- + qman_cgrs_xor(&c, &rr, &p->cgrs[1]);
- + /* update snapshot */
- + qman_cgrs_cp(&p->cgrs[1], &rr);
- + /* Invoke callback */
- + list_for_each_entry(cgr, &p->cgr_cbs, node)
- + if (cgr->cb && qman_cgrs_get(&c, cgr->cgrid))
- + cgr->cb(p, cgr, qman_cgrs_get(&rr, cgr->cgrid));
- + spin_unlock_irqrestore(&p->cgr_lock, irqflags);
- + }
- + if (is & QM_PIRQ_CCSCI) {
- + struct qman_ccgrs rr, c, congestion_result;
- + struct qm_mc_result *mcr;
- + struct qm_mc_command *mcc;
- + struct qm_ceetm_ccg *ccg;
- + unsigned long irqflags __maybe_unused;
- + int i, j, k;
- +
- + spin_lock_irqsave(&p->ccgr_lock, irqflags);
- + /*
- + * The CCSCI bit must be cleared _before_ issuing the
- + * Query Congestion State command, to ensure that a long
- + * CCGR State Change callback cannot miss an intervening
- + * state change.
- + */
- + qm_isr_status_clear(&p->p, QM_PIRQ_CCSCI);
- +
- + for (i = 0; i < num_ceetms; i++) {
- + for (j = 0; j < 2; j++) {
- + mcc = qm_mc_start(&p->p);
- + mcc->ccgr_query.ccgrid = cpu_to_be16(
- + CEETM_QUERY_CONGESTION_STATE | j);
- + mcc->ccgr_query.dcpid = i;
- + qm_mc_commit(&p->p, QM_CEETM_VERB_CCGR_QUERY);
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + for (k = 0; k < 8; k++)
- + mcr->ccgr_query.congestion_state.state.
- + __state[k] = be32_to_cpu(
- + mcr->ccgr_query.
- + congestion_state.state.
- + __state[k]);
- + congestion_result.q[j] =
- + mcr->ccgr_query.congestion_state.state;
- + }
- + /* mask out the ones I'm not interested in */
- + qman_ccgrs_and(&rr, &congestion_result,
- + &p->ccgrs[i][0]);
- + /*
- + * check previous snapshot for delta, enter/exit
- + * congestion.
- + */
- + qman_ccgrs_xor(&c, &rr, &p->ccgrs[i][1]);
- + /* update snapshot */
- + qman_ccgrs_cp(&p->ccgrs[i][1], &rr);
- + /* Invoke callback */
- + list_for_each_entry(ccg, &p->ccgr_cbs[i], cb_node)
- + if (ccg->cb && qman_ccgrs_get(&c,
- + (ccg->parent->idx << 4) | ccg->idx))
- + ccg->cb(ccg, ccg->cb_ctx,
- + qman_ccgrs_get(&rr,
- + (ccg->parent->idx << 4)
- + | ccg->idx));
- + }
- + spin_unlock_irqrestore(&p->ccgr_lock, irqflags);
- + }
- +
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC
- + if (is & QM_PIRQ_EQCI) {
- + unsigned long irqflags;
- + PORTAL_IRQ_LOCK(p, irqflags);
- + p->eqci_owned = NULL;
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + wake_up(&affine_queue);
- + }
- +#endif
- +
- + if (is & QM_PIRQ_EQRI) {
- + unsigned long irqflags __maybe_unused;
- + PORTAL_IRQ_LOCK(p, irqflags);
- + qm_eqcr_cce_update(&p->p);
- + qm_eqcr_set_ithresh(&p->p, 0);
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + wake_up(&affine_queue);
- + }
- +
- + if (is & QM_PIRQ_MRI) {
- + struct qman_fq *fq;
- + u8 verb, num = 0;
- +mr_loop:
- + qm_mr_pvb_update(&p->p);
- + msg = qm_mr_current(&p->p);
- + if (!msg)
- + goto mr_done;
- + verb = msg->verb & QM_MR_VERB_TYPE_MASK;
- + /* The message is a software ERN iff the 0x20 bit is set */
- + if (verb & 0x20) {
- + switch (verb) {
- + case QM_MR_VERB_FQRNI:
- + /* nada, we drop FQRNIs on the floor */
- + break;
- + case QM_MR_VERB_FQRN:
- + case QM_MR_VERB_FQRL:
- + /* Lookup in the retirement table */
- + fq = table_find_fq(p, be32_to_cpu(msg->fq.fqid));
- + BUG_ON(!fq);
- + fq_state_change(p, fq, msg, verb);
- + if (fq->cb.fqs)
- + fq->cb.fqs(p, fq, msg);
- + break;
- + case QM_MR_VERB_FQPN:
- + /* Parked */
- +#ifdef CONFIG_FSL_QMAN_FQ_LOOKUP
- + fq = get_fq_table_entry(
- + be32_to_cpu(msg->fq.contextB));
- +#else
- + fq = (void *)(uintptr_t)
- + be32_to_cpu(msg->fq.contextB);
- +#endif
- + fq_state_change(p, fq, msg, verb);
- + if (fq->cb.fqs)
- + fq->cb.fqs(p, fq, msg);
- + break;
- + case QM_MR_VERB_DC_ERN:
- + /* DCP ERN */
- + if (p->cb_dc_ern)
- + p->cb_dc_ern(p, msg);
- + else if (cb_dc_ern)
- + cb_dc_ern(p, msg);
- + else {
- + static int warn_once;
- + if (!warn_once) {
- + pr_crit("Leaking DCP ERNs!\n");
- + warn_once = 1;
- + }
- + }
- + break;
- + default:
- + pr_crit("Invalid MR verb 0x%02x\n", verb);
- + }
- + } else {
- + /* Its a software ERN */
- +#ifdef CONFIG_FSL_QMAN_FQ_LOOKUP
- + pr_info("ROY\n");
- + fq = get_fq_table_entry(be32_to_cpu(msg->ern.tag));
- +#else
- + fq = (void *)(uintptr_t)be32_to_cpu(msg->ern.tag);
- +#endif
- + fq->cb.ern(p, fq, msg);
- + }
- + num++;
- + qm_mr_next(&p->p);
- + goto mr_loop;
- +mr_done:
- + qm_mr_cci_consume(&p->p, num);
- + }
- + /*
- + * QM_PIRQ_CSCI/CCSCI has already been cleared, as part of its specific
- + * processing. If that interrupt source has meanwhile been re-asserted,
- + * we mustn't clear it here (or in the top-level interrupt handler).
- + */
- + return is & (QM_PIRQ_EQCI | QM_PIRQ_EQRI | QM_PIRQ_MRI);
- +}
- +
- +/* remove some slowish-path stuff from the "fast path" and make sure it isn't
- + * inlined. */
- +static noinline void clear_vdqcr(struct qman_portal *p, struct qman_fq *fq)
- +{
- + p->vdqcr_owned = NULL;
- + FQLOCK(fq);
- + fq_clear(fq, QMAN_FQ_STATE_VDQCR);
- + FQUNLOCK(fq);
- + wake_up(&affine_queue);
- +}
- +
- +/* Look: no locks, no irq_save()s, no preempt_disable()s! :-) The only states
- + * that would conflict with other things if they ran at the same time on the
- + * same cpu are;
- + *
- + * (i) setting/clearing vdqcr_owned, and
- + * (ii) clearing the NE (Not Empty) flag.
- + *
- + * Both are safe. Because;
- + *
- + * (i) this clearing can only occur after qman_volatile_dequeue() has set the
- + * vdqcr_owned field (which it does before setting VDQCR), and
- + * qman_volatile_dequeue() blocks interrupts and preemption while this is
- + * done so that we can't interfere.
- + * (ii) the NE flag is only cleared after qman_retire_fq() has set it, and as
- + * with (i) that API prevents us from interfering until it's safe.
- + *
- + * The good thing is that qman_volatile_dequeue() and qman_retire_fq() run far
- + * less frequently (ie. per-FQ) than __poll_portal_fast() does, so the nett
- + * advantage comes from this function not having to "lock" anything at all.
- + *
- + * Note also that the callbacks are invoked at points which are safe against the
- + * above potential conflicts, but that this function itself is not re-entrant
- + * (this is because the function tracks one end of each FIFO in the portal and
- + * we do *not* want to lock that). So the consequence is that it is safe for
- + * user callbacks to call into any Qman API *except* qman_poll() (as that's the
- + * sole API that could be invoking the callback through this function).
- + */
- +static inline unsigned int __poll_portal_fast(struct qman_portal *p,
- + unsigned int poll_limit)
- +{
- + const struct qm_dqrr_entry *dq;
- + struct qman_fq *fq;
- + enum qman_cb_dqrr_result res;
- + unsigned int limit = 0;
- +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
- + struct qm_dqrr_entry *shadow;
- +#endif
- +loop:
- + qm_dqrr_pvb_update(&p->p);
- + dq = qm_dqrr_current(&p->p);
- + if (!dq)
- + goto done;
- +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
- + /* If running on an LE system the fields of the
- + dequeue entry must be swapped. Because the
- + QMan HW will ignore writes the DQRR entry is
- + copied and the index stored within the copy */
- + shadow = &p->shadow_dqrr[DQRR_PTR2IDX(dq)];
- + *shadow = *dq;
- + dq = shadow;
- + shadow->fqid = be32_to_cpu(shadow->fqid);
- + shadow->contextB = be32_to_cpu(shadow->contextB);
- + shadow->seqnum = be16_to_cpu(shadow->seqnum);
- + hw_fd_to_cpu(&shadow->fd);
- +#endif
- + if (dq->stat & QM_DQRR_STAT_UNSCHEDULED) {
- + /* VDQCR: don't trust contextB as the FQ may have been
- + * configured for h/w consumption and we're draining it
- + * post-retirement. */
- + fq = p->vdqcr_owned;
- + /* We only set QMAN_FQ_STATE_NE when retiring, so we only need
- + * to check for clearing it when doing volatile dequeues. It's
- + * one less thing to check in the critical path (SDQCR). */
- + if (dq->stat & QM_DQRR_STAT_FQ_EMPTY)
- + fq_clear(fq, QMAN_FQ_STATE_NE);
- + /* this is duplicated from the SDQCR code, but we have stuff to
- + * do before *and* after this callback, and we don't want
- + * multiple if()s in the critical path (SDQCR). */
- + res = fq->cb.dqrr(p, fq, dq);
- + if (res == qman_cb_dqrr_stop)
- + goto done;
- + /* Check for VDQCR completion */
- + if (dq->stat & QM_DQRR_STAT_DQCR_EXPIRED)
- + clear_vdqcr(p, fq);
- + } else {
- + /* SDQCR: contextB points to the FQ */
- +#ifdef CONFIG_FSL_QMAN_FQ_LOOKUP
- + fq = get_fq_table_entry(dq->contextB);
- +#else
- + fq = (void *)(uintptr_t)dq->contextB;
- +#endif
- + /* Now let the callback do its stuff */
- + res = fq->cb.dqrr(p, fq, dq);
- +
- + /* The callback can request that we exit without consuming this
- + * entry nor advancing; */
- + if (res == qman_cb_dqrr_stop)
- + goto done;
- + }
- + /* Interpret 'dq' from a driver perspective. */
- + /* Parking isn't possible unless HELDACTIVE was set. NB,
- + * FORCEELIGIBLE implies HELDACTIVE, so we only need to
- + * check for HELDACTIVE to cover both. */
- + DPA_ASSERT((dq->stat & QM_DQRR_STAT_FQ_HELDACTIVE) ||
- + (res != qman_cb_dqrr_park));
- + /* Defer just means "skip it, I'll consume it myself later on" */
- + if (res != qman_cb_dqrr_defer)
- + qm_dqrr_cdc_consume_1ptr(&p->p, dq, (res == qman_cb_dqrr_park));
- + /* Move forward */
- + qm_dqrr_next(&p->p);
- + /* Entry processed and consumed, increment our counter. The callback can
- + * request that we exit after consuming the entry, and we also exit if
- + * we reach our processing limit, so loop back only if neither of these
- + * conditions is met. */
- + if ((++limit < poll_limit) && (res != qman_cb_dqrr_consume_stop))
- + goto loop;
- +done:
- + return limit;
- +}
- +
- +u32 qman_irqsource_get(void)
- +{
- + /* "irqsource" and "poll" APIs mustn't redirect when sharing, they
- + * should shut the user out if they are not the primary CPU hosting the
- + * portal. That's why we use the "raw" interface. */
- + struct qman_portal *p = get_raw_affine_portal();
- + u32 ret = p->irq_sources & QM_PIRQ_VISIBLE;
- + put_affine_portal();
- + return ret;
- +}
- +EXPORT_SYMBOL(qman_irqsource_get);
- +
- +int qman_p_irqsource_add(struct qman_portal *p, u32 bits __maybe_unused)
- +{
- + __maybe_unused unsigned long irqflags;
- +#ifdef CONFIG_FSL_DPA_PORTAL_SHARE
- + if (p->sharing_redirect)
- + return -EINVAL;
- + else
- +#endif
- + {
- + PORTAL_IRQ_LOCK(p, irqflags);
- + set_bits(bits & QM_PIRQ_VISIBLE, &p->irq_sources);
- + qm_isr_enable_write(&p->p, p->irq_sources);
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + }
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_p_irqsource_add);
- +
- +int qman_irqsource_add(u32 bits __maybe_unused)
- +{
- + struct qman_portal *p = get_raw_affine_portal();
- + int ret;
- + ret = qman_p_irqsource_add(p, bits);
- + put_affine_portal();
- + return ret;
- +}
- +EXPORT_SYMBOL(qman_irqsource_add);
- +
- +int qman_p_irqsource_remove(struct qman_portal *p, u32 bits)
- +{
- + __maybe_unused unsigned long irqflags;
- + u32 ier;
- +#ifdef CONFIG_FSL_DPA_PORTAL_SHARE
- + if (p->sharing_redirect) {
- + put_affine_portal();
- + return -EINVAL;
- + }
- +#endif
- + /* Our interrupt handler only processes+clears status register bits that
- + * are in p->irq_sources. As we're trimming that mask, if one of them
- + * were to assert in the status register just before we remove it from
- + * the enable register, there would be an interrupt-storm when we
- + * release the IRQ lock. So we wait for the enable register update to
- + * take effect in h/w (by reading it back) and then clear all other bits
- + * in the status register. Ie. we clear them from ISR once it's certain
- + * IER won't allow them to reassert. */
- + PORTAL_IRQ_LOCK(p, irqflags);
- + bits &= QM_PIRQ_VISIBLE;
- + clear_bits(bits, &p->irq_sources);
- + qm_isr_enable_write(&p->p, p->irq_sources);
- +
- + ier = qm_isr_enable_read(&p->p);
- + /* Using "~ier" (rather than "bits" or "~p->irq_sources") creates a
- + * data-dependency, ie. to protect against re-ordering. */
- + qm_isr_status_clear(&p->p, ~ier);
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_p_irqsource_remove);
- +
- +int qman_irqsource_remove(u32 bits)
- +{
- + struct qman_portal *p = get_raw_affine_portal();
- + int ret;
- + ret = qman_p_irqsource_remove(p, bits);
- + put_affine_portal();
- + return ret;
- +}
- +EXPORT_SYMBOL(qman_irqsource_remove);
- +
- +const cpumask_t *qman_affine_cpus(void)
- +{
- + return &affine_mask;
- +}
- +EXPORT_SYMBOL(qman_affine_cpus);
- +
- +u16 qman_affine_channel(int cpu)
- +{
- + if (cpu < 0) {
- + struct qman_portal *portal = get_raw_affine_portal();
- +#ifdef CONFIG_FSL_DPA_PORTAL_SHARE
- + BUG_ON(portal->sharing_redirect);
- +#endif
- + cpu = portal->config->public_cfg.cpu;
- + put_affine_portal();
- + }
- + BUG_ON(!cpumask_test_cpu(cpu, &affine_mask));
- + return affine_channels[cpu];
- +}
- +EXPORT_SYMBOL(qman_affine_channel);
- +
- +void *qman_get_affine_portal(int cpu)
- +{
- + return affine_portals[cpu];
- +}
- +EXPORT_SYMBOL(qman_get_affine_portal);
- +
- +int qman_p_poll_dqrr(struct qman_portal *p, unsigned int limit)
- +{
- + int ret;
- +
- +#ifdef CONFIG_FSL_DPA_PORTAL_SHARE
- + if (unlikely(p->sharing_redirect))
- + ret = -EINVAL;
- + else
- +#endif
- + {
- + BUG_ON(p->irq_sources & QM_PIRQ_DQRI);
- + ret = __poll_portal_fast(p, limit);
- + }
- + return ret;
- +}
- +EXPORT_SYMBOL(qman_p_poll_dqrr);
- +
- +int qman_poll_dqrr(unsigned int limit)
- +{
- + struct qman_portal *p = get_poll_portal();
- + int ret;
- + ret = qman_p_poll_dqrr(p, limit);
- + put_poll_portal();
- + return ret;
- +}
- +EXPORT_SYMBOL(qman_poll_dqrr);
- +
- +u32 qman_p_poll_slow(struct qman_portal *p)
- +{
- + u32 ret;
- +#ifdef CONFIG_FSL_DPA_PORTAL_SHARE
- + if (unlikely(p->sharing_redirect))
- + ret = (u32)-1;
- + else
- +#endif
- + {
- + u32 is = qm_isr_status_read(&p->p) & ~p->irq_sources;
- + ret = __poll_portal_slow(p, is);
- + qm_isr_status_clear(&p->p, ret);
- + }
- + return ret;
- +}
- +EXPORT_SYMBOL(qman_p_poll_slow);
- +
- +u32 qman_poll_slow(void)
- +{
- + struct qman_portal *p = get_poll_portal();
- + u32 ret;
- + ret = qman_p_poll_slow(p);
- + put_poll_portal();
- + return ret;
- +}
- +EXPORT_SYMBOL(qman_poll_slow);
- +
- +/* Legacy wrapper */
- +void qman_p_poll(struct qman_portal *p)
- +{
- +#ifdef CONFIG_FSL_DPA_PORTAL_SHARE
- + if (unlikely(p->sharing_redirect))
- + return;
- +#endif
- + if ((~p->irq_sources) & QM_PIRQ_SLOW) {
- + if (!(p->slowpoll--)) {
- + u32 is = qm_isr_status_read(&p->p) & ~p->irq_sources;
- + u32 active = __poll_portal_slow(p, is);
- + if (active) {
- + qm_isr_status_clear(&p->p, active);
- + p->slowpoll = SLOW_POLL_BUSY;
- + } else
- + p->slowpoll = SLOW_POLL_IDLE;
- + }
- + }
- + if ((~p->irq_sources) & QM_PIRQ_DQRI)
- + __poll_portal_fast(p, CONFIG_FSL_QMAN_POLL_LIMIT);
- +}
- +EXPORT_SYMBOL(qman_p_poll);
- +
- +void qman_poll(void)
- +{
- + struct qman_portal *p = get_poll_portal();
- + qman_p_poll(p);
- + put_poll_portal();
- +}
- +EXPORT_SYMBOL(qman_poll);
- +
- +void qman_p_stop_dequeues(struct qman_portal *p)
- +{
- + qman_stop_dequeues_ex(p);
- +}
- +EXPORT_SYMBOL(qman_p_stop_dequeues);
- +
- +void qman_stop_dequeues(void)
- +{
- + struct qman_portal *p = get_affine_portal();
- + qman_p_stop_dequeues(p);
- + put_affine_portal();
- +}
- +EXPORT_SYMBOL(qman_stop_dequeues);
- +
- +void qman_p_start_dequeues(struct qman_portal *p)
- +{
- + unsigned long irqflags __maybe_unused;
- + PORTAL_IRQ_LOCK(p, irqflags);
- + DPA_ASSERT(p->dqrr_disable_ref > 0);
- + if (!(--p->dqrr_disable_ref))
- + qm_dqrr_set_maxfill(&p->p, DQRR_MAXFILL);
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- +}
- +EXPORT_SYMBOL(qman_p_start_dequeues);
- +
- +void qman_start_dequeues(void)
- +{
- + struct qman_portal *p = get_affine_portal();
- + qman_p_start_dequeues(p);
- + put_affine_portal();
- +}
- +EXPORT_SYMBOL(qman_start_dequeues);
- +
- +void qman_p_static_dequeue_add(struct qman_portal *p, u32 pools)
- +{
- + unsigned long irqflags __maybe_unused;
- + PORTAL_IRQ_LOCK(p, irqflags);
- + pools &= p->config->public_cfg.pools;
- + p->sdqcr |= pools;
- + qm_dqrr_sdqcr_set(&p->p, p->sdqcr);
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- +}
- +EXPORT_SYMBOL(qman_p_static_dequeue_add);
- +
- +void qman_static_dequeue_add(u32 pools)
- +{
- + struct qman_portal *p = get_affine_portal();
- + qman_p_static_dequeue_add(p, pools);
- + put_affine_portal();
- +}
- +EXPORT_SYMBOL(qman_static_dequeue_add);
- +
- +void qman_p_static_dequeue_del(struct qman_portal *p, u32 pools)
- +{
- + unsigned long irqflags __maybe_unused;
- + PORTAL_IRQ_LOCK(p, irqflags);
- + pools &= p->config->public_cfg.pools;
- + p->sdqcr &= ~pools;
- + qm_dqrr_sdqcr_set(&p->p, p->sdqcr);
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- +}
- +EXPORT_SYMBOL(qman_p_static_dequeue_del);
- +
- +void qman_static_dequeue_del(u32 pools)
- +{
- + struct qman_portal *p = get_affine_portal();
- + qman_p_static_dequeue_del(p, pools);
- + put_affine_portal();
- +}
- +EXPORT_SYMBOL(qman_static_dequeue_del);
- +
- +u32 qman_p_static_dequeue_get(struct qman_portal *p)
- +{
- + return p->sdqcr;
- +}
- +EXPORT_SYMBOL(qman_p_static_dequeue_get);
- +
- +u32 qman_static_dequeue_get(void)
- +{
- + struct qman_portal *p = get_affine_portal();
- + u32 ret = qman_p_static_dequeue_get(p);
- + put_affine_portal();
- + return ret;
- +}
- +EXPORT_SYMBOL(qman_static_dequeue_get);
- +
- +void qman_p_dca(struct qman_portal *p, struct qm_dqrr_entry *dq,
- + int park_request)
- +{
- + qm_dqrr_cdc_consume_1ptr(&p->p, dq, park_request);
- +}
- +EXPORT_SYMBOL(qman_p_dca);
- +
- +void qman_dca(struct qm_dqrr_entry *dq, int park_request)
- +{
- + struct qman_portal *p = get_affine_portal();
- + qman_p_dca(p, dq, park_request);
- + put_affine_portal();
- +}
- +EXPORT_SYMBOL(qman_dca);
- +
- +/*******************/
- +/* Frame queue API */
- +/*******************/
- +
- +static const char *mcr_result_str(u8 result)
- +{
- + switch (result) {
- + case QM_MCR_RESULT_NULL:
- + return "QM_MCR_RESULT_NULL";
- + case QM_MCR_RESULT_OK:
- + return "QM_MCR_RESULT_OK";
- + case QM_MCR_RESULT_ERR_FQID:
- + return "QM_MCR_RESULT_ERR_FQID";
- + case QM_MCR_RESULT_ERR_FQSTATE:
- + return "QM_MCR_RESULT_ERR_FQSTATE";
- + case QM_MCR_RESULT_ERR_NOTEMPTY:
- + return "QM_MCR_RESULT_ERR_NOTEMPTY";
- + case QM_MCR_RESULT_PENDING:
- + return "QM_MCR_RESULT_PENDING";
- + case QM_MCR_RESULT_ERR_BADCOMMAND:
- + return "QM_MCR_RESULT_ERR_BADCOMMAND";
- + }
- + return "<unknown MCR result>";
- +}
- +
- +int qman_create_fq(u32 fqid, u32 flags, struct qman_fq *fq)
- +{
- + struct qm_fqd fqd;
- + struct qm_mcr_queryfq_np np;
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + struct qman_portal *p;
- + unsigned long irqflags __maybe_unused;
- +
- + if (flags & QMAN_FQ_FLAG_DYNAMIC_FQID) {
- + int ret = qman_alloc_fqid(&fqid);
- + if (ret)
- + return ret;
- + }
- + spin_lock_init(&fq->fqlock);
- + fq->fqid = fqid;
- + fq->flags = flags;
- + fq->state = qman_fq_state_oos;
- + fq->cgr_groupid = 0;
- +#ifdef CONFIG_FSL_QMAN_FQ_LOOKUP
- + if (unlikely(find_empty_fq_table_entry(&fq->key, fq)))
- + return -ENOMEM;
- +#endif
- + if (!(flags & QMAN_FQ_FLAG_AS_IS) || (flags & QMAN_FQ_FLAG_NO_MODIFY))
- + return 0;
- + /* Everything else is AS_IS support */
- + p = get_affine_portal();
- + PORTAL_IRQ_LOCK(p, irqflags);
- + mcc = qm_mc_start(&p->p);
- + mcc->queryfq.fqid = cpu_to_be32(fqid);
- + qm_mc_commit(&p->p, QM_MCC_VERB_QUERYFQ);
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCC_VERB_QUERYFQ);
- + if (mcr->result != QM_MCR_RESULT_OK) {
- + pr_err("QUERYFQ failed: %s\n", mcr_result_str(mcr->result));
- + goto err;
- + }
- + fqd = mcr->queryfq.fqd;
- + hw_fqd_to_cpu(&fqd);
- + mcc = qm_mc_start(&p->p);
- + mcc->queryfq_np.fqid = cpu_to_be32(fqid);
- + qm_mc_commit(&p->p, QM_MCC_VERB_QUERYFQ_NP);
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCC_VERB_QUERYFQ_NP);
- + if (mcr->result != QM_MCR_RESULT_OK) {
- + pr_err("QUERYFQ_NP failed: %s\n", mcr_result_str(mcr->result));
- + goto err;
- + }
- + np = mcr->queryfq_np;
- + /* Phew, have queryfq and queryfq_np results, stitch together
- + * the FQ object from those. */
- + fq->cgr_groupid = fqd.cgid;
- + switch (np.state & QM_MCR_NP_STATE_MASK) {
- + case QM_MCR_NP_STATE_OOS:
- + break;
- + case QM_MCR_NP_STATE_RETIRED:
- + fq->state = qman_fq_state_retired;
- + if (np.frm_cnt)
- + fq_set(fq, QMAN_FQ_STATE_NE);
- + break;
- + case QM_MCR_NP_STATE_TEN_SCHED:
- + case QM_MCR_NP_STATE_TRU_SCHED:
- + case QM_MCR_NP_STATE_ACTIVE:
- + fq->state = qman_fq_state_sched;
- + if (np.state & QM_MCR_NP_STATE_R)
- + fq_set(fq, QMAN_FQ_STATE_CHANGING);
- + break;
- + case QM_MCR_NP_STATE_PARKED:
- + fq->state = qman_fq_state_parked;
- + break;
- + default:
- + DPA_ASSERT(NULL == "invalid FQ state");
- + }
- + if (fqd.fq_ctrl & QM_FQCTRL_CGE)
- + fq->state |= QMAN_FQ_STATE_CGR_EN;
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- + return 0;
- +err:
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- + if (flags & QMAN_FQ_FLAG_DYNAMIC_FQID)
- + qman_release_fqid(fqid);
- + return -EIO;
- +}
- +EXPORT_SYMBOL(qman_create_fq);
- +
- +void qman_destroy_fq(struct qman_fq *fq, u32 flags __maybe_unused)
- +{
- +
- + /* We don't need to lock the FQ as it is a pre-condition that the FQ be
- + * quiesced. Instead, run some checks. */
- + switch (fq->state) {
- + case qman_fq_state_parked:
- + DPA_ASSERT(flags & QMAN_FQ_DESTROY_PARKED);
- + case qman_fq_state_oos:
- + if (fq_isset(fq, QMAN_FQ_FLAG_DYNAMIC_FQID))
- + qman_release_fqid(fq->fqid);
- +#ifdef CONFIG_FSL_QMAN_FQ_LOOKUP
- + clear_fq_table_entry(fq->key);
- +#endif
- + return;
- + default:
- + break;
- + }
- + DPA_ASSERT(NULL == "qman_free_fq() on unquiesced FQ!");
- +}
- +EXPORT_SYMBOL(qman_destroy_fq);
- +
- +u32 qman_fq_fqid(struct qman_fq *fq)
- +{
- + return fq->fqid;
- +}
- +EXPORT_SYMBOL(qman_fq_fqid);
- +
- +void qman_fq_state(struct qman_fq *fq, enum qman_fq_state *state, u32 *flags)
- +{
- + if (state)
- + *state = fq->state;
- + if (flags)
- + *flags = fq->flags;
- +}
- +EXPORT_SYMBOL(qman_fq_state);
- +
- +int qman_init_fq(struct qman_fq *fq, u32 flags, struct qm_mcc_initfq *opts)
- +{
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + struct qman_portal *p;
- + unsigned long irqflags __maybe_unused;
- + u8 res, myverb = (flags & QMAN_INITFQ_FLAG_SCHED) ?
- + QM_MCC_VERB_INITFQ_SCHED : QM_MCC_VERB_INITFQ_PARKED;
- +
- + if ((fq->state != qman_fq_state_oos) &&
- + (fq->state != qman_fq_state_parked))
- + return -EINVAL;
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + if (unlikely(fq_isset(fq, QMAN_FQ_FLAG_NO_MODIFY)))
- + return -EINVAL;
- +#endif
- + if (opts && (opts->we_mask & QM_INITFQ_WE_OAC)) {
- + /* And can't be set at the same time as TDTHRESH */
- + if (opts->we_mask & QM_INITFQ_WE_TDTHRESH)
- + return -EINVAL;
- + }
- + /* Issue an INITFQ_[PARKED|SCHED] management command */
- + p = get_affine_portal();
- + PORTAL_IRQ_LOCK(p, irqflags);
- + FQLOCK(fq);
- + if (unlikely((fq_isset(fq, QMAN_FQ_STATE_CHANGING)) ||
- + ((fq->state != qman_fq_state_oos) &&
- + (fq->state != qman_fq_state_parked)))) {
- + FQUNLOCK(fq);
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- + return -EBUSY;
- + }
- + mcc = qm_mc_start(&p->p);
- + if (opts)
- + mcc->initfq = *opts;
- + mcc->initfq.fqid = cpu_to_be32(fq->fqid);
- + mcc->initfq.count = 0;
- +
- + /* If the FQ does *not* have the TO_DCPORTAL flag, contextB is set as a
- + * demux pointer. Otherwise, the caller-provided value is allowed to
- + * stand, don't overwrite it. */
- + if (fq_isclear(fq, QMAN_FQ_FLAG_TO_DCPORTAL)) {
- + dma_addr_t phys_fq;
- + mcc->initfq.we_mask |= QM_INITFQ_WE_CONTEXTB;
- +#ifdef CONFIG_FSL_QMAN_FQ_LOOKUP
- + mcc->initfq.fqd.context_b = fq->key;
- +#else
- + mcc->initfq.fqd.context_b = (u32)(uintptr_t)fq;
- +#endif
- + /* and the physical address - NB, if the user wasn't trying to
- + * set CONTEXTA, clear the stashing settings. */
- + if (!(mcc->initfq.we_mask & QM_INITFQ_WE_CONTEXTA)) {
- + mcc->initfq.we_mask |= QM_INITFQ_WE_CONTEXTA;
- + memset(&mcc->initfq.fqd.context_a, 0,
- + sizeof(mcc->initfq.fqd.context_a));
- + } else {
- + phys_fq = dma_map_single(&p->pdev->dev, fq, sizeof(*fq),
- + DMA_TO_DEVICE);
- + qm_fqd_stashing_set64(&mcc->initfq.fqd, phys_fq);
- + }
- + }
- + if (flags & QMAN_INITFQ_FLAG_LOCAL) {
- + mcc->initfq.fqd.dest.channel = p->config->public_cfg.channel;
- + if (!(mcc->initfq.we_mask & QM_INITFQ_WE_DESTWQ)) {
- + mcc->initfq.we_mask |= QM_INITFQ_WE_DESTWQ;
- + mcc->initfq.fqd.dest.wq = 4;
- + }
- + }
- + mcc->initfq.we_mask = cpu_to_be16(mcc->initfq.we_mask);
- + cpu_to_hw_fqd(&mcc->initfq.fqd);
- + qm_mc_commit(&p->p, myverb);
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == myverb);
- + res = mcr->result;
- + if (res != QM_MCR_RESULT_OK) {
- + FQUNLOCK(fq);
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- + return -EIO;
- + }
- + if (opts) {
- + if (opts->we_mask & QM_INITFQ_WE_FQCTRL) {
- + if (opts->fqd.fq_ctrl & QM_FQCTRL_CGE)
- + fq_set(fq, QMAN_FQ_STATE_CGR_EN);
- + else
- + fq_clear(fq, QMAN_FQ_STATE_CGR_EN);
- + }
- + if (opts->we_mask & QM_INITFQ_WE_CGID)
- + fq->cgr_groupid = opts->fqd.cgid;
- + }
- + fq->state = (flags & QMAN_INITFQ_FLAG_SCHED) ?
- + qman_fq_state_sched : qman_fq_state_parked;
- + FQUNLOCK(fq);
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_init_fq);
- +
- +int qman_schedule_fq(struct qman_fq *fq)
- +{
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + struct qman_portal *p;
- + unsigned long irqflags __maybe_unused;
- + int ret = 0;
- + u8 res;
- +
- + if (fq->state != qman_fq_state_parked)
- + return -EINVAL;
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + if (unlikely(fq_isset(fq, QMAN_FQ_FLAG_NO_MODIFY)))
- + return -EINVAL;
- +#endif
- + /* Issue a ALTERFQ_SCHED management command */
- + p = get_affine_portal();
- + PORTAL_IRQ_LOCK(p, irqflags);
- + FQLOCK(fq);
- + if (unlikely((fq_isset(fq, QMAN_FQ_STATE_CHANGING)) ||
- + (fq->state != qman_fq_state_parked))) {
- + ret = -EBUSY;
- + goto out;
- + }
- + mcc = qm_mc_start(&p->p);
- + mcc->alterfq.fqid = cpu_to_be32(fq->fqid);
- + qm_mc_commit(&p->p, QM_MCC_VERB_ALTER_SCHED);
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_ALTER_SCHED);
- + res = mcr->result;
- + if (res != QM_MCR_RESULT_OK) {
- + ret = -EIO;
- + goto out;
- + }
- + fq->state = qman_fq_state_sched;
- +out:
- + FQUNLOCK(fq);
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- + return ret;
- +}
- +EXPORT_SYMBOL(qman_schedule_fq);
- +
- +int qman_retire_fq(struct qman_fq *fq, u32 *flags)
- +{
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + struct qman_portal *p;
- + unsigned long irqflags __maybe_unused;
- + int rval;
- + u8 res;
- +
- + if ((fq->state != qman_fq_state_parked) &&
- + (fq->state != qman_fq_state_sched))
- + return -EINVAL;
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + if (unlikely(fq_isset(fq, QMAN_FQ_FLAG_NO_MODIFY)))
- + return -EINVAL;
- +#endif
- + p = get_affine_portal();
- + PORTAL_IRQ_LOCK(p, irqflags);
- + FQLOCK(fq);
- + if (unlikely((fq_isset(fq, QMAN_FQ_STATE_CHANGING)) ||
- + (fq->state == qman_fq_state_retired) ||
- + (fq->state == qman_fq_state_oos))) {
- + rval = -EBUSY;
- + goto out;
- + }
- + rval = table_push_fq(p, fq);
- + if (rval)
- + goto out;
- + mcc = qm_mc_start(&p->p);
- + mcc->alterfq.fqid = cpu_to_be32(fq->fqid);
- + qm_mc_commit(&p->p, QM_MCC_VERB_ALTER_RETIRE);
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_ALTER_RETIRE);
- + res = mcr->result;
- + /* "Elegant" would be to treat OK/PENDING the same way; set CHANGING,
- + * and defer the flags until FQRNI or FQRN (respectively) show up. But
- + * "Friendly" is to process OK immediately, and not set CHANGING. We do
- + * friendly, otherwise the caller doesn't necessarily have a fully
- + * "retired" FQ on return even if the retirement was immediate. However
- + * this does mean some code duplication between here and
- + * fq_state_change(). */
- + if (likely(res == QM_MCR_RESULT_OK)) {
- + rval = 0;
- + /* Process 'fq' right away, we'll ignore FQRNI */
- + if (mcr->alterfq.fqs & QM_MCR_FQS_NOTEMPTY)
- + fq_set(fq, QMAN_FQ_STATE_NE);
- + if (mcr->alterfq.fqs & QM_MCR_FQS_ORLPRESENT)
- + fq_set(fq, QMAN_FQ_STATE_ORL);
- + else
- + table_del_fq(p, fq);
- + if (flags)
- + *flags = fq->flags;
- + fq->state = qman_fq_state_retired;
- + if (fq->cb.fqs) {
- + /* Another issue with supporting "immediate" retirement
- + * is that we're forced to drop FQRNIs, because by the
- + * time they're seen it may already be "too late" (the
- + * fq may have been OOS'd and free()'d already). But if
- + * the upper layer wants a callback whether it's
- + * immediate or not, we have to fake a "MR" entry to
- + * look like an FQRNI... */
- + struct qm_mr_entry msg;
- + msg.verb = QM_MR_VERB_FQRNI;
- + msg.fq.fqs = mcr->alterfq.fqs;
- + msg.fq.fqid = fq->fqid;
- +#ifdef CONFIG_FSL_QMAN_FQ_LOOKUP
- + msg.fq.contextB = fq->key;
- +#else
- + msg.fq.contextB = (u32)(uintptr_t)fq;
- +#endif
- + fq->cb.fqs(p, fq, &msg);
- + }
- + } else if (res == QM_MCR_RESULT_PENDING) {
- + rval = 1;
- + fq_set(fq, QMAN_FQ_STATE_CHANGING);
- + } else {
- + rval = -EIO;
- + table_del_fq(p, fq);
- + }
- +out:
- + FQUNLOCK(fq);
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- + return rval;
- +}
- +EXPORT_SYMBOL(qman_retire_fq);
- +
- +int qman_oos_fq(struct qman_fq *fq)
- +{
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + struct qman_portal *p;
- + unsigned long irqflags __maybe_unused;
- + int ret = 0;
- + u8 res;
- +
- + if (fq->state != qman_fq_state_retired)
- + return -EINVAL;
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + if (unlikely(fq_isset(fq, QMAN_FQ_FLAG_NO_MODIFY)))
- + return -EINVAL;
- +#endif
- + p = get_affine_portal();
- + PORTAL_IRQ_LOCK(p, irqflags);
- + FQLOCK(fq);
- + if (unlikely((fq_isset(fq, QMAN_FQ_STATE_BLOCKOOS)) ||
- + (fq->state != qman_fq_state_retired))) {
- + ret = -EBUSY;
- + goto out;
- + }
- + mcc = qm_mc_start(&p->p);
- + mcc->alterfq.fqid = cpu_to_be32(fq->fqid);
- + qm_mc_commit(&p->p, QM_MCC_VERB_ALTER_OOS);
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_ALTER_OOS);
- + res = mcr->result;
- + if (res != QM_MCR_RESULT_OK) {
- + ret = -EIO;
- + goto out;
- + }
- + fq->state = qman_fq_state_oos;
- +out:
- + FQUNLOCK(fq);
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- + return ret;
- +}
- +EXPORT_SYMBOL(qman_oos_fq);
- +
- +int qman_fq_flow_control(struct qman_fq *fq, int xon)
- +{
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + struct qman_portal *p;
- + unsigned long irqflags __maybe_unused;
- + int ret = 0;
- + u8 res;
- + u8 myverb;
- +
- + if ((fq->state == qman_fq_state_oos) ||
- + (fq->state == qman_fq_state_retired) ||
- + (fq->state == qman_fq_state_parked))
- + return -EINVAL;
- +
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + if (unlikely(fq_isset(fq, QMAN_FQ_FLAG_NO_MODIFY)))
- + return -EINVAL;
- +#endif
- + /* Issue a ALTER_FQXON or ALTER_FQXOFF management command */
- + p = get_affine_portal();
- + PORTAL_IRQ_LOCK(p, irqflags);
- + FQLOCK(fq);
- + if (unlikely((fq_isset(fq, QMAN_FQ_STATE_CHANGING)) ||
- + (fq->state == qman_fq_state_parked) ||
- + (fq->state == qman_fq_state_oos) ||
- + (fq->state == qman_fq_state_retired))) {
- + ret = -EBUSY;
- + goto out;
- + }
- + mcc = qm_mc_start(&p->p);
- + mcc->alterfq.fqid = fq->fqid;
- + mcc->alterfq.count = 0;
- + myverb = xon ? QM_MCC_VERB_ALTER_FQXON : QM_MCC_VERB_ALTER_FQXOFF;
- +
- + qm_mc_commit(&p->p, myverb);
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == myverb);
- +
- + res = mcr->result;
- + if (res != QM_MCR_RESULT_OK) {
- + ret = -EIO;
- + goto out;
- + }
- +out:
- + FQUNLOCK(fq);
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- + return ret;
- +}
- +EXPORT_SYMBOL(qman_fq_flow_control);
- +
- +int qman_query_fq(struct qman_fq *fq, struct qm_fqd *fqd)
- +{
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + struct qman_portal *p = get_affine_portal();
- + unsigned long irqflags __maybe_unused;
- + u8 res;
- +
- + PORTAL_IRQ_LOCK(p, irqflags);
- + mcc = qm_mc_start(&p->p);
- + mcc->queryfq.fqid = cpu_to_be32(fq->fqid);
- + qm_mc_commit(&p->p, QM_MCC_VERB_QUERYFQ);
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_QUERYFQ);
- + res = mcr->result;
- + if (res == QM_MCR_RESULT_OK)
- + memcpy_fromio(fqd, &mcr->queryfq.fqd, sizeof(*fqd));
- + hw_fqd_to_cpu(fqd);
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- + if (res != QM_MCR_RESULT_OK)
- + return -EIO;
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_query_fq);
- +
- +int qman_query_fq_np(struct qman_fq *fq, struct qm_mcr_queryfq_np *np)
- +{
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + struct qman_portal *p = get_affine_portal();
- + unsigned long irqflags __maybe_unused;
- + u8 res;
- +
- + PORTAL_IRQ_LOCK(p, irqflags);
- + mcc = qm_mc_start(&p->p);
- + mcc->queryfq.fqid = cpu_to_be32(fq->fqid);
- + qm_mc_commit(&p->p, QM_MCC_VERB_QUERYFQ_NP);
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_QUERYFQ_NP);
- + res = mcr->result;
- + if (res == QM_MCR_RESULT_OK) {
- + memcpy_fromio(np, &mcr->queryfq_np, sizeof(*np));
- + np->fqd_link = be24_to_cpu(np->fqd_link);
- + np->odp_seq = be16_to_cpu(np->odp_seq);
- + np->orp_nesn = be16_to_cpu(np->orp_nesn);
- + np->orp_ea_hseq = be16_to_cpu(np->orp_ea_hseq);
- + np->orp_ea_tseq = be16_to_cpu(np->orp_ea_tseq);
- + np->orp_ea_hptr = be24_to_cpu(np->orp_ea_hptr);
- + np->orp_ea_tptr = be24_to_cpu(np->orp_ea_tptr);
- + np->pfdr_hptr = be24_to_cpu(np->pfdr_hptr);
- + np->pfdr_tptr = be24_to_cpu(np->pfdr_tptr);
- + np->ics_surp = be16_to_cpu(np->ics_surp);
- + np->byte_cnt = be32_to_cpu(np->byte_cnt);
- + np->frm_cnt = be24_to_cpu(np->frm_cnt);
- + np->ra1_sfdr = be16_to_cpu(np->ra1_sfdr);
- + np->ra2_sfdr = be16_to_cpu(np->ra2_sfdr);
- + np->od1_sfdr = be16_to_cpu(np->od1_sfdr);
- + np->od2_sfdr = be16_to_cpu(np->od2_sfdr);
- + np->od3_sfdr = be16_to_cpu(np->od3_sfdr);
- +
- +
- + }
- +
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- + if (res == QM_MCR_RESULT_ERR_FQID)
- + return -ERANGE;
- + else if (res != QM_MCR_RESULT_OK)
- + return -EIO;
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_query_fq_np);
- +
- +int qman_query_wq(u8 query_dedicated, struct qm_mcr_querywq *wq)
- +{
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + struct qman_portal *p = get_affine_portal();
- + unsigned long irqflags __maybe_unused;
- + u8 res, myverb;
- +
- + PORTAL_IRQ_LOCK(p, irqflags);
- + myverb = (query_dedicated) ? QM_MCR_VERB_QUERYWQ_DEDICATED :
- + QM_MCR_VERB_QUERYWQ;
- + mcc = qm_mc_start(&p->p);
- + mcc->querywq.channel.id = cpu_to_be16(wq->channel.id);
- + qm_mc_commit(&p->p, myverb);
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == myverb);
- + res = mcr->result;
- + if (res == QM_MCR_RESULT_OK) {
- + int i, array_len;
- + wq->channel.id = be16_to_cpu(mcr->querywq.channel.id);
- + array_len = ARRAY_SIZE(mcr->querywq.wq_len);
- + for (i = 0; i < array_len; i++)
- + wq->wq_len[i] = be32_to_cpu(mcr->querywq.wq_len[i]);
- + }
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- + if (res != QM_MCR_RESULT_OK) {
- + pr_err("QUERYWQ failed: %s\n", mcr_result_str(res));
- + return -EIO;
- + }
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_query_wq);
- +
- +int qman_testwrite_cgr(struct qman_cgr *cgr, u64 i_bcnt,
- + struct qm_mcr_cgrtestwrite *result)
- +{
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + struct qman_portal *p = get_affine_portal();
- + unsigned long irqflags __maybe_unused;
- + u8 res;
- +
- + PORTAL_IRQ_LOCK(p, irqflags);
- + mcc = qm_mc_start(&p->p);
- + mcc->cgrtestwrite.cgid = cgr->cgrid;
- + mcc->cgrtestwrite.i_bcnt_hi = (u8)(i_bcnt >> 32);
- + mcc->cgrtestwrite.i_bcnt_lo = (u32)i_bcnt;
- + qm_mc_commit(&p->p, QM_MCC_VERB_CGRTESTWRITE);
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCC_VERB_CGRTESTWRITE);
- + res = mcr->result;
- + if (res == QM_MCR_RESULT_OK)
- + memcpy_fromio(result, &mcr->cgrtestwrite, sizeof(*result));
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- + if (res != QM_MCR_RESULT_OK) {
- + pr_err("CGR TEST WRITE failed: %s\n", mcr_result_str(res));
- + return -EIO;
- + }
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_testwrite_cgr);
- +
- +int qman_query_cgr(struct qman_cgr *cgr, struct qm_mcr_querycgr *cgrd)
- +{
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + struct qman_portal *p = get_affine_portal();
- + unsigned long irqflags __maybe_unused;
- + u8 res;
- + int i;
- +
- + PORTAL_IRQ_LOCK(p, irqflags);
- + mcc = qm_mc_start(&p->p);
- + mcc->querycgr.cgid = cgr->cgrid;
- + qm_mc_commit(&p->p, QM_MCC_VERB_QUERYCGR);
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCC_VERB_QUERYCGR);
- + res = mcr->result;
- + if (res == QM_MCR_RESULT_OK)
- + memcpy_fromio(cgrd, &mcr->querycgr, sizeof(*cgrd));
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- + if (res != QM_MCR_RESULT_OK) {
- + pr_err("QUERY_CGR failed: %s\n", mcr_result_str(res));
- + return -EIO;
- + }
- + cgrd->cgr.wr_parm_g.word =
- + be32_to_cpu(cgrd->cgr.wr_parm_g.word);
- + cgrd->cgr.wr_parm_y.word =
- + be32_to_cpu(cgrd->cgr.wr_parm_y.word);
- + cgrd->cgr.wr_parm_r.word =
- + be32_to_cpu(cgrd->cgr.wr_parm_r.word);
- + cgrd->cgr.cscn_targ = be32_to_cpu(cgrd->cgr.cscn_targ);
- + cgrd->cgr.__cs_thres = be16_to_cpu(cgrd->cgr.__cs_thres);
- + for (i = 0; i < ARRAY_SIZE(cgrd->cscn_targ_swp); i++)
- + be32_to_cpus(&cgrd->cscn_targ_swp[i]);
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_query_cgr);
- +
- +int qman_query_congestion(struct qm_mcr_querycongestion *congestion)
- +{
- + struct qm_mc_result *mcr;
- + struct qman_portal *p = get_affine_portal();
- + unsigned long irqflags __maybe_unused;
- + u8 res;
- + int i;
- +
- + PORTAL_IRQ_LOCK(p, irqflags);
- + qm_mc_start(&p->p);
- + qm_mc_commit(&p->p, QM_MCC_VERB_QUERYCONGESTION);
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) ==
- + QM_MCC_VERB_QUERYCONGESTION);
- + res = mcr->result;
- + if (res == QM_MCR_RESULT_OK)
- + memcpy_fromio(congestion, &mcr->querycongestion,
- + sizeof(*congestion));
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- + if (res != QM_MCR_RESULT_OK) {
- + pr_err("QUERY_CONGESTION failed: %s\n", mcr_result_str(res));
- + return -EIO;
- + }
- +
- + for (i = 0; i < ARRAY_SIZE(congestion->state.__state); i++)
- + be32_to_cpus(&congestion->state.__state[i]);
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_query_congestion);
- +
- +/* internal function used as a wait_event() expression */
- +static int set_p_vdqcr(struct qman_portal *p, struct qman_fq *fq, u32 vdqcr)
- +{
- + unsigned long irqflags __maybe_unused;
- + int ret = -EBUSY;
- + PORTAL_IRQ_LOCK(p, irqflags);
- + if (!p->vdqcr_owned) {
- + FQLOCK(fq);
- + if (fq_isset(fq, QMAN_FQ_STATE_VDQCR))
- + goto escape;
- + fq_set(fq, QMAN_FQ_STATE_VDQCR);
- + FQUNLOCK(fq);
- + p->vdqcr_owned = fq;
- + ret = 0;
- + }
- +escape:
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + if (!ret)
- + qm_dqrr_vdqcr_set(&p->p, vdqcr);
- + return ret;
- +}
- +
- +static int set_vdqcr(struct qman_portal **p, struct qman_fq *fq, u32 vdqcr)
- +{
- + int ret;
- + *p = get_affine_portal();
- + ret = set_p_vdqcr(*p, fq, vdqcr);
- + put_affine_portal();
- + return ret;
- +}
- +
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT
- +static int wait_p_vdqcr_start(struct qman_portal *p, struct qman_fq *fq,
- + u32 vdqcr, u32 flags)
- +{
- + int ret = 0;
- + if (flags & QMAN_VOLATILE_FLAG_WAIT_INT)
- + ret = wait_event_interruptible(affine_queue,
- + !(ret = set_p_vdqcr(p, fq, vdqcr)));
- + else
- + wait_event(affine_queue, !(ret = set_p_vdqcr(p, fq, vdqcr)));
- + return ret;
- +}
- +
- +static int wait_vdqcr_start(struct qman_portal **p, struct qman_fq *fq,
- + u32 vdqcr, u32 flags)
- +{
- + int ret = 0;
- + if (flags & QMAN_VOLATILE_FLAG_WAIT_INT)
- + ret = wait_event_interruptible(affine_queue,
- + !(ret = set_vdqcr(p, fq, vdqcr)));
- + else
- + wait_event(affine_queue, !(ret = set_vdqcr(p, fq, vdqcr)));
- + return ret;
- +}
- +#endif
- +
- +int qman_p_volatile_dequeue(struct qman_portal *p, struct qman_fq *fq,
- + u32 flags __maybe_unused, u32 vdqcr)
- +{
- + int ret;
- +
- + if ((fq->state != qman_fq_state_parked) &&
- + (fq->state != qman_fq_state_retired))
- + return -EINVAL;
- + if (vdqcr & QM_VDQCR_FQID_MASK)
- + return -EINVAL;
- + if (fq_isset(fq, QMAN_FQ_STATE_VDQCR))
- + return -EBUSY;
- + vdqcr = (vdqcr & ~QM_VDQCR_FQID_MASK) | fq->fqid;
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT
- + if (flags & QMAN_VOLATILE_FLAG_WAIT)
- + ret = wait_p_vdqcr_start(p, fq, vdqcr, flags);
- + else
- +#endif
- + ret = set_p_vdqcr(p, fq, vdqcr);
- + if (ret)
- + return ret;
- + /* VDQCR is set */
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT
- + if (flags & QMAN_VOLATILE_FLAG_FINISH) {
- + if (flags & QMAN_VOLATILE_FLAG_WAIT_INT)
- + /* NB: don't propagate any error - the caller wouldn't
- + * know whether the VDQCR was issued or not. A signal
- + * could arrive after returning anyway, so the caller
- + * can check signal_pending() if that's an issue. */
- + wait_event_interruptible(affine_queue,
- + !fq_isset(fq, QMAN_FQ_STATE_VDQCR));
- + else
- + wait_event(affine_queue,
- + !fq_isset(fq, QMAN_FQ_STATE_VDQCR));
- + }
- +#endif
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_p_volatile_dequeue);
- +
- +int qman_volatile_dequeue(struct qman_fq *fq, u32 flags __maybe_unused,
- + u32 vdqcr)
- +{
- + struct qman_portal *p;
- + int ret;
- +
- + if ((fq->state != qman_fq_state_parked) &&
- + (fq->state != qman_fq_state_retired))
- + return -EINVAL;
- + if (vdqcr & QM_VDQCR_FQID_MASK)
- + return -EINVAL;
- + if (fq_isset(fq, QMAN_FQ_STATE_VDQCR))
- + return -EBUSY;
- + vdqcr = (vdqcr & ~QM_VDQCR_FQID_MASK) | fq->fqid;
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT
- + if (flags & QMAN_VOLATILE_FLAG_WAIT)
- + ret = wait_vdqcr_start(&p, fq, vdqcr, flags);
- + else
- +#endif
- + ret = set_vdqcr(&p, fq, vdqcr);
- + if (ret)
- + return ret;
- + /* VDQCR is set */
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT
- + if (flags & QMAN_VOLATILE_FLAG_FINISH) {
- + if (flags & QMAN_VOLATILE_FLAG_WAIT_INT)
- + /* NB: don't propagate any error - the caller wouldn't
- + * know whether the VDQCR was issued or not. A signal
- + * could arrive after returning anyway, so the caller
- + * can check signal_pending() if that's an issue. */
- + wait_event_interruptible(affine_queue,
- + !fq_isset(fq, QMAN_FQ_STATE_VDQCR));
- + else
- + wait_event(affine_queue,
- + !fq_isset(fq, QMAN_FQ_STATE_VDQCR));
- + }
- +#endif
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_volatile_dequeue);
- +
- +static noinline void update_eqcr_ci(struct qman_portal *p, u8 avail)
- +{
- + if (avail)
- + qm_eqcr_cce_prefetch(&p->p);
- + else
- + qm_eqcr_cce_update(&p->p);
- +}
- +
- +int qman_eqcr_is_empty(void)
- +{
- + unsigned long irqflags __maybe_unused;
- + struct qman_portal *p = get_affine_portal();
- + u8 avail;
- +
- + PORTAL_IRQ_LOCK(p, irqflags);
- + update_eqcr_ci(p, 0);
- + avail = qm_eqcr_get_fill(&p->p);
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- + return avail == 0;
- +}
- +EXPORT_SYMBOL(qman_eqcr_is_empty);
- +
- +void qman_set_dc_ern(qman_cb_dc_ern handler, int affine)
- +{
- + if (affine) {
- + unsigned long irqflags __maybe_unused;
- + struct qman_portal *p = get_affine_portal();
- + PORTAL_IRQ_LOCK(p, irqflags);
- + p->cb_dc_ern = handler;
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- + } else
- + cb_dc_ern = handler;
- +}
- +EXPORT_SYMBOL(qman_set_dc_ern);
- +
- +static inline struct qm_eqcr_entry *try_p_eq_start(struct qman_portal *p,
- + unsigned long *irqflags __maybe_unused,
- + struct qman_fq *fq,
- + const struct qm_fd *fd,
- + u32 flags)
- +{
- + struct qm_eqcr_entry *eq;
- + u8 avail;
- + PORTAL_IRQ_LOCK(p, (*irqflags));
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC
- + if (unlikely((flags & QMAN_ENQUEUE_FLAG_WAIT) &&
- + (flags & QMAN_ENQUEUE_FLAG_WAIT_SYNC))) {
- + if (p->eqci_owned) {
- + PORTAL_IRQ_UNLOCK(p, (*irqflags));
- + return NULL;
- + }
- + p->eqci_owned = fq;
- + }
- +#endif
- + if (p->use_eqcr_ci_stashing) {
- + /*
- + * The stashing case is easy, only update if we need to in
- + * order to try and liberate ring entries.
- + */
- + eq = qm_eqcr_start_stash(&p->p);
- + } else {
- + /*
- + * The non-stashing case is harder, need to prefetch ahead of
- + * time.
- + */
- + avail = qm_eqcr_get_avail(&p->p);
- + if (avail < 2)
- + update_eqcr_ci(p, avail);
- + eq = qm_eqcr_start_no_stash(&p->p);
- + }
- +
- + if (unlikely(!eq)) {
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC
- + if (unlikely((flags & QMAN_ENQUEUE_FLAG_WAIT) &&
- + (flags & QMAN_ENQUEUE_FLAG_WAIT_SYNC)))
- + p->eqci_owned = NULL;
- +#endif
- + PORTAL_IRQ_UNLOCK(p, (*irqflags));
- + return NULL;
- + }
- + if (flags & QMAN_ENQUEUE_FLAG_DCA)
- + eq->dca = QM_EQCR_DCA_ENABLE |
- + ((flags & QMAN_ENQUEUE_FLAG_DCA_PARK) ?
- + QM_EQCR_DCA_PARK : 0) |
- + ((flags >> 8) & QM_EQCR_DCA_IDXMASK);
- + eq->fqid = cpu_to_be32(fq->fqid);
- +#ifdef CONFIG_FSL_QMAN_FQ_LOOKUP
- + eq->tag = cpu_to_be32(fq->key);
- +#else
- + eq->tag = cpu_to_be32((u32)(uintptr_t)fq);
- +#endif
- + eq->fd = *fd;
- + cpu_to_hw_fd(&eq->fd);
- + return eq;
- +}
- +
- +static inline struct qm_eqcr_entry *try_eq_start(struct qman_portal **p,
- + unsigned long *irqflags __maybe_unused,
- + struct qman_fq *fq,
- + const struct qm_fd *fd,
- + u32 flags)
- +{
- + struct qm_eqcr_entry *eq;
- + *p = get_affine_portal();
- + eq = try_p_eq_start(*p, irqflags, fq, fd, flags);
- + if (!eq)
- + put_affine_portal();
- + return eq;
- +}
- +
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT
- +static noinline struct qm_eqcr_entry *__wait_eq_start(struct qman_portal **p,
- + unsigned long *irqflags __maybe_unused,
- + struct qman_fq *fq,
- + const struct qm_fd *fd,
- + u32 flags)
- +{
- + struct qm_eqcr_entry *eq = try_eq_start(p, irqflags, fq, fd, flags);
- + if (!eq)
- + qm_eqcr_set_ithresh(&(*p)->p, EQCR_ITHRESH);
- + return eq;
- +}
- +static noinline struct qm_eqcr_entry *wait_eq_start(struct qman_portal **p,
- + unsigned long *irqflags __maybe_unused,
- + struct qman_fq *fq,
- + const struct qm_fd *fd,
- + u32 flags)
- +{
- + struct qm_eqcr_entry *eq;
- + if (flags & QMAN_ENQUEUE_FLAG_WAIT_INT)
- + /* NB: return NULL if signal occurs before completion. Signal
- + * can occur during return. Caller must check for signal */
- + wait_event_interruptible(affine_queue,
- + (eq = __wait_eq_start(p, irqflags, fq, fd, flags)));
- + else
- + wait_event(affine_queue,
- + (eq = __wait_eq_start(p, irqflags, fq, fd, flags)));
- + return eq;
- +}
- +static noinline struct qm_eqcr_entry *__wait_p_eq_start(struct qman_portal *p,
- + unsigned long *irqflags __maybe_unused,
- + struct qman_fq *fq,
- + const struct qm_fd *fd,
- + u32 flags)
- +{
- + struct qm_eqcr_entry *eq = try_p_eq_start(p, irqflags, fq, fd, flags);
- + if (!eq)
- + qm_eqcr_set_ithresh(&p->p, EQCR_ITHRESH);
- + return eq;
- +}
- +static noinline struct qm_eqcr_entry *wait_p_eq_start(struct qman_portal *p,
- + unsigned long *irqflags __maybe_unused,
- + struct qman_fq *fq,
- + const struct qm_fd *fd,
- + u32 flags)
- +{
- + struct qm_eqcr_entry *eq;
- + if (flags & QMAN_ENQUEUE_FLAG_WAIT_INT)
- + /* NB: return NULL if signal occurs before completion. Signal
- + * can occur during return. Caller must check for signal */
- + wait_event_interruptible(affine_queue,
- + (eq = __wait_p_eq_start(p, irqflags, fq, fd, flags)));
- + else
- + wait_event(affine_queue,
- + (eq = __wait_p_eq_start(p, irqflags, fq, fd, flags)));
- + return eq;
- +}
- +#endif
- +
- +int qman_p_enqueue(struct qman_portal *p, struct qman_fq *fq,
- + const struct qm_fd *fd, u32 flags)
- +{
- + struct qm_eqcr_entry *eq;
- + unsigned long irqflags __maybe_unused;
- +
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT
- + if (flags & QMAN_ENQUEUE_FLAG_WAIT)
- + eq = wait_p_eq_start(p, &irqflags, fq, fd, flags);
- + else
- +#endif
- + eq = try_p_eq_start(p, &irqflags, fq, fd, flags);
- + if (!eq)
- + return -EBUSY;
- + /* Note: QM_EQCR_VERB_INTERRUPT == QMAN_ENQUEUE_FLAG_WAIT_SYNC */
- + qm_eqcr_pvb_commit(&p->p, QM_EQCR_VERB_CMD_ENQUEUE |
- + (flags & (QM_EQCR_VERB_COLOUR_MASK | QM_EQCR_VERB_INTERRUPT)));
- + /* Factor the below out, it's used from qman_enqueue_orp() too */
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC
- + if (unlikely((flags & QMAN_ENQUEUE_FLAG_WAIT) &&
- + (flags & QMAN_ENQUEUE_FLAG_WAIT_SYNC))) {
- + if (flags & QMAN_ENQUEUE_FLAG_WAIT_INT)
- + /* NB: return success even if signal occurs before
- + * condition is true. pvb_commit guarantees success */
- + wait_event_interruptible(affine_queue,
- + (p->eqci_owned != fq));
- + else
- + wait_event(affine_queue, (p->eqci_owned != fq));
- + }
- +#endif
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_p_enqueue);
- +
- +int qman_enqueue(struct qman_fq *fq, const struct qm_fd *fd, u32 flags)
- +{
- + struct qman_portal *p;
- + struct qm_eqcr_entry *eq;
- + unsigned long irqflags __maybe_unused;
- +
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT
- + if (flags & QMAN_ENQUEUE_FLAG_WAIT)
- + eq = wait_eq_start(&p, &irqflags, fq, fd, flags);
- + else
- +#endif
- + eq = try_eq_start(&p, &irqflags, fq, fd, flags);
- + if (!eq)
- + return -EBUSY;
- + /* Note: QM_EQCR_VERB_INTERRUPT == QMAN_ENQUEUE_FLAG_WAIT_SYNC */
- + qm_eqcr_pvb_commit(&p->p, QM_EQCR_VERB_CMD_ENQUEUE |
- + (flags & (QM_EQCR_VERB_COLOUR_MASK | QM_EQCR_VERB_INTERRUPT)));
- + /* Factor the below out, it's used from qman_enqueue_orp() too */
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC
- + if (unlikely((flags & QMAN_ENQUEUE_FLAG_WAIT) &&
- + (flags & QMAN_ENQUEUE_FLAG_WAIT_SYNC))) {
- + if (flags & QMAN_ENQUEUE_FLAG_WAIT_INT)
- + /* NB: return success even if signal occurs before
- + * condition is true. pvb_commit guarantees success */
- + wait_event_interruptible(affine_queue,
- + (p->eqci_owned != fq));
- + else
- + wait_event(affine_queue, (p->eqci_owned != fq));
- + }
- +#endif
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_enqueue);
- +
- +int qman_p_enqueue_orp(struct qman_portal *p, struct qman_fq *fq,
- + const struct qm_fd *fd, u32 flags,
- + struct qman_fq *orp, u16 orp_seqnum)
- +{
- + struct qm_eqcr_entry *eq;
- + unsigned long irqflags __maybe_unused;
- +
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT
- + if (flags & QMAN_ENQUEUE_FLAG_WAIT)
- + eq = wait_p_eq_start(p, &irqflags, fq, fd, flags);
- + else
- +#endif
- + eq = try_p_eq_start(p, &irqflags, fq, fd, flags);
- + if (!eq)
- + return -EBUSY;
- + /* Process ORP-specifics here */
- + if (flags & QMAN_ENQUEUE_FLAG_NLIS)
- + orp_seqnum |= QM_EQCR_SEQNUM_NLIS;
- + else {
- + orp_seqnum &= ~QM_EQCR_SEQNUM_NLIS;
- + if (flags & QMAN_ENQUEUE_FLAG_NESN)
- + orp_seqnum |= QM_EQCR_SEQNUM_NESN;
- + else
- + /* No need to check 4 QMAN_ENQUEUE_FLAG_HOLE */
- + orp_seqnum &= ~QM_EQCR_SEQNUM_NESN;
- + }
- + eq->seqnum = cpu_to_be16(orp_seqnum);
- + eq->orp = cpu_to_be32(orp->fqid);
- + /* Note: QM_EQCR_VERB_INTERRUPT == QMAN_ENQUEUE_FLAG_WAIT_SYNC */
- + qm_eqcr_pvb_commit(&p->p, QM_EQCR_VERB_ORP |
- + ((flags & (QMAN_ENQUEUE_FLAG_HOLE | QMAN_ENQUEUE_FLAG_NESN)) ?
- + 0 : QM_EQCR_VERB_CMD_ENQUEUE) |
- + (flags & (QM_EQCR_VERB_COLOUR_MASK | QM_EQCR_VERB_INTERRUPT)));
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC
- + if (unlikely((flags & QMAN_ENQUEUE_FLAG_WAIT) &&
- + (flags & QMAN_ENQUEUE_FLAG_WAIT_SYNC))) {
- + if (flags & QMAN_ENQUEUE_FLAG_WAIT_INT)
- + /* NB: return success even if signal occurs before
- + * condition is true. pvb_commit guarantees success */
- + wait_event_interruptible(affine_queue,
- + (p->eqci_owned != fq));
- + else
- + wait_event(affine_queue, (p->eqci_owned != fq));
- + }
- +#endif
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_p_enqueue_orp);
- +
- +int qman_enqueue_orp(struct qman_fq *fq, const struct qm_fd *fd, u32 flags,
- + struct qman_fq *orp, u16 orp_seqnum)
- +{
- + struct qman_portal *p;
- + struct qm_eqcr_entry *eq;
- + unsigned long irqflags __maybe_unused;
- +
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT
- + if (flags & QMAN_ENQUEUE_FLAG_WAIT)
- + eq = wait_eq_start(&p, &irqflags, fq, fd, flags);
- + else
- +#endif
- + eq = try_eq_start(&p, &irqflags, fq, fd, flags);
- + if (!eq)
- + return -EBUSY;
- + /* Process ORP-specifics here */
- + if (flags & QMAN_ENQUEUE_FLAG_NLIS)
- + orp_seqnum |= QM_EQCR_SEQNUM_NLIS;
- + else {
- + orp_seqnum &= ~QM_EQCR_SEQNUM_NLIS;
- + if (flags & QMAN_ENQUEUE_FLAG_NESN)
- + orp_seqnum |= QM_EQCR_SEQNUM_NESN;
- + else
- + /* No need to check 4 QMAN_ENQUEUE_FLAG_HOLE */
- + orp_seqnum &= ~QM_EQCR_SEQNUM_NESN;
- + }
- + eq->seqnum = cpu_to_be16(orp_seqnum);
- + eq->orp = cpu_to_be32(orp->fqid);
- + /* Note: QM_EQCR_VERB_INTERRUPT == QMAN_ENQUEUE_FLAG_WAIT_SYNC */
- + qm_eqcr_pvb_commit(&p->p, QM_EQCR_VERB_ORP |
- + ((flags & (QMAN_ENQUEUE_FLAG_HOLE | QMAN_ENQUEUE_FLAG_NESN)) ?
- + 0 : QM_EQCR_VERB_CMD_ENQUEUE) |
- + (flags & (QM_EQCR_VERB_COLOUR_MASK | QM_EQCR_VERB_INTERRUPT)));
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC
- + if (unlikely((flags & QMAN_ENQUEUE_FLAG_WAIT) &&
- + (flags & QMAN_ENQUEUE_FLAG_WAIT_SYNC))) {
- + if (flags & QMAN_ENQUEUE_FLAG_WAIT_INT)
- + /* NB: return success even if signal occurs before
- + * condition is true. pvb_commit guarantees success */
- + wait_event_interruptible(affine_queue,
- + (p->eqci_owned != fq));
- + else
- + wait_event(affine_queue, (p->eqci_owned != fq));
- + }
- +#endif
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_enqueue_orp);
- +
- +int qman_p_enqueue_precommit(struct qman_portal *p, struct qman_fq *fq,
- + const struct qm_fd *fd, u32 flags,
- + qman_cb_precommit cb, void *cb_arg)
- +{
- + struct qm_eqcr_entry *eq;
- + unsigned long irqflags __maybe_unused;
- +
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT
- + if (flags & QMAN_ENQUEUE_FLAG_WAIT)
- + eq = wait_p_eq_start(p, &irqflags, fq, fd, flags);
- + else
- +#endif
- + eq = try_p_eq_start(p, &irqflags, fq, fd, flags);
- + if (!eq)
- + return -EBUSY;
- + /* invoke user supplied callback function before writing commit verb */
- + if (cb(cb_arg)) {
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + return -EINVAL;
- + }
- + /* Note: QM_EQCR_VERB_INTERRUPT == QMAN_ENQUEUE_FLAG_WAIT_SYNC */
- + qm_eqcr_pvb_commit(&p->p, QM_EQCR_VERB_CMD_ENQUEUE |
- + (flags & (QM_EQCR_VERB_COLOUR_MASK | QM_EQCR_VERB_INTERRUPT)));
- + /* Factor the below out, it's used from qman_enqueue_orp() too */
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC
- + if (unlikely((flags & QMAN_ENQUEUE_FLAG_WAIT) &&
- + (flags & QMAN_ENQUEUE_FLAG_WAIT_SYNC))) {
- + if (flags & QMAN_ENQUEUE_FLAG_WAIT_INT)
- + /* NB: return success even if signal occurs before
- + * condition is true. pvb_commit guarantees success */
- + wait_event_interruptible(affine_queue,
- + (p->eqci_owned != fq));
- + else
- + wait_event(affine_queue, (p->eqci_owned != fq));
- + }
- +#endif
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_p_enqueue_precommit);
- +
- +int qman_enqueue_precommit(struct qman_fq *fq, const struct qm_fd *fd,
- + u32 flags, qman_cb_precommit cb, void *cb_arg)
- +{
- + struct qman_portal *p;
- + struct qm_eqcr_entry *eq;
- + unsigned long irqflags __maybe_unused;
- +
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT
- + if (flags & QMAN_ENQUEUE_FLAG_WAIT)
- + eq = wait_eq_start(&p, &irqflags, fq, fd, flags);
- + else
- +#endif
- + eq = try_eq_start(&p, &irqflags, fq, fd, flags);
- + if (!eq)
- + return -EBUSY;
- + /* invoke user supplied callback function before writing commit verb */
- + if (cb(cb_arg)) {
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- + return -EINVAL;
- + }
- + /* Note: QM_EQCR_VERB_INTERRUPT == QMAN_ENQUEUE_FLAG_WAIT_SYNC */
- + qm_eqcr_pvb_commit(&p->p, QM_EQCR_VERB_CMD_ENQUEUE |
- + (flags & (QM_EQCR_VERB_COLOUR_MASK | QM_EQCR_VERB_INTERRUPT)));
- + /* Factor the below out, it's used from qman_enqueue_orp() too */
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC
- + if (unlikely((flags & QMAN_ENQUEUE_FLAG_WAIT) &&
- + (flags & QMAN_ENQUEUE_FLAG_WAIT_SYNC))) {
- + if (flags & QMAN_ENQUEUE_FLAG_WAIT_INT)
- + /* NB: return success even if signal occurs before
- + * condition is true. pvb_commit guarantees success */
- + wait_event_interruptible(affine_queue,
- + (p->eqci_owned != fq));
- + else
- + wait_event(affine_queue, (p->eqci_owned != fq));
- + }
- +#endif
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_enqueue_precommit);
- +
- +int qman_modify_cgr(struct qman_cgr *cgr, u32 flags,
- + struct qm_mcc_initcgr *opts)
- +{
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + struct qman_portal *p = get_affine_portal();
- + unsigned long irqflags __maybe_unused;
- + u8 res;
- + u8 verb = QM_MCC_VERB_MODIFYCGR;
- +
- + PORTAL_IRQ_LOCK(p, irqflags);
- + mcc = qm_mc_start(&p->p);
- + if (opts)
- + mcc->initcgr = *opts;
- + mcc->initcgr.we_mask = cpu_to_be16(mcc->initcgr.we_mask);
- + mcc->initcgr.cgr.wr_parm_g.word =
- + cpu_to_be32(mcc->initcgr.cgr.wr_parm_g.word);
- + mcc->initcgr.cgr.wr_parm_y.word =
- + cpu_to_be32(mcc->initcgr.cgr.wr_parm_y.word);
- + mcc->initcgr.cgr.wr_parm_r.word =
- + cpu_to_be32(mcc->initcgr.cgr.wr_parm_r.word);
- + mcc->initcgr.cgr.cscn_targ = cpu_to_be32(mcc->initcgr.cgr.cscn_targ);
- + mcc->initcgr.cgr.__cs_thres = cpu_to_be16(mcc->initcgr.cgr.__cs_thres);
- +
- + mcc->initcgr.cgid = cgr->cgrid;
- + if (flags & QMAN_CGR_FLAG_USE_INIT)
- + verb = QM_MCC_VERB_INITCGR;
- + qm_mc_commit(&p->p, verb);
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == verb);
- + res = mcr->result;
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- + return (res == QM_MCR_RESULT_OK) ? 0 : -EIO;
- +}
- +EXPORT_SYMBOL(qman_modify_cgr);
- +
- +#define TARG_MASK(n) (0x80000000 >> (n->config->public_cfg.channel - \
- + QM_CHANNEL_SWPORTAL0))
- +#define TARG_DCP_MASK(n) (0x80000000 >> (10 + n))
- +#define PORTAL_IDX(n) (n->config->public_cfg.channel - QM_CHANNEL_SWPORTAL0)
- +
- +static u8 qman_cgr_cpus[__CGR_NUM];
- +
- +int qman_create_cgr(struct qman_cgr *cgr, u32 flags,
- + struct qm_mcc_initcgr *opts)
- +{
- + unsigned long irqflags __maybe_unused;
- + struct qm_mcr_querycgr cgr_state;
- + struct qm_mcc_initcgr local_opts;
- + int ret;
- + struct qman_portal *p;
- +
- + /* We have to check that the provided CGRID is within the limits of the
- + * data-structures, for obvious reasons. However we'll let h/w take
- + * care of determining whether it's within the limits of what exists on
- + * the SoC. */
- + if (cgr->cgrid >= __CGR_NUM)
- + return -EINVAL;
- +
- + preempt_disable();
- + p = get_affine_portal();
- + qman_cgr_cpus[cgr->cgrid] = smp_processor_id();
- + preempt_enable();
- +
- + memset(&local_opts, 0, sizeof(struct qm_mcc_initcgr));
- + cgr->chan = p->config->public_cfg.channel;
- + spin_lock_irqsave(&p->cgr_lock, irqflags);
- +
- + /* if no opts specified, just add it to the list */
- + if (!opts)
- + goto add_list;
- +
- + ret = qman_query_cgr(cgr, &cgr_state);
- + if (ret)
- + goto release_lock;
- + if (opts)
- + local_opts = *opts;
- + if ((qman_ip_rev & 0xFF00) >= QMAN_REV30)
- + local_opts.cgr.cscn_targ_upd_ctrl =
- + QM_CGR_TARG_UDP_CTRL_WRITE_BIT | PORTAL_IDX(p);
- + else
- + /* Overwrite TARG */
- + local_opts.cgr.cscn_targ = cgr_state.cgr.cscn_targ |
- + TARG_MASK(p);
- + local_opts.we_mask |= QM_CGR_WE_CSCN_TARG;
- +
- + /* send init if flags indicate so */
- + if (opts && (flags & QMAN_CGR_FLAG_USE_INIT))
- + ret = qman_modify_cgr(cgr, QMAN_CGR_FLAG_USE_INIT, &local_opts);
- + else
- + ret = qman_modify_cgr(cgr, 0, &local_opts);
- + if (ret)
- + goto release_lock;
- +add_list:
- + list_add(&cgr->node, &p->cgr_cbs);
- +
- + /* Determine if newly added object requires its callback to be called */
- + ret = qman_query_cgr(cgr, &cgr_state);
- + if (ret) {
- + /* we can't go back, so proceed and return success, but screen
- + * and wail to the log file */
- + pr_crit("CGR HW state partially modified\n");
- + ret = 0;
- + goto release_lock;
- + }
- + if (cgr->cb && cgr_state.cgr.cscn_en && qman_cgrs_get(&p->cgrs[1],
- + cgr->cgrid))
- + cgr->cb(p, cgr, 1);
- +release_lock:
- + spin_unlock_irqrestore(&p->cgr_lock, irqflags);
- + put_affine_portal();
- + return ret;
- +}
- +EXPORT_SYMBOL(qman_create_cgr);
- +
- +int qman_create_cgr_to_dcp(struct qman_cgr *cgr, u32 flags, u16 dcp_portal,
- + struct qm_mcc_initcgr *opts)
- +{
- + unsigned long irqflags __maybe_unused;
- + struct qm_mcc_initcgr local_opts;
- + struct qm_mcr_querycgr cgr_state;
- + int ret;
- +
- + /* We have to check that the provided CGRID is within the limits of the
- + * data-structures, for obvious reasons. However we'll let h/w take
- + * care of determining whether it's within the limits of what exists on
- + * the SoC.
- + */
- + if (cgr->cgrid >= __CGR_NUM)
- + return -EINVAL;
- +
- + ret = qman_query_cgr(cgr, &cgr_state);
- + if (ret)
- + return ret;
- +
- + memset(&local_opts, 0, sizeof(struct qm_mcc_initcgr));
- + if (opts)
- + local_opts = *opts;
- +
- + if ((qman_ip_rev & 0xFF00) >= QMAN_REV30)
- + local_opts.cgr.cscn_targ_upd_ctrl =
- + QM_CGR_TARG_UDP_CTRL_WRITE_BIT |
- + QM_CGR_TARG_UDP_CTRL_DCP | dcp_portal;
- + else
- + local_opts.cgr.cscn_targ = cgr_state.cgr.cscn_targ |
- + TARG_DCP_MASK(dcp_portal);
- + local_opts.we_mask |= QM_CGR_WE_CSCN_TARG;
- +
- + /* send init if flags indicate so */
- + if (opts && (flags & QMAN_CGR_FLAG_USE_INIT))
- + ret = qman_modify_cgr(cgr, QMAN_CGR_FLAG_USE_INIT,
- + &local_opts);
- + else
- + ret = qman_modify_cgr(cgr, 0, &local_opts);
- +
- + return ret;
- +}
- +EXPORT_SYMBOL(qman_create_cgr_to_dcp);
- +
- +int qman_delete_cgr(struct qman_cgr *cgr)
- +{
- + unsigned long irqflags __maybe_unused;
- + struct qm_mcr_querycgr cgr_state;
- + struct qm_mcc_initcgr local_opts;
- + int ret = 0;
- + struct qman_cgr *i;
- + struct qman_portal *p = get_affine_portal();
- +
- + if (cgr->chan != p->config->public_cfg.channel) {
- + pr_crit("Attempting to delete cgr from different portal "
- + "than it was create: create 0x%x, delete 0x%x\n",
- + cgr->chan, p->config->public_cfg.channel);
- + ret = -EINVAL;
- + goto put_portal;
- + }
- + memset(&local_opts, 0, sizeof(struct qm_mcc_initcgr));
- + spin_lock_irqsave(&p->cgr_lock, irqflags);
- + list_del(&cgr->node);
- + /*
- + * If there are no other CGR objects for this CGRID in the list, update
- + * CSCN_TARG accordingly
- + */
- + list_for_each_entry(i, &p->cgr_cbs, node)
- + if ((i->cgrid == cgr->cgrid) && i->cb)
- + goto release_lock;
- + ret = qman_query_cgr(cgr, &cgr_state);
- + if (ret) {
- + /* add back to the list */
- + list_add(&cgr->node, &p->cgr_cbs);
- + goto release_lock;
- + }
- + /* Overwrite TARG */
- + local_opts.we_mask = QM_CGR_WE_CSCN_TARG;
- + if ((qman_ip_rev & 0xFF00) >= QMAN_REV30)
- + local_opts.cgr.cscn_targ_upd_ctrl = PORTAL_IDX(p);
- + else
- + local_opts.cgr.cscn_targ = cgr_state.cgr.cscn_targ &
- + ~(TARG_MASK(p));
- + ret = qman_modify_cgr(cgr, 0, &local_opts);
- + if (ret)
- + /* add back to the list */
- + list_add(&cgr->node, &p->cgr_cbs);
- +release_lock:
- + spin_unlock_irqrestore(&p->cgr_lock, irqflags);
- +put_portal:
- + put_affine_portal();
- + return ret;
- +}
- +EXPORT_SYMBOL(qman_delete_cgr);
- +
- +struct cgr_comp {
- + struct qman_cgr *cgr;
- + struct completion completion;
- +};
- +
- +static int qman_delete_cgr_thread(void *p)
- +{
- + struct cgr_comp *cgr_comp = (struct cgr_comp *)p;
- + int res;
- +
- + res = qman_delete_cgr((struct qman_cgr *)cgr_comp->cgr);
- + complete(&cgr_comp->completion);
- +
- + return res;
- +}
- +
- +void qman_delete_cgr_safe(struct qman_cgr *cgr)
- +{
- + struct task_struct *thread;
- + struct cgr_comp cgr_comp;
- +
- + preempt_disable();
- + if (qman_cgr_cpus[cgr->cgrid] != smp_processor_id()) {
- + init_completion(&cgr_comp.completion);
- + cgr_comp.cgr = cgr;
- + thread = kthread_create(qman_delete_cgr_thread, &cgr_comp,
- + "cgr_del");
- +
- + if (likely(!IS_ERR(thread))) {
- + kthread_bind(thread, qman_cgr_cpus[cgr->cgrid]);
- + wake_up_process(thread);
- + wait_for_completion(&cgr_comp.completion);
- + preempt_enable();
- + return;
- + }
- + }
- + qman_delete_cgr(cgr);
- + preempt_enable();
- +}
- +EXPORT_SYMBOL(qman_delete_cgr_safe);
- +
- +int qm_get_clock(u64 *clock_hz)
- +{
- + if (!qman_clk) {
- + pr_warn("Qman clock speed is unknown\n");
- + return -EINVAL;
- + }
- + *clock_hz = (u64)qman_clk;
- + return 0;
- +}
- +EXPORT_SYMBOL(qm_get_clock);
- +
- +int qm_set_clock(u64 clock_hz)
- +{
- + if (qman_clk)
- + return -1;
- + qman_clk = (u32)clock_hz;
- + return 0;
- +}
- +EXPORT_SYMBOL(qm_set_clock);
- +
- +/* CEETM management command */
- +static int qman_ceetm_configure_lfqmt(struct qm_mcc_ceetm_lfqmt_config *opts)
- +{
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + struct qman_portal *p;
- + unsigned long irqflags __maybe_unused;
- + u8 res;
- +
- + p = get_affine_portal();
- + PORTAL_IRQ_LOCK(p, irqflags);
- +
- + mcc = qm_mc_start(&p->p);
- + mcc->lfqmt_config = *opts;
- + qm_mc_commit(&p->p, QM_CEETM_VERB_LFQMT_CONFIG);
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) ==
- + QM_CEETM_VERB_LFQMT_CONFIG);
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- +
- + res = mcr->result;
- + if (res != QM_MCR_RESULT_OK) {
- + pr_err("CEETM: CONFIGURE LFQMT failed\n");
- + return -EIO;
- + }
- + return 0;
- +}
- +
- +int qman_ceetm_query_lfqmt(int lfqid,
- + struct qm_mcr_ceetm_lfqmt_query *lfqmt_query)
- +{
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + struct qman_portal *p;
- + unsigned long irqflags __maybe_unused;
- + u8 res;
- +
- + p = get_affine_portal();
- + PORTAL_IRQ_LOCK(p, irqflags);
- +
- + mcc = qm_mc_start(&p->p);
- + mcc->lfqmt_query.lfqid = lfqid;
- + qm_mc_commit(&p->p, QM_CEETM_VERB_LFQMT_QUERY);
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_CEETM_VERB_LFQMT_QUERY);
- + res = mcr->result;
- + if (res == QM_MCR_RESULT_OK)
- + *lfqmt_query = mcr->lfqmt_query;
- +
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- + if (res != QM_MCR_RESULT_OK) {
- + pr_err("CEETM: QUERY LFQMT failed\n");
- + return -EIO;
- + }
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_query_lfqmt);
- +
- +static int qman_ceetm_configure_cq(struct qm_mcc_ceetm_cq_config *opts)
- +{
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + struct qman_portal *p;
- + unsigned long irqflags __maybe_unused;
- + u8 res;
- +
- + p = get_affine_portal();
- + PORTAL_IRQ_LOCK(p, irqflags);
- +
- + mcc = qm_mc_start(&p->p);
- + mcc->cq_config = *opts;
- + qm_mc_commit(&p->p, QM_CEETM_VERB_CQ_CONFIG);
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + res = mcr->result;
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_CEETM_VERB_CQ_CONFIG);
- +
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- +
- + if (res != QM_MCR_RESULT_OK) {
- + pr_err("CEETM: CONFIGURE CQ failed\n");
- + return -EIO;
- + }
- + return 0;
- +}
- +
- +int qman_ceetm_query_cq(unsigned int cqid, unsigned int dcpid,
- + struct qm_mcr_ceetm_cq_query *cq_query)
- +{
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + struct qman_portal *p;
- + unsigned long irqflags __maybe_unused;
- + u8 res;
- +
- + p = get_affine_portal();
- + PORTAL_IRQ_LOCK(p, irqflags);
- +
- + mcc = qm_mc_start(&p->p);
- + mcc->cq_query.cqid = cpu_to_be16(cqid);
- + mcc->cq_query.dcpid = dcpid;
- + qm_mc_commit(&p->p, QM_CEETM_VERB_CQ_QUERY);
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_CEETM_VERB_CQ_QUERY);
- + res = mcr->result;
- + if (res == QM_MCR_RESULT_OK) {
- + *cq_query = mcr->cq_query;
- + hw_cq_query_to_cpu(cq_query);
- + }
- +
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- +
- + if (res != QM_MCR_RESULT_OK) {
- + pr_err("CEETM: QUERY CQ failed\n");
- + return -EIO;
- + }
- +
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_query_cq);
- +
- +static int qman_ceetm_configure_dct(struct qm_mcc_ceetm_dct_config *opts)
- +{
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + struct qman_portal *p;
- + unsigned long irqflags __maybe_unused;
- + u8 res;
- +
- + p = get_affine_portal();
- + PORTAL_IRQ_LOCK(p, irqflags);
- +
- + mcc = qm_mc_start(&p->p);
- + mcc->dct_config = *opts;
- + qm_mc_commit(&p->p, QM_CEETM_VERB_DCT_CONFIG);
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_CEETM_VERB_DCT_CONFIG);
- + res = mcr->result;
- +
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- +
- + if (res != QM_MCR_RESULT_OK) {
- + pr_err("CEETM: CONFIGURE DCT failed\n");
- + return -EIO;
- + }
- + return 0;
- +}
- +
- +static int qman_ceetm_query_dct(struct qm_mcc_ceetm_dct_query *opts,
- + struct qm_mcr_ceetm_dct_query *dct_query)
- +{
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + struct qman_portal *p = get_affine_portal();
- + unsigned long irqflags __maybe_unused;
- + u8 res;
- +
- + PORTAL_IRQ_LOCK(p, irqflags);
- +
- + mcc = qm_mc_start(&p->p);
- + mcc->dct_query = *opts;
- + qm_mc_commit(&p->p, QM_CEETM_VERB_DCT_QUERY);
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_CEETM_VERB_DCT_QUERY);
- + res = mcr->result;
- +
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- +
- + if (res != QM_MCR_RESULT_OK) {
- + pr_err("CEETM: QUERY DCT failed\n");
- + return -EIO;
- + }
- +
- + *dct_query = mcr->dct_query;
- + return 0;
- +}
- +
- +static int qman_ceetm_configure_class_scheduler(
- + struct qm_mcc_ceetm_class_scheduler_config *opts)
- +{
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + struct qman_portal *p;
- + unsigned long irqflags __maybe_unused;
- + u8 res;
- +
- + p = get_affine_portal();
- + PORTAL_IRQ_LOCK(p, irqflags);
- +
- + mcc = qm_mc_start(&p->p);
- + mcc->csch_config = *opts;
- + qm_mc_commit(&p->p, QM_CEETM_VERB_CLASS_SCHEDULER_CONFIG);
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) ==
- + QM_CEETM_VERB_CLASS_SCHEDULER_CONFIG);
- + res = mcr->result;
- +
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- +
- + if (res != QM_MCR_RESULT_OK) {
- + pr_err("CEETM: CONFIGURE CLASS SCHEDULER failed\n");
- + return -EIO;
- + }
- + return 0;
- +}
- +
- +static int qman_ceetm_query_class_scheduler(struct qm_ceetm_channel *channel,
- + struct qm_mcr_ceetm_class_scheduler_query *query)
- +{
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + struct qman_portal *p;
- + unsigned long irqflags __maybe_unused;
- + u8 res;
- +
- + p = get_affine_portal();
- + PORTAL_IRQ_LOCK(p, irqflags);
- +
- + mcc = qm_mc_start(&p->p);
- + mcc->csch_query.cqcid = channel->idx;
- + mcc->csch_query.dcpid = channel->dcp_idx;
- + qm_mc_commit(&p->p, QM_CEETM_VERB_CLASS_SCHEDULER_QUERY);
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) ==
- + QM_CEETM_VERB_CLASS_SCHEDULER_QUERY);
- + res = mcr->result;
- +
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- +
- + if (res != QM_MCR_RESULT_OK) {
- + pr_err("CEETM: QUERY CLASS SCHEDULER failed\n");
- + return -EIO;
- + }
- + *query = mcr->csch_query;
- + return 0;
- +}
- +
- +static int qman_ceetm_configure_mapping_shaper_tcfc(
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_config *opts)
- +{
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + struct qman_portal *p;
- + unsigned long irqflags __maybe_unused;
- + u8 res;
- +
- + p = get_affine_portal();
- + PORTAL_IRQ_LOCK(p, irqflags);
- +
- + mcc = qm_mc_start(&p->p);
- + mcc->mst_config = *opts;
- + qm_mc_commit(&p->p, QM_CEETM_VERB_MAPPING_SHAPER_TCFC_CONFIG);
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) ==
- + QM_CEETM_VERB_MAPPING_SHAPER_TCFC_CONFIG);
- + res = mcr->result;
- +
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- +
- + if (res != QM_MCR_RESULT_OK) {
- + pr_err("CEETM: CONFIGURE CHANNEL MAPPING failed\n");
- + return -EIO;
- + }
- + return 0;
- +}
- +
- +static int qman_ceetm_query_mapping_shaper_tcfc(
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_query *opts,
- + struct qm_mcr_ceetm_mapping_shaper_tcfc_query *response)
- +{
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + struct qman_portal *p;
- + unsigned long irqflags __maybe_unused;
- + u8 res;
- +
- + p = get_affine_portal();
- + PORTAL_IRQ_LOCK(p, irqflags);
- +
- + mcc = qm_mc_start(&p->p);
- + mcc->mst_query = *opts;
- + qm_mc_commit(&p->p, QM_CEETM_VERB_MAPPING_SHAPER_TCFC_QUERY);
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) ==
- + QM_CEETM_VERB_MAPPING_SHAPER_TCFC_QUERY);
- + res = mcr->result;
- +
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- +
- + if (res != QM_MCR_RESULT_OK) {
- + pr_err("CEETM: QUERY CHANNEL MAPPING failed\n");
- + return -EIO;
- + }
- +
- + *response = mcr->mst_query;
- + return 0;
- +}
- +
- +static int qman_ceetm_configure_ccgr(struct qm_mcc_ceetm_ccgr_config *opts)
- +{
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + struct qman_portal *p;
- + unsigned long irqflags __maybe_unused;
- + u8 res;
- +
- + p = get_affine_portal();
- + PORTAL_IRQ_LOCK(p, irqflags);
- +
- + mcc = qm_mc_start(&p->p);
- + mcc->ccgr_config = *opts;
- +
- + qm_mc_commit(&p->p, QM_CEETM_VERB_CCGR_CONFIG);
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_CEETM_VERB_CCGR_CONFIG);
- +
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- +
- + res = mcr->result;
- + if (res != QM_MCR_RESULT_OK) {
- + pr_err("CEETM: CONFIGURE CCGR failed\n");
- + return -EIO;
- + }
- + return 0;
- +}
- +
- +int qman_ceetm_query_ccgr(struct qm_mcc_ceetm_ccgr_query *ccgr_query,
- + struct qm_mcr_ceetm_ccgr_query *response)
- +{
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + struct qman_portal *p;
- + unsigned long irqflags __maybe_unused;
- + u8 res;
- +
- + p = get_affine_portal();
- + PORTAL_IRQ_LOCK(p, irqflags);
- +
- + mcc = qm_mc_start(&p->p);
- + mcc->ccgr_query.ccgrid = cpu_to_be16(ccgr_query->ccgrid);
- + mcc->ccgr_query.dcpid = ccgr_query->dcpid;
- + qm_mc_commit(&p->p, QM_CEETM_VERB_CCGR_QUERY);
- +
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_CEETM_VERB_CCGR_QUERY);
- + res = mcr->result;
- + if (res == QM_MCR_RESULT_OK) {
- + *response = mcr->ccgr_query;
- + hw_ccgr_query_to_cpu(response);
- + }
- +
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- + if (res != QM_MCR_RESULT_OK) {
- + pr_err("CEETM: QUERY CCGR failed\n");
- + return -EIO;
- + }
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_query_ccgr);
- +
- +static int qman_ceetm_cq_peek_pop_xsfdrread(struct qm_ceetm_cq *cq,
- + u8 command_type, u16 xsfdr,
- + struct qm_mcr_ceetm_cq_peek_pop_xsfdrread *cq_ppxr)
- +{
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + struct qman_portal *p;
- + unsigned long irqflags __maybe_unused;
- + u8 res;
- +
- + p = get_affine_portal();
- + PORTAL_IRQ_LOCK(p, irqflags);
- +
- + mcc = qm_mc_start(&p->p);
- + switch (command_type) {
- + case 0:
- + case 1:
- + mcc->cq_ppxr.cqid = (cq->parent->idx << 4) | cq->idx;
- + break;
- + case 2:
- + mcc->cq_ppxr.xsfdr = xsfdr;
- + break;
- + default:
- + break;
- + }
- + mcc->cq_ppxr.ct = command_type;
- + mcc->cq_ppxr.dcpid = cq->parent->dcp_idx;
- + qm_mc_commit(&p->p, QM_CEETM_VERB_CQ_PEEK_POP_XFDRREAD);
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) ==
- + QM_CEETM_VERB_CQ_PEEK_POP_XFDRREAD);
- +
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- +
- + res = mcr->result;
- + if (res != QM_MCR_RESULT_OK) {
- + pr_err("CEETM: CQ PEEK/POP/XSFDR READ failed\n");
- + return -EIO;
- + }
- + *cq_ppxr = mcr->cq_ppxr;
- + return 0;
- +}
- +
- +static int qman_ceetm_query_statistics(u16 cid,
- + enum qm_dc_portal dcp_idx,
- + u16 command_type,
- + struct qm_mcr_ceetm_statistics_query *query_result)
- +{
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + struct qman_portal *p;
- + unsigned long irqflags __maybe_unused;
- + u8 res;
- +
- + p = get_affine_portal();
- + PORTAL_IRQ_LOCK(p, irqflags);
- +
- + mcc = qm_mc_start(&p->p);
- + mcc->stats_query_write.cid = cid;
- + mcc->stats_query_write.dcpid = dcp_idx;
- + mcc->stats_query_write.ct = command_type;
- + qm_mc_commit(&p->p, QM_CEETM_VERB_STATISTICS_QUERY_WRITE);
- +
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) ==
- + QM_CEETM_VERB_STATISTICS_QUERY_WRITE);
- +
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- +
- + res = mcr->result;
- + if (res != QM_MCR_RESULT_OK) {
- + pr_err("CEETM: STATISTICS QUERY failed\n");
- + return -EIO;
- + }
- + *query_result = mcr->stats_query;
- + return 0;
- +}
- +
- +int qman_ceetm_query_write_statistics(u16 cid, enum qm_dc_portal dcp_idx,
- + u16 command_type, u64 frame_count,
- + u64 byte_count)
- +{
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + struct qman_portal *p;
- + unsigned long irqflags __maybe_unused;
- + u8 res;
- +
- + p = get_affine_portal();
- + PORTAL_IRQ_LOCK(p, irqflags);
- +
- + mcc = qm_mc_start(&p->p);
- + mcc->stats_query_write.cid = cid;
- + mcc->stats_query_write.dcpid = dcp_idx;
- + mcc->stats_query_write.ct = command_type;
- + mcc->stats_query_write.frm_cnt = frame_count;
- + mcc->stats_query_write.byte_cnt = byte_count;
- + qm_mc_commit(&p->p, QM_CEETM_VERB_STATISTICS_QUERY_WRITE);
- +
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) ==
- + QM_CEETM_VERB_STATISTICS_QUERY_WRITE);
- +
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- +
- + res = mcr->result;
- + if (res != QM_MCR_RESULT_OK) {
- + pr_err("CEETM: STATISTICS WRITE failed\n");
- + return -EIO;
- + }
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_query_write_statistics);
- +
- +int qman_ceetm_bps2tokenrate(u64 bps, struct qm_ceetm_rate *token_rate,
- + int rounding)
- +{
- + u16 pres;
- + u64 temp;
- + u64 qman_freq;
- + int ret;
- +
- + /* Read PRES from CEET_CFG_PRES register */
- + ret = qman_ceetm_get_prescaler(&pres);
- + if (ret)
- + return -EINVAL;
- +
- + ret = qm_get_clock(&qman_freq);
- + if (ret)
- + return -EINVAL;
- +
- + /* token-rate = bytes-per-second * update-reference-period
- + *
- + * Where token-rate is N/8192 for a integer N, and
- + * update-reference-period is (2^22)/(PRES*QHz), where PRES
- + * is the prescalar value and QHz is the QMan clock frequency.
- + * So:
- + *
- + * token-rate = (byte-per-second*2^22)/PRES*QHZ)
- + *
- + * Converting to bits-per-second gives;
- + *
- + * token-rate = (bps*2^19) / (PRES*QHZ)
- + * N = (bps*2^32) / (PRES*QHz)
- + *
- + * And to avoid 64-bit overflow if 'bps' is larger than 4Gbps
- + * (yet minimise rounding error if 'bps' is small), we reorganise
- + * the formula to use two 16-bit shifts rather than 1 32-bit shift.
- + * N = (((bps*2^16)/PRES)*2^16)/QHz
- + */
- + temp = ROUNDING((bps << 16), pres, rounding);
- + temp = ROUNDING((temp << 16), qman_freq, rounding);
- + token_rate->whole = temp >> 13;
- + token_rate->fraction = temp & (((u64)1 << 13) - 1);
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_bps2tokenrate);
- +
- +int qman_ceetm_tokenrate2bps(const struct qm_ceetm_rate *token_rate, u64 *bps,
- + int rounding)
- +{
- + u16 pres;
- + u64 temp;
- + u64 qman_freq;
- + int ret;
- +
- + /* Read PRES from CEET_CFG_PRES register */
- + ret = qman_ceetm_get_prescaler(&pres);
- + if (ret)
- + return -EINVAL;
- +
- + ret = qm_get_clock(&qman_freq);
- + if (ret)
- + return -EINVAL;
- +
- + /* bytes-per-second = token-rate / update-reference-period
- + *
- + * where "token-rate" is N/8192 for an integer N, and
- + * "update-reference-period" is (2^22)/(PRES*QHz), where PRES is
- + * the prescalar value and QHz is the QMan clock frequency. So;
- + *
- + * bytes-per-second = (N/8192) / (4194304/PRES*QHz)
- + * = N*PRES*QHz / (4194304*8192)
- + * = N*PRES*QHz / (2^35)
- + *
- + * Converting to bits-per-second gives;
- + *
- + * bps = N*PRES*QHZ / (2^32)
- + *
- + * Note, the numerator has a maximum width of 72 bits! So to
- + * avoid 64-bit overflow errors, we calculate PRES*QHZ (maximum
- + * width 48 bits) divided by 2^9 (reducing to maximum 39 bits), before
- + * multiplying by N (goes to maximum of 63 bits).
- + *
- + * temp = PRES*QHZ / (2^16)
- + * kbps = temp*N / (2^16)
- + */
- + temp = ROUNDING(qman_freq * pres, (u64)1 << 16 , rounding);
- + temp *= ((token_rate->whole << 13) + token_rate->fraction);
- + *bps = ROUNDING(temp, (u64)(1) << 16, rounding);
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_tokenrate2bps);
- +
- +int qman_ceetm_sp_claim(struct qm_ceetm_sp **sp, enum qm_dc_portal dcp_idx,
- + unsigned int sp_idx)
- +{
- + struct qm_ceetm_sp *p;
- +
- + DPA_ASSERT((dcp_idx == qm_dc_portal_fman0) ||
- + (dcp_idx == qm_dc_portal_fman1));
- +
- + if ((sp_idx < qman_ceetms[dcp_idx].sp_range[0]) ||
- + (sp_idx > (qman_ceetms[dcp_idx].sp_range[0] +
- + qman_ceetms[dcp_idx].sp_range[1]))) {
- + pr_err("Sub-portal index doesn't exist\n");
- + return -EINVAL;
- + }
- +
- + list_for_each_entry(p, &qman_ceetms[dcp_idx].sub_portals, node) {
- + if ((p->idx == sp_idx) && (p->is_claimed == 0)) {
- + p->is_claimed = 1;
- + *sp = p;
- + return 0;
- + }
- + }
- + pr_err("The sub-portal#%d is not available!\n", sp_idx);
- + return -ENODEV;
- +}
- +EXPORT_SYMBOL(qman_ceetm_sp_claim);
- +
- +int qman_ceetm_sp_release(struct qm_ceetm_sp *sp)
- +{
- + struct qm_ceetm_sp *p;
- +
- + if (sp->lni && sp->lni->is_claimed == 1) {
- + pr_err("The dependency of sub-portal has not been released!\n");
- + return -EBUSY;
- + }
- +
- + list_for_each_entry(p, &qman_ceetms[sp->dcp_idx].sub_portals, node) {
- + if (p->idx == sp->idx) {
- + p->is_claimed = 0;
- + p->lni = NULL;
- + }
- + }
- + /* Disable CEETM mode of this sub-portal */
- + qman_sp_disable_ceetm_mode(sp->dcp_idx, sp->idx);
- +
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_sp_release);
- +
- +int qman_ceetm_lni_claim(struct qm_ceetm_lni **lni, enum qm_dc_portal dcp_idx,
- + unsigned int lni_idx)
- +{
- + struct qm_ceetm_lni *p;
- +
- + if ((lni_idx < qman_ceetms[dcp_idx].lni_range[0]) ||
- + (lni_idx > (qman_ceetms[dcp_idx].lni_range[0] +
- + qman_ceetms[dcp_idx].lni_range[1]))) {
- + pr_err("The lni index is out of range\n");
- + return -EINVAL;
- + }
- +
- + list_for_each_entry(p, &qman_ceetms[dcp_idx].lnis, node) {
- + if ((p->idx == lni_idx) && (p->is_claimed == 0)) {
- + *lni = p;
- + p->is_claimed = 1;
- + return 0;
- + }
- + }
- +
- + pr_err("The LNI#%d is not available!\n", lni_idx);
- + return -EINVAL;
- +}
- +EXPORT_SYMBOL(qman_ceetm_lni_claim);
- +
- +int qman_ceetm_lni_release(struct qm_ceetm_lni *lni)
- +{
- + struct qm_ceetm_lni *p;
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_config config_opts;
- +
- + if (!list_empty(&lni->channels)) {
- + pr_err("The LNI dependencies are not released!\n");
- + return -EBUSY;
- + }
- +
- + list_for_each_entry(p, &qman_ceetms[lni->dcp_idx].lnis, node) {
- + if (p->idx == lni->idx) {
- + p->shaper_enable = 0;
- + p->shaper_couple = 0;
- + p->cr_token_rate.whole = 0;
- + p->cr_token_rate.fraction = 0;
- + p->er_token_rate.whole = 0;
- + p->er_token_rate.fraction = 0;
- + p->cr_token_bucket_limit = 0;
- + p->er_token_bucket_limit = 0;
- + p->is_claimed = 0;
- + }
- + }
- + config_opts.cid = cpu_to_be16(CEETM_COMMAND_LNI_SHAPER | lni->idx);
- + config_opts.dcpid = lni->dcp_idx;
- + memset(&config_opts.shaper_config, 0,
- + sizeof(config_opts.shaper_config));
- + return qman_ceetm_configure_mapping_shaper_tcfc(&config_opts);
- +}
- +EXPORT_SYMBOL(qman_ceetm_lni_release);
- +
- +int qman_ceetm_sp_set_lni(struct qm_ceetm_sp *sp, struct qm_ceetm_lni *lni)
- +{
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_config config_opts;
- +
- + config_opts.cid = cpu_to_be16(CEETM_COMMAND_SP_MAPPING | sp->idx);
- + config_opts.dcpid = sp->dcp_idx;
- + config_opts.sp_mapping.map_lni_id = lni->idx;
- + sp->lni = lni;
- +
- + if (qman_ceetm_configure_mapping_shaper_tcfc(&config_opts))
- + return -EINVAL;
- +
- + /* Enable CEETM mode for this sub-portal */
- + return qman_sp_enable_ceetm_mode(sp->dcp_idx, sp->idx);
- +}
- +EXPORT_SYMBOL(qman_ceetm_sp_set_lni);
- +
- +int qman_ceetm_sp_get_lni(struct qm_ceetm_sp *sp, unsigned int *lni_idx)
- +{
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts;
- + struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result;
- +
- + query_opts.cid = cpu_to_be16(CEETM_COMMAND_SP_MAPPING | sp->idx);
- + query_opts.dcpid = sp->dcp_idx;
- + if (qman_ceetm_query_mapping_shaper_tcfc(&query_opts, &query_result)) {
- + pr_err("Can't get SP <-> LNI mapping\n");
- + return -EINVAL;
- + }
- + *lni_idx = query_result.sp_mapping_query.map_lni_id;
- + sp->lni->idx = query_result.sp_mapping_query.map_lni_id;
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_sp_get_lni);
- +
- +int qman_ceetm_lni_enable_shaper(struct qm_ceetm_lni *lni, int coupled,
- + int oal)
- +{
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_config config_opts;
- +
- + lni->shaper_enable = 1;
- + lni->shaper_couple = coupled;
- + lni->oal = oal;
- +
- + config_opts.cid = cpu_to_be16(CEETM_COMMAND_LNI_SHAPER | lni->idx);
- + config_opts.dcpid = lni->dcp_idx;
- + config_opts.shaper_config.cpl = coupled;
- + config_opts.shaper_config.oal = oal;
- + config_opts.shaper_config.crtcr = cpu_to_be24((lni->cr_token_rate.whole
- + << 13) | lni->cr_token_rate.fraction);
- + config_opts.shaper_config.ertcr = cpu_to_be24((lni->er_token_rate.whole
- + << 13) | lni->er_token_rate.fraction);
- + config_opts.shaper_config.crtbl =
- + cpu_to_be16(lni->cr_token_bucket_limit);
- + config_opts.shaper_config.ertbl =
- + cpu_to_be16(lni->er_token_bucket_limit);
- + config_opts.shaper_config.mps = 60;
- +
- + return qman_ceetm_configure_mapping_shaper_tcfc(&config_opts);
- +}
- +EXPORT_SYMBOL(qman_ceetm_lni_enable_shaper);
- +
- +int qman_ceetm_lni_disable_shaper(struct qm_ceetm_lni *lni)
- +{
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_config config_opts;
- +
- + if (!lni->shaper_enable) {
- + pr_err("The shaper has been disabled\n");
- + return -EINVAL;
- + }
- +
- + config_opts.cid = CEETM_COMMAND_LNI_SHAPER | lni->idx;
- + config_opts.dcpid = lni->dcp_idx;
- + config_opts.shaper_config.cpl = (lni->shaper_couple << 7) | lni->oal;
- + config_opts.shaper_config.crtbl = lni->cr_token_bucket_limit;
- + config_opts.shaper_config.ertbl = lni->er_token_bucket_limit;
- + /* Set CR/ER rate with all 1's to configure an infinite rate, thus
- + * disable the shaping.
- + */
- + config_opts.shaper_config.crtcr = 0xFFFFFF;
- + config_opts.shaper_config.ertcr = 0xFFFFFF;
- + config_opts.shaper_config.mps = 60;
- + lni->shaper_enable = 0;
- + return qman_ceetm_configure_mapping_shaper_tcfc(&config_opts);
- +}
- +EXPORT_SYMBOL(qman_ceetm_lni_disable_shaper);
- +
- +int qman_ceetm_lni_is_shaper_enabled(struct qm_ceetm_lni *lni)
- +{
- + return lni->shaper_enable;
- +}
- +EXPORT_SYMBOL(qman_ceetm_lni_is_shaper_enabled);
- +
- +int qman_ceetm_lni_set_commit_rate(struct qm_ceetm_lni *lni,
- + const struct qm_ceetm_rate *token_rate,
- + u16 token_limit)
- +{
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_config config_opts;
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts;
- + struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result;
- + int ret;
- +
- + lni->cr_token_rate.whole = token_rate->whole;
- + lni->cr_token_rate.fraction = token_rate->fraction;
- + lni->cr_token_bucket_limit = token_limit;
- + if (!lni->shaper_enable)
- + return 0;
- + query_opts.cid = cpu_to_be16(CEETM_COMMAND_LNI_SHAPER | lni->idx);
- + query_opts.dcpid = lni->dcp_idx;
- + ret = qman_ceetm_query_mapping_shaper_tcfc(&query_opts,
- + &query_result);
- + if (ret) {
- + pr_err("Fail to get current LNI shaper setting\n");
- + return -EINVAL;
- + }
- +
- + config_opts.cid = cpu_to_be16(CEETM_COMMAND_LNI_SHAPER | lni->idx);
- + config_opts.dcpid = lni->dcp_idx;
- + config_opts.shaper_config.crtcr = cpu_to_be24((token_rate->whole << 13)
- + | (token_rate->fraction));
- + config_opts.shaper_config.crtbl = cpu_to_be16(token_limit);
- + config_opts.shaper_config.cpl = query_result.shaper_query.cpl;
- + config_opts.shaper_config.oal = query_result.shaper_query.oal;
- + config_opts.shaper_config.ertcr = query_result.shaper_query.ertcr;
- + config_opts.shaper_config.ertbl = query_result.shaper_query.ertbl;
- + config_opts.shaper_config.mps = query_result.shaper_query.mps;
- + return qman_ceetm_configure_mapping_shaper_tcfc(&config_opts);
- +}
- +EXPORT_SYMBOL(qman_ceetm_lni_set_commit_rate);
- +
- +int qman_ceetm_lni_set_commit_rate_bps(struct qm_ceetm_lni *lni,
- + u64 bps,
- + u16 token_limit)
- +{
- + struct qm_ceetm_rate token_rate;
- + int ret;
- +
- + ret = qman_ceetm_bps2tokenrate(bps, &token_rate, 0);
- + if (ret) {
- + pr_err("Can not convert bps to token rate\n");
- + return -EINVAL;
- + }
- +
- + return qman_ceetm_lni_set_commit_rate(lni, &token_rate, token_limit);
- +}
- +EXPORT_SYMBOL(qman_ceetm_lni_set_commit_rate_bps);
- +
- +int qman_ceetm_lni_get_commit_rate(struct qm_ceetm_lni *lni,
- + struct qm_ceetm_rate *token_rate,
- + u16 *token_limit)
- +{
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts;
- + struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result;
- + int ret;
- +
- + query_opts.cid = cpu_to_be16(CEETM_COMMAND_LNI_SHAPER | lni->idx);
- + query_opts.dcpid = lni->dcp_idx;
- +
- + ret = qman_ceetm_query_mapping_shaper_tcfc(&query_opts, &query_result);
- + if (ret) {
- + pr_err("The LNI CR rate or limit is not set\n");
- + return -EINVAL;
- + }
- + token_rate->whole = be24_to_cpu(query_result.shaper_query.crtcr) >> 13;
- + token_rate->fraction = be24_to_cpu(query_result.shaper_query.crtcr) &
- + 0x1FFF;
- + *token_limit = be16_to_cpu(query_result.shaper_query.crtbl);
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_lni_get_commit_rate);
- +
- +int qman_ceetm_lni_get_commit_rate_bps(struct qm_ceetm_lni *lni,
- + u64 *bps, u16 *token_limit)
- +{
- + struct qm_ceetm_rate token_rate;
- + int ret;
- +
- + ret = qman_ceetm_lni_get_commit_rate(lni, &token_rate, token_limit);
- + if (ret) {
- + pr_err("The LNI CR rate or limit is not available\n");
- + return -EINVAL;
- + }
- +
- + return qman_ceetm_tokenrate2bps(&token_rate, bps, 0);
- +}
- +EXPORT_SYMBOL(qman_ceetm_lni_get_commit_rate_bps);
- +
- +int qman_ceetm_lni_set_excess_rate(struct qm_ceetm_lni *lni,
- + const struct qm_ceetm_rate *token_rate,
- + u16 token_limit)
- +{
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_config config_opts;
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts;
- + struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result;
- + int ret;
- +
- + lni->er_token_rate.whole = token_rate->whole;
- + lni->er_token_rate.fraction = token_rate->fraction;
- + lni->er_token_bucket_limit = token_limit;
- + if (!lni->shaper_enable)
- + return 0;
- +
- + query_opts.cid = cpu_to_be16(CEETM_COMMAND_LNI_SHAPER | lni->idx);
- + query_opts.dcpid = lni->dcp_idx;
- + ret = qman_ceetm_query_mapping_shaper_tcfc(&query_opts,
- + &query_result);
- + if (ret) {
- + pr_err("Fail to get current LNI shaper setting\n");
- + return -EINVAL;
- + }
- +
- + config_opts.cid = cpu_to_be16(CEETM_COMMAND_LNI_SHAPER | lni->idx);
- + config_opts.dcpid = lni->dcp_idx;
- + config_opts.shaper_config.ertcr = cpu_to_be24(
- + (token_rate->whole << 13) | (token_rate->fraction));
- + config_opts.shaper_config.ertbl = cpu_to_be16(token_limit);
- + config_opts.shaper_config.cpl = query_result.shaper_query.cpl;
- + config_opts.shaper_config.oal = query_result.shaper_query.oal;
- + config_opts.shaper_config.crtcr = query_result.shaper_query.crtcr;
- + config_opts.shaper_config.crtbl = query_result.shaper_query.crtbl;
- + config_opts.shaper_config.mps = query_result.shaper_query.mps;
- + return qman_ceetm_configure_mapping_shaper_tcfc(&config_opts);
- +}
- +EXPORT_SYMBOL(qman_ceetm_lni_set_excess_rate);
- +
- +int qman_ceetm_lni_set_excess_rate_bps(struct qm_ceetm_lni *lni,
- + u64 bps,
- + u16 token_limit)
- +{
- + struct qm_ceetm_rate token_rate;
- + int ret;
- +
- + ret = qman_ceetm_bps2tokenrate(bps, &token_rate, 0);
- + if (ret) {
- + pr_err("Can not convert bps to token rate\n");
- + return -EINVAL;
- + }
- + return qman_ceetm_lni_set_excess_rate(lni, &token_rate, token_limit);
- +}
- +EXPORT_SYMBOL(qman_ceetm_lni_set_excess_rate_bps);
- +
- +int qman_ceetm_lni_get_excess_rate(struct qm_ceetm_lni *lni,
- + struct qm_ceetm_rate *token_rate,
- + u16 *token_limit)
- +{
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts;
- + struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result;
- + int ret;
- +
- + query_opts.cid = cpu_to_be16(CEETM_COMMAND_LNI_SHAPER | lni->idx);
- + query_opts.dcpid = lni->dcp_idx;
- + ret = qman_ceetm_query_mapping_shaper_tcfc(&query_opts, &query_result);
- + if (ret) {
- + pr_err("The LNI ER rate or limit is not set\n");
- + return -EINVAL;
- + }
- + token_rate->whole = be24_to_cpu(query_result.shaper_query.ertcr) >> 13;
- + token_rate->fraction = be24_to_cpu(query_result.shaper_query.ertcr) &
- + 0x1FFF;
- + *token_limit = be16_to_cpu(query_result.shaper_query.ertbl);
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_lni_get_excess_rate);
- +
- +int qman_ceetm_lni_get_excess_rate_bps(struct qm_ceetm_lni *lni,
- + u64 *bps, u16 *token_limit)
- +{
- + struct qm_ceetm_rate token_rate;
- + int ret;
- +
- + ret = qman_ceetm_lni_get_excess_rate(lni, &token_rate, token_limit);
- + if (ret) {
- + pr_err("The LNI ER rate or limit is not available\n");
- + return -EINVAL;
- + }
- +
- + return qman_ceetm_tokenrate2bps(&token_rate, bps, 0);
- +}
- +EXPORT_SYMBOL(qman_ceetm_lni_get_excess_rate_bps);
- +
- +#define QMAN_CEETM_LNITCFCC_CQ_LEVEL_SHIFT(n) ((15 - n) * 4)
- +#define QMAN_CEETM_LNITCFCC_ENABLE 0x8
- +int qman_ceetm_lni_set_tcfcc(struct qm_ceetm_lni *lni,
- + unsigned int cq_level,
- + int traffic_class)
- +{
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_config config_opts;
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts;
- + struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result;
- + u64 lnitcfcc;
- +
- + if ((cq_level > 15) | (traffic_class > 7)) {
- + pr_err("The CQ or traffic class id is out of range\n");
- + return -EINVAL;
- + }
- +
- + query_opts.cid = cpu_to_be16(CEETM_COMMAND_TCFC | lni->idx);
- + query_opts.dcpid = lni->dcp_idx;
- + if (qman_ceetm_query_mapping_shaper_tcfc(&query_opts, &query_result)) {
- + pr_err("Fail to query tcfcc\n");
- + return -EINVAL;
- + }
- +
- + lnitcfcc = be64_to_cpu(query_result.tcfc_query.lnitcfcc);
- + if (traffic_class == -1) {
- + /* disable tcfc for this CQ */
- + lnitcfcc &= ~((u64)QMAN_CEETM_LNITCFCC_ENABLE <<
- + QMAN_CEETM_LNITCFCC_CQ_LEVEL_SHIFT(cq_level));
- + } else {
- + lnitcfcc &= ~((u64)0xF <<
- + QMAN_CEETM_LNITCFCC_CQ_LEVEL_SHIFT(cq_level));
- + lnitcfcc |= ((u64)(QMAN_CEETM_LNITCFCC_ENABLE |
- + traffic_class)) <<
- + QMAN_CEETM_LNITCFCC_CQ_LEVEL_SHIFT(cq_level);
- + }
- + config_opts.tcfc_config.lnitcfcc = cpu_to_be64(lnitcfcc);
- + config_opts.cid = cpu_to_be16(CEETM_COMMAND_TCFC | lni->idx);
- + config_opts.dcpid = lni->dcp_idx;
- + return qman_ceetm_configure_mapping_shaper_tcfc(&config_opts);
- +}
- +EXPORT_SYMBOL(qman_ceetm_lni_set_tcfcc);
- +
- +#define QMAN_CEETM_LNITCFCC_TC_MASK 0x7
- +int qman_ceetm_lni_get_tcfcc(struct qm_ceetm_lni *lni, unsigned int cq_level,
- + int *traffic_class)
- +{
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts;
- + struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result;
- + int ret;
- + u8 lnitcfcc;
- +
- + if (cq_level > 15) {
- + pr_err("the CQ level is out of range\n");
- + return -EINVAL;
- + }
- +
- + query_opts.cid = cpu_to_be16(CEETM_COMMAND_TCFC | lni->idx);
- + query_opts.dcpid = lni->dcp_idx;
- + ret = qman_ceetm_query_mapping_shaper_tcfc(&query_opts, &query_result);
- + if (ret)
- + return ret;
- + lnitcfcc = (u8)be64_to_cpu((query_result.tcfc_query.lnitcfcc) >>
- + QMAN_CEETM_LNITCFCC_CQ_LEVEL_SHIFT(cq_level));
- + if (lnitcfcc & QMAN_CEETM_LNITCFCC_ENABLE)
- + *traffic_class = lnitcfcc & QMAN_CEETM_LNITCFCC_TC_MASK;
- + else
- + *traffic_class = -1;
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_lni_get_tcfcc);
- +
- +int qman_ceetm_channel_claim(struct qm_ceetm_channel **channel,
- + struct qm_ceetm_lni *lni)
- +{
- + struct qm_ceetm_channel *p;
- + u32 channel_idx;
- + int ret = 0;
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_config config_opts;
- +
- + if (lni->dcp_idx == qm_dc_portal_fman0) {
- + ret = qman_alloc_ceetm0_channel(&channel_idx);
- + } else if (lni->dcp_idx == qm_dc_portal_fman1) {
- + ret = qman_alloc_ceetm1_channel(&channel_idx);
- + } else {
- + pr_err("dcp_idx %u does not correspond to a known fman in this driver\n",
- + lni->dcp_idx);
- + return -EINVAL;
- + }
- +
- + if (ret) {
- + pr_err("The is no channel available for LNI#%d\n", lni->idx);
- + return -ENODEV;
- + }
- +
- + p = kzalloc(sizeof(*p), GFP_KERNEL);
- + if (!p)
- + return -ENOMEM;
- + p->idx = channel_idx;
- + p->dcp_idx = lni->dcp_idx;
- + list_add_tail(&p->node, &lni->channels);
- + INIT_LIST_HEAD(&p->class_queues);
- + INIT_LIST_HEAD(&p->ccgs);
- + config_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_MAPPING |
- + channel_idx);
- + config_opts.dcpid = lni->dcp_idx;
- + config_opts.channel_mapping.map_lni_id = lni->idx;
- + config_opts.channel_mapping.map_shaped = 0;
- + if (qman_ceetm_configure_mapping_shaper_tcfc(&config_opts)) {
- + pr_err("Can't map channel#%d for LNI#%d\n",
- + channel_idx, lni->idx);
- + return -EINVAL;
- + }
- + *channel = p;
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_channel_claim);
- +
- +int qman_ceetm_channel_release(struct qm_ceetm_channel *channel)
- +{
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_config config_opts;
- + if (!list_empty(&channel->class_queues)) {
- + pr_err("CEETM channel#%d has class queue unreleased!\n",
- + channel->idx);
- + return -EBUSY;
- + }
- + if (!list_empty(&channel->ccgs)) {
- + pr_err("CEETM channel#%d has ccg unreleased!\n",
- + channel->idx);
- + return -EBUSY;
- + }
- +
- + /* channel->dcp_idx corresponds to known fman validation */
- + if ((channel->dcp_idx != qm_dc_portal_fman0) &&
- + (channel->dcp_idx != qm_dc_portal_fman1)) {
- + pr_err("dcp_idx %u does not correspond to a known fman in this driver\n",
- + channel->dcp_idx);
- + return -EINVAL;
- + }
- +
- + config_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_SHAPER |
- + channel->idx);
- + config_opts.dcpid = channel->dcp_idx;
- + memset(&config_opts.shaper_config, 0,
- + sizeof(config_opts.shaper_config));
- + if (qman_ceetm_configure_mapping_shaper_tcfc(&config_opts)) {
- + pr_err("Can't reset channel shapping parameters\n");
- + return -EINVAL;
- + }
- +
- + if (channel->dcp_idx == qm_dc_portal_fman0) {
- + qman_release_ceetm0_channelid(channel->idx);
- + } else if (channel->dcp_idx == qm_dc_portal_fman1) {
- + qman_release_ceetm1_channelid(channel->idx);
- + } else {
- + pr_err("dcp_idx %u does not correspond to a known fman in this driver\n",
- + channel->dcp_idx);
- + return -EINVAL;
- + }
- + list_del(&channel->node);
- + kfree(channel);
- +
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_channel_release);
- +
- +int qman_ceetm_channel_enable_shaper(struct qm_ceetm_channel *channel,
- + int coupled)
- +{
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts;
- + struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result;
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_config config_opts;
- +
- + if (channel->shaper_enable == 1) {
- + pr_err("This channel shaper has been enabled!\n");
- + return -EINVAL;
- + }
- +
- + channel->shaper_enable = 1;
- + channel->shaper_couple = coupled;
- +
- + query_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_MAPPING |
- + channel->idx);
- + query_opts.dcpid = (u8)channel->dcp_idx;
- +
- + if (qman_ceetm_query_mapping_shaper_tcfc(&query_opts, &query_result)) {
- + pr_err("Can't query channel mapping\n");
- + return -EINVAL;
- + }
- +
- + config_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_MAPPING |
- + channel->idx);
- + config_opts.dcpid = channel->dcp_idx;
- + config_opts.channel_mapping.map_lni_id =
- + query_result.channel_mapping_query.map_lni_id;
- + config_opts.channel_mapping.map_shaped = 1;
- + if (qman_ceetm_configure_mapping_shaper_tcfc(&config_opts)) {
- + pr_err("Can't enable shaper for channel #%d\n", channel->idx);
- + return -EINVAL;
- + }
- +
- + config_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_SHAPER |
- + channel->idx);
- + config_opts.shaper_config.cpl = coupled;
- + config_opts.shaper_config.crtcr = cpu_to_be24((channel->cr_token_rate.
- + whole << 13) |
- + channel->cr_token_rate.fraction);
- + config_opts.shaper_config.ertcr = cpu_to_be24((channel->er_token_rate.
- + whole << 13) |
- + channel->er_token_rate.fraction);
- + config_opts.shaper_config.crtbl =
- + cpu_to_be16(channel->cr_token_bucket_limit);
- + config_opts.shaper_config.ertbl =
- + cpu_to_be16(channel->er_token_bucket_limit);
- + return qman_ceetm_configure_mapping_shaper_tcfc(&config_opts);
- +}
- +EXPORT_SYMBOL(qman_ceetm_channel_enable_shaper);
- +
- +int qman_ceetm_channel_disable_shaper(struct qm_ceetm_channel *channel)
- +{
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts;
- + struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result;
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_config config_opts;
- +
- +
- + query_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_MAPPING |
- + channel->idx);
- + query_opts.dcpid = channel->dcp_idx;
- +
- + if (qman_ceetm_query_mapping_shaper_tcfc(&query_opts, &query_result)) {
- + pr_err("Can't query channel mapping\n");
- + return -EINVAL;
- + }
- +
- + config_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_MAPPING |
- + channel->idx);
- + config_opts.dcpid = channel->dcp_idx;
- + config_opts.channel_mapping.map_shaped = 0;
- + config_opts.channel_mapping.map_lni_id =
- + query_result.channel_mapping_query.map_lni_id;
- +
- + return qman_ceetm_configure_mapping_shaper_tcfc(&config_opts);
- +}
- +EXPORT_SYMBOL(qman_ceetm_channel_disable_shaper);
- +
- +int qman_ceetm_channel_is_shaper_enabled(struct qm_ceetm_channel *channel)
- +{
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts;
- + struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result;
- +
- + query_opts.cid = CEETM_COMMAND_CHANNEL_MAPPING | channel->idx;
- + query_opts.dcpid = channel->dcp_idx;
- +
- + if (qman_ceetm_query_mapping_shaper_tcfc(&query_opts, &query_result)) {
- + pr_err("Can't query channel mapping\n");
- + return -EINVAL;
- + }
- +
- + return query_result.channel_mapping_query.map_shaped;
- +}
- +EXPORT_SYMBOL(qman_ceetm_channel_is_shaper_enabled);
- +
- +int qman_ceetm_channel_set_commit_rate(struct qm_ceetm_channel *channel,
- + const struct qm_ceetm_rate *token_rate,
- + u16 token_limit)
- +{
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_config config_opts;
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts;
- + struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result;
- + int ret;
- +
- + query_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_SHAPER |
- + channel->idx);
- + query_opts.dcpid = channel->dcp_idx;
- +
- + ret = qman_ceetm_query_mapping_shaper_tcfc(&query_opts, &query_result);
- + if (ret) {
- + pr_err("Fail to get the current channel shaper setting\n");
- + return -EINVAL;
- + }
- +
- + channel->cr_token_rate.whole = token_rate->whole;
- + channel->cr_token_rate.fraction = token_rate->fraction;
- + channel->cr_token_bucket_limit = token_limit;
- + config_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_SHAPER |
- + channel->idx);
- + config_opts.dcpid = channel->dcp_idx;
- + config_opts.shaper_config.crtcr = cpu_to_be24((token_rate->whole
- + << 13) | (token_rate->fraction));
- + config_opts.shaper_config.crtbl = cpu_to_be16(token_limit);
- + config_opts.shaper_config.cpl = query_result.shaper_query.cpl;
- + config_opts.shaper_config.ertcr = query_result.shaper_query.ertcr;
- + config_opts.shaper_config.ertbl = query_result.shaper_query.ertbl;
- + return qman_ceetm_configure_mapping_shaper_tcfc(&config_opts);
- +}
- +EXPORT_SYMBOL(qman_ceetm_channel_set_commit_rate);
- +
- +int qman_ceetm_channel_set_commit_rate_bps(struct qm_ceetm_channel *channel,
- + u64 bps, u16 token_limit)
- +{
- + struct qm_ceetm_rate token_rate;
- + int ret;
- +
- + ret = qman_ceetm_bps2tokenrate(bps, &token_rate, 0);
- + if (ret) {
- + pr_err("Can not convert bps to token rate\n");
- + return -EINVAL;
- + }
- + return qman_ceetm_channel_set_commit_rate(channel, &token_rate,
- + token_limit);
- +}
- +EXPORT_SYMBOL(qman_ceetm_channel_set_commit_rate_bps);
- +
- +int qman_ceetm_channel_get_commit_rate(struct qm_ceetm_channel *channel,
- + struct qm_ceetm_rate *token_rate,
- + u16 *token_limit)
- +{
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts;
- + struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result;
- + int ret;
- +
- + query_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_SHAPER |
- + channel->idx);
- + query_opts.dcpid = channel->dcp_idx;
- +
- + ret = qman_ceetm_query_mapping_shaper_tcfc(&query_opts, &query_result);
- + if (ret | !query_result.shaper_query.crtcr |
- + !query_result.shaper_query.crtbl) {
- + pr_err("The channel commit rate or limit is not set\n");
- + return -EINVAL;
- + }
- + token_rate->whole = be24_to_cpu(query_result.shaper_query.crtcr) >> 13;
- + token_rate->fraction = be24_to_cpu(query_result.shaper_query.crtcr) &
- + 0x1FFF;
- + *token_limit = be16_to_cpu(query_result.shaper_query.crtbl);
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_channel_get_commit_rate);
- +
- +int qman_ceetm_channel_get_commit_rate_bps(struct qm_ceetm_channel *channel,
- + u64 *bps, u16 *token_limit)
- +{
- + struct qm_ceetm_rate token_rate;
- + int ret;
- +
- + ret = qman_ceetm_channel_get_commit_rate(channel, &token_rate,
- + token_limit);
- + if (ret) {
- + pr_err("The channel CR rate or limit is not available\n");
- + return -EINVAL;
- + }
- +
- + return qman_ceetm_tokenrate2bps(&token_rate, bps, 0);
- +}
- +EXPORT_SYMBOL(qman_ceetm_channel_get_commit_rate_bps);
- +
- +int qman_ceetm_channel_set_excess_rate(struct qm_ceetm_channel *channel,
- + const struct qm_ceetm_rate *token_rate,
- + u16 token_limit)
- +{
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_config config_opts;
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts;
- + struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result;
- + int ret;
- +
- + query_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_SHAPER |
- + channel->idx);
- + query_opts.dcpid = channel->dcp_idx;
- + ret = qman_ceetm_query_mapping_shaper_tcfc(&query_opts, &query_result);
- + if (ret) {
- + pr_err("Fail to get the current channel shaper setting\n");
- + return -EINVAL;
- + }
- +
- + channel->er_token_rate.whole = token_rate->whole;
- + channel->er_token_rate.fraction = token_rate->fraction;
- + channel->er_token_bucket_limit = token_limit;
- + config_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_SHAPER |
- + channel->idx);
- + config_opts.dcpid = channel->dcp_idx;
- + config_opts.shaper_config.ertcr = cpu_to_be24(
- + (token_rate->whole << 13) | (token_rate->fraction));
- + config_opts.shaper_config.ertbl = cpu_to_be16(token_limit);
- + config_opts.shaper_config.cpl = query_result.shaper_query.cpl;
- + config_opts.shaper_config.crtcr = query_result.shaper_query.crtcr;
- + config_opts.shaper_config.crtbl = query_result.shaper_query.crtbl;
- + return qman_ceetm_configure_mapping_shaper_tcfc(&config_opts);
- +}
- +EXPORT_SYMBOL(qman_ceetm_channel_set_excess_rate);
- +
- +int qman_ceetm_channel_set_excess_rate_bps(struct qm_ceetm_channel *channel,
- + u64 bps, u16 token_limit)
- +{
- + struct qm_ceetm_rate token_rate;
- + int ret;
- +
- + ret = qman_ceetm_bps2tokenrate(bps, &token_rate, 0);
- + if (ret) {
- + pr_err("Can not convert bps to token rate\n");
- + return -EINVAL;
- + }
- + return qman_ceetm_channel_set_excess_rate(channel, &token_rate,
- + token_limit);
- +}
- +EXPORT_SYMBOL(qman_ceetm_channel_set_excess_rate_bps);
- +
- +int qman_ceetm_channel_get_excess_rate(struct qm_ceetm_channel *channel,
- + struct qm_ceetm_rate *token_rate,
- + u16 *token_limit)
- +{
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts;
- + struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result;
- + int ret;
- +
- + query_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_SHAPER |
- + channel->idx);
- + query_opts.dcpid = channel->dcp_idx;
- + ret = qman_ceetm_query_mapping_shaper_tcfc(&query_opts, &query_result);
- + if (ret | !query_result.shaper_query.ertcr |
- + !query_result.shaper_query.ertbl) {
- + pr_err("The channel excess rate or limit is not set\n");
- + return -EINVAL;
- + }
- + token_rate->whole = be24_to_cpu(query_result.shaper_query.ertcr) >> 13;
- + token_rate->fraction = be24_to_cpu(query_result.shaper_query.ertcr) &
- + 0x1FFF;
- + *token_limit = be16_to_cpu(query_result.shaper_query.ertbl);
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_channel_get_excess_rate);
- +
- +int qman_ceetm_channel_get_excess_rate_bps(struct qm_ceetm_channel *channel,
- + u64 *bps, u16 *token_limit)
- +{
- + struct qm_ceetm_rate token_rate;
- + int ret;
- +
- + ret = qman_ceetm_channel_get_excess_rate(channel, &token_rate,
- + token_limit);
- + if (ret) {
- + pr_err("The channel ER rate or limit is not available\n");
- + return -EINVAL;
- + }
- +
- + return qman_ceetm_tokenrate2bps(&token_rate, bps, 0);
- +}
- +EXPORT_SYMBOL(qman_ceetm_channel_get_excess_rate_bps);
- +
- +int qman_ceetm_channel_set_weight(struct qm_ceetm_channel *channel,
- + u16 token_limit)
- +{
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_config config_opts;
- +
- + if (channel->shaper_enable) {
- + pr_err("This channel is a shaped one\n");
- + return -EINVAL;
- + }
- +
- + channel->cr_token_bucket_limit = token_limit;
- + config_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_SHAPER |
- + channel->idx);
- + config_opts.dcpid = channel->dcp_idx;
- + config_opts.shaper_config.crtbl = cpu_to_be16(token_limit);
- + return qman_ceetm_configure_mapping_shaper_tcfc(&config_opts);
- +}
- +EXPORT_SYMBOL(qman_ceetm_channel_set_weight);
- +
- +int qman_ceetm_channel_get_weight(struct qm_ceetm_channel *channel,
- + u16 *token_limit)
- +{
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_query query_opts;
- + struct qm_mcr_ceetm_mapping_shaper_tcfc_query query_result;
- + int ret;
- +
- + query_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_SHAPER |
- + channel->idx);
- + query_opts.dcpid = channel->dcp_idx;
- + ret = qman_ceetm_query_mapping_shaper_tcfc(&query_opts, &query_result);
- + if (ret | !query_result.shaper_query.crtbl) {
- + pr_err("This unshaped channel's uFQ wight is unavailable\n");
- + return -EINVAL;
- + }
- + *token_limit = be16_to_cpu(query_result.shaper_query.crtbl);
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_channel_get_weight);
- +
- +int qman_ceetm_channel_set_group(struct qm_ceetm_channel *channel, int group_b,
- + unsigned int prio_a, unsigned int prio_b)
- +{
- + struct qm_mcc_ceetm_class_scheduler_config config_opts;
- + struct qm_mcr_ceetm_class_scheduler_query query_result;
- + int i;
- +
- + if (prio_a > 7) {
- + pr_err("The priority of group A is out of range\n");
- + return -EINVAL;
- + }
- + if (group_b && (prio_b > 7)) {
- + pr_err("The priority of group B is out of range\n");
- + return -EINVAL;
- + }
- +
- + if (qman_ceetm_query_class_scheduler(channel, &query_result)) {
- + pr_err("Can't query channel#%d's scheduler!\n", channel->idx);
- + return -EINVAL;
- + }
- +
- + config_opts.cqcid = cpu_to_be16(channel->idx);
- + config_opts.dcpid = channel->dcp_idx;
- + config_opts.gpc_combine_flag = !group_b;
- + config_opts.gpc_prio_a = prio_a;
- + config_opts.gpc_prio_b = prio_b;
- +
- + for (i = 0; i < 8; i++)
- + config_opts.w[i] = query_result.w[i];
- + config_opts.crem = query_result.crem;
- + config_opts.erem = query_result.erem;
- +
- + return qman_ceetm_configure_class_scheduler(&config_opts);
- +}
- +EXPORT_SYMBOL(qman_ceetm_channel_set_group);
- +
- +int qman_ceetm_channel_get_group(struct qm_ceetm_channel *channel, int *group_b,
- + unsigned int *prio_a, unsigned int *prio_b)
- +{
- + struct qm_mcr_ceetm_class_scheduler_query query_result;
- +
- + if (qman_ceetm_query_class_scheduler(channel, &query_result)) {
- + pr_err("Can't query channel#%d's scheduler!\n", channel->idx);
- + return -EINVAL;
- + }
- + *group_b = !query_result.gpc_combine_flag;
- + *prio_a = query_result.gpc_prio_a;
- + *prio_b = query_result.gpc_prio_b;
- +
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_channel_get_group);
- +
- +#define GROUP_A_ELIGIBILITY_SET (1 << 8)
- +#define GROUP_B_ELIGIBILITY_SET (1 << 9)
- +#define CQ_ELIGIBILITY_SET(n) (1 << (7 - n))
- +int qman_ceetm_channel_set_group_cr_eligibility(struct qm_ceetm_channel
- + *channel, int group_b, int cre)
- +{
- + struct qm_mcc_ceetm_class_scheduler_config csch_config;
- + struct qm_mcr_ceetm_class_scheduler_query csch_query;
- + int i;
- +
- + if (qman_ceetm_query_class_scheduler(channel, &csch_query)) {
- + pr_err("Cannot get the channel %d scheduler setting.\n",
- + channel->idx);
- + return -EINVAL;
- + }
- + csch_config.cqcid = cpu_to_be16(channel->idx);
- + csch_config.dcpid = channel->dcp_idx;
- + csch_config.gpc_combine_flag = csch_query.gpc_combine_flag;
- + csch_config.gpc_prio_a = csch_query.gpc_prio_a;
- + csch_config.gpc_prio_b = csch_query.gpc_prio_b;
- +
- + for (i = 0; i < 8; i++)
- + csch_config.w[i] = csch_query.w[i];
- + csch_config.erem = csch_query.erem;
- + if (group_b)
- + csch_config.crem = (be16_to_cpu(csch_query.crem)
- + & ~GROUP_B_ELIGIBILITY_SET)
- + | (cre ? GROUP_B_ELIGIBILITY_SET : 0);
- + else
- + csch_config.crem = (be16_to_cpu(csch_query.crem)
- + & ~GROUP_A_ELIGIBILITY_SET)
- + | (cre ? GROUP_A_ELIGIBILITY_SET : 0);
- +
- + csch_config.crem = cpu_to_be16(csch_config.crem);
- +
- + if (qman_ceetm_configure_class_scheduler(&csch_config)) {
- + pr_err("Cannot config channel %d's scheduler with "
- + "group_%c's cr eligibility\n", channel->idx,
- + group_b ? 'b' : 'a');
- + return -EINVAL;
- + }
- +
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_channel_set_group_cr_eligibility);
- +
- +int qman_ceetm_channel_set_group_er_eligibility(struct qm_ceetm_channel
- + *channel, int group_b, int ere)
- +{
- + struct qm_mcc_ceetm_class_scheduler_config csch_config;
- + struct qm_mcr_ceetm_class_scheduler_query csch_query;
- + int i;
- +
- + if (qman_ceetm_query_class_scheduler(channel, &csch_query)) {
- + pr_err("Cannot get the channel %d scheduler setting.\n",
- + channel->idx);
- + return -EINVAL;
- + }
- + csch_config.cqcid = cpu_to_be16(channel->idx);
- + csch_config.dcpid = channel->dcp_idx;
- + csch_config.gpc_combine_flag = csch_query.gpc_combine_flag;
- + csch_config.gpc_prio_a = csch_query.gpc_prio_a;
- + csch_config.gpc_prio_b = csch_query.gpc_prio_b;
- +
- + for (i = 0; i < 8; i++)
- + csch_config.w[i] = csch_query.w[i];
- + csch_config.crem = csch_query.crem;
- + if (group_b)
- + csch_config.erem = (be16_to_cpu(csch_query.erem)
- + & ~GROUP_B_ELIGIBILITY_SET)
- + | (ere ? GROUP_B_ELIGIBILITY_SET : 0);
- + else
- + csch_config.erem = (be16_to_cpu(csch_query.erem)
- + & ~GROUP_A_ELIGIBILITY_SET)
- + | (ere ? GROUP_A_ELIGIBILITY_SET : 0);
- +
- + csch_config.erem = cpu_to_be16(csch_config.erem);
- +
- + if (qman_ceetm_configure_class_scheduler(&csch_config)) {
- + pr_err("Cannot config channel %d's scheduler with "
- + "group_%c's er eligibility\n", channel->idx,
- + group_b ? 'b' : 'a');
- + return -EINVAL;
- + }
- +
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_channel_set_group_er_eligibility);
- +
- +int qman_ceetm_channel_set_cq_cr_eligibility(struct qm_ceetm_channel *channel,
- + unsigned int idx, int cre)
- +{
- + struct qm_mcc_ceetm_class_scheduler_config csch_config;
- + struct qm_mcr_ceetm_class_scheduler_query csch_query;
- + int i;
- +
- + if (idx > 7) {
- + pr_err("CQ index is out of range\n");
- + return -EINVAL;
- + }
- + if (qman_ceetm_query_class_scheduler(channel, &csch_query)) {
- + pr_err("Cannot get the channel %d scheduler setting.\n",
- + channel->idx);
- + return -EINVAL;
- + }
- + csch_config.cqcid = cpu_to_be16(channel->idx);
- + csch_config.dcpid = channel->dcp_idx;
- + csch_config.gpc_combine_flag = csch_query.gpc_combine_flag;
- + csch_config.gpc_prio_a = csch_query.gpc_prio_a;
- + csch_config.gpc_prio_b = csch_query.gpc_prio_b;
- + for (i = 0; i < 8; i++)
- + csch_config.w[i] = csch_query.w[i];
- + csch_config.erem = csch_query.erem;
- + csch_config.crem = (be16_to_cpu(csch_query.crem)
- + & ~CQ_ELIGIBILITY_SET(idx)) |
- + (cre ? CQ_ELIGIBILITY_SET(idx) : 0);
- + csch_config.crem = cpu_to_be16(csch_config.crem);
- + if (qman_ceetm_configure_class_scheduler(&csch_config)) {
- + pr_err("Cannot config channel scheduler to set "
- + "cr eligibility mask for CQ#%d\n", idx);
- + return -EINVAL;
- + }
- +
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_channel_set_cq_cr_eligibility);
- +
- +int qman_ceetm_channel_set_cq_er_eligibility(struct qm_ceetm_channel *channel,
- + unsigned int idx, int ere)
- +{
- + struct qm_mcc_ceetm_class_scheduler_config csch_config;
- + struct qm_mcr_ceetm_class_scheduler_query csch_query;
- + int i;
- +
- + if (idx > 7) {
- + pr_err("CQ index is out of range\n");
- + return -EINVAL;
- + }
- + if (qman_ceetm_query_class_scheduler(channel, &csch_query)) {
- + pr_err("Cannot get the channel %d scheduler setting.\n",
- + channel->idx);
- + return -EINVAL;
- + }
- + csch_config.cqcid = cpu_to_be16(channel->idx);
- + csch_config.dcpid = channel->dcp_idx;
- + csch_config.gpc_combine_flag = csch_query.gpc_combine_flag;
- + csch_config.gpc_prio_a = csch_query.gpc_prio_a;
- + csch_config.gpc_prio_b = csch_query.gpc_prio_b;
- + for (i = 0; i < 8; i++)
- + csch_config.w[i] = csch_query.w[i];
- + csch_config.crem = csch_query.crem;
- + csch_config.erem = (be16_to_cpu(csch_query.erem)
- + & ~CQ_ELIGIBILITY_SET(idx)) |
- + (ere ? CQ_ELIGIBILITY_SET(idx) : 0);
- + csch_config.erem = cpu_to_be16(csch_config.erem);
- + if (qman_ceetm_configure_class_scheduler(&csch_config)) {
- + pr_err("Cannot config channel scheduler to set "
- + "er eligibility mask for CQ#%d\n", idx);
- + return -EINVAL;
- + }
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_channel_set_cq_er_eligibility);
- +
- +int qman_ceetm_cq_claim(struct qm_ceetm_cq **cq,
- + struct qm_ceetm_channel *channel, unsigned int idx,
- + struct qm_ceetm_ccg *ccg)
- +{
- + struct qm_ceetm_cq *p;
- + struct qm_mcc_ceetm_cq_config cq_config;
- +
- + if (idx > 7) {
- + pr_err("The independent class queue id is out of range\n");
- + return -EINVAL;
- + }
- +
- + list_for_each_entry(p, &channel->class_queues, node) {
- + if (p->idx == idx) {
- + pr_err("The CQ#%d has been claimed!\n", idx);
- + return -EINVAL;
- + }
- + }
- +
- + p = kmalloc(sizeof(*p), GFP_KERNEL);
- + if (!p) {
- + pr_err("Can't allocate memory for CQ#%d!\n", idx);
- + return -ENOMEM;
- + }
- +
- + list_add_tail(&p->node, &channel->class_queues);
- + p->idx = idx;
- + p->is_claimed = 1;
- + p->parent = channel;
- + INIT_LIST_HEAD(&p->bound_lfqids);
- +
- + if (ccg) {
- + cq_config.cqid = cpu_to_be16((channel->idx << 4) | idx);
- + cq_config.dcpid = channel->dcp_idx;
- + cq_config.ccgid = cpu_to_be16(ccg->idx);
- + if (qman_ceetm_configure_cq(&cq_config)) {
- + pr_err("Can't configure the CQ#%d with CCGRID#%d\n",
- + idx, ccg->idx);
- + list_del(&p->node);
- + kfree(p);
- + return -EINVAL;
- + }
- + }
- +
- + *cq = p;
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_cq_claim);
- +
- +int qman_ceetm_cq_claim_A(struct qm_ceetm_cq **cq,
- + struct qm_ceetm_channel *channel, unsigned int idx,
- + struct qm_ceetm_ccg *ccg)
- +{
- + struct qm_ceetm_cq *p;
- + struct qm_mcc_ceetm_cq_config cq_config;
- +
- + if ((idx < 8) || (idx > 15)) {
- + pr_err("This grouped class queue id is out of range\n");
- + return -EINVAL;
- + }
- +
- + list_for_each_entry(p, &channel->class_queues, node) {
- + if (p->idx == idx) {
- + pr_err("The CQ#%d has been claimed!\n", idx);
- + return -EINVAL;
- + }
- + }
- +
- + p = kmalloc(sizeof(*p), GFP_KERNEL);
- + if (!p) {
- + pr_err("Can't allocate memory for CQ#%d!\n", idx);
- + return -ENOMEM;
- + }
- +
- + list_add_tail(&p->node, &channel->class_queues);
- + p->idx = idx;
- + p->is_claimed = 1;
- + p->parent = channel;
- + INIT_LIST_HEAD(&p->bound_lfqids);
- +
- + if (ccg) {
- + cq_config.cqid = cpu_to_be16((channel->idx << 4) | idx);
- + cq_config.dcpid = channel->dcp_idx;
- + cq_config.ccgid = cpu_to_be16(ccg->idx);
- + if (qman_ceetm_configure_cq(&cq_config)) {
- + pr_err("Can't configure the CQ#%d with CCGRID#%d\n",
- + idx, ccg->idx);
- + list_del(&p->node);
- + kfree(p);
- + return -EINVAL;
- + }
- + }
- + *cq = p;
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_cq_claim_A);
- +
- +int qman_ceetm_cq_claim_B(struct qm_ceetm_cq **cq,
- + struct qm_ceetm_channel *channel, unsigned int idx,
- + struct qm_ceetm_ccg *ccg)
- +{
- + struct qm_ceetm_cq *p;
- + struct qm_mcc_ceetm_cq_config cq_config;
- +
- + if ((idx < 12) || (idx > 15)) {
- + pr_err("This grouped class queue id is out of range\n");
- + return -EINVAL;
- + }
- +
- + list_for_each_entry(p, &channel->class_queues, node) {
- + if (p->idx == idx) {
- + pr_err("The CQ#%d has been claimed!\n", idx);
- + return -EINVAL;
- + }
- + }
- +
- + p = kmalloc(sizeof(*p), GFP_KERNEL);
- + if (!p) {
- + pr_err("Can't allocate memory for CQ#%d!\n", idx);
- + return -ENOMEM;
- + }
- +
- + list_add_tail(&p->node, &channel->class_queues);
- + p->idx = idx;
- + p->is_claimed = 1;
- + p->parent = channel;
- + INIT_LIST_HEAD(&p->bound_lfqids);
- +
- + if (ccg) {
- + cq_config.cqid = cpu_to_be16((channel->idx << 4) | idx);
- + cq_config.dcpid = channel->dcp_idx;
- + cq_config.ccgid = cpu_to_be16(ccg->idx);
- + if (qman_ceetm_configure_cq(&cq_config)) {
- + pr_err("Can't configure the CQ#%d with CCGRID#%d\n",
- + idx, ccg->idx);
- + list_del(&p->node);
- + kfree(p);
- + return -EINVAL;
- + }
- + }
- + *cq = p;
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_cq_claim_B);
- +
- +int qman_ceetm_cq_release(struct qm_ceetm_cq *cq)
- +{
- + if (!list_empty(&cq->bound_lfqids)) {
- + pr_err("The CQ#%d has unreleased LFQID\n", cq->idx);
- + return -EBUSY;
- + }
- + list_del(&cq->node);
- + qman_ceetm_drain_cq(cq);
- + kfree(cq);
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_cq_release);
- +
- +int qman_ceetm_set_queue_weight(struct qm_ceetm_cq *cq,
- + struct qm_ceetm_weight_code *weight_code)
- +{
- + struct qm_mcc_ceetm_class_scheduler_config config_opts;
- + struct qm_mcr_ceetm_class_scheduler_query query_result;
- + int i;
- +
- + if (cq->idx < 8) {
- + pr_err("Can not set weight for ungrouped class queue\n");
- + return -EINVAL;
- + }
- +
- + if (qman_ceetm_query_class_scheduler(cq->parent, &query_result)) {
- + pr_err("Can't query channel#%d's scheduler!\n",
- + cq->parent->idx);
- + return -EINVAL;
- + }
- +
- + config_opts.cqcid = cpu_to_be16(cq->parent->idx);
- + config_opts.dcpid = cq->parent->dcp_idx;
- + config_opts.crem = query_result.crem;
- + config_opts.erem = query_result.erem;
- + config_opts.gpc_combine_flag = query_result.gpc_combine_flag;
- + config_opts.gpc_prio_a = query_result.gpc_prio_a;
- + config_opts.gpc_prio_b = query_result.gpc_prio_b;
- +
- + for (i = 0; i < 8; i++)
- + config_opts.w[i] = query_result.w[i];
- + config_opts.w[cq->idx - 8] = ((weight_code->y << 3) |
- + (weight_code->x & 0x7));
- + return qman_ceetm_configure_class_scheduler(&config_opts);
- +}
- +EXPORT_SYMBOL(qman_ceetm_set_queue_weight);
- +
- +int qman_ceetm_get_queue_weight(struct qm_ceetm_cq *cq,
- + struct qm_ceetm_weight_code *weight_code)
- +{
- + struct qm_mcr_ceetm_class_scheduler_query query_result;
- +
- + if (cq->idx < 8) {
- + pr_err("Can not get weight for ungrouped class queue\n");
- + return -EINVAL;
- + }
- +
- + if (qman_ceetm_query_class_scheduler(cq->parent,
- + &query_result)) {
- + pr_err("Can't get the weight code for CQ#%d!\n", cq->idx);
- + return -EINVAL;
- + }
- + weight_code->y = query_result.w[cq->idx - 8] >> 3;
- + weight_code->x = query_result.w[cq->idx - 8] & 0x7;
- +
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_get_queue_weight);
- +
- +/* The WBFS code is represent as {x,y}, the effect wieght can be calculated as:
- + * effective weight = 2^x / (1 - (y/64))
- + * = 2^(x+6) / (64 - y)
- + */
- +static void reduce_fraction(u32 *n, u32 *d)
- +{
- + u32 factor = 2;
- + u32 lesser = (*n < *d) ? *n : *d;
- + /* If factor exceeds the square-root of the lesser of *n and *d,
- + * then there's no point continuing. Proof: if there was a factor
- + * bigger than the square root, that would imply there exists
- + * another factor smaller than the square-root with which it
- + * multiplies to give 'lesser' - but that's a contradiction
- + * because the other factor would have already been found and
- + * divided out.
- + */
- + while ((factor * factor) <= lesser) {
- + /* If 'factor' is a factor of *n and *d, divide them both
- + * by 'factor' as many times as possible.
- + */
- + while (!(*n % factor) && !(*d % factor)) {
- + *n /= factor;
- + *d /= factor;
- + lesser /= factor;
- + }
- + if (factor == 2)
- + factor = 3;
- + else
- + factor += 2;
- + }
- +}
- +
- +int qman_ceetm_wbfs2ratio(struct qm_ceetm_weight_code *weight_code,
- + u32 *numerator,
- + u32 *denominator)
- +{
- + *numerator = (u32) 1 << (weight_code->x + 6);
- + *denominator = 64 - weight_code->y;
- + reduce_fraction(numerator, denominator);
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_wbfs2ratio);
- +
- +/* For a given x, the weight is between 2^x (inclusive) and 2^(x+1) (exclusive).
- + * So find 'x' by range, and then estimate 'y' using:
- + * 64 - y = 2^(x + 6) / weight
- + * = 2^(x + 6) / (n/d)
- + * = d * 2^(x+6) / n
- + * y = 64 - (d * 2^(x+6) / n)
- + */
- +int qman_ceetm_ratio2wbfs(u32 numerator,
- + u32 denominator,
- + struct qm_ceetm_weight_code *weight_code,
- + int rounding)
- +{
- + unsigned int y, x = 0;
- + /* search incrementing 'x' until:
- + * weight < 2^(x+1)
- + * n/d < 2^(x+1)
- + * n < d * 2^(x+1)
- + */
- + while ((x < 8) && (numerator >= (denominator << (x + 1))))
- + x++;
- + if (x >= 8)
- + return -ERANGE;
- + /* because of the subtraction, use '-rounding' */
- + y = 64 - ROUNDING(denominator << (x + 6), numerator, -rounding);
- + if (y >= 32)
- + return -ERANGE;
- + weight_code->x = x;
- + weight_code->y = y;
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_ratio2wbfs);
- +
- +int qman_ceetm_set_queue_weight_in_ratio(struct qm_ceetm_cq *cq, u32 ratio)
- +{
- + struct qm_ceetm_weight_code weight_code;
- +
- + if (qman_ceetm_ratio2wbfs(ratio, 100, &weight_code, 0)) {
- + pr_err("Cannot get wbfs code for cq %x\n", cq->idx);
- + return -EINVAL;
- + }
- + return qman_ceetm_set_queue_weight(cq, &weight_code);
- +}
- +EXPORT_SYMBOL(qman_ceetm_set_queue_weight_in_ratio);
- +
- +int qman_ceetm_get_queue_weight_in_ratio(struct qm_ceetm_cq *cq, u32 *ratio)
- +{
- + struct qm_ceetm_weight_code weight_code;
- + u32 n, d;
- +
- + if (qman_ceetm_get_queue_weight(cq, &weight_code)) {
- + pr_err("Cannot query the weight code for cq%x\n", cq->idx);
- + return -EINVAL;
- + }
- +
- + if (qman_ceetm_wbfs2ratio(&weight_code, &n, &d)) {
- + pr_err("Cannot get the ratio with wbfs code\n");
- + return -EINVAL;
- + }
- +
- + *ratio = (n * (u32)100) / d;
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_get_queue_weight_in_ratio);
- +
- +int qman_ceetm_cq_get_dequeue_statistics(struct qm_ceetm_cq *cq, u32 flags,
- + u64 *frame_count, u64 *byte_count)
- +{
- + struct qm_mcr_ceetm_statistics_query result;
- + u16 cid, command_type;
- + enum qm_dc_portal dcp_idx;
- + int ret;
- +
- + cid = cpu_to_be16((cq->parent->idx << 4) | cq->idx);
- + dcp_idx = cq->parent->dcp_idx;
- + if (flags == QMAN_CEETM_FLAG_CLEAR_STATISTICS_COUNTER)
- + command_type = CEETM_QUERY_DEQUEUE_CLEAR_STATISTICS;
- + else
- + command_type = CEETM_QUERY_DEQUEUE_STATISTICS;
- +
- + ret = qman_ceetm_query_statistics(cid, dcp_idx, command_type, &result);
- + if (ret) {
- + pr_err("Can't query the statistics of CQ#%d!\n", cq->idx);
- + return -EINVAL;
- + }
- +
- + *frame_count = be40_to_cpu(result.frm_cnt);
- + *byte_count = be48_to_cpu(result.byte_cnt);
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_cq_get_dequeue_statistics);
- +
- +int qman_ceetm_drain_cq(struct qm_ceetm_cq *cq)
- +{
- + struct qm_mcr_ceetm_cq_peek_pop_xsfdrread ppxr;
- + int ret;
- +
- + do {
- + ret = qman_ceetm_cq_peek_pop_xsfdrread(cq, 1, 0, &ppxr);
- + if (ret) {
- + pr_err("Failed to pop frame from CQ\n");
- + return -EINVAL;
- + }
- + } while (!(ppxr.stat & 0x2));
- +
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_drain_cq);
- +
- +#define CEETM_LFQMT_LFQID_MSB 0xF00000
- +#define CEETM_LFQMT_LFQID_LSB 0x000FFF
- +int qman_ceetm_lfq_claim(struct qm_ceetm_lfq **lfq,
- + struct qm_ceetm_cq *cq)
- +{
- + struct qm_ceetm_lfq *p;
- + u32 lfqid;
- + int ret = 0;
- + struct qm_mcc_ceetm_lfqmt_config lfqmt_config;
- +
- + if (cq->parent->dcp_idx == qm_dc_portal_fman0) {
- + ret = qman_alloc_ceetm0_lfqid(&lfqid);
- + } else if (cq->parent->dcp_idx == qm_dc_portal_fman1) {
- + ret = qman_alloc_ceetm1_lfqid(&lfqid);
- + } else {
- + pr_err("dcp_idx %u does not correspond to a known fman in this driver\n",
- + cq->parent->dcp_idx);
- + return -EINVAL;
- + }
- +
- + if (ret) {
- + pr_err("There is no lfqid avalaible for CQ#%d!\n", cq->idx);
- + return -ENODEV;
- + }
- + p = kmalloc(sizeof(*p), GFP_KERNEL);
- + if (!p)
- + return -ENOMEM;
- + p->idx = lfqid;
- + p->dctidx = (u16)(lfqid & CEETM_LFQMT_LFQID_LSB);
- + p->parent = cq->parent;
- + list_add_tail(&p->node, &cq->bound_lfqids);
- +
- + lfqmt_config.lfqid = cpu_to_be24(CEETM_LFQMT_LFQID_MSB |
- + (cq->parent->dcp_idx << 16) |
- + (lfqid & CEETM_LFQMT_LFQID_LSB));
- + lfqmt_config.cqid = cpu_to_be16((cq->parent->idx << 4) | (cq->idx));
- + lfqmt_config.dctidx = cpu_to_be16(p->dctidx);
- + if (qman_ceetm_configure_lfqmt(&lfqmt_config)) {
- + pr_err("Can't configure LFQMT for LFQID#%d @ CQ#%d\n",
- + lfqid, cq->idx);
- + list_del(&p->node);
- + kfree(p);
- + return -EINVAL;
- + }
- + *lfq = p;
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_lfq_claim);
- +
- +int qman_ceetm_lfq_release(struct qm_ceetm_lfq *lfq)
- +{
- + if (lfq->parent->dcp_idx == qm_dc_portal_fman0) {
- + qman_release_ceetm0_lfqid(lfq->idx);
- + } else if (lfq->parent->dcp_idx == qm_dc_portal_fman1) {
- + qman_release_ceetm1_lfqid(lfq->idx);
- + } else {
- + pr_err("dcp_idx %u does not correspond to a known fman in this driver\n",
- + lfq->parent->dcp_idx);
- + return -EINVAL;
- + }
- + list_del(&lfq->node);
- + kfree(lfq);
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_lfq_release);
- +
- +int qman_ceetm_lfq_set_context(struct qm_ceetm_lfq *lfq, u64 context_a,
- + u32 context_b)
- +{
- + struct qm_mcc_ceetm_dct_config dct_config;
- + lfq->context_a = context_a;
- + lfq->context_b = context_b;
- + dct_config.dctidx = cpu_to_be16(lfq->dctidx);
- + dct_config.dcpid = lfq->parent->dcp_idx;
- + dct_config.context_b = cpu_to_be32(context_b);
- + dct_config.context_a = cpu_to_be64(context_a);
- + return qman_ceetm_configure_dct(&dct_config);
- +}
- +EXPORT_SYMBOL(qman_ceetm_lfq_set_context);
- +
- +int qman_ceetm_lfq_get_context(struct qm_ceetm_lfq *lfq, u64 *context_a,
- + u32 *context_b)
- +{
- + struct qm_mcc_ceetm_dct_query dct_query;
- + struct qm_mcr_ceetm_dct_query query_result;
- +
- + dct_query.dctidx = cpu_to_be16(lfq->dctidx);
- + dct_query.dcpid = lfq->parent->dcp_idx;
- + if (qman_ceetm_query_dct(&dct_query, &query_result)) {
- + pr_err("Can't query LFQID#%d's context!\n", lfq->idx);
- + return -EINVAL;
- + }
- + *context_a = be64_to_cpu(query_result.context_a);
- + *context_b = be32_to_cpu(query_result.context_b);
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_lfq_get_context);
- +
- +int qman_ceetm_create_fq(struct qm_ceetm_lfq *lfq, struct qman_fq *fq)
- +{
- + spin_lock_init(&fq->fqlock);
- + fq->fqid = lfq->idx;
- + fq->flags = QMAN_FQ_FLAG_NO_MODIFY;
- + if (lfq->ern)
- + fq->cb.ern = lfq->ern;
- +#ifdef CONFIG_FSL_QMAN_FQ_LOOKUP
- + if (unlikely(find_empty_fq_table_entry(&fq->key, fq)))
- + return -ENOMEM;
- +#endif
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_create_fq);
- +
- +int qman_ceetm_ccg_claim(struct qm_ceetm_ccg **ccg,
- + struct qm_ceetm_channel *channel,
- + unsigned int idx,
- + void (*cscn)(struct qm_ceetm_ccg *,
- + void *cb_ctx,
- + int congested),
- + void *cb_ctx)
- +{
- + struct qm_ceetm_ccg *p;
- +
- + if (idx > 15) {
- + pr_err("The given ccg index is out of range\n");
- + return -EINVAL;
- + }
- +
- + list_for_each_entry(p, &channel->ccgs, node) {
- + if (p->idx == idx) {
- + pr_err("The CCG#%d has been claimed\n", idx);
- + return -EINVAL;
- + }
- + }
- +
- + p = kmalloc(sizeof(*p), GFP_KERNEL);
- + if (!p) {
- + pr_err("Can't allocate memory for CCG#%d!\n", idx);
- + return -ENOMEM;
- + }
- +
- + list_add_tail(&p->node, &channel->ccgs);
- +
- + p->idx = idx;
- + p->parent = channel;
- + p->cb = cscn;
- + p->cb_ctx = cb_ctx;
- + INIT_LIST_HEAD(&p->cb_node);
- +
- + *ccg = p;
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_ccg_claim);
- +
- +int qman_ceetm_ccg_release(struct qm_ceetm_ccg *ccg)
- +{
- + unsigned long irqflags __maybe_unused;
- + struct qm_mcc_ceetm_ccgr_config config_opts;
- + int ret = 0;
- + struct qman_portal *p = get_affine_portal();
- +
- + memset(&config_opts, 0, sizeof(struct qm_mcc_ceetm_ccgr_config));
- + spin_lock_irqsave(&p->ccgr_lock, irqflags);
- + if (!list_empty(&ccg->cb_node))
- + list_del(&ccg->cb_node);
- + config_opts.ccgrid = cpu_to_be16(CEETM_CCGR_CM_CONFIGURE |
- + (ccg->parent->idx << 4) | ccg->idx);
- + config_opts.dcpid = ccg->parent->dcp_idx;
- + config_opts.we_mask = cpu_to_be16(QM_CCGR_WE_CSCN_TUPD);
- + config_opts.cm_config.cscn_tupd = cpu_to_be16(PORTAL_IDX(p));
- + ret = qman_ceetm_configure_ccgr(&config_opts);
- + spin_unlock_irqrestore(&p->ccgr_lock, irqflags);
- + put_affine_portal();
- +
- + list_del(&ccg->node);
- + kfree(ccg);
- + return ret;
- +}
- +EXPORT_SYMBOL(qman_ceetm_ccg_release);
- +
- +int qman_ceetm_ccg_set(struct qm_ceetm_ccg *ccg, u16 we_mask,
- + const struct qm_ceetm_ccg_params *params)
- +{
- + struct qm_mcc_ceetm_ccgr_config config_opts;
- + unsigned long irqflags __maybe_unused;
- + int ret;
- + struct qman_portal *p;
- +
- + if (((ccg->parent->idx << 4) | ccg->idx) >= (2 * __CGR_NUM))
- + return -EINVAL;
- +
- + p = get_affine_portal();
- +
- + memset(&config_opts, 0, sizeof(struct qm_mcc_ceetm_ccgr_config));
- + spin_lock_irqsave(&p->ccgr_lock, irqflags);
- +
- + config_opts.ccgrid = cpu_to_be16(CEETM_CCGR_CM_CONFIGURE |
- + (ccg->parent->idx << 4) | ccg->idx);
- + config_opts.dcpid = ccg->parent->dcp_idx;
- + config_opts.we_mask = we_mask;
- + if (we_mask & QM_CCGR_WE_CSCN_EN) {
- + config_opts.we_mask |= QM_CCGR_WE_CSCN_TUPD;
- + config_opts.cm_config.cscn_tupd = cpu_to_be16(
- + QM_CGR_TARG_UDP_CTRL_WRITE_BIT | PORTAL_IDX(p));
- + }
- + config_opts.we_mask = cpu_to_be16(config_opts.we_mask);
- + config_opts.cm_config.ctl_wr_en_g = params->wr_en_g;
- + config_opts.cm_config.ctl_wr_en_y = params->wr_en_y;
- + config_opts.cm_config.ctl_wr_en_r = params->wr_en_r;
- + config_opts.cm_config.ctl_td_en = params->td_en;
- + config_opts.cm_config.ctl_td_mode = params->td_mode;
- + config_opts.cm_config.ctl_cscn_en = params->cscn_en;
- + config_opts.cm_config.ctl_mode = params->mode;
- + config_opts.cm_config.oal = params->oal;
- + config_opts.cm_config.cs_thres.hword =
- + cpu_to_be16(params->cs_thres_in.hword);
- + config_opts.cm_config.cs_thres_x.hword =
- + cpu_to_be16(params->cs_thres_out.hword);
- + config_opts.cm_config.td_thres.hword =
- + cpu_to_be16(params->td_thres.hword);
- + config_opts.cm_config.wr_parm_g.word =
- + cpu_to_be32(params->wr_parm_g.word);
- + config_opts.cm_config.wr_parm_y.word =
- + cpu_to_be32(params->wr_parm_y.word);
- + config_opts.cm_config.wr_parm_r.word =
- + cpu_to_be32(params->wr_parm_r.word);
- + ret = qman_ceetm_configure_ccgr(&config_opts);
- + if (ret) {
- + pr_err("Configure CCGR CM failed!\n");
- + goto release_lock;
- + }
- +
- + if (we_mask & QM_CCGR_WE_CSCN_EN)
- + if (list_empty(&ccg->cb_node))
- + list_add(&ccg->cb_node,
- + &p->ccgr_cbs[ccg->parent->dcp_idx]);
- +release_lock:
- + spin_unlock_irqrestore(&p->ccgr_lock, irqflags);
- + put_affine_portal();
- + return ret;
- +}
- +EXPORT_SYMBOL(qman_ceetm_ccg_set);
- +
- +#define CEETM_CCGR_CTL_MASK 0x01
- +int qman_ceetm_ccg_get(struct qm_ceetm_ccg *ccg,
- + struct qm_ceetm_ccg_params *params)
- +{
- + struct qm_mcc_ceetm_ccgr_query query_opts;
- + struct qm_mcr_ceetm_ccgr_query query_result;
- +
- + query_opts.ccgrid = cpu_to_be16(CEETM_CCGR_CM_QUERY |
- + (ccg->parent->idx << 4) | ccg->idx);
- + query_opts.dcpid = ccg->parent->dcp_idx;
- +
- + if (qman_ceetm_query_ccgr(&query_opts, &query_result)) {
- + pr_err("Can't query CCGR#%d\n", ccg->idx);
- + return -EINVAL;
- + }
- +
- + params->wr_parm_r.word =
- + be32_to_cpu(query_result.cm_query.wr_parm_r.word);
- + params->wr_parm_y.word =
- + be32_to_cpu(query_result.cm_query.wr_parm_y.word);
- + params->wr_parm_g.word =
- + be32_to_cpu(query_result.cm_query.wr_parm_g.word);
- + params->td_thres.hword =
- + be16_to_cpu(query_result.cm_query.td_thres.hword);
- + params->cs_thres_out.hword =
- + be16_to_cpu(query_result.cm_query.cs_thres_x.hword);
- + params->cs_thres_in.hword =
- + be16_to_cpu(query_result.cm_query.cs_thres.hword);
- + params->oal = query_result.cm_query.oal;
- + params->wr_en_g = query_result.cm_query.ctl_wr_en_g;
- + params->wr_en_y = query_result.cm_query.ctl_wr_en_y;
- + params->wr_en_r = query_result.cm_query.ctl_wr_en_r;
- + params->td_en = query_result.cm_query.ctl_td_en;
- + params->td_mode = query_result.cm_query.ctl_td_mode;
- + params->cscn_en = query_result.cm_query.ctl_cscn_en;
- + params->mode = query_result.cm_query.ctl_mode;
- +
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_ccg_get);
- +
- +int qman_ceetm_ccg_get_reject_statistics(struct qm_ceetm_ccg *ccg, u32 flags,
- + u64 *frame_count, u64 *byte_count)
- +{
- + struct qm_mcr_ceetm_statistics_query result;
- + u16 cid, command_type;
- + enum qm_dc_portal dcp_idx;
- + int ret;
- +
- + cid = cpu_to_be16((ccg->parent->idx << 4) | ccg->idx);
- + dcp_idx = ccg->parent->dcp_idx;
- + if (flags == QMAN_CEETM_FLAG_CLEAR_STATISTICS_COUNTER)
- + command_type = CEETM_QUERY_REJECT_CLEAR_STATISTICS;
- + else
- + command_type = CEETM_QUERY_REJECT_STATISTICS;
- +
- + ret = qman_ceetm_query_statistics(cid, dcp_idx, command_type, &result);
- + if (ret) {
- + pr_err("Can't query the statistics of CCG#%d!\n", ccg->idx);
- + return -EINVAL;
- + }
- +
- + *frame_count = be40_to_cpu(result.frm_cnt);
- + *byte_count = be48_to_cpu(result.byte_cnt);
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_ccg_get_reject_statistics);
- +
- +int qman_ceetm_cscn_swp_get(struct qm_ceetm_ccg *ccg,
- + u16 swp_idx,
- + unsigned int *cscn_enabled)
- +{
- + struct qm_mcc_ceetm_ccgr_query query_opts;
- + struct qm_mcr_ceetm_ccgr_query query_result;
- + int i;
- +
- + DPA_ASSERT(swp_idx < 127);
- + query_opts.ccgrid = cpu_to_be16(CEETM_CCGR_CM_QUERY |
- + (ccg->parent->idx << 4) | ccg->idx);
- + query_opts.dcpid = ccg->parent->dcp_idx;
- +
- + if (qman_ceetm_query_ccgr(&query_opts, &query_result)) {
- + pr_err("Can't query CCGR#%d\n", ccg->idx);
- + return -EINVAL;
- + }
- +
- + i = swp_idx / 32;
- + i = 3 - i;
- + *cscn_enabled = be32_to_cpu(query_result.cm_query.cscn_targ_swp[i]) >>
- + (31 - swp_idx % 32);
- +
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_cscn_swp_get);
- +
- +int qman_ceetm_cscn_dcp_set(struct qm_ceetm_ccg *ccg,
- + u16 dcp_idx,
- + u8 vcgid,
- + unsigned int cscn_enabled,
- + u16 we_mask,
- + const struct qm_ceetm_ccg_params *params)
- +{
- + struct qm_mcc_ceetm_ccgr_config config_opts;
- + int ret;
- +
- + config_opts.ccgrid = cpu_to_be16(CEETM_CCGR_CM_CONFIGURE |
- + (ccg->parent->idx << 4) | ccg->idx);
- + config_opts.dcpid = ccg->parent->dcp_idx;
- + config_opts.we_mask = cpu_to_be16(we_mask | QM_CCGR_WE_CSCN_TUPD |
- + QM_CCGR_WE_CDV);
- + config_opts.cm_config.cdv = vcgid;
- + config_opts.cm_config.cscn_tupd = cpu_to_be16((cscn_enabled << 15) |
- + QM_CGR_TARG_UDP_CTRL_DCP | dcp_idx);
- + config_opts.cm_config.ctl_wr_en_g = params->wr_en_g;
- + config_opts.cm_config.ctl_wr_en_y = params->wr_en_y;
- + config_opts.cm_config.ctl_wr_en_r = params->wr_en_r;
- + config_opts.cm_config.ctl_td_en = params->td_en;
- + config_opts.cm_config.ctl_td_mode = params->td_mode;
- + config_opts.cm_config.ctl_cscn_en = params->cscn_en;
- + config_opts.cm_config.ctl_mode = params->mode;
- + config_opts.cm_config.cs_thres.hword =
- + cpu_to_be16(params->cs_thres_in.hword);
- + config_opts.cm_config.cs_thres_x.hword =
- + cpu_to_be16(params->cs_thres_out.hword);
- + config_opts.cm_config.td_thres.hword =
- + cpu_to_be16(params->td_thres.hword);
- + config_opts.cm_config.wr_parm_g.word =
- + cpu_to_be32(params->wr_parm_g.word);
- + config_opts.cm_config.wr_parm_y.word =
- + cpu_to_be32(params->wr_parm_y.word);
- + config_opts.cm_config.wr_parm_r.word =
- + cpu_to_be32(params->wr_parm_r.word);
- +
- + ret = qman_ceetm_configure_ccgr(&config_opts);
- + if (ret) {
- + pr_err("Configure CSCN_TARG_DCP failed!\n");
- + return -EINVAL;
- + }
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_cscn_dcp_set);
- +
- +int qman_ceetm_cscn_dcp_get(struct qm_ceetm_ccg *ccg,
- + u16 dcp_idx,
- + u8 *vcgid,
- + unsigned int *cscn_enabled)
- +{
- + struct qm_mcc_ceetm_ccgr_query query_opts;
- + struct qm_mcr_ceetm_ccgr_query query_result;
- +
- + query_opts.ccgrid = cpu_to_be16(CEETM_CCGR_CM_QUERY |
- + (ccg->parent->idx << 4) | ccg->idx);
- + query_opts.dcpid = ccg->parent->dcp_idx;
- +
- + if (qman_ceetm_query_ccgr(&query_opts, &query_result)) {
- + pr_err("Can't query CCGR#%d\n", ccg->idx);
- + return -EINVAL;
- + }
- +
- + *vcgid = query_result.cm_query.cdv;
- + *cscn_enabled = (be16_to_cpu(query_result.cm_query.cscn_targ_dcp >>
- + dcp_idx)) & 0x1;
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_ceetm_cscn_dcp_get);
- +
- +int qman_ceetm_querycongestion(struct __qm_mcr_querycongestion *ccg_state,
- + unsigned int dcp_idx)
- +{
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + struct qman_portal *p;
- + unsigned long irqflags __maybe_unused;
- + u8 res;
- + int i, j;
- +
- + p = get_affine_portal();
- + PORTAL_IRQ_LOCK(p, irqflags);
- +
- + mcc = qm_mc_start(&p->p);
- + for (i = 0; i < 2; i++) {
- + mcc->ccgr_query.ccgrid =
- + cpu_to_be16(CEETM_QUERY_CONGESTION_STATE | i);
- + mcc->ccgr_query.dcpid = dcp_idx;
- + qm_mc_commit(&p->p, QM_CEETM_VERB_CCGR_QUERY);
- +
- + while (!(mcr = qm_mc_result(&p->p)))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) ==
- + QM_CEETM_VERB_CCGR_QUERY);
- + res = mcr->result;
- + if (res == QM_MCR_RESULT_OK) {
- + for (j = 0; j < 8; j++)
- + mcr->ccgr_query.congestion_state.state.
- + __state[j] =
- + be32_to_cpu(mcr->ccgr_query.
- + congestion_state.state.__state[j]);
- +
- + *(ccg_state + i) =
- + mcr->ccgr_query.congestion_state.state;
- + } else {
- + pr_err("QUERY CEETM CONGESTION STATE failed\n");
- + return -EIO;
- + }
- + }
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- + return 0;
- +}
- +
- +int qman_set_wpm(int wpm_enable)
- +{
- + return qm_set_wpm(wpm_enable);
- +}
- +EXPORT_SYMBOL(qman_set_wpm);
- +
- +int qman_get_wpm(int *wpm_enable)
- +{
- + return qm_get_wpm(wpm_enable);
- +}
- +EXPORT_SYMBOL(qman_get_wpm);
- +
- +int qman_shutdown_fq(u32 fqid)
- +{
- + struct qman_portal *p;
- + unsigned long irqflags __maybe_unused;
- + int ret;
- + struct qm_portal *low_p;
- + p = get_affine_portal();
- + PORTAL_IRQ_LOCK(p, irqflags);
- + low_p = &p->p;
- + ret = qm_shutdown_fq(&low_p, 1, fqid);
- + PORTAL_IRQ_UNLOCK(p, irqflags);
- + put_affine_portal();
- + return ret;
- +}
- +
- +const struct qm_portal_config *qman_get_qm_portal_config(
- + struct qman_portal *portal)
- +{
- + return portal->sharing_redirect ? NULL : portal->config;
- +}
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/qman_low.h
- @@ -0,0 +1,1407 @@
- +/* Copyright 2008-2011 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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 "qman_private.h"
- +
- +/***************************/
- +/* Portal register assists */
- +/***************************/
- +
- +/* Cache-inhibited register offsets */
- +#if defined(CONFIG_PPC32) || defined(CONFIG_PPC64)
- +
- +#define QM_REG_EQCR_PI_CINH 0x0000
- +#define QM_REG_EQCR_CI_CINH 0x0004
- +#define QM_REG_EQCR_ITR 0x0008
- +#define QM_REG_DQRR_PI_CINH 0x0040
- +#define QM_REG_DQRR_CI_CINH 0x0044
- +#define QM_REG_DQRR_ITR 0x0048
- +#define QM_REG_DQRR_DCAP 0x0050
- +#define QM_REG_DQRR_SDQCR 0x0054
- +#define QM_REG_DQRR_VDQCR 0x0058
- +#define QM_REG_DQRR_PDQCR 0x005c
- +#define QM_REG_MR_PI_CINH 0x0080
- +#define QM_REG_MR_CI_CINH 0x0084
- +#define QM_REG_MR_ITR 0x0088
- +#define QM_REG_CFG 0x0100
- +#define QM_REG_ISR 0x0e00
- +#define QM_REG_IIR 0x0e0c
- +#define QM_REG_ITPR 0x0e14
- +
- +/* Cache-enabled register offsets */
- +#define QM_CL_EQCR 0x0000
- +#define QM_CL_DQRR 0x1000
- +#define QM_CL_MR 0x2000
- +#define QM_CL_EQCR_PI_CENA 0x3000
- +#define QM_CL_EQCR_CI_CENA 0x3100
- +#define QM_CL_DQRR_PI_CENA 0x3200
- +#define QM_CL_DQRR_CI_CENA 0x3300
- +#define QM_CL_MR_PI_CENA 0x3400
- +#define QM_CL_MR_CI_CENA 0x3500
- +#define QM_CL_CR 0x3800
- +#define QM_CL_RR0 0x3900
- +#define QM_CL_RR1 0x3940
- +
- +#endif
- +
- +#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
- +
- +#define QM_REG_EQCR_PI_CINH 0x3000
- +#define QM_REG_EQCR_CI_CINH 0x3040
- +#define QM_REG_EQCR_ITR 0x3080
- +#define QM_REG_DQRR_PI_CINH 0x3100
- +#define QM_REG_DQRR_CI_CINH 0x3140
- +#define QM_REG_DQRR_ITR 0x3180
- +#define QM_REG_DQRR_DCAP 0x31C0
- +#define QM_REG_DQRR_SDQCR 0x3200
- +#define QM_REG_DQRR_VDQCR 0x3240
- +#define QM_REG_DQRR_PDQCR 0x3280
- +#define QM_REG_MR_PI_CINH 0x3300
- +#define QM_REG_MR_CI_CINH 0x3340
- +#define QM_REG_MR_ITR 0x3380
- +#define QM_REG_CFG 0x3500
- +#define QM_REG_ISR 0x3600
- +#define QM_REG_IIR 0x36C0
- +#define QM_REG_ITPR 0x3740
- +
- +/* Cache-enabled register offsets */
- +#define QM_CL_EQCR 0x0000
- +#define QM_CL_DQRR 0x1000
- +#define QM_CL_MR 0x2000
- +#define QM_CL_EQCR_PI_CENA 0x3000
- +#define QM_CL_EQCR_CI_CENA 0x3040
- +#define QM_CL_DQRR_PI_CENA 0x3100
- +#define QM_CL_DQRR_CI_CENA 0x3140
- +#define QM_CL_MR_PI_CENA 0x3300
- +#define QM_CL_MR_CI_CENA 0x3340
- +#define QM_CL_CR 0x3800
- +#define QM_CL_RR0 0x3900
- +#define QM_CL_RR1 0x3940
- +
- +#endif
- +
- +
- +/* BTW, the drivers (and h/w programming model) already obtain the required
- + * synchronisation for portal accesses via lwsync(), hwsync(), and
- + * data-dependencies. Use of barrier()s or other order-preserving primitives
- + * simply degrade performance. Hence the use of the __raw_*() interfaces, which
- + * simply ensure that the compiler treats the portal registers as volatile (ie.
- + * non-coherent). */
- +
- +/* Cache-inhibited register access. */
- +#define __qm_in(qm, o) be32_to_cpu(__raw_readl((qm)->addr_ci + (o)))
- +#define __qm_out(qm, o, val) __raw_writel((cpu_to_be32(val)), \
- + (qm)->addr_ci + (o));
- +#define qm_in(reg) __qm_in(&portal->addr, QM_REG_##reg)
- +#define qm_out(reg, val) __qm_out(&portal->addr, QM_REG_##reg, val)
- +
- +/* Cache-enabled (index) register access */
- +#define __qm_cl_touch_ro(qm, o) dcbt_ro((qm)->addr_ce + (o))
- +#define __qm_cl_touch_rw(qm, o) dcbt_rw((qm)->addr_ce + (o))
- +#define __qm_cl_in(qm, o) be32_to_cpu(__raw_readl((qm)->addr_ce + (o)))
- +#define __qm_cl_out(qm, o, val) \
- + do { \
- + u32 *__tmpclout = (qm)->addr_ce + (o); \
- + __raw_writel(cpu_to_be32(val), __tmpclout); \
- + dcbf(__tmpclout); \
- + } while (0)
- +#define __qm_cl_invalidate(qm, o) dcbi((qm)->addr_ce + (o))
- +#define qm_cl_touch_ro(reg) __qm_cl_touch_ro(&portal->addr, QM_CL_##reg##_CENA)
- +#define qm_cl_touch_rw(reg) __qm_cl_touch_rw(&portal->addr, QM_CL_##reg##_CENA)
- +#define qm_cl_in(reg) __qm_cl_in(&portal->addr, QM_CL_##reg##_CENA)
- +#define qm_cl_out(reg, val) __qm_cl_out(&portal->addr, QM_CL_##reg##_CENA, val)
- +#define qm_cl_invalidate(reg)\
- + __qm_cl_invalidate(&portal->addr, QM_CL_##reg##_CENA)
- +
- +/* Cache-enabled ring access */
- +#define qm_cl(base, idx) ((void *)base + ((idx) << 6))
- +
- +/* Cyclic helper for rings. FIXME: once we are able to do fine-grain perf
- + * analysis, look at using the "extra" bit in the ring index registers to avoid
- + * cyclic issues. */
- +static inline u8 qm_cyc_diff(u8 ringsize, u8 first, u8 last)
- +{
- + /* 'first' is included, 'last' is excluded */
- + if (first <= last)
- + return last - first;
- + return ringsize + last - first;
- +}
- +
- +/* Portal modes.
- + * Enum types;
- + * pmode == production mode
- + * cmode == consumption mode,
- + * dmode == h/w dequeue mode.
- + * Enum values use 3 letter codes. First letter matches the portal mode,
- + * remaining two letters indicate;
- + * ci == cache-inhibited portal register
- + * ce == cache-enabled portal register
- + * vb == in-band valid-bit (cache-enabled)
- + * dc == DCA (Discrete Consumption Acknowledgement), DQRR-only
- + * As for "enum qm_dqrr_dmode", it should be self-explanatory.
- + */
- +enum qm_eqcr_pmode { /* matches QCSP_CFG::EPM */
- + qm_eqcr_pci = 0, /* PI index, cache-inhibited */
- + qm_eqcr_pce = 1, /* PI index, cache-enabled */
- + qm_eqcr_pvb = 2 /* valid-bit */
- +};
- +enum qm_dqrr_dmode { /* matches QCSP_CFG::DP */
- + qm_dqrr_dpush = 0, /* SDQCR + VDQCR */
- + qm_dqrr_dpull = 1 /* PDQCR */
- +};
- +enum qm_dqrr_pmode { /* s/w-only */
- + qm_dqrr_pci, /* reads DQRR_PI_CINH */
- + qm_dqrr_pce, /* reads DQRR_PI_CENA */
- + qm_dqrr_pvb /* reads valid-bit */
- +};
- +enum qm_dqrr_cmode { /* matches QCSP_CFG::DCM */
- + qm_dqrr_cci = 0, /* CI index, cache-inhibited */
- + qm_dqrr_cce = 1, /* CI index, cache-enabled */
- + qm_dqrr_cdc = 2 /* Discrete Consumption Acknowledgement */
- +};
- +enum qm_mr_pmode { /* s/w-only */
- + qm_mr_pci, /* reads MR_PI_CINH */
- + qm_mr_pce, /* reads MR_PI_CENA */
- + qm_mr_pvb /* reads valid-bit */
- +};
- +enum qm_mr_cmode { /* matches QCSP_CFG::MM */
- + qm_mr_cci = 0, /* CI index, cache-inhibited */
- + qm_mr_cce = 1 /* CI index, cache-enabled */
- +};
- +
- +
- +/* ------------------------- */
- +/* --- Portal structures --- */
- +
- +#define QM_EQCR_SIZE 8
- +#define QM_DQRR_SIZE 16
- +#define QM_MR_SIZE 8
- +
- +struct qm_eqcr {
- + struct qm_eqcr_entry *ring, *cursor;
- + u8 ci, available, ithresh, vbit;
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + u32 busy;
- + enum qm_eqcr_pmode pmode;
- +#endif
- +};
- +
- +struct qm_dqrr {
- + const struct qm_dqrr_entry *ring, *cursor;
- + u8 pi, ci, fill, ithresh, vbit;
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + enum qm_dqrr_dmode dmode;
- + enum qm_dqrr_pmode pmode;
- + enum qm_dqrr_cmode cmode;
- +#endif
- +};
- +
- +struct qm_mr {
- + const struct qm_mr_entry *ring, *cursor;
- + u8 pi, ci, fill, ithresh, vbit;
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + enum qm_mr_pmode pmode;
- + enum qm_mr_cmode cmode;
- +#endif
- +};
- +
- +struct qm_mc {
- + struct qm_mc_command *cr;
- + struct qm_mc_result *rr;
- + u8 rridx, vbit;
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + enum {
- + /* Can be _mc_start()ed */
- + qman_mc_idle,
- + /* Can be _mc_commit()ed or _mc_abort()ed */
- + qman_mc_user,
- + /* Can only be _mc_retry()ed */
- + qman_mc_hw
- + } state;
- +#endif
- +};
- +
- +#define QM_PORTAL_ALIGNMENT ____cacheline_aligned
- +
- +struct qm_addr {
- + void __iomem *addr_ce; /* cache-enabled */
- + void __iomem *addr_ci; /* cache-inhibited */
- +};
- +
- +struct qm_portal {
- + /* In the non-CONFIG_FSL_DPA_CHECKING case, the following stuff up to
- + * and including 'mc' fits within a cacheline (yay!). The 'config' part
- + * is setup-only, so isn't a cause for a concern. In other words, don't
- + * rearrange this structure on a whim, there be dragons ... */
- + struct qm_addr addr;
- + struct qm_eqcr eqcr;
- + struct qm_dqrr dqrr;
- + struct qm_mr mr;
- + struct qm_mc mc;
- +} QM_PORTAL_ALIGNMENT;
- +
- +
- +/* ---------------- */
- +/* --- EQCR API --- */
- +
- +/* Bit-wise logic to wrap a ring pointer by clearing the "carry bit" */
- +#define EQCR_CARRYCLEAR(p) \
- + (void *)((unsigned long)(p) & (~(unsigned long)(QM_EQCR_SIZE << 6)))
- +
- +/* Bit-wise logic to convert a ring pointer to a ring index */
- +static inline u8 EQCR_PTR2IDX(struct qm_eqcr_entry *e)
- +{
- + return ((uintptr_t)e >> 6) & (QM_EQCR_SIZE - 1);
- +}
- +
- +/* Increment the 'cursor' ring pointer, taking 'vbit' into account */
- +static inline void EQCR_INC(struct qm_eqcr *eqcr)
- +{
- + /* NB: this is odd-looking, but experiments show that it generates fast
- + * code with essentially no branching overheads. We increment to the
- + * next EQCR pointer and handle overflow and 'vbit'. */
- + struct qm_eqcr_entry *partial = eqcr->cursor + 1;
- + eqcr->cursor = EQCR_CARRYCLEAR(partial);
- + if (partial != eqcr->cursor)
- + eqcr->vbit ^= QM_EQCR_VERB_VBIT;
- +}
- +
- +static inline int qm_eqcr_init(struct qm_portal *portal,
- + enum qm_eqcr_pmode pmode,
- + unsigned int eq_stash_thresh,
- + int eq_stash_prio)
- +{
- + /* This use of 'register', as well as all other occurrences, is because
- + * it has been observed to generate much faster code with gcc than is
- + * otherwise the case. */
- + register struct qm_eqcr *eqcr = &portal->eqcr;
- + u32 cfg;
- + u8 pi;
- +
- + eqcr->ring = portal->addr.addr_ce + QM_CL_EQCR;
- + eqcr->ci = qm_in(EQCR_CI_CINH) & (QM_EQCR_SIZE - 1);
- + qm_cl_invalidate(EQCR_CI);
- + pi = qm_in(EQCR_PI_CINH) & (QM_EQCR_SIZE - 1);
- + eqcr->cursor = eqcr->ring + pi;
- + eqcr->vbit = (qm_in(EQCR_PI_CINH) & QM_EQCR_SIZE) ?
- + QM_EQCR_VERB_VBIT : 0;
- + eqcr->available = QM_EQCR_SIZE - 1 -
- + qm_cyc_diff(QM_EQCR_SIZE, eqcr->ci, pi);
- + eqcr->ithresh = qm_in(EQCR_ITR);
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + eqcr->busy = 0;
- + eqcr->pmode = pmode;
- +#endif
- + cfg = (qm_in(CFG) & 0x00ffffff) |
- + (eq_stash_thresh << 28) | /* QCSP_CFG: EST */
- + (eq_stash_prio << 26) | /* QCSP_CFG: EP */
- + ((pmode & 0x3) << 24); /* QCSP_CFG::EPM */
- + qm_out(CFG, cfg);
- + return 0;
- +}
- +
- +static inline unsigned int qm_eqcr_get_ci_stashing(struct qm_portal *portal)
- +{
- + return (qm_in(CFG) >> 28) & 0x7;
- +}
- +
- +static inline void qm_eqcr_finish(struct qm_portal *portal)
- +{
- + register struct qm_eqcr *eqcr = &portal->eqcr;
- + u8 pi, ci;
- + u32 cfg;
- +
- + /*
- + * Disable EQCI stashing because the QMan only
- + * presents the value it previously stashed to
- + * maintain coherency. Setting the stash threshold
- + * to 1 then 0 ensures that QMan has resyncronized
- + * its internal copy so that the portal is clean
- + * when it is reinitialized in the future
- + */
- + cfg = (qm_in(CFG) & 0x0fffffff) |
- + (1 << 28); /* QCSP_CFG: EST */
- + qm_out(CFG, cfg);
- + cfg &= 0x0fffffff; /* stash threshold = 0 */
- + qm_out(CFG, cfg);
- +
- + pi = qm_in(EQCR_PI_CINH) & (QM_EQCR_SIZE - 1);
- + ci = qm_in(EQCR_CI_CINH) & (QM_EQCR_SIZE - 1);
- +
- + /* Refresh EQCR CI cache value */
- + qm_cl_invalidate(EQCR_CI);
- + eqcr->ci = qm_cl_in(EQCR_CI) & (QM_EQCR_SIZE - 1);
- +
- + DPA_ASSERT(!eqcr->busy);
- + if (pi != EQCR_PTR2IDX(eqcr->cursor))
- + pr_crit("losing uncommited EQCR entries\n");
- + if (ci != eqcr->ci)
- + pr_crit("missing existing EQCR completions\n");
- + if (eqcr->ci != EQCR_PTR2IDX(eqcr->cursor))
- + pr_crit("EQCR destroyed unquiesced\n");
- +}
- +
- +static inline struct qm_eqcr_entry *qm_eqcr_start_no_stash(struct qm_portal
- + *portal)
- +{
- + register struct qm_eqcr *eqcr = &portal->eqcr;
- + DPA_ASSERT(!eqcr->busy);
- + if (!eqcr->available)
- + return NULL;
- +
- +
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + eqcr->busy = 1;
- +#endif
- + dcbz_64(eqcr->cursor);
- + return eqcr->cursor;
- +}
- +
- +static inline struct qm_eqcr_entry *qm_eqcr_start_stash(struct qm_portal
- + *portal)
- +{
- + register struct qm_eqcr *eqcr = &portal->eqcr;
- + u8 diff, old_ci;
- +
- + DPA_ASSERT(!eqcr->busy);
- + if (!eqcr->available) {
- + old_ci = eqcr->ci;
- + eqcr->ci = qm_cl_in(EQCR_CI) & (QM_EQCR_SIZE - 1);
- + diff = qm_cyc_diff(QM_EQCR_SIZE, old_ci, eqcr->ci);
- + eqcr->available += diff;
- + if (!diff)
- + return NULL;
- + }
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + eqcr->busy = 1;
- +#endif
- + dcbz_64(eqcr->cursor);
- + return eqcr->cursor;
- +}
- +
- +static inline void qm_eqcr_abort(struct qm_portal *portal)
- +{
- + __maybe_unused register struct qm_eqcr *eqcr = &portal->eqcr;
- + DPA_ASSERT(eqcr->busy);
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + eqcr->busy = 0;
- +#endif
- +}
- +
- +static inline struct qm_eqcr_entry *qm_eqcr_pend_and_next(
- + struct qm_portal *portal, u8 myverb)
- +{
- + register struct qm_eqcr *eqcr = &portal->eqcr;
- + DPA_ASSERT(eqcr->busy);
- + DPA_ASSERT(eqcr->pmode != qm_eqcr_pvb);
- + if (eqcr->available == 1)
- + return NULL;
- + eqcr->cursor->__dont_write_directly__verb = myverb | eqcr->vbit;
- + dcbf(eqcr->cursor);
- + EQCR_INC(eqcr);
- + eqcr->available--;
- + dcbz_64(eqcr->cursor);
- + return eqcr->cursor;
- +}
- +
- +#define EQCR_COMMIT_CHECKS(eqcr) \
- +do { \
- + DPA_ASSERT(eqcr->busy); \
- + DPA_ASSERT(eqcr->cursor->orp == (eqcr->cursor->orp & 0x00ffffff)); \
- + DPA_ASSERT(eqcr->cursor->fqid == (eqcr->cursor->fqid & 0x00ffffff)); \
- +} while (0)
- +
- +static inline void qm_eqcr_pci_commit(struct qm_portal *portal, u8 myverb)
- +{
- + register struct qm_eqcr *eqcr = &portal->eqcr;
- + EQCR_COMMIT_CHECKS(eqcr);
- + DPA_ASSERT(eqcr->pmode == qm_eqcr_pci);
- + eqcr->cursor->__dont_write_directly__verb = myverb | eqcr->vbit;
- + EQCR_INC(eqcr);
- + eqcr->available--;
- + dcbf(eqcr->cursor);
- + hwsync();
- + qm_out(EQCR_PI_CINH, EQCR_PTR2IDX(eqcr->cursor));
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + eqcr->busy = 0;
- +#endif
- +}
- +
- +static inline void qm_eqcr_pce_prefetch(struct qm_portal *portal)
- +{
- + __maybe_unused register struct qm_eqcr *eqcr = &portal->eqcr;
- + DPA_ASSERT(eqcr->pmode == qm_eqcr_pce);
- + qm_cl_invalidate(EQCR_PI);
- + qm_cl_touch_rw(EQCR_PI);
- +}
- +
- +static inline void qm_eqcr_pce_commit(struct qm_portal *portal, u8 myverb)
- +{
- + register struct qm_eqcr *eqcr = &portal->eqcr;
- + EQCR_COMMIT_CHECKS(eqcr);
- + DPA_ASSERT(eqcr->pmode == qm_eqcr_pce);
- + eqcr->cursor->__dont_write_directly__verb = myverb | eqcr->vbit;
- + EQCR_INC(eqcr);
- + eqcr->available--;
- + dcbf(eqcr->cursor);
- + lwsync();
- + qm_cl_out(EQCR_PI, EQCR_PTR2IDX(eqcr->cursor));
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + eqcr->busy = 0;
- +#endif
- +}
- +
- +static inline void qm_eqcr_pvb_commit(struct qm_portal *portal, u8 myverb)
- +{
- + register struct qm_eqcr *eqcr = &portal->eqcr;
- + struct qm_eqcr_entry *eqcursor;
- + EQCR_COMMIT_CHECKS(eqcr);
- + DPA_ASSERT(eqcr->pmode == qm_eqcr_pvb);
- + lwsync();
- + eqcursor = eqcr->cursor;
- + eqcursor->__dont_write_directly__verb = myverb | eqcr->vbit;
- + dcbf(eqcursor);
- + EQCR_INC(eqcr);
- + eqcr->available--;
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + eqcr->busy = 0;
- +#endif
- +}
- +
- +static inline u8 qm_eqcr_cci_update(struct qm_portal *portal)
- +{
- + register struct qm_eqcr *eqcr = &portal->eqcr;
- + u8 diff, old_ci = eqcr->ci;
- + eqcr->ci = qm_in(EQCR_CI_CINH) & (QM_EQCR_SIZE - 1);
- + diff = qm_cyc_diff(QM_EQCR_SIZE, old_ci, eqcr->ci);
- + eqcr->available += diff;
- + return diff;
- +}
- +
- +static inline void qm_eqcr_cce_prefetch(struct qm_portal *portal)
- +{
- + __maybe_unused register struct qm_eqcr *eqcr = &portal->eqcr;
- + qm_cl_touch_ro(EQCR_CI);
- +}
- +
- +static inline u8 qm_eqcr_cce_update(struct qm_portal *portal)
- +{
- + register struct qm_eqcr *eqcr = &portal->eqcr;
- + u8 diff, old_ci = eqcr->ci;
- + eqcr->ci = qm_cl_in(EQCR_CI) & (QM_EQCR_SIZE - 1);
- + qm_cl_invalidate(EQCR_CI);
- + diff = qm_cyc_diff(QM_EQCR_SIZE, old_ci, eqcr->ci);
- + eqcr->available += diff;
- + return diff;
- +}
- +
- +static inline u8 qm_eqcr_get_ithresh(struct qm_portal *portal)
- +{
- + register struct qm_eqcr *eqcr = &portal->eqcr;
- + return eqcr->ithresh;
- +}
- +
- +static inline void qm_eqcr_set_ithresh(struct qm_portal *portal, u8 ithresh)
- +{
- + register struct qm_eqcr *eqcr = &portal->eqcr;
- + eqcr->ithresh = ithresh;
- + qm_out(EQCR_ITR, ithresh);
- +}
- +
- +static inline u8 qm_eqcr_get_avail(struct qm_portal *portal)
- +{
- + register struct qm_eqcr *eqcr = &portal->eqcr;
- + return eqcr->available;
- +}
- +
- +static inline u8 qm_eqcr_get_fill(struct qm_portal *portal)
- +{
- + register struct qm_eqcr *eqcr = &portal->eqcr;
- + return QM_EQCR_SIZE - 1 - eqcr->available;
- +}
- +
- +
- +/* ---------------- */
- +/* --- DQRR API --- */
- +
- +/* FIXME: many possible improvements;
- + * - look at changing the API to use pointer rather than index parameters now
- + * that 'cursor' is a pointer,
- + * - consider moving other parameters to pointer if it could help (ci)
- + */
- +
- +#define DQRR_CARRYCLEAR(p) \
- + (void *)((unsigned long)(p) & (~(unsigned long)(QM_DQRR_SIZE << 6)))
- +
- +static inline u8 DQRR_PTR2IDX(const struct qm_dqrr_entry *e)
- +{
- + return ((uintptr_t)e >> 6) & (QM_DQRR_SIZE - 1);
- +}
- +
- +static inline const struct qm_dqrr_entry *DQRR_INC(
- + const struct qm_dqrr_entry *e)
- +{
- + return DQRR_CARRYCLEAR(e + 1);
- +}
- +
- +static inline void qm_dqrr_set_maxfill(struct qm_portal *portal, u8 mf)
- +{
- + qm_out(CFG, (qm_in(CFG) & 0xff0fffff) |
- + ((mf & (QM_DQRR_SIZE - 1)) << 20));
- +}
- +
- +static inline void qm_dqrr_cci_consume(struct qm_portal *portal, u8 num)
- +{
- + register struct qm_dqrr *dqrr = &portal->dqrr;
- + DPA_ASSERT(dqrr->cmode == qm_dqrr_cci);
- + dqrr->ci = (dqrr->ci + num) & (QM_DQRR_SIZE - 1);
- + qm_out(DQRR_CI_CINH, dqrr->ci);
- +}
- +
- +static inline void qm_dqrr_cce_consume(struct qm_portal *portal, u8 num)
- +{
- + register struct qm_dqrr *dqrr = &portal->dqrr;
- + DPA_ASSERT(dqrr->cmode == qm_dqrr_cce);
- + dqrr->ci = (dqrr->ci + num) & (QM_DQRR_SIZE - 1);
- + qm_cl_out(DQRR_CI, dqrr->ci);
- +}
- +
- +static inline void qm_dqrr_cdc_consume_n(struct qm_portal *portal, u16 bitmask)
- +{
- + __maybe_unused register struct qm_dqrr *dqrr = &portal->dqrr;
- + DPA_ASSERT(dqrr->cmode == qm_dqrr_cdc);
- + qm_out(DQRR_DCAP, (1 << 8) | /* DQRR_DCAP::S */
- + ((u32)bitmask << 16)); /* DQRR_DCAP::DCAP_CI */
- + dqrr->ci = qm_in(DQRR_CI_CINH) & (QM_DQRR_SIZE - 1);
- + dqrr->fill = qm_cyc_diff(QM_DQRR_SIZE, dqrr->ci, dqrr->pi);
- +}
- +
- +static inline int qm_dqrr_init(struct qm_portal *portal,
- + const struct qm_portal_config *config,
- + enum qm_dqrr_dmode dmode,
- + __maybe_unused enum qm_dqrr_pmode pmode,
- + enum qm_dqrr_cmode cmode, u8 max_fill)
- +{
- + register struct qm_dqrr *dqrr = &portal->dqrr;
- + u32 cfg;
- +
- + /* Make sure the DQRR will be idle when we enable */
- + qm_out(DQRR_SDQCR, 0);
- + qm_out(DQRR_VDQCR, 0);
- + qm_out(DQRR_PDQCR, 0);
- + dqrr->ring = portal->addr.addr_ce + QM_CL_DQRR;
- + dqrr->pi = qm_in(DQRR_PI_CINH) & (QM_DQRR_SIZE - 1);
- + dqrr->ci = qm_in(DQRR_CI_CINH) & (QM_DQRR_SIZE - 1);
- + dqrr->cursor = dqrr->ring + dqrr->ci;
- + dqrr->fill = qm_cyc_diff(QM_DQRR_SIZE, dqrr->ci, dqrr->pi);
- + dqrr->vbit = (qm_in(DQRR_PI_CINH) & QM_DQRR_SIZE) ?
- + QM_DQRR_VERB_VBIT : 0;
- + dqrr->ithresh = qm_in(DQRR_ITR);
- +
- + /* Free up pending DQRR entries if any as per current DCM */
- + if (dqrr->fill) {
- + enum qm_dqrr_cmode dcm = (qm_in(CFG) >> 16) & 3;
- +
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + dqrr->cmode = dcm;
- +#endif
- + switch (dcm) {
- + case qm_dqrr_cci:
- + qm_dqrr_cci_consume(portal, dqrr->fill);
- + break;
- + case qm_dqrr_cce:
- + qm_dqrr_cce_consume(portal, dqrr->fill);
- + break;
- + case qm_dqrr_cdc:
- + qm_dqrr_cdc_consume_n(portal, (QM_DQRR_SIZE - 1));
- + break;
- + default:
- + DPA_ASSERT(0);
- + }
- + }
- +
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + dqrr->dmode = dmode;
- + dqrr->pmode = pmode;
- + dqrr->cmode = cmode;
- +#endif
- + /* Invalidate every ring entry before beginning */
- + for (cfg = 0; cfg < QM_DQRR_SIZE; cfg++)
- + dcbi(qm_cl(dqrr->ring, cfg));
- + cfg = (qm_in(CFG) & 0xff000f00) |
- + ((max_fill & (QM_DQRR_SIZE - 1)) << 20) | /* DQRR_MF */
- + ((dmode & 1) << 18) | /* DP */
- + ((cmode & 3) << 16) | /* DCM */
- + 0xa0 | /* RE+SE */
- + (0 ? 0x40 : 0) | /* Ignore RP */
- + (0 ? 0x10 : 0); /* Ignore SP */
- + qm_out(CFG, cfg);
- + qm_dqrr_set_maxfill(portal, max_fill);
- + return 0;
- +}
- +
- +static inline void qm_dqrr_finish(struct qm_portal *portal)
- +{
- + __maybe_unused register struct qm_dqrr *dqrr = &portal->dqrr;
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + if ((dqrr->cmode != qm_dqrr_cdc) &&
- + (dqrr->ci != DQRR_PTR2IDX(dqrr->cursor)))
- + pr_crit("Ignoring completed DQRR entries\n");
- +#endif
- +}
- +
- +static inline const struct qm_dqrr_entry *qm_dqrr_current(
- + struct qm_portal *portal)
- +{
- + register struct qm_dqrr *dqrr = &portal->dqrr;
- + if (!dqrr->fill)
- + return NULL;
- + return dqrr->cursor;
- +}
- +
- +static inline u8 qm_dqrr_cursor(struct qm_portal *portal)
- +{
- + register struct qm_dqrr *dqrr = &portal->dqrr;
- + return DQRR_PTR2IDX(dqrr->cursor);
- +}
- +
- +static inline u8 qm_dqrr_next(struct qm_portal *portal)
- +{
- + register struct qm_dqrr *dqrr = &portal->dqrr;
- + DPA_ASSERT(dqrr->fill);
- + dqrr->cursor = DQRR_INC(dqrr->cursor);
- + return --dqrr->fill;
- +}
- +
- +static inline u8 qm_dqrr_pci_update(struct qm_portal *portal)
- +{
- + register struct qm_dqrr *dqrr = &portal->dqrr;
- + u8 diff, old_pi = dqrr->pi;
- + DPA_ASSERT(dqrr->pmode == qm_dqrr_pci);
- + dqrr->pi = qm_in(DQRR_PI_CINH) & (QM_DQRR_SIZE - 1);
- + diff = qm_cyc_diff(QM_DQRR_SIZE, old_pi, dqrr->pi);
- + dqrr->fill += diff;
- + return diff;
- +}
- +
- +static inline void qm_dqrr_pce_prefetch(struct qm_portal *portal)
- +{
- + __maybe_unused register struct qm_dqrr *dqrr = &portal->dqrr;
- + DPA_ASSERT(dqrr->pmode == qm_dqrr_pce);
- + qm_cl_invalidate(DQRR_PI);
- + qm_cl_touch_ro(DQRR_PI);
- +}
- +
- +static inline u8 qm_dqrr_pce_update(struct qm_portal *portal)
- +{
- + register struct qm_dqrr *dqrr = &portal->dqrr;
- + u8 diff, old_pi = dqrr->pi;
- + DPA_ASSERT(dqrr->pmode == qm_dqrr_pce);
- + dqrr->pi = qm_cl_in(DQRR_PI) & (QM_DQRR_SIZE - 1);
- + diff = qm_cyc_diff(QM_DQRR_SIZE, old_pi, dqrr->pi);
- + dqrr->fill += diff;
- + return diff;
- +}
- +
- +static inline void qm_dqrr_pvb_update(struct qm_portal *portal)
- +{
- + register struct qm_dqrr *dqrr = &portal->dqrr;
- + const struct qm_dqrr_entry *res = qm_cl(dqrr->ring, dqrr->pi);
- + DPA_ASSERT(dqrr->pmode == qm_dqrr_pvb);
- +#ifndef CONFIG_FSL_PAMU
- + /*
- + * If PAMU is not available we need to invalidate the cache.
- + * When PAMU is available the cache is updated by stash
- + */
- + dcbi(res);
- + dcbt_ro(res);
- +#endif
- +
- + /* when accessing 'verb', use __raw_readb() to ensure that compiler
- + * inlining doesn't try to optimise out "excess reads". */
- + if ((__raw_readb(&res->verb) & QM_DQRR_VERB_VBIT) == dqrr->vbit) {
- + dqrr->pi = (dqrr->pi + 1) & (QM_DQRR_SIZE - 1);
- + if (!dqrr->pi)
- + dqrr->vbit ^= QM_DQRR_VERB_VBIT;
- + dqrr->fill++;
- + }
- +}
- +
- +
- +static inline void qm_dqrr_cci_consume_to_current(struct qm_portal *portal)
- +{
- + register struct qm_dqrr *dqrr = &portal->dqrr;
- + DPA_ASSERT(dqrr->cmode == qm_dqrr_cci);
- + dqrr->ci = DQRR_PTR2IDX(dqrr->cursor);
- + qm_out(DQRR_CI_CINH, dqrr->ci);
- +}
- +
- +static inline void qm_dqrr_cce_prefetch(struct qm_portal *portal)
- +{
- + __maybe_unused register struct qm_dqrr *dqrr = &portal->dqrr;
- + DPA_ASSERT(dqrr->cmode == qm_dqrr_cce);
- + qm_cl_invalidate(DQRR_CI);
- + qm_cl_touch_rw(DQRR_CI);
- +}
- +
- +static inline void qm_dqrr_cce_consume_to_current(struct qm_portal *portal)
- +{
- + register struct qm_dqrr *dqrr = &portal->dqrr;
- + DPA_ASSERT(dqrr->cmode == qm_dqrr_cce);
- + dqrr->ci = DQRR_PTR2IDX(dqrr->cursor);
- + qm_cl_out(DQRR_CI, dqrr->ci);
- +}
- +
- +static inline void qm_dqrr_cdc_consume_1(struct qm_portal *portal, u8 idx,
- + int park)
- +{
- + __maybe_unused register struct qm_dqrr *dqrr = &portal->dqrr;
- + DPA_ASSERT(dqrr->cmode == qm_dqrr_cdc);
- + DPA_ASSERT(idx < QM_DQRR_SIZE);
- + qm_out(DQRR_DCAP, (0 << 8) | /* S */
- + ((park ? 1 : 0) << 6) | /* PK */
- + idx); /* DCAP_CI */
- +}
- +
- +static inline void qm_dqrr_cdc_consume_1ptr(struct qm_portal *portal,
- + const struct qm_dqrr_entry *dq,
- + int park)
- +{
- + __maybe_unused register struct qm_dqrr *dqrr = &portal->dqrr;
- + u8 idx = DQRR_PTR2IDX(dq);
- + DPA_ASSERT(dqrr->cmode == qm_dqrr_cdc);
- + DPA_ASSERT((dqrr->ring + idx) == dq);
- + DPA_ASSERT(idx < QM_DQRR_SIZE);
- + qm_out(DQRR_DCAP, (0 << 8) | /* DQRR_DCAP::S */
- + ((park ? 1 : 0) << 6) | /* DQRR_DCAP::PK */
- + idx); /* DQRR_DCAP::DCAP_CI */
- +}
- +
- +static inline u8 qm_dqrr_cdc_cci(struct qm_portal *portal)
- +{
- + __maybe_unused register struct qm_dqrr *dqrr = &portal->dqrr;
- + DPA_ASSERT(dqrr->cmode == qm_dqrr_cdc);
- + return qm_in(DQRR_CI_CINH) & (QM_DQRR_SIZE - 1);
- +}
- +
- +static inline void qm_dqrr_cdc_cce_prefetch(struct qm_portal *portal)
- +{
- + __maybe_unused register struct qm_dqrr *dqrr = &portal->dqrr;
- + DPA_ASSERT(dqrr->cmode == qm_dqrr_cdc);
- + qm_cl_invalidate(DQRR_CI);
- + qm_cl_touch_ro(DQRR_CI);
- +}
- +
- +static inline u8 qm_dqrr_cdc_cce(struct qm_portal *portal)
- +{
- + __maybe_unused register struct qm_dqrr *dqrr = &portal->dqrr;
- + DPA_ASSERT(dqrr->cmode == qm_dqrr_cdc);
- + return qm_cl_in(DQRR_CI) & (QM_DQRR_SIZE - 1);
- +}
- +
- +static inline u8 qm_dqrr_get_ci(struct qm_portal *portal)
- +{
- + register struct qm_dqrr *dqrr = &portal->dqrr;
- + DPA_ASSERT(dqrr->cmode != qm_dqrr_cdc);
- + return dqrr->ci;
- +}
- +
- +static inline void qm_dqrr_park(struct qm_portal *portal, u8 idx)
- +{
- + __maybe_unused register struct qm_dqrr *dqrr = &portal->dqrr;
- + DPA_ASSERT(dqrr->cmode != qm_dqrr_cdc);
- + qm_out(DQRR_DCAP, (0 << 8) | /* S */
- + (1 << 6) | /* PK */
- + (idx & (QM_DQRR_SIZE - 1))); /* DCAP_CI */
- +}
- +
- +static inline void qm_dqrr_park_current(struct qm_portal *portal)
- +{
- + register struct qm_dqrr *dqrr = &portal->dqrr;
- + DPA_ASSERT(dqrr->cmode != qm_dqrr_cdc);
- + qm_out(DQRR_DCAP, (0 << 8) | /* S */
- + (1 << 6) | /* PK */
- + DQRR_PTR2IDX(dqrr->cursor)); /* DCAP_CI */
- +}
- +
- +static inline void qm_dqrr_sdqcr_set(struct qm_portal *portal, u32 sdqcr)
- +{
- + qm_out(DQRR_SDQCR, sdqcr);
- +}
- +
- +static inline u32 qm_dqrr_sdqcr_get(struct qm_portal *portal)
- +{
- + return qm_in(DQRR_SDQCR);
- +}
- +
- +static inline void qm_dqrr_vdqcr_set(struct qm_portal *portal, u32 vdqcr)
- +{
- + qm_out(DQRR_VDQCR, vdqcr);
- +}
- +
- +static inline u32 qm_dqrr_vdqcr_get(struct qm_portal *portal)
- +{
- + return qm_in(DQRR_VDQCR);
- +}
- +
- +static inline void qm_dqrr_pdqcr_set(struct qm_portal *portal, u32 pdqcr)
- +{
- + qm_out(DQRR_PDQCR, pdqcr);
- +}
- +
- +static inline u32 qm_dqrr_pdqcr_get(struct qm_portal *portal)
- +{
- + return qm_in(DQRR_PDQCR);
- +}
- +
- +static inline u8 qm_dqrr_get_ithresh(struct qm_portal *portal)
- +{
- + register struct qm_dqrr *dqrr = &portal->dqrr;
- + return dqrr->ithresh;
- +}
- +
- +static inline void qm_dqrr_set_ithresh(struct qm_portal *portal, u8 ithresh)
- +{
- + qm_out(DQRR_ITR, ithresh);
- +}
- +
- +static inline u8 qm_dqrr_get_maxfill(struct qm_portal *portal)
- +{
- + return (qm_in(CFG) & 0x00f00000) >> 20;
- +}
- +
- +
- +/* -------------- */
- +/* --- MR API --- */
- +
- +#define MR_CARRYCLEAR(p) \
- + (void *)((unsigned long)(p) & (~(unsigned long)(QM_MR_SIZE << 6)))
- +
- +static inline u8 MR_PTR2IDX(const struct qm_mr_entry *e)
- +{
- + return ((uintptr_t)e >> 6) & (QM_MR_SIZE - 1);
- +}
- +
- +static inline const struct qm_mr_entry *MR_INC(const struct qm_mr_entry *e)
- +{
- + return MR_CARRYCLEAR(e + 1);
- +}
- +
- +static inline int qm_mr_init(struct qm_portal *portal, enum qm_mr_pmode pmode,
- + enum qm_mr_cmode cmode)
- +{
- + register struct qm_mr *mr = &portal->mr;
- + u32 cfg;
- +
- + mr->ring = portal->addr.addr_ce + QM_CL_MR;
- + mr->pi = qm_in(MR_PI_CINH) & (QM_MR_SIZE - 1);
- + mr->ci = qm_in(MR_CI_CINH) & (QM_MR_SIZE - 1);
- + mr->cursor = mr->ring + mr->ci;
- + mr->fill = qm_cyc_diff(QM_MR_SIZE, mr->ci, mr->pi);
- + mr->vbit = (qm_in(MR_PI_CINH) & QM_MR_SIZE) ? QM_MR_VERB_VBIT : 0;
- + mr->ithresh = qm_in(MR_ITR);
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + mr->pmode = pmode;
- + mr->cmode = cmode;
- +#endif
- + cfg = (qm_in(CFG) & 0xfffff0ff) |
- + ((cmode & 1) << 8); /* QCSP_CFG:MM */
- + qm_out(CFG, cfg);
- + return 0;
- +}
- +
- +static inline void qm_mr_finish(struct qm_portal *portal)
- +{
- + register struct qm_mr *mr = &portal->mr;
- + if (mr->ci != MR_PTR2IDX(mr->cursor))
- + pr_crit("Ignoring completed MR entries\n");
- +}
- +
- +static inline const struct qm_mr_entry *qm_mr_current(struct qm_portal *portal)
- +{
- + register struct qm_mr *mr = &portal->mr;
- + if (!mr->fill)
- + return NULL;
- + return mr->cursor;
- +}
- +
- +static inline u8 qm_mr_cursor(struct qm_portal *portal)
- +{
- + register struct qm_mr *mr = &portal->mr;
- + return MR_PTR2IDX(mr->cursor);
- +}
- +
- +static inline u8 qm_mr_next(struct qm_portal *portal)
- +{
- + register struct qm_mr *mr = &portal->mr;
- + DPA_ASSERT(mr->fill);
- + mr->cursor = MR_INC(mr->cursor);
- + return --mr->fill;
- +}
- +
- +static inline u8 qm_mr_pci_update(struct qm_portal *portal)
- +{
- + register struct qm_mr *mr = &portal->mr;
- + u8 diff, old_pi = mr->pi;
- + DPA_ASSERT(mr->pmode == qm_mr_pci);
- + mr->pi = qm_in(MR_PI_CINH);
- + diff = qm_cyc_diff(QM_MR_SIZE, old_pi, mr->pi);
- + mr->fill += diff;
- + return diff;
- +}
- +
- +static inline void qm_mr_pce_prefetch(struct qm_portal *portal)
- +{
- + __maybe_unused register struct qm_mr *mr = &portal->mr;
- + DPA_ASSERT(mr->pmode == qm_mr_pce);
- + qm_cl_invalidate(MR_PI);
- + qm_cl_touch_ro(MR_PI);
- +}
- +
- +static inline u8 qm_mr_pce_update(struct qm_portal *portal)
- +{
- + register struct qm_mr *mr = &portal->mr;
- + u8 diff, old_pi = mr->pi;
- + DPA_ASSERT(mr->pmode == qm_mr_pce);
- + mr->pi = qm_cl_in(MR_PI) & (QM_MR_SIZE - 1);
- + diff = qm_cyc_diff(QM_MR_SIZE, old_pi, mr->pi);
- + mr->fill += diff;
- + return diff;
- +}
- +
- +static inline void qm_mr_pvb_update(struct qm_portal *portal)
- +{
- + register struct qm_mr *mr = &portal->mr;
- + const struct qm_mr_entry *res = qm_cl(mr->ring, mr->pi);
- + DPA_ASSERT(mr->pmode == qm_mr_pvb);
- + /* when accessing 'verb', use __raw_readb() to ensure that compiler
- + * inlining doesn't try to optimise out "excess reads". */
- + if ((__raw_readb(&res->verb) & QM_MR_VERB_VBIT) == mr->vbit) {
- + mr->pi = (mr->pi + 1) & (QM_MR_SIZE - 1);
- + if (!mr->pi)
- + mr->vbit ^= QM_MR_VERB_VBIT;
- + mr->fill++;
- + res = MR_INC(res);
- + }
- + dcbit_ro(res);
- +}
- +
- +static inline void qm_mr_cci_consume(struct qm_portal *portal, u8 num)
- +{
- + register struct qm_mr *mr = &portal->mr;
- + DPA_ASSERT(mr->cmode == qm_mr_cci);
- + mr->ci = (mr->ci + num) & (QM_MR_SIZE - 1);
- + qm_out(MR_CI_CINH, mr->ci);
- +}
- +
- +static inline void qm_mr_cci_consume_to_current(struct qm_portal *portal)
- +{
- + register struct qm_mr *mr = &portal->mr;
- + DPA_ASSERT(mr->cmode == qm_mr_cci);
- + mr->ci = MR_PTR2IDX(mr->cursor);
- + qm_out(MR_CI_CINH, mr->ci);
- +}
- +
- +static inline void qm_mr_cce_prefetch(struct qm_portal *portal)
- +{
- + __maybe_unused register struct qm_mr *mr = &portal->mr;
- + DPA_ASSERT(mr->cmode == qm_mr_cce);
- + qm_cl_invalidate(MR_CI);
- + qm_cl_touch_rw(MR_CI);
- +}
- +
- +static inline void qm_mr_cce_consume(struct qm_portal *portal, u8 num)
- +{
- + register struct qm_mr *mr = &portal->mr;
- + DPA_ASSERT(mr->cmode == qm_mr_cce);
- + mr->ci = (mr->ci + num) & (QM_MR_SIZE - 1);
- + qm_cl_out(MR_CI, mr->ci);
- +}
- +
- +static inline void qm_mr_cce_consume_to_current(struct qm_portal *portal)
- +{
- + register struct qm_mr *mr = &portal->mr;
- + DPA_ASSERT(mr->cmode == qm_mr_cce);
- + mr->ci = MR_PTR2IDX(mr->cursor);
- + qm_cl_out(MR_CI, mr->ci);
- +}
- +
- +static inline u8 qm_mr_get_ci(struct qm_portal *portal)
- +{
- + register struct qm_mr *mr = &portal->mr;
- + return mr->ci;
- +}
- +
- +static inline u8 qm_mr_get_ithresh(struct qm_portal *portal)
- +{
- + register struct qm_mr *mr = &portal->mr;
- + return mr->ithresh;
- +}
- +
- +static inline void qm_mr_set_ithresh(struct qm_portal *portal, u8 ithresh)
- +{
- + qm_out(MR_ITR, ithresh);
- +}
- +
- +
- +/* ------------------------------ */
- +/* --- Management command API --- */
- +
- +static inline int qm_mc_init(struct qm_portal *portal)
- +{
- + register struct qm_mc *mc = &portal->mc;
- + mc->cr = portal->addr.addr_ce + QM_CL_CR;
- + mc->rr = portal->addr.addr_ce + QM_CL_RR0;
- + mc->rridx = (__raw_readb(&mc->cr->__dont_write_directly__verb) &
- + QM_MCC_VERB_VBIT) ? 0 : 1;
- + mc->vbit = mc->rridx ? QM_MCC_VERB_VBIT : 0;
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + mc->state = qman_mc_idle;
- +#endif
- + return 0;
- +}
- +
- +static inline void qm_mc_finish(struct qm_portal *portal)
- +{
- + __maybe_unused register struct qm_mc *mc = &portal->mc;
- + DPA_ASSERT(mc->state == qman_mc_idle);
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + if (mc->state != qman_mc_idle)
- + pr_crit("Losing incomplete MC command\n");
- +#endif
- +}
- +
- +static inline struct qm_mc_command *qm_mc_start(struct qm_portal *portal)
- +{
- + register struct qm_mc *mc = &portal->mc;
- + DPA_ASSERT(mc->state == qman_mc_idle);
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + mc->state = qman_mc_user;
- +#endif
- + dcbz_64(mc->cr);
- + return mc->cr;
- +}
- +
- +static inline void qm_mc_abort(struct qm_portal *portal)
- +{
- + __maybe_unused register struct qm_mc *mc = &portal->mc;
- + DPA_ASSERT(mc->state == qman_mc_user);
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + mc->state = qman_mc_idle;
- +#endif
- +}
- +
- +static inline void qm_mc_commit(struct qm_portal *portal, u8 myverb)
- +{
- + register struct qm_mc *mc = &portal->mc;
- + struct qm_mc_result *rr = mc->rr + mc->rridx;
- + DPA_ASSERT(mc->state == qman_mc_user);
- + lwsync();
- + mc->cr->__dont_write_directly__verb = myverb | mc->vbit;
- + dcbf(mc->cr);
- + dcbit_ro(rr);
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + mc->state = qman_mc_hw;
- +#endif
- +}
- +
- +static inline struct qm_mc_result *qm_mc_result(struct qm_portal *portal)
- +{
- + register struct qm_mc *mc = &portal->mc;
- + struct qm_mc_result *rr = mc->rr + mc->rridx;
- + DPA_ASSERT(mc->state == qman_mc_hw);
- + /* The inactive response register's verb byte always returns zero until
- + * its command is submitted and completed. This includes the valid-bit,
- + * in case you were wondering... */
- + if (!__raw_readb(&rr->verb)) {
- + dcbit_ro(rr);
- + return NULL;
- + }
- + mc->rridx ^= 1;
- + mc->vbit ^= QM_MCC_VERB_VBIT;
- +#ifdef CONFIG_FSL_DPA_CHECKING
- + mc->state = qman_mc_idle;
- +#endif
- + return rr;
- +}
- +
- +
- +/* ------------------------------------- */
- +/* --- Portal interrupt register API --- */
- +
- +static inline int qm_isr_init(__always_unused struct qm_portal *portal)
- +{
- + return 0;
- +}
- +
- +static inline void qm_isr_finish(__always_unused struct qm_portal *portal)
- +{
- +}
- +
- +static inline void qm_isr_set_iperiod(struct qm_portal *portal, u16 iperiod)
- +{
- + qm_out(ITPR, iperiod);
- +}
- +
- +static inline u32 __qm_isr_read(struct qm_portal *portal, enum qm_isr_reg n)
- +{
- +#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
- + return __qm_in(&portal->addr, QM_REG_ISR + (n << 6));
- +#else
- + return __qm_in(&portal->addr, QM_REG_ISR + (n << 2));
- +#endif
- +}
- +
- +static inline void __qm_isr_write(struct qm_portal *portal, enum qm_isr_reg n,
- + u32 val)
- +{
- +#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
- + __qm_out(&portal->addr, QM_REG_ISR + (n << 6), val);
- +#else
- + __qm_out(&portal->addr, QM_REG_ISR + (n << 2), val);
- +#endif
- +}
- +
- +/* Cleanup FQs */
- +static inline int qm_shutdown_fq(struct qm_portal **portal, int portal_count,
- + u32 fqid)
- +{
- +
- + struct qm_mc_command *mcc;
- + struct qm_mc_result *mcr;
- + u8 state;
- + int orl_empty, fq_empty, i, drain = 0;
- + u32 result;
- + u32 channel, wq;
- + u16 dest_wq;
- +
- + /* Determine the state of the FQID */
- + mcc = qm_mc_start(portal[0]);
- + mcc->queryfq_np.fqid = cpu_to_be32(fqid);
- + qm_mc_commit(portal[0], QM_MCC_VERB_QUERYFQ_NP);
- + while (!(mcr = qm_mc_result(portal[0])))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_QUERYFQ_NP);
- + state = mcr->queryfq_np.state & QM_MCR_NP_STATE_MASK;
- + if (state == QM_MCR_NP_STATE_OOS)
- + return 0; /* Already OOS, no need to do anymore checks */
- +
- + /* Query which channel the FQ is using */
- + mcc = qm_mc_start(portal[0]);
- + mcc->queryfq.fqid = cpu_to_be32(fqid);
- + qm_mc_commit(portal[0], QM_MCC_VERB_QUERYFQ);
- + while (!(mcr = qm_mc_result(portal[0])))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_QUERYFQ);
- +
- + /* Need to store these since the MCR gets reused */
- + dest_wq = be16_to_cpu(mcr->queryfq.fqd.dest_wq);
- + wq = dest_wq & 0x7;
- + channel = dest_wq>>3;
- +
- + switch (state) {
- + case QM_MCR_NP_STATE_TEN_SCHED:
- + case QM_MCR_NP_STATE_TRU_SCHED:
- + case QM_MCR_NP_STATE_ACTIVE:
- + case QM_MCR_NP_STATE_PARKED:
- + orl_empty = 0;
- + mcc = qm_mc_start(portal[0]);
- + mcc->alterfq.fqid = cpu_to_be32(fqid);
- + qm_mc_commit(portal[0], QM_MCC_VERB_ALTER_RETIRE);
- + while (!(mcr = qm_mc_result(portal[0])))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) ==
- + QM_MCR_VERB_ALTER_RETIRE);
- + result = mcr->result; /* Make a copy as we reuse MCR below */
- +
- + if (result == QM_MCR_RESULT_PENDING) {
- + /* Need to wait for the FQRN in the message ring, which
- + will only occur once the FQ has been drained. In
- + order for the FQ to drain the portal needs to be set
- + to dequeue from the channel the FQ is scheduled on */
- + const struct qm_mr_entry *msg;
- + const struct qm_dqrr_entry *dqrr = NULL;
- + int found_fqrn = 0;
- + u16 dequeue_wq = 0;
- +
- + /* Flag that we need to drain FQ */
- + drain = 1;
- +
- + if (channel >= qm_channel_pool1 &&
- + channel < (qm_channel_pool1 + 15)) {
- + /* Pool channel, enable the bit in the portal */
- + dequeue_wq = (channel -
- + qm_channel_pool1 + 1)<<4 | wq;
- + } else if (channel < qm_channel_pool1) {
- + /* Dedicated channel */
- + dequeue_wq = wq;
- + } else {
- + pr_info("Cannot recover FQ 0x%x, it is "
- + "scheduled on channel 0x%x",
- + fqid, channel);
- + return -EBUSY;
- + }
- + /* Set the sdqcr to drain this channel */
- + if (channel < qm_channel_pool1)
- + for (i = 0; i < portal_count; i++)
- + qm_dqrr_sdqcr_set(portal[i],
- + QM_SDQCR_TYPE_ACTIVE |
- + QM_SDQCR_CHANNELS_DEDICATED);
- + else
- + for (i = 0; i < portal_count; i++)
- + qm_dqrr_sdqcr_set(
- + portal[i],
- + QM_SDQCR_TYPE_ACTIVE |
- + QM_SDQCR_CHANNELS_POOL_CONV
- + (channel));
- + while (!found_fqrn) {
- + /* Keep draining DQRR while checking the MR*/
- + for (i = 0; i < portal_count; i++) {
- + qm_dqrr_pvb_update(portal[i]);
- + dqrr = qm_dqrr_current(portal[i]);
- + while (dqrr) {
- + qm_dqrr_cdc_consume_1ptr(
- + portal[i], dqrr, 0);
- + qm_dqrr_pvb_update(portal[i]);
- + qm_dqrr_next(portal[i]);
- + dqrr = qm_dqrr_current(
- + portal[i]);
- + }
- + /* Process message ring too */
- + qm_mr_pvb_update(portal[i]);
- + msg = qm_mr_current(portal[i]);
- + while (msg) {
- + if ((msg->verb &
- + QM_MR_VERB_TYPE_MASK)
- + == QM_MR_VERB_FQRN)
- + found_fqrn = 1;
- + qm_mr_next(portal[i]);
- + qm_mr_cci_consume_to_current(
- + portal[i]);
- + qm_mr_pvb_update(portal[i]);
- + msg = qm_mr_current(portal[i]);
- + }
- + cpu_relax();
- + }
- + }
- + }
- + if (result != QM_MCR_RESULT_OK &&
- + result != QM_MCR_RESULT_PENDING) {
- + /* error */
- + pr_err("qman_retire_fq failed on FQ 0x%x, result=0x%x\n",
- + fqid, result);
- + return -1;
- + }
- + if (!(mcr->alterfq.fqs & QM_MCR_FQS_ORLPRESENT)) {
- + /* ORL had no entries, no need to wait until the
- + ERNs come in */
- + orl_empty = 1;
- + }
- + /* Retirement succeeded, check to see if FQ needs
- + to be drained */
- + if (drain || mcr->alterfq.fqs & QM_MCR_FQS_NOTEMPTY) {
- + /* FQ is Not Empty, drain using volatile DQ commands */
- + fq_empty = 0;
- + do {
- + const struct qm_dqrr_entry *dqrr = NULL;
- + u32 vdqcr = fqid | QM_VDQCR_NUMFRAMES_SET(3);
- + qm_dqrr_vdqcr_set(portal[0], vdqcr);
- +
- + /* Wait for a dequeue to occur */
- + while (dqrr == NULL) {
- + qm_dqrr_pvb_update(portal[0]);
- + dqrr = qm_dqrr_current(portal[0]);
- + if (!dqrr)
- + cpu_relax();
- + }
- + /* Process the dequeues, making sure to
- + empty the ring completely */
- + while (dqrr) {
- + if (be32_to_cpu(dqrr->fqid) == fqid &&
- + dqrr->stat & QM_DQRR_STAT_FQ_EMPTY)
- + fq_empty = 1;
- + qm_dqrr_cdc_consume_1ptr(portal[0],
- + dqrr, 0);
- + qm_dqrr_pvb_update(portal[0]);
- + qm_dqrr_next(portal[0]);
- + dqrr = qm_dqrr_current(portal[0]);
- + }
- + } while (fq_empty == 0);
- + }
- + for (i = 0; i < portal_count; i++)
- + qm_dqrr_sdqcr_set(portal[i], 0);
- +
- + /* Wait for the ORL to have been completely drained */
- + while (orl_empty == 0) {
- + const struct qm_mr_entry *msg;
- + qm_mr_pvb_update(portal[0]);
- + msg = qm_mr_current(portal[0]);
- + while (msg) {
- + if ((msg->verb & QM_MR_VERB_TYPE_MASK) ==
- + QM_MR_VERB_FQRL)
- + orl_empty = 1;
- + qm_mr_next(portal[0]);
- + qm_mr_cci_consume_to_current(portal[0]);
- + qm_mr_pvb_update(portal[0]);
- + msg = qm_mr_current(portal[0]);
- + }
- + cpu_relax();
- + }
- + mcc = qm_mc_start(portal[0]);
- + mcc->alterfq.fqid = cpu_to_be32(fqid);
- + qm_mc_commit(portal[0], QM_MCC_VERB_ALTER_OOS);
- + while (!(mcr = qm_mc_result(portal[0])))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) ==
- + QM_MCR_VERB_ALTER_OOS);
- + if (mcr->result != QM_MCR_RESULT_OK) {
- + pr_err("OOS after drain Failed on FQID 0x%x, result 0x%x\n",
- + fqid, mcr->result);
- + return -1;
- + }
- + return 0;
- + case QM_MCR_NP_STATE_RETIRED:
- + /* Send OOS Command */
- + mcc = qm_mc_start(portal[0]);
- + mcc->alterfq.fqid = cpu_to_be32(fqid);
- + qm_mc_commit(portal[0], QM_MCC_VERB_ALTER_OOS);
- + while (!(mcr = qm_mc_result(portal[0])))
- + cpu_relax();
- + DPA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) ==
- + QM_MCR_VERB_ALTER_OOS);
- + if (mcr->result) {
- + pr_err("OOS Failed on FQID 0x%x\n", fqid);
- + return -1;
- + }
- + return 0;
- + }
- + return -1;
- +}
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/qman_private.h
- @@ -0,0 +1,398 @@
- +/* Copyright 2008-2012 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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 "dpa_sys.h"
- +#include <linux/fsl_qman.h>
- +#include <linux/iommu.h>
- +
- +#if defined(CONFIG_FSL_PAMU)
- +#include <asm/fsl_pamu_stash.h>
- +#endif
- +
- +#if !defined(CONFIG_FSL_QMAN_FQ_LOOKUP) && defined(CONFIG_PPC64)
- +#error "_PPC64 requires _FSL_QMAN_FQ_LOOKUP"
- +#endif
- +
- +#define QBMAN_ANY_PORTAL_IDX 0xffffffff
- + /* ----------------- */
- + /* Congestion Groups */
- + /* ----------------- */
- +/* This wrapper represents a bit-array for the state of the 256 Qman congestion
- + * groups. Is also used as a *mask* for congestion groups, eg. so we ignore
- + * those that don't concern us. We harness the structure and accessor details
- + * already used in the management command to query congestion groups. */
- +struct qman_cgrs {
- + struct __qm_mcr_querycongestion q;
- +};
- +static inline void qman_cgrs_init(struct qman_cgrs *c)
- +{
- + memset(c, 0, sizeof(*c));
- +}
- +static inline void qman_cgrs_fill(struct qman_cgrs *c)
- +{
- + memset(c, 0xff, sizeof(*c));
- +}
- +static inline int qman_cgrs_get(struct qman_cgrs *c, int num)
- +{
- + return QM_MCR_QUERYCONGESTION(&c->q, num);
- +}
- +static inline void qman_cgrs_set(struct qman_cgrs *c, int num)
- +{
- + c->q.__state[__CGR_WORD(num)] |= (0x80000000 >> __CGR_SHIFT(num));
- +}
- +static inline void qman_cgrs_unset(struct qman_cgrs *c, int num)
- +{
- + c->q.__state[__CGR_WORD(num)] &= ~(0x80000000 >> __CGR_SHIFT(num));
- +}
- +static inline int qman_cgrs_next(struct qman_cgrs *c, int num)
- +{
- + while ((++num < __CGR_NUM) && !qman_cgrs_get(c, num))
- + ;
- + return num;
- +}
- +static inline void qman_cgrs_cp(struct qman_cgrs *dest,
- + const struct qman_cgrs *src)
- +{
- + *dest = *src;
- +}
- +static inline void qman_cgrs_and(struct qman_cgrs *dest,
- + const struct qman_cgrs *a, const struct qman_cgrs *b)
- +{
- + int ret;
- + u32 *_d = dest->q.__state;
- + const u32 *_a = a->q.__state;
- + const u32 *_b = b->q.__state;
- + for (ret = 0; ret < 8; ret++)
- + *(_d++) = *(_a++) & *(_b++);
- +}
- +static inline void qman_cgrs_xor(struct qman_cgrs *dest,
- + const struct qman_cgrs *a, const struct qman_cgrs *b)
- +{
- + int ret;
- + u32 *_d = dest->q.__state;
- + const u32 *_a = a->q.__state;
- + const u32 *_b = b->q.__state;
- + for (ret = 0; ret < 8; ret++)
- + *(_d++) = *(_a++) ^ *(_b++);
- +}
- +
- + /* ----------------------- */
- + /* CEETM Congestion Groups */
- + /* ----------------------- */
- +/* This wrapper represents a bit-array for the state of the 512 Qman CEETM
- + * congestion groups.
- + */
- +struct qman_ccgrs {
- + struct __qm_mcr_querycongestion q[2];
- +};
- +static inline void qman_ccgrs_init(struct qman_ccgrs *c)
- +{
- + memset(c, 0, sizeof(*c));
- +}
- +static inline void qman_ccgrs_fill(struct qman_ccgrs *c)
- +{
- + memset(c, 0xff, sizeof(*c));
- +}
- +static inline int qman_ccgrs_get(struct qman_ccgrs *c, int num)
- +{
- + if (num < __CGR_NUM)
- + return QM_MCR_QUERYCONGESTION(&c->q[0], num);
- + else
- + return QM_MCR_QUERYCONGESTION(&c->q[1], (num - __CGR_NUM));
- +}
- +static inline int qman_ccgrs_next(struct qman_ccgrs *c, int num)
- +{
- + while ((++num < __CGR_NUM) && !qman_ccgrs_get(c, num))
- + ;
- + return num;
- +}
- +static inline void qman_ccgrs_cp(struct qman_ccgrs *dest,
- + const struct qman_ccgrs *src)
- +{
- + *dest = *src;
- +}
- +static inline void qman_ccgrs_and(struct qman_ccgrs *dest,
- + const struct qman_ccgrs *a, const struct qman_ccgrs *b)
- +{
- + int ret, i;
- + u32 *_d;
- + const u32 *_a, *_b;
- + for (i = 0; i < 2; i++) {
- + _d = dest->q[i].__state;
- + _a = a->q[i].__state;
- + _b = b->q[i].__state;
- + for (ret = 0; ret < 8; ret++)
- + *(_d++) = *(_a++) & *(_b++);
- + }
- +}
- +static inline void qman_ccgrs_xor(struct qman_ccgrs *dest,
- + const struct qman_ccgrs *a, const struct qman_ccgrs *b)
- +{
- + int ret, i;
- + u32 *_d;
- + const u32 *_a, *_b;
- + for (i = 0; i < 2; i++) {
- + _d = dest->q[i].__state;
- + _a = a->q[i].__state;
- + _b = b->q[i].__state;
- + for (ret = 0; ret < 8; ret++)
- + *(_d++) = *(_a++) ^ *(_b++);
- + }
- +}
- +
- +/* used by CCSR and portal interrupt code */
- +enum qm_isr_reg {
- + qm_isr_status = 0,
- + qm_isr_enable = 1,
- + qm_isr_disable = 2,
- + qm_isr_inhibit = 3
- +};
- +
- +struct qm_portal_config {
- + /* Corenet portal addresses;
- + * [0]==cache-enabled, [1]==cache-inhibited. */
- + __iomem void *addr_virt[2];
- + struct resource addr_phys[2];
- + struct device dev;
- + struct iommu_domain *iommu_domain;
- + /* Allow these to be joined in lists */
- + struct list_head list;
- + /* User-visible portal configuration settings */
- + struct qman_portal_config public_cfg;
- + /* power management saved data */
- + u32 saved_isdr;
- +};
- +
- +/* Revision info (for errata and feature handling) */
- +#define QMAN_REV11 0x0101
- +#define QMAN_REV12 0x0102
- +#define QMAN_REV20 0x0200
- +#define QMAN_REV30 0x0300
- +#define QMAN_REV31 0x0301
- +#define QMAN_REV32 0x0302
- +
- +/* QMan REV_2 register contains the Cfg option */
- +#define QMAN_REV_CFG_0 0x0
- +#define QMAN_REV_CFG_1 0x1
- +#define QMAN_REV_CFG_2 0x2
- +#define QMAN_REV_CFG_3 0x3
- +
- +extern u16 qman_ip_rev; /* 0 if uninitialised, otherwise QMAN_REVx */
- +extern u8 qman_ip_cfg;
- +extern u32 qman_clk;
- +extern u16 qman_portal_max;
- +
- +#ifdef CONFIG_FSL_QMAN_CONFIG
- +/* Hooks from qman_driver.c to qman_config.c */
- +int qman_init_ccsr(struct device_node *node);
- +void qman_liodn_fixup(u16 channel);
- +int qman_set_sdest(u16 channel, unsigned int cpu_idx);
- +size_t get_qman_fqd_size(void);
- +#else
- +static inline size_t get_qman_fqd_size(void)
- +{
- + return (PAGE_SIZE << CONFIG_FSL_QMAN_FQD_SZ);
- +}
- +#endif
- +
- +int qm_set_wpm(int wpm);
- +int qm_get_wpm(int *wpm);
- +
- +/* Hooks from qman_driver.c in to qman_high.c */
- +struct qman_portal *qman_create_portal(
- + struct qman_portal *portal,
- + const struct qm_portal_config *config,
- + const struct qman_cgrs *cgrs);
- +
- +struct qman_portal *qman_create_affine_portal(
- + const struct qm_portal_config *config,
- + const struct qman_cgrs *cgrs);
- +struct qman_portal *qman_create_affine_slave(struct qman_portal *redirect,
- + int cpu);
- +const struct qm_portal_config *qman_destroy_affine_portal(void);
- +void qman_destroy_portal(struct qman_portal *qm);
- +
- +/* Hooks from fsl_usdpaa.c to qman_driver.c */
- +struct qm_portal_config *qm_get_unused_portal(void);
- +struct qm_portal_config *qm_get_unused_portal_idx(uint32_t idx);
- +
- +void qm_put_unused_portal(struct qm_portal_config *pcfg);
- +void qm_set_liodns(struct qm_portal_config *pcfg);
- +
- +/* This CGR feature is supported by h/w and required by unit-tests and the
- + * debugfs hooks, so is implemented in the driver. However it allows an explicit
- + * corruption of h/w fields by s/w that are usually incorruptible (because the
- + * counters are usually maintained entirely within h/w). As such, we declare
- + * this API internally. */
- +int qman_testwrite_cgr(struct qman_cgr *cgr, u64 i_bcnt,
- + struct qm_mcr_cgrtestwrite *result);
- +
- +#ifdef CONFIG_FSL_QMAN_FQ_LOOKUP
- +/* If the fq object pointer is greater than the size of context_b field,
- + * than a lookup table is required. */
- +int qman_setup_fq_lookup_table(size_t num_entries);
- +#endif
- +
- +
- +/*************************************************/
- +/* QMan s/w corenet portal, low-level i/face */
- +/*************************************************/
- +
- +/* Note: most functions are only used by the high-level interface, so are
- + * inlined from qman_low.h. The stuff below is for use by other parts of the
- + * driver. */
- +
- +/* For qm_dqrr_sdqcr_set(); Choose one SOURCE. Choose one COUNT. Choose one
- + * dequeue TYPE. Choose TOKEN (8-bit).
- + * If SOURCE == CHANNELS,
- + * Choose CHANNELS_DEDICATED and/or CHANNELS_POOL(n).
- + * You can choose DEDICATED_PRECEDENCE if the portal channel should have
- + * priority.
- + * If SOURCE == SPECIFICWQ,
- + * Either select the work-queue ID with SPECIFICWQ_WQ(), or select the
- + * channel (SPECIFICWQ_DEDICATED or SPECIFICWQ_POOL()) and specify the
- + * work-queue priority (0-7) with SPECIFICWQ_WQ() - either way, you get the
- + * same value.
- + */
- +#define QM_SDQCR_SOURCE_CHANNELS 0x0
- +#define QM_SDQCR_SOURCE_SPECIFICWQ 0x40000000
- +#define QM_SDQCR_COUNT_EXACT1 0x0
- +#define QM_SDQCR_COUNT_UPTO3 0x20000000
- +#define QM_SDQCR_DEDICATED_PRECEDENCE 0x10000000
- +#define QM_SDQCR_TYPE_MASK 0x03000000
- +#define QM_SDQCR_TYPE_NULL 0x0
- +#define QM_SDQCR_TYPE_PRIO_QOS 0x01000000
- +#define QM_SDQCR_TYPE_ACTIVE_QOS 0x02000000
- +#define QM_SDQCR_TYPE_ACTIVE 0x03000000
- +#define QM_SDQCR_TOKEN_MASK 0x00ff0000
- +#define QM_SDQCR_TOKEN_SET(v) (((v) & 0xff) << 16)
- +#define QM_SDQCR_TOKEN_GET(v) (((v) >> 16) & 0xff)
- +#define QM_SDQCR_CHANNELS_DEDICATED 0x00008000
- +#define QM_SDQCR_SPECIFICWQ_MASK 0x000000f7
- +#define QM_SDQCR_SPECIFICWQ_DEDICATED 0x00000000
- +#define QM_SDQCR_SPECIFICWQ_POOL(n) ((n) << 4)
- +#define QM_SDQCR_SPECIFICWQ_WQ(n) (n)
- +
- +/* For qm_dqrr_vdqcr_set(): use FQID(n) to fill in the frame queue ID */
- +#define QM_VDQCR_FQID_MASK 0x00ffffff
- +#define QM_VDQCR_FQID(n) ((n) & QM_VDQCR_FQID_MASK)
- +
- +/* For qm_dqrr_pdqcr_set(); Choose one MODE. Choose one COUNT.
- + * If MODE==SCHEDULED
- + * Choose SCHEDULED_CHANNELS or SCHEDULED_SPECIFICWQ. Choose one dequeue TYPE.
- + * If CHANNELS,
- + * Choose CHANNELS_DEDICATED and/or CHANNELS_POOL() channels.
- + * You can choose DEDICATED_PRECEDENCE if the portal channel should have
- + * priority.
- + * If SPECIFICWQ,
- + * Either select the work-queue ID with SPECIFICWQ_WQ(), or select the
- + * channel (SPECIFICWQ_DEDICATED or SPECIFICWQ_POOL()) and specify the
- + * work-queue priority (0-7) with SPECIFICWQ_WQ() - either way, you get the
- + * same value.
- + * If MODE==UNSCHEDULED
- + * Choose FQID().
- + */
- +#define QM_PDQCR_MODE_SCHEDULED 0x0
- +#define QM_PDQCR_MODE_UNSCHEDULED 0x80000000
- +#define QM_PDQCR_SCHEDULED_CHANNELS 0x0
- +#define QM_PDQCR_SCHEDULED_SPECIFICWQ 0x40000000
- +#define QM_PDQCR_COUNT_EXACT1 0x0
- +#define QM_PDQCR_COUNT_UPTO3 0x20000000
- +#define QM_PDQCR_DEDICATED_PRECEDENCE 0x10000000
- +#define QM_PDQCR_TYPE_MASK 0x03000000
- +#define QM_PDQCR_TYPE_NULL 0x0
- +#define QM_PDQCR_TYPE_PRIO_QOS 0x01000000
- +#define QM_PDQCR_TYPE_ACTIVE_QOS 0x02000000
- +#define QM_PDQCR_TYPE_ACTIVE 0x03000000
- +#define QM_PDQCR_CHANNELS_DEDICATED 0x00008000
- +#define QM_PDQCR_CHANNELS_POOL(n) (0x00008000 >> (n))
- +#define QM_PDQCR_SPECIFICWQ_MASK 0x000000f7
- +#define QM_PDQCR_SPECIFICWQ_DEDICATED 0x00000000
- +#define QM_PDQCR_SPECIFICWQ_POOL(n) ((n) << 4)
- +#define QM_PDQCR_SPECIFICWQ_WQ(n) (n)
- +#define QM_PDQCR_FQID(n) ((n) & 0xffffff)
- +
- +/* Used by all portal interrupt registers except 'inhibit'
- + * Channels with frame availability
- + */
- +#define QM_PIRQ_DQAVAIL 0x0000ffff
- +
- +/* The DQAVAIL interrupt fields break down into these bits; */
- +#define QM_DQAVAIL_PORTAL 0x8000 /* Portal channel */
- +#define QM_DQAVAIL_POOL(n) (0x8000 >> (n)) /* Pool channel, n==[1..15] */
- +#define QM_DQAVAIL_MASK 0xffff
- +/* This mask contains all the "irqsource" bits visible to API users */
- +#define QM_PIRQ_VISIBLE (QM_PIRQ_SLOW | QM_PIRQ_DQRI)
- +
- +/* These are qm_<reg>_<verb>(). So for example, qm_disable_write() means "write
- + * the disable register" rather than "disable the ability to write". */
- +#define qm_isr_status_read(qm) __qm_isr_read(qm, qm_isr_status)
- +#define qm_isr_status_clear(qm, m) __qm_isr_write(qm, qm_isr_status, m)
- +#define qm_isr_enable_read(qm) __qm_isr_read(qm, qm_isr_enable)
- +#define qm_isr_enable_write(qm, v) __qm_isr_write(qm, qm_isr_enable, v)
- +#define qm_isr_disable_read(qm) __qm_isr_read(qm, qm_isr_disable)
- +#define qm_isr_disable_write(qm, v) __qm_isr_write(qm, qm_isr_disable, v)
- +/* TODO: unfortunate name-clash here, reword? */
- +#define qm_isr_inhibit(qm) __qm_isr_write(qm, qm_isr_inhibit, 1)
- +#define qm_isr_uninhibit(qm) __qm_isr_write(qm, qm_isr_inhibit, 0)
- +
- +#ifdef CONFIG_FSL_QMAN_CONFIG
- +int qman_have_ccsr(void);
- +#else
- +#define qman_have_ccsr 0
- +#endif
- +
- +__init int qman_init(void);
- +__init int qman_resource_init(void);
- +
- +/* CEETM related */
- +#define QMAN_CEETM_MAX 2
- +extern u8 num_ceetms;
- +extern struct qm_ceetm qman_ceetms[QMAN_CEETM_MAX];
- +int qman_sp_enable_ceetm_mode(enum qm_dc_portal portal, u16 sub_portal);
- +int qman_sp_disable_ceetm_mode(enum qm_dc_portal portal, u16 sub_portal);
- +int qman_ceetm_set_prescaler(enum qm_dc_portal portal);
- +int qman_ceetm_get_prescaler(u16 *pres);
- +int qman_ceetm_query_cq(unsigned int cqid, unsigned int dcpid,
- + struct qm_mcr_ceetm_cq_query *cq_query);
- +int qman_ceetm_query_ccgr(struct qm_mcc_ceetm_ccgr_query *ccgr_query,
- + struct qm_mcr_ceetm_ccgr_query *response);
- +int qman_ceetm_get_xsfdr(enum qm_dc_portal portal, unsigned int *num);
- +
- +extern void *affine_portals[NR_CPUS];
- +const struct qm_portal_config *qman_get_qm_portal_config(
- + struct qman_portal *portal);
- +
- +/* power management */
- +#ifdef CONFIG_SUSPEND
- +void suspend_unused_qportal(void);
- +void resume_unused_qportal(void);
- +#endif
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/qman_test.c
- @@ -0,0 +1,57 @@
- +/* Copyright 2008-2011 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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 "qman_test.h"
- +
- +MODULE_AUTHOR("Geoff Thorpe");
- +MODULE_LICENSE("Dual BSD/GPL");
- +MODULE_DESCRIPTION("Qman testing");
- +
- +static int test_init(void)
- +{
- + int loop = 1;
- + while (loop--) {
- +#ifdef CONFIG_FSL_QMAN_TEST_STASH_POTATO
- + qman_test_hotpotato();
- +#endif
- +#ifdef CONFIG_FSL_QMAN_TEST_HIGH
- + qman_test_high();
- +#endif
- + }
- + return 0;
- +}
- +
- +static void test_exit(void)
- +{
- +}
- +
- +module_init(test_init);
- +module_exit(test_exit);
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/qman_test.h
- @@ -0,0 +1,45 @@
- +/* Copyright 2008-2011 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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 <linux/kernel.h>
- +#include <linux/errno.h>
- +#include <linux/io.h>
- +#include <linux/slab.h>
- +#include <linux/module.h>
- +#include <linux/interrupt.h>
- +#include <linux/delay.h>
- +#include <linux/sched.h>
- +
- +#include <linux/fsl_qman.h>
- +
- +void qman_test_hotpotato(void);
- +void qman_test_high(void);
- +
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/qman_test_high.c
- @@ -0,0 +1,216 @@
- +/* Copyright 2008-2011 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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 "qman_test.h"
- +
- +/*************/
- +/* constants */
- +/*************/
- +
- +#define CGR_ID 27
- +#define POOL_ID 2
- +#define FQ_FLAGS QMAN_FQ_FLAG_DYNAMIC_FQID
- +#define NUM_ENQUEUES 10
- +#define NUM_PARTIAL 4
- +#define PORTAL_SDQCR (QM_SDQCR_SOURCE_CHANNELS | \
- + QM_SDQCR_TYPE_PRIO_QOS | \
- + QM_SDQCR_TOKEN_SET(0x98) | \
- + QM_SDQCR_CHANNELS_DEDICATED | \
- + QM_SDQCR_CHANNELS_POOL(POOL_ID))
- +#define PORTAL_OPAQUE ((void *)0xf00dbeef)
- +#define VDQCR_FLAGS (QMAN_VOLATILE_FLAG_WAIT | QMAN_VOLATILE_FLAG_FINISH)
- +
- +/*************************************/
- +/* Predeclarations (eg. for fq_base) */
- +/*************************************/
- +
- +static enum qman_cb_dqrr_result cb_dqrr(struct qman_portal *,
- + struct qman_fq *,
- + const struct qm_dqrr_entry *);
- +static void cb_ern(struct qman_portal *, struct qman_fq *,
- + const struct qm_mr_entry *);
- +static void cb_fqs(struct qman_portal *, struct qman_fq *,
- + const struct qm_mr_entry *);
- +
- +/***************/
- +/* global vars */
- +/***************/
- +
- +static struct qm_fd fd, fd_dq;
- +static struct qman_fq fq_base = {
- + .cb.dqrr = cb_dqrr,
- + .cb.ern = cb_ern,
- + .cb.fqs = cb_fqs
- +};
- +static DECLARE_WAIT_QUEUE_HEAD(waitqueue);
- +static int retire_complete, sdqcr_complete;
- +
- +/**********************/
- +/* internal functions */
- +/**********************/
- +
- +/* Helpers for initialising and "incrementing" a frame descriptor */
- +static void fd_init(struct qm_fd *__fd)
- +{
- + qm_fd_addr_set64(__fd, 0xabdeadbeefLLU);
- + __fd->format = qm_fd_contig_big;
- + __fd->length29 = 0x0000ffff;
- + __fd->cmd = 0xfeedf00d;
- +}
- +
- +static void fd_inc(struct qm_fd *__fd)
- +{
- + u64 t = qm_fd_addr_get64(__fd);
- + int z = t >> 40;
- + t <<= 1;
- + if (z)
- + t |= 1;
- + qm_fd_addr_set64(__fd, t);
- + __fd->length29--;
- + __fd->cmd++;
- +}
- +
- +/* The only part of the 'fd' we can't memcmp() is the ppid */
- +static int fd_cmp(const struct qm_fd *a, const struct qm_fd *b)
- +{
- + int r = (qm_fd_addr_get64(a) == qm_fd_addr_get64(b)) ? 0 : -1;
- + if (!r)
- + r = a->format - b->format;
- + if (!r)
- + r = a->opaque - b->opaque;
- + if (!r)
- + r = a->cmd - b->cmd;
- + return r;
- +}
- +
- +/********/
- +/* test */
- +/********/
- +
- +static void do_enqueues(struct qman_fq *fq)
- +{
- + unsigned int loop;
- + for (loop = 0; loop < NUM_ENQUEUES; loop++) {
- + if (qman_enqueue(fq, &fd, QMAN_ENQUEUE_FLAG_WAIT |
- + (((loop + 1) == NUM_ENQUEUES) ?
- + QMAN_ENQUEUE_FLAG_WAIT_SYNC : 0)))
- + panic("qman_enqueue() failed\n");
- + fd_inc(&fd);
- + }
- +}
- +
- +void qman_test_high(void)
- +{
- + unsigned int flags;
- + int res;
- + struct qman_fq *fq = &fq_base;
- +
- + pr_info("qman_test_high starting\n");
- + fd_init(&fd);
- + fd_init(&fd_dq);
- +
- + /* Initialise (parked) FQ */
- + if (qman_create_fq(0, FQ_FLAGS, fq))
- + panic("qman_create_fq() failed\n");
- + if (qman_init_fq(fq, QMAN_INITFQ_FLAG_LOCAL, NULL))
- + panic("qman_init_fq() failed\n");
- +
- + /* Do enqueues + VDQCR, twice. (Parked FQ) */
- + do_enqueues(fq);
- + pr_info("VDQCR (till-empty);\n");
- + if (qman_volatile_dequeue(fq, VDQCR_FLAGS,
- + QM_VDQCR_NUMFRAMES_TILLEMPTY))
- + panic("qman_volatile_dequeue() failed\n");
- + do_enqueues(fq);
- + pr_info("VDQCR (%d of %d);\n", NUM_PARTIAL, NUM_ENQUEUES);
- + if (qman_volatile_dequeue(fq, VDQCR_FLAGS,
- + QM_VDQCR_NUMFRAMES_SET(NUM_PARTIAL)))
- + panic("qman_volatile_dequeue() failed\n");
- + pr_info("VDQCR (%d of %d);\n", NUM_ENQUEUES - NUM_PARTIAL,
- + NUM_ENQUEUES);
- + if (qman_volatile_dequeue(fq, VDQCR_FLAGS,
- + QM_VDQCR_NUMFRAMES_SET(NUM_ENQUEUES - NUM_PARTIAL)))
- + panic("qman_volatile_dequeue() failed\n");
- +
- + do_enqueues(fq);
- + pr_info("scheduled dequeue (till-empty)\n");
- + if (qman_schedule_fq(fq))
- + panic("qman_schedule_fq() failed\n");
- + wait_event(waitqueue, sdqcr_complete);
- +
- + /* Retire and OOS the FQ */
- + res = qman_retire_fq(fq, &flags);
- + if (res < 0)
- + panic("qman_retire_fq() failed\n");
- + wait_event(waitqueue, retire_complete);
- + if (flags & QMAN_FQ_STATE_BLOCKOOS)
- + panic("leaking frames\n");
- + if (qman_oos_fq(fq))
- + panic("qman_oos_fq() failed\n");
- + qman_destroy_fq(fq, 0);
- + pr_info("qman_test_high finished\n");
- +}
- +
- +static enum qman_cb_dqrr_result cb_dqrr(struct qman_portal *p,
- + struct qman_fq *fq,
- + const struct qm_dqrr_entry *dq)
- +{
- + if (fd_cmp(&fd_dq, &dq->fd)) {
- + pr_err("BADNESS: dequeued frame doesn't match;\n");
- + pr_err("Expected 0x%llx, got 0x%llx\n",
- + (unsigned long long)fd_dq.length29,
- + (unsigned long long)dq->fd.length29);
- + BUG();
- + }
- + fd_inc(&fd_dq);
- + if (!(dq->stat & QM_DQRR_STAT_UNSCHEDULED) && !fd_cmp(&fd_dq, &fd)) {
- + sdqcr_complete = 1;
- + wake_up(&waitqueue);
- + }
- + return qman_cb_dqrr_consume;
- +}
- +
- +static void cb_ern(struct qman_portal *p, struct qman_fq *fq,
- + const struct qm_mr_entry *msg)
- +{
- + panic("cb_ern() unimplemented");
- +}
- +
- +static void cb_fqs(struct qman_portal *p, struct qman_fq *fq,
- + const struct qm_mr_entry *msg)
- +{
- + u8 verb = (msg->verb & QM_MR_VERB_TYPE_MASK);
- + if ((verb != QM_MR_VERB_FQRN) && (verb != QM_MR_VERB_FQRNI))
- + panic("unexpected FQS message");
- + pr_info("Retirement message received\n");
- + retire_complete = 1;
- + wake_up(&waitqueue);
- +}
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/qman_test_hotpotato.c
- @@ -0,0 +1,499 @@
- +/* Copyright 2009-2012 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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 <linux/kthread.h>
- +#include <linux/platform_device.h>
- +#include <linux/dma-mapping.h>
- +#include "qman_test.h"
- +
- +/* Algorithm:
- + *
- + * Each cpu will have HP_PER_CPU "handlers" set up, each of which incorporates
- + * an rx/tx pair of FQ objects (both of which are stashed on dequeue). The
- + * organisation of FQIDs is such that the HP_PER_CPU*NUM_CPUS handlers will
- + * shuttle a "hot potato" frame around them such that every forwarding action
- + * moves it from one cpu to another. (The use of more than one handler per cpu
- + * is to allow enough handlers/FQs to truly test the significance of caching -
- + * ie. when cache-expiries are occurring.)
- + *
- + * The "hot potato" frame content will be HP_NUM_WORDS*4 bytes in size, and the
- + * first and last words of the frame data will undergo a transformation step on
- + * each forwarding action. To achieve this, each handler will be assigned a
- + * 32-bit "mixer", that is produced using a 32-bit LFSR. When a frame is
- + * received by a handler, the mixer of the expected sender is XOR'd into all
- + * words of the entire frame, which is then validated against the original
- + * values. Then, before forwarding, the entire frame is XOR'd with the mixer of
- + * the current handler. Apart from validating that the frame is taking the
- + * expected path, this also provides some quasi-realistic overheads to each
- + * forwarding action - dereferencing *all* the frame data, computation, and
- + * conditional branching. There is a "special" handler designated to act as the
- + * instigator of the test by creating an enqueuing the "hot potato" frame, and
- + * to determine when the test has completed by counting HP_LOOPS iterations.
- + *
- + * Init phases:
- + *
- + * 1. prepare each cpu's 'hp_cpu' struct using on_each_cpu(,,1) and link them
- + * into 'hp_cpu_list'. Specifically, set processor_id, allocate HP_PER_CPU
- + * handlers and link-list them (but do no other handler setup).
- + *
- + * 2. scan over 'hp_cpu_list' HP_PER_CPU times, the first time sets each
- + * hp_cpu's 'iterator' to point to its first handler. With each loop,
- + * allocate rx/tx FQIDs and mixer values to the hp_cpu's iterator handler
- + * and advance the iterator for the next loop. This includes a final fixup,
- + * which connects the last handler to the first (and which is why phase 2
- + * and 3 are separate).
- + *
- + * 3. scan over 'hp_cpu_list' HP_PER_CPU times, the first time sets each
- + * hp_cpu's 'iterator' to point to its first handler. With each loop,
- + * initialise FQ objects and advance the iterator for the next loop.
- + * Moreover, do this initialisation on the cpu it applies to so that Rx FQ
- + * initialisation targets the correct cpu.
- + */
- +
- +/* helper to run something on all cpus (can't use on_each_cpu(), as that invokes
- + * the fn from irq context, which is too restrictive). */
- +struct bstrap {
- + void (*fn)(void);
- + atomic_t started;
- +};
- +static int bstrap_fn(void *__bstrap)
- +{
- + struct bstrap *bstrap = __bstrap;
- + atomic_inc(&bstrap->started);
- + bstrap->fn();
- + while (!kthread_should_stop())
- + msleep(1);
- + return 0;
- +}
- +static int on_all_cpus(void (*fn)(void))
- +{
- + int cpu;
- + for_each_cpu(cpu, cpu_online_mask) {
- + struct bstrap bstrap = {
- + .fn = fn,
- + .started = ATOMIC_INIT(0)
- + };
- + struct task_struct *k = kthread_create(bstrap_fn, &bstrap,
- + "hotpotato%d", cpu);
- + int ret;
- + if (IS_ERR(k))
- + return -ENOMEM;
- + kthread_bind(k, cpu);
- + wake_up_process(k);
- + /* If we call kthread_stop() before the "wake up" has had an
- + * effect, then the thread may exit with -EINTR without ever
- + * running the function. So poll until it's started before
- + * requesting it to stop. */
- + while (!atomic_read(&bstrap.started))
- + msleep(10);
- + ret = kthread_stop(k);
- + if (ret)
- + return ret;
- + }
- + return 0;
- +}
- +
- +struct hp_handler {
- +
- + /* The following data is stashed when 'rx' is dequeued; */
- + /* -------------- */
- + /* The Rx FQ, dequeues of which will stash the entire hp_handler */
- + struct qman_fq rx;
- + /* The Tx FQ we should forward to */
- + struct qman_fq tx;
- + /* The value we XOR post-dequeue, prior to validating */
- + u32 rx_mixer;
- + /* The value we XOR pre-enqueue, after validating */
- + u32 tx_mixer;
- + /* what the hotpotato address should be on dequeue */
- + dma_addr_t addr;
- + u32 *frame_ptr;
- +
- + /* The following data isn't (necessarily) stashed on dequeue; */
- + /* -------------- */
- + u32 fqid_rx, fqid_tx;
- + /* list node for linking us into 'hp_cpu' */
- + struct list_head node;
- + /* Just to check ... */
- + unsigned int processor_id;
- +} ____cacheline_aligned;
- +
- +struct hp_cpu {
- + /* identify the cpu we run on; */
- + unsigned int processor_id;
- + /* root node for the per-cpu list of handlers */
- + struct list_head handlers;
- + /* list node for linking us into 'hp_cpu_list' */
- + struct list_head node;
- + /* when repeatedly scanning 'hp_list', each time linking the n'th
- + * handlers together, this is used as per-cpu iterator state */
- + struct hp_handler *iterator;
- +};
- +
- +/* Each cpu has one of these */
- +static DEFINE_PER_CPU(struct hp_cpu, hp_cpus);
- +
- +/* links together the hp_cpu structs, in first-come first-serve order. */
- +static LIST_HEAD(hp_cpu_list);
- +static spinlock_t hp_lock = __SPIN_LOCK_UNLOCKED(hp_lock);
- +
- +static unsigned int hp_cpu_list_length;
- +
- +/* the "special" handler, that starts and terminates the test. */
- +static struct hp_handler *special_handler;
- +static int loop_counter;
- +
- +/* handlers are allocated out of this, so they're properly aligned. */
- +static struct kmem_cache *hp_handler_slab;
- +
- +/* this is the frame data */
- +static void *__frame_ptr;
- +static u32 *frame_ptr;
- +static dma_addr_t frame_dma;
- +
- +/* the main function waits on this */
- +static DECLARE_WAIT_QUEUE_HEAD(queue);
- +
- +#define HP_PER_CPU 2
- +#define HP_LOOPS 8
- +/* 80 bytes, like a small ethernet frame, and bleeds into a second cacheline */
- +#define HP_NUM_WORDS 80
- +/* First word of the LFSR-based frame data */
- +#define HP_FIRST_WORD 0xabbaf00d
- +
- +static inline u32 do_lfsr(u32 prev)
- +{
- + return (prev >> 1) ^ (-(prev & 1u) & 0xd0000001u);
- +}
- +
- +static void allocate_frame_data(void)
- +{
- + u32 lfsr = HP_FIRST_WORD;
- + int loop;
- + struct platform_device *pdev = platform_device_alloc("foobar", -1);
- + if (!pdev)
- + panic("platform_device_alloc() failed");
- + if (platform_device_add(pdev))
- + panic("platform_device_add() failed");
- + __frame_ptr = kmalloc(4 * HP_NUM_WORDS, GFP_KERNEL);
- + if (!__frame_ptr)
- + panic("kmalloc() failed");
- + frame_ptr = (void *)(((unsigned long)__frame_ptr + 63) &
- + ~(unsigned long)63);
- + for (loop = 0; loop < HP_NUM_WORDS; loop++) {
- + frame_ptr[loop] = lfsr;
- + lfsr = do_lfsr(lfsr);
- + }
- + frame_dma = dma_map_single(&pdev->dev, frame_ptr, 4 * HP_NUM_WORDS,
- + DMA_BIDIRECTIONAL);
- + platform_device_del(pdev);
- + platform_device_put(pdev);
- +}
- +
- +static void deallocate_frame_data(void)
- +{
- + kfree(__frame_ptr);
- +}
- +
- +static inline void process_frame_data(struct hp_handler *handler,
- + const struct qm_fd *fd)
- +{
- + u32 *p = handler->frame_ptr;
- + u32 lfsr = HP_FIRST_WORD;
- + int loop;
- + if (qm_fd_addr_get64(fd) != handler->addr)
- + panic("bad frame address");
- + for (loop = 0; loop < HP_NUM_WORDS; loop++, p++) {
- + *p ^= handler->rx_mixer;
- + if (*p != lfsr)
- + panic("corrupt frame data");
- + *p ^= handler->tx_mixer;
- + lfsr = do_lfsr(lfsr);
- + }
- +}
- +
- +static enum qman_cb_dqrr_result normal_dqrr(struct qman_portal *portal,
- + struct qman_fq *fq,
- + const struct qm_dqrr_entry *dqrr)
- +{
- + struct hp_handler *handler = (struct hp_handler *)fq;
- +
- + process_frame_data(handler, &dqrr->fd);
- + if (qman_enqueue(&handler->tx, &dqrr->fd, 0))
- + panic("qman_enqueue() failed");
- + return qman_cb_dqrr_consume;
- +}
- +
- +static enum qman_cb_dqrr_result special_dqrr(struct qman_portal *portal,
- + struct qman_fq *fq,
- + const struct qm_dqrr_entry *dqrr)
- +{
- + struct hp_handler *handler = (struct hp_handler *)fq;
- +
- + process_frame_data(handler, &dqrr->fd);
- + if (++loop_counter < HP_LOOPS) {
- + if (qman_enqueue(&handler->tx, &dqrr->fd, 0))
- + panic("qman_enqueue() failed");
- + } else {
- + pr_info("Received final (%dth) frame\n", loop_counter);
- + wake_up(&queue);
- + }
- + return qman_cb_dqrr_consume;
- +}
- +
- +static void create_per_cpu_handlers(void)
- +{
- + struct hp_handler *handler;
- + int loop;
- + struct hp_cpu *hp_cpu = &get_cpu_var(hp_cpus);
- +
- + hp_cpu->processor_id = smp_processor_id();
- + spin_lock(&hp_lock);
- + list_add_tail(&hp_cpu->node, &hp_cpu_list);
- + hp_cpu_list_length++;
- + spin_unlock(&hp_lock);
- + INIT_LIST_HEAD(&hp_cpu->handlers);
- + for (loop = 0; loop < HP_PER_CPU; loop++) {
- + handler = kmem_cache_alloc(hp_handler_slab, GFP_KERNEL);
- + if (!handler)
- + panic("kmem_cache_alloc() failed");
- + handler->processor_id = hp_cpu->processor_id;
- + handler->addr = frame_dma;
- + handler->frame_ptr = frame_ptr;
- + list_add_tail(&handler->node, &hp_cpu->handlers);
- + }
- + put_cpu_var(hp_cpus);
- +}
- +
- +static void destroy_per_cpu_handlers(void)
- +{
- + struct list_head *loop, *tmp;
- + struct hp_cpu *hp_cpu = &get_cpu_var(hp_cpus);
- +
- + spin_lock(&hp_lock);
- + list_del(&hp_cpu->node);
- + spin_unlock(&hp_lock);
- + list_for_each_safe(loop, tmp, &hp_cpu->handlers) {
- + u32 flags;
- + struct hp_handler *handler = list_entry(loop, struct hp_handler,
- + node);
- + if (qman_retire_fq(&handler->rx, &flags))
- + panic("qman_retire_fq(rx) failed");
- + BUG_ON(flags & QMAN_FQ_STATE_BLOCKOOS);
- + if (qman_oos_fq(&handler->rx))
- + panic("qman_oos_fq(rx) failed");
- + qman_destroy_fq(&handler->rx, 0);
- + qman_destroy_fq(&handler->tx, 0);
- + qman_release_fqid(handler->fqid_rx);
- + list_del(&handler->node);
- + kmem_cache_free(hp_handler_slab, handler);
- + }
- + put_cpu_var(hp_cpus);
- +}
- +
- +static inline u8 num_cachelines(u32 offset)
- +{
- + u8 res = (offset + (L1_CACHE_BYTES - 1))
- + / (L1_CACHE_BYTES);
- + if (res > 3)
- + return 3;
- + return res;
- +}
- +#define STASH_DATA_CL \
- + num_cachelines(HP_NUM_WORDS * 4)
- +#define STASH_CTX_CL \
- + num_cachelines(offsetof(struct hp_handler, fqid_rx))
- +
- +static void init_handler(void *__handler)
- +{
- + struct qm_mcc_initfq opts;
- + struct hp_handler *handler = __handler;
- + BUG_ON(handler->processor_id != smp_processor_id());
- + /* Set up rx */
- + memset(&handler->rx, 0, sizeof(handler->rx));
- + if (handler == special_handler)
- + handler->rx.cb.dqrr = special_dqrr;
- + else
- + handler->rx.cb.dqrr = normal_dqrr;
- + if (qman_create_fq(handler->fqid_rx, 0, &handler->rx))
- + panic("qman_create_fq(rx) failed");
- + memset(&opts, 0, sizeof(opts));
- + opts.we_mask = QM_INITFQ_WE_FQCTRL | QM_INITFQ_WE_CONTEXTA;
- + opts.fqd.fq_ctrl = QM_FQCTRL_CTXASTASHING;
- + opts.fqd.context_a.stashing.data_cl = STASH_DATA_CL;
- + opts.fqd.context_a.stashing.context_cl = STASH_CTX_CL;
- + if (qman_init_fq(&handler->rx, QMAN_INITFQ_FLAG_SCHED |
- + QMAN_INITFQ_FLAG_LOCAL, &opts))
- + panic("qman_init_fq(rx) failed");
- + /* Set up tx */
- + memset(&handler->tx, 0, sizeof(handler->tx));
- + if (qman_create_fq(handler->fqid_tx, QMAN_FQ_FLAG_NO_MODIFY,
- + &handler->tx))
- + panic("qman_create_fq(tx) failed");
- +}
- +
- +static void init_phase2(void)
- +{
- + int loop;
- + u32 fqid = 0;
- + u32 lfsr = 0xdeadbeef;
- + struct hp_cpu *hp_cpu;
- + struct hp_handler *handler;
- +
- + for (loop = 0; loop < HP_PER_CPU; loop++) {
- + list_for_each_entry(hp_cpu, &hp_cpu_list, node) {
- + int ret;
- + if (!loop)
- + hp_cpu->iterator = list_first_entry(
- + &hp_cpu->handlers,
- + struct hp_handler, node);
- + else
- + hp_cpu->iterator = list_entry(
- + hp_cpu->iterator->node.next,
- + struct hp_handler, node);
- + /* Rx FQID is the previous handler's Tx FQID */
- + hp_cpu->iterator->fqid_rx = fqid;
- + /* Allocate new FQID for Tx */
- + ret = qman_alloc_fqid(&fqid);
- + if (ret)
- + panic("qman_alloc_fqid() failed");
- + hp_cpu->iterator->fqid_tx = fqid;
- + /* Rx mixer is the previous handler's Tx mixer */
- + hp_cpu->iterator->rx_mixer = lfsr;
- + /* Get new mixer for Tx */
- + lfsr = do_lfsr(lfsr);
- + hp_cpu->iterator->tx_mixer = lfsr;
- + }
- + }
- + /* Fix up the first handler (fqid_rx==0, rx_mixer=0xdeadbeef) */
- + hp_cpu = list_first_entry(&hp_cpu_list, struct hp_cpu, node);
- + handler = list_first_entry(&hp_cpu->handlers, struct hp_handler, node);
- + BUG_ON((handler->fqid_rx != 0) || (handler->rx_mixer != 0xdeadbeef));
- + handler->fqid_rx = fqid;
- + handler->rx_mixer = lfsr;
- + /* and tag it as our "special" handler */
- + special_handler = handler;
- +}
- +
- +static void init_phase3(void)
- +{
- + int loop;
- + struct hp_cpu *hp_cpu;
- +
- + for (loop = 0; loop < HP_PER_CPU; loop++) {
- + list_for_each_entry(hp_cpu, &hp_cpu_list, node) {
- + if (!loop)
- + hp_cpu->iterator = list_first_entry(
- + &hp_cpu->handlers,
- + struct hp_handler, node);
- + else
- + hp_cpu->iterator = list_entry(
- + hp_cpu->iterator->node.next,
- + struct hp_handler, node);
- + preempt_disable();
- + if (hp_cpu->processor_id == smp_processor_id())
- + init_handler(hp_cpu->iterator);
- + else
- + smp_call_function_single(hp_cpu->processor_id,
- + init_handler, hp_cpu->iterator, 1);
- + preempt_enable();
- + }
- + }
- +}
- +
- +static void send_first_frame(void *ignore)
- +{
- + u32 *p = special_handler->frame_ptr;
- + u32 lfsr = HP_FIRST_WORD;
- + int loop;
- + struct qm_fd fd;
- +
- + BUG_ON(special_handler->processor_id != smp_processor_id());
- + memset(&fd, 0, sizeof(fd));
- + qm_fd_addr_set64(&fd, special_handler->addr);
- + fd.format = qm_fd_contig_big;
- + fd.length29 = HP_NUM_WORDS * 4;
- + for (loop = 0; loop < HP_NUM_WORDS; loop++, p++) {
- + if (*p != lfsr)
- + panic("corrupt frame data");
- + *p ^= special_handler->tx_mixer;
- + lfsr = do_lfsr(lfsr);
- + }
- + pr_info("Sending first frame\n");
- + if (qman_enqueue(&special_handler->tx, &fd, 0))
- + panic("qman_enqueue() failed");
- +}
- +
- +void qman_test_hotpotato(void)
- +{
- + if (cpumask_weight(cpu_online_mask) < 2) {
- + pr_info("qman_test_hotpotato, skip - only 1 CPU\n");
- + return;
- + }
- +
- + pr_info("qman_test_hotpotato starting\n");
- +
- + hp_cpu_list_length = 0;
- + loop_counter = 0;
- + hp_handler_slab = kmem_cache_create("hp_handler_slab",
- + sizeof(struct hp_handler), L1_CACHE_BYTES,
- + SLAB_HWCACHE_ALIGN, NULL);
- + if (!hp_handler_slab)
- + panic("kmem_cache_create() failed");
- +
- + allocate_frame_data();
- +
- + /* Init phase 1 */
- + pr_info("Creating %d handlers per cpu...\n", HP_PER_CPU);
- + if (on_all_cpus(create_per_cpu_handlers))
- + panic("on_each_cpu() failed");
- + pr_info("Number of cpus: %d, total of %d handlers\n",
- + hp_cpu_list_length, hp_cpu_list_length * HP_PER_CPU);
- +
- + init_phase2();
- +
- + init_phase3();
- +
- + preempt_disable();
- + if (special_handler->processor_id == smp_processor_id())
- + send_first_frame(NULL);
- + else
- + smp_call_function_single(special_handler->processor_id,
- + send_first_frame, NULL, 1);
- + preempt_enable();
- +
- + wait_event(queue, loop_counter == HP_LOOPS);
- + deallocate_frame_data();
- + if (on_all_cpus(destroy_per_cpu_handlers))
- + panic("on_each_cpu() failed");
- + kmem_cache_destroy(hp_handler_slab);
- + pr_info("qman_test_hotpotato finished\n");
- +}
- --- /dev/null
- +++ b/drivers/staging/fsl_qbman/qman_utility.c
- @@ -0,0 +1,129 @@
- +/* Copyright 2008-2011 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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 "qman_private.h"
- +
- +/* ----------------- */
- +/* --- FQID Pool --- */
- +
- +struct qman_fqid_pool {
- + /* Base and size of the FQID range */
- + u32 fqid_base;
- + u32 total;
- + /* Number of FQIDs currently "allocated" */
- + u32 used;
- + /* Allocation optimisation. When 'used<total', it is the index of an
- + * available FQID. Otherwise there are no available FQIDs, and this
- + * will be set when the next deallocation occurs. */
- + u32 next;
- + /* A bit-field representation of the FQID range. */
- + unsigned long *bits;
- +};
- +
- +#define QLONG_BYTES sizeof(unsigned long)
- +#define QLONG_BITS (QLONG_BYTES * 8)
- +/* Number of 'longs' required for the given number of bits */
- +#define QNUM_LONGS(b) (((b) + QLONG_BITS - 1) / QLONG_BITS)
- +/* Shorthand for the number of bytes of same (kmalloc, memset, etc) */
- +#define QNUM_BYTES(b) (QNUM_LONGS(b) * QLONG_BYTES)
- +/* And in bits */
- +#define QNUM_BITS(b) (QNUM_LONGS(b) * QLONG_BITS)
- +
- +struct qman_fqid_pool *qman_fqid_pool_create(u32 fqid_start, u32 num)
- +{
- + struct qman_fqid_pool *pool = kmalloc(sizeof(*pool), GFP_KERNEL);
- + unsigned int i;
- +
- + BUG_ON(!num);
- + if (!pool)
- + return NULL;
- + pool->fqid_base = fqid_start;
- + pool->total = num;
- + pool->used = 0;
- + pool->next = 0;
- + pool->bits = kzalloc(QNUM_BYTES(num), GFP_KERNEL);
- + if (!pool->bits) {
- + kfree(pool);
- + return NULL;
- + }
- + /* If num is not an even multiple of QLONG_BITS (or even 8, for
- + * byte-oriented searching) then we fill the trailing bits with 1, to
- + * make them look allocated (permanently). */
- + for (i = num + 1; i < QNUM_BITS(num); i++)
- + set_bit(i, pool->bits);
- + return pool;
- +}
- +EXPORT_SYMBOL(qman_fqid_pool_create);
- +
- +int qman_fqid_pool_destroy(struct qman_fqid_pool *pool)
- +{
- + int ret = pool->used;
- + kfree(pool->bits);
- + kfree(pool);
- + return ret;
- +}
- +EXPORT_SYMBOL(qman_fqid_pool_destroy);
- +
- +int qman_fqid_pool_alloc(struct qman_fqid_pool *pool, u32 *fqid)
- +{
- + int ret;
- + if (pool->used == pool->total)
- + return -ENOMEM;
- + *fqid = pool->fqid_base + pool->next;
- + ret = test_and_set_bit(pool->next, pool->bits);
- + BUG_ON(ret);
- + if (++pool->used == pool->total)
- + return 0;
- + pool->next = find_next_zero_bit(pool->bits, pool->total, pool->next);
- + if (pool->next >= pool->total)
- + pool->next = find_first_zero_bit(pool->bits, pool->total);
- + BUG_ON(pool->next >= pool->total);
- + return 0;
- +}
- +EXPORT_SYMBOL(qman_fqid_pool_alloc);
- +
- +void qman_fqid_pool_free(struct qman_fqid_pool *pool, u32 fqid)
- +{
- + int ret;
- +
- + fqid -= pool->fqid_base;
- + ret = test_and_clear_bit(fqid, pool->bits);
- + BUG_ON(!ret);
- + if (pool->used-- == pool->total)
- + pool->next = fqid;
- +}
- +EXPORT_SYMBOL(qman_fqid_pool_free);
- +
- +u32 qman_fqid_pool_used(struct qman_fqid_pool *pool)
- +{
- + return pool->used;
- +}
- +EXPORT_SYMBOL(qman_fqid_pool_used);
- --- /dev/null
- +++ b/include/linux/fsl_bman.h
- @@ -0,0 +1,532 @@
- +/* Copyright 2008-2012 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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 FSL_BMAN_H
- +#define FSL_BMAN_H
- +
- +#ifdef __cplusplus
- +extern "C" {
- +#endif
- +
- +/* Last updated for v00.79 of the BG */
- +
- +/* Portal processing (interrupt) sources */
- +#define BM_PIRQ_RCRI 0x00000002 /* RCR Ring (below threshold) */
- +#define BM_PIRQ_BSCN 0x00000001 /* Buffer depletion State Change */
- +
- +/* This wrapper represents a bit-array for the depletion state of the 64 Bman
- + * buffer pools. */
- +struct bman_depletion {
- + u32 __state[2];
- +};
- +#define BMAN_DEPLETION_EMPTY { { 0x00000000, 0x00000000 } }
- +#define BMAN_DEPLETION_FULL { { 0xffffffff, 0xffffffff } }
- +#define __bmdep_word(x) ((x) >> 5)
- +#define __bmdep_shift(x) ((x) & 0x1f)
- +#define __bmdep_bit(x) (0x80000000 >> __bmdep_shift(x))
- +static inline void bman_depletion_init(struct bman_depletion *c)
- +{
- + c->__state[0] = c->__state[1] = 0;
- +}
- +static inline void bman_depletion_fill(struct bman_depletion *c)
- +{
- + c->__state[0] = c->__state[1] = ~0;
- +}
- +static inline int bman_depletion_get(const struct bman_depletion *c, u8 bpid)
- +{
- + return c->__state[__bmdep_word(bpid)] & __bmdep_bit(bpid);
- +}
- +static inline void bman_depletion_set(struct bman_depletion *c, u8 bpid)
- +{
- + c->__state[__bmdep_word(bpid)] |= __bmdep_bit(bpid);
- +}
- +static inline void bman_depletion_unset(struct bman_depletion *c, u8 bpid)
- +{
- + c->__state[__bmdep_word(bpid)] &= ~__bmdep_bit(bpid);
- +}
- +
- +/* ------------------------------------------------------- */
- +/* --- Bman data structures (and associated constants) --- */
- +
- +/* Represents s/w corenet portal mapped data structures */
- +struct bm_rcr_entry; /* RCR (Release Command Ring) entries */
- +struct bm_mc_command; /* MC (Management Command) command */
- +struct bm_mc_result; /* MC result */
- +
- +/* Code-reduction, define a wrapper for 48-bit buffers. In cases where a buffer
- + * pool id specific to this buffer is needed (BM_RCR_VERB_CMD_BPID_MULTI,
- + * BM_MCC_VERB_ACQUIRE), the 'bpid' field is used. */
- +struct bm_buffer {
- + union {
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u8 __reserved1;
- + u8 bpid;
- + u16 hi; /* High 16-bits of 48-bit address */
- + u32 lo; /* Low 32-bits of 48-bit address */
- +#else
- + u32 lo;
- + u16 hi;
- + u8 bpid;
- + u8 __reserved;
- +#endif
- + };
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u64 __notaddress:16;
- + u64 addr:48;
- +#else
- + u64 addr:48;
- + u64 __notaddress:16;
- +#endif
- + };
- + u64 opaque;
- + };
- +} __aligned(8);
- +static inline u64 bm_buffer_get64(const struct bm_buffer *buf)
- +{
- + return buf->addr;
- +}
- +static inline dma_addr_t bm_buf_addr(const struct bm_buffer *buf)
- +{
- + return (dma_addr_t)buf->addr;
- +}
- +/* Macro, so we compile better if 'v' isn't always 64-bit */
- +#define bm_buffer_set64(buf, v) \
- + do { \
- + struct bm_buffer *__buf931 = (buf); \
- + __buf931->hi = upper_32_bits(v); \
- + __buf931->lo = lower_32_bits(v); \
- + } while (0)
- +
- +/* See 1.5.3.5.4: "Release Command" */
- +struct bm_rcr_entry {
- + union {
- + struct {
- + u8 __dont_write_directly__verb;
- + u8 bpid; /* used with BM_RCR_VERB_CMD_BPID_SINGLE */
- + u8 __reserved1[62];
- + };
- + struct bm_buffer bufs[8];
- + };
- +} __packed;
- +#define BM_RCR_VERB_VBIT 0x80
- +#define BM_RCR_VERB_CMD_MASK 0x70 /* one of two values; */
- +#define BM_RCR_VERB_CMD_BPID_SINGLE 0x20
- +#define BM_RCR_VERB_CMD_BPID_MULTI 0x30
- +#define BM_RCR_VERB_BUFCOUNT_MASK 0x0f /* values 1..8 */
- +
- +/* See 1.5.3.1: "Acquire Command" */
- +/* See 1.5.3.2: "Query Command" */
- +struct bm_mcc_acquire {
- + u8 bpid;
- + u8 __reserved1[62];
- +} __packed;
- +struct bm_mcc_query {
- + u8 __reserved2[63];
- +} __packed;
- +struct bm_mc_command {
- + u8 __dont_write_directly__verb;
- + union {
- + struct bm_mcc_acquire acquire;
- + struct bm_mcc_query query;
- + };
- +} __packed;
- +#define BM_MCC_VERB_VBIT 0x80
- +#define BM_MCC_VERB_CMD_MASK 0x70 /* where the verb contains; */
- +#define BM_MCC_VERB_CMD_ACQUIRE 0x10
- +#define BM_MCC_VERB_CMD_QUERY 0x40
- +#define BM_MCC_VERB_ACQUIRE_BUFCOUNT 0x0f /* values 1..8 go here */
- +
- +/* See 1.5.3.3: "Acquire Response" */
- +/* See 1.5.3.4: "Query Response" */
- +struct bm_pool_state {
- + u8 __reserved1[32];
- + /* "availability state" and "depletion state" */
- + struct {
- + u8 __reserved1[8];
- + /* Access using bman_depletion_***() */
- + struct bman_depletion state;
- + } as, ds;
- +};
- +struct bm_mc_result {
- + union {
- + struct {
- + u8 verb;
- + u8 __reserved1[63];
- + };
- + union {
- + struct {
- + u8 __reserved1;
- + u8 bpid;
- + u8 __reserved2[62];
- + };
- + struct bm_buffer bufs[8];
- + } acquire;
- + struct bm_pool_state query;
- + };
- +} __packed;
- +#define BM_MCR_VERB_VBIT 0x80
- +#define BM_MCR_VERB_CMD_MASK BM_MCC_VERB_CMD_MASK
- +#define BM_MCR_VERB_CMD_ACQUIRE BM_MCC_VERB_CMD_ACQUIRE
- +#define BM_MCR_VERB_CMD_QUERY BM_MCC_VERB_CMD_QUERY
- +#define BM_MCR_VERB_CMD_ERR_INVALID 0x60
- +#define BM_MCR_VERB_CMD_ERR_ECC 0x70
- +#define BM_MCR_VERB_ACQUIRE_BUFCOUNT BM_MCC_VERB_ACQUIRE_BUFCOUNT /* 0..8 */
- +/* Determine the "availability state" of pool 'p' from a query result 'r' */
- +#define BM_MCR_QUERY_AVAILABILITY(r, p) \
- + bman_depletion_get(&r->query.as.state, p)
- +/* Determine the "depletion state" of pool 'p' from a query result 'r' */
- +#define BM_MCR_QUERY_DEPLETION(r, p) \
- + bman_depletion_get(&r->query.ds.state, p)
- +
- +/*******************************************************************/
- +/* Managed (aka "shared" or "mux/demux") portal, high-level i/face */
- +/*******************************************************************/
- +
- + /* Portal and Buffer Pools */
- + /* ----------------------- */
- +/* Represents a managed portal */
- +struct bman_portal;
- +
- +/* This object type represents Bman buffer pools. */
- +struct bman_pool;
- +
- +struct bman_portal_config {
- + /* This is used for any "core-affine" portals, ie. default portals
- + * associated to the corresponding cpu. -1 implies that there is no core
- + * affinity configured. */
- + int cpu;
- + /* portal interrupt line */
- + int irq;
- + /* the unique index of this portal */
- + u32 index;
- + /* Is this portal shared? (If so, it has coarser locking and demuxes
- + * processing on behalf of other CPUs.) */
- + int is_shared;
- + /* These are the buffer pool IDs that may be used via this portal. */
- + struct bman_depletion mask;
- +};
- +
- +/* This callback type is used when handling pool depletion entry/exit. The
- + * 'cb_ctx' value is the opaque value associated with the pool object in
- + * bman_new_pool(). 'depleted' is non-zero on depletion-entry, and zero on
- + * depletion-exit. */
- +typedef void (*bman_cb_depletion)(struct bman_portal *bm,
- + struct bman_pool *pool, void *cb_ctx, int depleted);
- +
- +/* This struct specifies parameters for a bman_pool object. */
- +struct bman_pool_params {
- + /* index of the buffer pool to encapsulate (0-63), ignored if
- + * BMAN_POOL_FLAG_DYNAMIC_BPID is set. */
- + u32 bpid;
- + /* bit-mask of BMAN_POOL_FLAG_*** options */
- + u32 flags;
- + /* depletion-entry/exit callback, if BMAN_POOL_FLAG_DEPLETION is set */
- + bman_cb_depletion cb;
- + /* opaque user value passed as a parameter to 'cb' */
- + void *cb_ctx;
- + /* depletion-entry/exit thresholds, if BMAN_POOL_FLAG_THRESH is set. NB:
- + * this is only allowed if BMAN_POOL_FLAG_DYNAMIC_BPID is used *and*
- + * when run in the control plane (which controls Bman CCSR). This array
- + * matches the definition of bm_pool_set(). */
- + u32 thresholds[4];
- +};
- +
- +/* Flags to bman_new_pool() */
- +#define BMAN_POOL_FLAG_NO_RELEASE 0x00000001 /* can't release to pool */
- +#define BMAN_POOL_FLAG_ONLY_RELEASE 0x00000002 /* can only release to pool */
- +#define BMAN_POOL_FLAG_DEPLETION 0x00000004 /* track depletion entry/exit */
- +#define BMAN_POOL_FLAG_DYNAMIC_BPID 0x00000008 /* (de)allocate bpid */
- +#define BMAN_POOL_FLAG_THRESH 0x00000010 /* set depletion thresholds */
- +#define BMAN_POOL_FLAG_STOCKPILE 0x00000020 /* stockpile to reduce hw ops */
- +
- +/* Flags to bman_release() */
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT
- +#define BMAN_RELEASE_FLAG_WAIT 0x00000001 /* wait if RCR is full */
- +#define BMAN_RELEASE_FLAG_WAIT_INT 0x00000002 /* if we wait, interruptible? */
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC
- +#define BMAN_RELEASE_FLAG_WAIT_SYNC 0x00000004 /* if wait, until consumed? */
- +#endif
- +#endif
- +#define BMAN_RELEASE_FLAG_NOW 0x00000008 /* issue immediate release */
- +
- +/* Flags to bman_acquire() */
- +#define BMAN_ACQUIRE_FLAG_STOCKPILE 0x00000001 /* no hw op, stockpile only */
- +
- + /* Portal Management */
- + /* ----------------- */
- +/**
- + * bman_get_portal_config - get portal configuration settings
- + *
- + * This returns a read-only view of the current cpu's affine portal settings.
- + */
- +const struct bman_portal_config *bman_get_portal_config(void);
- +
- +/**
- + * bman_irqsource_get - return the portal work that is interrupt-driven
- + *
- + * Returns a bitmask of BM_PIRQ_**I processing sources that are currently
- + * enabled for interrupt handling on the current cpu's affine portal. These
- + * sources will trigger the portal interrupt and the interrupt handler (or a
- + * tasklet/bottom-half it defers to) will perform the corresponding processing
- + * work. The bman_poll_***() functions will only process sources that are not in
- + * this bitmask. If the current CPU is sharing a portal hosted on another CPU,
- + * this always returns zero.
- + */
- +u32 bman_irqsource_get(void);
- +
- +/**
- + * bman_irqsource_add - add processing sources to be interrupt-driven
- + * @bits: bitmask of BM_PIRQ_**I processing sources
- + *
- + * Adds processing sources that should be interrupt-driven (rather than
- + * processed via bman_poll_***() functions). Returns zero for success, or
- + * -EINVAL if the current CPU is sharing a portal hosted on another CPU. */
- +int bman_irqsource_add(u32 bits);
- +
- +/**
- + * bman_irqsource_remove - remove processing sources from being interrupt-driven
- + * @bits: bitmask of BM_PIRQ_**I processing sources
- + *
- + * Removes processing sources from being interrupt-driven, so that they will
- + * instead be processed via bman_poll_***() functions. Returns zero for success,
- + * or -EINVAL if the current CPU is sharing a portal hosted on another CPU. */
- +int bman_irqsource_remove(u32 bits);
- +
- +/**
- + * bman_affine_cpus - return a mask of cpus that have affine portals
- + */
- +const cpumask_t *bman_affine_cpus(void);
- +
- +/**
- + * bman_poll_slow - process anything that isn't interrupt-driven.
- + *
- + * This function does any portal processing that isn't interrupt-driven. If the
- + * current CPU is sharing a portal hosted on another CPU, this function will
- + * return -EINVAL, otherwise the return value is a bitmask of BM_PIRQ_* sources
- + * indicating what interrupt sources were actually processed by the call.
- + *
- + * NB, unlike the legacy wrapper bman_poll(), this function will
- + * deterministically check for the presence of portal processing work and do it,
- + * which implies some latency even if there's nothing to do. The bman_poll()
- + * wrapper on the other hand (like the qman_poll() wrapper) attenuates this by
- + * checking for (and doing) portal processing infrequently. Ie. such that
- + * qman_poll() and bman_poll() can be called from core-processing loops. Use
- + * bman_poll_slow() when you yourself are deciding when to incur the overhead of
- + * processing.
- + */
- +u32 bman_poll_slow(void);
- +
- +/**
- + * bman_poll - process anything that isn't interrupt-driven.
- + *
- + * Dispatcher logic on a cpu can use this to trigger any maintenance of the
- + * affine portal. This function does whatever processing is not triggered by
- + * interrupts. This is a legacy wrapper that can be used in core-processing
- + * loops but mitigates the performance overhead of portal processing by
- + * adaptively bypassing true portal processing most of the time. (Processing is
- + * done once every 10 calls if the previous processing revealed that work needed
- + * to be done, or once very 1000 calls if the previous processing revealed no
- + * work needed doing.) If you wish to control this yourself, call
- + * bman_poll_slow() instead, which always checks for portal processing work.
- + */
- +void bman_poll(void);
- +
- +/**
- + * bman_rcr_is_empty - Determine if portal's RCR is empty
- + *
- + * For use in situations where a cpu-affine caller needs to determine when all
- + * releases for the local portal have been processed by Bman but can't use the
- + * BMAN_RELEASE_FLAG_WAIT_SYNC flag to do this from the final bman_release().
- + * The function forces tracking of RCR consumption (which normally doesn't
- + * happen until release processing needs to find space to put new release
- + * commands), and returns zero if the ring still has unprocessed entries,
- + * non-zero if it is empty.
- + */
- +int bman_rcr_is_empty(void);
- +
- +/**
- + * bman_alloc_bpid_range - Allocate a contiguous range of BPIDs
- + * @result: is set by the API to the base BPID of the allocated range
- + * @count: the number of BPIDs required
- + * @align: required alignment of the allocated range
- + * @partial: non-zero if the API can return fewer than @count BPIDs
- + *
- + * Returns the number of buffer pools allocated, or a negative error code. If
- + * @partial is non zero, the allocation request may return a smaller range of
- + * BPs than requested (though alignment will be as requested). If @partial is
- + * zero, the return value will either be 'count' or negative.
- + */
- +int bman_alloc_bpid_range(u32 *result, u32 count, u32 align, int partial);
- +static inline int bman_alloc_bpid(u32 *result)
- +{
- + int ret = bman_alloc_bpid_range(result, 1, 0, 0);
- + return (ret > 0) ? 0 : ret;
- +}
- +
- +/**
- + * bman_release_bpid_range - Release the specified range of buffer pool IDs
- + * @bpid: the base BPID of the range to deallocate
- + * @count: the number of BPIDs in the range
- + *
- + * This function can also be used to seed the allocator with ranges of BPIDs
- + * that it can subsequently allocate from.
- + */
- +void bman_release_bpid_range(u32 bpid, unsigned int count);
- +static inline void bman_release_bpid(u32 bpid)
- +{
- + bman_release_bpid_range(bpid, 1);
- +}
- +
- +int bman_reserve_bpid_range(u32 bpid, unsigned int count);
- +static inline int bman_reserve_bpid(u32 bpid)
- +{
- + return bman_reserve_bpid_range(bpid, 1);
- +}
- +
- +void bman_seed_bpid_range(u32 bpid, unsigned int count);
- +
- +
- +int bman_shutdown_pool(u32 bpid);
- +
- + /* Pool management */
- + /* --------------- */
- +/**
- + * bman_new_pool - Allocates a Buffer Pool object
- + * @params: parameters specifying the buffer pool ID and behaviour
- + *
- + * Creates a pool object for the given @params. A portal and the depletion
- + * callback field of @params are only used if the BMAN_POOL_FLAG_DEPLETION flag
- + * is set. NB, the fields from @params are copied into the new pool object, so
- + * the structure provided by the caller can be released or reused after the
- + * function returns.
- + */
- +struct bman_pool *bman_new_pool(const struct bman_pool_params *params);
- +
- +/**
- + * bman_free_pool - Deallocates a Buffer Pool object
- + * @pool: the pool object to release
- + *
- + */
- +void bman_free_pool(struct bman_pool *pool);
- +
- +/**
- + * bman_get_params - Returns a pool object's parameters.
- + * @pool: the pool object
- + *
- + * The returned pointer refers to state within the pool object so must not be
- + * modified and can no longer be read once the pool object is destroyed.
- + */
- +const struct bman_pool_params *bman_get_params(const struct bman_pool *pool);
- +
- +/**
- + * bman_release - Release buffer(s) to the buffer pool
- + * @pool: the buffer pool object to release to
- + * @bufs: an array of buffers to release
- + * @num: the number of buffers in @bufs (1-8)
- + * @flags: bit-mask of BMAN_RELEASE_FLAG_*** options
- + *
- + * Adds the given buffers to RCR entries. If the portal @p was created with the
- + * "COMPACT" flag, then it will be using a compaction algorithm to improve
- + * utilisation of RCR. As such, these buffers may join an existing ring entry
- + * and/or it may not be issued right away so as to allow future releases to join
- + * the same ring entry. Use the BMAN_RELEASE_FLAG_NOW flag to override this
- + * behaviour by committing the RCR entry (or entries) right away. If the RCR
- + * ring is full, the function will return -EBUSY unless BMAN_RELEASE_FLAG_WAIT
- + * is selected, in which case it will sleep waiting for space to become
- + * available in RCR. If the function receives a signal before such time (and
- + * BMAN_RELEASE_FLAG_WAIT_INT is set), the function returns -EINTR. Otherwise,
- + * it returns zero.
- + */
- +int bman_release(struct bman_pool *pool, const struct bm_buffer *bufs, u8 num,
- + u32 flags);
- +
- +/**
- + * bman_acquire - Acquire buffer(s) from a buffer pool
- + * @pool: the buffer pool object to acquire from
- + * @bufs: array for storing the acquired buffers
- + * @num: the number of buffers desired (@bufs is at least this big)
- + *
- + * Issues an "Acquire" command via the portal's management command interface.
- + * The return value will be the number of buffers obtained from the pool, or a
- + * negative error code if a h/w error or pool starvation was encountered. In
- + * the latter case, the content of @bufs is undefined.
- + */
- +int bman_acquire(struct bman_pool *pool, struct bm_buffer *bufs, u8 num,
- + u32 flags);
- +
- +/**
- + * bman_flush_stockpile - Flush stockpile buffer(s) to the buffer pool
- + * @pool: the buffer pool object the stockpile belongs
- + * @flags: bit-mask of BMAN_RELEASE_FLAG_*** options
- + *
- + * Adds stockpile buffers to RCR entries until the stockpile is empty.
- + * The return value will be a negative error code if a h/w error occurred.
- + * If BMAN_RELEASE_FLAG_NOW flag is passed and RCR ring is full,
- + * -EAGAIN will be returned.
- + */
- +int bman_flush_stockpile(struct bman_pool *pool, u32 flags);
- +
- +/**
- + * bman_query_pools - Query all buffer pool states
- + * @state: storage for the queried availability and depletion states
- + */
- +int bman_query_pools(struct bm_pool_state *state);
- +
- +#ifdef CONFIG_FSL_BMAN_CONFIG
- +/**
- + * bman_query_free_buffers - Query how many free buffers are in buffer pool
- + * @pool: the buffer pool object to query
- + *
- + * Return the number of the free buffers
- + */
- +u32 bman_query_free_buffers(struct bman_pool *pool);
- +
- +/**
- + * bman_update_pool_thresholds - Change the buffer pool's depletion thresholds
- + * @pool: the buffer pool object to which the thresholds will be set
- + * @thresholds: the new thresholds
- + */
- +int bman_update_pool_thresholds(struct bman_pool *pool, const u32 *thresholds);
- +#endif
- +
- +/**
- + * The below bman_p_***() variant might be called in a situation that the cpu
- + * which the portal affine to is not online yet.
- + * @bman_portal specifies which portal the API will use.
- +*/
- +int bman_p_irqsource_add(struct bman_portal *p, __maybe_unused u32 bits);
- +#ifdef __cplusplus
- +}
- +#endif
- +
- +#endif /* FSL_BMAN_H */
- --- /dev/null
- +++ b/include/linux/fsl_qman.h
- @@ -0,0 +1,3889 @@
- +/* Copyright 2008-2012 Freescale Semiconductor, Inc.
- + *
- + * 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 Freescale Semiconductor nor the
- + * names of its contributors may 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 Freescale Semiconductor ``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 Freescale Semiconductor 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 FSL_QMAN_H
- +#define FSL_QMAN_H
- +
- +#ifdef __cplusplus
- +extern "C" {
- +#endif
- +
- +/* Last updated for v00.800 of the BG */
- +
- +/* Hardware constants */
- +#define QM_CHANNEL_SWPORTAL0 0
- +#define QMAN_CHANNEL_POOL1 0x21
- +#define QMAN_CHANNEL_CAAM 0x80
- +#define QMAN_CHANNEL_PME 0xa0
- +#define QMAN_CHANNEL_POOL1_REV3 0x401
- +#define QMAN_CHANNEL_CAAM_REV3 0x840
- +#define QMAN_CHANNEL_PME_REV3 0x860
- +#define QMAN_CHANNEL_DCE 0x8a0
- +#define QMAN_CHANNEL_DCE_QMANREV312 0x880
- +extern u16 qm_channel_pool1;
- +extern u16 qm_channel_caam;
- +extern u16 qm_channel_pme;
- +extern u16 qm_channel_dce;
- +enum qm_dc_portal {
- + qm_dc_portal_fman0 = 0,
- + qm_dc_portal_fman1 = 1,
- + qm_dc_portal_caam = 2,
- + qm_dc_portal_pme = 3,
- + qm_dc_portal_rman = 4,
- + qm_dc_portal_dce = 5
- +};
- +
- +/* Portal processing (interrupt) sources */
- +#define QM_PIRQ_CCSCI 0x00200000 /* CEETM Congestion State Change */
- +#define QM_PIRQ_CSCI 0x00100000 /* Congestion State Change */
- +#define QM_PIRQ_EQCI 0x00080000 /* Enqueue Command Committed */
- +#define QM_PIRQ_EQRI 0x00040000 /* EQCR Ring (below threshold) */
- +#define QM_PIRQ_DQRI 0x00020000 /* DQRR Ring (non-empty) */
- +#define QM_PIRQ_MRI 0x00010000 /* MR Ring (non-empty) */
- +/* This mask contains all the interrupt sources that need handling except DQRI,
- + * ie. that if present should trigger slow-path processing. */
- +#define QM_PIRQ_SLOW (QM_PIRQ_CSCI | QM_PIRQ_EQCI | QM_PIRQ_EQRI | \
- + QM_PIRQ_MRI | QM_PIRQ_CCSCI)
- +
- +/* --- Clock speed --- */
- +/* A qman driver instance may or may not know the current qman clock speed.
- + * However, certain CEETM calculations may not be possible if this is not known.
- + * The 'set' function will only succeed (return zero) if the driver did not
- + * already know the clock speed. Likewise, the 'get' function will only succeed
- + * if the driver does know the clock speed (either because it knew when booting,
- + * or was told via 'set'). In cases where software is running on a driver
- + * instance that does not know the clock speed (eg. on a hypervised data-plane),
- + * and the user can obtain the current qman clock speed by other means (eg. from
- + * a message sent from the control-plane), then the 'set' function can be used
- + * to enable rate-calculations in a driver where it would otherwise not be
- + * possible. */
- +int qm_get_clock(u64 *clock_hz);
- +int qm_set_clock(u64 clock_hz);
- +
- +/* For qman_static_dequeue_*** APIs */
- +#define QM_SDQCR_CHANNELS_POOL_MASK 0x00007fff
- +/* for n in [1,15] */
- +#define QM_SDQCR_CHANNELS_POOL(n) (0x00008000 >> (n))
- +/* for conversion from n of qm_channel */
- +static inline u32 QM_SDQCR_CHANNELS_POOL_CONV(u16 channel)
- +{
- + return QM_SDQCR_CHANNELS_POOL(channel + 1 - qm_channel_pool1);
- +}
- +
- +/* For qman_volatile_dequeue(); Choose one PRECEDENCE. EXACT is optional. Use
- + * NUMFRAMES(n) (6-bit) or NUMFRAMES_TILLEMPTY to fill in the frame-count. Use
- + * FQID(n) to fill in the frame queue ID. */
- +#define QM_VDQCR_PRECEDENCE_VDQCR 0x0
- +#define QM_VDQCR_PRECEDENCE_SDQCR 0x80000000
- +#define QM_VDQCR_EXACT 0x40000000
- +#define QM_VDQCR_NUMFRAMES_MASK 0x3f000000
- +#define QM_VDQCR_NUMFRAMES_SET(n) (((n) & 0x3f) << 24)
- +#define QM_VDQCR_NUMFRAMES_GET(n) (((n) >> 24) & 0x3f)
- +#define QM_VDQCR_NUMFRAMES_TILLEMPTY QM_VDQCR_NUMFRAMES_SET(0)
- +
- +
- +/* ------------------------------------------------------- */
- +/* --- Qman data structures (and associated constants) --- */
- +
- +/* Represents s/w corenet portal mapped data structures */
- +struct qm_eqcr_entry; /* EQCR (EnQueue Command Ring) entries */
- +struct qm_dqrr_entry; /* DQRR (DeQueue Response Ring) entries */
- +struct qm_mr_entry; /* MR (Message Ring) entries */
- +struct qm_mc_command; /* MC (Management Command) command */
- +struct qm_mc_result; /* MC result */
- +
- +/* See David Lapp's "Frame formats" document, "dpateam", Jan 07, 2008 */
- +#define QM_FD_FORMAT_SG 0x4
- +#define QM_FD_FORMAT_LONG 0x2
- +#define QM_FD_FORMAT_COMPOUND 0x1
- +enum qm_fd_format {
- + /* 'contig' implies a contiguous buffer, whereas 'sg' implies a
- + * scatter-gather table. 'big' implies a 29-bit length with no offset
- + * field, otherwise length is 20-bit and offset is 9-bit. 'compound'
- + * implies a s/g-like table, where each entry itself represents a frame
- + * (contiguous or scatter-gather) and the 29-bit "length" is
- + * interpreted purely for congestion calculations, ie. a "congestion
- + * weight". */
- + qm_fd_contig = 0,
- + qm_fd_contig_big = QM_FD_FORMAT_LONG,
- + qm_fd_sg = QM_FD_FORMAT_SG,
- + qm_fd_sg_big = QM_FD_FORMAT_SG | QM_FD_FORMAT_LONG,
- + qm_fd_compound = QM_FD_FORMAT_COMPOUND
- +};
- +
- +/* Capitalised versions are un-typed but can be used in static expressions */
- +#define QM_FD_CONTIG 0
- +#define QM_FD_CONTIG_BIG QM_FD_FORMAT_LONG
- +#define QM_FD_SG QM_FD_FORMAT_SG
- +#define QM_FD_SG_BIG (QM_FD_FORMAT_SG | QM_FD_FORMAT_LONG)
- +#define QM_FD_COMPOUND QM_FD_FORMAT_COMPOUND
- +
- +/* See 1.5.1.1: "Frame Descriptor (FD)" */
- +struct qm_fd {
- + union {
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u8 dd:2; /* dynamic debug */
- + u8 liodn_offset:6;
- + u8 bpid:8; /* Buffer Pool ID */
- + u8 eliodn_offset:4;
- + u8 __reserved:4;
- + u8 addr_hi; /* high 8-bits of 40-bit address */
- + u32 addr_lo; /* low 32-bits of 40-bit address */
- +#else
- + u8 liodn_offset:6;
- + u8 dd:2; /* dynamic debug */
- + u8 bpid:8; /* Buffer Pool ID */
- + u8 __reserved:4;
- + u8 eliodn_offset:4;
- + u8 addr_hi; /* high 8-bits of 40-bit address */
- + u32 addr_lo; /* low 32-bits of 40-bit address */
- +#endif
- + };
- + struct {
- + u64 __notaddress:24;
- + /* More efficient address accessor */
- + u64 addr:40;
- + };
- + u64 opaque_addr;
- + };
- + /* The 'format' field indicates the interpretation of the remaining 29
- + * bits of the 32-bit word. For packing reasons, it is duplicated in the
- + * other union elements. Note, union'd structs are difficult to use with
- + * static initialisation under gcc, in which case use the "opaque" form
- + * with one of the macros. */
- + union {
- + /* For easier/faster copying of this part of the fd (eg. from a
- + * DQRR entry to an EQCR entry) copy 'opaque' */
- + u32 opaque;
- + /* If 'format' is _contig or _sg, 20b length and 9b offset */
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + enum qm_fd_format format:3;
- + u16 offset:9;
- + u32 length20:20;
- +#else
- + u32 length20:20;
- + u16 offset:9;
- + enum qm_fd_format format:3;
- +#endif
- + };
- + /* If 'format' is _contig_big or _sg_big, 29b length */
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + enum qm_fd_format _format1:3;
- + u32 length29:29;
- +#else
- + u32 length29:29;
- + enum qm_fd_format _format1:3;
- +#endif
- + };
- + /* If 'format' is _compound, 29b "congestion weight" */
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + enum qm_fd_format _format2:3;
- + u32 cong_weight:29;
- +#else
- + u32 cong_weight:29;
- + enum qm_fd_format _format2:3;
- +#endif
- + };
- + };
- + union {
- + u32 cmd;
- + u32 status;
- + };
- +} __aligned(8);
- +#define QM_FD_DD_NULL 0x00
- +#define QM_FD_PID_MASK 0x3f
- +static inline u64 qm_fd_addr_get64(const struct qm_fd *fd)
- +{
- + return fd->addr;
- +}
- +
- +static inline dma_addr_t qm_fd_addr(const struct qm_fd *fd)
- +{
- + return (dma_addr_t)fd->addr;
- +}
- +/* Macro, so we compile better if 'v' isn't always 64-bit */
- +#define qm_fd_addr_set64(fd, v) \
- + do { \
- + struct qm_fd *__fd931 = (fd); \
- + __fd931->addr = v; \
- + } while (0)
- +
- +/* For static initialisation of FDs (which is complicated by the use of unions
- + * in "struct qm_fd"), use the following macros. Note that;
- + * - 'dd', 'pid' and 'bpid' are ignored because there's no static initialisation
- + * use-case),
- + * - use capitalised QM_FD_*** formats for static initialisation.
- + */
- +#define QM_FD_FMT_20(cmd, addr_hi, addr_lo, fmt, off, len) \
- + { 0, 0, 0, 0, 0, addr_hi, addr_lo, \
- + { (((fmt)&0x7) << 29) | (((off)&0x1ff) << 20) | ((len)&0xfffff) }, \
- + { cmd } }
- +#define QM_FD_FMT_29(cmd, addr_hi, addr_lo, fmt, len) \
- + { 0, 0, 0, 0, 0, addr_hi, addr_lo, \
- + { (((fmt)&0x7) << 29) | ((len)&0x1fffffff) }, \
- + { cmd } }
- +
- +/* See 2.2.1.3 Multi-Core Datapath Acceleration Architecture */
- +#define QM_SG_OFFSET_MASK 0x1FFF
- +struct qm_sg_entry {
- + union {
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u8 __reserved1[3];
- + u8 addr_hi; /* high 8-bits of 40-bit address */
- + u32 addr_lo; /* low 32-bits of 40-bit address */
- +#else
- + u32 addr_lo; /* low 32-bits of 40-bit address */
- + u8 addr_hi; /* high 8-bits of 40-bit address */
- + u8 __reserved1[3];
- +#endif
- + };
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u64 __notaddress:24;
- + u64 addr:40;
- +#else
- + u64 addr:40;
- + u64 __notaddress:24;
- +#endif
- + };
- + u64 opaque;
- + };
- + union {
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u32 extension:1; /* Extension bit */
- + u32 final:1; /* Final bit */
- + u32 length:30;
- +#else
- + u32 length:30;
- + u32 final:1; /* Final bit */
- + u32 extension:1; /* Extension bit */
- +#endif
- + };
- + u32 sgt_efl;
- + };
- + u8 __reserved2;
- + u8 bpid;
- + union {
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u16 __reserved3:3;
- + u16 offset:13;
- +#else
- + u16 offset:13;
- + u16 __reserved3:3;
- +#endif
- + };
- + u16 opaque_offset;
- + };
- +} __packed;
- +union qm_sg_efl {
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u32 extension:1; /* Extension bit */
- + u32 final:1; /* Final bit */
- + u32 length:30;
- +#else
- + u32 length:30;
- + u32 final:1; /* Final bit */
- + u32 extension:1; /* Extension bit */
- +#endif
- + };
- + u32 efl;
- +};
- +static inline u64 qm_sg_entry_get64(const struct qm_sg_entry *sg)
- +{
- + return be64_to_cpu(sg->opaque);
- +}
- +static inline dma_addr_t qm_sg_addr(const struct qm_sg_entry *sg)
- +{
- + return (dma_addr_t)be64_to_cpu(sg->opaque);
- +}
- +static inline u8 qm_sg_entry_get_ext(const struct qm_sg_entry *sg)
- +{
- + union qm_sg_efl u;
- +
- + u.efl = be32_to_cpu(sg->sgt_efl);
- + return u.extension;
- +}
- +static inline u8 qm_sg_entry_get_final(const struct qm_sg_entry *sg)
- +{
- + union qm_sg_efl u;
- +
- + u.efl = be32_to_cpu(sg->sgt_efl);
- + return u.final;
- +}
- +static inline u32 qm_sg_entry_get_len(const struct qm_sg_entry *sg)
- +{
- + union qm_sg_efl u;
- +
- + u.efl = be32_to_cpu(sg->sgt_efl);
- + return u.length;
- +}
- +static inline u8 qm_sg_entry_get_bpid(const struct qm_sg_entry *sg)
- +{
- + return sg->bpid;
- +}
- +static inline u16 qm_sg_entry_get_offset(const struct qm_sg_entry *sg)
- +{
- + u32 opaque_offset = be16_to_cpu(sg->opaque_offset);
- +
- + return opaque_offset & 0x1fff;
- +}
- +
- +/* Macro, so we compile better if 'v' isn't always 64-bit */
- +#define qm_sg_entry_set64(sg, v) \
- + do { \
- + struct qm_sg_entry *__sg931 = (sg); \
- + __sg931->opaque = cpu_to_be64(v); \
- + } while (0)
- +#define qm_sg_entry_set_ext(sg, v) \
- + do { \
- + union qm_sg_efl __u932; \
- + __u932.efl = be32_to_cpu((sg)->sgt_efl); \
- + __u932.extension = v; \
- + (sg)->sgt_efl = cpu_to_be32(__u932.efl); \
- + } while (0)
- +#define qm_sg_entry_set_final(sg, v) \
- + do { \
- + union qm_sg_efl __u933; \
- + __u933.efl = be32_to_cpu((sg)->sgt_efl); \
- + __u933.final = v; \
- + (sg)->sgt_efl = cpu_to_be32(__u933.efl); \
- + } while (0)
- +#define qm_sg_entry_set_len(sg, v) \
- + do { \
- + union qm_sg_efl __u934; \
- + __u934.efl = be32_to_cpu((sg)->sgt_efl); \
- + __u934.length = v; \
- + (sg)->sgt_efl = cpu_to_be32(__u934.efl); \
- + } while (0)
- +#define qm_sg_entry_set_bpid(sg, v) \
- + do { \
- + struct qm_sg_entry *__u935 = (sg); \
- + __u935->bpid = v; \
- + } while (0)
- +#define qm_sg_entry_set_offset(sg, v) \
- + do { \
- + struct qm_sg_entry *__u936 = (sg); \
- + __u936->opaque_offset = cpu_to_be16(v); \
- + } while (0)
- +
- +/* See 1.5.8.1: "Enqueue Command" */
- +struct qm_eqcr_entry {
- + u8 __dont_write_directly__verb;
- + u8 dca;
- + u16 seqnum;
- + u32 orp; /* 24-bit */
- + u32 fqid; /* 24-bit */
- + u32 tag;
- + struct qm_fd fd;
- + u8 __reserved3[32];
- +} __packed;
- +#define QM_EQCR_VERB_VBIT 0x80
- +#define QM_EQCR_VERB_CMD_MASK 0x61 /* but only one value; */
- +#define QM_EQCR_VERB_CMD_ENQUEUE 0x01
- +#define QM_EQCR_VERB_COLOUR_MASK 0x18 /* 4 possible values; */
- +#define QM_EQCR_VERB_COLOUR_GREEN 0x00
- +#define QM_EQCR_VERB_COLOUR_YELLOW 0x08
- +#define QM_EQCR_VERB_COLOUR_RED 0x10
- +#define QM_EQCR_VERB_COLOUR_OVERRIDE 0x18
- +#define QM_EQCR_VERB_INTERRUPT 0x04 /* on command consumption */
- +#define QM_EQCR_VERB_ORP 0x02 /* enable order restoration */
- +#define QM_EQCR_DCA_ENABLE 0x80
- +#define QM_EQCR_DCA_PARK 0x40
- +#define QM_EQCR_DCA_IDXMASK 0x0f /* "DQRR::idx" goes here */
- +#define QM_EQCR_SEQNUM_NESN 0x8000 /* Advance NESN */
- +#define QM_EQCR_SEQNUM_NLIS 0x4000 /* More fragments to come */
- +#define QM_EQCR_SEQNUM_SEQMASK 0x3fff /* sequence number goes here */
- +#define QM_EQCR_FQID_NULL 0 /* eg. for an ORP seqnum hole */
- +
- +/* See 1.5.8.2: "Frame Dequeue Response" */
- +struct qm_dqrr_entry {
- + u8 verb;
- + u8 stat;
- + u16 seqnum; /* 15-bit */
- + u8 tok;
- + u8 __reserved2[3];
- + u32 fqid; /* 24-bit */
- + u32 contextB;
- + struct qm_fd fd;
- + u8 __reserved4[32];
- +};
- +#define QM_DQRR_VERB_VBIT 0x80
- +#define QM_DQRR_VERB_MASK 0x7f /* where the verb contains; */
- +#define QM_DQRR_VERB_FRAME_DEQUEUE 0x60 /* "this format" */
- +#define QM_DQRR_STAT_FQ_EMPTY 0x80 /* FQ empty */
- +#define QM_DQRR_STAT_FQ_HELDACTIVE 0x40 /* FQ held active */
- +#define QM_DQRR_STAT_FQ_FORCEELIGIBLE 0x20 /* FQ was force-eligible'd */
- +#define QM_DQRR_STAT_FD_VALID 0x10 /* has a non-NULL FD */
- +#define QM_DQRR_STAT_UNSCHEDULED 0x02 /* Unscheduled dequeue */
- +#define QM_DQRR_STAT_DQCR_EXPIRED 0x01 /* VDQCR or PDQCR expired*/
- +
- +/* See 1.5.8.3: "ERN Message Response" */
- +/* See 1.5.8.4: "FQ State Change Notification" */
- +struct qm_mr_entry {
- + u8 verb;
- + union {
- + struct {
- + u8 dca;
- + u16 seqnum;
- + u8 rc; /* Rejection Code */
- + u32 orp:24;
- + u32 fqid; /* 24-bit */
- + u32 tag;
- + struct qm_fd fd;
- + } __packed ern;
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u8 colour:2; /* See QM_MR_DCERN_COLOUR_* */
- + u8 __reserved1:3;
- + enum qm_dc_portal portal:3;
- +#else
- + enum qm_dc_portal portal:3;
- + u8 __reserved1:3;
- + u8 colour:2; /* See QM_MR_DCERN_COLOUR_* */
- +#endif
- + u16 __reserved2;
- + u8 rc; /* Rejection Code */
- + u32 __reserved3:24;
- + u32 fqid; /* 24-bit */
- + u32 tag;
- + struct qm_fd fd;
- + } __packed dcern;
- + struct {
- + u8 fqs; /* Frame Queue Status */
- + u8 __reserved1[6];
- + u32 fqid; /* 24-bit */
- + u32 contextB;
- + u8 __reserved2[16];
- + } __packed fq; /* FQRN/FQRNI/FQRL/FQPN */
- + };
- + u8 __reserved2[32];
- +} __packed;
- +#define QM_MR_VERB_VBIT 0x80
- +/* The "ern" VERB bits match QM_EQCR_VERB_*** so aren't reproduced here. ERNs
- + * originating from direct-connect portals ("dcern") use 0x20 as a verb which
- + * would be invalid as a s/w enqueue verb. A s/w ERN can be distinguished from
- + * the other MR types by noting if the 0x20 bit is unset. */
- +#define QM_MR_VERB_TYPE_MASK 0x27
- +#define QM_MR_VERB_DC_ERN 0x20
- +#define QM_MR_VERB_FQRN 0x21
- +#define QM_MR_VERB_FQRNI 0x22
- +#define QM_MR_VERB_FQRL 0x23
- +#define QM_MR_VERB_FQPN 0x24
- +#define QM_MR_RC_MASK 0xf0 /* contains one of; */
- +#define QM_MR_RC_CGR_TAILDROP 0x00
- +#define QM_MR_RC_WRED 0x10
- +#define QM_MR_RC_ERROR 0x20
- +#define QM_MR_RC_ORPWINDOW_EARLY 0x30
- +#define QM_MR_RC_ORPWINDOW_LATE 0x40
- +#define QM_MR_RC_FQ_TAILDROP 0x50
- +#define QM_MR_RC_ORPWINDOW_RETIRED 0x60
- +#define QM_MR_RC_ORP_ZERO 0x70
- +#define QM_MR_FQS_ORLPRESENT 0x02 /* ORL fragments to come */
- +#define QM_MR_FQS_NOTEMPTY 0x01 /* FQ has enqueued frames */
- +#define QM_MR_DCERN_COLOUR_GREEN 0x00
- +#define QM_MR_DCERN_COLOUR_YELLOW 0x01
- +#define QM_MR_DCERN_COLOUR_RED 0x02
- +#define QM_MR_DCERN_COLOUR_OVERRIDE 0x03
- +
- +/* An identical structure of FQD fields is present in the "Init FQ" command and
- + * the "Query FQ" result, it's suctioned out into the "struct qm_fqd" type.
- + * Within that, the 'stashing' and 'taildrop' pieces are also factored out, the
- + * latter has two inlines to assist with converting to/from the mant+exp
- + * representation. */
- +struct qm_fqd_stashing {
- + /* See QM_STASHING_EXCL_<...> */
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u8 exclusive;
- + u8 __reserved1:2;
- + /* Numbers of cachelines */
- + u8 annotation_cl:2;
- + u8 data_cl:2;
- + u8 context_cl:2;
- +#else
- + u8 context_cl:2;
- + u8 data_cl:2;
- + u8 annotation_cl:2;
- + u8 __reserved1:2;
- + u8 exclusive;
- +#endif
- +} __packed;
- +struct qm_fqd_taildrop {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u16 __reserved1:3;
- + u16 mant:8;
- + u16 exp:5;
- +#else
- + u16 exp:5;
- + u16 mant:8;
- + u16 __reserved1:3;
- +#endif
- +} __packed;
- +struct qm_fqd_oac {
- + /* See QM_OAC_<...> */
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u8 oac:2; /* "Overhead Accounting Control" */
- + u8 __reserved1:6;
- +#else
- + u8 __reserved1:6;
- + u8 oac:2; /* "Overhead Accounting Control" */
- +#endif
- + /* Two's-complement value (-128 to +127) */
- + signed char oal; /* "Overhead Accounting Length" */
- +} __packed;
- +struct qm_fqd {
- + union {
- + u8 orpc;
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u8 __reserved1:2;
- + u8 orprws:3;
- + u8 oa:1;
- + u8 olws:2;
- +#else
- + u8 olws:2;
- + u8 oa:1;
- + u8 orprws:3;
- + u8 __reserved1:2;
- +#endif
- + } __packed;
- + };
- + u8 cgid;
- + u16 fq_ctrl; /* See QM_FQCTRL_<...> */
- + union {
- + u16 dest_wq;
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u16 channel:13; /* qm_channel */
- + u16 wq:3;
- +#else
- + u16 wq:3;
- + u16 channel:13; /* qm_channel */
- +#endif
- + } __packed dest;
- + };
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u16 __reserved2:1;
- + u16 ics_cred:15;
- +#else
- + u16 __reserved2:1;
- + u16 ics_cred:15;
- +#endif
- + /* For "Initialize Frame Queue" commands, the write-enable mask
- + * determines whether 'td' or 'oac_init' is observed. For query
- + * commands, this field is always 'td', and 'oac_query' (below) reflects
- + * the Overhead ACcounting values. */
- + union {
- + struct qm_fqd_taildrop td;
- + struct qm_fqd_oac oac_init;
- + };
- + u32 context_b;
- + union {
- + /* Treat it as 64-bit opaque */
- + u64 opaque;
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u32 hi;
- + u32 lo;
- +#else
- + u32 lo;
- + u32 hi;
- +#endif
- + };
- + /* Treat it as s/w portal stashing config */
- + /* See 1.5.6.7.1: "FQD Context_A field used for [...] */
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + struct qm_fqd_stashing stashing;
- + /* 48-bit address of FQ context to
- + * stash, must be cacheline-aligned */
- + u16 context_hi;
- + u32 context_lo;
- +#else
- + u32 context_lo;
- + u16 context_hi;
- + struct qm_fqd_stashing stashing;
- +#endif
- + } __packed;
- + } context_a;
- + struct qm_fqd_oac oac_query;
- +} __packed;
- +/* 64-bit converters for context_hi/lo */
- +static inline u64 qm_fqd_stashing_get64(const struct qm_fqd *fqd)
- +{
- + return ((u64)fqd->context_a.context_hi << 32) |
- + (u64)fqd->context_a.context_lo;
- +}
- +static inline dma_addr_t qm_fqd_stashing_addr(const struct qm_fqd *fqd)
- +{
- + return (dma_addr_t)qm_fqd_stashing_get64(fqd);
- +}
- +static inline u64 qm_fqd_context_a_get64(const struct qm_fqd *fqd)
- +{
- + return ((u64)fqd->context_a.hi << 32) |
- + (u64)fqd->context_a.lo;
- +}
- +/* Macro, so we compile better when 'v' isn't necessarily 64-bit */
- +#define qm_fqd_stashing_set64(fqd, v) \
- + do { \
- + struct qm_fqd *__fqd931 = (fqd); \
- + __fqd931->context_a.context_hi = upper_32_bits(v); \
- + __fqd931->context_a.context_lo = lower_32_bits(v); \
- + } while (0)
- +#define qm_fqd_context_a_set64(fqd, v) \
- + do { \
- + struct qm_fqd *__fqd931 = (fqd); \
- + __fqd931->context_a.hi = upper_32_bits(v); \
- + __fqd931->context_a.lo = lower_32_bits(v); \
- + } while (0)
- +/* convert a threshold value into mant+exp representation */
- +static inline int qm_fqd_taildrop_set(struct qm_fqd_taildrop *td, u32 val,
- + int roundup)
- +{
- + u32 e = 0;
- + int oddbit = 0;
- + if (val > 0xe0000000)
- + return -ERANGE;
- + while (val > 0xff) {
- + oddbit = val & 1;
- + val >>= 1;
- + e++;
- + if (roundup && oddbit)
- + val++;
- + }
- + td->exp = e;
- + td->mant = val;
- + return 0;
- +}
- +/* and the other direction */
- +static inline u32 qm_fqd_taildrop_get(const struct qm_fqd_taildrop *td)
- +{
- + return (u32)td->mant << td->exp;
- +}
- +
- +/* See 1.5.2.2: "Frame Queue Descriptor (FQD)" */
- +/* Frame Queue Descriptor (FQD) field 'fq_ctrl' uses these constants */
- +#define QM_FQCTRL_MASK 0x07ff /* 'fq_ctrl' flags; */
- +#define QM_FQCTRL_CGE 0x0400 /* Congestion Group Enable */
- +#define QM_FQCTRL_TDE 0x0200 /* Tail-Drop Enable */
- +#define QM_FQCTRL_ORP 0x0100 /* ORP Enable */
- +#define QM_FQCTRL_CTXASTASHING 0x0080 /* Context-A stashing */
- +#define QM_FQCTRL_CPCSTASH 0x0040 /* CPC Stash Enable */
- +#define QM_FQCTRL_FORCESFDR 0x0008 /* High-priority SFDRs */
- +#define QM_FQCTRL_AVOIDBLOCK 0x0004 /* Don't block active */
- +#define QM_FQCTRL_HOLDACTIVE 0x0002 /* Hold active in portal */
- +#define QM_FQCTRL_PREFERINCACHE 0x0001 /* Aggressively cache FQD */
- +#define QM_FQCTRL_LOCKINCACHE QM_FQCTRL_PREFERINCACHE /* older naming */
- +
- +/* See 1.5.6.7.1: "FQD Context_A field used for [...] */
- +/* Frame Queue Descriptor (FQD) field 'CONTEXT_A' uses these constants */
- +#define QM_STASHING_EXCL_ANNOTATION 0x04
- +#define QM_STASHING_EXCL_DATA 0x02
- +#define QM_STASHING_EXCL_CTX 0x01
- +
- +/* See 1.5.5.3: "Intra Class Scheduling" */
- +/* FQD field 'OAC' (Overhead ACcounting) uses these constants */
- +#define QM_OAC_ICS 0x2 /* Accounting for Intra-Class Scheduling */
- +#define QM_OAC_CG 0x1 /* Accounting for Congestion Groups */
- +
- +/* See 1.5.8.4: "FQ State Change Notification" */
- +/* This struct represents the 32-bit "WR_PARM_[GYR]" parameters in CGR fields
- + * and associated commands/responses. The WRED parameters are calculated from
- + * these fields as follows;
- + * MaxTH = MA * (2 ^ Mn)
- + * Slope = SA / (2 ^ Sn)
- + * MaxP = 4 * (Pn + 1)
- + */
- +struct qm_cgr_wr_parm {
- + union {
- + u32 word;
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u32 MA:8;
- + u32 Mn:5;
- + u32 SA:7; /* must be between 64-127 */
- + u32 Sn:6;
- + u32 Pn:6;
- +#else
- + u32 Pn:6;
- + u32 Sn:6;
- + u32 SA:7; /* must be between 64-127 */
- + u32 Mn:5;
- + u32 MA:8;
- +#endif
- + } __packed;
- + };
- +} __packed;
- +/* This struct represents the 13-bit "CS_THRES" CGR field. In the corresponding
- + * management commands, this is padded to a 16-bit structure field, so that's
- + * how we represent it here. The congestion state threshold is calculated from
- + * these fields as follows;
- + * CS threshold = TA * (2 ^ Tn)
- + */
- +struct qm_cgr_cs_thres {
- + union {
- + u16 hword;
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u16 __reserved:3;
- + u16 TA:8;
- + u16 Tn:5;
- +#else
- + u16 Tn:5;
- + u16 TA:8;
- + u16 __reserved:3;
- +#endif
- + } __packed;
- + };
- +} __packed;
- +/* This identical structure of CGR fields is present in the "Init/Modify CGR"
- + * commands and the "Query CGR" result. It's suctioned out here into its own
- + * struct. */
- +struct __qm_mc_cgr {
- + struct qm_cgr_wr_parm wr_parm_g;
- + struct qm_cgr_wr_parm wr_parm_y;
- + struct qm_cgr_wr_parm wr_parm_r;
- + u8 wr_en_g; /* boolean, use QM_CGR_EN */
- + u8 wr_en_y; /* boolean, use QM_CGR_EN */
- + u8 wr_en_r; /* boolean, use QM_CGR_EN */
- + u8 cscn_en; /* boolean, use QM_CGR_EN */
- + union {
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u16 cscn_targ_upd_ctrl; /* use QM_CSCN_TARG_UDP_ */
- + u16 cscn_targ_dcp_low; /* CSCN_TARG_DCP low-16bits */
- +#else
- + u16 cscn_targ_dcp_low; /* CSCN_TARG_DCP low-16bits */
- + u16 cscn_targ_upd_ctrl; /* use QM_CSCN_TARG_UDP_ */
- +#endif
- + };
- + u32 cscn_targ; /* use QM_CGR_TARG_* */
- + };
- + u8 cstd_en; /* boolean, use QM_CGR_EN */
- + u8 cs; /* boolean, only used in query response */
- + union {
- + /* use qm_cgr_cs_thres_set64() */
- + struct qm_cgr_cs_thres cs_thres;
- + u16 __cs_thres;
- + };
- + u8 mode; /* QMAN_CGR_MODE_FRAME not supported in rev1.0 */
- +} __packed;
- +#define QM_CGR_EN 0x01 /* For wr_en_*, cscn_en, cstd_en */
- +#define QM_CGR_TARG_UDP_CTRL_WRITE_BIT 0x8000 /* value written to portal bit*/
- +#define QM_CGR_TARG_UDP_CTRL_DCP 0x4000 /* 0: SWP, 1: DCP */
- +#define QM_CGR_TARG_PORTAL(n) (0x80000000 >> (n)) /* s/w portal, 0-9 */
- +#define QM_CGR_TARG_FMAN0 0x00200000 /* direct-connect portal: fman0 */
- +#define QM_CGR_TARG_FMAN1 0x00100000 /* : fman1 */
- +/* Convert CGR thresholds to/from "cs_thres" format */
- +static inline u64 qm_cgr_cs_thres_get64(const struct qm_cgr_cs_thres *th)
- +{
- + return (u64)th->TA << th->Tn;
- +}
- +static inline int qm_cgr_cs_thres_set64(struct qm_cgr_cs_thres *th, u64 val,
- + int roundup)
- +{
- + u32 e = 0;
- + int oddbit = 0;
- + while (val > 0xff) {
- + oddbit = val & 1;
- + val >>= 1;
- + e++;
- + if (roundup && oddbit)
- + val++;
- + }
- + th->Tn = e;
- + th->TA = val;
- + return 0;
- +}
- +
- +/* See 1.5.8.5.1: "Initialize FQ" */
- +/* See 1.5.8.5.2: "Query FQ" */
- +/* See 1.5.8.5.3: "Query FQ Non-Programmable Fields" */
- +/* See 1.5.8.5.4: "Alter FQ State Commands " */
- +/* See 1.5.8.6.1: "Initialize/Modify CGR" */
- +/* See 1.5.8.6.2: "CGR Test Write" */
- +/* See 1.5.8.6.3: "Query CGR" */
- +/* See 1.5.8.6.4: "Query Congestion Group State" */
- +struct qm_mcc_initfq {
- + u8 __reserved1;
- + u16 we_mask; /* Write Enable Mask */
- + u32 fqid; /* 24-bit */
- + u16 count; /* Initialises 'count+1' FQDs */
- + struct qm_fqd fqd; /* the FQD fields go here */
- + u8 __reserved3[30];
- +} __packed;
- +struct qm_mcc_queryfq {
- + u8 __reserved1[3];
- + u32 fqid; /* 24-bit */
- + u8 __reserved2[56];
- +} __packed;
- +struct qm_mcc_queryfq_np {
- + u8 __reserved1[3];
- + u32 fqid; /* 24-bit */
- + u8 __reserved2[56];
- +} __packed;
- +struct qm_mcc_alterfq {
- + u8 __reserved1[3];
- + u32 fqid; /* 24-bit */
- + u8 __reserved2;
- + u8 count; /* number of consecutive FQID */
- + u8 __reserved3[10];
- + u32 context_b; /* frame queue context b */
- + u8 __reserved4[40];
- +} __packed;
- +struct qm_mcc_initcgr {
- + u8 __reserved1;
- + u16 we_mask; /* Write Enable Mask */
- + struct __qm_mc_cgr cgr; /* CGR fields */
- + u8 __reserved2[2];
- + u8 cgid;
- + u8 __reserved4[32];
- +} __packed;
- +struct qm_mcc_cgrtestwrite {
- + u8 __reserved1[2];
- + u8 i_bcnt_hi:8;/* high 8-bits of 40-bit "Instant" */
- + u32 i_bcnt_lo; /* low 32-bits of 40-bit */
- + u8 __reserved2[23];
- + u8 cgid;
- + u8 __reserved3[32];
- +} __packed;
- +struct qm_mcc_querycgr {
- + u8 __reserved1[30];
- + u8 cgid;
- + u8 __reserved2[32];
- +} __packed;
- +struct qm_mcc_querycongestion {
- + u8 __reserved[63];
- +} __packed;
- +struct qm_mcc_querywq {
- + u8 __reserved;
- + /* select channel if verb != QUERYWQ_DEDICATED */
- + union {
- + u16 channel_wq; /* ignores wq (3 lsbits) */
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u16 id:13; /* qm_channel */
- + u16 __reserved1:3;
- +#else
- + u16 __reserved1:3;
- + u16 id:13; /* qm_channel */
- +#endif
- + } __packed channel;
- + };
- + u8 __reserved2[60];
- +} __packed;
- +
- +struct qm_mcc_ceetm_lfqmt_config {
- + u8 __reserved1[4];
- + u32 lfqid:24;
- + u8 __reserved2[2];
- + u16 cqid;
- + u8 __reserved3[2];
- + u16 dctidx;
- + u8 __reserved4[48];
- +} __packed;
- +
- +struct qm_mcc_ceetm_lfqmt_query {
- + u8 __reserved1[4];
- + u32 lfqid:24;
- + u8 __reserved2[56];
- +} __packed;
- +
- +struct qm_mcc_ceetm_cq_config {
- + u8 __reserved1;
- + u16 cqid;
- + u8 dcpid;
- + u8 __reserved2;
- + u16 ccgid;
- + u8 __reserved3[56];
- +} __packed;
- +
- +struct qm_mcc_ceetm_cq_query {
- + u8 __reserved1;
- + u16 cqid;
- + u8 dcpid;
- + u8 __reserved2[59];
- +} __packed;
- +
- +struct qm_mcc_ceetm_dct_config {
- + u8 __reserved1;
- + u16 dctidx;
- + u8 dcpid;
- + u8 __reserved2[15];
- + u32 context_b;
- + u64 context_a;
- + u8 __reserved3[32];
- +} __packed;
- +
- +struct qm_mcc_ceetm_dct_query {
- + u8 __reserved1;
- + u16 dctidx;
- + u8 dcpid;
- + u8 __reserved2[59];
- +} __packed;
- +
- +struct qm_mcc_ceetm_class_scheduler_config {
- + u8 __reserved1;
- + u16 cqcid;
- + u8 dcpid;
- + u8 __reserved2[6];
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u8 gpc_reserved:1;
- + u8 gpc_combine_flag:1;
- + u8 gpc_prio_b:3;
- + u8 gpc_prio_a:3;
- +#else
- + u8 gpc_prio_a:3;
- + u8 gpc_prio_b:3;
- + u8 gpc_combine_flag:1;
- + u8 gpc_reserved:1;
- +#endif
- + u16 crem;
- + u16 erem;
- + u8 w[8];
- + u8 __reserved3[40];
- +} __packed;
- +
- +struct qm_mcc_ceetm_class_scheduler_query {
- + u8 __reserved1;
- + u16 cqcid;
- + u8 dcpid;
- + u8 __reserved2[59];
- +} __packed;
- +
- +#define CEETM_COMMAND_CHANNEL_MAPPING (0 << 12)
- +#define CEETM_COMMAND_SP_MAPPING (1 << 12)
- +#define CEETM_COMMAND_CHANNEL_SHAPER (2 << 12)
- +#define CEETM_COMMAND_LNI_SHAPER (3 << 12)
- +#define CEETM_COMMAND_TCFC (4 << 12)
- +
- +#define CEETM_CCGRID_MASK 0x01FF
- +#define CEETM_CCGR_CM_CONFIGURE (0 << 14)
- +#define CEETM_CCGR_DN_CONFIGURE (1 << 14)
- +#define CEETM_CCGR_TEST_WRITE (2 << 14)
- +#define CEETM_CCGR_CM_QUERY (0 << 14)
- +#define CEETM_CCGR_DN_QUERY (1 << 14)
- +#define CEETM_CCGR_DN_QUERY_FLUSH (2 << 14)
- +#define CEETM_QUERY_CONGESTION_STATE (3 << 14)
- +
- +struct qm_mcc_ceetm_mapping_shaper_tcfc_config {
- + u8 __reserved1;
- + u16 cid;
- + u8 dcpid;
- + union {
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u8 map_shaped:1;
- + u8 map_reserved:4;
- + u8 map_lni_id:3;
- +#else
- + u8 map_lni_id:3;
- + u8 map_reserved:4;
- + u8 map_shaped:1;
- +#endif
- + u8 __reserved2[58];
- + } __packed channel_mapping;
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u8 map_reserved:5;
- + u8 map_lni_id:3;
- +#else
- + u8 map_lni_id:3;
- + u8 map_reserved:5;
- +#endif
- + u8 __reserved2[58];
- + } __packed sp_mapping;
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u8 cpl:1;
- + u8 cpl_reserved:2;
- + u8 oal:5;
- +#else
- + u8 oal:5;
- + u8 cpl_reserved:2;
- + u8 cpl:1;
- +#endif
- + u32 crtcr:24;
- + u32 ertcr:24;
- + u16 crtbl;
- + u16 ertbl;
- + u8 mps; /* This will be hardcoded by driver with 60 */
- + u8 __reserved2[47];
- + } __packed shaper_config;
- + struct {
- + u8 __reserved2[11];
- + u64 lnitcfcc;
- + u8 __reserved3[40];
- + } __packed tcfc_config;
- + };
- +} __packed;
- +
- +struct qm_mcc_ceetm_mapping_shaper_tcfc_query {
- + u8 __reserved1;
- + u16 cid;
- + u8 dcpid;
- + u8 __reserved2[59];
- +} __packed;
- +
- +struct qm_mcc_ceetm_ccgr_config {
- + u8 __reserved1;
- + u16 ccgrid;
- + u8 dcpid;
- + u8 __reserved2;
- + u16 we_mask;
- + union {
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u8 ctl_reserved:1;
- + u8 ctl_wr_en_g:1;
- + u8 ctl_wr_en_y:1;
- + u8 ctl_wr_en_r:1;
- + u8 ctl_td_en:1;
- + u8 ctl_td_mode:1;
- + u8 ctl_cscn_en:1;
- + u8 ctl_mode:1;
- +#else
- + u8 ctl_mode:1;
- + u8 ctl_cscn_en:1;
- + u8 ctl_td_mode:1;
- + u8 ctl_td_en:1;
- + u8 ctl_wr_en_r:1;
- + u8 ctl_wr_en_y:1;
- + u8 ctl_wr_en_g:1;
- + u8 ctl_reserved:1;
- +#endif
- + u8 cdv;
- + u16 cscn_tupd;
- + u8 oal;
- + u8 __reserved3;
- + struct qm_cgr_cs_thres cs_thres;
- + struct qm_cgr_cs_thres cs_thres_x;
- + struct qm_cgr_cs_thres td_thres;
- + struct qm_cgr_wr_parm wr_parm_g;
- + struct qm_cgr_wr_parm wr_parm_y;
- + struct qm_cgr_wr_parm wr_parm_r;
- + } __packed cm_config;
- + struct {
- + u8 dnc;
- + u8 dn0;
- + u8 dn1;
- + u64 dnba:40;
- + u8 __reserved3[2];
- + u16 dnth_0;
- + u8 __reserved4[2];
- + u16 dnth_1;
- + u8 __reserved5[8];
- + } __packed dn_config;
- + struct {
- + u8 __reserved3[3];
- + u64 i_cnt:40;
- + u8 __reserved4[16];
- + } __packed test_write;
- + };
- + u8 __reserved5[32];
- +} __packed;
- +
- +struct qm_mcc_ceetm_ccgr_query {
- + u8 __reserved1;
- + u16 ccgrid;
- + u8 dcpid;
- + u8 __reserved2[59];
- +} __packed;
- +
- +struct qm_mcc_ceetm_cq_peek_pop_xsfdrread {
- + u8 __reserved1;
- + u16 cqid;
- + u8 dcpid;
- + u8 ct;
- + u16 xsfdr;
- + u8 __reserved2[56];
- +} __packed;
- +
- +#define CEETM_QUERY_DEQUEUE_STATISTICS 0x00
- +#define CEETM_QUERY_DEQUEUE_CLEAR_STATISTICS 0x01
- +#define CEETM_WRITE_DEQUEUE_STATISTICS 0x02
- +#define CEETM_QUERY_REJECT_STATISTICS 0x03
- +#define CEETM_QUERY_REJECT_CLEAR_STATISTICS 0x04
- +#define CEETM_WRITE_REJECT_STATISTICS 0x05
- +struct qm_mcc_ceetm_statistics_query_write {
- + u8 __reserved1;
- + u16 cid;
- + u8 dcpid;
- + u8 ct;
- + u8 __reserved2[13];
- + u64 frm_cnt:40;
- + u8 __reserved3[2];
- + u64 byte_cnt:48;
- + u8 __reserved[32];
- +} __packed;
- +
- +struct qm_mc_command {
- + u8 __dont_write_directly__verb;
- + union {
- + struct qm_mcc_initfq initfq;
- + struct qm_mcc_queryfq queryfq;
- + struct qm_mcc_queryfq_np queryfq_np;
- + struct qm_mcc_alterfq alterfq;
- + struct qm_mcc_initcgr initcgr;
- + struct qm_mcc_cgrtestwrite cgrtestwrite;
- + struct qm_mcc_querycgr querycgr;
- + struct qm_mcc_querycongestion querycongestion;
- + struct qm_mcc_querywq querywq;
- + struct qm_mcc_ceetm_lfqmt_config lfqmt_config;
- + struct qm_mcc_ceetm_lfqmt_query lfqmt_query;
- + struct qm_mcc_ceetm_cq_config cq_config;
- + struct qm_mcc_ceetm_cq_query cq_query;
- + struct qm_mcc_ceetm_dct_config dct_config;
- + struct qm_mcc_ceetm_dct_query dct_query;
- + struct qm_mcc_ceetm_class_scheduler_config csch_config;
- + struct qm_mcc_ceetm_class_scheduler_query csch_query;
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_config mst_config;
- + struct qm_mcc_ceetm_mapping_shaper_tcfc_query mst_query;
- + struct qm_mcc_ceetm_ccgr_config ccgr_config;
- + struct qm_mcc_ceetm_ccgr_query ccgr_query;
- + struct qm_mcc_ceetm_cq_peek_pop_xsfdrread cq_ppxr;
- + struct qm_mcc_ceetm_statistics_query_write stats_query_write;
- + };
- +} __packed;
- +#define QM_MCC_VERB_VBIT 0x80
- +#define QM_MCC_VERB_MASK 0x7f /* where the verb contains; */
- +#define QM_MCC_VERB_INITFQ_PARKED 0x40
- +#define QM_MCC_VERB_INITFQ_SCHED 0x41
- +#define QM_MCC_VERB_QUERYFQ 0x44
- +#define QM_MCC_VERB_QUERYFQ_NP 0x45 /* "non-programmable" fields */
- +#define QM_MCC_VERB_QUERYWQ 0x46
- +#define QM_MCC_VERB_QUERYWQ_DEDICATED 0x47
- +#define QM_MCC_VERB_ALTER_SCHED 0x48 /* Schedule FQ */
- +#define QM_MCC_VERB_ALTER_FE 0x49 /* Force Eligible FQ */
- +#define QM_MCC_VERB_ALTER_RETIRE 0x4a /* Retire FQ */
- +#define QM_MCC_VERB_ALTER_OOS 0x4b /* Take FQ out of service */
- +#define QM_MCC_VERB_ALTER_FQXON 0x4d /* FQ XON */
- +#define QM_MCC_VERB_ALTER_FQXOFF 0x4e /* FQ XOFF */
- +#define QM_MCC_VERB_INITCGR 0x50
- +#define QM_MCC_VERB_MODIFYCGR 0x51
- +#define QM_MCC_VERB_CGRTESTWRITE 0x52
- +#define QM_MCC_VERB_QUERYCGR 0x58
- +#define QM_MCC_VERB_QUERYCONGESTION 0x59
- +/* INITFQ-specific flags */
- +#define QM_INITFQ_WE_MASK 0x01ff /* 'Write Enable' flags; */
- +#define QM_INITFQ_WE_OAC 0x0100
- +#define QM_INITFQ_WE_ORPC 0x0080
- +#define QM_INITFQ_WE_CGID 0x0040
- +#define QM_INITFQ_WE_FQCTRL 0x0020
- +#define QM_INITFQ_WE_DESTWQ 0x0010
- +#define QM_INITFQ_WE_ICSCRED 0x0008
- +#define QM_INITFQ_WE_TDTHRESH 0x0004
- +#define QM_INITFQ_WE_CONTEXTB 0x0002
- +#define QM_INITFQ_WE_CONTEXTA 0x0001
- +/* INITCGR/MODIFYCGR-specific flags */
- +#define QM_CGR_WE_MASK 0x07ff /* 'Write Enable Mask'; */
- +#define QM_CGR_WE_WR_PARM_G 0x0400
- +#define QM_CGR_WE_WR_PARM_Y 0x0200
- +#define QM_CGR_WE_WR_PARM_R 0x0100
- +#define QM_CGR_WE_WR_EN_G 0x0080
- +#define QM_CGR_WE_WR_EN_Y 0x0040
- +#define QM_CGR_WE_WR_EN_R 0x0020
- +#define QM_CGR_WE_CSCN_EN 0x0010
- +#define QM_CGR_WE_CSCN_TARG 0x0008
- +#define QM_CGR_WE_CSTD_EN 0x0004
- +#define QM_CGR_WE_CS_THRES 0x0002
- +#define QM_CGR_WE_MODE 0x0001
- +
- +/* See 1.5.9.7 CEETM Management Commands */
- +#define QM_CEETM_VERB_LFQMT_CONFIG 0x70
- +#define QM_CEETM_VERB_LFQMT_QUERY 0x71
- +#define QM_CEETM_VERB_CQ_CONFIG 0x72
- +#define QM_CEETM_VERB_CQ_QUERY 0x73
- +#define QM_CEETM_VERB_DCT_CONFIG 0x74
- +#define QM_CEETM_VERB_DCT_QUERY 0x75
- +#define QM_CEETM_VERB_CLASS_SCHEDULER_CONFIG 0x76
- +#define QM_CEETM_VERB_CLASS_SCHEDULER_QUERY 0x77
- +#define QM_CEETM_VERB_MAPPING_SHAPER_TCFC_CONFIG 0x78
- +#define QM_CEETM_VERB_MAPPING_SHAPER_TCFC_QUERY 0x79
- +#define QM_CEETM_VERB_CCGR_CONFIG 0x7A
- +#define QM_CEETM_VERB_CCGR_QUERY 0x7B
- +#define QM_CEETM_VERB_CQ_PEEK_POP_XFDRREAD 0x7C
- +#define QM_CEETM_VERB_STATISTICS_QUERY_WRITE 0x7D
- +
- +/* See 1.5.8.5.1: "Initialize FQ" */
- +/* See 1.5.8.5.2: "Query FQ" */
- +/* See 1.5.8.5.3: "Query FQ Non-Programmable Fields" */
- +/* See 1.5.8.5.4: "Alter FQ State Commands " */
- +/* See 1.5.8.6.1: "Initialize/Modify CGR" */
- +/* See 1.5.8.6.2: "CGR Test Write" */
- +/* See 1.5.8.6.3: "Query CGR" */
- +/* See 1.5.8.6.4: "Query Congestion Group State" */
- +struct qm_mcr_initfq {
- + u8 __reserved1[62];
- +} __packed;
- +struct qm_mcr_queryfq {
- + u8 __reserved1[8];
- + struct qm_fqd fqd; /* the FQD fields are here */
- + u8 __reserved2[30];
- +} __packed;
- +struct qm_mcr_queryfq_np {
- + u8 __reserved1;
- + u8 state; /* QM_MCR_NP_STATE_*** */
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u8 __reserved2;
- + u32 fqd_link:24;
- + u16 __reserved3:2;
- + u16 odp_seq:14;
- + u16 __reserved4:2;
- + u16 orp_nesn:14;
- + u16 __reserved5:1;
- + u16 orp_ea_hseq:15;
- + u16 __reserved6:1;
- + u16 orp_ea_tseq:15;
- + u8 __reserved7;
- + u32 orp_ea_hptr:24;
- + u8 __reserved8;
- + u32 orp_ea_tptr:24;
- + u8 __reserved9;
- + u32 pfdr_hptr:24;
- + u8 __reserved10;
- + u32 pfdr_tptr:24;
- + u8 __reserved11[5];
- + u8 __reserved12:7;
- + u8 is:1;
- + u16 ics_surp;
- + u32 byte_cnt;
- + u8 __reserved13;
- + u32 frm_cnt:24;
- + u32 __reserved14;
- + u16 ra1_sfdr; /* QM_MCR_NP_RA1_*** */
- + u16 ra2_sfdr; /* QM_MCR_NP_RA2_*** */
- + u16 __reserved15;
- + u16 od1_sfdr; /* QM_MCR_NP_OD1_*** */
- + u16 od2_sfdr; /* QM_MCR_NP_OD2_*** */
- + u16 od3_sfdr; /* QM_MCR_NP_OD3_*** */
- +#else
- + u8 __reserved2;
- + u32 fqd_link:24;
- +
- + u16 odp_seq:14;
- + u16 __reserved3:2;
- +
- + u16 orp_nesn:14;
- + u16 __reserved4:2;
- +
- + u16 orp_ea_hseq:15;
- + u16 __reserved5:1;
- +
- + u16 orp_ea_tseq:15;
- + u16 __reserved6:1;
- +
- + u8 __reserved7;
- + u32 orp_ea_hptr:24;
- +
- + u8 __reserved8;
- + u32 orp_ea_tptr:24;
- +
- + u8 __reserved9;
- + u32 pfdr_hptr:24;
- +
- + u8 __reserved10;
- + u32 pfdr_tptr:24;
- +
- + u8 __reserved11[5];
- + u8 is:1;
- + u8 __reserved12:7;
- + u16 ics_surp;
- + u32 byte_cnt;
- + u8 __reserved13;
- + u32 frm_cnt:24;
- + u32 __reserved14;
- + u16 ra1_sfdr; /* QM_MCR_NP_RA1_*** */
- + u16 ra2_sfdr; /* QM_MCR_NP_RA2_*** */
- + u16 __reserved15;
- + u16 od1_sfdr; /* QM_MCR_NP_OD1_*** */
- + u16 od2_sfdr; /* QM_MCR_NP_OD2_*** */
- + u16 od3_sfdr; /* QM_MCR_NP_OD3_*** */
- +#endif
- +} __packed;
- +
- +
- +struct qm_mcr_alterfq {
- + u8 fqs; /* Frame Queue Status */
- + u8 __reserved1[61];
- +} __packed;
- +struct qm_mcr_initcgr {
- + u8 __reserved1[62];
- +} __packed;
- +struct qm_mcr_cgrtestwrite {
- + u16 __reserved1;
- + struct __qm_mc_cgr cgr; /* CGR fields */
- + u8 __reserved2[3];
- + u32 __reserved3:24;
- + u32 i_bcnt_hi:8;/* high 8-bits of 40-bit "Instant" */
- + u32 i_bcnt_lo; /* low 32-bits of 40-bit */
- + u32 __reserved4:24;
- + u32 a_bcnt_hi:8;/* high 8-bits of 40-bit "Average" */
- + u32 a_bcnt_lo; /* low 32-bits of 40-bit */
- + u16 lgt; /* Last Group Tick */
- + u16 wr_prob_g;
- + u16 wr_prob_y;
- + u16 wr_prob_r;
- + u8 __reserved5[8];
- +} __packed;
- +struct qm_mcr_querycgr {
- + u16 __reserved1;
- + struct __qm_mc_cgr cgr; /* CGR fields */
- + u8 __reserved2[3];
- + union {
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u32 __reserved3:24;
- + u32 i_bcnt_hi:8;/* high 8-bits of 40-bit "Instant" */
- + u32 i_bcnt_lo; /* low 32-bits of 40-bit */
- +#else
- + u32 i_bcnt_lo; /* low 32-bits of 40-bit */
- + u32 i_bcnt_hi:8;/* high 8-bits of 40-bit "Instant" */
- + u32 __reserved3:24;
- +#endif
- + };
- + u64 i_bcnt;
- + };
- + union {
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u32 __reserved4:24;
- + u32 a_bcnt_hi:8;/* high 8-bits of 40-bit "Average" */
- + u32 a_bcnt_lo; /* low 32-bits of 40-bit */
- +#else
- + u32 a_bcnt_lo; /* low 32-bits of 40-bit */
- + u32 a_bcnt_hi:8;/* high 8-bits of 40-bit "Average" */
- + u32 __reserved4:24;
- +#endif
- + };
- + u64 a_bcnt;
- + };
- + union {
- + u32 cscn_targ_swp[4];
- + u8 __reserved5[16];
- + };
- +} __packed;
- +static inline u64 qm_mcr_querycgr_i_get64(const struct qm_mcr_querycgr *q)
- +{
- + return be64_to_cpu(q->i_bcnt);
- +}
- +static inline u64 qm_mcr_querycgr_a_get64(const struct qm_mcr_querycgr *q)
- +{
- + return be64_to_cpu(q->a_bcnt);
- +}
- +static inline u64 qm_mcr_cgrtestwrite_i_get64(
- + const struct qm_mcr_cgrtestwrite *q)
- +{
- + return be64_to_cpu(((u64)q->i_bcnt_hi << 32) | (u64)q->i_bcnt_lo);
- +}
- +static inline u64 qm_mcr_cgrtestwrite_a_get64(
- + const struct qm_mcr_cgrtestwrite *q)
- +{
- + return be64_to_cpu(((u64)q->a_bcnt_hi << 32) | (u64)q->a_bcnt_lo);
- +}
- +/* Macro, so we compile better if 'v' isn't always 64-bit */
- +#define qm_mcr_querycgr_i_set64(q, v) \
- + do { \
- + struct qm_mcr_querycgr *__q931 = (fd); \
- + __q931->i_bcnt_hi = upper_32_bits(v); \
- + __q931->i_bcnt_lo = lower_32_bits(v); \
- + } while (0)
- +#define qm_mcr_querycgr_a_set64(q, v) \
- + do { \
- + struct qm_mcr_querycgr *__q931 = (fd); \
- + __q931->a_bcnt_hi = upper_32_bits(v); \
- + __q931->a_bcnt_lo = lower_32_bits(v); \
- + } while (0)
- +struct __qm_mcr_querycongestion {
- + u32 __state[8];
- +};
- +struct qm_mcr_querycongestion {
- + u8 __reserved[30];
- + /* Access this struct using QM_MCR_QUERYCONGESTION() */
- + struct __qm_mcr_querycongestion state;
- +} __packed;
- +struct qm_mcr_querywq {
- + union {
- + u16 channel_wq; /* ignores wq (3 lsbits) */
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u16 id:13; /* qm_channel */
- + u16 __reserved:3;
- +#else
- + u16 __reserved:3;
- + u16 id:13; /* qm_channel */
- +#endif
- + } __packed channel;
- + };
- + u8 __reserved[28];
- + u32 wq_len[8];
- +} __packed;
- +
- +/* QMAN CEETM Management Command Response */
- +struct qm_mcr_ceetm_lfqmt_config {
- + u8 __reserved1[62];
- +} __packed;
- +struct qm_mcr_ceetm_lfqmt_query {
- + u8 __reserved1[8];
- + u16 cqid;
- + u8 __reserved2[2];
- + u16 dctidx;
- + u8 __reserved3[2];
- + u16 ccgid;
- + u8 __reserved4[44];
- +} __packed;
- +
- +struct qm_mcr_ceetm_cq_config {
- + u8 __reserved1[62];
- +} __packed;
- +
- +struct qm_mcr_ceetm_cq_query {
- + u8 __reserved1[4];
- + u16 ccgid;
- + u16 state;
- + u32 pfdr_hptr:24;
- + u32 pfdr_tptr:24;
- + u16 od1_xsfdr;
- + u16 od2_xsfdr;
- + u16 od3_xsfdr;
- + u16 od4_xsfdr;
- + u16 od5_xsfdr;
- + u16 od6_xsfdr;
- + u16 ra1_xsfdr;
- + u16 ra2_xsfdr;
- + u8 __reserved2;
- + u32 frm_cnt:24;
- + u8 __reserved333[28];
- +} __packed;
- +
- +struct qm_mcr_ceetm_dct_config {
- + u8 __reserved1[62];
- +} __packed;
- +
- +struct qm_mcr_ceetm_dct_query {
- + u8 __reserved1[18];
- + u32 context_b;
- + u64 context_a;
- + u8 __reserved2[32];
- +} __packed;
- +
- +struct qm_mcr_ceetm_class_scheduler_config {
- + u8 __reserved1[62];
- +} __packed;
- +
- +struct qm_mcr_ceetm_class_scheduler_query {
- + u8 __reserved1[9];
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u8 gpc_reserved:1;
- + u8 gpc_combine_flag:1;
- + u8 gpc_prio_b:3;
- + u8 gpc_prio_a:3;
- +#else
- + u8 gpc_prio_a:3;
- + u8 gpc_prio_b:3;
- + u8 gpc_combine_flag:1;
- + u8 gpc_reserved:1;
- +#endif
- + u16 crem;
- + u16 erem;
- + u8 w[8];
- + u8 __reserved2[5];
- + u32 wbfslist:24;
- + u32 d8;
- + u32 d9;
- + u32 d10;
- + u32 d11;
- + u32 d12;
- + u32 d13;
- + u32 d14;
- + u32 d15;
- +} __packed;
- +
- +struct qm_mcr_ceetm_mapping_shaper_tcfc_config {
- + u16 cid;
- + u8 __reserved2[60];
- +} __packed;
- +
- +struct qm_mcr_ceetm_mapping_shaper_tcfc_query {
- + u16 cid;
- + u8 __reserved1;
- + union {
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u8 map_shaped:1;
- + u8 map_reserved:4;
- + u8 map_lni_id:3;
- +#else
- + u8 map_lni_id:3;
- + u8 map_reserved:4;
- + u8 map_shaped:1;
- +#endif
- + u8 __reserved2[58];
- + } __packed channel_mapping_query;
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u8 map_reserved:5;
- + u8 map_lni_id:3;
- +#else
- + u8 map_lni_id:3;
- + u8 map_reserved:5;
- +#endif
- + u8 __reserved2[58];
- + } __packed sp_mapping_query;
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u8 cpl:1;
- + u8 cpl_reserved:2;
- + u8 oal:5;
- +#else
- + u8 oal:5;
- + u8 cpl_reserved:2;
- + u8 cpl:1;
- +#endif
- + u32 crtcr:24;
- + u32 ertcr:24;
- + u16 crtbl;
- + u16 ertbl;
- + u8 mps;
- + u8 __reserved2[15];
- + u32 crat;
- + u32 erat;
- + u8 __reserved3[24];
- + } __packed shaper_query;
- + struct {
- + u8 __reserved1[11];
- + u64 lnitcfcc;
- + u8 __reserved3[40];
- + } __packed tcfc_query;
- + };
- +} __packed;
- +
- +struct qm_mcr_ceetm_ccgr_config {
- + u8 __reserved1[46];
- + union {
- + u8 __reserved2[8];
- + struct {
- + u16 timestamp;
- + u16 wr_porb_g;
- + u16 wr_prob_y;
- + u16 wr_prob_r;
- + } __packed test_write;
- + };
- + u8 __reserved3[8];
- +} __packed;
- +
- +struct qm_mcr_ceetm_ccgr_query {
- + u8 __reserved1[6];
- + union {
- + struct {
- +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- + u8 ctl_reserved:1;
- + u8 ctl_wr_en_g:1;
- + u8 ctl_wr_en_y:1;
- + u8 ctl_wr_en_r:1;
- + u8 ctl_td_en:1;
- + u8 ctl_td_mode:1;
- + u8 ctl_cscn_en:1;
- + u8 ctl_mode:1;
- +#else
- + u8 ctl_mode:1;
- + u8 ctl_cscn_en:1;
- + u8 ctl_td_mode:1;
- + u8 ctl_td_en:1;
- + u8 ctl_wr_en_r:1;
- + u8 ctl_wr_en_y:1;
- + u8 ctl_wr_en_g:1;
- + u8 ctl_reserved:1;
- +#endif
- + u8 cdv;
- + u8 __reserved2[2];
- + u8 oal;
- + u8 __reserved3;
- + struct qm_cgr_cs_thres cs_thres;
- + struct qm_cgr_cs_thres cs_thres_x;
- + struct qm_cgr_cs_thres td_thres;
- + struct qm_cgr_wr_parm wr_parm_g;
- + struct qm_cgr_wr_parm wr_parm_y;
- + struct qm_cgr_wr_parm wr_parm_r;
- + u16 cscn_targ_dcp;
- + u8 dcp_lsn;
- + u64 i_cnt:40;
- + u8 __reserved4[3];
- + u64 a_cnt:40;
- + u32 cscn_targ_swp[4];
- + } __packed cm_query;
- + struct {
- + u8 dnc;
- + u8 dn0;
- + u8 dn1;
- + u64 dnba:40;
- + u8 __reserved2[2];
- + u16 dnth_0;
- + u8 __reserved3[2];
- + u16 dnth_1;
- + u8 __reserved4[10];
- + u16 dnacc_0;
- + u8 __reserved5[2];
- + u16 dnacc_1;
- + u8 __reserved6[24];
- + } __packed dn_query;
- + struct {
- + u8 __reserved2[24];
- + struct __qm_mcr_querycongestion state;
- + } __packed congestion_state;
- +
- + };
- +} __packed;
- +
- +struct qm_mcr_ceetm_cq_peek_pop_xsfdrread {
- + u8 stat;
- + u8 __reserved1[11];
- + u16 dctidx;
- + struct qm_fd fd;
- + u8 __reserved2[32];
- +} __packed;
- +
- +struct qm_mcr_ceetm_statistics_query {
- + u8 __reserved1[17];
- + u64 frm_cnt:40;
- + u8 __reserved2[2];
- + u64 byte_cnt:48;
- + u8 __reserved3[32];
- +} __packed;
- +
- +struct qm_mc_result {
- + u8 verb;
- + u8 result;
- + union {
- + struct qm_mcr_initfq initfq;
- + struct qm_mcr_queryfq queryfq;
- + struct qm_mcr_queryfq_np queryfq_np;
- + struct qm_mcr_alterfq alterfq;
- + struct qm_mcr_initcgr initcgr;
- + struct qm_mcr_cgrtestwrite cgrtestwrite;
- + struct qm_mcr_querycgr querycgr;
- + struct qm_mcr_querycongestion querycongestion;
- + struct qm_mcr_querywq querywq;
- + struct qm_mcr_ceetm_lfqmt_config lfqmt_config;
- + struct qm_mcr_ceetm_lfqmt_query lfqmt_query;
- + struct qm_mcr_ceetm_cq_config cq_config;
- + struct qm_mcr_ceetm_cq_query cq_query;
- + struct qm_mcr_ceetm_dct_config dct_config;
- + struct qm_mcr_ceetm_dct_query dct_query;
- + struct qm_mcr_ceetm_class_scheduler_config csch_config;
- + struct qm_mcr_ceetm_class_scheduler_query csch_query;
- + struct qm_mcr_ceetm_mapping_shaper_tcfc_config mst_config;
- + struct qm_mcr_ceetm_mapping_shaper_tcfc_query mst_query;
- + struct qm_mcr_ceetm_ccgr_config ccgr_config;
- + struct qm_mcr_ceetm_ccgr_query ccgr_query;
- + struct qm_mcr_ceetm_cq_peek_pop_xsfdrread cq_ppxr;
- + struct qm_mcr_ceetm_statistics_query stats_query;
- + };
- +} __packed;
- +
- +#define QM_MCR_VERB_RRID 0x80
- +#define QM_MCR_VERB_MASK QM_MCC_VERB_MASK
- +#define QM_MCR_VERB_INITFQ_PARKED QM_MCC_VERB_INITFQ_PARKED
- +#define QM_MCR_VERB_INITFQ_SCHED QM_MCC_VERB_INITFQ_SCHED
- +#define QM_MCR_VERB_QUERYFQ QM_MCC_VERB_QUERYFQ
- +#define QM_MCR_VERB_QUERYFQ_NP QM_MCC_VERB_QUERYFQ_NP
- +#define QM_MCR_VERB_QUERYWQ QM_MCC_VERB_QUERYWQ
- +#define QM_MCR_VERB_QUERYWQ_DEDICATED QM_MCC_VERB_QUERYWQ_DEDICATED
- +#define QM_MCR_VERB_ALTER_SCHED QM_MCC_VERB_ALTER_SCHED
- +#define QM_MCR_VERB_ALTER_FE QM_MCC_VERB_ALTER_FE
- +#define QM_MCR_VERB_ALTER_RETIRE QM_MCC_VERB_ALTER_RETIRE
- +#define QM_MCR_VERB_ALTER_OOS QM_MCC_VERB_ALTER_OOS
- +#define QM_MCR_RESULT_NULL 0x00
- +#define QM_MCR_RESULT_OK 0xf0
- +#define QM_MCR_RESULT_ERR_FQID 0xf1
- +#define QM_MCR_RESULT_ERR_FQSTATE 0xf2
- +#define QM_MCR_RESULT_ERR_NOTEMPTY 0xf3 /* OOS fails if FQ is !empty */
- +#define QM_MCR_RESULT_ERR_BADCHANNEL 0xf4
- +#define QM_MCR_RESULT_PENDING 0xf8
- +#define QM_MCR_RESULT_ERR_BADCOMMAND 0xff
- +#define QM_MCR_NP_STATE_FE 0x10
- +#define QM_MCR_NP_STATE_R 0x08
- +#define QM_MCR_NP_STATE_MASK 0x07 /* Reads FQD::STATE; */
- +#define QM_MCR_NP_STATE_OOS 0x00
- +#define QM_MCR_NP_STATE_RETIRED 0x01
- +#define QM_MCR_NP_STATE_TEN_SCHED 0x02
- +#define QM_MCR_NP_STATE_TRU_SCHED 0x03
- +#define QM_MCR_NP_STATE_PARKED 0x04
- +#define QM_MCR_NP_STATE_ACTIVE 0x05
- +#define QM_MCR_NP_PTR_MASK 0x07ff /* for RA[12] & OD[123] */
- +#define QM_MCR_NP_RA1_NRA(v) (((v) >> 14) & 0x3) /* FQD::NRA */
- +#define QM_MCR_NP_RA2_IT(v) (((v) >> 14) & 0x1) /* FQD::IT */
- +#define QM_MCR_NP_OD1_NOD(v) (((v) >> 14) & 0x3) /* FQD::NOD */
- +#define QM_MCR_NP_OD3_NPC(v) (((v) >> 14) & 0x3) /* FQD::NPC */
- +#define QM_MCR_FQS_ORLPRESENT 0x02 /* ORL fragments to come */
- +#define QM_MCR_FQS_NOTEMPTY 0x01 /* FQ has enqueued frames */
- +/* This extracts the state for congestion group 'n' from a query response.
- + * Eg.
- + * u8 cgr = [...];
- + * struct qm_mc_result *res = [...];
- + * printf("congestion group %d congestion state: %d\n", cgr,
- + * QM_MCR_QUERYCONGESTION(&res->querycongestion.state, cgr));
- + */
- +#define __CGR_WORD(num) (num >> 5)
- +#define __CGR_SHIFT(num) (num & 0x1f)
- +#define __CGR_NUM (sizeof(struct __qm_mcr_querycongestion) << 3)
- +static inline int QM_MCR_QUERYCONGESTION(struct __qm_mcr_querycongestion *p,
- + u8 cgr)
- +{
- + return be32_to_cpu(p->__state[__CGR_WORD(cgr)]) &
- + (0x80000000 >> __CGR_SHIFT(cgr));
- +}
- +
- +
- +/*********************/
- +/* Utility interface */
- +/*********************/
- +
- +/* Represents an allocator over a range of FQIDs. NB, accesses are not locked,
- + * spinlock them yourself if needed. */
- +struct qman_fqid_pool;
- +
- +/* Create/destroy a FQID pool, num must be a multiple of 32. NB, _destroy()
- + * always succeeds, but returns non-zero if there were "leaked" FQID
- + * allocations. */
- +struct qman_fqid_pool *qman_fqid_pool_create(u32 fqid_start, u32 num);
- +int qman_fqid_pool_destroy(struct qman_fqid_pool *pool);
- +/* Alloc/free a FQID from the range. _alloc() returns zero for success. */
- +int qman_fqid_pool_alloc(struct qman_fqid_pool *pool, u32 *fqid);
- +void qman_fqid_pool_free(struct qman_fqid_pool *pool, u32 fqid);
- +u32 qman_fqid_pool_used(struct qman_fqid_pool *pool);
- +
- +/*******************************************************************/
- +/* Managed (aka "shared" or "mux/demux") portal, high-level i/face */
- +/*******************************************************************/
- +
- + /* Portal and Frame Queues */
- + /* ----------------------- */
- +/* Represents a managed portal */
- +struct qman_portal;
- +
- +/* This object type represents Qman frame queue descriptors (FQD), it is
- + * cacheline-aligned, and initialised by qman_create_fq(). The structure is
- + * defined further down. */
- +struct qman_fq;
- +
- +/* This object type represents a Qman congestion group, it is defined further
- + * down. */
- +struct qman_cgr;
- +
- +struct qman_portal_config {
- + /* If the caller enables DQRR stashing (and thus wishes to operate the
- + * portal from only one cpu), this is the logical CPU that the portal
- + * will stash to. Whether stashing is enabled or not, this setting is
- + * also used for any "core-affine" portals, ie. default portals
- + * associated to the corresponding cpu. -1 implies that there is no core
- + * affinity configured. */
- + int cpu;
- + /* portal interrupt line */
- + int irq;
- + /* the unique index of this portal */
- + u32 index;
- + /* Is this portal shared? (If so, it has coarser locking and demuxes
- + * processing on behalf of other CPUs.) */
- + int is_shared;
- + /* The portal's dedicated channel id, use this value for initialising
- + * frame queues to target this portal when scheduled. */
- + u16 channel;
- + /* A mask of which pool channels this portal has dequeue access to
- + * (using QM_SDQCR_CHANNELS_POOL(n) for the bitmask) */
- + u32 pools;
- +};
- +
- +/* This enum, and the callback type that returns it, are used when handling
- + * dequeued frames via DQRR. Note that for "null" callbacks registered with the
- + * portal object (for handling dequeues that do not demux because contextB is
- + * NULL), the return value *MUST* be qman_cb_dqrr_consume. */
- +enum qman_cb_dqrr_result {
- + /* DQRR entry can be consumed */
- + qman_cb_dqrr_consume,
- + /* Like _consume, but requests parking - FQ must be held-active */
- + qman_cb_dqrr_park,
- + /* Does not consume, for DCA mode only. This allows out-of-order
- + * consumes by explicit calls to qman_dca() and/or the use of implicit
- + * DCA via EQCR entries. */
- + qman_cb_dqrr_defer,
- + /* Stop processing without consuming this ring entry. Exits the current
- + * qman_poll_dqrr() or interrupt-handling, as appropriate. If within an
- + * interrupt handler, the callback would typically call
- + * qman_irqsource_remove(QM_PIRQ_DQRI) before returning this value,
- + * otherwise the interrupt will reassert immediately. */
- + qman_cb_dqrr_stop,
- + /* Like qman_cb_dqrr_stop, but consumes the current entry. */
- + qman_cb_dqrr_consume_stop
- +};
- +typedef enum qman_cb_dqrr_result (*qman_cb_dqrr)(struct qman_portal *qm,
- + struct qman_fq *fq,
- + const struct qm_dqrr_entry *dqrr);
- +
- +/* This callback type is used when handling ERNs, FQRNs and FQRLs via MR. They
- + * are always consumed after the callback returns. */
- +typedef void (*qman_cb_mr)(struct qman_portal *qm, struct qman_fq *fq,
- + const struct qm_mr_entry *msg);
- +
- +/* This callback type is used when handling DCP ERNs */
- +typedef void (*qman_cb_dc_ern)(struct qman_portal *qm,
- + const struct qm_mr_entry *msg);
- +
- +/* s/w-visible states. Ie. tentatively scheduled + truly scheduled + active +
- + * held-active + held-suspended are just "sched". Things like "retired" will not
- + * be assumed until it is complete (ie. QMAN_FQ_STATE_CHANGING is set until
- + * then, to indicate it's completing and to gate attempts to retry the retire
- + * command). Note, park commands do not set QMAN_FQ_STATE_CHANGING because it's
- + * technically impossible in the case of enqueue DCAs (which refer to DQRR ring
- + * index rather than the FQ that ring entry corresponds to), so repeated park
- + * commands are allowed (if you're silly enough to try) but won't change FQ
- + * state, and the resulting park notifications move FQs from "sched" to
- + * "parked". */
- +enum qman_fq_state {
- + qman_fq_state_oos,
- + qman_fq_state_parked,
- + qman_fq_state_sched,
- + qman_fq_state_retired
- +};
- +
- +/* Frame queue objects (struct qman_fq) are stored within memory passed to
- + * qman_create_fq(), as this allows stashing of caller-provided demux callback
- + * pointers at no extra cost to stashing of (driver-internal) FQ state. If the
- + * caller wishes to add per-FQ state and have it benefit from dequeue-stashing,
- + * they should;
- + *
- + * (a) extend the qman_fq structure with their state; eg.
- + *
- + * // myfq is allocated and driver_fq callbacks filled in;
- + * struct my_fq {
- + * struct qman_fq base;
- + * int an_extra_field;
- + * [ ... add other fields to be associated with each FQ ...]
- + * } *myfq = some_my_fq_allocator();
- + * struct qman_fq *fq = qman_create_fq(fqid, flags, &myfq->base);
- + *
- + * // in a dequeue callback, access extra fields from 'fq' via a cast;
- + * struct my_fq *myfq = (struct my_fq *)fq;
- + * do_something_with(myfq->an_extra_field);
- + * [...]
- + *
- + * (b) when and if configuring the FQ for context stashing, specify how ever
- + * many cachelines are required to stash 'struct my_fq', to accelerate not
- + * only the Qman driver but the callback as well.
- + */
- +
- +struct qman_fq_cb {
- + qman_cb_dqrr dqrr; /* for dequeued frames */
- + qman_cb_mr ern; /* for s/w ERNs */
- + qman_cb_mr fqs; /* frame-queue state changes*/
- +};
- +
- +struct qman_fq {
- + /* Caller of qman_create_fq() provides these demux callbacks */
- + struct qman_fq_cb cb;
- + /* These are internal to the driver, don't touch. In particular, they
- + * may change, be removed, or extended (so you shouldn't rely on
- + * sizeof(qman_fq) being a constant). */
- + spinlock_t fqlock;
- + u32 fqid;
- + volatile unsigned long flags;
- + enum qman_fq_state state;
- + int cgr_groupid;
- + struct rb_node node;
- +#ifdef CONFIG_FSL_QMAN_FQ_LOOKUP
- + u32 key;
- +#endif
- +};
- +
- +/* This callback type is used when handling congestion group entry/exit.
- + * 'congested' is non-zero on congestion-entry, and zero on congestion-exit. */
- +typedef void (*qman_cb_cgr)(struct qman_portal *qm,
- + struct qman_cgr *cgr, int congested);
- +
- +struct qman_cgr {
- + /* Set these prior to qman_create_cgr() */
- + u32 cgrid; /* 0..255, but u32 to allow specials like -1, 256, etc.*/
- + qman_cb_cgr cb;
- + /* These are private to the driver */
- + u16 chan; /* portal channel this object is created on */
- + struct list_head node;
- +};
- +
- +/* Flags to qman_create_fq() */
- +#define QMAN_FQ_FLAG_NO_ENQUEUE 0x00000001 /* can't enqueue */
- +#define QMAN_FQ_FLAG_NO_MODIFY 0x00000002 /* can only enqueue */
- +#define QMAN_FQ_FLAG_TO_DCPORTAL 0x00000004 /* consumed by CAAM/PME/Fman */
- +#define QMAN_FQ_FLAG_LOCKED 0x00000008 /* multi-core locking */
- +#define QMAN_FQ_FLAG_AS_IS 0x00000010 /* query h/w state */
- +#define QMAN_FQ_FLAG_DYNAMIC_FQID 0x00000020 /* (de)allocate fqid */
- +
- +/* Flags to qman_destroy_fq() */
- +#define QMAN_FQ_DESTROY_PARKED 0x00000001 /* FQ can be parked or OOS */
- +
- +/* Flags from qman_fq_state() */
- +#define QMAN_FQ_STATE_CHANGING 0x80000000 /* 'state' is changing */
- +#define QMAN_FQ_STATE_NE 0x40000000 /* retired FQ isn't empty */
- +#define QMAN_FQ_STATE_ORL 0x20000000 /* retired FQ has ORL */
- +#define QMAN_FQ_STATE_BLOCKOOS 0xe0000000 /* if any are set, no OOS */
- +#define QMAN_FQ_STATE_CGR_EN 0x10000000 /* CGR enabled */
- +#define QMAN_FQ_STATE_VDQCR 0x08000000 /* being volatile dequeued */
- +
- +/* Flags to qman_init_fq() */
- +#define QMAN_INITFQ_FLAG_SCHED 0x00000001 /* schedule rather than park */
- +#define QMAN_INITFQ_FLAG_LOCAL 0x00000004 /* set dest portal */
- +
- +/* Flags to qman_volatile_dequeue() */
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT
- +#define QMAN_VOLATILE_FLAG_WAIT 0x00000001 /* wait if VDQCR is in use */
- +#define QMAN_VOLATILE_FLAG_WAIT_INT 0x00000002 /* if wait, interruptible? */
- +#define QMAN_VOLATILE_FLAG_FINISH 0x00000004 /* wait till VDQCR completes */
- +#endif
- +
- +/* Flags to qman_enqueue(). NB, the strange numbering is to align with hardware,
- + * bit-wise. (NB: the PME API is sensitive to these precise numberings too, so
- + * any change here should be audited in PME.) */
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT
- +#define QMAN_ENQUEUE_FLAG_WAIT 0x00010000 /* wait if EQCR is full */
- +#define QMAN_ENQUEUE_FLAG_WAIT_INT 0x00020000 /* if wait, interruptible? */
- +#ifdef CONFIG_FSL_DPA_CAN_WAIT_SYNC
- +#define QMAN_ENQUEUE_FLAG_WAIT_SYNC 0x00000004 /* if wait, until consumed? */
- +#endif
- +#endif
- +#define QMAN_ENQUEUE_FLAG_WATCH_CGR 0x00080000 /* watch congestion state */
- +#define QMAN_ENQUEUE_FLAG_DCA 0x00008000 /* perform enqueue-DCA */
- +#define QMAN_ENQUEUE_FLAG_DCA_PARK 0x00004000 /* If DCA, requests park */
- +#define QMAN_ENQUEUE_FLAG_DCA_PTR(p) /* If DCA, p is DQRR entry */ \
- + (((u32)(p) << 2) & 0x00000f00)
- +#define QMAN_ENQUEUE_FLAG_C_GREEN 0x00000000 /* choose one C_*** flag */
- +#define QMAN_ENQUEUE_FLAG_C_YELLOW 0x00000008
- +#define QMAN_ENQUEUE_FLAG_C_RED 0x00000010
- +#define QMAN_ENQUEUE_FLAG_C_OVERRIDE 0x00000018
- +/* For the ORP-specific qman_enqueue_orp() variant;
- + * - this flag indicates "Not Last In Sequence", ie. all but the final fragment
- + * of a frame. */
- +#define QMAN_ENQUEUE_FLAG_NLIS 0x01000000
- +/* - this flag performs no enqueue but fills in an ORP sequence number that
- + * would otherwise block it (eg. if a frame has been dropped). */
- +#define QMAN_ENQUEUE_FLAG_HOLE 0x02000000
- +/* - this flag performs no enqueue but advances NESN to the given sequence
- + * number. */
- +#define QMAN_ENQUEUE_FLAG_NESN 0x04000000
- +
- +/* Flags to qman_modify_cgr() */
- +#define QMAN_CGR_FLAG_USE_INIT 0x00000001
- +#define QMAN_CGR_MODE_FRAME 0x00000001
- +
- + /* Portal Management */
- + /* ----------------- */
- +/**
- + * qman_get_portal_config - get portal configuration settings
- + *
- + * This returns a read-only view of the current cpu's affine portal settings.
- + */
- +const struct qman_portal_config *qman_get_portal_config(void);
- +
- +/**
- + * qman_irqsource_get - return the portal work that is interrupt-driven
- + *
- + * Returns a bitmask of QM_PIRQ_**I processing sources that are currently
- + * enabled for interrupt handling on the current cpu's affine portal. These
- + * sources will trigger the portal interrupt and the interrupt handler (or a
- + * tasklet/bottom-half it defers to) will perform the corresponding processing
- + * work. The qman_poll_***() functions will only process sources that are not in
- + * this bitmask. If the current CPU is sharing a portal hosted on another CPU,
- + * this always returns zero.
- + */
- +u32 qman_irqsource_get(void);
- +
- +/**
- + * qman_irqsource_add - add processing sources to be interrupt-driven
- + * @bits: bitmask of QM_PIRQ_**I processing sources
- + *
- + * Adds processing sources that should be interrupt-driven (rather than
- + * processed via qman_poll_***() functions). Returns zero for success, or
- + * -EINVAL if the current CPU is sharing a portal hosted on another CPU.
- + */
- +int qman_irqsource_add(u32 bits);
- +
- +/**
- + * qman_irqsource_remove - remove processing sources from being interrupt-driven
- + * @bits: bitmask of QM_PIRQ_**I processing sources
- + *
- + * Removes processing sources from being interrupt-driven, so that they will
- + * instead be processed via qman_poll_***() functions. Returns zero for success,
- + * or -EINVAL if the current CPU is sharing a portal hosted on another CPU.
- + */
- +int qman_irqsource_remove(u32 bits);
- +
- +/**
- + * qman_affine_cpus - return a mask of cpus that have affine portals
- + */
- +const cpumask_t *qman_affine_cpus(void);
- +
- +/**
- + * qman_affine_channel - return the channel ID of an portal
- + * @cpu: the cpu whose affine portal is the subject of the query
- + *
- + * If @cpu is -1, the affine portal for the current CPU will be used. It is a
- + * bug to call this function for any value of @cpu (other than -1) that is not a
- + * member of the mask returned from qman_affine_cpus().
- + */
- +u16 qman_affine_channel(int cpu);
- +
- +/**
- + * qman_get_affine_portal - return the portal pointer affine to cpu
- + * @cpu: the cpu whose affine portal is the subject of the query
- + *
- + */
- +void *qman_get_affine_portal(int cpu);
- +
- +/**
- + * qman_poll_dqrr - process DQRR (fast-path) entries
- + * @limit: the maximum number of DQRR entries to process
- + *
- + * Use of this function requires that DQRR processing not be interrupt-driven.
- + * Ie. the value returned by qman_irqsource_get() should not include
- + * QM_PIRQ_DQRI. If the current CPU is sharing a portal hosted on another CPU,
- + * this function will return -EINVAL, otherwise the return value is >=0 and
- + * represents the number of DQRR entries processed.
- + */
- +int qman_poll_dqrr(unsigned int limit);
- +
- +/**
- + * qman_poll_slow - process anything (except DQRR) that isn't interrupt-driven.
- + *
- + * This function does any portal processing that isn't interrupt-driven. If the
- + * current CPU is sharing a portal hosted on another CPU, this function will
- + * return (u32)-1, otherwise the return value is a bitmask of QM_PIRQ_* sources
- + * indicating what interrupt sources were actually processed by the call.
- + */
- +u32 qman_poll_slow(void);
- +
- +/**
- + * qman_poll - legacy wrapper for qman_poll_dqrr() and qman_poll_slow()
- + *
- + * Dispatcher logic on a cpu can use this to trigger any maintenance of the
- + * affine portal. There are two classes of portal processing in question;
- + * fast-path (which involves demuxing dequeue ring (DQRR) entries and tracking
- + * enqueue ring (EQCR) consumption), and slow-path (which involves EQCR
- + * thresholds, congestion state changes, etc). This function does whatever
- + * processing is not triggered by interrupts.
- + *
- + * Note, if DQRR and some slow-path processing are poll-driven (rather than
- + * interrupt-driven) then this function uses a heuristic to determine how often
- + * to run slow-path processing - as slow-path processing introduces at least a
- + * minimum latency each time it is run, whereas fast-path (DQRR) processing is
- + * close to zero-cost if there is no work to be done. Applications can tune this
- + * behaviour themselves by using qman_poll_dqrr() and qman_poll_slow() directly
- + * rather than going via this wrapper.
- + */
- +void qman_poll(void);
- +
- +/**
- + * qman_stop_dequeues - Stop h/w dequeuing to the s/w portal
- + *
- + * Disables DQRR processing of the portal. This is reference-counted, so
- + * qman_start_dequeues() must be called as many times as qman_stop_dequeues() to
- + * truly re-enable dequeuing.
- + */
- +void qman_stop_dequeues(void);
- +
- +/**
- + * qman_start_dequeues - (Re)start h/w dequeuing to the s/w portal
- + *
- + * Enables DQRR processing of the portal. This is reference-counted, so
- + * qman_start_dequeues() must be called as many times as qman_stop_dequeues() to
- + * truly re-enable dequeuing.
- + */
- +void qman_start_dequeues(void);
- +
- +/**
- + * qman_static_dequeue_add - Add pool channels to the portal SDQCR
- + * @pools: bit-mask of pool channels, using QM_SDQCR_CHANNELS_POOL(n)
- + *
- + * Adds a set of pool channels to the portal's static dequeue command register
- + * (SDQCR). The requested pools are limited to those the portal has dequeue
- + * access to.
- + */
- +void qman_static_dequeue_add(u32 pools);
- +
- +/**
- + * qman_static_dequeue_del - Remove pool channels from the portal SDQCR
- + * @pools: bit-mask of pool channels, using QM_SDQCR_CHANNELS_POOL(n)
- + *
- + * Removes a set of pool channels from the portal's static dequeue command
- + * register (SDQCR). The requested pools are limited to those the portal has
- + * dequeue access to.
- + */
- +void qman_static_dequeue_del(u32 pools);
- +
- +/**
- + * qman_static_dequeue_get - return the portal's current SDQCR
- + *
- + * Returns the portal's current static dequeue command register (SDQCR). The
- + * entire register is returned, so if only the currently-enabled pool channels
- + * are desired, mask the return value with QM_SDQCR_CHANNELS_POOL_MASK.
- + */
- +u32 qman_static_dequeue_get(void);
- +
- +/**
- + * qman_dca - Perform a Discrete Consumption Acknowledgement
- + * @dq: the DQRR entry to be consumed
- + * @park_request: indicates whether the held-active @fq should be parked
- + *
- + * Only allowed in DCA-mode portals, for DQRR entries whose handler callback had
- + * previously returned 'qman_cb_dqrr_defer'. NB, as with the other APIs, this
- + * does not take a 'portal' argument but implies the core affine portal from the
- + * cpu that is currently executing the function. For reasons of locking, this
- + * function must be called from the same CPU as that which processed the DQRR
- + * entry in the first place.
- + */
- +void qman_dca(struct qm_dqrr_entry *dq, int park_request);
- +
- +/**
- + * qman_eqcr_is_empty - Determine if portal's EQCR is empty
- + *
- + * For use in situations where a cpu-affine caller needs to determine when all
- + * enqueues for the local portal have been processed by Qman but can't use the
- + * QMAN_ENQUEUE_FLAG_WAIT_SYNC flag to do this from the final qman_enqueue().
- + * The function forces tracking of EQCR consumption (which normally doesn't
- + * happen until enqueue processing needs to find space to put new enqueue
- + * commands), and returns zero if the ring still has unprocessed entries,
- + * non-zero if it is empty.
- + */
- +int qman_eqcr_is_empty(void);
- +
- +/**
- + * qman_set_dc_ern - Set the handler for DCP enqueue rejection notifications
- + * @handler: callback for processing DCP ERNs
- + * @affine: whether this handler is specific to the locally affine portal
- + *
- + * If a hardware block's interface to Qman (ie. its direct-connect portal, or
- + * DCP) is configured not to receive enqueue rejections, then any enqueues
- + * through that DCP that are rejected will be sent to a given software portal.
- + * If @affine is non-zero, then this handler will only be used for DCP ERNs
- + * received on the portal affine to the current CPU. If multiple CPUs share a
- + * portal and they all call this function, they will be setting the handler for
- + * the same portal! If @affine is zero, then this handler will be global to all
- + * portals handled by this instance of the driver. Only those portals that do
- + * not have their own affine handler will use the global handler.
- + */
- +void qman_set_dc_ern(qman_cb_dc_ern handler, int affine);
- +
- + /* FQ management */
- + /* ------------- */
- +/**
- + * qman_create_fq - Allocates a FQ
- + * @fqid: the index of the FQD to encapsulate, must be "Out of Service"
- + * @flags: bit-mask of QMAN_FQ_FLAG_*** options
- + * @fq: memory for storing the 'fq', with callbacks filled in
- + *
- + * Creates a frame queue object for the given @fqid, unless the
- + * QMAN_FQ_FLAG_DYNAMIC_FQID flag is set in @flags, in which case a FQID is
- + * dynamically allocated (or the function fails if none are available). Once
- + * created, the caller should not touch the memory at 'fq' except as extended to
- + * adjacent memory for user-defined fields (see the definition of "struct
- + * qman_fq" for more info). NO_MODIFY is only intended for enqueuing to
- + * pre-existing frame-queues that aren't to be otherwise interfered with, it
- + * prevents all other modifications to the frame queue. The TO_DCPORTAL flag
- + * causes the driver to honour any contextB modifications requested in the
- + * qm_init_fq() API, as this indicates the frame queue will be consumed by a
- + * direct-connect portal (PME, CAAM, or Fman). When frame queues are consumed by
- + * software portals, the contextB field is controlled by the driver and can't be
- + * modified by the caller. If the AS_IS flag is specified, management commands
- + * will be used on portal @p to query state for frame queue @fqid and construct
- + * a frame queue object based on that, rather than assuming/requiring that it be
- + * Out of Service.
- + */
- +int qman_create_fq(u32 fqid, u32 flags, struct qman_fq *fq);
- +
- +/**
- + * qman_destroy_fq - Deallocates a FQ
- + * @fq: the frame queue object to release
- + * @flags: bit-mask of QMAN_FQ_FREE_*** options
- + *
- + * The memory for this frame queue object ('fq' provided in qman_create_fq()) is
- + * not deallocated but the caller regains ownership, to do with as desired. The
- + * FQ must be in the 'out-of-service' state unless the QMAN_FQ_FREE_PARKED flag
- + * is specified, in which case it may also be in the 'parked' state.
- + */
- +void qman_destroy_fq(struct qman_fq *fq, u32 flags);
- +
- +/**
- + * qman_fq_fqid - Queries the frame queue ID of a FQ object
- + * @fq: the frame queue object to query
- + */
- +u32 qman_fq_fqid(struct qman_fq *fq);
- +
- +/**
- + * qman_fq_state - Queries the state of a FQ object
- + * @fq: the frame queue object to query
- + * @state: pointer to state enum to return the FQ scheduling state
- + * @flags: pointer to state flags to receive QMAN_FQ_STATE_*** bitmask
- + *
- + * Queries the state of the FQ object, without performing any h/w commands.
- + * This captures the state, as seen by the driver, at the time the function
- + * executes.
- + */
- +void qman_fq_state(struct qman_fq *fq, enum qman_fq_state *state, u32 *flags);
- +
- +/**
- + * qman_init_fq - Initialises FQ fields, leaves the FQ "parked" or "scheduled"
- + * @fq: the frame queue object to modify, must be 'parked' or new.
- + * @flags: bit-mask of QMAN_INITFQ_FLAG_*** options
- + * @opts: the FQ-modification settings, as defined in the low-level API
- + *
- + * The @opts parameter comes from the low-level portal API. Select
- + * QMAN_INITFQ_FLAG_SCHED in @flags to cause the frame queue to be scheduled
- + * rather than parked. NB, @opts can be NULL.
- + *
- + * Note that some fields and options within @opts may be ignored or overwritten
- + * by the driver;
- + * 1. the 'count' and 'fqid' fields are always ignored (this operation only
- + * affects one frame queue: @fq).
- + * 2. the QM_INITFQ_WE_CONTEXTB option of the 'we_mask' field and the associated
- + * 'fqd' structure's 'context_b' field are sometimes overwritten;
- + * - if @fq was not created with QMAN_FQ_FLAG_TO_DCPORTAL, then context_b is
- + * initialised to a value used by the driver for demux.
- + * - if context_b is initialised for demux, so is context_a in case stashing
- + * is requested (see item 4).
- + * (So caller control of context_b is only possible for TO_DCPORTAL frame queue
- + * objects.)
- + * 3. if @flags contains QMAN_INITFQ_FLAG_LOCAL, the 'fqd' structure's
- + * 'dest::channel' field will be overwritten to match the portal used to issue
- + * the command. If the WE_DESTWQ write-enable bit had already been set by the
- + * caller, the channel workqueue will be left as-is, otherwise the write-enable
- + * bit is set and the workqueue is set to a default of 4. If the "LOCAL" flag
- + * isn't set, the destination channel/workqueue fields and the write-enable bit
- + * are left as-is.
- + * 4. if the driver overwrites context_a/b for demux, then if
- + * QM_INITFQ_WE_CONTEXTA is set, the driver will only overwrite
- + * context_a.address fields and will leave the stashing fields provided by the
- + * user alone, otherwise it will zero out the context_a.stashing fields.
- + */
- +int qman_init_fq(struct qman_fq *fq, u32 flags, struct qm_mcc_initfq *opts);
- +
- +/**
- + * qman_schedule_fq - Schedules a FQ
- + * @fq: the frame queue object to schedule, must be 'parked'
- + *
- + * Schedules the frame queue, which must be Parked, which takes it to
- + * Tentatively-Scheduled or Truly-Scheduled depending on its fill-level.
- + */
- +int qman_schedule_fq(struct qman_fq *fq);
- +
- +/**
- + * qman_retire_fq - Retires a FQ
- + * @fq: the frame queue object to retire
- + * @flags: FQ flags (as per qman_fq_state) if retirement completes immediately
- + *
- + * Retires the frame queue. This returns zero if it succeeds immediately, +1 if
- + * the retirement was started asynchronously, otherwise it returns negative for
- + * failure. When this function returns zero, @flags is set to indicate whether
- + * the retired FQ is empty and/or whether it has any ORL fragments (to show up
- + * as ERNs). Otherwise the corresponding flags will be known when a subsequent
- + * FQRN message shows up on the portal's message ring.
- + *
- + * NB, if the retirement is asynchronous (the FQ was in the Truly Scheduled or
- + * Active state), the completion will be via the message ring as a FQRN - but
- + * the corresponding callback may occur before this function returns!! Ie. the
- + * caller should be prepared to accept the callback as the function is called,
- + * not only once it has returned.
- + */
- +int qman_retire_fq(struct qman_fq *fq, u32 *flags);
- +
- +/**
- + * qman_oos_fq - Puts a FQ "out of service"
- + * @fq: the frame queue object to be put out-of-service, must be 'retired'
- + *
- + * The frame queue must be retired and empty, and if any order restoration list
- + * was released as ERNs at the time of retirement, they must all be consumed.
- + */
- +int qman_oos_fq(struct qman_fq *fq);
- +
- +/**
- + * qman_fq_flow_control - Set the XON/XOFF state of a FQ
- + * @fq: the frame queue object to be set to XON/XOFF state, must not be 'oos',
- + * or 'retired' or 'parked' state
- + * @xon: boolean to set fq in XON or XOFF state
- + *
- + * The frame should be in Tentatively Scheduled state or Truly Schedule sate,
- + * otherwise the IFSI interrupt will be asserted.
- + */
- +int qman_fq_flow_control(struct qman_fq *fq, int xon);
- +
- +/**
- + * qman_query_fq - Queries FQD fields (via h/w query command)
- + * @fq: the frame queue object to be queried
- + * @fqd: storage for the queried FQD fields
- + */
- +int qman_query_fq(struct qman_fq *fq, struct qm_fqd *fqd);
- +
- +/**
- + * qman_query_fq_np - Queries non-programmable FQD fields
- + * @fq: the frame queue object to be queried
- + * @np: storage for the queried FQD fields
- + */
- +int qman_query_fq_np(struct qman_fq *fq, struct qm_mcr_queryfq_np *np);
- +
- +/**
- + * qman_query_wq - Queries work queue lengths
- + * @query_dedicated: If non-zero, query length of WQs in the channel dedicated
- + * to this software portal. Otherwise, query length of WQs in a
- + * channel specified in wq.
- + * @wq: storage for the queried WQs lengths. Also specified the channel to
- + * to query if query_dedicated is zero.
- + */
- +int qman_query_wq(u8 query_dedicated, struct qm_mcr_querywq *wq);
- +
- +/**
- + * qman_volatile_dequeue - Issue a volatile dequeue command
- + * @fq: the frame queue object to dequeue from
- + * @flags: a bit-mask of QMAN_VOLATILE_FLAG_*** options
- + * @vdqcr: bit mask of QM_VDQCR_*** options, as per qm_dqrr_vdqcr_set()
- + *
- + * Attempts to lock access to the portal's VDQCR volatile dequeue functionality.
- + * The function will block and sleep if QMAN_VOLATILE_FLAG_WAIT is specified and
- + * the VDQCR is already in use, otherwise returns non-zero for failure. If
- + * QMAN_VOLATILE_FLAG_FINISH is specified, the function will only return once
- + * the VDQCR command has finished executing (ie. once the callback for the last
- + * DQRR entry resulting from the VDQCR command has been called). If not using
- + * the FINISH flag, completion can be determined either by detecting the
- + * presence of the QM_DQRR_STAT_UNSCHEDULED and QM_DQRR_STAT_DQCR_EXPIRED bits
- + * in the "stat" field of the "struct qm_dqrr_entry" passed to the FQ's dequeue
- + * callback, or by waiting for the QMAN_FQ_STATE_VDQCR bit to disappear from the
- + * "flags" retrieved from qman_fq_state().
- + */
- +int qman_volatile_dequeue(struct qman_fq *fq, u32 flags, u32 vdqcr);
- +
- +/**
- + * qman_enqueue - Enqueue a frame to a frame queue
- + * @fq: the frame queue object to enqueue to
- + * @fd: a descriptor of the frame to be enqueued
- + * @flags: bit-mask of QMAN_ENQUEUE_FLAG_*** options
- + *
- + * Fills an entry in the EQCR of portal @qm to enqueue the frame described by
- + * @fd. The descriptor details are copied from @fd to the EQCR entry, the 'pid'
- + * field is ignored. The return value is non-zero on error, such as ring full
- + * (and FLAG_WAIT not specified), congestion avoidance (FLAG_WATCH_CGR
- + * specified), etc. If the ring is full and FLAG_WAIT is specified, this
- + * function will block. If FLAG_INTERRUPT is set, the EQCI bit of the portal
- + * interrupt will assert when Qman consumes the EQCR entry (subject to "status
- + * disable", "enable", and "inhibit" registers). If FLAG_DCA is set, Qman will
- + * perform an implied "discrete consumption acknowledgement" on the dequeue
- + * ring's (DQRR) entry, at the ring index specified by the FLAG_DCA_IDX(x)
- + * macro. (As an alternative to issuing explicit DCA actions on DQRR entries,
- + * this implicit DCA can delay the release of a "held active" frame queue
- + * corresponding to a DQRR entry until Qman consumes the EQCR entry - providing
- + * order-preservation semantics in packet-forwarding scenarios.) If FLAG_DCA is
- + * set, then FLAG_DCA_PARK can also be set to imply that the DQRR consumption
- + * acknowledgement should "park request" the "held active" frame queue. Ie.
- + * when the portal eventually releases that frame queue, it will be left in the
- + * Parked state rather than Tentatively Scheduled or Truly Scheduled. If the
- + * portal is watching congestion groups, the QMAN_ENQUEUE_FLAG_WATCH_CGR flag
- + * is requested, and the FQ is a member of a congestion group, then this
- + * function returns -EAGAIN if the congestion group is currently congested.
- + * Note, this does not eliminate ERNs, as the async interface means we can be
- + * sending enqueue commands to an un-congested FQ that becomes congested before
- + * the enqueue commands are processed, but it does minimise needless thrashing
- + * of an already busy hardware resource by throttling many of the to-be-dropped
- + * enqueues "at the source".
- + */
- +int qman_enqueue(struct qman_fq *fq, const struct qm_fd *fd, u32 flags);
- +
- +typedef int (*qman_cb_precommit) (void *arg);
- +/**
- + * qman_enqueue_precommit - Enqueue a frame to a frame queue and call cb
- + * @fq: the frame queue object to enqueue to
- + * @fd: a descriptor of the frame to be enqueued
- + * @flags: bit-mask of QMAN_ENQUEUE_FLAG_*** options
- + * @cb: user supplied callback function to invoke before writing commit verb.
- + * @cb_arg: callback function argument
- + *
- + * This is similar to qman_enqueue except that it will invoke a user supplied
- + * callback function just before writng the commit verb. This is useful
- + * when the user want to do something *just before* enqueuing the request and
- + * the enqueue can't fail.
- + */
- +int qman_enqueue_precommit(struct qman_fq *fq, const struct qm_fd *fd,
- + u32 flags, qman_cb_precommit cb, void *cb_arg);
- +
- +/**
- + * qman_enqueue_orp - Enqueue a frame to a frame queue using an ORP
- + * @fq: the frame queue object to enqueue to
- + * @fd: a descriptor of the frame to be enqueued
- + * @flags: bit-mask of QMAN_ENQUEUE_FLAG_*** options
- + * @orp: the frame queue object used as an order restoration point.
- + * @orp_seqnum: the sequence number of this frame in the order restoration path
- + *
- + * Similar to qman_enqueue(), but with the addition of an Order Restoration
- + * Point (@orp) and corresponding sequence number (@orp_seqnum) for this
- + * enqueue operation to employ order restoration. Each frame queue object acts
- + * as an Order Definition Point (ODP) by providing each frame dequeued from it
- + * with an incrementing sequence number, this value is generally ignored unless
- + * that sequence of dequeued frames will need order restoration later. Each
- + * frame queue object also encapsulates an Order Restoration Point (ORP), which
- + * is a re-assembly context for re-ordering frames relative to their sequence
- + * numbers as they are enqueued. The ORP does not have to be within the frame
- + * queue that receives the enqueued frame, in fact it is usually the frame
- + * queue from which the frames were originally dequeued. For the purposes of
- + * order restoration, multiple frames (or "fragments") can be enqueued for a
- + * single sequence number by setting the QMAN_ENQUEUE_FLAG_NLIS flag for all
- + * enqueues except the final fragment of a given sequence number. Ordering
- + * between sequence numbers is guaranteed, even if fragments of different
- + * sequence numbers are interlaced with one another. Fragments of the same
- + * sequence number will retain the order in which they are enqueued. If no
- + * enqueue is to performed, QMAN_ENQUEUE_FLAG_HOLE indicates that the given
- + * sequence number is to be "skipped" by the ORP logic (eg. if a frame has been
- + * dropped from a sequence), or QMAN_ENQUEUE_FLAG_NESN indicates that the given
- + * sequence number should become the ORP's "Next Expected Sequence Number".
- + *
- + * Side note: a frame queue object can be used purely as an ORP, without
- + * carrying any frames at all. Care should be taken not to deallocate a frame
- + * queue object that is being actively used as an ORP, as a future allocation
- + * of the frame queue object may start using the internal ORP before the
- + * previous use has finished.
- + */
- +int qman_enqueue_orp(struct qman_fq *fq, const struct qm_fd *fd, u32 flags,
- + struct qman_fq *orp, u16 orp_seqnum);
- +
- +/**
- + * qman_alloc_fqid_range - Allocate a contiguous range of FQIDs
- + * @result: is set by the API to the base FQID of the allocated range
- + * @count: the number of FQIDs required
- + * @align: required alignment of the allocated range
- + * @partial: non-zero if the API can return fewer than @count FQIDs
- + *
- + * Returns the number of frame queues allocated, or a negative error code. If
- + * @partial is non zero, the allocation request may return a smaller range of
- + * FQs than requested (though alignment will be as requested). If @partial is
- + * zero, the return value will either be 'count' or negative.
- + */
- +int qman_alloc_fqid_range(u32 *result, u32 count, u32 align, int partial);
- +static inline int qman_alloc_fqid(u32 *result)
- +{
- + int ret = qman_alloc_fqid_range(result, 1, 0, 0);
- + return (ret > 0) ? 0 : ret;
- +}
- +
- +/**
- + * qman_release_fqid_range - Release the specified range of frame queue IDs
- + * @fqid: the base FQID of the range to deallocate
- + * @count: the number of FQIDs in the range
- + *
- + * This function can also be used to seed the allocator with ranges of FQIDs
- + * that it can subsequently allocate from.
- + */
- +void qman_release_fqid_range(u32 fqid, unsigned int count);
- +static inline void qman_release_fqid(u32 fqid)
- +{
- + qman_release_fqid_range(fqid, 1);
- +}
- +
- +void qman_seed_fqid_range(u32 fqid, unsigned int count);
- +
- +
- +int qman_shutdown_fq(u32 fqid);
- +
- +/**
- + * qman_reserve_fqid_range - Reserve the specified range of frame queue IDs
- + * @fqid: the base FQID of the range to deallocate
- + * @count: the number of FQIDs in the range
- + */
- +int qman_reserve_fqid_range(u32 fqid, unsigned int count);
- +static inline int qman_reserve_fqid(u32 fqid)
- +{
- + return qman_reserve_fqid_range(fqid, 1);
- +}
- +
- + /* Pool-channel management */
- + /* ----------------------- */
- +/**
- + * qman_alloc_pool_range - Allocate a contiguous range of pool-channel IDs
- + * @result: is set by the API to the base pool-channel ID of the allocated range
- + * @count: the number of pool-channel IDs required
- + * @align: required alignment of the allocated range
- + * @partial: non-zero if the API can return fewer than @count
- + *
- + * Returns the number of pool-channel IDs allocated, or a negative error code.
- + * If @partial is non zero, the allocation request may return a smaller range of
- + * than requested (though alignment will be as requested). If @partial is zero,
- + * the return value will either be 'count' or negative.
- + */
- +int qman_alloc_pool_range(u32 *result, u32 count, u32 align, int partial);
- +static inline int qman_alloc_pool(u32 *result)
- +{
- + int ret = qman_alloc_pool_range(result, 1, 0, 0);
- + return (ret > 0) ? 0 : ret;
- +}
- +
- +/**
- + * qman_release_pool_range - Release the specified range of pool-channel IDs
- + * @id: the base pool-channel ID of the range to deallocate
- + * @count: the number of pool-channel IDs in the range
- + */
- +void qman_release_pool_range(u32 id, unsigned int count);
- +static inline void qman_release_pool(u32 id)
- +{
- + qman_release_pool_range(id, 1);
- +}
- +
- +/**
- + * qman_reserve_pool_range - Reserve the specified range of pool-channel IDs
- + * @id: the base pool-channel ID of the range to reserve
- + * @count: the number of pool-channel IDs in the range
- + */
- +int qman_reserve_pool_range(u32 id, unsigned int count);
- +static inline int qman_reserve_pool(u32 id)
- +{
- + return qman_reserve_pool_range(id, 1);
- +}
- +
- +void qman_seed_pool_range(u32 id, unsigned int count);
- +
- + /* CGR management */
- + /* -------------- */
- +/**
- + * qman_create_cgr - Register a congestion group object
- + * @cgr: the 'cgr' object, with fields filled in
- + * @flags: QMAN_CGR_FLAG_* values
- + * @opts: optional state of CGR settings
- + *
- + * Registers this object to receiving congestion entry/exit callbacks on the
- + * portal affine to the cpu portal on which this API is executed. If opts is
- + * NULL then only the callback (cgr->cb) function is registered. If @flags
- + * contains QMAN_CGR_FLAG_USE_INIT, then an init hw command (which will reset
- + * any unspecified parameters) will be used rather than a modify hw hardware
- + * (which only modifies the specified parameters).
- + */
- +int qman_create_cgr(struct qman_cgr *cgr, u32 flags,
- + struct qm_mcc_initcgr *opts);
- +
- +/**
- + * qman_create_cgr_to_dcp - Register a congestion group object to DCP portal
- + * @cgr: the 'cgr' object, with fields filled in
- + * @flags: QMAN_CGR_FLAG_* values
- + * @dcp_portal: the DCP portal to which the cgr object is registered.
- + * @opts: optional state of CGR settings
- + *
- + */
- +int qman_create_cgr_to_dcp(struct qman_cgr *cgr, u32 flags, u16 dcp_portal,
- + struct qm_mcc_initcgr *opts);
- +
- +/**
- + * qman_delete_cgr - Deregisters a congestion group object
- + * @cgr: the 'cgr' object to deregister
- + *
- + * "Unplugs" this CGR object from the portal affine to the cpu on which this API
- + * is executed. This must be excuted on the same affine portal on which it was
- + * created.
- + */
- +int qman_delete_cgr(struct qman_cgr *cgr);
- +
- +/**
- + * qman_delete_cgr_safe - Deregisters a congestion group object from any CPU
- + * @cgr: the 'cgr' object to deregister
- + *
- + * This will select the proper CPU and run there qman_delete_cgr().
- + */
- +void qman_delete_cgr_safe(struct qman_cgr *cgr);
- +
- +/**
- + * qman_modify_cgr - Modify CGR fields
- + * @cgr: the 'cgr' object to modify
- + * @flags: QMAN_CGR_FLAG_* values
- + * @opts: the CGR-modification settings
- + *
- + * The @opts parameter comes from the low-level portal API, and can be NULL.
- + * Note that some fields and options within @opts may be ignored or overwritten
- + * by the driver, in particular the 'cgrid' field is ignored (this operation
- + * only affects the given CGR object). If @flags contains
- + * QMAN_CGR_FLAG_USE_INIT, then an init hw command (which will reset any
- + * unspecified parameters) will be used rather than a modify hw hardware (which
- + * only modifies the specified parameters).
- + */
- +int qman_modify_cgr(struct qman_cgr *cgr, u32 flags,
- + struct qm_mcc_initcgr *opts);
- +
- +/**
- +* qman_query_cgr - Queries CGR fields
- +* @cgr: the 'cgr' object to query
- +* @result: storage for the queried congestion group record
- +*/
- +int qman_query_cgr(struct qman_cgr *cgr, struct qm_mcr_querycgr *result);
- +
- +/**
- + * qman_query_congestion - Queries the state of all congestion groups
- + * @congestion: storage for the queried state of all congestion groups
- + */
- +int qman_query_congestion(struct qm_mcr_querycongestion *congestion);
- +
- +/**
- + * qman_alloc_cgrid_range - Allocate a contiguous range of CGR IDs
- + * @result: is set by the API to the base CGR ID of the allocated range
- + * @count: the number of CGR IDs required
- + * @align: required alignment of the allocated range
- + * @partial: non-zero if the API can return fewer than @count
- + *
- + * Returns the number of CGR IDs allocated, or a negative error code.
- + * If @partial is non zero, the allocation request may return a smaller range of
- + * than requested (though alignment will be as requested). If @partial is zero,
- + * the return value will either be 'count' or negative.
- + */
- +int qman_alloc_cgrid_range(u32 *result, u32 count, u32 align, int partial);
- +static inline int qman_alloc_cgrid(u32 *result)
- +{
- + int ret = qman_alloc_cgrid_range(result, 1, 0, 0);
- + return (ret > 0) ? 0 : ret;
- +}
- +
- +/**
- + * qman_release_cgrid_range - Release the specified range of CGR IDs
- + * @id: the base CGR ID of the range to deallocate
- + * @count: the number of CGR IDs in the range
- + */
- +void qman_release_cgrid_range(u32 id, unsigned int count);
- +static inline void qman_release_cgrid(u32 id)
- +{
- + qman_release_cgrid_range(id, 1);
- +}
- +
- +/**
- + * qman_reserve_cgrid_range - Reserve the specified range of CGR ID
- + * @id: the base CGR ID of the range to reserve
- + * @count: the number of CGR IDs in the range
- + */
- +int qman_reserve_cgrid_range(u32 id, unsigned int count);
- +static inline int qman_reserve_cgrid(u32 id)
- +{
- + return qman_reserve_cgrid_range(id, 1);
- +}
- +
- +void qman_seed_cgrid_range(u32 id, unsigned int count);
- +
- +
- + /* Helpers */
- + /* ------- */
- +/**
- + * qman_poll_fq_for_init - Check if an FQ has been initialised from OOS
- + * @fqid: the FQID that will be initialised by other s/w
- + *
- + * In many situations, a FQID is provided for communication between s/w
- + * entities, and whilst the consumer is responsible for initialising and
- + * scheduling the FQ, the producer(s) generally create a wrapper FQ object using
- + * and only call qman_enqueue() (no FQ initialisation, scheduling, etc). Ie;
- + * qman_create_fq(..., QMAN_FQ_FLAG_NO_MODIFY, ...);
- + * However, data can not be enqueued to the FQ until it is initialised out of
- + * the OOS state - this function polls for that condition. It is particularly
- + * useful for users of IPC functions - each endpoint's Rx FQ is the other
- + * endpoint's Tx FQ, so each side can initialise and schedule their Rx FQ object
- + * and then use this API on the (NO_MODIFY) Tx FQ object in order to
- + * synchronise. The function returns zero for success, +1 if the FQ is still in
- + * the OOS state, or negative if there was an error.
- + */
- +static inline int qman_poll_fq_for_init(struct qman_fq *fq)
- +{
- + struct qm_mcr_queryfq_np np;
- + int err;
- + err = qman_query_fq_np(fq, &np);
- + if (err)
- + return err;
- + if ((np.state & QM_MCR_NP_STATE_MASK) == QM_MCR_NP_STATE_OOS)
- + return 1;
- + return 0;
- +}
- +
- + /* -------------- */
- + /* CEETM :: types */
- + /* -------------- */
- +/**
- + * Token Rate Structure
- + * Shaping rates are based on a "credit" system and a pre-configured h/w
- + * internal timer. The following type represents a shaper "rate" parameter as a
- + * fractional number of "tokens". Here's how it works. This (fractional) number
- + * of tokens is added to the shaper's "credit" every time the h/w timer elapses
- + * (up to a limit which is set by another shaper parameter). Every time a frame
- + * is enqueued through a shaper, the shaper deducts as many tokens as there are
- + * bytes of data in the enqueued frame. A shaper will not allow itself to
- + * enqueue any frames if its token count is negative. As such;
- + *
- + * The rate at which data is enqueued is limited by the
- + * rate at which tokens are added.
- + *
- + * Therefore if the user knows the period between these h/w timer updates in
- + * seconds, they can calculate the maximum traffic rate of the shaper (in
- + * bytes-per-second) from the token rate. And vice versa, they can calculate
- + * the token rate to use in order to achieve a given traffic rate.
- + */
- +struct qm_ceetm_rate {
- + /* The token rate is; whole + (fraction/8192) */
- + u32 whole:11; /* 0..2047 */
- + u32 fraction:13; /* 0..8191 */
- +};
- +
- +struct qm_ceetm_weight_code {
- + /* The weight code is; 5 msbits + 3 lsbits */
- + u8 y:5;
- + u8 x:3;
- +};
- +
- +struct qm_ceetm {
- + unsigned int idx;
- + struct list_head sub_portals;
- + struct list_head lnis;
- + unsigned int sp_range[2];
- + unsigned int lni_range[2];
- +};
- +
- +struct qm_ceetm_sp {
- + struct list_head node;
- + unsigned int idx;
- + unsigned int dcp_idx;
- + int is_claimed;
- + struct qm_ceetm_lni *lni;
- +};
- +
- +/* Logical Network Interface */
- +struct qm_ceetm_lni {
- + struct list_head node;
- + unsigned int idx;
- + unsigned int dcp_idx;
- + int is_claimed;
- + struct qm_ceetm_sp *sp;
- + struct list_head channels;
- + int shaper_enable;
- + int shaper_couple;
- + int oal;
- + struct qm_ceetm_rate cr_token_rate;
- + struct qm_ceetm_rate er_token_rate;
- + u16 cr_token_bucket_limit;
- + u16 er_token_bucket_limit;
- +};
- +
- +/* Class Queue Channel */
- +struct qm_ceetm_channel {
- + struct list_head node;
- + unsigned int idx;
- + unsigned int lni_idx;
- + unsigned int dcp_idx;
- + struct list_head class_queues;
- + struct list_head ccgs;
- + u8 shaper_enable;
- + u8 shaper_couple;
- + struct qm_ceetm_rate cr_token_rate;
- + struct qm_ceetm_rate er_token_rate;
- + u16 cr_token_bucket_limit;
- + u16 er_token_bucket_limit;
- +};
- +
- +struct qm_ceetm_ccg;
- +
- +/* This callback type is used when handling congestion entry/exit. The
- + * 'cb_ctx' value is the opaque value associated with ccg object.
- + * 'congested' is non-zero on congestion-entry, and zero on congestion-exit.
- + */
- +typedef void (*qman_cb_ccgr)(struct qm_ceetm_ccg *ccg, void *cb_ctx,
- + int congested);
- +
- +/* Class Congestion Group */
- +struct qm_ceetm_ccg {
- + struct qm_ceetm_channel *parent;
- + struct list_head node;
- + struct list_head cb_node;
- + qman_cb_ccgr cb;
- + void *cb_ctx;
- + unsigned int idx;
- +};
- +
- +/* Class Queue */
- +struct qm_ceetm_cq {
- + struct qm_ceetm_channel *parent;
- + struct qm_ceetm_ccg *ccg;
- + struct list_head node;
- + unsigned int idx;
- + int is_claimed;
- + struct list_head bound_lfqids;
- + struct list_head binding_node;
- +};
- +
- +/* Logical Frame Queue */
- +struct qm_ceetm_lfq {
- + struct qm_ceetm_channel *parent;
- + struct list_head node;
- + unsigned int idx;
- + unsigned int dctidx;
- + u64 context_a;
- + u32 context_b;
- + qman_cb_mr ern;
- +};
- +
- +/**
- + * qman_ceetm_bps2tokenrate - Given a desired rate 'bps' measured in bps
- + * (ie. bits-per-second), compute the 'token_rate' fraction that best
- + * approximates that rate.
- + * @bps: the desired shaper rate in bps.
- + * @token_rate: the output token rate computed with the given kbps.
- + * @rounding: dictates how to round if an exact conversion is not possible; if
- + * it is negative then 'token_rate' will round down to the highest value that
- + * does not exceed the desired rate, if it is positive then 'token_rate' will
- + * round up to the lowest value that is greater than or equal to the desired
- + * rate, and if it is zero then it will round to the nearest approximation,
- + * whether that be up or down.
- + *
- + * Return 0 for success, or -EINVAL if prescaler or qman clock is not available.
- + */
- +int qman_ceetm_bps2tokenrate(u64 bps,
- + struct qm_ceetm_rate *token_rate,
- + int rounding);
- +
- +/**
- + * qman_ceetm_tokenrate2bps - Given a 'token_rate', compute the
- + * corresponding number of 'bps'.
- + * @token_rate: the input desired token_rate fraction.
- + * @bps: the output shaper rate in bps computed with the give token rate.
- + * @rounding: has the same semantics as the previous function.
- + *
- + * Return 0 for success, or -EINVAL if prescaler or qman clock is not available.
- + */
- +int qman_ceetm_tokenrate2bps(const struct qm_ceetm_rate *token_rate,
- + u64 *bps,
- + int rounding);
- +
- +int qman_alloc_ceetm0_channel_range(u32 *result, u32 count, u32 align,
- + int partial);
- +static inline int qman_alloc_ceetm0_channel(u32 *result)
- +{
- + int ret = qman_alloc_ceetm0_channel_range(result, 1, 0, 0);
- + return (ret > 0) ? 0 : ret;
- +}
- +void qman_release_ceetm0_channel_range(u32 channelid, u32 count);
- +static inline void qman_release_ceetm0_channelid(u32 channelid)
- +{
- + qman_release_ceetm0_channel_range(channelid, 1);
- +}
- +
- +int qman_reserve_ceetm0_channel_range(u32 channelid, u32 count);
- +static inline int qman_reserve_ceetm0_channelid(u32 channelid)
- +{
- + return qman_reserve_ceetm0_channel_range(channelid, 1);
- +}
- +
- +void qman_seed_ceetm0_channel_range(u32 channelid, u32 count);
- +
- +
- +int qman_alloc_ceetm1_channel_range(u32 *result, u32 count, u32 align,
- + int partial);
- +static inline int qman_alloc_ceetm1_channel(u32 *result)
- +{
- + int ret = qman_alloc_ceetm1_channel_range(result, 1, 0, 0);
- + return (ret > 0) ? 0 : ret;
- +}
- +void qman_release_ceetm1_channel_range(u32 channelid, u32 count);
- +static inline void qman_release_ceetm1_channelid(u32 channelid)
- +{
- + qman_release_ceetm1_channel_range(channelid, 1);
- +}
- +int qman_reserve_ceetm1_channel_range(u32 channelid, u32 count);
- +static inline int qman_reserve_ceetm1_channelid(u32 channelid)
- +{
- + return qman_reserve_ceetm1_channel_range(channelid, 1);
- +}
- +
- +void qman_seed_ceetm1_channel_range(u32 channelid, u32 count);
- +
- +
- +int qman_alloc_ceetm0_lfqid_range(u32 *result, u32 count, u32 align,
- + int partial);
- +static inline int qman_alloc_ceetm0_lfqid(u32 *result)
- +{
- + int ret = qman_alloc_ceetm0_lfqid_range(result, 1, 0, 0);
- + return (ret > 0) ? 0 : ret;
- +}
- +void qman_release_ceetm0_lfqid_range(u32 lfqid, u32 count);
- +static inline void qman_release_ceetm0_lfqid(u32 lfqid)
- +{
- + qman_release_ceetm0_lfqid_range(lfqid, 1);
- +}
- +int qman_reserve_ceetm0_lfqid_range(u32 lfqid, u32 count);
- +static inline int qman_reserve_ceetm0_lfqid(u32 lfqid)
- +{
- + return qman_reserve_ceetm0_lfqid_range(lfqid, 1);
- +}
- +
- +void qman_seed_ceetm0_lfqid_range(u32 lfqid, u32 count);
- +
- +
- +int qman_alloc_ceetm1_lfqid_range(u32 *result, u32 count, u32 align,
- + int partial);
- +static inline int qman_alloc_ceetm1_lfqid(u32 *result)
- +{
- + int ret = qman_alloc_ceetm1_lfqid_range(result, 1, 0, 0);
- + return (ret > 0) ? 0 : ret;
- +}
- +void qman_release_ceetm1_lfqid_range(u32 lfqid, u32 count);
- +static inline void qman_release_ceetm1_lfqid(u32 lfqid)
- +{
- + qman_release_ceetm1_lfqid_range(lfqid, 1);
- +}
- +int qman_reserve_ceetm1_lfqid_range(u32 lfqid, u32 count);
- +static inline int qman_reserve_ceetm1_lfqid(u32 lfqid)
- +{
- + return qman_reserve_ceetm1_lfqid_range(lfqid, 1);
- +}
- +
- +void qman_seed_ceetm1_lfqid_range(u32 lfqid, u32 count);
- +
- +
- + /* ----------------------------- */
- + /* CEETM :: sub-portals */
- + /* ----------------------------- */
- +
- +/**
- + * qman_ceetm_claim_sp - Claims the given sub-portal, provided it is available
- + * to us and configured for traffic-management.
- + * @sp: the returned sub-portal object, if successful.
- + * @dcp_id: specifies the desired Fman block (and thus the relevant CEETM
- + * instance),
- + * @sp_idx" is the desired sub-portal index from 0 to 15.
- + *
- + * Returns zero for success, or -ENODEV if the sub-portal is in use, or -EINVAL
- + * if the sp_idx is out of range.
- + *
- + * Note that if there are multiple driver domains (eg. a linux kernel versus
- + * user-space drivers in USDPAA, or multiple guests running under a hypervisor)
- + * then a sub-portal may be accessible by more than one instance of a qman
- + * driver and so it may be claimed multiple times. If this is the case, it is
- + * up to the system architect to prevent conflicting configuration actions
- + * coming from the different driver domains. The qman drivers do not have any
- + * behind-the-scenes coordination to prevent this from happening.
- + */
- +int qman_ceetm_sp_claim(struct qm_ceetm_sp **sp,
- + enum qm_dc_portal dcp_idx,
- + unsigned int sp_idx);
- +
- +/**
- + * qman_ceetm_sp_release - Releases a previously claimed sub-portal.
- + * @sp: the sub-portal to be released.
- + *
- + * Returns 0 for success, or -EBUSY for failure if the dependencies are not
- + * released.
- + */
- +int qman_ceetm_sp_release(struct qm_ceetm_sp *sp);
- +
- + /* ----------------------------------- */
- + /* CEETM :: logical network interfaces */
- + /* ----------------------------------- */
- +
- +/**
- + * qman_ceetm_lni_claim - Claims an unclaimed LNI.
- + * @lni: the returned LNI object, if successful.
- + * @dcp_id: specifies the desired Fman block (and thus the relevant CEETM
- + * instance)
- + * @lni_idx: is the desired LNI index.
- + *
- + * Returns zero for success, or -EINVAL on failure, which will happen if the LNI
- + * is not available or has already been claimed (and not yet successfully
- + * released), or lni_dix is out of range.
- + *
- + * Note that there may be multiple driver domains (or instances) that need to
- + * transmit out the same LNI, so this claim is only guaranteeing exclusivity
- + * within the domain of the driver being called. See qman_ceetm_sp_claim() and
- + * qman_ceetm_sp_get_lni() for more information.
- + */
- +int qman_ceetm_lni_claim(struct qm_ceetm_lni **lni,
- + enum qm_dc_portal dcp_id,
- + unsigned int lni_idx);
- +
- +/**
- + * qman_ceetm_lni_releaes - Releases a previously claimed LNI.
- + * @lni: the lni needs to be released.
- + *
- + * This will only succeed if all dependent objects have been released.
- + * Returns zero for success, or -EBUSY if the dependencies are not released.
- + */
- +int qman_ceetm_lni_release(struct qm_ceetm_lni *lni);
- +
- +/**
- + * qman_ceetm_sp_set_lni
- + * qman_ceetm_sp_get_lni - Set/get the LNI that the sub-portal is currently
- + * mapped to.
- + * @sp: the given sub-portal.
- + * @lni(in "set"function): the LNI object which the sp will be mappaed to.
- + * @lni_idx(in "get" function): the LNI index which the sp is mapped to.
- + *
- + * Returns zero for success, or -EINVAL for the "set" function when this sp-lni
- + * mapping has been set, or configure mapping command returns error, and
- + * -EINVAL for "get" function when this sp-lni mapping is not set or the query
- + * mapping command returns error.
- + *
- + * This may be useful in situations where multiple driver domains have access
- + * to the same sub-portals in order to all be able to transmit out the same
- + * physical interface (perhaps they're on different IP addresses or VPNs, so
- + * Fman is splitting Rx traffic and here we need to converge Tx traffic). In
- + * that case, a control-plane is likely to use qman_ceetm_lni_claim() followed
- + * by qman_ceetm_sp_set_lni() to configure the sub-portal, and other domains
- + * are likely to use qman_ceetm_sp_get_lni() followed by qman_ceetm_lni_claim()
- + * in order to determine the LNI that the control-plane had assigned. This is
- + * why the "get" returns an index, whereas the "set" takes an (already claimed)
- + * LNI object.
- + */
- +int qman_ceetm_sp_set_lni(struct qm_ceetm_sp *sp,
- + struct qm_ceetm_lni *lni);
- +int qman_ceetm_sp_get_lni(struct qm_ceetm_sp *sp,
- + unsigned int *lni_idx);
- +
- +/**
- + * qman_ceetm_lni_enable_shaper
- + * qman_ceetm_lni_disable_shaper - Enables/disables shaping on the LNI.
- + * @lni: the given LNI.
- + * @coupled: indicates whether CR and ER are coupled.
- + * @oal: the overhead accounting length which is added to the actual length of
- + * each frame when performing shaper calculations.
- + *
- + * When the number of (unused) committed-rate tokens reach the committed-rate
- + * token limit, 'coupled' indicates whether surplus tokens should be added to
- + * the excess-rate token count (up to the excess-rate token limit).
- + * When LNI is claimed, the shaper is disabled by default. The enable function
- + * will turn on this shaper for this lni.
- + * Whenever a claimed LNI is first enabled for shaping, its committed and
- + * excess token rates and limits are zero, so will need to be changed to do
- + * anything useful. The shaper can subsequently be enabled/disabled without
- + * resetting the shaping parameters, but the shaping parameters will be reset
- + * when the LNI is released.
- + *
- + * Returns zero for success, or errno for "enable" function in the cases as:
- + * a) -EINVAL if the shaper is already enabled,
- + * b) -EIO if the configure shaper command returns error.
- + * For "disable" function, returns:
- + * a) -EINVAL if the shaper is has already disabled.
- + * b) -EIO if calling configure shaper command returns error.
- + */
- +int qman_ceetm_lni_enable_shaper(struct qm_ceetm_lni *lni, int coupled,
- + int oal);
- +int qman_ceetm_lni_disable_shaper(struct qm_ceetm_lni *lni);
- +
- +/**
- + * qman_ceetm_lni_is_shaper_enabled - Check LNI shaper status
- + * @lni: the give LNI
- + */
- +int qman_ceetm_lni_is_shaper_enabled(struct qm_ceetm_lni *lni);
- +
- +/**
- + * qman_ceetm_lni_set_commit_rate
- + * qman_ceetm_lni_get_commit_rate
- + * qman_ceetm_lni_set_excess_rate
- + * qman_ceetm_lni_get_excess_rate - Set/get the shaper CR/ER token rate and
- + * token limit for the given LNI.
- + * @lni: the given LNI.
- + * @token_rate: the desired token rate for "set" fuction, or the token rate of
- + * the LNI queried by "get" function.
- + * @token_limit: the desired token bucket limit for "set" function, or the token
- + * limit of the given LNI queried by "get" function.
- + *
- + * Returns zero for success. The "set" function returns -EINVAL if the given
- + * LNI is unshapped or -EIO if the configure shaper command returns error.
- + * The "get" function returns -EINVAL if the token rate or the token limit is
- + * not set or the query command returns error.
- + */
- +int qman_ceetm_lni_set_commit_rate(struct qm_ceetm_lni *lni,
- + const struct qm_ceetm_rate *token_rate,
- + u16 token_limit);
- +int qman_ceetm_lni_get_commit_rate(struct qm_ceetm_lni *lni,
- + struct qm_ceetm_rate *token_rate,
- + u16 *token_limit);
- +int qman_ceetm_lni_set_excess_rate(struct qm_ceetm_lni *lni,
- + const struct qm_ceetm_rate *token_rate,
- + u16 token_limit);
- +int qman_ceetm_lni_get_excess_rate(struct qm_ceetm_lni *lni,
- + struct qm_ceetm_rate *token_rate,
- + u16 *token_limit);
- +/**
- + * qman_ceetm_lni_set_commit_rate_bps
- + * qman_ceetm_lni_get_commit_rate_bps
- + * qman_ceetm_lni_set_excess_rate_bps
- + * qman_ceetm_lni_get_excess_rate_bps - Set/get the shaper CR/ER rate
- + * and token limit for the given LNI.
- + * @lni: the given LNI.
- + * @bps: the desired shaping rate in bps for "set" fuction, or the shaping rate
- + * of the LNI queried by "get" function.
- + * @token_limit: the desired token bucket limit for "set" function, or the token
- + * limit of the given LNI queried by "get" function.
- + *
- + * Returns zero for success. The "set" function returns -EINVAL if the given
- + * LNI is unshapped or -EIO if the configure shaper command returns error.
- + * The "get" function returns -EINVAL if the token rate or the token limit is
- + * not set or the query command returns error.
- + */
- +int qman_ceetm_lni_set_commit_rate_bps(struct qm_ceetm_lni *lni,
- + u64 bps,
- + u16 token_limit);
- +int qman_ceetm_lni_get_commit_rate_bps(struct qm_ceetm_lni *lni,
- + u64 *bps, u16 *token_limit);
- +int qman_ceetm_lni_set_excess_rate_bps(struct qm_ceetm_lni *lni,
- + u64 bps,
- + u16 token_limit);
- +int qman_ceetm_lni_get_excess_rate_bps(struct qm_ceetm_lni *lni,
- + u64 *bps, u16 *token_limit);
- +
- +/**
- + * qman_ceetm_lni_set_tcfcc
- + * qman_ceetm_lni_get_tcfcc - Configure/query "Traffic Class Flow Control".
- + * @lni: the given LNI.
- + * @cq_level: is between 0 and 15, representing individual class queue levels
- + * (CQ0 to CQ7 for every channel) and grouped class queue levels (CQ8 to CQ15
- + * for every channel).
- + * @traffic_class: is between 0 and 7 when associating a given class queue level
- + * to a traffic class, or -1 when disabling traffic class flow control for this
- + * class queue level.
- + *
- + * Return zero for success, or -EINVAL if the cq_level or traffic_class is out
- + * of range as indicated above, or -EIO if the configure/query tcfcc command
- + * returns error.
- + *
- + * Refer to the section of QMan CEETM traffic class flow control in the
- + * Reference Manual.
- + */
- +int qman_ceetm_lni_set_tcfcc(struct qm_ceetm_lni *lni,
- + unsigned int cq_level,
- + int traffic_class);
- +int qman_ceetm_lni_get_tcfcc(struct qm_ceetm_lni *lni,
- + unsigned int cq_level,
- + int *traffic_class);
- +
- + /* ----------------------------- */
- + /* CEETM :: class queue channels */
- + /* ----------------------------- */
- +
- +/**
- + * qman_ceetm_channel_claim - Claims an unclaimed CQ channel that is mapped to
- + * the given LNI.
- + * @channel: the returned class queue channel object, if successful.
- + * @lni: the LNI that the channel belongs to.
- + *
- + * Channels are always initially "unshaped".
- + *
- + * Return zero for success, or -ENODEV if there is no channel available(all 32
- + * channels are claimed) or -EINVAL if the channel mapping command returns
- + * error.
- + */
- +int qman_ceetm_channel_claim(struct qm_ceetm_channel **channel,
- + struct qm_ceetm_lni *lni);
- +
- +/**
- + * qman_ceetm_channel_release - Releases a previously claimed CQ channel.
- + * @channel: the channel needs to be released.
- + *
- + * Returns zero for success, or -EBUSY if the dependencies are still in use.
- + *
- + * Note any shaping of the channel will be cleared to leave it in an unshaped
- + * state.
- + */
- +int qman_ceetm_channel_release(struct qm_ceetm_channel *channel);
- +
- +/**
- + * qman_ceetm_channel_enable_shaper
- + * qman_ceetm_channel_disable_shaper - Enables/disables shaping on the channel.
- + * @channel: the given channel.
- + * @coupled: indicates whether surplus CR tokens should be added to the
- + * excess-rate token count (up to the excess-rate token limit) when the number
- + * of (unused) committed-rate tokens reach the committed_rate token limit.
- + *
- + * Whenever a claimed channel is first enabled for shaping, its committed and
- + * excess token rates and limits are zero, so will need to be changed to do
- + * anything useful. The shaper can subsequently be enabled/disabled without
- + * resetting the shaping parameters, but the shaping parameters will be reset
- + * when the channel is released.
- + *
- + * Return 0 for success, or -EINVAL for failure, in the case that the channel
- + * shaper has been enabled/disabled or the management command returns error.
- + */
- +int qman_ceetm_channel_enable_shaper(struct qm_ceetm_channel *channel,
- + int coupled);
- +int qman_ceetm_channel_disable_shaper(struct qm_ceetm_channel *channel);
- +
- +/**
- + * qman_ceetm_channel_is_shaper_enabled - Check channel shaper status.
- + * @channel: the give channel.
- + */
- +int qman_ceetm_channel_is_shaper_enabled(struct qm_ceetm_channel *channel);
- +
- +/**
- + * qman_ceetm_channel_set_commit_rate
- + * qman_ceetm_channel_get_commit_rate
- + * qman_ceetm_channel_set_excess_rate
- + * qman_ceetm_channel_get_excess_rate - Set/get channel CR/ER shaper parameters.
- + * @channel: the given channel.
- + * @token_rate: the desired token rate for "set" function, or the queried token
- + * rate for "get" function.
- + * @token_limit: the desired token limit for "set" function, or the queried
- + * token limit for "get" function.
- + *
- + * Return zero for success. The "set" function returns -EINVAL if the channel
- + * is unshaped, or -EIO if the configure shapper command returns error. The
- + * "get" function returns -EINVAL if token rate of token limit is not set, or
- + * the query shaper command returns error.
- + */
- +int qman_ceetm_channel_set_commit_rate(struct qm_ceetm_channel *channel,
- + const struct qm_ceetm_rate *token_rate,
- + u16 token_limit);
- +int qman_ceetm_channel_get_commit_rate(struct qm_ceetm_channel *channel,
- + struct qm_ceetm_rate *token_rate,
- + u16 *token_limit);
- +int qman_ceetm_channel_set_excess_rate(struct qm_ceetm_channel *channel,
- + const struct qm_ceetm_rate *token_rate,
- + u16 token_limit);
- +int qman_ceetm_channel_get_excess_rate(struct qm_ceetm_channel *channel,
- + struct qm_ceetm_rate *token_rate,
- + u16 *token_limit);
- +/**
- + * qman_ceetm_channel_set_commit_rate_bps
- + * qman_ceetm_channel_get_commit_rate_bps
- + * qman_ceetm_channel_set_excess_rate_bps
- + * qman_ceetm_channel_get_excess_rate_bps - Set/get channel CR/ER shaper
- + * parameters.
- + * @channel: the given channel.
- + * @token_rate: the desired shaper rate in bps for "set" function, or the
- + * shaper rate in bps for "get" function.
- + * @token_limit: the desired token limit for "set" function, or the queried
- + * token limit for "get" function.
- + *
- + * Return zero for success. The "set" function returns -EINVAL if the channel
- + * is unshaped, or -EIO if the configure shapper command returns error. The
- + * "get" function returns -EINVAL if token rate of token limit is not set, or
- + * the query shaper command returns error.
- + */
- +int qman_ceetm_channel_set_commit_rate_bps(struct qm_ceetm_channel *channel,
- + u64 bps, u16 token_limit);
- +int qman_ceetm_channel_get_commit_rate_bps(struct qm_ceetm_channel *channel,
- + u64 *bps, u16 *token_limit);
- +int qman_ceetm_channel_set_excess_rate_bps(struct qm_ceetm_channel *channel,
- + u64 bps, u16 token_limit);
- +int qman_ceetm_channel_get_excess_rate_bps(struct qm_ceetm_channel *channel,
- + u64 *bps, u16 *token_limit);
- +
- +/**
- + * qman_ceetm_channel_set_weight
- + * qman_ceetm_channel_get_weight - Set/get the weight for unshaped channel
- + * @channel: the given channel.
- + * @token_limit: the desired token limit as the weight of the unshaped channel
- + * for "set" function, or the queried token limit for "get" function.
- + *
- + * The algorithm of unshaped fair queuing (uFQ) is used for unshaped channel.
- + * It allows the unshaped channels to be included in the CR time eligible list,
- + * and thus use the configured CR token limit value as their fair queuing
- + * weight.
- + *
- + * Return zero for success, or -EINVAL if the channel is a shaped channel or
- + * the management command returns error.
- + */
- +int qman_ceetm_channel_set_weight(struct qm_ceetm_channel *channel,
- + u16 token_limit);
- +int qman_ceetm_channel_get_weight(struct qm_ceetm_channel *channel,
- + u16 *token_limit);
- +
- +/**
- + * qman_ceetm_channel_set_group
- + * qman_ceetm_channel_get_group - Set/get the grouping of the class scheduler.
- + * @channel: the given channel.
- + * @group_b: indicates whether there is group B in this channel.
- + * @prio_a: the priority of group A.
- + * @prio_b: the priority of group B.
- + *
- + * There are 8 individual class queues (CQ0-CQ7), and 8 grouped class queues
- + * (CQ8-CQ15). If 'group_b' is zero, then all the grouped class queues are in
- + * group A, otherwise they are split into group A (CQ8-11) and group B
- + * (CQ12-C15). The individual class queues and the group(s) are in strict
- + * priority order relative to each other. Within the group(s), the scheduling
- + * is not strict priority order, but the result of scheduling within a group
- + * is in strict priority order relative to the other class queues in the
- + * channel. 'prio_a' and 'prio_b' control the priority order of the groups
- + * relative to the individual class queues, and take values from 0-7. Eg. if
- + * 'group_b' is non-zero, 'prio_a' is 2 and 'prio_b' is 6, then the strict
- + * priority order would be;
- + * CQ0, CQ1, CQ2, GROUPA, CQ3, CQ4, CQ5, CQ6, GROUPB, CQ7
- + *
- + * Return 0 for success. For "set" function, returns -EINVAL if prio_a or
- + * prio_b are out of the range 0 - 7 (priority of group A or group B can not
- + * be 0, CQ0 is always the highest class queue in this channel.), or -EIO if
- + * the configure scheduler command returns error. For "get" function, return
- + * -EINVAL if the query scheduler command returns error.
- + */
- +int qman_ceetm_channel_set_group(struct qm_ceetm_channel *channel,
- + int group_b,
- + unsigned int prio_a,
- + unsigned int prio_b);
- +int qman_ceetm_channel_get_group(struct qm_ceetm_channel *channel,
- + int *group_b,
- + unsigned int *prio_a,
- + unsigned int *prio_b);
- +
- +/**
- + * qman_ceetm_channel_set_group_cr_eligibility
- + * qman_ceetm_channel_set_group_er_eligibility - Set channel group eligibility
- + * @channel: the given channel object
- + * @group_b: indicates whether there is group B in this channel.
- + * @cre: the commit rate eligibility, 1 for enable, 0 for disable.
- + *
- + * Return zero for success, or -EINVAL if eligibility setting fails.
- +*/
- +int qman_ceetm_channel_set_group_cr_eligibility(struct qm_ceetm_channel
- + *channel, int group_b, int cre);
- +int qman_ceetm_channel_set_group_er_eligibility(struct qm_ceetm_channel
- + *channel, int group_b, int ere);
- +
- +/**
- + * qman_ceetm_channel_set_cq_cr_eligibility
- + * qman_ceetm_channel_set_cq_er_eligibility - Set channel cq eligibility
- + * @channel: the given channel object
- + * @idx: is from 0 to 7 (representing CQ0 to CQ7).
- + * @cre: the commit rate eligibility, 1 for enable, 0 for disable.
- + *
- + * Return zero for success, or -EINVAL if eligibility setting fails.
- +*/
- +int qman_ceetm_channel_set_cq_cr_eligibility(struct qm_ceetm_channel *channel,
- + unsigned int idx, int cre);
- +int qman_ceetm_channel_set_cq_er_eligibility(struct qm_ceetm_channel *channel,
- + unsigned int idx, int ere);
- +
- + /* --------------------- */
- + /* CEETM :: class queues */
- + /* --------------------- */
- +
- +/**
- + * qman_ceetm_cq_claim - Claims an individual class queue.
- + * @cq: the returned class queue object, if successful.
- + * @channel: the class queue channel.
- + * @idx: is from 0 to 7 (representing CQ0 to CQ7).
- + * @ccg: represents the class congestion group that this class queue should be
- + * subscribed to, or NULL if no congestion group membership is desired.
- + *
- + * Returns zero for success, or -EINVAL if @idx is out of range 0 - 7 or
- + * if this class queue has been claimed, or configure class queue command
- + * returns error, or returns -ENOMEM if allocating CQ memory fails.
- + */
- +int qman_ceetm_cq_claim(struct qm_ceetm_cq **cq,
- + struct qm_ceetm_channel *channel,
- + unsigned int idx,
- + struct qm_ceetm_ccg *ccg);
- +
- +/**
- + * qman_ceetm_cq_claim_A - Claims a class queue group A.
- + * @cq: the returned class queue object, if successful.
- + * @channel: the class queue channel.
- + * @idx: is from 8 to 15 if only group A exits, otherwise, it is from 8 to 11.
- + * @ccg: represents the class congestion group that this class queue should be
- + * subscribed to, or NULL if no congestion group membership is desired.
- + *
- + * Return zero for success, or -EINVAL if @idx is out the range or if
- + * this class queue has been claimed or configure class queue command returns
- + * error, or returns -ENOMEM if allocating CQ memory fails.
- + */
- +int qman_ceetm_cq_claim_A(struct qm_ceetm_cq **cq,
- + struct qm_ceetm_channel *channel,
- + unsigned int idx,
- + struct qm_ceetm_ccg *ccg);
- +
- +/**
- + * qman_ceetm_cq_claim_B - Claims a class queue group B.
- + * @cq: the returned class queue object, if successful.
- + * @channel: the class queue channel.
- + * @idx: is from 0 to 3 (CQ12 to CQ15).
- + * @ccg: represents the class congestion group that this class queue should be
- + * subscribed to, or NULL if no congestion group membership is desired.
- + *
- + * Return zero for success, or -EINVAL if @idx is out the range or if
- + * this class queue has been claimed or configure class queue command returns
- + * error, or returns -ENOMEM if allocating CQ memory fails.
- + */
- +int qman_ceetm_cq_claim_B(struct qm_ceetm_cq **cq,
- + struct qm_ceetm_channel *channel,
- + unsigned int idx,
- + struct qm_ceetm_ccg *ccg);
- +
- +/**
- + * qman_ceetm_cq_release - Releases a previously claimed class queue.
- + * @cq: The class queue to be released.
- + *
- + * Return zero for success, or -EBUSY if the dependent objects (eg. logical
- + * FQIDs) have not been released.
- + */
- +int qman_ceetm_cq_release(struct qm_ceetm_cq *cq);
- +
- +/**
- + * qman_ceetm_set_queue_weight
- + * qman_ceetm_get_queue_weight - Configure/query the weight of a grouped class
- + * queue.
- + * @cq: the given class queue.
- + * @weight_code: the desired weight code to set for the given class queue for
- + * "set" function or the queired weight code for "get" function.
- + *
- + * Grouped class queues have a default weight code of zero, which corresponds to
- + * a scheduler weighting of 1. This function can be used to modify a grouped
- + * class queue to another weight, (Use the helpers qman_ceetm_wbfs2ratio()
- + * and qman_ceetm_ratio2wbfs() to convert between these 'weight_code' values
- + * and the corresponding sharing weight.)
- + *
- + * Returns zero for success, or -EIO if the configure weight command returns
- + * error for "set" function, or -EINVAL if the query command returns
- + * error for "get" function.
- + * See section "CEETM Weighted Scheduling among Grouped Classes" in Reference
- + * Manual for weight and weight code.
- + */
- +int qman_ceetm_set_queue_weight(struct qm_ceetm_cq *cq,
- + struct qm_ceetm_weight_code *weight_code);
- +int qman_ceetm_get_queue_weight(struct qm_ceetm_cq *cq,
- + struct qm_ceetm_weight_code *weight_code);
- +
- +/**
- + * qman_ceetm_set_queue_weight_in_ratio
- + * qman_ceetm_get_queue_weight_in_ratio - Configure/query the weight of a
- + * grouped class queue.
- + * @cq: the given class queue.
- + * @ratio: the weight in ratio. It should be the real ratio number multiplied
- + * by 100 to get rid of fraction.
- + *
- + * Returns zero for success, or -EIO if the configure weight command returns
- + * error for "set" function, or -EINVAL if the query command returns
- + * error for "get" function.
- + */
- +int qman_ceetm_set_queue_weight_in_ratio(struct qm_ceetm_cq *cq, u32 ratio);
- +int qman_ceetm_get_queue_weight_in_ratio(struct qm_ceetm_cq *cq, u32 *ratio);
- +
- +/* Weights are encoded using a pseudo-exponential scheme. The weight codes 0,
- + * 32, 64, [...] correspond to weights of 1, 2, 4, [...]. The weights
- + * corresponding to intermediate weight codes are calculated using linear
- + * interpolation on the inverted values. Or put another way, the inverse weights
- + * for each 32nd weight code are 1, 1/2, 1/4, [...], and so the intervals
- + * between these are divided linearly into 32 intermediate values, the inverses
- + * of which form the remaining weight codes.
- + *
- + * The Weighted Bandwidth Fair Scheduling (WBFS) algorithm provides a form of
- + * scheduling within a group of class queues (group A or B). Weights are used to
- + * normalise the class queues to an underlying BFS algorithm where all class
- + * queues are assumed to require "equal bandwidth". So the weights referred to
- + * by the weight codes act as divisors on the size of frames being enqueued. Ie.
- + * one class queue in a group is assigned a weight of 2 whilst the other class
- + * queues in the group keep the default weight of 1, then the WBFS scheduler
- + * will effectively treat all frames enqueued on the weight-2 class queue as
- + * having half the number of bytes they really have. Ie. if all other things are
- + * equal, that class queue would get twice as much bytes-per-second bandwidth as
- + * the others. So weights should be chosen to provide bandwidth ratios between
- + * members of the same class queue group. These weights have no bearing on
- + * behaviour outside that group's WBFS mechanism though.
- + */
- +
- +/**
- + * qman_ceetm_wbfs2ratio - Given a weight code ('wbfs'), an accurate fractional
- + * representation of the corresponding weight is given (in order to not lose
- + * any precision).
- + * @weight_code: The given weight code in WBFS.
- + * @numerator: the numerator part of the weight computed by the weight code.
- + * @denominator: the denominator part of the weight computed by the weight code
- + *
- + * Returns zero for success or -EINVAL if the given weight code is illegal.
- + */
- +int qman_ceetm_wbfs2ratio(struct qm_ceetm_weight_code *weight_code,
- + u32 *numerator,
- + u32 *denominator);
- +/**
- + * qman_ceetm_ratio2wbfs - Given a weight, find the nearest possible weight code
- + * If the user needs to know how close this is, convert the resulting weight
- + * code back to a weight and compare.
- + * @numerator: numerator part of the given weight.
- + * @denominator: denominator part of the given weight.
- + * @weight_code: the weight code computed from the given weight.
- + *
- + * Returns zero for success, or -ERANGE if "numerator/denominator" is outside
- + * the range of weights.
- + */
- +int qman_ceetm_ratio2wbfs(u32 numerator,
- + u32 denominator,
- + struct qm_ceetm_weight_code *weight_code,
- + int rounding);
- +
- +#define QMAN_CEETM_FLAG_CLEAR_STATISTICS_COUNTER 0x1
- +/**
- + * qman_ceetm_cq_get_dequeue_statistics - Get the statistics provided by CEETM
- + * CQ counters.
- + * @cq: the given CQ object.
- + * @flags: indicates whether the statistics counter will be cleared after query.
- + * @frame_count: The number of the frames that have been counted since the
- + * counter was cleared last time.
- + * @byte_count: the number of bytes in all frames that have been counted.
- + *
- + * Return zero for success or -EINVAL if query statistics command returns error.
- + *
- + */
- +int qman_ceetm_cq_get_dequeue_statistics(struct qm_ceetm_cq *cq, u32 flags,
- + u64 *frame_count, u64 *byte_count);
- +
- +/**
- + * qman_ceetm_drain_cq - drain the CQ till it is empty.
- + * @cq: the give CQ object.
- + * Return 0 for success or -EINVAL for unsuccessful command to empty CQ.
- + */
- +int qman_ceetm_drain_cq(struct qm_ceetm_cq *cq);
- +
- + /* ---------------------- */
- + /* CEETM :: logical FQIDs */
- + /* ---------------------- */
- +/**
- + * qman_ceetm_lfq_claim - Claims an unused logical FQID, associates it with
- + * the given class queue.
- + * @lfq: the returned lfq object, if successful.
- + * @cq: the class queue which needs to claim a LFQID.
- + *
- + * Return zero for success, or -ENODEV if no LFQID is available or -ENOMEM if
- + * allocating memory for lfq fails, or -EINVAL if configuring LFQMT fails.
- + */
- +int qman_ceetm_lfq_claim(struct qm_ceetm_lfq **lfq,
- + struct qm_ceetm_cq *cq);
- +
- +/**
- + * qman_ceetm_lfq_release - Releases a previously claimed logical FQID.
- + * @lfq: the lfq to be released.
- + *
- + * Return zero for success.
- + */
- +int qman_ceetm_lfq_release(struct qm_ceetm_lfq *lfq);
- +
- +/**
- + * qman_ceetm_lfq_set_context
- + * qman_ceetm_lfq_get_context - Set/get the context_a/context_b pair to the
- + * "dequeue context table" associated with the logical FQID.
- + * @lfq: the given logical FQ object.
- + * @context_a: contextA of the dequeue context.
- + * @context_b: contextB of the dequeue context.
- + *
- + * Returns zero for success, or -EINVAL if there is error to set/get the
- + * context pair.
- + */
- +int qman_ceetm_lfq_set_context(struct qm_ceetm_lfq *lfq,
- + u64 context_a,
- + u32 context_b);
- +int qman_ceetm_lfq_get_context(struct qm_ceetm_lfq *lfq,
- + u64 *context_a,
- + u32 *context_b);
- +
- +/**
- + * qman_ceetm_create_fq - Initialise a FQ object for the LFQ.
- + * @lfq: the given logic fq.
- + * @fq: the fq object created for the given logic fq.
- + *
- + * The FQ object can be used in qman_enqueue() and qman_enqueue_orp() APIs to
- + * target a logical FQID (and the class queue it is associated with).
- + * Note that this FQ object can only be used for enqueues, and
- + * in the case of qman_enqueue_orp() it can not be used as the 'orp' parameter,
- + * only as 'fq'. This FQ object can not (and shouldn't) be destroyed, it is only
- + * valid as long as the underlying 'lfq' remains claimed. It is the user's
- + * responsibility to ensure that the underlying 'lfq' is not released until any
- + * enqueues to this FQ object have completed. The only field the user needs to
- + * fill in is fq->cb.ern, as that enqueue rejection handler is the callback that
- + * could conceivably be called on this FQ object. This API can be called
- + * multiple times to create multiple FQ objects referring to the same logical
- + * FQID, and any enqueue rejections will respect the callback of the object that
- + * issued the enqueue (and will identify the object via the parameter passed to
- + * the callback too). There is no 'flags' parameter to this API as there is for
- + * qman_create_fq() - the created FQ object behaves as though qman_create_fq()
- + * had been called with the single flag QMAN_FQ_FLAG_NO_MODIFY.
- + *
- + * Returns 0 for success.
- + */
- +int qman_ceetm_create_fq(struct qm_ceetm_lfq *lfq, struct qman_fq *fq);
- +
- + /* -------------------------------- */
- + /* CEETM :: class congestion groups */
- + /* -------------------------------- */
- +
- +/**
- + * qman_ceetm_ccg_claim - Claims an unused CCG.
- + * @ccg: the returned CCG object, if successful.
- + * @channel: the given class queue channel
- + * @cscn: the callback function of this CCG.
- + * @cb_ctx: the corresponding context to be used used if state change
- + * notifications are later enabled for this CCG.
- + *
- + * The congestion group is local to the given class queue channel, so only
- + * class queues within the channel can be associated with that congestion group.
- + * The association of class queues to congestion groups occurs when the class
- + * queues are claimed, see qman_ceetm_cq_claim() and related functions.
- + * Congestion groups are in a "zero" state when initially claimed, and they are
- + * returned to that state when released.
- + *
- + * Return zero for success, or -EINVAL if no CCG in the channel is available.
- + */
- +int qman_ceetm_ccg_claim(struct qm_ceetm_ccg **ccg,
- + struct qm_ceetm_channel *channel,
- + unsigned int idx,
- + void (*cscn)(struct qm_ceetm_ccg *,
- + void *cb_ctx,
- + int congested),
- + void *cb_ctx);
- +
- +/**
- + * qman_ceetm_ccg_release - Releases a previously claimed CCG.
- + * @ccg: the given ccg.
- + *
- + * Returns zero for success, or -EBUSY if the given ccg's dependent objects
- + * (class queues that are associated with the CCG) have not been released.
- + */
- +int qman_ceetm_ccg_release(struct qm_ceetm_ccg *ccg);
- +
- +/* This struct is used to specify attributes for a CCG. The 'we_mask' field
- + * controls which CCG attributes are to be updated, and the remainder specify
- + * the values for those attributes. A CCG counts either frames or the bytes
- + * within those frames, but not both ('mode'). A CCG can optionally cause
- + * enqueues to be rejected, due to tail-drop or WRED, or both (they are
- + * independent options, 'td_en' and 'wr_en_g,wr_en_y,wr_en_r'). Tail-drop can be
- + * level-triggered due to a single threshold ('td_thres') or edge-triggered due
- + * to a "congestion state", but not both ('td_mode'). Congestion state has
- + * distinct entry and exit thresholds ('cs_thres_in' and 'cs_thres_out'), and
- + * notifications can be sent to software the CCG goes in to and out of this
- + * congested state ('cscn_en'). */
- +struct qm_ceetm_ccg_params {
- + /* Boolean fields together in a single bitfield struct */
- + struct {
- + /* Whether to count bytes or frames. 1==frames */
- + u8 mode:1;
- + /* En/disable tail-drop. 1==enable */
- + u8 td_en:1;
- + /* Tail-drop on congestion-state or threshold. 1=threshold */
- + u8 td_mode:1;
- + /* Generate congestion state change notifications. 1==enable */
- + u8 cscn_en:1;
- + /* Enable WRED rejections (per colour). 1==enable */
- + u8 wr_en_g:1;
- + u8 wr_en_y:1;
- + u8 wr_en_r:1;
- + } __packed;
- + /* Tail-drop threshold. See qm_cgr_thres_[gs]et64(). */
- + struct qm_cgr_cs_thres td_thres;
- + /* Congestion state thresholds, for entry and exit. */
- + struct qm_cgr_cs_thres cs_thres_in;
- + struct qm_cgr_cs_thres cs_thres_out;
- + /* Overhead accounting length. Per-packet "tax", from -128 to +127 */
- + signed char oal;
- + /* Congestion state change notification for DCP portal, virtual CCGID*/
- + /* WRED parameters. */
- + struct qm_cgr_wr_parm wr_parm_g;
- + struct qm_cgr_wr_parm wr_parm_y;
- + struct qm_cgr_wr_parm wr_parm_r;
- +};
- +/* Bits used in 'we_mask' to qman_ceetm_ccg_set(), controls which attributes of
- + * the CCGR are to be updated. */
- +#define QM_CCGR_WE_MODE 0x0001 /* mode (bytes/frames) */
- +#define QM_CCGR_WE_CS_THRES_IN 0x0002 /* congestion state entry threshold */
- +#define QM_CCGR_WE_TD_EN 0x0004 /* congestion state tail-drop enable */
- +#define QM_CCGR_WE_CSCN_TUPD 0x0008 /* CSCN target update */
- +#define QM_CCGR_WE_CSCN_EN 0x0010 /* congestion notification enable */
- +#define QM_CCGR_WE_WR_EN_R 0x0020 /* WRED enable - red */
- +#define QM_CCGR_WE_WR_EN_Y 0x0040 /* WRED enable - yellow */
- +#define QM_CCGR_WE_WR_EN_G 0x0080 /* WRED enable - green */
- +#define QM_CCGR_WE_WR_PARM_R 0x0100 /* WRED parameters - red */
- +#define QM_CCGR_WE_WR_PARM_Y 0x0200 /* WRED parameters - yellow */
- +#define QM_CCGR_WE_WR_PARM_G 0x0400 /* WRED parameters - green */
- +#define QM_CCGR_WE_OAL 0x0800 /* overhead accounting length */
- +#define QM_CCGR_WE_CS_THRES_OUT 0x1000 /* congestion state exit threshold */
- +#define QM_CCGR_WE_TD_THRES 0x2000 /* tail-drop threshold */
- +#define QM_CCGR_WE_TD_MODE 0x4000 /* tail-drop mode (state/threshold) */
- +#define QM_CCGR_WE_CDV 0x8000 /* cdv */
- +
- +/**
- + * qman_ceetm_ccg_set
- + * qman_ceetm_ccg_get - Configure/query a subset of CCG attributes.
- + * @ccg: the given CCG object.
- + * @we_mask: the write enable mask.
- + * @params: the parameters setting for this ccg
- + *
- + * Return 0 for success, or -EIO if configure ccg command returns error for
- + * "set" function, or -EINVAL if query ccg command returns error for "get"
- + * function.
- + */
- +int qman_ceetm_ccg_set(struct qm_ceetm_ccg *ccg,
- + u16 we_mask,
- + const struct qm_ceetm_ccg_params *params);
- +int qman_ceetm_ccg_get(struct qm_ceetm_ccg *ccg,
- + struct qm_ceetm_ccg_params *params);
- +
- +/** qman_ceetm_cscn_swp_set - Add or remove a software portal from the target
- + * mask.
- + * qman_ceetm_cscn_swp_get - Query whether a given software portal index is
- + * in the cscn target mask.
- + * @ccg: the give CCG object.
- + * @swp_idx: the index of the software portal.
- + * @cscn_enabled: 1: Set the swp to be cscn target. 0: remove the swp from
- + * the target mask.
- + * @we_mask: the write enable mask.
- + * @params: the parameters setting for this ccg
- + *
- + * Return 0 for success, or -EINVAL if command in set/get function fails.
- + */
- +int qman_ceetm_cscn_swp_set(struct qm_ceetm_ccg *ccg,
- + u16 swp_idx,
- + unsigned int cscn_enabled,
- + u16 we_mask,
- + const struct qm_ceetm_ccg_params *params);
- +int qman_ceetm_cscn_swp_get(struct qm_ceetm_ccg *ccg,
- + u16 swp_idx,
- + unsigned int *cscn_enabled);
- +
- +/** qman_ceetm_cscn_dcp_set - Add or remove a direct connect portal from the\
- + * target mask.
- + * qman_ceetm_cscn_swp_get - Query whether a given direct connect portal index
- + * is in the cscn target mask.
- + * @ccg: the give CCG object.
- + * @dcp_idx: the index of the direct connect portal.
- + * @vcgid: congestion state change notification for dcp portal, virtual CGID.
- + * @cscn_enabled: 1: Set the dcp to be cscn target. 0: remove the dcp from
- + * the target mask.
- + * @we_mask: the write enable mask.
- + * @params: the parameters setting for this ccg
- + *
- + * Return 0 for success, or -EINVAL if command in set/get function fails.
- + */
- +int qman_ceetm_cscn_dcp_set(struct qm_ceetm_ccg *ccg,
- + u16 dcp_idx,
- + u8 vcgid,
- + unsigned int cscn_enabled,
- + u16 we_mask,
- + const struct qm_ceetm_ccg_params *params);
- +int qman_ceetm_cscn_dcp_get(struct qm_ceetm_ccg *ccg,
- + u16 dcp_idx,
- + u8 *vcgid,
- + unsigned int *cscn_enabled);
- +
- +/**
- + * qman_ceetm_ccg_get_reject_statistics - Get the statistics provided by
- + * CEETM CCG counters.
- + * @ccg: the given CCG object.
- + * @flags: indicates whether the statistics counter will be cleared after query.
- + * @frame_count: The number of the frames that have been counted since the
- + * counter was cleared last time.
- + * @byte_count: the number of bytes in all frames that have been counted.
- + *
- + * Return zero for success or -EINVAL if query statistics command returns error.
- + *
- + */
- +int qman_ceetm_ccg_get_reject_statistics(struct qm_ceetm_ccg *ccg, u32 flags,
- + u64 *frame_count, u64 *byte_count);
- +
- +/**
- + * qman_ceetm_query_lfqmt - Query the logical frame queue mapping table
- + * @lfqid: Logical Frame Queue ID
- + * @lfqmt_query: Results of the query command
- + *
- + * Returns zero for success or -EIO if the query command returns error.
- + *
- + */
- +int qman_ceetm_query_lfqmt(int lfqid,
- + struct qm_mcr_ceetm_lfqmt_query *lfqmt_query);
- +
- +/**
- + * qman_ceetm_query_write_statistics - Query (and optionally write) statistics
- + * @cid: Target ID (CQID or CCGRID)
- + * @dcp_idx: CEETM portal ID
- + * @command_type: One of the following:
- + * 0 = Query dequeue statistics. CID carries the CQID to be queried.
- + * 1 = Query and clear dequeue statistics. CID carries the CQID to be queried
- + * 2 = Write dequeue statistics. CID carries the CQID to be written.
- + * 3 = Query reject statistics. CID carries the CCGRID to be queried.
- + * 4 = Query and clear reject statistics. CID carries the CCGRID to be queried
- + * 5 = Write reject statistics. CID carries the CCGRID to be written
- + * @frame_count: Frame count value to be written if this is a write command
- + * @byte_count: Bytes count value to be written if this is a write command
- + *
- + * Returns zero for success or -EIO if the query command returns error.
- + */
- +int qman_ceetm_query_write_statistics(u16 cid, enum qm_dc_portal dcp_idx,
- + u16 command_type, u64 frame_count,
- + u64 byte_count);
- +
- +/**
- + * qman_set_wpm - Set waterfall power management
- + *
- + * @wpm_enable: boolean, 1 = enable wpm, 0 = disable wpm.
- + *
- + * Return 0 for success, return -ENODEV if QMan misc_cfg register is not
- + * accessible.
- + */
- +int qman_set_wpm(int wpm_enable);
- +
- +/**
- + * qman_get_swp - Query the waterfall power management setting
- + *
- + * @wpm_enable: boolean, 1 = enable wpm, 0 = disable wpm.
- + *
- + * Return 0 for success, return -ENODEV if QMan misc_cfg register is not
- + * accessible.
- + */
- +int qman_get_wpm(int *wpm_enable);
- +
- +/* The below qman_p_***() variants might be called in a migration situation
- + * (e.g. cpu hotplug). They are used to continue accessing the portal that
- + * execution was affine to prior to migration.
- + * @qman_portal specifies which portal the APIs will use.
- +*/
- +const struct qman_portal_config *qman_p_get_portal_config(struct qman_portal
- + *p);
- +int qman_p_irqsource_add(struct qman_portal *p, u32 bits);
- +int qman_p_irqsource_remove(struct qman_portal *p, u32 bits);
- +int qman_p_poll_dqrr(struct qman_portal *p, unsigned int limit);
- +u32 qman_p_poll_slow(struct qman_portal *p);
- +void qman_p_poll(struct qman_portal *p);
- +void qman_p_stop_dequeues(struct qman_portal *p);
- +void qman_p_start_dequeues(struct qman_portal *p);
- +void qman_p_static_dequeue_add(struct qman_portal *p, u32 pools);
- +void qman_p_static_dequeue_del(struct qman_portal *p, u32 pools);
- +u32 qman_p_static_dequeue_get(struct qman_portal *p);
- +void qman_p_dca(struct qman_portal *p, struct qm_dqrr_entry *dq,
- + int park_request);
- +int qman_p_volatile_dequeue(struct qman_portal *p, struct qman_fq *fq,
- + u32 flags __maybe_unused, u32 vdqcr);
- +int qman_p_enqueue(struct qman_portal *p, struct qman_fq *fq,
- + const struct qm_fd *fd, u32 flags);
- +int qman_p_enqueue_orp(struct qman_portal *p, struct qman_fq *fq,
- + const struct qm_fd *fd, u32 flags,
- + struct qman_fq *orp, u16 orp_seqnum);
- +int qman_p_enqueue_precommit(struct qman_portal *p, struct qman_fq *fq,
- + const struct qm_fd *fd, u32 flags,
- + qman_cb_precommit cb, void *cb_arg);
- +#ifdef __cplusplus
- +}
- +#endif
- +
- +#endif /* FSL_QMAN_H */
- --- /dev/null
- +++ b/include/linux/fsl_usdpaa.h
- @@ -0,0 +1,372 @@
- +/* Copyright 2011-2012 Freescale Semiconductor, Inc.
- + *
- + * This file is licensed under the terms of the GNU General Public License
- + * version 2. This program is licensed "as is" without any warranty of any
- + * kind, whether express or implied.
- + */
- +
- +#ifndef FSL_USDPAA_H
- +#define FSL_USDPAA_H
- +
- +#ifdef __cplusplus
- +extern "C" {
- +#endif
- +
- +#include <linux/uaccess.h>
- +#include <linux/ioctl.h>
- +#include <linux/fsl_qman.h> /* For "enum qm_channel" */
- +#include <linux/compat.h>
- +
- +#ifdef CONFIG_FSL_USDPAA
- +
- +/******************************/
- +/* Allocation of resource IDs */
- +/******************************/
- +
- +/* This enum is used to distinguish between the type of underlying object being
- + * manipulated. */
- +enum usdpaa_id_type {
- + usdpaa_id_fqid,
- + usdpaa_id_bpid,
- + usdpaa_id_qpool,
- + usdpaa_id_cgrid,
- + usdpaa_id_ceetm0_lfqid,
- + usdpaa_id_ceetm0_channelid,
- + usdpaa_id_ceetm1_lfqid,
- + usdpaa_id_ceetm1_channelid,
- + usdpaa_id_max /* <-- not a valid type, represents the number of types */
- +};
- +#define USDPAA_IOCTL_MAGIC 'u'
- +struct usdpaa_ioctl_id_alloc {
- + uint32_t base; /* Return value, the start of the allocated range */
- + enum usdpaa_id_type id_type; /* what kind of resource(s) to allocate */
- + uint32_t num; /* how many IDs to allocate (and return value) */
- + uint32_t align; /* must be a power of 2, 0 is treated like 1 */
- + int partial; /* whether to allow less than 'num' */
- +};
- +struct usdpaa_ioctl_id_release {
- + /* Input; */
- + enum usdpaa_id_type id_type;
- + uint32_t base;
- + uint32_t num;
- +};
- +struct usdpaa_ioctl_id_reserve {
- + enum usdpaa_id_type id_type;
- + uint32_t base;
- + uint32_t num;
- +};
- +
- +
- +/* ioctl() commands */
- +#define USDPAA_IOCTL_ID_ALLOC \
- + _IOWR(USDPAA_IOCTL_MAGIC, 0x01, struct usdpaa_ioctl_id_alloc)
- +#define USDPAA_IOCTL_ID_RELEASE \
- + _IOW(USDPAA_IOCTL_MAGIC, 0x02, struct usdpaa_ioctl_id_release)
- +#define USDPAA_IOCTL_ID_RESERVE \
- + _IOW(USDPAA_IOCTL_MAGIC, 0x0A, struct usdpaa_ioctl_id_reserve)
- +
- +/**********************/
- +/* Mapping DMA memory */
- +/**********************/
- +
- +/* Maximum length for a map name, including NULL-terminator */
- +#define USDPAA_DMA_NAME_MAX 16
- +/* Flags for requesting DMA maps. Maps are private+unnamed or sharable+named.
- + * For a sharable and named map, specify _SHARED (whether creating one or
- + * binding to an existing one). If _SHARED is specified and _CREATE is not, then
- + * the mapping must already exist. If _SHARED and _CREATE are specified and the
- + * mapping doesn't already exist, it will be created. If _SHARED and _CREATE are
- + * specified and the mapping already exists, the mapping will fail unless _LAZY
- + * is specified. When mapping to a pre-existing sharable map, the length must be
- + * an exact match. Lengths must be a power-of-4 multiple of page size.
- + *
- + * Note that this does not actually map the memory to user-space, that is done
- + * by a subsequent mmap() using the page offset returned from this ioctl(). The
- + * ioctl() is what gives the process permission to do this, and a page-offset
- + * with which to do so.
- + */
- +#define USDPAA_DMA_FLAG_SHARE 0x01
- +#define USDPAA_DMA_FLAG_CREATE 0x02
- +#define USDPAA_DMA_FLAG_LAZY 0x04
- +#define USDPAA_DMA_FLAG_RDONLY 0x08
- +struct usdpaa_ioctl_dma_map {
- + /* Output parameters - virtual and physical addresses */
- + void *ptr;
- + uint64_t phys_addr;
- + /* Input parameter, the length of the region to be created (or if
- + * mapping an existing region, this must match it). Must be a power-of-4
- + * multiple of page size. */
- + uint64_t len;
- + /* Input parameter, the USDPAA_DMA_FLAG_* settings. */
- + uint32_t flags;
- + /* If _FLAG_SHARE is specified, the name of the region to be created (or
- + * of the existing mapping to use). */
- + char name[USDPAA_DMA_NAME_MAX];
- + /* If this ioctl() creates the mapping, this is an input parameter
- + * stating whether the region supports locking. If mapping an existing
- + * region, this is a return value indicating the same thing. */
- + int has_locking;
- + /* In the case of a successful map with _CREATE and _LAZY, this return
- + * value indicates whether we created the mapped region or whether it
- + * already existed. */
- + int did_create;
- +};
- +
- +#ifdef CONFIG_COMPAT
- +struct usdpaa_ioctl_dma_map_compat {
- + /* Output parameters - virtual and physical addresses */
- + compat_uptr_t ptr;
- + uint64_t phys_addr;
- + /* Input parameter, the length of the region to be created (or if
- + * mapping an existing region, this must match it). Must be a power-of-4
- + * multiple of page size. */
- + uint64_t len;
- + /* Input parameter, the USDPAA_DMA_FLAG_* settings. */
- + uint32_t flags;
- + /* If _FLAG_SHARE is specified, the name of the region to be created (or
- + * of the existing mapping to use). */
- + char name[USDPAA_DMA_NAME_MAX];
- + /* If this ioctl() creates the mapping, this is an input parameter
- + * stating whether the region supports locking. If mapping an existing
- + * region, this is a return value indicating the same thing. */
- + int has_locking;
- + /* In the case of a successful map with _CREATE and _LAZY, this return
- + * value indicates whether we created the mapped region or whether it
- + * already existed. */
- + int did_create;
- +};
- +
- +#define USDPAA_IOCTL_DMA_MAP_COMPAT \
- + _IOWR(USDPAA_IOCTL_MAGIC, 0x03, struct usdpaa_ioctl_dma_map_compat)
- +#endif
- +
- +
- +#define USDPAA_IOCTL_DMA_MAP \
- + _IOWR(USDPAA_IOCTL_MAGIC, 0x03, struct usdpaa_ioctl_dma_map)
- +/* munmap() does not remove the DMA map, just the user-space mapping to it.
- + * This ioctl will do both (though you can munmap() before calling the ioctl
- + * too). */
- +#define USDPAA_IOCTL_DMA_UNMAP \
- + _IOW(USDPAA_IOCTL_MAGIC, 0x04, unsigned char)
- +/* We implement a cross-process locking scheme per DMA map. Call this ioctl()
- + * with a mmap()'d address, and the process will (interruptible) sleep if the
- + * lock is already held by another process. Process destruction will
- + * automatically clean up any held locks. */
- +#define USDPAA_IOCTL_DMA_LOCK \
- + _IOW(USDPAA_IOCTL_MAGIC, 0x05, unsigned char)
- +#define USDPAA_IOCTL_DMA_UNLOCK \
- + _IOW(USDPAA_IOCTL_MAGIC, 0x06, unsigned char)
- +
- +/***************************************/
- +/* Mapping and using QMan/BMan portals */
- +/***************************************/
- +enum usdpaa_portal_type {
- + usdpaa_portal_qman,
- + usdpaa_portal_bman,
- +};
- +
- +#define QBMAN_ANY_PORTAL_IDX 0xffffffff
- +
- +struct usdpaa_ioctl_portal_map {
- + /* Input parameter, is a qman or bman portal required. */
- +
- + enum usdpaa_portal_type type;
- + /* Specifes a specific portal index to map or QBMAN_ANY_PORTAL_IDX
- + for don't care. The portal index will be populated by the
- + driver when the ioctl() successfully completes */
- + uint32_t index;
- +
- + /* Return value if the map succeeds, this gives the mapped
- + * cache-inhibited (cinh) and cache-enabled (cena) addresses. */
- + struct usdpaa_portal_map {
- + void *cinh;
- + void *cena;
- + } addr;
- + /* Qman-specific return values */
- + uint16_t channel;
- + uint32_t pools;
- +};
- +
- +#ifdef CONFIG_COMPAT
- +struct compat_usdpaa_ioctl_portal_map {
- + /* Input parameter, is a qman or bman portal required. */
- + enum usdpaa_portal_type type;
- + /* Specifes a specific portal index to map or QBMAN_ANY_PORTAL_IDX
- + for don't care. The portal index will be populated by the
- + driver when the ioctl() successfully completes */
- + uint32_t index;
- + /* Return value if the map succeeds, this gives the mapped
- + * cache-inhibited (cinh) and cache-enabled (cena) addresses. */
- + struct usdpaa_portal_map_compat {
- + compat_uptr_t cinh;
- + compat_uptr_t cena;
- + } addr;
- + /* Qman-specific return values */
- + uint16_t channel;
- + uint32_t pools;
- +};
- +#define USDPAA_IOCTL_PORTAL_MAP_COMPAT \
- + _IOWR(USDPAA_IOCTL_MAGIC, 0x07, struct compat_usdpaa_ioctl_portal_map)
- +#define USDPAA_IOCTL_PORTAL_UNMAP_COMPAT \
- + _IOW(USDPAA_IOCTL_MAGIC, 0x08, struct usdpaa_portal_map_compat)
- +#endif
- +
- +#define USDPAA_IOCTL_PORTAL_MAP \
- + _IOWR(USDPAA_IOCTL_MAGIC, 0x07, struct usdpaa_ioctl_portal_map)
- +#define USDPAA_IOCTL_PORTAL_UNMAP \
- + _IOW(USDPAA_IOCTL_MAGIC, 0x08, struct usdpaa_portal_map)
- +
- +struct usdpaa_ioctl_irq_map {
- + enum usdpaa_portal_type type; /* Type of portal to map */
- + int fd; /* File descriptor that contains the portal */
- + void *portal_cinh; /* Cache inhibited area to identify the portal */
- +};
- +
- +#define USDPAA_IOCTL_PORTAL_IRQ_MAP \
- + _IOW(USDPAA_IOCTL_MAGIC, 0x09, struct usdpaa_ioctl_irq_map)
- +
- +#ifdef CONFIG_COMPAT
- +
- +struct compat_ioctl_irq_map {
- + enum usdpaa_portal_type type; /* Type of portal to map */
- + compat_int_t fd; /* File descriptor that contains the portal */
- + compat_uptr_t portal_cinh; /* Used identify the portal */};
- +
- +#define USDPAA_IOCTL_PORTAL_IRQ_MAP_COMPAT \
- + _IOW(USDPAA_IOCTL_MAGIC, 0x09, struct compat_ioctl_irq_map)
- +#endif
- +
- +/* ioctl to query the amount of DMA memory used in the system */
- +struct usdpaa_ioctl_dma_used {
- + uint64_t free_bytes;
- + uint64_t total_bytes;
- +};
- +#define USDPAA_IOCTL_DMA_USED \
- + _IOR(USDPAA_IOCTL_MAGIC, 0x0B, struct usdpaa_ioctl_dma_used)
- +
- +/* ioctl to allocate a raw portal */
- +struct usdpaa_ioctl_raw_portal {
- + /* inputs */
- + enum usdpaa_portal_type type; /* Type of portal to allocate */
- +
- + /* set to non zero to turn on stashing */
- + uint8_t enable_stash;
- + /* Stashing attributes for the portal */
- + uint32_t cpu;
- + uint32_t cache;
- + uint32_t window;
- +
- + /* Specifies the stash request queue this portal should use */
- + uint8_t sdest;
- +
- + /* Specifes a specific portal index to map or QBMAN_ANY_PORTAL_IDX
- + * for don't care. The portal index will be populated by the
- + * driver when the ioctl() successfully completes */
- + uint32_t index;
- +
- + /* outputs */
- + uint64_t cinh;
- + uint64_t cena;
- +};
- +
- +#define USDPAA_IOCTL_ALLOC_RAW_PORTAL \
- + _IOWR(USDPAA_IOCTL_MAGIC, 0x0C, struct usdpaa_ioctl_raw_portal)
- +
- +#define USDPAA_IOCTL_FREE_RAW_PORTAL \
- + _IOR(USDPAA_IOCTL_MAGIC, 0x0D, struct usdpaa_ioctl_raw_portal)
- +
- +#ifdef CONFIG_COMPAT
- +
- +struct compat_ioctl_raw_portal {
- + /* inputs */
- + enum usdpaa_portal_type type; /* Type of portal to allocate */
- +
- + /* set to non zero to turn on stashing */
- + uint8_t enable_stash;
- + /* Stashing attributes for the portal */
- + uint32_t cpu;
- + uint32_t cache;
- + uint32_t window;
- + /* Specifies the stash request queue this portal should use */
- + uint8_t sdest;
- +
- + /* Specifes a specific portal index to map or QBMAN_ANY_PORTAL_IDX
- + * for don't care. The portal index will be populated by the
- + * driver when the ioctl() successfully completes */
- + uint32_t index;
- +
- + /* outputs */
- + uint64_t cinh;
- + uint64_t cena;
- +};
- +
- +#define USDPAA_IOCTL_ALLOC_RAW_PORTAL_COMPAT \
- + _IOWR(USDPAA_IOCTL_MAGIC, 0x0C, struct compat_ioctl_raw_portal)
- +
- +#define USDPAA_IOCTL_FREE_RAW_PORTAL_COMPAT \
- + _IOR(USDPAA_IOCTL_MAGIC, 0x0D, struct compat_ioctl_raw_portal)
- +
- +#endif
- +
- +#ifdef __KERNEL__
- +
- +/* Early-boot hook */
- +int __init fsl_usdpaa_init_early(void);
- +
- +/* Fault-handling in arch/powerpc/mm/mem.c gives USDPAA an opportunity to detect
- + * faults within its ranges via this hook. */
- +int usdpaa_test_fault(unsigned long pfn, u64 *phys_addr, u64 *size);
- +
- +#endif /* __KERNEL__ */
- +
- +#endif /* CONFIG_FSL_USDPAA */
- +
- +#ifdef __KERNEL__
- +/* This interface is needed in a few places and though it's not specific to
- + * USDPAA as such, creating a new header for it doesn't make any sense. The
- + * qbman kernel driver implements this interface and uses it as the backend for
- + * both the FQID and BPID allocators. The fsl_usdpaa driver also uses this
- + * interface for tracking per-process allocations handed out to user-space. */
- +struct dpa_alloc {
- + struct list_head free;
- + spinlock_t lock;
- + struct list_head used;
- +};
- +#define DECLARE_DPA_ALLOC(name) \
- + struct dpa_alloc name = { \
- + .free = { \
- + .prev = &name.free, \
- + .next = &name.free \
- + }, \
- + .lock = __SPIN_LOCK_UNLOCKED(name.lock), \
- + .used = { \
- + .prev = &name.used, \
- + .next = &name.used \
- + } \
- + }
- +static inline void dpa_alloc_init(struct dpa_alloc *alloc)
- +{
- + INIT_LIST_HEAD(&alloc->free);
- + INIT_LIST_HEAD(&alloc->used);
- + spin_lock_init(&alloc->lock);
- +}
- +int dpa_alloc_new(struct dpa_alloc *alloc, u32 *result, u32 count, u32 align,
- + int partial);
- +void dpa_alloc_free(struct dpa_alloc *alloc, u32 base_id, u32 count);
- +void dpa_alloc_seed(struct dpa_alloc *alloc, u32 fqid, u32 count);
- +
- +/* Like 'new' but specifies the desired range, returns -ENOMEM if the entire
- + * desired range is not available, or 0 for success. */
- +int dpa_alloc_reserve(struct dpa_alloc *alloc, u32 base_id, u32 count);
- +/* Pops and returns contiguous ranges from the allocator. Returns -ENOMEM when
- + * 'alloc' is empty. */
- +int dpa_alloc_pop(struct dpa_alloc *alloc, u32 *result, u32 *count);
- +/* Returns 1 if the specified id is alloced, 0 otherwise */
- +int dpa_alloc_check(struct dpa_alloc *list, u32 id);
- +#endif /* __KERNEL__ */
- +
- +#ifdef __cplusplus
- +}
- +#endif
- +
- +#endif /* FSL_USDPAA_H */
|