1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682 |
- /*
- * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #define pr_fmt(fmt) "%s: " fmt, __func__
- #include <linux/bitops.h>
- #include <linux/debugfs.h>
- #include <linux/delay.h>
- #include <linux/err.h>
- #include <linux/init.h>
- #include <linux/interrupt.h>
- #include <linux/io.h>
- #include <linux/kernel.h>
- #include <linux/ktime.h>
- #include <linux/list.h>
- #include <linux/module.h>
- #include <linux/of.h>
- #include <linux/of_device.h>
- #include <linux/platform_device.h>
- #include <linux/pm_opp.h>
- #include <linux/slab.h>
- #include <linux/sort.h>
- #include <linux/string.h>
- #include <linux/uaccess.h>
- #include <linux/regulator/driver.h>
- #include <linux/regulator/machine.h>
- #include <linux/regulator/of_regulator.h>
- #include <linux/regulator/msm-ldo-regulator.h>
- #include <soc/qcom/spm.h>
- #include "cpr3-regulator.h"
- #define CPR3_REGULATOR_CORNER_INVALID (-1)
- #define CPR3_RO_MASK GENMASK(CPR3_RO_COUNT - 1, 0)
- /* CPR3 registers */
- #define CPR3_REG_CPR_VERSION 0x0
- #define CPRH_CPR_VERSION_4P5 0x40050000
- #define CPR3_REG_CPR_CTL 0x4
- #define CPR3_CPR_CTL_LOOP_EN_MASK BIT(0)
- #define CPR3_CPR_CTL_LOOP_ENABLE BIT(0)
- #define CPR3_CPR_CTL_LOOP_DISABLE 0
- #define CPR3_CPR_CTL_IDLE_CLOCKS_MASK GENMASK(5, 1)
- #define CPR3_CPR_CTL_IDLE_CLOCKS_SHIFT 1
- #define CPR3_CPR_CTL_COUNT_MODE_MASK GENMASK(7, 6)
- #define CPR3_CPR_CTL_COUNT_MODE_SHIFT 6
- #define CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_MIN 0
- #define CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_MAX 1
- #define CPR3_CPR_CTL_COUNT_MODE_STAGGERED 2
- #define CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_AGE 3
- #define CPR3_CPR_CTL_COUNT_REPEAT_MASK GENMASK(31, 9)
- #define CPR3_CPR_CTL_COUNT_REPEAT_SHIFT 9
- #define CPR3_REG_CPR_STATUS 0x8
- #define CPR3_CPR_STATUS_BUSY_MASK BIT(0)
- #define CPR3_CPR_STATUS_AGING_MEASUREMENT_MASK BIT(1)
- /*
- * This register is not present on controllers that support HW closed-loop
- * except CPR4 APSS controller.
- */
- #define CPR3_REG_CPR_TIMER_AUTO_CONT 0xC
- #define CPR3_REG_CPR_STEP_QUOT 0x14
- #define CPR3_CPR_STEP_QUOT_MIN_MASK GENMASK(5, 0)
- #define CPR3_CPR_STEP_QUOT_MIN_SHIFT 0
- #define CPR3_CPR_STEP_QUOT_MAX_MASK GENMASK(11, 6)
- #define CPR3_CPR_STEP_QUOT_MAX_SHIFT 6
- #define CPR3_REG_GCNT(ro) (0xA0 + 0x4 * (ro))
- #define CPR3_REG_SENSOR_BYPASS_WRITE(sensor) (0xE0 + 0x4 * ((sensor) / 32))
- #define CPR3_REG_SENSOR_BYPASS_WRITE_BANK(bank) (0xE0 + 0x4 * (bank))
- #define CPR3_REG_SENSOR_MASK_WRITE(sensor) (0x120 + 0x4 * ((sensor) / 32))
- #define CPR3_REG_SENSOR_MASK_WRITE_BANK(bank) (0x120 + 0x4 * (bank))
- #define CPR3_REG_SENSOR_MASK_READ(sensor) (0x140 + 0x4 * ((sensor) / 32))
- #define CPR3_REG_SENSOR_OWNER(sensor) (0x200 + 0x4 * (sensor))
- #define CPR3_REG_CONT_CMD 0x800
- #define CPR3_CONT_CMD_ACK 0x1
- #define CPR3_CONT_CMD_NACK 0x0
- #define CPR3_REG_THRESH(thread) (0x808 + 0x440 * (thread))
- #define CPR3_THRESH_CONS_DOWN_MASK GENMASK(3, 0)
- #define CPR3_THRESH_CONS_DOWN_SHIFT 0
- #define CPR3_THRESH_CONS_UP_MASK GENMASK(7, 4)
- #define CPR3_THRESH_CONS_UP_SHIFT 4
- #define CPR3_THRESH_DOWN_THRESH_MASK GENMASK(12, 8)
- #define CPR3_THRESH_DOWN_THRESH_SHIFT 8
- #define CPR3_THRESH_UP_THRESH_MASK GENMASK(17, 13)
- #define CPR3_THRESH_UP_THRESH_SHIFT 13
- #define CPR3_REG_RO_MASK(thread) (0x80C + 0x440 * (thread))
- #define CPR3_REG_RESULT0(thread) (0x810 + 0x440 * (thread))
- #define CPR3_RESULT0_BUSY_MASK BIT(0)
- #define CPR3_RESULT0_STEP_DN_MASK BIT(1)
- #define CPR3_RESULT0_STEP_UP_MASK BIT(2)
- #define CPR3_RESULT0_ERROR_STEPS_MASK GENMASK(7, 3)
- #define CPR3_RESULT0_ERROR_STEPS_SHIFT 3
- #define CPR3_RESULT0_ERROR_MASK GENMASK(19, 8)
- #define CPR3_RESULT0_ERROR_SHIFT 8
- #define CPR3_RESULT0_NEGATIVE_MASK BIT(20)
- #define CPR3_REG_RESULT1(thread) (0x814 + 0x440 * (thread))
- #define CPR3_RESULT1_QUOT_MIN_MASK GENMASK(11, 0)
- #define CPR3_RESULT1_QUOT_MIN_SHIFT 0
- #define CPR3_RESULT1_QUOT_MAX_MASK GENMASK(23, 12)
- #define CPR3_RESULT1_QUOT_MAX_SHIFT 12
- #define CPR3_RESULT1_RO_MIN_MASK GENMASK(27, 24)
- #define CPR3_RESULT1_RO_MIN_SHIFT 24
- #define CPR3_RESULT1_RO_MAX_MASK GENMASK(31, 28)
- #define CPR3_RESULT1_RO_MAX_SHIFT 28
- #define CPR3_REG_RESULT2(thread) (0x818 + 0x440 * (thread))
- #define CPR3_RESULT2_STEP_QUOT_MIN_MASK GENMASK(5, 0)
- #define CPR3_RESULT2_STEP_QUOT_MIN_SHIFT 0
- #define CPR3_RESULT2_STEP_QUOT_MAX_MASK GENMASK(11, 6)
- #define CPR3_RESULT2_STEP_QUOT_MAX_SHIFT 6
- #define CPR3_RESULT2_SENSOR_MIN_MASK GENMASK(23, 16)
- #define CPR3_RESULT2_SENSOR_MIN_SHIFT 16
- #define CPR3_RESULT2_SENSOR_MAX_MASK GENMASK(31, 24)
- #define CPR3_RESULT2_SENSOR_MAX_SHIFT 24
- #define CPR3_REG_IRQ_EN 0x81C
- #define CPR3_REG_IRQ_CLEAR 0x820
- #define CPR3_REG_IRQ_STATUS 0x824
- #define CPR3_IRQ_UP BIT(3)
- #define CPR3_IRQ_MID BIT(2)
- #define CPR3_IRQ_DOWN BIT(1)
- #define CPR3_REG_TARGET_QUOT(thread, ro) \
- (0x840 + 0x440 * (thread) + 0x4 * (ro))
- /* Registers found only on controllers that support HW closed-loop. */
- #define CPR3_REG_PD_THROTTLE 0xE8
- #define CPR3_PD_THROTTLE_DISABLE 0x0
- #define CPR3_REG_HW_CLOSED_LOOP 0x3000
- #define CPR3_HW_CLOSED_LOOP_ENABLE 0x0
- #define CPR3_HW_CLOSED_LOOP_DISABLE 0x1
- #define CPR3_REG_CPR_TIMER_MID_CONT 0x3004
- #define CPR3_REG_CPR_TIMER_UP_DN_CONT 0x3008
- #define CPR3_REG_LAST_MEASUREMENT 0x7F8
- #define CPR3_LAST_MEASUREMENT_THREAD_DN_SHIFT 0
- #define CPR3_LAST_MEASUREMENT_THREAD_UP_SHIFT 4
- #define CPR3_LAST_MEASUREMENT_THREAD_DN(thread) \
- (BIT(thread) << CPR3_LAST_MEASUREMENT_THREAD_DN_SHIFT)
- #define CPR3_LAST_MEASUREMENT_THREAD_UP(thread) \
- (BIT(thread) << CPR3_LAST_MEASUREMENT_THREAD_UP_SHIFT)
- #define CPR3_LAST_MEASUREMENT_AGGR_DN BIT(8)
- #define CPR3_LAST_MEASUREMENT_AGGR_MID BIT(9)
- #define CPR3_LAST_MEASUREMENT_AGGR_UP BIT(10)
- #define CPR3_LAST_MEASUREMENT_VALID BIT(11)
- #define CPR3_LAST_MEASUREMENT_SAW_ERROR BIT(12)
- #define CPR3_LAST_MEASUREMENT_PD_BYPASS_MASK GENMASK(23, 16)
- #define CPR3_LAST_MEASUREMENT_PD_BYPASS_SHIFT 16
- /* CPR4 controller specific registers and bit definitions */
- #define CPR4_REG_CPR_TIMER_CLAMP 0x10
- #define CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN BIT(27)
- #define CPR4_REG_MISC 0x700
- #define CPR4_MISC_RESET_STEP_QUOT_LOOP_EN BIT(2)
- #define CPR4_MISC_THREAD_HAS_ALWAYS_VOTE_EN BIT(3)
- #define CPR4_MISC_MARGIN_TABLE_ROW_SELECT_MASK GENMASK(23, 20)
- #define CPR4_MISC_MARGIN_TABLE_ROW_SELECT_SHIFT 20
- #define CPR4_MISC_TEMP_SENSOR_ID_START_MASK GENMASK(27, 24)
- #define CPR4_MISC_TEMP_SENSOR_ID_START_SHIFT 24
- #define CPR4_MISC_TEMP_SENSOR_ID_END_MASK GENMASK(31, 28)
- #define CPR4_MISC_TEMP_SENSOR_ID_END_SHIFT 28
- #define CPR4_REG_SAW_ERROR_STEP_LIMIT 0x7A4
- #define CPR4_SAW_ERROR_STEP_LIMIT_UP_MASK GENMASK(4, 0)
- #define CPR4_SAW_ERROR_STEP_LIMIT_UP_SHIFT 0
- #define CPR4_SAW_ERROR_STEP_LIMIT_DN_MASK GENMASK(9, 5)
- #define CPR4_SAW_ERROR_STEP_LIMIT_DN_SHIFT 5
- #define CPR4_REG_MARGIN_TEMP_CORE_TIMERS 0x7A8
- #define CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_MASK GENMASK(28, 18)
- #define CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_SHIFT 18
- #define CPR4_REG_MARGIN_TEMP_CORE(core) (0x7AC + 0x4 * (core))
- #define CPR4_MARGIN_TEMP_CORE_ADJ_MASK GENMASK(7, 0)
- #define CPR4_MARGIN_TEMP_CORE_ADJ_SHIFT 8
- #define CPR4_REG_MARGIN_TEMP_POINT0N1 0x7F0
- #define CPR4_MARGIN_TEMP_POINT0_MASK GENMASK(11, 0)
- #define CPR4_MARGIN_TEMP_POINT0_SHIFT 0
- #define CPR4_MARGIN_TEMP_POINT1_MASK GENMASK(23, 12)
- #define CPR4_MARGIN_TEMP_POINT1_SHIFT 12
- #define CPR4_REG_MARGIN_TEMP_POINT2 0x7F4
- #define CPR4_MARGIN_TEMP_POINT2_MASK GENMASK(11, 0)
- #define CPR4_MARGIN_TEMP_POINT2_SHIFT 0
- #define CPR4_REG_MARGIN_ADJ_CTL 0x7F8
- #define CPR4_MARGIN_ADJ_CTL_BOOST_EN BIT(0)
- #define CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN BIT(1)
- #define CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN BIT(2)
- #define CPR4_MARGIN_ADJ_CTL_TIMER_SETTLE_VOLTAGE_EN BIT(3)
- #define CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK BIT(4)
- #define CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE BIT(4)
- #define CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE 0
- #define CPR4_MARGIN_ADJ_CTL_PER_RO_KV_MARGIN_EN BIT(7)
- #define CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN BIT(8)
- #define CPR4_MARGIN_ADJ_CTL_PMIC_STEP_SIZE_MASK GENMASK(16, 12)
- #define CPR4_MARGIN_ADJ_CTL_PMIC_STEP_SIZE_SHIFT 12
- #define CPR4_MARGIN_ADJ_CTL_INITIAL_TEMP_BAND_MASK GENMASK(21, 19)
- #define CPR4_MARGIN_ADJ_CTL_INITIAL_TEMP_BAND_SHIFT 19
- #define CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_MASK GENMASK(25, 22)
- #define CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_SHIFT 22
- #define CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_STEP_QUOT_MASK GENMASK(31, 26)
- #define CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_STEP_QUOT_SHIFT 26
- #define CPR4_REG_CPR_MASK_THREAD(thread) (0x80C + 0x440 * (thread))
- #define CPR4_CPR_MASK_THREAD_DISABLE_THREAD BIT(31)
- #define CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK GENMASK(15, 0)
- /* CPRh controller specific registers and bit definitions */
- #define CPRH_REG_CORNER(thread, corner) \
- ((thread)->ctrl->cpr_hw_version >= CPRH_CPR_VERSION_4P5 \
- ? 0x3500 + 0xA0 * (thread)->thread_id + 0x4 * (corner) \
- : 0x3A00 + 0x4 * (corner))
- #define CPRH_CORNER_INIT_VOLTAGE_MASK GENMASK(7, 0)
- #define CPRH_CORNER_INIT_VOLTAGE_SHIFT 0
- #define CPRH_CORNER_FLOOR_VOLTAGE_MASK GENMASK(15, 8)
- #define CPRH_CORNER_FLOOR_VOLTAGE_SHIFT 8
- #define CPRH_CORNER_QUOT_DELTA_MASK GENMASK(24, 16)
- #define CPRH_CORNER_QUOT_DELTA_SHIFT 16
- #define CPRH_CORNER_RO_SEL_MASK GENMASK(28, 25)
- #define CPRH_CORNER_RO_SEL_SHIFT 25
- #define CPRH_CORNER_CPR_CL_DISABLE BIT(29)
- #define CPRH_CORNER_CORE_TEMP_MARGIN_DISABLE BIT(30)
- #define CPRH_CORNER_LAST_KNOWN_VOLTAGE_ENABLE BIT(31)
- #define CPRH_CORNER_INIT_VOLTAGE_MAX_VALUE 255
- #define CPRH_CORNER_FLOOR_VOLTAGE_MAX_VALUE 255
- #define CPRH_CORNER_QUOT_DELTA_MAX_VALUE 511
- #define CPRH_REG_CTL(ctrl) \
- ((ctrl)->cpr_hw_version >= CPRH_CPR_VERSION_4P5 ? 0x3A80 : 0x3AA0)
- #define CPRH_CTL_OSM_ENABLED BIT(0)
- #define CPRH_CTL_BASE_VOLTAGE_MASK GENMASK(10, 1)
- #define CPRH_CTL_BASE_VOLTAGE_SHIFT 1
- #define CPRH_CTL_INIT_MODE_MASK GENMASK(16, 11)
- #define CPRH_CTL_INIT_MODE_SHIFT 11
- #define CPRH_CTL_MODE_SWITCH_DELAY_MASK GENMASK(24, 17)
- #define CPRH_CTL_MODE_SWITCH_DELAY_SHIFT 17
- #define CPRH_CTL_VOLTAGE_MULTIPLIER_MASK GENMASK(28, 25)
- #define CPRH_CTL_VOLTAGE_MULTIPLIER_SHIFT 25
- #define CPRH_CTL_LAST_KNOWN_VOLTAGE_MARGIN_MASK GENMASK(31, 29)
- #define CPRH_CTL_LAST_KNOWN_VOLTAGE_MARGIN_SHIFT 29
- #define CPRH_REG_STATUS(thread) \
- ((thread)->ctrl->cpr_hw_version >= CPRH_CPR_VERSION_4P5 \
- ? 0x3A84 + 0x4 * (thread)->thread_id : 0x3AA4)
- #define CPRH_STATUS_CORNER GENMASK(5, 0)
- #define CPRH_STATUS_CORNER_LAST_VOLT_MASK GENMASK(17, 6)
- #define CPRH_STATUS_CORNER_LAST_VOLT_SHIFT 6
- #define CPRH_REG_CORNER_BAND 0x3AA8
- #define CPRH_CORNER_BAND_MASK GENMASK(5, 0)
- #define CPRH_CORNER_BAND_SHIFT 6
- #define CPRH_CORNER_BAND_MAX_COUNT 4
- #define CPRH_MARGIN_TEMP_CORE_VBAND(core, vband) \
- ((vband) == 0 ? CPR4_REG_MARGIN_TEMP_CORE(core) \
- : 0x3AB0 + 0x40 * ((vband) - 1) + 0x4 * (core))
- #define CPRH_REG_MISC_REG2 0x3AAC
- #define CPRH_MISC_REG2_ACD_ADJ_STEP_UP_LIMIT_MASK GENMASK(31, 29)
- #define CPRH_MISC_REG2_ACD_ADJ_STEP_UP_LIMIT_SHIFT 29
- #define CPRH_MISC_REG2_ACD_ADJ_STEP_DOWN_LIMIT_MASK GENMASK(28, 24)
- #define CPRH_MISC_REG2_ACD_ADJ_STEP_DOWN_LIMIT_SHIFT 24
- #define CPRH_MISC_REG2_ACD_ADJ_STEP_SIZE_UP_MASK GENMASK(23, 22)
- #define CPRH_MISC_REG2_ACD_ADJ_STEP_SIZE_UP_SHIFT 22
- #define CPRH_MISC_REG2_ACD_ADJ_STEP_SIZE_DOWN_MASK GENMASK(21, 20)
- #define CPRH_MISC_REG2_ACD_ADJ_STEP_SIZE_DOWN_SHIFT 20
- #define CPRH_MISC_REG2_ACD_NOTWAIT_4_CL_SETTLE_MASK BIT(16)
- #define CPRH_MISC_REG2_ACD_NOTWAIT_4_CL_SETTLE_EN BIT(16)
- #define CPRH_MISC_REG2_ACD_AVG_FAST_UPDATE_EN_MASK BIT(13)
- #define CPRH_MISC_REG2_ACD_AVG_FAST_UPDATE_EN BIT(13)
- #define CPRH_MISC_REG2_ACD_AVG_EN_MASK BIT(12)
- #define CPRH_MISC_REG2_ACD_AVG_ENABLE BIT(12)
- /* SAW module registers */
- #define SAW_REG_AVS_CTL 0x904
- #define SAW_REG_AVS_LIMIT 0x908
- /*
- * The amount of time to wait for the CPR controller to become idle when
- * performing an aging measurement.
- */
- #define CPR3_AGING_MEASUREMENT_TIMEOUT_NS 5000000
- /*
- * The number of individual aging measurements to perform which are then
- * averaged together in order to determine the final aging adjustment value.
- */
- #define CPR3_AGING_MEASUREMENT_ITERATIONS 16
- /*
- * Aging measurements for the aged and unaged ring oscillators take place a few
- * microseconds apart. If the vdd-supply voltage fluctuates between the two
- * measurements, then the difference between them will be incorrect. The
- * difference could end up too high or too low. This constant defines the
- * number of lowest and highest measurements to ignore when averaging.
- */
- #define CPR3_AGING_MEASUREMENT_FILTER 3
- /*
- * The number of times to attempt the full aging measurement sequence before
- * declaring a measurement failure.
- */
- #define CPR3_AGING_RETRY_COUNT 5
- /*
- * The maximum time to wait in microseconds for a CPR register write to
- * complete.
- */
- #define CPR3_REGISTER_WRITE_DELAY_US 200
- /*
- * The number of times the CPRh controller multiplies the mode switch
- * delay before utilizing it.
- */
- #define CPRH_MODE_SWITCH_DELAY_FACTOR 4
- /*
- * The number of times the CPRh controller multiplies the delta quotient
- * steps before utilizing it.
- */
- #define CPRH_DELTA_QUOT_STEP_FACTOR 4
- static DEFINE_MUTEX(cpr3_controller_list_mutex);
- static LIST_HEAD(cpr3_controller_list);
- static struct dentry *cpr3_debugfs_base;
- /**
- * cpr3_read() - read four bytes from the memory address specified
- * @ctrl: Pointer to the CPR3 controller
- * @offset: Offset in bytes from the CPR3 controller's base address
- *
- * Return: memory address value
- */
- static inline u32 cpr3_read(struct cpr3_controller *ctrl, u32 offset)
- {
- if (!ctrl->cpr_enabled) {
- cpr3_err(ctrl, "CPR register reads are not possible when CPR clocks are disabled\n");
- return 0;
- }
- return readl_relaxed(ctrl->cpr_ctrl_base + offset);
- }
- /**
- * cpr3_write() - write four bytes to the memory address specified
- * @ctrl: Pointer to the CPR3 controller
- * @offset: Offset in bytes from the CPR3 controller's base address
- * @value: Value to write to the memory address
- *
- * Return: none
- */
- static inline void cpr3_write(struct cpr3_controller *ctrl, u32 offset,
- u32 value)
- {
- if (!ctrl->cpr_enabled) {
- cpr3_err(ctrl, "CPR register writes are not possible when CPR clocks are disabled\n");
- return;
- }
- writel_relaxed(value, ctrl->cpr_ctrl_base + offset);
- }
- /**
- * cpr3_masked_write() - perform a read-modify-write sequence so that only
- * masked bits are modified
- * @ctrl: Pointer to the CPR3 controller
- * @offset: Offset in bytes from the CPR3 controller's base address
- * @mask: Mask identifying the bits that should be modified
- * @value: Value to write to the memory address
- *
- * Return: none
- */
- static inline void cpr3_masked_write(struct cpr3_controller *ctrl, u32 offset,
- u32 mask, u32 value)
- {
- u32 reg_val, orig_val;
- if (!ctrl->cpr_enabled) {
- cpr3_err(ctrl, "CPR register writes are not possible when CPR clocks are disabled\n");
- return;
- }
- reg_val = orig_val = readl_relaxed(ctrl->cpr_ctrl_base + offset);
- reg_val &= ~mask;
- reg_val |= value & mask;
- if (reg_val != orig_val)
- writel_relaxed(reg_val, ctrl->cpr_ctrl_base + offset);
- }
- /**
- * cpr3_ctrl_loop_enable() - enable the CPR sensing loop for a given controller
- * @ctrl: Pointer to the CPR3 controller
- *
- * Return: none
- */
- static inline void cpr3_ctrl_loop_enable(struct cpr3_controller *ctrl)
- {
- if (ctrl->cpr_enabled && !(ctrl->aggr_corner.sdelta
- && ctrl->aggr_corner.sdelta->allow_boost))
- cpr3_masked_write(ctrl, CPR3_REG_CPR_CTL,
- CPR3_CPR_CTL_LOOP_EN_MASK, CPR3_CPR_CTL_LOOP_ENABLE);
- }
- /**
- * cpr3_ctrl_loop_disable() - disable the CPR sensing loop for a given
- * controller
- * @ctrl: Pointer to the CPR3 controller
- *
- * Return: none
- */
- static inline void cpr3_ctrl_loop_disable(struct cpr3_controller *ctrl)
- {
- if (ctrl->cpr_enabled)
- cpr3_masked_write(ctrl, CPR3_REG_CPR_CTL,
- CPR3_CPR_CTL_LOOP_EN_MASK, CPR3_CPR_CTL_LOOP_DISABLE);
- }
- /**
- * cpr3_clock_enable() - prepare and enable all clocks used by this CPR3
- * controller
- * @ctrl: Pointer to the CPR3 controller
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_clock_enable(struct cpr3_controller *ctrl)
- {
- int rc;
- rc = clk_prepare_enable(ctrl->bus_clk);
- if (rc) {
- cpr3_err(ctrl, "failed to enable bus clock, rc=%d\n", rc);
- return rc;
- }
- rc = clk_prepare_enable(ctrl->iface_clk);
- if (rc) {
- cpr3_err(ctrl, "failed to enable interface clock, rc=%d\n", rc);
- clk_disable_unprepare(ctrl->bus_clk);
- return rc;
- }
- rc = clk_prepare_enable(ctrl->core_clk);
- if (rc) {
- cpr3_err(ctrl, "failed to enable core clock, rc=%d\n", rc);
- clk_disable_unprepare(ctrl->iface_clk);
- clk_disable_unprepare(ctrl->bus_clk);
- return rc;
- }
- return 0;
- }
- /**
- * cpr3_clock_disable() - disable and unprepare all clocks used by this CPR3
- * controller
- * @ctrl: Pointer to the CPR3 controller
- *
- * Return: none
- */
- static void cpr3_clock_disable(struct cpr3_controller *ctrl)
- {
- clk_disable_unprepare(ctrl->core_clk);
- clk_disable_unprepare(ctrl->iface_clk);
- clk_disable_unprepare(ctrl->bus_clk);
- }
- /**
- * cpr3_ctrl_clear_cpr4_config() - clear the CPR4 register configuration
- * programmed for current aggregated corner of a given controller
- * @ctrl: Pointer to the CPR3 controller
- *
- * Return: 0 on success, errno on failure
- */
- static inline int cpr3_ctrl_clear_cpr4_config(struct cpr3_controller *ctrl)
- {
- struct cpr4_sdelta *aggr_sdelta = ctrl->aggr_corner.sdelta;
- bool cpr_enabled = ctrl->cpr_enabled;
- int i, rc = 0;
- if (!aggr_sdelta || !(aggr_sdelta->allow_core_count_adj
- || aggr_sdelta->allow_temp_adj || aggr_sdelta->allow_boost))
- /* cpr4 features are not enabled */
- return 0;
- /* Ensure that CPR clocks are enabled before writing to registers. */
- if (!cpr_enabled) {
- rc = cpr3_clock_enable(ctrl);
- if (rc) {
- cpr3_err(ctrl, "clock enable failed, rc=%d\n", rc);
- return rc;
- }
- ctrl->cpr_enabled = true;
- }
- /*
- * Clear feature enable configuration made for current
- * aggregated corner.
- */
- cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
- CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_MASK
- | CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN
- | CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN
- | CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN
- | CPR4_MARGIN_ADJ_CTL_BOOST_EN
- | CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK, 0);
- cpr3_masked_write(ctrl, CPR4_REG_MISC,
- CPR4_MISC_MARGIN_TABLE_ROW_SELECT_MASK,
- 0 << CPR4_MISC_MARGIN_TABLE_ROW_SELECT_SHIFT);
- for (i = 0; i <= aggr_sdelta->max_core_count; i++) {
- /* Clear voltage margin adjustments programmed in TEMP_COREi */
- cpr3_write(ctrl, CPR4_REG_MARGIN_TEMP_CORE(i), 0);
- }
- /* Turn off CPR clocks if they were off before this function call. */
- if (!cpr_enabled) {
- cpr3_clock_disable(ctrl);
- ctrl->cpr_enabled = false;
- }
- return 0;
- }
- /**
- * cpr3_closed_loop_enable() - enable logical CPR closed-loop operation
- * @ctrl: Pointer to the CPR3 controller
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_closed_loop_enable(struct cpr3_controller *ctrl)
- {
- int rc;
- if (!ctrl->cpr_allowed_hw || !ctrl->cpr_allowed_sw) {
- cpr3_err(ctrl, "cannot enable closed-loop CPR operation because it is disallowed\n");
- return -EPERM;
- } else if (ctrl->cpr_enabled) {
- /* Already enabled */
- return 0;
- } else if (ctrl->cpr_suspended) {
- /*
- * CPR must remain disabled as the system is entering suspend.
- */
- return 0;
- }
- rc = cpr3_clock_enable(ctrl);
- if (rc) {
- cpr3_err(ctrl, "unable to enable CPR clocks, rc=%d\n", rc);
- return rc;
- }
- ctrl->cpr_enabled = true;
- cpr3_debug(ctrl, "CPR closed-loop operation enabled\n");
- return 0;
- }
- /**
- * cpr3_closed_loop_disable() - disable logical CPR closed-loop operation
- * @ctrl: Pointer to the CPR3 controller
- *
- * Return: 0 on success, errno on failure
- */
- static inline int cpr3_closed_loop_disable(struct cpr3_controller *ctrl)
- {
- if (!ctrl->cpr_enabled) {
- /* Already disabled */
- return 0;
- }
- cpr3_clock_disable(ctrl);
- ctrl->cpr_enabled = false;
- cpr3_debug(ctrl, "CPR closed-loop operation disabled\n");
- return 0;
- }
- /**
- * cpr3_regulator_get_gcnt() - returns the GCNT register value corresponding
- * to the clock rate and sensor time of the CPR3 controller
- * @ctrl: Pointer to the CPR3 controller
- *
- * Return: GCNT value
- */
- static u32 cpr3_regulator_get_gcnt(struct cpr3_controller *ctrl)
- {
- u64 temp;
- unsigned int remainder;
- u32 gcnt;
- temp = (u64)ctrl->cpr_clock_rate * (u64)ctrl->sensor_time;
- remainder = do_div(temp, 1000000000);
- if (remainder)
- temp++;
- /*
- * GCNT == 0 corresponds to a single ref clock measurement interval so
- * offset GCNT values by 1.
- */
- gcnt = temp - 1;
- return gcnt;
- }
- /**
- * cpr3_regulator_init_thread() - performs hardware initialization of CPR
- * thread registers
- * @thread: Pointer to the CPR3 thread
- *
- * CPR interface/bus clocks must be enabled before calling this function.
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_regulator_init_thread(struct cpr3_thread *thread)
- {
- u32 reg;
- reg = (thread->consecutive_up << CPR3_THRESH_CONS_UP_SHIFT)
- & CPR3_THRESH_CONS_UP_MASK;
- reg |= (thread->consecutive_down << CPR3_THRESH_CONS_DOWN_SHIFT)
- & CPR3_THRESH_CONS_DOWN_MASK;
- reg |= (thread->up_threshold << CPR3_THRESH_UP_THRESH_SHIFT)
- & CPR3_THRESH_UP_THRESH_MASK;
- reg |= (thread->down_threshold << CPR3_THRESH_DOWN_THRESH_SHIFT)
- & CPR3_THRESH_DOWN_THRESH_MASK;
- cpr3_write(thread->ctrl, CPR3_REG_THRESH(thread->thread_id), reg);
- /*
- * Mask all RO's initially so that unused thread doesn't contribute
- * to closed-loop voltage.
- */
- cpr3_write(thread->ctrl, CPR3_REG_RO_MASK(thread->thread_id),
- CPR3_RO_MASK);
- return 0;
- }
- /**
- * cpr4_regulator_init_temp_points() - performs hardware initialization of CPR4
- * registers to track tsen temperature data and also specify the
- * temperature band range values to apply different voltage margins
- * @ctrl: Pointer to the CPR3 controller
- *
- * CPR interface/bus clocks must be enabled before calling this function.
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr4_regulator_init_temp_points(struct cpr3_controller *ctrl)
- {
- if (!ctrl->allow_temp_adj)
- return 0;
- cpr3_masked_write(ctrl, CPR4_REG_MISC,
- CPR4_MISC_TEMP_SENSOR_ID_START_MASK,
- ctrl->temp_sensor_id_start
- << CPR4_MISC_TEMP_SENSOR_ID_START_SHIFT);
- cpr3_masked_write(ctrl, CPR4_REG_MISC,
- CPR4_MISC_TEMP_SENSOR_ID_END_MASK,
- ctrl->temp_sensor_id_end
- << CPR4_MISC_TEMP_SENSOR_ID_END_SHIFT);
- cpr3_masked_write(ctrl, CPR4_REG_MARGIN_TEMP_POINT2,
- CPR4_MARGIN_TEMP_POINT2_MASK,
- (ctrl->temp_band_count == 4 ? ctrl->temp_points[2] : 0x7FF)
- << CPR4_MARGIN_TEMP_POINT2_SHIFT);
- cpr3_masked_write(ctrl, CPR4_REG_MARGIN_TEMP_POINT0N1,
- CPR4_MARGIN_TEMP_POINT1_MASK,
- (ctrl->temp_band_count >= 3 ? ctrl->temp_points[1] : 0x7FF)
- << CPR4_MARGIN_TEMP_POINT1_SHIFT);
- cpr3_masked_write(ctrl, CPR4_REG_MARGIN_TEMP_POINT0N1,
- CPR4_MARGIN_TEMP_POINT0_MASK,
- (ctrl->temp_band_count >= 2 ? ctrl->temp_points[0] : 0x7FF)
- << CPR4_MARGIN_TEMP_POINT0_SHIFT);
- return 0;
- }
- /**
- * cpr3_regulator_init_cpr4() - performs hardware initialization at the
- * controller and thread level required for CPR4 operation.
- * @ctrl: Pointer to the CPR3 controller
- *
- * CPR interface/bus clocks must be enabled before calling this function.
- * This function allocates sdelta structures and sdelta tables for aggregated
- * corners of the controller and its threads.
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_regulator_init_cpr4(struct cpr3_controller *ctrl)
- {
- struct cpr3_thread *thread;
- struct cpr3_regulator *vreg;
- struct cpr4_sdelta *sdelta;
- int i, j, ctrl_max_core_count, thread_max_core_count, rc = 0;
- bool ctrl_valid_sdelta, thread_valid_sdelta;
- u32 pmic_step_size = 1;
- int thread_id = 0;
- u64 temp;
- if (ctrl->reset_step_quot_loop_en)
- cpr3_masked_write(ctrl, CPR4_REG_MISC,
- CPR4_MISC_RESET_STEP_QUOT_LOOP_EN,
- CPR4_MISC_RESET_STEP_QUOT_LOOP_EN);
- if (ctrl->thread_has_always_vote_en)
- cpr3_masked_write(ctrl, CPR4_REG_MISC,
- CPR4_MISC_THREAD_HAS_ALWAYS_VOTE_EN,
- CPR4_MISC_THREAD_HAS_ALWAYS_VOTE_EN);
- if (ctrl->supports_hw_closed_loop) {
- if (ctrl->saw_use_unit_mV)
- pmic_step_size = ctrl->step_volt / 1000;
- cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
- CPR4_MARGIN_ADJ_CTL_PMIC_STEP_SIZE_MASK,
- (pmic_step_size
- << CPR4_MARGIN_ADJ_CTL_PMIC_STEP_SIZE_SHIFT));
- cpr3_masked_write(ctrl, CPR4_REG_SAW_ERROR_STEP_LIMIT,
- CPR4_SAW_ERROR_STEP_LIMIT_DN_MASK,
- (ctrl->down_error_step_limit
- << CPR4_SAW_ERROR_STEP_LIMIT_DN_SHIFT));
- cpr3_masked_write(ctrl, CPR4_REG_SAW_ERROR_STEP_LIMIT,
- CPR4_SAW_ERROR_STEP_LIMIT_UP_MASK,
- (ctrl->up_error_step_limit
- << CPR4_SAW_ERROR_STEP_LIMIT_UP_SHIFT));
- /*
- * Enable thread aggregation regardless of which threads are
- * enabled or disabled.
- */
- cpr3_masked_write(ctrl, CPR4_REG_CPR_TIMER_CLAMP,
- CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN,
- CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN);
- switch (ctrl->thread_count) {
- case 0:
- /* Disable both threads */
- cpr3_masked_write(ctrl, CPR4_REG_CPR_MASK_THREAD(0),
- CPR4_CPR_MASK_THREAD_DISABLE_THREAD
- | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK,
- CPR4_CPR_MASK_THREAD_DISABLE_THREAD
- | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK);
- cpr3_masked_write(ctrl, CPR4_REG_CPR_MASK_THREAD(1),
- CPR4_CPR_MASK_THREAD_DISABLE_THREAD
- | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK,
- CPR4_CPR_MASK_THREAD_DISABLE_THREAD
- | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK);
- break;
- case 1:
- /* Disable unused thread */
- thread_id = ctrl->thread[0].thread_id ? 0 : 1;
- cpr3_masked_write(ctrl,
- CPR4_REG_CPR_MASK_THREAD(thread_id),
- CPR4_CPR_MASK_THREAD_DISABLE_THREAD
- | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK,
- CPR4_CPR_MASK_THREAD_DISABLE_THREAD
- | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK);
- break;
- }
- }
- if (!ctrl->allow_core_count_adj && !ctrl->allow_temp_adj
- && !ctrl->allow_boost) {
- /*
- * Skip below configuration as none of the features
- * are enabled.
- */
- return rc;
- }
- if (ctrl->supports_hw_closed_loop)
- cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
- CPR4_MARGIN_ADJ_CTL_TIMER_SETTLE_VOLTAGE_EN,
- CPR4_MARGIN_ADJ_CTL_TIMER_SETTLE_VOLTAGE_EN);
- cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
- CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_STEP_QUOT_MASK,
- ctrl->step_quot_fixed
- << CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_STEP_QUOT_SHIFT);
- cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
- CPR4_MARGIN_ADJ_CTL_PER_RO_KV_MARGIN_EN,
- (ctrl->use_dynamic_step_quot
- ? CPR4_MARGIN_ADJ_CTL_PER_RO_KV_MARGIN_EN : 0));
- cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
- CPR4_MARGIN_ADJ_CTL_INITIAL_TEMP_BAND_MASK,
- ctrl->initial_temp_band
- << CPR4_MARGIN_ADJ_CTL_INITIAL_TEMP_BAND_SHIFT);
- rc = cpr4_regulator_init_temp_points(ctrl);
- if (rc) {
- cpr3_err(ctrl, "initialize temp points failed, rc=%d\n", rc);
- return rc;
- }
- if (ctrl->voltage_settling_time) {
- /*
- * Configure the settling timer used to account for
- * one VDD supply step.
- */
- temp = (u64)ctrl->cpr_clock_rate
- * (u64)ctrl->voltage_settling_time;
- do_div(temp, 1000000000);
- cpr3_masked_write(ctrl, CPR4_REG_MARGIN_TEMP_CORE_TIMERS,
- CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_MASK,
- temp
- << CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_SHIFT);
- }
- /*
- * Allocate memory for cpr4_sdelta structure and sdelta table for
- * controller aggregated corner by finding the maximum core count
- * used by any cpr3 regulators.
- */
- ctrl_max_core_count = 1;
- ctrl_valid_sdelta = false;
- for (i = 0; i < ctrl->thread_count; i++) {
- thread = &ctrl->thread[i];
- /*
- * Allocate memory for cpr4_sdelta structure and sdelta table
- * for thread aggregated corner by finding the maximum core
- * count used by any cpr3 regulators of the thread.
- */
- thread_max_core_count = 1;
- thread_valid_sdelta = false;
- for (j = 0; j < thread->vreg_count; j++) {
- vreg = &thread->vreg[j];
- thread_max_core_count = max(thread_max_core_count,
- vreg->max_core_count);
- thread_valid_sdelta |= (vreg->allow_core_count_adj
- | vreg->allow_temp_adj
- | vreg->allow_boost);
- }
- if (thread_valid_sdelta) {
- sdelta = devm_kzalloc(ctrl->dev, sizeof(*sdelta),
- GFP_KERNEL);
- if (!sdelta)
- return -ENOMEM;
- sdelta->table = devm_kcalloc(ctrl->dev,
- thread_max_core_count
- * ctrl->temp_band_count,
- sizeof(*sdelta->table),
- GFP_KERNEL);
- if (!sdelta->table)
- return -ENOMEM;
- sdelta->boost_table = devm_kcalloc(ctrl->dev,
- ctrl->temp_band_count,
- sizeof(*sdelta->boost_table),
- GFP_KERNEL);
- if (!sdelta->boost_table)
- return -ENOMEM;
- thread->aggr_corner.sdelta = sdelta;
- }
- ctrl_valid_sdelta |= thread_valid_sdelta;
- ctrl_max_core_count = max(ctrl_max_core_count,
- thread_max_core_count);
- }
- if (ctrl_valid_sdelta) {
- sdelta = devm_kzalloc(ctrl->dev, sizeof(*sdelta), GFP_KERNEL);
- if (!sdelta)
- return -ENOMEM;
- sdelta->table = devm_kcalloc(ctrl->dev, ctrl_max_core_count
- * ctrl->temp_band_count,
- sizeof(*sdelta->table), GFP_KERNEL);
- if (!sdelta->table)
- return -ENOMEM;
- sdelta->boost_table = devm_kcalloc(ctrl->dev,
- ctrl->temp_band_count,
- sizeof(*sdelta->boost_table),
- GFP_KERNEL);
- if (!sdelta->boost_table)
- return -ENOMEM;
- ctrl->aggr_corner.sdelta = sdelta;
- }
- return 0;
- }
- /**
- * cpr3_write_temp_core_margin() - programs hardware SDELTA registers with
- * the voltage margin adjustments that need to be applied for
- * different online core-count and temperature bands.
- * @ctrl: Pointer to the CPR3 controller
- * @addr: SDELTA register address
- * @temp_core_adj: Array of voltage margin values for different temperature
- * bands.
- *
- * CPR interface/bus clocks must be enabled before calling this function.
- *
- * Return: none
- */
- static void cpr3_write_temp_core_margin(struct cpr3_controller *ctrl,
- int addr, int *temp_core_adj)
- {
- int i, margin_steps;
- u32 reg = 0;
- for (i = 0; i < ctrl->temp_band_count; i++) {
- margin_steps = max(min(temp_core_adj[i], 127), -128);
- reg |= (margin_steps & CPR4_MARGIN_TEMP_CORE_ADJ_MASK) <<
- (i * CPR4_MARGIN_TEMP_CORE_ADJ_SHIFT);
- }
- cpr3_write(ctrl, addr, reg);
- cpr3_debug(ctrl, "sdelta offset=0x%08x, val=0x%08x\n", addr, reg);
- }
- /**
- * cpr3_controller_program_sdelta() - programs hardware SDELTA registers with
- * the voltage margin adjustments that need to be applied at
- * different online core-count and temperature bands. Also,
- * programs hardware register configuration for per-online-core
- * and per-temperature based adjustments.
- * @ctrl: Pointer to the CPR3 controller
- *
- * CPR interface/bus clocks must be enabled before calling this function.
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_controller_program_sdelta(struct cpr3_controller *ctrl)
- {
- struct cpr3_corner *corner = &ctrl->aggr_corner;
- struct cpr4_sdelta *sdelta = corner->sdelta;
- int i, index, max_core_count, rc = 0;
- bool cpr_enabled = ctrl->cpr_enabled;
- if (!sdelta)
- /* cpr4_sdelta not defined for current aggregated corner */
- return 0;
- if (ctrl->supports_hw_closed_loop && ctrl->cpr_enabled) {
- cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
- CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK,
- (ctrl->use_hw_closed_loop && !sdelta->allow_boost)
- ? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE : 0);
- }
- if (!sdelta->allow_core_count_adj && !sdelta->allow_temp_adj
- && !sdelta->allow_boost) {
- /*
- * Per-online-core, per-temperature and voltage boost
- * adjustments are disabled for this aggregation corner.
- */
- return 0;
- }
- /* Ensure that CPR clocks are enabled before writing to registers. */
- if (!cpr_enabled) {
- rc = cpr3_clock_enable(ctrl);
- if (rc) {
- cpr3_err(ctrl, "clock enable failed, rc=%d\n", rc);
- return rc;
- }
- ctrl->cpr_enabled = true;
- }
- max_core_count = sdelta->max_core_count;
- if (sdelta->allow_core_count_adj || sdelta->allow_temp_adj) {
- if (sdelta->allow_core_count_adj) {
- /* Program TEMP_CORE0 to same margins as TEMP_CORE1 */
- cpr3_write_temp_core_margin(ctrl,
- CPR4_REG_MARGIN_TEMP_CORE(0),
- &sdelta->table[0]);
- }
- for (i = 0; i < max_core_count; i++) {
- index = i * sdelta->temp_band_count;
- /*
- * Program TEMP_COREi with voltage margin adjustments
- * that need to be applied when the number of cores
- * becomes i.
- */
- cpr3_write_temp_core_margin(ctrl,
- CPR4_REG_MARGIN_TEMP_CORE(
- sdelta->allow_core_count_adj
- ? i + 1 : max_core_count),
- &sdelta->table[index]);
- }
- }
- if (sdelta->allow_boost) {
- /* Program only boost_num_cores row of SDELTA */
- cpr3_write_temp_core_margin(ctrl,
- CPR4_REG_MARGIN_TEMP_CORE(sdelta->boost_num_cores),
- &sdelta->boost_table[0]);
- }
- if (!sdelta->allow_core_count_adj && !sdelta->allow_boost) {
- cpr3_masked_write(ctrl, CPR4_REG_MISC,
- CPR4_MISC_MARGIN_TABLE_ROW_SELECT_MASK,
- max_core_count
- << CPR4_MISC_MARGIN_TABLE_ROW_SELECT_SHIFT);
- }
- cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
- CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_MASK
- | CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN
- | CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN
- | CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN
- | CPR4_MARGIN_ADJ_CTL_BOOST_EN,
- max_core_count << CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_SHIFT
- | ((sdelta->allow_core_count_adj || sdelta->allow_boost)
- ? CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN : 0)
- | ((sdelta->allow_temp_adj && ctrl->supports_hw_closed_loop)
- ? CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN : 0)
- | (((ctrl->use_hw_closed_loop && !sdelta->allow_boost)
- || !ctrl->supports_hw_closed_loop)
- ? CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN : 0)
- | (sdelta->allow_boost
- ? CPR4_MARGIN_ADJ_CTL_BOOST_EN : 0));
- /*
- * Ensure that all previous CPR register writes have completed before
- * continuing.
- */
- mb();
- /* Turn off CPR clocks if they were off before this function call. */
- if (!cpr_enabled) {
- cpr3_clock_disable(ctrl);
- ctrl->cpr_enabled = false;
- }
- return 0;
- }
- /**
- * cpr3_regulator_set_base_target_quot() - configure the target quotient
- * for each RO of the CPR3 regulator for CPRh operation.
- * In particular, the quotient of the RO selected for operation
- * should correspond to the lowest target quotient across the
- * corners supported by the single regulator of the CPR3 thread.
- * @vreg: Pointer to the CPR3 regulator
- * @base_quots: Pointer to the base quotient array. The array must be
- * of size CPR3_RO_COUNT and it is populated with the
- * base quotient per-RO.
- *
- * Return: none
- */
- static void cpr3_regulator_set_base_target_quot(struct cpr3_regulator *vreg,
- u32 *base_quots)
- {
- struct cpr3_controller *ctrl = vreg->thread->ctrl;
- int i, j, ro_mask = CPR3_RO_MASK;
- u32 min_quot;
- for (i = 0; i < vreg->corner_count; i++)
- ro_mask &= vreg->corner[i].ro_mask;
- /* Unmask the ROs selected for active use. */
- cpr3_write(ctrl, CPR3_REG_RO_MASK(vreg->thread->thread_id),
- ro_mask);
- for (i = 0; i < CPR3_RO_COUNT; i++) {
- for (j = 0, min_quot = INT_MAX; j < vreg->corner_count; j++)
- if (vreg->corner[j].target_quot[i])
- min_quot = min(min_quot,
- vreg->corner[j].target_quot[i]);
- if (min_quot == INT_MAX)
- min_quot = 0;
- cpr3_write(ctrl,
- CPR3_REG_TARGET_QUOT(vreg->thread->thread_id, i),
- min_quot);
- base_quots[i] = min_quot;
- }
- }
- /**
- * cpr3_regulator_init_cprh_corners() - configure the per-corner CPRh registers
- * @vreg: Pointer to the CPR3 regulator
- *
- * This function programs the controller registers which contain all information
- * necessary to resolve the closed-loop voltage per-corner at runtime such as
- * open-loop and floor voltages, target quotient delta, and RO select value.
- * These registers also provide a means to disable closed-loop operation, core
- * and temperature adjustments.
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_regulator_init_cprh_corners(struct cpr3_regulator *vreg)
- {
- struct cpr3_controller *ctrl = vreg->thread->ctrl;
- struct cpr3_corner *corner;
- u32 reg, delta_quot_steps, ro_sel;
- u32 *base_quots;
- int open_loop_volt_steps, floor_volt_steps, i, j, rc = 0;
- base_quots = kcalloc(CPR3_RO_COUNT, sizeof(*base_quots),
- GFP_KERNEL);
- if (!base_quots)
- return -ENOMEM;
- cpr3_regulator_set_base_target_quot(vreg, base_quots);
- for (i = 0; i < vreg->corner_count; i++) {
- corner = &vreg->corner[i];
- for (j = 0, ro_sel = INT_MAX; j < CPR3_RO_COUNT; j++) {
- if (corner->target_quot[j]) {
- ro_sel = j;
- break;
- }
- }
- if (ro_sel == INT_MAX) {
- if (!corner->proc_freq) {
- /*
- * Corner is not used as active DCVS set point
- * select RO 0 arbitrarily.
- */
- ro_sel = 0;
- } else if (ctrl->ignore_invalid_fuses) {
- /*
- * Fuses are not initialized on some simulator
- * targets. Select RO 0 arbitrarily.
- */
- cpr3_debug(vreg, "ignored that corner=%d has invalid RO select value\n",
- i);
- ro_sel = 0;
- } else {
- cpr3_err(vreg, "corner=%d has invalid RO select value\n",
- i);
- rc = -EINVAL;
- goto free_base_quots;
- }
- }
- open_loop_volt_steps = DIV_ROUND_UP(corner->open_loop_volt -
- ctrl->base_volt,
- ctrl->step_volt);
- floor_volt_steps = DIV_ROUND_UP(corner->floor_volt -
- ctrl->base_volt,
- ctrl->step_volt);
- delta_quot_steps = corner->proc_freq ?
- DIV_ROUND_UP(corner->target_quot[ro_sel] -
- base_quots[ro_sel],
- CPRH_DELTA_QUOT_STEP_FACTOR) :
- 0;
- if (open_loop_volt_steps > CPRH_CORNER_INIT_VOLTAGE_MAX_VALUE ||
- floor_volt_steps > CPRH_CORNER_FLOOR_VOLTAGE_MAX_VALUE ||
- delta_quot_steps > CPRH_CORNER_QUOT_DELTA_MAX_VALUE) {
- cpr3_err(ctrl, "invalid CPRh corner configuration: open_loop_volt_steps=%d (%d max.), floor_volt_steps=%d (%d max), delta_quot_steps=%d (%d max)\n",
- open_loop_volt_steps,
- CPRH_CORNER_INIT_VOLTAGE_MAX_VALUE,
- floor_volt_steps,
- CPRH_CORNER_FLOOR_VOLTAGE_MAX_VALUE,
- delta_quot_steps,
- CPRH_CORNER_QUOT_DELTA_MAX_VALUE);
- rc = -EINVAL;
- goto free_base_quots;
- }
- reg = (open_loop_volt_steps << CPRH_CORNER_INIT_VOLTAGE_SHIFT)
- & CPRH_CORNER_INIT_VOLTAGE_MASK;
- reg |= (floor_volt_steps << CPRH_CORNER_FLOOR_VOLTAGE_SHIFT)
- & CPRH_CORNER_FLOOR_VOLTAGE_MASK;
- reg |= (delta_quot_steps << CPRH_CORNER_QUOT_DELTA_SHIFT)
- & CPRH_CORNER_QUOT_DELTA_MASK;
- reg |= (ro_sel << CPRH_CORNER_RO_SEL_SHIFT)
- & CPRH_CORNER_RO_SEL_MASK;
- if (corner->use_open_loop)
- reg |= CPRH_CORNER_CPR_CL_DISABLE;
- cpr3_debug(ctrl, "corner=%d open_loop_volt_steps=%d, floor_volt_steps=%d, delta_quot_steps=%d, base_volt=%d, step_volt=%d, base_quot=%d\n",
- i, open_loop_volt_steps, floor_volt_steps,
- delta_quot_steps, ctrl->base_volt,
- ctrl->step_volt, base_quots[ro_sel]);
- cpr3_write(ctrl, CPRH_REG_CORNER(vreg->thread, i), reg);
- }
- free_base_quots:
- kfree(base_quots);
- return rc;
- }
- /**
- * cprh_controller_program_sdelta() - programs hardware SDELTA registers with
- * the margins that need to be applied at different online
- * core-count and temperature bands for each corner band. Also,
- * programs hardware register configuration for core-count and
- * temp-based adjustments
- *
- * @ctrl: Pointer to the CPR3 controller
- *
- * CPR interface/bus clocks must be enabled before calling this function.
- *
- * Return: none
- */
- static void cprh_controller_program_sdelta(
- struct cpr3_controller *ctrl)
- {
- /* Only thread 0 supports sdelta */
- struct cpr3_regulator *vreg = &ctrl->thread[0].vreg[0];
- struct cprh_corner_band *corner_band;
- struct cpr4_sdelta *sdelta;
- int i, j, index;
- u32 reg = 0;
- if (!vreg->allow_core_count_adj && !vreg->allow_temp_adj)
- return;
- if (vreg->thread->thread_id != 0) {
- cpr3_err(vreg, "core count and temperature based adjustments are only allowed for CPR thread 0\n");
- return;
- }
- cpr4_regulator_init_temp_points(ctrl);
- for (i = 0; i < CPRH_CORNER_BAND_MAX_COUNT; i++) {
- reg |= (i < vreg->corner_band_count ?
- vreg->corner_band[i].corner
- & CPRH_CORNER_BAND_MASK :
- vreg->corner_count + 1)
- << (i * CPRH_CORNER_BAND_SHIFT);
- }
- cpr3_write(ctrl, CPRH_REG_CORNER_BAND, reg);
- for (i = 0; i < vreg->corner_band_count; i++) {
- corner_band = &vreg->corner_band[i];
- sdelta = corner_band->sdelta;
- if (!sdelta->allow_core_count_adj && !sdelta->allow_temp_adj) {
- /*
- * Per-online-core and per-temperature margin
- * adjustments are disabled for this corner band.
- */
- continue;
- }
- if (vreg->allow_core_count_adj)
- cpr3_write_temp_core_margin(ctrl,
- CPRH_MARGIN_TEMP_CORE_VBAND(0, i),
- &sdelta->table[0]);
- for (j = 0; j < sdelta->max_core_count; j++) {
- index = j * sdelta->temp_band_count;
- cpr3_write_temp_core_margin(ctrl,
- CPRH_MARGIN_TEMP_CORE_VBAND(
- sdelta->allow_core_count_adj
- ? j + 1 : vreg->max_core_count, i),
- &sdelta->table[index]);
- }
- }
- if (!vreg->allow_core_count_adj) {
- cpr3_masked_write(ctrl, CPR4_REG_MISC,
- CPR4_MISC_MARGIN_TABLE_ROW_SELECT_MASK,
- vreg->max_core_count
- << CPR4_MISC_MARGIN_TABLE_ROW_SELECT_SHIFT);
- }
- cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
- CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_MASK
- | CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN
- | CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN
- | CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN
- | CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK,
- vreg->max_core_count << CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_SHIFT
- | ((vreg->allow_core_count_adj)
- ? CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN : 0)
- | (vreg->allow_temp_adj ? CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN : 0)
- | ((ctrl->use_hw_closed_loop)
- ? CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN : 0)
- | (ctrl->use_hw_closed_loop
- ? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE : 0));
- /* Ensure that previous CPR register writes complete */
- mb();
- }
- static int cprh_regulator_aging_adjust(struct cpr3_controller *ctrl);
- /**
- * cpr3_regulator_cprh_initialized() - checks if CPRh has already been
- * initialized by the boot loader
- * @ctrl: Pointer to the CPR3 controller
- *
- * Return: true if CPRh controller is already initialized else false
- */
- static bool cpr3_regulator_cprh_initialized(struct cpr3_controller *ctrl)
- {
- u32 reg;
- if (ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH)
- return false;
- ctrl->cpr_hw_version = readl_relaxed(ctrl->cpr_ctrl_base
- + CPR3_REG_CPR_VERSION);
- reg = readl_relaxed(ctrl->cpr_ctrl_base + CPRH_REG_CTL(ctrl));
- return reg & CPRH_CTL_OSM_ENABLED;
- }
- /**
- * cpr3_regulator_init_cprh() - performs hardware initialization at the
- * controller and thread level required for CPRh operation.
- * @ctrl: Pointer to the CPR3 controller
- *
- * CPR interface/bus clocks must be enabled before calling this function.
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_regulator_init_cprh(struct cpr3_controller *ctrl)
- {
- u32 reg, pmic_step_size = 1;
- u64 temp;
- int i, rc;
- /* One or two threads each with a single regulator supported */
- if (ctrl->thread_count < 1 || ctrl->thread_count > 2) {
- cpr3_err(ctrl, "expected 1 or 2 threads but found %d\n",
- ctrl->thread_count);
- return -EINVAL;
- } else if (ctrl->thread[0].vreg_count != 1) {
- cpr3_err(ctrl, "expected 1 regulator for thread 0 but found %d\n",
- ctrl->thread[0].vreg_count);
- return -EINVAL;
- } else if (ctrl->thread_count == 2 && ctrl->thread[1].vreg_count != 1) {
- cpr3_err(ctrl, "expected 1 regulator for thread 1 but found %d\n",
- ctrl->thread[1].vreg_count);
- return -EINVAL;
- }
- rc = cprh_regulator_aging_adjust(ctrl);
- if (rc && rc != -ETIMEDOUT) {
- /*
- * Don't fail initialization if the CPR aging measurement
- * timed out due to sensors not being available.
- */
- cpr3_err(ctrl, "CPR aging adjustment failed, rc=%d\n", rc);
- return rc;
- }
- cprh_controller_program_sdelta(ctrl);
- for (i = 0; i < ctrl->thread_count; i++) {
- rc = cpr3_regulator_init_cprh_corners(&ctrl->thread[i].vreg[0]);
- if (rc) {
- cpr3_err(ctrl, "failed to initialize CPRh corner registers\n");
- return rc;
- }
- }
- if (ctrl->reset_step_quot_loop_en)
- cpr3_masked_write(ctrl, CPR4_REG_MISC,
- CPR4_MISC_RESET_STEP_QUOT_LOOP_EN,
- CPR4_MISC_RESET_STEP_QUOT_LOOP_EN);
- if (ctrl->saw_use_unit_mV)
- pmic_step_size = ctrl->step_volt / 1000;
- cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
- CPR4_MARGIN_ADJ_CTL_PMIC_STEP_SIZE_MASK,
- (pmic_step_size
- << CPR4_MARGIN_ADJ_CTL_PMIC_STEP_SIZE_SHIFT));
- cpr3_masked_write(ctrl, CPR4_REG_SAW_ERROR_STEP_LIMIT,
- CPR4_SAW_ERROR_STEP_LIMIT_DN_MASK,
- (ctrl->down_error_step_limit
- << CPR4_SAW_ERROR_STEP_LIMIT_DN_SHIFT));
- cpr3_masked_write(ctrl, CPR4_REG_SAW_ERROR_STEP_LIMIT,
- CPR4_SAW_ERROR_STEP_LIMIT_UP_MASK,
- (ctrl->up_error_step_limit
- << CPR4_SAW_ERROR_STEP_LIMIT_UP_SHIFT));
- cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
- CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_STEP_QUOT_MASK,
- ctrl->step_quot_fixed
- << CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_STEP_QUOT_SHIFT);
- cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
- CPR4_MARGIN_ADJ_CTL_PER_RO_KV_MARGIN_EN,
- (ctrl->use_dynamic_step_quot
- ? CPR4_MARGIN_ADJ_CTL_PER_RO_KV_MARGIN_EN : 0));
- if (ctrl->thread_count > 1)
- cpr3_masked_write(ctrl, CPR4_REG_CPR_TIMER_CLAMP,
- CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN,
- CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN);
- if (ctrl->voltage_settling_time) {
- /*
- * Configure the settling timer used to account for
- * one VDD supply step.
- */
- temp = (u64)ctrl->cpr_clock_rate
- * (u64)ctrl->voltage_settling_time;
- do_div(temp, 1000000000);
- cpr3_masked_write(ctrl, CPR4_REG_MARGIN_TEMP_CORE_TIMERS,
- CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_MASK,
- temp
- << CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_SHIFT);
- }
- if (ctrl->corner_switch_delay_time) {
- /*
- * Configure the settling timer used to delay
- * following SAW requests
- */
- temp = (u64)ctrl->cpr_clock_rate
- * (u64)ctrl->corner_switch_delay_time;
- do_div(temp, 1000000000);
- do_div(temp, CPRH_MODE_SWITCH_DELAY_FACTOR);
- cpr3_masked_write(ctrl, CPRH_REG_CTL(ctrl),
- CPRH_CTL_MODE_SWITCH_DELAY_MASK,
- temp << CPRH_CTL_MODE_SWITCH_DELAY_SHIFT);
- }
- /*
- * Configure CPRh ACD AVG registers on controllers
- * that support this feature.
- */
- if (ctrl->cpr_hw_version >= CPRH_CPR_VERSION_4P5
- && ctrl->acd_avg_enabled) {
- cpr3_masked_write(ctrl, CPRH_REG_MISC_REG2,
- CPRH_MISC_REG2_ACD_ADJ_STEP_UP_LIMIT_MASK,
- ctrl->acd_adj_up_step_limit <<
- CPRH_MISC_REG2_ACD_ADJ_STEP_UP_LIMIT_SHIFT);
- cpr3_masked_write(ctrl, CPRH_REG_MISC_REG2,
- CPRH_MISC_REG2_ACD_ADJ_STEP_DOWN_LIMIT_MASK,
- ctrl->acd_adj_down_step_limit <<
- CPRH_MISC_REG2_ACD_ADJ_STEP_DOWN_LIMIT_SHIFT);
- cpr3_masked_write(ctrl, CPRH_REG_MISC_REG2,
- CPRH_MISC_REG2_ACD_ADJ_STEP_SIZE_UP_MASK,
- ctrl->acd_adj_up_step_size <<
- CPRH_MISC_REG2_ACD_ADJ_STEP_SIZE_UP_SHIFT);
- cpr3_masked_write(ctrl, CPRH_REG_MISC_REG2,
- CPRH_MISC_REG2_ACD_ADJ_STEP_SIZE_DOWN_MASK,
- ctrl->acd_adj_down_step_size <<
- CPRH_MISC_REG2_ACD_ADJ_STEP_SIZE_DOWN_SHIFT);
- cpr3_masked_write(ctrl, CPRH_REG_MISC_REG2,
- CPRH_MISC_REG2_ACD_NOTWAIT_4_CL_SETTLE_MASK,
- (ctrl->acd_notwait_for_cl_settled
- ? CPRH_MISC_REG2_ACD_NOTWAIT_4_CL_SETTLE_EN
- : 0));
- cpr3_masked_write(ctrl, CPRH_REG_MISC_REG2,
- CPRH_MISC_REG2_ACD_AVG_FAST_UPDATE_EN_MASK,
- (ctrl->acd_adj_avg_fast_update
- ? CPRH_MISC_REG2_ACD_AVG_FAST_UPDATE_EN
- : 0));
- cpr3_masked_write(ctrl, CPRH_REG_MISC_REG2,
- CPRH_MISC_REG2_ACD_AVG_EN_MASK,
- CPRH_MISC_REG2_ACD_AVG_ENABLE);
- }
- /*
- * Program base voltage and voltage multiplier values which
- * are used for floor and initial voltage calculations by the
- * CPRh controller.
- */
- reg = (DIV_ROUND_UP(ctrl->base_volt, ctrl->step_volt)
- << CPRH_CTL_BASE_VOLTAGE_SHIFT)
- & CPRH_CTL_BASE_VOLTAGE_MASK;
- reg |= (DIV_ROUND_UP(ctrl->step_volt, 1000)
- << CPRH_CTL_VOLTAGE_MULTIPLIER_SHIFT)
- & CPRH_CTL_VOLTAGE_MULTIPLIER_MASK;
- /* Enable OSM block interface with CPR */
- reg |= CPRH_CTL_OSM_ENABLED;
- cpr3_masked_write(ctrl, CPRH_REG_CTL(ctrl), CPRH_CTL_BASE_VOLTAGE_MASK
- | CPRH_CTL_VOLTAGE_MULTIPLIER_MASK
- | CPRH_CTL_OSM_ENABLED, reg);
- /* Enable loop_en */
- cpr3_ctrl_loop_enable(ctrl);
- return 0;
- }
- /**
- * cpr3_regulator_init_ctrl() - performs hardware initialization of CPR
- * controller registers
- * @ctrl: Pointer to the CPR3 controller
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_regulator_init_ctrl(struct cpr3_controller *ctrl)
- {
- int i, j, k, m, rc;
- u32 ro_used = 0;
- u32 gcnt, cont_dly, up_down_dly, val;
- u64 temp;
- char *mode;
- if (ctrl->core_clk) {
- rc = clk_set_rate(ctrl->core_clk, ctrl->cpr_clock_rate);
- if (rc) {
- cpr3_err(ctrl, "clk_set_rate(core_clk, %u) failed, rc=%d\n",
- ctrl->cpr_clock_rate, rc);
- return rc;
- }
- }
- rc = cpr3_clock_enable(ctrl);
- if (rc) {
- cpr3_err(ctrl, "clock enable failed, rc=%d\n", rc);
- return rc;
- }
- ctrl->cpr_enabled = true;
- ctrl->cpr_hw_version = cpr3_read(ctrl, CPR3_REG_CPR_VERSION);
- /* Find all RO's used by any corner of any regulator. */
- for (i = 0; i < ctrl->thread_count; i++)
- for (j = 0; j < ctrl->thread[i].vreg_count; j++)
- for (k = 0; k < ctrl->thread[i].vreg[j].corner_count;
- k++)
- for (m = 0; m < CPR3_RO_COUNT; m++)
- if (ctrl->thread[i].vreg[j].corner[k].
- target_quot[m])
- ro_used |= BIT(m);
- /* Configure the GCNT of the RO's that will be used */
- gcnt = cpr3_regulator_get_gcnt(ctrl);
- for (i = 0; i < CPR3_RO_COUNT; i++)
- if (ro_used & BIT(i))
- cpr3_write(ctrl, CPR3_REG_GCNT(i), gcnt);
- /* Configure the loop delay time */
- temp = (u64)ctrl->cpr_clock_rate * (u64)ctrl->loop_time;
- do_div(temp, 1000000000);
- cont_dly = temp;
- if (ctrl->supports_hw_closed_loop
- && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3)
- cpr3_write(ctrl, CPR3_REG_CPR_TIMER_MID_CONT, cont_dly);
- else
- cpr3_write(ctrl, CPR3_REG_CPR_TIMER_AUTO_CONT, cont_dly);
- if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) {
- temp = (u64)ctrl->cpr_clock_rate *
- (u64)ctrl->up_down_delay_time;
- do_div(temp, 1000000000);
- up_down_dly = temp;
- if (ctrl->supports_hw_closed_loop)
- cpr3_write(ctrl, CPR3_REG_CPR_TIMER_UP_DN_CONT,
- up_down_dly);
- cpr3_debug(ctrl, "up_down_dly=%u, up_down_delay_time=%u ns\n",
- up_down_dly, ctrl->up_down_delay_time);
- }
- cpr3_debug(ctrl, "cpr_clock_rate=%u HZ, sensor_time=%u ns, loop_time=%u ns, gcnt=%u, cont_dly=%u\n",
- ctrl->cpr_clock_rate, ctrl->sensor_time, ctrl->loop_time,
- gcnt, cont_dly);
- /* Configure CPR sensor operation */
- val = (ctrl->idle_clocks << CPR3_CPR_CTL_IDLE_CLOCKS_SHIFT)
- & CPR3_CPR_CTL_IDLE_CLOCKS_MASK;
- val |= (ctrl->count_mode << CPR3_CPR_CTL_COUNT_MODE_SHIFT)
- & CPR3_CPR_CTL_COUNT_MODE_MASK;
- val |= (ctrl->count_repeat << CPR3_CPR_CTL_COUNT_REPEAT_SHIFT)
- & CPR3_CPR_CTL_COUNT_REPEAT_MASK;
- cpr3_write(ctrl, CPR3_REG_CPR_CTL, val);
- cpr3_debug(ctrl, "idle_clocks=%u, count_mode=%u, count_repeat=%u; CPR_CTL=0x%08X\n",
- ctrl->idle_clocks, ctrl->count_mode, ctrl->count_repeat, val);
- /* Configure CPR default step quotients */
- val = (ctrl->step_quot_init_min << CPR3_CPR_STEP_QUOT_MIN_SHIFT)
- & CPR3_CPR_STEP_QUOT_MIN_MASK;
- val |= (ctrl->step_quot_init_max << CPR3_CPR_STEP_QUOT_MAX_SHIFT)
- & CPR3_CPR_STEP_QUOT_MAX_MASK;
- cpr3_write(ctrl, CPR3_REG_CPR_STEP_QUOT, val);
- cpr3_debug(ctrl, "step_quot_min=%u, step_quot_max=%u; STEP_QUOT=0x%08X\n",
- ctrl->step_quot_init_min, ctrl->step_quot_init_max, val);
- /* Configure the CPR sensor ownership */
- for (i = 0; i < ctrl->sensor_count; i++)
- cpr3_write(ctrl, CPR3_REG_SENSOR_OWNER(i),
- ctrl->sensor_owner[i]);
- /* Configure per-thread registers */
- for (i = 0; i < ctrl->thread_count; i++) {
- rc = cpr3_regulator_init_thread(&ctrl->thread[i]);
- if (rc) {
- cpr3_err(ctrl, "CPR thread register initialization failed, rc=%d\n",
- rc);
- return rc;
- }
- }
- if (ctrl->supports_hw_closed_loop) {
- if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4 ||
- ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH) {
- cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
- CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK,
- ctrl->use_hw_closed_loop
- ? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE
- : CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE);
- } else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) {
- cpr3_write(ctrl, CPR3_REG_HW_CLOSED_LOOP,
- ctrl->use_hw_closed_loop
- ? CPR3_HW_CLOSED_LOOP_ENABLE
- : CPR3_HW_CLOSED_LOOP_DISABLE);
- cpr3_debug(ctrl, "PD_THROTTLE=0x%08X\n",
- ctrl->proc_clock_throttle);
- }
- if ((ctrl->use_hw_closed_loop ||
- ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) &&
- ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH) {
- rc = regulator_enable(ctrl->vdd_limit_regulator);
- if (rc) {
- cpr3_err(ctrl, "CPR limit regulator enable failed, rc=%d\n",
- rc);
- return rc;
- }
- if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) {
- rc = msm_spm_avs_enable_irq(0,
- MSM_SPM_AVS_IRQ_MAX);
- if (rc) {
- cpr3_err(ctrl, "could not enable max IRQ, rc=%d\n",
- rc);
- return rc;
- }
- }
- }
- }
- if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) {
- rc = cpr3_regulator_init_cpr4(ctrl);
- if (rc) {
- cpr3_err(ctrl, "CPR4-specific controller initialization failed, rc=%d\n",
- rc);
- return rc;
- }
- } else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH) {
- rc = cpr3_regulator_init_cprh(ctrl);
- if (rc) {
- cpr3_err(ctrl, "CPRh-specific controller initialization failed, rc=%d\n",
- rc);
- return rc;
- }
- }
- /* Ensure that all register writes complete before disabling clocks. */
- wmb();
- /* Keep CPR clocks on for CPRh full HW closed-loop operation */
- if (ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH) {
- cpr3_clock_disable(ctrl);
- ctrl->cpr_enabled = false;
- }
- if (!ctrl->cpr_allowed_sw || !ctrl->cpr_allowed_hw)
- mode = "open-loop";
- else if (ctrl->supports_hw_closed_loop &&
- ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH)
- mode = ctrl->use_hw_closed_loop
- ? "HW closed-loop" : "SW closed-loop";
- else if (ctrl->supports_hw_closed_loop &&
- ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH)
- mode = ctrl->use_hw_closed_loop
- ? "full HW closed-loop" : "open-loop";
- else
- mode = "closed-loop";
- cpr3_info(ctrl, "Default CPR mode = %s", mode);
- return 0;
- }
- /**
- * cpr3_regulator_init_hw_closed_loop_dependencies() - perform hardware
- * initialization steps to ensure that CPR HW closed-loop voltage
- * change requests are able to reach the PMIC regulator
- * @pdev: Platform device pointer for the CPR3 controller
- * @ctrl: Pointer to the CPR3 controller
- *
- * Return: 0 on success, errno on failure
- */
- static int
- cpr3_regulator_init_hw_closed_loop_dependencies(struct platform_device *pdev,
- struct cpr3_controller *ctrl)
- {
- struct resource *res;
- int rc = 0;
- u32 val;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "saw");
- if (res && res->start)
- ctrl->saw_base = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
- if (ctrl->saw_base) {
- /* Configure SAW registers directly. */
- rc = of_property_read_u32(ctrl->dev->of_node,
- "qcom,saw-avs-ctrl", &val);
- if (rc) {
- cpr3_err(ctrl, "unable to read DT property qcom,saw-avs-ctrl, rc=%d\n",
- rc);
- return rc;
- }
- writel_relaxed(val, ctrl->saw_base + SAW_REG_AVS_CTL);
- rc = of_property_read_u32(ctrl->dev->of_node,
- "qcom,saw-avs-limit", &val);
- if (rc) {
- cpr3_err(ctrl, "unable to read DT property qcom,saw-avs-limit, rc=%d\n",
- rc);
- return rc;
- }
- writel_relaxed(val, ctrl->saw_base + SAW_REG_AVS_LIMIT);
- } else {
- /* Wait for SPM driver to configure SAW registers. */
- rc = msm_spm_probe_done();
- if (rc) {
- if (rc != -EPROBE_DEFER)
- cpr3_err(ctrl, "spm unavailable, rc=%d\n", rc);
- return rc;
- }
- }
- return 0;
- }
- /**
- * cpr3_regulator_set_target_quot() - configure the target quotient for each
- * RO of the CPR3 thread and set the RO mask
- * @thread: Pointer to the CPR3 thread
- *
- * Return: none
- */
- static void cpr3_regulator_set_target_quot(struct cpr3_thread *thread)
- {
- u32 new_quot, last_quot;
- int i;
- if (thread->aggr_corner.ro_mask == CPR3_RO_MASK
- && thread->last_closed_loop_aggr_corner.ro_mask == CPR3_RO_MASK) {
- /* Avoid writing target quotients since all RO's are masked. */
- return;
- } else if (thread->aggr_corner.ro_mask == CPR3_RO_MASK) {
- cpr3_write(thread->ctrl, CPR3_REG_RO_MASK(thread->thread_id),
- CPR3_RO_MASK);
- thread->last_closed_loop_aggr_corner.ro_mask = CPR3_RO_MASK;
- /*
- * Only the RO_MASK register needs to be written since all
- * RO's are masked.
- */
- return;
- } else if (thread->aggr_corner.ro_mask
- != thread->last_closed_loop_aggr_corner.ro_mask) {
- cpr3_write(thread->ctrl, CPR3_REG_RO_MASK(thread->thread_id),
- thread->aggr_corner.ro_mask);
- }
- for (i = 0; i < CPR3_RO_COUNT; i++) {
- new_quot = thread->aggr_corner.target_quot[i];
- last_quot = thread->last_closed_loop_aggr_corner.target_quot[i];
- if (new_quot != last_quot)
- cpr3_write(thread->ctrl,
- CPR3_REG_TARGET_QUOT(thread->thread_id, i),
- new_quot);
- }
- thread->last_closed_loop_aggr_corner = thread->aggr_corner;
- }
- /**
- * cpr3_update_vreg_closed_loop_volt() - update the last known settled
- * closed loop voltage for a CPR3 regulator
- * @vreg: Pointer to the CPR3 regulator
- * @vdd_volt: Last known settled voltage in microvolts for the
- * VDD supply
- * @reg_last_measurement: Value read from the LAST_MEASUREMENT register
- *
- * Return: none
- */
- static void cpr3_update_vreg_closed_loop_volt(struct cpr3_regulator *vreg,
- int vdd_volt, u32 reg_last_measurement)
- {
- bool step_dn, step_up, aggr_step_up, aggr_step_dn, aggr_step_mid;
- bool valid, pd_valid, saw_error;
- struct cpr3_controller *ctrl = vreg->thread->ctrl;
- struct cpr3_corner *corner;
- u32 id;
- if (vreg->last_closed_loop_corner == CPR3_REGULATOR_CORNER_INVALID)
- return;
- corner = &vreg->corner[vreg->last_closed_loop_corner];
- if (vreg->thread->last_closed_loop_aggr_corner.ro_mask
- == CPR3_RO_MASK || !vreg->aggregated) {
- return;
- } else if (!ctrl->cpr_enabled || !ctrl->last_corner_was_closed_loop) {
- return;
- } else if (ctrl->thread_count == 1
- && vdd_volt >= corner->floor_volt
- && vdd_volt <= corner->ceiling_volt) {
- corner->last_volt = vdd_volt;
- cpr3_debug(vreg, "last_volt updated: last_volt[%d]=%d, ceiling_volt[%d]=%d, floor_volt[%d]=%d\n",
- vreg->last_closed_loop_corner, corner->last_volt,
- vreg->last_closed_loop_corner,
- corner->ceiling_volt,
- vreg->last_closed_loop_corner,
- corner->floor_volt);
- return;
- } else if (!ctrl->supports_hw_closed_loop) {
- return;
- } else if (ctrl->ctrl_type != CPR_CTRL_TYPE_CPR3) {
- corner->last_volt = vdd_volt;
- cpr3_debug(vreg, "last_volt updated: last_volt[%d]=%d, ceiling_volt[%d]=%d, floor_volt[%d]=%d\n",
- vreg->last_closed_loop_corner, corner->last_volt,
- vreg->last_closed_loop_corner,
- corner->ceiling_volt,
- vreg->last_closed_loop_corner,
- corner->floor_volt);
- return;
- }
- /* CPR clocks are on and HW closed loop is supported */
- valid = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_VALID);
- if (!valid) {
- cpr3_debug(vreg, "CPR_LAST_VALID_MEASUREMENT=0x%X valid bit not set\n",
- reg_last_measurement);
- return;
- }
- id = vreg->thread->thread_id;
- step_dn
- = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_THREAD_DN(id));
- step_up
- = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_THREAD_UP(id));
- aggr_step_dn = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_AGGR_DN);
- aggr_step_mid
- = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_AGGR_MID);
- aggr_step_up = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_AGGR_UP);
- saw_error = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_SAW_ERROR);
- pd_valid
- = !((((reg_last_measurement & CPR3_LAST_MEASUREMENT_PD_BYPASS_MASK)
- >> CPR3_LAST_MEASUREMENT_PD_BYPASS_SHIFT)
- & vreg->pd_bypass_mask) == vreg->pd_bypass_mask);
- if (!pd_valid) {
- cpr3_debug(vreg, "CPR_LAST_VALID_MEASUREMENT=0x%X, all power domains bypassed\n",
- reg_last_measurement);
- return;
- } else if (step_dn && step_up) {
- cpr3_err(vreg, "both up and down status bits set, CPR_LAST_VALID_MEASUREMENT=0x%X\n",
- reg_last_measurement);
- return;
- } else if (aggr_step_dn && step_dn && vdd_volt < corner->last_volt
- && vdd_volt >= corner->floor_volt) {
- corner->last_volt = vdd_volt;
- } else if (aggr_step_up && step_up && vdd_volt > corner->last_volt
- && vdd_volt <= corner->ceiling_volt) {
- corner->last_volt = vdd_volt;
- } else if (aggr_step_mid
- && vdd_volt >= corner->floor_volt
- && vdd_volt <= corner->ceiling_volt) {
- corner->last_volt = vdd_volt;
- } else if (saw_error && (vdd_volt == corner->ceiling_volt
- || vdd_volt == corner->floor_volt)) {
- corner->last_volt = vdd_volt;
- } else {
- cpr3_debug(vreg, "last_volt not updated: last_volt[%d]=%d, ceiling_volt[%d]=%d, floor_volt[%d]=%d, vdd_volt=%d, CPR_LAST_VALID_MEASUREMENT=0x%X\n",
- vreg->last_closed_loop_corner, corner->last_volt,
- vreg->last_closed_loop_corner,
- corner->ceiling_volt,
- vreg->last_closed_loop_corner, corner->floor_volt,
- vdd_volt, reg_last_measurement);
- return;
- }
- cpr3_debug(vreg, "last_volt updated: last_volt[%d]=%d, ceiling_volt[%d]=%d, floor_volt[%d]=%d, CPR_LAST_VALID_MEASUREMENT=0x%X\n",
- vreg->last_closed_loop_corner, corner->last_volt,
- vreg->last_closed_loop_corner, corner->ceiling_volt,
- vreg->last_closed_loop_corner, corner->floor_volt,
- reg_last_measurement);
- }
- /**
- * cpr3_regulator_config_ldo_retention() - configure per-regulator LDO retention
- * mode
- * @vreg: Pointer to the CPR3 regulator to configure
- * @ref_volt: Reference voltage used to determine if LDO retention
- * mode can be allowed. It corresponds either to the
- * aggregated floor voltage or the next VDD supply setpoint
- *
- * This function determines if a CPR3 regulator's configuration satisfies safe
- * operating voltages for LDO retention and uses the regulator_allow_bypass()
- * interface on the LDO retention regulator to enable or disable such feature
- * accordingly.
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_regulator_config_ldo_retention(struct cpr3_regulator *vreg,
- int ref_volt)
- {
- struct regulator *ldo_ret_reg = vreg->ldo_ret_regulator;
- int retention_volt, rc;
- enum msm_ldo_supply_mode mode;
- if (!ldo_ret_reg) {
- /* LDO retention regulator is not defined */
- return 0;
- }
- retention_volt = regulator_get_voltage(ldo_ret_reg);
- if (retention_volt < 0) {
- cpr3_err(vreg, "regulator_get_voltage(ldo_ret) failed, rc=%d\n",
- retention_volt);
- return retention_volt;
- }
- mode = ref_volt >= retention_volt + vreg->ldo_min_headroom_volt
- ? LDO_MODE : BHS_MODE;
- rc = regulator_allow_bypass(ldo_ret_reg, mode);
- if (rc)
- cpr3_err(vreg, "regulator_allow_bypass(ldo_ret) == %s failed, rc=%d\n",
- mode ? "true" : "false", rc);
- return rc;
- }
- /**
- * cpr3_regulator_config_kryo_ldo_mem_acc() - configure the mem-acc regulator
- * corner based upon a future Kryo LDO regulator voltage setpoint
- * @vreg: Pointer to the CPR3 regulator
- * @new_volt: New voltage in microvolts that the LDO regulator needs
- * to end up at
- *
- * This function determines if a new LDO regulator set point will result
- * in crossing the voltage threshold that requires reconfiguration of
- * the mem-acc regulator associated with a CPR3 regulator and if so, performs
- * the correct sequence to select the correct mem-acc corner.
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_regulator_config_kryo_ldo_mem_acc(struct cpr3_regulator *vreg,
- int new_volt)
- {
- struct cpr3_controller *ctrl = vreg->thread->ctrl;
- struct regulator *ldo_reg = vreg->ldo_regulator;
- struct regulator *mem_acc_reg = vreg->mem_acc_regulator;
- int mem_acc_volt = ctrl->mem_acc_threshold_volt;
- int last_volt, safe_volt, mem_acc_corn, rc;
- enum msm_apm_supply apm_mode;
- if (!mem_acc_reg || !mem_acc_volt || !ldo_reg)
- return 0;
- apm_mode = msm_apm_get_supply(ctrl->apm);
- if (apm_mode < 0) {
- cpr3_err(ctrl, "APM get supply failed, rc=%d\n",
- apm_mode);
- return apm_mode;
- }
- last_volt = regulator_get_voltage(ldo_reg);
- if (last_volt < 0) {
- cpr3_err(vreg, "regulator_get_voltage(ldo) failed, rc=%d\n",
- last_volt);
- return last_volt;
- }
- if (((last_volt < mem_acc_volt && mem_acc_volt <= new_volt)
- || (last_volt >= mem_acc_volt && mem_acc_volt > new_volt))) {
- if (apm_mode == ctrl->apm_high_supply)
- safe_volt = min(vreg->ldo_max_volt, mem_acc_volt);
- else
- safe_volt = min(max(ctrl->system_supply_max_volt -
- vreg->ldo_max_headroom_volt,
- mem_acc_volt), vreg->ldo_max_volt);
- rc = regulator_set_voltage(ldo_reg, safe_volt,
- max(new_volt, last_volt));
- if (rc) {
- cpr3_err(ctrl, "regulator_set_voltage(ldo) == %d failed, rc=%d\n",
- mem_acc_volt, rc);
- return rc;
- }
- mem_acc_corn = new_volt < mem_acc_volt ?
- ctrl->mem_acc_corner_map[CPR3_MEM_ACC_LOW_CORNER] :
- ctrl->mem_acc_corner_map[CPR3_MEM_ACC_HIGH_CORNER];
- rc = regulator_set_voltage(mem_acc_reg, mem_acc_corn,
- mem_acc_corn);
- if (rc) {
- cpr3_err(ctrl, "regulator_set_voltage(mem_acc) == %d failed, rc=%d\n",
- 0, rc);
- return rc;
- }
- }
- return 0;
- }
- /**
- * cpr3_regulator_kryo_bhs_prepare() - configure the Kryo LDO regulator
- * associated with a CPR3 regulator in preparation for BHS
- * mode switch.
- * @vreg: Pointer to the CPR3 regulator
- * @vdd_volt: Last known settled voltage in microvolts for the VDD
- * supply
- * @vdd_ceiling_volt: Last known aggregated ceiling voltage in microvolts for
- * the VDD supply
- *
- * This function performs the necessary steps prior to switching a Kryo LDO
- * regulator to BHS mode (LDO bypassed mode).
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_regulator_kryo_bhs_prepare(struct cpr3_regulator *vreg,
- int vdd_volt, int vdd_ceiling_volt)
- {
- struct regulator *ldo_reg = vreg->ldo_regulator;
- int bhs_volt, rc;
- bhs_volt = vdd_volt - vreg->ldo_min_headroom_volt;
- if (bhs_volt > vreg->ldo_max_volt) {
- cpr3_debug(vreg, "limited to LDO output of %d uV when switching to BHS mode\n",
- vreg->ldo_max_volt);
- bhs_volt = vreg->ldo_max_volt;
- }
- rc = cpr3_regulator_config_kryo_ldo_mem_acc(vreg, bhs_volt);
- if (rc) {
- cpr3_err(vreg, "failed to configure mem-acc settings\n");
- return rc;
- }
- rc = regulator_set_voltage(ldo_reg, bhs_volt, min(vdd_ceiling_volt,
- vreg->ldo_max_volt));
- if (rc) {
- cpr3_err(vreg, "regulator_set_voltage(ldo) == %d failed, rc=%d\n",
- bhs_volt, rc);
- return rc;
- }
- return rc;
- }
- /**
- * cpr3_regulator_set_bhs_mode() - configure the LDO regulator associated with
- * a CPR3 regulator to BHS mode
- * @vreg: Pointer to the CPR3 regulator
- * @vdd_volt: Last known settled voltage in microvolts for the VDD
- * supply
- * @vdd_ceiling_volt: Last known aggregated ceiling voltage in microvolts for
- * the VDD supply
- *
- * This function performs the necessary steps to switch an LDO regulator
- * to BHS mode (LDO bypassed mode).
- */
- static int cpr3_regulator_set_bhs_mode(struct cpr3_regulator *vreg,
- int vdd_volt, int vdd_ceiling_volt)
- {
- struct regulator *ldo_reg = vreg->ldo_regulator;
- int rc;
- if (vreg->ldo_type == CPR3_LDO_KRYO) {
- rc = cpr3_regulator_kryo_bhs_prepare(vreg, vdd_volt,
- vdd_ceiling_volt);
- if (rc) {
- cpr3_err(vreg, "cpr3 regulator bhs mode prepare failed, rc=%d\n",
- rc);
- return rc;
- }
- }
- rc = regulator_allow_bypass(ldo_reg, BHS_MODE);
- if (rc) {
- cpr3_err(vreg, "regulator_allow_bypass(bhs) == %s failed, rc=%d\n",
- BHS_MODE ? "true" : "false", rc);
- return rc;
- }
- vreg->ldo_regulator_bypass = BHS_MODE;
- return rc;
- }
- /**
- * cpr3_regulator_ldo_apm_prepare() - configure LDO regulators associated
- * with each CPR3 regulator of a CPR3 controller in preparation
- * for an APM switch.
- * @ctrl: Pointer to the CPR3 controller
- * @new_volt: New voltage in microvolts that the VDD supply
- * needs to end up at
- * @last_volt: Last known voltage in microvolts for the VDD supply
- * @aggr_corner: Pointer to the CPR3 corner which corresponds to the max
- * corner aggregated from all CPR3 threads managed by the
- * CPR3 controller
- *
- * This function ensures LDO regulator hardware requirements are met before
- * an APM switch is requested. The function must be called as the last step
- * before switching the APM mode.
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_regulator_ldo_apm_prepare(struct cpr3_controller *ctrl,
- int new_volt, int last_volt,
- struct cpr3_corner *aggr_corner)
- {
- struct cpr3_regulator *vreg;
- struct cpr3_corner *current_corner;
- enum msm_apm_supply apm_mode;
- int i, j, safe_volt, max_volt, ldo_volt, ref_volt, rc;
- apm_mode = msm_apm_get_supply(ctrl->apm);
- if (apm_mode < 0) {
- cpr3_err(ctrl, "APM get supply failed, rc=%d\n", apm_mode);
- return apm_mode;
- }
- if (apm_mode == ctrl->apm_low_supply ||
- new_volt >= ctrl->apm_threshold_volt)
- return 0;
- /*
- * Guarantee LDO maximum headroom is not violated when the APM is
- * switched to the system-supply source.
- */
- for (i = 0; i < ctrl->thread_count; i++) {
- for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
- vreg = &ctrl->thread[i].vreg[j];
- if (!vreg->vreg_enabled || vreg->current_corner
- == CPR3_REGULATOR_CORNER_INVALID)
- continue;
- if (!vreg->ldo_regulator || !vreg->ldo_mode_allowed ||
- vreg->ldo_regulator_bypass == BHS_MODE)
- continue;
- /*
- * If the new VDD configuration does not satisfy
- * requirements for LDO usage, switch the regulator
- * to BHS mode. By doing so, the LDO maximum headroom
- * does not need to be enforced.
- */
- current_corner = &vreg->corner[vreg->current_corner];
- ldo_volt = current_corner->open_loop_volt
- - vreg->ldo_adjust_volt;
- ref_volt = ctrl->use_hw_closed_loop ?
- aggr_corner->floor_volt :
- new_volt;
- if (ref_volt < ldo_volt + vreg->ldo_min_headroom_volt
- || ldo_volt < ctrl->system_supply_max_volt -
- vreg->ldo_max_headroom_volt ||
- ldo_volt > vreg->ldo_max_volt) {
- rc = cpr3_regulator_set_bhs_mode(vreg,
- last_volt, aggr_corner->ceiling_volt);
- if (rc)
- return rc;
- /*
- * Do not enforce LDO maximum headroom since the
- * regulator is now configured to BHS mode.
- */
- continue;
- }
- safe_volt = min(max(ldo_volt,
- ctrl->system_supply_max_volt
- - vreg->ldo_max_headroom_volt),
- vreg->ldo_max_volt);
- max_volt = min(ctrl->system_supply_max_volt,
- vreg->ldo_max_volt);
- rc = regulator_set_voltage(vreg->ldo_regulator,
- safe_volt, max_volt);
- if (rc) {
- cpr3_err(vreg, "regulator_set_voltage(ldo) == %d failed, rc=%d\n",
- safe_volt, rc);
- return rc;
- }
- }
- }
- return 0;
- }
- /**
- * cpr3_regulator_config_vreg_kryo_ldo() - configure the voltage and bypass
- * state for the Kryo LDO regulator associated with a single CPR3
- * regulator.
- *
- * @vreg: Pointer to the CPR3 regulator
- * @vdd_floor_volt: Last known aggregated floor voltage in microvolts for
- * the VDD supply
- * @vdd_ceiling_volt: Last known aggregated ceiling voltage in microvolts for
- * the VDD supply
- * @ref_volt: Reference voltage in microvolts corresponds either to
- * the aggregated floor voltage or the next VDD supply
- * setpoint.
- * @last_volt: Last known voltage in microvolts for the VDD supply
- *
- * This function performs all relevant LDO or BHS configurations if a Kryo LDO
- * regulator is specified.
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_regulator_config_vreg_kryo_ldo(struct cpr3_regulator *vreg,
- int vdd_floor_volt, int vdd_ceiling_volt,
- int ref_volt, int last_volt)
- {
- struct cpr3_controller *ctrl = vreg->thread->ctrl;
- struct regulator *ldo_reg = vreg->ldo_regulator;
- struct cpr3_corner *current_corner;
- enum msm_apm_supply apm_mode;
- int rc, ldo_volt, final_ldo_volt, bhs_volt, max_volt, safe_volt;
- current_corner = &vreg->corner[vreg->current_corner];
- ldo_volt = current_corner->open_loop_volt
- - vreg->ldo_adjust_volt;
- bhs_volt = last_volt - vreg->ldo_min_headroom_volt;
- max_volt = min(vdd_ceiling_volt, vreg->ldo_max_volt);
- if (ref_volt >= ldo_volt + vreg->ldo_min_headroom_volt &&
- ldo_volt >= ctrl->system_supply_max_volt -
- vreg->ldo_max_headroom_volt &&
- bhs_volt >= ctrl->system_supply_max_volt -
- vreg->ldo_max_headroom_volt &&
- ldo_volt <= vreg->ldo_max_volt) {
- /* LDO minimum and maximum headrooms satisfied */
- apm_mode = msm_apm_get_supply(ctrl->apm);
- if (apm_mode < 0) {
- cpr3_err(ctrl, "APM get supply failed, rc=%d\n",
- apm_mode);
- return apm_mode;
- }
- if (vreg->ldo_regulator_bypass == BHS_MODE) {
- /*
- * BHS to LDO transition. Configure LDO output
- * to min(max LDO output, VDD - LDO headroom)
- * voltage if APM is on high supply source or
- * min(max(system-supply ceiling - LDO max headroom,
- * VDD - LDO headroom), max LDO output) if
- * APM is on low supply source, then switch
- * regulator mode.
- */
- if (apm_mode == ctrl->apm_high_supply)
- safe_volt = min(vreg->ldo_max_volt, bhs_volt);
- else
- safe_volt =
- min(max(ctrl->system_supply_max_volt -
- vreg->ldo_max_headroom_volt,
- bhs_volt),
- vreg->ldo_max_volt);
- rc = cpr3_regulator_config_kryo_ldo_mem_acc(vreg,
- safe_volt);
- if (rc) {
- cpr3_err(vreg, "failed to configure mem-acc settings\n");
- return rc;
- }
- rc = regulator_set_voltage(ldo_reg, safe_volt,
- max_volt);
- if (rc) {
- cpr3_err(vreg, "regulator_set_voltage(ldo) == %d failed, rc=%d\n",
- safe_volt, rc);
- return rc;
- }
- rc = regulator_allow_bypass(ldo_reg, LDO_MODE);
- if (rc) {
- cpr3_err(vreg, "regulator_allow_bypass(ldo) == %s failed, rc=%d\n",
- LDO_MODE ? "true" : "false", rc);
- return rc;
- }
- vreg->ldo_regulator_bypass = LDO_MODE;
- }
- /* Configure final LDO output voltage */
- if (apm_mode == ctrl->apm_high_supply)
- final_ldo_volt = max(ldo_volt,
- vdd_ceiling_volt -
- vreg->ldo_max_headroom_volt);
- else
- final_ldo_volt = ldo_volt;
- rc = cpr3_regulator_config_kryo_ldo_mem_acc(vreg,
- final_ldo_volt);
- if (rc) {
- cpr3_err(vreg, "failed to configure mem-acc settings\n");
- return rc;
- }
- rc = regulator_set_voltage(ldo_reg, final_ldo_volt, max_volt);
- if (rc) {
- cpr3_err(vreg, "regulator_set_voltage(ldo) == %d failed, rc=%d\n",
- final_ldo_volt, rc);
- return rc;
- }
- } else {
- if (vreg->ldo_regulator_bypass == LDO_MODE) {
- /* LDO to BHS transition */
- rc = cpr3_regulator_set_bhs_mode(vreg, last_volt,
- vdd_ceiling_volt);
- if (rc)
- return rc;
- }
- }
- return 0;
- }
- /**
- * cpr3_regulator_config_vreg_ldo300() - configure the voltage and bypass state
- * for the LDO300 regulator associated with a single CPR3
- * regulator.
- *
- * @vreg: Pointer to the CPR3 regulator
- * @new_volt: New voltage in microvolts that VDD supply needs to
- * end up at
- * @vdd_ceiling_volt: Last known aggregated ceiling voltage in microvolts for
- * the VDD supply
- *
- * This function performs all relevant LDO or BHS configurations for an LDO300
- * type regulator.
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_regulator_config_vreg_ldo300(struct cpr3_regulator *vreg,
- int new_volt, int vdd_ceiling_volt)
- {
- struct regulator *ldo_reg = vreg->ldo_regulator;
- struct cpr3_corner *corner;
- bool mode;
- int rc = 0;
- corner = &vreg->corner[vreg->current_corner];
- mode = corner->ldo_mode_allowed ? LDO_MODE : BHS_MODE;
- if (mode == LDO_MODE) {
- rc = regulator_set_voltage(ldo_reg, new_volt, vdd_ceiling_volt);
- if (rc) {
- cpr3_err(vreg, "regulator_set_voltage(ldo) == %d failed, rc=%d\n",
- new_volt, rc);
- return rc;
- }
- }
- if (vreg->ldo_regulator_bypass != mode) {
- rc = regulator_allow_bypass(ldo_reg, mode);
- if (rc) {
- cpr3_err(vreg, "regulator_allow_bypass(%s) is failed, rc=%d\n",
- mode == LDO_MODE ? "ldo" : "bhs", rc);
- return rc;
- }
- vreg->ldo_regulator_bypass = mode;
- }
- return rc;
- }
- /**
- * cpr3_regulator_config_vreg_ldo() - configure the voltage and bypass state for
- * the LDO regulator associated with a single CPR3 regulator.
- *
- * @vreg: Pointer to the CPR3 regulator
- * @vdd_floor_volt: Last known aggregated floor voltage in microvolts for
- * the VDD supply
- * @vdd_ceiling_volt: Last known aggregated ceiling voltage in microvolts for
- * the VDD supply
- * @new_volt: New voltage in microvolts that VDD supply needs to
- * end up at
- * @last_volt: Last known voltage in microvolts for the VDD supply
- *
- * This function identifies the type of LDO regulator associated with a CPR3
- * regulator and invokes the LDO specific configuration functions.
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_regulator_config_vreg_ldo(struct cpr3_regulator *vreg,
- int vdd_floor_volt, int vdd_ceiling_volt,
- int new_volt, int last_volt)
- {
- struct cpr3_controller *ctrl = vreg->thread->ctrl;
- int ref_volt, rc;
- ref_volt = ctrl->use_hw_closed_loop ? vdd_floor_volt :
- new_volt;
- rc = cpr3_regulator_config_ldo_retention(vreg, ref_volt);
- if (rc)
- return rc;
- if (!vreg->vreg_enabled ||
- vreg->current_corner == CPR3_REGULATOR_CORNER_INVALID)
- return 0;
- switch (vreg->ldo_type) {
- case CPR3_LDO_KRYO:
- rc = cpr3_regulator_config_vreg_kryo_ldo(vreg, vdd_floor_volt,
- vdd_ceiling_volt, ref_volt, last_volt);
- if (rc)
- cpr3_err(vreg, "kryo ldo regulator config failed, rc=%d\n",
- rc);
- break;
- case CPR3_LDO300:
- rc = cpr3_regulator_config_vreg_ldo300(vreg, new_volt,
- vdd_ceiling_volt);
- if (rc)
- cpr3_err(vreg, "ldo300 regulator config failed, rc=%d\n",
- rc);
- break;
- default:
- cpr3_err(vreg, "invalid ldo regulator type = %d\n",
- vreg->ldo_type);
- rc = -EINVAL;
- }
- return rc;
- }
- /**
- * cpr3_regulator_config_ldo() - configure the voltage and bypass state for the
- * LDO regulator associated with each CPR3 regulator of a CPR3
- * controller
- * @ctrl: Pointer to the CPR3 controller
- * @vdd_floor_volt: Last known aggregated floor voltage in microvolts for
- * the VDD supply
- * @vdd_ceiling_volt: Last known aggregated ceiling voltage in microvolts for
- * the VDD supply
- * @new_volt: New voltage in microvolts that VDD supply needs to
- * end up at
- * @last_volt: Last known voltage in microvolts for the VDD supply
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_regulator_config_ldo(struct cpr3_controller *ctrl,
- int vdd_floor_volt, int vdd_ceiling_volt,
- int new_volt, int last_volt)
- {
- struct cpr3_regulator *vreg;
- int i, j, rc;
- for (i = 0; i < ctrl->thread_count; i++) {
- for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
- vreg = &ctrl->thread[i].vreg[j];
- if (!vreg->ldo_regulator || !vreg->ldo_mode_allowed)
- continue;
- rc = cpr3_regulator_config_vreg_ldo(vreg,
- vdd_floor_volt, vdd_ceiling_volt,
- new_volt, last_volt);
- if (rc)
- return rc;
- }
- }
- return 0;
- }
- /**
- * cpr3_regulator_mem_acc_bhs_used() - determines if mem-acc regulators powered
- * through a BHS are associated with the CPR3 controller or any of
- * the CPR3 regulators it controls.
- * @ctrl: Pointer to the CPR3 controller
- *
- * This function determines if the CPR3 controller or any of its CPR3 regulators
- * need to manage mem-acc regulators that are currently powered through a BHS
- * and whose corner selection is based upon a particular voltage threshold.
- *
- * Return: true or false
- */
- static bool cpr3_regulator_mem_acc_bhs_used(struct cpr3_controller *ctrl)
- {
- struct cpr3_regulator *vreg;
- int i, j;
- if (!ctrl->mem_acc_threshold_volt)
- return false;
- if (ctrl->mem_acc_regulator)
- return true;
- for (i = 0; i < ctrl->thread_count; i++) {
- for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
- vreg = &ctrl->thread[i].vreg[j];
- if (vreg->mem_acc_regulator &&
- (!vreg->ldo_regulator ||
- vreg->ldo_regulator_bypass
- == BHS_MODE))
- return true;
- }
- }
- return false;
- }
- /**
- * cpr3_regulator_config_bhs_mem_acc() - configure the mem-acc regulator
- * settings for hardware blocks currently powered through the BHS.
- * @ctrl: Pointer to the CPR3 controller
- * @new_volt: New voltage in microvolts that VDD supply needs to
- * end up at
- * @last_volt: Pointer to the last known voltage in microvolts for the
- * VDD supply
- * @aggr_corner: Pointer to the CPR3 corner which corresponds to the max
- * corner aggregated from all CPR3 threads managed by the
- * CPR3 controller
- *
- * This function programs the mem-acc regulator corners for CPR3 regulators
- * whose LDO regulators are in bypassed state. The function also handles
- * CPR3 controllers which utilize mem-acc regulators that operate independently
- * from the LDO hardware and that must be programmed when the VDD supply
- * crosses a particular voltage threshold.
- *
- * Return: 0 on success, errno on failure. If the VDD supply voltage is
- * modified, last_volt is updated to reflect the new voltage setpoint.
- */
- static int cpr3_regulator_config_bhs_mem_acc(struct cpr3_controller *ctrl,
- int new_volt, int *last_volt,
- struct cpr3_corner *aggr_corner)
- {
- struct cpr3_regulator *vreg;
- int i, j, rc, mem_acc_corn, safe_volt;
- int mem_acc_volt = ctrl->mem_acc_threshold_volt;
- int ref_volt;
- if (!cpr3_regulator_mem_acc_bhs_used(ctrl))
- return 0;
- ref_volt = ctrl->use_hw_closed_loop ? aggr_corner->floor_volt :
- new_volt;
- if (((*last_volt < mem_acc_volt && mem_acc_volt <= ref_volt) ||
- (*last_volt >= mem_acc_volt && mem_acc_volt > ref_volt))) {
- if (ref_volt < *last_volt)
- safe_volt = max(mem_acc_volt, aggr_corner->last_volt);
- else
- safe_volt = max(mem_acc_volt, *last_volt);
- rc = regulator_set_voltage(ctrl->vdd_regulator, safe_volt,
- new_volt < *last_volt ?
- ctrl->aggr_corner.ceiling_volt :
- new_volt);
- if (rc) {
- cpr3_err(ctrl, "regulator_set_voltage(vdd) == %d failed, rc=%d\n",
- safe_volt, rc);
- return rc;
- }
- *last_volt = safe_volt;
- mem_acc_corn = ref_volt < mem_acc_volt ?
- ctrl->mem_acc_corner_map[CPR3_MEM_ACC_LOW_CORNER] :
- ctrl->mem_acc_corner_map[CPR3_MEM_ACC_HIGH_CORNER];
- if (ctrl->mem_acc_regulator) {
- rc = regulator_set_voltage(ctrl->mem_acc_regulator,
- mem_acc_corn, mem_acc_corn);
- if (rc) {
- cpr3_err(ctrl, "regulator_set_voltage(mem_acc) == %d failed, rc=%d\n",
- mem_acc_corn, rc);
- return rc;
- }
- }
- for (i = 0; i < ctrl->thread_count; i++) {
- for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
- vreg = &ctrl->thread[i].vreg[j];
- if (!vreg->mem_acc_regulator ||
- (vreg->ldo_regulator &&
- vreg->ldo_regulator_bypass
- == LDO_MODE))
- continue;
- rc = regulator_set_voltage(
- vreg->mem_acc_regulator, mem_acc_corn,
- mem_acc_corn);
- if (rc) {
- cpr3_err(vreg, "regulator_set_voltage(mem_acc) == %d failed, rc=%d\n",
- mem_acc_corn, rc);
- return rc;
- }
- }
- }
- }
- return 0;
- }
- /**
- * cpr3_regulator_switch_apm_mode() - switch the mode of the APM controller
- * associated with a given CPR3 controller
- * @ctrl: Pointer to the CPR3 controller
- * @new_volt: New voltage in microvolts that VDD supply needs to
- * end up at
- * @last_volt: Pointer to the last known voltage in microvolts for the
- * VDD supply
- * @aggr_corner: Pointer to the CPR3 corner which corresponds to the max
- * corner aggregated from all CPR3 threads managed by the
- * CPR3 controller
- *
- * This function requests a switch of the APM mode while guaranteeing
- * any LDO regulator hardware requirements are satisfied. The function must
- * be called once it is known a new VDD supply setpoint crosses the APM
- * voltage threshold.
- *
- * Return: 0 on success, errno on failure. If the VDD supply voltage is
- * modified, last_volt is updated to reflect the new voltage setpoint.
- */
- static int cpr3_regulator_switch_apm_mode(struct cpr3_controller *ctrl,
- int new_volt, int *last_volt,
- struct cpr3_corner *aggr_corner)
- {
- struct regulator *vdd = ctrl->vdd_regulator;
- int apm_volt = ctrl->apm_threshold_volt;
- int orig_last_volt = *last_volt;
- int rc;
- rc = regulator_set_voltage(vdd, apm_volt, apm_volt);
- if (rc) {
- cpr3_err(ctrl, "regulator_set_voltage(vdd) == %d failed, rc=%d\n",
- apm_volt, rc);
- return rc;
- }
- *last_volt = apm_volt;
- rc = cpr3_regulator_ldo_apm_prepare(ctrl, new_volt, *last_volt,
- aggr_corner);
- if (rc) {
- cpr3_err(ctrl, "unable to prepare LDO state for APM switch, rc=%d\n",
- rc);
- return rc;
- }
- rc = msm_apm_set_supply(ctrl->apm, new_volt >= apm_volt
- ? ctrl->apm_high_supply : ctrl->apm_low_supply);
- if (rc) {
- cpr3_err(ctrl, "APM switch failed, rc=%d\n", rc);
- /* Roll back the voltage. */
- regulator_set_voltage(vdd, orig_last_volt, INT_MAX);
- *last_volt = orig_last_volt;
- return rc;
- }
- return 0;
- }
- /**
- * cpr3_regulator_config_voltage_crossings() - configure APM and mem-acc
- * settings depending upon a new VDD supply setpoint
- *
- * @ctrl: Pointer to the CPR3 controller
- * @new_volt: New voltage in microvolts that VDD supply needs to
- * end up at
- * @last_volt: Pointer to the last known voltage in microvolts for the
- * VDD supply
- * @aggr_corner: Pointer to the CPR3 corner which corresponds to the max
- * corner aggregated from all CPR3 threads managed by the
- * CPR3 controller
- *
- * This function handles the APM and mem-acc regulator reconfiguration if
- * the new VDD supply voltage will result in crossing their respective voltage
- * thresholds.
- *
- * Return: 0 on success, errno on failure. If the VDD supply voltage is
- * modified, last_volt is updated to reflect the new voltage setpoint.
- */
- static int cpr3_regulator_config_voltage_crossings(struct cpr3_controller *ctrl,
- int new_volt, int *last_volt,
- struct cpr3_corner *aggr_corner)
- {
- bool apm_crossing = false, mem_acc_crossing = false;
- bool mem_acc_bhs_used;
- int apm_volt = ctrl->apm_threshold_volt;
- int mem_acc_volt = ctrl->mem_acc_threshold_volt;
- int ref_volt, rc;
- if (ctrl->apm && apm_volt > 0
- && ((*last_volt < apm_volt && apm_volt <= new_volt)
- || (*last_volt >= apm_volt && apm_volt > new_volt)))
- apm_crossing = true;
- mem_acc_bhs_used = cpr3_regulator_mem_acc_bhs_used(ctrl);
- ref_volt = ctrl->use_hw_closed_loop ? aggr_corner->floor_volt :
- new_volt;
- if (mem_acc_bhs_used &&
- (((*last_volt < mem_acc_volt && mem_acc_volt <= ref_volt) ||
- (*last_volt >= mem_acc_volt && mem_acc_volt > ref_volt))))
- mem_acc_crossing = true;
- if (apm_crossing && mem_acc_crossing) {
- if ((new_volt < *last_volt && apm_volt >= mem_acc_volt) ||
- (new_volt >= *last_volt && apm_volt < mem_acc_volt)) {
- rc = cpr3_regulator_switch_apm_mode(ctrl, new_volt,
- last_volt,
- aggr_corner);
- if (rc) {
- cpr3_err(ctrl, "unable to switch APM mode\n");
- return rc;
- }
- rc = cpr3_regulator_config_bhs_mem_acc(ctrl, new_volt,
- last_volt, aggr_corner);
- if (rc) {
- cpr3_err(ctrl, "unable to configure BHS mem-acc settings\n");
- return rc;
- }
- } else {
- rc = cpr3_regulator_config_bhs_mem_acc(ctrl, new_volt,
- last_volt, aggr_corner);
- if (rc) {
- cpr3_err(ctrl, "unable to configure BHS mem-acc settings\n");
- return rc;
- }
- rc = cpr3_regulator_switch_apm_mode(ctrl, new_volt,
- last_volt,
- aggr_corner);
- if (rc) {
- cpr3_err(ctrl, "unable to switch APM mode\n");
- return rc;
- }
- }
- } else if (apm_crossing) {
- rc = cpr3_regulator_switch_apm_mode(ctrl, new_volt, last_volt,
- aggr_corner);
- if (rc) {
- cpr3_err(ctrl, "unable to switch APM mode\n");
- return rc;
- }
- } else if (mem_acc_crossing) {
- rc = cpr3_regulator_config_bhs_mem_acc(ctrl, new_volt,
- last_volt, aggr_corner);
- if (rc) {
- cpr3_err(ctrl, "unable to configure BHS mem-acc settings\n");
- return rc;
- }
- }
- return 0;
- }
- /**
- * cpr3_regulator_config_mem_acc() - configure the corner of the mem-acc
- * regulator associated with the CPR3 controller
- * @ctrl: Pointer to the CPR3 controller
- * @aggr_corner: Pointer to the CPR3 corner which corresponds to the max
- * corner aggregated from all CPR3 threads managed by the
- * CPR3 controller
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_regulator_config_mem_acc(struct cpr3_controller *ctrl,
- struct cpr3_corner *aggr_corner)
- {
- int rc;
- if (ctrl->mem_acc_regulator && aggr_corner->mem_acc_volt) {
- rc = regulator_set_voltage(ctrl->mem_acc_regulator,
- aggr_corner->mem_acc_volt,
- aggr_corner->mem_acc_volt);
- if (rc) {
- cpr3_err(ctrl, "regulator_set_voltage(mem_acc) == %d failed, rc=%d\n",
- aggr_corner->mem_acc_volt, rc);
- return rc;
- }
- }
- return 0;
- }
- /**
- * cpr3_regulator_scale_vdd_voltage() - scale the CPR controlled VDD supply
- * voltage to the new level while satisfying any other hardware
- * requirements
- * @ctrl: Pointer to the CPR3 controller
- * @new_volt: New voltage in microvolts that VDD supply needs to end
- * up at
- * @last_volt: Last known voltage in microvolts for the VDD supply
- * @aggr_corner: Pointer to the CPR3 corner which corresponds to the max
- * corner aggregated from all CPR3 threads managed by the
- * CPR3 controller
- *
- * This function scales the CPR controlled VDD supply voltage from its
- * current level to the new voltage that is specified. If the supply is
- * configured to use the APM and the APM threshold is crossed as a result of
- * the voltage scaling, then this function also stops at the APM threshold,
- * switches the APM source, and finally sets the final new voltage.
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_regulator_scale_vdd_voltage(struct cpr3_controller *ctrl,
- int new_volt, int last_volt,
- struct cpr3_corner *aggr_corner)
- {
- struct regulator *vdd = ctrl->vdd_regulator;
- int rc;
- if (new_volt < last_volt) {
- if (ctrl->support_ldo300_vreg) {
- rc = cpr3_regulator_config_mem_acc(ctrl, aggr_corner);
- if (rc)
- return rc;
- }
- /* Decreasing VDD voltage */
- rc = cpr3_regulator_config_ldo(ctrl, aggr_corner->floor_volt,
- ctrl->aggr_corner.ceiling_volt,
- new_volt, last_volt);
- if (rc) {
- cpr3_err(ctrl, "unable to configure LDO state, rc=%d\n",
- rc);
- return rc;
- }
- if (!ctrl->support_ldo300_vreg) {
- rc = cpr3_regulator_config_mem_acc(ctrl, aggr_corner);
- if (rc)
- return rc;
- }
- } else {
- /* Increasing VDD voltage */
- if (ctrl->system_regulator) {
- rc = regulator_set_voltage(ctrl->system_regulator,
- aggr_corner->system_volt, INT_MAX);
- if (rc) {
- cpr3_err(ctrl, "regulator_set_voltage(system) == %d failed, rc=%d\n",
- aggr_corner->system_volt, rc);
- return rc;
- }
- }
- }
- rc = cpr3_regulator_config_voltage_crossings(ctrl, new_volt, &last_volt,
- aggr_corner);
- if (rc) {
- cpr3_err(ctrl, "unable to handle voltage threshold crossing configurations, rc=%d\n",
- rc);
- return rc;
- }
- /*
- * Subtract a small amount from the min_uV parameter so that the
- * set voltage request is not dropped by the framework due to being
- * duplicate. This is needed in order to switch from hardware
- * closed-loop to open-loop successfully.
- */
- rc = regulator_set_voltage(vdd, new_volt - (ctrl->cpr_enabled ? 0 : 1),
- aggr_corner->ceiling_volt);
- if (rc) {
- cpr3_err(ctrl, "regulator_set_voltage(vdd) == %d failed, rc=%d\n",
- new_volt, rc);
- return rc;
- }
- if (new_volt == last_volt && ctrl->supports_hw_closed_loop
- && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) {
- /*
- * CPR4 features enforce voltage reprogramming when the last
- * set voltage and new set voltage are same. This way, we can
- * ensure that SAW PMIC STATUS register is updated with newly
- * programmed voltage.
- */
- rc = regulator_sync_voltage(vdd);
- if (rc) {
- cpr3_err(ctrl, "regulator_sync_voltage(vdd) == %d failed, rc=%d\n",
- new_volt, rc);
- return rc;
- }
- }
- if (new_volt >= last_volt) {
- /* Increasing VDD voltage */
- rc = cpr3_regulator_config_ldo(ctrl, aggr_corner->floor_volt,
- aggr_corner->ceiling_volt,
- new_volt, new_volt);
- if (rc) {
- cpr3_err(ctrl, "unable to configure LDO state, rc=%d\n",
- rc);
- return rc;
- }
- rc = cpr3_regulator_config_mem_acc(ctrl, aggr_corner);
- if (rc)
- return rc;
- } else {
- /* Decreasing VDD voltage */
- if (ctrl->system_regulator) {
- rc = regulator_set_voltage(ctrl->system_regulator,
- aggr_corner->system_volt, INT_MAX);
- if (rc) {
- cpr3_err(ctrl, "regulator_set_voltage(system) == %d failed, rc=%d\n",
- aggr_corner->system_volt, rc);
- return rc;
- }
- }
- }
- return 0;
- }
- /**
- * cpr3_regulator_get_dynamic_floor_volt() - returns the current dynamic floor
- * voltage based upon static configurations and the state of all
- * power domains during the last CPR measurement
- * @ctrl: Pointer to the CPR3 controller
- * @reg_last_measurement: Value read from the LAST_MEASUREMENT register
- *
- * When using HW closed-loop, the dynamic floor voltage is always returned
- * regardless of the current state of the power domains.
- *
- * Return: dynamic floor voltage in microvolts or 0 if dynamic floor is not
- * currently required
- */
- static int cpr3_regulator_get_dynamic_floor_volt(struct cpr3_controller *ctrl,
- u32 reg_last_measurement)
- {
- int dynamic_floor_volt = 0;
- struct cpr3_regulator *vreg;
- bool valid, pd_valid;
- u32 bypass_bits;
- int i, j;
- if (!ctrl->supports_hw_closed_loop)
- return 0;
- if (likely(!ctrl->use_hw_closed_loop)) {
- valid = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_VALID);
- bypass_bits
- = (reg_last_measurement & CPR3_LAST_MEASUREMENT_PD_BYPASS_MASK)
- >> CPR3_LAST_MEASUREMENT_PD_BYPASS_SHIFT;
- } else {
- /*
- * Ensure that the dynamic floor voltage is always used for
- * HW closed-loop since the conditions below cannot be evaluated
- * after each CPR measurement.
- */
- valid = false;
- bypass_bits = 0;
- }
- for (i = 0; i < ctrl->thread_count; i++) {
- for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
- vreg = &ctrl->thread[i].vreg[j];
- if (!vreg->uses_dynamic_floor)
- continue;
- pd_valid = !((bypass_bits & vreg->pd_bypass_mask)
- == vreg->pd_bypass_mask);
- if (!valid || !pd_valid)
- dynamic_floor_volt = max(dynamic_floor_volt,
- vreg->corner[
- vreg->dynamic_floor_corner].last_volt);
- }
- }
- return dynamic_floor_volt;
- }
- /**
- * cpr3_regulator_max_sdelta_diff() - returns the maximum voltage difference in
- * microvolts that can result from different operating conditions
- * for the specified sdelta struct
- * @sdelta: Pointer to the sdelta structure
- * @step_volt: Step size in microvolts between available set
- * points of the VDD supply.
- *
- * Return: voltage difference between the highest and lowest adjustments if
- * sdelta and sdelta->table are valid, else 0.
- */
- static int cpr3_regulator_max_sdelta_diff(const struct cpr4_sdelta *sdelta,
- int step_volt)
- {
- int i, j, index, sdelta_min = INT_MAX, sdelta_max = INT_MIN;
- if (!sdelta || !sdelta->table)
- return 0;
- for (i = 0; i < sdelta->max_core_count; i++) {
- for (j = 0; j < sdelta->temp_band_count; j++) {
- index = i * sdelta->temp_band_count + j;
- sdelta_min = min(sdelta_min, sdelta->table[index]);
- sdelta_max = max(sdelta_max, sdelta->table[index]);
- }
- }
- return (sdelta_max - sdelta_min) * step_volt;
- }
- /**
- * cpr3_regulator_aggregate_sdelta() - check open-loop voltages of current
- * aggregated corner and current corner of a given regulator
- * and adjust the sdelta strucuture data of aggregate corner.
- * @aggr_corner: Pointer to accumulated aggregated corner which
- * is both an input and an output
- * @corner: Pointer to the corner to be aggregated with
- * aggr_corner
- * @step_volt: Step size in microvolts between available set
- * points of the VDD supply.
- *
- * Return: none
- */
- static void cpr3_regulator_aggregate_sdelta(
- struct cpr3_corner *aggr_corner,
- const struct cpr3_corner *corner, int step_volt)
- {
- struct cpr4_sdelta *aggr_sdelta, *sdelta;
- int aggr_core_count, core_count, temp_band_count;
- u32 aggr_index, index;
- int i, j, sdelta_size, cap_steps, adjust_sdelta;
- aggr_sdelta = aggr_corner->sdelta;
- sdelta = corner->sdelta;
- if (aggr_corner->open_loop_volt < corner->open_loop_volt) {
- /*
- * Found the new dominant regulator as its open-loop requirement
- * is higher than previous dominant regulator. Calculate cap
- * voltage to limit the SDELTA values to make sure the runtime
- * (Core-count/temp) adjustments do not violate other
- * regulators' voltage requirements. Use cpr4_sdelta values of
- * new dominant regulator.
- */
- aggr_sdelta->cap_volt = min(aggr_sdelta->cap_volt,
- (corner->open_loop_volt -
- aggr_corner->open_loop_volt));
- /* Clear old data in the sdelta table */
- sdelta_size = aggr_sdelta->max_core_count
- * aggr_sdelta->temp_band_count;
- if (aggr_sdelta->allow_core_count_adj
- || aggr_sdelta->allow_temp_adj)
- memset(aggr_sdelta->table, 0, sdelta_size
- * sizeof(*aggr_sdelta->table));
- if (sdelta->allow_temp_adj || sdelta->allow_core_count_adj) {
- /* Copy new data in sdelta table */
- sdelta_size = sdelta->max_core_count
- * sdelta->temp_band_count;
- if (sdelta->table)
- memcpy(aggr_sdelta->table, sdelta->table,
- sdelta_size * sizeof(*sdelta->table));
- }
- if (sdelta->allow_boost) {
- memcpy(aggr_sdelta->boost_table, sdelta->boost_table,
- sdelta->temp_band_count
- * sizeof(*sdelta->boost_table));
- aggr_sdelta->boost_num_cores = sdelta->boost_num_cores;
- } else if (aggr_sdelta->allow_boost) {
- for (i = 0; i < aggr_sdelta->temp_band_count; i++) {
- adjust_sdelta = (corner->open_loop_volt
- - aggr_corner->open_loop_volt)
- / step_volt;
- aggr_sdelta->boost_table[i] += adjust_sdelta;
- aggr_sdelta->boost_table[i]
- = min(aggr_sdelta->boost_table[i], 0);
- }
- }
- aggr_corner->open_loop_volt = corner->open_loop_volt;
- aggr_sdelta->allow_temp_adj = sdelta->allow_temp_adj;
- aggr_sdelta->allow_core_count_adj
- = sdelta->allow_core_count_adj;
- aggr_sdelta->max_core_count = sdelta->max_core_count;
- aggr_sdelta->temp_band_count = sdelta->temp_band_count;
- } else if (aggr_corner->open_loop_volt > corner->open_loop_volt) {
- /*
- * Adjust the cap voltage if the open-loop requirement of new
- * regulator is the next highest.
- */
- aggr_sdelta->cap_volt = min(aggr_sdelta->cap_volt,
- (aggr_corner->open_loop_volt
- - corner->open_loop_volt));
- if (sdelta->allow_boost) {
- for (i = 0; i < aggr_sdelta->temp_band_count; i++) {
- adjust_sdelta = (aggr_corner->open_loop_volt
- - corner->open_loop_volt)
- / step_volt;
- aggr_sdelta->boost_table[i] =
- sdelta->boost_table[i] + adjust_sdelta;
- aggr_sdelta->boost_table[i]
- = min(aggr_sdelta->boost_table[i], 0);
- }
- aggr_sdelta->boost_num_cores = sdelta->boost_num_cores;
- }
- } else {
- /*
- * Found another dominant regulator with same open-loop
- * requirement. Make cap voltage to '0'. Disable core-count
- * adjustments as we couldn't support for both regulators.
- * Keep enable temp based adjustments if enabled for both
- * regulators and choose mininum margin adjustment values
- * between them.
- */
- aggr_sdelta->cap_volt = 0;
- aggr_sdelta->allow_core_count_adj = false;
- if (aggr_sdelta->allow_temp_adj
- && sdelta->allow_temp_adj) {
- aggr_core_count = aggr_sdelta->max_core_count - 1;
- core_count = sdelta->max_core_count - 1;
- temp_band_count = sdelta->temp_band_count;
- for (j = 0; j < temp_band_count; j++) {
- aggr_index = aggr_core_count * temp_band_count
- + j;
- index = core_count * temp_band_count + j;
- aggr_sdelta->table[aggr_index] =
- min(aggr_sdelta->table[aggr_index],
- sdelta->table[index]);
- }
- } else {
- aggr_sdelta->allow_temp_adj = false;
- }
- if (sdelta->allow_boost) {
- memcpy(aggr_sdelta->boost_table, sdelta->boost_table,
- sdelta->temp_band_count
- * sizeof(*sdelta->boost_table));
- aggr_sdelta->boost_num_cores = sdelta->boost_num_cores;
- }
- }
- /* Keep non-dominant clients boost enable state */
- aggr_sdelta->allow_boost |= sdelta->allow_boost;
- if (aggr_sdelta->allow_boost)
- aggr_sdelta->allow_core_count_adj = false;
- if (aggr_sdelta->cap_volt && !(aggr_sdelta->cap_volt == INT_MAX)) {
- core_count = aggr_sdelta->max_core_count;
- temp_band_count = aggr_sdelta->temp_band_count;
- /*
- * Convert cap voltage from uV to PMIC steps and use to limit
- * sdelta margin adjustments.
- */
- cap_steps = aggr_sdelta->cap_volt / step_volt;
- for (i = 0; i < core_count; i++)
- for (j = 0; j < temp_band_count; j++) {
- index = i * temp_band_count + j;
- aggr_sdelta->table[index] =
- min(aggr_sdelta->table[index],
- cap_steps);
- }
- }
- }
- /**
- * cpr3_regulator_aggregate_corners() - aggregate two corners together
- * @aggr_corner: Pointer to accumulated aggregated corner which
- * is both an input and an output
- * @corner: Pointer to the corner to be aggregated with
- * aggr_corner
- * @aggr_quot: Flag indicating that target quotients should be
- * aggregated as well.
- * @step_volt: Step size in microvolts between available set
- * points of the VDD supply.
- *
- * Return: none
- */
- static void cpr3_regulator_aggregate_corners(struct cpr3_corner *aggr_corner,
- const struct cpr3_corner *corner, bool aggr_quot,
- int step_volt)
- {
- int i;
- aggr_corner->ceiling_volt
- = max(aggr_corner->ceiling_volt, corner->ceiling_volt);
- aggr_corner->floor_volt
- = max(aggr_corner->floor_volt, corner->floor_volt);
- aggr_corner->last_volt
- = max(aggr_corner->last_volt, corner->last_volt);
- aggr_corner->system_volt
- = max(aggr_corner->system_volt, corner->system_volt);
- aggr_corner->mem_acc_volt
- = max(aggr_corner->mem_acc_volt, corner->mem_acc_volt);
- aggr_corner->irq_en |= corner->irq_en;
- aggr_corner->use_open_loop |= corner->use_open_loop;
- aggr_corner->ldo_mode_allowed |= corner->ldo_mode_allowed;
- if (aggr_quot) {
- aggr_corner->ro_mask &= corner->ro_mask;
- for (i = 0; i < CPR3_RO_COUNT; i++)
- aggr_corner->target_quot[i]
- = max(aggr_corner->target_quot[i],
- corner->target_quot[i]);
- }
- if (aggr_corner->sdelta && corner->sdelta
- && (aggr_corner->sdelta->table
- || aggr_corner->sdelta->boost_table)) {
- cpr3_regulator_aggregate_sdelta(aggr_corner, corner, step_volt);
- } else {
- aggr_corner->open_loop_volt
- = max(aggr_corner->open_loop_volt,
- corner->open_loop_volt);
- }
- }
- /**
- * cpr3_regulator_update_ctrl_state() - update the state of the CPR controller
- * to reflect the corners used by all CPR3 regulators as well as
- * the CPR operating mode
- * @ctrl: Pointer to the CPR3 controller
- *
- * This function aggregates the CPR parameters for all CPR3 regulators
- * associated with the VDD supply. Upon success, it sets the aggregated last
- * known good voltage.
- *
- * The VDD supply voltage will not be physically configured unless this
- * condition is met by at least one of the regulators of the controller:
- * regulator->vreg_enabled == true &&
- * regulator->current_corner != CPR3_REGULATOR_CORNER_INVALID
- *
- * CPR registers for the controller and each thread are updated as long as
- * ctrl->cpr_enabled == true.
- *
- * Note, CPR3 controller lock must be held by the caller.
- *
- * Return: 0 on success, errno on failure
- */
- static int _cpr3_regulator_update_ctrl_state(struct cpr3_controller *ctrl)
- {
- struct cpr3_corner aggr_corner = {};
- struct cpr3_thread *thread;
- struct cpr3_regulator *vreg;
- struct cpr4_sdelta *sdelta;
- bool valid = false;
- bool thread_valid;
- int i, j, rc, new_volt, vdd_volt, dynamic_floor_volt;
- int last_corner_volt = 0;
- u32 reg_last_measurement = 0, sdelta_size;
- int *sdelta_table, *boost_table;
- if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) {
- rc = cpr3_ctrl_clear_cpr4_config(ctrl);
- if (rc) {
- cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n",
- rc);
- return rc;
- }
- }
- cpr3_ctrl_loop_disable(ctrl);
- vdd_volt = regulator_get_voltage(ctrl->vdd_regulator);
- if (vdd_volt < 0) {
- cpr3_err(ctrl, "regulator_get_voltage(vdd) failed, rc=%d\n",
- vdd_volt);
- return vdd_volt;
- }
- if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) {
- /*
- * Save aggregated corner open-loop voltage which was programmed
- * during last corner switch which is used when programming new
- * aggregated corner open-loop voltage.
- */
- last_corner_volt = ctrl->aggr_corner.open_loop_volt;
- }
- if (ctrl->cpr_enabled && ctrl->use_hw_closed_loop &&
- ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3)
- reg_last_measurement
- = cpr3_read(ctrl, CPR3_REG_LAST_MEASUREMENT);
- aggr_corner.sdelta = ctrl->aggr_corner.sdelta;
- if (aggr_corner.sdelta) {
- sdelta = aggr_corner.sdelta;
- sdelta_table = sdelta->table;
- if (sdelta_table) {
- sdelta_size = sdelta->max_core_count *
- sdelta->temp_band_count;
- memset(sdelta_table, 0, sdelta_size
- * sizeof(*sdelta_table));
- }
- boost_table = sdelta->boost_table;
- if (boost_table)
- memset(boost_table, 0, sdelta->temp_band_count
- * sizeof(*boost_table));
- memset(sdelta, 0, sizeof(*sdelta));
- sdelta->table = sdelta_table;
- sdelta->cap_volt = INT_MAX;
- sdelta->boost_table = boost_table;
- }
- /* Aggregate the requests of all threads */
- for (i = 0; i < ctrl->thread_count; i++) {
- thread = &ctrl->thread[i];
- thread_valid = false;
- sdelta = thread->aggr_corner.sdelta;
- if (sdelta) {
- sdelta_table = sdelta->table;
- if (sdelta_table) {
- sdelta_size = sdelta->max_core_count *
- sdelta->temp_band_count;
- memset(sdelta_table, 0, sdelta_size
- * sizeof(*sdelta_table));
- }
- boost_table = sdelta->boost_table;
- if (boost_table)
- memset(boost_table, 0, sdelta->temp_band_count
- * sizeof(*boost_table));
- memset(sdelta, 0, sizeof(*sdelta));
- sdelta->table = sdelta_table;
- sdelta->cap_volt = INT_MAX;
- sdelta->boost_table = boost_table;
- }
- memset(&thread->aggr_corner, 0, sizeof(thread->aggr_corner));
- thread->aggr_corner.sdelta = sdelta;
- thread->aggr_corner.ro_mask = CPR3_RO_MASK;
- for (j = 0; j < thread->vreg_count; j++) {
- vreg = &thread->vreg[j];
- if (ctrl->cpr_enabled && ctrl->use_hw_closed_loop)
- cpr3_update_vreg_closed_loop_volt(vreg,
- vdd_volt, reg_last_measurement);
- if (!vreg->vreg_enabled
- || vreg->current_corner
- == CPR3_REGULATOR_CORNER_INVALID) {
- /* Cannot participate in aggregation. */
- vreg->aggregated = false;
- continue;
- } else {
- vreg->aggregated = true;
- thread_valid = true;
- }
- cpr3_regulator_aggregate_corners(&thread->aggr_corner,
- &vreg->corner[vreg->current_corner],
- true, ctrl->step_volt);
- }
- valid |= thread_valid;
- if (thread_valid)
- cpr3_regulator_aggregate_corners(&aggr_corner,
- &thread->aggr_corner,
- false, ctrl->step_volt);
- }
- if (valid && ctrl->cpr_allowed_hw && ctrl->cpr_allowed_sw) {
- rc = cpr3_closed_loop_enable(ctrl);
- if (rc) {
- cpr3_err(ctrl, "could not enable CPR, rc=%d\n", rc);
- return rc;
- }
- } else {
- rc = cpr3_closed_loop_disable(ctrl);
- if (rc) {
- cpr3_err(ctrl, "could not disable CPR, rc=%d\n", rc);
- return rc;
- }
- }
- /* No threads are enabled with a valid corner so exit. */
- if (!valid)
- return 0;
- /*
- * When using CPR hardware closed-loop, the voltage may vary anywhere
- * between the floor and ceiling voltage without software notification.
- * Therefore, it is required that the floor to ceiling range for the
- * aggregated corner not intersect the APM threshold voltage. Adjust
- * the floor to ceiling range if this requirement is violated.
- *
- * The following algorithm is applied in the case that
- * floor < threshold <= ceiling:
- * if open_loop >= threshold - adj, then floor = threshold
- * else ceiling = threshold - step
- * where adj = an adjustment factor to ensure sufficient voltage margin
- * and step = VDD output step size
- *
- * The open-loop and last known voltages are also bounded by the new
- * floor or ceiling value as needed.
- */
- if (ctrl->use_hw_closed_loop
- && aggr_corner.ceiling_volt >= ctrl->apm_threshold_volt
- && aggr_corner.floor_volt < ctrl->apm_threshold_volt) {
- if (aggr_corner.open_loop_volt
- >= ctrl->apm_threshold_volt - ctrl->apm_adj_volt)
- aggr_corner.floor_volt = ctrl->apm_threshold_volt;
- else
- aggr_corner.ceiling_volt
- = ctrl->apm_threshold_volt - ctrl->step_volt;
- aggr_corner.last_volt
- = max(aggr_corner.last_volt, aggr_corner.floor_volt);
- aggr_corner.last_volt
- = min(aggr_corner.last_volt, aggr_corner.ceiling_volt);
- aggr_corner.open_loop_volt
- = max(aggr_corner.open_loop_volt, aggr_corner.floor_volt);
- aggr_corner.open_loop_volt
- = min(aggr_corner.open_loop_volt, aggr_corner.ceiling_volt);
- }
- if (ctrl->use_hw_closed_loop
- && aggr_corner.ceiling_volt >= ctrl->mem_acc_threshold_volt
- && aggr_corner.floor_volt < ctrl->mem_acc_threshold_volt) {
- aggr_corner.floor_volt = ctrl->mem_acc_threshold_volt;
- aggr_corner.last_volt = max(aggr_corner.last_volt,
- aggr_corner.floor_volt);
- aggr_corner.open_loop_volt = max(aggr_corner.open_loop_volt,
- aggr_corner.floor_volt);
- }
- if (ctrl->use_hw_closed_loop) {
- dynamic_floor_volt
- = cpr3_regulator_get_dynamic_floor_volt(ctrl,
- reg_last_measurement);
- if (aggr_corner.floor_volt < dynamic_floor_volt) {
- aggr_corner.floor_volt = dynamic_floor_volt;
- aggr_corner.last_volt = max(aggr_corner.last_volt,
- aggr_corner.floor_volt);
- aggr_corner.open_loop_volt
- = max(aggr_corner.open_loop_volt,
- aggr_corner.floor_volt);
- aggr_corner.ceiling_volt = max(aggr_corner.ceiling_volt,
- aggr_corner.floor_volt);
- }
- }
- if (ctrl->cpr_enabled && ctrl->last_corner_was_closed_loop) {
- /*
- * Always program open-loop voltage for CPR4 controllers which
- * support hardware closed-loop. Storing the last closed loop
- * voltage in corner structure can still help with debugging.
- */
- if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3)
- new_volt = aggr_corner.last_volt;
- else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4
- && ctrl->supports_hw_closed_loop)
- new_volt = aggr_corner.open_loop_volt;
- else
- new_volt = min(aggr_corner.last_volt +
- cpr3_regulator_max_sdelta_diff(aggr_corner.sdelta,
- ctrl->step_volt),
- aggr_corner.ceiling_volt);
- } else {
- new_volt = aggr_corner.open_loop_volt;
- aggr_corner.last_volt = aggr_corner.open_loop_volt;
- }
- if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4
- && ctrl->supports_hw_closed_loop) {
- /*
- * Store last aggregated corner open-loop voltage in vdd_volt
- * which is used when programming current aggregated corner
- * required voltage.
- */
- vdd_volt = last_corner_volt;
- }
- cpr3_debug(ctrl, "setting new voltage=%d uV\n", new_volt);
- rc = cpr3_regulator_scale_vdd_voltage(ctrl, new_volt,
- vdd_volt, &aggr_corner);
- if (rc) {
- cpr3_err(ctrl, "vdd voltage scaling failed, rc=%d\n", rc);
- return rc;
- }
- /* Only update registers if CPR is enabled. */
- if (ctrl->cpr_enabled) {
- if (ctrl->use_hw_closed_loop) {
- /* Hardware closed-loop */
- /* Set ceiling and floor limits in hardware */
- rc = regulator_set_voltage(ctrl->vdd_limit_regulator,
- aggr_corner.floor_volt,
- aggr_corner.ceiling_volt);
- if (rc) {
- cpr3_err(ctrl, "could not configure HW closed-loop voltage limits, rc=%d\n",
- rc);
- return rc;
- }
- } else {
- /* Software closed-loop */
- /*
- * Disable UP or DOWN interrupts when at ceiling or
- * floor respectively.
- */
- if (new_volt == aggr_corner.floor_volt)
- aggr_corner.irq_en &= ~CPR3_IRQ_DOWN;
- if (new_volt == aggr_corner.ceiling_volt)
- aggr_corner.irq_en &= ~CPR3_IRQ_UP;
- cpr3_write(ctrl, CPR3_REG_IRQ_CLEAR,
- CPR3_IRQ_UP | CPR3_IRQ_DOWN);
- cpr3_write(ctrl, CPR3_REG_IRQ_EN, aggr_corner.irq_en);
- }
- for (i = 0; i < ctrl->thread_count; i++) {
- cpr3_regulator_set_target_quot(&ctrl->thread[i]);
- for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
- vreg = &ctrl->thread[i].vreg[j];
- if (vreg->vreg_enabled)
- vreg->last_closed_loop_corner
- = vreg->current_corner;
- }
- }
- if (ctrl->proc_clock_throttle) {
- if (aggr_corner.ceiling_volt > aggr_corner.floor_volt
- && (ctrl->use_hw_closed_loop
- || new_volt < aggr_corner.ceiling_volt))
- cpr3_write(ctrl, CPR3_REG_PD_THROTTLE,
- ctrl->proc_clock_throttle);
- else
- cpr3_write(ctrl, CPR3_REG_PD_THROTTLE,
- CPR3_PD_THROTTLE_DISABLE);
- }
- /*
- * Ensure that all CPR register writes complete before
- * re-enabling CPR loop operation.
- */
- wmb();
- } else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4
- && ctrl->vdd_limit_regulator) {
- /* Set ceiling and floor limits in hardware */
- rc = regulator_set_voltage(ctrl->vdd_limit_regulator,
- aggr_corner.floor_volt,
- aggr_corner.ceiling_volt);
- if (rc) {
- cpr3_err(ctrl, "could not configure HW closed-loop voltage limits, rc=%d\n",
- rc);
- return rc;
- }
- }
- ctrl->aggr_corner = aggr_corner;
- if (ctrl->allow_core_count_adj || ctrl->allow_temp_adj
- || ctrl->allow_boost) {
- rc = cpr3_controller_program_sdelta(ctrl);
- if (rc) {
- cpr3_err(ctrl, "failed to program sdelta, rc=%d\n", rc);
- return rc;
- }
- }
- /*
- * Only enable the CPR controller if it is possible to set more than
- * one vdd-supply voltage.
- */
- if (aggr_corner.ceiling_volt > aggr_corner.floor_volt &&
- !aggr_corner.use_open_loop)
- cpr3_ctrl_loop_enable(ctrl);
- ctrl->last_corner_was_closed_loop = ctrl->cpr_enabled;
- cpr3_debug(ctrl, "CPR configuration updated\n");
- return 0;
- }
- /**
- * cpr3_regulator_wait_for_idle() - wait for the CPR controller to no longer be
- * busy
- * @ctrl: Pointer to the CPR3 controller
- * @max_wait_ns: Max wait time in nanoseconds
- *
- * Return: 0 on success or -ETIMEDOUT if the controller was still busy after
- * the maximum delay time
- */
- static int cpr3_regulator_wait_for_idle(struct cpr3_controller *ctrl,
- s64 max_wait_ns)
- {
- ktime_t start, end;
- s64 time_ns;
- u32 reg;
- /*
- * Ensure that all previous CPR register writes have completed before
- * checking the status register.
- */
- mb();
- start = ktime_get();
- do {
- end = ktime_get();
- time_ns = ktime_to_ns(ktime_sub(end, start));
- if (time_ns > max_wait_ns) {
- cpr3_err(ctrl, "CPR controller still busy after %lld us\n",
- div_s64(time_ns, 1000));
- return -ETIMEDOUT;
- }
- usleep_range(50, 100);
- reg = cpr3_read(ctrl, CPR3_REG_CPR_STATUS);
- } while (reg & CPR3_CPR_STATUS_BUSY_MASK);
- return 0;
- }
- /**
- * cmp_int() - int comparison function to be passed into the sort() function
- * which leads to ascending sorting
- * @a: First int value
- * @b: Second int value
- *
- * Return: >0 if a > b, 0 if a == b, <0 if a < b
- */
- static int cmp_int(const void *a, const void *b)
- {
- return *(int *)a - *(int *)b;
- }
- /**
- * cpr3_regulator_measure_aging() - measure the quotient difference for the
- * specified CPR aging sensor
- * @ctrl: Pointer to the CPR3 controller
- * @aging_sensor: Aging sensor to measure
- *
- * Note that vdd-supply must be configured to the aging reference voltage before
- * calling this function.
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_regulator_measure_aging(struct cpr3_controller *ctrl,
- struct cpr3_aging_sensor_info *aging_sensor)
- {
- u32 mask, reg, result, quot_min, quot_max, sel_min, sel_max;
- u32 quot_min_scaled, quot_max_scaled;
- u32 gcnt, gcnt_ref, gcnt0_restore, gcnt1_restore, irq_restore;
- u32 ro_mask_restore, cont_dly_restore, up_down_dly_restore = 0;
- int quot_delta, quot_delta_scaled, quot_delta_scaled_sum;
- int *quot_delta_results;
- int rc, rc2, i, aging_measurement_count, filtered_count;
- bool is_aging_measurement;
- quot_delta_results = kcalloc(CPR3_AGING_MEASUREMENT_ITERATIONS,
- sizeof(*quot_delta_results), GFP_KERNEL);
- if (!quot_delta_results)
- return -ENOMEM;
- if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) {
- rc = cpr3_ctrl_clear_cpr4_config(ctrl);
- if (rc) {
- cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n",
- rc);
- kfree(quot_delta_results);
- return rc;
- }
- }
- cpr3_ctrl_loop_disable(ctrl);
- /* Enable up, down, and mid CPR interrupts */
- irq_restore = cpr3_read(ctrl, CPR3_REG_IRQ_EN);
- cpr3_write(ctrl, CPR3_REG_IRQ_EN,
- CPR3_IRQ_UP | CPR3_IRQ_DOWN | CPR3_IRQ_MID);
- /* Ensure that the aging sensor is assigned to CPR thread 0 */
- cpr3_write(ctrl, CPR3_REG_SENSOR_OWNER(aging_sensor->sensor_id), 0);
- /* Switch from HW to SW closed-loop if necessary */
- if (ctrl->supports_hw_closed_loop) {
- if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4 ||
- ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH) {
- cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
- CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK,
- CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE);
- } else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) {
- cpr3_write(ctrl, CPR3_REG_HW_CLOSED_LOOP,
- CPR3_HW_CLOSED_LOOP_DISABLE);
- }
- }
- /* Configure the GCNT for RO0 and RO1 that are used for aging */
- gcnt0_restore = cpr3_read(ctrl, CPR3_REG_GCNT(0));
- gcnt1_restore = cpr3_read(ctrl, CPR3_REG_GCNT(1));
- gcnt_ref = cpr3_regulator_get_gcnt(ctrl);
- gcnt = gcnt_ref * 3 / 2;
- cpr3_write(ctrl, CPR3_REG_GCNT(0), gcnt);
- cpr3_write(ctrl, CPR3_REG_GCNT(1), gcnt);
- /* Unmask all RO's */
- ro_mask_restore = cpr3_read(ctrl, CPR3_REG_RO_MASK(0));
- cpr3_write(ctrl, CPR3_REG_RO_MASK(0), 0);
- /*
- * Mask all sensors except for the one to measure and bypass all
- * sensors in collapsible domains.
- */
- for (i = 0; i <= ctrl->sensor_count / 32; i++) {
- mask = GENMASK(min(31, ctrl->sensor_count - i * 32), 0);
- if (aging_sensor->sensor_id / 32 >= i
- && aging_sensor->sensor_id / 32 < (i + 1))
- mask &= ~BIT(aging_sensor->sensor_id % 32);
- cpr3_write(ctrl, CPR3_REG_SENSOR_MASK_WRITE_BANK(i), mask);
- cpr3_write(ctrl, CPR3_REG_SENSOR_BYPASS_WRITE_BANK(i),
- aging_sensor->bypass_mask[i]);
- }
- /* Set CPR loop delays to 0 us */
- if (ctrl->supports_hw_closed_loop
- && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) {
- cont_dly_restore = cpr3_read(ctrl, CPR3_REG_CPR_TIMER_MID_CONT);
- up_down_dly_restore = cpr3_read(ctrl,
- CPR3_REG_CPR_TIMER_UP_DN_CONT);
- cpr3_write(ctrl, CPR3_REG_CPR_TIMER_MID_CONT, 0);
- cpr3_write(ctrl, CPR3_REG_CPR_TIMER_UP_DN_CONT, 0);
- } else {
- cont_dly_restore = cpr3_read(ctrl,
- CPR3_REG_CPR_TIMER_AUTO_CONT);
- cpr3_write(ctrl, CPR3_REG_CPR_TIMER_AUTO_CONT, 0);
- }
- /* Set count mode to all-at-once min with no repeat */
- cpr3_masked_write(ctrl, CPR3_REG_CPR_CTL,
- CPR3_CPR_CTL_COUNT_MODE_MASK | CPR3_CPR_CTL_COUNT_REPEAT_MASK,
- CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_MIN
- << CPR3_CPR_CTL_COUNT_MODE_SHIFT);
- cpr3_ctrl_loop_enable(ctrl);
- rc = cpr3_regulator_wait_for_idle(ctrl,
- CPR3_AGING_MEASUREMENT_TIMEOUT_NS);
- if (rc)
- goto cleanup;
- /* Set count mode to all-at-once aging */
- cpr3_masked_write(ctrl, CPR3_REG_CPR_CTL, CPR3_CPR_CTL_COUNT_MODE_MASK,
- CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_AGE
- << CPR3_CPR_CTL_COUNT_MODE_SHIFT);
- aging_measurement_count = 0;
- for (i = 0; i < CPR3_AGING_MEASUREMENT_ITERATIONS; i++) {
- /* Send CONT_NACK */
- cpr3_write(ctrl, CPR3_REG_CONT_CMD, CPR3_CONT_CMD_NACK);
- rc = cpr3_regulator_wait_for_idle(ctrl,
- CPR3_AGING_MEASUREMENT_TIMEOUT_NS);
- if (rc)
- goto cleanup;
- /* Check for PAGE_IS_AGE flag in status register */
- reg = cpr3_read(ctrl, CPR3_REG_CPR_STATUS);
- is_aging_measurement
- = reg & CPR3_CPR_STATUS_AGING_MEASUREMENT_MASK;
- /* Read CPR measurement results */
- result = cpr3_read(ctrl, CPR3_REG_RESULT1(0));
- quot_min = (result & CPR3_RESULT1_QUOT_MIN_MASK)
- >> CPR3_RESULT1_QUOT_MIN_SHIFT;
- quot_max = (result & CPR3_RESULT1_QUOT_MAX_MASK)
- >> CPR3_RESULT1_QUOT_MAX_SHIFT;
- sel_min = (result & CPR3_RESULT1_RO_MIN_MASK)
- >> CPR3_RESULT1_RO_MIN_SHIFT;
- sel_max = (result & CPR3_RESULT1_RO_MAX_MASK)
- >> CPR3_RESULT1_RO_MAX_SHIFT;
- /*
- * Scale the quotients so that they are equivalent to the fused
- * values. This accounts for the difference in measurement
- * interval times.
- */
- quot_min_scaled = quot_min * (gcnt_ref + 1) / (gcnt + 1);
- quot_max_scaled = quot_max * (gcnt_ref + 1) / (gcnt + 1);
- if (sel_max == 1) {
- quot_delta = quot_max - quot_min;
- quot_delta_scaled = quot_max_scaled - quot_min_scaled;
- } else {
- quot_delta = quot_min - quot_max;
- quot_delta_scaled = quot_min_scaled - quot_max_scaled;
- }
- if (is_aging_measurement)
- quot_delta_results[aging_measurement_count++]
- = quot_delta_scaled;
- cpr3_debug(ctrl, "aging results: page_is_age=%u, sel_min=%u, sel_max=%u, quot_min=%u, quot_max=%u, quot_delta=%d, quot_min_scaled=%u, quot_max_scaled=%u, quot_delta_scaled=%d\n",
- is_aging_measurement, sel_min, sel_max, quot_min,
- quot_max, quot_delta, quot_min_scaled, quot_max_scaled,
- quot_delta_scaled);
- }
- filtered_count
- = aging_measurement_count - CPR3_AGING_MEASUREMENT_FILTER * 2;
- if (filtered_count > 0) {
- sort(quot_delta_results, aging_measurement_count,
- sizeof(*quot_delta_results), cmp_int, NULL);
- quot_delta_scaled_sum = 0;
- for (i = 0; i < filtered_count; i++)
- quot_delta_scaled_sum
- += quot_delta_results[i
- + CPR3_AGING_MEASUREMENT_FILTER];
- aging_sensor->measured_quot_diff
- = quot_delta_scaled_sum / filtered_count;
- cpr3_info(ctrl, "average quotient delta=%d (count=%d)\n",
- aging_sensor->measured_quot_diff,
- filtered_count);
- } else {
- cpr3_err(ctrl, "%d aging measurements completed after %d iterations\n",
- aging_measurement_count,
- CPR3_AGING_MEASUREMENT_ITERATIONS);
- rc = -EBUSY;
- }
- cleanup:
- kfree(quot_delta_results);
- if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) {
- rc2 = cpr3_ctrl_clear_cpr4_config(ctrl);
- if (rc2) {
- cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n",
- rc2);
- rc = rc2;
- }
- }
- cpr3_ctrl_loop_disable(ctrl);
- cpr3_write(ctrl, CPR3_REG_IRQ_EN, irq_restore);
- cpr3_write(ctrl, CPR3_REG_RO_MASK(0), ro_mask_restore);
- cpr3_write(ctrl, CPR3_REG_GCNT(0), gcnt0_restore);
- cpr3_write(ctrl, CPR3_REG_GCNT(1), gcnt1_restore);
- if (ctrl->supports_hw_closed_loop
- && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) {
- cpr3_write(ctrl, CPR3_REG_CPR_TIMER_MID_CONT, cont_dly_restore);
- cpr3_write(ctrl, CPR3_REG_CPR_TIMER_UP_DN_CONT,
- up_down_dly_restore);
- } else {
- cpr3_write(ctrl, CPR3_REG_CPR_TIMER_AUTO_CONT,
- cont_dly_restore);
- }
- for (i = 0; i <= ctrl->sensor_count / 32; i++) {
- cpr3_write(ctrl, CPR3_REG_SENSOR_MASK_WRITE_BANK(i), 0);
- cpr3_write(ctrl, CPR3_REG_SENSOR_BYPASS_WRITE_BANK(i), 0);
- }
- cpr3_masked_write(ctrl, CPR3_REG_CPR_CTL,
- CPR3_CPR_CTL_COUNT_MODE_MASK | CPR3_CPR_CTL_COUNT_REPEAT_MASK,
- (ctrl->count_mode << CPR3_CPR_CTL_COUNT_MODE_SHIFT)
- | (ctrl->count_repeat << CPR3_CPR_CTL_COUNT_REPEAT_SHIFT));
- cpr3_write(ctrl, CPR3_REG_SENSOR_OWNER(aging_sensor->sensor_id),
- ctrl->sensor_owner[aging_sensor->sensor_id]);
- cpr3_write(ctrl, CPR3_REG_IRQ_CLEAR,
- CPR3_IRQ_UP | CPR3_IRQ_DOWN | CPR3_IRQ_MID);
- if (ctrl->supports_hw_closed_loop) {
- if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4 ||
- ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH) {
- cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
- CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK,
- ctrl->use_hw_closed_loop
- ? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE
- : CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE);
- } else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) {
- cpr3_write(ctrl, CPR3_REG_HW_CLOSED_LOOP,
- ctrl->use_hw_closed_loop
- ? CPR3_HW_CLOSED_LOOP_ENABLE
- : CPR3_HW_CLOSED_LOOP_DISABLE);
- }
- }
- return rc;
- }
- /**
- * cpr3_regulator_readjust_volt_and_quot() - readjust the target quotients as
- * well as the floor, ceiling, and open-loop voltages for the
- * regulator by removing the old adjustment and adding the new one
- * @vreg: Pointer to the CPR3 regulator
- * @old_adjust_volt: Old aging adjustment voltage in microvolts
- * @new_adjust_volt: New aging adjustment voltage in microvolts
- *
- * Also reset the cached closed loop voltage (last_volt) to equal the open-loop
- * voltage for each corner.
- *
- * Return: None
- */
- static void cpr3_regulator_readjust_volt_and_quot(struct cpr3_regulator *vreg,
- int old_adjust_volt, int new_adjust_volt)
- {
- unsigned long long temp;
- int i, j, old_volt, new_volt, rounded_volt;
- if (!vreg->aging_allowed)
- return;
- for (i = 0; i < vreg->corner_count; i++) {
- temp = (unsigned long long)old_adjust_volt
- * (unsigned long long)vreg->corner[i].aging_derate;
- do_div(temp, 1000);
- old_volt = temp;
- temp = (unsigned long long)new_adjust_volt
- * (unsigned long long)vreg->corner[i].aging_derate;
- do_div(temp, 1000);
- new_volt = temp;
- old_volt = min(vreg->aging_max_adjust_volt, old_volt);
- new_volt = min(vreg->aging_max_adjust_volt, new_volt);
- for (j = 0; j < CPR3_RO_COUNT; j++) {
- if (vreg->corner[i].target_quot[j] != 0) {
- vreg->corner[i].target_quot[j]
- += cpr3_quot_adjustment(
- vreg->corner[i].ro_scale[j],
- new_volt)
- - cpr3_quot_adjustment(
- vreg->corner[i].ro_scale[j],
- old_volt);
- }
- }
- rounded_volt = CPR3_ROUND(new_volt,
- vreg->thread->ctrl->step_volt);
- if (!vreg->aging_allow_open_loop_adj)
- rounded_volt = 0;
- vreg->corner[i].ceiling_volt
- = vreg->corner[i].unaged_ceiling_volt + rounded_volt;
- vreg->corner[i].ceiling_volt = min(vreg->corner[i].ceiling_volt,
- vreg->corner[i].abs_ceiling_volt);
- vreg->corner[i].floor_volt
- = vreg->corner[i].unaged_floor_volt + rounded_volt;
- vreg->corner[i].floor_volt = min(vreg->corner[i].floor_volt,
- vreg->corner[i].ceiling_volt);
- vreg->corner[i].open_loop_volt
- = vreg->corner[i].unaged_open_loop_volt + rounded_volt;
- vreg->corner[i].open_loop_volt
- = min(vreg->corner[i].open_loop_volt,
- vreg->corner[i].ceiling_volt);
- vreg->corner[i].last_volt = vreg->corner[i].open_loop_volt;
- cpr3_debug(vreg, "corner %d: applying %d uV closed-loop and %d uV open-loop voltage margin adjustment\n",
- i, new_volt, rounded_volt);
- }
- }
- /**
- * cpr3_regulator_set_aging_ref_adjustment() - adjust target quotients for the
- * regulators managed by this CPR controller to account for aging
- * @ctrl: Pointer to the CPR3 controller
- * @ref_adjust_volt: New aging reference adjustment voltage in microvolts to
- * apply to all regulators managed by this CPR controller
- *
- * The existing aging adjustment as defined by ctrl->aging_ref_adjust_volt is
- * first removed and then the adjustment is applied. Lastly, the value of
- * ctrl->aging_ref_adjust_volt is updated to ref_adjust_volt.
- */
- static void cpr3_regulator_set_aging_ref_adjustment(
- struct cpr3_controller *ctrl, int ref_adjust_volt)
- {
- struct cpr3_regulator *vreg;
- int i, j;
- for (i = 0; i < ctrl->thread_count; i++) {
- for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
- vreg = &ctrl->thread[i].vreg[j];
- cpr3_regulator_readjust_volt_and_quot(vreg,
- ctrl->aging_ref_adjust_volt, ref_adjust_volt);
- if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH)
- cprh_adjust_voltages_for_apm(vreg);
- }
- }
- ctrl->aging_ref_adjust_volt = ref_adjust_volt;
- }
- /**
- * cpr3_regulator_aging_adjust() - adjust the target quotients for regulators
- * based on the output of CPR aging sensors
- * @ctrl: Pointer to the CPR3 controller
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_regulator_aging_adjust(struct cpr3_controller *ctrl)
- {
- struct cpr3_regulator *vreg;
- struct cpr3_corner restore_aging_corner;
- struct cpr3_corner *corner;
- int *restore_current_corner;
- bool *restore_vreg_enabled;
- int i, j, id, rc, rc2, vreg_count, aging_volt, max_aging_volt = 0;
- u32 reg;
- if (!ctrl->aging_required || !ctrl->cpr_enabled
- || ctrl->aggr_corner.ceiling_volt == 0
- || ctrl->aggr_corner.ceiling_volt > ctrl->aging_ref_volt)
- return 0;
- for (i = 0, vreg_count = 0; i < ctrl->thread_count; i++) {
- for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
- vreg = &ctrl->thread[i].vreg[j];
- vreg_count++;
- if (vreg->aging_allowed && vreg->vreg_enabled
- && vreg->current_corner > vreg->aging_corner)
- return 0;
- }
- }
- /* Verify that none of the aging sensors are currently masked. */
- for (i = 0; i < ctrl->aging_sensor_count; i++) {
- id = ctrl->aging_sensor[i].sensor_id;
- reg = cpr3_read(ctrl, CPR3_REG_SENSOR_MASK_READ(id));
- if (reg & BIT(id % 32))
- return 0;
- }
- /*
- * Verify that the aging possible register (if specified) has an
- * acceptable value.
- */
- if (ctrl->aging_possible_reg) {
- reg = readl_relaxed(ctrl->aging_possible_reg);
- reg &= ctrl->aging_possible_mask;
- if (reg != ctrl->aging_possible_val)
- return 0;
- }
- restore_current_corner = kcalloc(vreg_count,
- sizeof(*restore_current_corner), GFP_KERNEL);
- restore_vreg_enabled = kcalloc(vreg_count,
- sizeof(*restore_vreg_enabled), GFP_KERNEL);
- if (!restore_current_corner || !restore_vreg_enabled) {
- kfree(restore_current_corner);
- kfree(restore_vreg_enabled);
- return -ENOMEM;
- }
- /* Force all regulators to the aging corner */
- for (i = 0, vreg_count = 0; i < ctrl->thread_count; i++) {
- for (j = 0; j < ctrl->thread[i].vreg_count; j++, vreg_count++) {
- vreg = &ctrl->thread[i].vreg[j];
- restore_current_corner[vreg_count]
- = vreg->current_corner;
- restore_vreg_enabled[vreg_count]
- = vreg->vreg_enabled;
- vreg->current_corner = vreg->aging_corner;
- vreg->vreg_enabled = true;
- }
- }
- /* Force one of the regulators to require the aging reference voltage */
- vreg = &ctrl->thread[0].vreg[0];
- corner = &vreg->corner[vreg->current_corner];
- restore_aging_corner = *corner;
- corner->ceiling_volt = ctrl->aging_ref_volt;
- corner->floor_volt = ctrl->aging_ref_volt;
- corner->open_loop_volt = ctrl->aging_ref_volt;
- corner->last_volt = ctrl->aging_ref_volt;
- /* Skip last_volt caching */
- ctrl->last_corner_was_closed_loop = false;
- /* Set the vdd supply voltage to the aging reference voltage */
- rc = _cpr3_regulator_update_ctrl_state(ctrl);
- if (rc) {
- cpr3_err(ctrl, "unable to force vdd-supply to the aging reference voltage=%d uV, rc=%d\n",
- ctrl->aging_ref_volt, rc);
- goto cleanup;
- }
- if (ctrl->aging_vdd_mode) {
- rc = regulator_set_mode(ctrl->vdd_regulator,
- ctrl->aging_vdd_mode);
- if (rc) {
- cpr3_err(ctrl, "unable to configure vdd-supply for mode=%u, rc=%d\n",
- ctrl->aging_vdd_mode, rc);
- goto cleanup;
- }
- }
- /* Perform aging measurement on all aging sensors */
- for (i = 0; i < ctrl->aging_sensor_count; i++) {
- for (j = 0; j < CPR3_AGING_RETRY_COUNT; j++) {
- rc = cpr3_regulator_measure_aging(ctrl,
- &ctrl->aging_sensor[i]);
- if (!rc)
- break;
- }
- if (!rc) {
- aging_volt =
- cpr3_voltage_adjustment(
- ctrl->aging_sensor[i].ro_scale,
- ctrl->aging_sensor[i].measured_quot_diff
- - ctrl->aging_sensor[i].init_quot_diff);
- max_aging_volt = max(max_aging_volt, aging_volt);
- } else {
- cpr3_err(ctrl, "CPR aging measurement failed after %d tries, rc=%d\n",
- j, rc);
- ctrl->aging_failed = true;
- ctrl->aging_required = false;
- goto cleanup;
- }
- }
- cleanup:
- vreg = &ctrl->thread[0].vreg[0];
- vreg->corner[vreg->current_corner] = restore_aging_corner;
- for (i = 0, vreg_count = 0; i < ctrl->thread_count; i++) {
- for (j = 0; j < ctrl->thread[i].vreg_count; j++, vreg_count++) {
- vreg = &ctrl->thread[i].vreg[j];
- vreg->current_corner
- = restore_current_corner[vreg_count];
- vreg->vreg_enabled = restore_vreg_enabled[vreg_count];
- }
- }
- kfree(restore_current_corner);
- kfree(restore_vreg_enabled);
- /* Adjust the CPR target quotients according to the aging measurement */
- if (!rc) {
- cpr3_regulator_set_aging_ref_adjustment(ctrl, max_aging_volt);
- cpr3_info(ctrl, "aging measurement successful; aging reference adjustment voltage=%d uV\n",
- ctrl->aging_ref_adjust_volt);
- ctrl->aging_succeeded = true;
- ctrl->aging_required = false;
- }
- if (ctrl->aging_complete_vdd_mode) {
- rc = regulator_set_mode(ctrl->vdd_regulator,
- ctrl->aging_complete_vdd_mode);
- if (rc)
- cpr3_err(ctrl, "unable to configure vdd-supply for mode=%u, rc=%d\n",
- ctrl->aging_complete_vdd_mode, rc);
- }
- /* Skip last_volt caching */
- ctrl->last_corner_was_closed_loop = false;
- /*
- * Restore vdd-supply to the voltage before the aging measurement and
- * restore the CPR3 controller hardware state.
- */
- rc2 = _cpr3_regulator_update_ctrl_state(ctrl);
- /* Stop last_volt caching on for the next request */
- ctrl->last_corner_was_closed_loop = false;
- return rc ? rc : rc2;
- }
- /**
- * cprh_regulator_aging_adjust() - adjust the target quotients and open-loop
- * voltages for CPRh regulators based on the output of CPR aging
- * sensors
- * @ctrl: Pointer to the CPR3 controller
- *
- * Return: 0 on success, errno on failure
- */
- static int cprh_regulator_aging_adjust(struct cpr3_controller *ctrl)
- {
- int i, j, id, rc, rc2, aging_volt, init_volt;
- int max_aging_volt = 0;
- u32 reg;
- if (!ctrl->aging_required || !ctrl->cpr_enabled)
- return 0;
- if (!ctrl->vdd_regulator) {
- cpr3_err(ctrl, "vdd-supply regulator missing\n");
- return -ENODEV;
- }
- init_volt = regulator_get_voltage(ctrl->vdd_regulator);
- if (init_volt < 0) {
- cpr3_err(ctrl, "could not get vdd-supply voltage, rc=%d\n",
- init_volt);
- return init_volt;
- }
- if (init_volt > ctrl->aging_ref_volt) {
- cpr3_info(ctrl, "unable to perform CPR aging measurement as vdd=%d uV > aging voltage=%d uV\n",
- init_volt, ctrl->aging_ref_volt);
- return 0;
- }
- /* Verify that none of the aging sensors are currently masked. */
- for (i = 0; i < ctrl->aging_sensor_count; i++) {
- id = ctrl->aging_sensor[i].sensor_id;
- reg = cpr3_read(ctrl, CPR3_REG_SENSOR_MASK_READ(id));
- if (reg & BIT(id % 32)) {
- cpr3_info(ctrl, "unable to perform CPR aging measurement as CPR sensor %d is masked\n",
- id);
- return 0;
- }
- }
- rc = regulator_set_voltage(ctrl->vdd_regulator, ctrl->aging_ref_volt,
- INT_MAX);
- if (rc) {
- cpr3_err(ctrl, "unable to set vdd-supply to aging voltage=%d uV, rc=%d\n",
- ctrl->aging_ref_volt, rc);
- return rc;
- }
- if (ctrl->aging_vdd_mode) {
- rc = regulator_set_mode(ctrl->vdd_regulator,
- ctrl->aging_vdd_mode);
- if (rc) {
- cpr3_err(ctrl, "unable to configure vdd-supply for mode=%u, rc=%d\n",
- ctrl->aging_vdd_mode, rc);
- goto cleanup;
- }
- }
- /* Perform aging measurement on all aging sensors */
- for (i = 0; i < ctrl->aging_sensor_count; i++) {
- for (j = 0; j < CPR3_AGING_RETRY_COUNT; j++) {
- rc = cpr3_regulator_measure_aging(ctrl,
- &ctrl->aging_sensor[i]);
- if (!rc)
- break;
- }
- if (!rc) {
- aging_volt =
- cpr3_voltage_adjustment(
- ctrl->aging_sensor[i].ro_scale,
- ctrl->aging_sensor[i].measured_quot_diff
- - ctrl->aging_sensor[i].init_quot_diff);
- max_aging_volt = max(max_aging_volt, aging_volt);
- } else {
- cpr3_err(ctrl, "CPR aging measurement failed after %d tries, rc=%d\n",
- j, rc);
- ctrl->aging_failed = true;
- ctrl->aging_required = false;
- goto cleanup;
- }
- }
- cleanup:
- /* Adjust the CPR target quotients according to the aging measurement */
- if (!rc) {
- cpr3_regulator_set_aging_ref_adjustment(ctrl, max_aging_volt);
- cpr3_info(ctrl, "aging measurement successful; aging reference adjustment voltage=%d uV\n",
- ctrl->aging_ref_adjust_volt);
- ctrl->aging_succeeded = true;
- ctrl->aging_required = false;
- }
- rc2 = regulator_set_voltage(ctrl->vdd_regulator, init_volt, INT_MAX);
- if (rc2) {
- cpr3_err(ctrl, "unable to reset vdd-supply to initial voltage=%d uV, rc=%d\n",
- init_volt, rc2);
- return rc2;
- }
- if (ctrl->aging_complete_vdd_mode) {
- rc2 = regulator_set_mode(ctrl->vdd_regulator,
- ctrl->aging_complete_vdd_mode);
- if (rc2) {
- cpr3_err(ctrl, "unable to configure vdd-supply for mode=%u, rc=%d\n",
- ctrl->aging_complete_vdd_mode, rc2);
- return rc2;
- }
- }
- return rc;
- }
- /**
- * cpr3_regulator_update_ctrl_state() - update the state of the CPR controller
- * to reflect the corners used by all CPR3 regulators as well as
- * the CPR operating mode and perform aging adjustments if needed
- * @ctrl: Pointer to the CPR3 controller
- *
- * Note, CPR3 controller lock must be held by the caller.
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_regulator_update_ctrl_state(struct cpr3_controller *ctrl)
- {
- int rc;
- rc = _cpr3_regulator_update_ctrl_state(ctrl);
- if (rc)
- return rc;
- return cpr3_regulator_aging_adjust(ctrl);
- }
- /**
- * cpr3_regulator_set_voltage() - set the voltage corner for the CPR3 regulator
- * associated with the regulator device
- * @rdev: Regulator device pointer for the cpr3-regulator
- * @corner: New voltage corner to set (offset by CPR3_CORNER_OFFSET)
- * @corner_max: Maximum voltage corner allowed (offset by
- * CPR3_CORNER_OFFSET)
- * @selector: Pointer which is filled with the selector value for the
- * corner
- *
- * This function is passed as a callback function into the regulator ops that
- * are registered for each cpr3-regulator device. The VDD voltage will not be
- * physically configured until both this function and cpr3_regulator_enable()
- * are called.
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_regulator_set_voltage(struct regulator_dev *rdev,
- int corner, int corner_max, unsigned int *selector)
- {
- struct cpr3_regulator *vreg = rdev_get_drvdata(rdev);
- struct cpr3_controller *ctrl = vreg->thread->ctrl;
- int rc = 0;
- int last_corner;
- corner -= CPR3_CORNER_OFFSET;
- corner_max -= CPR3_CORNER_OFFSET;
- *selector = corner;
- mutex_lock(&ctrl->lock);
- if (!vreg->vreg_enabled) {
- vreg->current_corner = corner;
- cpr3_debug(vreg, "stored corner=%d\n", corner);
- goto done;
- } else if (vreg->current_corner == corner) {
- goto done;
- }
- last_corner = vreg->current_corner;
- vreg->current_corner = corner;
- rc = cpr3_regulator_update_ctrl_state(ctrl);
- if (rc) {
- cpr3_err(vreg, "could not update CPR state, rc=%d\n", rc);
- vreg->current_corner = last_corner;
- }
- cpr3_debug(vreg, "set corner=%d\n", corner);
- done:
- mutex_unlock(&ctrl->lock);
- return rc;
- }
- /**
- * cpr3_regulator_get_voltage() - get the voltage corner for the CPR3 regulator
- * associated with the regulator device
- * @rdev: Regulator device pointer for the cpr3-regulator
- *
- * This function is passed as a callback function into the regulator ops that
- * are registered for each cpr3-regulator device.
- *
- * Return: voltage corner value offset by CPR3_CORNER_OFFSET
- */
- static int cpr3_regulator_get_voltage(struct regulator_dev *rdev)
- {
- struct cpr3_regulator *vreg = rdev_get_drvdata(rdev);
- if (vreg->current_corner == CPR3_REGULATOR_CORNER_INVALID)
- return CPR3_CORNER_OFFSET;
- else
- return vreg->current_corner + CPR3_CORNER_OFFSET;
- }
- /**
- * cpr3_regulator_list_voltage() - return the voltage corner mapped to the
- * specified selector
- * @rdev: Regulator device pointer for the cpr3-regulator
- * @selector: Regulator selector
- *
- * This function is passed as a callback function into the regulator ops that
- * are registered for each cpr3-regulator device.
- *
- * Return: voltage corner value offset by CPR3_CORNER_OFFSET
- */
- static int cpr3_regulator_list_voltage(struct regulator_dev *rdev,
- unsigned int selector)
- {
- struct cpr3_regulator *vreg = rdev_get_drvdata(rdev);
- if (selector < vreg->corner_count)
- return selector + CPR3_CORNER_OFFSET;
- else
- return 0;
- }
- /**
- * cpr3_regulator_list_corner_voltage() - return the ceiling voltage mapped to
- * the specified voltage corner
- * @rdev: Regulator device pointer for the cpr3-regulator
- * @corner: Voltage corner
- *
- * This function is passed as a callback function into the regulator ops that
- * are registered for each cpr3-regulator device.
- *
- * Return: voltage value in microvolts or -EINVAL if the corner is out of range
- */
- static int cpr3_regulator_list_corner_voltage(struct regulator_dev *rdev,
- int corner)
- {
- struct cpr3_regulator *vreg = rdev_get_drvdata(rdev);
- corner -= CPR3_CORNER_OFFSET;
- if (corner >= 0 && corner < vreg->corner_count)
- return vreg->corner[corner].ceiling_volt;
- else
- return -EINVAL;
- }
- /**
- * cpr3_regulator_is_enabled() - return the enable state of the CPR3 regulator
- * @rdev: Regulator device pointer for the cpr3-regulator
- *
- * This function is passed as a callback function into the regulator ops that
- * are registered for each cpr3-regulator device.
- *
- * Return: true if regulator is enabled, false if regulator is disabled
- */
- static int cpr3_regulator_is_enabled(struct regulator_dev *rdev)
- {
- struct cpr3_regulator *vreg = rdev_get_drvdata(rdev);
- return vreg->vreg_enabled;
- }
- /**
- * cpr3_regulator_enable() - enable the CPR3 regulator
- * @rdev: Regulator device pointer for the cpr3-regulator
- *
- * This function is passed as a callback function into the regulator ops that
- * are registered for each cpr3-regulator device.
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_regulator_enable(struct regulator_dev *rdev)
- {
- struct cpr3_regulator *vreg = rdev_get_drvdata(rdev);
- struct cpr3_controller *ctrl = vreg->thread->ctrl;
- int rc = 0;
- if (vreg->vreg_enabled == true)
- return 0;
- mutex_lock(&ctrl->lock);
- if (ctrl->system_regulator) {
- rc = regulator_enable(ctrl->system_regulator);
- if (rc) {
- cpr3_err(ctrl, "regulator_enable(system) failed, rc=%d\n",
- rc);
- goto done;
- }
- }
- rc = regulator_enable(ctrl->vdd_regulator);
- if (rc) {
- cpr3_err(vreg, "regulator_enable(vdd) failed, rc=%d\n", rc);
- goto done;
- }
- if (vreg->ldo_regulator) {
- rc = regulator_enable(vreg->ldo_regulator);
- if (rc) {
- cpr3_err(vreg, "regulator_enable(ldo) failed, rc=%d\n",
- rc);
- goto done;
- }
- }
- vreg->vreg_enabled = true;
- rc = cpr3_regulator_update_ctrl_state(ctrl);
- if (rc) {
- cpr3_err(vreg, "could not update CPR state, rc=%d\n", rc);
- regulator_disable(ctrl->vdd_regulator);
- vreg->vreg_enabled = false;
- goto done;
- }
- cpr3_debug(vreg, "Enabled\n");
- done:
- mutex_unlock(&ctrl->lock);
- return rc;
- }
- /**
- * cpr3_regulator_disable() - disable the CPR3 regulator
- * @rdev: Regulator device pointer for the cpr3-regulator
- *
- * This function is passed as a callback function into the regulator ops that
- * are registered for each cpr3-regulator device.
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_regulator_disable(struct regulator_dev *rdev)
- {
- struct cpr3_regulator *vreg = rdev_get_drvdata(rdev);
- struct cpr3_controller *ctrl = vreg->thread->ctrl;
- int rc, rc2;
- if (vreg->vreg_enabled == false)
- return 0;
- mutex_lock(&ctrl->lock);
- if (vreg->ldo_regulator && vreg->ldo_regulator_bypass == LDO_MODE) {
- rc = regulator_get_voltage(ctrl->vdd_regulator);
- if (rc < 0) {
- cpr3_err(vreg, "regulator_get_voltage(vdd) failed, rc=%d\n",
- rc);
- goto done;
- }
- /* Switch back to BHS for safe operation */
- rc = cpr3_regulator_set_bhs_mode(vreg, rc,
- ctrl->aggr_corner.ceiling_volt);
- if (rc) {
- cpr3_err(vreg, "unable to switch to BHS mode, rc=%d\n",
- rc);
- goto done;
- }
- }
- if (vreg->ldo_regulator) {
- rc = regulator_disable(vreg->ldo_regulator);
- if (rc) {
- cpr3_err(vreg, "regulator_disable(ldo) failed, rc=%d\n",
- rc);
- goto done;
- }
- }
- rc = regulator_disable(ctrl->vdd_regulator);
- if (rc) {
- cpr3_err(vreg, "regulator_disable(vdd) failed, rc=%d\n", rc);
- goto done;
- }
- vreg->vreg_enabled = false;
- rc = cpr3_regulator_update_ctrl_state(ctrl);
- if (rc) {
- cpr3_err(vreg, "could not update CPR state, rc=%d\n", rc);
- rc2 = regulator_enable(ctrl->vdd_regulator);
- vreg->vreg_enabled = true;
- goto done;
- }
- if (ctrl->system_regulator) {
- rc = regulator_disable(ctrl->system_regulator);
- if (rc) {
- cpr3_err(ctrl, "regulator_disable(system) failed, rc=%d\n",
- rc);
- goto done;
- }
- if (ctrl->support_ldo300_vreg) {
- rc = regulator_set_voltage(ctrl->system_regulator, 0,
- INT_MAX);
- if (rc)
- cpr3_err(ctrl, "failed to set voltage on system rc=%d\n",
- rc);
- goto done;
- }
- }
- cpr3_debug(vreg, "Disabled\n");
- done:
- mutex_unlock(&ctrl->lock);
- return rc;
- }
- static struct regulator_ops cpr3_regulator_ops = {
- .enable = cpr3_regulator_enable,
- .disable = cpr3_regulator_disable,
- .is_enabled = cpr3_regulator_is_enabled,
- .set_voltage = cpr3_regulator_set_voltage,
- .get_voltage = cpr3_regulator_get_voltage,
- .list_voltage = cpr3_regulator_list_voltage,
- .list_corner_voltage = cpr3_regulator_list_corner_voltage,
- };
- /**
- * cprh_regulator_get_voltage() - get the voltage corner for the CPR3 regulator
- * associated with the regulator device
- * @rdev: Regulator device pointer for the cpr3-regulator
- *
- * This function is passed as a callback function into the regulator ops that
- * are registered for each cpr3-regulator device of a CPRh controller. The
- * corner is read directly from CPRh hardware register.
- *
- * Return: voltage corner value offset by CPR3_CORNER_OFFSET
- */
- static int cprh_regulator_get_voltage(struct regulator_dev *rdev)
- {
- struct cpr3_regulator *vreg = rdev_get_drvdata(rdev);
- struct cpr3_controller *ctrl = vreg->thread->ctrl;
- bool cpr_enabled;
- u32 reg, rc;
- mutex_lock(&ctrl->lock);
- cpr_enabled = ctrl->cpr_enabled;
- if (!cpr_enabled) {
- rc = cpr3_clock_enable(ctrl);
- if (rc) {
- cpr3_err(ctrl, "clock enable failed, rc=%d\n", rc);
- mutex_unlock(&ctrl->lock);
- return CPR3_REGULATOR_CORNER_INVALID;
- }
- ctrl->cpr_enabled = true;
- }
- reg = cpr3_read(vreg->thread->ctrl, CPRH_REG_STATUS(vreg->thread));
- if (!cpr_enabled) {
- cpr3_clock_disable(ctrl);
- ctrl->cpr_enabled = false;
- }
- mutex_unlock(&ctrl->lock);
- return (reg & CPRH_STATUS_CORNER)
- + CPR3_CORNER_OFFSET;
- }
- static struct regulator_ops cprh_regulator_ops = {
- .get_voltage = cprh_regulator_get_voltage,
- .list_corner_voltage = cpr3_regulator_list_corner_voltage,
- };
- /**
- * cpr3_print_result() - print CPR measurement results to the kernel log for
- * debugging purposes
- * @thread: Pointer to the CPR3 thread
- *
- * Return: None
- */
- static void cpr3_print_result(struct cpr3_thread *thread)
- {
- struct cpr3_controller *ctrl = thread->ctrl;
- u32 result[3], busy, step_dn, step_up, error_steps, error, negative;
- u32 quot_min, quot_max, ro_min, ro_max, step_quot_min, step_quot_max;
- u32 sensor_min, sensor_max;
- char *sign;
- result[0] = cpr3_read(ctrl, CPR3_REG_RESULT0(thread->thread_id));
- result[1] = cpr3_read(ctrl, CPR3_REG_RESULT1(thread->thread_id));
- result[2] = cpr3_read(ctrl, CPR3_REG_RESULT2(thread->thread_id));
- busy = !!(result[0] & CPR3_RESULT0_BUSY_MASK);
- step_dn = !!(result[0] & CPR3_RESULT0_STEP_DN_MASK);
- step_up = !!(result[0] & CPR3_RESULT0_STEP_UP_MASK);
- error_steps = (result[0] & CPR3_RESULT0_ERROR_STEPS_MASK)
- >> CPR3_RESULT0_ERROR_STEPS_SHIFT;
- error = (result[0] & CPR3_RESULT0_ERROR_MASK)
- >> CPR3_RESULT0_ERROR_SHIFT;
- negative = !!(result[0] & CPR3_RESULT0_NEGATIVE_MASK);
- quot_min = (result[1] & CPR3_RESULT1_QUOT_MIN_MASK)
- >> CPR3_RESULT1_QUOT_MIN_SHIFT;
- quot_max = (result[1] & CPR3_RESULT1_QUOT_MAX_MASK)
- >> CPR3_RESULT1_QUOT_MAX_SHIFT;
- ro_min = (result[1] & CPR3_RESULT1_RO_MIN_MASK)
- >> CPR3_RESULT1_RO_MIN_SHIFT;
- ro_max = (result[1] & CPR3_RESULT1_RO_MAX_MASK)
- >> CPR3_RESULT1_RO_MAX_SHIFT;
- step_quot_min = (result[2] & CPR3_RESULT2_STEP_QUOT_MIN_MASK)
- >> CPR3_RESULT2_STEP_QUOT_MIN_SHIFT;
- step_quot_max = (result[2] & CPR3_RESULT2_STEP_QUOT_MAX_MASK)
- >> CPR3_RESULT2_STEP_QUOT_MAX_SHIFT;
- sensor_min = (result[2] & CPR3_RESULT2_SENSOR_MIN_MASK)
- >> CPR3_RESULT2_SENSOR_MIN_SHIFT;
- sensor_max = (result[2] & CPR3_RESULT2_SENSOR_MAX_MASK)
- >> CPR3_RESULT2_SENSOR_MAX_SHIFT;
- sign = negative ? "-" : "";
- cpr3_debug(ctrl, "thread %u: busy=%u, step_dn=%u, step_up=%u, error_steps=%s%u, error=%s%u\n",
- thread->thread_id, busy, step_dn, step_up, sign, error_steps,
- sign, error);
- cpr3_debug(ctrl, "thread %u: quot_min=%u, quot_max=%u, ro_min=%u, ro_max=%u\n",
- thread->thread_id, quot_min, quot_max, ro_min, ro_max);
- cpr3_debug(ctrl, "thread %u: step_quot_min=%u, step_quot_max=%u, sensor_min=%u, sensor_max=%u\n",
- thread->thread_id, step_quot_min, step_quot_max, sensor_min,
- sensor_max);
- }
- /**
- * cpr3_thread_busy() - returns if the specified CPR3 thread is busy taking
- * a measurement
- * @thread: Pointer to the CPR3 thread
- *
- * Return: CPR3 busy status
- */
- static bool cpr3_thread_busy(struct cpr3_thread *thread)
- {
- u32 result;
- result = cpr3_read(thread->ctrl, CPR3_REG_RESULT0(thread->thread_id));
- return !!(result & CPR3_RESULT0_BUSY_MASK);
- }
- /**
- * cpr3_irq_handler() - CPR interrupt handler callback function used for
- * software closed-loop operation
- * @irq: CPR interrupt number
- * @data: Private data corresponding to the CPR3 controller
- * pointer
- *
- * This function increases or decreases the vdd supply voltage based upon the
- * CPR controller recommendation.
- *
- * Return: IRQ_HANDLED
- */
- static irqreturn_t cpr3_irq_handler(int irq, void *data)
- {
- struct cpr3_controller *ctrl = data;
- struct cpr3_corner *aggr = &ctrl->aggr_corner;
- u32 cont = CPR3_CONT_CMD_NACK;
- u32 reg_last_measurement = 0;
- struct cpr3_regulator *vreg;
- struct cpr3_corner *corner;
- unsigned long flags;
- int i, j, new_volt, last_volt, dynamic_floor_volt, rc;
- u32 irq_en, status, cpr_status, ctl;
- bool up, down;
- mutex_lock(&ctrl->lock);
- if (!ctrl->cpr_enabled) {
- cpr3_debug(ctrl, "CPR interrupt received but CPR is disabled\n");
- mutex_unlock(&ctrl->lock);
- return IRQ_HANDLED;
- } else if (ctrl->use_hw_closed_loop) {
- cpr3_debug(ctrl, "CPR interrupt received but CPR is using HW closed-loop\n");
- goto done;
- }
- /*
- * CPR IRQ status checking and CPR controller disabling must happen
- * atomically and without invening delay in order to avoid an interrupt
- * storm caused by the handler racing with the CPR controller.
- */
- local_irq_save(flags);
- preempt_disable();
- status = cpr3_read(ctrl, CPR3_REG_IRQ_STATUS);
- up = status & CPR3_IRQ_UP;
- down = status & CPR3_IRQ_DOWN;
- if (!up && !down) {
- /*
- * Toggle the CPR controller off and then back on since the
- * hardware and software states are out of sync. This condition
- * occurs after an aging measurement completes as the CPR IRQ
- * physically triggers during the aging measurement but the
- * handler is stuck waiting on the mutex lock.
- */
- cpr3_ctrl_loop_disable(ctrl);
- local_irq_restore(flags);
- preempt_enable();
- /* Wait for the loop disable write to complete */
- mb();
- /* Wait for BUSY=1 and LOOP_EN=0 in CPR controller registers. */
- for (i = 0; i < CPR3_REGISTER_WRITE_DELAY_US / 10; i++) {
- cpr_status = cpr3_read(ctrl, CPR3_REG_CPR_STATUS);
- ctl = cpr3_read(ctrl, CPR3_REG_CPR_CTL);
- if (cpr_status & CPR3_CPR_STATUS_BUSY_MASK
- && (ctl & CPR3_CPR_CTL_LOOP_EN_MASK)
- == CPR3_CPR_CTL_LOOP_DISABLE)
- break;
- udelay(10);
- }
- if (i == CPR3_REGISTER_WRITE_DELAY_US / 10)
- cpr3_debug(ctrl, "CPR controller not disabled after %d us\n",
- CPR3_REGISTER_WRITE_DELAY_US);
- /* Clear interrupt status */
- cpr3_write(ctrl, CPR3_REG_IRQ_CLEAR,
- CPR3_IRQ_UP | CPR3_IRQ_DOWN);
- /* Wait for the interrupt clearing write to complete */
- mb();
- /* Wait for IRQ_STATUS register to be cleared. */
- for (i = 0; i < CPR3_REGISTER_WRITE_DELAY_US / 10; i++) {
- status = cpr3_read(ctrl, CPR3_REG_IRQ_STATUS);
- if (!(status & (CPR3_IRQ_UP | CPR3_IRQ_DOWN)))
- break;
- udelay(10);
- }
- if (i == CPR3_REGISTER_WRITE_DELAY_US / 10)
- cpr3_debug(ctrl, "CPR interrupts not cleared after %d us\n",
- CPR3_REGISTER_WRITE_DELAY_US);
- cpr3_ctrl_loop_enable(ctrl);
- cpr3_debug(ctrl, "CPR interrupt received but no up or down status bit is set\n");
- mutex_unlock(&ctrl->lock);
- return IRQ_HANDLED;
- } else if (up && down) {
- cpr3_debug(ctrl, "both up and down status bits set\n");
- /* The up flag takes precedence over the down flag. */
- down = false;
- }
- if (ctrl->supports_hw_closed_loop)
- reg_last_measurement
- = cpr3_read(ctrl, CPR3_REG_LAST_MEASUREMENT);
- dynamic_floor_volt = cpr3_regulator_get_dynamic_floor_volt(ctrl,
- reg_last_measurement);
- local_irq_restore(flags);
- preempt_enable();
- irq_en = aggr->irq_en;
- last_volt = aggr->last_volt;
- for (i = 0; i < ctrl->thread_count; i++) {
- if (cpr3_thread_busy(&ctrl->thread[i])) {
- cpr3_debug(ctrl, "CPR thread %u busy when it should be waiting for SW cont\n",
- ctrl->thread[i].thread_id);
- goto done;
- }
- }
- new_volt = up ? last_volt + ctrl->step_volt
- : last_volt - ctrl->step_volt;
- /* Re-enable UP/DOWN interrupt when its opposite is received. */
- irq_en |= up ? CPR3_IRQ_DOWN : CPR3_IRQ_UP;
- if (new_volt > aggr->ceiling_volt) {
- new_volt = aggr->ceiling_volt;
- irq_en &= ~CPR3_IRQ_UP;
- cpr3_debug(ctrl, "limiting to ceiling=%d uV\n",
- aggr->ceiling_volt);
- } else if (new_volt < aggr->floor_volt) {
- new_volt = aggr->floor_volt;
- irq_en &= ~CPR3_IRQ_DOWN;
- cpr3_debug(ctrl, "limiting to floor=%d uV\n", aggr->floor_volt);
- }
- if (down && new_volt < dynamic_floor_volt) {
- /*
- * The vdd-supply voltage should not be decreased below the
- * dynamic floor voltage. However, it is not necessary (and
- * counter productive) to force the voltage up to this level
- * if it happened to be below it since the closed-loop voltage
- * must have gotten there in a safe manner while the power
- * domains for the CPR3 regulator imposing the dynamic floor
- * were not bypassed.
- */
- new_volt = last_volt;
- irq_en &= ~CPR3_IRQ_DOWN;
- cpr3_debug(ctrl, "limiting to dynamic floor=%d uV\n",
- dynamic_floor_volt);
- }
- for (i = 0; i < ctrl->thread_count; i++)
- cpr3_print_result(&ctrl->thread[i]);
- cpr3_debug(ctrl, "%s: new_volt=%d uV, last_volt=%d uV\n",
- up ? "UP" : "DN", new_volt, last_volt);
- if (ctrl->proc_clock_throttle && last_volt == aggr->ceiling_volt
- && new_volt < last_volt)
- cpr3_write(ctrl, CPR3_REG_PD_THROTTLE,
- ctrl->proc_clock_throttle);
- if (new_volt != last_volt) {
- rc = cpr3_regulator_scale_vdd_voltage(ctrl, new_volt,
- last_volt,
- aggr);
- if (rc) {
- cpr3_err(ctrl, "scale_vdd() failed to set vdd=%d uV, rc=%d\n",
- new_volt, rc);
- goto done;
- }
- cont = CPR3_CONT_CMD_ACK;
- /*
- * Update the closed-loop voltage for all regulators managed
- * by this CPR controller.
- */
- for (i = 0; i < ctrl->thread_count; i++) {
- for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
- vreg = &ctrl->thread[i].vreg[j];
- cpr3_update_vreg_closed_loop_volt(vreg,
- new_volt, reg_last_measurement);
- }
- }
- }
- if (ctrl->proc_clock_throttle && new_volt == aggr->ceiling_volt)
- cpr3_write(ctrl, CPR3_REG_PD_THROTTLE,
- CPR3_PD_THROTTLE_DISABLE);
- corner = &ctrl->thread[0].vreg[0].corner[
- ctrl->thread[0].vreg[0].current_corner];
- if (irq_en != aggr->irq_en) {
- aggr->irq_en = irq_en;
- cpr3_write(ctrl, CPR3_REG_IRQ_EN, irq_en);
- }
- aggr->last_volt = new_volt;
- done:
- /* Clear interrupt status */
- cpr3_write(ctrl, CPR3_REG_IRQ_CLEAR, CPR3_IRQ_UP | CPR3_IRQ_DOWN);
- /* ACK or NACK the CPR controller */
- cpr3_write(ctrl, CPR3_REG_CONT_CMD, cont);
- mutex_unlock(&ctrl->lock);
- return IRQ_HANDLED;
- }
- /**
- * cpr3_ceiling_irq_handler() - CPR ceiling reached interrupt handler callback
- * function used for hardware closed-loop operation
- * @irq: CPR ceiling interrupt number
- * @data: Private data corresponding to the CPR3 controller
- * pointer
- *
- * This function disables processor clock throttling and closed-loop operation
- * when the ceiling voltage is reached.
- *
- * Return: IRQ_HANDLED
- */
- static irqreturn_t cpr3_ceiling_irq_handler(int irq, void *data)
- {
- struct cpr3_controller *ctrl = data;
- int rc, volt;
- mutex_lock(&ctrl->lock);
- if (!ctrl->cpr_enabled) {
- cpr3_debug(ctrl, "CPR ceiling interrupt received but CPR is disabled\n");
- goto done;
- } else if (!ctrl->use_hw_closed_loop) {
- cpr3_debug(ctrl, "CPR ceiling interrupt received but CPR is using SW closed-loop\n");
- goto done;
- }
- volt = regulator_get_voltage(ctrl->vdd_regulator);
- if (volt < 0) {
- cpr3_err(ctrl, "could not get vdd voltage, rc=%d\n", volt);
- goto done;
- } else if (volt != ctrl->aggr_corner.ceiling_volt) {
- cpr3_debug(ctrl, "CPR ceiling interrupt received but vdd voltage: %d uV != ceiling voltage: %d uV\n",
- volt, ctrl->aggr_corner.ceiling_volt);
- goto done;
- }
- if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) {
- /*
- * Since the ceiling voltage has been reached, disable processor
- * clock throttling as well as CPR closed-loop operation.
- */
- cpr3_write(ctrl, CPR3_REG_PD_THROTTLE,
- CPR3_PD_THROTTLE_DISABLE);
- cpr3_ctrl_loop_disable(ctrl);
- cpr3_debug(ctrl, "CPR closed-loop and throttling disabled\n");
- }
- done:
- rc = msm_spm_avs_clear_irq(0, MSM_SPM_AVS_IRQ_MAX);
- if (rc)
- cpr3_err(ctrl, "could not clear max IRQ, rc=%d\n", rc);
- mutex_unlock(&ctrl->lock);
- return IRQ_HANDLED;
- }
- /**
- * cpr3_regulator_vreg_register() - register a regulator device for a CPR3
- * regulator
- * @vreg: Pointer to the CPR3 regulator
- *
- * This function initializes all regulator framework related structures and then
- * calls regulator_register() for the CPR3 regulator.
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_regulator_vreg_register(struct cpr3_regulator *vreg)
- {
- struct regulator_config config = {};
- struct regulator_desc *rdesc;
- struct regulator_init_data *init_data;
- int rc;
- init_data = of_get_regulator_init_data(vreg->thread->ctrl->dev,
- vreg->of_node, &vreg->rdesc);
- if (!init_data) {
- cpr3_err(vreg, "regulator init data is missing\n");
- return -EINVAL;
- }
- init_data->constraints.input_uV = init_data->constraints.max_uV;
- rdesc = &vreg->rdesc;
- if (vreg->thread->ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH) {
- /* CPRh regulators are treated as always-on regulators */
- rdesc->ops = &cprh_regulator_ops;
- } else {
- init_data->constraints.valid_ops_mask
- |= REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS;
- rdesc->ops = &cpr3_regulator_ops;
- }
- rdesc->n_voltages = vreg->corner_count;
- rdesc->name = init_data->constraints.name;
- rdesc->owner = THIS_MODULE;
- rdesc->type = REGULATOR_VOLTAGE;
- config.dev = vreg->thread->ctrl->dev;
- config.driver_data = vreg;
- config.init_data = init_data;
- config.of_node = vreg->of_node;
- vreg->rdev = regulator_register(rdesc, &config);
- if (IS_ERR(vreg->rdev)) {
- rc = PTR_ERR(vreg->rdev);
- cpr3_err(vreg, "regulator_register failed, rc=%d\n", rc);
- return rc;
- }
- return 0;
- }
- static int debugfs_int_set(void *data, u64 val)
- {
- *(int *)data = val;
- return 0;
- }
- static int debugfs_int_get(void *data, u64 *val)
- {
- *val = *(int *)data;
- return 0;
- }
- DEFINE_SIMPLE_ATTRIBUTE(fops_int, debugfs_int_get, debugfs_int_set, "%lld\n");
- DEFINE_SIMPLE_ATTRIBUTE(fops_int_ro, debugfs_int_get, NULL, "%lld\n");
- DEFINE_SIMPLE_ATTRIBUTE(fops_int_wo, NULL, debugfs_int_set, "%lld\n");
- /**
- * debugfs_create_int - create a debugfs file that is used to read and write a
- * signed int value
- * @name: Pointer to a string containing the name of the file to
- * create
- * @mode: The permissions that the file should have
- * @parent: Pointer to the parent dentry for this file. This should
- * be a directory dentry if set. If this parameter is
- * %NULL, then the file will be created in the root of the
- * debugfs filesystem.
- * @value: Pointer to the variable that the file should read to and
- * write from
- *
- * This function creates a file in debugfs with the given name that
- * contains the value of the variable @value. If the @mode variable is so
- * set, it can be read from, and written to.
- *
- * This function will return a pointer to a dentry if it succeeds. This
- * pointer must be passed to the debugfs_remove() function when the file is
- * to be removed. If an error occurs, %NULL will be returned.
- */
- static struct dentry *debugfs_create_int(const char *name, umode_t mode,
- struct dentry *parent, int *value)
- {
- /* if there are no write bits set, make read only */
- if (!(mode & 0222))
- return debugfs_create_file(name, mode, parent, value,
- &fops_int_ro);
- /* if there are no read bits set, make write only */
- if (!(mode & 0444))
- return debugfs_create_file(name, mode, parent, value,
- &fops_int_wo);
- return debugfs_create_file(name, mode, parent, value, &fops_int);
- }
- static int debugfs_bool_get(void *data, u64 *val)
- {
- *val = *(bool *)data;
- return 0;
- }
- DEFINE_SIMPLE_ATTRIBUTE(fops_bool_ro, debugfs_bool_get, NULL, "%lld\n");
- /**
- * cpr3_debug_ldo_mode_allowed_set() - debugfs callback used to change the
- * value of the CPR3 regulator ldo_mode_allowed flag
- * @data: Pointer to private data which is equal to the CPR3
- * regulator pointer
- * @val: New value for ldo_mode_allowed
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_debug_ldo_mode_allowed_set(void *data, u64 val)
- {
- struct cpr3_regulator *vreg = data;
- struct cpr3_controller *ctrl = vreg->thread->ctrl;
- bool allow = !!val;
- int rc, vdd_volt;
- mutex_lock(&ctrl->lock);
- if (vreg->ldo_mode_allowed == allow)
- goto done;
- vreg->ldo_mode_allowed = allow;
- if (!allow && vreg->ldo_regulator_bypass == LDO_MODE) {
- vdd_volt = regulator_get_voltage(ctrl->vdd_regulator);
- if (vdd_volt < 0) {
- cpr3_err(vreg, "regulator_get_voltage(vdd) failed, rc=%d\n",
- vdd_volt);
- goto done;
- }
- /* Switch back to BHS */
- rc = cpr3_regulator_set_bhs_mode(vreg, vdd_volt,
- ctrl->aggr_corner.ceiling_volt);
- if (rc) {
- cpr3_err(vreg, "unable to switch to BHS mode, rc=%d\n",
- rc);
- goto done;
- }
- } else {
- rc = cpr3_regulator_update_ctrl_state(ctrl);
- if (rc) {
- cpr3_err(vreg, "could not change LDO mode=%s, rc=%d\n",
- allow ? "allowed" : "disallowed", rc);
- goto done;
- }
- }
- cpr3_debug(vreg, "LDO mode=%s\n", allow ? "allowed" : "disallowed");
- done:
- mutex_unlock(&ctrl->lock);
- return 0;
- }
- /**
- * cpr3_debug_ldo_mode_allowed_get() - debugfs callback used to retrieve the
- * value of the CPR3 regulator ldo_mode_allowed flag
- * @data: Pointer to private data which is equal to the CPR3
- * regulator pointer
- * @val: Output parameter written with a value of the
- * ldo_mode_allowed flag
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_debug_ldo_mode_allowed_get(void *data, u64 *val)
- {
- struct cpr3_regulator *vreg = data;
- *val = vreg->ldo_mode_allowed;
- return 0;
- }
- DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_ldo_mode_allowed_fops,
- cpr3_debug_ldo_mode_allowed_get,
- cpr3_debug_ldo_mode_allowed_set,
- "%llu\n");
- /**
- * cpr3_debug_ldo_mode_get() - debugfs callback used to retrieve the state of
- * the CPR3 regulator's LDO
- * @data: Pointer to private data which is equal to the CPR3
- * regulator pointer
- * @val: Output parameter written with a value of 1 if using
- * LDO mode or 0 if the LDO is bypassed
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_debug_ldo_mode_get(void *data, u64 *val)
- {
- struct cpr3_regulator *vreg = data;
- *val = (vreg->ldo_regulator_bypass == LDO_MODE);
- return 0;
- }
- DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_ldo_mode_fops, cpr3_debug_ldo_mode_get,
- NULL, "%llu\n");
- /**
- * struct cpr3_debug_corner_info - data structure used by the
- * cpr3_debugfs_create_corner_int function
- * @vreg: Pointer to the CPR3 regulator
- * @index: Pointer to the corner array index
- * @member_offset: Offset in bytes from the beginning of struct cpr3_corner
- * to the beginning of the value to be read from
- * @corner: Pointer to the CPR3 corner array
- */
- struct cpr3_debug_corner_info {
- struct cpr3_regulator *vreg;
- int *index;
- size_t member_offset;
- struct cpr3_corner *corner;
- };
- static int cpr3_debug_corner_int_get(void *data, u64 *val)
- {
- struct cpr3_debug_corner_info *info = data;
- struct cpr3_controller *ctrl = info->vreg->thread->ctrl;
- int i;
- mutex_lock(&ctrl->lock);
- i = *info->index;
- if (i < 0)
- i = 0;
- *val = *(int *)((char *)&info->vreg->corner[i] + info->member_offset);
- mutex_unlock(&ctrl->lock);
- return 0;
- }
- DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_corner_int_fops, cpr3_debug_corner_int_get,
- NULL, "%lld\n");
- /**
- * cpr3_debugfs_create_corner_int - create a debugfs file that is used to read
- * a signed int value out of a CPR3 regulator's corner array
- * @vreg: Pointer to the CPR3 regulator
- * @name: Pointer to a string containing the name of the file to
- * create
- * @mode: The permissions that the file should have
- * @parent: Pointer to the parent dentry for this file. This should
- * be a directory dentry if set. If this parameter is
- * %NULL, then the file will be created in the root of the
- * debugfs filesystem.
- * @index: Pointer to the corner array index
- * @member_offset: Offset in bytes from the beginning of struct cpr3_corner
- * to the beginning of the value to be read from
- *
- * This function creates a file in debugfs with the given name that
- * contains the value of the int type variable vreg->corner[index].member
- * where member_offset == offsetof(struct cpr3_corner, member).
- */
- static struct dentry *cpr3_debugfs_create_corner_int(
- struct cpr3_regulator *vreg, const char *name, umode_t mode,
- struct dentry *parent, int *index, size_t member_offset)
- {
- struct cpr3_debug_corner_info *info;
- info = devm_kzalloc(vreg->thread->ctrl->dev, sizeof(*info), GFP_KERNEL);
- if (!info)
- return NULL;
- info->vreg = vreg;
- info->index = index;
- info->member_offset = member_offset;
- return debugfs_create_file(name, mode, parent, info,
- &cpr3_debug_corner_int_fops);
- }
- static int cpr3_debug_quot_open(struct inode *inode, struct file *file)
- {
- struct cpr3_debug_corner_info *info = inode->i_private;
- struct cpr3_thread *thread = info->vreg->thread;
- int size, i, pos;
- u32 *quot;
- char *buf;
- /*
- * Max size:
- * - 10 digits + ' ' or '\n' = 11 bytes per number
- * - terminating '\0'
- */
- size = CPR3_RO_COUNT * 11;
- buf = kzalloc(size + 1, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
- file->private_data = buf;
- mutex_lock(&thread->ctrl->lock);
- quot = info->corner[*info->index].target_quot;
- for (i = 0, pos = 0; i < CPR3_RO_COUNT; i++)
- pos += scnprintf(buf + pos, size - pos, "%u%c",
- quot[i], i < CPR3_RO_COUNT - 1 ? ' ' : '\n');
- mutex_unlock(&thread->ctrl->lock);
- return nonseekable_open(inode, file);
- }
- static ssize_t cpr3_debug_quot_read(struct file *file, char __user *buf,
- size_t len, loff_t *ppos)
- {
- return simple_read_from_buffer(buf, len, ppos, file->private_data,
- strlen(file->private_data));
- }
- static int cpr3_debug_quot_release(struct inode *inode, struct file *file)
- {
- kfree(file->private_data);
- return 0;
- }
- static const struct file_operations cpr3_debug_quot_fops = {
- .owner = THIS_MODULE,
- .open = cpr3_debug_quot_open,
- .release = cpr3_debug_quot_release,
- .read = cpr3_debug_quot_read,
- .llseek = no_llseek,
- };
- /**
- * cpr3_regulator_debugfs_corner_add() - add debugfs files to expose
- * configuration data for the CPR corner
- * @vreg: Pointer to the CPR3 regulator
- * @corner_dir: Pointer to the parent corner dentry for the new files
- * @index: Pointer to the corner array index
- *
- * Return: none
- */
- static void cpr3_regulator_debugfs_corner_add(struct cpr3_regulator *vreg,
- struct dentry *corner_dir, int *index)
- {
- struct cpr3_debug_corner_info *info;
- struct dentry *temp;
- temp = cpr3_debugfs_create_corner_int(vreg, "floor_volt", 0444,
- corner_dir, index, offsetof(struct cpr3_corner, floor_volt));
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(vreg, "floor_volt debugfs file creation failed\n");
- return;
- }
- temp = cpr3_debugfs_create_corner_int(vreg, "ceiling_volt", 0444,
- corner_dir, index, offsetof(struct cpr3_corner, ceiling_volt));
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(vreg, "ceiling_volt debugfs file creation failed\n");
- return;
- }
- temp = cpr3_debugfs_create_corner_int(vreg, "open_loop_volt", 0444,
- corner_dir, index,
- offsetof(struct cpr3_corner, open_loop_volt));
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(vreg, "open_loop_volt debugfs file creation failed\n");
- return;
- }
- temp = cpr3_debugfs_create_corner_int(vreg, "last_volt", 0444,
- corner_dir, index, offsetof(struct cpr3_corner, last_volt));
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(vreg, "last_volt debugfs file creation failed\n");
- return;
- }
- info = devm_kzalloc(vreg->thread->ctrl->dev, sizeof(*info), GFP_KERNEL);
- if (!info)
- return;
- info->vreg = vreg;
- info->index = index;
- info->corner = vreg->corner;
- temp = debugfs_create_file("target_quots", 0444, corner_dir, info,
- &cpr3_debug_quot_fops);
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(vreg, "target_quots debugfs file creation failed\n");
- return;
- }
- }
- /**
- * cpr3_debug_corner_index_set() - debugfs callback used to change the
- * value of the CPR3 regulator debug_corner index
- * @data: Pointer to private data which is equal to the CPR3
- * regulator pointer
- * @val: New value for debug_corner
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_debug_corner_index_set(void *data, u64 val)
- {
- struct cpr3_regulator *vreg = data;
- if (val < CPR3_CORNER_OFFSET || val > vreg->corner_count) {
- cpr3_err(vreg, "invalid corner index %llu; allowed values: %d-%d\n",
- val, CPR3_CORNER_OFFSET, vreg->corner_count);
- return -EINVAL;
- }
- mutex_lock(&vreg->thread->ctrl->lock);
- vreg->debug_corner = val - CPR3_CORNER_OFFSET;
- mutex_unlock(&vreg->thread->ctrl->lock);
- return 0;
- }
- /**
- * cpr3_debug_corner_index_get() - debugfs callback used to retrieve
- * the value of the CPR3 regulator debug_corner index
- * @data: Pointer to private data which is equal to the CPR3
- * regulator pointer
- * @val: Output parameter written with the value of
- * debug_corner
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_debug_corner_index_get(void *data, u64 *val)
- {
- struct cpr3_regulator *vreg = data;
- *val = vreg->debug_corner + CPR3_CORNER_OFFSET;
- return 0;
- }
- DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_corner_index_fops,
- cpr3_debug_corner_index_get,
- cpr3_debug_corner_index_set,
- "%llu\n");
- /**
- * cpr3_debug_current_corner_index_get() - debugfs callback used to retrieve
- * the value of the CPR3 regulator current_corner index
- * @data: Pointer to private data which is equal to the CPR3
- * regulator pointer
- * @val: Output parameter written with the value of
- * current_corner
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_debug_current_corner_index_get(void *data, u64 *val)
- {
- struct cpr3_regulator *vreg = data;
- *val = vreg->current_corner + CPR3_CORNER_OFFSET;
- return 0;
- }
- DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_current_corner_index_fops,
- cpr3_debug_current_corner_index_get,
- NULL, "%llu\n");
- /**
- * cpr3_regulator_debugfs_vreg_add() - add debugfs files to expose configuration
- * data for the CPR3 regulator
- * @vreg: Pointer to the CPR3 regulator
- * @thread_dir CPR3 thread debugfs directory handle
- *
- * Return: none
- */
- static void cpr3_regulator_debugfs_vreg_add(struct cpr3_regulator *vreg,
- struct dentry *thread_dir)
- {
- struct dentry *temp, *corner_dir, *vreg_dir;
- vreg_dir = debugfs_create_dir(vreg->name, thread_dir);
- if (IS_ERR_OR_NULL(vreg_dir)) {
- cpr3_err(vreg, "%s debugfs directory creation failed\n",
- vreg->name);
- return;
- }
- temp = debugfs_create_int("speed_bin_fuse", 0444, vreg_dir,
- &vreg->speed_bin_fuse);
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(vreg, "speed_bin_fuse debugfs file creation failed\n");
- return;
- }
- temp = debugfs_create_int("cpr_rev_fuse", 0444, vreg_dir,
- &vreg->cpr_rev_fuse);
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(vreg, "cpr_rev_fuse debugfs file creation failed\n");
- return;
- }
- temp = debugfs_create_int("fuse_combo", 0444, vreg_dir,
- &vreg->fuse_combo);
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(vreg, "fuse_combo debugfs file creation failed\n");
- return;
- }
- if (vreg->ldo_regulator) {
- temp = debugfs_create_file("ldo_mode", 0444, vreg_dir, vreg,
- &cpr3_debug_ldo_mode_fops);
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(vreg, "ldo_mode debugfs file creation failed\n");
- return;
- }
- temp = debugfs_create_file("ldo_mode_allowed",
- 0644, vreg_dir, vreg,
- &cpr3_debug_ldo_mode_allowed_fops);
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(vreg, "ldo_mode_allowed debugfs file creation failed\n");
- return;
- }
- }
- temp = debugfs_create_int("corner_count", 0444, vreg_dir,
- &vreg->corner_count);
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(vreg, "corner_count debugfs file creation failed\n");
- return;
- }
- corner_dir = debugfs_create_dir("corner", vreg_dir);
- if (IS_ERR_OR_NULL(corner_dir)) {
- cpr3_err(vreg, "corner debugfs directory creation failed\n");
- return;
- }
- temp = debugfs_create_file("index", 0644, corner_dir, vreg,
- &cpr3_debug_corner_index_fops);
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(vreg, "index debugfs file creation failed\n");
- return;
- }
- cpr3_regulator_debugfs_corner_add(vreg, corner_dir,
- &vreg->debug_corner);
- corner_dir = debugfs_create_dir("current_corner", vreg_dir);
- if (IS_ERR_OR_NULL(corner_dir)) {
- cpr3_err(vreg, "current_corner debugfs directory creation failed\n");
- return;
- }
- temp = debugfs_create_file("index", 0444, corner_dir, vreg,
- &cpr3_debug_current_corner_index_fops);
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(vreg, "index debugfs file creation failed\n");
- return;
- }
- cpr3_regulator_debugfs_corner_add(vreg, corner_dir,
- &vreg->current_corner);
- }
- /**
- * cpr3_regulator_debugfs_thread_add() - add debugfs files to expose
- * configuration data for the CPR thread
- * @thread: Pointer to the CPR3 thread
- *
- * Return: none
- */
- static void cpr3_regulator_debugfs_thread_add(struct cpr3_thread *thread)
- {
- struct cpr3_controller *ctrl = thread->ctrl;
- struct dentry *aggr_dir, *temp, *thread_dir;
- struct cpr3_debug_corner_info *info;
- char buf[20];
- int *index;
- int i;
- scnprintf(buf, sizeof(buf), "thread%u", thread->thread_id);
- thread_dir = debugfs_create_dir(buf, thread->ctrl->debugfs);
- if (IS_ERR_OR_NULL(thread_dir)) {
- cpr3_err(ctrl, "thread %u %s debugfs directory creation failed\n",
- thread->thread_id, buf);
- return;
- }
- aggr_dir = debugfs_create_dir("max_aggregated_params", thread_dir);
- if (IS_ERR_OR_NULL(aggr_dir)) {
- cpr3_err(ctrl, "thread %u max_aggregated_params debugfs directory creation failed\n",
- thread->thread_id);
- return;
- }
- temp = debugfs_create_int("floor_volt", 0444, aggr_dir,
- &thread->aggr_corner.floor_volt);
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(ctrl, "thread %u aggr floor_volt debugfs file creation failed\n",
- thread->thread_id);
- return;
- }
- temp = debugfs_create_int("ceiling_volt", 0444, aggr_dir,
- &thread->aggr_corner.ceiling_volt);
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(ctrl, "thread %u aggr ceiling_volt debugfs file creation failed\n",
- thread->thread_id);
- return;
- }
- temp = debugfs_create_int("open_loop_volt", 0444, aggr_dir,
- &thread->aggr_corner.open_loop_volt);
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(ctrl, "thread %u aggr open_loop_volt debugfs file creation failed\n",
- thread->thread_id);
- return;
- }
- temp = debugfs_create_int("last_volt", 0444, aggr_dir,
- &thread->aggr_corner.last_volt);
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(ctrl, "thread %u aggr last_volt debugfs file creation failed\n",
- thread->thread_id);
- return;
- }
- info = devm_kzalloc(thread->ctrl->dev, sizeof(*info), GFP_KERNEL);
- index = devm_kzalloc(thread->ctrl->dev, sizeof(*index), GFP_KERNEL);
- if (!info || !index)
- return;
- *index = 0;
- info->vreg = &thread->vreg[0];
- info->index = index;
- info->corner = &thread->aggr_corner;
- temp = debugfs_create_file("target_quots", 0444, aggr_dir, info,
- &cpr3_debug_quot_fops);
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(ctrl, "thread %u target_quots debugfs file creation failed\n",
- thread->thread_id);
- return;
- }
- for (i = 0; i < thread->vreg_count; i++)
- cpr3_regulator_debugfs_vreg_add(&thread->vreg[i], thread_dir);
- }
- /**
- * cpr3_debug_closed_loop_enable_set() - debugfs callback used to change the
- * value of the CPR controller cpr_allowed_sw flag which enables or
- * disables closed-loop operation
- * @data: Pointer to private data which is equal to the CPR
- * controller pointer
- * @val: New value for cpr_allowed_sw
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_debug_closed_loop_enable_set(void *data, u64 val)
- {
- struct cpr3_controller *ctrl = data;
- bool enable = !!val;
- int rc;
- mutex_lock(&ctrl->lock);
- if (ctrl->cpr_allowed_sw == enable)
- goto done;
- if (enable && !ctrl->cpr_allowed_hw) {
- cpr3_err(ctrl, "CPR closed-loop operation is not allowed\n");
- goto done;
- }
- ctrl->cpr_allowed_sw = enable;
- if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH) {
- cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
- CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK,
- ctrl->cpr_allowed_sw && ctrl->use_hw_closed_loop
- ? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE
- : CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE);
- } else {
- rc = cpr3_regulator_update_ctrl_state(ctrl);
- if (rc) {
- cpr3_err(ctrl, "could not change CPR enable state=%u, rc=%d\n",
- enable, rc);
- goto done;
- }
- if (ctrl->proc_clock_throttle && !ctrl->cpr_enabled) {
- rc = cpr3_clock_enable(ctrl);
- if (rc) {
- cpr3_err(ctrl, "clock enable failed, rc=%d\n",
- rc);
- goto done;
- }
- ctrl->cpr_enabled = true;
- cpr3_write(ctrl, CPR3_REG_PD_THROTTLE,
- CPR3_PD_THROTTLE_DISABLE);
- cpr3_clock_disable(ctrl);
- ctrl->cpr_enabled = false;
- }
- }
- if (ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH) {
- cpr3_debug(ctrl, "closed-loop=%s\n", enable ?
- "enabled" : "disabled");
- } else {
- cpr3_debug(ctrl, "closed-loop=%s\n", enable &&
- ctrl->use_hw_closed_loop ? "enabled" : "disabled");
- }
- done:
- mutex_unlock(&ctrl->lock);
- return 0;
- }
- /**
- * cpr3_debug_closed_loop_enable_get() - debugfs callback used to retrieve
- * the value of the CPR controller cpr_allowed_sw flag which
- * indicates if closed-loop operation is enabled
- * @data: Pointer to private data which is equal to the CPR
- * controller pointer
- * @val: Output parameter written with the value of
- * cpr_allowed_sw
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_debug_closed_loop_enable_get(void *data, u64 *val)
- {
- struct cpr3_controller *ctrl = data;
- *val = ctrl->cpr_allowed_sw;
- return 0;
- }
- DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_closed_loop_enable_fops,
- cpr3_debug_closed_loop_enable_get,
- cpr3_debug_closed_loop_enable_set,
- "%llu\n");
- /**
- * cpr3_debug_hw_closed_loop_enable_set() - debugfs callback used to change the
- * value of the CPR controller use_hw_closed_loop flag which
- * switches between software closed-loop and hardware closed-loop
- * operation for CPR3 and CPR4 controllers and between open-loop
- * and full hardware closed-loop operation for CPRh controllers.
- * @data: Pointer to private data which is equal to the CPR
- * controller pointer
- * @val: New value for use_hw_closed_loop
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_debug_hw_closed_loop_enable_set(void *data, u64 val)
- {
- struct cpr3_controller *ctrl = data;
- bool use_hw_closed_loop = !!val;
- struct cpr3_regulator *vreg;
- bool cpr_enabled;
- int i, j, k, rc;
- mutex_lock(&ctrl->lock);
- if (ctrl->use_hw_closed_loop == use_hw_closed_loop)
- goto done;
- if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) {
- rc = cpr3_ctrl_clear_cpr4_config(ctrl);
- if (rc) {
- cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n",
- rc);
- goto done;
- }
- }
- if (ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH)
- cpr3_ctrl_loop_disable(ctrl);
- ctrl->use_hw_closed_loop = use_hw_closed_loop;
- cpr_enabled = ctrl->cpr_enabled;
- /* Ensure that CPR clocks are enabled before writing to registers. */
- if (!cpr_enabled) {
- rc = cpr3_clock_enable(ctrl);
- if (rc) {
- cpr3_err(ctrl, "clock enable failed, rc=%d\n", rc);
- goto done;
- }
- ctrl->cpr_enabled = true;
- }
- if (ctrl->use_hw_closed_loop && ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH)
- cpr3_write(ctrl, CPR3_REG_IRQ_EN, 0);
- if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) {
- cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
- CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK,
- ctrl->use_hw_closed_loop
- ? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE
- : CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE);
- } else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH) {
- cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
- CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK,
- ctrl->cpr_allowed_sw && ctrl->use_hw_closed_loop
- ? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE
- : CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE);
- } else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) {
- cpr3_write(ctrl, CPR3_REG_HW_CLOSED_LOOP,
- ctrl->use_hw_closed_loop
- ? CPR3_HW_CLOSED_LOOP_ENABLE
- : CPR3_HW_CLOSED_LOOP_DISABLE);
- }
- /* Turn off CPR clocks if they were off before this function call. */
- if (!cpr_enabled) {
- cpr3_clock_disable(ctrl);
- ctrl->cpr_enabled = false;
- }
- if (ctrl->use_hw_closed_loop && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) {
- rc = regulator_enable(ctrl->vdd_limit_regulator);
- if (rc) {
- cpr3_err(ctrl, "CPR limit regulator enable failed, rc=%d\n",
- rc);
- goto done;
- }
- rc = msm_spm_avs_enable_irq(0, MSM_SPM_AVS_IRQ_MAX);
- if (rc) {
- cpr3_err(ctrl, "could not enable max IRQ, rc=%d\n", rc);
- goto done;
- }
- } else if (!ctrl->use_hw_closed_loop
- && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) {
- rc = regulator_disable(ctrl->vdd_limit_regulator);
- if (rc) {
- cpr3_err(ctrl, "CPR limit regulator disable failed, rc=%d\n",
- rc);
- goto done;
- }
- rc = msm_spm_avs_disable_irq(0, MSM_SPM_AVS_IRQ_MAX);
- if (rc) {
- cpr3_err(ctrl, "could not disable max IRQ, rc=%d\n",
- rc);
- goto done;
- }
- }
- if (ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH) {
- /*
- * Due to APM and mem-acc floor restriction constraints,
- * the closed-loop voltage may be different when using
- * software closed-loop vs hardware closed-loop. Therefore,
- * reset the cached closed-loop voltage for all corners to the
- * corresponding open-loop voltage when switching between
- * SW and HW closed-loop mode.
- */
- for (i = 0; i < ctrl->thread_count; i++) {
- for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
- vreg = &ctrl->thread[i].vreg[j];
- for (k = 0; k < vreg->corner_count; k++)
- vreg->corner[k].last_volt
- = vreg->corner[k].open_loop_volt;
- }
- }
- /* Skip last_volt caching */
- ctrl->last_corner_was_closed_loop = false;
- rc = cpr3_regulator_update_ctrl_state(ctrl);
- if (rc) {
- cpr3_err(ctrl, "could not change CPR HW closed-loop enable state=%u, rc=%d\n",
- use_hw_closed_loop, rc);
- goto done;
- }
- cpr3_debug(ctrl, "CPR mode=%s\n",
- use_hw_closed_loop ?
- "HW closed-loop" : "SW closed-loop");
- } else {
- cpr3_debug(ctrl, "CPR mode=%s\n",
- ctrl->cpr_allowed_sw && use_hw_closed_loop ?
- "full HW closed-loop" : "open-loop");
- }
- done:
- mutex_unlock(&ctrl->lock);
- return 0;
- }
- /**
- * cpr3_debug_hw_closed_loop_enable_get() - debugfs callback used to retrieve
- * the value of the CPR controller use_hw_closed_loop flag which
- * indicates if hardware closed-loop operation is being used in
- * place of software closed-loop operation
- * @data: Pointer to private data which is equal to the CPR
- * controller pointer
- * @val: Output parameter written with the value of
- * use_hw_closed_loop
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_debug_hw_closed_loop_enable_get(void *data, u64 *val)
- {
- struct cpr3_controller *ctrl = data;
- *val = ctrl->use_hw_closed_loop;
- return 0;
- }
- DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_hw_closed_loop_enable_fops,
- cpr3_debug_hw_closed_loop_enable_get,
- cpr3_debug_hw_closed_loop_enable_set,
- "%llu\n");
- /**
- * cpr3_debug_trigger_aging_measurement_set() - debugfs callback used to trigger
- * another CPR measurement
- * @data: Pointer to private data which is equal to the CPR
- * controller pointer
- * @val: Unused
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_debug_trigger_aging_measurement_set(void *data, u64 val)
- {
- struct cpr3_controller *ctrl = data;
- int rc;
- mutex_lock(&ctrl->lock);
- if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) {
- rc = cpr3_ctrl_clear_cpr4_config(ctrl);
- if (rc) {
- cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n",
- rc);
- goto done;
- }
- }
- cpr3_ctrl_loop_disable(ctrl);
- cpr3_regulator_set_aging_ref_adjustment(ctrl, INT_MAX);
- ctrl->aging_required = true;
- ctrl->aging_succeeded = false;
- ctrl->aging_failed = false;
- rc = cpr3_regulator_update_ctrl_state(ctrl);
- if (rc) {
- cpr3_err(ctrl, "could not update the CPR controller state, rc=%d\n",
- rc);
- goto done;
- }
- done:
- mutex_unlock(&ctrl->lock);
- return 0;
- }
- DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_trigger_aging_measurement_fops,
- NULL,
- cpr3_debug_trigger_aging_measurement_set,
- "%llu\n");
- /**
- * cpr3_regulator_debugfs_ctrl_add() - add debugfs files to expose configuration
- * data for the CPR controller
- * @ctrl: Pointer to the CPR3 controller
- *
- * Return: none
- */
- static void cpr3_regulator_debugfs_ctrl_add(struct cpr3_controller *ctrl)
- {
- struct dentry *temp, *aggr_dir;
- int i;
- /* Add cpr3-regulator base directory if it isn't present already. */
- if (cpr3_debugfs_base == NULL) {
- cpr3_debugfs_base = debugfs_create_dir("cpr3-regulator", NULL);
- if (IS_ERR_OR_NULL(cpr3_debugfs_base)) {
- cpr3_err(ctrl, "cpr3-regulator debugfs base directory creation failed\n");
- cpr3_debugfs_base = NULL;
- return;
- }
- }
- ctrl->debugfs = debugfs_create_dir(ctrl->name, cpr3_debugfs_base);
- if (IS_ERR_OR_NULL(ctrl->debugfs)) {
- cpr3_err(ctrl, "cpr3-regulator controller debugfs directory creation failed\n");
- return;
- }
- temp = debugfs_create_file("cpr_closed_loop_enable", 0644,
- ctrl->debugfs, ctrl,
- &cpr3_debug_closed_loop_enable_fops);
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(ctrl, "cpr_closed_loop_enable debugfs file creation failed\n");
- return;
- }
- if (ctrl->supports_hw_closed_loop) {
- temp = debugfs_create_file("use_hw_closed_loop", 0644,
- ctrl->debugfs, ctrl,
- &cpr3_debug_hw_closed_loop_enable_fops);
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(ctrl, "use_hw_closed_loop debugfs file creation failed\n");
- return;
- }
- }
- temp = debugfs_create_int("thread_count", 0444, ctrl->debugfs,
- &ctrl->thread_count);
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(ctrl, "thread_count debugfs file creation failed\n");
- return;
- }
- if (ctrl->apm) {
- temp = debugfs_create_int("apm_threshold_volt", 0444,
- ctrl->debugfs, &ctrl->apm_threshold_volt);
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(ctrl, "apm_threshold_volt debugfs file creation failed\n");
- return;
- }
- }
- if (ctrl->aging_required || ctrl->aging_succeeded
- || ctrl->aging_failed) {
- temp = debugfs_create_int("aging_adj_volt", 0444,
- ctrl->debugfs, &ctrl->aging_ref_adjust_volt);
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(ctrl, "aging_adj_volt debugfs file creation failed\n");
- return;
- }
- temp = debugfs_create_file("aging_succeeded", 0444,
- ctrl->debugfs, &ctrl->aging_succeeded, &fops_bool_ro);
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(ctrl, "aging_succeeded debugfs file creation failed\n");
- return;
- }
- temp = debugfs_create_file("aging_failed", 0444,
- ctrl->debugfs, &ctrl->aging_failed, &fops_bool_ro);
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(ctrl, "aging_failed debugfs file creation failed\n");
- return;
- }
- temp = debugfs_create_file("aging_trigger", 0200,
- ctrl->debugfs, ctrl,
- &cpr3_debug_trigger_aging_measurement_fops);
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(ctrl, "aging_trigger debugfs file creation failed\n");
- return;
- }
- }
- aggr_dir = debugfs_create_dir("max_aggregated_voltages", ctrl->debugfs);
- if (IS_ERR_OR_NULL(aggr_dir)) {
- cpr3_err(ctrl, "max_aggregated_voltages debugfs directory creation failed\n");
- return;
- }
- temp = debugfs_create_int("floor_volt", 0444, aggr_dir,
- &ctrl->aggr_corner.floor_volt);
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(ctrl, "aggr floor_volt debugfs file creation failed\n");
- return;
- }
- temp = debugfs_create_int("ceiling_volt", 0444, aggr_dir,
- &ctrl->aggr_corner.ceiling_volt);
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(ctrl, "aggr ceiling_volt debugfs file creation failed\n");
- return;
- }
- temp = debugfs_create_int("open_loop_volt", 0444, aggr_dir,
- &ctrl->aggr_corner.open_loop_volt);
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(ctrl, "aggr open_loop_volt debugfs file creation failed\n");
- return;
- }
- temp = debugfs_create_int("last_volt", 0444, aggr_dir,
- &ctrl->aggr_corner.last_volt);
- if (IS_ERR_OR_NULL(temp)) {
- cpr3_err(ctrl, "aggr last_volt debugfs file creation failed\n");
- return;
- }
- for (i = 0; i < ctrl->thread_count; i++)
- cpr3_regulator_debugfs_thread_add(&ctrl->thread[i]);
- }
- /**
- * cpr3_regulator_debugfs_ctrl_remove() - remove debugfs files for the CPR
- * controller
- * @ctrl: Pointer to the CPR3 controller
- *
- * Note, this function must be called after the controller has been removed from
- * cpr3_controller_list and while the cpr3_controller_list_mutex lock is held.
- *
- * Return: none
- */
- static void cpr3_regulator_debugfs_ctrl_remove(struct cpr3_controller *ctrl)
- {
- if (list_empty(&cpr3_controller_list)) {
- debugfs_remove_recursive(cpr3_debugfs_base);
- cpr3_debugfs_base = NULL;
- } else {
- debugfs_remove_recursive(ctrl->debugfs);
- }
- }
- /**
- * cpr3_regulator_init_ctrl_data() - performs initialization of CPR controller
- * elements
- * @ctrl: Pointer to the CPR3 controller
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_regulator_init_ctrl_data(struct cpr3_controller *ctrl)
- {
- /* Read the initial vdd voltage from hardware. */
- ctrl->aggr_corner.last_volt
- = regulator_get_voltage(ctrl->vdd_regulator);
- if (ctrl->aggr_corner.last_volt < 0) {
- cpr3_err(ctrl, "regulator_get_voltage(vdd) failed, rc=%d\n",
- ctrl->aggr_corner.last_volt);
- return ctrl->aggr_corner.last_volt;
- }
- ctrl->aggr_corner.open_loop_volt = ctrl->aggr_corner.last_volt;
- return 0;
- }
- /**
- * cpr3_regulator_init_vreg_data() - performs initialization of common CPR3
- * regulator elements and validate aging configurations
- * @vreg: Pointer to the CPR3 regulator
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_regulator_init_vreg_data(struct cpr3_regulator *vreg)
- {
- int i, j;
- bool init_aging;
- vreg->current_corner = CPR3_REGULATOR_CORNER_INVALID;
- vreg->last_closed_loop_corner = CPR3_REGULATOR_CORNER_INVALID;
- init_aging = vreg->aging_allowed && vreg->thread->ctrl->aging_required;
- for (i = 0; i < vreg->corner_count; i++) {
- vreg->corner[i].last_volt = vreg->corner[i].open_loop_volt;
- vreg->corner[i].irq_en = CPR3_IRQ_UP | CPR3_IRQ_DOWN;
- vreg->corner[i].ro_mask = 0;
- for (j = 0; j < CPR3_RO_COUNT; j++) {
- if (vreg->corner[i].target_quot[j] == 0)
- vreg->corner[i].ro_mask |= BIT(j);
- }
- if (init_aging) {
- vreg->corner[i].unaged_floor_volt
- = vreg->corner[i].floor_volt;
- vreg->corner[i].unaged_ceiling_volt
- = vreg->corner[i].ceiling_volt;
- vreg->corner[i].unaged_open_loop_volt
- = vreg->corner[i].open_loop_volt;
- }
- if (vreg->aging_allowed) {
- if (vreg->corner[i].unaged_floor_volt <= 0) {
- cpr3_err(vreg, "invalid unaged_floor_volt[%d] = %d\n",
- i, vreg->corner[i].unaged_floor_volt);
- return -EINVAL;
- }
- if (vreg->corner[i].unaged_ceiling_volt <= 0) {
- cpr3_err(vreg, "invalid unaged_ceiling_volt[%d] = %d\n",
- i, vreg->corner[i].unaged_ceiling_volt);
- return -EINVAL;
- }
- if (vreg->corner[i].unaged_open_loop_volt <= 0) {
- cpr3_err(vreg, "invalid unaged_open_loop_volt[%d] = %d\n",
- i, vreg->corner[i].unaged_open_loop_volt);
- return -EINVAL;
- }
- }
- }
- if (vreg->aging_allowed && vreg->corner[vreg->aging_corner].ceiling_volt
- > vreg->thread->ctrl->aging_ref_volt) {
- cpr3_err(vreg, "aging corner %d ceiling voltage = %d > aging ref voltage = %d uV\n",
- vreg->aging_corner,
- vreg->corner[vreg->aging_corner].ceiling_volt,
- vreg->thread->ctrl->aging_ref_volt);
- return -EINVAL;
- }
- return 0;
- }
- /**
- * cpr3_regulator_suspend() - perform common required CPR3 power down steps
- * before the system enters suspend
- * @ctrl: Pointer to the CPR3 controller
- *
- * Return: 0 on success, errno on failure
- */
- int cpr3_regulator_suspend(struct cpr3_controller *ctrl)
- {
- int rc;
- mutex_lock(&ctrl->lock);
- if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) {
- rc = cpr3_ctrl_clear_cpr4_config(ctrl);
- if (rc) {
- cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n",
- rc);
- mutex_unlock(&ctrl->lock);
- return rc;
- }
- }
- cpr3_ctrl_loop_disable(ctrl);
- if (ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH) {
- rc = cpr3_closed_loop_disable(ctrl);
- if (rc)
- cpr3_err(ctrl, "could not disable CPR, rc=%d\n", rc);
- ctrl->cpr_suspended = true;
- }
- mutex_unlock(&ctrl->lock);
- return 0;
- }
- /**
- * cpr3_regulator_resume() - perform common required CPR3 power up steps after
- * the system resumes from suspend
- * @ctrl: Pointer to the CPR3 controller
- *
- * Return: 0 on success, errno on failure
- */
- int cpr3_regulator_resume(struct cpr3_controller *ctrl)
- {
- int rc;
- mutex_lock(&ctrl->lock);
- if (ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH) {
- ctrl->cpr_suspended = false;
- rc = cpr3_regulator_update_ctrl_state(ctrl);
- if (rc)
- cpr3_err(ctrl, "could not enable CPR, rc=%d\n", rc);
- } else {
- cpr3_ctrl_loop_enable(ctrl);
- }
- mutex_unlock(&ctrl->lock);
- return 0;
- }
- /**
- * cpr3_regulator_cpu_hotplug_callback() - reset CPR IRQ affinity when a CPU is
- * brought online via hotplug
- * @nb: Pointer to the notifier block
- * @action: hotplug action
- * @hcpu: long value corresponding to the CPU number
- *
- * Return: NOTIFY_OK
- */
- static int cpr3_regulator_cpu_hotplug_callback(struct notifier_block *nb,
- unsigned long action, void *hcpu)
- {
- struct cpr3_controller *ctrl = container_of(nb, struct cpr3_controller,
- cpu_hotplug_notifier);
- int cpu = (long)hcpu;
- action &= ~CPU_TASKS_FROZEN;
- if (action == CPU_ONLINE
- && cpumask_test_cpu(cpu, &ctrl->irq_affinity_mask))
- irq_set_affinity(ctrl->irq, &ctrl->irq_affinity_mask);
- return NOTIFY_OK;
- }
- /**
- * cpr3_regulator_validate_controller() - verify the data passed in via the
- * cpr3_controller data structure
- * @ctrl: Pointer to the CPR3 controller
- *
- * Return: 0 on success, errno on failure
- */
- static int cpr3_regulator_validate_controller(struct cpr3_controller *ctrl)
- {
- struct cpr3_thread *thread;
- struct cpr3_regulator *vreg;
- int i, j, allow_boost_vreg_count = 0;
- if (!ctrl->vdd_regulator && ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH) {
- cpr3_err(ctrl, "vdd regulator missing\n");
- return -EINVAL;
- } else if (ctrl->sensor_count <= 0
- || ctrl->sensor_count > CPR3_MAX_SENSOR_COUNT) {
- cpr3_err(ctrl, "invalid CPR sensor count=%d\n",
- ctrl->sensor_count);
- return -EINVAL;
- } else if (!ctrl->sensor_owner) {
- cpr3_err(ctrl, "CPR sensor ownership table missing\n");
- return -EINVAL;
- }
- if (ctrl->aging_required) {
- for (i = 0; i < ctrl->aging_sensor_count; i++) {
- if (ctrl->aging_sensor[i].sensor_id
- >= ctrl->sensor_count) {
- cpr3_err(ctrl, "aging_sensor[%d] id=%u is not in the value range 0-%d",
- i, ctrl->aging_sensor[i].sensor_id,
- ctrl->sensor_count - 1);
- return -EINVAL;
- }
- }
- }
- for (i = 0; i < ctrl->thread_count; i++) {
- thread = &ctrl->thread[i];
- for (j = 0; j < thread->vreg_count; j++) {
- vreg = &thread->vreg[j];
- if (vreg->allow_boost)
- allow_boost_vreg_count++;
- }
- }
- if (allow_boost_vreg_count > 1) {
- /*
- * Boost feature is not allowed to be used for more
- * than one CPR3 regulator of a CPR3 controller.
- */
- cpr3_err(ctrl, "Boost feature is enabled for more than one regulator\n");
- return -EINVAL;
- }
- return 0;
- }
- /**
- * cpr3_panic_callback() - panic notification callback function. This function
- * is invoked when a kernel panic occurs.
- * @nfb: Notifier block pointer of CPR3 controller
- * @event: Value passed unmodified to notifier function
- * @data: Pointer passed unmodified to notifier function
- *
- * Return: NOTIFY_OK
- */
- static int cpr3_panic_callback(struct notifier_block *nfb,
- unsigned long event, void *data)
- {
- struct cpr3_controller *ctrl = container_of(nfb,
- struct cpr3_controller, panic_notifier);
- struct cpr3_panic_regs_info *regs_info = ctrl->panic_regs_info;
- struct cpr3_reg_info *reg;
- int i = 0;
- for (i = 0; i < regs_info->reg_count; i++) {
- reg = &(regs_info->regs[i]);
- reg->value = readl_relaxed(reg->virt_addr);
- pr_err("%s[0x%08x] = 0x%08x\n", reg->name, reg->addr,
- reg->value);
- }
- /*
- * Barrier to ensure that the information has been updated in the
- * structure.
- */
- mb();
- return NOTIFY_OK;
- }
- /**
- * cpr3_regulator_register() - register the regulators for a CPR3 controller and
- * perform CPR hardware initialization
- * @pdev: Platform device pointer for the CPR3 controller
- * @ctrl: Pointer to the CPR3 controller
- *
- * Return: 0 on success, errno on failure
- */
- int cpr3_regulator_register(struct platform_device *pdev,
- struct cpr3_controller *ctrl)
- {
- struct device *dev = &pdev->dev;
- struct resource *res;
- int i, j, rc;
- if (!dev->of_node) {
- dev_err(dev, "%s: Device tree node is missing\n", __func__);
- return -EINVAL;
- }
- if (!ctrl || !ctrl->name) {
- dev_err(dev, "%s: CPR controller data is missing\n", __func__);
- return -EINVAL;
- }
- rc = cpr3_regulator_validate_controller(ctrl);
- if (rc) {
- cpr3_err(ctrl, "controller validation failed, rc=%d\n", rc);
- return rc;
- }
- mutex_init(&ctrl->lock);
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cpr_ctrl");
- if (!res || !res->start) {
- cpr3_err(ctrl, "CPR controller address is missing\n");
- return -ENXIO;
- }
- ctrl->cpr_ctrl_base = devm_ioremap(dev, res->start, resource_size(res));
- if (cpr3_regulator_cprh_initialized(ctrl)) {
- cpr3_err(ctrl, "CPRh controller already initialized by boot loader\n");
- return -EPERM;
- }
- if (ctrl->aging_possible_mask) {
- /*
- * Aging possible register address is required if an aging
- * possible mask has been specified.
- */
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "aging_allowed");
- if (!res || !res->start) {
- cpr3_err(ctrl, "CPR aging allowed address is missing\n");
- return -ENXIO;
- }
- ctrl->aging_possible_reg = devm_ioremap(dev, res->start,
- resource_size(res));
- }
- if (ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH) {
- ctrl->irq = platform_get_irq_byname(pdev, "cpr");
- if (ctrl->irq < 0) {
- cpr3_err(ctrl, "missing CPR interrupt\n");
- return ctrl->irq;
- }
- }
- if (ctrl->supports_hw_closed_loop) {
- rc = cpr3_regulator_init_hw_closed_loop_dependencies(pdev,
- ctrl);
- if (rc)
- return rc;
- if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) {
- ctrl->ceiling_irq = platform_get_irq_byname(pdev,
- "ceiling");
- if (ctrl->ceiling_irq < 0) {
- cpr3_err(ctrl, "missing ceiling interrupt\n");
- return ctrl->ceiling_irq;
- }
- }
- }
- if (ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH) {
- rc = cpr3_regulator_init_ctrl_data(ctrl);
- if (rc) {
- cpr3_err(ctrl, "CPR controller data initialization failed, rc=%d\n",
- rc);
- return rc;
- }
- }
- for (i = 0; i < ctrl->thread_count; i++) {
- for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
- rc = cpr3_regulator_init_vreg_data(
- &ctrl->thread[i].vreg[j]);
- if (rc)
- return rc;
- cpr3_print_quots(&ctrl->thread[i].vreg[j]);
- }
- }
- /*
- * Add the maximum possible aging voltage margin until it is possible
- * to perform an aging measurement.
- */
- if (ctrl->aging_required)
- cpr3_regulator_set_aging_ref_adjustment(ctrl, INT_MAX);
- rc = cpr3_regulator_init_ctrl(ctrl);
- if (rc) {
- cpr3_err(ctrl, "CPR controller initialization failed, rc=%d\n",
- rc);
- return rc;
- }
- /* Register regulator devices for all threads. */
- for (i = 0; i < ctrl->thread_count; i++) {
- for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
- rc = cpr3_regulator_vreg_register(
- &ctrl->thread[i].vreg[j]);
- if (rc) {
- cpr3_err(&ctrl->thread[i].vreg[j], "failed to register regulator, rc=%d\n",
- rc);
- goto free_regulators;
- }
- }
- }
- if (ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH) {
- rc = devm_request_threaded_irq(dev, ctrl->irq, NULL,
- cpr3_irq_handler,
- IRQF_ONESHOT |
- IRQF_TRIGGER_RISING,
- "cpr3", ctrl);
- if (rc) {
- cpr3_err(ctrl, "could not request IRQ %d, rc=%d\n",
- ctrl->irq, rc);
- goto free_regulators;
- }
- }
- if (ctrl->supports_hw_closed_loop &&
- ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) {
- rc = devm_request_threaded_irq(dev, ctrl->ceiling_irq, NULL,
- cpr3_ceiling_irq_handler,
- IRQF_ONESHOT | IRQF_TRIGGER_RISING,
- "cpr3_ceiling", ctrl);
- if (rc) {
- cpr3_err(ctrl, "could not request ceiling IRQ %d, rc=%d\n",
- ctrl->ceiling_irq, rc);
- goto free_regulators;
- }
- }
- if (ctrl->irq && !cpumask_empty(&ctrl->irq_affinity_mask)) {
- irq_set_affinity(ctrl->irq, &ctrl->irq_affinity_mask);
- ctrl->cpu_hotplug_notifier.notifier_call
- = cpr3_regulator_cpu_hotplug_callback;
- register_hotcpu_notifier(&ctrl->cpu_hotplug_notifier);
- }
- mutex_lock(&cpr3_controller_list_mutex);
- cpr3_regulator_debugfs_ctrl_add(ctrl);
- list_add(&ctrl->list, &cpr3_controller_list);
- mutex_unlock(&cpr3_controller_list_mutex);
- if (ctrl->panic_regs_info) {
- /* Register panic notification call back */
- ctrl->panic_notifier.notifier_call = cpr3_panic_callback;
- atomic_notifier_chain_register(&panic_notifier_list,
- &ctrl->panic_notifier);
- }
- return 0;
- free_regulators:
- for (i = 0; i < ctrl->thread_count; i++)
- for (j = 0; j < ctrl->thread[i].vreg_count; j++)
- if (!IS_ERR_OR_NULL(ctrl->thread[i].vreg[j].rdev))
- regulator_unregister(
- ctrl->thread[i].vreg[j].rdev);
- return rc;
- }
- /**
- * cpr3_regulator_unregister() - unregister the regulators for a CPR3 controller
- * and perform CPR hardware shutdown
- * @ctrl: Pointer to the CPR3 controller
- *
- * Return: 0 on success, errno on failure
- */
- int cpr3_regulator_unregister(struct cpr3_controller *ctrl)
- {
- int i, j, rc = 0;
- mutex_lock(&cpr3_controller_list_mutex);
- list_del(&ctrl->list);
- cpr3_regulator_debugfs_ctrl_remove(ctrl);
- mutex_unlock(&cpr3_controller_list_mutex);
- if (ctrl->irq && !cpumask_empty(&ctrl->irq_affinity_mask))
- unregister_hotcpu_notifier(&ctrl->cpu_hotplug_notifier);
- if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) {
- rc = cpr3_ctrl_clear_cpr4_config(ctrl);
- if (rc)
- cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n",
- rc);
- }
- cpr3_ctrl_loop_disable(ctrl);
- cpr3_closed_loop_disable(ctrl);
- if (ctrl->vdd_limit_regulator) {
- regulator_disable(ctrl->vdd_limit_regulator);
- if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3)
- msm_spm_avs_disable_irq(0, MSM_SPM_AVS_IRQ_MAX);
- }
- for (i = 0; i < ctrl->thread_count; i++)
- for (j = 0; j < ctrl->thread[i].vreg_count; j++)
- regulator_unregister(ctrl->thread[i].vreg[j].rdev);
- if (ctrl->panic_notifier.notifier_call)
- atomic_notifier_chain_unregister(&panic_notifier_list,
- &ctrl->panic_notifier);
- return 0;
- }
|