Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164// SPDX-License-Identifier: GPL-2.0+/* Framework for finding and configuring PHYs. * Also contains generic PHY driver * * Author: Andy Fleming * * Copyright (c) 2004 Freescale Semiconductor, Inc. */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/acpi.h>#include <linux/bitmap.h>#include <linux/delay.h>#include <linux/errno.h>#include <linux/etherdevice.h>#include <linux/ethtool.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/io.h>#include <linux/kernel.h>#include <linux/list.h>#include <linux/mdio.h>#include <linux/mii.h>#include <linux/mm.h>#include <linux/module.h>#include <linux/of.h>#include <linux/netdevice.h>#include <linux/phy.h>#include <linux/phylib_stubs.h>#include <linux/phy_led_triggers.h>#include <linux/phy_link_topology.h>#include <linux/phy_port.h>#include <linux/pse-pd/pse.h>#include <linux/property.h>#include <linux/ptp_clock_kernel.h>#include <linux/rtnetlink.h>#include <linux/sfp.h>#include <linux/skbuff.h>#include <linux/slab.h>#include <linux/string.h>#include <linux/uaccess.h>#include <linux/unistd.h>
#include "phylib-internal.h"#include "phy-caps.h"
MODULE_DESCRIPTION("PHY library");MODULE_AUTHOR("Andy Fleming");MODULE_LICENSE("GPL");
static struct phy_driver genphy_c45_driver = { .phy_id = 0xffffffff, .phy_id_mask = 0xffffffff, .name = "Generic Clause 45 PHY", .read_status = genphy_c45_read_status,};
__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_basic_features) __ro_after_init;EXPORT_SYMBOL_GPL(phy_basic_features);
__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_basic_t1_features) __ro_after_init;EXPORT_SYMBOL_GPL(phy_basic_t1_features);
__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_basic_t1s_p2mp_features) __ro_after_init;EXPORT_SYMBOL_GPL(phy_basic_t1s_p2mp_features);
__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_gbit_features) __ro_after_init;EXPORT_SYMBOL_GPL(phy_gbit_features);
__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_gbit_fibre_features) __ro_after_init;EXPORT_SYMBOL_GPL(phy_gbit_fibre_features);
__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_10gbit_features) __ro_after_init;EXPORT_SYMBOL_GPL(phy_10gbit_features);
const int phy_basic_ports_array[3] = { ETHTOOL_LINK_MODE_Autoneg_BIT, ETHTOOL_LINK_MODE_TP_BIT, ETHTOOL_LINK_MODE_MII_BIT,};EXPORT_SYMBOL_GPL(phy_basic_ports_array);
static const int phy_all_ports_features_array[7] __initconst = { ETHTOOL_LINK_MODE_Autoneg_BIT, ETHTOOL_LINK_MODE_TP_BIT, ETHTOOL_LINK_MODE_MII_BIT, ETHTOOL_LINK_MODE_FIBRE_BIT, ETHTOOL_LINK_MODE_AUI_BIT, ETHTOOL_LINK_MODE_BNC_BIT, ETHTOOL_LINK_MODE_Backplane_BIT,};
static const int phy_10_100_features_array[4] __initconst = { ETHTOOL_LINK_MODE_10baseT_Half_BIT, ETHTOOL_LINK_MODE_10baseT_Full_BIT, ETHTOOL_LINK_MODE_100baseT_Half_BIT, ETHTOOL_LINK_MODE_100baseT_Full_BIT,};
static const int phy_basic_t1_features_array[3] __initconst = { ETHTOOL_LINK_MODE_TP_BIT, ETHTOOL_LINK_MODE_10baseT1L_Full_BIT, ETHTOOL_LINK_MODE_100baseT1_Full_BIT,};
static const int phy_basic_t1s_p2mp_features_array[2] __initconst = { ETHTOOL_LINK_MODE_TP_BIT, ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT,};
static const int phy_gbit_features_array[2] __initconst = { ETHTOOL_LINK_MODE_1000baseT_Half_BIT, ETHTOOL_LINK_MODE_1000baseT_Full_BIT,};
static const int phy_eee_cap1_features_array[] __initconst = { ETHTOOL_LINK_MODE_100baseT_Full_BIT, ETHTOOL_LINK_MODE_1000baseT_Full_BIT, ETHTOOL_LINK_MODE_10000baseT_Full_BIT, ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,};
__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_eee_cap1_features) __ro_after_init;EXPORT_SYMBOL_GPL(phy_eee_cap1_features);
static const int phy_eee_cap2_features_array[] __initconst = { ETHTOOL_LINK_MODE_2500baseT_Full_BIT, ETHTOOL_LINK_MODE_5000baseT_Full_BIT,};
__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_eee_cap2_features) __ro_after_init;EXPORT_SYMBOL_GPL(phy_eee_cap2_features);
static void __init features_init(void){ /* 10/100 half/full*/ linkmode_set_bit_array(phy_basic_ports_array, ARRAY_SIZE(phy_basic_ports_array), phy_basic_features); linkmode_set_bit_array(phy_10_100_features_array, ARRAY_SIZE(phy_10_100_features_array), phy_basic_features);
/* 100 full, TP */ linkmode_set_bit_array(phy_basic_t1_features_array, ARRAY_SIZE(phy_basic_t1_features_array), phy_basic_t1_features);
/* 10 half, P2MP, TP */ linkmode_set_bit_array(phy_basic_t1s_p2mp_features_array, ARRAY_SIZE(phy_basic_t1s_p2mp_features_array), phy_basic_t1s_p2mp_features);
/* 10/100 half/full + 1000 half/full */ linkmode_set_bit_array(phy_basic_ports_array, ARRAY_SIZE(phy_basic_ports_array), phy_gbit_features); linkmode_set_bit_array(phy_10_100_features_array, ARRAY_SIZE(phy_10_100_features_array), phy_gbit_features); linkmode_set_bit_array(phy_gbit_features_array, ARRAY_SIZE(phy_gbit_features_array), phy_gbit_features);
/* 10/100 half/full + 1000 half/full + fibre*/ linkmode_set_bit_array(phy_basic_ports_array, ARRAY_SIZE(phy_basic_ports_array), phy_gbit_fibre_features); linkmode_set_bit_array(phy_10_100_features_array, ARRAY_SIZE(phy_10_100_features_array), phy_gbit_fibre_features); linkmode_set_bit_array(phy_gbit_features_array, ARRAY_SIZE(phy_gbit_features_array), phy_gbit_fibre_features); linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phy_gbit_fibre_features);
/* 10/100 half/full + 1000 half/full + 10G full*/ linkmode_set_bit_array(phy_all_ports_features_array, ARRAY_SIZE(phy_all_ports_features_array), phy_10gbit_features); linkmode_set_bit_array(phy_10_100_features_array, ARRAY_SIZE(phy_10_100_features_array), phy_10gbit_features); linkmode_set_bit_array(phy_gbit_features_array, ARRAY_SIZE(phy_gbit_features_array), phy_10gbit_features); linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, phy_10gbit_features);
linkmode_set_bit_array(phy_eee_cap1_features_array, ARRAY_SIZE(phy_eee_cap1_features_array), phy_eee_cap1_features); linkmode_set_bit_array(phy_eee_cap2_features_array, ARRAY_SIZE(phy_eee_cap2_features_array), phy_eee_cap2_features);
}
void phy_device_free(struct phy_device *phydev){ put_device(&phydev->mdio.dev);}EXPORT_SYMBOL(phy_device_free);
static void phy_mdio_device_free(struct mdio_device *mdiodev){ struct phy_device *phydev;
phydev = container_of(mdiodev, struct phy_device, mdio); phy_device_free(phydev);}
static void phy_device_release(struct device *dev){ fwnode_handle_put(dev->fwnode); kfree(to_phy_device(dev));}
static void phy_mdio_device_remove(struct mdio_device *mdiodev){ struct phy_device *phydev;
phydev = container_of(mdiodev, struct phy_device, mdio); phy_device_remove(phydev);}
static struct phy_driver genphy_driver;
static bool phy_drv_wol_enabled(struct phy_device *phydev){ struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
phy_ethtool_get_wol(phydev, &wol);
return wol.wolopts != 0;}
bool phy_may_wakeup(struct phy_device *phydev){ /* If the PHY is using driver-model based wakeup, use that state. */ if (phy_can_wakeup(phydev)) return device_may_wakeup(&phydev->mdio.dev);
return phy_drv_wol_enabled(phydev);}EXPORT_SYMBOL_GPL(phy_may_wakeup);
static void phy_link_change(struct phy_device *phydev, bool up){ struct net_device *netdev = phydev->attached_dev;
if (up) netif_carrier_on(netdev); else netif_carrier_off(netdev); phydev->adjust_link(netdev); if (phydev->mii_ts && phydev->mii_ts->link_state) phydev->mii_ts->link_state(phydev->mii_ts, phydev);}
/** * phy_uses_state_machine - test whether consumer driver uses PAL state machine * @phydev: the target PHY device structure * * Ultimately, this aims to indirectly determine whether the PHY is attached * to a consumer which uses the state machine by calling phy_start() and * phy_stop(). * * When the PHY driver consumer uses phylib, it must have previously called * phy_connect_direct() or one of its derivatives, so that phy_prepare_link() * has set up a hook for monitoring state changes. * * When the PHY driver is used by the MAC driver consumer through phylink (the * only other provider of a phy_link_change() method), using the PHY state * machine is not optional. * * Return: true if consumer calls phy_start() and phy_stop(), false otherwise. */static bool phy_uses_state_machine(struct phy_device *phydev){ if (phydev->phy_link_change == phy_link_change) return phydev->attached_dev && phydev->adjust_link;
return !!phydev->phy_link_change;}
static bool mdio_bus_phy_may_suspend(struct phy_device *phydev){ struct device_driver *drv = phydev->mdio.dev.driver; struct phy_driver *phydrv = to_phy_driver(drv); struct net_device *netdev = phydev->attached_dev;
if (!drv || !phydrv->suspend) return false;
/* If the PHY on the mido bus is not attached but has WOL enabled * we cannot suspend the PHY. */ if (!netdev && phy_may_wakeup(phydev)) return false;
/* PHY not attached? May suspend if the PHY has not already been * suspended as part of a prior call to phy_disconnect() -> * phy_detach() -> phy_suspend() because the parent netdev might be the * MDIO bus driver and clock gated at this point. */ if (!netdev) goto out;
if (netdev->ethtool->wol_enabled) return false;
/* As long as not all affected network drivers support the * wol_enabled flag, let's check for hints that WoL is enabled. * Don't suspend PHY if the attached netdev parent may wake up. * The parent may point to a PCI device, as in tg3 driver. */ if (netdev->dev.parent && device_may_wakeup(netdev->dev.parent)) return false;
/* Also don't suspend PHY if the netdev itself may wakeup. This * is the case for devices w/o underlaying pwr. mgmt. aware bus, * e.g. SoC devices. */ if (device_may_wakeup(&netdev->dev)) return false;
out: return !phydev->suspended;}
static __maybe_unused int mdio_bus_phy_suspend(struct device *dev){ struct phy_device *phydev = to_phy_device(dev);
if (phydev->mac_managed_pm) return 0;
/* Wakeup interrupts may occur during the system sleep transition when * the PHY is inaccessible. Set flag to postpone handling until the PHY * has resumed. Wait for concurrent interrupt handler to complete. */ if (phy_interrupt_is_valid(phydev)) { phydev->irq_suspended = 1; synchronize_irq(phydev->irq); }
/* We must stop the state machine manually, otherwise it stops out of * control, possibly with the phydev->lock held. Upon resume, netdev * may call phy routines that try to grab the same lock, and that may * lead to a deadlock. */ if (phy_uses_state_machine(phydev)) phy_stop_machine(phydev);
if (!mdio_bus_phy_may_suspend(phydev)) return 0;
phydev->suspended_by_mdio_bus = 1;
return phy_suspend(phydev);}
static __maybe_unused int mdio_bus_phy_resume(struct device *dev){ struct phy_device *phydev = to_phy_device(dev); int ret;
if (phydev->mac_managed_pm) return 0;
if (!phydev->suspended_by_mdio_bus) goto no_resume;
phydev->suspended_by_mdio_bus = 0;
/* If we managed to get here with the PHY state machine in a state * neither PHY_HALTED, PHY_READY nor PHY_UP, this is an indication * that something went wrong and we should most likely be using * MAC managed PM, but we are not. */ WARN_ON(phydev->state != PHY_HALTED && phydev->state != PHY_READY && phydev->state != PHY_UP);
ret = phy_init_hw(phydev); if (ret < 0) return ret;
ret = phy_resume(phydev); if (ret < 0) return ret;no_resume: if (phy_interrupt_is_valid(phydev)) { phydev->irq_suspended = 0; synchronize_irq(phydev->irq);
/* Rerun interrupts which were postponed by phy_interrupt() * because they occurred during the system sleep transition. */ if (phydev->irq_rerun) { phydev->irq_rerun = 0; enable_irq(phydev->irq); irq_wake_thread(phydev->irq, phydev); } }
if (phy_uses_state_machine(phydev)) phy_start_machine(phydev);
return 0;}
static SIMPLE_DEV_PM_OPS(mdio_bus_phy_pm_ops, mdio_bus_phy_suspend, mdio_bus_phy_resume);
/** * genphy_match_phy_device - match a PHY device with a PHY driver * @phydev: target phy_device struct * @phydrv: target phy_driver struct * * Description: Checks whether the given PHY device matches the specified * PHY driver. For Clause 45 PHYs, iterates over the available device * identifiers and compares them against the driver's expected PHY ID, * applying the provided mask. For Clause 22 PHYs, a direct ID comparison * is performed. * * Return: 1 if the PHY device matches the driver, 0 otherwise. */int genphy_match_phy_device(struct phy_device *phydev, const struct phy_driver *phydrv){ if (phydev->is_c45) { const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids); int i;
for (i = 1; i < num_ids; i++) { if (phydev->c45_ids.device_ids[i] == 0xffffffff) continue;
if (phy_id_compare(phydev->c45_ids.device_ids[i], phydrv->phy_id, phydrv->phy_id_mask)) return 1; }
return 0; }
return phy_id_compare(phydev->phy_id, phydrv->phy_id, phydrv->phy_id_mask);}EXPORT_SYMBOL_GPL(genphy_match_phy_device);
static int phy_bus_match(struct device *dev, const struct device_driver *drv){ struct phy_device *phydev = to_phy_device(dev); const struct phy_driver *phydrv = to_phy_driver(drv);
if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY)) return 0;
if (phydrv->match_phy_device) return phydrv->match_phy_device(phydev, phydrv);
return genphy_match_phy_device(phydev, phydrv);}
static ssize_tphy_id_show(struct device *dev, struct device_attribute *attr, char *buf){ struct phy_device *phydev = to_phy_device(dev);
return sysfs_emit(buf, "0x%.8lx\n", (unsigned long)phydev->phy_id);}static DEVICE_ATTR_RO(phy_id);
static ssize_tphy_interface_show(struct device *dev, struct device_attribute *attr, char *buf){ struct phy_device *phydev = to_phy_device(dev); const char *mode = NULL;
if (phydev->is_internal) mode = "internal"; else mode = phy_modes(phydev->interface);
return sysfs_emit(buf, "%s\n", mode);}static DEVICE_ATTR_RO(phy_interface);
static ssize_tphy_has_fixups_show(struct device *dev, struct device_attribute *attr, char *buf){ struct phy_device *phydev = to_phy_device(dev);
return sysfs_emit(buf, "%d\n", phydev->has_fixups);}static DEVICE_ATTR_RO(phy_has_fixups);
static ssize_t phy_dev_flags_show(struct device *dev, struct device_attribute *attr, char *buf){ struct phy_device *phydev = to_phy_device(dev);
return sysfs_emit(buf, "0x%08x\n", phydev->dev_flags);}static DEVICE_ATTR_RO(phy_dev_flags);
static struct attribute *phy_dev_attrs[] = { &dev_attr_phy_id.attr, &dev_attr_phy_interface.attr, &dev_attr_phy_has_fixups.attr, &dev_attr_phy_dev_flags.attr, NULL,};
static const struct attribute_group phy_dev_group = { .attrs = phy_dev_attrs,};
#define MMD_DEVICE_ID_ATTR(n) \static ssize_t mmd##n##_device_id_show(struct device *dev, \ struct device_attribute *attr, char *buf) \{ \ struct phy_device *phydev = to_phy_device(dev); \ return sysfs_emit(buf, "0x%.8lx\n", \ (unsigned long)phydev->c45_ids.device_ids[n]); \} \static DEVICE_ATTR_RO(mmd##n##_device_id)
MMD_DEVICE_ID_ATTR(1);MMD_DEVICE_ID_ATTR(2);MMD_DEVICE_ID_ATTR(3);MMD_DEVICE_ID_ATTR(4);MMD_DEVICE_ID_ATTR(5);MMD_DEVICE_ID_ATTR(6);MMD_DEVICE_ID_ATTR(7);MMD_DEVICE_ID_ATTR(8);MMD_DEVICE_ID_ATTR(9);MMD_DEVICE_ID_ATTR(10);MMD_DEVICE_ID_ATTR(11);MMD_DEVICE_ID_ATTR(12);MMD_DEVICE_ID_ATTR(13);MMD_DEVICE_ID_ATTR(14);MMD_DEVICE_ID_ATTR(15);MMD_DEVICE_ID_ATTR(16);MMD_DEVICE_ID_ATTR(17);MMD_DEVICE_ID_ATTR(18);MMD_DEVICE_ID_ATTR(19);MMD_DEVICE_ID_ATTR(20);MMD_DEVICE_ID_ATTR(21);MMD_DEVICE_ID_ATTR(22);MMD_DEVICE_ID_ATTR(23);MMD_DEVICE_ID_ATTR(24);MMD_DEVICE_ID_ATTR(25);MMD_DEVICE_ID_ATTR(26);MMD_DEVICE_ID_ATTR(27);MMD_DEVICE_ID_ATTR(28);MMD_DEVICE_ID_ATTR(29);MMD_DEVICE_ID_ATTR(30);MMD_DEVICE_ID_ATTR(31);
static struct attribute *phy_mmd_attrs[] = { &dev_attr_mmd1_device_id.attr, &dev_attr_mmd2_device_id.attr, &dev_attr_mmd3_device_id.attr, &dev_attr_mmd4_device_id.attr, &dev_attr_mmd5_device_id.attr, &dev_attr_mmd6_device_id.attr, &dev_attr_mmd7_device_id.attr, &dev_attr_mmd8_device_id.attr, &dev_attr_mmd9_device_id.attr, &dev_attr_mmd10_device_id.attr, &dev_attr_mmd11_device_id.attr, &dev_attr_mmd12_device_id.attr, &dev_attr_mmd13_device_id.attr, &dev_attr_mmd14_device_id.attr, &dev_attr_mmd15_device_id.attr, &dev_attr_mmd16_device_id.attr, &dev_attr_mmd17_device_id.attr, &dev_attr_mmd18_device_id.attr, &dev_attr_mmd19_device_id.attr, &dev_attr_mmd20_device_id.attr, &dev_attr_mmd21_device_id.attr, &dev_attr_mmd22_device_id.attr, &dev_attr_mmd23_device_id.attr, &dev_attr_mmd24_device_id.attr, &dev_attr_mmd25_device_id.attr, &dev_attr_mmd26_device_id.attr, &dev_attr_mmd27_device_id.attr, &dev_attr_mmd28_device_id.attr, &dev_attr_mmd29_device_id.attr, &dev_attr_mmd30_device_id.attr, &dev_attr_mmd31_device_id.attr, NULL};
static umode_t phy_mmd_is_visible(struct kobject *kobj, struct attribute *attr, int index){ struct device *dev = kobj_to_dev(kobj); struct phy_device *phydev = to_phy_device(dev); const int i = index + 1;
if (!phydev->is_c45) return 0; if (i >= ARRAY_SIZE(phydev->c45_ids.device_ids) || phydev->c45_ids.device_ids[i] == 0xffffffff) return 0;
return attr->mode;}
static const struct attribute_group phy_mmd_group = { .name = "c45_phy_ids", .attrs = phy_mmd_attrs, .is_visible = phy_mmd_is_visible,};
static const struct attribute_group *phy_device_groups[] = { &phy_dev_group, &phy_mmd_group, NULL,};
static const struct device_type mdio_bus_phy_type = { .name = "PHY", .groups = phy_device_groups, .release = phy_device_release, .pm = pm_ptr(&mdio_bus_phy_pm_ops),};
static int phy_request_driver_module(struct phy_device *dev, u32 phy_id){ int ret;
ret = request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT, MDIO_ID_ARGS(phy_id)); /* We only check for failures in executing the usermode binary, * not whether a PHY driver module exists for the PHY ID. * Accept -ENOENT because this may occur in case no initramfs exists, * then modprobe isn't available. */ if (IS_ENABLED(CONFIG_MODULES) && ret < 0 && ret != -ENOENT) { phydev_err(dev, "error %d loading PHY driver module for ID 0x%08lx\n", ret, (unsigned long)phy_id); return ret; }
return 0;}
struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id, bool is_c45, struct phy_c45_device_ids *c45_ids){ struct phy_device *dev; struct mdio_device *mdiodev; int ret = 0;
/* We allocate the device, and initialize the default values */ dev = kzalloc_obj(*dev); if (!dev) return ERR_PTR(-ENOMEM);
mdiodev = &dev->mdio; mdiodev->dev.parent = &bus->dev; mdiodev->dev.bus = &mdio_bus_type; mdiodev->dev.type = &mdio_bus_phy_type; mdiodev->bus = bus; mdiodev->bus_match = phy_bus_match; mdiodev->addr = addr; mdiodev->flags = MDIO_DEVICE_FLAG_PHY; mdiodev->device_free = phy_mdio_device_free; mdiodev->device_remove = phy_mdio_device_remove; mdiodev->reset_state = -1;
dev->speed = SPEED_UNKNOWN; dev->duplex = DUPLEX_UNKNOWN; dev->pause = false; dev->asym_pause = false; dev->link = 0; dev->port = PORT_TP; dev->interface = PHY_INTERFACE_MODE_GMII;
dev->autoneg = AUTONEG_ENABLE;
dev->pma_extable = -ENODATA; dev->is_c45 = is_c45; dev->phy_id = phy_id; if (c45_ids) dev->c45_ids = *c45_ids; dev->irq = bus->irq[addr];
dev_set_name(&mdiodev->dev, PHY_ID_FMT, bus->id, addr); device_initialize(&mdiodev->dev);
dev->state = PHY_DOWN; INIT_LIST_HEAD(&dev->leds); INIT_LIST_HEAD(&dev->ports);
/* The driver's probe function must change that to the real number * of ports possible on the PHY. We assume by default we are dealing * with a single-port PHY */ dev->max_n_ports = 1;
mutex_init(&dev->lock); INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);
/* Request the appropriate module unconditionally; don't * bother trying to do so only if it isn't already loaded, * because that gets complicated. A hotplug event would have * done an unconditional modprobe anyway. * We don't do normal hotplug because it won't work for MDIO * -- because it relies on the device staying around for long * enough for the driver to get loaded. With MDIO, the NIC * driver will get bored and give up as soon as it finds that * there's no driver _already_ loaded. */ if (is_c45 && c45_ids) { const int num_ids = ARRAY_SIZE(c45_ids->device_ids); int i;
for (i = 1; i < num_ids; i++) { if (c45_ids->device_ids[i] == 0xffffffff) continue;
ret = phy_request_driver_module(dev, c45_ids->device_ids[i]); if (ret) break; } } else { ret = phy_request_driver_module(dev, phy_id); }
if (ret) { put_device(&mdiodev->dev); dev = ERR_PTR(ret); }
return dev;}EXPORT_SYMBOL(phy_device_create);
/* phy_c45_probe_present - checks to see if a MMD is present in the package * @bus: the target MII bus * @prtad: PHY package address on the MII bus * @devad: PHY device (MMD) address * * Read the MDIO_STAT2 register, and check whether a device is responding * at this address. * * Returns: negative error number on bus access error, zero if no device * is responding, or positive if a device is present. */static int phy_c45_probe_present(struct mii_bus *bus, int prtad, int devad){ int stat2;
stat2 = mdiobus_c45_read(bus, prtad, devad, MDIO_STAT2); if (stat2 < 0) return stat2;
return (stat2 & MDIO_STAT2_DEVPRST) == MDIO_STAT2_DEVPRST_VAL;}
/* get_phy_c45_devs_in_pkg - reads a MMD's devices in package registers. * @bus: the target MII bus * @addr: PHY address on the MII bus * @dev_addr: MMD address in the PHY. * @devices_in_package: where to store the devices in package information. * * Description: reads devices in package registers of a MMD at @dev_addr * from PHY at @addr on @bus. * * Returns: 0 on success, -EIO on failure. */static int get_phy_c45_devs_in_pkg(struct mii_bus *bus, int addr, int dev_addr, u32 *devices_in_package){ int phy_reg;
phy_reg = mdiobus_c45_read(bus, addr, dev_addr, MDIO_DEVS2); if (phy_reg < 0) return -EIO; *devices_in_package = phy_reg << 16;
phy_reg = mdiobus_c45_read(bus, addr, dev_addr, MDIO_DEVS1); if (phy_reg < 0) return -EIO; *devices_in_package |= phy_reg;
return 0;}
/** * get_phy_c45_ids - reads the specified addr for its 802.3-c45 IDs. * @bus: the target MII bus * @addr: PHY address on the MII bus * @c45_ids: where to store the c45 ID information. * * Read the PHY "devices in package". If this appears to be valid, read * the PHY identifiers for each device. Return the "devices in package" * and identifiers in @c45_ids. * * Returns zero on success, %-EIO on bus access error, or %-ENODEV if * the "devices in package" is invalid or no device responds. */static int get_phy_c45_ids(struct mii_bus *bus, int addr, struct phy_c45_device_ids *c45_ids){ const int num_ids = ARRAY_SIZE(c45_ids->device_ids); u32 devs_in_pkg = 0; int i, ret, phy_reg;
/* Find first non-zero Devices In package. Device zero is reserved * for 802.3 c45 complied PHYs, so don't probe it at first. */ for (i = 1; i < MDIO_MMD_NUM && (devs_in_pkg == 0 || (devs_in_pkg & 0x1fffffff) == 0x1fffffff); i++) { if (i == MDIO_MMD_VEND1 || i == MDIO_MMD_VEND2) { /* Check that there is a device present at this * address before reading the devices-in-package * register to avoid reading garbage from the PHY. * Some PHYs (88x3310) vendor space is not IEEE802.3 * compliant. */ ret = phy_c45_probe_present(bus, addr, i); if (ret < 0) /* returning -ENODEV doesn't stop bus * scanning */ return (ret == -EIO || ret == -ENODEV) ? -ENODEV : -EIO;
if (!ret) continue; } phy_reg = get_phy_c45_devs_in_pkg(bus, addr, i, &devs_in_pkg); if (phy_reg < 0) return -EIO; }
if ((devs_in_pkg & 0x1fffffff) == 0x1fffffff) { /* If mostly Fs, there is no device there, then let's probe * MMD 0, as some 10G PHYs have zero Devices In package, * e.g. Cortina CS4315/CS4340 PHY. */ phy_reg = get_phy_c45_devs_in_pkg(bus, addr, 0, &devs_in_pkg); if (phy_reg < 0) return -EIO;
/* no device there, let's get out of here */ if ((devs_in_pkg & 0x1fffffff) == 0x1fffffff) return -ENODEV; }
/* Now probe Device Identifiers for each device present. */ for (i = 1; i < num_ids; i++) { if (!(devs_in_pkg & (1 << i))) continue;
if (i == MDIO_MMD_VEND1 || i == MDIO_MMD_VEND2) { /* Probe the "Device Present" bits for the vendor MMDs * to ignore these if they do not contain IEEE 802.3 * registers. */ ret = phy_c45_probe_present(bus, addr, i); if (ret < 0) return ret;
if (!ret) continue; }
phy_reg = mdiobus_c45_read(bus, addr, i, MII_PHYSID1); if (phy_reg < 0) return -EIO; c45_ids->device_ids[i] = phy_reg << 16;
phy_reg = mdiobus_c45_read(bus, addr, i, MII_PHYSID2); if (phy_reg < 0) return -EIO; c45_ids->device_ids[i] |= phy_reg; }
c45_ids->devices_in_package = devs_in_pkg; /* Bit 0 doesn't represent a device, it indicates c22 regs presence */ c45_ids->mmds_present = devs_in_pkg & ~BIT(0);
return 0;}
/** * get_phy_c22_id - reads the specified addr for its clause 22 ID. * @bus: the target MII bus * @addr: PHY address on the MII bus * @phy_id: where to store the ID retrieved. * * Read the 802.3 clause 22 PHY ID from the PHY at @addr on the @bus, * placing it in @phy_id. Return zero on successful read and the ID is * valid, %-EIO on bus access error, or %-ENODEV if no device responds * or invalid ID. */static int get_phy_c22_id(struct mii_bus *bus, int addr, u32 *phy_id){ int phy_reg;
/* Grab the bits from PHYIR1, and put them in the upper half */ phy_reg = mdiobus_read(bus, addr, MII_PHYSID1); if (phy_reg < 0) { /* returning -ENODEV doesn't stop bus scanning */ return (phy_reg == -EIO || phy_reg == -ENODEV) ? -ENODEV : -EIO; }
*phy_id = phy_reg << 16;
/* Grab the bits from PHYIR2, and put them in the lower half */ phy_reg = mdiobus_read(bus, addr, MII_PHYSID2); if (phy_reg < 0) { /* returning -ENODEV doesn't stop bus scanning */ return (phy_reg == -EIO || phy_reg == -ENODEV) ? -ENODEV : -EIO; }
*phy_id |= phy_reg;
/* If the phy_id is mostly Fs, there is no device there */ if ((*phy_id & 0x1fffffff) == 0x1fffffff) return -ENODEV;
return 0;}
/* Extract the phy ID from the compatible string of the form * ethernet-phy-idAAAA.BBBB. */int fwnode_get_phy_id(struct fwnode_handle *fwnode, u32 *phy_id){ unsigned int upper, lower; const char *cp; int ret;
ret = fwnode_property_read_string(fwnode, "compatible", &cp); if (ret) return ret;
if (sscanf(cp, "ethernet-phy-id%4x.%4x", &upper, &lower) != 2) return -EINVAL;
*phy_id = ((upper & GENMASK(15, 0)) << 16) | (lower & GENMASK(15, 0)); return 0;}EXPORT_SYMBOL(fwnode_get_phy_id);
/** * get_phy_device - reads the specified PHY device and returns its @phy_device * struct * @bus: the target MII bus * @addr: PHY address on the MII bus * @is_c45: If true the PHY uses the 802.3 clause 45 protocol * * Probe for a PHY at @addr on @bus. * * When probing for a clause 22 PHY, then read the ID registers. If we find * a valid ID, allocate and return a &struct phy_device. * * When probing for a clause 45 PHY, read the "devices in package" registers. * If the "devices in package" appears valid, read the ID registers for each * MMD, allocate and return a &struct phy_device. * * Returns an allocated &struct phy_device on success, %-ENODEV if there is * no PHY present, or %-EIO on bus access error. */struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45){ struct phy_c45_device_ids c45_ids; u32 phy_id = 0; int r;
c45_ids.devices_in_package = 0; c45_ids.mmds_present = 0; memset(c45_ids.device_ids, 0xff, sizeof(c45_ids.device_ids));
if (is_c45) r = get_phy_c45_ids(bus, addr, &c45_ids); else r = get_phy_c22_id(bus, addr, &phy_id);
if (r) return ERR_PTR(r);
/* PHY device such as the Marvell Alaska 88E2110 will return a PHY ID * of 0 when probed using get_phy_c22_id() with no error. Proceed to * probe with C45 to see if we're able to get a valid PHY ID in the C45 * space, if successful, create the C45 PHY device. */ if (!is_c45 && phy_id == 0 && bus->read_c45) { r = get_phy_c45_ids(bus, addr, &c45_ids); if (!r) return phy_device_create(bus, addr, phy_id, true, &c45_ids); }
return phy_device_create(bus, addr, phy_id, is_c45, &c45_ids);}EXPORT_SYMBOL(get_phy_device);
/** * phy_device_register - Register the phy device on the MDIO bus * @phydev: phy_device structure to be added to the MDIO bus */int phy_device_register(struct phy_device *phydev){ int err;
err = mdiobus_register_device(&phydev->mdio); if (err) return err;
/* Deassert the reset signal */ phy_device_reset(phydev, 0);
/* Run all of the fixups for this PHY */ err = phy_scan_fixups(phydev); if (err) { phydev_err(phydev, "failed to initialize\n"); goto out; }
err = device_add(&phydev->mdio.dev); if (err) { phydev_err(phydev, "failed to add\n"); goto out; }
return 0;
out: /* Assert the reset signal */ phy_device_reset(phydev, 1);
mdiobus_unregister_device(&phydev->mdio); return err;}EXPORT_SYMBOL(phy_device_register);
/** * phy_device_remove - Remove a previously registered phy device from the MDIO bus * @phydev: phy_device structure to remove * * This doesn't free the phy_device itself, it merely reverses the effects * of phy_device_register(). Use phy_device_free() to free the device * after calling this function. */void phy_device_remove(struct phy_device *phydev){ unregister_mii_timestamper(phydev->mii_ts); pse_control_put(phydev->psec);
device_del(&phydev->mdio.dev);
/* Assert the reset signal */ phy_device_reset(phydev, 1);
mdiobus_unregister_device(&phydev->mdio);}EXPORT_SYMBOL(phy_device_remove);
/** * phy_get_c45_ids - Read 802.3-c45 IDs for phy device. * @phydev: phy_device structure to read 802.3-c45 IDs * * Returns zero on success, %-EIO on bus access error, or %-ENODEV if * the "devices in package" is invalid. */int phy_get_c45_ids(struct phy_device *phydev){ return get_phy_c45_ids(phydev->mdio.bus, phydev->mdio.addr, &phydev->c45_ids);}EXPORT_SYMBOL(phy_get_c45_ids);
/** * phy_find_next - finds the next PHY device on the bus * @bus: the target MII bus * @pos: cursor * * Return: next phy_device on the bus, or NULL */struct phy_device *phy_find_next(struct mii_bus *bus, struct phy_device *pos){ for (int addr = pos ? pos->mdio.addr + 1 : 0; addr < PHY_MAX_ADDR; addr++) { struct phy_device *phydev = mdiobus_get_phy(bus, addr);
if (phydev) return phydev; } return NULL;}EXPORT_SYMBOL_GPL(phy_find_next);
/** * phy_prepare_link - prepares the PHY layer to monitor link status * @phydev: target phy_device struct * @handler: callback function for link status change notifications * * Description: Tells the PHY infrastructure to handle the * gory details on monitoring link status (whether through * polling or an interrupt), and to call back to the * connected device driver when the link status changes. * If you want to monitor your own link state, don't call * this function. */static void phy_prepare_link(struct phy_device *phydev, void (*handler)(struct net_device *)){ phydev->adjust_link = handler;}
/** * phy_connect_direct - connect an ethernet device to a specific phy_device * @dev: the network device to connect * @phydev: the pointer to the phy device * @handler: callback function for state change notifications * @interface: PHY device's interface */int phy_connect_direct(struct net_device *dev, struct phy_device *phydev, void (*handler)(struct net_device *), phy_interface_t interface){ int rc;
if (!dev) return -EINVAL;
rc = phy_attach_direct(dev, phydev, phydev->dev_flags, interface); if (rc) return rc;
phy_prepare_link(phydev, handler); if (phy_interrupt_is_valid(phydev)) phy_request_interrupt(phydev);
return 0;}EXPORT_SYMBOL(phy_connect_direct);
/** * phy_connect - connect an ethernet device to a PHY device * @dev: the network device to connect * @bus_id: the id string of the PHY device to connect * @handler: callback function for state change notifications * @interface: PHY device's interface * * Description: Convenience function for connecting ethernet * devices to PHY devices. The default behavior is for * the PHY infrastructure to handle everything, and only notify * the connected driver when the link status changes. If you * don't want, or can't use the provided functionality, you may * choose to call only the subset of functions which provide * the desired functionality. */struct phy_device *phy_connect(struct net_device *dev, const char *bus_id, void (*handler)(struct net_device *), phy_interface_t interface){ struct phy_device *phydev; struct device *d; int rc;
/* Search the list of PHY devices on the mdio bus for the * PHY with the requested name */ d = bus_find_device_by_name(&mdio_bus_type, NULL, bus_id); if (!d) { pr_err("PHY %s not found\n", bus_id); return ERR_PTR(-ENODEV); } phydev = to_phy_device(d);
rc = phy_connect_direct(dev, phydev, handler, interface); put_device(d); if (rc) return ERR_PTR(rc);
return phydev;}EXPORT_SYMBOL(phy_connect);
/** * phy_disconnect - disable interrupts, stop state machine, and detach a PHY * device * @phydev: target phy_device struct */void phy_disconnect(struct phy_device *phydev){ if (phy_is_started(phydev)) phy_stop(phydev);
if (phy_interrupt_is_valid(phydev)) phy_free_interrupt(phydev);
phydev->adjust_link = NULL;
phy_detach(phydev);}EXPORT_SYMBOL(phy_disconnect);
/** * phy_poll_reset - Safely wait until a PHY reset has properly completed * @phydev: The PHY device to poll * * Description: According to IEEE 802.3, Section 2, Subsection 22.2.4.1.1, as * published in 2008, a PHY reset may take up to 0.5 seconds. The MII BMCR * register must be polled until the BMCR_RESET bit clears. * * Furthermore, any attempts to write to PHY registers may have no effect * or even generate MDIO bus errors until this is complete. * * Some PHYs (such as the Marvell 88E1111) don't entirely conform to the * standard and do not fully reset after the BMCR_RESET bit is set, and may * even *REQUIRE* a soft-reset to properly restart autonegotiation. In an * effort to support such broken PHYs, this function is separate from the * standard phy_init_hw() which will zero all the other bits in the BMCR * and reapply all driver-specific and board-specific fixups. */static int phy_poll_reset(struct phy_device *phydev){ /* Poll until the reset bit clears (50ms per retry == 0.6 sec) */ int ret, val;
ret = phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET), 50000, 600000, true); if (ret) return ret; /* Some chips (smsc911x) may still need up to another 1ms after the * BMCR_RESET bit is cleared before they are usable. */ msleep(1); return 0;}
int phy_init_hw(struct phy_device *phydev){ int ret = 0;
/* Deassert the reset signal */ phy_device_reset(phydev, 0);
if (!phydev->drv) return 0;
if (phydev->drv->soft_reset) { ret = phydev->drv->soft_reset(phydev); if (ret < 0) return ret;
/* see comment in genphy_soft_reset for an explanation */ phydev->suspended = 0; }
ret = phy_scan_fixups(phydev); if (ret < 0) return ret;
phy_interface_zero(phydev->possible_interfaces);
if (phydev->drv->config_init) { ret = phydev->drv->config_init(phydev); if (ret < 0) return ret; }
if (phydev->drv->config_intr) { ret = phydev->drv->config_intr(phydev); if (ret < 0) return ret; }
/* Re-apply autonomous EEE disable after soft reset */ if (phydev->autonomous_eee_disabled && phydev->drv->disable_autonomous_eee) { ret = phydev->drv->disable_autonomous_eee(phydev); if (ret) return ret; }
return 0;}EXPORT_SYMBOL(phy_init_hw);
void phy_attached_info(struct phy_device *phydev){ phy_attached_print(phydev, NULL);}EXPORT_SYMBOL(phy_attached_info);
#define ATTACHED_FMT "attached PHY driver %s(mii_bus:phy_addr=%s, irq=%s)"char *phy_attached_info_irq(struct phy_device *phydev){ char *irq_str; char irq_num[8];
switch(phydev->irq) { case PHY_POLL: irq_str = "POLL"; break; case PHY_MAC_INTERRUPT: irq_str = "MAC"; break; default: snprintf(irq_num, sizeof(irq_num), "%d", phydev->irq); irq_str = irq_num; break; }
return kasprintf(GFP_KERNEL, "%s", irq_str);}EXPORT_SYMBOL(phy_attached_info_irq);
void phy_attached_print(struct phy_device *phydev, const char *fmt, ...){ const char *unbound = phydev->drv ? "" : "[unbound] "; char *irq_str = phy_attached_info_irq(phydev);
if (!fmt) { phydev_info(phydev, ATTACHED_FMT "\n", unbound, phydev_name(phydev), irq_str); } else { va_list ap;
phydev_info(phydev, ATTACHED_FMT, unbound, phydev_name(phydev), irq_str);
va_start(ap, fmt); vprintk(fmt, ap); va_end(ap); } kfree(irq_str);}EXPORT_SYMBOL(phy_attached_print);
static void phy_sysfs_create_links(struct phy_device *phydev){ struct net_device *dev = phydev->attached_dev; int err;
if (!dev) return;
err = sysfs_create_link(&phydev->mdio.dev.kobj, &dev->dev.kobj, "attached_dev"); if (err) return;
err = sysfs_create_link_nowarn(&dev->dev.kobj, &phydev->mdio.dev.kobj, "phydev"); if (err) { dev_err(&dev->dev, "could not add device link to %s err %d\n", kobject_name(&phydev->mdio.dev.kobj), err); /* non-fatal - some net drivers can use one netdevice * with more then one phy */ }
phydev->sysfs_links = true;}
static ssize_tphy_standalone_show(struct device *dev, struct device_attribute *attr, char *buf){ struct phy_device *phydev = to_phy_device(dev);
return sysfs_emit(buf, "%d\n", !phydev->attached_dev);}static DEVICE_ATTR_RO(phy_standalone);
/** * phy_sfp_connect_phy - Connect the SFP module's PHY to the upstream PHY * @upstream: pointer to the upstream phy device * @phy: pointer to the SFP module's phy device * * This helper allows keeping track of PHY devices on the link. It adds the * SFP module's phy to the phy namespace of the upstream phy * * Return: 0 on success, otherwise a negative error code. */static int phy_sfp_connect_phy(void *upstream, struct phy_device *phy){ struct phy_device *phydev = upstream; struct net_device *dev = phydev->attached_dev; int ret;
phydev->has_sfp_mod_phy = true; phy_set_upstream_port(phy, phydev->sfp_cage_port);
/* If we aren't attached to a netdev, we can't add the SFP PHY to its * topology. */ if (!dev) return 0;
ret = phy_link_topo_add_phy(dev, phy, PHY_UPSTREAM_PHY, phydev); if (ret) phydev->has_sfp_mod_phy = false;
return ret;}
/** * phy_sfp_disconnect_phy - Disconnect the SFP module's PHY from the upstream PHY * @upstream: pointer to the upstream phy device * @phy: pointer to the SFP module's phy device * * This helper allows keeping track of PHY devices on the link. It removes the * SFP module's phy to the phy namespace of the upstream phy. As the module phy * will be destroyed, re-inserting the same module will add a new phy with a * new index. */static void phy_sfp_disconnect_phy(void *upstream, struct phy_device *phy){ struct phy_device *phydev = upstream; struct net_device *dev = phydev->attached_dev;
phydev->has_sfp_mod_phy = false;
if (dev) phy_link_topo_del_phy(dev, phy);
phy_set_upstream_port(phy, NULL);}
/** * phy_sfp_attach - attach the SFP bus to the PHY upstream network device * @upstream: pointer to the phy device * @bus: sfp bus representing cage being attached * * This is used to fill in the sfp_upstream_ops .attach member. */static void phy_sfp_attach(void *upstream, struct sfp_bus *bus){ struct phy_device *phydev = upstream;
if (phydev->attached_dev) phydev->attached_dev->sfp_bus = bus; phydev->sfp_bus_attached = true;}
/** * phy_sfp_detach - detach the SFP bus from the PHY upstream network device * @upstream: pointer to the phy device * @bus: sfp bus representing cage being attached * * This is used to fill in the sfp_upstream_ops .detach member. */static void phy_sfp_detach(void *upstream, struct sfp_bus *bus){ struct phy_device *phydev = upstream;
if (phydev->attached_dev) phydev->attached_dev->sfp_bus = NULL; phydev->sfp_bus_attached = false;}
static int phy_sfp_module_insert(void *upstream, const struct sfp_eeprom_id *id){ __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); struct phy_device *phydev = upstream; const struct sfp_module_caps *caps; struct phy_port *port;
phy_interface_t iface;
linkmode_zero(sfp_support);
port = phy_get_sfp_port(phydev); if (!port) return -EINVAL;
caps = sfp_get_module_caps(phydev->sfp_bus);
linkmode_and(sfp_support, port->supported, caps->link_modes); if (linkmode_empty(sfp_support)) { dev_err(&phydev->mdio.dev, "incompatible SFP module inserted, no common linkmode\n"); return -EINVAL; }
iface = sfp_select_interface(phydev->sfp_bus, sfp_support); if (iface == PHY_INTERFACE_MODE_NA) { dev_err(&phydev->mdio.dev, "PHY %s does not support the SFP module's requested MII interfaces\n", phydev_name(phydev)); return -EINVAL; }
if (phydev->n_ports == 1) phydev->port = caps->port;
if (port->ops && port->ops->configure_mii) return port->ops->configure_mii(port, true, iface);
return 0;}
static void phy_sfp_module_remove(void *upstream){ struct phy_device *phydev = upstream; struct phy_port *port = phy_get_sfp_port(phydev);
if (port && port->ops && port->ops->configure_mii) port->ops->configure_mii(port, false, PHY_INTERFACE_MODE_NA);
if (phydev->n_ports == 1) phydev->port = PORT_NONE;}
static void phy_sfp_link_up(void *upstream){ struct phy_device *phydev = upstream; struct phy_port *port = phy_get_sfp_port(phydev);
if (port && port->ops && port->ops->link_up) port->ops->link_up(port);}
static void phy_sfp_link_down(void *upstream){ struct phy_device *phydev = upstream; struct phy_port *port = phy_get_sfp_port(phydev);
if (port && port->ops && port->ops->link_down) port->ops->link_down(port);}
static int phy_add_sfp_mod_port(struct phy_device *phydev){ DECLARE_PHY_INTERFACE_MASK(interfaces); const struct sfp_module_caps *caps; struct phy_port *port; int ret = 0;
/* Create mod port */ port = phy_port_alloc(); if (!port) return -ENOMEM;
port->active = true;
caps = sfp_get_module_caps(phydev->sfp_bus);
phy_interface_and(interfaces, phydev->sfp_cage_port->interfaces, caps->interfaces); phy_caps_linkmode_filter_ifaces(port->supported, caps->link_modes, interfaces);
if (phydev->attached_dev) { ret = phy_link_topo_add_port(phydev->attached_dev, port); if (ret) { phy_port_destroy(port); return ret; } }
/* we don't use phy_add_port() here as the module port isn't a direct * interface from the PHY, but rather an extension to the sfp-bus, that * is already represented by its own phy_port */ phydev->mod_port = port;
port->upstream_port = phydev->sfp_cage_port;
return 0;}
static void phy_del_sfp_mod_port(struct phy_device *phydev){ if (!phydev->mod_port) return;
if (phydev->attached_dev) phy_link_topo_del_port(phydev->attached_dev, phydev->mod_port);
phy_port_destroy(phydev->mod_port); phydev->mod_port = NULL;}
static int phy_sfp_module_start(void *upstream){ struct phy_device *phydev = upstream;
/* If there's a downstream SFP module, and it doesn't contain a PHY * device, let's create a phy_port to represent that module. */ if (!phydev->has_sfp_mod_phy) return phy_add_sfp_mod_port(phydev);
return 0;}
static void phy_sfp_module_stop(void *upstream){ struct phy_device *phydev = upstream;
/* Called upon module removal or upstream removal */ if (!phydev->has_sfp_mod_phy) phy_del_sfp_mod_port(phydev);}
static const struct sfp_upstream_ops sfp_phydev_ops = { .attach = phy_sfp_attach, .detach = phy_sfp_detach, .module_insert = phy_sfp_module_insert, .module_remove = phy_sfp_module_remove, .link_up = phy_sfp_link_up, .link_down = phy_sfp_link_down, .connect_phy = phy_sfp_connect_phy, .disconnect_phy = phy_sfp_disconnect_phy, .module_start = phy_sfp_module_start, .module_stop = phy_sfp_module_stop,};
static int phy_add_port(struct phy_device *phydev, struct phy_port *port){ int ret = 0;
if (phydev->n_ports == phydev->max_n_ports) return -EBUSY;
/* We set all ports as active by default, PHY drivers may deactivate * them (when unused) */ port->active = true;
if (port->is_mii) { if (phydev->drv && phydev->drv->attach_mii_port) ret = phydev->drv->attach_mii_port(phydev, port); } else { if (phydev->drv && phydev->drv->attach_mdi_port) ret = phydev->drv->attach_mdi_port(phydev, port); }
if (ret) return ret;
/* The PHY driver might have added, removed or set medium/pairs info, * so update the port supported accordingly. */ phy_port_update_supported(port);
list_add(&port->head, &phydev->ports);
phydev->n_ports++;
return 0;}
static void phy_del_port(struct phy_device *phydev, struct phy_port *port){ if (!phydev->n_ports) return;
list_del(&port->head);
if (phydev->attached_dev) phy_link_topo_del_port(phydev->attached_dev, port);
phydev->n_ports--;}
static struct phy_port *phy_setup_sfp_port(struct phy_device *phydev){ struct phy_port *port = phy_port_alloc(); int ret;
if (!port) return ERR_PTR(-ENOMEM);
port->parent_type = PHY_PORT_PHY; port->phy = phydev;
/* The PHY is a media converter, the port connected to the SFP cage * is a MII port. */ port->is_mii = true; port->is_sfp = true;
/* The port->supported and port->interfaces list will be populated * when attaching the port to the phydev. */ ret = phy_add_port(phydev, port); if (ret) { phy_port_destroy(port); return ERR_PTR(ret); }
return port;}
/** * phy_sfp_probe - probe for a SFP cage attached to this PHY device * @phydev: Pointer to phy_device */static int phy_sfp_probe(struct phy_device *phydev){ struct phy_port *port; struct sfp_bus *bus;
if (!phydev->mdio.dev.fwnode) return 0;
bus = sfp_bus_find_fwnode(phydev->mdio.dev.fwnode); if (IS_ERR(bus)) return PTR_ERR(bus);
phydev->sfp_bus = bus;
if (bus) { port = phy_setup_sfp_port(phydev); if (IS_ERR(port)) { sfp_bus_put(bus); phydev->sfp_bus = NULL; return PTR_ERR(port); }
phydev->sfp_cage_port = port; }
return 0;}
static int phy_sfp_add_upstream(struct phy_device *phydev){ struct sfp_bus *bus = phydev->sfp_bus; int ret;
ret = sfp_bus_add_upstream(bus, phydev, &sfp_phydev_ops); if (ret) { sfp_bus_put(bus); phydev->sfp_bus = NULL; phydev->sfp_cage_port = NULL; return ret; }
/* sfp_bus_add_upstream() grabs a ref to the sfp bus on success, it's * safe to release it now. */ sfp_bus_put(bus);
return 0;}
/** * phy_sfp_release - release resources set up by phy_sfp_probe() * @phydev: the PHY device * * Release the SFP resources set up by a successful phy_sfp_probe(). Unregister * the upstream before destroying its phy_port, so SFP upstream callbacks cannot * race with port destruction. */static void phy_sfp_release(struct phy_device *phydev){ struct phy_port *port, *tmp;
sfp_bus_del_upstream(phydev->sfp_bus); phydev->sfp_bus = NULL;
list_for_each_entry_safe(port, tmp, &phydev->ports, head) { if (!port->is_sfp) continue;
phy_del_port(phydev, port); phy_port_destroy(port); }}
static bool phy_drv_supports_irq(const struct phy_driver *phydrv){ return phydrv->config_intr && phydrv->handle_interrupt;}
/** * phy_detach_internal - detach a PHY device from its network device * @phydev: target phy_device struct * @notify_bus: whether to notify the MDIO bus about the PHY detach * * This detaches the phy device from its network device and the phy * driver, and drops the reference count taken in phy_attach_direct(). */static void phy_detach_internal(struct phy_device *phydev, bool notify_bus){ struct net_device *dev = phydev->attached_dev; struct module *ndev_owner = NULL; struct mii_bus *bus;
if (phydev->devlink) { device_link_del(phydev->devlink); phydev->devlink = NULL; }
if (phydev->sysfs_links) { if (dev) sysfs_remove_link(&dev->dev.kobj, "phydev"); sysfs_remove_link(&phydev->mdio.dev.kobj, "attached_dev"); }
if (!phydev->attached_dev) sysfs_remove_file(&phydev->mdio.dev.kobj, &dev_attr_phy_standalone.attr);
phy_suspend(phydev);
if (notify_bus && phydev->mdio.bus->notify_phy_detach) phydev->mdio.bus->notify_phy_detach(phydev);
if (dev) { struct hwtstamp_provider *hwprov;
/* hwprov may technically be protected by ops lock but * not for devices with a phydev, see phy_link_topo_add_phy() */ hwprov = rtnl_dereference(dev->hwprov); /* Disable timestamp if it is the one selected */ if (hwprov && hwprov->phydev == phydev) { rcu_assign_pointer(dev->hwprov, NULL); kfree_rcu(hwprov, rcu_head); }
phydev->attached_dev->phydev = NULL; phydev->attached_dev = NULL; phy_link_topo_del_phy(dev, phydev); if (phydev->mod_port) phy_link_topo_del_port(dev, phydev->mod_port); }
phydev->phy_link_change = NULL; phydev->phylink = NULL;
module_put(phydev->drv_owner); phydev->drv_owner = NULL;
/* If the device had no specific driver before (i.e. - it * was using the generic driver), we unbind the device * from the generic driver so that there's a chance a * real driver could be loaded */ if (phydev->is_genphy_driven) { /* The release below lets phy_probe() write this field. */ phydev->irq = phydev->mdio.bus->irq[phydev->mdio.addr]; device_release_driver(&phydev->mdio.dev); phydev->is_genphy_driven = 0; }
/* Assert the reset signal */ phy_device_reset(phydev, 1);
/* * The phydev might go away on the put_device() below, so avoid * a use-after-free bug by reading the underlying bus first. */ bus = phydev->mdio.bus;
put_device(&phydev->mdio.dev); if (dev) ndev_owner = dev->dev.parent->driver->owner; if (ndev_owner != bus->owner) module_put(bus->owner);}
/** * phy_detach - detach a PHY device from its network device * @phydev: target phy_device struct * * This detaches the phy device from its network device and the phy * driver, and drops the reference count taken in phy_attach_direct(). */void phy_detach(struct phy_device *phydev){ /* cleanup including bus notification */ phy_detach_internal(phydev, true);}EXPORT_SYMBOL(phy_detach);
/** * phy_attach_direct - attach a network device to a given PHY device pointer * @dev: network device to attach * @phydev: Pointer to phy_device to attach * @flags: PHY device's dev_flags * @interface: PHY device's interface * * Description: Called by drivers to attach to a particular PHY * device. The phy_device is found, and properly hooked up * to the phy_driver. If no driver is attached, then a * generic driver is used. The phy_device is given a ptr to * the attaching device, and given a callback for link status * change. The phy_device is returned to the attaching driver. * This function takes a reference on the phy device. */int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, u32 flags, phy_interface_t interface){ struct mii_bus *bus = phydev->mdio.bus; struct device *d = &phydev->mdio.dev; struct module *ndev_owner = NULL; struct module *drv_owner; int irq = phydev->irq; int err;
/* For Ethernet device drivers that register their own MDIO bus, we * will have bus->owner match ndev_mod, so we do not want to increment * our own module->refcnt here, otherwise we would not be able to * unload later on. */ if (dev) ndev_owner = dev->dev.parent->driver->owner; if (ndev_owner != bus->owner && !try_module_get(bus->owner)) { phydev_err(phydev, "failed to get the bus module\n"); return -EIO; }
get_device(d);
/* Set by every attach, with or without a netdev */ if (phydev->phy_link_change) { phydev_err(phydev, "PHY already attached\n"); err = -EBUSY; goto error_put_device; }
/* Assume that if there is no driver, that it doesn't * exist, and we should use the genphy driver. */ if (!d->driver) { if (phydev->is_c45) d->driver = &genphy_c45_driver.mdiodrv.driver; else d->driver = &genphy_driver.mdiodrv.driver;
phydev->is_genphy_driven = 1; }
drv_owner = d->driver->owner; if (!try_module_get(drv_owner)) { phydev_err(phydev, "failed to get the device driver module\n"); err = -EIO; goto error_put_device; } phydev->drv_owner = drv_owner;
if (phydev->is_genphy_driven) { err = d->driver->probe(d); if (err >= 0) err = device_bind_driver(d);
if (err) goto error_module_put; }
phydev->phy_link_change = phy_link_change; if (dev) { phydev->attached_dev = dev; dev->phydev = phydev;
if (phydev->sfp_bus_attached) dev->sfp_bus = phydev->sfp_bus;
err = phy_link_topo_add_phy(dev, phydev, PHY_UPSTREAM_MAC, dev); if (err) goto error;
if (phydev->mod_port) { err = phy_link_topo_add_port(dev, phydev->mod_port); if (err) goto error; } }
/* Some Ethernet drivers try to connect to a PHY device before * calling register_netdevice() -> netdev_register_kobject() and * does the dev->dev.kobj initialization. Here we only check for * success which indicates that the network device kobject is * ready. Once we do that we still need to keep track of whether * links were successfully set up or not for phy_detach() to * remove them accordingly. */ phydev->sysfs_links = false;
phy_sysfs_create_links(phydev);
if (!phydev->attached_dev) { err = sysfs_create_file(&phydev->mdio.dev.kobj, &dev_attr_phy_standalone.attr); if (err) phydev_err(phydev, "error creating 'phy_standalone' sysfs entry\n"); }
phydev->dev_flags |= flags;
phydev->interface = interface;
phydev->state = PHY_READY;
phydev->interrupts = PHY_INTERRUPT_DISABLED;
/* PHYs can request to use poll mode even though they have an * associated interrupt line. This could be the case if they * detect a broken interrupt handling. */ if (phydev->dev_flags & PHY_F_NO_IRQ) phydev->irq = PHY_POLL;
if (!phy_drv_supports_irq(phydev->drv) && phy_interrupt_is_valid(phydev)) phydev->irq = PHY_POLL;
/* Port is set to PORT_TP by default and the actual PHY driver will set * it to different value depending on the PHY configuration. If we have * the generic PHY driver we can't figure it out, thus set the old * legacy PORT_MII value. */ if (phydev->is_genphy_driven) phydev->port = PORT_MII;
/* Initial carrier state is off as the phy is about to be * (re)initialized. */ if (dev) netif_carrier_off(phydev->attached_dev);
/* Do initial configuration here, now that * we have certain key parameters * (dev_flags and interface) */ err = phy_init_hw(phydev); if (err) goto error;
if (phydev->mdio.bus->notify_phy_attach) { err = phydev->mdio.bus->notify_phy_attach(phydev); if (err) goto error; }
phy_resume(phydev);
/** * If the external phy used by current mac interface is managed by * another mac interface, so we should create a device link between * phy dev and mac dev. */ if (dev && phydev->mdio.bus->parent && dev->dev.parent != phydev->mdio.bus->parent) phydev->devlink = device_link_add(dev->dev.parent, &phydev->mdio.dev, DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS);
return err;
error: /* phy_detach_internal() does all of the cleanup below */ phy_detach_internal(phydev, false); return err;
error_module_put: module_put(phydev->drv_owner); phydev->drv_owner = NULL; phydev->irq = irq; phydev->is_genphy_driven = 0; d->driver = NULL;error_put_device: put_device(d); if (ndev_owner != bus->owner) module_put(bus->owner); return err;}EXPORT_SYMBOL(phy_attach_direct);
int phy_suspend(struct phy_device *phydev){ struct net_device *netdev = phydev->attached_dev; const struct phy_driver *phydrv = phydev->drv; int ret;
if (phydev->suspended || !phydrv) return 0;
phydev->wol_enabled = phy_may_wakeup(phydev) || (netdev && netdev->ethtool->wol_enabled); /* If the device has WOL enabled, we cannot suspend the PHY */ if (phydev->wol_enabled && !(phydrv->flags & PHY_ALWAYS_CALL_SUSPEND)) return -EBUSY;
if (!phydrv->suspend) return 0;
ret = phydrv->suspend(phydev); if (!ret) phydev->suspended = true;
return ret;}EXPORT_SYMBOL(phy_suspend);
int __phy_resume(struct phy_device *phydev){ const struct phy_driver *phydrv = phydev->drv; int ret;
lockdep_assert_held(&phydev->lock);
if (!phydrv || !phydrv->resume) return 0;
ret = phydrv->resume(phydev); if (!ret) phydev->suspended = false;
return ret;}EXPORT_SYMBOL(__phy_resume);
int phy_resume(struct phy_device *phydev){ int ret;
mutex_lock(&phydev->lock); ret = __phy_resume(phydev); mutex_unlock(&phydev->lock);
return ret;}EXPORT_SYMBOL(phy_resume);
/** * phy_reset_after_clk_enable - perform a PHY reset if needed * @phydev: target phy_device struct * * Description: Some PHYs are known to need a reset after their refclk was * enabled. This function evaluates the flags and perform the reset if it's * needed. Returns < 0 on error, 0 if the phy wasn't reset and 1 if the phy * was reset. */int phy_reset_after_clk_enable(struct phy_device *phydev){ if (!phydev || !phydev->drv) return -ENODEV;
if (phydev->drv->flags & PHY_RST_AFTER_CLK_EN) { phy_device_reset(phydev, 1); phy_device_reset(phydev, 0); return 1; }
return 0;}EXPORT_SYMBOL(phy_reset_after_clk_enable);
/* Generic PHY support and helper functions */
/** * genphy_config_advert - sanitize and advertise auto-negotiation parameters * @phydev: target phy_device struct * @advert: auto-negotiation parameters to advertise * * Description: Writes MII_ADVERTISE with the appropriate values, * after sanitizing the values to make sure we only advertise * what is supported. Returns < 0 on error, 0 if the PHY's advertisement * hasn't changed, and > 0 if it has changed. */static int genphy_config_advert(struct phy_device *phydev, const unsigned long *advert){ int err, bmsr, changed = 0; u32 adv;
adv = linkmode_adv_to_mii_adv_t(advert);
/* Setup standard advertisement */ err = phy_modify_changed(phydev, MII_ADVERTISE, ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM, adv); if (err < 0) return err; if (err > 0) changed = 1;
bmsr = phy_read(phydev, MII_BMSR); if (bmsr < 0) return bmsr;
/* Per 802.3-2008, Section 22.2.4.2.16 Extended status all * 1000Mbits/sec capable PHYs shall have the BMSR_ESTATEN bit set to a * logical 1. */ if (!(bmsr & BMSR_ESTATEN)) return changed;
adv = linkmode_adv_to_mii_ctrl1000_t(advert);
err = phy_modify_changed(phydev, MII_CTRL1000, ADVERTISE_1000FULL | ADVERTISE_1000HALF, adv); if (err < 0) return err; if (err > 0) changed = 1;
return changed;}
/** * genphy_c37_config_advert - sanitize and advertise auto-negotiation parameters * @phydev: target phy_device struct * * Description: Writes MII_ADVERTISE with the appropriate values, * after sanitizing the values to make sure we only advertise * what is supported. Returns < 0 on error, 0 if the PHY's advertisement * hasn't changed, and > 0 if it has changed. This function is intended * for Clause 37 1000Base-X mode. */static int genphy_c37_config_advert(struct phy_device *phydev){ u16 adv = 0;
/* Only allow advertising what this PHY supports */ linkmode_and(phydev->advertising, phydev->advertising, phydev->supported);
if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, phydev->advertising)) adv |= ADVERTISE_1000XFULL; if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->advertising)) adv |= ADVERTISE_1000XPAUSE; if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->advertising)) adv |= ADVERTISE_1000XPSE_ASYM;
return phy_modify_changed(phydev, MII_ADVERTISE, ADVERTISE_1000XFULL | ADVERTISE_1000XPAUSE | ADVERTISE_1000XHALF | ADVERTISE_1000XPSE_ASYM, adv);}
/** * genphy_setup_forced - configures/forces speed/duplex from @phydev * @phydev: target phy_device struct * * Description: Configures MII_BMCR to force speed/duplex * to the values in phydev. Assumes that the values are valid. * Please see phy_sanitize_settings(). */int genphy_setup_forced(struct phy_device *phydev){ u16 ctl;
phydev->pause = false; phydev->asym_pause = false;
ctl = mii_bmcr_encode_fixed(phydev->speed, phydev->duplex);
return phy_modify(phydev, MII_BMCR, ~(BMCR_LOOPBACK | BMCR_ISOLATE | BMCR_PDOWN), ctl);}EXPORT_SYMBOL(genphy_setup_forced);
static int genphy_setup_master_slave(struct phy_device *phydev){ u16 ctl = 0;
if (!phydev->is_gigabit_capable) return 0;
switch (phydev->master_slave_set) { case MASTER_SLAVE_CFG_MASTER_PREFERRED: ctl |= CTL1000_PREFER_MASTER; break; case MASTER_SLAVE_CFG_SLAVE_PREFERRED: break; case MASTER_SLAVE_CFG_MASTER_FORCE: ctl |= CTL1000_AS_MASTER; fallthrough; case MASTER_SLAVE_CFG_SLAVE_FORCE: ctl |= CTL1000_ENABLE_MASTER; break; case MASTER_SLAVE_CFG_UNKNOWN: case MASTER_SLAVE_CFG_UNSUPPORTED: return 0; default: phydev_warn(phydev, "Unsupported Master/Slave mode\n"); return -EOPNOTSUPP; }
return phy_modify_changed(phydev, MII_CTRL1000, (CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER | CTL1000_PREFER_MASTER), ctl);}
int genphy_read_master_slave(struct phy_device *phydev){ int cfg, state; int val;
phydev->master_slave_get = MASTER_SLAVE_CFG_UNKNOWN; phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
val = phy_read(phydev, MII_CTRL1000); if (val < 0) return val;
if (val & CTL1000_ENABLE_MASTER) { if (val & CTL1000_AS_MASTER) cfg = MASTER_SLAVE_CFG_MASTER_FORCE; else cfg = MASTER_SLAVE_CFG_SLAVE_FORCE; } else { if (val & CTL1000_PREFER_MASTER) cfg = MASTER_SLAVE_CFG_MASTER_PREFERRED; else cfg = MASTER_SLAVE_CFG_SLAVE_PREFERRED; }
val = phy_read(phydev, MII_STAT1000); if (val < 0) return val;
if (val & LPA_1000MSFAIL) { state = MASTER_SLAVE_STATE_ERR; } else if (phydev->link) { /* this bits are valid only for active link */ if (val & LPA_1000MSRES) state = MASTER_SLAVE_STATE_MASTER; else state = MASTER_SLAVE_STATE_SLAVE; } else { state = MASTER_SLAVE_STATE_UNKNOWN; }
phydev->master_slave_get = cfg; phydev->master_slave_state = state;
return 0;}EXPORT_SYMBOL(genphy_read_master_slave);
/** * genphy_restart_aneg - Enable and Restart Autonegotiation * @phydev: target phy_device struct */int genphy_restart_aneg(struct phy_device *phydev){ /* Don't isolate the PHY if we're negotiating */ return phy_modify(phydev, MII_BMCR, BMCR_ISOLATE, BMCR_ANENABLE | BMCR_ANRESTART);}EXPORT_SYMBOL(genphy_restart_aneg);
/** * genphy_check_and_restart_aneg - Enable and restart auto-negotiation * @phydev: target phy_device struct * @restart: whether aneg restart is requested * * Check, and restart auto-negotiation if needed. */int genphy_check_and_restart_aneg(struct phy_device *phydev, bool restart){ int ret;
if (!restart) { /* Advertisement hasn't changed, but maybe aneg was never on to * begin with? Or maybe phy was isolated? */ ret = phy_read(phydev, MII_BMCR); if (ret < 0) return ret;
if (!(ret & BMCR_ANENABLE) || (ret & BMCR_ISOLATE)) restart = true; }
if (restart) return genphy_restart_aneg(phydev);
return 0;}EXPORT_SYMBOL(genphy_check_and_restart_aneg);
/** * __genphy_config_aneg - restart auto-negotiation or write BMCR * @phydev: target phy_device struct * @changed: whether autoneg is requested * * Description: If auto-negotiation is enabled, we configure the * advertising, and then restart auto-negotiation. If it is not * enabled, then we write the BMCR. */int __genphy_config_aneg(struct phy_device *phydev, bool changed){ __ETHTOOL_DECLARE_LINK_MODE_MASK(fixed_advert); const struct link_capabilities *c; unsigned long *advert; int err;
err = genphy_c45_an_config_eee_aneg(phydev); if (err < 0) return err; else if (err) changed = true;
err = genphy_setup_master_slave(phydev); if (err < 0) return err; else if (err) changed = true;
if (phydev->autoneg == AUTONEG_ENABLE) { /* Only allow advertising what this PHY supports */ linkmode_and(phydev->advertising, phydev->advertising, phydev->supported); advert = phydev->advertising; } else if (phydev->speed < SPEED_1000) { return genphy_setup_forced(phydev); } else { linkmode_zero(fixed_advert);
c = phy_caps_lookup(phydev->speed, phydev->duplex, phydev->supported, true); if (c) linkmode_and(fixed_advert, phydev->supported, c->linkmodes);
advert = fixed_advert; }
err = genphy_config_advert(phydev, advert); if (err < 0) /* error */ return err; else if (err) changed = true;
return genphy_check_and_restart_aneg(phydev, changed);}EXPORT_SYMBOL(__genphy_config_aneg);
/** * genphy_c37_config_aneg - restart auto-negotiation or write BMCR * @phydev: target phy_device struct * * Description: If auto-negotiation is enabled, we configure the * advertising, and then restart auto-negotiation. If it is not * enabled, then we write the BMCR. This function is intended * for use with Clause 37 1000Base-X mode. */int genphy_c37_config_aneg(struct phy_device *phydev){ int err, changed;
if (phydev->autoneg != AUTONEG_ENABLE) return genphy_setup_forced(phydev);
err = phy_modify(phydev, MII_BMCR, BMCR_SPEED1000 | BMCR_SPEED100, BMCR_SPEED1000); if (err) return err;
changed = genphy_c37_config_advert(phydev); if (changed < 0) /* error */ return changed;
if (!changed) { /* Advertisement hasn't changed, but maybe aneg was never on to * begin with? Or maybe phy was isolated? */ int ctl = phy_read(phydev, MII_BMCR);
if (ctl < 0) return ctl;
if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE)) changed = 1; /* do restart aneg */ }
/* Only restart aneg if we are advertising something different * than we were before. */ if (changed > 0) return genphy_restart_aneg(phydev);
return 0;}EXPORT_SYMBOL(genphy_c37_config_aneg);
/** * genphy_aneg_done - return auto-negotiation status * @phydev: target phy_device struct * * Description: Reads the status register and returns 0 either if * auto-negotiation is incomplete, or if there was an error. * Returns BMSR_ANEGCOMPLETE if auto-negotiation is done. */int genphy_aneg_done(struct phy_device *phydev){ int retval = phy_read(phydev, MII_BMSR);
return (retval < 0) ? retval : (retval & BMSR_ANEGCOMPLETE);}EXPORT_SYMBOL(genphy_aneg_done);
/** * genphy_update_link - update link status in @phydev * @phydev: target phy_device struct * * Description: Update the value in phydev->link to reflect the * current link value. In order to do this, we need to read * the status register twice, keeping the second value. */int genphy_update_link(struct phy_device *phydev){ int status = 0, bmcr;
bmcr = phy_read(phydev, MII_BMCR); if (bmcr < 0) return bmcr;
/* Autoneg is being started, therefore disregard BMSR value and * report link as down. */ if (bmcr & BMCR_ANRESTART) goto done;
/* The link state is latched low so that momentary link * drops can be detected. Do not double-read the status * in polling mode to detect such short link drops except * if the link was already down. */ if (!phy_polling_mode(phydev) || !phydev->link) { status = phy_read(phydev, MII_BMSR); if (status < 0) return status; else if (status & BMSR_LSTATUS) goto done; }
/* Read link and autonegotiation status */ status = phy_read(phydev, MII_BMSR); if (status < 0) return status;done: phydev->link = status & BMSR_LSTATUS ? 1 : 0; phydev->autoneg_complete = status & BMSR_ANEGCOMPLETE ? 1 : 0;
/* Consider the case that autoneg was started and "aneg complete" * bit has been reset, but "link up" bit not yet. */ if (phydev->autoneg == AUTONEG_ENABLE && !phydev->autoneg_complete) phydev->link = 0;
return 0;}EXPORT_SYMBOL(genphy_update_link);
int genphy_read_lpa(struct phy_device *phydev){ int lpa, lpagb;
if (phydev->autoneg == AUTONEG_ENABLE) { if (!phydev->autoneg_complete) { mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, 0); mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, 0); return 0; }
if (phydev->is_gigabit_capable) { lpagb = phy_read(phydev, MII_STAT1000); if (lpagb < 0) return lpagb;
if (lpagb & LPA_1000MSFAIL) { int adv = phy_read(phydev, MII_CTRL1000);
if (adv < 0) return adv;
if (adv & CTL1000_ENABLE_MASTER) phydev_err(phydev, "Master/Slave resolution failed, maybe conflicting manual settings?\n"); else phydev_err(phydev, "Master/Slave resolution failed\n"); return -ENOLINK; }
mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, lpagb); }
lpa = phy_read(phydev, MII_LPA); if (lpa < 0) return lpa;
mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa); } else { linkmode_zero(phydev->lp_advertising); }
return 0;}EXPORT_SYMBOL(genphy_read_lpa);
/** * genphy_read_status_fixed - read the link parameters for !aneg mode * @phydev: target phy_device struct * * Read the current duplex and speed state for a PHY operating with * autonegotiation disabled. */int genphy_read_status_fixed(struct phy_device *phydev){ int bmcr = phy_read(phydev, MII_BMCR);
if (bmcr < 0) return bmcr;
if (bmcr & BMCR_FULLDPLX) phydev->duplex = DUPLEX_FULL; else phydev->duplex = DUPLEX_HALF;
if (bmcr & BMCR_SPEED1000) phydev->speed = SPEED_1000; else if (bmcr & BMCR_SPEED100) phydev->speed = SPEED_100; else phydev->speed = SPEED_10;
return 0;}EXPORT_SYMBOL(genphy_read_status_fixed);
/** * genphy_read_status - check the link status and update current link state * @phydev: target phy_device struct * * Description: Check the link, then figure out the current state * by comparing what we advertise with what the link partner * advertises. Start by checking the gigabit possibilities, * then move on to 10/100. */int genphy_read_status(struct phy_device *phydev){ int err, old_link = phydev->link;
/* Update the link, but return if there was an error */ err = genphy_update_link(phydev); if (err) return err;
/* why bother the PHY if nothing can have changed */ if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) return 0;
phydev->master_slave_get = MASTER_SLAVE_CFG_UNSUPPORTED; phydev->master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED; phydev->speed = SPEED_UNKNOWN; phydev->duplex = DUPLEX_UNKNOWN; phydev->pause = false; phydev->asym_pause = false;
if (phydev->is_gigabit_capable) { err = genphy_read_master_slave(phydev); if (err < 0) return err; }
err = genphy_read_lpa(phydev); if (err < 0) return err;
if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) { phy_resolve_aneg_linkmode(phydev); } else if (phydev->autoneg == AUTONEG_DISABLE) { err = genphy_read_status_fixed(phydev); if (err < 0) return err; }
return 0;}EXPORT_SYMBOL(genphy_read_status);
/** * genphy_c37_read_status - check the link status and update current link state * @phydev: target phy_device struct * @changed: pointer where to store if link changed * * Description: Check the link, then figure out the current state * by comparing what we advertise with what the link partner * advertises. This function is for Clause 37 1000Base-X mode. * * If link has changed, @changed is set to true, false otherwise. */int genphy_c37_read_status(struct phy_device *phydev, bool *changed){ int lpa, err, old_link = phydev->link;
/* Update the link, but return if there was an error */ err = genphy_update_link(phydev); if (err) return err;
/* why bother the PHY if nothing can have changed */ if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) { *changed = false; return 0; }
/* Signal link has changed */ *changed = true; phydev->duplex = DUPLEX_UNKNOWN; phydev->pause = false; phydev->asym_pause = false;
if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) { lpa = phy_read(phydev, MII_LPA); if (lpa < 0) return lpa;
linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->lp_advertising, lpa & LPA_LPACK); linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, phydev->lp_advertising, lpa & LPA_1000XFULL); linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->lp_advertising, lpa & LPA_1000XPAUSE); linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->lp_advertising, lpa & LPA_1000XPAUSE_ASYM);
phy_resolve_aneg_linkmode(phydev); } else if (phydev->autoneg == AUTONEG_DISABLE) { int bmcr = phy_read(phydev, MII_BMCR);
if (bmcr < 0) return bmcr;
if (bmcr & BMCR_FULLDPLX) phydev->duplex = DUPLEX_FULL; else phydev->duplex = DUPLEX_HALF; }
return 0;}EXPORT_SYMBOL(genphy_c37_read_status);
/** * genphy_soft_reset - software reset the PHY via BMCR_RESET bit * @phydev: target phy_device struct * * Description: Perform a software PHY reset using the standard * BMCR_RESET bit and poll for the reset bit to be cleared. * * Returns: 0 on success, < 0 on failure */int genphy_soft_reset(struct phy_device *phydev){ u16 res = BMCR_RESET; int ret;
if (phydev->autoneg == AUTONEG_ENABLE) res |= BMCR_ANRESTART;
ret = phy_modify(phydev, MII_BMCR, BMCR_ISOLATE, res); if (ret < 0) return ret;
/* Clause 22 states that setting bit BMCR_RESET sets control registers * to their default value. Therefore the POWER DOWN bit is supposed to * be cleared after soft reset. */ phydev->suspended = 0;
ret = phy_poll_reset(phydev); if (ret) return ret;
/* BMCR may be reset to defaults */ if (phydev->autoneg == AUTONEG_DISABLE) ret = genphy_setup_forced(phydev);
return ret;}EXPORT_SYMBOL(genphy_soft_reset);
irqreturn_t genphy_handle_interrupt_no_ack(struct phy_device *phydev){ /* It seems there are cases where the interrupts are handled by another * entity (ie an IRQ controller embedded inside the PHY) and do not * need any other interraction from phylib. In this case, just trigger * the state machine directly. */ phy_trigger_machine(phydev);
return 0;}EXPORT_SYMBOL(genphy_handle_interrupt_no_ack);
/** * genphy_read_abilities - read PHY abilities from Clause 22 registers * @phydev: target phy_device struct * * Description: Reads the PHY's abilities and populates * phydev->supported accordingly. * * Returns: 0 on success, < 0 on failure */int genphy_read_abilities(struct phy_device *phydev){ int val;
linkmode_set_bit_array(phy_basic_ports_array, ARRAY_SIZE(phy_basic_ports_array), phydev->supported);
val = phy_read(phydev, MII_BMSR); if (val < 0) return val;
linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported, val & BMSR_ANEGCAPABLE);
linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, phydev->supported, val & BMSR_100FULL); linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, phydev->supported, val & BMSR_100HALF); linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, phydev->supported, val & BMSR_10FULL); linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, phydev->supported, val & BMSR_10HALF);
if (val & BMSR_ESTATEN) { val = phy_read(phydev, MII_ESTATUS); if (val < 0) return val;
linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, phydev->supported, val & ESTATUS_1000_TFULL); linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, phydev->supported, val & ESTATUS_1000_THALF); linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, phydev->supported, val & ESTATUS_1000_XFULL); }
/* This is optional functionality. If not supported, we may get an error * which should be ignored. */ genphy_c45_read_eee_abilities(phydev);
return 0;}EXPORT_SYMBOL(genphy_read_abilities);
/* Some PHYs support direct C45 register access but not C22 indirect * MMD access (registers 13 and 14). When discovered via C22, phylib * routes MMD access through the indirect path, which won't work on * these devices. These helpers bypass indirect access and use the bus * C45 accessors directly. */int genphy_read_mmd_c45(struct phy_device *phydev, int devnum, u16 regnum){ struct mii_bus *bus = phydev->mdio.bus; int addr = phydev->mdio.addr;
return __mdiobus_c45_read(bus, addr, devnum, regnum);}EXPORT_SYMBOL(genphy_read_mmd_c45);
int genphy_write_mmd_c45(struct phy_device *phydev, int devnum, u16 regnum, u16 val){ struct mii_bus *bus = phydev->mdio.bus; int addr = phydev->mdio.addr;
return __mdiobus_c45_write(bus, addr, devnum, regnum, val);}EXPORT_SYMBOL(genphy_write_mmd_c45);
/* This is used for the phy device which doesn't support the MMD extended * register access, but it does have side effect when we are trying to access * the MMD register via indirect method. */int genphy_read_mmd_unsupported(struct phy_device *phdev, int devad, u16 regnum){ return -EOPNOTSUPP;}EXPORT_SYMBOL(genphy_read_mmd_unsupported);
int genphy_write_mmd_unsupported(struct phy_device *phdev, int devnum, u16 regnum, u16 val){ return -EOPNOTSUPP;}EXPORT_SYMBOL(genphy_write_mmd_unsupported);
int genphy_suspend(struct phy_device *phydev){ return phy_set_bits(phydev, MII_BMCR, BMCR_PDOWN);}EXPORT_SYMBOL(genphy_suspend);
int genphy_resume(struct phy_device *phydev){ return phy_clear_bits(phydev, MII_BMCR, BMCR_PDOWN);}EXPORT_SYMBOL(genphy_resume);
int genphy_loopback(struct phy_device *phydev, bool enable, int speed){ if (enable) { u16 ctl = BMCR_LOOPBACK; int ret, val;
if (speed == SPEED_10 || speed == SPEED_100 || speed == SPEED_1000) phydev->speed = speed; else if (speed) return -EINVAL;
ctl |= mii_bmcr_encode_fixed(phydev->speed, phydev->duplex);
phy_modify(phydev, MII_BMCR, ~0, ctl);
ret = phy_read_poll_timeout(phydev, MII_BMSR, val, val & BMSR_LSTATUS, 5000, 500000, true); if (ret) return ret; } else { phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK, 0);
phy_config_aneg(phydev); }
return 0;}EXPORT_SYMBOL(genphy_loopback);
/** * phy_remove_link_mode - Remove a supported link mode * @phydev: phy_device structure to remove link mode from * @link_mode: Link mode to be removed * * Description: Some MACs don't support all link modes which the PHY * does. e.g. a 1G MAC often does not support 1000Half. Add a helper * to remove a link mode. */void phy_remove_link_mode(struct phy_device *phydev, u32 link_mode){ linkmode_clear_bit(link_mode, phydev->supported); phy_advertise_supported(phydev);}EXPORT_SYMBOL(phy_remove_link_mode);
static void phy_copy_pause_bits(unsigned long *dst, unsigned long *src){ linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, dst, linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, src)); linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT, dst, linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, src));}
/** * phy_advertise_supported - Advertise all supported modes * @phydev: target phy_device struct * * Description: Called to advertise all supported modes, doesn't touch * pause mode advertising. */void phy_advertise_supported(struct phy_device *phydev){ __ETHTOOL_DECLARE_LINK_MODE_MASK(new);
linkmode_copy(new, phydev->supported); phy_copy_pause_bits(new, phydev->advertising); linkmode_copy(phydev->advertising, new);}EXPORT_SYMBOL(phy_advertise_supported);
/** * phy_advertise_eee_all - Advertise all supported EEE modes * @phydev: target phy_device struct * * Description: Per default phylib preserves the EEE advertising at the time of * phy probing, which might be a subset of the supported EEE modes. Use this * function when all supported EEE modes should be advertised. This does not * trigger auto-negotiation, so must be called before phy_start()/ * phylink_start() which will start auto-negotiation. */void phy_advertise_eee_all(struct phy_device *phydev){ linkmode_andnot(phydev->advertising_eee, phydev->supported_eee, phydev->eee_disabled_modes);}EXPORT_SYMBOL_GPL(phy_advertise_eee_all);
/** * phy_support_eee - Set initial EEE policy configuration * @phydev: Target phy_device struct * * This function configures the initial policy for Energy Efficient Ethernet * (EEE) on the specified PHY device, influencing that EEE capabilities are * advertised before the link is established. It should be called during PHY * registration by the MAC driver and/or the PHY driver (for SmartEEE PHYs) * if MAC supports LPI or PHY is capable to compensate missing LPI functionality * of the MAC. * * The function sets default EEE policy parameters, including preparing the PHY * to advertise EEE capabilities based on hardware support. * * It also sets the expected configuration for Low Power Idle (LPI) in the MAC * driver. If the PHY framework determines that both local and remote * advertisements support EEE, and the negotiated link mode is compatible with * EEE, it will set enable_tx_lpi = true. The MAC driver is expected to act on * this setting by enabling the LPI timer if enable_tx_lpi is set. */void phy_support_eee(struct phy_device *phydev){ linkmode_andnot(phydev->advertising_eee, phydev->supported_eee, phydev->eee_disabled_modes); phydev->eee_cfg.tx_lpi_enabled = true; phydev->eee_cfg.eee_enabled = true;
/* If the PHY supports autonomous EEE, disable it so the MAC can * manage LPI signaling instead. The flag is stored so it can be * re-applied after a PHY soft reset (e.g. suspend/resume). */ if (phydev->drv && phydev->drv->disable_autonomous_eee) { int ret = phydev->drv->disable_autonomous_eee(phydev);
if (ret) phydev_warn(phydev, "Failed to disable autonomous EEE: %pe\n", ERR_PTR(ret)); else phydev->autonomous_eee_disabled = true; }}EXPORT_SYMBOL(phy_support_eee);
/** * phy_disable_eee - Disable EEE for the PHY * @phydev: Target phy_device struct * * This function is used by MAC drivers for MAC's which don't support EEE. * It disables EEE on the PHY layer. */void phy_disable_eee(struct phy_device *phydev){ linkmode_zero(phydev->advertising_eee); phydev->eee_cfg.tx_lpi_enabled = false; phydev->eee_cfg.eee_enabled = false; /* don't let userspace re-enable EEE advertisement */ linkmode_fill(phydev->eee_disabled_modes);}EXPORT_SYMBOL_GPL(phy_disable_eee);
/** * phy_support_sym_pause - Enable support of symmetrical pause * @phydev: target phy_device struct * * Description: Called by the MAC to indicate is supports symmetrical * Pause, but not asym pause. */void phy_support_sym_pause(struct phy_device *phydev){ linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported); phy_copy_pause_bits(phydev->advertising, phydev->supported);}EXPORT_SYMBOL(phy_support_sym_pause);
/** * phy_support_asym_pause - Enable support of asym pause * @phydev: target phy_device struct * * Description: Called by the MAC to indicate is supports Asym Pause. */void phy_support_asym_pause(struct phy_device *phydev){ phy_copy_pause_bits(phydev->advertising, phydev->supported);}EXPORT_SYMBOL(phy_support_asym_pause);
/** * phy_set_sym_pause - Configure symmetric Pause * @phydev: target phy_device struct * @rx: Receiver Pause is supported * @tx: Transmit Pause is supported * @autoneg: Auto neg should be used * * Description: Configure advertised Pause support depending on if * receiver pause and pause auto neg is supported. Generally called * from the set_pauseparam .ndo. */void phy_set_sym_pause(struct phy_device *phydev, bool rx, bool tx, bool autoneg){ linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported);
if (rx && tx && autoneg) linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported);
linkmode_copy(phydev->advertising, phydev->supported);}EXPORT_SYMBOL(phy_set_sym_pause);
/** * phy_set_asym_pause - Configure Pause and Asym Pause * @phydev: target phy_device struct * @rx: Receiver Pause is supported * @tx: Transmit Pause is supported * * Description: Configure advertised Pause support depending on if * transmit and receiver pause is supported. If there has been a * change in adverting, trigger a new autoneg. Generally called from * the set_pauseparam .ndo. */void phy_set_asym_pause(struct phy_device *phydev, bool rx, bool tx){ __ETHTOOL_DECLARE_LINK_MODE_MASK(oldadv);
linkmode_copy(oldadv, phydev->advertising); linkmode_set_pause(phydev->advertising, tx, rx);
if (!linkmode_equal(oldadv, phydev->advertising) && phydev->autoneg) phy_start_aneg(phydev);}EXPORT_SYMBOL(phy_set_asym_pause);
/** * phy_validate_pause - Test if the PHY/MAC support the pause configuration * @phydev: phy_device struct * @pp: requested pause configuration * * Description: Test if the PHY/MAC combination supports the Pause * configuration the user is requesting. Returns True if it is * supported, false otherwise. */bool phy_validate_pause(struct phy_device *phydev, struct ethtool_pauseparam *pp){ if (!linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported) && pp->rx_pause) return false;
if (!linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported) && pp->rx_pause != pp->tx_pause) return false;
return true;}EXPORT_SYMBOL(phy_validate_pause);
/** * phy_get_pause - resolve negotiated pause modes * @phydev: phy_device struct * @tx_pause: pointer to bool to indicate whether transmit pause should be * enabled. * @rx_pause: pointer to bool to indicate whether receive pause should be * enabled. * * Resolve and return the flow control modes according to the negotiation * result. This includes checking that we are operating in full duplex mode. * See linkmode_resolve_pause() for further details. */void phy_get_pause(struct phy_device *phydev, bool *tx_pause, bool *rx_pause){ if (phydev->duplex != DUPLEX_FULL) { *tx_pause = false; *rx_pause = false; return; }
return linkmode_resolve_pause(phydev->advertising, phydev->lp_advertising, tx_pause, rx_pause);}EXPORT_SYMBOL(phy_get_pause);
#if IS_ENABLED(CONFIG_OF_MDIO)static int phy_get_u32_property(struct device *dev, const char *name, u32 *val){ return device_property_read_u32(dev, name, val);}#elsestatic int phy_get_u32_property(struct device *dev, const char *name, u32 *val){ return -EINVAL;}#endif
/** * phy_get_internal_delay - returns the index of the internal delay * @phydev: phy_device struct * @delay_values: array of delays the PHY supports * @size: the size of the delay array * @is_rx: boolean to indicate to get the rx internal delay * * Returns the index within the array of internal delay passed in. * If the device property is not present then the interface type is checked * if the interface defines use of internal delay then a 1 is returned otherwise * a 0 is returned. * The array must be in ascending order. If PHY does not have an ascending order * array then size = 0 and the value of the delay property is returned. * Return -EINVAL if the delay is invalid or cannot be found. */s32 phy_get_internal_delay(struct phy_device *phydev, const int *delay_values, int size, bool is_rx){ struct device *dev = &phydev->mdio.dev; int i, ret; u32 delay;
if (is_rx) { ret = phy_get_u32_property(dev, "rx-internal-delay-ps", &delay); if (ret < 0 && size == 0) { if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) return 1; else return 0; }
} else { ret = phy_get_u32_property(dev, "tx-internal-delay-ps", &delay); if (ret < 0 && size == 0) { if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) return 1; else return 0; } }
if (ret < 0) return ret;
if (size == 0) return delay;
if (delay < delay_values[0] || delay > delay_values[size - 1]) { phydev_err(phydev, "Delay %d is out of range\n", delay); return -EINVAL; }
if (delay == delay_values[0]) return 0;
for (i = 1; i < size; i++) { if (delay == delay_values[i]) return i;
/* Find an approximate index by looking up the table */ if (delay > delay_values[i - 1] && delay < delay_values[i]) { if (delay - delay_values[i - 1] < delay_values[i] - delay) return i - 1; else return i; } }
phydev_err(phydev, "error finding internal delay index for %d\n", delay);
return -EINVAL;}EXPORT_SYMBOL(phy_get_internal_delay);
/** * phy_get_tx_amplitude_gain - stores tx amplitude gain in @val * @phydev: phy_device struct * @dev: pointer to the devices device struct * @linkmode: linkmode for which the tx amplitude gain should be retrieved * @val: tx amplitude gain * * Returns: 0 on success, < 0 on failure */int phy_get_tx_amplitude_gain(struct phy_device *phydev, struct device *dev, enum ethtool_link_mode_bit_indices linkmode, u32 *val){ switch (linkmode) { case ETHTOOL_LINK_MODE_100baseT_Full_BIT: return phy_get_u32_property(dev, "tx-amplitude-100base-tx-percent", val); default: return -EINVAL; }}EXPORT_SYMBOL_GPL(phy_get_tx_amplitude_gain);
/** * phy_get_mac_termination - stores MAC termination in @val * @phydev: phy_device struct * @dev: pointer to the devices device struct * @val: MAC termination * * Returns: 0 on success, < 0 on failure */int phy_get_mac_termination(struct phy_device *phydev, struct device *dev, u32 *val){ return phy_get_u32_property(dev, "mac-termination-ohms", val);}EXPORT_SYMBOL_GPL(phy_get_mac_termination);
static int phy_led_set_brightness(struct led_classdev *led_cdev, enum led_brightness value){ struct phy_led *phyled = to_phy_led(led_cdev); struct phy_device *phydev = phyled->phydev; int err;
mutex_lock(&phydev->lock); err = phydev->drv->led_brightness_set(phydev, phyled->index, value); mutex_unlock(&phydev->lock);
return err;}
static int phy_led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on, unsigned long *delay_off){ struct phy_led *phyled = to_phy_led(led_cdev); struct phy_device *phydev = phyled->phydev; int err;
mutex_lock(&phydev->lock); err = phydev->drv->led_blink_set(phydev, phyled->index, delay_on, delay_off); mutex_unlock(&phydev->lock);
return err;}
static __maybe_unused struct device *phy_led_hw_control_get_device(struct led_classdev *led_cdev){ struct phy_led *phyled = to_phy_led(led_cdev); struct phy_device *phydev = phyled->phydev;
if (phydev->attached_dev) return &phydev->attached_dev->dev; return NULL;}
static int __maybe_unusedphy_led_hw_control_get(struct led_classdev *led_cdev, unsigned long *rules){ struct phy_led *phyled = to_phy_led(led_cdev); struct phy_device *phydev = phyled->phydev; int err;
mutex_lock(&phydev->lock); err = phydev->drv->led_hw_control_get(phydev, phyled->index, rules); mutex_unlock(&phydev->lock);
return err;}
static int __maybe_unusedphy_led_hw_control_set(struct led_classdev *led_cdev, unsigned long rules){ struct phy_led *phyled = to_phy_led(led_cdev); struct phy_device *phydev = phyled->phydev; int err;
mutex_lock(&phydev->lock); err = phydev->drv->led_hw_control_set(phydev, phyled->index, rules); mutex_unlock(&phydev->lock);
return err;}
static __maybe_unused int phy_led_hw_is_supported(struct led_classdev *led_cdev, unsigned long rules){ struct phy_led *phyled = to_phy_led(led_cdev); struct phy_device *phydev = phyled->phydev; int err;
mutex_lock(&phydev->lock); err = phydev->drv->led_hw_is_supported(phydev, phyled->index, rules); mutex_unlock(&phydev->lock);
return err;}
static void phy_leds_unregister(struct phy_device *phydev){ struct phy_led *phyled, *tmp;
list_for_each_entry_safe(phyled, tmp, &phydev->leds, list) { led_classdev_unregister(&phyled->led_cdev); list_del(&phyled->list); }}
static int of_phy_led(struct phy_device *phydev, struct device_node *led){ struct device *dev = &phydev->mdio.dev; struct led_init_data init_data = {}; struct led_classdev *cdev; unsigned long modes = 0; struct phy_led *phyled; u32 index; int err;
phyled = devm_kzalloc(dev, sizeof(*phyled), GFP_KERNEL); if (!phyled) return -ENOMEM;
cdev = &phyled->led_cdev; phyled->phydev = phydev;
err = of_property_read_u32(led, "reg", &index); if (err) return err; if (index > U8_MAX) return -EINVAL;
if (of_property_read_bool(led, "active-high")) set_bit(PHY_LED_ACTIVE_HIGH, &modes); if (of_property_read_bool(led, "active-low")) set_bit(PHY_LED_ACTIVE_LOW, &modes); if (of_property_read_bool(led, "inactive-high-impedance")) set_bit(PHY_LED_INACTIVE_HIGH_IMPEDANCE, &modes);
if (WARN_ON(modes & BIT(PHY_LED_ACTIVE_LOW) && modes & BIT(PHY_LED_ACTIVE_HIGH))) return -EINVAL;
if (modes) { /* Return error if asked to set polarity modes but not supported */ if (!phydev->drv->led_polarity_set) return -EINVAL;
err = phydev->drv->led_polarity_set(phydev, index, modes); if (err) return err; }
phyled->index = index; if (phydev->drv->led_brightness_set) cdev->brightness_set_blocking = phy_led_set_brightness; if (phydev->drv->led_blink_set) cdev->blink_set = phy_led_blink_set;
#ifdef CONFIG_LEDS_TRIGGERS if (phydev->drv->led_hw_is_supported && phydev->drv->led_hw_control_set && phydev->drv->led_hw_control_get) { cdev->hw_control_is_supported = phy_led_hw_is_supported; cdev->hw_control_set = phy_led_hw_control_set; cdev->hw_control_get = phy_led_hw_control_get; cdev->hw_control_trigger = "netdev"; }
cdev->hw_control_get_device = phy_led_hw_control_get_device;#endif cdev->max_brightness = 1; init_data.devicename = dev_name(&phydev->mdio.dev); init_data.fwnode = of_fwnode_handle(led); init_data.devname_mandatory = true;
err = led_classdev_register_ext(dev, cdev, &init_data); if (err) return err;
list_add(&phyled->list, &phydev->leds);
return 0;}
static int of_phy_leds(struct phy_device *phydev){ struct device_node *node = phydev->mdio.dev.of_node; struct device_node *leds; int err;
if (!IS_ENABLED(CONFIG_OF_MDIO)) return 0;
if (!node) return 0;
leds = of_get_child_by_name(node, "leds"); if (!leds) return 0;
/* Check if the PHY driver have at least an OP to * set the LEDs. */ if (!(phydev->drv->led_brightness_set || phydev->drv->led_blink_set || phydev->drv->led_hw_control_set)) { phydev_dbg(phydev, "ignoring leds node defined with no PHY driver support\n"); goto exit; }
for_each_available_child_of_node_scoped(leds, led) { err = of_phy_led(phydev, led); if (err) { of_node_put(leds); phy_leds_unregister(phydev); return err; } }
exit: of_node_put(leds); return 0;}
static void phy_cleanup_ports(struct phy_device *phydev){ struct phy_port *tmp, *port;
list_for_each_entry_safe(port, tmp, &phydev->ports, head) { phy_del_port(phydev, port); phy_port_destroy(port); }}
static int phy_default_setup_single_port(struct phy_device *phydev){ struct phy_port *port = phy_port_alloc(); unsigned long mode; int ret;
if (!port) return -ENOMEM;
port->parent_type = PHY_PORT_PHY; port->phy = phydev;
/* Let the PHY driver know that this port was never described anywhere. * This is the usual case, where we assume single-port PHY devices with * no SFP. In that case, the port supports exactly the same thing as * the PHY itself. * * However, this can also be because we have a combo-port PHY, with * only one port described in DT, through SFP for example. * * In that case, the PHY driver will be in charge of saying what we can * do on that non-represented port. */ port->not_described = true; linkmode_copy(port->supported, phydev->supported); port->mediums = phy_caps_mediums_from_linkmodes(port->supported);
for_each_set_bit(mode, port->supported, __ETHTOOL_LINK_MODE_MASK_NBITS) port->pairs = max_t(int, port->pairs, ethtool_linkmode_n_pairs(mode));
ret = phy_add_port(phydev, port); if (ret) phy_port_destroy(port);
return ret;}
static int of_phy_ports(struct phy_device *phydev){ struct device_node *node = phydev->mdio.dev.of_node; struct device_node *mdi; struct phy_port *port; int err;
if (!IS_ENABLED(CONFIG_OF_MDIO)) return 0;
if (!node) return 0;
mdi = of_get_child_by_name(node, "mdi"); if (!mdi) return 0;
for_each_available_child_of_node_scoped(mdi, port_node) { port = phy_of_parse_port(port_node); if (IS_ERR(port)) { err = PTR_ERR(port); goto out_err; }
port->parent_type = PHY_PORT_PHY; port->phy = phydev;
linkmode_copy(port->supported, phydev->supported);
err = phy_add_port(phydev, port); if (err) { phy_port_destroy(port); goto out_err; } } of_node_put(mdi);
return 0;
out_err: phy_cleanup_ports(phydev); of_node_put(mdi); return err;}
static int phy_setup_ports(struct phy_device *phydev){ __ETHTOOL_DECLARE_LINK_MODE_MASK(ports_supported); struct phy_port *port; int ret;
ret = of_phy_ports(phydev); if (ret) return ret;
/* We don't support SFP with genphy drivers. Also, genphy driver * binding occurs with RTNL help, which will deadlock the call to * sfp_bus_add_upstream(). */ if (!phydev->is_genphy_driven) { ret = phy_sfp_probe(phydev); if (ret) goto err_ports; }
if (phydev->n_ports < phydev->max_n_ports) { ret = phy_default_setup_single_port(phydev); if (ret) goto err_sfp; }
linkmode_zero(ports_supported);
/* Aggregate the supported modes, which are made-up of : * - What the PHY itself supports * - What the sum of all ports support */ list_for_each_entry(port, &phydev->ports, head) if (port->active) linkmode_or(ports_supported, ports_supported, port->supported);
if (!linkmode_empty(ports_supported)) linkmode_and(phydev->supported, phydev->supported, ports_supported);
/* For now, the phy->port field is set as the first active port's type */ list_for_each_entry(port, &phydev->ports, head) if (port->active) { phydev->port = phy_port_get_type(port); break; }
ret = phy_sfp_add_upstream(phydev); if (ret) goto err_sfp;
return 0;
err_sfp: phy_sfp_release(phydev);err_ports: phy_cleanup_ports(phydev); return ret;}
/** * phy_get_sfp_port() - Returns the first valid SFP port of a PHY * @phydev: pointer to the PHY device to get the SFP port from * * Returns: The first active SFP (serdes) port of a PHY device, NULL if none * exist. */struct phy_port *phy_get_sfp_port(struct phy_device *phydev){ struct phy_port *port;
list_for_each_entry(port, &phydev->ports, head) if (port->active && port->is_sfp) return port;
return NULL;}EXPORT_SYMBOL_GPL(phy_get_sfp_port);
/** * phy_set_upstream_port() - Sets the phy_port controlling the MII this PHY is * attached to. * @phydev: pointer to the PHY device we set the upstream of. * @port: The phy_port upstream of this PHY, can be NULL. */void phy_set_upstream_port(struct phy_device *phydev, struct phy_port *port){ struct phy_port *local_port;
ASSERT_RTNL();
phydev->upstream_port = port;
phy_for_each_port(phydev, local_port) local_port->upstream_port = port;}EXPORT_SYMBOL_GPL(phy_set_upstream_port);
/** * fwnode_mdio_find_device - Given a fwnode, find the mdio_device * @fwnode: pointer to the mdio_device's fwnode * * If successful, returns a pointer to the mdio_device with the embedded * struct device refcount incremented by one, or NULL on failure. * The caller should call put_device() on the mdio_device after its use. */struct mdio_device *fwnode_mdio_find_device(struct fwnode_handle *fwnode){ struct device *d;
if (!fwnode) return NULL;
d = bus_find_device_by_fwnode(&mdio_bus_type, fwnode); if (!d) return NULL;
return to_mdio_device(d);}EXPORT_SYMBOL(fwnode_mdio_find_device);
/** * fwnode_phy_find_device - For provided phy_fwnode, find phy_device. * * @phy_fwnode: Pointer to the phy's fwnode. * * If successful, returns a pointer to the phy_device with the embedded * struct device refcount incremented by one, or NULL on failure. */struct phy_device *fwnode_phy_find_device(struct fwnode_handle *phy_fwnode){ struct mdio_device *mdiodev;
mdiodev = fwnode_mdio_find_device(phy_fwnode); if (!mdiodev) return NULL;
if (mdiodev->flags & MDIO_DEVICE_FLAG_PHY) return to_phy_device(&mdiodev->dev);
put_device(&mdiodev->dev);
return NULL;}EXPORT_SYMBOL(fwnode_phy_find_device);
/** * fwnode_get_phy_node - Get the phy_node using the named reference. * @fwnode: Pointer to fwnode from which phy_node has to be obtained. * * Refer return conditions of fwnode_find_reference(). * For ACPI, only "phy-handle" is supported. Legacy DT properties "phy" * and "phy-device" are not supported in ACPI. DT supports all the three * named references to the phy node. */struct fwnode_handle *fwnode_get_phy_node(const struct fwnode_handle *fwnode){ struct fwnode_handle *phy_node;
/* Only phy-handle is used for ACPI */ phy_node = fwnode_find_reference(fwnode, "phy-handle", 0); if (!IS_ERR(phy_node) || is_acpi_node(fwnode)) return phy_node; phy_node = fwnode_find_reference(fwnode, "phy", 0); if (!IS_ERR(phy_node)) return phy_node; return fwnode_find_reference(fwnode, "phy-device", 0);}EXPORT_SYMBOL_GPL(fwnode_get_phy_node);
/** * phy_probe - probe and init a PHY device * @dev: device to probe and init * * Take care of setting up the phy_device structure, set the state to READY. */static int phy_probe(struct device *dev){ struct phy_device *phydev = to_phy_device(dev); struct device_driver *drv = phydev->mdio.dev.driver; struct phy_driver *phydrv = to_phy_driver(drv); int err = 0;
phydev->drv = phydrv;
/* Disable the interrupt if the PHY doesn't support it * but the interrupt is still a valid one */ if (!phy_drv_supports_irq(phydrv) && phy_interrupt_is_valid(phydev)) phydev->irq = PHY_POLL;
if (phydrv->flags & PHY_IS_INTERNAL) phydev->is_internal = true;
/* Deassert the reset signal */ phy_device_reset(phydev, 0);
if (phydev->drv->probe) { err = phydev->drv->probe(phydev); if (err) goto out_reset; }
phy_disable_interrupts(phydev);
/* Start out supporting everything. Eventually, * a controller will attach, and may modify one * or both of these values */ if (phydrv->features) { linkmode_copy(phydev->supported, phydrv->features); genphy_c45_read_eee_abilities(phydev); } else if (phydrv->get_features) err = phydrv->get_features(phydev); else if (phydev->is_c45) err = genphy_c45_pma_read_abilities(phydev); else err = genphy_read_abilities(phydev);
if (err) goto out_remove;
if (!linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported)) phydev->autoneg = 0;
if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, phydev->supported)) phydev->is_gigabit_capable = 1; if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, phydev->supported)) phydev->is_gigabit_capable = 1;
of_set_phy_supported(phydev);
err = phy_setup_ports(phydev); if (err) goto out_remove;
phy_advertise_supported(phydev);
/* Get PHY default EEE advertising modes and handle them as potentially * safe initial configuration. */ err = genphy_c45_read_eee_adv(phydev, phydev->advertising_eee); if (err) goto out_sfp_release;
/* Get the EEE modes we want to prohibit. */ of_set_phy_eee_broken(phydev);
/* Some PHYs may advertise, by default, not support EEE modes. So, * we need to clean them. In addition remove all disabled EEE modes. */ linkmode_and(phydev->advertising_eee, phydev->supported_eee, phydev->advertising_eee); linkmode_andnot(phydev->advertising_eee, phydev->advertising_eee, phydev->eee_disabled_modes);
/* There is no "enabled" flag. If PHY is advertising, assume it is * kind of enabled. */ phydev->eee_cfg.eee_enabled = !linkmode_empty(phydev->advertising_eee);
/* Get master/slave strap overrides */ of_set_phy_timing_role(phydev);
/* The Pause Frame bits indicate that the PHY can support passing * pause frames. During autonegotiation, the PHYs will determine if * they should allow pause frames to pass. The MAC driver should then * use that result to determine whether to enable flow control via * pause frames. * * Normally, PHY drivers should not set the Pause bits, and instead * allow phylib to do that. However, there may be some situations * (e.g. hardware erratum) where the driver wants to set only one * of these bits. */ if (!test_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported) && !test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported)) { linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported); linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported); }
/* Register the PHY LED triggers */ if (!phydev->is_on_sfp_module) phy_led_triggers_register(phydev);
/* Get the LEDs from the device tree, and instantiate standard * LEDs for them. */ if (IS_ENABLED(CONFIG_PHYLIB_LEDS) && !phy_driver_is_genphy(phydev)) { err = of_phy_leds(phydev); if (err) goto out_unreg_led_triggers; }
/* Set the state to READY by default */ phydev->state = PHY_READY;
return 0;
out_unreg_led_triggers: if (!phydev->is_on_sfp_module) phy_led_triggers_unregister(phydev);
out_sfp_release: phy_sfp_release(phydev); phy_cleanup_ports(phydev);
out_remove: if (phydev->drv->remove) phydev->drv->remove(phydev);
out_reset: /* Re-assert the reset signal on error */ phy_device_reset(phydev, 1);
return err;}
static int phy_remove(struct device *dev){ struct phy_device *phydev = to_phy_device(dev);
cancel_delayed_work_sync(&phydev->state_queue);
if (IS_ENABLED(CONFIG_PHYLIB_LEDS) && !phy_driver_is_genphy(phydev)) phy_leds_unregister(phydev);
if (!phydev->is_on_sfp_module) phy_led_triggers_unregister(phydev);
phydev->state = PHY_DOWN;
phy_sfp_release(phydev); phy_cleanup_ports(phydev);
if (phydev->drv && phydev->drv->remove) phydev->drv->remove(phydev);
/* Assert the reset signal */ phy_device_reset(phydev, 1);
phydev->drv = NULL;
return 0;}
/** * phy_driver_register - register a phy_driver with the PHY layer * @new_driver: new phy_driver to register * @owner: module owning this PHY */static int phy_driver_register(struct phy_driver *new_driver, struct module *owner){ int retval;
/* Either the features are hard coded, or dynamically * determined. It cannot be both. */ if (WARN_ON(new_driver->features && new_driver->get_features)) { pr_err("%s: features and get_features must not both be set\n", new_driver->name); return -EINVAL; }
/* PHYLIB device drivers must not match using a DT compatible table * as this bypasses our checks that the mdiodev that is being matched * is backed by a struct phy_device. If such a case happens, we will * make out-of-bounds accesses and lockup in phydev->lock. */ if (WARN(new_driver->mdiodrv.driver.of_match_table, "%s: driver must not provide a DT match table\n", new_driver->name)) return -EINVAL;
new_driver->mdiodrv.flags |= MDIO_DEVICE_IS_PHY; new_driver->mdiodrv.driver.name = new_driver->name; new_driver->mdiodrv.driver.bus = &mdio_bus_type; new_driver->mdiodrv.driver.probe = phy_probe; new_driver->mdiodrv.driver.remove = phy_remove; new_driver->mdiodrv.driver.owner = owner; new_driver->mdiodrv.driver.probe_type = PROBE_FORCE_SYNCHRONOUS;
retval = driver_register(&new_driver->mdiodrv.driver); if (retval) { pr_err("%s: Error %d in registering driver\n", new_driver->name, retval);
return retval; }
pr_debug("%s: Registered new driver\n", new_driver->name);
return 0;}
static void phy_driver_unregister(struct phy_driver *drv){ driver_unregister(&drv->mdiodrv.driver);}
int phy_drivers_register(struct phy_driver *new_driver, int n, struct module *owner){ int i, ret = 0;
for (i = 0; i < n; i++) { ret = phy_driver_register(new_driver + i, owner); if (ret) { while (i-- > 0) phy_driver_unregister(new_driver + i); break; } } return ret;}EXPORT_SYMBOL(phy_drivers_register);
void phy_drivers_unregister(struct phy_driver *drv, int n){ int i;
for (i = 0; i < n; i++) phy_driver_unregister(drv + i);}EXPORT_SYMBOL(phy_drivers_unregister);
static struct phy_driver genphy_driver = { .phy_id = 0xffffffff, .phy_id_mask = 0xffffffff, .name = "Generic PHY", .get_features = genphy_read_abilities, .suspend = genphy_suspend, .resume = genphy_resume, .set_loopback = genphy_loopback,};
static const struct ethtool_phy_ops phy_ethtool_phy_ops = { .get_sset_count = phy_ethtool_get_sset_count, .get_strings = phy_ethtool_get_strings, .get_stats = phy_ethtool_get_stats, .get_plca_cfg = phy_ethtool_get_plca_cfg, .set_plca_cfg = phy_ethtool_set_plca_cfg, .get_plca_status = phy_ethtool_get_plca_status, .start_cable_test = phy_start_cable_test, .start_cable_test_tdr = phy_start_cable_test_tdr,};
static const struct phylib_stubs __phylib_stubs = { .hwtstamp_get = __phy_hwtstamp_get, .hwtstamp_set = __phy_hwtstamp_set, .get_phy_stats = __phy_ethtool_get_phy_stats, .get_link_ext_stats = __phy_ethtool_get_link_ext_stats,};
static void phylib_register_stubs(void){ phylib_stubs = &__phylib_stubs;}
static void phylib_unregister_stubs(void){ phylib_stubs = NULL;}
static int __init phy_init(void){ int rc;
rc = class_register(&mdio_bus_class); if (rc) return rc;
rc = bus_register(&mdio_bus_type); if (rc) goto err_class;
rtnl_lock(); ethtool_set_ethtool_phy_ops(&phy_ethtool_phy_ops); phylib_register_stubs(); rtnl_unlock();
rc = phy_caps_init(); if (rc) goto err_ethtool_phy_ops;
features_init();
rc = phy_driver_register(&genphy_c45_driver, THIS_MODULE); if (rc) goto err_ethtool_phy_ops;
rc = phy_driver_register(&genphy_driver, THIS_MODULE); if (rc) goto err_c45;
return 0;
err_c45: phy_driver_unregister(&genphy_c45_driver);err_ethtool_phy_ops: rtnl_lock(); phylib_unregister_stubs(); ethtool_set_ethtool_phy_ops(NULL); rtnl_unlock(); bus_unregister(&mdio_bus_type);err_class: class_unregister(&mdio_bus_class);
return rc;}
static void __exit phy_exit(void){ phy_driver_unregister(&genphy_c45_driver); phy_driver_unregister(&genphy_driver); rtnl_lock(); phylib_unregister_stubs(); ethtool_set_ethtool_phy_ops(NULL); rtnl_unlock(); bus_unregister(&mdio_bus_type); class_unregister(&mdio_bus_class);}
subsys_initcall(phy_init);module_exit(phy_exit);