| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- * Driver for Motorcomm YT921x Switch
- *
- * Should work on YT9213/YT9214/YT9215/YT9218, but only tested on YT9215+SGMII,
- * be sure to do your own checks before porting to another chip.
- *
- * Copyright (c) 2025 David Yang
- */
- #include <linux/dcbnl.h>
- #include <linux/etherdevice.h>
- #include <linux/if_bridge.h>
- #include <linux/if_hsr.h>
- #include <linux/if_vlan.h>
- #include <linux/iopoll.h>
- #include <linux/mdio.h>
- #include <linux/module.h>
- #include <linux/of.h>
- #include <linux/of_mdio.h>
- #include <linux/of_net.h>
- #include <linux/sort.h>
- #include <net/dsa.h>
- #include <net/dscp.h>
- #include <net/ieee8021q.h>
- #include "yt921x.h"
- struct yt921x_mib_desc {
- unsigned int size;
- unsigned int offset;
- const char *name;
- };
- #define MIB_DESC(_size, _offset, _name) \
- {_size, _offset, _name}
- /* Must agree with yt921x_mib
- *
- * Unstructured fields (name != NULL) will appear in get_ethtool_stats(),
- * structured go to their *_stats() methods, but we need their sizes and offsets
- * to perform 32bit MIB overflow wraparound.
- */
- static const struct yt921x_mib_desc yt921x_mib_descs[] = {
- MIB_DESC(1, YT921X_MIB_DATA_RX_BROADCAST, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_PAUSE, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_MULTICAST, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_CRC_ERR, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_ALIGN_ERR, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_UNDERSIZE_ERR, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_FRAG_ERR, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_64, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX, NULL),
- MIB_DESC(2, YT921X_MIB_DATA_RX_GOOD_BYTES, NULL),
- MIB_DESC(2, YT921X_MIB_DATA_RX_BAD_BYTES, "RxBadBytes"),
- MIB_DESC(1, YT921X_MIB_DATA_RX_OVERSIZE_ERR, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_DROPPED, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_BROADCAST, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_PAUSE, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_MULTICAST, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_UNDERSIZE_ERR, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_64, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX, NULL),
- MIB_DESC(2, YT921X_MIB_DATA_TX_GOOD_BYTES, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_COLLISION, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_MULTIPLE_COLLISION, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_SINGLE_COLLISION, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_PKT, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_DEFERRED, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_TX_LATE_COLLISION, NULL),
- MIB_DESC(1, YT921X_MIB_DATA_RX_OAM, "RxOAM"),
- MIB_DESC(1, YT921X_MIB_DATA_TX_OAM, "TxOAM"),
- };
- struct yt921x_info {
- const char *name;
- u16 major;
- /* Unknown, seems to be plain enumeration */
- u8 mode;
- u8 extmode;
- /* Ports with integral GbE PHYs, not including MCU Port 10 */
- u16 internal_mask;
- /* TODO: see comments in yt921x_dsa_phylink_get_caps() */
- u16 external_mask;
- };
- #define YT921X_PORT_MASK_INTn(port) BIT(port)
- #define YT921X_PORT_MASK_INT0_n(n) GENMASK((n) - 1, 0)
- #define YT921X_PORT_MASK_EXT0 BIT(8)
- #define YT921X_PORT_MASK_EXT1 BIT(9)
- static const struct yt921x_info yt921x_infos[] = {
- {
- "YT9215SC", YT9215_MAJOR, 1, 0,
- YT921X_PORT_MASK_INT0_n(5),
- YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
- },
- {
- "YT9215S", YT9215_MAJOR, 2, 0,
- YT921X_PORT_MASK_INT0_n(5),
- YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
- },
- {
- "YT9215RB", YT9215_MAJOR, 3, 0,
- YT921X_PORT_MASK_INT0_n(5),
- YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
- },
- {
- "YT9214NB", YT9215_MAJOR, 3, 2,
- YT921X_PORT_MASK_INTn(1) | YT921X_PORT_MASK_INTn(3),
- YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
- },
- {
- "YT9213NB", YT9215_MAJOR, 3, 3,
- YT921X_PORT_MASK_INTn(1) | YT921X_PORT_MASK_INTn(3),
- YT921X_PORT_MASK_EXT1,
- },
- {
- "YT9218N", YT9218_MAJOR, 0, 0,
- YT921X_PORT_MASK_INT0_n(8),
- 0,
- },
- {
- "YT9218MB", YT9218_MAJOR, 1, 0,
- YT921X_PORT_MASK_INT0_n(8),
- YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
- },
- {}
- };
- #define YT921X_NAME "yt921x"
- #define YT921X_VID_UNWARE 4095
- #define YT921X_POLL_SLEEP_US 10000
- #define YT921X_POLL_TIMEOUT_US 100000
- /* The interval should be small enough to avoid overflow of 32bit MIBs.
- *
- * Until we can read MIBs from stats64 call directly (i.e. sleep
- * there), we have to poll stats more frequently then it is actually needed.
- * For overflow protection, normally, 100 sec interval should have been OK.
- */
- #define YT921X_STATS_INTERVAL_JIFFIES (3 * HZ)
- struct yt921x_reg_mdio {
- struct mii_bus *bus;
- int addr;
- /* SWITCH_ID_1 / SWITCH_ID_0 of the device
- *
- * This is a way to multiplex multiple devices on the same MII phyaddr
- * and should be configurable in DT. However, MDIO core simply doesn't
- * allow multiple devices over one reg addr, so this is a fixed value
- * for now until a solution is found.
- *
- * Keep this because we need switchid to form MII regaddrs anyway.
- */
- unsigned char switchid;
- };
- /* TODO: SPI/I2C */
- #define to_yt921x_priv(_ds) container_of_const(_ds, struct yt921x_priv, ds)
- #define to_device(priv) ((priv)->ds.dev)
- static int yt921x_reg_read(struct yt921x_priv *priv, u32 reg, u32 *valp)
- {
- WARN_ON(!mutex_is_locked(&priv->reg_lock));
- return priv->reg_ops->read(priv->reg_ctx, reg, valp);
- }
- static int yt921x_reg_write(struct yt921x_priv *priv, u32 reg, u32 val)
- {
- WARN_ON(!mutex_is_locked(&priv->reg_lock));
- return priv->reg_ops->write(priv->reg_ctx, reg, val);
- }
- static int
- yt921x_reg_wait(struct yt921x_priv *priv, u32 reg, u32 mask, u32 *valp)
- {
- u32 val;
- int res;
- int ret;
- ret = read_poll_timeout(yt921x_reg_read, res,
- res || (val & mask) == *valp,
- YT921X_POLL_SLEEP_US, YT921X_POLL_TIMEOUT_US,
- false, priv, reg, &val);
- if (ret)
- return ret;
- if (res)
- return res;
- *valp = val;
- return 0;
- }
- static int
- yt921x_reg_update_bits(struct yt921x_priv *priv, u32 reg, u32 mask, u32 val)
- {
- int res;
- u32 v;
- u32 u;
- res = yt921x_reg_read(priv, reg, &v);
- if (res)
- return res;
- u = v;
- u &= ~mask;
- u |= val;
- if (u == v)
- return 0;
- return yt921x_reg_write(priv, reg, u);
- }
- static int yt921x_reg_set_bits(struct yt921x_priv *priv, u32 reg, u32 mask)
- {
- return yt921x_reg_update_bits(priv, reg, 0, mask);
- }
- static int yt921x_reg_clear_bits(struct yt921x_priv *priv, u32 reg, u32 mask)
- {
- return yt921x_reg_update_bits(priv, reg, mask, 0);
- }
- static int
- yt921x_reg_toggle_bits(struct yt921x_priv *priv, u32 reg, u32 mask, bool set)
- {
- return yt921x_reg_update_bits(priv, reg, mask, !set ? 0 : mask);
- }
- /* Some registers, like VLANn_CTRL, should always be written in 64-bit, even if
- * you are to write only the lower / upper 32 bits.
- *
- * There is no such restriction for reading, but we still provide 64-bit read
- * wrappers so that we always handle u64 values.
- */
- static int yt921x_reg64_read(struct yt921x_priv *priv, u32 reg, u64 *valp)
- {
- u32 lo;
- u32 hi;
- int res;
- res = yt921x_reg_read(priv, reg, &lo);
- if (res)
- return res;
- res = yt921x_reg_read(priv, reg + 4, &hi);
- if (res)
- return res;
- *valp = ((u64)hi << 32) | lo;
- return 0;
- }
- static int yt921x_reg64_write(struct yt921x_priv *priv, u32 reg, u64 val)
- {
- int res;
- res = yt921x_reg_write(priv, reg, (u32)val);
- if (res)
- return res;
- return yt921x_reg_write(priv, reg + 4, (u32)(val >> 32));
- }
- static int
- yt921x_reg64_update_bits(struct yt921x_priv *priv, u32 reg, u64 mask, u64 val)
- {
- int res;
- u64 v;
- u64 u;
- res = yt921x_reg64_read(priv, reg, &v);
- if (res)
- return res;
- u = v;
- u &= ~mask;
- u |= val;
- if (u == v)
- return 0;
- return yt921x_reg64_write(priv, reg, u);
- }
- static int yt921x_reg64_clear_bits(struct yt921x_priv *priv, u32 reg, u64 mask)
- {
- return yt921x_reg64_update_bits(priv, reg, mask, 0);
- }
- static int yt921x_reg_mdio_read(void *context, u32 reg, u32 *valp)
- {
- struct yt921x_reg_mdio *mdio = context;
- struct mii_bus *bus = mdio->bus;
- int addr = mdio->addr;
- u32 reg_addr;
- u32 reg_data;
- u32 val;
- int res;
- /* Hold the mdio bus lock to avoid (un)locking for 4 times */
- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
- reg_addr = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_ADDR |
- YT921X_SMI_READ;
- res = __mdiobus_write(bus, addr, reg_addr, (u16)(reg >> 16));
- if (res)
- goto end;
- res = __mdiobus_write(bus, addr, reg_addr, (u16)reg);
- if (res)
- goto end;
- reg_data = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_DATA |
- YT921X_SMI_READ;
- res = __mdiobus_read(bus, addr, reg_data);
- if (res < 0)
- goto end;
- val = (u16)res;
- res = __mdiobus_read(bus, addr, reg_data);
- if (res < 0)
- goto end;
- val = (val << 16) | (u16)res;
- *valp = val;
- res = 0;
- end:
- mutex_unlock(&bus->mdio_lock);
- return res;
- }
- static int yt921x_reg_mdio_write(void *context, u32 reg, u32 val)
- {
- struct yt921x_reg_mdio *mdio = context;
- struct mii_bus *bus = mdio->bus;
- int addr = mdio->addr;
- u32 reg_addr;
- u32 reg_data;
- int res;
- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
- reg_addr = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_ADDR |
- YT921X_SMI_WRITE;
- res = __mdiobus_write(bus, addr, reg_addr, (u16)(reg >> 16));
- if (res)
- goto end;
- res = __mdiobus_write(bus, addr, reg_addr, (u16)reg);
- if (res)
- goto end;
- reg_data = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_DATA |
- YT921X_SMI_WRITE;
- res = __mdiobus_write(bus, addr, reg_data, (u16)(val >> 16));
- if (res)
- goto end;
- res = __mdiobus_write(bus, addr, reg_data, (u16)val);
- if (res)
- goto end;
- res = 0;
- end:
- mutex_unlock(&bus->mdio_lock);
- return res;
- }
- static const struct yt921x_reg_ops yt921x_reg_ops_mdio = {
- .read = yt921x_reg_mdio_read,
- .write = yt921x_reg_mdio_write,
- };
- /* TODO: SPI/I2C */
- static int yt921x_intif_wait(struct yt921x_priv *priv)
- {
- u32 val = 0;
- return yt921x_reg_wait(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START,
- &val);
- }
- static int
- yt921x_intif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp)
- {
- struct device *dev = to_device(priv);
- u32 mask;
- u32 ctrl;
- u32 val;
- int res;
- res = yt921x_intif_wait(priv);
- if (res)
- return res;
- mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M |
- YT921X_MBUS_CTRL_OP_M;
- ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) |
- YT921X_MBUS_CTRL_READ;
- res = yt921x_reg_update_bits(priv, YT921X_INT_MBUS_CTRL, mask, ctrl);
- if (res)
- return res;
- res = yt921x_reg_write(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START);
- if (res)
- return res;
- res = yt921x_intif_wait(priv);
- if (res)
- return res;
- res = yt921x_reg_read(priv, YT921X_INT_MBUS_DIN, &val);
- if (res)
- return res;
- if ((u16)val != val)
- dev_info(dev,
- "%s: port %d, reg 0x%x: Expected u16, got 0x%08x\n",
- __func__, port, reg, val);
- *valp = (u16)val;
- return 0;
- }
- static int
- yt921x_intif_write(struct yt921x_priv *priv, int port, int reg, u16 val)
- {
- u32 mask;
- u32 ctrl;
- int res;
- res = yt921x_intif_wait(priv);
- if (res)
- return res;
- mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M |
- YT921X_MBUS_CTRL_OP_M;
- ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) |
- YT921X_MBUS_CTRL_WRITE;
- res = yt921x_reg_update_bits(priv, YT921X_INT_MBUS_CTRL, mask, ctrl);
- if (res)
- return res;
- res = yt921x_reg_write(priv, YT921X_INT_MBUS_DOUT, val);
- if (res)
- return res;
- res = yt921x_reg_write(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START);
- if (res)
- return res;
- return yt921x_intif_wait(priv);
- }
- static int yt921x_mbus_int_read(struct mii_bus *mbus, int port, int reg)
- {
- struct yt921x_priv *priv = mbus->priv;
- u16 val;
- int res;
- if (port >= YT921X_PORT_NUM)
- return U16_MAX;
- mutex_lock(&priv->reg_lock);
- res = yt921x_intif_read(priv, port, reg, &val);
- mutex_unlock(&priv->reg_lock);
- if (res)
- return res;
- return val;
- }
- static int
- yt921x_mbus_int_write(struct mii_bus *mbus, int port, int reg, u16 data)
- {
- struct yt921x_priv *priv = mbus->priv;
- int res;
- if (port >= YT921X_PORT_NUM)
- return -ENODEV;
- mutex_lock(&priv->reg_lock);
- res = yt921x_intif_write(priv, port, reg, data);
- mutex_unlock(&priv->reg_lock);
- return res;
- }
- static int
- yt921x_mbus_int_init(struct yt921x_priv *priv, struct device_node *mnp)
- {
- struct device *dev = to_device(priv);
- struct mii_bus *mbus;
- int res;
- mbus = devm_mdiobus_alloc(dev);
- if (!mbus)
- return -ENOMEM;
- mbus->name = "YT921x internal MDIO bus";
- snprintf(mbus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
- mbus->priv = priv;
- mbus->read = yt921x_mbus_int_read;
- mbus->write = yt921x_mbus_int_write;
- mbus->parent = dev;
- mbus->phy_mask = (u32)~GENMASK(YT921X_PORT_NUM - 1, 0);
- res = devm_of_mdiobus_register(dev, mbus, mnp);
- if (res)
- return res;
- priv->mbus_int = mbus;
- return 0;
- }
- static int yt921x_extif_wait(struct yt921x_priv *priv)
- {
- u32 val = 0;
- return yt921x_reg_wait(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START,
- &val);
- }
- static int
- yt921x_extif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp)
- {
- struct device *dev = to_device(priv);
- u32 mask;
- u32 ctrl;
- u32 val;
- int res;
- res = yt921x_extif_wait(priv);
- if (res)
- return res;
- mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M |
- YT921X_MBUS_CTRL_TYPE_M | YT921X_MBUS_CTRL_OP_M;
- ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) |
- YT921X_MBUS_CTRL_TYPE_C22 | YT921X_MBUS_CTRL_READ;
- res = yt921x_reg_update_bits(priv, YT921X_EXT_MBUS_CTRL, mask, ctrl);
- if (res)
- return res;
- res = yt921x_reg_write(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START);
- if (res)
- return res;
- res = yt921x_extif_wait(priv);
- if (res)
- return res;
- res = yt921x_reg_read(priv, YT921X_EXT_MBUS_DIN, &val);
- if (res)
- return res;
- if ((u16)val != val)
- dev_info(dev,
- "%s: port %d, reg 0x%x: Expected u16, got 0x%08x\n",
- __func__, port, reg, val);
- *valp = (u16)val;
- return 0;
- }
- static int
- yt921x_extif_write(struct yt921x_priv *priv, int port, int reg, u16 val)
- {
- u32 mask;
- u32 ctrl;
- int res;
- res = yt921x_extif_wait(priv);
- if (res)
- return res;
- mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M |
- YT921X_MBUS_CTRL_TYPE_M | YT921X_MBUS_CTRL_OP_M;
- ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) |
- YT921X_MBUS_CTRL_TYPE_C22 | YT921X_MBUS_CTRL_WRITE;
- res = yt921x_reg_update_bits(priv, YT921X_EXT_MBUS_CTRL, mask, ctrl);
- if (res)
- return res;
- res = yt921x_reg_write(priv, YT921X_EXT_MBUS_DOUT, val);
- if (res)
- return res;
- res = yt921x_reg_write(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START);
- if (res)
- return res;
- return yt921x_extif_wait(priv);
- }
- static int yt921x_mbus_ext_read(struct mii_bus *mbus, int port, int reg)
- {
- struct yt921x_priv *priv = mbus->priv;
- u16 val;
- int res;
- mutex_lock(&priv->reg_lock);
- res = yt921x_extif_read(priv, port, reg, &val);
- mutex_unlock(&priv->reg_lock);
- if (res)
- return res;
- return val;
- }
- static int
- yt921x_mbus_ext_write(struct mii_bus *mbus, int port, int reg, u16 data)
- {
- struct yt921x_priv *priv = mbus->priv;
- int res;
- mutex_lock(&priv->reg_lock);
- res = yt921x_extif_write(priv, port, reg, data);
- mutex_unlock(&priv->reg_lock);
- return res;
- }
- static int
- yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp)
- {
- struct device *dev = to_device(priv);
- struct mii_bus *mbus;
- int res;
- mbus = devm_mdiobus_alloc(dev);
- if (!mbus)
- return -ENOMEM;
- mbus->name = "YT921x external MDIO bus";
- snprintf(mbus->id, MII_BUS_ID_SIZE, "%s@ext", dev_name(dev));
- mbus->priv = priv;
- /* TODO: c45? */
- mbus->read = yt921x_mbus_ext_read;
- mbus->write = yt921x_mbus_ext_write;
- mbus->parent = dev;
- res = devm_of_mdiobus_register(dev, mbus, mnp);
- if (res)
- return res;
- priv->mbus_ext = mbus;
- return 0;
- }
- /* Read and handle overflow of 32bit MIBs. MIB buffer must be zeroed before. */
- static int yt921x_read_mib(struct yt921x_priv *priv, int port)
- {
- struct yt921x_port *pp = &priv->ports[port];
- struct device *dev = to_device(priv);
- struct yt921x_mib *mib = &pp->mib;
- int res = 0;
- /* Reading of yt921x_port::mib is not protected by a lock and it's vain
- * to keep its consistency, since we have to read registers one by one
- * and there is no way to make a snapshot of MIB stats.
- *
- * Writing (by this function only) is and should be protected by
- * reg_lock.
- */
- for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
- const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
- u32 reg = YT921X_MIBn_DATA0(port) + desc->offset;
- u64 *valp = &((u64 *)mib)[i];
- u32 val0;
- u64 val;
- res = yt921x_reg_read(priv, reg, &val0);
- if (res)
- break;
- if (desc->size <= 1) {
- u64 old_val = *valp;
- val = (old_val & ~(u64)U32_MAX) | val0;
- if (val < old_val)
- val += 1ull << 32;
- } else {
- u32 val1;
- res = yt921x_reg_read(priv, reg + 4, &val1);
- if (res)
- break;
- val = ((u64)val1 << 32) | val0;
- }
- WRITE_ONCE(*valp, val);
- }
- pp->rx_frames = mib->rx_64byte + mib->rx_65_127byte +
- mib->rx_128_255byte + mib->rx_256_511byte +
- mib->rx_512_1023byte + mib->rx_1024_1518byte +
- mib->rx_jumbo;
- pp->tx_frames = mib->tx_64byte + mib->tx_65_127byte +
- mib->tx_128_255byte + mib->tx_256_511byte +
- mib->tx_512_1023byte + mib->tx_1024_1518byte +
- mib->tx_jumbo;
- if (res)
- dev_err(dev, "Failed to %s port %d: %i\n", "read stats for",
- port, res);
- return res;
- }
- static void yt921x_poll_mib(struct work_struct *work)
- {
- struct yt921x_port *pp = container_of_const(work, struct yt921x_port,
- mib_read.work);
- struct yt921x_priv *priv = (void *)(pp - pp->index) -
- offsetof(struct yt921x_priv, ports);
- unsigned long delay = YT921X_STATS_INTERVAL_JIFFIES;
- int port = pp->index;
- int res;
- mutex_lock(&priv->reg_lock);
- res = yt921x_read_mib(priv, port);
- mutex_unlock(&priv->reg_lock);
- if (res)
- delay *= 4;
- schedule_delayed_work(&pp->mib_read, delay);
- }
- static void
- yt921x_dsa_get_strings(struct dsa_switch *ds, int port, u32 stringset,
- uint8_t *data)
- {
- if (stringset != ETH_SS_STATS)
- return;
- for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
- const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
- if (desc->name)
- ethtool_puts(&data, desc->name);
- }
- }
- static void
- yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *mib = &pp->mib;
- size_t j;
- mutex_lock(&priv->reg_lock);
- yt921x_read_mib(priv, port);
- mutex_unlock(&priv->reg_lock);
- j = 0;
- for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
- const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
- if (!desc->name)
- continue;
- data[j] = ((u64 *)mib)[i];
- j++;
- }
- }
- static int yt921x_dsa_get_sset_count(struct dsa_switch *ds, int port, int sset)
- {
- int cnt = 0;
- if (sset != ETH_SS_STATS)
- return 0;
- for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
- const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
- if (desc->name)
- cnt++;
- }
- return cnt;
- }
- static void
- yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port,
- struct ethtool_eth_mac_stats *mac_stats)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *mib = &pp->mib;
- mutex_lock(&priv->reg_lock);
- yt921x_read_mib(priv, port);
- mutex_unlock(&priv->reg_lock);
- mac_stats->FramesTransmittedOK = pp->tx_frames;
- mac_stats->SingleCollisionFrames = mib->tx_single_collisions;
- mac_stats->MultipleCollisionFrames = mib->tx_multiple_collisions;
- mac_stats->FramesReceivedOK = pp->rx_frames;
- mac_stats->FrameCheckSequenceErrors = mib->rx_crc_errors;
- mac_stats->AlignmentErrors = mib->rx_alignment_errors;
- mac_stats->OctetsTransmittedOK = mib->tx_good_bytes;
- mac_stats->FramesWithDeferredXmissions = mib->tx_deferred;
- mac_stats->LateCollisions = mib->tx_late_collisions;
- mac_stats->FramesAbortedDueToXSColls = mib->tx_aborted_errors;
- /* mac_stats->FramesLostDueToIntMACXmitError */
- /* mac_stats->CarrierSenseErrors */
- mac_stats->OctetsReceivedOK = mib->rx_good_bytes;
- /* mac_stats->FramesLostDueToIntMACRcvError */
- mac_stats->MulticastFramesXmittedOK = mib->tx_multicast;
- mac_stats->BroadcastFramesXmittedOK = mib->tx_broadcast;
- /* mac_stats->FramesWithExcessiveDeferral */
- mac_stats->MulticastFramesReceivedOK = mib->rx_multicast;
- mac_stats->BroadcastFramesReceivedOK = mib->rx_broadcast;
- /* mac_stats->InRangeLengthErrors */
- /* mac_stats->OutOfRangeLengthField */
- mac_stats->FrameTooLongErrors = mib->rx_oversize_errors;
- }
- static void
- yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
- struct ethtool_eth_ctrl_stats *ctrl_stats)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *mib = &pp->mib;
- mutex_lock(&priv->reg_lock);
- yt921x_read_mib(priv, port);
- mutex_unlock(&priv->reg_lock);
- ctrl_stats->MACControlFramesTransmitted = mib->tx_pause;
- ctrl_stats->MACControlFramesReceived = mib->rx_pause;
- /* ctrl_stats->UnsupportedOpcodesReceived */
- }
- static const struct ethtool_rmon_hist_range yt921x_rmon_ranges[] = {
- { 0, 64 },
- { 65, 127 },
- { 128, 255 },
- { 256, 511 },
- { 512, 1023 },
- { 1024, 1518 },
- { 1519, YT921X_FRAME_SIZE_MAX },
- {}
- };
- static void
- yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port,
- struct ethtool_rmon_stats *rmon_stats,
- const struct ethtool_rmon_hist_range **ranges)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *mib = &pp->mib;
- mutex_lock(&priv->reg_lock);
- yt921x_read_mib(priv, port);
- mutex_unlock(&priv->reg_lock);
- *ranges = yt921x_rmon_ranges;
- rmon_stats->undersize_pkts = mib->rx_undersize_errors;
- rmon_stats->oversize_pkts = mib->rx_oversize_errors;
- rmon_stats->fragments = mib->rx_alignment_errors;
- /* rmon_stats->jabbers */
- rmon_stats->hist[0] = mib->rx_64byte;
- rmon_stats->hist[1] = mib->rx_65_127byte;
- rmon_stats->hist[2] = mib->rx_128_255byte;
- rmon_stats->hist[3] = mib->rx_256_511byte;
- rmon_stats->hist[4] = mib->rx_512_1023byte;
- rmon_stats->hist[5] = mib->rx_1024_1518byte;
- rmon_stats->hist[6] = mib->rx_jumbo;
- rmon_stats->hist_tx[0] = mib->tx_64byte;
- rmon_stats->hist_tx[1] = mib->tx_65_127byte;
- rmon_stats->hist_tx[2] = mib->tx_128_255byte;
- rmon_stats->hist_tx[3] = mib->tx_256_511byte;
- rmon_stats->hist_tx[4] = mib->tx_512_1023byte;
- rmon_stats->hist_tx[5] = mib->tx_1024_1518byte;
- rmon_stats->hist_tx[6] = mib->tx_jumbo;
- }
- static void
- yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,
- struct rtnl_link_stats64 *stats)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *mib = &pp->mib;
- stats->rx_length_errors = mib->rx_undersize_errors +
- mib->rx_fragment_errors;
- stats->rx_over_errors = mib->rx_oversize_errors;
- stats->rx_crc_errors = mib->rx_crc_errors;
- stats->rx_frame_errors = mib->rx_alignment_errors;
- /* stats->rx_fifo_errors */
- /* stats->rx_missed_errors */
- stats->tx_aborted_errors = mib->tx_aborted_errors;
- /* stats->tx_carrier_errors */
- stats->tx_fifo_errors = mib->tx_undersize_errors;
- /* stats->tx_heartbeat_errors */
- stats->tx_window_errors = mib->tx_late_collisions;
- stats->rx_packets = pp->rx_frames;
- stats->tx_packets = pp->tx_frames;
- stats->rx_bytes = mib->rx_good_bytes - ETH_FCS_LEN * stats->rx_packets;
- stats->tx_bytes = mib->tx_good_bytes - ETH_FCS_LEN * stats->tx_packets;
- stats->rx_errors = stats->rx_length_errors + stats->rx_over_errors +
- stats->rx_crc_errors + stats->rx_frame_errors;
- stats->tx_errors = stats->tx_aborted_errors + stats->tx_fifo_errors +
- stats->tx_window_errors;
- stats->rx_dropped = mib->rx_dropped;
- /* stats->tx_dropped */
- stats->multicast = mib->rx_multicast;
- stats->collisions = mib->tx_collisions;
- }
- static void
- yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port,
- struct ethtool_pause_stats *pause_stats)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct yt921x_port *pp = &priv->ports[port];
- struct yt921x_mib *mib = &pp->mib;
- mutex_lock(&priv->reg_lock);
- yt921x_read_mib(priv, port);
- mutex_unlock(&priv->reg_lock);
- pause_stats->tx_pause_frames = mib->tx_pause;
- pause_stats->rx_pause_frames = mib->rx_pause;
- }
- static int
- yt921x_set_eee(struct yt921x_priv *priv, int port, struct ethtool_keee *e)
- {
- /* Poor datasheet for EEE operations; don't ask if you are confused */
- bool enable = e->eee_enabled;
- u16 new_mask;
- int res;
- /* Enable / disable global EEE */
- new_mask = priv->eee_ports_mask;
- new_mask &= ~BIT(port);
- new_mask |= !enable ? 0 : BIT(port);
- if (!!new_mask != !!priv->eee_ports_mask) {
- res = yt921x_reg_toggle_bits(priv, YT921X_PON_STRAP_FUNC,
- YT921X_PON_STRAP_EEE, !!new_mask);
- if (res)
- return res;
- res = yt921x_reg_toggle_bits(priv, YT921X_PON_STRAP_VAL,
- YT921X_PON_STRAP_EEE, !!new_mask);
- if (res)
- return res;
- }
- priv->eee_ports_mask = new_mask;
- /* Enable / disable port EEE */
- res = yt921x_reg_toggle_bits(priv, YT921X_EEE_CTRL,
- YT921X_EEE_CTRL_ENn(port), enable);
- if (res)
- return res;
- res = yt921x_reg_toggle_bits(priv, YT921X_EEEn_VAL(port),
- YT921X_EEE_VAL_DATA, enable);
- if (res)
- return res;
- return 0;
- }
- static int
- yt921x_dsa_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_keee *e)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- int res;
- mutex_lock(&priv->reg_lock);
- res = yt921x_set_eee(priv, port, e);
- mutex_unlock(&priv->reg_lock);
- return res;
- }
- static int
- yt921x_dsa_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
- {
- /* Only serves as packet filter, since the frame size is always set to
- * maximum after reset
- */
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct dsa_port *dp = dsa_to_port(ds, port);
- int frame_size;
- int res;
- frame_size = new_mtu + ETH_HLEN + ETH_FCS_LEN;
- if (dsa_port_is_cpu(dp))
- frame_size += YT921X_TAG_LEN;
- mutex_lock(&priv->reg_lock);
- res = yt921x_reg_update_bits(priv, YT921X_MACn_FRAME(port),
- YT921X_MAC_FRAME_SIZE_M,
- YT921X_MAC_FRAME_SIZE(frame_size));
- mutex_unlock(&priv->reg_lock);
- return res;
- }
- static int yt921x_dsa_port_max_mtu(struct dsa_switch *ds, int port)
- {
- /* Only called for user ports, exclude tag len here */
- return YT921X_FRAME_SIZE_MAX - ETH_HLEN - ETH_FCS_LEN - YT921X_TAG_LEN;
- }
- static int
- yt921x_mirror_del(struct yt921x_priv *priv, int port, bool ingress)
- {
- u32 mask;
- if (ingress)
- mask = YT921X_MIRROR_IGR_PORTn(port);
- else
- mask = YT921X_MIRROR_EGR_PORTn(port);
- return yt921x_reg_clear_bits(priv, YT921X_MIRROR, mask);
- }
- static int
- yt921x_mirror_add(struct yt921x_priv *priv, int port, bool ingress,
- int to_local_port, struct netlink_ext_ack *extack)
- {
- u32 srcs;
- u32 ctrl;
- u32 val;
- u32 dst;
- int res;
- if (ingress)
- srcs = YT921X_MIRROR_IGR_PORTn(port);
- else
- srcs = YT921X_MIRROR_EGR_PORTn(port);
- dst = YT921X_MIRROR_PORT(to_local_port);
- res = yt921x_reg_read(priv, YT921X_MIRROR, &val);
- if (res)
- return res;
- /* other mirror tasks & different dst port -> conflict */
- if ((val & ~srcs & (YT921X_MIRROR_EGR_PORTS_M |
- YT921X_MIRROR_IGR_PORTS_M)) &&
- (val & YT921X_MIRROR_PORT_M) != dst) {
- NL_SET_ERR_MSG_MOD(extack,
- "Sniffer port is already configured, delete existing rules & retry");
- return -EBUSY;
- }
- ctrl = val & ~YT921X_MIRROR_PORT_M;
- ctrl |= srcs;
- ctrl |= dst;
- if (ctrl == val)
- return 0;
- return yt921x_reg_write(priv, YT921X_MIRROR, ctrl);
- }
- static void
- yt921x_dsa_port_mirror_del(struct dsa_switch *ds, int port,
- struct dsa_mall_mirror_tc_entry *mirror)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct device *dev = to_device(priv);
- int res;
- mutex_lock(&priv->reg_lock);
- res = yt921x_mirror_del(priv, port, mirror->ingress);
- mutex_unlock(&priv->reg_lock);
- if (res)
- dev_err(dev, "Failed to %s port %d: %i\n", "unmirror",
- port, res);
- }
- static int
- yt921x_dsa_port_mirror_add(struct dsa_switch *ds, int port,
- struct dsa_mall_mirror_tc_entry *mirror,
- bool ingress, struct netlink_ext_ack *extack)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- int res;
- mutex_lock(&priv->reg_lock);
- res = yt921x_mirror_add(priv, port, ingress,
- mirror->to_local_port, extack);
- mutex_unlock(&priv->reg_lock);
- return res;
- }
- static int yt921x_lag_hash(struct yt921x_priv *priv, u32 ctrl, bool unique_lag,
- struct netlink_ext_ack *extack)
- {
- u32 val;
- int res;
- /* Hash Mode is global. Make sure the same Hash Mode is set to all the
- * 2 possible lags.
- * If we are the unique LAG we can set whatever hash mode we want.
- * To change hash mode it's needed to remove all LAG and change the mode
- * with the latest.
- */
- if (unique_lag) {
- res = yt921x_reg_write(priv, YT921X_LAG_HASH, ctrl);
- if (res)
- return res;
- } else {
- res = yt921x_reg_read(priv, YT921X_LAG_HASH, &val);
- if (res)
- return res;
- if (val != ctrl) {
- NL_SET_ERR_MSG_MOD(extack,
- "Mismatched Hash Mode across different lags is not supported");
- return -EOPNOTSUPP;
- }
- }
- return 0;
- }
- static int yt921x_lag_set(struct yt921x_priv *priv, u8 index, u16 ports_mask)
- {
- unsigned long targets_mask = ports_mask;
- unsigned int cnt;
- u32 ctrl;
- int port;
- int res;
- cnt = 0;
- for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) {
- ctrl = YT921X_LAG_MEMBER_PORT(port);
- res = yt921x_reg_write(priv, YT921X_LAG_MEMBERnm(index, cnt),
- ctrl);
- if (res)
- return res;
- cnt++;
- }
- ctrl = YT921X_LAG_GROUP_PORTS(ports_mask) |
- YT921X_LAG_GROUP_MEMBER_NUM(cnt);
- return yt921x_reg_write(priv, YT921X_LAG_GROUPn(index), ctrl);
- }
- static int
- yt921x_dsa_port_lag_leave(struct dsa_switch *ds, int port, struct dsa_lag lag)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct dsa_port *dp;
- u32 ctrl;
- int res;
- if (!lag.id)
- return -EINVAL;
- ctrl = 0;
- dsa_lag_foreach_port(dp, ds->dst, &lag)
- ctrl |= BIT(dp->index);
- mutex_lock(&priv->reg_lock);
- res = yt921x_lag_set(priv, lag.id - 1, ctrl);
- mutex_unlock(&priv->reg_lock);
- return res;
- }
- static int
- yt921x_dsa_port_lag_check(struct dsa_switch *ds, struct dsa_lag lag,
- struct netdev_lag_upper_info *info,
- struct netlink_ext_ack *extack)
- {
- unsigned int members;
- struct dsa_port *dp;
- if (!lag.id)
- return -EINVAL;
- members = 0;
- dsa_lag_foreach_port(dp, ds->dst, &lag)
- /* Includes the port joining the LAG */
- members++;
- if (members > YT921X_LAG_PORT_NUM) {
- NL_SET_ERR_MSG_MOD(extack,
- "Cannot offload more than 4 LAG ports");
- return -EOPNOTSUPP;
- }
- if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) {
- NL_SET_ERR_MSG_MOD(extack,
- "Can only offload LAG using hash TX type");
- return -EOPNOTSUPP;
- }
- if (info->hash_type != NETDEV_LAG_HASH_L2 &&
- info->hash_type != NETDEV_LAG_HASH_L23 &&
- info->hash_type != NETDEV_LAG_HASH_L34) {
- NL_SET_ERR_MSG_MOD(extack,
- "Can only offload L2 or L2+L3 or L3+L4 TX hash");
- return -EOPNOTSUPP;
- }
- return 0;
- }
- static int
- yt921x_dsa_port_lag_join(struct dsa_switch *ds, int port, struct dsa_lag lag,
- struct netdev_lag_upper_info *info,
- struct netlink_ext_ack *extack)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct dsa_port *dp;
- bool unique_lag;
- unsigned int i;
- u32 ctrl;
- int res;
- res = yt921x_dsa_port_lag_check(ds, lag, info, extack);
- if (res)
- return res;
- ctrl = 0;
- switch (info->hash_type) {
- case NETDEV_LAG_HASH_L34:
- ctrl |= YT921X_LAG_HASH_IP_DST;
- ctrl |= YT921X_LAG_HASH_IP_SRC;
- ctrl |= YT921X_LAG_HASH_IP_PROTO;
- ctrl |= YT921X_LAG_HASH_L4_DPORT;
- ctrl |= YT921X_LAG_HASH_L4_SPORT;
- break;
- case NETDEV_LAG_HASH_L23:
- ctrl |= YT921X_LAG_HASH_MAC_DA;
- ctrl |= YT921X_LAG_HASH_MAC_SA;
- ctrl |= YT921X_LAG_HASH_IP_DST;
- ctrl |= YT921X_LAG_HASH_IP_SRC;
- ctrl |= YT921X_LAG_HASH_IP_PROTO;
- break;
- case NETDEV_LAG_HASH_L2:
- ctrl |= YT921X_LAG_HASH_MAC_DA;
- ctrl |= YT921X_LAG_HASH_MAC_SA;
- break;
- default:
- return -EOPNOTSUPP;
- }
- /* Check if we are the unique configured LAG */
- unique_lag = true;
- dsa_lags_foreach_id(i, ds->dst)
- if (i != lag.id && dsa_lag_by_id(ds->dst, i)) {
- unique_lag = false;
- break;
- }
- mutex_lock(&priv->reg_lock);
- do {
- res = yt921x_lag_hash(priv, ctrl, unique_lag, extack);
- if (res)
- break;
- ctrl = 0;
- dsa_lag_foreach_port(dp, ds->dst, &lag)
- ctrl |= BIT(dp->index);
- res = yt921x_lag_set(priv, lag.id - 1, ctrl);
- } while (0);
- mutex_unlock(&priv->reg_lock);
- return res;
- }
- static int yt921x_fdb_wait(struct yt921x_priv *priv, u32 *valp)
- {
- struct device *dev = to_device(priv);
- u32 val = YT921X_FDB_RESULT_DONE;
- int res;
- res = yt921x_reg_wait(priv, YT921X_FDB_RESULT, YT921X_FDB_RESULT_DONE,
- &val);
- if (res) {
- dev_err(dev, "FDB probably stuck\n");
- return res;
- }
- *valp = val;
- return 0;
- }
- static int
- yt921x_fdb_in01(struct yt921x_priv *priv, const unsigned char *addr,
- u16 vid, u32 ctrl1)
- {
- u32 ctrl;
- int res;
- ctrl = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3];
- res = yt921x_reg_write(priv, YT921X_FDB_IN0, ctrl);
- if (res)
- return res;
- ctrl = ctrl1 | YT921X_FDB_IO1_FID(vid) | (addr[4] << 8) | addr[5];
- return yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl);
- }
- static int
- yt921x_fdb_has(struct yt921x_priv *priv, const unsigned char *addr, u16 vid,
- u16 *indexp)
- {
- u32 ctrl;
- u32 val;
- int res;
- res = yt921x_fdb_in01(priv, addr, vid, 0);
- if (res)
- return res;
- ctrl = 0;
- res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl);
- if (res)
- return res;
- ctrl = YT921X_FDB_OP_OP_GET_ONE | YT921X_FDB_OP_START;
- res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
- if (res)
- return res;
- res = yt921x_fdb_wait(priv, &val);
- if (res)
- return res;
- if (val & YT921X_FDB_RESULT_NOTFOUND) {
- *indexp = YT921X_FDB_NUM;
- return 0;
- }
- *indexp = FIELD_GET(YT921X_FDB_RESULT_INDEX_M, val);
- return 0;
- }
- static int
- yt921x_fdb_read(struct yt921x_priv *priv, unsigned char *addr, u16 *vidp,
- u16 *ports_maskp, u16 *indexp, u8 *statusp)
- {
- struct device *dev = to_device(priv);
- u16 index;
- u32 data0;
- u32 data1;
- u32 data2;
- u32 val;
- int res;
- res = yt921x_fdb_wait(priv, &val);
- if (res)
- return res;
- if (val & YT921X_FDB_RESULT_NOTFOUND) {
- *ports_maskp = 0;
- return 0;
- }
- index = FIELD_GET(YT921X_FDB_RESULT_INDEX_M, val);
- res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &data1);
- if (res)
- return res;
- if ((data1 & YT921X_FDB_IO1_STATUS_M) ==
- YT921X_FDB_IO1_STATUS_INVALID) {
- *ports_maskp = 0;
- return 0;
- }
- res = yt921x_reg_read(priv, YT921X_FDB_OUT0, &data0);
- if (res)
- return res;
- res = yt921x_reg_read(priv, YT921X_FDB_OUT2, &data2);
- if (res)
- return res;
- addr[0] = data0 >> 24;
- addr[1] = data0 >> 16;
- addr[2] = data0 >> 8;
- addr[3] = data0;
- addr[4] = data1 >> 8;
- addr[5] = data1;
- *vidp = FIELD_GET(YT921X_FDB_IO1_FID_M, data1);
- *indexp = index;
- *ports_maskp = FIELD_GET(YT921X_FDB_IO2_EGR_PORTS_M, data2);
- *statusp = FIELD_GET(YT921X_FDB_IO1_STATUS_M, data1);
- dev_dbg(dev,
- "%s: index 0x%x, mac %02x:%02x:%02x:%02x:%02x:%02x, vid %d, ports 0x%x, status %d\n",
- __func__, *indexp, addr[0], addr[1], addr[2], addr[3],
- addr[4], addr[5], *vidp, *ports_maskp, *statusp);
- return 0;
- }
- static int
- yt921x_fdb_dump(struct yt921x_priv *priv, u16 ports_mask,
- dsa_fdb_dump_cb_t *cb, void *data)
- {
- unsigned char addr[ETH_ALEN];
- u8 status;
- u16 pmask;
- u16 index;
- u32 ctrl;
- u16 vid;
- int res;
- ctrl = YT921X_FDB_OP_INDEX(0) | YT921X_FDB_OP_MODE_INDEX |
- YT921X_FDB_OP_OP_GET_ONE | YT921X_FDB_OP_START;
- res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
- if (res)
- return res;
- res = yt921x_fdb_read(priv, addr, &vid, &pmask, &index, &status);
- if (res)
- return res;
- if ((pmask & ports_mask) && !is_multicast_ether_addr(addr)) {
- res = cb(addr, vid,
- status == YT921X_FDB_ENTRY_STATUS_STATIC, data);
- if (res)
- return res;
- }
- ctrl = YT921X_FDB_IO2_EGR_PORTS(ports_mask);
- res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl);
- if (res)
- return res;
- index = 0;
- do {
- ctrl = YT921X_FDB_OP_INDEX(index) | YT921X_FDB_OP_MODE_INDEX |
- YT921X_FDB_OP_NEXT_TYPE_UCAST_PORT |
- YT921X_FDB_OP_OP_GET_NEXT | YT921X_FDB_OP_START;
- res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
- if (res)
- return res;
- res = yt921x_fdb_read(priv, addr, &vid, &pmask, &index,
- &status);
- if (res)
- return res;
- if (!pmask)
- break;
- if ((pmask & ports_mask) && !is_multicast_ether_addr(addr)) {
- res = cb(addr, vid,
- status == YT921X_FDB_ENTRY_STATUS_STATIC,
- data);
- if (res)
- return res;
- }
- /* Never call GET_NEXT with 4095, otherwise it will hang
- * forever until a reset!
- */
- } while (index < YT921X_FDB_NUM - 1);
- return 0;
- }
- static int
- yt921x_fdb_flush_raw(struct yt921x_priv *priv, u16 ports_mask, u16 vid,
- bool flush_static)
- {
- u32 ctrl;
- u32 val;
- int res;
- if (vid < 4096) {
- ctrl = YT921X_FDB_IO1_FID(vid);
- res = yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl);
- if (res)
- return res;
- }
- ctrl = YT921X_FDB_IO2_EGR_PORTS(ports_mask);
- res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl);
- if (res)
- return res;
- ctrl = YT921X_FDB_OP_OP_FLUSH | YT921X_FDB_OP_START;
- if (vid >= 4096)
- ctrl |= YT921X_FDB_OP_FLUSH_PORT;
- else
- ctrl |= YT921X_FDB_OP_FLUSH_PORT_VID;
- if (flush_static)
- ctrl |= YT921X_FDB_OP_FLUSH_STATIC;
- res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
- if (res)
- return res;
- res = yt921x_fdb_wait(priv, &val);
- if (res)
- return res;
- return 0;
- }
- static int
- yt921x_fdb_flush_port(struct yt921x_priv *priv, int port, bool flush_static)
- {
- return yt921x_fdb_flush_raw(priv, BIT(port), 4096, flush_static);
- }
- static int
- yt921x_fdb_add_index_in12(struct yt921x_priv *priv, u16 index, u16 ctrl1,
- u16 ctrl2)
- {
- u32 ctrl;
- u32 val;
- int res;
- res = yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl1);
- if (res)
- return res;
- res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl2);
- if (res)
- return res;
- ctrl = YT921X_FDB_OP_INDEX(index) | YT921X_FDB_OP_MODE_INDEX |
- YT921X_FDB_OP_OP_ADD | YT921X_FDB_OP_START;
- res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
- if (res)
- return res;
- return yt921x_fdb_wait(priv, &val);
- }
- static int
- yt921x_fdb_add(struct yt921x_priv *priv, const unsigned char *addr, u16 vid,
- u16 ports_mask)
- {
- u32 ctrl;
- u32 val;
- int res;
- ctrl = YT921X_FDB_IO1_STATUS_STATIC;
- res = yt921x_fdb_in01(priv, addr, vid, ctrl);
- if (res)
- return res;
- ctrl = YT921X_FDB_IO2_EGR_PORTS(ports_mask);
- res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl);
- if (res)
- return res;
- ctrl = YT921X_FDB_OP_OP_ADD | YT921X_FDB_OP_START;
- res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
- if (res)
- return res;
- return yt921x_fdb_wait(priv, &val);
- }
- static int
- yt921x_fdb_leave(struct yt921x_priv *priv, const unsigned char *addr,
- u16 vid, u16 ports_mask)
- {
- u16 index;
- u32 ctrl1;
- u32 ctrl2;
- u32 ctrl;
- u32 val2;
- u32 val;
- int res;
- /* Check for presence */
- res = yt921x_fdb_has(priv, addr, vid, &index);
- if (res)
- return res;
- if (index >= YT921X_FDB_NUM)
- return 0;
- /* Check if action required */
- res = yt921x_reg_read(priv, YT921X_FDB_OUT2, &val2);
- if (res)
- return res;
- ctrl2 = val2 & ~YT921X_FDB_IO2_EGR_PORTS(ports_mask);
- if (ctrl2 == val2)
- return 0;
- if (!(ctrl2 & YT921X_FDB_IO2_EGR_PORTS_M)) {
- ctrl = YT921X_FDB_OP_OP_DEL | YT921X_FDB_OP_START;
- res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
- if (res)
- return res;
- return yt921x_fdb_wait(priv, &val);
- }
- res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &ctrl1);
- if (res)
- return res;
- return yt921x_fdb_add_index_in12(priv, index, ctrl1, ctrl2);
- }
- static int
- yt921x_fdb_join(struct yt921x_priv *priv, const unsigned char *addr, u16 vid,
- u16 ports_mask)
- {
- u16 index;
- u32 ctrl1;
- u32 ctrl2;
- u32 val1;
- u32 val2;
- int res;
- /* Check for presence */
- res = yt921x_fdb_has(priv, addr, vid, &index);
- if (res)
- return res;
- if (index >= YT921X_FDB_NUM)
- return yt921x_fdb_add(priv, addr, vid, ports_mask);
- /* Check if action required */
- res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &val1);
- if (res)
- return res;
- res = yt921x_reg_read(priv, YT921X_FDB_OUT2, &val2);
- if (res)
- return res;
- ctrl1 = val1 & ~YT921X_FDB_IO1_STATUS_M;
- ctrl1 |= YT921X_FDB_IO1_STATUS_STATIC;
- ctrl2 = val2 | YT921X_FDB_IO2_EGR_PORTS(ports_mask);
- if (ctrl1 == val1 && ctrl2 == val2)
- return 0;
- return yt921x_fdb_add_index_in12(priv, index, ctrl1, ctrl2);
- }
- static int
- yt921x_dsa_port_fdb_dump(struct dsa_switch *ds, int port,
- dsa_fdb_dump_cb_t *cb, void *data)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- int res;
- mutex_lock(&priv->reg_lock);
- /* Hardware FDB is shared for fdb and mdb, "bridge fdb show"
- * only wants to see unicast
- */
- res = yt921x_fdb_dump(priv, BIT(port), cb, data);
- mutex_unlock(&priv->reg_lock);
- return res;
- }
- static void yt921x_dsa_port_fast_age(struct dsa_switch *ds, int port)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct device *dev = to_device(priv);
- int res;
- mutex_lock(&priv->reg_lock);
- res = yt921x_fdb_flush_port(priv, port, false);
- mutex_unlock(&priv->reg_lock);
- if (res)
- dev_err(dev, "Failed to %s port %d: %i\n", "clear FDB for",
- port, res);
- }
- static int
- yt921x_dsa_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- u32 ctrl;
- int res;
- /* AGEING reg is set in 5s step */
- ctrl = clamp(msecs / 5000, 1, U16_MAX);
- mutex_lock(&priv->reg_lock);
- res = yt921x_reg_write(priv, YT921X_AGEING, ctrl);
- mutex_unlock(&priv->reg_lock);
- return res;
- }
- static int
- yt921x_dsa_port_fdb_del(struct dsa_switch *ds, int port,
- const unsigned char *addr, u16 vid, struct dsa_db db)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- int res;
- mutex_lock(&priv->reg_lock);
- res = yt921x_fdb_leave(priv, addr, vid, BIT(port));
- mutex_unlock(&priv->reg_lock);
- return res;
- }
- static int
- yt921x_dsa_port_fdb_add(struct dsa_switch *ds, int port,
- const unsigned char *addr, u16 vid, struct dsa_db db)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- int res;
- mutex_lock(&priv->reg_lock);
- res = yt921x_fdb_join(priv, addr, vid, BIT(port));
- mutex_unlock(&priv->reg_lock);
- return res;
- }
- static int
- yt921x_dsa_port_mdb_del(struct dsa_switch *ds, int port,
- const struct switchdev_obj_port_mdb *mdb,
- struct dsa_db db)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- const unsigned char *addr = mdb->addr;
- u16 vid = mdb->vid;
- int res;
- mutex_lock(&priv->reg_lock);
- res = yt921x_fdb_leave(priv, addr, vid, BIT(port));
- mutex_unlock(&priv->reg_lock);
- return res;
- }
- static int
- yt921x_dsa_port_mdb_add(struct dsa_switch *ds, int port,
- const struct switchdev_obj_port_mdb *mdb,
- struct dsa_db db)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- const unsigned char *addr = mdb->addr;
- u16 vid = mdb->vid;
- int res;
- mutex_lock(&priv->reg_lock);
- res = yt921x_fdb_join(priv, addr, vid, BIT(port));
- mutex_unlock(&priv->reg_lock);
- return res;
- }
- static int
- yt921x_vlan_aware_set(struct yt921x_priv *priv, int port, bool vlan_aware)
- {
- u32 ctrl;
- /* Abuse SVLAN for PCP parsing without polluting the FDB - it just works
- * despite YT921X_VLAN_CTRL_SVLAN_EN never being set
- */
- if (!vlan_aware)
- ctrl = YT921X_PORT_IGR_TPIDn_STAG(0);
- else
- ctrl = YT921X_PORT_IGR_TPIDn_CTAG(0);
- return yt921x_reg_write(priv, YT921X_PORTn_IGR_TPID(port), ctrl);
- }
- static int
- yt921x_port_set_pvid(struct yt921x_priv *priv, int port, u16 vid)
- {
- u32 mask;
- u32 ctrl;
- mask = YT921X_PORT_VLAN_CTRL_CVID_M;
- ctrl = YT921X_PORT_VLAN_CTRL_CVID(vid);
- return yt921x_reg_update_bits(priv, YT921X_PORTn_VLAN_CTRL(port),
- mask, ctrl);
- }
- static int
- yt921x_vlan_filtering(struct yt921x_priv *priv, int port, bool vlan_filtering)
- {
- struct dsa_port *dp = dsa_to_port(&priv->ds, port);
- struct net_device *bdev;
- u16 pvid;
- u32 mask;
- u32 ctrl;
- int res;
- bdev = dsa_port_bridge_dev_get(dp);
- if (!bdev || !vlan_filtering)
- pvid = YT921X_VID_UNWARE;
- else
- br_vlan_get_pvid(bdev, &pvid);
- res = yt921x_port_set_pvid(priv, port, pvid);
- if (res)
- return res;
- mask = YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_TAGGED |
- YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED;
- ctrl = 0;
- /* Do not drop tagged frames here; let VLAN_IGR_FILTER do it */
- if (vlan_filtering && !pvid)
- ctrl |= YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED;
- res = yt921x_reg_update_bits(priv, YT921X_PORTn_VLAN_CTRL1(port),
- mask, ctrl);
- if (res)
- return res;
- res = yt921x_reg_toggle_bits(priv, YT921X_VLAN_IGR_FILTER,
- YT921X_VLAN_IGR_FILTER_PORTn(port),
- vlan_filtering);
- if (res)
- return res;
- res = yt921x_vlan_aware_set(priv, port, vlan_filtering);
- if (res)
- return res;
- return 0;
- }
- static int
- yt921x_vlan_del(struct yt921x_priv *priv, int port, u16 vid)
- {
- u64 mask64;
- mask64 = YT921X_VLAN_CTRL_PORTS(port) |
- YT921X_VLAN_CTRL_UNTAG_PORTn(port);
- return yt921x_reg64_clear_bits(priv, YT921X_VLANn_CTRL(vid), mask64);
- }
- static int
- yt921x_vlan_add(struct yt921x_priv *priv, int port, u16 vid, bool untagged)
- {
- u64 mask64;
- u64 ctrl64;
- mask64 = YT921X_VLAN_CTRL_PORTn(port) |
- YT921X_VLAN_CTRL_PORTS(priv->cpu_ports_mask);
- ctrl64 = mask64;
- mask64 |= YT921X_VLAN_CTRL_UNTAG_PORTn(port);
- if (untagged)
- ctrl64 |= YT921X_VLAN_CTRL_UNTAG_PORTn(port);
- return yt921x_reg64_update_bits(priv, YT921X_VLANn_CTRL(vid),
- mask64, ctrl64);
- }
- static int
- yt921x_pvid_clear(struct yt921x_priv *priv, int port)
- {
- struct dsa_port *dp = dsa_to_port(&priv->ds, port);
- bool vlan_filtering;
- u32 mask;
- int res;
- vlan_filtering = dsa_port_is_vlan_filtering(dp);
- res = yt921x_port_set_pvid(priv, port,
- vlan_filtering ? 0 : YT921X_VID_UNWARE);
- if (res)
- return res;
- if (vlan_filtering) {
- mask = YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED;
- res = yt921x_reg_set_bits(priv, YT921X_PORTn_VLAN_CTRL1(port),
- mask);
- if (res)
- return res;
- }
- return 0;
- }
- static int
- yt921x_pvid_set(struct yt921x_priv *priv, int port, u16 vid)
- {
- struct dsa_port *dp = dsa_to_port(&priv->ds, port);
- bool vlan_filtering;
- u32 mask;
- int res;
- vlan_filtering = dsa_port_is_vlan_filtering(dp);
- if (vlan_filtering) {
- res = yt921x_port_set_pvid(priv, port, vid);
- if (res)
- return res;
- }
- mask = YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED;
- res = yt921x_reg_clear_bits(priv, YT921X_PORTn_VLAN_CTRL1(port), mask);
- if (res)
- return res;
- return 0;
- }
- static int
- yt921x_dsa_port_vlan_filtering(struct dsa_switch *ds, int port,
- bool vlan_filtering,
- struct netlink_ext_ack *extack)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- int res;
- if (dsa_is_cpu_port(ds, port))
- return 0;
- mutex_lock(&priv->reg_lock);
- res = yt921x_vlan_filtering(priv, port, vlan_filtering);
- mutex_unlock(&priv->reg_lock);
- return res;
- }
- static int
- yt921x_dsa_port_vlan_del(struct dsa_switch *ds, int port,
- const struct switchdev_obj_port_vlan *vlan)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- u16 vid = vlan->vid;
- u16 pvid;
- int res;
- if (dsa_is_cpu_port(ds, port))
- return 0;
- mutex_lock(&priv->reg_lock);
- do {
- struct dsa_port *dp = dsa_to_port(ds, port);
- struct net_device *bdev;
- res = yt921x_vlan_del(priv, port, vid);
- if (res)
- break;
- bdev = dsa_port_bridge_dev_get(dp);
- if (bdev) {
- br_vlan_get_pvid(bdev, &pvid);
- if (pvid == vid)
- res = yt921x_pvid_clear(priv, port);
- }
- } while (0);
- mutex_unlock(&priv->reg_lock);
- return res;
- }
- static int
- yt921x_dsa_port_vlan_add(struct dsa_switch *ds, int port,
- const struct switchdev_obj_port_vlan *vlan,
- struct netlink_ext_ack *extack)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- u16 vid = vlan->vid;
- u16 pvid;
- int res;
- /* CPU port is supposed to be a member of every VLAN; see
- * yt921x_vlan_add() and yt921x_port_setup()
- */
- if (dsa_is_cpu_port(ds, port))
- return 0;
- mutex_lock(&priv->reg_lock);
- do {
- struct dsa_port *dp = dsa_to_port(ds, port);
- struct net_device *bdev;
- res = yt921x_vlan_add(priv, port, vid,
- vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED);
- if (res)
- break;
- bdev = dsa_port_bridge_dev_get(dp);
- if (bdev) {
- if (vlan->flags & BRIDGE_VLAN_INFO_PVID) {
- res = yt921x_pvid_set(priv, port, vid);
- } else {
- br_vlan_get_pvid(bdev, &pvid);
- if (pvid == vid)
- res = yt921x_pvid_clear(priv, port);
- }
- }
- } while (0);
- mutex_unlock(&priv->reg_lock);
- return res;
- }
- static int yt921x_userport_standalone(struct yt921x_priv *priv, int port)
- {
- u32 mask;
- u32 ctrl;
- int res;
- ctrl = ~priv->cpu_ports_mask;
- res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port), ctrl);
- if (res)
- return res;
- /* Turn off FDB learning to prevent FDB pollution */
- mask = YT921X_PORT_LEARN_DIS;
- res = yt921x_reg_set_bits(priv, YT921X_PORTn_LEARN(port), mask);
- if (res)
- return res;
- /* Turn off VLAN awareness */
- res = yt921x_vlan_aware_set(priv, port, false);
- if (res)
- return res;
- /* Unrelated since learning is off and all packets are trapped;
- * set it anyway
- */
- res = yt921x_port_set_pvid(priv, port, YT921X_VID_UNWARE);
- if (res)
- return res;
- return 0;
- }
- static int yt921x_userport_bridge(struct yt921x_priv *priv, int port)
- {
- u32 mask;
- int res;
- mask = YT921X_PORT_LEARN_DIS;
- res = yt921x_reg_clear_bits(priv, YT921X_PORTn_LEARN(port), mask);
- if (res)
- return res;
- return 0;
- }
- static int yt921x_isolate(struct yt921x_priv *priv, int port)
- {
- u32 mask;
- int res;
- mask = BIT(port);
- for (int i = 0; i < YT921X_PORT_NUM; i++) {
- if ((BIT(i) & priv->cpu_ports_mask) || i == port)
- continue;
- res = yt921x_reg_set_bits(priv, YT921X_PORTn_ISOLATION(i),
- mask);
- if (res)
- return res;
- }
- return 0;
- }
- /* Make sure to include the CPU port in ports_mask, or your bridge will
- * not have it.
- */
- static int yt921x_bridge(struct yt921x_priv *priv, u16 ports_mask)
- {
- unsigned long targets_mask = ports_mask & ~priv->cpu_ports_mask;
- u32 isolated_mask;
- u32 ctrl;
- int port;
- int res;
- isolated_mask = 0;
- for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) {
- struct yt921x_port *pp = &priv->ports[port];
- if (pp->isolated)
- isolated_mask |= BIT(port);
- }
- /* Block from non-cpu bridge ports ... */
- for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) {
- struct yt921x_port *pp = &priv->ports[port];
- /* to non-bridge ports */
- ctrl = ~ports_mask;
- /* to isolated ports when isolated */
- if (pp->isolated)
- ctrl |= isolated_mask;
- /* to itself when non-hairpin */
- if (!pp->hairpin)
- ctrl |= BIT(port);
- else
- ctrl &= ~BIT(port);
- res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port),
- ctrl);
- if (res)
- return res;
- }
- return 0;
- }
- static int yt921x_bridge_leave(struct yt921x_priv *priv, int port)
- {
- int res;
- res = yt921x_userport_standalone(priv, port);
- if (res)
- return res;
- res = yt921x_isolate(priv, port);
- if (res)
- return res;
- return 0;
- }
- static int
- yt921x_bridge_join(struct yt921x_priv *priv, int port, u16 ports_mask)
- {
- int res;
- res = yt921x_userport_bridge(priv, port);
- if (res)
- return res;
- res = yt921x_bridge(priv, ports_mask);
- if (res)
- return res;
- return 0;
- }
- static u32
- dsa_bridge_ports(struct dsa_switch *ds, const struct net_device *bdev)
- {
- struct dsa_port *dp;
- u32 mask = 0;
- dsa_switch_for_each_user_port(dp, ds)
- if (dsa_port_offloads_bridge_dev(dp, bdev))
- mask |= BIT(dp->index);
- return mask;
- }
- static int
- yt921x_bridge_flags(struct yt921x_priv *priv, int port,
- struct switchdev_brport_flags flags)
- {
- struct yt921x_port *pp = &priv->ports[port];
- bool do_flush;
- u32 mask;
- int res;
- if (flags.mask & BR_LEARNING) {
- bool learning = flags.val & BR_LEARNING;
- mask = YT921X_PORT_LEARN_DIS;
- res = yt921x_reg_toggle_bits(priv, YT921X_PORTn_LEARN(port),
- mask, !learning);
- if (res)
- return res;
- }
- /* BR_FLOOD, BR_MCAST_FLOOD: see the comment where ACT_UNK_ACTn_TRAP
- * is set
- */
- /* BR_BCAST_FLOOD: we can filter bcast, but cannot trap them */
- do_flush = false;
- if (flags.mask & BR_HAIRPIN_MODE) {
- pp->hairpin = flags.val & BR_HAIRPIN_MODE;
- do_flush = true;
- }
- if (flags.mask & BR_ISOLATED) {
- pp->isolated = flags.val & BR_ISOLATED;
- do_flush = true;
- }
- if (do_flush) {
- struct dsa_switch *ds = &priv->ds;
- struct dsa_port *dp = dsa_to_port(ds, port);
- struct net_device *bdev;
- bdev = dsa_port_bridge_dev_get(dp);
- if (bdev) {
- u32 ports_mask;
- ports_mask = dsa_bridge_ports(ds, bdev);
- ports_mask |= priv->cpu_ports_mask;
- res = yt921x_bridge(priv, ports_mask);
- if (res)
- return res;
- }
- }
- return 0;
- }
- static int
- yt921x_dsa_port_pre_bridge_flags(struct dsa_switch *ds, int port,
- struct switchdev_brport_flags flags,
- struct netlink_ext_ack *extack)
- {
- if (flags.mask & ~(BR_HAIRPIN_MODE | BR_LEARNING | BR_FLOOD |
- BR_MCAST_FLOOD | BR_ISOLATED))
- return -EINVAL;
- return 0;
- }
- static int
- yt921x_dsa_port_bridge_flags(struct dsa_switch *ds, int port,
- struct switchdev_brport_flags flags,
- struct netlink_ext_ack *extack)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- int res;
- if (dsa_is_cpu_port(ds, port))
- return 0;
- mutex_lock(&priv->reg_lock);
- res = yt921x_bridge_flags(priv, port, flags);
- mutex_unlock(&priv->reg_lock);
- return res;
- }
- static void
- yt921x_dsa_port_bridge_leave(struct dsa_switch *ds, int port,
- struct dsa_bridge bridge)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct device *dev = to_device(priv);
- int res;
- if (dsa_is_cpu_port(ds, port))
- return;
- mutex_lock(&priv->reg_lock);
- res = yt921x_bridge_leave(priv, port);
- mutex_unlock(&priv->reg_lock);
- if (res)
- dev_err(dev, "Failed to %s port %d: %i\n", "unbridge",
- port, res);
- }
- static int
- yt921x_dsa_port_bridge_join(struct dsa_switch *ds, int port,
- struct dsa_bridge bridge, bool *tx_fwd_offload,
- struct netlink_ext_ack *extack)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- u16 ports_mask;
- int res;
- if (dsa_is_cpu_port(ds, port))
- return 0;
- ports_mask = dsa_bridge_ports(ds, bridge.dev);
- ports_mask |= priv->cpu_ports_mask;
- mutex_lock(&priv->reg_lock);
- res = yt921x_bridge_join(priv, port, ports_mask);
- mutex_unlock(&priv->reg_lock);
- return res;
- }
- static int
- yt921x_dsa_port_mst_state_set(struct dsa_switch *ds, int port,
- const struct switchdev_mst_state *st)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- u32 mask;
- u32 ctrl;
- int res;
- mask = YT921X_STP_PORTn_M(port);
- switch (st->state) {
- case BR_STATE_DISABLED:
- ctrl = YT921X_STP_PORTn_DISABLED(port);
- break;
- case BR_STATE_LISTENING:
- case BR_STATE_LEARNING:
- ctrl = YT921X_STP_PORTn_LEARNING(port);
- break;
- case BR_STATE_FORWARDING:
- default:
- ctrl = YT921X_STP_PORTn_FORWARD(port);
- break;
- case BR_STATE_BLOCKING:
- ctrl = YT921X_STP_PORTn_BLOCKING(port);
- break;
- }
- mutex_lock(&priv->reg_lock);
- res = yt921x_reg_update_bits(priv, YT921X_STPn(st->msti), mask, ctrl);
- mutex_unlock(&priv->reg_lock);
- return res;
- }
- static int
- yt921x_dsa_vlan_msti_set(struct dsa_switch *ds, struct dsa_bridge bridge,
- const struct switchdev_vlan_msti *msti)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- u64 mask64;
- u64 ctrl64;
- int res;
- if (!msti->vid)
- return -EINVAL;
- if (!msti->msti || msti->msti >= YT921X_MSTI_NUM)
- return -EINVAL;
- mask64 = YT921X_VLAN_CTRL_STP_ID_M;
- ctrl64 = YT921X_VLAN_CTRL_STP_ID(msti->msti);
- mutex_lock(&priv->reg_lock);
- res = yt921x_reg64_update_bits(priv, YT921X_VLANn_CTRL(msti->vid),
- mask64, ctrl64);
- mutex_unlock(&priv->reg_lock);
- return res;
- }
- static void
- yt921x_dsa_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct dsa_port *dp = dsa_to_port(ds, port);
- struct device *dev = to_device(priv);
- bool learning;
- u32 mask;
- u32 ctrl;
- int res;
- mask = YT921X_STP_PORTn_M(port);
- learning = false;
- switch (state) {
- case BR_STATE_DISABLED:
- ctrl = YT921X_STP_PORTn_DISABLED(port);
- break;
- case BR_STATE_LISTENING:
- ctrl = YT921X_STP_PORTn_LEARNING(port);
- break;
- case BR_STATE_LEARNING:
- ctrl = YT921X_STP_PORTn_LEARNING(port);
- learning = dp->learning;
- break;
- case BR_STATE_FORWARDING:
- default:
- ctrl = YT921X_STP_PORTn_FORWARD(port);
- learning = dp->learning;
- break;
- case BR_STATE_BLOCKING:
- ctrl = YT921X_STP_PORTn_BLOCKING(port);
- break;
- }
- mutex_lock(&priv->reg_lock);
- do {
- res = yt921x_reg_update_bits(priv, YT921X_STPn(0), mask, ctrl);
- if (res)
- break;
- mask = YT921X_PORT_LEARN_DIS;
- ctrl = !learning ? YT921X_PORT_LEARN_DIS : 0;
- res = yt921x_reg_update_bits(priv, YT921X_PORTn_LEARN(port),
- mask, ctrl);
- } while (0);
- mutex_unlock(&priv->reg_lock);
- if (res)
- dev_err(dev, "Failed to %s port %d: %i\n", "set STP state for",
- port, res);
- }
- static int __maybe_unused
- yt921x_dsa_port_get_default_prio(struct dsa_switch *ds, int port)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- u32 val;
- int res;
- mutex_lock(&priv->reg_lock);
- res = yt921x_reg_read(priv, YT921X_PORTn_QOS(port), &val);
- mutex_unlock(&priv->reg_lock);
- if (res)
- return res;
- return FIELD_GET(YT921X_PORT_QOS_PRIO_M, val);
- }
- static int __maybe_unused
- yt921x_dsa_port_set_default_prio(struct dsa_switch *ds, int port, u8 prio)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- u32 mask;
- u32 ctrl;
- int res;
- if (prio >= YT921X_PRIO_NUM)
- return -EINVAL;
- mutex_lock(&priv->reg_lock);
- mask = YT921X_PORT_QOS_PRIO_M | YT921X_PORT_QOS_PRIO_EN;
- ctrl = YT921X_PORT_QOS_PRIO(prio) | YT921X_PORT_QOS_PRIO_EN;
- res = yt921x_reg_update_bits(priv, YT921X_PORTn_QOS(port), mask, ctrl);
- mutex_unlock(&priv->reg_lock);
- return res;
- }
- static int __maybe_unused appprios_cmp(const void *a, const void *b)
- {
- return ((const u8 *)b)[1] - ((const u8 *)a)[1];
- }
- static int __maybe_unused
- yt921x_dsa_port_get_apptrust(struct dsa_switch *ds, int port, u8 *sel,
- int *nselp)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- u8 appprios[2][2] = {};
- int nsel;
- u32 val;
- int res;
- mutex_lock(&priv->reg_lock);
- res = yt921x_reg_read(priv, YT921X_PORTn_PRIO_ORD(port), &val);
- mutex_unlock(&priv->reg_lock);
- if (res)
- return res;
- appprios[0][0] = IEEE_8021QAZ_APP_SEL_DSCP;
- appprios[0][1] = (val >> (3 * YT921X_APP_SEL_DSCP)) & 7;
- appprios[1][0] = DCB_APP_SEL_PCP;
- appprios[1][1] = (val >> (3 * YT921X_APP_SEL_CVLAN_PCP)) & 7;
- sort(appprios, ARRAY_SIZE(appprios), sizeof(appprios[0]), appprios_cmp,
- NULL);
- nsel = 0;
- for (int i = 0; i < ARRAY_SIZE(appprios) && appprios[i][1]; i++) {
- sel[nsel] = appprios[i][0];
- nsel++;
- }
- *nselp = nsel;
- return 0;
- }
- static int __maybe_unused
- yt921x_dsa_port_set_apptrust(struct dsa_switch *ds, int port, const u8 *sel,
- int nsel)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct device *dev = to_device(priv);
- u32 ctrl;
- int res;
- if (nsel > YT921X_APP_SEL_NUM)
- return -EINVAL;
- ctrl = 0;
- for (int i = 0; i < nsel; i++) {
- switch (sel[i]) {
- case IEEE_8021QAZ_APP_SEL_DSCP:
- ctrl |= YT921X_PORT_PRIO_ORD_APPm(YT921X_APP_SEL_DSCP,
- 7 - i);
- break;
- case DCB_APP_SEL_PCP:
- ctrl |= YT921X_PORT_PRIO_ORD_APPm(YT921X_APP_SEL_CVLAN_PCP,
- 7 - i);
- ctrl |= YT921X_PORT_PRIO_ORD_APPm(YT921X_APP_SEL_SVLAN_PCP,
- 7 - i);
- break;
- default:
- dev_err(dev,
- "Invalid apptrust selector (at %d-th). Supported: dscp, pcp\n",
- i + 1);
- return -EOPNOTSUPP;
- }
- }
- mutex_lock(&priv->reg_lock);
- res = yt921x_reg_write(priv, YT921X_PORTn_PRIO_ORD(port), ctrl);
- mutex_unlock(&priv->reg_lock);
- return res;
- }
- static int yt921x_port_down(struct yt921x_priv *priv, int port)
- {
- u32 mask;
- int res;
- mask = YT921X_PORT_LINK | YT921X_PORT_RX_MAC_EN | YT921X_PORT_TX_MAC_EN;
- res = yt921x_reg_clear_bits(priv, YT921X_PORTn_CTRL(port), mask);
- if (res)
- return res;
- if (yt921x_port_is_external(port)) {
- mask = YT921X_SERDES_LINK;
- res = yt921x_reg_clear_bits(priv, YT921X_SERDESn(port), mask);
- if (res)
- return res;
- mask = YT921X_XMII_LINK;
- res = yt921x_reg_clear_bits(priv, YT921X_XMIIn(port), mask);
- if (res)
- return res;
- }
- return 0;
- }
- static int
- yt921x_port_up(struct yt921x_priv *priv, int port, unsigned int mode,
- phy_interface_t interface, int speed, int duplex,
- bool tx_pause, bool rx_pause)
- {
- u32 mask;
- u32 ctrl;
- int res;
- switch (speed) {
- case SPEED_10:
- ctrl = YT921X_PORT_SPEED_10;
- break;
- case SPEED_100:
- ctrl = YT921X_PORT_SPEED_100;
- break;
- case SPEED_1000:
- ctrl = YT921X_PORT_SPEED_1000;
- break;
- case SPEED_2500:
- ctrl = YT921X_PORT_SPEED_2500;
- break;
- case SPEED_10000:
- ctrl = YT921X_PORT_SPEED_10000;
- break;
- default:
- return -EINVAL;
- }
- if (duplex == DUPLEX_FULL)
- ctrl |= YT921X_PORT_DUPLEX_FULL;
- if (tx_pause)
- ctrl |= YT921X_PORT_TX_PAUSE;
- if (rx_pause)
- ctrl |= YT921X_PORT_RX_PAUSE;
- ctrl |= YT921X_PORT_RX_MAC_EN | YT921X_PORT_TX_MAC_EN;
- res = yt921x_reg_write(priv, YT921X_PORTn_CTRL(port), ctrl);
- if (res)
- return res;
- if (yt921x_port_is_external(port)) {
- mask = YT921X_SERDES_SPEED_M;
- switch (speed) {
- case SPEED_10:
- ctrl = YT921X_SERDES_SPEED_10;
- break;
- case SPEED_100:
- ctrl = YT921X_SERDES_SPEED_100;
- break;
- case SPEED_1000:
- ctrl = YT921X_SERDES_SPEED_1000;
- break;
- case SPEED_2500:
- ctrl = YT921X_SERDES_SPEED_2500;
- break;
- case SPEED_10000:
- ctrl = YT921X_SERDES_SPEED_10000;
- break;
- default:
- return -EINVAL;
- }
- mask |= YT921X_SERDES_DUPLEX_FULL;
- if (duplex == DUPLEX_FULL)
- ctrl |= YT921X_SERDES_DUPLEX_FULL;
- mask |= YT921X_SERDES_TX_PAUSE;
- if (tx_pause)
- ctrl |= YT921X_SERDES_TX_PAUSE;
- mask |= YT921X_SERDES_RX_PAUSE;
- if (rx_pause)
- ctrl |= YT921X_SERDES_RX_PAUSE;
- mask |= YT921X_SERDES_LINK;
- ctrl |= YT921X_SERDES_LINK;
- res = yt921x_reg_update_bits(priv, YT921X_SERDESn(port),
- mask, ctrl);
- if (res)
- return res;
- mask = YT921X_XMII_LINK;
- res = yt921x_reg_set_bits(priv, YT921X_XMIIn(port), mask);
- if (res)
- return res;
- switch (speed) {
- case SPEED_10:
- ctrl = YT921X_MDIO_POLLING_SPEED_10;
- break;
- case SPEED_100:
- ctrl = YT921X_MDIO_POLLING_SPEED_100;
- break;
- case SPEED_1000:
- ctrl = YT921X_MDIO_POLLING_SPEED_1000;
- break;
- case SPEED_2500:
- ctrl = YT921X_MDIO_POLLING_SPEED_2500;
- break;
- case SPEED_10000:
- ctrl = YT921X_MDIO_POLLING_SPEED_10000;
- break;
- default:
- return -EINVAL;
- }
- if (duplex == DUPLEX_FULL)
- ctrl |= YT921X_MDIO_POLLING_DUPLEX_FULL;
- ctrl |= YT921X_MDIO_POLLING_LINK;
- res = yt921x_reg_write(priv, YT921X_MDIO_POLLINGn(port), ctrl);
- if (res)
- return res;
- }
- return 0;
- }
- static int
- yt921x_port_config(struct yt921x_priv *priv, int port, unsigned int mode,
- phy_interface_t interface)
- {
- struct device *dev = to_device(priv);
- u32 mask;
- u32 ctrl;
- int res;
- if (!yt921x_port_is_external(port)) {
- if (interface != PHY_INTERFACE_MODE_INTERNAL) {
- dev_err(dev, "Wrong mode %d on port %d\n",
- interface, port);
- return -EINVAL;
- }
- return 0;
- }
- switch (interface) {
- /* SERDES */
- case PHY_INTERFACE_MODE_SGMII:
- case PHY_INTERFACE_MODE_100BASEX:
- case PHY_INTERFACE_MODE_1000BASEX:
- case PHY_INTERFACE_MODE_2500BASEX:
- mask = YT921X_SERDES_CTRL_PORTn(port);
- res = yt921x_reg_set_bits(priv, YT921X_SERDES_CTRL, mask);
- if (res)
- return res;
- mask = YT921X_XMII_CTRL_PORTn(port);
- res = yt921x_reg_clear_bits(priv, YT921X_XMII_CTRL, mask);
- if (res)
- return res;
- mask = YT921X_SERDES_MODE_M;
- switch (interface) {
- case PHY_INTERFACE_MODE_SGMII:
- ctrl = YT921X_SERDES_MODE_SGMII;
- break;
- case PHY_INTERFACE_MODE_100BASEX:
- ctrl = YT921X_SERDES_MODE_100BASEX;
- break;
- case PHY_INTERFACE_MODE_1000BASEX:
- ctrl = YT921X_SERDES_MODE_1000BASEX;
- break;
- case PHY_INTERFACE_MODE_2500BASEX:
- ctrl = YT921X_SERDES_MODE_2500BASEX;
- break;
- default:
- return -EINVAL;
- }
- res = yt921x_reg_update_bits(priv, YT921X_SERDESn(port),
- mask, ctrl);
- if (res)
- return res;
- break;
- /* add XMII support here */
- default:
- return -EINVAL;
- }
- return 0;
- }
- static void
- yt921x_phylink_mac_link_down(struct phylink_config *config, unsigned int mode,
- phy_interface_t interface)
- {
- struct dsa_port *dp = dsa_phylink_to_port(config);
- struct yt921x_priv *priv = to_yt921x_priv(dp->ds);
- int port = dp->index;
- int res;
- /* No need to sync; port control block is hold until device remove */
- cancel_delayed_work(&priv->ports[port].mib_read);
- mutex_lock(&priv->reg_lock);
- res = yt921x_port_down(priv, port);
- mutex_unlock(&priv->reg_lock);
- if (res)
- dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring down",
- port, res);
- }
- static void
- yt921x_phylink_mac_link_up(struct phylink_config *config,
- struct phy_device *phydev, unsigned int mode,
- phy_interface_t interface, int speed, int duplex,
- bool tx_pause, bool rx_pause)
- {
- struct dsa_port *dp = dsa_phylink_to_port(config);
- struct yt921x_priv *priv = to_yt921x_priv(dp->ds);
- int port = dp->index;
- int res;
- mutex_lock(&priv->reg_lock);
- res = yt921x_port_up(priv, port, mode, interface, speed, duplex,
- tx_pause, rx_pause);
- mutex_unlock(&priv->reg_lock);
- if (res)
- dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring up",
- port, res);
- schedule_delayed_work(&priv->ports[port].mib_read, 0);
- }
- static void
- yt921x_phylink_mac_config(struct phylink_config *config, unsigned int mode,
- const struct phylink_link_state *state)
- {
- struct dsa_port *dp = dsa_phylink_to_port(config);
- struct yt921x_priv *priv = to_yt921x_priv(dp->ds);
- int port = dp->index;
- int res;
- mutex_lock(&priv->reg_lock);
- res = yt921x_port_config(priv, port, mode, state->interface);
- mutex_unlock(&priv->reg_lock);
- if (res)
- dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "config",
- port, res);
- }
- static void
- yt921x_dsa_phylink_get_caps(struct dsa_switch *ds, int port,
- struct phylink_config *config)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- const struct yt921x_info *info = priv->info;
- config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
- MAC_10 | MAC_100 | MAC_1000;
- if (info->internal_mask & BIT(port)) {
- /* Port 10 for MCU should probably go here too. But since that
- * is untested yet, turn it down for the moment by letting it
- * fall to the default branch.
- */
- __set_bit(PHY_INTERFACE_MODE_INTERNAL,
- config->supported_interfaces);
- } else if (info->external_mask & BIT(port)) {
- /* TODO: external ports may support SERDES only, XMII only, or
- * SERDES + XMII depending on the chip. However, we can't get
- * the accurate config table due to lack of document, thus
- * we simply declare SERDES + XMII and rely on the correctness
- * of devicetree for now.
- */
- /* SERDES */
- __set_bit(PHY_INTERFACE_MODE_SGMII,
- config->supported_interfaces);
- /* REVSGMII (SGMII in PHY role) should go here, once
- * PHY_INTERFACE_MODE_REVSGMII is introduced.
- */
- __set_bit(PHY_INTERFACE_MODE_100BASEX,
- config->supported_interfaces);
- __set_bit(PHY_INTERFACE_MODE_1000BASEX,
- config->supported_interfaces);
- __set_bit(PHY_INTERFACE_MODE_2500BASEX,
- config->supported_interfaces);
- config->mac_capabilities |= MAC_2500FD;
- /* XMII */
- /* Not tested. To add support for XMII:
- * - Add proper interface modes below
- * - Handle them in yt921x_port_config()
- */
- }
- /* no such port: empty supported_interfaces causes phylink to turn it
- * down
- */
- }
- static int yt921x_port_setup(struct yt921x_priv *priv, int port)
- {
- struct dsa_switch *ds = &priv->ds;
- u32 ctrl;
- int res;
- res = yt921x_userport_standalone(priv, port);
- if (res)
- return res;
- /* Clear prio order (even if DCB is not enabled) to avoid unsolicited
- * priorities
- */
- res = yt921x_reg_write(priv, YT921X_PORTn_PRIO_ORD(port), 0);
- if (res)
- return res;
- if (dsa_is_cpu_port(ds, port)) {
- /* Egress of CPU port is supposed to be completely controlled
- * via tagging, so set to oneway isolated (drop all packets
- * without tag).
- */
- ctrl = ~(u32)0;
- res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port),
- ctrl);
- if (res)
- return res;
- /* To simplify FDB "isolation" simulation, we also disable
- * learning on the CPU port, and let software identify packets
- * towarding CPU (either trapped or a static FDB entry is
- * matched, no matter which bridge that entry is for), which is
- * already done by yt921x_userport_standalone(). As a result,
- * VLAN-awareness becomes unrelated on the CPU port (set to
- * VLAN-unaware by the way).
- */
- }
- return 0;
- }
- static enum dsa_tag_protocol
- yt921x_dsa_get_tag_protocol(struct dsa_switch *ds, int port,
- enum dsa_tag_protocol m)
- {
- return DSA_TAG_PROTO_YT921X;
- }
- static int yt921x_dsa_port_setup(struct dsa_switch *ds, int port)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- int res;
- mutex_lock(&priv->reg_lock);
- res = yt921x_port_setup(priv, port);
- mutex_unlock(&priv->reg_lock);
- return res;
- }
- /* Not "port" - DSCP mapping is global */
- static int __maybe_unused
- yt921x_dsa_port_get_dscp_prio(struct dsa_switch *ds, int port, u8 dscp)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- u32 val;
- int res;
- mutex_lock(&priv->reg_lock);
- res = yt921x_reg_read(priv, YT921X_IPM_DSCPn(dscp), &val);
- mutex_unlock(&priv->reg_lock);
- if (res)
- return res;
- return FIELD_GET(YT921X_IPM_PRIO_M, val);
- }
- static int __maybe_unused
- yt921x_dsa_port_del_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, u8 prio)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- u32 val;
- int res;
- mutex_lock(&priv->reg_lock);
- /* During a "dcb app replace" command, the new app table entry will be
- * added first, then the old one will be deleted. But the hardware only
- * supports one QoS class per DSCP value (duh), so if we blindly delete
- * the app table entry for this DSCP value, we end up deleting the
- * entry with the new priority. Avoid that by checking whether user
- * space wants to delete the priority which is currently configured, or
- * something else which is no longer current.
- */
- res = yt921x_reg_read(priv, YT921X_IPM_DSCPn(dscp), &val);
- if (!res && FIELD_GET(YT921X_IPM_PRIO_M, val) == prio)
- res = yt921x_reg_write(priv, YT921X_IPM_DSCPn(dscp),
- YT921X_IPM_PRIO(IEEE8021Q_TT_BK));
- mutex_unlock(&priv->reg_lock);
- return res;
- }
- static int __maybe_unused
- yt921x_dsa_port_add_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, u8 prio)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- int res;
- if (prio >= YT921X_PRIO_NUM)
- return -EINVAL;
- mutex_lock(&priv->reg_lock);
- res = yt921x_reg_write(priv, YT921X_IPM_DSCPn(dscp),
- YT921X_IPM_PRIO(prio));
- mutex_unlock(&priv->reg_lock);
- return res;
- }
- static int yt921x_edata_wait(struct yt921x_priv *priv, u32 *valp)
- {
- u32 val = YT921X_EDATA_DATA_IDLE;
- int res;
- res = yt921x_reg_wait(priv, YT921X_EDATA_DATA,
- YT921X_EDATA_DATA_STATUS_M, &val);
- if (res)
- return res;
- *valp = val;
- return 0;
- }
- static int
- yt921x_edata_read_cont(struct yt921x_priv *priv, u8 addr, u8 *valp)
- {
- u32 ctrl;
- u32 val;
- int res;
- ctrl = YT921X_EDATA_CTRL_ADDR(addr) | YT921X_EDATA_CTRL_READ;
- res = yt921x_reg_write(priv, YT921X_EDATA_CTRL, ctrl);
- if (res)
- return res;
- res = yt921x_edata_wait(priv, &val);
- if (res)
- return res;
- *valp = FIELD_GET(YT921X_EDATA_DATA_DATA_M, val);
- return 0;
- }
- static int yt921x_edata_read(struct yt921x_priv *priv, u8 addr, u8 *valp)
- {
- u32 val;
- int res;
- res = yt921x_edata_wait(priv, &val);
- if (res)
- return res;
- return yt921x_edata_read_cont(priv, addr, valp);
- }
- static int yt921x_chip_detect(struct yt921x_priv *priv)
- {
- struct device *dev = to_device(priv);
- const struct yt921x_info *info;
- u8 extmode;
- u32 chipid;
- u32 major;
- u32 mode;
- int res;
- res = yt921x_reg_read(priv, YT921X_CHIP_ID, &chipid);
- if (res)
- return res;
- major = FIELD_GET(YT921X_CHIP_ID_MAJOR, chipid);
- for (info = yt921x_infos; info->name; info++)
- if (info->major == major)
- break;
- if (!info->name) {
- dev_err(dev, "Unexpected chipid 0x%x\n", chipid);
- return -ENODEV;
- }
- res = yt921x_reg_read(priv, YT921X_CHIP_MODE, &mode);
- if (res)
- return res;
- res = yt921x_edata_read(priv, YT921X_EDATA_EXTMODE, &extmode);
- if (res)
- return res;
- for (; info->name; info++)
- if (info->major == major && info->mode == mode &&
- info->extmode == extmode)
- break;
- if (!info->name) {
- dev_err(dev,
- "Unsupported chipid 0x%x with chipmode 0x%x 0x%x\n",
- chipid, mode, extmode);
- return -ENODEV;
- }
- /* Print chipid here since we are interested in lower 16 bits */
- dev_info(dev,
- "Motorcomm %s ethernet switch, chipid: 0x%x, chipmode: 0x%x 0x%x\n",
- info->name, chipid, mode, extmode);
- priv->info = info;
- return 0;
- }
- static int yt921x_chip_reset(struct yt921x_priv *priv)
- {
- struct device *dev = to_device(priv);
- u16 eth_p_tag;
- u32 val;
- int res;
- res = yt921x_chip_detect(priv);
- if (res)
- return res;
- /* Reset */
- res = yt921x_reg_write(priv, YT921X_RST, YT921X_RST_HW);
- if (res)
- return res;
- /* RST_HW is almost same as GPIO hard reset, so we need this delay. */
- fsleep(YT921X_RST_DELAY_US);
- val = 0;
- res = yt921x_reg_wait(priv, YT921X_RST, ~0, &val);
- if (res)
- return res;
- /* Check for tag EtherType; do it after reset in case you messed it up
- * before.
- */
- res = yt921x_reg_read(priv, YT921X_CPU_TAG_TPID, &val);
- if (res)
- return res;
- eth_p_tag = FIELD_GET(YT921X_CPU_TAG_TPID_TPID_M, val);
- if (eth_p_tag != ETH_P_YT921X) {
- dev_err(dev, "Tag type 0x%x != 0x%x\n", eth_p_tag,
- ETH_P_YT921X);
- /* Despite being possible, we choose not to set CPU_TAG_TPID,
- * since there is no way it can be different unless you have the
- * wrong chip.
- */
- return -EINVAL;
- }
- return 0;
- }
- static int yt921x_chip_setup_dsa(struct yt921x_priv *priv)
- {
- struct dsa_switch *ds = &priv->ds;
- unsigned long cpu_ports_mask;
- u64 ctrl64;
- u32 ctrl;
- int port;
- int res;
- /* Enable DSA */
- priv->cpu_ports_mask = dsa_cpu_ports(ds);
- ctrl = YT921X_EXT_CPU_PORT_TAG_EN | YT921X_EXT_CPU_PORT_PORT_EN |
- YT921X_EXT_CPU_PORT_PORT(__ffs(priv->cpu_ports_mask));
- res = yt921x_reg_write(priv, YT921X_EXT_CPU_PORT, ctrl);
- if (res)
- return res;
- /* Setup software switch */
- ctrl = YT921X_CPU_COPY_TO_EXT_CPU;
- res = yt921x_reg_write(priv, YT921X_CPU_COPY, ctrl);
- if (res)
- return res;
- ctrl = GENMASK(10, 0);
- res = yt921x_reg_write(priv, YT921X_FILTER_UNK_UCAST, ctrl);
- if (res)
- return res;
- res = yt921x_reg_write(priv, YT921X_FILTER_UNK_MCAST, ctrl);
- if (res)
- return res;
- /* YT921x does not support native DSA port bridging, so we use port
- * isolation to emulate it. However, be especially careful that port
- * isolation takes _after_ FDB lookups, i.e. if an FDB entry (from
- * another bridge) is matched and the destination port (in another
- * bridge) is blocked, the packet will be dropped instead of flooding to
- * the "bridged" ports, thus we need to trap and handle those packets by
- * software.
- *
- * If there is no more than one bridge, we might be able to drop them
- * directly given some conditions are met, but we trap them in all cases
- * for now.
- */
- ctrl = 0;
- for (int i = 0; i < YT921X_PORT_NUM; i++)
- ctrl |= YT921X_ACT_UNK_ACTn_TRAP(i);
- /* Except for CPU ports, if any packets are sent via CPU ports without
- * tag, they should be dropped.
- */
- cpu_ports_mask = priv->cpu_ports_mask;
- for_each_set_bit(port, &cpu_ports_mask, YT921X_PORT_NUM) {
- ctrl &= ~YT921X_ACT_UNK_ACTn_M(port);
- ctrl |= YT921X_ACT_UNK_ACTn_DROP(port);
- }
- res = yt921x_reg_write(priv, YT921X_ACT_UNK_UCAST, ctrl);
- if (res)
- return res;
- res = yt921x_reg_write(priv, YT921X_ACT_UNK_MCAST, ctrl);
- if (res)
- return res;
- /* Tagged VID 0 should be treated as untagged, which confuses the
- * hardware a lot
- */
- ctrl64 = YT921X_VLAN_CTRL_LEARN_DIS | YT921X_VLAN_CTRL_PORTS_M;
- res = yt921x_reg64_write(priv, YT921X_VLANn_CTRL(0), ctrl64);
- if (res)
- return res;
- return 0;
- }
- static int __maybe_unused yt921x_chip_setup_qos(struct yt921x_priv *priv)
- {
- u32 ctrl;
- int res;
- /* DSCP to internal priorities */
- for (u8 dscp = 0; dscp < DSCP_MAX; dscp++) {
- int prio = ietf_dscp_to_ieee8021q_tt(dscp);
- if (prio < 0)
- return prio;
- res = yt921x_reg_write(priv, YT921X_IPM_DSCPn(dscp),
- YT921X_IPM_PRIO(prio));
- if (res)
- return res;
- }
- /* 802.1Q QoS to internal priorities */
- for (u8 pcp = 0; pcp < 8; pcp++)
- for (u8 dei = 0; dei < 2; dei++) {
- ctrl = YT921X_IPM_PRIO(pcp);
- if (dei)
- /* "Red" almost means drop, so it's not that
- * useful. Note that tc police does not support
- * Three-Color very well
- */
- ctrl |= YT921X_IPM_COLOR_YELLOW;
- for (u8 svlan = 0; svlan < 2; svlan++) {
- u32 reg = YT921X_IPM_PCPn(svlan, dei, pcp);
- res = yt921x_reg_write(priv, reg, ctrl);
- if (res)
- return res;
- }
- }
- return 0;
- }
- static int yt921x_chip_setup(struct yt921x_priv *priv)
- {
- u32 ctrl;
- int res;
- ctrl = YT921X_FUNC_MIB;
- res = yt921x_reg_set_bits(priv, YT921X_FUNC, ctrl);
- if (res)
- return res;
- res = yt921x_chip_setup_dsa(priv);
- if (res)
- return res;
- #if IS_ENABLED(CONFIG_DCB)
- res = yt921x_chip_setup_qos(priv);
- if (res)
- return res;
- #endif
- /* Clear MIB */
- ctrl = YT921X_MIB_CTRL_CLEAN | YT921X_MIB_CTRL_ALL_PORT;
- res = yt921x_reg_write(priv, YT921X_MIB_CTRL, ctrl);
- if (res)
- return res;
- /* Miscellaneous */
- res = yt921x_reg_set_bits(priv, YT921X_SENSOR, YT921X_SENSOR_TEMP);
- if (res)
- return res;
- return 0;
- }
- static int yt921x_dsa_setup(struct dsa_switch *ds)
- {
- struct yt921x_priv *priv = to_yt921x_priv(ds);
- struct device *dev = to_device(priv);
- struct device_node *np = dev->of_node;
- struct device_node *child;
- int res;
- mutex_lock(&priv->reg_lock);
- res = yt921x_chip_reset(priv);
- mutex_unlock(&priv->reg_lock);
- if (res)
- return res;
- /* Register the internal mdio bus. Nodes for internal ports should have
- * proper phy-handle pointing to their PHYs. Not enabling the internal
- * bus is possible, though pretty wired, if internal ports are not used.
- */
- child = of_get_child_by_name(np, "mdio");
- if (child) {
- res = yt921x_mbus_int_init(priv, child);
- of_node_put(child);
- if (res)
- return res;
- }
- /* External mdio bus is optional */
- child = of_get_child_by_name(np, "mdio-external");
- if (child) {
- res = yt921x_mbus_ext_init(priv, child);
- of_node_put(child);
- if (res)
- return res;
- dev_err(dev, "Untested external mdio bus\n");
- return -ENODEV;
- }
- mutex_lock(&priv->reg_lock);
- res = yt921x_chip_setup(priv);
- mutex_unlock(&priv->reg_lock);
- if (res)
- return res;
- return 0;
- }
- static const struct phylink_mac_ops yt921x_phylink_mac_ops = {
- .mac_link_down = yt921x_phylink_mac_link_down,
- .mac_link_up = yt921x_phylink_mac_link_up,
- .mac_config = yt921x_phylink_mac_config,
- };
- static const struct dsa_switch_ops yt921x_dsa_switch_ops = {
- /* mib */
- .get_strings = yt921x_dsa_get_strings,
- .get_ethtool_stats = yt921x_dsa_get_ethtool_stats,
- .get_sset_count = yt921x_dsa_get_sset_count,
- .get_eth_mac_stats = yt921x_dsa_get_eth_mac_stats,
- .get_eth_ctrl_stats = yt921x_dsa_get_eth_ctrl_stats,
- .get_rmon_stats = yt921x_dsa_get_rmon_stats,
- .get_stats64 = yt921x_dsa_get_stats64,
- .get_pause_stats = yt921x_dsa_get_pause_stats,
- /* eee */
- .support_eee = dsa_supports_eee,
- .set_mac_eee = yt921x_dsa_set_mac_eee,
- /* mtu */
- .port_change_mtu = yt921x_dsa_port_change_mtu,
- .port_max_mtu = yt921x_dsa_port_max_mtu,
- /* hsr */
- .port_hsr_leave = dsa_port_simple_hsr_leave,
- .port_hsr_join = dsa_port_simple_hsr_join,
- /* mirror */
- .port_mirror_del = yt921x_dsa_port_mirror_del,
- .port_mirror_add = yt921x_dsa_port_mirror_add,
- /* lag */
- .port_lag_leave = yt921x_dsa_port_lag_leave,
- .port_lag_join = yt921x_dsa_port_lag_join,
- /* fdb */
- .port_fdb_dump = yt921x_dsa_port_fdb_dump,
- .port_fast_age = yt921x_dsa_port_fast_age,
- .set_ageing_time = yt921x_dsa_set_ageing_time,
- .port_fdb_del = yt921x_dsa_port_fdb_del,
- .port_fdb_add = yt921x_dsa_port_fdb_add,
- .port_mdb_del = yt921x_dsa_port_mdb_del,
- .port_mdb_add = yt921x_dsa_port_mdb_add,
- /* vlan */
- .port_vlan_filtering = yt921x_dsa_port_vlan_filtering,
- .port_vlan_del = yt921x_dsa_port_vlan_del,
- .port_vlan_add = yt921x_dsa_port_vlan_add,
- /* bridge */
- .port_pre_bridge_flags = yt921x_dsa_port_pre_bridge_flags,
- .port_bridge_flags = yt921x_dsa_port_bridge_flags,
- .port_bridge_leave = yt921x_dsa_port_bridge_leave,
- .port_bridge_join = yt921x_dsa_port_bridge_join,
- /* mst */
- .port_mst_state_set = yt921x_dsa_port_mst_state_set,
- .vlan_msti_set = yt921x_dsa_vlan_msti_set,
- .port_stp_state_set = yt921x_dsa_port_stp_state_set,
- #if IS_ENABLED(CONFIG_DCB)
- /* dcb */
- .port_get_default_prio = yt921x_dsa_port_get_default_prio,
- .port_set_default_prio = yt921x_dsa_port_set_default_prio,
- .port_get_apptrust = yt921x_dsa_port_get_apptrust,
- .port_set_apptrust = yt921x_dsa_port_set_apptrust,
- #endif
- /* port */
- .get_tag_protocol = yt921x_dsa_get_tag_protocol,
- .phylink_get_caps = yt921x_dsa_phylink_get_caps,
- .port_setup = yt921x_dsa_port_setup,
- #if IS_ENABLED(CONFIG_DCB)
- /* dscp */
- .port_get_dscp_prio = yt921x_dsa_port_get_dscp_prio,
- .port_del_dscp_prio = yt921x_dsa_port_del_dscp_prio,
- .port_add_dscp_prio = yt921x_dsa_port_add_dscp_prio,
- #endif
- /* chip */
- .setup = yt921x_dsa_setup,
- };
- static void yt921x_mdio_shutdown(struct mdio_device *mdiodev)
- {
- struct yt921x_priv *priv = mdiodev_get_drvdata(mdiodev);
- if (!priv)
- return;
- dsa_switch_shutdown(&priv->ds);
- }
- static void yt921x_mdio_remove(struct mdio_device *mdiodev)
- {
- struct yt921x_priv *priv = mdiodev_get_drvdata(mdiodev);
- if (!priv)
- return;
- for (size_t i = ARRAY_SIZE(priv->ports); i-- > 0; ) {
- struct yt921x_port *pp = &priv->ports[i];
- disable_delayed_work_sync(&pp->mib_read);
- }
- dsa_unregister_switch(&priv->ds);
- mutex_destroy(&priv->reg_lock);
- }
- static int yt921x_mdio_probe(struct mdio_device *mdiodev)
- {
- struct device *dev = &mdiodev->dev;
- struct yt921x_reg_mdio *mdio;
- struct yt921x_priv *priv;
- struct dsa_switch *ds;
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
- mdio = devm_kzalloc(dev, sizeof(*mdio), GFP_KERNEL);
- if (!mdio)
- return -ENOMEM;
- mdio->bus = mdiodev->bus;
- mdio->addr = mdiodev->addr;
- mdio->switchid = 0;
- mutex_init(&priv->reg_lock);
- priv->reg_ops = &yt921x_reg_ops_mdio;
- priv->reg_ctx = mdio;
- for (size_t i = 0; i < ARRAY_SIZE(priv->ports); i++) {
- struct yt921x_port *pp = &priv->ports[i];
- pp->index = i;
- INIT_DELAYED_WORK(&pp->mib_read, yt921x_poll_mib);
- }
- ds = &priv->ds;
- ds->dev = dev;
- ds->assisted_learning_on_cpu_port = true;
- ds->dscp_prio_mapping_is_global = true;
- ds->priv = priv;
- ds->ops = &yt921x_dsa_switch_ops;
- ds->ageing_time_min = 1 * 5000;
- ds->ageing_time_max = U16_MAX * 5000;
- ds->phylink_mac_ops = &yt921x_phylink_mac_ops;
- ds->num_lag_ids = YT921X_LAG_NUM;
- ds->num_ports = YT921X_PORT_NUM;
- mdiodev_set_drvdata(mdiodev, priv);
- return dsa_register_switch(ds);
- }
- static const struct of_device_id yt921x_of_match[] = {
- { .compatible = "motorcomm,yt9215" },
- {}
- };
- MODULE_DEVICE_TABLE(of, yt921x_of_match);
- static struct mdio_driver yt921x_mdio_driver = {
- .probe = yt921x_mdio_probe,
- .remove = yt921x_mdio_remove,
- .shutdown = yt921x_mdio_shutdown,
- .mdiodrv.driver = {
- .name = YT921X_NAME,
- .of_match_table = yt921x_of_match,
- },
- };
- mdio_module_driver(yt921x_mdio_driver);
- MODULE_AUTHOR("David Yang <mmyangfl@gmail.com>");
- MODULE_DESCRIPTION("Driver for Motorcomm YT921x Switch");
- MODULE_LICENSE("GPL");
|