| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740 |
- // SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
- /* ADIN1110 Low Power 10BASE-T1L Ethernet MAC-PHY
- * ADIN2111 2-Port Ethernet Switch with Integrated 10BASE-T1L PHY
- *
- * Copyright 2021 Analog Devices Inc.
- */
- #include <linux/bitfield.h>
- #include <linux/bits.h>
- #include <linux/cache.h>
- #include <linux/crc8.h>
- #include <linux/etherdevice.h>
- #include <linux/ethtool.h>
- #include <linux/gpio/consumer.h>
- #include <linux/if_bridge.h>
- #include <linux/interrupt.h>
- #include <linux/iopoll.h>
- #include <linux/kernel.h>
- #include <linux/mii.h>
- #include <linux/module.h>
- #include <linux/netdevice.h>
- #include <linux/regulator/consumer.h>
- #include <linux/phy.h>
- #include <linux/property.h>
- #include <linux/spi/spi.h>
- #include <net/switchdev.h>
- #include <linux/unaligned.h>
- #define ADIN1110_PHY_ID 0x1
- #define ADIN1110_RESET 0x03
- #define ADIN1110_SWRESET BIT(0)
- #define ADIN1110_CONFIG1 0x04
- #define ADIN1110_CONFIG1_SYNC BIT(15)
- #define ADIN1110_CONFIG2 0x06
- #define ADIN2111_P2_FWD_UNK2HOST BIT(12)
- #define ADIN2111_PORT_CUT_THRU_EN BIT(11)
- #define ADIN1110_CRC_APPEND BIT(5)
- #define ADIN1110_FWD_UNK2HOST BIT(2)
- #define ADIN1110_STATUS0 0x08
- #define ADIN1110_STATUS1 0x09
- #define ADIN2111_P2_RX_RDY BIT(17)
- #define ADIN1110_SPI_ERR BIT(10)
- #define ADIN1110_RX_RDY BIT(4)
- #define ADIN1110_IMASK1 0x0D
- #define ADIN2111_RX_RDY_IRQ BIT(17)
- #define ADIN1110_SPI_ERR_IRQ BIT(10)
- #define ADIN1110_RX_RDY_IRQ BIT(4)
- #define ADIN1110_TX_RDY_IRQ BIT(3)
- #define ADIN1110_MDIOACC 0x20
- #define ADIN1110_MDIO_TRDONE BIT(31)
- #define ADIN1110_MDIO_ST GENMASK(29, 28)
- #define ADIN1110_MDIO_OP GENMASK(27, 26)
- #define ADIN1110_MDIO_PRTAD GENMASK(25, 21)
- #define ADIN1110_MDIO_DEVAD GENMASK(20, 16)
- #define ADIN1110_MDIO_DATA GENMASK(15, 0)
- #define ADIN1110_TX_FSIZE 0x30
- #define ADIN1110_TX 0x31
- #define ADIN1110_TX_SPACE 0x32
- #define ADIN1110_MAC_ADDR_FILTER_UPR 0x50
- #define ADIN2111_MAC_ADDR_APPLY2PORT2 BIT(31)
- #define ADIN1110_MAC_ADDR_APPLY2PORT BIT(30)
- #define ADIN2111_MAC_ADDR_TO_OTHER_PORT BIT(17)
- #define ADIN1110_MAC_ADDR_TO_HOST BIT(16)
- #define ADIN1110_MAC_ADDR_FILTER_LWR 0x51
- #define ADIN1110_MAC_ADDR_MASK_UPR 0x70
- #define ADIN1110_MAC_ADDR_MASK_LWR 0x71
- #define ADIN1110_RX_FSIZE 0x90
- #define ADIN1110_RX 0x91
- #define ADIN2111_RX_P2_FSIZE 0xC0
- #define ADIN2111_RX_P2 0xC1
- #define ADIN1110_CLEAR_STATUS0 0xFFF
- /* MDIO_OP codes */
- #define ADIN1110_MDIO_OP_WR 0x1
- #define ADIN1110_MDIO_OP_RD 0x3
- #define ADIN1110_CD BIT(7)
- #define ADIN1110_WRITE BIT(5)
- #define ADIN1110_MAX_BUFF 2048
- #define ADIN1110_MAX_FRAMES_READ 64
- #define ADIN1110_WR_HEADER_LEN 2
- #define ADIN1110_FRAME_HEADER_LEN 2
- #define ADIN1110_INTERNAL_SIZE_HEADER_LEN 2
- #define ADIN1110_RD_HEADER_LEN 3
- #define ADIN1110_REG_LEN 4
- #define ADIN1110_FEC_LEN 4
- #define ADIN1110_PHY_ID_VAL 0x0283BC91
- #define ADIN2111_PHY_ID_VAL 0x0283BCA1
- #define ADIN_MAC_MAX_PORTS 2
- #define ADIN_MAC_MAX_ADDR_SLOTS 16
- #define ADIN_MAC_MULTICAST_ADDR_SLOT 0
- #define ADIN_MAC_BROADCAST_ADDR_SLOT 1
- #define ADIN_MAC_P1_ADDR_SLOT 2
- #define ADIN_MAC_P2_ADDR_SLOT 3
- #define ADIN_MAC_FDB_ADDR_SLOT 4
- DECLARE_CRC8_TABLE(adin1110_crc_table);
- enum adin1110_chips_id {
- ADIN1110_MAC = 0,
- ADIN2111_MAC,
- };
- struct adin1110_cfg {
- enum adin1110_chips_id id;
- const char *name;
- u32 phy_ids[PHY_MAX_ADDR];
- u32 ports_nr;
- u32 phy_id_val;
- };
- struct adin1110_port_priv {
- struct adin1110_priv *priv;
- struct net_device *netdev;
- struct net_device *bridge;
- struct phy_device *phydev;
- struct work_struct tx_work;
- u64 rx_packets;
- u64 tx_packets;
- u64 rx_bytes;
- u64 tx_bytes;
- struct work_struct rx_mode_work;
- u32 flags;
- struct sk_buff_head txq;
- u32 nr;
- u32 state;
- struct adin1110_cfg *cfg;
- };
- struct adin1110_priv {
- struct mutex lock; /* protect spi */
- spinlock_t state_lock; /* protect RX mode */
- struct mii_bus *mii_bus;
- struct spi_device *spidev;
- bool append_crc;
- struct adin1110_cfg *cfg;
- u32 tx_space;
- u32 irq_mask;
- bool forwarding;
- int irq;
- struct adin1110_port_priv *ports[ADIN_MAC_MAX_PORTS];
- char mii_bus_name[MII_BUS_ID_SIZE];
- u8 data[ADIN1110_MAX_BUFF] ____cacheline_aligned;
- };
- struct adin1110_switchdev_event_work {
- struct work_struct work;
- struct switchdev_notifier_fdb_info fdb_info;
- struct adin1110_port_priv *port_priv;
- unsigned long event;
- };
- static struct adin1110_cfg adin1110_cfgs[] = {
- {
- .id = ADIN1110_MAC,
- .name = "adin1110",
- .phy_ids = {1},
- .ports_nr = 1,
- .phy_id_val = ADIN1110_PHY_ID_VAL,
- },
- {
- .id = ADIN2111_MAC,
- .name = "adin2111",
- .phy_ids = {1, 2},
- .ports_nr = 2,
- .phy_id_val = ADIN2111_PHY_ID_VAL,
- },
- };
- static u8 adin1110_crc_data(u8 *data, u32 len)
- {
- return crc8(adin1110_crc_table, data, len, 0);
- }
- static int adin1110_read_reg(struct adin1110_priv *priv, u16 reg, u32 *val)
- {
- u32 header_len = ADIN1110_RD_HEADER_LEN;
- u32 read_len = ADIN1110_REG_LEN;
- struct spi_transfer t = {0};
- int ret;
- priv->data[0] = ADIN1110_CD | FIELD_GET(GENMASK(12, 8), reg);
- priv->data[1] = FIELD_GET(GENMASK(7, 0), reg);
- priv->data[2] = 0x00;
- if (priv->append_crc) {
- priv->data[2] = adin1110_crc_data(&priv->data[0], 2);
- priv->data[3] = 0x00;
- header_len++;
- }
- if (priv->append_crc)
- read_len++;
- memset(&priv->data[header_len], 0, read_len);
- t.tx_buf = &priv->data[0];
- t.rx_buf = &priv->data[0];
- t.len = read_len + header_len;
- ret = spi_sync_transfer(priv->spidev, &t, 1);
- if (ret)
- return ret;
- if (priv->append_crc) {
- u8 recv_crc;
- u8 crc;
- crc = adin1110_crc_data(&priv->data[header_len],
- ADIN1110_REG_LEN);
- recv_crc = priv->data[header_len + ADIN1110_REG_LEN];
- if (crc != recv_crc) {
- dev_err_ratelimited(&priv->spidev->dev, "CRC error.");
- return -EBADMSG;
- }
- }
- *val = get_unaligned_be32(&priv->data[header_len]);
- return ret;
- }
- static int adin1110_write_reg(struct adin1110_priv *priv, u16 reg, u32 val)
- {
- u32 header_len = ADIN1110_WR_HEADER_LEN;
- u32 write_len = ADIN1110_REG_LEN;
- priv->data[0] = ADIN1110_CD | ADIN1110_WRITE | FIELD_GET(GENMASK(12, 8), reg);
- priv->data[1] = FIELD_GET(GENMASK(7, 0), reg);
- if (priv->append_crc) {
- priv->data[2] = adin1110_crc_data(&priv->data[0], header_len);
- header_len++;
- }
- put_unaligned_be32(val, &priv->data[header_len]);
- if (priv->append_crc) {
- priv->data[header_len + write_len] = adin1110_crc_data(&priv->data[header_len],
- write_len);
- write_len++;
- }
- return spi_write(priv->spidev, &priv->data[0], header_len + write_len);
- }
- static int adin1110_set_bits(struct adin1110_priv *priv, u16 reg,
- unsigned long mask, unsigned long val)
- {
- u32 write_val;
- int ret;
- ret = adin1110_read_reg(priv, reg, &write_val);
- if (ret < 0)
- return ret;
- set_mask_bits(&write_val, mask, val);
- return adin1110_write_reg(priv, reg, write_val);
- }
- static int adin1110_round_len(int len)
- {
- /* can read/write only mutiples of 4 bytes of payload */
- len = ALIGN(len, 4);
- /* NOTE: ADIN1110_WR_HEADER_LEN should be used for write ops. */
- if (len + ADIN1110_RD_HEADER_LEN > ADIN1110_MAX_BUFF)
- return -EINVAL;
- return len;
- }
- static int adin1110_read_fifo(struct adin1110_port_priv *port_priv)
- {
- struct adin1110_priv *priv = port_priv->priv;
- u32 header_len = ADIN1110_RD_HEADER_LEN;
- struct spi_transfer t = {0};
- u32 frame_size_no_fcs;
- struct sk_buff *rxb;
- u32 frame_size;
- int round_len;
- u16 reg;
- int ret;
- if (!port_priv->nr) {
- reg = ADIN1110_RX;
- ret = adin1110_read_reg(priv, ADIN1110_RX_FSIZE, &frame_size);
- } else {
- reg = ADIN2111_RX_P2;
- ret = adin1110_read_reg(priv, ADIN2111_RX_P2_FSIZE,
- &frame_size);
- }
- if (ret < 0)
- return ret;
- /* The read frame size includes the extra 2 bytes
- * from the ADIN1110 frame header.
- */
- if (frame_size < ADIN1110_FRAME_HEADER_LEN + ADIN1110_FEC_LEN)
- return -EINVAL;
- round_len = adin1110_round_len(frame_size);
- if (round_len < 0)
- return -EINVAL;
- frame_size_no_fcs = frame_size - ADIN1110_FRAME_HEADER_LEN - ADIN1110_FEC_LEN;
- memset(priv->data, 0, ADIN1110_RD_HEADER_LEN);
- priv->data[0] = ADIN1110_CD | FIELD_GET(GENMASK(12, 8), reg);
- priv->data[1] = FIELD_GET(GENMASK(7, 0), reg);
- if (priv->append_crc) {
- priv->data[2] = adin1110_crc_data(&priv->data[0], 2);
- header_len++;
- }
- rxb = netdev_alloc_skb(port_priv->netdev, round_len + header_len);
- if (!rxb)
- return -ENOMEM;
- skb_put(rxb, frame_size_no_fcs + header_len + ADIN1110_FRAME_HEADER_LEN);
- t.tx_buf = &priv->data[0];
- t.rx_buf = &rxb->data[0];
- t.len = header_len + round_len;
- ret = spi_sync_transfer(priv->spidev, &t, 1);
- if (ret) {
- kfree_skb(rxb);
- return ret;
- }
- skb_pull(rxb, header_len + ADIN1110_FRAME_HEADER_LEN);
- rxb->protocol = eth_type_trans(rxb, port_priv->netdev);
- if ((port_priv->flags & IFF_ALLMULTI && rxb->pkt_type == PACKET_MULTICAST) ||
- (port_priv->flags & IFF_BROADCAST && rxb->pkt_type == PACKET_BROADCAST))
- rxb->offload_fwd_mark = port_priv->priv->forwarding;
- netif_rx(rxb);
- port_priv->rx_bytes += frame_size - ADIN1110_FRAME_HEADER_LEN;
- port_priv->rx_packets++;
- return 0;
- }
- static int adin1110_write_fifo(struct adin1110_port_priv *port_priv,
- struct sk_buff *txb)
- {
- struct adin1110_priv *priv = port_priv->priv;
- u32 header_len = ADIN1110_WR_HEADER_LEN;
- __be16 frame_header;
- int padding = 0;
- int padded_len;
- int round_len;
- int ret;
- /* Pad frame to 64 byte length,
- * MAC nor PHY will otherwise add the
- * required padding.
- * The FEC will be added by the MAC internally.
- */
- if (txb->len + ADIN1110_FEC_LEN < 64)
- padding = 64 - (txb->len + ADIN1110_FEC_LEN);
- padded_len = txb->len + padding + ADIN1110_FRAME_HEADER_LEN;
- round_len = adin1110_round_len(padded_len);
- if (round_len < 0)
- return round_len;
- ret = adin1110_write_reg(priv, ADIN1110_TX_FSIZE, padded_len);
- if (ret < 0)
- return ret;
- memset(priv->data, 0, round_len + ADIN1110_WR_HEADER_LEN);
- priv->data[0] = ADIN1110_CD | ADIN1110_WRITE;
- priv->data[0] |= FIELD_GET(GENMASK(12, 8), ADIN1110_TX);
- priv->data[1] = FIELD_GET(GENMASK(7, 0), ADIN1110_TX);
- if (priv->append_crc) {
- priv->data[2] = adin1110_crc_data(&priv->data[0], 2);
- header_len++;
- }
- /* mention the port on which to send the frame in the frame header */
- frame_header = cpu_to_be16(port_priv->nr);
- memcpy(&priv->data[header_len], &frame_header,
- ADIN1110_FRAME_HEADER_LEN);
- memcpy(&priv->data[header_len + ADIN1110_FRAME_HEADER_LEN],
- txb->data, txb->len);
- ret = spi_write(priv->spidev, &priv->data[0], round_len + header_len);
- if (ret < 0)
- return ret;
- port_priv->tx_bytes += txb->len;
- port_priv->tx_packets++;
- return 0;
- }
- static int adin1110_read_mdio_acc(struct adin1110_priv *priv)
- {
- u32 val;
- int ret;
- mutex_lock(&priv->lock);
- ret = adin1110_read_reg(priv, ADIN1110_MDIOACC, &val);
- mutex_unlock(&priv->lock);
- if (ret < 0)
- return 0;
- return val;
- }
- static int adin1110_mdio_read(struct mii_bus *bus, int phy_id, int reg)
- {
- struct adin1110_priv *priv = bus->priv;
- u32 val = 0;
- int ret;
- if (mdio_phy_id_is_c45(phy_id))
- return -EOPNOTSUPP;
- val |= FIELD_PREP(ADIN1110_MDIO_OP, ADIN1110_MDIO_OP_RD);
- val |= FIELD_PREP(ADIN1110_MDIO_ST, 0x1);
- val |= FIELD_PREP(ADIN1110_MDIO_PRTAD, phy_id);
- val |= FIELD_PREP(ADIN1110_MDIO_DEVAD, reg);
- /* write the clause 22 read command to the chip */
- mutex_lock(&priv->lock);
- ret = adin1110_write_reg(priv, ADIN1110_MDIOACC, val);
- mutex_unlock(&priv->lock);
- if (ret < 0)
- return ret;
- /* ADIN1110_MDIO_TRDONE BIT of the ADIN1110_MDIOACC
- * register is set when the read is done.
- * After the transaction is done, ADIN1110_MDIO_DATA
- * bitfield of ADIN1110_MDIOACC register will contain
- * the requested register value.
- */
- ret = readx_poll_timeout_atomic(adin1110_read_mdio_acc, priv, val,
- (val & ADIN1110_MDIO_TRDONE),
- 100, 30000);
- if (ret < 0)
- return ret;
- return (val & ADIN1110_MDIO_DATA);
- }
- static int adin1110_mdio_write(struct mii_bus *bus, int phy_id,
- int reg, u16 reg_val)
- {
- struct adin1110_priv *priv = bus->priv;
- u32 val = 0;
- int ret;
- if (mdio_phy_id_is_c45(phy_id))
- return -EOPNOTSUPP;
- val |= FIELD_PREP(ADIN1110_MDIO_OP, ADIN1110_MDIO_OP_WR);
- val |= FIELD_PREP(ADIN1110_MDIO_ST, 0x1);
- val |= FIELD_PREP(ADIN1110_MDIO_PRTAD, phy_id);
- val |= FIELD_PREP(ADIN1110_MDIO_DEVAD, reg);
- val |= FIELD_PREP(ADIN1110_MDIO_DATA, reg_val);
- /* write the clause 22 write command to the chip */
- mutex_lock(&priv->lock);
- ret = adin1110_write_reg(priv, ADIN1110_MDIOACC, val);
- mutex_unlock(&priv->lock);
- if (ret < 0)
- return ret;
- return readx_poll_timeout_atomic(adin1110_read_mdio_acc, priv, val,
- (val & ADIN1110_MDIO_TRDONE),
- 100, 30000);
- }
- /* ADIN1110 MAC-PHY contains an ADIN1100 PHY.
- * ADIN2111 MAC-PHY contains two ADIN1100 PHYs.
- * By registering a new MDIO bus we allow the PAL to discover
- * the encapsulated PHY and probe the ADIN1100 driver.
- */
- static int adin1110_register_mdiobus(struct adin1110_priv *priv,
- struct device *dev)
- {
- struct mii_bus *mii_bus;
- int ret;
- mii_bus = devm_mdiobus_alloc(dev);
- if (!mii_bus)
- return -ENOMEM;
- snprintf(priv->mii_bus_name, MII_BUS_ID_SIZE, "%s-%u",
- priv->cfg->name, spi_get_chipselect(priv->spidev, 0));
- mii_bus->name = priv->mii_bus_name;
- mii_bus->read = adin1110_mdio_read;
- mii_bus->write = adin1110_mdio_write;
- mii_bus->priv = priv;
- mii_bus->parent = dev;
- mii_bus->phy_mask = ~((u32)GENMASK(2, 0));
- snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
- ret = devm_mdiobus_register(dev, mii_bus);
- if (ret)
- return ret;
- priv->mii_bus = mii_bus;
- return 0;
- }
- static bool adin1110_port_rx_ready(struct adin1110_port_priv *port_priv,
- u32 status)
- {
- if (!netif_oper_up(port_priv->netdev))
- return false;
- if (!port_priv->nr)
- return !!(status & ADIN1110_RX_RDY);
- else
- return !!(status & ADIN2111_P2_RX_RDY);
- }
- static void adin1110_read_frames(struct adin1110_port_priv *port_priv,
- unsigned int budget)
- {
- struct adin1110_priv *priv = port_priv->priv;
- u32 status1;
- int ret;
- while (budget) {
- ret = adin1110_read_reg(priv, ADIN1110_STATUS1, &status1);
- if (ret < 0)
- return;
- if (!adin1110_port_rx_ready(port_priv, status1))
- break;
- ret = adin1110_read_fifo(port_priv);
- if (ret < 0)
- return;
- budget--;
- }
- }
- static void adin1110_wake_queues(struct adin1110_priv *priv)
- {
- int i;
- for (i = 0; i < priv->cfg->ports_nr; i++)
- netif_wake_queue(priv->ports[i]->netdev);
- }
- static irqreturn_t adin1110_irq(int irq, void *p)
- {
- struct adin1110_priv *priv = p;
- u32 status1;
- u32 val;
- int ret;
- int i;
- mutex_lock(&priv->lock);
- ret = adin1110_read_reg(priv, ADIN1110_STATUS1, &status1);
- if (ret < 0)
- goto out;
- if (priv->append_crc && (status1 & ADIN1110_SPI_ERR))
- dev_warn_ratelimited(&priv->spidev->dev,
- "SPI CRC error on write.\n");
- ret = adin1110_read_reg(priv, ADIN1110_TX_SPACE, &val);
- if (ret < 0)
- goto out;
- /* TX FIFO space is expressed in half-words */
- priv->tx_space = 2 * val;
- for (i = 0; i < priv->cfg->ports_nr; i++) {
- if (adin1110_port_rx_ready(priv->ports[i], status1))
- adin1110_read_frames(priv->ports[i],
- ADIN1110_MAX_FRAMES_READ);
- }
- /* clear IRQ sources */
- adin1110_write_reg(priv, ADIN1110_STATUS0, ADIN1110_CLEAR_STATUS0);
- adin1110_write_reg(priv, ADIN1110_STATUS1, priv->irq_mask);
- out:
- mutex_unlock(&priv->lock);
- if (priv->tx_space > 0 && ret >= 0)
- adin1110_wake_queues(priv);
- return IRQ_HANDLED;
- }
- /* ADIN1110 can filter up to 16 MAC addresses, mac_nr here is the slot used */
- static int adin1110_write_mac_address(struct adin1110_port_priv *port_priv,
- int mac_nr, const u8 *addr,
- u8 *mask, u32 port_rules)
- {
- struct adin1110_priv *priv = port_priv->priv;
- u32 offset = mac_nr * 2;
- u32 port_rules_mask;
- int ret;
- u32 val;
- if (!port_priv->nr)
- port_rules_mask = ADIN1110_MAC_ADDR_APPLY2PORT;
- else
- port_rules_mask = ADIN2111_MAC_ADDR_APPLY2PORT2;
- if (port_rules & port_rules_mask)
- port_rules_mask |= ADIN1110_MAC_ADDR_TO_HOST | ADIN2111_MAC_ADDR_TO_OTHER_PORT;
- port_rules_mask |= GENMASK(15, 0);
- val = port_rules | get_unaligned_be16(&addr[0]);
- ret = adin1110_set_bits(priv, ADIN1110_MAC_ADDR_FILTER_UPR + offset,
- port_rules_mask, val);
- if (ret < 0)
- return ret;
- val = get_unaligned_be32(&addr[2]);
- ret = adin1110_write_reg(priv,
- ADIN1110_MAC_ADDR_FILTER_LWR + offset, val);
- if (ret < 0)
- return ret;
- /* Only the first two MAC address slots support masking. */
- if (mac_nr < ADIN_MAC_P1_ADDR_SLOT) {
- val = get_unaligned_be16(&mask[0]);
- ret = adin1110_write_reg(priv,
- ADIN1110_MAC_ADDR_MASK_UPR + offset,
- val);
- if (ret < 0)
- return ret;
- val = get_unaligned_be32(&mask[2]);
- return adin1110_write_reg(priv,
- ADIN1110_MAC_ADDR_MASK_LWR + offset,
- val);
- }
- return 0;
- }
- static int adin1110_clear_mac_address(struct adin1110_priv *priv, int mac_nr)
- {
- u32 offset = mac_nr * 2;
- int ret;
- ret = adin1110_write_reg(priv, ADIN1110_MAC_ADDR_FILTER_UPR + offset, 0);
- if (ret < 0)
- return ret;
- ret = adin1110_write_reg(priv, ADIN1110_MAC_ADDR_FILTER_LWR + offset, 0);
- if (ret < 0)
- return ret;
- /* only the first two MAC address slots are maskable */
- if (mac_nr <= 1) {
- ret = adin1110_write_reg(priv, ADIN1110_MAC_ADDR_MASK_UPR + offset, 0);
- if (ret < 0)
- return ret;
- ret = adin1110_write_reg(priv, ADIN1110_MAC_ADDR_MASK_LWR + offset, 0);
- }
- return ret;
- }
- static u32 adin1110_port_rules(struct adin1110_port_priv *port_priv,
- bool fw_to_host,
- bool fw_to_other_port)
- {
- u32 port_rules = 0;
- if (!port_priv->nr)
- port_rules |= ADIN1110_MAC_ADDR_APPLY2PORT;
- else
- port_rules |= ADIN2111_MAC_ADDR_APPLY2PORT2;
- if (fw_to_host)
- port_rules |= ADIN1110_MAC_ADDR_TO_HOST;
- if (fw_to_other_port && port_priv->priv->forwarding)
- port_rules |= ADIN2111_MAC_ADDR_TO_OTHER_PORT;
- return port_rules;
- }
- static int adin1110_multicast_filter(struct adin1110_port_priv *port_priv,
- int mac_nr, bool accept_multicast)
- {
- u8 mask[ETH_ALEN] = {0};
- u8 mac[ETH_ALEN] = {0};
- u32 port_rules = 0;
- mask[0] = BIT(0);
- mac[0] = BIT(0);
- if (accept_multicast && port_priv->state == BR_STATE_FORWARDING)
- port_rules = adin1110_port_rules(port_priv, true, true);
- return adin1110_write_mac_address(port_priv, mac_nr, mac,
- mask, port_rules);
- }
- static int adin1110_broadcasts_filter(struct adin1110_port_priv *port_priv,
- int mac_nr, bool accept_broadcast)
- {
- u32 port_rules = 0;
- u8 mask[ETH_ALEN];
- eth_broadcast_addr(mask);
- if (accept_broadcast && port_priv->state == BR_STATE_FORWARDING)
- port_rules = adin1110_port_rules(port_priv, true, true);
- return adin1110_write_mac_address(port_priv, mac_nr, mask,
- mask, port_rules);
- }
- static int adin1110_set_mac_address(struct net_device *netdev,
- const unsigned char *dev_addr)
- {
- struct adin1110_port_priv *port_priv = netdev_priv(netdev);
- u8 mask[ETH_ALEN];
- u32 port_rules;
- u32 mac_slot;
- if (!is_valid_ether_addr(dev_addr))
- return -EADDRNOTAVAIL;
- eth_hw_addr_set(netdev, dev_addr);
- eth_broadcast_addr(mask);
- mac_slot = (!port_priv->nr) ? ADIN_MAC_P1_ADDR_SLOT : ADIN_MAC_P2_ADDR_SLOT;
- port_rules = adin1110_port_rules(port_priv, true, false);
- return adin1110_write_mac_address(port_priv, mac_slot, netdev->dev_addr,
- mask, port_rules);
- }
- static int adin1110_ndo_set_mac_address(struct net_device *netdev, void *addr)
- {
- struct sockaddr *sa = addr;
- int ret;
- ret = eth_prepare_mac_addr_change(netdev, addr);
- if (ret < 0)
- return ret;
- return adin1110_set_mac_address(netdev, sa->sa_data);
- }
- static int adin1110_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
- {
- if (!netif_running(netdev))
- return -EINVAL;
- return phy_do_ioctl(netdev, rq, cmd);
- }
- static int adin1110_set_promisc_mode(struct adin1110_port_priv *port_priv,
- bool promisc)
- {
- struct adin1110_priv *priv = port_priv->priv;
- u32 mask;
- if (port_priv->state != BR_STATE_FORWARDING)
- promisc = false;
- if (!port_priv->nr)
- mask = ADIN1110_FWD_UNK2HOST;
- else
- mask = ADIN2111_P2_FWD_UNK2HOST;
- return adin1110_set_bits(priv, ADIN1110_CONFIG2,
- mask, promisc ? mask : 0);
- }
- static int adin1110_setup_rx_mode(struct adin1110_port_priv *port_priv)
- {
- int ret;
- ret = adin1110_set_promisc_mode(port_priv,
- !!(port_priv->flags & IFF_PROMISC));
- if (ret < 0)
- return ret;
- ret = adin1110_multicast_filter(port_priv, ADIN_MAC_MULTICAST_ADDR_SLOT,
- !!(port_priv->flags & IFF_ALLMULTI));
- if (ret < 0)
- return ret;
- ret = adin1110_broadcasts_filter(port_priv,
- ADIN_MAC_BROADCAST_ADDR_SLOT,
- !!(port_priv->flags & IFF_BROADCAST));
- if (ret < 0)
- return ret;
- return adin1110_set_bits(port_priv->priv, ADIN1110_CONFIG1,
- ADIN1110_CONFIG1_SYNC, ADIN1110_CONFIG1_SYNC);
- }
- static bool adin1110_can_offload_forwarding(struct adin1110_priv *priv)
- {
- int i;
- if (priv->cfg->id != ADIN2111_MAC)
- return false;
- /* Can't enable forwarding if ports do not belong to the same bridge */
- if (priv->ports[0]->bridge != priv->ports[1]->bridge || !priv->ports[0]->bridge)
- return false;
- /* Can't enable forwarding if there is a port
- * that has been blocked by STP.
- */
- for (i = 0; i < priv->cfg->ports_nr; i++) {
- if (priv->ports[i]->state != BR_STATE_FORWARDING)
- return false;
- }
- return true;
- }
- static void adin1110_rx_mode_work(struct work_struct *work)
- {
- struct adin1110_port_priv *port_priv;
- struct adin1110_priv *priv;
- port_priv = container_of(work, struct adin1110_port_priv, rx_mode_work);
- priv = port_priv->priv;
- mutex_lock(&priv->lock);
- adin1110_setup_rx_mode(port_priv);
- mutex_unlock(&priv->lock);
- }
- static void adin1110_set_rx_mode(struct net_device *dev)
- {
- struct adin1110_port_priv *port_priv = netdev_priv(dev);
- struct adin1110_priv *priv = port_priv->priv;
- spin_lock(&priv->state_lock);
- port_priv->flags = dev->flags;
- schedule_work(&port_priv->rx_mode_work);
- spin_unlock(&priv->state_lock);
- }
- static int adin1110_net_open(struct net_device *net_dev)
- {
- struct adin1110_port_priv *port_priv = netdev_priv(net_dev);
- struct adin1110_priv *priv = port_priv->priv;
- u32 val;
- int ret;
- mutex_lock(&priv->lock);
- /* Configure MAC to compute and append the FCS itself. */
- ret = adin1110_write_reg(priv, ADIN1110_CONFIG2, ADIN1110_CRC_APPEND);
- if (ret < 0)
- goto out;
- val = ADIN1110_TX_RDY_IRQ | ADIN1110_RX_RDY_IRQ | ADIN1110_SPI_ERR_IRQ;
- if (priv->cfg->id == ADIN2111_MAC)
- val |= ADIN2111_RX_RDY_IRQ;
- priv->irq_mask = val;
- ret = adin1110_write_reg(priv, ADIN1110_IMASK1, ~val);
- if (ret < 0) {
- netdev_err(net_dev, "Failed to enable chip IRQs: %d\n", ret);
- goto out;
- }
- ret = adin1110_read_reg(priv, ADIN1110_TX_SPACE, &val);
- if (ret < 0) {
- netdev_err(net_dev, "Failed to read TX FIFO space: %d\n", ret);
- goto out;
- }
- priv->tx_space = 2 * val;
- port_priv->state = BR_STATE_FORWARDING;
- ret = adin1110_set_mac_address(net_dev, net_dev->dev_addr);
- if (ret < 0) {
- netdev_err(net_dev, "Could not set MAC address: %pM, %d\n",
- net_dev->dev_addr, ret);
- goto out;
- }
- ret = adin1110_set_bits(priv, ADIN1110_CONFIG1, ADIN1110_CONFIG1_SYNC,
- ADIN1110_CONFIG1_SYNC);
- out:
- mutex_unlock(&priv->lock);
- if (ret < 0)
- return ret;
- phy_start(port_priv->phydev);
- netif_start_queue(net_dev);
- return 0;
- }
- static int adin1110_net_stop(struct net_device *net_dev)
- {
- struct adin1110_port_priv *port_priv = netdev_priv(net_dev);
- struct adin1110_priv *priv = port_priv->priv;
- u32 mask;
- int ret;
- mask = !port_priv->nr ? ADIN2111_RX_RDY_IRQ : ADIN1110_RX_RDY_IRQ;
- /* Disable RX RDY IRQs */
- mutex_lock(&priv->lock);
- ret = adin1110_set_bits(priv, ADIN1110_IMASK1, mask, mask);
- mutex_unlock(&priv->lock);
- if (ret < 0)
- return ret;
- netif_stop_queue(port_priv->netdev);
- flush_work(&port_priv->tx_work);
- phy_stop(port_priv->phydev);
- return 0;
- }
- static void adin1110_tx_work(struct work_struct *work)
- {
- struct adin1110_port_priv *port_priv;
- struct adin1110_priv *priv;
- struct sk_buff *txb;
- int ret;
- port_priv = container_of(work, struct adin1110_port_priv, tx_work);
- priv = port_priv->priv;
- mutex_lock(&priv->lock);
- while ((txb = skb_dequeue(&port_priv->txq))) {
- ret = adin1110_write_fifo(port_priv, txb);
- if (ret < 0)
- dev_err_ratelimited(&priv->spidev->dev,
- "Frame write error: %d\n", ret);
- dev_kfree_skb(txb);
- }
- mutex_unlock(&priv->lock);
- }
- static netdev_tx_t adin1110_start_xmit(struct sk_buff *skb, struct net_device *dev)
- {
- struct adin1110_port_priv *port_priv = netdev_priv(dev);
- struct adin1110_priv *priv = port_priv->priv;
- netdev_tx_t netdev_ret = NETDEV_TX_OK;
- u32 tx_space_needed;
- tx_space_needed = skb->len + ADIN1110_FRAME_HEADER_LEN + ADIN1110_INTERNAL_SIZE_HEADER_LEN;
- if (tx_space_needed > priv->tx_space) {
- netif_stop_queue(dev);
- netdev_ret = NETDEV_TX_BUSY;
- } else {
- priv->tx_space -= tx_space_needed;
- skb_queue_tail(&port_priv->txq, skb);
- }
- schedule_work(&port_priv->tx_work);
- return netdev_ret;
- }
- static void adin1110_ndo_get_stats64(struct net_device *dev,
- struct rtnl_link_stats64 *storage)
- {
- struct adin1110_port_priv *port_priv = netdev_priv(dev);
- storage->rx_packets = port_priv->rx_packets;
- storage->tx_packets = port_priv->tx_packets;
- storage->rx_bytes = port_priv->rx_bytes;
- storage->tx_bytes = port_priv->tx_bytes;
- }
- static int adin1110_port_get_port_parent_id(struct net_device *dev,
- struct netdev_phys_item_id *ppid)
- {
- struct adin1110_port_priv *port_priv = netdev_priv(dev);
- struct adin1110_priv *priv = port_priv->priv;
- ppid->id_len = strnlen(priv->mii_bus_name, MAX_PHYS_ITEM_ID_LEN);
- memcpy(ppid->id, priv->mii_bus_name, ppid->id_len);
- return 0;
- }
- static int adin1110_ndo_get_phys_port_name(struct net_device *dev,
- char *name, size_t len)
- {
- struct adin1110_port_priv *port_priv = netdev_priv(dev);
- int err;
- err = snprintf(name, len, "p%d", port_priv->nr);
- if (err >= len)
- return -EINVAL;
- return 0;
- }
- static const struct net_device_ops adin1110_netdev_ops = {
- .ndo_open = adin1110_net_open,
- .ndo_stop = adin1110_net_stop,
- .ndo_eth_ioctl = adin1110_ioctl,
- .ndo_start_xmit = adin1110_start_xmit,
- .ndo_set_mac_address = adin1110_ndo_set_mac_address,
- .ndo_set_rx_mode = adin1110_set_rx_mode,
- .ndo_validate_addr = eth_validate_addr,
- .ndo_get_stats64 = adin1110_ndo_get_stats64,
- .ndo_get_port_parent_id = adin1110_port_get_port_parent_id,
- .ndo_get_phys_port_name = adin1110_ndo_get_phys_port_name,
- };
- static void adin1110_get_drvinfo(struct net_device *dev,
- struct ethtool_drvinfo *di)
- {
- strscpy(di->driver, "ADIN1110", sizeof(di->driver));
- strscpy(di->bus_info, dev_name(dev->dev.parent), sizeof(di->bus_info));
- }
- static const struct ethtool_ops adin1110_ethtool_ops = {
- .get_drvinfo = adin1110_get_drvinfo,
- .get_link = ethtool_op_get_link,
- .get_link_ksettings = phy_ethtool_get_link_ksettings,
- .set_link_ksettings = phy_ethtool_set_link_ksettings,
- };
- static void adin1110_adjust_link(struct net_device *dev)
- {
- struct phy_device *phydev = dev->phydev;
- if (!phydev->link)
- phy_print_status(phydev);
- }
- /* PHY ID is stored in the MAC registers too,
- * check spi connection by reading it.
- */
- static int adin1110_check_spi(struct adin1110_priv *priv)
- {
- struct gpio_desc *reset_gpio;
- int ret;
- u32 val;
- reset_gpio = devm_gpiod_get_optional(&priv->spidev->dev, "reset",
- GPIOD_OUT_LOW);
- if (IS_ERR(reset_gpio))
- return dev_err_probe(&priv->spidev->dev, PTR_ERR(reset_gpio),
- "failed to get reset gpio\n");
- if (reset_gpio) {
- /* MISO pin is used for internal configuration, can't have
- * anyone else disturbing the SDO line.
- */
- spi_bus_lock(priv->spidev->controller);
- gpiod_set_value(reset_gpio, 1);
- fsleep(10000);
- gpiod_set_value(reset_gpio, 0);
- /* Need to wait 90 ms before interacting with
- * the MAC after a HW reset.
- */
- fsleep(90000);
- spi_bus_unlock(priv->spidev->controller);
- }
- ret = adin1110_read_reg(priv, ADIN1110_PHY_ID, &val);
- if (ret < 0)
- return ret;
- if (val != priv->cfg->phy_id_val) {
- dev_err(&priv->spidev->dev, "PHY ID expected: %x, read: %x\n",
- priv->cfg->phy_id_val, val);
- return -EIO;
- }
- return 0;
- }
- static int adin1110_hw_forwarding(struct adin1110_priv *priv, bool enable)
- {
- int ret;
- int i;
- priv->forwarding = enable;
- if (!priv->forwarding) {
- for (i = ADIN_MAC_FDB_ADDR_SLOT; i < ADIN_MAC_MAX_ADDR_SLOTS; i++) {
- ret = adin1110_clear_mac_address(priv, i);
- if (ret < 0)
- return ret;
- }
- }
- /* Forwarding is optimised when MAC runs in Cut Through mode. */
- ret = adin1110_set_bits(priv, ADIN1110_CONFIG2,
- ADIN2111_PORT_CUT_THRU_EN,
- priv->forwarding ? ADIN2111_PORT_CUT_THRU_EN : 0);
- if (ret < 0)
- return ret;
- for (i = 0; i < priv->cfg->ports_nr; i++) {
- ret = adin1110_setup_rx_mode(priv->ports[i]);
- if (ret < 0)
- return ret;
- }
- return ret;
- }
- static int adin1110_port_bridge_join(struct adin1110_port_priv *port_priv,
- struct net_device *bridge)
- {
- struct adin1110_priv *priv = port_priv->priv;
- int ret;
- port_priv->bridge = bridge;
- if (adin1110_can_offload_forwarding(priv)) {
- mutex_lock(&priv->lock);
- ret = adin1110_hw_forwarding(priv, true);
- mutex_unlock(&priv->lock);
- if (ret < 0)
- return ret;
- }
- return adin1110_set_mac_address(port_priv->netdev, bridge->dev_addr);
- }
- static int adin1110_port_bridge_leave(struct adin1110_port_priv *port_priv,
- struct net_device *bridge)
- {
- struct adin1110_priv *priv = port_priv->priv;
- int ret;
- port_priv->bridge = NULL;
- mutex_lock(&priv->lock);
- ret = adin1110_hw_forwarding(priv, false);
- mutex_unlock(&priv->lock);
- return ret;
- }
- static bool adin1110_port_dev_check(const struct net_device *dev)
- {
- return dev->netdev_ops == &adin1110_netdev_ops;
- }
- static int adin1110_netdevice_event(struct notifier_block *unused,
- unsigned long event, void *ptr)
- {
- struct net_device *dev = netdev_notifier_info_to_dev(ptr);
- struct adin1110_port_priv *port_priv = netdev_priv(dev);
- struct netdev_notifier_changeupper_info *info = ptr;
- int ret = 0;
- if (!adin1110_port_dev_check(dev))
- return NOTIFY_DONE;
- switch (event) {
- case NETDEV_CHANGEUPPER:
- if (netif_is_bridge_master(info->upper_dev)) {
- if (info->linking)
- ret = adin1110_port_bridge_join(port_priv, info->upper_dev);
- else
- ret = adin1110_port_bridge_leave(port_priv, info->upper_dev);
- }
- break;
- default:
- break;
- }
- return notifier_from_errno(ret);
- }
- static struct notifier_block adin1110_netdevice_nb = {
- .notifier_call = adin1110_netdevice_event,
- };
- static void adin1110_disconnect_phy(void *data)
- {
- phy_disconnect(data);
- }
- static int adin1110_port_set_forwarding_state(struct adin1110_port_priv *port_priv)
- {
- struct adin1110_priv *priv = port_priv->priv;
- int ret;
- port_priv->state = BR_STATE_FORWARDING;
- mutex_lock(&priv->lock);
- ret = adin1110_set_mac_address(port_priv->netdev,
- port_priv->netdev->dev_addr);
- if (ret < 0)
- goto out;
- if (adin1110_can_offload_forwarding(priv))
- ret = adin1110_hw_forwarding(priv, true);
- else
- ret = adin1110_setup_rx_mode(port_priv);
- out:
- mutex_unlock(&priv->lock);
- return ret;
- }
- static int adin1110_port_set_blocking_state(struct adin1110_port_priv *port_priv)
- {
- u8 mac[ETH_ALEN] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x00};
- struct adin1110_priv *priv = port_priv->priv;
- u8 mask[ETH_ALEN];
- u32 port_rules;
- int mac_slot;
- int ret;
- port_priv->state = BR_STATE_BLOCKING;
- mutex_lock(&priv->lock);
- mac_slot = (!port_priv->nr) ? ADIN_MAC_P1_ADDR_SLOT : ADIN_MAC_P2_ADDR_SLOT;
- ret = adin1110_clear_mac_address(priv, mac_slot);
- if (ret < 0)
- goto out;
- ret = adin1110_hw_forwarding(priv, false);
- if (ret < 0)
- goto out;
- /* Allow only BPDUs to be passed to the CPU */
- eth_broadcast_addr(mask);
- port_rules = adin1110_port_rules(port_priv, true, false);
- ret = adin1110_write_mac_address(port_priv, mac_slot, mac,
- mask, port_rules);
- out:
- mutex_unlock(&priv->lock);
- return ret;
- }
- /* ADIN1110/2111 does not have any native STP support.
- * Listen for bridge core state changes and
- * allow all frames to pass or only the BPDUs.
- */
- static int adin1110_port_attr_stp_state_set(struct adin1110_port_priv *port_priv,
- u8 state)
- {
- switch (state) {
- case BR_STATE_FORWARDING:
- return adin1110_port_set_forwarding_state(port_priv);
- case BR_STATE_LEARNING:
- case BR_STATE_LISTENING:
- case BR_STATE_DISABLED:
- case BR_STATE_BLOCKING:
- return adin1110_port_set_blocking_state(port_priv);
- default:
- return -EINVAL;
- }
- }
- static int adin1110_port_attr_set(struct net_device *dev, const void *ctx,
- const struct switchdev_attr *attr,
- struct netlink_ext_ack *extack)
- {
- struct adin1110_port_priv *port_priv = netdev_priv(dev);
- switch (attr->id) {
- case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
- return adin1110_port_attr_stp_state_set(port_priv,
- attr->u.stp_state);
- default:
- return -EOPNOTSUPP;
- }
- }
- static int adin1110_switchdev_blocking_event(struct notifier_block *unused,
- unsigned long event,
- void *ptr)
- {
- struct net_device *netdev = switchdev_notifier_info_to_dev(ptr);
- int ret;
- if (event == SWITCHDEV_PORT_ATTR_SET) {
- ret = switchdev_handle_port_attr_set(netdev, ptr,
- adin1110_port_dev_check,
- adin1110_port_attr_set);
- return notifier_from_errno(ret);
- }
- return NOTIFY_DONE;
- }
- static struct notifier_block adin1110_switchdev_blocking_notifier = {
- .notifier_call = adin1110_switchdev_blocking_event,
- };
- static void adin1110_fdb_offload_notify(struct net_device *netdev,
- struct switchdev_notifier_fdb_info *rcv)
- {
- struct switchdev_notifier_fdb_info info = {};
- info.addr = rcv->addr;
- info.vid = rcv->vid;
- info.offloaded = true;
- call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED,
- netdev, &info.info, NULL);
- }
- static int adin1110_fdb_add(struct adin1110_port_priv *port_priv,
- struct switchdev_notifier_fdb_info *fdb)
- {
- struct adin1110_priv *priv = port_priv->priv;
- struct adin1110_port_priv *other_port;
- u8 mask[ETH_ALEN];
- u32 port_rules;
- int mac_nr;
- u32 val;
- int ret;
- netdev_dbg(port_priv->netdev,
- "DEBUG: %s: MACID = %pM vid = %u flags = %u %u -- port %d\n",
- __func__, fdb->addr, fdb->vid, fdb->added_by_user,
- fdb->offloaded, port_priv->nr);
- if (!priv->forwarding)
- return 0;
- if (fdb->is_local)
- return -EINVAL;
- /* Find free FDB slot on device. */
- for (mac_nr = ADIN_MAC_FDB_ADDR_SLOT; mac_nr < ADIN_MAC_MAX_ADDR_SLOTS; mac_nr++) {
- ret = adin1110_read_reg(priv, ADIN1110_MAC_ADDR_FILTER_UPR + (mac_nr * 2), &val);
- if (ret < 0)
- return ret;
- if (!val)
- break;
- }
- if (mac_nr == ADIN_MAC_MAX_ADDR_SLOTS)
- return -ENOMEM;
- other_port = priv->ports[!port_priv->nr];
- port_rules = adin1110_port_rules(other_port, false, true);
- eth_broadcast_addr(mask);
- return adin1110_write_mac_address(other_port, mac_nr, (u8 *)fdb->addr,
- mask, port_rules);
- }
- static int adin1110_read_mac(struct adin1110_priv *priv, int mac_nr, u8 *addr)
- {
- u32 val;
- int ret;
- ret = adin1110_read_reg(priv, ADIN1110_MAC_ADDR_FILTER_UPR + (mac_nr * 2), &val);
- if (ret < 0)
- return ret;
- put_unaligned_be16(val, addr);
- ret = adin1110_read_reg(priv, ADIN1110_MAC_ADDR_FILTER_LWR + (mac_nr * 2), &val);
- if (ret < 0)
- return ret;
- put_unaligned_be32(val, addr + 2);
- return 0;
- }
- static int adin1110_fdb_del(struct adin1110_port_priv *port_priv,
- struct switchdev_notifier_fdb_info *fdb)
- {
- struct adin1110_priv *priv = port_priv->priv;
- u8 addr[ETH_ALEN];
- int mac_nr;
- int ret;
- netdev_dbg(port_priv->netdev,
- "DEBUG: %s: MACID = %pM vid = %u flags = %u %u -- port %d\n",
- __func__, fdb->addr, fdb->vid, fdb->added_by_user,
- fdb->offloaded, port_priv->nr);
- if (fdb->is_local)
- return -EINVAL;
- for (mac_nr = ADIN_MAC_FDB_ADDR_SLOT; mac_nr < ADIN_MAC_MAX_ADDR_SLOTS; mac_nr++) {
- ret = adin1110_read_mac(priv, mac_nr, addr);
- if (ret < 0)
- return ret;
- if (ether_addr_equal(addr, fdb->addr)) {
- ret = adin1110_clear_mac_address(priv, mac_nr);
- if (ret < 0)
- return ret;
- }
- }
- return 0;
- }
- static void adin1110_switchdev_event_work(struct work_struct *work)
- {
- struct adin1110_switchdev_event_work *switchdev_work;
- struct adin1110_port_priv *port_priv;
- int ret;
- switchdev_work = container_of(work, struct adin1110_switchdev_event_work, work);
- port_priv = switchdev_work->port_priv;
- mutex_lock(&port_priv->priv->lock);
- switch (switchdev_work->event) {
- case SWITCHDEV_FDB_ADD_TO_DEVICE:
- ret = adin1110_fdb_add(port_priv, &switchdev_work->fdb_info);
- if (!ret)
- adin1110_fdb_offload_notify(port_priv->netdev,
- &switchdev_work->fdb_info);
- break;
- case SWITCHDEV_FDB_DEL_TO_DEVICE:
- adin1110_fdb_del(port_priv, &switchdev_work->fdb_info);
- break;
- default:
- break;
- }
- mutex_unlock(&port_priv->priv->lock);
- kfree(switchdev_work->fdb_info.addr);
- kfree(switchdev_work);
- dev_put(port_priv->netdev);
- }
- /* called under rcu_read_lock() */
- static int adin1110_switchdev_event(struct notifier_block *unused,
- unsigned long event, void *ptr)
- {
- struct net_device *netdev = switchdev_notifier_info_to_dev(ptr);
- struct adin1110_port_priv *port_priv = netdev_priv(netdev);
- struct adin1110_switchdev_event_work *switchdev_work;
- struct switchdev_notifier_fdb_info *fdb_info = ptr;
- if (!adin1110_port_dev_check(netdev))
- return NOTIFY_DONE;
- switchdev_work = kzalloc_obj(*switchdev_work, GFP_ATOMIC);
- if (WARN_ON(!switchdev_work))
- return NOTIFY_BAD;
- INIT_WORK(&switchdev_work->work, adin1110_switchdev_event_work);
- switchdev_work->port_priv = port_priv;
- switchdev_work->event = event;
- switch (event) {
- case SWITCHDEV_FDB_ADD_TO_DEVICE:
- case SWITCHDEV_FDB_DEL_TO_DEVICE:
- memcpy(&switchdev_work->fdb_info, ptr,
- sizeof(switchdev_work->fdb_info));
- switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
- if (!switchdev_work->fdb_info.addr)
- goto err_addr_alloc;
- ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
- fdb_info->addr);
- dev_hold(netdev);
- break;
- default:
- kfree(switchdev_work);
- return NOTIFY_DONE;
- }
- queue_work(system_long_wq, &switchdev_work->work);
- return NOTIFY_DONE;
- err_addr_alloc:
- kfree(switchdev_work);
- return NOTIFY_BAD;
- }
- static struct notifier_block adin1110_switchdev_notifier = {
- .notifier_call = adin1110_switchdev_event,
- };
- static void adin1110_unregister_notifiers(void)
- {
- unregister_switchdev_blocking_notifier(&adin1110_switchdev_blocking_notifier);
- unregister_switchdev_notifier(&adin1110_switchdev_notifier);
- unregister_netdevice_notifier(&adin1110_netdevice_nb);
- }
- static int adin1110_setup_notifiers(void)
- {
- int ret;
- ret = register_netdevice_notifier(&adin1110_netdevice_nb);
- if (ret < 0)
- return ret;
- ret = register_switchdev_notifier(&adin1110_switchdev_notifier);
- if (ret < 0)
- goto err_netdev;
- ret = register_switchdev_blocking_notifier(&adin1110_switchdev_blocking_notifier);
- if (ret < 0)
- goto err_sdev;
- return 0;
- err_sdev:
- unregister_switchdev_notifier(&adin1110_switchdev_notifier);
- err_netdev:
- unregister_netdevice_notifier(&adin1110_netdevice_nb);
- return ret;
- }
- static int adin1110_probe_netdevs(struct adin1110_priv *priv)
- {
- struct device *dev = &priv->spidev->dev;
- struct adin1110_port_priv *port_priv;
- struct net_device *netdev;
- int ret;
- int i;
- for (i = 0; i < priv->cfg->ports_nr; i++) {
- netdev = devm_alloc_etherdev(dev, sizeof(*port_priv));
- if (!netdev)
- return -ENOMEM;
- port_priv = netdev_priv(netdev);
- port_priv->netdev = netdev;
- port_priv->priv = priv;
- port_priv->cfg = priv->cfg;
- port_priv->nr = i;
- priv->ports[i] = port_priv;
- SET_NETDEV_DEV(netdev, dev);
- ret = device_get_ethdev_address(dev, netdev);
- if (ret < 0)
- return ret;
- netdev->irq = priv->spidev->irq;
- INIT_WORK(&port_priv->tx_work, adin1110_tx_work);
- INIT_WORK(&port_priv->rx_mode_work, adin1110_rx_mode_work);
- skb_queue_head_init(&port_priv->txq);
- netif_carrier_off(netdev);
- netdev->if_port = IF_PORT_10BASET;
- netdev->netdev_ops = &adin1110_netdev_ops;
- netdev->ethtool_ops = &adin1110_ethtool_ops;
- netdev->priv_flags |= IFF_UNICAST_FLT;
- netdev->netns_immutable = true;
- port_priv->phydev = get_phy_device(priv->mii_bus, i + 1, false);
- if (IS_ERR(port_priv->phydev)) {
- netdev_err(netdev, "Could not find PHY with device address: %d.\n", i);
- return PTR_ERR(port_priv->phydev);
- }
- port_priv->phydev = phy_connect(netdev,
- phydev_name(port_priv->phydev),
- adin1110_adjust_link,
- PHY_INTERFACE_MODE_INTERNAL);
- if (IS_ERR(port_priv->phydev)) {
- netdev_err(netdev, "Could not connect PHY with device address: %d.\n", i);
- return PTR_ERR(port_priv->phydev);
- }
- ret = devm_add_action_or_reset(dev, adin1110_disconnect_phy,
- port_priv->phydev);
- if (ret < 0)
- return ret;
- }
- /* ADIN1110 INT_N pin will be used to signal the host */
- ret = devm_request_threaded_irq(dev, priv->spidev->irq, NULL,
- adin1110_irq,
- IRQF_TRIGGER_LOW | IRQF_ONESHOT,
- dev_name(dev), priv);
- if (ret < 0)
- return ret;
- for (i = 0; i < priv->cfg->ports_nr; i++) {
- ret = devm_register_netdev(dev, priv->ports[i]->netdev);
- if (ret < 0) {
- dev_err(dev, "Failed to register network device.\n");
- return ret;
- }
- }
- return 0;
- }
- static int adin1110_probe(struct spi_device *spi)
- {
- const struct spi_device_id *dev_id = spi_get_device_id(spi);
- struct device *dev = &spi->dev;
- struct adin1110_priv *priv;
- int ret;
- priv = devm_kzalloc(dev, sizeof(struct adin1110_priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
- priv->spidev = spi;
- priv->cfg = &adin1110_cfgs[dev_id->driver_data];
- spi->bits_per_word = 8;
- spi->mode = SPI_MODE_0;
- mutex_init(&priv->lock);
- spin_lock_init(&priv->state_lock);
- /* use of CRC on control and data transactions is pin dependent */
- priv->append_crc = device_property_read_bool(dev, "adi,spi-crc");
- if (priv->append_crc)
- crc8_populate_msb(adin1110_crc_table, 0x7);
- ret = adin1110_check_spi(priv);
- if (ret < 0) {
- dev_err(dev, "Probe SPI Read check failed: %d\n", ret);
- return ret;
- }
- ret = adin1110_write_reg(priv, ADIN1110_RESET, ADIN1110_SWRESET);
- if (ret < 0)
- return ret;
- ret = adin1110_register_mdiobus(priv, dev);
- if (ret < 0) {
- dev_err(dev, "Could not register MDIO bus %d\n", ret);
- return ret;
- }
- return adin1110_probe_netdevs(priv);
- }
- static const struct of_device_id adin1110_match_table[] = {
- { .compatible = "adi,adin1110" },
- { .compatible = "adi,adin2111" },
- { }
- };
- MODULE_DEVICE_TABLE(of, adin1110_match_table);
- static const struct spi_device_id adin1110_spi_id[] = {
- { .name = "adin1110", .driver_data = ADIN1110_MAC },
- { .name = "adin2111", .driver_data = ADIN2111_MAC },
- { }
- };
- MODULE_DEVICE_TABLE(spi, adin1110_spi_id);
- static struct spi_driver adin1110_driver = {
- .driver = {
- .name = "adin1110",
- .of_match_table = adin1110_match_table,
- },
- .probe = adin1110_probe,
- .id_table = adin1110_spi_id,
- };
- static int __init adin1110_driver_init(void)
- {
- int ret;
- ret = adin1110_setup_notifiers();
- if (ret < 0)
- return ret;
- ret = spi_register_driver(&adin1110_driver);
- if (ret < 0) {
- adin1110_unregister_notifiers();
- return ret;
- }
- return 0;
- }
- static void __exit adin1110_exit(void)
- {
- adin1110_unregister_notifiers();
- spi_unregister_driver(&adin1110_driver);
- }
- module_init(adin1110_driver_init);
- module_exit(adin1110_exit);
- MODULE_DESCRIPTION("ADIN1110 Network driver");
- MODULE_AUTHOR("Alexandru Tachici <alexandru.tachici@analog.com>");
- MODULE_LICENSE("Dual BSD/GPL");
|