1bc93e19dSAlexandru Tachici // SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause 2bc93e19dSAlexandru Tachici /* ADIN1110 Low Power 10BASE-T1L Ethernet MAC-PHY 3bc93e19dSAlexandru Tachici * ADIN2111 2-Port Ethernet Switch with Integrated 10BASE-T1L PHY 4bc93e19dSAlexandru Tachici * 5bc93e19dSAlexandru Tachici * Copyright 2021 Analog Devices Inc. 6bc93e19dSAlexandru Tachici */ 7bc93e19dSAlexandru Tachici 8bc93e19dSAlexandru Tachici #include <linux/bitfield.h> 9bc93e19dSAlexandru Tachici #include <linux/bits.h> 10bc93e19dSAlexandru Tachici #include <linux/cache.h> 11bc93e19dSAlexandru Tachici #include <linux/crc8.h> 12bc93e19dSAlexandru Tachici #include <linux/etherdevice.h> 13bc93e19dSAlexandru Tachici #include <linux/ethtool.h> 14bc93e19dSAlexandru Tachici #include <linux/if_bridge.h> 15bc93e19dSAlexandru Tachici #include <linux/interrupt.h> 16bc93e19dSAlexandru Tachici #include <linux/iopoll.h> 17bc93e19dSAlexandru Tachici #include <linux/gpio.h> 18bc93e19dSAlexandru Tachici #include <linux/kernel.h> 19bc93e19dSAlexandru Tachici #include <linux/mii.h> 20bc93e19dSAlexandru Tachici #include <linux/module.h> 21bc93e19dSAlexandru Tachici #include <linux/netdevice.h> 22bc93e19dSAlexandru Tachici #include <linux/regulator/consumer.h> 23bc93e19dSAlexandru Tachici #include <linux/phy.h> 24bc93e19dSAlexandru Tachici #include <linux/property.h> 25bc93e19dSAlexandru Tachici #include <linux/spi/spi.h> 26bc93e19dSAlexandru Tachici 27bc93e19dSAlexandru Tachici #include <net/switchdev.h> 28bc93e19dSAlexandru Tachici 29bc93e19dSAlexandru Tachici #include <asm/unaligned.h> 30bc93e19dSAlexandru Tachici 31bc93e19dSAlexandru Tachici #define ADIN1110_PHY_ID 0x1 32bc93e19dSAlexandru Tachici 33bc93e19dSAlexandru Tachici #define ADIN1110_RESET 0x03 34bc93e19dSAlexandru Tachici #define ADIN1110_SWRESET BIT(0) 35bc93e19dSAlexandru Tachici 36bc93e19dSAlexandru Tachici #define ADIN1110_CONFIG1 0x04 37bc93e19dSAlexandru Tachici #define ADIN1110_CONFIG1_SYNC BIT(15) 38bc93e19dSAlexandru Tachici 39bc93e19dSAlexandru Tachici #define ADIN1110_CONFIG2 0x06 40bc93e19dSAlexandru Tachici #define ADIN2111_P2_FWD_UNK2HOST BIT(12) 41bc93e19dSAlexandru Tachici #define ADIN2111_PORT_CUT_THRU_EN BIT(11) 42bc93e19dSAlexandru Tachici #define ADIN1110_CRC_APPEND BIT(5) 43bc93e19dSAlexandru Tachici #define ADIN1110_FWD_UNK2HOST BIT(2) 44bc93e19dSAlexandru Tachici 45bc93e19dSAlexandru Tachici #define ADIN1110_STATUS0 0x08 46bc93e19dSAlexandru Tachici 47bc93e19dSAlexandru Tachici #define ADIN1110_STATUS1 0x09 48bc93e19dSAlexandru Tachici #define ADIN2111_P2_RX_RDY BIT(17) 49bc93e19dSAlexandru Tachici #define ADIN1110_SPI_ERR BIT(10) 50bc93e19dSAlexandru Tachici #define ADIN1110_RX_RDY BIT(4) 51bc93e19dSAlexandru Tachici 52bc93e19dSAlexandru Tachici #define ADIN1110_IMASK1 0x0D 53bc93e19dSAlexandru Tachici #define ADIN2111_RX_RDY_IRQ BIT(17) 54bc93e19dSAlexandru Tachici #define ADIN1110_SPI_ERR_IRQ BIT(10) 55bc93e19dSAlexandru Tachici #define ADIN1110_RX_RDY_IRQ BIT(4) 56bc93e19dSAlexandru Tachici #define ADIN1110_TX_RDY_IRQ BIT(3) 57bc93e19dSAlexandru Tachici 58bc93e19dSAlexandru Tachici #define ADIN1110_MDIOACC 0x20 59bc93e19dSAlexandru Tachici #define ADIN1110_MDIO_TRDONE BIT(31) 60bc93e19dSAlexandru Tachici #define ADIN1110_MDIO_ST GENMASK(29, 28) 61bc93e19dSAlexandru Tachici #define ADIN1110_MDIO_OP GENMASK(27, 26) 62bc93e19dSAlexandru Tachici #define ADIN1110_MDIO_PRTAD GENMASK(25, 21) 63bc93e19dSAlexandru Tachici #define ADIN1110_MDIO_DEVAD GENMASK(20, 16) 64bc93e19dSAlexandru Tachici #define ADIN1110_MDIO_DATA GENMASK(15, 0) 65bc93e19dSAlexandru Tachici 66bc93e19dSAlexandru Tachici #define ADIN1110_TX_FSIZE 0x30 67bc93e19dSAlexandru Tachici #define ADIN1110_TX 0x31 68bc93e19dSAlexandru Tachici #define ADIN1110_TX_SPACE 0x32 69bc93e19dSAlexandru Tachici 70bc93e19dSAlexandru Tachici #define ADIN1110_MAC_ADDR_FILTER_UPR 0x50 71bc93e19dSAlexandru Tachici #define ADIN2111_MAC_ADDR_APPLY2PORT2 BIT(31) 72bc93e19dSAlexandru Tachici #define ADIN1110_MAC_ADDR_APPLY2PORT BIT(30) 73bc93e19dSAlexandru Tachici #define ADIN2111_MAC_ADDR_TO_OTHER_PORT BIT(17) 74bc93e19dSAlexandru Tachici #define ADIN1110_MAC_ADDR_TO_HOST BIT(16) 75bc93e19dSAlexandru Tachici 76bc93e19dSAlexandru Tachici #define ADIN1110_MAC_ADDR_FILTER_LWR 0x51 77bc93e19dSAlexandru Tachici 78bc93e19dSAlexandru Tachici #define ADIN1110_MAC_ADDR_MASK_UPR 0x70 79bc93e19dSAlexandru Tachici #define ADIN1110_MAC_ADDR_MASK_LWR 0x71 80bc93e19dSAlexandru Tachici 81bc93e19dSAlexandru Tachici #define ADIN1110_RX_FSIZE 0x90 82bc93e19dSAlexandru Tachici #define ADIN1110_RX 0x91 83bc93e19dSAlexandru Tachici 84bc93e19dSAlexandru Tachici #define ADIN2111_RX_P2_FSIZE 0xC0 85bc93e19dSAlexandru Tachici #define ADIN2111_RX_P2 0xC1 86bc93e19dSAlexandru Tachici 87bc93e19dSAlexandru Tachici #define ADIN1110_CLEAR_STATUS0 0xFFF 88bc93e19dSAlexandru Tachici 89bc93e19dSAlexandru Tachici /* MDIO_OP codes */ 90bc93e19dSAlexandru Tachici #define ADIN1110_MDIO_OP_WR 0x1 91bc93e19dSAlexandru Tachici #define ADIN1110_MDIO_OP_RD 0x3 92bc93e19dSAlexandru Tachici 93bc93e19dSAlexandru Tachici #define ADIN1110_CD BIT(7) 94bc93e19dSAlexandru Tachici #define ADIN1110_WRITE BIT(5) 95bc93e19dSAlexandru Tachici 96bc93e19dSAlexandru Tachici #define ADIN1110_MAX_BUFF 2048 97bc93e19dSAlexandru Tachici #define ADIN1110_MAX_FRAMES_READ 64 98bc93e19dSAlexandru Tachici #define ADIN1110_WR_HEADER_LEN 2 99bc93e19dSAlexandru Tachici #define ADIN1110_FRAME_HEADER_LEN 2 100bc93e19dSAlexandru Tachici #define ADIN1110_INTERNAL_SIZE_HEADER_LEN 2 101bc93e19dSAlexandru Tachici #define ADIN1110_RD_HEADER_LEN 3 102bc93e19dSAlexandru Tachici #define ADIN1110_REG_LEN 4 103bc93e19dSAlexandru Tachici #define ADIN1110_FEC_LEN 4 104bc93e19dSAlexandru Tachici 105bc93e19dSAlexandru Tachici #define ADIN1110_PHY_ID_VAL 0x0283BC91 106bc93e19dSAlexandru Tachici #define ADIN2111_PHY_ID_VAL 0x0283BCA1 107bc93e19dSAlexandru Tachici 108bc93e19dSAlexandru Tachici #define ADIN_MAC_MAX_PORTS 2 109bc93e19dSAlexandru Tachici #define ADIN_MAC_MAX_ADDR_SLOTS 16 110bc93e19dSAlexandru Tachici 111bc93e19dSAlexandru Tachici #define ADIN_MAC_MULTICAST_ADDR_SLOT 0 112bc93e19dSAlexandru Tachici #define ADIN_MAC_BROADCAST_ADDR_SLOT 1 113bc93e19dSAlexandru Tachici #define ADIN_MAC_P1_ADDR_SLOT 2 114bc93e19dSAlexandru Tachici #define ADIN_MAC_P2_ADDR_SLOT 3 115bc93e19dSAlexandru Tachici #define ADIN_MAC_FDB_ADDR_SLOT 4 116bc93e19dSAlexandru Tachici 117bc93e19dSAlexandru Tachici DECLARE_CRC8_TABLE(adin1110_crc_table); 118bc93e19dSAlexandru Tachici 119bc93e19dSAlexandru Tachici enum adin1110_chips_id { 120bc93e19dSAlexandru Tachici ADIN1110_MAC = 0, 121bc93e19dSAlexandru Tachici ADIN2111_MAC, 122bc93e19dSAlexandru Tachici }; 123bc93e19dSAlexandru Tachici 124bc93e19dSAlexandru Tachici struct adin1110_cfg { 125bc93e19dSAlexandru Tachici enum adin1110_chips_id id; 126bc93e19dSAlexandru Tachici char name[MDIO_NAME_SIZE]; 127bc93e19dSAlexandru Tachici u32 phy_ids[PHY_MAX_ADDR]; 128bc93e19dSAlexandru Tachici u32 ports_nr; 129bc93e19dSAlexandru Tachici u32 phy_id_val; 130bc93e19dSAlexandru Tachici }; 131bc93e19dSAlexandru Tachici 132bc93e19dSAlexandru Tachici struct adin1110_port_priv { 133bc93e19dSAlexandru Tachici struct adin1110_priv *priv; 134bc93e19dSAlexandru Tachici struct net_device *netdev; 135bc93e19dSAlexandru Tachici struct net_device *bridge; 136bc93e19dSAlexandru Tachici struct phy_device *phydev; 137bc93e19dSAlexandru Tachici struct work_struct tx_work; 138bc93e19dSAlexandru Tachici u64 rx_packets; 139bc93e19dSAlexandru Tachici u64 tx_packets; 140bc93e19dSAlexandru Tachici u64 rx_bytes; 141bc93e19dSAlexandru Tachici u64 tx_bytes; 142bc93e19dSAlexandru Tachici struct work_struct rx_mode_work; 143bc93e19dSAlexandru Tachici u32 flags; 144bc93e19dSAlexandru Tachici struct sk_buff_head txq; 145bc93e19dSAlexandru Tachici u32 nr; 146bc93e19dSAlexandru Tachici u32 state; 147bc93e19dSAlexandru Tachici struct adin1110_cfg *cfg; 148bc93e19dSAlexandru Tachici }; 149bc93e19dSAlexandru Tachici 150bc93e19dSAlexandru Tachici struct adin1110_priv { 151bc93e19dSAlexandru Tachici struct mutex lock; /* protect spi */ 152bc93e19dSAlexandru Tachici spinlock_t state_lock; /* protect RX mode */ 153bc93e19dSAlexandru Tachici struct mii_bus *mii_bus; 154bc93e19dSAlexandru Tachici struct spi_device *spidev; 155bc93e19dSAlexandru Tachici bool append_crc; 156bc93e19dSAlexandru Tachici struct adin1110_cfg *cfg; 157bc93e19dSAlexandru Tachici u32 tx_space; 158bc93e19dSAlexandru Tachici u32 irq_mask; 159bc93e19dSAlexandru Tachici bool forwarding; 160bc93e19dSAlexandru Tachici int irq; 161bc93e19dSAlexandru Tachici struct adin1110_port_priv *ports[ADIN_MAC_MAX_PORTS]; 162bc93e19dSAlexandru Tachici char mii_bus_name[MII_BUS_ID_SIZE]; 163bc93e19dSAlexandru Tachici u8 data[ADIN1110_MAX_BUFF] ____cacheline_aligned; 164bc93e19dSAlexandru Tachici }; 165bc93e19dSAlexandru Tachici 166bc93e19dSAlexandru Tachici struct adin1110_switchdev_event_work { 167bc93e19dSAlexandru Tachici struct work_struct work; 168bc93e19dSAlexandru Tachici struct switchdev_notifier_fdb_info fdb_info; 169bc93e19dSAlexandru Tachici struct adin1110_port_priv *port_priv; 170bc93e19dSAlexandru Tachici unsigned long event; 171bc93e19dSAlexandru Tachici }; 172bc93e19dSAlexandru Tachici 173bc93e19dSAlexandru Tachici static struct adin1110_cfg adin1110_cfgs[] = { 174bc93e19dSAlexandru Tachici { 175bc93e19dSAlexandru Tachici .id = ADIN1110_MAC, 176bc93e19dSAlexandru Tachici .name = "adin1110", 177bc93e19dSAlexandru Tachici .phy_ids = {1}, 178bc93e19dSAlexandru Tachici .ports_nr = 1, 179bc93e19dSAlexandru Tachici .phy_id_val = ADIN1110_PHY_ID_VAL, 180bc93e19dSAlexandru Tachici }, 181bc93e19dSAlexandru Tachici { 182bc93e19dSAlexandru Tachici .id = ADIN2111_MAC, 183bc93e19dSAlexandru Tachici .name = "adin2111", 184bc93e19dSAlexandru Tachici .phy_ids = {1, 2}, 185bc93e19dSAlexandru Tachici .ports_nr = 2, 186bc93e19dSAlexandru Tachici .phy_id_val = ADIN2111_PHY_ID_VAL, 187bc93e19dSAlexandru Tachici }, 188bc93e19dSAlexandru Tachici }; 189bc93e19dSAlexandru Tachici 190bc93e19dSAlexandru Tachici static u8 adin1110_crc_data(u8 *data, u32 len) 191bc93e19dSAlexandru Tachici { 192bc93e19dSAlexandru Tachici return crc8(adin1110_crc_table, data, len, 0); 193bc93e19dSAlexandru Tachici } 194bc93e19dSAlexandru Tachici 195bc93e19dSAlexandru Tachici static int adin1110_read_reg(struct adin1110_priv *priv, u16 reg, u32 *val) 196bc93e19dSAlexandru Tachici { 197bc93e19dSAlexandru Tachici u32 header_len = ADIN1110_RD_HEADER_LEN; 198bc93e19dSAlexandru Tachici u32 read_len = ADIN1110_REG_LEN; 199a526a3ccSAlexandru Tachici struct spi_transfer t = {0}; 200bc93e19dSAlexandru Tachici int ret; 201bc93e19dSAlexandru Tachici 202bc93e19dSAlexandru Tachici priv->data[0] = ADIN1110_CD | FIELD_GET(GENMASK(12, 8), reg); 203bc93e19dSAlexandru Tachici priv->data[1] = FIELD_GET(GENMASK(7, 0), reg); 204bc93e19dSAlexandru Tachici priv->data[2] = 0x00; 205bc93e19dSAlexandru Tachici 206bc93e19dSAlexandru Tachici if (priv->append_crc) { 207bc93e19dSAlexandru Tachici priv->data[2] = adin1110_crc_data(&priv->data[0], 2); 208bc93e19dSAlexandru Tachici priv->data[3] = 0x00; 209bc93e19dSAlexandru Tachici header_len++; 210bc93e19dSAlexandru Tachici } 211bc93e19dSAlexandru Tachici 212bc93e19dSAlexandru Tachici if (priv->append_crc) 213bc93e19dSAlexandru Tachici read_len++; 214bc93e19dSAlexandru Tachici 215bc93e19dSAlexandru Tachici memset(&priv->data[header_len], 0, read_len); 216a526a3ccSAlexandru Tachici t.tx_buf = &priv->data[0]; 217a526a3ccSAlexandru Tachici t.rx_buf = &priv->data[0]; 218a526a3ccSAlexandru Tachici t.len = read_len + header_len; 219bc93e19dSAlexandru Tachici 220a526a3ccSAlexandru Tachici ret = spi_sync_transfer(priv->spidev, &t, 1); 221bc93e19dSAlexandru Tachici if (ret) 222bc93e19dSAlexandru Tachici return ret; 223bc93e19dSAlexandru Tachici 224bc93e19dSAlexandru Tachici if (priv->append_crc) { 225bc93e19dSAlexandru Tachici u8 recv_crc; 226bc93e19dSAlexandru Tachici u8 crc; 227bc93e19dSAlexandru Tachici 228bc93e19dSAlexandru Tachici crc = adin1110_crc_data(&priv->data[header_len], 229bc93e19dSAlexandru Tachici ADIN1110_REG_LEN); 230bc93e19dSAlexandru Tachici recv_crc = priv->data[header_len + ADIN1110_REG_LEN]; 231bc93e19dSAlexandru Tachici 232bc93e19dSAlexandru Tachici if (crc != recv_crc) { 233bc93e19dSAlexandru Tachici dev_err_ratelimited(&priv->spidev->dev, "CRC error."); 234bc93e19dSAlexandru Tachici return -EBADMSG; 235bc93e19dSAlexandru Tachici } 236bc93e19dSAlexandru Tachici } 237bc93e19dSAlexandru Tachici 238bc93e19dSAlexandru Tachici *val = get_unaligned_be32(&priv->data[header_len]); 239bc93e19dSAlexandru Tachici 240bc93e19dSAlexandru Tachici return ret; 241bc93e19dSAlexandru Tachici } 242bc93e19dSAlexandru Tachici 243bc93e19dSAlexandru Tachici static int adin1110_write_reg(struct adin1110_priv *priv, u16 reg, u32 val) 244bc93e19dSAlexandru Tachici { 245bc93e19dSAlexandru Tachici u32 header_len = ADIN1110_WR_HEADER_LEN; 246bc93e19dSAlexandru Tachici u32 write_len = ADIN1110_REG_LEN; 247bc93e19dSAlexandru Tachici 248bc93e19dSAlexandru Tachici priv->data[0] = ADIN1110_CD | ADIN1110_WRITE | FIELD_GET(GENMASK(12, 8), reg); 249bc93e19dSAlexandru Tachici priv->data[1] = FIELD_GET(GENMASK(7, 0), reg); 250bc93e19dSAlexandru Tachici 251bc93e19dSAlexandru Tachici if (priv->append_crc) { 252bc93e19dSAlexandru Tachici priv->data[2] = adin1110_crc_data(&priv->data[0], header_len); 253bc93e19dSAlexandru Tachici header_len++; 254bc93e19dSAlexandru Tachici } 255bc93e19dSAlexandru Tachici 256bc93e19dSAlexandru Tachici put_unaligned_be32(val, &priv->data[header_len]); 257bc93e19dSAlexandru Tachici if (priv->append_crc) { 258bc93e19dSAlexandru Tachici priv->data[header_len + write_len] = adin1110_crc_data(&priv->data[header_len], 259bc93e19dSAlexandru Tachici write_len); 260bc93e19dSAlexandru Tachici write_len++; 261bc93e19dSAlexandru Tachici } 262bc93e19dSAlexandru Tachici 263bc93e19dSAlexandru Tachici return spi_write(priv->spidev, &priv->data[0], header_len + write_len); 264bc93e19dSAlexandru Tachici } 265bc93e19dSAlexandru Tachici 266bc93e19dSAlexandru Tachici static int adin1110_set_bits(struct adin1110_priv *priv, u16 reg, 267bc93e19dSAlexandru Tachici unsigned long mask, unsigned long val) 268bc93e19dSAlexandru Tachici { 269bc93e19dSAlexandru Tachici u32 write_val; 270bc93e19dSAlexandru Tachici int ret; 271bc93e19dSAlexandru Tachici 272bc93e19dSAlexandru Tachici ret = adin1110_read_reg(priv, reg, &write_val); 273bc93e19dSAlexandru Tachici if (ret < 0) 274bc93e19dSAlexandru Tachici return ret; 275bc93e19dSAlexandru Tachici 276bc93e19dSAlexandru Tachici set_mask_bits(&write_val, mask, val); 277bc93e19dSAlexandru Tachici 278bc93e19dSAlexandru Tachici return adin1110_write_reg(priv, reg, write_val); 279bc93e19dSAlexandru Tachici } 280bc93e19dSAlexandru Tachici 281bc93e19dSAlexandru Tachici static int adin1110_round_len(int len) 282bc93e19dSAlexandru Tachici { 283bc93e19dSAlexandru Tachici /* can read/write only mutiples of 4 bytes of payload */ 284bc93e19dSAlexandru Tachici len = ALIGN(len, 4); 285bc93e19dSAlexandru Tachici 286bc93e19dSAlexandru Tachici /* NOTE: ADIN1110_WR_HEADER_LEN should be used for write ops. */ 287bc93e19dSAlexandru Tachici if (len + ADIN1110_RD_HEADER_LEN > ADIN1110_MAX_BUFF) 288bc93e19dSAlexandru Tachici return -EINVAL; 289bc93e19dSAlexandru Tachici 290bc93e19dSAlexandru Tachici return len; 291bc93e19dSAlexandru Tachici } 292bc93e19dSAlexandru Tachici 293bc93e19dSAlexandru Tachici static int adin1110_read_fifo(struct adin1110_port_priv *port_priv) 294bc93e19dSAlexandru Tachici { 295bc93e19dSAlexandru Tachici struct adin1110_priv *priv = port_priv->priv; 296bc93e19dSAlexandru Tachici u32 header_len = ADIN1110_RD_HEADER_LEN; 297a526a3ccSAlexandru Tachici struct spi_transfer t; 298bc93e19dSAlexandru Tachici u32 frame_size_no_fcs; 299bc93e19dSAlexandru Tachici struct sk_buff *rxb; 300bc93e19dSAlexandru Tachici u32 frame_size; 301bc93e19dSAlexandru Tachici int round_len; 302bc93e19dSAlexandru Tachici u16 reg; 303bc93e19dSAlexandru Tachici int ret; 304bc93e19dSAlexandru Tachici 305bc93e19dSAlexandru Tachici if (!port_priv->nr) { 306bc93e19dSAlexandru Tachici reg = ADIN1110_RX; 307bc93e19dSAlexandru Tachici ret = adin1110_read_reg(priv, ADIN1110_RX_FSIZE, &frame_size); 308bc93e19dSAlexandru Tachici } else { 309bc93e19dSAlexandru Tachici reg = ADIN2111_RX_P2; 310bc93e19dSAlexandru Tachici ret = adin1110_read_reg(priv, ADIN2111_RX_P2_FSIZE, 311bc93e19dSAlexandru Tachici &frame_size); 312bc93e19dSAlexandru Tachici } 313bc93e19dSAlexandru Tachici 314bc93e19dSAlexandru Tachici if (ret < 0) 315bc93e19dSAlexandru Tachici return ret; 316bc93e19dSAlexandru Tachici 317bc93e19dSAlexandru Tachici /* The read frame size includes the extra 2 bytes 318bc93e19dSAlexandru Tachici * from the ADIN1110 frame header. 319bc93e19dSAlexandru Tachici */ 320bc93e19dSAlexandru Tachici if (frame_size < ADIN1110_FRAME_HEADER_LEN + ADIN1110_FEC_LEN) 321bc93e19dSAlexandru Tachici return ret; 322bc93e19dSAlexandru Tachici 323bc93e19dSAlexandru Tachici round_len = adin1110_round_len(frame_size); 324bc93e19dSAlexandru Tachici if (round_len < 0) 325bc93e19dSAlexandru Tachici return ret; 326bc93e19dSAlexandru Tachici 327bc93e19dSAlexandru Tachici frame_size_no_fcs = frame_size - ADIN1110_FRAME_HEADER_LEN - ADIN1110_FEC_LEN; 328a526a3ccSAlexandru Tachici memset(priv->data, 0, ADIN1110_RD_HEADER_LEN); 329bc93e19dSAlexandru Tachici 330bc93e19dSAlexandru Tachici priv->data[0] = ADIN1110_CD | FIELD_GET(GENMASK(12, 8), reg); 331bc93e19dSAlexandru Tachici priv->data[1] = FIELD_GET(GENMASK(7, 0), reg); 332bc93e19dSAlexandru Tachici 333bc93e19dSAlexandru Tachici if (priv->append_crc) { 334bc93e19dSAlexandru Tachici priv->data[2] = adin1110_crc_data(&priv->data[0], 2); 335bc93e19dSAlexandru Tachici header_len++; 336bc93e19dSAlexandru Tachici } 337bc93e19dSAlexandru Tachici 338a526a3ccSAlexandru Tachici rxb = netdev_alloc_skb(port_priv->netdev, round_len + header_len); 339a526a3ccSAlexandru Tachici if (!rxb) 340a526a3ccSAlexandru Tachici return -ENOMEM; 341bc93e19dSAlexandru Tachici 342a526a3ccSAlexandru Tachici skb_put(rxb, frame_size_no_fcs + header_len + ADIN1110_FRAME_HEADER_LEN); 343bc93e19dSAlexandru Tachici 344a526a3ccSAlexandru Tachici t.tx_buf = &priv->data[0]; 345a526a3ccSAlexandru Tachici t.rx_buf = &rxb->data[0]; 346a526a3ccSAlexandru Tachici t.len = header_len + round_len; 347bc93e19dSAlexandru Tachici 348a526a3ccSAlexandru Tachici ret = spi_sync_transfer(priv->spidev, &t, 1); 349bc93e19dSAlexandru Tachici if (ret) { 350bc93e19dSAlexandru Tachici kfree_skb(rxb); 351bc93e19dSAlexandru Tachici return ret; 352bc93e19dSAlexandru Tachici } 353bc93e19dSAlexandru Tachici 354a526a3ccSAlexandru Tachici skb_pull(rxb, header_len + ADIN1110_FRAME_HEADER_LEN); 355bc93e19dSAlexandru Tachici rxb->protocol = eth_type_trans(rxb, port_priv->netdev); 356bc93e19dSAlexandru Tachici 357bc93e19dSAlexandru Tachici if ((port_priv->flags & IFF_ALLMULTI && rxb->pkt_type == PACKET_MULTICAST) || 358bc93e19dSAlexandru Tachici (port_priv->flags & IFF_BROADCAST && rxb->pkt_type == PACKET_BROADCAST)) 359bc93e19dSAlexandru Tachici rxb->offload_fwd_mark = 1; 360bc93e19dSAlexandru Tachici 361bc93e19dSAlexandru Tachici netif_rx(rxb); 362bc93e19dSAlexandru Tachici 363bc93e19dSAlexandru Tachici port_priv->rx_bytes += frame_size - ADIN1110_FRAME_HEADER_LEN; 364bc93e19dSAlexandru Tachici port_priv->rx_packets++; 365bc93e19dSAlexandru Tachici 366bc93e19dSAlexandru Tachici return 0; 367bc93e19dSAlexandru Tachici } 368bc93e19dSAlexandru Tachici 369bc93e19dSAlexandru Tachici static int adin1110_write_fifo(struct adin1110_port_priv *port_priv, 370bc93e19dSAlexandru Tachici struct sk_buff *txb) 371bc93e19dSAlexandru Tachici { 372bc93e19dSAlexandru Tachici struct adin1110_priv *priv = port_priv->priv; 373bc93e19dSAlexandru Tachici u32 header_len = ADIN1110_WR_HEADER_LEN; 374bc93e19dSAlexandru Tachici __be16 frame_header; 375bc93e19dSAlexandru Tachici int padding = 0; 376bc93e19dSAlexandru Tachici int padded_len; 377bc93e19dSAlexandru Tachici int round_len; 378bc93e19dSAlexandru Tachici int ret; 379bc93e19dSAlexandru Tachici 380bc93e19dSAlexandru Tachici /* Pad frame to 64 byte length, 381bc93e19dSAlexandru Tachici * MAC nor PHY will otherwise add the 382bc93e19dSAlexandru Tachici * required padding. 383bc93e19dSAlexandru Tachici * The FEC will be added by the MAC internally. 384bc93e19dSAlexandru Tachici */ 385bc93e19dSAlexandru Tachici if (txb->len + ADIN1110_FEC_LEN < 64) 386bc93e19dSAlexandru Tachici padding = 64 - (txb->len + ADIN1110_FEC_LEN); 387bc93e19dSAlexandru Tachici 388bc93e19dSAlexandru Tachici padded_len = txb->len + padding + ADIN1110_FRAME_HEADER_LEN; 389bc93e19dSAlexandru Tachici 390bc93e19dSAlexandru Tachici round_len = adin1110_round_len(padded_len); 391bc93e19dSAlexandru Tachici if (round_len < 0) 392bc93e19dSAlexandru Tachici return round_len; 393bc93e19dSAlexandru Tachici 394bc93e19dSAlexandru Tachici ret = adin1110_write_reg(priv, ADIN1110_TX_FSIZE, padded_len); 395bc93e19dSAlexandru Tachici if (ret < 0) 396bc93e19dSAlexandru Tachici return ret; 397bc93e19dSAlexandru Tachici 398bc93e19dSAlexandru Tachici memset(priv->data, 0, round_len + ADIN1110_WR_HEADER_LEN); 399bc93e19dSAlexandru Tachici 400bc93e19dSAlexandru Tachici priv->data[0] = ADIN1110_CD | ADIN1110_WRITE; 401bc93e19dSAlexandru Tachici priv->data[0] |= FIELD_GET(GENMASK(12, 8), ADIN1110_TX); 402bc93e19dSAlexandru Tachici priv->data[1] = FIELD_GET(GENMASK(7, 0), ADIN1110_TX); 403bc93e19dSAlexandru Tachici if (priv->append_crc) { 404bc93e19dSAlexandru Tachici priv->data[2] = adin1110_crc_data(&priv->data[0], 2); 405bc93e19dSAlexandru Tachici header_len++; 406bc93e19dSAlexandru Tachici } 407bc93e19dSAlexandru Tachici 408bc93e19dSAlexandru Tachici /* mention the port on which to send the frame in the frame header */ 409bc93e19dSAlexandru Tachici frame_header = cpu_to_be16(port_priv->nr); 410bc93e19dSAlexandru Tachici memcpy(&priv->data[header_len], &frame_header, 411bc93e19dSAlexandru Tachici ADIN1110_FRAME_HEADER_LEN); 412bc93e19dSAlexandru Tachici 413bc93e19dSAlexandru Tachici memcpy(&priv->data[header_len + ADIN1110_FRAME_HEADER_LEN], 414bc93e19dSAlexandru Tachici txb->data, txb->len); 415bc93e19dSAlexandru Tachici 416bc93e19dSAlexandru Tachici ret = spi_write(priv->spidev, &priv->data[0], round_len + header_len); 417bc93e19dSAlexandru Tachici if (ret < 0) 418bc93e19dSAlexandru Tachici return ret; 419bc93e19dSAlexandru Tachici 420bc93e19dSAlexandru Tachici port_priv->tx_bytes += txb->len; 421bc93e19dSAlexandru Tachici port_priv->tx_packets++; 422bc93e19dSAlexandru Tachici 423bc93e19dSAlexandru Tachici return 0; 424bc93e19dSAlexandru Tachici } 425bc93e19dSAlexandru Tachici 426bc93e19dSAlexandru Tachici static int adin1110_read_mdio_acc(struct adin1110_priv *priv) 427bc93e19dSAlexandru Tachici { 428bc93e19dSAlexandru Tachici u32 val; 429bc93e19dSAlexandru Tachici int ret; 430bc93e19dSAlexandru Tachici 431bc93e19dSAlexandru Tachici mutex_lock(&priv->lock); 432bc93e19dSAlexandru Tachici ret = adin1110_read_reg(priv, ADIN1110_MDIOACC, &val); 433bc93e19dSAlexandru Tachici mutex_unlock(&priv->lock); 434bc93e19dSAlexandru Tachici if (ret < 0) 435bc93e19dSAlexandru Tachici return 0; 436bc93e19dSAlexandru Tachici 437bc93e19dSAlexandru Tachici return val; 438bc93e19dSAlexandru Tachici } 439bc93e19dSAlexandru Tachici 440bc93e19dSAlexandru Tachici static int adin1110_mdio_read(struct mii_bus *bus, int phy_id, int reg) 441bc93e19dSAlexandru Tachici { 442bc93e19dSAlexandru Tachici struct adin1110_priv *priv = bus->priv; 443bc93e19dSAlexandru Tachici u32 val = 0; 444bc93e19dSAlexandru Tachici int ret; 445bc93e19dSAlexandru Tachici 446bc93e19dSAlexandru Tachici if (mdio_phy_id_is_c45(phy_id)) 447bc93e19dSAlexandru Tachici return -EOPNOTSUPP; 448bc93e19dSAlexandru Tachici 449bc93e19dSAlexandru Tachici val |= FIELD_PREP(ADIN1110_MDIO_OP, ADIN1110_MDIO_OP_RD); 450bc93e19dSAlexandru Tachici val |= FIELD_PREP(ADIN1110_MDIO_ST, 0x1); 451bc93e19dSAlexandru Tachici val |= FIELD_PREP(ADIN1110_MDIO_PRTAD, phy_id); 452bc93e19dSAlexandru Tachici val |= FIELD_PREP(ADIN1110_MDIO_DEVAD, reg); 453bc93e19dSAlexandru Tachici 454bc93e19dSAlexandru Tachici /* write the clause 22 read command to the chip */ 455bc93e19dSAlexandru Tachici mutex_lock(&priv->lock); 456bc93e19dSAlexandru Tachici ret = adin1110_write_reg(priv, ADIN1110_MDIOACC, val); 457bc93e19dSAlexandru Tachici mutex_unlock(&priv->lock); 458bc93e19dSAlexandru Tachici if (ret < 0) 459bc93e19dSAlexandru Tachici return ret; 460bc93e19dSAlexandru Tachici 461bc93e19dSAlexandru Tachici /* ADIN1110_MDIO_TRDONE BIT of the ADIN1110_MDIOACC 462bc93e19dSAlexandru Tachici * register is set when the read is done. 463bc93e19dSAlexandru Tachici * After the transaction is done, ADIN1110_MDIO_DATA 464bc93e19dSAlexandru Tachici * bitfield of ADIN1110_MDIOACC register will contain 465bc93e19dSAlexandru Tachici * the requested register value. 466bc93e19dSAlexandru Tachici */ 467bc93e19dSAlexandru Tachici ret = readx_poll_timeout(adin1110_read_mdio_acc, priv, val, 468bc93e19dSAlexandru Tachici (val & ADIN1110_MDIO_TRDONE), 10000, 30000); 469bc93e19dSAlexandru Tachici if (ret < 0) 470bc93e19dSAlexandru Tachici return ret; 471bc93e19dSAlexandru Tachici 472bc93e19dSAlexandru Tachici return (val & ADIN1110_MDIO_DATA); 473bc93e19dSAlexandru Tachici } 474bc93e19dSAlexandru Tachici 475bc93e19dSAlexandru Tachici static int adin1110_mdio_write(struct mii_bus *bus, int phy_id, 476bc93e19dSAlexandru Tachici int reg, u16 reg_val) 477bc93e19dSAlexandru Tachici { 478bc93e19dSAlexandru Tachici struct adin1110_priv *priv = bus->priv; 479bc93e19dSAlexandru Tachici u32 val = 0; 480bc93e19dSAlexandru Tachici int ret; 481bc93e19dSAlexandru Tachici 482bc93e19dSAlexandru Tachici if (mdio_phy_id_is_c45(phy_id)) 483bc93e19dSAlexandru Tachici return -EOPNOTSUPP; 484bc93e19dSAlexandru Tachici 485bc93e19dSAlexandru Tachici val |= FIELD_PREP(ADIN1110_MDIO_OP, ADIN1110_MDIO_OP_WR); 486bc93e19dSAlexandru Tachici val |= FIELD_PREP(ADIN1110_MDIO_ST, 0x1); 487bc93e19dSAlexandru Tachici val |= FIELD_PREP(ADIN1110_MDIO_PRTAD, phy_id); 488bc93e19dSAlexandru Tachici val |= FIELD_PREP(ADIN1110_MDIO_DEVAD, reg); 489bc93e19dSAlexandru Tachici val |= FIELD_PREP(ADIN1110_MDIO_DATA, reg_val); 490bc93e19dSAlexandru Tachici 491bc93e19dSAlexandru Tachici /* write the clause 22 write command to the chip */ 492bc93e19dSAlexandru Tachici mutex_lock(&priv->lock); 493bc93e19dSAlexandru Tachici ret = adin1110_write_reg(priv, ADIN1110_MDIOACC, val); 494bc93e19dSAlexandru Tachici mutex_unlock(&priv->lock); 495bc93e19dSAlexandru Tachici if (ret < 0) 496bc93e19dSAlexandru Tachici return ret; 497bc93e19dSAlexandru Tachici 498bc93e19dSAlexandru Tachici return readx_poll_timeout(adin1110_read_mdio_acc, priv, val, 499bc93e19dSAlexandru Tachici (val & ADIN1110_MDIO_TRDONE), 10000, 30000); 500bc93e19dSAlexandru Tachici } 501bc93e19dSAlexandru Tachici 502bc93e19dSAlexandru Tachici /* ADIN1110 MAC-PHY contains an ADIN1100 PHY. 503bc93e19dSAlexandru Tachici * ADIN2111 MAC-PHY contains two ADIN1100 PHYs. 504bc93e19dSAlexandru Tachici * By registering a new MDIO bus we allow the PAL to discover 505bc93e19dSAlexandru Tachici * the encapsulated PHY and probe the ADIN1100 driver. 506bc93e19dSAlexandru Tachici */ 507bc93e19dSAlexandru Tachici static int adin1110_register_mdiobus(struct adin1110_priv *priv, 508bc93e19dSAlexandru Tachici struct device *dev) 509bc93e19dSAlexandru Tachici { 510bc93e19dSAlexandru Tachici struct mii_bus *mii_bus; 511bc93e19dSAlexandru Tachici int ret; 512bc93e19dSAlexandru Tachici 513bc93e19dSAlexandru Tachici mii_bus = devm_mdiobus_alloc(dev); 514bc93e19dSAlexandru Tachici if (!mii_bus) 515bc93e19dSAlexandru Tachici return -ENOMEM; 516bc93e19dSAlexandru Tachici 517bc93e19dSAlexandru Tachici snprintf(priv->mii_bus_name, MII_BUS_ID_SIZE, "%s-%u", 518bc93e19dSAlexandru Tachici priv->cfg->name, priv->spidev->chip_select); 519bc93e19dSAlexandru Tachici 520bc93e19dSAlexandru Tachici mii_bus->name = priv->mii_bus_name; 521bc93e19dSAlexandru Tachici mii_bus->read = adin1110_mdio_read; 522bc93e19dSAlexandru Tachici mii_bus->write = adin1110_mdio_write; 523bc93e19dSAlexandru Tachici mii_bus->priv = priv; 524bc93e19dSAlexandru Tachici mii_bus->parent = dev; 525bc93e19dSAlexandru Tachici mii_bus->phy_mask = ~((u32)GENMASK(2, 0)); 526bc93e19dSAlexandru Tachici mii_bus->probe_capabilities = MDIOBUS_C22; 527bc93e19dSAlexandru Tachici snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev)); 528bc93e19dSAlexandru Tachici 529bc93e19dSAlexandru Tachici ret = devm_mdiobus_register(dev, mii_bus); 530bc93e19dSAlexandru Tachici if (ret) 531bc93e19dSAlexandru Tachici return ret; 532bc93e19dSAlexandru Tachici 533bc93e19dSAlexandru Tachici priv->mii_bus = mii_bus; 534bc93e19dSAlexandru Tachici 535bc93e19dSAlexandru Tachici return 0; 536bc93e19dSAlexandru Tachici } 537bc93e19dSAlexandru Tachici 538bc93e19dSAlexandru Tachici static bool adin1110_port_rx_ready(struct adin1110_port_priv *port_priv, 539bc93e19dSAlexandru Tachici u32 status) 540bc93e19dSAlexandru Tachici { 541bc93e19dSAlexandru Tachici if (!netif_oper_up(port_priv->netdev)) 542bc93e19dSAlexandru Tachici return false; 543bc93e19dSAlexandru Tachici 544bc93e19dSAlexandru Tachici if (!port_priv->nr) 545bc93e19dSAlexandru Tachici return !!(status & ADIN1110_RX_RDY); 546bc93e19dSAlexandru Tachici else 547bc93e19dSAlexandru Tachici return !!(status & ADIN2111_P2_RX_RDY); 548bc93e19dSAlexandru Tachici } 549bc93e19dSAlexandru Tachici 550bc93e19dSAlexandru Tachici static void adin1110_read_frames(struct adin1110_port_priv *port_priv, 551bc93e19dSAlexandru Tachici unsigned int budget) 552bc93e19dSAlexandru Tachici { 553bc93e19dSAlexandru Tachici struct adin1110_priv *priv = port_priv->priv; 554bc93e19dSAlexandru Tachici u32 status1; 555bc93e19dSAlexandru Tachici int ret; 556bc93e19dSAlexandru Tachici 557bc93e19dSAlexandru Tachici while (budget) { 558bc93e19dSAlexandru Tachici ret = adin1110_read_reg(priv, ADIN1110_STATUS1, &status1); 559bc93e19dSAlexandru Tachici if (ret < 0) 560bc93e19dSAlexandru Tachici return; 561bc93e19dSAlexandru Tachici 562bc93e19dSAlexandru Tachici if (!adin1110_port_rx_ready(port_priv, status1)) 563bc93e19dSAlexandru Tachici break; 564bc93e19dSAlexandru Tachici 565bc93e19dSAlexandru Tachici ret = adin1110_read_fifo(port_priv); 566bc93e19dSAlexandru Tachici if (ret < 0) 567bc93e19dSAlexandru Tachici return; 568bc93e19dSAlexandru Tachici 569bc93e19dSAlexandru Tachici budget--; 570bc93e19dSAlexandru Tachici } 571bc93e19dSAlexandru Tachici } 572bc93e19dSAlexandru Tachici 573bc93e19dSAlexandru Tachici static void adin1110_wake_queues(struct adin1110_priv *priv) 574bc93e19dSAlexandru Tachici { 575bc93e19dSAlexandru Tachici int i; 576bc93e19dSAlexandru Tachici 577bc93e19dSAlexandru Tachici for (i = 0; i < priv->cfg->ports_nr; i++) 578bc93e19dSAlexandru Tachici netif_wake_queue(priv->ports[i]->netdev); 579bc93e19dSAlexandru Tachici } 580bc93e19dSAlexandru Tachici 581bc93e19dSAlexandru Tachici static irqreturn_t adin1110_irq(int irq, void *p) 582bc93e19dSAlexandru Tachici { 583bc93e19dSAlexandru Tachici struct adin1110_priv *priv = p; 584bc93e19dSAlexandru Tachici u32 status1; 585bc93e19dSAlexandru Tachici u32 val; 586bc93e19dSAlexandru Tachici int ret; 587bc93e19dSAlexandru Tachici int i; 588bc93e19dSAlexandru Tachici 589bc93e19dSAlexandru Tachici mutex_lock(&priv->lock); 590bc93e19dSAlexandru Tachici 591bc93e19dSAlexandru Tachici ret = adin1110_read_reg(priv, ADIN1110_STATUS1, &status1); 592bc93e19dSAlexandru Tachici if (ret < 0) 593bc93e19dSAlexandru Tachici goto out; 594bc93e19dSAlexandru Tachici 595bc93e19dSAlexandru Tachici if (priv->append_crc && (status1 & ADIN1110_SPI_ERR)) 596bc93e19dSAlexandru Tachici dev_warn_ratelimited(&priv->spidev->dev, 597bc93e19dSAlexandru Tachici "SPI CRC error on write.\n"); 598bc93e19dSAlexandru Tachici 599bc93e19dSAlexandru Tachici ret = adin1110_read_reg(priv, ADIN1110_TX_SPACE, &val); 600bc93e19dSAlexandru Tachici if (ret < 0) 601bc93e19dSAlexandru Tachici goto out; 602bc93e19dSAlexandru Tachici 603bc93e19dSAlexandru Tachici /* TX FIFO space is expressed in half-words */ 604bc93e19dSAlexandru Tachici priv->tx_space = 2 * val; 605bc93e19dSAlexandru Tachici 606bc93e19dSAlexandru Tachici for (i = 0; i < priv->cfg->ports_nr; i++) { 607bc93e19dSAlexandru Tachici if (adin1110_port_rx_ready(priv->ports[i], status1)) 608bc93e19dSAlexandru Tachici adin1110_read_frames(priv->ports[i], 609bc93e19dSAlexandru Tachici ADIN1110_MAX_FRAMES_READ); 610bc93e19dSAlexandru Tachici } 611bc93e19dSAlexandru Tachici 612bc93e19dSAlexandru Tachici /* clear IRQ sources */ 613bc93e19dSAlexandru Tachici adin1110_write_reg(priv, ADIN1110_STATUS0, ADIN1110_CLEAR_STATUS0); 614bc93e19dSAlexandru Tachici adin1110_write_reg(priv, ADIN1110_STATUS1, priv->irq_mask); 615bc93e19dSAlexandru Tachici 616bc93e19dSAlexandru Tachici out: 617bc93e19dSAlexandru Tachici mutex_unlock(&priv->lock); 618bc93e19dSAlexandru Tachici 619bc93e19dSAlexandru Tachici if (priv->tx_space > 0 && ret >= 0) 620bc93e19dSAlexandru Tachici adin1110_wake_queues(priv); 621bc93e19dSAlexandru Tachici 622bc93e19dSAlexandru Tachici return IRQ_HANDLED; 623bc93e19dSAlexandru Tachici } 624bc93e19dSAlexandru Tachici 625bc93e19dSAlexandru Tachici /* ADIN1110 can filter up to 16 MAC addresses, mac_nr here is the slot used */ 626bc93e19dSAlexandru Tachici static int adin1110_write_mac_address(struct adin1110_port_priv *port_priv, 627bc93e19dSAlexandru Tachici int mac_nr, const u8 *addr, 628bc93e19dSAlexandru Tachici u8 *mask, u32 port_rules) 629bc93e19dSAlexandru Tachici { 630bc93e19dSAlexandru Tachici struct adin1110_priv *priv = port_priv->priv; 631bc93e19dSAlexandru Tachici u32 offset = mac_nr * 2; 632bc93e19dSAlexandru Tachici u32 port_rules_mask; 633bc93e19dSAlexandru Tachici int ret; 634bc93e19dSAlexandru Tachici u32 val; 635bc93e19dSAlexandru Tachici 636bc93e19dSAlexandru Tachici if (!port_priv->nr) 637bc93e19dSAlexandru Tachici port_rules_mask = ADIN1110_MAC_ADDR_APPLY2PORT; 638bc93e19dSAlexandru Tachici else 639bc93e19dSAlexandru Tachici port_rules_mask = ADIN2111_MAC_ADDR_APPLY2PORT2; 640bc93e19dSAlexandru Tachici 641bc93e19dSAlexandru Tachici if (port_rules & port_rules_mask) 642bc93e19dSAlexandru Tachici port_rules_mask |= ADIN1110_MAC_ADDR_TO_HOST | ADIN2111_MAC_ADDR_TO_OTHER_PORT; 643bc93e19dSAlexandru Tachici 644bc93e19dSAlexandru Tachici port_rules_mask |= GENMASK(15, 0); 645bc93e19dSAlexandru Tachici val = port_rules | get_unaligned_be16(&addr[0]); 646bc93e19dSAlexandru Tachici ret = adin1110_set_bits(priv, ADIN1110_MAC_ADDR_FILTER_UPR + offset, 647bc93e19dSAlexandru Tachici port_rules_mask, val); 648bc93e19dSAlexandru Tachici if (ret < 0) 649bc93e19dSAlexandru Tachici return ret; 650bc93e19dSAlexandru Tachici 651bc93e19dSAlexandru Tachici val = get_unaligned_be32(&addr[2]); 652bc93e19dSAlexandru Tachici ret = adin1110_write_reg(priv, 653bc93e19dSAlexandru Tachici ADIN1110_MAC_ADDR_FILTER_LWR + offset, val); 654bc93e19dSAlexandru Tachici if (ret < 0) 655bc93e19dSAlexandru Tachici return ret; 656bc93e19dSAlexandru Tachici 657bc93e19dSAlexandru Tachici /* Only the first two MAC address slots support masking. */ 658bc93e19dSAlexandru Tachici if (mac_nr < ADIN_MAC_P1_ADDR_SLOT) { 659bc93e19dSAlexandru Tachici val = get_unaligned_be16(&mask[0]); 660bc93e19dSAlexandru Tachici ret = adin1110_write_reg(priv, 661bc93e19dSAlexandru Tachici ADIN1110_MAC_ADDR_MASK_UPR + offset, 662bc93e19dSAlexandru Tachici val); 663bc93e19dSAlexandru Tachici if (ret < 0) 664bc93e19dSAlexandru Tachici return ret; 665bc93e19dSAlexandru Tachici 666bc93e19dSAlexandru Tachici val = get_unaligned_be32(&mask[2]); 667bc93e19dSAlexandru Tachici return adin1110_write_reg(priv, 668bc93e19dSAlexandru Tachici ADIN1110_MAC_ADDR_MASK_LWR + offset, 669bc93e19dSAlexandru Tachici val); 670bc93e19dSAlexandru Tachici } 671bc93e19dSAlexandru Tachici 672bc93e19dSAlexandru Tachici return 0; 673bc93e19dSAlexandru Tachici } 674bc93e19dSAlexandru Tachici 675bc93e19dSAlexandru Tachici static int adin1110_clear_mac_address(struct adin1110_priv *priv, int mac_nr) 676bc93e19dSAlexandru Tachici { 677bc93e19dSAlexandru Tachici u32 offset = mac_nr * 2; 678bc93e19dSAlexandru Tachici int ret; 679bc93e19dSAlexandru Tachici 680bc93e19dSAlexandru Tachici ret = adin1110_write_reg(priv, ADIN1110_MAC_ADDR_FILTER_UPR + offset, 0); 681bc93e19dSAlexandru Tachici if (ret < 0) 682bc93e19dSAlexandru Tachici return ret; 683bc93e19dSAlexandru Tachici 684bc93e19dSAlexandru Tachici ret = adin1110_write_reg(priv, ADIN1110_MAC_ADDR_FILTER_LWR + offset, 0); 685bc93e19dSAlexandru Tachici if (ret < 0) 686bc93e19dSAlexandru Tachici return ret; 687bc93e19dSAlexandru Tachici 688bc93e19dSAlexandru Tachici /* only the first two MAC address slots are maskable */ 689bc93e19dSAlexandru Tachici if (mac_nr <= 1) { 690bc93e19dSAlexandru Tachici ret = adin1110_write_reg(priv, ADIN1110_MAC_ADDR_MASK_UPR + offset, 0); 691bc93e19dSAlexandru Tachici if (ret < 0) 692bc93e19dSAlexandru Tachici return ret; 693bc93e19dSAlexandru Tachici 694bc93e19dSAlexandru Tachici ret = adin1110_write_reg(priv, ADIN1110_MAC_ADDR_MASK_LWR + offset, 0); 695bc93e19dSAlexandru Tachici } 696bc93e19dSAlexandru Tachici 697bc93e19dSAlexandru Tachici return ret; 698bc93e19dSAlexandru Tachici } 699bc93e19dSAlexandru Tachici 700bc93e19dSAlexandru Tachici static u32 adin1110_port_rules(struct adin1110_port_priv *port_priv, 701bc93e19dSAlexandru Tachici bool fw_to_host, 702bc93e19dSAlexandru Tachici bool fw_to_other_port) 703bc93e19dSAlexandru Tachici { 704bc93e19dSAlexandru Tachici u32 port_rules = 0; 705bc93e19dSAlexandru Tachici 706bc93e19dSAlexandru Tachici if (!port_priv->nr) 707bc93e19dSAlexandru Tachici port_rules |= ADIN1110_MAC_ADDR_APPLY2PORT; 708bc93e19dSAlexandru Tachici else 709bc93e19dSAlexandru Tachici port_rules |= ADIN2111_MAC_ADDR_APPLY2PORT2; 710bc93e19dSAlexandru Tachici 711bc93e19dSAlexandru Tachici if (fw_to_host) 712bc93e19dSAlexandru Tachici port_rules |= ADIN1110_MAC_ADDR_TO_HOST; 713bc93e19dSAlexandru Tachici 714bc93e19dSAlexandru Tachici if (fw_to_other_port && port_priv->priv->forwarding) 715bc93e19dSAlexandru Tachici port_rules |= ADIN2111_MAC_ADDR_TO_OTHER_PORT; 716bc93e19dSAlexandru Tachici 717bc93e19dSAlexandru Tachici return port_rules; 718bc93e19dSAlexandru Tachici } 719bc93e19dSAlexandru Tachici 720bc93e19dSAlexandru Tachici static int adin1110_multicast_filter(struct adin1110_port_priv *port_priv, 721bc93e19dSAlexandru Tachici int mac_nr, bool accept_multicast) 722bc93e19dSAlexandru Tachici { 723bc93e19dSAlexandru Tachici u8 mask[ETH_ALEN] = {0}; 724bc93e19dSAlexandru Tachici u8 mac[ETH_ALEN] = {0}; 725bc93e19dSAlexandru Tachici u32 port_rules = 0; 726bc93e19dSAlexandru Tachici 727bc93e19dSAlexandru Tachici mask[0] = BIT(0); 728bc93e19dSAlexandru Tachici mac[0] = BIT(0); 729bc93e19dSAlexandru Tachici 730bc93e19dSAlexandru Tachici if (accept_multicast && port_priv->state == BR_STATE_FORWARDING) 731bc93e19dSAlexandru Tachici port_rules = adin1110_port_rules(port_priv, true, true); 732bc93e19dSAlexandru Tachici 733bc93e19dSAlexandru Tachici return adin1110_write_mac_address(port_priv, mac_nr, mac, 734bc93e19dSAlexandru Tachici mask, port_rules); 735bc93e19dSAlexandru Tachici } 736bc93e19dSAlexandru Tachici 737bc93e19dSAlexandru Tachici static int adin1110_broadcasts_filter(struct adin1110_port_priv *port_priv, 738bc93e19dSAlexandru Tachici int mac_nr, bool accept_broadcast) 739bc93e19dSAlexandru Tachici { 740bc93e19dSAlexandru Tachici u32 port_rules = 0; 741bc93e19dSAlexandru Tachici u8 mask[ETH_ALEN]; 742bc93e19dSAlexandru Tachici 743bc93e19dSAlexandru Tachici memset(mask, 0xFF, ETH_ALEN); 744bc93e19dSAlexandru Tachici 745bc93e19dSAlexandru Tachici if (accept_broadcast && port_priv->state == BR_STATE_FORWARDING) 746bc93e19dSAlexandru Tachici port_rules = adin1110_port_rules(port_priv, true, true); 747bc93e19dSAlexandru Tachici 748bc93e19dSAlexandru Tachici return adin1110_write_mac_address(port_priv, mac_nr, mask, 749bc93e19dSAlexandru Tachici mask, port_rules); 750bc93e19dSAlexandru Tachici } 751bc93e19dSAlexandru Tachici 752bc93e19dSAlexandru Tachici static int adin1110_set_mac_address(struct net_device *netdev, 753bc93e19dSAlexandru Tachici const unsigned char *dev_addr) 754bc93e19dSAlexandru Tachici { 755bc93e19dSAlexandru Tachici struct adin1110_port_priv *port_priv = netdev_priv(netdev); 756bc93e19dSAlexandru Tachici u8 mask[ETH_ALEN]; 757bc93e19dSAlexandru Tachici u32 port_rules; 758bc93e19dSAlexandru Tachici u32 mac_slot; 759bc93e19dSAlexandru Tachici 760bc93e19dSAlexandru Tachici if (!is_valid_ether_addr(dev_addr)) 761bc93e19dSAlexandru Tachici return -EADDRNOTAVAIL; 762bc93e19dSAlexandru Tachici 763bc93e19dSAlexandru Tachici eth_hw_addr_set(netdev, dev_addr); 764bc93e19dSAlexandru Tachici memset(mask, 0xFF, ETH_ALEN); 765bc93e19dSAlexandru Tachici 766bc93e19dSAlexandru Tachici mac_slot = (!port_priv->nr) ? ADIN_MAC_P1_ADDR_SLOT : ADIN_MAC_P2_ADDR_SLOT; 767bc93e19dSAlexandru Tachici port_rules = adin1110_port_rules(port_priv, true, false); 768bc93e19dSAlexandru Tachici 769bc93e19dSAlexandru Tachici return adin1110_write_mac_address(port_priv, mac_slot, netdev->dev_addr, 770bc93e19dSAlexandru Tachici mask, port_rules); 771bc93e19dSAlexandru Tachici } 772bc93e19dSAlexandru Tachici 773bc93e19dSAlexandru Tachici static int adin1110_ndo_set_mac_address(struct net_device *netdev, void *addr) 774bc93e19dSAlexandru Tachici { 775bc93e19dSAlexandru Tachici struct sockaddr *sa = addr; 776bc93e19dSAlexandru Tachici int ret; 777bc93e19dSAlexandru Tachici 778bc93e19dSAlexandru Tachici ret = eth_prepare_mac_addr_change(netdev, addr); 779bc93e19dSAlexandru Tachici if (ret < 0) 780bc93e19dSAlexandru Tachici return ret; 781bc93e19dSAlexandru Tachici 782bc93e19dSAlexandru Tachici return adin1110_set_mac_address(netdev, sa->sa_data); 783bc93e19dSAlexandru Tachici } 784bc93e19dSAlexandru Tachici 785bc93e19dSAlexandru Tachici static int adin1110_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) 786bc93e19dSAlexandru Tachici { 787bc93e19dSAlexandru Tachici if (!netif_running(netdev)) 788bc93e19dSAlexandru Tachici return -EINVAL; 789bc93e19dSAlexandru Tachici 790bc93e19dSAlexandru Tachici return phy_do_ioctl(netdev, rq, cmd); 791bc93e19dSAlexandru Tachici } 792bc93e19dSAlexandru Tachici 793bc93e19dSAlexandru Tachici static int adin1110_set_promisc_mode(struct adin1110_port_priv *port_priv, 794bc93e19dSAlexandru Tachici bool promisc) 795bc93e19dSAlexandru Tachici { 796bc93e19dSAlexandru Tachici struct adin1110_priv *priv = port_priv->priv; 797bc93e19dSAlexandru Tachici u32 mask; 798bc93e19dSAlexandru Tachici 799bc93e19dSAlexandru Tachici if (port_priv->state != BR_STATE_FORWARDING) 800bc93e19dSAlexandru Tachici promisc = false; 801bc93e19dSAlexandru Tachici 802bc93e19dSAlexandru Tachici if (!port_priv->nr) 803bc93e19dSAlexandru Tachici mask = ADIN1110_FWD_UNK2HOST; 804bc93e19dSAlexandru Tachici else 805bc93e19dSAlexandru Tachici mask = ADIN2111_P2_FWD_UNK2HOST; 806bc93e19dSAlexandru Tachici 807bc93e19dSAlexandru Tachici return adin1110_set_bits(priv, ADIN1110_CONFIG2, 808bc93e19dSAlexandru Tachici mask, promisc ? mask : 0); 809bc93e19dSAlexandru Tachici } 810bc93e19dSAlexandru Tachici 811bc93e19dSAlexandru Tachici static int adin1110_setup_rx_mode(struct adin1110_port_priv *port_priv) 812bc93e19dSAlexandru Tachici { 813bc93e19dSAlexandru Tachici int ret; 814bc93e19dSAlexandru Tachici 815bc93e19dSAlexandru Tachici ret = adin1110_set_promisc_mode(port_priv, 816bc93e19dSAlexandru Tachici !!(port_priv->flags & IFF_PROMISC)); 817bc93e19dSAlexandru Tachici if (ret < 0) 818bc93e19dSAlexandru Tachici return ret; 819bc93e19dSAlexandru Tachici 820bc93e19dSAlexandru Tachici ret = adin1110_multicast_filter(port_priv, ADIN_MAC_MULTICAST_ADDR_SLOT, 821bc93e19dSAlexandru Tachici !!(port_priv->flags & IFF_ALLMULTI)); 822bc93e19dSAlexandru Tachici if (ret < 0) 823bc93e19dSAlexandru Tachici return ret; 824bc93e19dSAlexandru Tachici 825bc93e19dSAlexandru Tachici ret = adin1110_broadcasts_filter(port_priv, 826bc93e19dSAlexandru Tachici ADIN_MAC_BROADCAST_ADDR_SLOT, 827bc93e19dSAlexandru Tachici !!(port_priv->flags & IFF_BROADCAST)); 828bc93e19dSAlexandru Tachici if (ret < 0) 829bc93e19dSAlexandru Tachici return ret; 830bc93e19dSAlexandru Tachici 831bc93e19dSAlexandru Tachici return adin1110_set_bits(port_priv->priv, ADIN1110_CONFIG1, 832bc93e19dSAlexandru Tachici ADIN1110_CONFIG1_SYNC, ADIN1110_CONFIG1_SYNC); 833bc93e19dSAlexandru Tachici } 834bc93e19dSAlexandru Tachici 835bc93e19dSAlexandru Tachici static bool adin1110_can_offload_forwarding(struct adin1110_priv *priv) 836bc93e19dSAlexandru Tachici { 837bc93e19dSAlexandru Tachici int i; 838bc93e19dSAlexandru Tachici 839bc93e19dSAlexandru Tachici if (priv->cfg->id != ADIN2111_MAC) 840bc93e19dSAlexandru Tachici return false; 841bc93e19dSAlexandru Tachici 842bc93e19dSAlexandru Tachici /* Can't enable forwarding if ports do not belong to the same bridge */ 843bc93e19dSAlexandru Tachici if (priv->ports[0]->bridge != priv->ports[1]->bridge || !priv->ports[0]->bridge) 844bc93e19dSAlexandru Tachici return false; 845bc93e19dSAlexandru Tachici 846bc93e19dSAlexandru Tachici /* Can't enable forwarding if there is a port 847bc93e19dSAlexandru Tachici * that has been blocked by STP. 848bc93e19dSAlexandru Tachici */ 849bc93e19dSAlexandru Tachici for (i = 0; i < priv->cfg->ports_nr; i++) { 850bc93e19dSAlexandru Tachici if (priv->ports[i]->state != BR_STATE_FORWARDING) 851bc93e19dSAlexandru Tachici return false; 852bc93e19dSAlexandru Tachici } 853bc93e19dSAlexandru Tachici 854bc93e19dSAlexandru Tachici return true; 855bc93e19dSAlexandru Tachici } 856bc93e19dSAlexandru Tachici 857bc93e19dSAlexandru Tachici static void adin1110_rx_mode_work(struct work_struct *work) 858bc93e19dSAlexandru Tachici { 859bc93e19dSAlexandru Tachici struct adin1110_port_priv *port_priv; 860bc93e19dSAlexandru Tachici struct adin1110_priv *priv; 861bc93e19dSAlexandru Tachici 862bc93e19dSAlexandru Tachici port_priv = container_of(work, struct adin1110_port_priv, rx_mode_work); 863bc93e19dSAlexandru Tachici priv = port_priv->priv; 864bc93e19dSAlexandru Tachici 865bc93e19dSAlexandru Tachici mutex_lock(&priv->lock); 866bc93e19dSAlexandru Tachici adin1110_setup_rx_mode(port_priv); 867bc93e19dSAlexandru Tachici mutex_unlock(&priv->lock); 868bc93e19dSAlexandru Tachici } 869bc93e19dSAlexandru Tachici 870bc93e19dSAlexandru Tachici static void adin1110_set_rx_mode(struct net_device *dev) 871bc93e19dSAlexandru Tachici { 872bc93e19dSAlexandru Tachici struct adin1110_port_priv *port_priv = netdev_priv(dev); 873bc93e19dSAlexandru Tachici struct adin1110_priv *priv = port_priv->priv; 874bc93e19dSAlexandru Tachici 875bc93e19dSAlexandru Tachici spin_lock(&priv->state_lock); 876bc93e19dSAlexandru Tachici 877bc93e19dSAlexandru Tachici port_priv->flags = dev->flags; 878bc93e19dSAlexandru Tachici schedule_work(&port_priv->rx_mode_work); 879bc93e19dSAlexandru Tachici 880bc93e19dSAlexandru Tachici spin_unlock(&priv->state_lock); 881bc93e19dSAlexandru Tachici } 882bc93e19dSAlexandru Tachici 883bc93e19dSAlexandru Tachici static int adin1110_net_open(struct net_device *net_dev) 884bc93e19dSAlexandru Tachici { 885bc93e19dSAlexandru Tachici struct adin1110_port_priv *port_priv = netdev_priv(net_dev); 886bc93e19dSAlexandru Tachici struct adin1110_priv *priv = port_priv->priv; 887bc93e19dSAlexandru Tachici u32 val; 888bc93e19dSAlexandru Tachici int ret; 889bc93e19dSAlexandru Tachici 890bc93e19dSAlexandru Tachici mutex_lock(&priv->lock); 891bc93e19dSAlexandru Tachici 892bc93e19dSAlexandru Tachici /* Configure MAC to compute and append the FCS itself. */ 893bc93e19dSAlexandru Tachici ret = adin1110_write_reg(priv, ADIN1110_CONFIG2, ADIN1110_CRC_APPEND); 894bc93e19dSAlexandru Tachici if (ret < 0) 895bc93e19dSAlexandru Tachici goto out; 896bc93e19dSAlexandru Tachici 897bc93e19dSAlexandru Tachici val = ADIN1110_TX_RDY_IRQ | ADIN1110_RX_RDY_IRQ | ADIN1110_SPI_ERR_IRQ; 898bc93e19dSAlexandru Tachici if (priv->cfg->id == ADIN2111_MAC) 899bc93e19dSAlexandru Tachici val |= ADIN2111_RX_RDY_IRQ; 900bc93e19dSAlexandru Tachici 901bc93e19dSAlexandru Tachici priv->irq_mask = val; 902bc93e19dSAlexandru Tachici ret = adin1110_write_reg(priv, ADIN1110_IMASK1, ~val); 903bc93e19dSAlexandru Tachici if (ret < 0) { 904bc93e19dSAlexandru Tachici netdev_err(net_dev, "Failed to enable chip IRQs: %d\n", ret); 905bc93e19dSAlexandru Tachici goto out; 906bc93e19dSAlexandru Tachici } 907bc93e19dSAlexandru Tachici 908bc93e19dSAlexandru Tachici ret = adin1110_read_reg(priv, ADIN1110_TX_SPACE, &val); 909bc93e19dSAlexandru Tachici if (ret < 0) { 910bc93e19dSAlexandru Tachici netdev_err(net_dev, "Failed to read TX FIFO space: %d\n", ret); 911bc93e19dSAlexandru Tachici goto out; 912bc93e19dSAlexandru Tachici } 913bc93e19dSAlexandru Tachici 914bc93e19dSAlexandru Tachici priv->tx_space = 2 * val; 915bc93e19dSAlexandru Tachici 916bc93e19dSAlexandru Tachici port_priv->state = BR_STATE_FORWARDING; 917bc93e19dSAlexandru Tachici ret = adin1110_set_mac_address(net_dev, net_dev->dev_addr); 918bc93e19dSAlexandru Tachici if (ret < 0) { 919bc93e19dSAlexandru Tachici netdev_err(net_dev, "Could not set MAC address: %pM, %d\n", 920bc93e19dSAlexandru Tachici net_dev->dev_addr, ret); 921bc93e19dSAlexandru Tachici goto out; 922bc93e19dSAlexandru Tachici } 923bc93e19dSAlexandru Tachici 924bc93e19dSAlexandru Tachici ret = adin1110_set_bits(priv, ADIN1110_CONFIG1, ADIN1110_CONFIG1_SYNC, 925bc93e19dSAlexandru Tachici ADIN1110_CONFIG1_SYNC); 926bc93e19dSAlexandru Tachici 927bc93e19dSAlexandru Tachici out: 928bc93e19dSAlexandru Tachici mutex_unlock(&priv->lock); 929bc93e19dSAlexandru Tachici 930bc93e19dSAlexandru Tachici if (ret < 0) 931bc93e19dSAlexandru Tachici return ret; 932bc93e19dSAlexandru Tachici 933bc93e19dSAlexandru Tachici phy_start(port_priv->phydev); 934bc93e19dSAlexandru Tachici 935bc93e19dSAlexandru Tachici netif_start_queue(net_dev); 936bc93e19dSAlexandru Tachici 937bc93e19dSAlexandru Tachici return 0; 938bc93e19dSAlexandru Tachici } 939bc93e19dSAlexandru Tachici 940bc93e19dSAlexandru Tachici static int adin1110_net_stop(struct net_device *net_dev) 941bc93e19dSAlexandru Tachici { 942bc93e19dSAlexandru Tachici struct adin1110_port_priv *port_priv = netdev_priv(net_dev); 943bc93e19dSAlexandru Tachici struct adin1110_priv *priv = port_priv->priv; 944bc93e19dSAlexandru Tachici u32 mask; 945bc93e19dSAlexandru Tachici int ret; 946bc93e19dSAlexandru Tachici 947bc93e19dSAlexandru Tachici mask = !port_priv->nr ? ADIN2111_RX_RDY_IRQ : ADIN1110_RX_RDY_IRQ; 948bc93e19dSAlexandru Tachici 949bc93e19dSAlexandru Tachici /* Disable RX RDY IRQs */ 950bc93e19dSAlexandru Tachici mutex_lock(&priv->lock); 951bc93e19dSAlexandru Tachici ret = adin1110_set_bits(priv, ADIN1110_IMASK1, mask, mask); 952bc93e19dSAlexandru Tachici mutex_unlock(&priv->lock); 953bc93e19dSAlexandru Tachici if (ret < 0) 954bc93e19dSAlexandru Tachici return ret; 955bc93e19dSAlexandru Tachici 956bc93e19dSAlexandru Tachici netif_stop_queue(port_priv->netdev); 957bc93e19dSAlexandru Tachici flush_work(&port_priv->tx_work); 958bc93e19dSAlexandru Tachici phy_stop(port_priv->phydev); 959bc93e19dSAlexandru Tachici 960bc93e19dSAlexandru Tachici return 0; 961bc93e19dSAlexandru Tachici } 962bc93e19dSAlexandru Tachici 963bc93e19dSAlexandru Tachici static void adin1110_tx_work(struct work_struct *work) 964bc93e19dSAlexandru Tachici { 965bc93e19dSAlexandru Tachici struct adin1110_port_priv *port_priv; 966bc93e19dSAlexandru Tachici struct adin1110_priv *priv; 967bc93e19dSAlexandru Tachici struct sk_buff *txb; 968bc93e19dSAlexandru Tachici int ret; 969bc93e19dSAlexandru Tachici 970bc93e19dSAlexandru Tachici port_priv = container_of(work, struct adin1110_port_priv, tx_work); 971bc93e19dSAlexandru Tachici priv = port_priv->priv; 972bc93e19dSAlexandru Tachici 973bc93e19dSAlexandru Tachici mutex_lock(&priv->lock); 974bc93e19dSAlexandru Tachici 975bc93e19dSAlexandru Tachici while ((txb = skb_dequeue(&port_priv->txq))) { 976bc93e19dSAlexandru Tachici ret = adin1110_write_fifo(port_priv, txb); 977bc93e19dSAlexandru Tachici if (ret < 0) 978bc93e19dSAlexandru Tachici dev_err_ratelimited(&priv->spidev->dev, 979bc93e19dSAlexandru Tachici "Frame write error: %d\n", ret); 980bc93e19dSAlexandru Tachici 981bc93e19dSAlexandru Tachici dev_kfree_skb(txb); 982bc93e19dSAlexandru Tachici } 983bc93e19dSAlexandru Tachici 984bc93e19dSAlexandru Tachici mutex_unlock(&priv->lock); 985bc93e19dSAlexandru Tachici } 986bc93e19dSAlexandru Tachici 987bc93e19dSAlexandru Tachici static netdev_tx_t adin1110_start_xmit(struct sk_buff *skb, struct net_device *dev) 988bc93e19dSAlexandru Tachici { 989bc93e19dSAlexandru Tachici struct adin1110_port_priv *port_priv = netdev_priv(dev); 990bc93e19dSAlexandru Tachici struct adin1110_priv *priv = port_priv->priv; 991bc93e19dSAlexandru Tachici netdev_tx_t netdev_ret = NETDEV_TX_OK; 992bc93e19dSAlexandru Tachici u32 tx_space_needed; 993bc93e19dSAlexandru Tachici 994bc93e19dSAlexandru Tachici tx_space_needed = skb->len + ADIN1110_FRAME_HEADER_LEN + ADIN1110_INTERNAL_SIZE_HEADER_LEN; 995bc93e19dSAlexandru Tachici if (tx_space_needed > priv->tx_space) { 996bc93e19dSAlexandru Tachici netif_stop_queue(dev); 997bc93e19dSAlexandru Tachici netdev_ret = NETDEV_TX_BUSY; 998bc93e19dSAlexandru Tachici } else { 999bc93e19dSAlexandru Tachici priv->tx_space -= tx_space_needed; 1000bc93e19dSAlexandru Tachici skb_queue_tail(&port_priv->txq, skb); 1001bc93e19dSAlexandru Tachici } 1002bc93e19dSAlexandru Tachici 1003bc93e19dSAlexandru Tachici schedule_work(&port_priv->tx_work); 1004bc93e19dSAlexandru Tachici 1005bc93e19dSAlexandru Tachici return netdev_ret; 1006bc93e19dSAlexandru Tachici } 1007bc93e19dSAlexandru Tachici 1008bc93e19dSAlexandru Tachici static void adin1110_ndo_get_stats64(struct net_device *dev, 1009bc93e19dSAlexandru Tachici struct rtnl_link_stats64 *storage) 1010bc93e19dSAlexandru Tachici { 1011bc93e19dSAlexandru Tachici struct adin1110_port_priv *port_priv = netdev_priv(dev); 1012bc93e19dSAlexandru Tachici 1013bc93e19dSAlexandru Tachici storage->rx_packets = port_priv->rx_packets; 1014bc93e19dSAlexandru Tachici storage->tx_packets = port_priv->tx_packets; 1015bc93e19dSAlexandru Tachici 1016bc93e19dSAlexandru Tachici storage->rx_bytes = port_priv->rx_bytes; 1017bc93e19dSAlexandru Tachici storage->tx_bytes = port_priv->tx_bytes; 1018bc93e19dSAlexandru Tachici } 1019bc93e19dSAlexandru Tachici 1020bc93e19dSAlexandru Tachici static int adin1110_port_get_port_parent_id(struct net_device *dev, 1021bc93e19dSAlexandru Tachici struct netdev_phys_item_id *ppid) 1022bc93e19dSAlexandru Tachici { 1023bc93e19dSAlexandru Tachici struct adin1110_port_priv *port_priv = netdev_priv(dev); 1024bc93e19dSAlexandru Tachici struct adin1110_priv *priv = port_priv->priv; 1025bc93e19dSAlexandru Tachici 10262b997747SAlexandru Tachici ppid->id_len = strnlen(priv->mii_bus_name, MAX_PHYS_ITEM_ID_LEN); 1027bc93e19dSAlexandru Tachici memcpy(ppid->id, priv->mii_bus_name, ppid->id_len); 1028bc93e19dSAlexandru Tachici 1029bc93e19dSAlexandru Tachici return 0; 1030bc93e19dSAlexandru Tachici } 1031bc93e19dSAlexandru Tachici 1032bc93e19dSAlexandru Tachici static int adin1110_ndo_get_phys_port_name(struct net_device *dev, 1033bc93e19dSAlexandru Tachici char *name, size_t len) 1034bc93e19dSAlexandru Tachici { 1035bc93e19dSAlexandru Tachici struct adin1110_port_priv *port_priv = netdev_priv(dev); 1036bc93e19dSAlexandru Tachici int err; 1037bc93e19dSAlexandru Tachici 1038bc93e19dSAlexandru Tachici err = snprintf(name, len, "p%d", port_priv->nr); 1039bc93e19dSAlexandru Tachici if (err >= len) 1040bc93e19dSAlexandru Tachici return -EINVAL; 1041bc93e19dSAlexandru Tachici 1042bc93e19dSAlexandru Tachici return 0; 1043bc93e19dSAlexandru Tachici } 1044bc93e19dSAlexandru Tachici 1045bc93e19dSAlexandru Tachici static const struct net_device_ops adin1110_netdev_ops = { 1046bc93e19dSAlexandru Tachici .ndo_open = adin1110_net_open, 1047bc93e19dSAlexandru Tachici .ndo_stop = adin1110_net_stop, 1048bc93e19dSAlexandru Tachici .ndo_eth_ioctl = adin1110_ioctl, 1049bc93e19dSAlexandru Tachici .ndo_start_xmit = adin1110_start_xmit, 1050bc93e19dSAlexandru Tachici .ndo_set_mac_address = adin1110_ndo_set_mac_address, 1051bc93e19dSAlexandru Tachici .ndo_set_rx_mode = adin1110_set_rx_mode, 1052bc93e19dSAlexandru Tachici .ndo_validate_addr = eth_validate_addr, 1053bc93e19dSAlexandru Tachici .ndo_get_stats64 = adin1110_ndo_get_stats64, 1054bc93e19dSAlexandru Tachici .ndo_get_port_parent_id = adin1110_port_get_port_parent_id, 1055bc93e19dSAlexandru Tachici .ndo_get_phys_port_name = adin1110_ndo_get_phys_port_name, 1056bc93e19dSAlexandru Tachici }; 1057bc93e19dSAlexandru Tachici 1058bc93e19dSAlexandru Tachici static void adin1110_get_drvinfo(struct net_device *dev, 1059bc93e19dSAlexandru Tachici struct ethtool_drvinfo *di) 1060bc93e19dSAlexandru Tachici { 1061bc93e19dSAlexandru Tachici strscpy(di->driver, "ADIN1110", sizeof(di->driver)); 1062bc93e19dSAlexandru Tachici strscpy(di->bus_info, dev_name(dev->dev.parent), sizeof(di->bus_info)); 1063bc93e19dSAlexandru Tachici } 1064bc93e19dSAlexandru Tachici 1065bc93e19dSAlexandru Tachici static const struct ethtool_ops adin1110_ethtool_ops = { 1066bc93e19dSAlexandru Tachici .get_drvinfo = adin1110_get_drvinfo, 1067bc93e19dSAlexandru Tachici .get_link = ethtool_op_get_link, 1068bc93e19dSAlexandru Tachici .get_link_ksettings = phy_ethtool_get_link_ksettings, 1069bc93e19dSAlexandru Tachici .set_link_ksettings = phy_ethtool_set_link_ksettings, 1070bc93e19dSAlexandru Tachici }; 1071bc93e19dSAlexandru Tachici 1072bc93e19dSAlexandru Tachici static void adin1110_adjust_link(struct net_device *dev) 1073bc93e19dSAlexandru Tachici { 1074bc93e19dSAlexandru Tachici struct phy_device *phydev = dev->phydev; 1075bc93e19dSAlexandru Tachici 1076bc93e19dSAlexandru Tachici if (!phydev->link) 1077bc93e19dSAlexandru Tachici phy_print_status(phydev); 1078bc93e19dSAlexandru Tachici } 1079bc93e19dSAlexandru Tachici 1080bc93e19dSAlexandru Tachici /* PHY ID is stored in the MAC registers too, 1081bc93e19dSAlexandru Tachici * check spi connection by reading it. 1082bc93e19dSAlexandru Tachici */ 1083bc93e19dSAlexandru Tachici static int adin1110_check_spi(struct adin1110_priv *priv) 1084bc93e19dSAlexandru Tachici { 1085*36934cacSAlexandru Tachici struct gpio_desc *reset_gpio; 1086bc93e19dSAlexandru Tachici int ret; 1087bc93e19dSAlexandru Tachici u32 val; 1088bc93e19dSAlexandru Tachici 1089*36934cacSAlexandru Tachici reset_gpio = devm_gpiod_get_optional(&priv->spidev->dev, "reset", 1090*36934cacSAlexandru Tachici GPIOD_OUT_LOW); 1091*36934cacSAlexandru Tachici if (reset_gpio) { 1092*36934cacSAlexandru Tachici /* MISO pin is used for internal configuration, can't have 1093*36934cacSAlexandru Tachici * anyone else disturbing the SDO line. 1094*36934cacSAlexandru Tachici */ 1095*36934cacSAlexandru Tachici spi_bus_lock(priv->spidev->controller); 1096*36934cacSAlexandru Tachici 1097*36934cacSAlexandru Tachici gpiod_set_value(reset_gpio, 1); 1098*36934cacSAlexandru Tachici fsleep(10000); 1099*36934cacSAlexandru Tachici gpiod_set_value(reset_gpio, 0); 1100*36934cacSAlexandru Tachici 1101*36934cacSAlexandru Tachici /* Need to wait 90 ms before interacting with 1102*36934cacSAlexandru Tachici * the MAC after a HW reset. 1103*36934cacSAlexandru Tachici */ 1104*36934cacSAlexandru Tachici fsleep(90000); 1105*36934cacSAlexandru Tachici 1106*36934cacSAlexandru Tachici spi_bus_unlock(priv->spidev->controller); 1107*36934cacSAlexandru Tachici } 1108*36934cacSAlexandru Tachici 1109bc93e19dSAlexandru Tachici ret = adin1110_read_reg(priv, ADIN1110_PHY_ID, &val); 1110bc93e19dSAlexandru Tachici if (ret < 0) 1111bc93e19dSAlexandru Tachici return ret; 1112bc93e19dSAlexandru Tachici 1113bc93e19dSAlexandru Tachici if (val != priv->cfg->phy_id_val) { 1114bc93e19dSAlexandru Tachici dev_err(&priv->spidev->dev, "PHY ID expected: %x, read: %x\n", 1115bc93e19dSAlexandru Tachici priv->cfg->phy_id_val, val); 1116bc93e19dSAlexandru Tachici return -EIO; 1117bc93e19dSAlexandru Tachici } 1118bc93e19dSAlexandru Tachici 1119bc93e19dSAlexandru Tachici return 0; 1120bc93e19dSAlexandru Tachici } 1121bc93e19dSAlexandru Tachici 1122bc93e19dSAlexandru Tachici static int adin1110_hw_forwarding(struct adin1110_priv *priv, bool enable) 1123bc93e19dSAlexandru Tachici { 1124bc93e19dSAlexandru Tachici int ret; 1125bc93e19dSAlexandru Tachici int i; 1126bc93e19dSAlexandru Tachici 1127bc93e19dSAlexandru Tachici priv->forwarding = enable; 1128bc93e19dSAlexandru Tachici 1129bc93e19dSAlexandru Tachici if (!priv->forwarding) { 1130bc93e19dSAlexandru Tachici for (i = ADIN_MAC_FDB_ADDR_SLOT; i < ADIN_MAC_MAX_ADDR_SLOTS; i++) { 1131bc93e19dSAlexandru Tachici ret = adin1110_clear_mac_address(priv, i); 1132bc93e19dSAlexandru Tachici if (ret < 0) 1133bc93e19dSAlexandru Tachici return ret; 1134bc93e19dSAlexandru Tachici } 1135bc93e19dSAlexandru Tachici } 1136bc93e19dSAlexandru Tachici 1137bc93e19dSAlexandru Tachici /* Forwarding is optimised when MAC runs in Cut Through mode. */ 1138bc93e19dSAlexandru Tachici ret = adin1110_set_bits(priv, ADIN1110_CONFIG2, 1139bc93e19dSAlexandru Tachici ADIN2111_PORT_CUT_THRU_EN, 1140bc93e19dSAlexandru Tachici priv->forwarding ? ADIN2111_PORT_CUT_THRU_EN : 0); 1141bc93e19dSAlexandru Tachici if (ret < 0) 1142bc93e19dSAlexandru Tachici return ret; 1143bc93e19dSAlexandru Tachici 1144bc93e19dSAlexandru Tachici for (i = 0; i < priv->cfg->ports_nr; i++) { 1145bc93e19dSAlexandru Tachici ret = adin1110_setup_rx_mode(priv->ports[i]); 1146bc93e19dSAlexandru Tachici if (ret < 0) 1147bc93e19dSAlexandru Tachici return ret; 1148bc93e19dSAlexandru Tachici } 1149bc93e19dSAlexandru Tachici 1150bc93e19dSAlexandru Tachici return ret; 1151bc93e19dSAlexandru Tachici } 1152bc93e19dSAlexandru Tachici 1153bc93e19dSAlexandru Tachici static int adin1110_port_bridge_join(struct adin1110_port_priv *port_priv, 1154bc93e19dSAlexandru Tachici struct net_device *bridge) 1155bc93e19dSAlexandru Tachici { 1156bc93e19dSAlexandru Tachici struct adin1110_priv *priv = port_priv->priv; 1157bc93e19dSAlexandru Tachici int ret; 1158bc93e19dSAlexandru Tachici 1159bc93e19dSAlexandru Tachici port_priv->bridge = bridge; 1160bc93e19dSAlexandru Tachici 1161bc93e19dSAlexandru Tachici if (adin1110_can_offload_forwarding(priv)) { 1162bc93e19dSAlexandru Tachici mutex_lock(&priv->lock); 1163bc93e19dSAlexandru Tachici ret = adin1110_hw_forwarding(priv, true); 1164bc93e19dSAlexandru Tachici mutex_unlock(&priv->lock); 1165bc93e19dSAlexandru Tachici 1166bc93e19dSAlexandru Tachici if (ret < 0) 1167bc93e19dSAlexandru Tachici return ret; 1168bc93e19dSAlexandru Tachici } 1169bc93e19dSAlexandru Tachici 1170bc93e19dSAlexandru Tachici return adin1110_set_mac_address(port_priv->netdev, bridge->dev_addr); 1171bc93e19dSAlexandru Tachici } 1172bc93e19dSAlexandru Tachici 1173bc93e19dSAlexandru Tachici static int adin1110_port_bridge_leave(struct adin1110_port_priv *port_priv, 1174bc93e19dSAlexandru Tachici struct net_device *bridge) 1175bc93e19dSAlexandru Tachici { 1176bc93e19dSAlexandru Tachici struct adin1110_priv *priv = port_priv->priv; 1177bc93e19dSAlexandru Tachici int ret; 1178bc93e19dSAlexandru Tachici 1179bc93e19dSAlexandru Tachici port_priv->bridge = NULL; 1180bc93e19dSAlexandru Tachici 1181bc93e19dSAlexandru Tachici mutex_lock(&priv->lock); 1182bc93e19dSAlexandru Tachici ret = adin1110_hw_forwarding(priv, false); 1183bc93e19dSAlexandru Tachici mutex_unlock(&priv->lock); 1184bc93e19dSAlexandru Tachici 1185bc93e19dSAlexandru Tachici return ret; 1186bc93e19dSAlexandru Tachici } 1187bc93e19dSAlexandru Tachici 1188f9371935SAlexandru Tachici static bool adin1110_port_dev_check(const struct net_device *dev) 1189f9371935SAlexandru Tachici { 1190f9371935SAlexandru Tachici return dev->netdev_ops == &adin1110_netdev_ops; 1191f9371935SAlexandru Tachici } 1192f9371935SAlexandru Tachici 1193bc93e19dSAlexandru Tachici static int adin1110_netdevice_event(struct notifier_block *unused, 1194bc93e19dSAlexandru Tachici unsigned long event, void *ptr) 1195bc93e19dSAlexandru Tachici { 1196bc93e19dSAlexandru Tachici struct net_device *dev = netdev_notifier_info_to_dev(ptr); 1197bc93e19dSAlexandru Tachici struct adin1110_port_priv *port_priv = netdev_priv(dev); 1198bc93e19dSAlexandru Tachici struct netdev_notifier_changeupper_info *info = ptr; 1199bc93e19dSAlexandru Tachici int ret = 0; 1200bc93e19dSAlexandru Tachici 1201f9371935SAlexandru Tachici if (!adin1110_port_dev_check(dev)) 1202f9371935SAlexandru Tachici return NOTIFY_DONE; 1203f9371935SAlexandru Tachici 1204bc93e19dSAlexandru Tachici switch (event) { 1205bc93e19dSAlexandru Tachici case NETDEV_CHANGEUPPER: 1206bc93e19dSAlexandru Tachici if (netif_is_bridge_master(info->upper_dev)) { 1207bc93e19dSAlexandru Tachici if (info->linking) 1208bc93e19dSAlexandru Tachici ret = adin1110_port_bridge_join(port_priv, info->upper_dev); 1209bc93e19dSAlexandru Tachici else 1210bc93e19dSAlexandru Tachici ret = adin1110_port_bridge_leave(port_priv, info->upper_dev); 1211bc93e19dSAlexandru Tachici } 1212bc93e19dSAlexandru Tachici break; 1213bc93e19dSAlexandru Tachici default: 1214bc93e19dSAlexandru Tachici break; 1215bc93e19dSAlexandru Tachici } 1216bc93e19dSAlexandru Tachici 1217bc93e19dSAlexandru Tachici return notifier_from_errno(ret); 1218bc93e19dSAlexandru Tachici } 1219bc93e19dSAlexandru Tachici 1220bc93e19dSAlexandru Tachici static struct notifier_block adin1110_netdevice_nb = { 1221bc93e19dSAlexandru Tachici .notifier_call = adin1110_netdevice_event, 1222bc93e19dSAlexandru Tachici }; 1223bc93e19dSAlexandru Tachici 1224bc93e19dSAlexandru Tachici static void adin1110_disconnect_phy(void *data) 1225bc93e19dSAlexandru Tachici { 1226bc93e19dSAlexandru Tachici phy_disconnect(data); 1227bc93e19dSAlexandru Tachici } 1228bc93e19dSAlexandru Tachici 1229bc93e19dSAlexandru Tachici static int adin1110_port_set_forwarding_state(struct adin1110_port_priv *port_priv) 1230bc93e19dSAlexandru Tachici { 1231bc93e19dSAlexandru Tachici struct adin1110_priv *priv = port_priv->priv; 1232bc93e19dSAlexandru Tachici int ret; 1233bc93e19dSAlexandru Tachici 1234bc93e19dSAlexandru Tachici port_priv->state = BR_STATE_FORWARDING; 1235bc93e19dSAlexandru Tachici 1236bc93e19dSAlexandru Tachici mutex_lock(&priv->lock); 1237bc93e19dSAlexandru Tachici ret = adin1110_set_mac_address(port_priv->netdev, 1238bc93e19dSAlexandru Tachici port_priv->netdev->dev_addr); 1239bc93e19dSAlexandru Tachici if (ret < 0) 1240bc93e19dSAlexandru Tachici goto out; 1241bc93e19dSAlexandru Tachici 1242bc93e19dSAlexandru Tachici if (adin1110_can_offload_forwarding(priv)) 1243bc93e19dSAlexandru Tachici ret = adin1110_hw_forwarding(priv, true); 1244bc93e19dSAlexandru Tachici else 1245bc93e19dSAlexandru Tachici ret = adin1110_setup_rx_mode(port_priv); 1246bc93e19dSAlexandru Tachici out: 1247bc93e19dSAlexandru Tachici mutex_unlock(&priv->lock); 1248bc93e19dSAlexandru Tachici 1249bc93e19dSAlexandru Tachici return ret; 1250bc93e19dSAlexandru Tachici } 1251bc93e19dSAlexandru Tachici 1252bc93e19dSAlexandru Tachici static int adin1110_port_set_blocking_state(struct adin1110_port_priv *port_priv) 1253bc93e19dSAlexandru Tachici { 1254bc93e19dSAlexandru Tachici u8 mac[ETH_ALEN] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x00}; 1255bc93e19dSAlexandru Tachici struct adin1110_priv *priv = port_priv->priv; 1256bc93e19dSAlexandru Tachici u8 mask[ETH_ALEN]; 1257bc93e19dSAlexandru Tachici u32 port_rules; 1258bc93e19dSAlexandru Tachici int mac_slot; 1259bc93e19dSAlexandru Tachici int ret; 1260bc93e19dSAlexandru Tachici 1261bc93e19dSAlexandru Tachici port_priv->state = BR_STATE_BLOCKING; 1262bc93e19dSAlexandru Tachici 1263bc93e19dSAlexandru Tachici mutex_lock(&priv->lock); 1264bc93e19dSAlexandru Tachici 1265bc93e19dSAlexandru Tachici mac_slot = (!port_priv->nr) ? ADIN_MAC_P1_ADDR_SLOT : ADIN_MAC_P2_ADDR_SLOT; 1266bc93e19dSAlexandru Tachici ret = adin1110_clear_mac_address(priv, mac_slot); 1267bc93e19dSAlexandru Tachici if (ret < 0) 1268bc93e19dSAlexandru Tachici goto out; 1269bc93e19dSAlexandru Tachici 1270bc93e19dSAlexandru Tachici ret = adin1110_hw_forwarding(priv, false); 1271bc93e19dSAlexandru Tachici if (ret < 0) 1272bc93e19dSAlexandru Tachici goto out; 1273bc93e19dSAlexandru Tachici 1274bc93e19dSAlexandru Tachici /* Allow only BPDUs to be passed to the CPU */ 1275bc93e19dSAlexandru Tachici memset(mask, 0xFF, ETH_ALEN); 1276bc93e19dSAlexandru Tachici port_rules = adin1110_port_rules(port_priv, true, false); 1277bc93e19dSAlexandru Tachici ret = adin1110_write_mac_address(port_priv, mac_slot, mac, 1278bc93e19dSAlexandru Tachici mask, port_rules); 1279bc93e19dSAlexandru Tachici out: 1280bc93e19dSAlexandru Tachici mutex_unlock(&priv->lock); 1281bc93e19dSAlexandru Tachici 1282bc93e19dSAlexandru Tachici return ret; 1283bc93e19dSAlexandru Tachici } 1284bc93e19dSAlexandru Tachici 1285bc93e19dSAlexandru Tachici /* ADIN1110/2111 does not have any native STP support. 1286bc93e19dSAlexandru Tachici * Listen for bridge core state changes and 1287bc93e19dSAlexandru Tachici * allow all frames to pass or only the BPDUs. 1288bc93e19dSAlexandru Tachici */ 1289bc93e19dSAlexandru Tachici static int adin1110_port_attr_stp_state_set(struct adin1110_port_priv *port_priv, 1290bc93e19dSAlexandru Tachici u8 state) 1291bc93e19dSAlexandru Tachici { 1292bc93e19dSAlexandru Tachici switch (state) { 1293bc93e19dSAlexandru Tachici case BR_STATE_FORWARDING: 1294bc93e19dSAlexandru Tachici return adin1110_port_set_forwarding_state(port_priv); 1295bc93e19dSAlexandru Tachici case BR_STATE_LEARNING: 1296bc93e19dSAlexandru Tachici case BR_STATE_LISTENING: 1297bc93e19dSAlexandru Tachici case BR_STATE_DISABLED: 1298bc93e19dSAlexandru Tachici case BR_STATE_BLOCKING: 1299bc93e19dSAlexandru Tachici return adin1110_port_set_blocking_state(port_priv); 1300bc93e19dSAlexandru Tachici default: 1301bc93e19dSAlexandru Tachici return -EINVAL; 1302bc93e19dSAlexandru Tachici } 1303bc93e19dSAlexandru Tachici } 1304bc93e19dSAlexandru Tachici 1305bc93e19dSAlexandru Tachici static int adin1110_port_attr_set(struct net_device *dev, const void *ctx, 1306bc93e19dSAlexandru Tachici const struct switchdev_attr *attr, 1307bc93e19dSAlexandru Tachici struct netlink_ext_ack *extack) 1308bc93e19dSAlexandru Tachici { 1309bc93e19dSAlexandru Tachici struct adin1110_port_priv *port_priv = netdev_priv(dev); 1310bc93e19dSAlexandru Tachici 1311bc93e19dSAlexandru Tachici switch (attr->id) { 1312bc93e19dSAlexandru Tachici case SWITCHDEV_ATTR_ID_PORT_STP_STATE: 1313bc93e19dSAlexandru Tachici return adin1110_port_attr_stp_state_set(port_priv, 1314bc93e19dSAlexandru Tachici attr->u.stp_state); 1315bc93e19dSAlexandru Tachici default: 1316bc93e19dSAlexandru Tachici return -EOPNOTSUPP; 1317bc93e19dSAlexandru Tachici } 1318bc93e19dSAlexandru Tachici } 1319bc93e19dSAlexandru Tachici 1320bc93e19dSAlexandru Tachici static int adin1110_switchdev_blocking_event(struct notifier_block *unused, 1321bc93e19dSAlexandru Tachici unsigned long event, 1322bc93e19dSAlexandru Tachici void *ptr) 1323bc93e19dSAlexandru Tachici { 1324bc93e19dSAlexandru Tachici struct net_device *netdev = switchdev_notifier_info_to_dev(ptr); 1325bc93e19dSAlexandru Tachici int ret; 1326bc93e19dSAlexandru Tachici 1327bc93e19dSAlexandru Tachici if (event == SWITCHDEV_PORT_ATTR_SET) { 1328bc93e19dSAlexandru Tachici ret = switchdev_handle_port_attr_set(netdev, ptr, 1329bc93e19dSAlexandru Tachici adin1110_port_dev_check, 1330bc93e19dSAlexandru Tachici adin1110_port_attr_set); 1331bc93e19dSAlexandru Tachici 1332bc93e19dSAlexandru Tachici return notifier_from_errno(ret); 1333bc93e19dSAlexandru Tachici } 1334bc93e19dSAlexandru Tachici 1335bc93e19dSAlexandru Tachici return NOTIFY_DONE; 1336bc93e19dSAlexandru Tachici } 1337bc93e19dSAlexandru Tachici 1338bc93e19dSAlexandru Tachici static struct notifier_block adin1110_switchdev_blocking_notifier = { 1339bc93e19dSAlexandru Tachici .notifier_call = adin1110_switchdev_blocking_event, 1340bc93e19dSAlexandru Tachici }; 1341bc93e19dSAlexandru Tachici 1342bc93e19dSAlexandru Tachici static void adin1110_fdb_offload_notify(struct net_device *netdev, 1343bc93e19dSAlexandru Tachici struct switchdev_notifier_fdb_info *rcv) 1344bc93e19dSAlexandru Tachici { 1345bc93e19dSAlexandru Tachici struct switchdev_notifier_fdb_info info = {}; 1346bc93e19dSAlexandru Tachici 1347bc93e19dSAlexandru Tachici info.addr = rcv->addr; 1348bc93e19dSAlexandru Tachici info.vid = rcv->vid; 1349bc93e19dSAlexandru Tachici info.offloaded = true; 1350bc93e19dSAlexandru Tachici call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, 1351bc93e19dSAlexandru Tachici netdev, &info.info, NULL); 1352bc93e19dSAlexandru Tachici } 1353bc93e19dSAlexandru Tachici 1354bc93e19dSAlexandru Tachici static int adin1110_fdb_add(struct adin1110_port_priv *port_priv, 1355bc93e19dSAlexandru Tachici struct switchdev_notifier_fdb_info *fdb) 1356bc93e19dSAlexandru Tachici { 1357bc93e19dSAlexandru Tachici struct adin1110_priv *priv = port_priv->priv; 1358bc93e19dSAlexandru Tachici struct adin1110_port_priv *other_port; 1359bc93e19dSAlexandru Tachici u8 mask[ETH_ALEN]; 1360bc93e19dSAlexandru Tachici u32 port_rules; 1361bc93e19dSAlexandru Tachici int mac_nr; 1362bc93e19dSAlexandru Tachici u32 val; 1363bc93e19dSAlexandru Tachici int ret; 1364bc93e19dSAlexandru Tachici 1365bc93e19dSAlexandru Tachici netdev_dbg(port_priv->netdev, 1366bc93e19dSAlexandru Tachici "DEBUG: %s: MACID = %pM vid = %u flags = %u %u -- port %d\n", 1367bc93e19dSAlexandru Tachici __func__, fdb->addr, fdb->vid, fdb->added_by_user, 1368bc93e19dSAlexandru Tachici fdb->offloaded, port_priv->nr); 1369bc93e19dSAlexandru Tachici 1370bc93e19dSAlexandru Tachici if (!priv->forwarding) 1371bc93e19dSAlexandru Tachici return 0; 1372bc93e19dSAlexandru Tachici 1373bc93e19dSAlexandru Tachici if (fdb->is_local) 1374bc93e19dSAlexandru Tachici return -EINVAL; 1375bc93e19dSAlexandru Tachici 1376bc93e19dSAlexandru Tachici /* Find free FDB slot on device. */ 1377bc93e19dSAlexandru Tachici for (mac_nr = ADIN_MAC_FDB_ADDR_SLOT; mac_nr < ADIN_MAC_MAX_ADDR_SLOTS; mac_nr++) { 1378bc93e19dSAlexandru Tachici ret = adin1110_read_reg(priv, ADIN1110_MAC_ADDR_FILTER_UPR + (mac_nr * 2), &val); 1379bc93e19dSAlexandru Tachici if (ret < 0) 1380bc93e19dSAlexandru Tachici return ret; 1381bc93e19dSAlexandru Tachici if (!val) 1382bc93e19dSAlexandru Tachici break; 1383bc93e19dSAlexandru Tachici } 1384bc93e19dSAlexandru Tachici 1385bc93e19dSAlexandru Tachici if (mac_nr == ADIN_MAC_MAX_ADDR_SLOTS) 1386bc93e19dSAlexandru Tachici return -ENOMEM; 1387bc93e19dSAlexandru Tachici 1388bc93e19dSAlexandru Tachici other_port = priv->ports[!port_priv->nr]; 1389bc93e19dSAlexandru Tachici port_rules = adin1110_port_rules(port_priv, false, true); 1390bc93e19dSAlexandru Tachici memset(mask, 0xFF, ETH_ALEN); 1391bc93e19dSAlexandru Tachici 1392bc93e19dSAlexandru Tachici return adin1110_write_mac_address(other_port, mac_nr, (u8 *)fdb->addr, 1393bc93e19dSAlexandru Tachici mask, port_rules); 1394bc93e19dSAlexandru Tachici } 1395bc93e19dSAlexandru Tachici 1396bc93e19dSAlexandru Tachici static int adin1110_read_mac(struct adin1110_priv *priv, int mac_nr, u8 *addr) 1397bc93e19dSAlexandru Tachici { 1398bc93e19dSAlexandru Tachici u32 val; 1399bc93e19dSAlexandru Tachici int ret; 1400bc93e19dSAlexandru Tachici 1401bc93e19dSAlexandru Tachici ret = adin1110_read_reg(priv, ADIN1110_MAC_ADDR_FILTER_UPR + (mac_nr * 2), &val); 1402bc93e19dSAlexandru Tachici if (ret < 0) 1403bc93e19dSAlexandru Tachici return ret; 1404bc93e19dSAlexandru Tachici 1405bc93e19dSAlexandru Tachici put_unaligned_be16(val, addr); 1406bc93e19dSAlexandru Tachici 1407bc93e19dSAlexandru Tachici ret = adin1110_read_reg(priv, ADIN1110_MAC_ADDR_FILTER_LWR + (mac_nr * 2), &val); 1408bc93e19dSAlexandru Tachici if (ret < 0) 1409bc93e19dSAlexandru Tachici return ret; 1410bc93e19dSAlexandru Tachici 1411bc93e19dSAlexandru Tachici put_unaligned_be32(val, addr + 2); 1412bc93e19dSAlexandru Tachici 1413bc93e19dSAlexandru Tachici return 0; 1414bc93e19dSAlexandru Tachici } 1415bc93e19dSAlexandru Tachici 1416bc93e19dSAlexandru Tachici static int adin1110_fdb_del(struct adin1110_port_priv *port_priv, 1417bc93e19dSAlexandru Tachici struct switchdev_notifier_fdb_info *fdb) 1418bc93e19dSAlexandru Tachici { 1419bc93e19dSAlexandru Tachici struct adin1110_priv *priv = port_priv->priv; 1420bc93e19dSAlexandru Tachici u8 addr[ETH_ALEN]; 1421bc93e19dSAlexandru Tachici int mac_nr; 1422bc93e19dSAlexandru Tachici int ret; 1423bc93e19dSAlexandru Tachici 1424bc93e19dSAlexandru Tachici netdev_dbg(port_priv->netdev, 1425bc93e19dSAlexandru Tachici "DEBUG: %s: MACID = %pM vid = %u flags = %u %u -- port %d\n", 1426bc93e19dSAlexandru Tachici __func__, fdb->addr, fdb->vid, fdb->added_by_user, 1427bc93e19dSAlexandru Tachici fdb->offloaded, port_priv->nr); 1428bc93e19dSAlexandru Tachici 1429bc93e19dSAlexandru Tachici if (fdb->is_local) 1430bc93e19dSAlexandru Tachici return -EINVAL; 1431bc93e19dSAlexandru Tachici 1432bc93e19dSAlexandru Tachici for (mac_nr = ADIN_MAC_FDB_ADDR_SLOT; mac_nr < ADIN_MAC_MAX_ADDR_SLOTS; mac_nr++) { 1433bc93e19dSAlexandru Tachici ret = adin1110_read_mac(priv, mac_nr, addr); 1434bc93e19dSAlexandru Tachici if (ret < 0) 1435bc93e19dSAlexandru Tachici return ret; 1436bc93e19dSAlexandru Tachici 1437bc93e19dSAlexandru Tachici if (ether_addr_equal(addr, fdb->addr)) { 1438bc93e19dSAlexandru Tachici ret = adin1110_clear_mac_address(priv, mac_nr); 1439bc93e19dSAlexandru Tachici if (ret < 0) 1440bc93e19dSAlexandru Tachici return ret; 1441bc93e19dSAlexandru Tachici } 1442bc93e19dSAlexandru Tachici } 1443bc93e19dSAlexandru Tachici 1444bc93e19dSAlexandru Tachici return 0; 1445bc93e19dSAlexandru Tachici } 1446bc93e19dSAlexandru Tachici 1447bc93e19dSAlexandru Tachici static void adin1110_switchdev_event_work(struct work_struct *work) 1448bc93e19dSAlexandru Tachici { 1449bc93e19dSAlexandru Tachici struct adin1110_switchdev_event_work *switchdev_work; 1450bc93e19dSAlexandru Tachici struct adin1110_port_priv *port_priv; 1451bc93e19dSAlexandru Tachici int ret; 1452bc93e19dSAlexandru Tachici 1453bc93e19dSAlexandru Tachici switchdev_work = container_of(work, struct adin1110_switchdev_event_work, work); 1454bc93e19dSAlexandru Tachici port_priv = switchdev_work->port_priv; 1455bc93e19dSAlexandru Tachici 1456bc93e19dSAlexandru Tachici mutex_lock(&port_priv->priv->lock); 1457bc93e19dSAlexandru Tachici 1458bc93e19dSAlexandru Tachici switch (switchdev_work->event) { 1459bc93e19dSAlexandru Tachici case SWITCHDEV_FDB_ADD_TO_DEVICE: 1460bc93e19dSAlexandru Tachici ret = adin1110_fdb_add(port_priv, &switchdev_work->fdb_info); 1461bc93e19dSAlexandru Tachici if (!ret) 1462bc93e19dSAlexandru Tachici adin1110_fdb_offload_notify(port_priv->netdev, 1463bc93e19dSAlexandru Tachici &switchdev_work->fdb_info); 1464bc93e19dSAlexandru Tachici break; 1465bc93e19dSAlexandru Tachici case SWITCHDEV_FDB_DEL_TO_DEVICE: 1466bc93e19dSAlexandru Tachici adin1110_fdb_del(port_priv, &switchdev_work->fdb_info); 1467bc93e19dSAlexandru Tachici break; 1468bc93e19dSAlexandru Tachici default: 1469bc93e19dSAlexandru Tachici break; 1470bc93e19dSAlexandru Tachici } 1471bc93e19dSAlexandru Tachici 1472bc93e19dSAlexandru Tachici mutex_unlock(&port_priv->priv->lock); 1473bc93e19dSAlexandru Tachici 1474bc93e19dSAlexandru Tachici kfree(switchdev_work->fdb_info.addr); 1475bc93e19dSAlexandru Tachici kfree(switchdev_work); 1476bc93e19dSAlexandru Tachici dev_put(port_priv->netdev); 1477bc93e19dSAlexandru Tachici } 1478bc93e19dSAlexandru Tachici 1479bc93e19dSAlexandru Tachici /* called under rcu_read_lock() */ 1480bc93e19dSAlexandru Tachici static int adin1110_switchdev_event(struct notifier_block *unused, 1481bc93e19dSAlexandru Tachici unsigned long event, void *ptr) 1482bc93e19dSAlexandru Tachici { 1483bc93e19dSAlexandru Tachici struct net_device *netdev = switchdev_notifier_info_to_dev(ptr); 1484bc93e19dSAlexandru Tachici struct adin1110_port_priv *port_priv = netdev_priv(netdev); 1485bc93e19dSAlexandru Tachici struct adin1110_switchdev_event_work *switchdev_work; 1486bc93e19dSAlexandru Tachici struct switchdev_notifier_fdb_info *fdb_info = ptr; 1487bc93e19dSAlexandru Tachici 1488bc93e19dSAlexandru Tachici if (!adin1110_port_dev_check(netdev)) 1489bc93e19dSAlexandru Tachici return NOTIFY_DONE; 1490bc93e19dSAlexandru Tachici 1491bc93e19dSAlexandru Tachici switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); 1492bc93e19dSAlexandru Tachici if (WARN_ON(!switchdev_work)) 1493bc93e19dSAlexandru Tachici return NOTIFY_BAD; 1494bc93e19dSAlexandru Tachici 1495bc93e19dSAlexandru Tachici INIT_WORK(&switchdev_work->work, adin1110_switchdev_event_work); 1496bc93e19dSAlexandru Tachici switchdev_work->port_priv = port_priv; 1497bc93e19dSAlexandru Tachici switchdev_work->event = event; 1498bc93e19dSAlexandru Tachici 1499bc93e19dSAlexandru Tachici switch (event) { 1500bc93e19dSAlexandru Tachici case SWITCHDEV_FDB_ADD_TO_DEVICE: 1501bc93e19dSAlexandru Tachici case SWITCHDEV_FDB_DEL_TO_DEVICE: 1502bc93e19dSAlexandru Tachici memcpy(&switchdev_work->fdb_info, ptr, 1503bc93e19dSAlexandru Tachici sizeof(switchdev_work->fdb_info)); 1504bc93e19dSAlexandru Tachici switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC); 1505bc93e19dSAlexandru Tachici 1506bc93e19dSAlexandru Tachici if (!switchdev_work->fdb_info.addr) 1507bc93e19dSAlexandru Tachici goto err_addr_alloc; 1508bc93e19dSAlexandru Tachici 1509bc93e19dSAlexandru Tachici ether_addr_copy((u8 *)switchdev_work->fdb_info.addr, 1510bc93e19dSAlexandru Tachici fdb_info->addr); 1511bc93e19dSAlexandru Tachici dev_hold(netdev); 1512bc93e19dSAlexandru Tachici break; 1513bc93e19dSAlexandru Tachici default: 1514bc93e19dSAlexandru Tachici kfree(switchdev_work); 1515bc93e19dSAlexandru Tachici return NOTIFY_DONE; 1516bc93e19dSAlexandru Tachici } 1517bc93e19dSAlexandru Tachici 1518bc93e19dSAlexandru Tachici queue_work(system_long_wq, &switchdev_work->work); 1519bc93e19dSAlexandru Tachici 1520bc93e19dSAlexandru Tachici return NOTIFY_DONE; 1521bc93e19dSAlexandru Tachici 1522bc93e19dSAlexandru Tachici err_addr_alloc: 1523bc93e19dSAlexandru Tachici kfree(switchdev_work); 1524bc93e19dSAlexandru Tachici return NOTIFY_BAD; 1525bc93e19dSAlexandru Tachici } 1526bc93e19dSAlexandru Tachici 1527bc93e19dSAlexandru Tachici static struct notifier_block adin1110_switchdev_notifier = { 1528bc93e19dSAlexandru Tachici .notifier_call = adin1110_switchdev_event, 1529bc93e19dSAlexandru Tachici }; 1530bc93e19dSAlexandru Tachici 1531bc93e19dSAlexandru Tachici static void adin1110_unregister_notifiers(void *data) 1532bc93e19dSAlexandru Tachici { 1533bc93e19dSAlexandru Tachici unregister_switchdev_blocking_notifier(&adin1110_switchdev_blocking_notifier); 1534bc93e19dSAlexandru Tachici unregister_switchdev_notifier(&adin1110_switchdev_notifier); 1535bc93e19dSAlexandru Tachici unregister_netdevice_notifier(&adin1110_netdevice_nb); 1536bc93e19dSAlexandru Tachici } 1537bc93e19dSAlexandru Tachici 1538bc93e19dSAlexandru Tachici static int adin1110_setup_notifiers(struct adin1110_priv *priv) 1539bc93e19dSAlexandru Tachici { 1540bc93e19dSAlexandru Tachici struct device *dev = &priv->spidev->dev; 1541bc93e19dSAlexandru Tachici int ret; 1542bc93e19dSAlexandru Tachici 1543bc93e19dSAlexandru Tachici ret = register_netdevice_notifier(&adin1110_netdevice_nb); 1544bc93e19dSAlexandru Tachici if (ret < 0) 1545bc93e19dSAlexandru Tachici return ret; 1546bc93e19dSAlexandru Tachici 1547bc93e19dSAlexandru Tachici ret = register_switchdev_notifier(&adin1110_switchdev_notifier); 1548bc93e19dSAlexandru Tachici if (ret < 0) 1549bc93e19dSAlexandru Tachici goto err_netdev; 1550bc93e19dSAlexandru Tachici 1551bc93e19dSAlexandru Tachici ret = register_switchdev_blocking_notifier(&adin1110_switchdev_blocking_notifier); 1552bc93e19dSAlexandru Tachici if (ret < 0) 1553bc93e19dSAlexandru Tachici goto err_sdev; 1554bc93e19dSAlexandru Tachici 1555bc93e19dSAlexandru Tachici return devm_add_action_or_reset(dev, adin1110_unregister_notifiers, NULL); 1556bc93e19dSAlexandru Tachici 1557bc93e19dSAlexandru Tachici err_sdev: 1558bc93e19dSAlexandru Tachici unregister_switchdev_notifier(&adin1110_switchdev_notifier); 1559bc93e19dSAlexandru Tachici 1560bc93e19dSAlexandru Tachici err_netdev: 1561bc93e19dSAlexandru Tachici unregister_netdevice_notifier(&adin1110_netdevice_nb); 1562bc93e19dSAlexandru Tachici return ret; 1563bc93e19dSAlexandru Tachici } 1564bc93e19dSAlexandru Tachici 1565bc93e19dSAlexandru Tachici static int adin1110_probe_netdevs(struct adin1110_priv *priv) 1566bc93e19dSAlexandru Tachici { 1567bc93e19dSAlexandru Tachici struct device *dev = &priv->spidev->dev; 1568bc93e19dSAlexandru Tachici struct adin1110_port_priv *port_priv; 1569bc93e19dSAlexandru Tachici struct net_device *netdev; 1570bc93e19dSAlexandru Tachici int ret; 1571bc93e19dSAlexandru Tachici int i; 1572bc93e19dSAlexandru Tachici 1573bc93e19dSAlexandru Tachici for (i = 0; i < priv->cfg->ports_nr; i++) { 1574bc93e19dSAlexandru Tachici netdev = devm_alloc_etherdev(dev, sizeof(*port_priv)); 1575bc93e19dSAlexandru Tachici if (!netdev) 1576bc93e19dSAlexandru Tachici return -ENOMEM; 1577bc93e19dSAlexandru Tachici 1578bc93e19dSAlexandru Tachici port_priv = netdev_priv(netdev); 1579bc93e19dSAlexandru Tachici port_priv->netdev = netdev; 1580bc93e19dSAlexandru Tachici port_priv->priv = priv; 1581bc93e19dSAlexandru Tachici port_priv->cfg = priv->cfg; 1582bc93e19dSAlexandru Tachici port_priv->nr = i; 1583bc93e19dSAlexandru Tachici priv->ports[i] = port_priv; 1584bc93e19dSAlexandru Tachici SET_NETDEV_DEV(netdev, dev); 1585bc93e19dSAlexandru Tachici 1586bc93e19dSAlexandru Tachici ret = device_get_ethdev_address(dev, netdev); 1587bc93e19dSAlexandru Tachici if (ret < 0) 1588bc93e19dSAlexandru Tachici return ret; 1589bc93e19dSAlexandru Tachici 1590bc93e19dSAlexandru Tachici netdev->irq = priv->spidev->irq; 1591bc93e19dSAlexandru Tachici INIT_WORK(&port_priv->tx_work, adin1110_tx_work); 1592bc93e19dSAlexandru Tachici INIT_WORK(&port_priv->rx_mode_work, adin1110_rx_mode_work); 1593bc93e19dSAlexandru Tachici skb_queue_head_init(&port_priv->txq); 1594bc93e19dSAlexandru Tachici 1595bc93e19dSAlexandru Tachici netif_carrier_off(netdev); 1596bc93e19dSAlexandru Tachici 1597bc93e19dSAlexandru Tachici netdev->if_port = IF_PORT_10BASET; 1598bc93e19dSAlexandru Tachici netdev->netdev_ops = &adin1110_netdev_ops; 1599bc93e19dSAlexandru Tachici netdev->ethtool_ops = &adin1110_ethtool_ops; 1600bc93e19dSAlexandru Tachici netdev->priv_flags |= IFF_UNICAST_FLT; 1601bc93e19dSAlexandru Tachici netdev->features |= NETIF_F_NETNS_LOCAL; 1602bc93e19dSAlexandru Tachici 1603bc93e19dSAlexandru Tachici port_priv->phydev = get_phy_device(priv->mii_bus, i + 1, false); 16049f1e3378SWei Yongjun if (IS_ERR(port_priv->phydev)) { 1605bc93e19dSAlexandru Tachici netdev_err(netdev, "Could not find PHY with device address: %d.\n", i); 16069f1e3378SWei Yongjun return PTR_ERR(port_priv->phydev); 1607bc93e19dSAlexandru Tachici } 1608bc93e19dSAlexandru Tachici 1609bc93e19dSAlexandru Tachici port_priv->phydev = phy_connect(netdev, 1610bc93e19dSAlexandru Tachici phydev_name(port_priv->phydev), 1611bc93e19dSAlexandru Tachici adin1110_adjust_link, 1612bc93e19dSAlexandru Tachici PHY_INTERFACE_MODE_INTERNAL); 1613bc93e19dSAlexandru Tachici if (IS_ERR(port_priv->phydev)) { 1614bc93e19dSAlexandru Tachici netdev_err(netdev, "Could not connect PHY with device address: %d.\n", i); 1615bc93e19dSAlexandru Tachici return PTR_ERR(port_priv->phydev); 1616bc93e19dSAlexandru Tachici } 1617bc93e19dSAlexandru Tachici 1618bc93e19dSAlexandru Tachici ret = devm_add_action_or_reset(dev, adin1110_disconnect_phy, 1619bc93e19dSAlexandru Tachici port_priv->phydev); 1620bc93e19dSAlexandru Tachici if (ret < 0) 1621bc93e19dSAlexandru Tachici return ret; 1622bc93e19dSAlexandru Tachici } 1623bc93e19dSAlexandru Tachici 1624bc93e19dSAlexandru Tachici /* ADIN1110 INT_N pin will be used to signal the host */ 1625bc93e19dSAlexandru Tachici ret = devm_request_threaded_irq(dev, priv->spidev->irq, NULL, 1626bc93e19dSAlexandru Tachici adin1110_irq, 1627bc93e19dSAlexandru Tachici IRQF_TRIGGER_LOW | IRQF_ONESHOT, 1628bc93e19dSAlexandru Tachici dev_name(dev), priv); 1629bc93e19dSAlexandru Tachici if (ret < 0) 1630bc93e19dSAlexandru Tachici return ret; 1631bc93e19dSAlexandru Tachici 1632bc93e19dSAlexandru Tachici ret = adin1110_setup_notifiers(priv); 1633bc93e19dSAlexandru Tachici if (ret < 0) 1634bc93e19dSAlexandru Tachici return ret; 1635bc93e19dSAlexandru Tachici 1636bc93e19dSAlexandru Tachici for (i = 0; i < priv->cfg->ports_nr; i++) { 1637bc93e19dSAlexandru Tachici ret = devm_register_netdev(dev, priv->ports[i]->netdev); 1638bc93e19dSAlexandru Tachici if (ret < 0) { 1639bc93e19dSAlexandru Tachici dev_err(dev, "Failed to register network device.\n"); 1640bc93e19dSAlexandru Tachici return ret; 1641bc93e19dSAlexandru Tachici } 1642bc93e19dSAlexandru Tachici } 1643bc93e19dSAlexandru Tachici 1644bc93e19dSAlexandru Tachici return 0; 1645bc93e19dSAlexandru Tachici } 1646bc93e19dSAlexandru Tachici 1647bc93e19dSAlexandru Tachici static int adin1110_probe(struct spi_device *spi) 1648bc93e19dSAlexandru Tachici { 1649bc93e19dSAlexandru Tachici const struct spi_device_id *dev_id = spi_get_device_id(spi); 1650bc93e19dSAlexandru Tachici struct device *dev = &spi->dev; 1651bc93e19dSAlexandru Tachici struct adin1110_priv *priv; 1652bc93e19dSAlexandru Tachici int ret; 1653bc93e19dSAlexandru Tachici 1654bc93e19dSAlexandru Tachici priv = devm_kzalloc(dev, sizeof(struct adin1110_priv), GFP_KERNEL); 1655bc93e19dSAlexandru Tachici if (!priv) 1656bc93e19dSAlexandru Tachici return -ENOMEM; 1657bc93e19dSAlexandru Tachici 1658bc93e19dSAlexandru Tachici priv->spidev = spi; 1659bc93e19dSAlexandru Tachici priv->cfg = &adin1110_cfgs[dev_id->driver_data]; 1660bc93e19dSAlexandru Tachici spi->bits_per_word = 8; 1661bc93e19dSAlexandru Tachici spi->mode = SPI_MODE_0; 1662bc93e19dSAlexandru Tachici 1663bc93e19dSAlexandru Tachici mutex_init(&priv->lock); 1664bc93e19dSAlexandru Tachici spin_lock_init(&priv->state_lock); 1665bc93e19dSAlexandru Tachici 1666bc93e19dSAlexandru Tachici /* use of CRC on control and data transactions is pin dependent */ 1667bc93e19dSAlexandru Tachici priv->append_crc = device_property_read_bool(dev, "adi,spi-crc"); 1668bc93e19dSAlexandru Tachici if (priv->append_crc) 1669bc93e19dSAlexandru Tachici crc8_populate_msb(adin1110_crc_table, 0x7); 1670bc93e19dSAlexandru Tachici 1671bc93e19dSAlexandru Tachici ret = adin1110_check_spi(priv); 1672bc93e19dSAlexandru Tachici if (ret < 0) { 1673bc93e19dSAlexandru Tachici dev_err(dev, "Probe SPI Read check failed: %d\n", ret); 1674bc93e19dSAlexandru Tachici return ret; 1675bc93e19dSAlexandru Tachici } 1676bc93e19dSAlexandru Tachici 1677bc93e19dSAlexandru Tachici ret = adin1110_write_reg(priv, ADIN1110_RESET, ADIN1110_SWRESET); 1678bc93e19dSAlexandru Tachici if (ret < 0) 1679bc93e19dSAlexandru Tachici return ret; 1680bc93e19dSAlexandru Tachici 1681bc93e19dSAlexandru Tachici ret = adin1110_register_mdiobus(priv, dev); 1682bc93e19dSAlexandru Tachici if (ret < 0) { 1683bc93e19dSAlexandru Tachici dev_err(dev, "Could not register MDIO bus %d\n", ret); 1684bc93e19dSAlexandru Tachici return ret; 1685bc93e19dSAlexandru Tachici } 1686bc93e19dSAlexandru Tachici 1687bc93e19dSAlexandru Tachici return adin1110_probe_netdevs(priv); 1688bc93e19dSAlexandru Tachici } 1689bc93e19dSAlexandru Tachici 1690bc93e19dSAlexandru Tachici static const struct of_device_id adin1110_match_table[] = { 1691bc93e19dSAlexandru Tachici { .compatible = "adi,adin1110" }, 1692bc93e19dSAlexandru Tachici { .compatible = "adi,adin2111" }, 1693bc93e19dSAlexandru Tachici { } 1694bc93e19dSAlexandru Tachici }; 1695bc93e19dSAlexandru Tachici MODULE_DEVICE_TABLE(of, adin1110_match_table); 1696bc93e19dSAlexandru Tachici 1697bc93e19dSAlexandru Tachici static const struct spi_device_id adin1110_spi_id[] = { 1698bc93e19dSAlexandru Tachici { .name = "adin1110", .driver_data = ADIN1110_MAC }, 1699bc93e19dSAlexandru Tachici { .name = "adin2111", .driver_data = ADIN2111_MAC }, 1700bc93e19dSAlexandru Tachici { } 1701bc93e19dSAlexandru Tachici }; 1702bb65131bSYang Yingliang MODULE_DEVICE_TABLE(spi, adin1110_spi_id); 1703bc93e19dSAlexandru Tachici 1704bc93e19dSAlexandru Tachici static struct spi_driver adin1110_driver = { 1705bc93e19dSAlexandru Tachici .driver = { 1706bc93e19dSAlexandru Tachici .name = "adin1110", 1707bc93e19dSAlexandru Tachici .of_match_table = adin1110_match_table, 1708bc93e19dSAlexandru Tachici }, 1709bc93e19dSAlexandru Tachici .probe = adin1110_probe, 1710bc93e19dSAlexandru Tachici .id_table = adin1110_spi_id, 1711bc93e19dSAlexandru Tachici }; 1712bc93e19dSAlexandru Tachici module_spi_driver(adin1110_driver); 1713bc93e19dSAlexandru Tachici 1714bc93e19dSAlexandru Tachici MODULE_DESCRIPTION("ADIN1110 Network driver"); 1715bc93e19dSAlexandru Tachici MODULE_AUTHOR("Alexandru Tachici <alexandru.tachici@analog.com>"); 1716bc93e19dSAlexandru Tachici MODULE_LICENSE("Dual BSD/GPL"); 1717