12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
- */
- #include <linux/device.h>
- #include <linux/regmap.h>
- #include <linux/delay.h>
- #include <linux/power_supply.h>
- #include <linux/regulator/driver.h>
- #include <linux/irq.h>
- #include <linux/iio/consumer.h>
- #include <dt-bindings/iio/qti_power_supply_iio.h>
- #include <linux/pmic-voter.h>
- #include <linux/ktime.h>
- #include <linux/usb/typec.h>
- #include <linux/alarmtimer.h>
- #include "smb5-lib.h"
- #include "smb5-reg.h"
- #include "schgm-flash.h"
- #include "step-chg-jeita.h"
- #include "storm-watch.h"
- #include "smb5-iio.h"
- #include "battery-profile-loader.h"
- #define smblib_err(chg, fmt, ...) \
- pr_err("%s: %s: " fmt, chg->name, \
- __func__, ##__VA_ARGS__) \
- #define smblib_dbg(chg, reason, fmt, ...) \
- do { \
- if (*chg->debug_mask & (reason)) \
- pr_info("%s: %s: " fmt, chg->name, \
- __func__, ##__VA_ARGS__); \
- else \
- pr_debug("%s: %s: " fmt, chg->name, \
- __func__, ##__VA_ARGS__); \
- } while (0)
- #define typec_rp_med_high(chg, typec_mode) \
- ((typec_mode == QTI_POWER_SUPPLY_TYPEC_SOURCE_MEDIUM \
- || typec_mode == QTI_POWER_SUPPLY_TYPEC_SOURCE_HIGH) \
- && (!chg->typec_legacy || chg->typec_legacy_use_rp_icl))
- static void update_sw_icl_max(struct smb_charger *chg, int val);
- static int smblib_get_prop_typec_mode(struct smb_charger *chg);
- int smblib_read(struct smb_charger *chg, u16 addr, u8 *val)
- {
- unsigned int value;
- int rc = 0;
- rc = regmap_read(chg->regmap, addr, &value);
- if (rc >= 0)
- *val = (u8)value;
- return rc;
- }
- int smblib_batch_read(struct smb_charger *chg, u16 addr, u8 *val,
- int count)
- {
- return regmap_bulk_read(chg->regmap, addr, val, count);
- }
- int smblib_write(struct smb_charger *chg, u16 addr, u8 val)
- {
- return regmap_write(chg->regmap, addr, val);
- }
- int smblib_batch_write(struct smb_charger *chg, u16 addr, u8 *val,
- int count)
- {
- return regmap_bulk_write(chg->regmap, addr, val, count);
- }
- int smblib_masked_write(struct smb_charger *chg, u16 addr, u8 mask, u8 val)
- {
- return regmap_update_bits(chg->regmap, addr, mask, val);
- }
- int smblib_get_iio_channel(struct smb_charger *chg, const char *propname,
- struct iio_channel **chan)
- {
- int rc = 0;
- rc = of_property_match_string(chg->dev->of_node,
- "io-channel-names", propname);
- if (rc < 0)
- return 0;
- *chan = devm_iio_channel_get(chg->dev, propname);
- if (IS_ERR(*chan)) {
- rc = PTR_ERR(*chan);
- if (rc != -EPROBE_DEFER)
- smblib_err(chg, "%s channel unavailable, %d\n",
- propname, rc);
- *chan = NULL;
- }
- return rc;
- }
- #define DIV_FACTOR_MICRO_V_I 1
- #define DIV_FACTOR_MILI_V_I 1000
- #define DIV_FACTOR_DECIDEGC 100
- static int smblib_read_iio_channel(struct smb_charger *chg,
- struct iio_channel *chan, int div, int *data)
- {
- int rc = 0;
- *data = -ENODATA;
- if (chan) {
- rc = iio_read_channel_processed(chan, data);
- if (rc < 0) {
- smblib_err(chg, "Error in reading IIO channel data, rc=%d\n",
- rc);
- return rc;
- }
- if (div != 0)
- *data /= div;
- }
- return rc;
- }
- static int smblib_get_jeita_cc_delta(struct smb_charger *chg, int *cc_delta_ua)
- {
- int rc, cc_minus_ua;
- u8 stat;
- rc = smblib_read(chg, BATTERY_CHARGER_STATUS_7_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_2 rc=%d\n",
- rc);
- return rc;
- }
- if (stat & BAT_TEMP_STATUS_HOT_SOFT_BIT) {
- rc = smblib_get_charge_param(chg, &chg->param.jeita_cc_comp_hot,
- &cc_minus_ua);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get jeita cc minus rc=%d\n",
- rc);
- return rc;
- }
- } else if (stat & BAT_TEMP_STATUS_COLD_SOFT_BIT) {
- rc = smblib_get_charge_param(chg,
- &chg->param.jeita_cc_comp_cold,
- &cc_minus_ua);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get jeita cc minus rc=%d\n",
- rc);
- return rc;
- }
- } else {
- cc_minus_ua = 0;
- }
- *cc_delta_ua = -cc_minus_ua;
- return 0;
- }
- int smblib_icl_override(struct smb_charger *chg, enum icl_override_mode mode)
- {
- int rc;
- u8 usb51_mode, icl_override, apsd_override;
- switch (mode) {
- case SW_OVERRIDE_USB51_MODE:
- usb51_mode = 0;
- icl_override = ICL_OVERRIDE_BIT;
- apsd_override = 0;
- break;
- case SW_OVERRIDE_HC_MODE:
- usb51_mode = USBIN_MODE_CHG_BIT;
- icl_override = 0;
- apsd_override = ICL_OVERRIDE_AFTER_APSD_BIT;
- break;
- case HW_AUTO_MODE:
- default:
- usb51_mode = USBIN_MODE_CHG_BIT;
- icl_override = 0;
- apsd_override = 0;
- break;
- }
- rc = smblib_masked_write(chg, USBIN_ICL_OPTIONS_REG,
- USBIN_MODE_CHG_BIT, usb51_mode);
- if (rc < 0) {
- smblib_err(chg, "Couldn't set USBIN_ICL_OPTIONS rc=%d\n", rc);
- return rc;
- }
- rc = smblib_masked_write(chg, CMD_ICL_OVERRIDE_REG,
- ICL_OVERRIDE_BIT, icl_override);
- if (rc < 0) {
- smblib_err(chg, "Couldn't override ICL rc=%d\n", rc);
- return rc;
- }
- rc = smblib_masked_write(chg, USBIN_LOAD_CFG_REG,
- ICL_OVERRIDE_AFTER_APSD_BIT, apsd_override);
- if (rc < 0) {
- smblib_err(chg, "Couldn't override ICL_AFTER_APSD rc=%d\n", rc);
- return rc;
- }
- return rc;
- }
- /*
- * This function does smb_en pin access, which is lock protected.
- * It should be called with smb_lock held.
- */
- static int smblib_select_sec_charger_locked(struct smb_charger *chg,
- int sec_chg)
- {
- int rc = 0;
- switch (sec_chg) {
- case QTI_POWER_SUPPLY_CHARGER_SEC_CP:
- vote(chg->pl_disable_votable, PL_SMB_EN_VOTER, true, 0);
- /* select Charge Pump instead of slave charger */
- rc = smblib_masked_write(chg, MISC_SMB_CFG_REG,
- SMB_EN_SEL_BIT, SMB_EN_SEL_BIT);
- if (rc < 0) {
- dev_err(chg->dev, "Couldn't select SMB charger rc=%d\n",
- rc);
- return rc;
- }
- /* Enable Charge Pump, under HW control */
- rc = smblib_masked_write(chg, MISC_SMB_EN_CMD_REG,
- EN_CP_CMD_BIT, EN_CP_CMD_BIT);
- if (rc < 0) {
- dev_err(chg->dev, "Couldn't enable SMB charger rc=%d\n",
- rc);
- return rc;
- }
- vote(chg->smb_override_votable, PL_SMB_EN_VOTER, false, 0);
- break;
- case QTI_POWER_SUPPLY_CHARGER_SEC_PL:
- /* select slave charger instead of Charge Pump */
- rc = smblib_masked_write(chg, MISC_SMB_CFG_REG,
- SMB_EN_SEL_BIT, 0);
- if (rc < 0) {
- dev_err(chg->dev, "Couldn't select SMB charger rc=%d\n",
- rc);
- return rc;
- }
- /* Enable slave charger, under HW control */
- rc = smblib_masked_write(chg, MISC_SMB_EN_CMD_REG,
- EN_STAT_CMD_BIT, EN_STAT_CMD_BIT);
- if (rc < 0) {
- dev_err(chg->dev, "Couldn't enable SMB charger rc=%d\n",
- rc);
- return rc;
- }
- vote(chg->smb_override_votable, PL_SMB_EN_VOTER, false, 0);
- vote(chg->pl_disable_votable, PL_SMB_EN_VOTER, false, 0);
- break;
- case QTI_POWER_SUPPLY_CHARGER_SEC_NONE:
- default:
- vote(chg->pl_disable_votable, PL_SMB_EN_VOTER, true, 0);
- /* SW override, disabling secondary charger(s) */
- vote(chg->smb_override_votable, PL_SMB_EN_VOTER, true, 0);
- break;
- }
- return rc;
- }
- static int smblib_select_sec_charger(struct smb_charger *chg, int sec_chg,
- int reason, bool toggle)
- {
- int rc;
- mutex_lock(&chg->smb_lock);
- if (toggle && sec_chg == QTI_POWER_SUPPLY_CHARGER_SEC_CP) {
- rc = smblib_select_sec_charger_locked(chg,
- QTI_POWER_SUPPLY_CHARGER_SEC_NONE);
- if (rc < 0) {
- dev_err(chg->dev, "Couldn't disable secondary charger rc=%d\n",
- rc);
- goto unlock_out;
- }
- /*
- * A minimum of 20us delay is expected before switching on STAT
- * pin.
- */
- usleep_range(20, 30);
- }
- rc = smblib_select_sec_charger_locked(chg, sec_chg);
- if (rc < 0) {
- dev_err(chg->dev, "Couldn't switch secondary charger rc=%d\n",
- rc);
- goto unlock_out;
- }
- chg->sec_chg_selected = sec_chg;
- chg->cp_reason = reason;
- unlock_out:
- mutex_unlock(&chg->smb_lock);
- return rc;
- }
- static void smblib_notify_extcon_props(struct smb_charger *chg, int id)
- {
- union extcon_property_value val;
- int prop_val;
- if (chg->connector_type == QTI_POWER_SUPPLY_CONNECTOR_TYPEC) {
- smblib_get_prop_typec_cc_orientation(chg, &prop_val);
- val.intval = ((prop_val == 2) ? 1 : 0);
- extcon_set_property(chg->extcon, id,
- EXTCON_PROP_USB_TYPEC_POLARITY, val);
- val.intval = true;
- extcon_set_property(chg->extcon, id,
- EXTCON_PROP_USB_SS, val);
- } else if (chg->connector_type ==
- QTI_POWER_SUPPLY_CONNECTOR_MICRO_USB) {
- val.intval = false;
- extcon_set_property(chg->extcon, id,
- EXTCON_PROP_USB_SS, val);
- }
- }
- static void smblib_notify_device_mode(struct smb_charger *chg, bool enable)
- {
- if (enable)
- smblib_notify_extcon_props(chg, EXTCON_USB);
- extcon_set_state_sync(chg->extcon, EXTCON_USB, enable);
- }
- static void smblib_notify_usb_host(struct smb_charger *chg, bool enable)
- {
- int rc = 0;
- if (enable) {
- smblib_dbg(chg, PR_OTG, "enabling VBUS in OTG mode\n");
- rc = smblib_masked_write(chg, DCDC_CMD_OTG_REG,
- OTG_EN_BIT, OTG_EN_BIT);
- if (rc < 0) {
- smblib_err(chg,
- "Couldn't enable VBUS in OTG mode rc=%d\n", rc);
- return;
- }
- smblib_notify_extcon_props(chg, EXTCON_USB_HOST);
- } else {
- smblib_dbg(chg, PR_OTG, "disabling VBUS in OTG mode\n");
- rc = smblib_masked_write(chg, DCDC_CMD_OTG_REG,
- OTG_EN_BIT, 0);
- if (rc < 0) {
- smblib_err(chg,
- "Couldn't disable VBUS in OTG mode rc=%d\n",
- rc);
- return;
- }
- }
- extcon_set_state_sync(chg->extcon, EXTCON_USB_HOST, enable);
- }
- /********************
- * REGISTER GETTERS *
- ********************/
- int smblib_get_charge_param(struct smb_charger *chg,
- struct smb_chg_param *param, int *val_u)
- {
- int rc = 0;
- u8 val_raw;
- rc = smblib_read(chg, param->reg, &val_raw);
- if (rc < 0) {
- smblib_err(chg, "%s: Couldn't read from 0x%04x rc=%d\n",
- param->name, param->reg, rc);
- return rc;
- }
- if (param->get_proc)
- *val_u = param->get_proc(param, val_raw);
- else
- *val_u = val_raw * param->step_u + param->min_u;
- smblib_dbg(chg, PR_REGISTER, "%s = %d (0x%02x)\n",
- param->name, *val_u, val_raw);
- return rc;
- }
- static int smblib_get_usb_suspend(struct smb_charger *chg, int *suspend)
- {
- int rc = 0;
- u8 temp;
- rc = smblib_read(chg, USBIN_CMD_IL_REG, &temp);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read USBIN_CMD_IL rc=%d\n", rc);
- return rc;
- }
- *suspend = temp & USBIN_SUSPEND_BIT;
- return rc;
- }
- static const s16 therm_lookup_table[] = {
- /* Index -30C~85C, ADC raw code */
- 0x6C92, 0x6C43, 0x6BF0, 0x6B98, 0x6B3A, 0x6AD8, 0x6A70, 0x6A03,
- 0x6990, 0x6916, 0x6897, 0x6811, 0x6785, 0x66F2, 0x6658, 0x65B7,
- 0x650F, 0x6460, 0x63AA, 0x62EC, 0x6226, 0x6159, 0x6084, 0x5FA8,
- 0x5EC3, 0x5DD8, 0x5CE4, 0x5BE9, 0x5AE7, 0x59DD, 0x58CD, 0x57B5,
- 0x5696, 0x5571, 0x5446, 0x5314, 0x51DD, 0x50A0, 0x4F5E, 0x4E17,
- 0x4CCC, 0x4B7D, 0x4A2A, 0x48D4, 0x477C, 0x4621, 0x44C4, 0x4365,
- 0x4206, 0x40A6, 0x3F45, 0x3DE6, 0x3C86, 0x3B28, 0x39CC, 0x3872,
- 0x3719, 0x35C4, 0x3471, 0x3322, 0x31D7, 0x308F, 0x2F4C, 0x2E0D,
- 0x2CD3, 0x2B9E, 0x2A6E, 0x2943, 0x281D, 0x26FE, 0x25E3, 0x24CF,
- 0x23C0, 0x22B8, 0x21B5, 0x20B8, 0x1FC2, 0x1ED1, 0x1DE6, 0x1D01,
- 0x1C22, 0x1B49, 0x1A75, 0x19A8, 0x18E0, 0x181D, 0x1761, 0x16A9,
- 0x15F7, 0x154A, 0x14A2, 0x13FF, 0x1361, 0x12C8, 0x1234, 0x11A4,
- 0x1119, 0x1091, 0x100F, 0x0F90, 0x0F15, 0x0E9E, 0x0E2B, 0x0DBC,
- 0x0D50, 0x0CE8, 0x0C83, 0x0C21, 0x0BC3, 0x0B67, 0x0B0F, 0x0AB9,
- 0x0A66, 0x0A16, 0x09C9, 0x097E,
- };
- int smblib_get_thermal_threshold(struct smb_charger *chg, u16 addr, int *val)
- {
- u8 buff[2];
- s16 temp;
- int rc = 0;
- int i, lower, upper;
- rc = smblib_batch_read(chg, addr, buff, 2);
- if (rc < 0) {
- pr_err("failed to write to 0x%04X, rc=%d\n", addr, rc);
- return rc;
- }
- temp = buff[1] | buff[0] << 8;
- lower = 0;
- upper = ARRAY_SIZE(therm_lookup_table) - 1;
- while (lower <= upper) {
- i = (upper + lower) / 2;
- if (therm_lookup_table[i] < temp)
- upper = i - 1;
- else if (therm_lookup_table[i] > temp)
- lower = i + 1;
- else
- break;
- }
- /* index 0 corresonds to -30C */
- *val = (i - 30) * 10;
- return rc;
- }
- struct apsd_result {
- const char * const name;
- const u8 bit;
- const int val;
- };
- enum {
- UNKNOWN,
- SDP,
- CDP,
- DCP,
- OCP,
- FLOAT,
- HVDCP2,
- HVDCP3,
- MAX_TYPES
- };
- static const struct apsd_result smblib_apsd_results[] = {
- [UNKNOWN] = {
- .name = "UNKNOWN",
- .bit = 0,
- .val = POWER_SUPPLY_TYPE_UNKNOWN
- },
- [SDP] = {
- .name = "SDP",
- .bit = SDP_CHARGER_BIT,
- .val = POWER_SUPPLY_TYPE_USB
- },
- [CDP] = {
- .name = "CDP",
- .bit = CDP_CHARGER_BIT,
- .val = POWER_SUPPLY_TYPE_USB_CDP
- },
- [DCP] = {
- .name = "DCP",
- .bit = DCP_CHARGER_BIT,
- .val = POWER_SUPPLY_TYPE_USB_DCP
- },
- [OCP] = {
- .name = "OCP",
- .bit = OCP_CHARGER_BIT,
- .val = POWER_SUPPLY_TYPE_USB_DCP
- },
- [FLOAT] = {
- .name = "FLOAT",
- .bit = FLOAT_CHARGER_BIT,
- .val = QTI_POWER_SUPPLY_TYPE_USB_FLOAT
- },
- [HVDCP2] = {
- .name = "HVDCP2",
- .bit = DCP_CHARGER_BIT | QC_2P0_BIT,
- .val = QTI_POWER_SUPPLY_TYPE_USB_HVDCP
- },
- [HVDCP3] = {
- .name = "HVDCP3",
- .bit = DCP_CHARGER_BIT | QC_3P0_BIT,
- .val = QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3,
- },
- };
- static const struct apsd_result *smblib_get_apsd_result(struct smb_charger *chg)
- {
- int rc, i;
- u8 apsd_stat, stat;
- const struct apsd_result *result = &smblib_apsd_results[UNKNOWN];
- rc = smblib_read(chg, APSD_STATUS_REG, &apsd_stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read APSD_STATUS rc=%d\n", rc);
- return result;
- }
- smblib_dbg(chg, PR_REGISTER, "APSD_STATUS = 0x%02x\n", apsd_stat);
- if (!(apsd_stat & APSD_DTC_STATUS_DONE_BIT))
- return result;
- rc = smblib_read(chg, APSD_RESULT_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read APSD_RESULT_STATUS rc=%d\n",
- rc);
- return result;
- }
- stat &= APSD_RESULT_STATUS_MASK;
- for (i = 0; i < ARRAY_SIZE(smblib_apsd_results); i++) {
- if (smblib_apsd_results[i].bit == stat)
- result = &smblib_apsd_results[i];
- }
- if (apsd_stat & QC_CHARGER_BIT) {
- /* since its a qc_charger, either return HVDCP3 or HVDCP2 */
- if (result != &smblib_apsd_results[HVDCP3])
- result = &smblib_apsd_results[HVDCP2];
- }
- return result;
- }
- #define INPUT_NOT_PRESENT 0
- #define INPUT_PRESENT_USB BIT(1)
- #define INPUT_PRESENT_DC BIT(2)
- static int smblib_is_input_present(struct smb_charger *chg,
- int *present)
- {
- int rc;
- union power_supply_propval pval = {0, };
- *present = INPUT_NOT_PRESENT;
- rc = smblib_get_prop_usb_present(chg, &pval);
- if (rc < 0) {
- pr_err("Couldn't get usb presence status rc=%d\n", rc);
- return rc;
- }
- *present |= pval.intval ? INPUT_PRESENT_USB : INPUT_NOT_PRESENT;
- rc = smblib_get_prop_dc_present(chg, &pval);
- if (rc < 0) {
- pr_err("Couldn't get dc presence status rc=%d\n", rc);
- return rc;
- }
- *present |= pval.intval ? INPUT_PRESENT_DC : INPUT_NOT_PRESENT;
- return 0;
- }
- #define AICL_RANGE2_MIN_MV 5600
- #define AICL_RANGE2_STEP_DELTA_MV 200
- #define AICL_RANGE2_OFFSET 16
- int smblib_get_aicl_cont_threshold(struct smb_chg_param *param, u8 val_raw)
- {
- int base = param->min_u;
- u8 reg = val_raw;
- int step = param->step_u;
- if (val_raw >= AICL_RANGE2_OFFSET) {
- reg = val_raw - AICL_RANGE2_OFFSET;
- base = AICL_RANGE2_MIN_MV;
- step = AICL_RANGE2_STEP_DELTA_MV;
- }
- return base + (reg * step);
- }
- /********************
- * REGISTER SETTERS *
- ********************/
- static const struct buck_boost_freq chg_freq_list[] = {
- [0] = {
- .freq_khz = 2400,
- .val = 7,
- },
- [1] = {
- .freq_khz = 2100,
- .val = 8,
- },
- [2] = {
- .freq_khz = 1600,
- .val = 11,
- },
- [3] = {
- .freq_khz = 1200,
- .val = 15,
- },
- };
- int smblib_set_chg_freq(struct smb_chg_param *param,
- int val_u, u8 *val_raw)
- {
- u8 i;
- if (val_u > param->max_u || val_u < param->min_u)
- return -EINVAL;
- /* Charger FSW is the configured freqency / 2 */
- val_u *= 2;
- for (i = 0; i < ARRAY_SIZE(chg_freq_list); i++) {
- if (chg_freq_list[i].freq_khz == val_u)
- break;
- }
- if (i == ARRAY_SIZE(chg_freq_list)) {
- pr_err("Invalid frequency %d Hz\n", val_u / 2);
- return -EINVAL;
- }
- *val_raw = chg_freq_list[i].val;
- return 0;
- }
- static int smblib_set_opt_switcher_freq(struct smb_charger *chg, int fsw_khz)
- {
- int rc = 0;
- rc = smblib_set_charge_param(chg, &chg->param.freq_switcher, fsw_khz);
- if (rc < 0)
- dev_err(chg->dev, "Error in setting freq_buck rc=%d\n", rc);
- return rc;
- }
- int smblib_set_charge_param(struct smb_charger *chg,
- struct smb_chg_param *param, int val_u)
- {
- int rc = 0;
- u8 val_raw;
- if (param->set_proc) {
- rc = param->set_proc(param, val_u, &val_raw);
- if (rc < 0)
- return -EINVAL;
- } else {
- if (val_u > param->max_u || val_u < param->min_u)
- smblib_dbg(chg, PR_MISC,
- "%s: %d is out of range [%d, %d]\n",
- param->name, val_u, param->min_u, param->max_u);
- if (val_u > param->max_u)
- val_u = param->max_u;
- if (val_u < param->min_u)
- val_u = param->min_u;
- val_raw = (val_u - param->min_u) / param->step_u;
- }
- rc = smblib_write(chg, param->reg, val_raw);
- if (rc < 0) {
- smblib_err(chg, "%s: Couldn't write 0x%02x to 0x%04x rc=%d\n",
- param->name, val_raw, param->reg, rc);
- return rc;
- }
- smblib_dbg(chg, PR_REGISTER, "%s = %d (0x%02x)\n",
- param->name, val_u, val_raw);
- return rc;
- }
- static int smblib_set_usb_suspend(struct smb_charger *chg, bool suspend)
- {
- int rc = 0;
- if (suspend)
- vote(chg->icl_irq_disable_votable, USB_SUSPEND_VOTER,
- true, 0);
- rc = smblib_masked_write(chg, USBIN_CMD_IL_REG, USBIN_SUSPEND_BIT,
- suspend ? USBIN_SUSPEND_BIT : 0);
- if (rc < 0)
- smblib_err(chg, "Couldn't write %s to USBIN_SUSPEND_BIT rc=%d\n",
- suspend ? "suspend" : "resume", rc);
- if (!suspend)
- vote(chg->icl_irq_disable_votable, USB_SUSPEND_VOTER,
- false, 0);
- return rc;
- }
- static int smblib_set_dc_suspend(struct smb_charger *chg, bool suspend)
- {
- int rc = 0;
- rc = smblib_masked_write(chg, DCIN_CMD_IL_REG, DCIN_SUSPEND_BIT,
- suspend ? DCIN_SUSPEND_BIT : 0);
- if (rc < 0)
- smblib_err(chg, "Couldn't write %s to DCIN_SUSPEND_BIT rc=%d\n",
- suspend ? "suspend" : "resume", rc);
- return rc;
- }
- static int smblib_usb_pd_adapter_allowance_override(struct smb_charger *chg,
- u8 allowed_voltage)
- {
- int rc = 0;
- if (chg->chg_param.smb_version == PMI632)
- return 0;
- rc = smblib_write(chg, USBIN_ADAPTER_ALLOW_OVERRIDE_REG,
- allowed_voltage);
- if (rc < 0)
- smblib_err(chg, "Couldn't write 0x%02x to USBIN_ADAPTER_ALLOW_OVERRIDE_REG rc=%d\n",
- allowed_voltage, rc);
- smblib_dbg(chg, PR_MISC, "set USBIN_ALLOW_OVERRIDE: %d\n",
- allowed_voltage);
- return rc;
- }
- #define MICRO_5V 5000000
- #define MICRO_9V 9000000
- #define MICRO_12V 12000000
- static int smblib_set_usb_pd_fsw(struct smb_charger *chg, int voltage)
- {
- int rc = 0;
- if (voltage == MICRO_5V)
- rc = smblib_set_opt_switcher_freq(chg, chg->chg_freq.freq_5V);
- else if (voltage > MICRO_5V && voltage < MICRO_9V)
- rc = smblib_set_opt_switcher_freq(chg,
- chg->chg_freq.freq_6V_8V);
- else if (voltage >= MICRO_9V && voltage < MICRO_12V)
- rc = smblib_set_opt_switcher_freq(chg, chg->chg_freq.freq_9V);
- else if (voltage == MICRO_12V)
- rc = smblib_set_opt_switcher_freq(chg, chg->chg_freq.freq_12V);
- else {
- smblib_err(chg, "Couldn't set Fsw: invalid voltage %d\n",
- voltage);
- return -EINVAL;
- }
- return rc;
- }
- #define CONT_AICL_HEADROOM_MV 1000
- #define AICL_THRESHOLD_MV_IN_CC 5000
- static int smblib_set_usb_pd_allowed_voltage(struct smb_charger *chg,
- int min_allowed_uv, int max_allowed_uv)
- {
- int rc, aicl_threshold;
- u8 vbus_allowance;
- if (chg->chg_param.smb_version == PMI632)
- return 0;
- if (chg->pd_active == QTI_POWER_SUPPLY_PD_PPS_ACTIVE) {
- vbus_allowance = CONTINUOUS;
- } else if (min_allowed_uv == MICRO_5V && max_allowed_uv == MICRO_5V) {
- vbus_allowance = FORCE_5V;
- } else if (min_allowed_uv == MICRO_9V && max_allowed_uv == MICRO_9V) {
- vbus_allowance = FORCE_9V;
- } else if (min_allowed_uv == MICRO_12V && max_allowed_uv == MICRO_12V) {
- vbus_allowance = FORCE_12V;
- } else if (min_allowed_uv < MICRO_12V && max_allowed_uv <= MICRO_12V) {
- vbus_allowance = CONTINUOUS;
- } else {
- smblib_err(chg, "invalid allowed voltage [%d, %d]\n",
- min_allowed_uv, max_allowed_uv);
- return -EINVAL;
- }
- rc = smblib_usb_pd_adapter_allowance_override(chg, vbus_allowance);
- if (rc < 0) {
- smblib_err(chg, "set CONTINUOUS allowance failed, rc=%d\n",
- rc);
- return rc;
- }
- if (vbus_allowance != CONTINUOUS)
- return 0;
- aicl_threshold = min_allowed_uv / 1000 - CONT_AICL_HEADROOM_MV;
- if (chg->adapter_cc_mode)
- aicl_threshold = min(aicl_threshold, AICL_THRESHOLD_MV_IN_CC);
- rc = smblib_set_charge_param(chg, &chg->param.aicl_cont_threshold,
- aicl_threshold);
- if (rc < 0) {
- smblib_err(chg, "set CONT_AICL_THRESHOLD failed, rc=%d\n",
- rc);
- return rc;
- }
- return rc;
- }
- int smblib_set_aicl_cont_threshold(struct smb_chg_param *param,
- int val_u, u8 *val_raw)
- {
- int base = param->min_u;
- int offset = 0;
- int step = param->step_u;
- if (val_u > param->max_u)
- val_u = param->max_u;
- if (val_u < param->min_u)
- val_u = param->min_u;
- if (val_u >= AICL_RANGE2_MIN_MV) {
- base = AICL_RANGE2_MIN_MV;
- step = AICL_RANGE2_STEP_DELTA_MV;
- offset = AICL_RANGE2_OFFSET;
- }
- *val_raw = ((val_u - base) / step) + offset;
- return 0;
- }
- /********************
- * HELPER FUNCTIONS *
- ********************/
- /* CP channels */
- static const char * const smblib_cp_ext_iio_chan[] = {
- [CP_PARALLEL_OUTPUT_MODE] = "cp_parallel_output_mode",
- [CP_MASTER_ENABLE] = "cp_enable",
- [CP_ILIM] = "cp_ilim",
- [CP_DIE_TEMP] = "cp_die_temp",
- };
- /* SMB1355 channels */
- static const char * const smblib_parallel_ext_iio_chan[] = {
- [SMB_CHARGER_TEMP] = "pl_charger_temp",
- [SMB_CHARGER_TEMP_MAX] = "pl_charger_temp_max",
- [SMB_SET_SHIP_MODE] = "pl_set_ship_mode",
- };
- /* QG/FG channels */
- static const char * const smblib_qg_ext_iio_chan[] = {
- [SMB5_QG_DEBUG_BATTERY] = "debug_battery",
- [SMB5_QG_CAPACITY] = "capacity",
- [SMB5_QG_REAL_CAPACITY] = "real_capacity",
- [SMB5_QG_CC_SOC] = "cc_soc",
- [SMB5_QG_CURRENT_NOW] = "current_now",
- [SMB5_QG_VOLTAGE_NOW] = "voltage_now",
- [SMB5_QG_VOLTAGE_MAX] = "voltage_max",
- [SMB5_QG_CHARGE_FULL] = "charge_full",
- [SMB5_QG_RESISTANCE_ID] = "resistance_id",
- [SMB5_QG_TEMP] = "temp",
- [SMB5_QG_CHARGE_COUNTER] = "charge_counter",
- [SMB5_QG_CYCLE_COUNT] = "cycle_count",
- [SMB5_QG_CHARGE_FULL_DESIGN] = "charge_full_design",
- [SMB5_QG_TIME_TO_FULL_NOW] = "time_to_full_now",
- };
- static int smblib_read_iio_prop(struct smb_charger *chg,
- enum iio_type type, int iio_chan, int *val)
- {
- struct iio_channel *iio_chan_list;
- int rc;
- switch (type) {
- case QG:
- if (IS_ERR_OR_NULL(chg->iio_chan_list_qg))
- return -ENODEV;
- iio_chan_list = chg->iio_chan_list_qg[iio_chan];
- break;
- case CP:
- if (IS_ERR_OR_NULL(chg->iio_chan_list_cp))
- return -ENODEV;
- iio_chan_list = chg->iio_chan_list_cp[iio_chan];
- break;
- case SMB_PARALLEL:
- if (IS_ERR_OR_NULL(chg->iio_chan_list_smb_parallel))
- return -ENODEV;
- iio_chan_list = chg->iio_chan_list_smb_parallel[iio_chan];
- break;
- default:
- pr_err_ratelimited("iio_type %d is not supported\n", type);
- return -EINVAL;
- }
- rc = iio_read_channel_processed(iio_chan_list, val);
- return rc < 0 ? rc : 0;
- }
- static int smblib_write_iio_prop(struct smb_charger *chg,
- enum iio_type type, int iio_chan, int val)
- {
- struct iio_channel *iio_chan_list;
- switch (type) {
- case QG:
- if (IS_ERR_OR_NULL(chg->iio_chan_list_qg))
- return -ENODEV;
- iio_chan_list = chg->iio_chan_list_qg[iio_chan];
- break;
- case CP:
- if (IS_ERR_OR_NULL(chg->iio_chan_list_cp))
- return -ENODEV;
- iio_chan_list = chg->iio_chan_list_cp[iio_chan];
- break;
- case SMB_PARALLEL:
- if (IS_ERR_OR_NULL(chg->iio_chan_list_smb_parallel))
- return -ENODEV;
- iio_chan_list = chg->iio_chan_list_smb_parallel[iio_chan];
- break;
- default:
- pr_err_ratelimited("iio_type %d is not supported\n", type);
- return -EINVAL;
- }
- return iio_write_channel_raw(iio_chan_list, val);
- }
- static bool is_cp_available(struct smb_charger *chg)
- {
- int rc;
- struct iio_channel **iio_list;
- if (IS_ERR(chg->iio_chan_list_cp))
- return false;
- if (!chg->iio_chan_list_cp) {
- iio_list = get_ext_channels(chg->dev,
- smblib_cp_ext_iio_chan,
- ARRAY_SIZE(smblib_cp_ext_iio_chan));
- if (IS_ERR(iio_list)) {
- rc = PTR_ERR(iio_list);
- if (rc != -EPROBE_DEFER) {
- dev_err(chg->dev, "Failed to get channels, rc=%d\n",
- rc);
- chg->iio_chan_list_cp = ERR_PTR(-EINVAL);
- }
- return false;
- }
- chg->iio_chan_list_cp = iio_list;
- }
- return true;
- }
- static bool is_cp_topo_vbatt(struct smb_charger *chg)
- {
- int rc = 0, val;
- bool is_vbatt;
- if (!is_cp_available(chg))
- return false;
- rc = smblib_read_iio_prop(chg, CP, CP_PARALLEL_OUTPUT_MODE, &val);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get CP PARALLEL_OUTPUT_MODE rc=%d\n",
- rc);
- return false;
- }
- is_vbatt = (val == QTI_POWER_SUPPLY_PL_OUTPUT_VBAT);
- smblib_dbg(chg, PR_WLS, "%s\n", is_vbatt ? "true" : "false");
- return is_vbatt;
- }
- #define CP_TO_MAIN_ICL_OFFSET_PC 10
- int smblib_get_qc3_main_icl_offset(struct smb_charger *chg, int *offset_ua)
- {
- int rc = 0, val;
- /*
- * Apply ILIM offset to main charger's FCC if all of the following
- * conditions are met:
- * - HVDCP3 adapter with CP as parallel charger
- * - Output connection topology is VBAT
- */
- if (!is_cp_topo_vbatt(chg) || chg->hvdcp3_standalone_config ||
- ((chg->real_charger_type !=
- QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3) &&
- chg->real_charger_type !=
- QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3P5))
- return -EINVAL;
- rc = smblib_read_iio_prop(chg, CP, CP_MASTER_ENABLE, &val);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get CP ENABLE rc=%d\n", rc);
- return rc;
- }
- if (!val)
- return -EINVAL;
- rc = smblib_read_iio_prop(chg, CP, CP_ILIM, &val);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get CP ILIM rc=%d\n", rc);
- return rc;
- }
- *offset_ua = (val * CP_TO_MAIN_ICL_OFFSET_PC * 2) / 100;
- return 0;
- }
- int smblib_get_prop_from_bms(struct smb_charger *chg,
- int channel, int *val)
- {
- int rc;
- if (IS_ERR_OR_NULL(chg->iio_chan_list_qg))
- return -ENODEV;
- rc = iio_read_channel_processed(chg->iio_chan_list_qg[channel],
- val);
- return rc < 0 ? rc : 0;
- }
- void smblib_apsd_enable(struct smb_charger *chg, bool enable)
- {
- int rc;
- rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
- BC1P2_SRC_DETECT_BIT,
- enable ? BC1P2_SRC_DETECT_BIT : 0);
- if (rc < 0)
- smblib_err(chg, "failed to write USBIN_OPTIONS_1_CFG rc=%d\n",
- rc);
- }
- void smblib_hvdcp_detect_enable(struct smb_charger *chg, bool enable)
- {
- int rc;
- u8 mask;
- mask = HVDCP_AUTH_ALG_EN_CFG_BIT | HVDCP_EN_BIT;
- rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG, mask,
- enable ? mask : 0);
- if (rc < 0)
- smblib_err(chg, "failed to write USBIN_OPTIONS_1_CFG rc=%d\n",
- rc);
- }
- static void smblib_hvdcp_detect_try_enable(struct smb_charger *chg, bool enable)
- {
- if (chg->hvdcp_disable || chg->pd_not_supported)
- return;
- smblib_hvdcp_detect_enable(chg, enable);
- }
- void smblib_hvdcp_hw_inov_enable(struct smb_charger *chg, bool enable)
- {
- int rc;
- rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
- HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT,
- enable ? HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT : 0);
- if (rc < 0)
- smblib_err(chg, "failed to write USBIN_OPTIONS_1_CFG rc=%d\n",
- rc);
- }
- void smblib_hvdcp_exit_config(struct smb_charger *chg)
- {
- u8 stat;
- int rc;
- rc = smblib_read(chg, APSD_RESULT_STATUS_REG, &stat);
- if (rc < 0)
- return;
- if (stat & (QC_3P0_BIT | QC_2P0_BIT)) {
- /* force HVDCP to 5V */
- smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
- HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT, 0);
- smblib_write(chg, CMD_HVDCP_2_REG, FORCE_5V_BIT);
- /* rerun APSD */
- smblib_masked_write(chg, CMD_APSD_REG, APSD_RERUN_BIT,
- APSD_RERUN_BIT);
- }
- }
- static int smblib_request_dpdm(struct smb_charger *chg, bool enable)
- {
- int rc = 0;
- if (chg->pr_swap_in_progress)
- return 0;
- /* fetch the DPDM regulator */
- if (!chg->dpdm_reg && of_get_property(chg->dev->of_node,
- "dpdm-supply", NULL)) {
- chg->dpdm_reg = devm_regulator_get(chg->dev, "dpdm");
- if (IS_ERR(chg->dpdm_reg)) {
- rc = PTR_ERR(chg->dpdm_reg);
- smblib_err(chg, "Couldn't get dpdm regulator rc=%d\n",
- rc);
- chg->dpdm_reg = NULL;
- return rc;
- }
- }
- mutex_lock(&chg->dpdm_lock);
- if (enable) {
- if (chg->dpdm_reg && !chg->dpdm_enabled) {
- smblib_dbg(chg, PR_MISC, "enabling DPDM regulator\n");
- rc = regulator_enable(chg->dpdm_reg);
- if (rc < 0)
- smblib_err(chg,
- "Couldn't enable dpdm regulator rc=%d\n",
- rc);
- else
- chg->dpdm_enabled = true;
- }
- } else {
- if (chg->dpdm_reg && chg->dpdm_enabled) {
- smblib_dbg(chg, PR_MISC, "disabling DPDM regulator\n");
- rc = regulator_disable(chg->dpdm_reg);
- if (rc < 0)
- smblib_err(chg,
- "Couldn't disable dpdm regulator rc=%d\n",
- rc);
- else
- chg->dpdm_enabled = false;
- }
- }
- mutex_unlock(&chg->dpdm_lock);
- return rc;
- }
- void smblib_rerun_apsd(struct smb_charger *chg)
- {
- int rc;
- smblib_dbg(chg, PR_MISC, "re-running APSD\n");
- rc = smblib_masked_write(chg, CMD_APSD_REG,
- APSD_RERUN_BIT, APSD_RERUN_BIT);
- if (rc < 0)
- smblib_err(chg, "Couldn't re-run APSD rc=%d\n", rc);
- }
- static const struct apsd_result *smblib_update_usb_type(struct smb_charger *chg)
- {
- const struct apsd_result *apsd_result = smblib_get_apsd_result(chg);
- /* if PD is active, APSD is disabled so won't have a valid result */
- if (chg->pd_active) {
- chg->real_charger_type = POWER_SUPPLY_TYPE_USB_PD;
- } else if (chg->qc3p5_detected) {
- chg->real_charger_type = QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3P5;
- } else {
- /*
- * Update real charger type only if its not FLOAT
- * detected as SDP
- */
- if (!(apsd_result->val == QTI_POWER_SUPPLY_TYPE_USB_FLOAT &&
- chg->real_charger_type == POWER_SUPPLY_TYPE_USB))
- chg->real_charger_type = apsd_result->val;
- }
- smblib_dbg(chg, PR_MISC, "APSD=%s PD=%d QC3P5=%d\n",
- apsd_result->name, chg->pd_active, chg->qc3p5_detected);
- return apsd_result;
- }
- static int smblib_notifier_call(struct notifier_block *nb,
- unsigned long ev, void *v)
- {
- struct power_supply *psy = v;
- struct smb_charger *chg = container_of(nb, struct smb_charger, nb);
- if (!strcmp(psy->desc->name, "bms")) {
- if (ev == PSY_EVENT_PROP_CHANGED)
- schedule_work(&chg->bms_update_work);
- }
- if (chg->jeita_configured == JEITA_CFG_NONE)
- schedule_work(&chg->jeita_update_work);
- if (chg->sec_pl_present && !chg->iio_chan_list_smb_parallel
- && !strcmp(psy->desc->name, "parallel"))
- schedule_work(&chg->pl_update_work);
- if (!strcmp(psy->desc->name, "charge_pump_master")) {
- pm_stay_awake(chg->dev);
- schedule_work(&chg->cp_status_change_work);
- }
- return NOTIFY_OK;
- }
- static int smblib_register_notifier(struct smb_charger *chg)
- {
- int rc;
- chg->nb.notifier_call = smblib_notifier_call;
- rc = power_supply_reg_notifier(&chg->nb);
- if (rc < 0) {
- smblib_err(chg, "Couldn't register psy notifier rc = %d\n", rc);
- return rc;
- }
- return 0;
- }
- static void smblib_uusb_removal(struct smb_charger *chg)
- {
- int rc;
- struct smb_irq_data *data;
- struct storm_watch *wdata;
- int sec_charger;
- sec_charger = chg->sec_pl_present ? QTI_POWER_SUPPLY_CHARGER_SEC_PL :
- QTI_POWER_SUPPLY_CHARGER_SEC_NONE;
- smblib_select_sec_charger(chg, sec_charger, QTI_POWER_SUPPLY_CP_NONE,
- false);
- cancel_delayed_work_sync(&chg->pl_enable_work);
- if (chg->wa_flags & BOOST_BACK_WA) {
- data = chg->irq_info[SWITCHER_POWER_OK_IRQ].irq_data;
- if (data) {
- wdata = &data->storm_data;
- update_storm_count(wdata, WEAK_CHG_STORM_COUNT);
- vote(chg->usb_icl_votable, BOOST_BACK_VOTER, false, 0);
- vote(chg->usb_icl_votable, WEAK_CHARGER_VOTER,
- false, 0);
- }
- }
- vote(chg->pl_disable_votable, PL_DELAY_VOTER, true, 0);
- vote(chg->awake_votable, PL_DELAY_VOTER, false, 0);
- /* reset both usbin current and voltage votes */
- vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0);
- vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0);
- vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true,
- is_flash_active(chg) ? SDP_CURRENT_UA : SDP_100_MA);
- vote(chg->usb_icl_votable, SW_QC3_VOTER, false, 0);
- vote(chg->usb_icl_votable, HVDCP2_ICL_VOTER, false, 0);
- vote(chg->usb_icl_votable, CHG_TERMINATION_VOTER, false, 0);
- vote(chg->usb_icl_votable, THERMAL_THROTTLE_VOTER, false, 0);
- vote(chg->limited_irq_disable_votable, CHARGER_TYPE_VOTER,
- true, 0);
- vote(chg->hdc_irq_disable_votable, CHARGER_TYPE_VOTER, true, 0);
- vote(chg->hdc_irq_disable_votable, HDC_IRQ_VOTER, false, 0);
- /* Remove SW thermal regulation WA votes */
- vote(chg->usb_icl_votable, SW_THERM_REGULATION_VOTER, false, 0);
- vote(chg->pl_disable_votable, SW_THERM_REGULATION_VOTER, false, 0);
- vote(chg->dc_suspend_votable, SW_THERM_REGULATION_VOTER, false, 0);
- if (chg->cp_disable_votable)
- vote(chg->cp_disable_votable, SW_THERM_REGULATION_VOTER,
- false, 0);
- /* reset USBOV votes and cancel work */
- cancel_delayed_work_sync(&chg->usbov_dbc_work);
- vote(chg->awake_votable, USBOV_DBC_VOTER, false, 0);
- chg->dbc_usbov = false;
- chg->voltage_min_uv = MICRO_5V;
- chg->voltage_max_uv = MICRO_5V;
- chg->usbin_forced_max_uv = 0;
- chg->usb_icl_delta_ua = 0;
- chg->pulse_cnt = 0;
- chg->uusb_apsd_rerun_done = false;
- chg->chg_param.forced_main_fcc = 0;
- del_timer_sync(&chg->apsd_timer);
- chg->apsd_ext_timeout = false;
- /* write back the default FLOAT charger configuration */
- rc = smblib_masked_write(chg, USBIN_OPTIONS_2_CFG_REG,
- (u8)FLOAT_OPTIONS_MASK, chg->float_cfg);
- if (rc < 0)
- smblib_err(chg, "Couldn't write float charger options rc=%d\n",
- rc);
- /* clear USB ICL vote for USB_PSY_VOTER */
- rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
- if (rc < 0)
- smblib_err(chg, "Couldn't un-vote for USB ICL rc=%d\n", rc);
- /* clear USB ICL vote for DCP_VOTER */
- rc = vote(chg->usb_icl_votable, DCP_VOTER, false, 0);
- if (rc < 0)
- smblib_err(chg,
- "Couldn't un-vote DCP from USB ICL rc=%d\n", rc);
- /*
- * if non-compliant charger caused UV, restore original max pulses
- * and turn SUSPEND_ON_COLLAPSE_USBIN_BIT back on.
- */
- if (chg->qc2_unsupported_voltage) {
- rc = smblib_masked_write(chg, HVDCP_PULSE_COUNT_MAX_REG,
- HVDCP_PULSE_COUNT_MAX_QC2_MASK,
- chg->qc2_max_pulses);
- if (rc < 0)
- smblib_err(chg, "Couldn't restore max pulses rc=%d\n",
- rc);
- if (!chg->disable_suspend_on_collapse) {
- rc = smblib_masked_write(chg, USBIN_AICL_OPTIONS_CFG_REG,
- SUSPEND_ON_COLLAPSE_USBIN_BIT,
- SUSPEND_ON_COLLAPSE_USBIN_BIT);
- if (rc < 0)
- smblib_err(chg,
- "Couldn't turn on SUSPEND_ON_COLLAPSE_USBIN_BIT rc=%d\n",
- rc);
- }
- chg->qc2_unsupported_voltage = QC2_COMPLIANT;
- }
- chg->qc3p5_detected = false;
- chg->qc3p5_detected_mw = 0;
- smblib_update_usb_type(chg);
- }
- void smblib_config_charger_on_debug_battery(struct smb_charger *chg)
- {
- int rc = 0, val;
- rc = smblib_get_prop_from_bms(chg, SMB5_QG_DEBUG_BATTERY, &val);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get debug battery prop rc=%d\n", rc);
- return;
- }
- vote(chg->bat_temp_irq_disable_votable, DEBUG_BOARD_VOTER, val, 0);
- if (chg->suspend_input_on_debug_batt) {
- vote(chg->usb_icl_votable, DEBUG_BOARD_VOTER, val, 0);
- vote(chg->dc_suspend_votable, DEBUG_BOARD_VOTER, val, 0);
- if (val)
- pr_info("Input suspended: Fake battery\n");
- } else {
- vote(chg->chg_disable_votable, DEBUG_BOARD_VOTER,
- val, 0);
- }
- }
- int smblib_rerun_apsd_if_required(struct smb_charger *chg)
- {
- union power_supply_propval val;
- int rc;
- rc = smblib_get_prop_usb_present(chg, &val);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get usb present rc = %d\n", rc);
- return rc;
- }
- if (!val.intval)
- return 0;
- rc = smblib_request_dpdm(chg, true);
- if (rc < 0)
- smblib_err(chg, "Couldn't to enable DPDM rc=%d\n", rc);
- chg->uusb_apsd_rerun_done = true;
- smblib_rerun_apsd(chg);
- return 0;
- }
- static int smblib_get_pulse_cnt(struct smb_charger *chg, int *count)
- {
- *count = chg->pulse_cnt;
- return 0;
- }
- #define USBIN_25MA 25000
- #define USBIN_100MA 100000
- #define USBIN_150MA 150000
- #define USBIN_500MA 500000
- #define USBIN_900MA 900000
- #define USBIN_1000MA 1000000
- static int set_sdp_current(struct smb_charger *chg, int icl_ua)
- {
- int rc;
- u8 icl_options;
- const struct apsd_result *apsd_result = smblib_get_apsd_result(chg);
- /* power source is SDP */
- switch (icl_ua) {
- case USBIN_100MA:
- /* USB 2.0 100mA */
- icl_options = 0;
- break;
- case USBIN_150MA:
- /* USB 3.0 150mA */
- icl_options = CFG_USB3P0_SEL_BIT;
- break;
- case USBIN_500MA:
- /* USB 2.0 500mA */
- icl_options = USB51_MODE_BIT;
- break;
- case USBIN_900MA:
- /* USB 3.0 900mA */
- icl_options = CFG_USB3P0_SEL_BIT | USB51_MODE_BIT;
- break;
- default:
- return -EINVAL;
- }
- if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB &&
- apsd_result->val == QTI_POWER_SUPPLY_TYPE_USB_FLOAT) {
- /*
- * change the float charger configuration to SDP, if this
- * is the case of SDP being detected as FLOAT
- */
- rc = smblib_masked_write(chg, USBIN_OPTIONS_2_CFG_REG,
- FORCE_FLOAT_SDP_CFG_BIT, FORCE_FLOAT_SDP_CFG_BIT);
- if (rc < 0) {
- smblib_err(chg, "Couldn't set float ICL options rc=%d\n",
- rc);
- return rc;
- }
- }
- rc = smblib_masked_write(chg, USBIN_ICL_OPTIONS_REG,
- CFG_USB3P0_SEL_BIT | USB51_MODE_BIT, icl_options);
- if (rc < 0) {
- smblib_err(chg, "Couldn't set ICL options rc=%d\n", rc);
- return rc;
- }
- rc = smblib_icl_override(chg, SW_OVERRIDE_USB51_MODE);
- if (rc < 0) {
- smblib_err(chg, "Couldn't set ICL override rc=%d\n", rc);
- return rc;
- }
- return rc;
- }
- int smblib_set_icl_current(struct smb_charger *chg, int icl_ua)
- {
- int rc = 0;
- enum icl_override_mode icl_override = HW_AUTO_MODE;
- /* suspend if 25mA or less is requested */
- bool suspend = (icl_ua <= USBIN_25MA);
- if (chg->chg_param.smb_version == PMI632)
- schgm_flash_torch_priority(chg, suspend ? TORCH_BOOST_MODE :
- TORCH_BUCK_MODE);
- /* Do not configure ICL from SW for DAM cables */
- if (smblib_get_prop_typec_mode(chg) ==
- QTI_POWER_SUPPLY_TYPEC_SINK_DEBUG_ACCESSORY)
- return 0;
- if (suspend)
- return smblib_set_usb_suspend(chg, true);
- if (icl_ua == INT_MAX)
- goto set_mode;
- /* configure current */
- if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB
- && (chg->typec_legacy
- || chg->typec_mode == QTI_POWER_SUPPLY_TYPEC_SOURCE_DEFAULT
- || chg->connector_type ==
- QTI_POWER_SUPPLY_CONNECTOR_MICRO_USB)) {
- rc = set_sdp_current(chg, icl_ua);
- if (rc < 0) {
- smblib_err(chg, "Couldn't set SDP ICL rc=%d\n", rc);
- goto out;
- }
- } else {
- /*
- * Try USB 2.0/3,0 option first on USB path when maximum input
- * current limit is 500mA or below for better accuracy; in case
- * of error, proceed to use USB high-current mode.
- */
- if (icl_ua <= USBIN_500MA) {
- rc = set_sdp_current(chg, icl_ua);
- if (rc >= 0)
- goto unsuspend;
- }
- rc = smblib_set_charge_param(chg, &chg->param.usb_icl, icl_ua);
- if (rc < 0) {
- smblib_err(chg, "Couldn't set HC ICL rc=%d\n", rc);
- goto out;
- }
- icl_override = SW_OVERRIDE_HC_MODE;
- }
- set_mode:
- rc = smblib_icl_override(chg, icl_override);
- if (rc < 0) {
- smblib_err(chg, "Couldn't set ICL override rc=%d\n", rc);
- goto out;
- }
- unsuspend:
- /* unsuspend after configuring current and override */
- rc = smblib_set_usb_suspend(chg, false);
- if (rc < 0) {
- smblib_err(chg, "Couldn't resume input rc=%d\n", rc);
- goto out;
- }
- /* Re-run AICL */
- if (icl_override != SW_OVERRIDE_HC_MODE)
- rc = smblib_run_aicl(chg, RERUN_AICL);
- out:
- return rc;
- }
- int smblib_get_icl_current(struct smb_charger *chg, int *icl_ua)
- {
- int rc;
- rc = smblib_get_charge_param(chg, &chg->param.icl_max_stat, icl_ua);
- if (rc < 0)
- smblib_err(chg, "Couldn't get HC ICL rc=%d\n", rc);
- return rc;
- }
- int smblib_toggle_smb_en(struct smb_charger *chg, int toggle)
- {
- int rc = 0;
- if (!toggle)
- return rc;
- rc = smblib_select_sec_charger(chg, chg->sec_chg_selected,
- chg->cp_reason, true);
- return rc;
- }
- int smblib_get_irq_status(struct smb_charger *chg, int *val)
- {
- int rc;
- u8 reg;
- if (chg->wa_flags & SKIP_MISC_PBS_IRQ_WA) {
- *val = 0;
- return 0;
- }
- mutex_lock(&chg->irq_status_lock);
- /* Report and clear cached status */
- *val = chg->irq_status;
- chg->irq_status = 0;
- /* get real time status of pulse skip irq */
- rc = smblib_read(chg, MISC_PBS_RT_STS_REG, ®);
- if (rc < 0)
- smblib_err(chg, "Couldn't read MISC_PBS_RT_STS_REG rc=%d\n",
- rc);
- else
- *val |= (reg & PULSE_SKIP_IRQ_BIT);
- mutex_unlock(&chg->irq_status_lock);
- return rc;
- }
- /****************************
- * uUSB Moisture Protection *
- ****************************/
- #define MICRO_USB_DETECTION_ON_TIME_20_MS 0x08
- #define MICRO_USB_DETECTION_PERIOD_X_100 0x03
- #define U_USB_STATUS_WATER_PRESENT 0x00
- static int smblib_set_moisture_protection(struct smb_charger *chg,
- bool enable)
- {
- int rc = 0;
- if (chg->moisture_present == enable) {
- smblib_dbg(chg, PR_MISC, "No change in moisture protection status\n");
- return rc;
- }
- if (enable) {
- chg->moisture_present = true;
- /* Disable uUSB factory mode detection */
- rc = smblib_masked_write(chg, TYPEC_U_USB_CFG_REG,
- EN_MICRO_USB_FACTORY_MODE_BIT, 0);
- if (rc < 0) {
- smblib_err(chg, "Couldn't disable uUSB factory mode detection rc=%d\n",
- rc);
- return rc;
- }
- /* Disable moisture detection and uUSB state change interrupt */
- rc = smblib_masked_write(chg, TYPE_C_INTERRUPT_EN_CFG_2_REG,
- TYPEC_WATER_DETECTION_INT_EN_BIT |
- MICRO_USB_STATE_CHANGE_INT_EN_BIT, 0);
- if (rc < 0) {
- smblib_err(chg, "Couldn't disable moisture detection interrupt rc=%d\n",
- rc);
- return rc;
- }
- /* Set 1% duty cycle on ID detection */
- rc = smblib_masked_write(chg,
- ((chg->chg_param.smb_version == PMI632)
- ? PMI632_TYPEC_U_USB_WATER_PROTECTION_CFG_REG :
- TYPEC_U_USB_WATER_PROTECTION_CFG_REG),
- EN_MICRO_USB_WATER_PROTECTION_BIT |
- MICRO_USB_DETECTION_ON_TIME_CFG_MASK |
- MICRO_USB_DETECTION_PERIOD_CFG_MASK,
- EN_MICRO_USB_WATER_PROTECTION_BIT |
- MICRO_USB_DETECTION_ON_TIME_20_MS |
- MICRO_USB_DETECTION_PERIOD_X_100);
- if (rc < 0) {
- smblib_err(chg, "Couldn't set 1 percent CC_ID duty cycle rc=%d\n",
- rc);
- return rc;
- }
- vote(chg->usb_icl_votable, MOISTURE_VOTER, true, 0);
- } else {
- chg->moisture_present = false;
- vote(chg->usb_icl_votable, MOISTURE_VOTER, false, 0);
- /* Enable moisture detection and uUSB state change interrupt */
- rc = smblib_masked_write(chg, TYPE_C_INTERRUPT_EN_CFG_2_REG,
- TYPEC_WATER_DETECTION_INT_EN_BIT |
- MICRO_USB_STATE_CHANGE_INT_EN_BIT,
- TYPEC_WATER_DETECTION_INT_EN_BIT |
- MICRO_USB_STATE_CHANGE_INT_EN_BIT);
- if (rc < 0) {
- smblib_err(chg, "Couldn't enable moisture detection and uUSB state change interrupt rc=%d\n",
- rc);
- return rc;
- }
- /* Disable periodic monitoring of CC_ID pin */
- rc = smblib_write(chg,
- ((chg->chg_param.smb_version == PMI632)
- ? PMI632_TYPEC_U_USB_WATER_PROTECTION_CFG_REG :
- TYPEC_U_USB_WATER_PROTECTION_CFG_REG), 0);
- if (rc < 0) {
- smblib_err(chg, "Couldn't disable 1 percent CC_ID duty cycle rc=%d\n",
- rc);
- return rc;
- }
- /* Enable uUSB factory mode detection */
- rc = smblib_masked_write(chg, TYPEC_U_USB_CFG_REG,
- EN_MICRO_USB_FACTORY_MODE_BIT,
- EN_MICRO_USB_FACTORY_MODE_BIT);
- if (rc < 0) {
- smblib_err(chg, "Couldn't disable uUSB factory mode detection rc=%d\n",
- rc);
- return rc;
- }
- }
- smblib_dbg(chg, PR_MISC, "Moisture protection %s\n",
- chg->moisture_present ? "enabled" : "disabled");
- return rc;
- }
- /*********************
- * VOTABLE CALLBACKS *
- *********************/
- static int smblib_smb_disable_override_vote_callback(struct votable *votable,
- void *data, int disable_smb, const char *client)
- {
- struct smb_charger *chg = data;
- int rc = 0;
- /* Enable/disable SMB_EN pin */
- rc = smblib_masked_write(chg, MISC_SMB_EN_CMD_REG,
- SMB_EN_OVERRIDE_BIT | SMB_EN_OVERRIDE_VALUE_BIT,
- disable_smb ? SMB_EN_OVERRIDE_BIT : 0);
- if (rc < 0)
- smblib_err(chg, "Couldn't configure SMB_EN, rc=%d\n", rc);
- return rc;
- }
- static int smblib_dc_suspend_vote_callback(struct votable *votable, void *data,
- int suspend, const char *client)
- {
- struct smb_charger *chg = data;
- if (chg->chg_param.smb_version == PMI632)
- return 0;
- /* resume input if suspend is invalid */
- if (suspend < 0)
- suspend = 0;
- return smblib_set_dc_suspend(chg, (bool)suspend);
- }
- static int smblib_awake_vote_callback(struct votable *votable, void *data,
- int awake, const char *client)
- {
- struct smb_charger *chg = data;
- if (awake)
- pm_stay_awake(chg->dev);
- else
- pm_relax(chg->dev);
- return 0;
- }
- static int smblib_chg_disable_vote_callback(struct votable *votable, void *data,
- int chg_disable, const char *client)
- {
- struct smb_charger *chg = data;
- int rc;
- rc = smblib_masked_write(chg, CHARGING_ENABLE_CMD_REG,
- CHARGING_ENABLE_CMD_BIT,
- chg_disable ? 0 : CHARGING_ENABLE_CMD_BIT);
- if (rc < 0) {
- smblib_err(chg, "Couldn't %s charging rc=%d\n",
- chg_disable ? "disable" : "enable", rc);
- return rc;
- }
- return 0;
- }
- static int smblib_hdc_irq_disable_vote_callback(struct votable *votable,
- void *data, int disable, const char *client)
- {
- struct smb_charger *chg = data;
- if (!chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq)
- return 0;
- if (chg->irq_info[HIGH_DUTY_CYCLE_IRQ].enabled) {
- if (disable)
- disable_irq_nosync(
- chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq);
- } else {
- if (!disable)
- enable_irq(chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq);
- }
- chg->irq_info[HIGH_DUTY_CYCLE_IRQ].enabled = !disable;
- return 0;
- }
- static int smblib_limited_irq_disable_vote_callback(struct votable *votable,
- void *data, int disable, const char *client)
- {
- struct smb_charger *chg = data;
- if (!chg->irq_info[INPUT_CURRENT_LIMITING_IRQ].irq)
- return 0;
- if (chg->irq_info[INPUT_CURRENT_LIMITING_IRQ].enabled) {
- if (disable)
- disable_irq_nosync(
- chg->irq_info[INPUT_CURRENT_LIMITING_IRQ].irq);
- } else {
- if (!disable)
- enable_irq(
- chg->irq_info[INPUT_CURRENT_LIMITING_IRQ].irq);
- }
- chg->irq_info[INPUT_CURRENT_LIMITING_IRQ].enabled = !disable;
- return 0;
- }
- static int smblib_icl_irq_disable_vote_callback(struct votable *votable,
- void *data, int disable, const char *client)
- {
- struct smb_charger *chg = data;
- if (!chg->irq_info[USBIN_ICL_CHANGE_IRQ].irq)
- return 0;
- if (chg->irq_info[USBIN_ICL_CHANGE_IRQ].enabled) {
- if (disable)
- disable_irq_nosync(
- chg->irq_info[USBIN_ICL_CHANGE_IRQ].irq);
- } else {
- if (!disable)
- enable_irq(chg->irq_info[USBIN_ICL_CHANGE_IRQ].irq);
- }
- chg->irq_info[USBIN_ICL_CHANGE_IRQ].enabled = !disable;
- return 0;
- }
- static int smblib_temp_change_irq_disable_vote_callback(struct votable *votable,
- void *data, int disable, const char *client)
- {
- struct smb_charger *chg = data;
- if (!chg->irq_info[TEMP_CHANGE_IRQ].irq)
- return 0;
- if (chg->irq_info[TEMP_CHANGE_IRQ].enabled && disable) {
- if (chg->irq_info[TEMP_CHANGE_IRQ].wake)
- disable_irq_wake(chg->irq_info[TEMP_CHANGE_IRQ].irq);
- disable_irq_nosync(chg->irq_info[TEMP_CHANGE_IRQ].irq);
- } else if (!chg->irq_info[TEMP_CHANGE_IRQ].enabled && !disable) {
- enable_irq(chg->irq_info[TEMP_CHANGE_IRQ].irq);
- if (chg->irq_info[TEMP_CHANGE_IRQ].wake)
- enable_irq_wake(chg->irq_info[TEMP_CHANGE_IRQ].irq);
- }
- chg->irq_info[TEMP_CHANGE_IRQ].enabled = !disable;
- return 0;
- }
- static int smblib_bat_temp_irq_disable_vote_callback(struct votable *votable,
- void *data, int disable, const char *client)
- {
- struct smb_charger *chg = data;
- if (!chg->irq_info[BAT_TEMP_IRQ].irq)
- return 0;
- if (chg->irq_info[BAT_TEMP_IRQ].enabled && disable) {
- disable_irq_wake(chg->irq_info[BAT_TEMP_IRQ].irq);
- disable_irq_nosync(chg->irq_info[BAT_TEMP_IRQ].irq);
- } else if (!chg->irq_info[BAT_TEMP_IRQ].enabled && !disable) {
- enable_irq(chg->irq_info[BAT_TEMP_IRQ].irq);
- enable_irq_wake(chg->irq_info[BAT_TEMP_IRQ].irq);
- }
- chg->irq_info[BAT_TEMP_IRQ].enabled = !disable;
- return 0;
- }
- /*******************
- * VCONN REGULATOR *
- * *****************/
- int smblib_vconn_regulator_enable(struct regulator_dev *rdev)
- {
- struct smb_charger *chg = rdev_get_drvdata(rdev);
- int rc = 0;
- u8 stat, orientation;
- smblib_dbg(chg, PR_OTG, "enabling VCONN\n");
- rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
- return rc;
- }
- /* VCONN orientation is opposite to that of CC */
- orientation =
- stat & TYPEC_CCOUT_VALUE_BIT ? 0 : VCONN_EN_ORIENTATION_BIT;
- rc = smblib_masked_write(chg, TYPE_C_VCONN_CONTROL_REG,
- VCONN_EN_VALUE_BIT | VCONN_EN_ORIENTATION_BIT,
- VCONN_EN_VALUE_BIT | orientation);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_CCOUT_CONTROL_REG rc=%d\n",
- rc);
- return rc;
- }
- return 0;
- }
- int smblib_vconn_regulator_disable(struct regulator_dev *rdev)
- {
- struct smb_charger *chg = rdev_get_drvdata(rdev);
- int rc = 0;
- smblib_dbg(chg, PR_OTG, "disabling VCONN\n");
- rc = smblib_masked_write(chg, TYPE_C_VCONN_CONTROL_REG,
- VCONN_EN_VALUE_BIT, 0);
- if (rc < 0)
- smblib_err(chg, "Couldn't disable vconn regulator rc=%d\n", rc);
- return 0;
- }
- int smblib_vconn_regulator_is_enabled(struct regulator_dev *rdev)
- {
- struct smb_charger *chg = rdev_get_drvdata(rdev);
- int rc;
- u8 cmd;
- rc = smblib_read(chg, TYPE_C_VCONN_CONTROL_REG, &cmd);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_INTRPT_ENB_SOFTWARE_CTRL rc=%d\n",
- rc);
- return rc;
- }
- return (cmd & VCONN_EN_VALUE_BIT) ? 1 : 0;
- }
- /*****************
- * OTG REGULATOR *
- *****************/
- int smblib_vbus_regulator_enable(struct regulator_dev *rdev)
- {
- struct smb_charger *chg = rdev_get_drvdata(rdev);
- int rc;
- smblib_dbg(chg, PR_OTG, "enabling OTG\n");
- rc = smblib_masked_write(chg, DCDC_CMD_OTG_REG, OTG_EN_BIT, OTG_EN_BIT);
- if (rc < 0) {
- smblib_err(chg, "Couldn't enable OTG rc=%d\n", rc);
- return rc;
- }
- return 0;
- }
- int smblib_vbus_regulator_disable(struct regulator_dev *rdev)
- {
- struct smb_charger *chg = rdev_get_drvdata(rdev);
- int rc;
- smblib_dbg(chg, PR_OTG, "disabling OTG\n");
- rc = smblib_masked_write(chg, DCDC_CMD_OTG_REG, OTG_EN_BIT, 0);
- if (rc < 0) {
- smblib_err(chg, "Couldn't disable OTG regulator rc=%d\n", rc);
- return rc;
- }
- return 0;
- }
- int smblib_vbus_regulator_is_enabled(struct regulator_dev *rdev)
- {
- struct smb_charger *chg = rdev_get_drvdata(rdev);
- int rc = 0;
- u8 cmd;
- rc = smblib_read(chg, DCDC_CMD_OTG_REG, &cmd);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read CMD_OTG rc=%d", rc);
- return rc;
- }
- return (cmd & OTG_EN_BIT) ? 1 : 0;
- }
- /********************
- * BATT PSY GETTERS *
- ********************/
- int smblib_get_prop_input_suspend(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- val->intval = (get_client_vote(chg->usb_icl_votable, USER_VOTER) == 0)
- && get_client_vote(chg->dc_suspend_votable, USER_VOTER);
- return 0;
- }
- int smblib_get_prop_batt_present(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- int rc;
- u8 stat;
- rc = smblib_read(chg, BATIF_BASE + INT_RT_STS_OFFSET, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read BATIF_INT_RT_STS rc=%d\n", rc);
- return rc;
- }
- val->intval = !(stat & (BAT_THERM_OR_ID_MISSING_RT_STS_BIT
- | BAT_TERMINAL_MISSING_RT_STS_BIT));
- return rc;
- }
- int smblib_get_prop_batt_capacity(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- int rc = -EINVAL;
- if (chg->fake_capacity >= 0) {
- val->intval = chg->fake_capacity;
- return 0;
- }
- rc = smblib_get_prop_from_bms(chg, SMB5_QG_CAPACITY, &val->intval);
- if (rc < 0)
- smblib_err(chg, "Couldn't get capacity prop rc=%d\n", rc);
- return rc;
- }
- static bool is_charging_paused(struct smb_charger *chg)
- {
- int rc;
- u8 val;
- rc = smblib_read(chg, CHARGING_PAUSE_CMD_REG, &val);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read CHARGING_PAUSE_CMD rc=%d\n", rc);
- return false;
- }
- return val & CHARGING_PAUSE_CMD_BIT;
- }
- #define CUTOFF_COUNT 3
- int smblib_get_prop_batt_status(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- union power_supply_propval pval = {0, };
- bool usb_online, dc_online;
- u8 stat;
- int rc, suspend = 0, input_present = 0;
- if (chg->fake_chg_status_on_debug_batt) {
- rc = smblib_get_prop_from_bms(chg, SMB5_QG_DEBUG_BATTERY,
- &pval.intval);
- if (rc < 0) {
- pr_err_ratelimited("Couldn't get debug battery prop rc=%d\n",
- rc);
- } else if (pval.intval == 1) {
- val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
- return 0;
- }
- }
- rc = smblib_get_prop_batt_health(chg, &pval);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get batt health rc=%d\n", rc);
- return rc;
- }
- /*
- * The charger status register shows charging even though the battery
- * is discharging when the over voltage condition is hit. Report power
- * supply state as NOT_CHARGING when the battery health reports
- * over voltage.
- */
- if (pval.intval == POWER_SUPPLY_HEALTH_OVERVOLTAGE) {
- val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
- return 0;
- }
- /*
- * If SOC = 0 and we are discharging with input connected, report
- * the battery status as DISCHARGING.
- */
- smblib_is_input_present(chg, &input_present);
- rc = smblib_get_prop_from_bms(chg, SMB5_QG_CAPACITY, &pval.intval);
- if (!rc && pval.intval == 0 && input_present) {
- rc = smblib_get_prop_from_bms(chg, SMB5_QG_CURRENT_NOW,
- &pval.intval);
- if (!rc && pval.intval > 0) {
- if (chg->cutoff_count > CUTOFF_COUNT) {
- val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
- return 0;
- }
- chg->cutoff_count++;
- } else {
- chg->cutoff_count = 0;
- }
- } else {
- chg->cutoff_count = 0;
- }
- if (chg->dbc_usbov) {
- rc = smblib_get_prop_usb_present(chg, &pval);
- if (rc < 0) {
- smblib_err(chg,
- "Couldn't get usb present prop rc=%d\n", rc);
- return rc;
- }
- rc = smblib_get_usb_suspend(chg, &suspend);
- if (rc < 0) {
- smblib_err(chg,
- "Couldn't get usb suspend rc=%d\n", rc);
- return rc;
- }
- /*
- * Report charging as long as USBOV is not debounced and
- * charging path is un-suspended.
- */
- if (pval.intval && !suspend) {
- val->intval = POWER_SUPPLY_STATUS_CHARGING;
- return 0;
- }
- }
- rc = smblib_get_prop_usb_online(chg, &pval);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get usb online property rc=%d\n",
- rc);
- return rc;
- }
- usb_online = (bool)pval.intval;
- rc = smblib_get_prop_dc_online(chg, &pval);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get dc online property rc=%d\n",
- rc);
- return rc;
- }
- dc_online = (bool)pval.intval;
- rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n",
- rc);
- return rc;
- }
- stat = stat & BATTERY_CHARGER_STATUS_MASK;
- if (!usb_online && !dc_online) {
- switch (stat) {
- case TERMINATE_CHARGE:
- fallthrough;
- case INHIBIT_CHARGE:
- val->intval = POWER_SUPPLY_STATUS_FULL;
- break;
- default:
- val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
- break;
- }
- return rc;
- }
- switch (stat) {
- case TRICKLE_CHARGE:
- fallthrough;
- case PRE_CHARGE:
- fallthrough;
- case FULLON_CHARGE:
- fallthrough;
- case TAPER_CHARGE:
- val->intval = POWER_SUPPLY_STATUS_CHARGING;
- break;
- case TERMINATE_CHARGE:
- fallthrough;
- case INHIBIT_CHARGE:
- val->intval = POWER_SUPPLY_STATUS_FULL;
- break;
- case DISABLE_CHARGE:
- fallthrough;
- case PAUSE_CHARGE:
- val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
- break;
- default:
- val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
- break;
- }
- if (is_charging_paused(chg)) {
- val->intval = POWER_SUPPLY_STATUS_CHARGING;
- return 0;
- }
- /*
- * If charge termination WA is active and has suspended charging, then
- * continue reporting charging status as FULL.
- */
- if (is_client_vote_enabled_locked(chg->usb_icl_votable,
- CHG_TERMINATION_VOTER)) {
- val->intval = POWER_SUPPLY_STATUS_FULL;
- return 0;
- }
- if (val->intval != POWER_SUPPLY_STATUS_CHARGING)
- return 0;
- if (!usb_online && dc_online
- && chg->fake_batt_status == POWER_SUPPLY_STATUS_FULL) {
- val->intval = POWER_SUPPLY_STATUS_FULL;
- return 0;
- }
- rc = smblib_read(chg, BATTERY_CHARGER_STATUS_5_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_2 rc=%d\n",
- rc);
- return rc;
- }
- stat &= ENABLE_TRICKLE_BIT | ENABLE_PRE_CHARGING_BIT |
- ENABLE_FULLON_MODE_BIT;
- if (!stat)
- val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
- return 0;
- }
- int smblib_get_prop_batt_charge_type(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- int rc;
- u8 stat;
- rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n",
- rc);
- return rc;
- }
- switch (stat & BATTERY_CHARGER_STATUS_MASK) {
- case TRICKLE_CHARGE:
- fallthrough;
- case PRE_CHARGE:
- val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
- break;
- case FULLON_CHARGE:
- val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
- break;
- case TAPER_CHARGE:
- val->intval = POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE;
- break;
- default:
- val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
- }
- return rc;
- }
- int smblib_get_prop_batt_health(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- union power_supply_propval pval;
- int rc;
- int effective_fv_uv;
- u8 stat;
- rc = smblib_read(chg, BATTERY_CHARGER_STATUS_2_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_2 rc=%d\n",
- rc);
- return rc;
- }
- smblib_dbg(chg, PR_REGISTER, "BATTERY_CHARGER_STATUS_2 = 0x%02x\n",
- stat);
- if (stat & CHARGER_ERROR_STATUS_BAT_OV_BIT) {
- rc = smblib_get_prop_from_bms(chg, SMB5_QG_VOLTAGE_NOW,
- &pval.intval);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get voltage_now prop rc=%d\n",
- rc);
- } else {
- /*
- * If Vbatt is within 40mV above Vfloat, then don't
- * treat it as overvoltage.
- */
- effective_fv_uv = get_effective_result_locked(
- chg->fv_votable);
- if (pval.intval >= effective_fv_uv + 40000) {
- val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
- smblib_err(chg, "battery over-voltage vbat_fg = %duV, fv = %duV\n",
- pval.intval, effective_fv_uv);
- goto done;
- }
- }
- }
- rc = smblib_read(chg, BATTERY_CHARGER_STATUS_7_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_2 rc=%d\n",
- rc);
- return rc;
- }
- if (stat & BAT_TEMP_STATUS_TOO_COLD_BIT)
- val->intval = POWER_SUPPLY_HEALTH_COLD;
- else if (stat & BAT_TEMP_STATUS_TOO_HOT_BIT)
- val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
- else if (stat & BAT_TEMP_STATUS_COLD_SOFT_BIT)
- val->intval = POWER_SUPPLY_HEALTH_COOL;
- else if (stat & BAT_TEMP_STATUS_HOT_SOFT_BIT)
- val->intval = POWER_SUPPLY_HEALTH_WARM;
- else
- val->intval = POWER_SUPPLY_HEALTH_GOOD;
- done:
- return rc;
- }
- int smblib_get_prop_system_temp_level(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- val->intval = chg->system_temp_level;
- return 0;
- }
- int smblib_get_prop_system_temp_level_max(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- val->intval = chg->thermal_levels;
- return 0;
- }
- int smblib_get_prop_input_current_limited(struct smb_charger *chg,
- int *val)
- {
- u8 stat;
- int rc;
- if (chg->fake_input_current_limited >= 0) {
- *val = chg->fake_input_current_limited;
- return 0;
- }
- rc = smblib_read(chg, AICL_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read AICL_STATUS rc=%d\n", rc);
- return rc;
- }
- *val = (stat & SOFT_ILIMIT_BIT) || chg->is_hdc;
- return 0;
- }
- int smblib_get_prop_batt_iterm(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- int rc, temp;
- u8 stat, buf[2];
- /*
- * Currently, only ADC comparator-based termination is supported,
- * hence read only the threshold corresponding to ADC source.
- * Proceed only if CHGR_ITERM_USE_ANALOG_BIT is 0.
- */
- rc = smblib_read(chg, CHGR_ENG_CHARGING_CFG_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read CHGR_ENG_CHARGING_CFG_REG rc=%d\n",
- rc);
- return rc;
- }
- if (stat & CHGR_ITERM_USE_ANALOG_BIT) {
- val->intval = -EINVAL;
- return 0;
- }
- rc = smblib_batch_read(chg, CHGR_ADC_ITERM_UP_THD_MSB_REG, buf, 2);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read CHGR_ADC_ITERM_UP_THD_MSB_REG rc=%d\n",
- rc);
- return rc;
- }
- temp = buf[1] | (buf[0] << 8);
- temp = sign_extend32(temp, 15);
- if (chg->chg_param.smb_version == PMI632)
- temp = DIV_ROUND_CLOSEST(temp * ITERM_LIMITS_PMI632_MA,
- ADC_CHG_ITERM_MASK);
- else
- temp = DIV_ROUND_CLOSEST(temp * ITERM_LIMITS_PM8150B_MA,
- ADC_CHG_ITERM_MASK);
- val->intval = temp;
- return rc;
- }
- int smblib_get_prop_batt_charge_done(struct smb_charger *chg,
- int *val)
- {
- int rc;
- u8 stat;
- rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n",
- rc);
- return rc;
- }
- stat = stat & BATTERY_CHARGER_STATUS_MASK;
- *val = (stat == TERMINATE_CHARGE);
- return 0;
- }
- int smblib_get_batt_current_now(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- int rc;
- rc = smblib_get_prop_from_bms(chg, SMB5_QG_CURRENT_NOW, &val->intval);
- if (!rc)
- val->intval *= (-1);
- else
- smblib_err(chg, "Couldn't get current_now prop rc=%d\n", rc);
- return rc;
- }
- /***********************
- * BATTERY PSY SETTERS *
- ***********************/
- int smblib_set_prop_input_suspend(struct smb_charger *chg,
- const union power_supply_propval *val)
- {
- int rc;
- /* vote 0mA when suspended */
- rc = vote(chg->usb_icl_votable, USER_VOTER, (bool)val->intval, 0);
- if (rc < 0) {
- smblib_err(chg, "Couldn't vote to %s USB rc=%d\n",
- (bool)val->intval ? "suspend" : "resume", rc);
- return rc;
- }
- rc = vote(chg->dc_suspend_votable, USER_VOTER, (bool)val->intval, 0);
- if (rc < 0) {
- smblib_err(chg, "Couldn't vote to %s DC rc=%d\n",
- (bool)val->intval ? "suspend" : "resume", rc);
- return rc;
- }
- power_supply_changed(chg->batt_psy);
- return rc;
- }
- int smblib_set_prop_batt_capacity(struct smb_charger *chg,
- const union power_supply_propval *val)
- {
- chg->fake_capacity = val->intval;
- power_supply_changed(chg->batt_psy);
- return 0;
- }
- int smblib_set_prop_batt_status(struct smb_charger *chg,
- const union power_supply_propval *val)
- {
- /* Faking battery full */
- if (val->intval == POWER_SUPPLY_STATUS_FULL)
- chg->fake_batt_status = val->intval;
- else
- chg->fake_batt_status = -EINVAL;
- power_supply_changed(chg->batt_psy);
- return 0;
- }
- int smblib_set_prop_system_temp_level(struct smb_charger *chg,
- const union power_supply_propval *val)
- {
- if (val->intval < 0)
- return -EINVAL;
- if (chg->thermal_levels <= 0)
- return -EINVAL;
- if (val->intval > chg->thermal_levels)
- return -EINVAL;
- chg->system_temp_level = val->intval;
- if (chg->system_temp_level == chg->thermal_levels)
- return vote(chg->chg_disable_votable,
- THERMAL_DAEMON_VOTER, true, 0);
- vote(chg->chg_disable_votable, THERMAL_DAEMON_VOTER, false, 0);
- if (chg->system_temp_level == 0)
- return vote(chg->fcc_votable, THERMAL_DAEMON_VOTER, false, 0);
- vote(chg->fcc_votable, THERMAL_DAEMON_VOTER, true,
- chg->thermal_mitigation[chg->system_temp_level]);
- return 0;
- }
- int smblib_set_prop_input_current_limited(struct smb_charger *chg,
- int val)
- {
- chg->fake_input_current_limited = val;
- return 0;
- }
- int smblib_set_prop_rechg_soc_thresh(struct smb_charger *chg,
- int val)
- {
- int rc;
- u8 new_thr = DIV_ROUND_CLOSEST(val * 255, 100);
- rc = smblib_write(chg, CHARGE_RCHG_SOC_THRESHOLD_CFG_REG,
- new_thr);
- if (rc < 0) {
- smblib_err(chg, "Couldn't write to RCHG_SOC_THRESHOLD_CFG_REG rc=%d\n",
- rc);
- return rc;
- }
- chg->auto_recharge_soc = val;
- return rc;
- }
- int smblib_run_aicl(struct smb_charger *chg, int type)
- {
- int rc;
- u8 stat;
- rc = smblib_read(chg, POWER_PATH_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read POWER_PATH_STATUS rc=%d\n",
- rc);
- return rc;
- }
- /* USB is suspended so skip re-running AICL */
- if (stat & USBIN_SUSPEND_STS_BIT)
- return rc;
- smblib_dbg(chg, PR_MISC, "re-running AICL\n");
- stat = (type == RERUN_AICL) ? RERUN_AICL_BIT : RESTART_AICL_BIT;
- rc = smblib_masked_write(chg, AICL_CMD_REG, stat, stat);
- if (rc < 0)
- smblib_err(chg, "Couldn't write to AICL_CMD_REG rc=%d\n",
- rc);
- return 0;
- }
- static int smblib_dp_pulse(struct smb_charger *chg)
- {
- int rc;
- /* QC 3.0 increment */
- rc = smblib_masked_write(chg, CMD_HVDCP_2_REG, SINGLE_INCREMENT_BIT,
- SINGLE_INCREMENT_BIT);
- if (rc < 0)
- smblib_err(chg, "Couldn't write to CMD_HVDCP_2_REG rc=%d\n",
- rc);
- return rc;
- }
- static int smblib_dm_pulse(struct smb_charger *chg)
- {
- int rc;
- /* QC 3.0 decrement */
- rc = smblib_masked_write(chg, CMD_HVDCP_2_REG, SINGLE_DECREMENT_BIT,
- SINGLE_DECREMENT_BIT);
- if (rc < 0)
- smblib_err(chg, "Couldn't write to CMD_HVDCP_2_REG rc=%d\n",
- rc);
- return rc;
- }
- int smblib_force_vbus_voltage(struct smb_charger *chg, u8 val)
- {
- int rc;
- rc = smblib_masked_write(chg, CMD_HVDCP_2_REG, val, val);
- if (rc < 0)
- smblib_err(chg, "Couldn't write to CMD_HVDCP_2_REG rc=%d\n",
- rc);
- return rc;
- }
- static void smblib_hvdcp_set_fsw(struct smb_charger *chg, int bit)
- {
- switch (bit) {
- case QC_5V_BIT:
- smblib_set_opt_switcher_freq(chg,
- chg->chg_freq.freq_5V);
- break;
- case QC_9V_BIT:
- smblib_set_opt_switcher_freq(chg,
- chg->chg_freq.freq_9V);
- break;
- case QC_12V_BIT:
- smblib_set_opt_switcher_freq(chg,
- chg->chg_freq.freq_12V);
- break;
- default:
- smblib_set_opt_switcher_freq(chg,
- chg->chg_freq.freq_removal);
- break;
- }
- }
- #define QC3_PULSES_FOR_6V 5
- #define QC3_PULSES_FOR_9V 20
- #define QC3_PULSES_FOR_12V 35
- static int smblib_hvdcp3_set_fsw(struct smb_charger *chg)
- {
- int pulse_count, rc;
- rc = smblib_get_pulse_cnt(chg, &pulse_count);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read QC_PULSE_COUNT rc=%d\n", rc);
- return rc;
- }
- if (pulse_count < QC3_PULSES_FOR_6V)
- smblib_set_opt_switcher_freq(chg,
- chg->chg_freq.freq_5V);
- else if (pulse_count < QC3_PULSES_FOR_9V)
- smblib_set_opt_switcher_freq(chg,
- chg->chg_freq.freq_6V_8V);
- else if (pulse_count < QC3_PULSES_FOR_12V)
- smblib_set_opt_switcher_freq(chg,
- chg->chg_freq.freq_9V);
- else
- smblib_set_opt_switcher_freq(chg,
- chg->chg_freq.freq_12V);
- return 0;
- }
- static void smblib_hvdcp_adaptive_voltage_change(struct smb_charger *chg)
- {
- int rc;
- u8 stat;
- if (chg->real_charger_type == QTI_POWER_SUPPLY_TYPE_USB_HVDCP) {
- rc = smblib_read(chg, QC_CHANGE_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg,
- "Couldn't read QC_CHANGE_STATUS rc=%d\n", rc);
- return;
- }
- smblib_hvdcp_set_fsw(chg, stat & QC_2P0_STATUS_MASK);
- vote(chg->usb_icl_votable, HVDCP2_ICL_VOTER, false, 0);
- }
- if (chg->real_charger_type == QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3
- || chg->real_charger_type ==
- QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3P5) {
- rc = smblib_hvdcp3_set_fsw(chg);
- if (rc < 0)
- smblib_err(chg, "Couldn't set QC3.0 Fsw rc=%d\n", rc);
- }
- power_supply_changed(chg->batt_psy);
- }
- int smblib_dp_dm(struct smb_charger *chg, int val)
- {
- int target_icl_ua, data, rc = 0;
- u8 stat;
- switch (val) {
- case QTI_POWER_SUPPLY_DP_DM_DP_PULSE:
- /*
- * Pre-emptively increment pulse count to enable the setting
- * of FSW prior to increasing voltage.
- */
- chg->pulse_cnt++;
- rc = smblib_hvdcp3_set_fsw(chg);
- if (rc < 0)
- smblib_err(chg, "Couldn't set QC3.0 Fsw rc=%d\n", rc);
- rc = smblib_dp_pulse(chg);
- if (rc < 0) {
- smblib_err(chg, "Couldn't increase pulse count rc=%d\n",
- rc);
- /*
- * Increment pulse count failed;
- * reset to former value.
- */
- chg->pulse_cnt--;
- }
- smblib_dbg(chg, PR_PARALLEL, "DP_DM_DP_PULSE rc=%d cnt=%d\n",
- rc, chg->pulse_cnt);
- break;
- case QTI_POWER_SUPPLY_DP_DM_DM_PULSE:
- rc = smblib_dm_pulse(chg);
- if (!rc && chg->pulse_cnt)
- chg->pulse_cnt--;
- smblib_dbg(chg, PR_PARALLEL, "DP_DM_DM_PULSE rc=%d cnt=%d\n",
- rc, chg->pulse_cnt);
- break;
- case QTI_POWER_SUPPLY_DP_DM_ICL_DOWN:
- target_icl_ua = get_effective_result(chg->usb_icl_votable);
- if (target_icl_ua < 0) {
- /* no client vote, get the ICL from charger */
- rc = smblib_get_charge_current(chg, &data);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get max curr rc=%d\n",
- rc);
- return rc;
- }
- target_icl_ua = data;
- }
- /*
- * Check if any other voter voted on USB_ICL in case of
- * voter other than SW_QC3_VOTER reset and restart reduction
- * again.
- */
- if (target_icl_ua != get_client_vote(chg->usb_icl_votable,
- SW_QC3_VOTER))
- chg->usb_icl_delta_ua = 0;
- chg->usb_icl_delta_ua += 100000;
- vote(chg->usb_icl_votable, SW_QC3_VOTER, true,
- target_icl_ua - 100000);
- smblib_dbg(chg, PR_PARALLEL, "ICL DOWN ICL=%d reduction=%d\n",
- target_icl_ua, chg->usb_icl_delta_ua);
- break;
- case QTI_POWER_SUPPLY_DP_DM_FORCE_5V:
- rc = smblib_force_vbus_voltage(chg, FORCE_5V_BIT);
- if (rc < 0)
- pr_err("Failed to force 5V\n");
- break;
- case QTI_POWER_SUPPLY_DP_DM_FORCE_9V:
- if (chg->qc2_unsupported_voltage == QC2_NON_COMPLIANT_9V) {
- smblib_err(chg, "Couldn't set 9V: unsupported\n");
- return -EINVAL;
- }
- /* If we are increasing voltage to get to 9V, set FSW first */
- rc = smblib_read(chg, QC_CHANGE_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read QC_CHANGE_STATUS_REG rc=%d\n",
- rc);
- break;
- }
- if (stat & QC_5V_BIT) {
- /* Force 1A ICL before requesting higher voltage */
- vote(chg->usb_icl_votable, HVDCP2_ICL_VOTER,
- true, 1000000);
- smblib_hvdcp_set_fsw(chg, QC_9V_BIT);
- }
- rc = smblib_force_vbus_voltage(chg, FORCE_9V_BIT);
- if (rc < 0)
- pr_err("Failed to force 9V\n");
- break;
- case QTI_POWER_SUPPLY_DP_DM_FORCE_12V:
- if (chg->qc2_unsupported_voltage == QC2_NON_COMPLIANT_12V) {
- smblib_err(chg, "Couldn't set 12V: unsupported\n");
- return -EINVAL;
- }
- /* If we are increasing voltage to get to 12V, set FSW first */
- rc = smblib_read(chg, QC_CHANGE_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read QC_CHANGE_STATUS_REG rc=%d\n",
- rc);
- break;
- }
- if ((stat & QC_9V_BIT) || (stat & QC_5V_BIT)) {
- /* Force 1A ICL before requesting higher voltage */
- vote(chg->usb_icl_votable, HVDCP2_ICL_VOTER,
- true, 1000000);
- smblib_hvdcp_set_fsw(chg, QC_12V_BIT);
- }
- rc = smblib_force_vbus_voltage(chg, FORCE_12V_BIT);
- if (rc < 0)
- pr_err("Failed to force 12V\n");
- break;
- case QTI_POWER_SUPPLY_DP_DM_CONFIRMED_HVDCP3P5:
- chg->qc3p5_detected = true;
- smblib_update_usb_type(chg);
- break;
- case QTI_POWER_SUPPLY_DP_DM_ICL_UP:
- default:
- break;
- }
- return rc;
- }
- int smblib_disable_hw_jeita(struct smb_charger *chg, bool disable)
- {
- int rc;
- u8 mask;
- /*
- * Disable h/w base JEITA compensation if s/w JEITA is enabled
- */
- mask = JEITA_EN_COLD_SL_FCV_BIT
- | JEITA_EN_HOT_SL_FCV_BIT
- | JEITA_EN_HOT_SL_CCC_BIT
- | JEITA_EN_COLD_SL_CCC_BIT,
- rc = smblib_masked_write(chg, JEITA_EN_CFG_REG, mask,
- disable ? 0 : mask);
- if (rc < 0) {
- dev_err(chg->dev, "Couldn't configure s/w jeita rc=%d\n",
- rc);
- return rc;
- }
- return 0;
- }
- static int smblib_set_sw_thermal_regulation(struct smb_charger *chg,
- bool enable)
- {
- int rc = 0;
- if (!(chg->wa_flags & SW_THERM_REGULATION_WA))
- return rc;
- if (enable) {
- /*
- * Configure min time to quickly address thermal
- * condition.
- */
- rc = smblib_masked_write(chg, SNARL_BARK_BITE_WD_CFG_REG,
- SNARL_WDOG_TIMEOUT_MASK, SNARL_WDOG_TMOUT_62P5MS);
- if (rc < 0) {
- smblib_err(chg, "Couldn't configure snarl wdog tmout, rc=%d\n",
- rc);
- return rc;
- }
- /*
- * Schedule SW_THERM_REGULATION_WORK directly if USB input
- * is suspended due to SW thermal regulation WA since WDOG
- * IRQ won't trigger with input suspended.
- */
- if (is_client_vote_enabled(chg->usb_icl_votable,
- SW_THERM_REGULATION_VOTER)) {
- vote(chg->awake_votable, SW_THERM_REGULATION_VOTER,
- true, 0);
- schedule_delayed_work(&chg->thermal_regulation_work, 0);
- }
- } else {
- cancel_delayed_work_sync(&chg->thermal_regulation_work);
- vote(chg->awake_votable, SW_THERM_REGULATION_VOTER, false, 0);
- }
- smblib_dbg(chg, PR_MISC, "WDOG SNARL INT %s\n",
- enable ? "Enabled" : "Disabled");
- return rc;
- }
- static int smblib_update_thermal_readings(struct smb_charger *chg)
- {
- int rc = 0, data = 0;
- struct iio_channel **iio_list;
- if (!chg->iio_chan_list_smb_parallel) {
- iio_list = get_ext_channels(chg->dev,
- smblib_parallel_ext_iio_chan,
- ARRAY_SIZE(smblib_parallel_ext_iio_chan));
- if (IS_ERR(iio_list)) {
- rc = PTR_ERR(iio_list);
- if (rc != -EPROBE_DEFER) {
- dev_err(chg->dev, "Failed to get channels, %d\n",
- rc);
- chg->iio_chan_list_smb_parallel =
- ERR_PTR(-EINVAL);
- }
- } else {
- chg->iio_chan_list_smb_parallel = iio_list;
- }
- }
- rc = smblib_read_iio_channel(chg, chg->iio.die_temp_chan,
- DIV_FACTOR_DECIDEGC, &chg->die_temp);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read DIE TEMP channel, rc=%d\n", rc);
- return rc;
- }
- rc = smblib_read_iio_channel(chg, chg->iio.connector_temp_chan,
- DIV_FACTOR_DECIDEGC, &chg->connector_temp);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read CONN TEMP channel, rc=%d\n", rc);
- return rc;
- }
- rc = smblib_read_iio_channel(chg, chg->iio.skin_temp_chan,
- DIV_FACTOR_DECIDEGC, &chg->skin_temp);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read SKIN TEMP channel, rc=%d\n", rc);
- return rc;
- }
- if (chg->sec_chg_selected == QTI_POWER_SUPPLY_CHARGER_SEC_CP) {
- if (is_cp_available(chg)) {
- rc = smblib_read_iio_prop(chg, CP, CP_DIE_TEMP, &data);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get smb1390 charger temp, rc=%d\n",
- rc);
- return rc;
- }
- chg->smb_temp = data;
- } else {
- smblib_dbg(chg, PR_MISC, "Coudln't find cp_psy\n");
- chg->smb_temp = -ENODATA;
- }
- } else if (!IS_ERR_OR_NULL(chg->iio_chan_list_smb_parallel) &&
- chg->sec_chg_selected == QTI_POWER_SUPPLY_CHARGER_SEC_PL) {
- rc = smblib_read_iio_prop(chg, SMB_PARALLEL, SMB_CHARGER_TEMP,
- &data);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get smb1355 charger temp, rc=%d\n",
- rc);
- return rc;
- }
- chg->smb_temp = data;
- } else {
- chg->smb_temp = -ENODATA;
- }
- return rc;
- }
- /* SW thermal regulation thresholds in deciDegC */
- #define DIE_TEMP_RST_THRESH 1000
- #define DIE_TEMP_REG_H_THRESH 800
- #define DIE_TEMP_REG_L_THRESH 600
- #define CONNECTOR_TEMP_SHDN_THRESH 700
- #define CONNECTOR_TEMP_RST_THRESH 600
- #define CONNECTOR_TEMP_REG_H_THRESH 550
- #define CONNECTOR_TEMP_REG_L_THRESH 500
- #define SMB_TEMP_SHDN_THRESH 1400
- #define SMB_TEMP_RST_THRESH 900
- #define SMB_TEMP_REG_H_THRESH 800
- #define SMB_TEMP_REG_L_THRESH 600
- #define SKIN_TEMP_SHDN_THRESH 700
- #define SKIN_TEMP_RST_THRESH 600
- #define SKIN_TEMP_REG_H_THRESH 550
- #define SKIN_TEMP_REG_L_THRESH 500
- #define THERM_REG_RECHECK_DELAY_1S 1000 /* 1 sec */
- #define THERM_REG_RECHECK_DELAY_8S 8000 /* 8 sec */
- static int smblib_process_thermal_readings(struct smb_charger *chg)
- {
- int rc = 0, wdog_timeout = SNARL_WDOG_TMOUT_8S;
- u32 thermal_status = TEMP_BELOW_RANGE;
- bool suspend_input = false, disable_smb = false;
- /*
- * Following is the SW thermal regulation flow:
- *
- * TEMP_SHUT_DOWN_LEVEL: If either connector temp or skin temp
- * exceeds their respective SHDN threshold. Need to suspend input
- * and secondary charger.
- *
- * TEMP_SHUT_DOWN_SMB_LEVEL: If smb temp exceed its SHDN threshold
- * but connector and skin temp are below it. Need to suspend SMB.
- *
- * TEMP_ALERT_LEVEL: If die, connector, smb or skin temp exceeds it's
- * respective RST threshold. Stay put and monitor temperature closely.
- *
- * TEMP_ABOVE_RANGE or TEMP_WITHIN_RANGE or TEMP_BELOW_RANGE: If die,
- * connector, smb or skin temp exceeds it's respective REG_H or REG_L
- * threshold. Unsuspend input and SMB.
- */
- if (chg->connector_temp > CONNECTOR_TEMP_SHDN_THRESH ||
- chg->skin_temp > SKIN_TEMP_SHDN_THRESH) {
- thermal_status = TEMP_SHUT_DOWN;
- wdog_timeout = SNARL_WDOG_TMOUT_1S;
- suspend_input = true;
- disable_smb = true;
- goto out;
- }
- if (chg->smb_temp > SMB_TEMP_SHDN_THRESH) {
- thermal_status = TEMP_SHUT_DOWN_SMB;
- wdog_timeout = SNARL_WDOG_TMOUT_1S;
- disable_smb = true;
- goto out;
- }
- if (chg->connector_temp > CONNECTOR_TEMP_RST_THRESH ||
- chg->skin_temp > SKIN_TEMP_RST_THRESH ||
- chg->smb_temp > SMB_TEMP_RST_THRESH ||
- chg->die_temp > DIE_TEMP_RST_THRESH) {
- thermal_status = TEMP_ALERT_LEVEL;
- wdog_timeout = SNARL_WDOG_TMOUT_1S;
- goto out;
- }
- if (chg->connector_temp > CONNECTOR_TEMP_REG_H_THRESH ||
- chg->skin_temp > SKIN_TEMP_REG_H_THRESH ||
- chg->smb_temp > SMB_TEMP_REG_H_THRESH ||
- chg->die_temp > DIE_TEMP_REG_H_THRESH) {
- thermal_status = TEMP_ABOVE_RANGE;
- wdog_timeout = SNARL_WDOG_TMOUT_1S;
- goto out;
- }
- if (chg->connector_temp > CONNECTOR_TEMP_REG_L_THRESH ||
- chg->skin_temp > SKIN_TEMP_REG_L_THRESH ||
- chg->smb_temp > SMB_TEMP_REG_L_THRESH ||
- chg->die_temp > DIE_TEMP_REG_L_THRESH) {
- thermal_status = TEMP_WITHIN_RANGE;
- wdog_timeout = SNARL_WDOG_TMOUT_8S;
- }
- out:
- smblib_dbg(chg, PR_MISC, "Current temperatures: \tDIE_TEMP: %d,\tCONN_TEMP: %d,\tSMB_TEMP: %d,\tSKIN_TEMP: %d\nTHERMAL_STATUS: %d\n",
- chg->die_temp, chg->connector_temp, chg->smb_temp,
- chg->skin_temp, thermal_status);
- if (thermal_status != chg->thermal_status) {
- chg->thermal_status = thermal_status;
- /*
- * If thermal level changes to TEMP ALERT LEVEL, don't
- * enable/disable main/parallel charging.
- */
- if (chg->thermal_status == TEMP_ALERT_LEVEL)
- goto exit;
- vote(chg->smb_override_votable, SW_THERM_REGULATION_VOTER,
- disable_smb, 0);
- /*
- * Enable/disable secondary charger through votables to ensure
- * that if SMB_EN pin get's toggled somehow, secondary charger
- * remains enabled/disabled according to SW thermal regulation.
- */
- if (!chg->cp_disable_votable)
- chg->cp_disable_votable = find_votable("CP_DISABLE");
- if (chg->cp_disable_votable)
- vote(chg->cp_disable_votable, SW_THERM_REGULATION_VOTER,
- disable_smb, 0);
- vote(chg->pl_disable_votable, SW_THERM_REGULATION_VOTER,
- disable_smb, 0);
- smblib_dbg(chg, PR_MISC, "Parallel %s as per SW thermal regulation\n",
- disable_smb ? "disabled" : "enabled");
- /*
- * If thermal level changes to TEMP_SHUT_DOWN_SMB, don't
- * enable/disable main charger.
- */
- if (chg->thermal_status == TEMP_SHUT_DOWN_SMB)
- goto exit;
- /* Suspend input if SHDN threshold reached */
- vote(chg->dc_suspend_votable, SW_THERM_REGULATION_VOTER,
- suspend_input, 0);
- vote(chg->usb_icl_votable, SW_THERM_REGULATION_VOTER,
- suspend_input, 0);
- smblib_dbg(chg, PR_MISC, "USB/DC %s as per SW thermal regulation\n",
- suspend_input ? "suspended" : "unsuspended");
- }
- exit:
- /*
- * On USB suspend, WDOG IRQ stops triggering. To continue thermal
- * monitoring and regulation until USB is plugged out, reschedule
- * the SW thermal regulation work without releasing the wake lock.
- */
- if (is_client_vote_enabled(chg->usb_icl_votable,
- SW_THERM_REGULATION_VOTER)) {
- schedule_delayed_work(&chg->thermal_regulation_work,
- msecs_to_jiffies(THERM_REG_RECHECK_DELAY_1S));
- return 0;
- }
- rc = smblib_masked_write(chg, SNARL_BARK_BITE_WD_CFG_REG,
- SNARL_WDOG_TIMEOUT_MASK, wdog_timeout);
- if (rc < 0)
- smblib_err(chg, "Couldn't set WD SNARL timer, rc=%d\n", rc);
- vote(chg->awake_votable, SW_THERM_REGULATION_VOTER, false, 0);
- return rc;
- }
- /*******************
- * DC PSY GETTERS *
- *******************/
- int smblib_get_prop_voltage_wls_output(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- int rc = 0;
- if (!chg->wls_psy) {
- chg->wls_psy = power_supply_get_by_name("wireless");
- if (!chg->wls_psy) {
- val->intval = -EINVAL;
- return 0;
- }
- }
- rc = power_supply_get_property(chg->wls_psy,
- POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT,
- val);
- return rc;
- }
- int smblib_get_prop_dc_present(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- int rc;
- u8 stat;
- if (chg->chg_param.smb_version == PMI632) {
- val->intval = 0;
- return 0;
- }
- rc = smblib_read(chg, DCIN_BASE + INT_RT_STS_OFFSET, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read DCIN_RT_STS rc=%d\n", rc);
- return rc;
- }
- val->intval = (bool)(stat & DCIN_PLUGIN_RT_STS_BIT);
- return 0;
- }
- int smblib_get_prop_dc_online(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- int rc = 0;
- u8 stat;
- if (chg->chg_param.smb_version == PMI632) {
- val->intval = 0;
- return 0;
- }
- if (get_client_vote(chg->dc_suspend_votable, USER_VOTER)) {
- val->intval = false;
- return rc;
- }
- if (is_client_vote_enabled(chg->dc_suspend_votable,
- CHG_TERMINATION_VOTER)) {
- rc = smblib_get_prop_dc_present(chg, val);
- return rc;
- }
- rc = smblib_read(chg, POWER_PATH_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read POWER_PATH_STATUS rc=%d\n",
- rc);
- return rc;
- }
- smblib_dbg(chg, PR_REGISTER, "POWER_PATH_STATUS = 0x%02x\n",
- stat);
- val->intval = (stat & USE_DCIN_BIT) &&
- (stat & VALID_INPUT_POWER_SOURCE_STS_BIT);
- return rc;
- }
- int smblib_get_prop_dc_current_max(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- return smblib_get_charge_param(chg, &chg->param.dc_icl, &val->intval);
- }
- int smblib_get_prop_dc_voltage_max(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- int rc;
- val->intval = MICRO_12V;
- if (!chg->wls_psy)
- chg->wls_psy = power_supply_get_by_name("wireless");
- if (chg->wls_psy) {
- rc = power_supply_get_property(chg->wls_psy,
- POWER_SUPPLY_PROP_VOLTAGE_MAX,
- val);
- if (rc < 0) {
- dev_err(chg->dev, "Couldn't get VOLTAGE_MAX, rc=%d\n",
- rc);
- return rc;
- }
- }
- return 0;
- }
- int smblib_get_prop_dc_voltage_now(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- int rc = 0;
- if (!chg->wls_psy) {
- chg->wls_psy = power_supply_get_by_name("wireless");
- if (!chg->wls_psy) {
- val->intval = -EINVAL;
- return 0;
- }
- }
- rc = power_supply_get_property(chg->wls_psy,
- POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT,
- val);
- if (rc < 0) {
- dev_err(chg->dev, "Couldn't get POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT, rc=%d\n",
- rc);
- return rc;
- }
- return rc;
- }
- /*******************
- * DC PSY SETTERS *
- *******************/
- int smblib_set_prop_dc_current_max(struct smb_charger *chg,
- const union power_supply_propval *val)
- {
- chg->dcin_icl_user_set = true;
- return smblib_set_charge_param(chg, &chg->param.dc_icl, val->intval);
- }
- #define DCIN_AICL_RERUN_DELAY_MS 5000
- int smblib_set_prop_voltage_wls_output(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- int rc = 0;
- if (!chg->wls_psy) {
- chg->wls_psy = power_supply_get_by_name("wireless");
- if (!chg->wls_psy)
- return -ENODEV;
- }
- rc = power_supply_set_property(chg->wls_psy,
- POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT,
- val);
- if (rc < 0)
- dev_err(chg->dev, "Couldn't set POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT, rc=%d\n",
- rc);
- smblib_dbg(chg, PR_WLS, "%d\n", val->intval);
- /*
- * When WLS VOUT goes down, the power-constrained adaptor may be able
- * to supply more current, so allow it to do so - unless userspace has
- * changed DCIN ICL value already due to thermal considerations.
- */
- if (!chg->dcin_icl_user_set && (val->intval > 0) &&
- (val->intval < chg->last_wls_vout)) {
- alarm_start_relative(&chg->dcin_aicl_alarm,
- ms_to_ktime(DCIN_AICL_RERUN_DELAY_MS));
- }
- chg->last_wls_vout = val->intval;
- return rc;
- }
- int smblib_set_prop_dc_reset(struct smb_charger *chg)
- {
- int rc;
- rc = vote(chg->dc_suspend_votable, VOUT_VOTER, true, 0);
- if (rc < 0) {
- smblib_err(chg, "Couldn't suspend DC rc=%d\n", rc);
- return rc;
- }
- rc = smblib_masked_write(chg, DCIN_CMD_IL_REG, DCIN_EN_MASK,
- DCIN_EN_OVERRIDE_BIT);
- if (rc < 0) {
- smblib_err(chg, "Couldn't set DCIN_EN_OVERRIDE_BIT rc=%d\n",
- rc);
- return rc;
- }
- rc = smblib_write(chg, DCIN_CMD_PON_REG, DCIN_PON_BIT | MID_CHG_BIT);
- if (rc < 0) {
- smblib_err(chg, "Couldn't write %d to DCIN_CMD_PON_REG rc=%d\n",
- DCIN_PON_BIT | MID_CHG_BIT, rc);
- return rc;
- }
- /* Wait for 10ms to allow the charge to get drained */
- usleep_range(10000, 10010);
- rc = smblib_write(chg, DCIN_CMD_PON_REG, 0);
- if (rc < 0) {
- smblib_err(chg, "Couldn't clear DCIN_CMD_PON_REG rc=%d\n", rc);
- return rc;
- }
- rc = smblib_masked_write(chg, DCIN_CMD_IL_REG, DCIN_EN_MASK, 0);
- if (rc < 0) {
- smblib_err(chg, "Couldn't clear DCIN_EN_OVERRIDE_BIT rc=%d\n",
- rc);
- return rc;
- }
- rc = vote(chg->dc_suspend_votable, VOUT_VOTER, false, 0);
- if (rc < 0) {
- smblib_err(chg, "Couldn't unsuspend DC rc=%d\n", rc);
- return rc;
- }
- smblib_dbg(chg, PR_MISC, "Wireless charger removal detection successful\n");
- return rc;
- }
- /*******************
- * USB PSY GETTERS *
- *******************/
- int smblib_get_prop_usb_present(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- int rc;
- u8 stat;
- rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read USBIN_RT_STS rc=%d\n", rc);
- return rc;
- }
- val->intval = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT);
- return 0;
- }
- int smblib_get_prop_usb_online(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- int rc = 0;
- u8 stat;
- if (get_client_vote_locked(chg->usb_icl_votable, USER_VOTER) == 0) {
- val->intval = false;
- return rc;
- }
- if (is_client_vote_enabled_locked(chg->usb_icl_votable,
- CHG_TERMINATION_VOTER)) {
- rc = smblib_get_prop_usb_present(chg, val);
- return rc;
- }
- rc = smblib_read(chg, POWER_PATH_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read POWER_PATH_STATUS rc=%d\n",
- rc);
- return rc;
- }
- smblib_dbg(chg, PR_REGISTER, "POWER_PATH_STATUS = 0x%02x\n",
- stat);
- val->intval = (stat & USE_USBIN_BIT) &&
- (stat & VALID_INPUT_POWER_SOURCE_STS_BIT);
- return rc;
- }
- int smblib_get_usb_online(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- int rc;
- rc = smblib_get_prop_usb_online(chg, val);
- if (!val->intval)
- goto exit;
- if (((chg->typec_mode == QTI_POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) ||
- (chg->connector_type == QTI_POWER_SUPPLY_CONNECTOR_MICRO_USB))
- && (chg->real_charger_type == POWER_SUPPLY_TYPE_USB))
- val->intval = 0;
- else
- val->intval = 1;
- if (chg->real_charger_type == POWER_SUPPLY_TYPE_UNKNOWN)
- val->intval = 0;
- exit:
- return rc;
- }
- int smblib_get_prop_usb_voltage_max_design(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- switch (chg->real_charger_type) {
- case QTI_POWER_SUPPLY_TYPE_USB_HVDCP:
- if (chg->qc2_unsupported_voltage == QC2_NON_COMPLIANT_9V) {
- val->intval = MICRO_5V;
- break;
- } else if (chg->qc2_unsupported_voltage ==
- QC2_NON_COMPLIANT_12V) {
- val->intval = MICRO_9V;
- break;
- }
- fallthrough;
- case QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3P5:
- case QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3:
- case POWER_SUPPLY_TYPE_USB_PD:
- if (chg->chg_param.smb_version == PMI632)
- val->intval = MICRO_9V;
- else
- val->intval = MICRO_12V;
- break;
- default:
- val->intval = MICRO_5V;
- break;
- }
- return 0;
- }
- int smblib_get_prop_usb_voltage_max(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- switch (chg->real_charger_type) {
- case QTI_POWER_SUPPLY_TYPE_USB_HVDCP:
- if (chg->qc2_unsupported_voltage == QC2_NON_COMPLIANT_9V) {
- val->intval = MICRO_5V;
- break;
- } else if (chg->qc2_unsupported_voltage ==
- QC2_NON_COMPLIANT_12V) {
- val->intval = MICRO_9V;
- break;
- }
- fallthrough;
- case QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3P5:
- case QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3:
- if (chg->chg_param.smb_version == PMI632)
- val->intval = MICRO_9V;
- else
- val->intval = MICRO_12V;
- break;
- case POWER_SUPPLY_TYPE_USB_PD:
- val->intval = chg->voltage_max_uv;
- break;
- default:
- val->intval = MICRO_5V;
- break;
- }
- return 0;
- }
- #define HVDCP3_STEP_UV 200000
- #define HVDCP3P5_STEP_UV 20000
- static int smblib_estimate_adaptor_voltage(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- int step_uv = HVDCP3_STEP_UV;
- switch (chg->real_charger_type) {
- case QTI_POWER_SUPPLY_TYPE_USB_HVDCP:
- val->intval = MICRO_12V;
- break;
- case QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3P5:
- step_uv = HVDCP3P5_STEP_UV;
- fallthrough;
- case QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3:
- val->intval = MICRO_5V + (step_uv * chg->pulse_cnt);
- break;
- case POWER_SUPPLY_TYPE_USB_PD:
- /* Take the average of min and max values */
- val->intval = chg->voltage_min_uv +
- ((chg->voltage_max_uv - chg->voltage_min_uv) / 2);
- break;
- default:
- val->intval = MICRO_5V;
- break;
- }
- return 0;
- }
- static int smblib_read_mid_voltage_chan(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- int rc;
- if (!chg->iio.mid_chan)
- return -ENODATA;
- rc = iio_read_channel_processed(chg->iio.mid_chan, &val->intval);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read MID channel rc=%d\n", rc);
- return rc;
- }
- /*
- * If MID voltage < 1V, it is unreliable.
- * Figure out voltage from registers and calculations.
- */
- if (val->intval < 1000000)
- return smblib_estimate_adaptor_voltage(chg, val);
- return 0;
- }
- static int smblib_read_usbin_voltage_chan(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- int rc;
- if (!chg->iio.usbin_v_chan)
- return -ENODATA;
- rc = iio_read_channel_processed(chg->iio.usbin_v_chan, &val->intval);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read USBIN channel rc=%d\n", rc);
- return rc;
- }
- return 0;
- }
- int smblib_get_prop_usb_voltage_now(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- union power_supply_propval pval = {0, };
- int rc, ret = 0;
- u8 reg, adc_ch_reg;
- mutex_lock(&chg->adc_lock);
- if (chg->wa_flags & USBIN_ADC_WA) {
- /* Store ADC channel config in order to restore later */
- rc = smblib_read(chg, BATIF_ADC_CHANNEL_EN_REG, &adc_ch_reg);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read ADC config rc=%d\n", rc);
- ret = rc;
- goto unlock;
- }
- /* Disable all ADC channels except IBAT channel */
- rc = smblib_write(chg, BATIF_ADC_CHANNEL_EN_REG,
- IBATT_CHANNEL_EN_BIT);
- if (rc < 0) {
- smblib_err(chg, "Couldn't disable ADC channels rc=%d\n",
- rc);
- ret = rc;
- goto unlock;
- }
- }
- rc = smblib_get_prop_usb_present(chg, &pval);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get usb presence status rc=%d\n", rc);
- ret = -ENODATA;
- goto restore_adc_config;
- }
- /*
- * Skip reading voltage only if USB is not present and we are not in
- * OTG mode.
- */
- if (!pval.intval) {
- rc = smblib_read(chg, DCDC_CMD_OTG_REG, ®);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read CMD_OTG rc=%d", rc);
- goto restore_adc_config;
- }
- if (!(reg & OTG_EN_BIT))
- goto restore_adc_config;
- }
- /*
- * For PM8150B, use MID_CHG ADC channel because overvoltage is observed
- * to occur randomly in the USBIN channel, particularly at high
- * voltages.
- */
- if (chg->chg_param.smb_version == PM8150B)
- rc = smblib_read_mid_voltage_chan(chg, val);
- else
- rc = smblib_read_usbin_voltage_chan(chg, val);
- if (rc < 0) {
- smblib_err(chg, "Failed to read USBIN over vadc, rc=%d\n", rc);
- ret = rc;
- }
- restore_adc_config:
- /* Restore ADC channel config */
- if (chg->wa_flags & USBIN_ADC_WA) {
- rc = smblib_write(chg, BATIF_ADC_CHANNEL_EN_REG, adc_ch_reg);
- if (rc < 0)
- smblib_err(chg, "Couldn't write ADC config rc=%d\n",
- rc);
- }
- unlock:
- mutex_unlock(&chg->adc_lock);
- return ret;
- }
- int smblib_get_prop_vph_voltage_now(struct smb_charger *chg,
- int *val)
- {
- int rc;
- if (!chg->iio.vph_v_chan)
- return -ENODATA;
- rc = iio_read_channel_processed(chg->iio.vph_v_chan, val);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read vph channel rc=%d\n", rc);
- return rc;
- }
- return 0;
- }
- static bool smblib_rsbux_low(struct smb_charger *chg, int r_thr)
- {
- int r_sbu1, r_sbu2;
- bool ret = false;
- int rc;
- if (!chg->iio.sbux_chan)
- return false;
- /* disable crude sensors */
- rc = smblib_masked_write(chg, TYPE_C_CRUDE_SENSOR_CFG_REG,
- EN_SRC_CRUDE_SENSOR_BIT | EN_SNK_CRUDE_SENSOR_BIT,
- 0);
- if (rc < 0) {
- smblib_err(chg, "Couldn't disable crude sensor rc=%d\n", rc);
- return false;
- }
- /* select SBU1 as current source */
- rc = smblib_write(chg, TYPE_C_SBU_CFG_REG, SEL_SBU1_ISRC_VAL);
- if (rc < 0) {
- smblib_err(chg, "Couldn't select SBU1 rc=%d\n", rc);
- goto cleanup;
- }
- rc = iio_read_channel_processed(chg->iio.sbux_chan, &r_sbu1);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read SBU1 rc=%d\n", rc);
- goto cleanup;
- }
- if (r_sbu1 < r_thr) {
- ret = true;
- goto cleanup;
- }
- /* select SBU2 as current source */
- rc = smblib_write(chg, TYPE_C_SBU_CFG_REG, SEL_SBU2_ISRC_VAL);
- if (rc < 0) {
- smblib_err(chg, "Couldn't select SBU1 rc=%d\n", rc);
- goto cleanup;
- }
- rc = iio_read_channel_processed(chg->iio.sbux_chan, &r_sbu2);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read SBU1 rc=%d\n", rc);
- goto cleanup;
- }
- if (r_sbu2 < r_thr)
- ret = true;
- cleanup:
- /* enable crude sensors */
- rc = smblib_masked_write(chg, TYPE_C_CRUDE_SENSOR_CFG_REG,
- EN_SRC_CRUDE_SENSOR_BIT | EN_SNK_CRUDE_SENSOR_BIT,
- EN_SRC_CRUDE_SENSOR_BIT | EN_SNK_CRUDE_SENSOR_BIT);
- if (rc < 0)
- smblib_err(chg, "Couldn't enable crude sensor rc=%d\n", rc);
- /* disable current source */
- rc = smblib_write(chg, TYPE_C_SBU_CFG_REG, 0);
- if (rc < 0)
- smblib_err(chg, "Couldn't select SBU1 rc=%d\n", rc);
- return ret;
- }
- int smblib_get_prop_charger_temp(struct smb_charger *chg,
- int *val)
- {
- int temp, rc;
- int input_present;
- rc = smblib_is_input_present(chg, &input_present);
- if (rc < 0)
- return rc;
- if (input_present == INPUT_NOT_PRESENT)
- return -ENODATA;
- if (chg->iio.temp_chan) {
- rc = iio_read_channel_processed(chg->iio.temp_chan,
- &temp);
- if (rc < 0) {
- pr_err("Error in reading temp channel, rc=%d\n", rc);
- return rc;
- }
- *val = temp / 100;
- } else {
- return -ENODATA;
- }
- return rc;
- }
- int smblib_get_prop_typec_cc_orientation(struct smb_charger *chg,
- int *val)
- {
- int rc = 0;
- u8 stat;
- *val = 0;
- if (chg->connector_type == QTI_POWER_SUPPLY_CONNECTOR_MICRO_USB)
- return 0;
- rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
- return rc;
- }
- smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_4 = 0x%02x\n", stat);
- if (stat & CC_ATTACHED_BIT)
- *val = (bool)(stat & CC_ORIENTATION_BIT) + 1;
- return rc;
- }
- static const char * const smblib_typec_mode_name[] = {
- [QTI_POWER_SUPPLY_TYPEC_NONE] = "NONE",
- [QTI_POWER_SUPPLY_TYPEC_SOURCE_DEFAULT] = "SOURCE_DEFAULT",
- [QTI_POWER_SUPPLY_TYPEC_SOURCE_MEDIUM] = "SOURCE_MEDIUM",
- [QTI_POWER_SUPPLY_TYPEC_SOURCE_HIGH] = "SOURCE_HIGH",
- [QTI_POWER_SUPPLY_TYPEC_NON_COMPLIANT] = "NON_COMPLIANT",
- [QTI_POWER_SUPPLY_TYPEC_SINK] = "SINK",
- [QTI_POWER_SUPPLY_TYPEC_SINK_POWERED_CABLE] = "SINK_POWERED_CABLE",
- [QTI_POWER_SUPPLY_TYPEC_SINK_DEBUG_ACCESSORY] = "SINK_DEBUG_ACCESSORY",
- [QTI_POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER] = "SINK_AUDIO_ADAPTER",
- [QTI_POWER_SUPPLY_TYPEC_POWERED_CABLE_ONLY] = "POWERED_CABLE_ONLY",
- };
- static int smblib_get_prop_ufp_mode(struct smb_charger *chg)
- {
- int rc;
- u8 stat;
- rc = smblib_read(chg, TYPE_C_SNK_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_1 rc=%d\n", rc);
- return QTI_POWER_SUPPLY_TYPEC_NONE;
- }
- smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_1 = 0x%02x\n", stat);
- switch (stat & DETECTED_SRC_TYPE_MASK) {
- case SNK_RP_STD_BIT:
- return QTI_POWER_SUPPLY_TYPEC_SOURCE_DEFAULT;
- case SNK_RP_1P5_BIT:
- return QTI_POWER_SUPPLY_TYPEC_SOURCE_MEDIUM;
- case SNK_RP_3P0_BIT:
- return QTI_POWER_SUPPLY_TYPEC_SOURCE_HIGH;
- case SNK_RP_SHORT_BIT:
- return QTI_POWER_SUPPLY_TYPEC_NON_COMPLIANT;
- case SNK_DAM_500MA_BIT:
- case SNK_DAM_1500MA_BIT:
- case SNK_DAM_3000MA_BIT:
- return QTI_POWER_SUPPLY_TYPEC_SINK_DEBUG_ACCESSORY;
- default:
- break;
- }
- return QTI_POWER_SUPPLY_TYPEC_NONE;
- }
- static int smblib_get_prop_dfp_mode(struct smb_charger *chg)
- {
- int rc;
- u8 stat;
- if (chg->lpd_stage == LPD_STAGE_COMMIT)
- return QTI_POWER_SUPPLY_TYPEC_NONE;
- rc = smblib_read(chg, TYPE_C_SRC_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_SRC_STATUS_REG rc=%d\n",
- rc);
- return QTI_POWER_SUPPLY_TYPEC_NONE;
- }
- smblib_dbg(chg, PR_REGISTER, "TYPE_C_SRC_STATUS_REG = 0x%02x\n", stat);
- switch (stat & DETECTED_SNK_TYPE_MASK) {
- case AUDIO_ACCESS_RA_RA_BIT:
- return QTI_POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER;
- case SRC_DEBUG_ACCESS_BIT:
- return QTI_POWER_SUPPLY_TYPEC_SINK_DEBUG_ACCESSORY;
- case SRC_RD_RA_VCONN_BIT:
- return QTI_POWER_SUPPLY_TYPEC_SINK_POWERED_CABLE;
- case SRC_RD_OPEN_BIT:
- return QTI_POWER_SUPPLY_TYPEC_SINK;
- default:
- break;
- }
- return QTI_POWER_SUPPLY_TYPEC_NONE;
- }
- static int smblib_get_prop_typec_mode(struct smb_charger *chg)
- {
- int rc;
- u8 stat;
- rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_MISC_STATUS_REG rc=%d\n",
- rc);
- return 0;
- }
- smblib_dbg(chg, PR_REGISTER, "TYPE_C_MISC_STATUS_REG = 0x%02x\n", stat);
- if (stat & SNK_SRC_MODE_BIT)
- return smblib_get_prop_dfp_mode(chg);
- else
- return smblib_get_prop_ufp_mode(chg);
- }
- inline int smblib_get_usb_prop_typec_mode(struct smb_charger *chg,
- int *val)
- {
- if (chg->connector_type == QTI_POWER_SUPPLY_CONNECTOR_MICRO_USB)
- *val = QTI_POWER_SUPPLY_TYPEC_NONE;
- else {
- chg->typec_mode = smblib_get_prop_typec_mode(chg);
- *val = chg->typec_mode;
- }
- return 0;
- }
- inline int smblib_get_usb_prop_typec_accessory_mode(struct smb_charger *chg,
- int *val)
- {
- if (chg->connector_type == QTI_POWER_SUPPLY_CONNECTOR_MICRO_USB) {
- *val = TYPEC_ACCESSORY_NONE;
- return 0;
- }
- switch (chg->typec_mode) {
- case QTI_POWER_SUPPLY_TYPEC_NONE:
- *val = TYPEC_ACCESSORY_NONE;
- break;
- case QTI_POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER:
- *val = TYPEC_ACCESSORY_AUDIO;
- break;
- case QTI_POWER_SUPPLY_TYPEC_SINK_DEBUG_ACCESSORY:
- *val = TYPEC_ACCESSORY_DEBUG;
- break;
- default:
- *val = -EINVAL;
- }
- return 0;
- }
- int smblib_get_prop_typec_power_role(struct smb_charger *chg, int *val)
- {
- int rc = 0;
- u8 ctrl;
- if (chg->connector_type == QTI_POWER_SUPPLY_CONNECTOR_MICRO_USB) {
- *val = QTI_POWER_SUPPLY_TYPEC_PR_NONE;
- return 0;
- }
- spin_lock(&chg->typec_pr_lock);
- rc = smblib_read(chg, TYPE_C_MODE_CFG_REG, &ctrl);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_MODE_CFG_REG rc=%d\n",
- rc);
- goto unlock;
- }
- smblib_dbg(chg, PR_REGISTER, "TYPE_C_MODE_CFG_REG = 0x%02x\n",
- ctrl);
- if (ctrl & TYPEC_DISABLE_CMD_BIT) {
- *val = QTI_POWER_SUPPLY_TYPEC_PR_NONE;
- goto unlock;
- }
- switch (ctrl & (EN_SRC_ONLY_BIT | EN_SNK_ONLY_BIT)) {
- case 0:
- *val = QTI_POWER_SUPPLY_TYPEC_PR_DUAL;
- break;
- case EN_SRC_ONLY_BIT:
- *val = QTI_POWER_SUPPLY_TYPEC_PR_SOURCE;
- break;
- case EN_SNK_ONLY_BIT:
- *val = QTI_POWER_SUPPLY_TYPEC_PR_SINK;
- break;
- default:
- *val = QTI_POWER_SUPPLY_TYPEC_PR_NONE;
- smblib_err(chg, "unsupported power role 0x%02lx\n",
- ctrl & (EN_SRC_ONLY_BIT | EN_SNK_ONLY_BIT));
- rc = -EINVAL;
- goto unlock;
- }
- chg->power_role = *val;
- unlock:
- spin_unlock(&chg->typec_pr_lock);
- return rc;
- }
- static inline bool typec_in_src_mode(struct smb_charger *chg)
- {
- return (chg->typec_mode > QTI_POWER_SUPPLY_TYPEC_NONE &&
- chg->typec_mode < QTI_POWER_SUPPLY_TYPEC_SOURCE_DEFAULT);
- }
- int smblib_get_prop_typec_select_rp(struct smb_charger *chg,
- int *val)
- {
- int rc, rp;
- u8 stat;
- if (!typec_in_src_mode(chg)) {
- *val = -EINVAL;
- return 0;
- }
- rc = smblib_read(chg, TYPE_C_CURRSRC_CFG_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_CURRSRC_CFG_REG rc=%d\n",
- rc);
- return rc;
- }
- switch (stat & TYPEC_SRC_RP_SEL_MASK) {
- case TYPEC_SRC_RP_STD:
- rp = QTI_POWER_SUPPLY_TYPEC_SRC_RP_STD;
- break;
- case TYPEC_SRC_RP_1P5A:
- rp = QTI_POWER_SUPPLY_TYPEC_SRC_RP_1P5A;
- break;
- case TYPEC_SRC_RP_3A:
- case TYPEC_SRC_RP_3A_DUPLICATE:
- rp = QTI_POWER_SUPPLY_TYPEC_SRC_RP_3A;
- break;
- default:
- return -EINVAL;
- }
- *val = rp;
- return 0;
- }
- int smblib_get_prop_usb_current_now(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- union power_supply_propval pval = {0, };
- int rc = 0, buck_scale = 1, boost_scale = 1;
- if (chg->iio.usbin_i_chan) {
- rc = iio_read_channel_processed(chg->iio.usbin_i_chan,
- &val->intval);
- if (rc < 0) {
- pr_err("Error in reading USBIN_I channel, rc=%d\n", rc);
- return rc;
- }
- /*
- * For PM8150B, scaling factor = reciprocal of
- * 0.2V/A in Buck mode, 0.4V/A in Boost mode.
- * For PMI632, scaling factor = reciprocal of
- * 0.4V/A in Buck mode, 0.8V/A in Boost mode.
- */
- switch (chg->chg_param.smb_version) {
- case PMI632:
- buck_scale = 40;
- boost_scale = 80;
- break;
- default:
- buck_scale = 20;
- boost_scale = 40;
- break;
- }
- if (chg->otg_present || smblib_get_prop_dfp_mode(chg) !=
- QTI_POWER_SUPPLY_TYPEC_NONE) {
- val->intval = DIV_ROUND_CLOSEST(val->intval * 100,
- boost_scale);
- return rc;
- }
- rc = smblib_get_prop_usb_present(chg, &pval);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get usb present status,rc=%d\n",
- rc);
- return -ENODATA;
- }
- /* If USB is not present, return 0 */
- if (!pval.intval)
- val->intval = 0;
- else
- val->intval = DIV_ROUND_CLOSEST(val->intval * 100,
- buck_scale);
- } else {
- val->intval = 0;
- rc = -ENODATA;
- }
- return rc;
- }
- int smblib_get_prop_low_power(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- int rc;
- u8 stat;
- if (chg->sink_src_mode != SRC_MODE)
- return -ENODATA;
- rc = smblib_read(chg, TYPE_C_SRC_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_SRC_STATUS_REG rc=%d\n",
- rc);
- return rc;
- }
- val->intval = !(stat & SRC_HIGH_BATT_BIT);
- return 0;
- }
- int smblib_get_prop_input_current_settled(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- return smblib_get_charge_param(chg, &chg->param.icl_stat, &val->intval);
- }
- int smblib_get_prop_input_current_max(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- int icl_ua = 0, rc;
- rc = smblib_get_charge_param(chg, &chg->param.usb_icl, &icl_ua);
- if (rc < 0)
- return rc;
- if (is_override_vote_enabled_locked(chg->usb_icl_votable) &&
- icl_ua < USBIN_1000MA) {
- val->intval = USBIN_1000MA;
- return 0;
- }
- return smblib_get_charge_param(chg, &chg->param.icl_stat, &val->intval);
- }
- int smblib_get_prop_input_voltage_settled(struct smb_charger *chg,
- int *val)
- {
- int rc, pulses;
- int step_uv = HVDCP3_STEP_UV;
- switch (chg->real_charger_type) {
- case QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3P5:
- step_uv = HVDCP3P5_STEP_UV;
- fallthrough;
- case QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3:
- rc = smblib_get_pulse_cnt(chg, &pulses);
- if (rc < 0) {
- smblib_err(chg,
- "Couldn't read QC_PULSE_COUNT rc=%d\n", rc);
- return 0;
- }
- *val = MICRO_5V + step_uv * pulses;
- break;
- case POWER_SUPPLY_TYPE_USB_PD:
- *val = chg->voltage_min_uv;
- break;
- default:
- *val = MICRO_5V;
- break;
- }
- return 0;
- }
- int smblib_get_prop_pd_in_hard_reset(struct smb_charger *chg,
- int *val)
- {
- *val = chg->pd_hard_reset;
- return 0;
- }
- int smblib_get_pe_start(struct smb_charger *chg,
- int *val)
- {
- *val = chg->ok_to_pd;
- return 0;
- }
- int smblib_get_prop_smb_health(struct smb_charger *chg)
- {
- int rc, val;
- int input_present;
- rc = smblib_is_input_present(chg, &input_present);
- if (rc < 0)
- return rc;
- if ((input_present == INPUT_NOT_PRESENT) || (!is_cp_available(chg)))
- return POWER_SUPPLY_HEALTH_UNKNOWN;
- rc = smblib_read_iio_prop(chg, CP, CP_DIE_TEMP, &val);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get CP DIE_TEMP rc=%d\n", rc);
- return rc;
- }
- if (val > SMB_TEMP_RST_THRESH)
- return POWER_SUPPLY_HEALTH_OVERHEAT;
- if (val > SMB_TEMP_REG_H_THRESH)
- return POWER_SUPPLY_HEALTH_HOT;
- if (val > SMB_TEMP_REG_L_THRESH)
- return POWER_SUPPLY_HEALTH_WARM;
- return POWER_SUPPLY_HEALTH_COOL;
- }
- static int smblib_get_prop_die_health(struct smb_charger *chg)
- {
- int rc;
- u8 stat;
- int input_present;
- rc = smblib_is_input_present(chg, &input_present);
- if (rc < 0)
- return rc;
- if (input_present == INPUT_NOT_PRESENT)
- return POWER_SUPPLY_HEALTH_UNKNOWN;
- if (chg->wa_flags & SW_THERM_REGULATION_WA) {
- if (chg->die_temp == -ENODATA)
- return POWER_SUPPLY_HEALTH_UNKNOWN;
- if (chg->die_temp > DIE_TEMP_RST_THRESH)
- return POWER_SUPPLY_HEALTH_OVERHEAT;
- if (chg->die_temp > DIE_TEMP_REG_H_THRESH)
- return POWER_SUPPLY_HEALTH_HOT;
- if (chg->die_temp > DIE_TEMP_REG_L_THRESH)
- return POWER_SUPPLY_HEALTH_WARM;
- return POWER_SUPPLY_HEALTH_COOL;
- }
- rc = smblib_read(chg, DIE_TEMP_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read DIE_TEMP_STATUS_REG, rc=%d\n",
- rc);
- return POWER_SUPPLY_HEALTH_UNKNOWN;
- }
- if (stat & DIE_TEMP_RST_BIT)
- return POWER_SUPPLY_HEALTH_OVERHEAT;
- if (stat & DIE_TEMP_UB_BIT)
- return POWER_SUPPLY_HEALTH_HOT;
- if (stat & DIE_TEMP_LB_BIT)
- return POWER_SUPPLY_HEALTH_WARM;
- return POWER_SUPPLY_HEALTH_COOL;
- }
- int smblib_get_die_health(struct smb_charger *chg,
- int *val)
- {
- if (chg->die_health == -EINVAL)
- *val = smblib_get_prop_die_health(chg);
- else
- *val = chg->die_health;
- return 0;
- }
- int smblib_get_prop_scope(struct smb_charger *chg,
- union power_supply_propval *val)
- {
- int rc;
- union power_supply_propval pval;
- val->intval = POWER_SUPPLY_SCOPE_UNKNOWN;
- rc = smblib_get_prop_usb_present(chg, &pval);
- if (rc < 0)
- return rc;
- val->intval = pval.intval ? POWER_SUPPLY_SCOPE_DEVICE
- : chg->otg_present ? POWER_SUPPLY_SCOPE_SYSTEM
- : POWER_SUPPLY_SCOPE_UNKNOWN;
- return 0;
- }
- static int smblib_get_typec_connector_temp_status(struct smb_charger *chg)
- {
- int rc;
- u8 stat;
- if (chg->connector_health != -EINVAL)
- return chg->connector_health;
- if (chg->wa_flags & SW_THERM_REGULATION_WA) {
- if (chg->connector_temp == -ENODATA)
- return POWER_SUPPLY_HEALTH_UNKNOWN;
- if (chg->connector_temp > CONNECTOR_TEMP_RST_THRESH)
- return POWER_SUPPLY_HEALTH_OVERHEAT;
- if (chg->connector_temp > CONNECTOR_TEMP_REG_H_THRESH)
- return POWER_SUPPLY_HEALTH_HOT;
- if (chg->connector_temp > CONNECTOR_TEMP_REG_L_THRESH)
- return POWER_SUPPLY_HEALTH_WARM;
- return POWER_SUPPLY_HEALTH_COOL;
- }
- rc = smblib_read(chg, CONNECTOR_TEMP_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read CONNECTOR_TEMP_STATUS_REG, rc=%d\n",
- rc);
- return POWER_SUPPLY_HEALTH_UNKNOWN;
- }
- if (stat & CONNECTOR_TEMP_RST_BIT)
- return POWER_SUPPLY_HEALTH_OVERHEAT;
- if (stat & CONNECTOR_TEMP_UB_BIT)
- return POWER_SUPPLY_HEALTH_HOT;
- if (stat & CONNECTOR_TEMP_LB_BIT)
- return POWER_SUPPLY_HEALTH_WARM;
- return POWER_SUPPLY_HEALTH_COOL;
- }
- int smblib_get_skin_temp_status(struct smb_charger *chg)
- {
- int rc;
- u8 stat;
- if (!chg->en_skin_therm_mitigation)
- return POWER_SUPPLY_HEALTH_UNKNOWN;
- rc = smblib_read(chg, SKIN_TEMP_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read SKIN_TEMP_STATUS_REG, rc=%d\n",
- rc);
- return POWER_SUPPLY_HEALTH_UNKNOWN;
- }
- if (stat & SKIN_TEMP_RST_BIT)
- return POWER_SUPPLY_HEALTH_OVERHEAT;
- if (stat & SKIN_TEMP_UB_BIT)
- return POWER_SUPPLY_HEALTH_HOT;
- if (stat & SKIN_TEMP_LB_BIT)
- return POWER_SUPPLY_HEALTH_WARM;
- return POWER_SUPPLY_HEALTH_COOL;
- }
- int smblib_get_prop_connector_health(struct smb_charger *chg)
- {
- bool dc_present, usb_present;
- int input_present;
- int rc;
- rc = smblib_is_input_present(chg, &input_present);
- if (rc < 0)
- return POWER_SUPPLY_HEALTH_UNKNOWN;
- dc_present = input_present & INPUT_PRESENT_DC;
- usb_present = input_present & INPUT_PRESENT_USB;
- if (usb_present)
- return smblib_get_typec_connector_temp_status(chg);
- /*
- * In PM8150B, SKIN channel measures Wireless charger receiver
- * temp, used to regulate DC ICL.
- */
- if (chg->chg_param.smb_version == PM8150B && dc_present)
- return smblib_get_skin_temp_status(chg);
- return POWER_SUPPLY_HEALTH_COOL;
- }
- static int get_rp_based_dcp_current(struct smb_charger *chg, int typec_mode)
- {
- int rp_ua;
- switch (typec_mode) {
- case QTI_POWER_SUPPLY_TYPEC_SOURCE_HIGH:
- rp_ua = TYPEC_HIGH_CURRENT_UA;
- break;
- case QTI_POWER_SUPPLY_TYPEC_SOURCE_MEDIUM:
- fallthrough;
- case QTI_POWER_SUPPLY_TYPEC_SOURCE_DEFAULT:
- fallthrough;
- default:
- rp_ua = DCP_CURRENT_UA;
- }
- return rp_ua;
- }
- /*******************
- * USB PSY SETTERS *
- * *****************/
- int smblib_set_prop_pd_current_max(struct smb_charger *chg,
- int val)
- {
- int rc, icl;
- if (chg->pd_active) {
- icl = get_client_vote(chg->usb_icl_votable, PD_VOTER);
- rc = vote(chg->usb_icl_votable, PD_VOTER, true, val);
- if (val != icl)
- power_supply_changed(chg->usb_psy);
- } else {
- rc = -EPERM;
- }
- return rc;
- }
- static int smblib_handle_usb_current(struct smb_charger *chg,
- int usb_current)
- {
- int rc = 0, rp_ua, typec_mode;
- union power_supply_propval val = {0, };
- if (chg->real_charger_type == QTI_POWER_SUPPLY_TYPE_USB_FLOAT) {
- if (usb_current == -ETIMEDOUT) {
- if ((chg->float_cfg & FLOAT_OPTIONS_MASK)
- == FORCE_FLOAT_SDP_CFG_BIT) {
- /*
- * Confiugure USB500 mode if Float charger is
- * configured for SDP mode.
- */
- rc = vote(chg->usb_icl_votable,
- SW_ICL_MAX_VOTER, true, USBIN_500MA);
- if (rc < 0)
- smblib_err(chg,
- "Couldn't set SDP ICL rc=%d\n",
- rc);
- return rc;
- }
- if (chg->connector_type ==
- QTI_POWER_SUPPLY_CONNECTOR_TYPEC) {
- /*
- * Valid FLOAT charger, report the current
- * based of Rp.
- */
- typec_mode = smblib_get_prop_typec_mode(chg);
- rp_ua = get_rp_based_dcp_current(chg,
- typec_mode);
- rc = vote(chg->usb_icl_votable,
- SW_ICL_MAX_VOTER, true, rp_ua);
- if (rc < 0)
- return rc;
- } else {
- rc = vote(chg->usb_icl_votable,
- SW_ICL_MAX_VOTER, true, DCP_CURRENT_UA);
- if (rc < 0)
- return rc;
- }
- } else {
- /*
- * FLOAT charger detected as SDP by USB driver,
- * charge with the requested current and update the
- * real_charger_type
- */
- chg->real_charger_type = POWER_SUPPLY_TYPE_USB;
- rc = vote(chg->usb_icl_votable, USB_PSY_VOTER,
- true, usb_current);
- if (rc < 0)
- return rc;
- rc = vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER,
- false, 0);
- if (rc < 0)
- return rc;
- }
- } else {
- rc = smblib_get_prop_usb_present(chg, &val);
- if (!rc && !val.intval)
- return 0;
- /* if flash is active force 500mA */
- if ((usb_current < SDP_CURRENT_UA) && is_flash_active(chg))
- usb_current = SDP_CURRENT_UA;
- rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, true,
- usb_current);
- if (rc < 0) {
- pr_err("Couldn't vote ICL USB_PSY_VOTER rc=%d\n", rc);
- return rc;
- }
- rc = vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0);
- if (rc < 0) {
- pr_err("Couldn't remove SW_ICL_MAX vote rc=%d\n", rc);
- return rc;
- }
- }
- return 0;
- }
- int smblib_set_prop_sdp_current_max(struct smb_charger *chg,
- int val)
- {
- union power_supply_propval pval;
- int rc = 0;
- if (!chg->pd_active) {
- rc = smblib_get_prop_usb_present(chg, &pval);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get usb present rc = %d\n",
- rc);
- return rc;
- }
- /* handle the request only when USB is present */
- if (pval.intval)
- rc = smblib_handle_usb_current(chg, val);
- } else if (chg->system_suspend_supported) {
- if (val <= USBIN_25MA)
- rc = vote(chg->usb_icl_votable,
- PD_SUSPEND_SUPPORTED_VOTER, true, val);
- else
- rc = vote(chg->usb_icl_votable,
- PD_SUSPEND_SUPPORTED_VOTER, false, 0);
- }
- return rc;
- }
- int smblib_set_prop_usb_voltage_max_limit(struct smb_charger *chg,
- int val)
- {
- union power_supply_propval pval = {0, };
- /* Exit if same value is re-configured */
- if (val == chg->usbin_forced_max_uv)
- return 0;
- smblib_get_prop_usb_voltage_max_design(chg, &pval);
- if (val >= MICRO_5V && val <= pval.intval) {
- chg->usbin_forced_max_uv = val;
- smblib_dbg(chg, PR_MISC, "Max VBUS limit changed to: %d\n",
- val);
- } else if (chg->usbin_forced_max_uv) {
- chg->usbin_forced_max_uv = 0;
- } else {
- return 0;
- }
- power_supply_changed(chg->usb_psy);
- return 0;
- }
- static void smblib_typec_irq_config(struct smb_charger *chg, bool en)
- {
- if (en == chg->typec_irq_en)
- return;
- if (en) {
- enable_irq(
- chg->irq_info[TYPEC_ATTACH_DETACH_IRQ].irq);
- enable_irq(
- chg->irq_info[TYPEC_CC_STATE_CHANGE_IRQ].irq);
- enable_irq(
- chg->irq_info[TYPEC_OR_RID_DETECTION_CHANGE_IRQ].irq);
- } else {
- disable_irq_nosync(
- chg->irq_info[TYPEC_ATTACH_DETACH_IRQ].irq);
- disable_irq_nosync(
- chg->irq_info[TYPEC_CC_STATE_CHANGE_IRQ].irq);
- disable_irq_nosync(
- chg->irq_info[TYPEC_OR_RID_DETECTION_CHANGE_IRQ].irq);
- }
- chg->typec_irq_en = en;
- }
- #define PR_LOCK_TIMEOUT_MS 1000
- int smblib_set_prop_typec_power_role(struct smb_charger *chg, int val)
- {
- int rc = 0;
- u8 power_role;
- enum power_supply_typec_mode typec_mode;
- bool snk_attached = false, src_attached = false, is_pr_lock = false;
- if (chg->connector_type == QTI_POWER_SUPPLY_CONNECTOR_MICRO_USB)
- return 0;
- spin_lock(&chg->typec_pr_lock);
- smblib_dbg(chg, PR_MISC, "power role change: %d --> %d!",
- chg->power_role, val);
- /* Force the power-role if the initial value is NONE, for the legacy cable detection WA. */
- if (chg->power_role == val && chg->power_role != QTI_POWER_SUPPLY_TYPEC_PR_NONE) {
- smblib_dbg(chg, PR_MISC, "power role already in %d, ignore!",
- chg->power_role);
- goto unlock;
- }
- typec_mode = smblib_get_prop_typec_mode(chg);
- if (typec_mode >= QTI_POWER_SUPPLY_TYPEC_SINK &&
- typec_mode <= QTI_POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER)
- snk_attached = true;
- else if (typec_mode >= QTI_POWER_SUPPLY_TYPEC_SOURCE_DEFAULT &&
- typec_mode <= QTI_POWER_SUPPLY_TYPEC_SOURCE_HIGH)
- src_attached = true;
- /*
- * If current power role is in DRP, and type-c is already in the
- * mode (source or sink) that's being requested, it means this is
- * a power role locking request from USBPD driver. Disable type-c
- * related interrupts for locking power role to avoid the redundant
- * notifications.
- */
- if ((chg->power_role == QTI_POWER_SUPPLY_TYPEC_PR_DUAL) &&
- ((src_attached && val == QTI_POWER_SUPPLY_TYPEC_PR_SINK) ||
- (snk_attached && val == QTI_POWER_SUPPLY_TYPEC_PR_SOURCE)))
- is_pr_lock = true;
- smblib_dbg(chg, PR_MISC, "snk_attached = %d, src_attached = %d, is_pr_lock = %d\n",
- snk_attached, src_attached, is_pr_lock);
- cancel_delayed_work(&chg->pr_lock_clear_work);
- if (!chg->pr_lock_in_progress && is_pr_lock) {
- smblib_dbg(chg, PR_MISC, "disable type-c interrupts for power role locking\n");
- smblib_typec_irq_config(chg, false);
- schedule_delayed_work(&chg->pr_lock_clear_work,
- msecs_to_jiffies(PR_LOCK_TIMEOUT_MS));
- } else if (chg->pr_lock_in_progress && !is_pr_lock) {
- smblib_dbg(chg, PR_MISC, "restore type-c interrupts after exit power role locking\n");
- smblib_typec_irq_config(chg, true);
- }
- chg->pr_lock_in_progress = is_pr_lock;
- switch (val) {
- case QTI_POWER_SUPPLY_TYPEC_PR_NONE:
- power_role = TYPEC_DISABLE_CMD_BIT;
- break;
- case QTI_POWER_SUPPLY_TYPEC_PR_DUAL:
- power_role = chg->typec_try_mode;
- break;
- case QTI_POWER_SUPPLY_TYPEC_PR_SINK:
- power_role = EN_SNK_ONLY_BIT;
- break;
- case QTI_POWER_SUPPLY_TYPEC_PR_SOURCE:
- power_role = EN_SRC_ONLY_BIT;
- break;
- default:
- smblib_err(chg, "power role %d not supported\n", val);
- rc = -EINVAL;
- goto unlock;
- }
- rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG,
- TYPEC_POWER_ROLE_CMD_MASK | TYPEC_TRY_MODE_MASK,
- power_role);
- if (rc < 0) {
- smblib_err(chg, "Couldn't write 0x%02x to TYPE_C_INTRPT_ENB_SOFTWARE_CTRL rc=%d\n",
- power_role, rc);
- goto unlock;
- }
- chg->power_role = val;
- unlock:
- spin_unlock(&chg->typec_pr_lock);
- return rc;
- }
- int smblib_set_prop_typec_select_rp(struct smb_charger *chg,
- int val)
- {
- int rc;
- if (!typec_in_src_mode(chg)) {
- smblib_err(chg, "Couldn't set curr src: not in SRC mode\n");
- return -EINVAL;
- }
- if (val < TYPEC_SRC_RP_MAX_ELEMENTS) {
- rc = smblib_masked_write(chg, TYPE_C_CURRSRC_CFG_REG,
- TYPEC_SRC_RP_SEL_MASK,
- val);
- if (rc < 0)
- smblib_err(chg, "Couldn't write to TYPE_C_CURRSRC_CFG rc=%d\n",
- rc);
- return rc;
- }
- return -EINVAL;
- }
- int smblib_set_prop_pd_voltage_min(struct smb_charger *chg,
- int val)
- {
- int rc, min_uv;
- min_uv = min(val, chg->voltage_max_uv);
- if (chg->voltage_min_uv == min_uv)
- return 0;
- rc = smblib_set_usb_pd_allowed_voltage(chg, min_uv,
- chg->voltage_max_uv);
- if (rc < 0) {
- smblib_err(chg, "invalid min voltage %duV rc=%d\n",
- val, rc);
- return rc;
- }
- chg->voltage_min_uv = min_uv;
- power_supply_changed(chg->batt_psy);
- return rc;
- }
- int smblib_set_prop_pd_voltage_max(struct smb_charger *chg,
- int val)
- {
- int rc, max_uv;
- max_uv = max(val, chg->voltage_min_uv);
- if (chg->voltage_max_uv == max_uv)
- return 0;
- rc = smblib_set_usb_pd_fsw(chg, max_uv);
- if (rc < 0) {
- smblib_err(chg, "Couldn't set FSW for voltage %duV rc=%d\n",
- val, rc);
- return rc;
- }
- rc = smblib_set_usb_pd_allowed_voltage(chg, chg->voltage_min_uv,
- max_uv);
- if (rc < 0) {
- smblib_err(chg, "invalid max voltage %duV rc=%d\n",
- val, rc);
- return rc;
- }
- chg->voltage_max_uv = max_uv;
- power_supply_changed(chg->batt_psy);
- return rc;
- }
- int smblib_set_prop_pd_active(struct smb_charger *chg,
- int val)
- {
- const struct apsd_result *apsd = smblib_get_apsd_result(chg);
- int rc = 0;
- int sec_charger, typec_mode;
- /*
- * Ignore repetitive notification while PD is active, which
- * is caused by hard reset.
- */
- if (chg->pd_active && chg->pd_active == val)
- return 0;
- chg->pd_active = val;
- smblib_apsd_enable(chg, !chg->pd_active);
- update_sw_icl_max(chg, apsd->val);
- if (chg->pd_active) {
- vote(chg->limited_irq_disable_votable, CHARGER_TYPE_VOTER,
- false, 0);
- vote(chg->hdc_irq_disable_votable, CHARGER_TYPE_VOTER,
- false, 0);
- /*
- * Enforce 100mA for PD until the real vote comes in later.
- * It is guaranteed that pd_active is set prior to
- * pd_current_max
- */
- vote(chg->usb_icl_votable, PD_VOTER, true, USBIN_100MA);
- vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
- vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0);
- /*
- * For PPS, Charge Pump is preferred over parallel charger if
- * present.
- */
- if (chg->pd_active == QTI_POWER_SUPPLY_PD_PPS_ACTIVE
- && chg->sec_cp_present) {
- rc = smblib_select_sec_charger(chg,
- QTI_POWER_SUPPLY_CHARGER_SEC_CP,
- QTI_POWER_SUPPLY_CP_PPS, false);
- if (rc < 0)
- dev_err(chg->dev, "Couldn't enable secondary charger rc=%d\n",
- rc);
- }
- } else {
- vote(chg->usb_icl_votable, PD_VOTER, false, 0);
- vote(chg->limited_irq_disable_votable, CHARGER_TYPE_VOTER,
- true, 0);
- vote(chg->hdc_irq_disable_votable, CHARGER_TYPE_VOTER,
- true, 0);
- sec_charger = chg->sec_pl_present ?
- QTI_POWER_SUPPLY_CHARGER_SEC_PL :
- QTI_POWER_SUPPLY_CHARGER_SEC_NONE;
- rc = smblib_select_sec_charger(chg, sec_charger,
- QTI_POWER_SUPPLY_CP_NONE, false);
- if (rc < 0)
- dev_err(chg->dev,
- "Couldn't enable secondary charger rc=%d\n",
- rc);
- /* PD hard resets failed, proceed to detect QC2/3 */
- if (chg->ok_to_pd) {
- chg->ok_to_pd = false;
- smblib_hvdcp_detect_try_enable(chg, true);
- }
- }
- smblib_usb_pd_adapter_allowance_override(chg,
- !!chg->pd_active ? FORCE_5V : FORCE_NULL);
- smblib_update_usb_type(chg);
- if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB &&
- !chg->ok_to_pd) {
- typec_mode = smblib_get_prop_typec_mode(chg);
- if (typec_rp_med_high(chg, typec_mode))
- vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
- }
- power_supply_changed(chg->usb_psy);
- return rc;
- }
- int smblib_set_prop_ship_mode(struct smb_charger *chg,
- int val)
- {
- int rc;
- smblib_dbg(chg, PR_MISC, "Set ship mode: %d!!\n", !!val);
- rc = smblib_masked_write(chg, SHIP_MODE_REG, SHIP_MODE_EN_BIT,
- !!val ? SHIP_MODE_EN_BIT : 0);
- if (rc < 0)
- dev_err(chg->dev, "Couldn't %s ship mode, rc=%d\n",
- !!val ? "enable" : "disable", rc);
- return rc;
- }
- int smblib_set_prop_pd_in_hard_reset(struct smb_charger *chg,
- int val)
- {
- int rc = 0;
- if (chg->pd_hard_reset == val)
- return rc;
- chg->pd_hard_reset = val;
- rc = smblib_masked_write(chg, TYPE_C_EXIT_STATE_CFG_REG,
- EXIT_SNK_BASED_ON_CC_BIT,
- (chg->pd_hard_reset) ? EXIT_SNK_BASED_ON_CC_BIT : 0);
- if (rc < 0)
- smblib_err(chg, "Couldn't set EXIT_SNK_BASED_ON_CC rc=%d\n",
- rc);
- return rc;
- }
- #define JEITA_SOFT 0
- #define JEITA_HARD 1
- static int smblib_update_jeita(struct smb_charger *chg, u32 *thresholds,
- int type)
- {
- int rc;
- u16 temp, base;
- base = CHGR_JEITA_THRESHOLD_BASE_REG(type);
- temp = thresholds[1] & 0xFFFF;
- temp = ((temp & 0xFF00) >> 8) | ((temp & 0xFF) << 8);
- rc = smblib_batch_write(chg, base, (u8 *)&temp, 2);
- if (rc < 0) {
- smblib_err(chg,
- "Couldn't configure Jeita %s hot threshold rc=%d\n",
- (type == JEITA_SOFT) ? "Soft" : "Hard", rc);
- return rc;
- }
- temp = thresholds[0] & 0xFFFF;
- temp = ((temp & 0xFF00) >> 8) | ((temp & 0xFF) << 8);
- rc = smblib_batch_write(chg, base + 2, (u8 *)&temp, 2);
- if (rc < 0) {
- smblib_err(chg,
- "Couldn't configure Jeita %s cold threshold rc=%d\n",
- (type == JEITA_SOFT) ? "Soft" : "Hard", rc);
- return rc;
- }
- smblib_dbg(chg, PR_MISC, "%s Jeita threshold configured\n",
- (type == JEITA_SOFT) ? "Soft" : "Hard");
- return 0;
- }
- static int smblib_charge_inhibit_en(struct smb_charger *chg, bool enable)
- {
- int rc;
- rc = smblib_masked_write(chg, CHGR_CFG2_REG,
- CHARGER_INHIBIT_BIT,
- enable ? CHARGER_INHIBIT_BIT : 0);
- return rc;
- }
- static int smblib_soft_jeita_arb_wa(struct smb_charger *chg)
- {
- union power_supply_propval pval;
- int rc = 0;
- bool soft_jeita;
- rc = smblib_get_prop_batt_health(chg, &pval);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get battery health rc=%d\n", rc);
- return rc;
- }
- /* Do nothing on entering hard JEITA condition */
- if (pval.intval == POWER_SUPPLY_HEALTH_COLD ||
- pval.intval == POWER_SUPPLY_HEALTH_HOT)
- return 0;
- if (chg->jeita_soft_fcc[0] < 0 || chg->jeita_soft_fcc[1] < 0 ||
- chg->jeita_soft_fv[0] < 0 || chg->jeita_soft_fv[1] < 0)
- return 0;
- soft_jeita = (pval.intval == POWER_SUPPLY_HEALTH_COOL) ||
- (pval.intval == POWER_SUPPLY_HEALTH_WARM);
- /* Do nothing on entering soft JEITA from hard JEITA */
- if (chg->jeita_arb_flag && soft_jeita)
- return 0;
- /* Do nothing, initial to health condition */
- if (!chg->jeita_arb_flag && !soft_jeita)
- return 0;
- /* Entering soft JEITA from normal state */
- if (!chg->jeita_arb_flag && soft_jeita) {
- vote(chg->chg_disable_votable, JEITA_ARB_VOTER, true, 0);
- rc = smblib_charge_inhibit_en(chg, true);
- if (rc < 0)
- smblib_err(chg, "Couldn't enable charge inhibit rc=%d\n",
- rc);
- rc = smblib_update_jeita(chg, chg->jeita_soft_hys_thlds,
- JEITA_SOFT);
- if (rc < 0)
- smblib_err(chg,
- "Couldn't configure Jeita soft threshold rc=%d\n",
- rc);
- if (pval.intval == POWER_SUPPLY_HEALTH_COOL) {
- vote(chg->fcc_votable, JEITA_ARB_VOTER, true,
- chg->jeita_soft_fcc[0]);
- vote(chg->fv_votable, JEITA_ARB_VOTER, true,
- chg->jeita_soft_fv[0]);
- } else {
- vote(chg->fcc_votable, JEITA_ARB_VOTER, true,
- chg->jeita_soft_fcc[1]);
- vote(chg->fv_votable, JEITA_ARB_VOTER, true,
- chg->jeita_soft_fv[1]);
- }
- vote(chg->chg_disable_votable, JEITA_ARB_VOTER, false, 0);
- chg->jeita_arb_flag = true;
- } else if (chg->jeita_arb_flag && !soft_jeita) {
- /* Exit to health state from soft JEITA */
- vote(chg->chg_disable_votable, JEITA_ARB_VOTER, true, 0);
- rc = smblib_charge_inhibit_en(chg, false);
- if (rc < 0)
- smblib_err(chg, "Couldn't disable charge inhibit rc=%d\n",
- rc);
- rc = smblib_update_jeita(chg, chg->jeita_soft_thlds,
- JEITA_SOFT);
- if (rc < 0)
- smblib_err(chg, "Couldn't configure Jeita soft threshold rc=%d\n",
- rc);
- vote(chg->fcc_votable, JEITA_ARB_VOTER, false, 0);
- vote(chg->fv_votable, JEITA_ARB_VOTER, false, 0);
- vote(chg->chg_disable_votable, JEITA_ARB_VOTER, false, 0);
- chg->jeita_arb_flag = false;
- }
- smblib_dbg(chg, PR_MISC, "JEITA ARB status %d, soft JEITA status %d\n",
- chg->jeita_arb_flag, soft_jeita);
- return rc;
- }
- /************************
- * USB MAIN PSY GETTERS *
- ************************/
- int smblib_get_prop_fcc_delta(struct smb_charger *chg,
- int *val)
- {
- int rc, jeita_cc_delta_ua = 0;
- if (chg->sw_jeita_enabled) {
- *val = 0;
- return 0;
- }
- rc = smblib_get_jeita_cc_delta(chg, &jeita_cc_delta_ua);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get jeita cc delta rc=%d\n", rc);
- jeita_cc_delta_ua = 0;
- }
- *val = jeita_cc_delta_ua;
- return 0;
- }
- /************************
- * USB MAIN PSY SETTERS *
- ************************/
- int smblib_get_charge_current(struct smb_charger *chg,
- int *total_current_ua)
- {
- const struct apsd_result *apsd_result = smblib_get_apsd_result(chg);
- union power_supply_propval val = {0, };
- int rc = 0, typec_source_rd, current_ua;
- bool non_compliant;
- u8 stat;
- if (chg->pd_active) {
- *total_current_ua =
- get_client_vote_locked(chg->usb_icl_votable, PD_VOTER);
- return rc;
- }
- rc = smblib_read(chg, LEGACY_CABLE_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_5 rc=%d\n", rc);
- return rc;
- }
- non_compliant = stat & TYPEC_NONCOMP_LEGACY_CABLE_STATUS_BIT;
- /* get settled ICL */
- rc = smblib_get_prop_input_current_settled(chg, &val);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get settled ICL rc=%d\n", rc);
- return rc;
- }
- typec_source_rd = smblib_get_prop_ufp_mode(chg);
- /* QC 2.0/3.0 adapter */
- if (apsd_result->bit & (QC_3P0_BIT | QC_2P0_BIT)) {
- *total_current_ua = HVDCP_CURRENT_UA;
- return 0;
- }
- if (non_compliant && !chg->typec_legacy_use_rp_icl) {
- switch (apsd_result->bit) {
- case CDP_CHARGER_BIT:
- current_ua = CDP_CURRENT_UA;
- break;
- case DCP_CHARGER_BIT:
- case OCP_CHARGER_BIT:
- case FLOAT_CHARGER_BIT:
- current_ua = DCP_CURRENT_UA;
- break;
- default:
- current_ua = 0;
- break;
- }
- *total_current_ua = max(current_ua, val.intval);
- return 0;
- }
- switch (typec_source_rd) {
- case QTI_POWER_SUPPLY_TYPEC_SOURCE_DEFAULT:
- switch (apsd_result->bit) {
- case CDP_CHARGER_BIT:
- current_ua = CDP_CURRENT_UA;
- break;
- case DCP_CHARGER_BIT:
- case OCP_CHARGER_BIT:
- case FLOAT_CHARGER_BIT:
- current_ua = chg->default_icl_ua;
- break;
- default:
- current_ua = 0;
- break;
- }
- break;
- case QTI_POWER_SUPPLY_TYPEC_SOURCE_MEDIUM:
- current_ua = TYPEC_MEDIUM_CURRENT_UA;
- break;
- case QTI_POWER_SUPPLY_TYPEC_SOURCE_HIGH:
- current_ua = TYPEC_HIGH_CURRENT_UA;
- break;
- case QTI_POWER_SUPPLY_TYPEC_NON_COMPLIANT:
- case QTI_POWER_SUPPLY_TYPEC_NONE:
- default:
- current_ua = 0;
- break;
- }
- *total_current_ua = max(current_ua, val.intval);
- return 0;
- }
- #define IADP_OVERHEAT_UA 500000
- int smblib_set_prop_thermal_overheat(struct smb_charger *chg,
- int therm_overheat)
- {
- int icl_ua = 0;
- if (chg->thermal_overheat == !!therm_overheat)
- return 0;
- /* Configure ICL to 500mA in case system health is Overheat */
- if (therm_overheat)
- icl_ua = IADP_OVERHEAT_UA;
- if (!chg->cp_disable_votable)
- chg->cp_disable_votable = find_votable("CP_DISABLE");
- if (chg->cp_disable_votable) {
- vote(chg->cp_disable_votable, OVERHEAT_LIMIT_VOTER,
- therm_overheat, 0);
- vote(chg->usb_icl_votable, OVERHEAT_LIMIT_VOTER,
- therm_overheat, icl_ua);
- }
- chg->thermal_overheat = !!therm_overheat;
- return 0;
- }
- /**********************
- * INTERRUPT HANDLERS *
- **********************/
- irqreturn_t smb5_default_irq_handler(int irq, void *data)
- {
- struct smb_irq_data *irq_data = data;
- struct smb_charger *chg = irq_data->parent_data;
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
- return IRQ_HANDLED;
- }
- irqreturn_t smb5_smb_en_irq_handler(int irq, void *data)
- {
- struct smb_irq_data *irq_data = data;
- struct smb_charger *chg = irq_data->parent_data;
- int rc, input_present;
- if (!chg->cp_disable_votable) {
- chg->cp_disable_votable = find_votable("CP_DISABLE");
- if (!chg->cp_disable_votable)
- return IRQ_HANDLED;
- }
- if (chg->pd_hard_reset) {
- vote(chg->cp_disable_votable, BOOST_BACK_VOTER, true, 0);
- return IRQ_HANDLED;
- }
- rc = smblib_is_input_present(chg, &input_present);
- if (rc < 0) {
- pr_err("Couldn't get usb presence status rc=%d\n", rc);
- return IRQ_HANDLED;
- }
- if (input_present) {
- /*
- * Add some delay to enable SMB1390 switcher after SMB_EN
- * pin goes high
- */
- usleep_range(1000, 1100);
- vote(chg->cp_disable_votable, BOOST_BACK_VOTER, false, 0);
- }
- return IRQ_HANDLED;
- }
- irqreturn_t smb5_sdam_sts_change_irq_handler(int irq, void *data)
- {
- struct smb_irq_data *irq_data = data;
- struct smb_charger *chg = irq_data->parent_data;
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
- mutex_lock(&chg->irq_status_lock);
- chg->irq_status |= PULSE_SKIP_IRQ_BIT;
- mutex_unlock(&chg->irq_status_lock);
- power_supply_changed(chg->batt_psy);
- return IRQ_HANDLED;
- }
- #define CHG_TERM_WA_ENTRY_DELAY_MS 300000 /* 5 min */
- #define CHG_TERM_WA_EXIT_DELAY_MS 60000 /* 1 min */
- static void smblib_eval_chg_termination(struct smb_charger *chg, u8 batt_status)
- {
- union power_supply_propval pval = {0, };
- int rc = 0;
- rc = smblib_get_prop_from_bms(chg, SMB5_QG_REAL_CAPACITY, &pval.intval);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read SOC value, rc=%d\n", rc);
- return;
- }
- /*
- * Post charge termination, switch to BSM mode triggers the risk of
- * over charging as BATFET opening may take some time post the necessity
- * of staying in supplemental mode, leading to unintended charging of
- * battery. Trigger the charge termination WA once charging is completed
- * to prevent overcharing.
- */
- if ((batt_status == TERMINATE_CHARGE) && (pval.intval == 100) &&
- (ktime_to_ms(alarm_expires_remaining(/* alarm not pending */
- &chg->chg_termination_alarm)) <= 0)) {
- chg->cc_soc_ref = 0;
- chg->last_cc_soc = 0;
- chg->term_vbat_uv = 0;
- alarm_start_relative(&chg->chg_termination_alarm,
- ms_to_ktime(CHG_TERM_WA_ENTRY_DELAY_MS));
- } else if (pval.intval < 100) {
- /*
- * Reset CC_SOC reference value for charge termination WA once
- * we exit the TERMINATE_CHARGE state and soc drops below 100%
- */
- chg->cc_soc_ref = 0;
- chg->last_cc_soc = 0;
- chg->term_vbat_uv = 0;
- }
- }
- irqreturn_t smb5_chg_state_change_irq_handler(int irq, void *data)
- {
- struct smb_irq_data *irq_data = data;
- struct smb_charger *chg = irq_data->parent_data;
- u8 stat;
- int rc;
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
- rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n",
- rc);
- return IRQ_HANDLED;
- }
- stat = stat & BATTERY_CHARGER_STATUS_MASK;
- if (chg->wa_flags & CHG_TERMINATION_WA)
- smblib_eval_chg_termination(chg, stat);
- power_supply_changed(chg->batt_psy);
- return IRQ_HANDLED;
- }
- irqreturn_t smb5_batt_temp_changed_irq_handler(int irq, void *data)
- {
- struct smb_irq_data *irq_data = data;
- struct smb_charger *chg = irq_data->parent_data;
- int rc;
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
- if (chg->jeita_configured != JEITA_CFG_COMPLETE)
- return IRQ_HANDLED;
- rc = smblib_soft_jeita_arb_wa(chg);
- if (rc < 0) {
- smblib_err(chg, "Couldn't fix soft jeita arb rc=%d\n",
- rc);
- return IRQ_HANDLED;
- }
- return IRQ_HANDLED;
- }
- irqreturn_t smb5_batt_psy_changed_irq_handler(int irq, void *data)
- {
- struct smb_irq_data *irq_data = data;
- struct smb_charger *chg = irq_data->parent_data;
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
- power_supply_changed(chg->batt_psy);
- return IRQ_HANDLED;
- }
- #define AICL_STEP_MV 200
- #define MAX_AICL_THRESHOLD_MV 4800
- irqreturn_t smb5_usbin_uv_irq_handler(int irq, void *data)
- {
- struct smb_irq_data *irq_data = data;
- struct smb_charger *chg = irq_data->parent_data;
- struct storm_watch *wdata;
- const struct apsd_result *apsd = smblib_get_apsd_result(chg);
- int rc;
- u8 stat = 0, max_pulses = 0;
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
- if ((chg->wa_flags & WEAK_ADAPTER_WA)
- && is_storming(&irq_data->storm_data)) {
- if (chg->aicl_max_reached) {
- smblib_dbg(chg, PR_MISC,
- "USBIN_UV storm at max AICL threshold\n");
- return IRQ_HANDLED;
- }
- smblib_dbg(chg, PR_MISC, "USBIN_UV storm at threshold %d\n",
- chg->aicl_5v_threshold_mv);
- /* suspend USBIN before updating AICL threshold */
- vote(chg->usb_icl_votable, AICL_THRESHOLD_VOTER, true, 0);
- /* delay for VASHDN deglitch */
- msleep(20);
- if (chg->aicl_5v_threshold_mv > MAX_AICL_THRESHOLD_MV) {
- /* reached max AICL threshold */
- chg->aicl_max_reached = true;
- goto unsuspend_input;
- }
- /* Increase AICL threshold by 200mV */
- rc = smblib_set_charge_param(chg, &chg->param.aicl_5v_threshold,
- chg->aicl_5v_threshold_mv + AICL_STEP_MV);
- if (rc < 0)
- dev_err(chg->dev,
- "Error in setting AICL threshold rc=%d\n", rc);
- else
- chg->aicl_5v_threshold_mv += AICL_STEP_MV;
- rc = smblib_set_charge_param(chg,
- &chg->param.aicl_cont_threshold,
- chg->aicl_cont_threshold_mv + AICL_STEP_MV);
- if (rc < 0)
- dev_err(chg->dev,
- "Error in setting AICL threshold rc=%d\n", rc);
- else
- chg->aicl_cont_threshold_mv += AICL_STEP_MV;
- unsuspend_input:
- /* Force torch in boost mode to ensure it works with low ICL */
- if (chg->chg_param.smb_version == PMI632)
- schgm_flash_torch_priority(chg, TORCH_BOOST_MODE);
- if (chg->aicl_max_reached) {
- smblib_dbg(chg, PR_MISC,
- "Reached max AICL threshold resctricting ICL to 100mA\n");
- vote(chg->usb_icl_votable, AICL_THRESHOLD_VOTER,
- true, USBIN_100MA);
- smblib_run_aicl(chg, RESTART_AICL);
- } else {
- smblib_run_aicl(chg, RESTART_AICL);
- vote(chg->usb_icl_votable, AICL_THRESHOLD_VOTER,
- false, 0);
- }
- wdata = &chg->irq_info[USBIN_UV_IRQ].irq_data->storm_data;
- reset_storm_count(wdata);
- }
- if (!chg->irq_info[SWITCHER_POWER_OK_IRQ].irq_data)
- return IRQ_HANDLED;
- wdata = &chg->irq_info[SWITCHER_POWER_OK_IRQ].irq_data->storm_data;
- reset_storm_count(wdata);
- /* Workaround for non-QC2.0-compliant chargers follows */
- if (!chg->qc2_unsupported_voltage &&
- apsd->val == QTI_POWER_SUPPLY_TYPE_USB_HVDCP) {
- rc = smblib_read(chg, QC_CHANGE_STATUS_REG, &stat);
- if (rc < 0)
- smblib_err(chg,
- "Couldn't read CHANGE_STATUS_REG rc=%d\n", rc);
- if (stat & QC_5V_BIT)
- return IRQ_HANDLED;
- rc = smblib_read(chg, HVDCP_PULSE_COUNT_MAX_REG, &max_pulses);
- if (rc < 0)
- smblib_err(chg,
- "Couldn't read QC2 max pulses rc=%d\n", rc);
- chg->qc2_max_pulses = (max_pulses &
- HVDCP_PULSE_COUNT_MAX_QC2_MASK);
- if (stat & QC_12V_BIT) {
- chg->qc2_unsupported_voltage = QC2_NON_COMPLIANT_12V;
- rc = smblib_masked_write(chg, HVDCP_PULSE_COUNT_MAX_REG,
- HVDCP_PULSE_COUNT_MAX_QC2_MASK,
- HVDCP_PULSE_COUNT_MAX_QC2_9V);
- if (rc < 0)
- smblib_err(chg, "Couldn't force max pulses to 9V rc=%d\n",
- rc);
- } else if (stat & QC_9V_BIT) {
- chg->qc2_unsupported_voltage = QC2_NON_COMPLIANT_9V;
- rc = smblib_masked_write(chg, HVDCP_PULSE_COUNT_MAX_REG,
- HVDCP_PULSE_COUNT_MAX_QC2_MASK,
- HVDCP_PULSE_COUNT_MAX_QC2_5V);
- if (rc < 0)
- smblib_err(chg, "Couldn't force max pulses to 5V rc=%d\n",
- rc);
- }
- rc = smblib_masked_write(chg, USBIN_AICL_OPTIONS_CFG_REG,
- SUSPEND_ON_COLLAPSE_USBIN_BIT,
- 0);
- if (rc < 0)
- smblib_err(chg, "Couldn't turn off SUSPEND_ON_COLLAPSE_USBIN_BIT rc=%d\n",
- rc);
- smblib_rerun_apsd(chg);
- }
- return IRQ_HANDLED;
- }
- #define USB_WEAK_INPUT_UA 1400000
- #define ICL_CHANGE_DELAY_MS 1000
- irqreturn_t smb5_icl_change_irq_handler(int irq, void *data)
- {
- u8 stat;
- int rc, settled_ua, delay = ICL_CHANGE_DELAY_MS;
- struct smb_irq_data *irq_data = data;
- struct smb_charger *chg = irq_data->parent_data;
- if (chg->mode == PARALLEL_MASTER) {
- /*
- * Ignore if change in ICL is due to DIE temp mitigation.
- * This is to prevent any further ICL split.
- */
- if (chg->hw_die_temp_mitigation) {
- rc = smblib_read(chg, DIE_TEMP_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg,
- "Couldn't read DIE_TEMP rc=%d\n", rc);
- return IRQ_HANDLED;
- }
- if (stat & (DIE_TEMP_UB_BIT | DIE_TEMP_LB_BIT)) {
- smblib_dbg(chg, PR_PARALLEL,
- "skip ICL change DIE_TEMP %x\n", stat);
- return IRQ_HANDLED;
- }
- }
- rc = smblib_read(chg, AICL_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read AICL_STATUS rc=%d\n",
- rc);
- return IRQ_HANDLED;
- }
- rc = smblib_get_charge_param(chg, &chg->param.icl_stat,
- &settled_ua);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get ICL status rc=%d\n", rc);
- return IRQ_HANDLED;
- }
- /* If AICL settled then schedule work now */
- if (settled_ua == get_effective_result(chg->usb_icl_votable))
- delay = 0;
- cancel_delayed_work_sync(&chg->icl_change_work);
- schedule_delayed_work(&chg->icl_change_work,
- msecs_to_jiffies(delay));
- }
- return IRQ_HANDLED;
- }
- static int smblib_role_switch_failure(struct smb_charger *chg)
- {
- int rc = 0;
- union power_supply_propval pval = {0, };
- if (!chg->use_extcon)
- return 0;
- rc = smblib_get_prop_usb_present(chg, &pval);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get usb presence status rc=%d\n",
- rc);
- return rc;
- }
- /*
- * When role switch fails notify the
- * current charger state to usb driver.
- */
- if (pval.intval) {
- smblib_dbg(chg, PR_MISC, " Role reversal failed, notifying device mode to usb driver.\n");
- smblib_notify_device_mode(chg, true);
- }
- return rc;
- }
- static int typec_partner_register(struct smb_charger *chg)
- {
- int typec_mode, rc = 0;
- mutex_lock(&chg->typec_lock);
- if (!chg->typec_port || chg->pr_swap_in_progress)
- goto unlock;
- if (!chg->typec_partner) {
- if (chg->sink_src_mode == AUDIO_ACCESS_MODE)
- chg->typec_partner_desc.accessory =
- TYPEC_ACCESSORY_AUDIO;
- else
- chg->typec_partner_desc.accessory =
- TYPEC_ACCESSORY_NONE;
- chg->typec_partner = typec_register_partner(chg->typec_port,
- &chg->typec_partner_desc);
- if (IS_ERR(chg->typec_partner)) {
- rc = PTR_ERR(chg->typec_partner);
- pr_err("failed to register typec_partner rc=%d\n", rc);
- goto unlock;
- }
- }
- typec_mode = smblib_get_prop_typec_mode(chg);
- if (typec_mode >= QTI_POWER_SUPPLY_TYPEC_SOURCE_DEFAULT
- || typec_mode == QTI_POWER_SUPPLY_TYPEC_NONE) {
- if (chg->typec_role_swap_failed) {
- rc = smblib_role_switch_failure(chg);
- if (rc < 0)
- smblib_err(chg, "Failed to role switch rc=%d\n",
- rc);
- chg->typec_role_swap_failed = false;
- }
- typec_set_data_role(chg->typec_port, TYPEC_DEVICE);
- typec_set_pwr_role(chg->typec_port, TYPEC_SINK);
- } else {
- typec_set_data_role(chg->typec_port, TYPEC_HOST);
- typec_set_pwr_role(chg->typec_port, TYPEC_SOURCE);
- }
- unlock:
- mutex_unlock(&chg->typec_lock);
- return rc;
- }
- static void typec_partner_unregister(struct smb_charger *chg)
- {
- mutex_lock(&chg->typec_lock);
- if (!chg->typec_port)
- goto unlock;
- if (chg->typec_partner && !chg->pr_swap_in_progress) {
- smblib_dbg(chg, PR_MISC, "Un-registering typeC partner\n");
- typec_unregister_partner(chg->typec_partner);
- chg->typec_partner = NULL;
- }
- unlock:
- mutex_unlock(&chg->typec_lock);
- }
- static void smblib_micro_usb_plugin(struct smb_charger *chg, bool vbus_rising)
- {
- int rc = 0;
- if (!vbus_rising) {
- smblib_update_usb_type(chg);
- smblib_notify_device_mode(chg, false);
- smblib_uusb_removal(chg);
- typec_partner_unregister(chg);
- } else {
- rc = typec_partner_register(chg);
- if (rc < 0)
- smblib_err(chg, "Couldn't register partner rc =%d\n",
- rc);
- }
- }
- static void smblib_usb_plugin_hard_reset_locked(struct smb_charger *chg)
- {
- int rc;
- u8 stat;
- bool vbus_rising;
- struct smb_irq_data *data;
- struct storm_watch *wdata;
- rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read USB_INT_RT_STS rc=%d\n", rc);
- return;
- }
- vbus_rising = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT);
- if (vbus_rising) {
- /* Remove FCC_STEPPER 1.5A init vote to allow FCC ramp up */
- if (chg->fcc_stepper_enable)
- vote(chg->fcc_votable, FCC_STEPPER_VOTER, false, 0);
- } else {
- if (chg->wa_flags & BOOST_BACK_WA) {
- data = chg->irq_info[SWITCHER_POWER_OK_IRQ].irq_data;
- if (data) {
- wdata = &data->storm_data;
- update_storm_count(wdata,
- WEAK_CHG_STORM_COUNT);
- vote(chg->usb_icl_votable, BOOST_BACK_VOTER,
- false, 0);
- vote(chg->usb_icl_votable, WEAK_CHARGER_VOTER,
- false, 0);
- }
- }
- /* Force 1500mA FCC on USB removal if fcc stepper is enabled */
- if (chg->fcc_stepper_enable)
- vote(chg->fcc_votable, FCC_STEPPER_VOTER,
- true, 1500000);
- }
- power_supply_changed(chg->usb_psy);
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: usbin-plugin %s\n",
- vbus_rising ? "attached" : "detached");
- }
- #define PL_DELAY_MS 30000
- static void smblib_usb_plugin_locked(struct smb_charger *chg)
- {
- int rc;
- u8 stat;
- bool vbus_rising;
- struct smb_irq_data *data;
- struct storm_watch *wdata;
- rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read USB_INT_RT_STS rc=%d\n", rc);
- return;
- }
- vbus_rising = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT);
- smblib_set_opt_switcher_freq(chg, vbus_rising ? chg->chg_freq.freq_5V :
- chg->chg_freq.freq_removal);
- if (vbus_rising) {
- cancel_delayed_work_sync(&chg->pr_swap_detach_work);
- vote(chg->awake_votable, DETACH_DETECT_VOTER, false, 0);
- rc = smblib_request_dpdm(chg, true);
- if (rc < 0)
- smblib_err(chg, "Couldn't to enable DPDM rc=%d\n", rc);
- /* Enable SW Thermal regulation */
- rc = smblib_set_sw_thermal_regulation(chg, true);
- if (rc < 0)
- smblib_err(chg, "Couldn't start SW thermal regulation WA, rc=%d\n",
- rc);
- /* Remove FCC_STEPPER 1.5A init vote to allow FCC ramp up */
- if (chg->fcc_stepper_enable)
- vote(chg->fcc_votable, FCC_STEPPER_VOTER, false, 0);
- /* Schedule work to enable parallel charger */
- vote(chg->awake_votable, PL_DELAY_VOTER, true, 0);
- schedule_delayed_work(&chg->pl_enable_work,
- msecs_to_jiffies(PL_DELAY_MS));
- } else {
- /* Disable SW Thermal Regulation */
- rc = smblib_set_sw_thermal_regulation(chg, false);
- if (rc < 0)
- smblib_err(chg, "Couldn't stop SW thermal regulation WA, rc=%d\n",
- rc);
- if (chg->wa_flags & BOOST_BACK_WA) {
- data = chg->irq_info[SWITCHER_POWER_OK_IRQ].irq_data;
- if (data) {
- wdata = &data->storm_data;
- update_storm_count(wdata,
- WEAK_CHG_STORM_COUNT);
- vote(chg->usb_icl_votable, BOOST_BACK_VOTER,
- false, 0);
- vote(chg->usb_icl_votable, WEAK_CHARGER_VOTER,
- false, 0);
- }
- }
- /* Force 1500mA FCC on removal if fcc stepper is enabled */
- if (chg->fcc_stepper_enable)
- vote(chg->fcc_votable, FCC_STEPPER_VOTER,
- true, 1500000);
- if (chg->wa_flags & WEAK_ADAPTER_WA) {
- chg->aicl_5v_threshold_mv =
- chg->default_aicl_5v_threshold_mv;
- chg->aicl_cont_threshold_mv =
- chg->default_aicl_cont_threshold_mv;
- smblib_set_charge_param(chg,
- &chg->param.aicl_5v_threshold,
- chg->aicl_5v_threshold_mv);
- smblib_set_charge_param(chg,
- &chg->param.aicl_cont_threshold,
- chg->aicl_cont_threshold_mv);
- chg->aicl_max_reached = false;
- if (chg->chg_param.smb_version == PMI632)
- schgm_flash_torch_priority(chg,
- TORCH_BUCK_MODE);
- data = chg->irq_info[USBIN_UV_IRQ].irq_data;
- if (data) {
- wdata = &data->storm_data;
- reset_storm_count(wdata);
- }
- vote(chg->usb_icl_votable, AICL_THRESHOLD_VOTER,
- false, 0);
- }
- rc = smblib_request_dpdm(chg, false);
- if (rc < 0)
- smblib_err(chg, "Couldn't disable DPDM rc=%d\n", rc);
- smblib_update_usb_type(chg);
- }
- if (chg->connector_type == QTI_POWER_SUPPLY_CONNECTOR_MICRO_USB)
- smblib_micro_usb_plugin(chg, vbus_rising);
- vote(chg->temp_change_irq_disable_votable, DEFAULT_VOTER,
- !vbus_rising, 0);
- power_supply_changed(chg->usb_psy);
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: usbin-plugin %s\n",
- vbus_rising ? "attached" : "detached");
- }
- irqreturn_t smb5_usb_plugin_irq_handler(int irq, void *data)
- {
- struct smb_irq_data *irq_data = data;
- struct smb_charger *chg = irq_data->parent_data;
- if (chg->pd_hard_reset)
- smblib_usb_plugin_hard_reset_locked(chg);
- else
- smblib_usb_plugin_locked(chg);
- return IRQ_HANDLED;
- }
- static void smblib_handle_slow_plugin_timeout(struct smb_charger *chg,
- bool rising)
- {
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: slow-plugin-timeout %s\n",
- rising ? "rising" : "falling");
- }
- static void smblib_handle_sdp_enumeration_done(struct smb_charger *chg,
- bool rising)
- {
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: sdp-enumeration-done %s\n",
- rising ? "rising" : "falling");
- }
- #define APSD_EXTENDED_TIMEOUT_MS 400
- /* triggers when HVDCP 3.0 authentication has finished */
- static void smblib_handle_hvdcp_3p0_auth_done(struct smb_charger *chg,
- bool rising)
- {
- const struct apsd_result *apsd_result;
- int rc;
- if (!rising)
- return;
- if (chg->mode == PARALLEL_MASTER)
- vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, true, 0);
- /* the APSD done handler will set the USB supply type */
- apsd_result = smblib_get_apsd_result(chg);
- if (apsd_result->bit & QC_3P0_BIT) {
- /* for QC3, switch to CP if present */
- if (chg->sec_cp_present) {
- rc = smblib_select_sec_charger(chg,
- QTI_POWER_SUPPLY_CHARGER_SEC_CP,
- QTI_POWER_SUPPLY_CP_HVDCP3, false);
- if (rc < 0)
- dev_err(chg->dev,
- "Couldn't enable secondary chargers rc=%d\n",
- rc);
- }
- /* QC3.5 detection timeout */
- if (!chg->apsd_ext_timeout &&
- !timer_pending(&chg->apsd_timer)) {
- smblib_dbg(chg, PR_MISC,
- "APSD Extented timer started at %lld\n",
- jiffies_to_msecs(jiffies));
- mod_timer(&chg->apsd_timer,
- msecs_to_jiffies(APSD_EXTENDED_TIMEOUT_MS)
- + jiffies);
- }
- }
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: hvdcp-3p0-auth-done rising; %s detected\n",
- apsd_result->name);
- }
- static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg,
- bool rising, bool qc_charger)
- {
- u32 hvdcp_ua = 0;
- if (rising) {
- if (qc_charger) {
- hvdcp_ua = (chg->real_charger_type ==
- QTI_POWER_SUPPLY_TYPE_USB_HVDCP) ?
- chg->chg_param.hvdcp2_max_icl_ua :
- HVDCP_CURRENT_UA;
- /* enable HDC and ICL irq for QC2/3 charger */
- vote(chg->limited_irq_disable_votable,
- CHARGER_TYPE_VOTER, false, 0);
- vote(chg->hdc_irq_disable_votable,
- CHARGER_TYPE_VOTER, false, 0);
- vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true,
- hvdcp_ua);
- } else {
- /* A plain DCP, enforce DCP ICL if specified */
- vote(chg->usb_icl_votable, DCP_VOTER,
- chg->dcp_icl_ua != -EINVAL, chg->dcp_icl_ua);
- }
- }
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s %s\n", __func__,
- rising ? "rising" : "falling");
- }
- /* triggers when HVDCP is detected */
- static void smblib_handle_hvdcp_detect_done(struct smb_charger *chg,
- bool rising)
- {
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: hvdcp-detect-done %s\n",
- rising ? "rising" : "falling");
- }
- static void update_sw_icl_max(struct smb_charger *chg, int val)
- {
- int typec_mode;
- int rp_ua;
- /* while PD is active it should have complete ICL control */
- if (chg->pd_active)
- return;
- if (chg->typec_mode == QTI_POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER) {
- vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, 500000);
- return;
- }
- /*
- * HVDCP 2/3, handled separately
- */
- if (val == QTI_POWER_SUPPLY_TYPE_USB_HVDCP
- || val == QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3)
- return;
- /* TypeC rp med or high, use rp value */
- typec_mode = smblib_get_prop_typec_mode(chg);
- if (typec_rp_med_high(chg, typec_mode)) {
- rp_ua = get_rp_based_dcp_current(chg, typec_mode);
- vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, rp_ua);
- return;
- }
- /* rp-std or legacy, USB BC 1.2 */
- switch (val) {
- case POWER_SUPPLY_TYPE_USB:
- /*
- * USB_PSY will vote to increase the current to 500/900mA once
- * enumeration is done.
- */
- if (!is_client_vote_enabled(chg->usb_icl_votable,
- USB_PSY_VOTER)) {
- /* if flash is active force 500mA */
- vote(chg->usb_icl_votable, USB_PSY_VOTER, true,
- is_flash_active(chg) ?
- SDP_CURRENT_UA : SDP_100_MA);
- }
- vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0);
- break;
- case POWER_SUPPLY_TYPE_USB_CDP:
- vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true,
- CDP_CURRENT_UA);
- break;
- case POWER_SUPPLY_TYPE_USB_DCP:
- rp_ua = get_rp_based_dcp_current(chg, typec_mode);
- vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, rp_ua);
- break;
- case QTI_POWER_SUPPLY_TYPE_USB_FLOAT:
- /*
- * limit ICL to 100mA, the USB driver will enumerate to check
- * if this is a SDP and appropriately set the current
- */
- vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true,
- SDP_100_MA);
- break;
- case POWER_SUPPLY_TYPE_UNKNOWN:
- default:
- vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true,
- SDP_100_MA);
- break;
- }
- }
- static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising)
- {
- const struct apsd_result *apsd_result;
- if (!rising)
- return;
- apsd_result = smblib_update_usb_type(chg);
- update_sw_icl_max(chg, apsd_result->val);
- switch (apsd_result->bit) {
- case SDP_CHARGER_BIT:
- case CDP_CHARGER_BIT:
- case FLOAT_CHARGER_BIT:
- if (chg->use_extcon)
- smblib_notify_device_mode(chg, true);
- break;
- case OCP_CHARGER_BIT:
- case DCP_CHARGER_BIT:
- break;
- default:
- break;
- }
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: apsd-done rising; %s detected\n",
- apsd_result->name);
- }
- irqreturn_t smb5_usb_source_change_irq_handler(int irq, void *data)
- {
- struct smb_irq_data *irq_data = data;
- struct smb_charger *chg = irq_data->parent_data;
- int rc = 0;
- u8 stat;
- /* PD session is ongoing, ignore BC1.2 and QC detection */
- if (chg->pd_active)
- return IRQ_HANDLED;
- rc = smblib_read(chg, APSD_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read APSD_STATUS rc=%d\n", rc);
- return IRQ_HANDLED;
- }
- smblib_dbg(chg, PR_INTERRUPT, "APSD_STATUS = 0x%02x\n", stat);
- if ((chg->connector_type == QTI_POWER_SUPPLY_CONNECTOR_MICRO_USB)
- && (stat & APSD_DTC_STATUS_DONE_BIT)
- && !chg->uusb_apsd_rerun_done) {
- /*
- * Force re-run APSD to handle slow insertion related
- * charger-mis-detection.
- */
- chg->uusb_apsd_rerun_done = true;
- smblib_rerun_apsd_if_required(chg);
- return IRQ_HANDLED;
- }
- smblib_handle_apsd_done(chg,
- (bool)(stat & APSD_DTC_STATUS_DONE_BIT));
- smblib_handle_hvdcp_detect_done(chg,
- (bool)(stat & QC_CHARGER_BIT));
- smblib_handle_hvdcp_check_timeout(chg,
- (bool)(stat & HVDCP_CHECK_TIMEOUT_BIT),
- (bool)(stat & QC_CHARGER_BIT));
- smblib_handle_hvdcp_3p0_auth_done(chg,
- (bool)(stat & QC_AUTH_DONE_STATUS_BIT));
- smblib_handle_sdp_enumeration_done(chg,
- (bool)(stat & ENUMERATION_DONE_BIT));
- smblib_handle_slow_plugin_timeout(chg,
- (bool)(stat & SLOW_PLUGIN_TIMEOUT_BIT));
- smblib_hvdcp_adaptive_voltage_change(chg);
- power_supply_changed(chg->usb_psy);
- rc = smblib_read(chg, APSD_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read APSD_STATUS rc=%d\n", rc);
- return IRQ_HANDLED;
- }
- smblib_dbg(chg, PR_INTERRUPT, "APSD_STATUS = 0x%02x\n", stat);
- return IRQ_HANDLED;
- }
- enum alarmtimer_restart smblib_lpd_recheck_timer(struct alarm *alarm,
- ktime_t time)
- {
- int val;
- struct smb_charger *chg = container_of(alarm, struct smb_charger,
- lpd_recheck_timer);
- int rc;
- if (chg->lpd_reason == LPD_MOISTURE_DETECTED) {
- val = QTI_POWER_SUPPLY_TYPEC_PR_DUAL;
- rc = smblib_set_prop_typec_power_role(chg, val);
- if (rc < 0) {
- smblib_err(chg, "Couldn't write 0x%02x to TYPE_C_INTRPT_ENB_SOFTWARE_CTRL rc=%d\n",
- val, rc);
- return ALARMTIMER_NORESTART;
- }
- chg->moisture_present = false;
- power_supply_changed(chg->usb_psy);
- } else {
- rc = smblib_masked_write(chg, TYPE_C_INTERRUPT_EN_CFG_2_REG,
- TYPEC_WATER_DETECTION_INT_EN_BIT,
- TYPEC_WATER_DETECTION_INT_EN_BIT);
- if (rc < 0) {
- smblib_err(chg, "Couldn't set TYPE_C_INTERRUPT_EN_CFG_2_REG rc=%d\n",
- rc);
- return ALARMTIMER_NORESTART;
- }
- }
- chg->lpd_stage = LPD_STAGE_NONE;
- chg->lpd_reason = LPD_NONE;
- return ALARMTIMER_NORESTART;
- }
- #define RSBU_K_300K_UV 3000000
- static bool smblib_src_lpd(struct smb_charger *chg)
- {
- bool lpd_flag = false;
- u8 stat;
- int rc, val;
- if (chg->lpd_disabled)
- return false;
- rc = smblib_read(chg, TYPE_C_SRC_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_SRC_STATUS_REG rc=%d\n",
- rc);
- return false;
- }
- switch (stat & DETECTED_SNK_TYPE_MASK) {
- case SRC_DEBUG_ACCESS_BIT:
- if (smblib_rsbux_low(chg, RSBU_K_300K_UV))
- lpd_flag = true;
- break;
- case SRC_RD_RA_VCONN_BIT:
- case SRC_RD_OPEN_BIT:
- case AUDIO_ACCESS_RA_RA_BIT:
- default:
- break;
- }
- if (lpd_flag) {
- chg->lpd_stage = LPD_STAGE_COMMIT;
- val = QTI_POWER_SUPPLY_TYPEC_PR_SINK;
- rc = smblib_set_prop_typec_power_role(chg, val);
- if (rc < 0)
- smblib_err(chg, "Couldn't write 0x%02x to TYPE_C_INTRPT_ENB_SOFTWARE_CTRL rc=%d\n",
- val, rc);
- chg->lpd_reason = LPD_MOISTURE_DETECTED;
- chg->moisture_present = true;
- vote(chg->usb_icl_votable, LPD_VOTER, true, 0);
- alarm_start_relative(&chg->lpd_recheck_timer,
- ms_to_ktime(60000));
- power_supply_changed(chg->usb_psy);
- } else {
- chg->lpd_reason = LPD_NONE;
- chg->typec_mode = smblib_get_prop_typec_mode(chg);
- }
- return lpd_flag;
- }
- static void typec_src_fault_condition_cfg(struct smb_charger *chg, bool src)
- {
- int rc;
- u8 mask = USBIN_MID_COMP_FAULT_EN_BIT | USBIN_COLLAPSE_FAULT_EN_BIT;
- rc = smblib_masked_write(chg, OTG_FAULT_CONDITION_CFG_REG, mask,
- src ? 0 : mask);
- if (rc < 0)
- smblib_err(chg, "Couldn't write OTG_FAULT_CONDITION_CFG_REG rc=%d\n",
- rc);
- }
- static void typec_sink_insertion(struct smb_charger *chg)
- {
- int rc;
- typec_src_fault_condition_cfg(chg, true);
- rc = smblib_set_charge_param(chg, &chg->param.freq_switcher,
- chg->chg_freq.freq_above_otg_threshold);
- if (rc < 0)
- dev_err(chg->dev, "Error in setting freq_boost rc=%d\n", rc);
- if (chg->use_extcon) {
- smblib_notify_usb_host(chg, true);
- chg->otg_present = true;
- }
- if (!chg->pr_swap_in_progress)
- chg->ok_to_pd = (!(chg->pd_disabled) || chg->early_usb_attach)
- && !chg->pd_not_supported;
- }
- static void typec_src_insertion(struct smb_charger *chg)
- {
- int rc = 0;
- u8 stat;
- if (chg->pr_swap_in_progress) {
- vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0);
- return;
- }
- rc = smblib_read(chg, LEGACY_CABLE_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATE_MACHINE_STATUS_REG rc=%d\n",
- rc);
- return;
- }
- chg->typec_legacy = stat & TYPEC_LEGACY_CABLE_STATUS_BIT;
- chg->ok_to_pd = (!(chg->typec_legacy || chg->pd_disabled)
- || chg->early_usb_attach) && !chg->pd_not_supported;
- /* allow apsd proceed to detect QC2/3 */
- if (!chg->ok_to_pd)
- smblib_hvdcp_detect_try_enable(chg, true);
- }
- static void typec_ra_ra_insertion(struct smb_charger *chg)
- {
- vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, 500000);
- vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
- chg->ok_to_pd = false;
- smblib_hvdcp_detect_enable(chg, true);
- }
- static const char * const dr_mode_text[] = {
- "ufp", "dfp", "none"
- };
- static int smblib_force_dr_mode(struct smb_charger *chg, int mode)
- {
- int rc = 0;
- switch (mode) {
- case TYPEC_PORT_SNK:
- rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG,
- TYPEC_POWER_ROLE_CMD_MASK, EN_SNK_ONLY_BIT);
- if (rc < 0) {
- smblib_err(chg, "Couldn't enable snk, rc=%d\n", rc);
- return rc;
- }
- break;
- case TYPEC_PORT_SRC:
- rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG,
- TYPEC_POWER_ROLE_CMD_MASK, EN_SRC_ONLY_BIT);
- if (rc < 0) {
- smblib_err(chg, "Couldn't enable src, rc=%d\n", rc);
- return rc;
- }
- break;
- case TYPEC_PORT_DRP:
- rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG,
- TYPEC_POWER_ROLE_CMD_MASK, 0);
- if (rc < 0) {
- smblib_err(chg, "Couldn't enable DRP, rc=%d\n", rc);
- return rc;
- }
- break;
- default:
- smblib_err(chg, "Power role %d not supported\n", mode);
- return -EINVAL;
- }
- chg->dr_mode = mode;
- return rc;
- }
- int smblib_typec_port_type_set(const struct typec_capability *cap,
- enum typec_port_type type)
- {
- struct smb_charger *chg = container_of(cap,
- struct smb_charger, typec_caps);
- int rc = 0;
- mutex_lock(&chg->typec_lock);
- if ((chg->pr_swap_in_progress) || (type == TYPEC_PORT_DRP)) {
- smblib_dbg(chg, PR_MISC, "Ignoring port type request type = %d swap_in_progress = %d\n",
- type, chg->pr_swap_in_progress);
- goto unlock;
- }
- chg->pr_swap_in_progress = true;
- rc = smblib_force_dr_mode(chg, type);
- if (rc < 0) {
- chg->pr_swap_in_progress = false;
- smblib_err(chg, "Failed to force mode, rc=%d\n", rc);
- goto unlock;
- }
- smblib_dbg(chg, PR_MISC, "Requested role %s\n",
- type ? "SINK" : "SOURCE");
- /*
- * As per the hardware requirements,
- * schedule the work with required delay.
- */
- if (!(delayed_work_pending(&chg->role_reversal_check))) {
- cancel_delayed_work_sync(&chg->role_reversal_check);
- schedule_delayed_work(&chg->role_reversal_check,
- msecs_to_jiffies(ROLE_REVERSAL_DELAY_MS));
- vote(chg->awake_votable, TYPEC_SWAP_VOTER, true, 0);
- }
- unlock:
- mutex_unlock(&chg->typec_lock);
- return rc;
- }
- static void smblib_typec_role_check_work(struct work_struct *work)
- {
- struct smb_charger *chg = container_of(work, struct smb_charger,
- role_reversal_check.work);
- int rc = 0;
- mutex_lock(&chg->typec_lock);
- switch (chg->dr_mode) {
- case TYPEC_PORT_SNK:
- if (chg->typec_mode < QTI_POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) {
- smblib_dbg(chg, PR_MISC, "Role reversal not latched to UFP in %d msecs. Resetting to DRP mode\n",
- ROLE_REVERSAL_DELAY_MS);
- rc = smblib_force_dr_mode(chg, TYPEC_PORT_DRP);
- if (rc < 0)
- smblib_err(chg, "Failed to set DRP mode, rc=%d\n",
- rc);
- } else {
- chg->power_role = QTI_POWER_SUPPLY_TYPEC_PR_SINK;
- typec_set_pwr_role(chg->typec_port, TYPEC_SINK);
- typec_set_data_role(chg->typec_port, TYPEC_DEVICE);
- smblib_dbg(chg, PR_MISC, "Role changed successfully to SINK");
- }
- break;
- case TYPEC_PORT_SRC:
- if (chg->typec_mode >= QTI_POWER_SUPPLY_TYPEC_SOURCE_DEFAULT
- || chg->typec_mode == QTI_POWER_SUPPLY_TYPEC_NONE) {
- smblib_dbg(chg, PR_MISC, "Role reversal not latched to DFP in %d msecs. Resetting to DRP mode\n",
- ROLE_REVERSAL_DELAY_MS);
- chg->pr_swap_in_progress = false;
- chg->typec_role_swap_failed = true;
- rc = smblib_force_dr_mode(chg, TYPEC_PORT_DRP);
- if (rc < 0)
- smblib_err(chg, "Failed to set DRP mode, rc=%d\n",
- rc);
- } else {
- chg->power_role = QTI_POWER_SUPPLY_TYPEC_PR_SOURCE;
- typec_set_pwr_role(chg->typec_port, TYPEC_SOURCE);
- typec_set_data_role(chg->typec_port, TYPEC_HOST);
- smblib_dbg(chg, PR_MISC, "Role changed successfully to SOURCE");
- }
- break;
- default:
- pr_debug("Already in DRP mode\n");
- break;
- }
- chg->pr_swap_in_progress = false;
- vote(chg->awake_votable, TYPEC_SWAP_VOTER, false, 0);
- mutex_unlock(&chg->typec_lock);
- }
- static void typec_sink_removal(struct smb_charger *chg)
- {
- int rc;
- typec_src_fault_condition_cfg(chg, false);
- rc = smblib_set_charge_param(chg, &chg->param.freq_switcher,
- chg->chg_freq.freq_removal);
- if (rc < 0)
- dev_err(chg->dev, "Error in setting freq_removal rc=%d\n", rc);
- if (chg->use_extcon) {
- if (chg->otg_present)
- smblib_notify_usb_host(chg, false);
- chg->otg_present = false;
- }
- }
- static void typec_src_removal(struct smb_charger *chg)
- {
- int rc;
- struct smb_irq_data *data;
- struct storm_watch *wdata;
- int sec_charger;
- u8 val[2] = {0};
- sec_charger = chg->sec_pl_present ? QTI_POWER_SUPPLY_CHARGER_SEC_PL :
- QTI_POWER_SUPPLY_CHARGER_SEC_NONE;
- rc = smblib_select_sec_charger(chg, sec_charger,
- QTI_POWER_SUPPLY_CP_NONE, false);
- if (rc < 0)
- dev_err(chg->dev,
- "Couldn't disable secondary charger rc=%d\n", rc);
- chg->qc3p5_detected = false;
- chg->qc3p5_detected_mw = 0;
- typec_src_fault_condition_cfg(chg, false);
- smblib_hvdcp_detect_try_enable(chg, false);
- smblib_update_usb_type(chg);
- if (chg->wa_flags & BOOST_BACK_WA) {
- data = chg->irq_info[SWITCHER_POWER_OK_IRQ].irq_data;
- if (data) {
- wdata = &data->storm_data;
- update_storm_count(wdata, WEAK_CHG_STORM_COUNT);
- vote(chg->usb_icl_votable, BOOST_BACK_VOTER, false, 0);
- vote(chg->usb_icl_votable, WEAK_CHARGER_VOTER,
- false, 0);
- }
- }
- cancel_delayed_work_sync(&chg->pl_enable_work);
- /* reset input current limit voters */
- vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true,
- is_flash_active(chg) ? SDP_CURRENT_UA : SDP_100_MA);
- vote(chg->usb_icl_votable, PD_VOTER, false, 0);
- vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
- vote(chg->usb_icl_votable, DCP_VOTER, false, 0);
- vote(chg->usb_icl_votable, SW_QC3_VOTER, false, 0);
- vote(chg->usb_icl_votable, CTM_VOTER, false, 0);
- vote(chg->usb_icl_votable, HVDCP2_ICL_VOTER, false, 0);
- vote(chg->usb_icl_votable, CHG_TERMINATION_VOTER, false, 0);
- vote(chg->usb_icl_votable, THERMAL_THROTTLE_VOTER, false, 0);
- vote(chg->usb_icl_votable, LPD_VOTER, false, 0);
- /* reset usb irq voters */
- vote(chg->limited_irq_disable_votable, CHARGER_TYPE_VOTER,
- true, 0);
- vote(chg->hdc_irq_disable_votable, CHARGER_TYPE_VOTER, true, 0);
- vote(chg->hdc_irq_disable_votable, HDC_IRQ_VOTER, false, 0);
- /* reset parallel voters */
- vote(chg->pl_disable_votable, PL_DELAY_VOTER, true, 0);
- vote(chg->pl_disable_votable, PL_FCC_LOW_VOTER, false, 0);
- vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0);
- vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0);
- vote(chg->awake_votable, PL_DELAY_VOTER, false, 0);
- /* Remove SW thermal regulation WA votes */
- vote(chg->usb_icl_votable, SW_THERM_REGULATION_VOTER, false, 0);
- vote(chg->pl_disable_votable, SW_THERM_REGULATION_VOTER, false, 0);
- vote(chg->dc_suspend_votable, SW_THERM_REGULATION_VOTER, false, 0);
- if (chg->cp_disable_votable)
- vote(chg->cp_disable_votable, SW_THERM_REGULATION_VOTER,
- false, 0);
- /* reset USBOV votes and cancel work */
- cancel_delayed_work_sync(&chg->usbov_dbc_work);
- vote(chg->awake_votable, USBOV_DBC_VOTER, false, 0);
- chg->dbc_usbov = false;
- chg->pulse_cnt = 0;
- chg->usb_icl_delta_ua = 0;
- chg->voltage_min_uv = MICRO_5V;
- chg->voltage_max_uv = MICRO_5V;
- chg->usbin_forced_max_uv = 0;
- chg->chg_param.forced_main_fcc = 0;
- /* Reset all CC mode votes */
- vote(chg->fcc_main_votable, MAIN_FCC_VOTER, false, 0);
- chg->adapter_cc_mode = 0;
- chg->thermal_overheat = 0;
- vote_override(chg->fcc_votable, CC_MODE_VOTER, false, 0);
- vote_override(chg->usb_icl_votable, CC_MODE_VOTER, false, 0);
- vote(chg->cp_disable_votable, OVERHEAT_LIMIT_VOTER, false, 0);
- vote(chg->usb_icl_votable, OVERHEAT_LIMIT_VOTER, false, 0);
- /* write back the default FLOAT charger configuration */
- rc = smblib_masked_write(chg, USBIN_OPTIONS_2_CFG_REG,
- (u8)FLOAT_OPTIONS_MASK, chg->float_cfg);
- if (rc < 0)
- smblib_err(chg, "Couldn't write float charger options rc=%d\n",
- rc);
- if (chg->sdam_base) {
- rc = smblib_write(chg,
- chg->sdam_base + SDAM_QC_DET_STATUS_REG, 0);
- if (rc < 0)
- pr_err("Couldn't clear SDAM QC status rc=%d\n", rc);
- rc = smblib_batch_write(chg,
- chg->sdam_base + SDAM_QC_ADC_LSB_REG, val, 2);
- if (rc < 0)
- pr_err("Couldn't clear SDAM ADC status rc=%d\n", rc);
- }
- if (!chg->pr_swap_in_progress) {
- rc = smblib_usb_pd_adapter_allowance_override(chg, FORCE_NULL);
- if (rc < 0)
- smblib_err(chg, "Couldn't set FORCE_NULL rc=%d\n", rc);
- rc = smblib_set_charge_param(chg,
- &chg->param.aicl_cont_threshold,
- chg->default_aicl_cont_threshold_mv);
- if (rc < 0)
- smblib_err(chg, "Couldn't restore aicl_cont_threshold, rc=%d",
- rc);
- }
- /*
- * if non-compliant charger caused UV, restore original max pulses
- * and turn SUSPEND_ON_COLLAPSE_USBIN_BIT back on.
- */
- if (chg->qc2_unsupported_voltage) {
- rc = smblib_masked_write(chg, HVDCP_PULSE_COUNT_MAX_REG,
- HVDCP_PULSE_COUNT_MAX_QC2_MASK,
- chg->qc2_max_pulses);
- if (rc < 0)
- smblib_err(chg, "Couldn't restore max pulses rc=%d\n",
- rc);
- if (!chg->disable_suspend_on_collapse) {
- rc = smblib_masked_write(chg, USBIN_AICL_OPTIONS_CFG_REG,
- SUSPEND_ON_COLLAPSE_USBIN_BIT,
- SUSPEND_ON_COLLAPSE_USBIN_BIT);
- if (rc < 0)
- smblib_err(chg,
- "Couldn't turn on SUSPEND_ON_COLLAPSE_USBIN_BIT rc=%d\n",
- rc);
- }
- chg->qc2_unsupported_voltage = QC2_COMPLIANT;
- }
- if (chg->use_extcon)
- smblib_notify_device_mode(chg, false);
- chg->typec_legacy = false;
- del_timer_sync(&chg->apsd_timer);
- chg->apsd_ext_timeout = false;
- }
- static void typec_mode_unattached(struct smb_charger *chg)
- {
- vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, USBIN_100MA);
- }
- static void smblib_handle_rp_change(struct smb_charger *chg, int typec_mode)
- {
- const struct apsd_result *apsd = smblib_get_apsd_result(chg);
- /*
- * We want the ICL vote @ 100mA for a FLOAT charger
- * until the detection by the USB stack is complete.
- * Ignore the Rp changes unless there is a
- * pre-existing valid vote or FLOAT is configured for
- * SDP current.
- */
- if (apsd->val == QTI_POWER_SUPPLY_TYPE_USB_FLOAT) {
- if (get_client_vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER)
- <= USBIN_100MA
- || (chg->float_cfg & FLOAT_OPTIONS_MASK)
- == FORCE_FLOAT_SDP_CFG_BIT)
- return;
- }
- update_sw_icl_max(chg, apsd->val);
- smblib_dbg(chg, PR_MISC, "CC change old_mode=%d new_mode=%d\n",
- chg->typec_mode, typec_mode);
- }
- static void smblib_lpd_launch_ra_open_work(struct smb_charger *chg)
- {
- u8 stat;
- int rc;
- if (chg->lpd_disabled)
- return;
- rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_MISC_STATUS_REG rc=%d\n",
- rc);
- return;
- }
- if (!(stat & TYPEC_TCCDEBOUNCE_DONE_STATUS_BIT)
- && chg->lpd_stage == LPD_STAGE_NONE) {
- chg->lpd_stage = LPD_STAGE_FLOAT;
- cancel_delayed_work_sync(&chg->lpd_ra_open_work);
- vote(chg->awake_votable, LPD_VOTER, true, 0);
- schedule_delayed_work(&chg->lpd_ra_open_work,
- msecs_to_jiffies(300));
- }
- }
- irqreturn_t smb5_typec_or_rid_detection_change_irq_handler(int irq, void *data)
- {
- struct smb_irq_data *irq_data = data;
- struct smb_charger *chg = irq_data->parent_data;
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
- if (chg->connector_type == QTI_POWER_SUPPLY_CONNECTOR_MICRO_USB) {
- if (chg->uusb_moisture_protection_enabled) {
- /*
- * Adding pm_stay_awake as because pm_relax is called
- * on exit path from the work routine.
- */
- pm_stay_awake(chg->dev);
- schedule_work(&chg->moisture_protection_work);
- }
- cancel_delayed_work_sync(&chg->uusb_otg_work);
- /*
- * Skip OTG enablement if RID interrupt triggers with moisture
- * protection still enabled.
- */
- if (!chg->moisture_present) {
- vote(chg->awake_votable, OTG_DELAY_VOTER, true, 0);
- smblib_dbg(chg, PR_INTERRUPT, "Scheduling OTG work\n");
- schedule_delayed_work(&chg->uusb_otg_work,
- msecs_to_jiffies(chg->otg_delay_ms));
- }
- goto out;
- }
- if (chg->pr_swap_in_progress || chg->pd_hard_reset)
- goto out;
- smblib_lpd_launch_ra_open_work(chg);
- if (chg->usb_psy)
- power_supply_changed(chg->usb_psy);
- out:
- return IRQ_HANDLED;
- }
- irqreturn_t smb5_typec_state_change_irq_handler(int irq, void *data)
- {
- struct smb_irq_data *irq_data = data;
- struct smb_charger *chg = irq_data->parent_data;
- int typec_mode;
- if (chg->connector_type == QTI_POWER_SUPPLY_CONNECTOR_MICRO_USB) {
- smblib_dbg(chg, PR_INTERRUPT,
- "Ignoring for micro USB\n");
- return IRQ_HANDLED;
- }
- typec_mode = smblib_get_prop_typec_mode(chg);
- if (chg->sink_src_mode != UNATTACHED_MODE
- && (typec_mode != chg->typec_mode))
- smblib_handle_rp_change(chg, typec_mode);
- chg->typec_mode = typec_mode;
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: cc-state-change; Type-C %s detected\n",
- smblib_typec_mode_name[chg->typec_mode]);
- power_supply_changed(chg->usb_psy);
- return IRQ_HANDLED;
- }
- static void smblib_lpd_clear_ra_open_work(struct smb_charger *chg)
- {
- if (chg->lpd_disabled)
- return;
- cancel_delayed_work_sync(&chg->lpd_detach_work);
- chg->lpd_stage = LPD_STAGE_FLOAT_CANCEL;
- cancel_delayed_work_sync(&chg->lpd_ra_open_work);
- vote(chg->awake_votable, LPD_VOTER, false, 0);
- }
- #define TYPEC_DETACH_DETECT_DELAY_MS 2000
- irqreturn_t smb5_typec_attach_detach_irq_handler(int irq, void *data)
- {
- struct smb_irq_data *irq_data = data;
- struct smb_charger *chg = irq_data->parent_data;
- u8 stat;
- bool attached = false;
- int rc;
- /* IRQ not expected to be executed for uUSB, return */
- if (chg->connector_type == QTI_POWER_SUPPLY_CONNECTOR_MICRO_USB)
- return IRQ_HANDLED;
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
- rc = smblib_read(chg, TYPE_C_STATE_MACHINE_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATE_MACHINE_STATUS_REG rc=%d\n",
- rc);
- return IRQ_HANDLED;
- }
- attached = !!(stat & TYPEC_ATTACH_DETACH_STATE_BIT);
- if (attached) {
- smblib_lpd_clear_ra_open_work(chg);
- rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_MISC_STATUS_REG rc=%d\n",
- rc);
- return IRQ_HANDLED;
- }
- if (smblib_get_prop_dfp_mode(chg) ==
- QTI_POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER) {
- chg->sink_src_mode = AUDIO_ACCESS_MODE;
- typec_ra_ra_insertion(chg);
- } else if (stat & SNK_SRC_MODE_BIT) {
- if (smblib_src_lpd(chg))
- return IRQ_HANDLED;
- chg->sink_src_mode = SRC_MODE;
- typec_sink_insertion(chg);
- } else {
- chg->sink_src_mode = SINK_MODE;
- typec_src_insertion(chg);
- }
- rc = typec_partner_register(chg);
- if (rc < 0)
- smblib_err(chg, "failed to register partner rc =%d\n",
- rc);
- } else {
- switch (chg->sink_src_mode) {
- case SRC_MODE:
- typec_sink_removal(chg);
- break;
- case SINK_MODE:
- case AUDIO_ACCESS_MODE:
- typec_src_removal(chg);
- break;
- case UNATTACHED_MODE:
- default:
- typec_mode_unattached(chg);
- break;
- }
- if (!chg->pr_swap_in_progress) {
- chg->ok_to_pd = false;
- chg->sink_src_mode = UNATTACHED_MODE;
- chg->early_usb_attach = false;
- smblib_apsd_enable(chg, true);
- }
- /*
- * Restore DRP mode on type-C cable disconnect if role
- * swap is not in progress, to ensure forced sink or src
- * mode configuration is reset properly.
- */
- mutex_lock(&chg->typec_lock);
- if (chg->typec_port && !chg->pr_swap_in_progress) {
- /*
- * Schedule the work to differentiate actual removal
- * of cable and detach interrupt during role swap,
- * unregister the partner only during actual cable
- * removal.
- */
- cancel_delayed_work(&chg->pr_swap_detach_work);
- vote(chg->awake_votable, DETACH_DETECT_VOTER, true, 0);
- schedule_delayed_work(&chg->pr_swap_detach_work,
- msecs_to_jiffies(TYPEC_DETACH_DETECT_DELAY_MS));
- smblib_force_dr_mode(chg, TYPEC_PORT_DRP);
- /*
- * To handle cable removal during role
- * swap failure.
- */
- chg->typec_role_swap_failed = false;
- }
- mutex_unlock(&chg->typec_lock);
- if (chg->lpd_stage == LPD_STAGE_FLOAT_CANCEL)
- schedule_delayed_work(&chg->lpd_detach_work,
- msecs_to_jiffies(1000));
- }
- rc = smblib_masked_write(chg, USB_CMD_PULLDOWN_REG,
- EN_PULLDOWN_USB_IN_BIT,
- attached ? 0 : EN_PULLDOWN_USB_IN_BIT);
- if (rc < 0)
- smblib_err(chg, "Couldn't configure pulldown on USB_IN rc=%d\n",
- rc);
- power_supply_changed(chg->usb_psy);
- return IRQ_HANDLED;
- }
- static void dcin_aicl(struct smb_charger *chg)
- {
- int rc, icl, icl_save;
- int input_present;
- bool aicl_done = true;
- /*
- * Hold awake votable to prevent pm_relax being called prior to
- * completion of this work.
- */
- vote(chg->awake_votable, DCIN_AICL_VOTER, true, 0);
- increment:
- mutex_lock(&chg->dcin_aicl_lock);
- rc = smblib_get_charge_param(chg, &chg->param.dc_icl, &icl);
- if (rc < 0)
- goto err;
- if (icl == chg->wls_icl_ua) {
- /* Upper limit reached; do nothing */
- smblib_dbg(chg, PR_WLS, "hit max ICL: stop\n");
- rc = smblib_is_input_present(chg, &input_present);
- if (rc < 0 || !(input_present & INPUT_PRESENT_DC))
- aicl_done = false;
- goto unlock;
- }
- icl = min(chg->wls_icl_ua, icl + DCIN_ICL_STEP_UA);
- icl_save = icl;
- rc = smblib_set_charge_param(chg, &chg->param.dc_icl, icl);
- if (rc < 0)
- goto err;
- mutex_unlock(&chg->dcin_aicl_lock);
- smblib_dbg(chg, PR_WLS, "icl: %d mA\n", (icl / 1000));
- /* Check to see if DC is still present before and after sleep */
- rc = smblib_is_input_present(chg, &input_present);
- if (rc < 0 || !(input_present & INPUT_PRESENT_DC)) {
- aicl_done = false;
- goto unvote;
- }
- /*
- * Wait awhile to check for any DCIN_UVs (the UV handler reduces the
- * ICL). If the adaptor collapses, the ICL read after waking up will be
- * lesser, indicating that the AICL process is complete.
- */
- msleep(500);
- rc = smblib_is_input_present(chg, &input_present);
- if (rc < 0 || !(input_present & INPUT_PRESENT_DC)) {
- aicl_done = false;
- goto unvote;
- }
- mutex_lock(&chg->dcin_aicl_lock);
- rc = smblib_get_charge_param(chg, &chg->param.dc_icl, &icl);
- if (rc < 0)
- goto err;
- if (icl < icl_save) {
- smblib_dbg(chg, PR_WLS, "done: icl: %d mA\n", (icl / 1000));
- goto unlock;
- }
- mutex_unlock(&chg->dcin_aicl_lock);
- goto increment;
- err:
- aicl_done = false;
- unlock:
- mutex_unlock(&chg->dcin_aicl_lock);
- unvote:
- vote(chg->awake_votable, DCIN_AICL_VOTER, false, 0);
- chg->dcin_aicl_done = aicl_done;
- }
- static void dcin_aicl_work(struct work_struct *work)
- {
- struct smb_charger *chg = container_of(work, struct smb_charger,
- dcin_aicl_work);
- dcin_aicl(chg);
- }
- static enum alarmtimer_restart dcin_aicl_alarm_cb(struct alarm *alarm,
- ktime_t now)
- {
- struct smb_charger *chg = container_of(alarm, struct smb_charger,
- dcin_aicl_alarm);
- smblib_dbg(chg, PR_WLS, "rerunning DCIN AICL\n");
- pm_stay_awake(chg->dev);
- schedule_work(&chg->dcin_aicl_work);
- return ALARMTIMER_NORESTART;
- }
- static void dcin_icl_decrement(struct smb_charger *chg)
- {
- int rc, icl;
- ktime_t now = ktime_get();
- rc = smblib_get_charge_param(chg, &chg->param.dc_icl, &icl);
- if (rc < 0) {
- smblib_err(chg, "reading DCIN ICL failed: %d\n", rc);
- return;
- }
- if (icl == DCIN_ICL_MIN_UA) {
- /* Cannot possibly decrease ICL any further - do nothing */
- smblib_dbg(chg, PR_WLS, "hit min ICL: stop\n");
- return;
- }
- /* Reduce ICL by 100 mA if 3 UVs happen in a row */
- if (ktime_us_delta(now, chg->dcin_uv_last_time) > (200 * 1000)) {
- chg->dcin_uv_count = 0;
- } else if (chg->dcin_uv_count >= 3) {
- icl -= DCIN_ICL_STEP_UA;
- smblib_dbg(chg, PR_WLS, "icl: %d mA\n", (icl / 1000));
- rc = smblib_set_charge_param(chg, &chg->param.dc_icl, icl);
- if (rc < 0) {
- smblib_err(chg, "setting DCIN ICL failed: %d\n", rc);
- return;
- }
- chg->dcin_uv_count = 0;
- }
- chg->dcin_uv_last_time = now;
- }
- irqreturn_t smb5_dcin_uv_irq_handler(int irq, void *data)
- {
- struct smb_irq_data *irq_data = data;
- struct smb_charger *chg = irq_data->parent_data;
- mutex_lock(&chg->dcin_aicl_lock);
- chg->dcin_uv_count++;
- smblib_dbg(chg, (PR_WLS | PR_INTERRUPT), "DCIN UV count: %d\n",
- chg->dcin_uv_count);
- dcin_icl_decrement(chg);
- mutex_unlock(&chg->dcin_aicl_lock);
- return IRQ_HANDLED;
- }
- irqreturn_t smb5_dc_plugin_irq_handler(int irq, void *data)
- {
- struct smb_irq_data *irq_data = data;
- struct smb_charger *chg = irq_data->parent_data;
- union power_supply_propval pval;
- int input_present;
- bool dcin_present, vbus_present;
- int rc, wireless_vout = 0, wls_set = 0;
- int sec_charger, val;
- rc = smblib_get_prop_vph_voltage_now(chg, &val);
- if (rc < 0)
- return IRQ_HANDLED;
- /* 2*VPH, with a granularity of 100mV */
- wireless_vout = ((val * 2) / 100000) * 100000;
- rc = smblib_is_input_present(chg, &input_present);
- if (rc < 0)
- return IRQ_HANDLED;
- dcin_present = input_present & INPUT_PRESENT_DC;
- vbus_present = input_present & INPUT_PRESENT_USB;
- if (!chg->cp_ilim_votable)
- chg->cp_ilim_votable = find_votable("CP_ILIM");
- if (dcin_present && !vbus_present) {
- cancel_work_sync(&chg->dcin_aicl_work);
- /* Reset DCIN ICL to 100 mA */
- mutex_lock(&chg->dcin_aicl_lock);
- rc = smblib_set_charge_param(chg, &chg->param.dc_icl,
- DCIN_ICL_MIN_UA);
- mutex_unlock(&chg->dcin_aicl_lock);
- if (rc < 0)
- return IRQ_HANDLED;
- smblib_dbg(chg, (PR_WLS | PR_INTERRUPT), "reset: icl: 100 mA\n");
- /*
- * Remove USB's CP ILIM vote - inapplicable for wireless
- * parallel charging.
- */
- if (chg->cp_ilim_votable)
- vote(chg->cp_ilim_votable, ICL_CHANGE_VOTER, false, 0);
- if (chg->sec_cp_present) {
- /*
- * If CP output topology is VBATT, limit main charger's
- * FCC share and let the CPs handle the rest.
- */
- if (is_cp_topo_vbatt(chg))
- vote(chg->fcc_main_votable,
- WLS_PL_CHARGING_VOTER, true, 800000);
- rc = smblib_get_prop_batt_status(chg, &pval);
- if (rc < 0)
- smblib_err(chg, "Couldn't read batt status rc=%d\n",
- rc);
- wls_set = (pval.intval == POWER_SUPPLY_STATUS_FULL) ?
- MICRO_5V : wireless_vout;
- pval.intval = wls_set;
- rc = smblib_set_prop_voltage_wls_output(chg, &pval);
- if (rc < 0)
- dev_err(chg->dev, "Couldn't set dc voltage to 2*vph rc=%d\n",
- rc);
- rc = smblib_select_sec_charger(chg,
- QTI_POWER_SUPPLY_CHARGER_SEC_CP,
- QTI_POWER_SUPPLY_CP_WIRELESS, false);
- if (rc < 0)
- dev_err(chg->dev, "Couldn't enable secondary chargers rc=%d\n",
- rc);
- } else {
- /*
- * If no secondary charger is present, commence
- * wireless charging at 5 V by default.
- */
- pval.intval = 5000000;
- rc = smblib_set_prop_voltage_wls_output(chg, &pval);
- if (rc < 0)
- dev_err(chg->dev, "Couldn't set dc voltage to 5 V rc=%d\n",
- rc);
- }
- schedule_work(&chg->dcin_aicl_work);
- } else {
- if (chg->cp_reason == QTI_POWER_SUPPLY_CP_WIRELESS) {
- sec_charger = chg->sec_pl_present ?
- QTI_POWER_SUPPLY_CHARGER_SEC_PL :
- QTI_POWER_SUPPLY_CHARGER_SEC_NONE;
- rc = smblib_select_sec_charger(chg, sec_charger,
- QTI_POWER_SUPPLY_CP_NONE, false);
- if (rc < 0)
- dev_err(chg->dev, "Couldn't disable secondary charger rc=%d\n",
- rc);
- }
- vote(chg->dc_suspend_votable, CHG_TERMINATION_VOTER, false, 0);
- vote(chg->fcc_main_votable, WLS_PL_CHARGING_VOTER, false, 0);
- chg->last_wls_vout = 0;
- chg->dcin_aicl_done = false;
- chg->dcin_icl_user_set = false;
- }
- /*
- * Vote for 1500mA FCC upon WLS detach and remove vote upon attach if
- * FCC stepper is enabled.
- */
- if (chg->fcc_stepper_enable && !vbus_present)
- vote(chg->fcc_votable, FCC_STEPPER_VOTER, !dcin_present,
- dcin_present ? 0 : 1500000);
- if (chg->dc_psy)
- power_supply_changed(chg->dc_psy);
- smblib_dbg(chg, (PR_WLS | PR_INTERRUPT), "dcin_present= %d, usbin_present= %d, cp_reason = %d\n",
- dcin_present, vbus_present, chg->cp_reason);
- return IRQ_HANDLED;
- }
- irqreturn_t smb5_high_duty_cycle_irq_handler(int irq, void *data)
- {
- struct smb_irq_data *irq_data = data;
- struct smb_charger *chg = irq_data->parent_data;
- chg->is_hdc = true;
- /*
- * Disable usb IRQs after the flag set and re-enable IRQs after
- * the flag cleared in the delayed work queue, to avoid any IRQ
- * storming during the delays
- */
- vote(chg->hdc_irq_disable_votable, HDC_IRQ_VOTER, true, 0);
- schedule_delayed_work(&chg->clear_hdc_work, msecs_to_jiffies(60));
- return IRQ_HANDLED;
- }
- static void smblib_bb_removal_work(struct work_struct *work)
- {
- struct smb_charger *chg = container_of(work, struct smb_charger,
- bb_removal_work.work);
- vote(chg->usb_icl_votable, BOOST_BACK_VOTER, false, 0);
- vote(chg->awake_votable, BOOST_BACK_VOTER, false, 0);
- }
- #define BOOST_BACK_UNVOTE_DELAY_MS 750
- #define BOOST_BACK_STORM_COUNT 3
- #define WEAK_CHG_STORM_COUNT 8
- irqreturn_t smb5_switcher_power_ok_irq_handler(int irq, void *data)
- {
- struct smb_irq_data *irq_data = data;
- struct smb_charger *chg = irq_data->parent_data;
- struct storm_watch *wdata = &irq_data->storm_data;
- int rc, usb_icl;
- u8 stat;
- if (!(chg->wa_flags & BOOST_BACK_WA))
- return IRQ_HANDLED;
- rc = smblib_read(chg, POWER_PATH_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read POWER_PATH_STATUS rc=%d\n", rc);
- return IRQ_HANDLED;
- }
- /* skip suspending input if its already suspended by some other voter */
- usb_icl = get_effective_result(chg->usb_icl_votable);
- if ((stat & USE_USBIN_BIT) && usb_icl >= 0 && usb_icl <= USBIN_25MA)
- return IRQ_HANDLED;
- if (stat & USE_DCIN_BIT)
- return IRQ_HANDLED;
- if (is_storming(&irq_data->storm_data)) {
- /* This could be a weak charger reduce ICL */
- if (!is_client_vote_enabled(chg->usb_icl_votable,
- WEAK_CHARGER_VOTER)) {
- smblib_err(chg,
- "Weak charger detected: voting %dmA ICL\n",
- chg->weak_chg_icl_ua / 1000);
- vote(chg->usb_icl_votable, WEAK_CHARGER_VOTER,
- true, chg->weak_chg_icl_ua);
- /*
- * reset storm data and set the storm threshold
- * to 3 for reverse boost detection.
- */
- update_storm_count(wdata, BOOST_BACK_STORM_COUNT);
- } else {
- smblib_err(chg,
- "Reverse boost detected: voting 0mA to suspend input\n");
- vote(chg->usb_icl_votable, BOOST_BACK_VOTER, true, 0);
- vote(chg->awake_votable, BOOST_BACK_VOTER, true, 0);
- /*
- * Remove the boost-back vote after a delay, to avoid
- * permanently suspending the input if the boost-back
- * condition is unintentionally hit.
- */
- schedule_delayed_work(&chg->bb_removal_work,
- msecs_to_jiffies(BOOST_BACK_UNVOTE_DELAY_MS));
- }
- }
- return IRQ_HANDLED;
- }
- irqreturn_t smb5_wdog_snarl_irq_handler(int irq, void *data)
- {
- struct smb_irq_data *irq_data = data;
- struct smb_charger *chg = irq_data->parent_data;
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
- if (chg->wa_flags & SW_THERM_REGULATION_WA) {
- cancel_delayed_work_sync(&chg->thermal_regulation_work);
- vote(chg->awake_votable, SW_THERM_REGULATION_VOTER, true, 0);
- schedule_delayed_work(&chg->thermal_regulation_work, 0);
- }
- power_supply_changed(chg->batt_psy);
- return IRQ_HANDLED;
- }
- irqreturn_t smb5_wdog_bark_irq_handler(int irq, void *data)
- {
- struct smb_irq_data *irq_data = data;
- struct smb_charger *chg = irq_data->parent_data;
- int rc;
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
- rc = smblib_write(chg, BARK_BITE_WDOG_PET_REG, BARK_BITE_WDOG_PET_BIT);
- if (rc < 0)
- smblib_err(chg, "Couldn't pet the dog rc=%d\n", rc);
- return IRQ_HANDLED;
- }
- static void smblib_die_rst_icl_regulate(struct smb_charger *chg)
- {
- int rc;
- u8 temp;
- rc = smblib_read(chg, DIE_TEMP_STATUS_REG, &temp);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read DIE_TEMP_STATUS_REG rc=%d\n",
- rc);
- return;
- }
- /* Regulate ICL on die temp crossing DIE_RST threshold */
- vote(chg->usb_icl_votable, DIE_TEMP_VOTER,
- temp & DIE_TEMP_RST_BIT, 500000);
- }
- /*
- * triggered when DIE or SKIN or CONNECTOR temperature across
- * either of the _REG_L, _REG_H, _RST, or _SHDN thresholds
- */
- irqreturn_t smb5_temp_change_irq_handler(int irq, void *data)
- {
- struct smb_irq_data *irq_data = data;
- struct smb_charger *chg = irq_data->parent_data;
- smblib_die_rst_icl_regulate(chg);
- return IRQ_HANDLED;
- }
- static void smblib_usbov_dbc_work(struct work_struct *work)
- {
- struct smb_charger *chg = container_of(work, struct smb_charger,
- usbov_dbc_work.work);
- smblib_dbg(chg, PR_MISC, "Resetting USBOV debounce\n");
- chg->dbc_usbov = false;
- vote(chg->awake_votable, USBOV_DBC_VOTER, false, 0);
- }
- #define USB_OV_DBC_PERIOD_MS 1000
- irqreturn_t smb5_usbin_ov_irq_handler(int irq, void *data)
- {
- struct smb_irq_data *irq_data = data;
- struct smb_charger *chg = irq_data->parent_data;
- u8 stat;
- int rc;
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
- if (!(chg->wa_flags & USBIN_OV_WA))
- return IRQ_HANDLED;
- rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read USB_INT_RT_STS rc=%d\n", rc);
- return IRQ_HANDLED;
- }
- /*
- * On specific PMICs, OV IRQ triggers for very small duration in
- * interim periods affecting charging status reflection. In order to
- * differentiate between OV IRQ glitch and real OV_IRQ, add a debounce
- * period for evaluation.
- */
- if (stat & USBIN_OV_RT_STS_BIT) {
- chg->dbc_usbov = true;
- vote(chg->awake_votable, USBOV_DBC_VOTER, true, 0);
- schedule_delayed_work(&chg->usbov_dbc_work,
- msecs_to_jiffies(USB_OV_DBC_PERIOD_MS));
- } else {
- cancel_delayed_work_sync(&chg->usbov_dbc_work);
- chg->dbc_usbov = false;
- vote(chg->awake_votable, USBOV_DBC_VOTER, false, 0);
- }
- smblib_dbg(chg, PR_MISC, "USBOV debounce status %d\n",
- chg->dbc_usbov);
- return IRQ_HANDLED;
- }
- /**************
- * Additional USB PSY getters/setters
- * that call interrupt functions
- ***************/
- int smblib_get_prop_pr_swap_in_progress(struct smb_charger *chg,
- int *val)
- {
- *val = chg->pr_swap_in_progress;
- return 0;
- }
- #define DETACH_DETECT_DELAY_MS 20
- int smblib_set_prop_pr_swap_in_progress(struct smb_charger *chg,
- int val)
- {
- int rc;
- u8 stat = 0, orientation;
- smblib_dbg(chg, PR_MISC, "Requested PR_SWAP %d\n", val);
- chg->pr_swap_in_progress = val;
- /* check for cable removal during pr_swap */
- if (!chg->pr_swap_in_progress) {
- cancel_delayed_work_sync(&chg->pr_swap_detach_work);
- vote(chg->awake_votable, DETACH_DETECT_VOTER, true, 0);
- schedule_delayed_work(&chg->pr_swap_detach_work,
- msecs_to_jiffies(DETACH_DETECT_DELAY_MS));
- }
- rc = smblib_masked_write(chg, TYPE_C_DEBOUNCE_OPTION_REG,
- REDUCE_TCCDEBOUNCE_TO_2MS_BIT,
- val ? REDUCE_TCCDEBOUNCE_TO_2MS_BIT : 0);
- if (rc < 0)
- smblib_err(chg, "Couldn't set tCC debounce rc=%d\n", rc);
- rc = smblib_masked_write(chg, TYPE_C_EXIT_STATE_CFG_REG,
- BYPASS_VSAFE0V_DURING_ROLE_SWAP_BIT,
- val ? BYPASS_VSAFE0V_DURING_ROLE_SWAP_BIT : 0);
- if (rc < 0)
- smblib_err(chg, "Couldn't set exit state cfg rc=%d\n", rc);
- if (chg->pr_swap_in_progress) {
- rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n",
- rc);
- }
- orientation =
- stat & CC_ORIENTATION_BIT ? TYPEC_CCOUT_VALUE_BIT : 0;
- rc = smblib_masked_write(chg, TYPE_C_CCOUT_CONTROL_REG,
- TYPEC_CCOUT_SRC_BIT | TYPEC_CCOUT_BUFFER_EN_BIT
- | TYPEC_CCOUT_VALUE_BIT,
- TYPEC_CCOUT_SRC_BIT | TYPEC_CCOUT_BUFFER_EN_BIT
- | orientation);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_CCOUT_CONTROL_REG rc=%d\n",
- rc);
- }
- } else {
- rc = smblib_masked_write(chg, TYPE_C_CCOUT_CONTROL_REG,
- TYPEC_CCOUT_SRC_BIT, 0);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_CCOUT_CONTROL_REG rc=%d\n",
- rc);
- return rc;
- }
- /* enable DRP */
- rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG,
- TYPEC_POWER_ROLE_CMD_MASK, 0);
- if (rc < 0) {
- smblib_err(chg, "Couldn't enable DRP rc=%d\n", rc);
- return rc;
- }
- chg->power_role = QTI_POWER_SUPPLY_TYPEC_PR_DUAL;
- smblib_dbg(chg, PR_MISC, "restore power role: %d\n",
- chg->power_role);
- }
- return 0;
- }
- /***************
- * Work Queues *
- ***************/
- static void smblib_pr_lock_clear_work(struct work_struct *work)
- {
- struct smb_charger *chg = container_of(work, struct smb_charger,
- pr_lock_clear_work.work);
- spin_lock(&chg->typec_pr_lock);
- if (chg->pr_lock_in_progress) {
- smblib_dbg(chg, PR_MISC, "restore type-c interrupts\n");
- smblib_typec_irq_config(chg, true);
- chg->pr_lock_in_progress = false;
- }
- spin_unlock(&chg->typec_pr_lock);
- }
- static void smblib_pr_swap_detach_work(struct work_struct *work)
- {
- struct smb_charger *chg = container_of(work, struct smb_charger,
- pr_swap_detach_work.work);
- int rc;
- u8 stat;
- rc = smblib_read(chg, TYPE_C_STATE_MACHINE_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read STATE_MACHINE_STS rc=%d\n", rc);
- goto out;
- }
- smblib_dbg(chg, PR_REGISTER, "STATE_MACHINE_STS %x\n", stat);
- if (!(stat & TYPEC_ATTACH_DETACH_STATE_BIT)) {
- rc = smblib_request_dpdm(chg, false);
- if (rc < 0)
- smblib_err(chg, "Couldn't disable DPDM rc=%d\n", rc);
- if (chg->typec_port)
- typec_partner_unregister(chg);
- }
- out:
- vote(chg->awake_votable, DETACH_DETECT_VOTER, false, 0);
- }
- static void smblib_uusb_otg_work(struct work_struct *work)
- {
- struct smb_charger *chg = container_of(work, struct smb_charger,
- uusb_otg_work.work);
- int rc;
- u8 stat;
- bool otg;
- rc = smblib_read(chg, TYPEC_U_USB_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_STATUS_3 rc=%d\n", rc);
- goto out;
- }
- otg = !!(stat & U_USB_GROUND_NOVBUS_BIT);
- if (chg->otg_present != otg) {
- if (otg) {
- /* otg cable inserted */
- if (chg->typec_port) {
- typec_partner_register(chg);
- typec_set_data_role(chg->typec_port,
- TYPEC_HOST);
- typec_set_pwr_role(chg->typec_port,
- TYPEC_SOURCE);
- }
- } else if (chg->typec_port) {
- /* otg cable removed */
- typec_set_data_role(chg->typec_port, TYPEC_DEVICE);
- typec_set_pwr_role(chg->typec_port, TYPEC_SINK);
- typec_partner_unregister(chg);
- }
- smblib_notify_usb_host(chg, otg);
- } else {
- goto out;
- }
- chg->otg_present = otg;
- if (!otg)
- chg->boost_current_ua = 0;
- rc = smblib_set_charge_param(chg, &chg->param.freq_switcher,
- otg ? chg->chg_freq.freq_below_otg_threshold
- : chg->chg_freq.freq_removal);
- if (rc < 0)
- dev_err(chg->dev, "Error in setting freq_boost rc=%d\n", rc);
- smblib_dbg(chg, PR_REGISTER, "TYPE_C_U_USB_STATUS = 0x%02x OTG=%d\n",
- stat, otg);
- power_supply_changed(chg->usb_psy);
- out:
- vote(chg->awake_votable, OTG_DELAY_VOTER, false, 0);
- }
- static void bms_update_work(struct work_struct *work)
- {
- struct smb_charger *chg = container_of(work, struct smb_charger,
- bms_update_work);
- struct iio_channel **qg_list;
- int rc;
- if (IS_ERR(chg->iio_chan_list_qg))
- return;
- if (!chg->iio_chan_list_qg) {
- qg_list = get_ext_channels(chg->dev,
- smblib_qg_ext_iio_chan,
- ARRAY_SIZE(smblib_qg_ext_iio_chan));
- if (IS_ERR(qg_list)) {
- rc = PTR_ERR(qg_list);
- if (rc != -EPROBE_DEFER) {
- dev_err(chg->dev, "Failed to get channels, %d\n",
- rc);
- chg->iio_chan_list_qg = ERR_PTR(-EINVAL);
- }
- return;
- }
- chg->iio_chan_list_qg = qg_list;
- }
- smblib_config_charger_on_debug_battery(chg);
- if (chg->batt_psy)
- power_supply_changed(chg->batt_psy);
- }
- static void pl_update_work(struct work_struct *work)
- {
- int val, rc;
- struct smb_charger *chg = container_of(work, struct smb_charger,
- pl_update_work);
- struct iio_channel **iio_list;
- iio_list = get_ext_channels(chg->dev,
- smblib_parallel_ext_iio_chan,
- ARRAY_SIZE(smblib_parallel_ext_iio_chan));
- if (IS_ERR(iio_list)) {
- rc = PTR_ERR(iio_list);
- if (rc != -EPROBE_DEFER) {
- dev_err(chg->dev, "Failed to get channels, %d\n",
- rc);
- chg->iio_chan_list_smb_parallel = ERR_PTR(-EINVAL);
- }
- return;
- }
- chg->iio_chan_list_smb_parallel = iio_list;
- if (chg->smb_temp_max == -EINVAL) {
- rc = smblib_get_thermal_threshold(chg,
- SMB_REG_H_THRESHOLD_MSB_REG,
- &chg->smb_temp_max);
- if (rc < 0) {
- dev_err(chg->dev, "Couldn't get charger_temp_max rc=%d\n",
- rc);
- return;
- }
- }
- val = chg->smb_temp_max;
- rc = smblib_write_iio_prop(chg, SMB_PARALLEL,
- SMB_CHARGER_TEMP_MAX, val);
- if (rc < 0) {
- dev_err(chg->dev, "Couldn't set POWER_SUPPLY_PROP_CHARGER_TEMP_MAX rc=%d\n",
- rc);
- return;
- }
- if (chg->sec_chg_selected == QTI_POWER_SUPPLY_CHARGER_SEC_CP)
- return;
- smblib_select_sec_charger(chg, QTI_POWER_SUPPLY_CHARGER_SEC_PL,
- QTI_POWER_SUPPLY_CP_NONE, false);
- }
- static void clear_hdc_work(struct work_struct *work)
- {
- struct smb_charger *chg = container_of(work, struct smb_charger,
- clear_hdc_work.work);
- chg->is_hdc = false;
- vote(chg->hdc_irq_disable_votable, HDC_IRQ_VOTER, false, 0);
- }
- static void smblib_icl_change_work(struct work_struct *work)
- {
- struct smb_charger *chg = container_of(work, struct smb_charger,
- icl_change_work.work);
- int rc, settled_ua;
- rc = smblib_get_charge_param(chg, &chg->param.icl_stat, &settled_ua);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get ICL status rc=%d\n", rc);
- return;
- }
- power_supply_changed(chg->batt_psy);
- smblib_dbg(chg, PR_INTERRUPT, "icl_settled=%d\n", settled_ua);
- }
- static void smblib_pl_enable_work(struct work_struct *work)
- {
- struct smb_charger *chg = container_of(work, struct smb_charger,
- pl_enable_work.work);
- smblib_dbg(chg, PR_PARALLEL, "timer expired, enabling parallel\n");
- vote(chg->pl_disable_votable, PL_DELAY_VOTER, false, 0);
- vote(chg->awake_votable, PL_DELAY_VOTER, false, 0);
- }
- static void smblib_thermal_regulation_work(struct work_struct *work)
- {
- struct smb_charger *chg = container_of(work, struct smb_charger,
- thermal_regulation_work.work);
- int rc;
- rc = smblib_update_thermal_readings(chg);
- if (rc < 0)
- smblib_err(chg, "Couldn't read current thermal values %d\n",
- rc);
- rc = smblib_process_thermal_readings(chg);
- if (rc < 0)
- smblib_err(chg, "Couldn't run sw thermal regulation %d\n",
- rc);
- }
- #define MOISTURE_PROTECTION_CHECK_DELAY_MS 300000 /* 5 mins */
- static void smblib_moisture_protection_work(struct work_struct *work)
- {
- struct smb_charger *chg = container_of(work, struct smb_charger,
- moisture_protection_work);
- int rc;
- bool usb_plugged_in;
- u8 stat;
- /*
- * Hold awake votable to prevent pm_relax being called prior to
- * completion of this work.
- */
- vote(chg->awake_votable, MOISTURE_VOTER, true, 0);
- /*
- * Disable 1% duty cycle on CC_ID pin and enable uUSB factory mode
- * detection to track any change on RID, as interrupts are disable.
- */
- rc = smblib_write(chg, ((chg->chg_param.smb_version == PMI632) ?
- PMI632_TYPEC_U_USB_WATER_PROTECTION_CFG_REG :
- TYPEC_U_USB_WATER_PROTECTION_CFG_REG), 0);
- if (rc < 0) {
- smblib_err(chg, "Couldn't disable periodic monitoring of CC_ID rc=%d\n",
- rc);
- goto out;
- }
- rc = smblib_masked_write(chg, TYPEC_U_USB_CFG_REG,
- EN_MICRO_USB_FACTORY_MODE_BIT,
- EN_MICRO_USB_FACTORY_MODE_BIT);
- if (rc < 0) {
- smblib_err(chg, "Couldn't enable uUSB factory mode detection rc=%d\n",
- rc);
- goto out;
- }
- /*
- * Add a delay of 100ms to allow change in rid to reflect on
- * status registers.
- */
- msleep(100);
- rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read USB_INT_RT_STS rc=%d\n", rc);
- goto out;
- }
- usb_plugged_in = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT);
- /* Check uUSB status for moisture presence */
- rc = smblib_read(chg, TYPEC_U_USB_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_U_USB_STATUS_REG rc=%d\n",
- rc);
- goto out;
- }
- /*
- * Factory mode detection happens in case of USB plugged-in by using
- * a different current source of 2uA which can hamper moisture
- * detection. Since factory mode is not supported in kernel, factory
- * mode detection can be considered as equivalent to presence of
- * moisture.
- */
- if (stat == U_USB_STATUS_WATER_PRESENT || stat == U_USB_FMB1_BIT ||
- stat == U_USB_FMB2_BIT || (usb_plugged_in &&
- stat == U_USB_FLOAT1_BIT)) {
- smblib_set_moisture_protection(chg, true);
- alarm_start_relative(&chg->moisture_protection_alarm,
- ms_to_ktime(MOISTURE_PROTECTION_CHECK_DELAY_MS));
- } else {
- smblib_set_moisture_protection(chg, false);
- rc = alarm_cancel(&chg->moisture_protection_alarm);
- if (rc < 0)
- smblib_err(chg, "Couldn't cancel moisture protection alarm\n");
- }
- out:
- vote(chg->awake_votable, MOISTURE_VOTER, false, 0);
- }
- static enum alarmtimer_restart moisture_protection_alarm_cb(struct alarm *alarm,
- ktime_t now)
- {
- struct smb_charger *chg = container_of(alarm, struct smb_charger,
- moisture_protection_alarm);
- smblib_dbg(chg, PR_MISC, "moisture Protection Alarm Triggered %lld\n",
- ktime_to_ms(now));
- /* Atomic context, cannot use voter */
- pm_stay_awake(chg->dev);
- schedule_work(&chg->moisture_protection_work);
- return ALARMTIMER_NORESTART;
- }
- static void smblib_chg_termination_work(struct work_struct *work)
- {
- int val;
- struct smb_charger *chg = container_of(work, struct smb_charger,
- chg_termination_work);
- union power_supply_propval pval = {0, };
- int rc, input_present, delay = CHG_TERM_WA_ENTRY_DELAY_MS;
- int vbat_now_uv, max_fv_uv;
- u8 stat = 0;
- /*
- * Hold awake votable to prevent pm_relax being called prior to
- * completion of this work.
- */
- vote(chg->awake_votable, CHG_TERMINATION_VOTER, true, 0);
- rc = smblib_is_input_present(chg, &input_present);
- if ((rc < 0) || !input_present)
- goto out;
- rc = smblib_get_prop_from_bms(chg, SMB5_QG_REAL_CAPACITY, &val);
- if ((rc < 0) || (val < 100)) {
- vote(chg->usb_icl_votable, CHG_TERMINATION_VOTER, false, 0);
- vote(chg->dc_suspend_votable, CHG_TERMINATION_VOTER, false, 0);
- smblib_err(chg, "Couldn't read SOC value, rc=%d\n", rc);
- goto out;
- }
- if ((rc < 0) || (pval.intval < 100)) {
- rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &stat);
- if (rc < 0)
- goto out;
- /* check we are not in termination to exit the WA */
- if ((stat & BATTERY_CHARGER_STATUS_MASK) != TERMINATE_CHARGE) {
- vote(chg->usb_icl_votable, CHG_TERMINATION_VOTER,
- false, 0);
- vote(chg->dc_suspend_votable, CHG_TERMINATION_VOTER,
- false, 0);
- goto out;
- }
- }
- /* Get the battery float voltage */
- rc = smblib_get_prop_from_bms(chg, SMB5_QG_VOLTAGE_MAX, &val);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read voltage_max prop, rc=%d\n", rc);
- goto out;
- }
- max_fv_uv = val;
- rc = smblib_get_prop_from_bms(chg, SMB5_QG_CHARGE_FULL, &val);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read charge_full prop, rc=%d\n", rc);
- goto out;
- }
- /*
- * On change in the value of learned capacity, re-initialize the
- * reference cc_soc value due to change in cc_soc characteristic value
- * at full capacity. Also, in case cc_soc_ref value is reset,
- * re-initialize it.
- */
- if (val != chg->charge_full_cc || !chg->cc_soc_ref) {
- chg->charge_full_cc = val;
- rc = smblib_get_prop_from_bms(chg, SMB5_QG_VOLTAGE_NOW, &val);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read voltage_now prop, rc=%d\n",
- rc);
- goto out;
- }
- /*
- * Store the Vbat at the charge termination to compare with
- * the current voltage to see if the Vbat is increasing after
- * charge termination in BSM.
- */
- chg->term_vbat_uv = val;
- vbat_now_uv = val;
- rc = smblib_get_prop_from_bms(chg, SMB5_QG_CC_SOC, &val);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read cc_soc prop, rc=%d\n",
- rc);
- goto out;
- }
- chg->cc_soc_ref = val;
- } else {
- rc = smblib_get_prop_from_bms(chg, SMB5_QG_VOLTAGE_NOW, &val);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read voltage_now prop, rc=%d\n",
- rc);
- goto out;
- }
- vbat_now_uv = val;
- rc = smblib_get_prop_from_bms(chg, SMB5_QG_CC_SOC, &val);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read cc_soc prop, rc=%d\n",
- rc);
- goto out;
- }
- }
- /*
- * In BSM a sudden jump in CC_SOC is not expected. If seen, its a
- * good_ocv or updated capacity, reject it.
- */
- if (chg->last_cc_soc && val > (chg->last_cc_soc + 100)) {
- /* CC_SOC has increased by 1% from last time */
- chg->cc_soc_ref = val;
- smblib_dbg(chg, PR_MISC, "cc_soc jumped(%d->%d), reset cc_soc_ref\n",
- chg->last_cc_soc, val);
- }
- chg->last_cc_soc = val;
- /*
- * Suspend/Unsuspend USB input to keep cc_soc within the 0.5% to 0.75%
- * overshoot range of the cc_soc value at termination and make sure that
- * vbat is indeed rising above vfloat.
- */
- if (val < DIV_ROUND_CLOSEST(chg->cc_soc_ref * 10050, 10000)) {
- vote(chg->usb_icl_votable, CHG_TERMINATION_VOTER, false, 0);
- vote(chg->dc_suspend_votable, CHG_TERMINATION_VOTER, false, 0);
- delay = CHG_TERM_WA_ENTRY_DELAY_MS;
- } else if ((val > DIV_ROUND_CLOSEST(chg->cc_soc_ref * 10075,
- 10000))
- && ((vbat_now_uv > chg->term_vbat_uv) &&
- (vbat_now_uv > max_fv_uv))) {
- if (input_present & INPUT_PRESENT_USB)
- vote(chg->usb_icl_votable, CHG_TERMINATION_VOTER,
- true, 0);
- if (input_present & INPUT_PRESENT_DC)
- vote(chg->dc_suspend_votable, CHG_TERMINATION_VOTER,
- true, 0);
- delay = CHG_TERM_WA_EXIT_DELAY_MS;
- }
- smblib_dbg(chg, PR_MISC, "Chg Term WA readings: cc_soc: %d, cc_soc_ref: %d, delay: %d vbat_now %d term_vbat %d\n",
- val, chg->cc_soc_ref, delay, vbat_now_uv,
- chg->term_vbat_uv);
- alarm_start_relative(&chg->chg_termination_alarm, ms_to_ktime(delay));
- out:
- vote(chg->awake_votable, CHG_TERMINATION_VOTER, false, 0);
- }
- static enum alarmtimer_restart chg_termination_alarm_cb(struct alarm *alarm,
- ktime_t now)
- {
- struct smb_charger *chg = container_of(alarm, struct smb_charger,
- chg_termination_alarm);
- smblib_dbg(chg, PR_MISC, "Charge termination WA alarm triggered %lld\n",
- ktime_to_ms(now));
- /* Atomic context, cannot use voter */
- pm_stay_awake(chg->dev);
- schedule_work(&chg->chg_termination_work);
- return ALARMTIMER_NORESTART;
- }
- static void apsd_timer_cb(struct timer_list *tm)
- {
- struct smb_charger *chg = container_of(tm, struct smb_charger,
- apsd_timer);
- smblib_dbg(chg, PR_MISC, "APSD Extented timer timeout at %lld\n",
- jiffies_to_msecs(jiffies));
- chg->apsd_ext_timeout = true;
- }
- #define SOFT_JEITA_HYSTERESIS_OFFSET 0x200
- static void jeita_update_work(struct work_struct *work)
- {
- struct smb_charger *chg = container_of(work, struct smb_charger,
- jeita_update_work);
- struct device_node *node = chg->dev->of_node;
- struct device_node *batt_node, *pnode;
- union power_supply_propval val;
- int rc, tmp[2], max_fcc_ma, max_fv_uv;
- u32 jeita_hard_thresholds[2];
- u16 addr;
- u8 buff[2];
- batt_node = of_find_node_by_name(node, "qcom,battery-data");
- if (!batt_node) {
- smblib_err(chg, "Batterydata not available\n");
- goto out;
- }
- /* if BMS is not ready, defer the work */
- if (IS_ERR_OR_NULL(chg->iio_chan_list_qg))
- return;
- rc = smblib_get_prop_from_bms(chg, SMB5_QG_RESISTANCE_ID, &val.intval);
- if (rc < 0) {
- smblib_err(chg, "Failed to get batt-id rc=%d\n", rc);
- goto out;
- }
- /* if BMS hasn't read out the batt_id yet, defer the work */
- if (val.intval <= 0)
- return;
- pnode = of_batterydata_get_best_profile(batt_node,
- val.intval / 1000, NULL);
- if (IS_ERR(pnode)) {
- rc = PTR_ERR(pnode);
- smblib_err(chg, "Failed to detect valid battery profile %d\n",
- rc);
- goto out;
- }
- rc = of_property_read_u32_array(pnode, "qcom,jeita-hard-thresholds",
- jeita_hard_thresholds, 2);
- if (!rc) {
- rc = smblib_update_jeita(chg, jeita_hard_thresholds,
- JEITA_HARD);
- if (rc < 0) {
- smblib_err(chg, "Couldn't configure Hard Jeita rc=%d\n",
- rc);
- goto out;
- }
- }
- rc = of_property_read_u32_array(pnode, "qcom,jeita-soft-thresholds",
- chg->jeita_soft_thlds, 2);
- if (!rc) {
- rc = smblib_update_jeita(chg, chg->jeita_soft_thlds,
- JEITA_SOFT);
- if (rc < 0) {
- smblib_err(chg, "Couldn't configure Soft Jeita rc=%d\n",
- rc);
- goto out;
- }
- rc = of_property_read_u32_array(pnode,
- "qcom,jeita-soft-hys-thresholds",
- chg->jeita_soft_hys_thlds, 2);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get Soft Jeita hysteresis thresholds rc=%d\n",
- rc);
- goto out;
- }
- } else {
- /* Populate the jeita-soft-thresholds */
- addr = CHGR_JEITA_THRESHOLD_BASE_REG(JEITA_SOFT);
- rc = smblib_batch_read(chg, addr, buff, 2);
- if (rc < 0) {
- pr_err("failed to read 0x%4X, rc=%d\n", addr, rc);
- goto out;
- }
- chg->jeita_soft_thlds[1] = buff[1] | buff[0] << 8;
- rc = smblib_batch_read(chg, addr + 2, buff, 2);
- if (rc < 0) {
- pr_err("failed to read 0x%4X, rc=%d\n", addr + 2, rc);
- goto out;
- }
- chg->jeita_soft_thlds[0] = buff[1] | buff[0] << 8;
- /*
- * Update the soft jeita hysteresis 2 DegC less for warm and
- * 2 DegC more for cool than the soft jeita thresholds to avoid
- * overwriting the registers with invalid values.
- */
- chg->jeita_soft_hys_thlds[0] =
- chg->jeita_soft_thlds[0] - SOFT_JEITA_HYSTERESIS_OFFSET;
- chg->jeita_soft_hys_thlds[1] =
- chg->jeita_soft_thlds[1] + SOFT_JEITA_HYSTERESIS_OFFSET;
- }
- chg->jeita_soft_fcc[0] = chg->jeita_soft_fcc[1] = -EINVAL;
- chg->jeita_soft_fv[0] = chg->jeita_soft_fv[1] = -EINVAL;
- max_fcc_ma = max_fv_uv = -EINVAL;
- of_property_read_u32(pnode, "qcom,fastchg-current-ma", &max_fcc_ma);
- of_property_read_u32(pnode, "qcom,max-voltage-uv", &max_fv_uv);
- if (max_fcc_ma <= 0 || max_fv_uv <= 0) {
- smblib_err(chg, "Incorrect fastchg-current-ma or max-voltage-uv\n");
- goto out;
- }
- rc = of_property_read_u32_array(pnode, "qcom,jeita-soft-fcc-ua",
- tmp, 2);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get fcc values for soft JEITA rc=%d\n",
- rc);
- goto out;
- }
- max_fcc_ma *= 1000;
- if (tmp[0] > max_fcc_ma || tmp[1] > max_fcc_ma) {
- smblib_err(chg, "Incorrect FCC value [%d %d] max: %d\n", tmp[0],
- tmp[1], max_fcc_ma);
- goto out;
- }
- chg->jeita_soft_fcc[0] = tmp[0];
- chg->jeita_soft_fcc[1] = tmp[1];
- rc = of_property_read_u32_array(pnode, "qcom,jeita-soft-fv-uv", tmp,
- 2);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get fv values for soft JEITA rc=%d\n",
- rc);
- goto out;
- }
- if (tmp[0] > max_fv_uv || tmp[1] > max_fv_uv) {
- smblib_err(chg, "Incorrect FV value [%d %d] max: %d\n", tmp[0],
- tmp[1], max_fv_uv);
- goto out;
- }
- chg->jeita_soft_fv[0] = tmp[0];
- chg->jeita_soft_fv[1] = tmp[1];
- rc = smblib_soft_jeita_arb_wa(chg);
- if (rc < 0) {
- smblib_err(chg, "Couldn't fix soft jeita arb rc=%d\n",
- rc);
- goto out;
- }
- chg->jeita_configured = JEITA_CFG_COMPLETE;
- return;
- out:
- chg->jeita_configured = JEITA_CFG_FAILURE;
- }
- void smblib_moisture_detection_enable(struct smb_charger *chg, int pval)
- {
- int rc, input_present, val;
- if (chg->pd_disabled)
- return;
- smblib_is_input_present(chg, &input_present);
- if (pval) {
- chg->lpd_disabled = false;
- pr_debug("Moisture detection enabled\n");
- if (input_present)
- schedule_delayed_work(&chg->lpd_ra_open_work,
- msecs_to_jiffies(300));
- return;
- }
- chg->lpd_disabled = true;
- if (!is_client_vote_enabled(chg->usb_icl_votable, LPD_VOTER))
- goto done;
- cancel_delayed_work_sync(&chg->lpd_ra_open_work);
- alarm_cancel(&chg->lpd_recheck_timer);
- vote(chg->usb_icl_votable, LPD_VOTER, false, 0);
- /* restore DRP mode */
- val = QTI_POWER_SUPPLY_TYPEC_PR_DUAL;
- rc = smblib_set_prop_typec_power_role(chg, val);
- if (rc < 0) {
- smblib_err(chg, "Failed to set power-role to DRP rc=%d\n",
- rc);
- return;
- }
- chg->lpd_reason = LPD_NONE;
- chg->lpd_stage = LPD_STAGE_NONE;
- done:
- pr_debug("Moisture detection disabled\n");
- }
- static void smblib_lpd_ra_open_work(struct work_struct *work)
- {
- struct smb_charger *chg = container_of(work, struct smb_charger,
- lpd_ra_open_work.work);
- u8 stat;
- int rc, val;
- if (chg->pr_swap_in_progress || chg->pd_hard_reset || chg->lpd_disabled) {
- chg->lpd_stage = LPD_STAGE_NONE;
- goto out;
- }
- if (chg->lpd_stage != LPD_STAGE_FLOAT)
- goto out;
- rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read TYPE_C_MISC_STATUS_REG rc=%d\n",
- rc);
- goto out;
- }
- /* quit if moisture status is gone or in attached state */
- if (!(stat & TYPEC_WATER_DETECTION_STATUS_BIT)
- || (stat & TYPEC_TCCDEBOUNCE_DONE_STATUS_BIT)) {
- chg->lpd_stage = LPD_STAGE_NONE;
- goto out;
- }
- chg->lpd_stage = LPD_STAGE_COMMIT;
- /* Enable source only mode */
- val = QTI_POWER_SUPPLY_TYPEC_PR_SOURCE;
- rc = smblib_set_prop_typec_power_role(chg, val);
- if (rc < 0) {
- smblib_err(chg, "Couldn't set typec source only mode rc=%d\n",
- rc);
- goto out;
- }
- /* Wait 1.5ms to get SBUx ready */
- usleep_range(1500, 1510);
- if (smblib_rsbux_low(chg, RSBU_K_300K_UV)) {
- /* Moisture detected, enable sink only mode */
- val = QTI_POWER_SUPPLY_TYPEC_PR_SINK;
- rc = smblib_set_prop_typec_power_role(chg, val);
- if (rc < 0) {
- smblib_err(chg, "Couldn't set typec sink only rc=%d\n",
- rc);
- goto out;
- }
- chg->lpd_reason = LPD_MOISTURE_DETECTED;
- chg->moisture_present = true;
- vote(chg->usb_icl_votable, LPD_VOTER, true, 0);
- } else {
- /* Floating cable, disable water detection irq temporarily */
- rc = smblib_masked_write(chg, TYPE_C_INTERRUPT_EN_CFG_2_REG,
- TYPEC_WATER_DETECTION_INT_EN_BIT, 0);
- if (rc < 0) {
- smblib_err(chg, "Couldn't set TYPE_C_INTERRUPT_EN_CFG_2_REG rc=%d\n",
- rc);
- goto out;
- }
- /* restore DRP mode */
- val = QTI_POWER_SUPPLY_TYPEC_PR_DUAL;
- rc = smblib_set_prop_typec_power_role(chg, val);
- if (rc < 0) {
- smblib_err(chg, "Couldn't write 0x%02x to TYPE_C_INTRPT_ENB_SOFTWARE_CTRL rc=%d\n",
- val, rc);
- goto out;
- }
- chg->lpd_reason = LPD_FLOATING_CABLE;
- }
- /* recheck in 60 seconds */
- alarm_start_relative(&chg->lpd_recheck_timer, ms_to_ktime(60000));
- out:
- vote(chg->awake_votable, LPD_VOTER, false, 0);
- }
- static void smblib_lpd_detach_work(struct work_struct *work)
- {
- struct smb_charger *chg = container_of(work, struct smb_charger,
- lpd_detach_work.work);
- if (chg->lpd_stage == LPD_STAGE_FLOAT_CANCEL)
- chg->lpd_stage = LPD_STAGE_NONE;
- }
- static void smblib_cp_status_change_work(struct work_struct *work)
- {
- int rc = 0, val;
- struct smb_charger *chg = container_of(work, struct smb_charger,
- cp_status_change_work);
- if (!is_cp_available(chg))
- goto relax;
- if (chg->cp_topo == -EINVAL) {
- rc = smblib_read_iio_prop(chg, CP, CP_PARALLEL_OUTPUT_MODE,
- &val);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read cp topo rc=%d\n", rc);
- goto relax;
- }
- chg->cp_topo = val;
- if (chg->cp_topo == QTI_POWER_SUPPLY_PL_OUTPUT_VBAT &&
- chg->cp_reason == QTI_POWER_SUPPLY_CP_WIRELESS)
- vote(chg->fcc_main_votable, WLS_PL_CHARGING_VOTER, true,
- 800000);
- }
- relax:
- pm_relax(chg->dev);
- }
- static int smblib_create_votables(struct smb_charger *chg)
- {
- int rc = 0;
- chg->fcc_votable = find_votable("FCC");
- if (chg->fcc_votable == NULL) {
- rc = -EINVAL;
- smblib_err(chg, "Couldn't find FCC votable rc=%d\n", rc);
- return rc;
- }
- chg->fcc_main_votable = find_votable("FCC_MAIN");
- if (chg->fcc_main_votable == NULL) {
- rc = -EINVAL;
- smblib_err(chg, "Couldn't find FCC Main votable rc=%d\n", rc);
- return rc;
- }
- chg->fv_votable = find_votable("FV");
- if (chg->fv_votable == NULL) {
- rc = -EINVAL;
- smblib_err(chg, "Couldn't find FV votable rc=%d\n", rc);
- return rc;
- }
- chg->usb_icl_votable = find_votable("USB_ICL");
- if (chg->usb_icl_votable == NULL) {
- rc = -EINVAL;
- smblib_err(chg, "Couldn't find USB_ICL votable rc=%d\n", rc);
- return rc;
- }
- chg->pl_disable_votable = find_votable("PL_DISABLE");
- if (chg->pl_disable_votable == NULL) {
- rc = -EINVAL;
- smblib_err(chg, "Couldn't find votable PL_DISABLE rc=%d\n", rc);
- return rc;
- }
- chg->pl_enable_votable_indirect = find_votable("PL_ENABLE_INDIRECT");
- if (chg->pl_enable_votable_indirect == NULL) {
- rc = -EINVAL;
- smblib_err(chg,
- "Couldn't find votable PL_ENABLE_INDIRECT rc=%d\n",
- rc);
- return rc;
- }
- vote(chg->pl_disable_votable, PL_DELAY_VOTER, true, 0);
- chg->smb_override_votable = create_votable("SMB_EN_OVERRIDE",
- VOTE_SET_ANY,
- smblib_smb_disable_override_vote_callback, chg);
- if (IS_ERR(chg->smb_override_votable)) {
- rc = PTR_ERR(chg->smb_override_votable);
- chg->smb_override_votable = NULL;
- return rc;
- }
- chg->dc_suspend_votable = create_votable("DC_SUSPEND", VOTE_SET_ANY,
- smblib_dc_suspend_vote_callback,
- chg);
- if (IS_ERR(chg->dc_suspend_votable)) {
- rc = PTR_ERR(chg->dc_suspend_votable);
- chg->dc_suspend_votable = NULL;
- return rc;
- }
- chg->awake_votable = create_votable("AWAKE", VOTE_SET_ANY,
- smblib_awake_vote_callback,
- chg);
- if (IS_ERR(chg->awake_votable)) {
- rc = PTR_ERR(chg->awake_votable);
- chg->awake_votable = NULL;
- return rc;
- }
- chg->chg_disable_votable = create_votable("CHG_DISABLE", VOTE_SET_ANY,
- smblib_chg_disable_vote_callback,
- chg);
- if (IS_ERR(chg->chg_disable_votable)) {
- rc = PTR_ERR(chg->chg_disable_votable);
- chg->chg_disable_votable = NULL;
- return rc;
- }
- chg->limited_irq_disable_votable = create_votable(
- "USB_LIMITED_IRQ_DISABLE",
- VOTE_SET_ANY,
- smblib_limited_irq_disable_vote_callback,
- chg);
- if (IS_ERR(chg->limited_irq_disable_votable)) {
- rc = PTR_ERR(chg->limited_irq_disable_votable);
- chg->limited_irq_disable_votable = NULL;
- return rc;
- }
- chg->hdc_irq_disable_votable = create_votable("USB_HDC_IRQ_DISABLE",
- VOTE_SET_ANY,
- smblib_hdc_irq_disable_vote_callback,
- chg);
- if (IS_ERR(chg->hdc_irq_disable_votable)) {
- rc = PTR_ERR(chg->hdc_irq_disable_votable);
- chg->hdc_irq_disable_votable = NULL;
- return rc;
- }
- chg->icl_irq_disable_votable = create_votable("USB_ICL_IRQ_DISABLE",
- VOTE_SET_ANY,
- smblib_icl_irq_disable_vote_callback,
- chg);
- if (IS_ERR(chg->icl_irq_disable_votable)) {
- rc = PTR_ERR(chg->icl_irq_disable_votable);
- chg->icl_irq_disable_votable = NULL;
- return rc;
- }
- chg->temp_change_irq_disable_votable = create_votable(
- "TEMP_CHANGE_IRQ_DISABLE", VOTE_SET_ANY,
- smblib_temp_change_irq_disable_vote_callback, chg);
- if (IS_ERR(chg->temp_change_irq_disable_votable)) {
- rc = PTR_ERR(chg->temp_change_irq_disable_votable);
- chg->temp_change_irq_disable_votable = NULL;
- return rc;
- }
- chg->bat_temp_irq_disable_votable = create_votable(
- "BAT_TEMP_IRQ_DISABLE", VOTE_SET_ANY,
- smblib_bat_temp_irq_disable_vote_callback, chg);
- if (IS_ERR(chg->bat_temp_irq_disable_votable)) {
- rc = PTR_ERR(chg->bat_temp_irq_disable_votable);
- chg->bat_temp_irq_disable_votable = NULL;
- return rc;
- }
- return rc;
- }
- static void smblib_destroy_votables(struct smb_charger *chg)
- {
- if (chg->dc_suspend_votable)
- destroy_votable(chg->dc_suspend_votable);
- if (chg->usb_icl_votable)
- destroy_votable(chg->usb_icl_votable);
- if (chg->awake_votable)
- destroy_votable(chg->awake_votable);
- if (chg->chg_disable_votable)
- destroy_votable(chg->chg_disable_votable);
- if (chg->bat_temp_irq_disable_votable)
- destroy_votable(chg->bat_temp_irq_disable_votable);
- }
- int smblib_init(struct smb_charger *chg)
- {
- int rc = 0, val;
- struct iio_channel **iio_list;
- mutex_init(&chg->smb_lock);
- mutex_init(&chg->irq_status_lock);
- mutex_init(&chg->dcin_aicl_lock);
- mutex_init(&chg->dpdm_lock);
- spin_lock_init(&chg->typec_pr_lock);
- INIT_WORK(&chg->bms_update_work, bms_update_work);
- INIT_WORK(&chg->pl_update_work, pl_update_work);
- INIT_WORK(&chg->jeita_update_work, jeita_update_work);
- INIT_WORK(&chg->dcin_aicl_work, dcin_aicl_work);
- INIT_WORK(&chg->cp_status_change_work, smblib_cp_status_change_work);
- INIT_DELAYED_WORK(&chg->clear_hdc_work, clear_hdc_work);
- INIT_DELAYED_WORK(&chg->icl_change_work, smblib_icl_change_work);
- INIT_DELAYED_WORK(&chg->pl_enable_work, smblib_pl_enable_work);
- INIT_DELAYED_WORK(&chg->uusb_otg_work, smblib_uusb_otg_work);
- INIT_DELAYED_WORK(&chg->bb_removal_work, smblib_bb_removal_work);
- INIT_DELAYED_WORK(&chg->lpd_ra_open_work, smblib_lpd_ra_open_work);
- INIT_DELAYED_WORK(&chg->lpd_detach_work, smblib_lpd_detach_work);
- INIT_DELAYED_WORK(&chg->thermal_regulation_work,
- smblib_thermal_regulation_work);
- INIT_DELAYED_WORK(&chg->usbov_dbc_work, smblib_usbov_dbc_work);
- INIT_DELAYED_WORK(&chg->pr_swap_detach_work,
- smblib_pr_swap_detach_work);
- INIT_DELAYED_WORK(&chg->pr_lock_clear_work,
- smblib_pr_lock_clear_work);
- timer_setup(&chg->apsd_timer, apsd_timer_cb, 0);
- INIT_DELAYED_WORK(&chg->role_reversal_check,
- smblib_typec_role_check_work);
- if (chg->wa_flags & CHG_TERMINATION_WA) {
- INIT_WORK(&chg->chg_termination_work,
- smblib_chg_termination_work);
- if (alarmtimer_get_rtcdev()) {
- alarm_init(&chg->chg_termination_alarm, ALARM_BOOTTIME,
- chg_termination_alarm_cb);
- } else {
- smblib_err(chg, "Couldn't get rtc device\n");
- return -ENODEV;
- }
- }
- if (chg->uusb_moisture_protection_enabled) {
- INIT_WORK(&chg->moisture_protection_work,
- smblib_moisture_protection_work);
- if (alarmtimer_get_rtcdev()) {
- alarm_init(&chg->moisture_protection_alarm,
- ALARM_BOOTTIME, moisture_protection_alarm_cb);
- } else {
- smblib_err(chg, "Failed to initialize moisture protection alarm\n");
- return -ENODEV;
- }
- }
- if (alarmtimer_get_rtcdev()) {
- alarm_init(&chg->dcin_aicl_alarm, ALARM_REALTIME,
- dcin_aicl_alarm_cb);
- } else {
- smblib_err(chg, "Failed to initialize dcin aicl alarm\n");
- return -ENODEV;
- }
- chg->fake_capacity = -EINVAL;
- chg->fake_input_current_limited = -EINVAL;
- chg->fake_batt_status = -EINVAL;
- chg->sink_src_mode = UNATTACHED_MODE;
- chg->jeita_configured = false;
- chg->sec_chg_selected = QTI_POWER_SUPPLY_CHARGER_SEC_NONE;
- chg->cp_reason = QTI_POWER_SUPPLY_CP_NONE;
- chg->thermal_status = TEMP_BELOW_RANGE;
- chg->typec_irq_en = true;
- chg->cp_topo = -EINVAL;
- chg->dr_mode = TYPEC_PORT_DRP;
- switch (chg->mode) {
- case PARALLEL_MASTER:
- rc = qcom_batt_init(chg->dev, &chg->chg_param);
- if (rc < 0) {
- smblib_err(chg, "Couldn't init qcom_batt_init rc=%d\n",
- rc);
- return rc;
- }
- rc = qcom_step_chg_init(chg->dev, chg->step_chg_enabled,
- chg->sw_jeita_enabled, chg->jeita_arb_enable,
- chg->iio_chans);
- if (rc < 0) {
- smblib_err(chg, "Couldn't init qcom_step_chg_init rc=%d\n",
- rc);
- return rc;
- }
- rc = smblib_create_votables(chg);
- if (rc < 0) {
- smblib_err(chg, "Couldn't create votables rc=%d\n",
- rc);
- return rc;
- }
- iio_list = get_ext_channels(chg->dev, smblib_qg_ext_iio_chan,
- ARRAY_SIZE(smblib_qg_ext_iio_chan));
- if (!IS_ERR(iio_list))
- chg->iio_chan_list_qg = iio_list;
- if (chg->sec_pl_present) {
- iio_list = get_ext_channels(
- chg->dev, smblib_parallel_ext_iio_chan,
- ARRAY_SIZE(smblib_parallel_ext_iio_chan));
- if (!IS_ERR(iio_list))
- chg->iio_chan_list_smb_parallel = iio_list;
- if (chg->iio_chan_list_smb_parallel) {
- if (chg->sec_chg_selected
- != QTI_POWER_SUPPLY_CHARGER_SEC_CP) {
- rc = smblib_select_sec_charger(chg,
- QTI_POWER_SUPPLY_CHARGER_SEC_PL,
- QTI_POWER_SUPPLY_CP_NONE,
- false);
- if (rc < 0)
- smblib_err(chg, "Couldn't config pl charger rc=%d\n",
- rc);
- }
- if (chg->smb_temp_max == -EINVAL) {
- rc = smblib_get_thermal_threshold(chg,
- SMB_REG_H_THRESHOLD_MSB_REG,
- &chg->smb_temp_max);
- if (rc < 0) {
- dev_err(chg->dev, "Couldn't get charger_temp_max rc=%d\n",
- rc);
- return rc;
- }
- }
- val = chg->smb_temp_max;
- rc = smblib_write_iio_prop(chg, SMB_PARALLEL,
- SMB_CHARGER_TEMP_MAX, val);
- if (rc < 0) {
- dev_err(chg->dev, "Couldn't set POWER_SUPPLY_PROP_CHARGER_TEMP_MAX rc=%d\n",
- rc);
- return rc;
- }
- }
- }
- rc = smblib_register_notifier(chg);
- if (rc < 0) {
- smblib_err(chg,
- "Couldn't register notifier rc=%d\n", rc);
- return rc;
- }
- break;
- case PARALLEL_SLAVE:
- break;
- default:
- smblib_err(chg, "Unsupported mode %d\n", chg->mode);
- return -EINVAL;
- }
- return rc;
- }
- int smblib_deinit(struct smb_charger *chg)
- {
- switch (chg->mode) {
- case PARALLEL_MASTER:
- if (chg->uusb_moisture_protection_enabled) {
- alarm_cancel(&chg->moisture_protection_alarm);
- cancel_work_sync(&chg->moisture_protection_work);
- }
- if (chg->wa_flags & CHG_TERMINATION_WA) {
- alarm_cancel(&chg->chg_termination_alarm);
- cancel_work_sync(&chg->chg_termination_work);
- }
- del_timer_sync(&chg->apsd_timer);
- cancel_work_sync(&chg->bms_update_work);
- cancel_work_sync(&chg->jeita_update_work);
- cancel_work_sync(&chg->pl_update_work);
- cancel_work_sync(&chg->dcin_aicl_work);
- cancel_work_sync(&chg->cp_status_change_work);
- cancel_delayed_work_sync(&chg->clear_hdc_work);
- cancel_delayed_work_sync(&chg->icl_change_work);
- cancel_delayed_work_sync(&chg->pl_enable_work);
- cancel_delayed_work_sync(&chg->uusb_otg_work);
- cancel_delayed_work_sync(&chg->bb_removal_work);
- cancel_delayed_work_sync(&chg->lpd_ra_open_work);
- cancel_delayed_work_sync(&chg->lpd_detach_work);
- cancel_delayed_work_sync(&chg->thermal_regulation_work);
- cancel_delayed_work_sync(&chg->usbov_dbc_work);
- cancel_delayed_work_sync(&chg->role_reversal_check);
- cancel_delayed_work_sync(&chg->pr_swap_detach_work);
- power_supply_unreg_notifier(&chg->nb);
- smblib_destroy_votables(chg);
- qcom_step_chg_deinit();
- qcom_batt_deinit();
- break;
- case PARALLEL_SLAVE:
- break;
- default:
- smblib_err(chg, "Unsupported mode %d\n", chg->mode);
- return -EINVAL;
- }
- return 0;
- }
|