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 
adin1110_crc_data(u8 * data,u32 len)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 
adin1110_read_reg(struct adin1110_priv * priv,u16 reg,u32 * val)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 
adin1110_write_reg(struct adin1110_priv * priv,u16 reg,u32 val)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 
adin1110_set_bits(struct adin1110_priv * priv,u16 reg,unsigned long mask,unsigned long val)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 
adin1110_round_len(int len)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 
adin1110_read_fifo(struct adin1110_port_priv * port_priv)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;
297*965f9b8cSDell Jin 	struct spi_transfer t = {0};
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))
3598a4f6d02SAlexandru Tachici 		rxb->offload_fwd_mark = port_priv->priv->forwarding;
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 
adin1110_write_fifo(struct adin1110_port_priv * port_priv,struct sk_buff * txb)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 
adin1110_read_mdio_acc(struct adin1110_priv * priv)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 
adin1110_mdio_read(struct mii_bus * bus,int phy_id,int reg)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 
adin1110_mdio_write(struct mii_bus * bus,int phy_id,int reg,u16 reg_val)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  */
adin1110_register_mdiobus(struct adin1110_priv * priv,struct device * dev)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",
51825fd0550SAmit Kumar Mahapatra 		 priv->cfg->name, spi_get_chipselect(priv->spidev, 0));
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 	snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
527bc93e19dSAlexandru Tachici 
528bc93e19dSAlexandru Tachici 	ret = devm_mdiobus_register(dev, mii_bus);
529bc93e19dSAlexandru Tachici 	if (ret)
530bc93e19dSAlexandru Tachici 		return ret;
531bc93e19dSAlexandru Tachici 
532bc93e19dSAlexandru Tachici 	priv->mii_bus = mii_bus;
533bc93e19dSAlexandru Tachici 
534bc93e19dSAlexandru Tachici 	return 0;
535bc93e19dSAlexandru Tachici }
536bc93e19dSAlexandru Tachici 
adin1110_port_rx_ready(struct adin1110_port_priv * port_priv,u32 status)537bc93e19dSAlexandru Tachici static bool adin1110_port_rx_ready(struct adin1110_port_priv *port_priv,
538bc93e19dSAlexandru Tachici 				   u32 status)
539bc93e19dSAlexandru Tachici {
540bc93e19dSAlexandru Tachici 	if (!netif_oper_up(port_priv->netdev))
541bc93e19dSAlexandru Tachici 		return false;
542bc93e19dSAlexandru Tachici 
543bc93e19dSAlexandru Tachici 	if (!port_priv->nr)
544bc93e19dSAlexandru Tachici 		return !!(status & ADIN1110_RX_RDY);
545bc93e19dSAlexandru Tachici 	else
546bc93e19dSAlexandru Tachici 		return !!(status & ADIN2111_P2_RX_RDY);
547bc93e19dSAlexandru Tachici }
548bc93e19dSAlexandru Tachici 
adin1110_read_frames(struct adin1110_port_priv * port_priv,unsigned int budget)549bc93e19dSAlexandru Tachici static void adin1110_read_frames(struct adin1110_port_priv *port_priv,
550bc93e19dSAlexandru Tachici 				 unsigned int budget)
551bc93e19dSAlexandru Tachici {
552bc93e19dSAlexandru Tachici 	struct adin1110_priv *priv = port_priv->priv;
553bc93e19dSAlexandru Tachici 	u32 status1;
554bc93e19dSAlexandru Tachici 	int ret;
555bc93e19dSAlexandru Tachici 
556bc93e19dSAlexandru Tachici 	while (budget) {
557bc93e19dSAlexandru Tachici 		ret = adin1110_read_reg(priv, ADIN1110_STATUS1, &status1);
558bc93e19dSAlexandru Tachici 		if (ret < 0)
559bc93e19dSAlexandru Tachici 			return;
560bc93e19dSAlexandru Tachici 
561bc93e19dSAlexandru Tachici 		if (!adin1110_port_rx_ready(port_priv, status1))
562bc93e19dSAlexandru Tachici 			break;
563bc93e19dSAlexandru Tachici 
564bc93e19dSAlexandru Tachici 		ret = adin1110_read_fifo(port_priv);
565bc93e19dSAlexandru Tachici 		if (ret < 0)
566bc93e19dSAlexandru Tachici 			return;
567bc93e19dSAlexandru Tachici 
568bc93e19dSAlexandru Tachici 		budget--;
569bc93e19dSAlexandru Tachici 	}
570bc93e19dSAlexandru Tachici }
571bc93e19dSAlexandru Tachici 
adin1110_wake_queues(struct adin1110_priv * priv)572bc93e19dSAlexandru Tachici static void adin1110_wake_queues(struct adin1110_priv *priv)
573bc93e19dSAlexandru Tachici {
574bc93e19dSAlexandru Tachici 	int i;
575bc93e19dSAlexandru Tachici 
576bc93e19dSAlexandru Tachici 	for (i = 0; i < priv->cfg->ports_nr; i++)
577bc93e19dSAlexandru Tachici 		netif_wake_queue(priv->ports[i]->netdev);
578bc93e19dSAlexandru Tachici }
579bc93e19dSAlexandru Tachici 
adin1110_irq(int irq,void * p)580bc93e19dSAlexandru Tachici static irqreturn_t adin1110_irq(int irq, void *p)
581bc93e19dSAlexandru Tachici {
582bc93e19dSAlexandru Tachici 	struct adin1110_priv *priv = p;
583bc93e19dSAlexandru Tachici 	u32 status1;
584bc93e19dSAlexandru Tachici 	u32 val;
585bc93e19dSAlexandru Tachici 	int ret;
586bc93e19dSAlexandru Tachici 	int i;
587bc93e19dSAlexandru Tachici 
588bc93e19dSAlexandru Tachici 	mutex_lock(&priv->lock);
589bc93e19dSAlexandru Tachici 
590bc93e19dSAlexandru Tachici 	ret = adin1110_read_reg(priv, ADIN1110_STATUS1, &status1);
591bc93e19dSAlexandru Tachici 	if (ret < 0)
592bc93e19dSAlexandru Tachici 		goto out;
593bc93e19dSAlexandru Tachici 
594bc93e19dSAlexandru Tachici 	if (priv->append_crc && (status1 & ADIN1110_SPI_ERR))
595bc93e19dSAlexandru Tachici 		dev_warn_ratelimited(&priv->spidev->dev,
596bc93e19dSAlexandru Tachici 				     "SPI CRC error on write.\n");
597bc93e19dSAlexandru Tachici 
598bc93e19dSAlexandru Tachici 	ret = adin1110_read_reg(priv, ADIN1110_TX_SPACE, &val);
599bc93e19dSAlexandru Tachici 	if (ret < 0)
600bc93e19dSAlexandru Tachici 		goto out;
601bc93e19dSAlexandru Tachici 
602bc93e19dSAlexandru Tachici 	/* TX FIFO space is expressed in half-words */
603bc93e19dSAlexandru Tachici 	priv->tx_space = 2 * val;
604bc93e19dSAlexandru Tachici 
605bc93e19dSAlexandru Tachici 	for (i = 0; i < priv->cfg->ports_nr; i++) {
606bc93e19dSAlexandru Tachici 		if (adin1110_port_rx_ready(priv->ports[i], status1))
607bc93e19dSAlexandru Tachici 			adin1110_read_frames(priv->ports[i],
608bc93e19dSAlexandru Tachici 					     ADIN1110_MAX_FRAMES_READ);
609bc93e19dSAlexandru Tachici 	}
610bc93e19dSAlexandru Tachici 
611bc93e19dSAlexandru Tachici 	/* clear IRQ sources */
612bc93e19dSAlexandru Tachici 	adin1110_write_reg(priv, ADIN1110_STATUS0, ADIN1110_CLEAR_STATUS0);
613bc93e19dSAlexandru Tachici 	adin1110_write_reg(priv, ADIN1110_STATUS1, priv->irq_mask);
614bc93e19dSAlexandru Tachici 
615bc93e19dSAlexandru Tachici out:
616bc93e19dSAlexandru Tachici 	mutex_unlock(&priv->lock);
617bc93e19dSAlexandru Tachici 
618bc93e19dSAlexandru Tachici 	if (priv->tx_space > 0 && ret >= 0)
619bc93e19dSAlexandru Tachici 		adin1110_wake_queues(priv);
620bc93e19dSAlexandru Tachici 
621bc93e19dSAlexandru Tachici 	return IRQ_HANDLED;
622bc93e19dSAlexandru Tachici }
623bc93e19dSAlexandru Tachici 
624bc93e19dSAlexandru Tachici /* ADIN1110 can filter up to 16 MAC addresses, mac_nr here is the slot used */
adin1110_write_mac_address(struct adin1110_port_priv * port_priv,int mac_nr,const u8 * addr,u8 * mask,u32 port_rules)625bc93e19dSAlexandru Tachici static int adin1110_write_mac_address(struct adin1110_port_priv *port_priv,
626bc93e19dSAlexandru Tachici 				      int mac_nr, const u8 *addr,
627bc93e19dSAlexandru Tachici 				      u8 *mask, u32 port_rules)
628bc93e19dSAlexandru Tachici {
629bc93e19dSAlexandru Tachici 	struct adin1110_priv *priv = port_priv->priv;
630bc93e19dSAlexandru Tachici 	u32 offset = mac_nr * 2;
631bc93e19dSAlexandru Tachici 	u32 port_rules_mask;
632bc93e19dSAlexandru Tachici 	int ret;
633bc93e19dSAlexandru Tachici 	u32 val;
634bc93e19dSAlexandru Tachici 
635bc93e19dSAlexandru Tachici 	if (!port_priv->nr)
636bc93e19dSAlexandru Tachici 		port_rules_mask = ADIN1110_MAC_ADDR_APPLY2PORT;
637bc93e19dSAlexandru Tachici 	else
638bc93e19dSAlexandru Tachici 		port_rules_mask = ADIN2111_MAC_ADDR_APPLY2PORT2;
639bc93e19dSAlexandru Tachici 
640bc93e19dSAlexandru Tachici 	if (port_rules & port_rules_mask)
641bc93e19dSAlexandru Tachici 		port_rules_mask |= ADIN1110_MAC_ADDR_TO_HOST | ADIN2111_MAC_ADDR_TO_OTHER_PORT;
642bc93e19dSAlexandru Tachici 
643bc93e19dSAlexandru Tachici 	port_rules_mask |= GENMASK(15, 0);
644bc93e19dSAlexandru Tachici 	val = port_rules | get_unaligned_be16(&addr[0]);
645bc93e19dSAlexandru Tachici 	ret = adin1110_set_bits(priv, ADIN1110_MAC_ADDR_FILTER_UPR + offset,
646bc93e19dSAlexandru Tachici 				port_rules_mask, val);
647bc93e19dSAlexandru Tachici 	if (ret < 0)
648bc93e19dSAlexandru Tachici 		return ret;
649bc93e19dSAlexandru Tachici 
650bc93e19dSAlexandru Tachici 	val = get_unaligned_be32(&addr[2]);
651bc93e19dSAlexandru Tachici 	ret =  adin1110_write_reg(priv,
652bc93e19dSAlexandru Tachici 				  ADIN1110_MAC_ADDR_FILTER_LWR + offset, val);
653bc93e19dSAlexandru Tachici 	if (ret < 0)
654bc93e19dSAlexandru Tachici 		return ret;
655bc93e19dSAlexandru Tachici 
656bc93e19dSAlexandru Tachici 	/* Only the first two MAC address slots support masking. */
657bc93e19dSAlexandru Tachici 	if (mac_nr < ADIN_MAC_P1_ADDR_SLOT) {
658bc93e19dSAlexandru Tachici 		val = get_unaligned_be16(&mask[0]);
659bc93e19dSAlexandru Tachici 		ret = adin1110_write_reg(priv,
660bc93e19dSAlexandru Tachici 					 ADIN1110_MAC_ADDR_MASK_UPR + offset,
661bc93e19dSAlexandru Tachici 					 val);
662bc93e19dSAlexandru Tachici 		if (ret < 0)
663bc93e19dSAlexandru Tachici 			return ret;
664bc93e19dSAlexandru Tachici 
665bc93e19dSAlexandru Tachici 		val = get_unaligned_be32(&mask[2]);
666bc93e19dSAlexandru Tachici 		return adin1110_write_reg(priv,
667bc93e19dSAlexandru Tachici 					  ADIN1110_MAC_ADDR_MASK_LWR + offset,
668bc93e19dSAlexandru Tachici 					  val);
669bc93e19dSAlexandru Tachici 	}
670bc93e19dSAlexandru Tachici 
671bc93e19dSAlexandru Tachici 	return 0;
672bc93e19dSAlexandru Tachici }
673bc93e19dSAlexandru Tachici 
adin1110_clear_mac_address(struct adin1110_priv * priv,int mac_nr)674bc93e19dSAlexandru Tachici static int adin1110_clear_mac_address(struct adin1110_priv *priv, int mac_nr)
675bc93e19dSAlexandru Tachici {
676bc93e19dSAlexandru Tachici 	u32 offset = mac_nr * 2;
677bc93e19dSAlexandru Tachici 	int ret;
678bc93e19dSAlexandru Tachici 
679bc93e19dSAlexandru Tachici 	ret = adin1110_write_reg(priv, ADIN1110_MAC_ADDR_FILTER_UPR + offset, 0);
680bc93e19dSAlexandru Tachici 	if (ret < 0)
681bc93e19dSAlexandru Tachici 		return ret;
682bc93e19dSAlexandru Tachici 
683bc93e19dSAlexandru Tachici 	ret =  adin1110_write_reg(priv, ADIN1110_MAC_ADDR_FILTER_LWR + offset, 0);
684bc93e19dSAlexandru Tachici 	if (ret < 0)
685bc93e19dSAlexandru Tachici 		return ret;
686bc93e19dSAlexandru Tachici 
687bc93e19dSAlexandru Tachici 	/* only the first two MAC address slots are maskable */
688bc93e19dSAlexandru Tachici 	if (mac_nr <= 1) {
689bc93e19dSAlexandru Tachici 		ret = adin1110_write_reg(priv, ADIN1110_MAC_ADDR_MASK_UPR + offset, 0);
690bc93e19dSAlexandru Tachici 		if (ret < 0)
691bc93e19dSAlexandru Tachici 			return ret;
692bc93e19dSAlexandru Tachici 
693bc93e19dSAlexandru Tachici 		ret = adin1110_write_reg(priv, ADIN1110_MAC_ADDR_MASK_LWR + offset, 0);
694bc93e19dSAlexandru Tachici 	}
695bc93e19dSAlexandru Tachici 
696bc93e19dSAlexandru Tachici 	return ret;
697bc93e19dSAlexandru Tachici }
698bc93e19dSAlexandru Tachici 
adin1110_port_rules(struct adin1110_port_priv * port_priv,bool fw_to_host,bool fw_to_other_port)699bc93e19dSAlexandru Tachici static u32 adin1110_port_rules(struct adin1110_port_priv *port_priv,
700bc93e19dSAlexandru Tachici 			       bool fw_to_host,
701bc93e19dSAlexandru Tachici 			       bool fw_to_other_port)
702bc93e19dSAlexandru Tachici {
703bc93e19dSAlexandru Tachici 	u32 port_rules = 0;
704bc93e19dSAlexandru Tachici 
705bc93e19dSAlexandru Tachici 	if (!port_priv->nr)
706bc93e19dSAlexandru Tachici 		port_rules |= ADIN1110_MAC_ADDR_APPLY2PORT;
707bc93e19dSAlexandru Tachici 	else
708bc93e19dSAlexandru Tachici 		port_rules |= ADIN2111_MAC_ADDR_APPLY2PORT2;
709bc93e19dSAlexandru Tachici 
710bc93e19dSAlexandru Tachici 	if (fw_to_host)
711bc93e19dSAlexandru Tachici 		port_rules |= ADIN1110_MAC_ADDR_TO_HOST;
712bc93e19dSAlexandru Tachici 
713bc93e19dSAlexandru Tachici 	if (fw_to_other_port && port_priv->priv->forwarding)
714bc93e19dSAlexandru Tachici 		port_rules |= ADIN2111_MAC_ADDR_TO_OTHER_PORT;
715bc93e19dSAlexandru Tachici 
716bc93e19dSAlexandru Tachici 	return port_rules;
717bc93e19dSAlexandru Tachici }
718bc93e19dSAlexandru Tachici 
adin1110_multicast_filter(struct adin1110_port_priv * port_priv,int mac_nr,bool accept_multicast)719bc93e19dSAlexandru Tachici static int adin1110_multicast_filter(struct adin1110_port_priv *port_priv,
720bc93e19dSAlexandru Tachici 				     int mac_nr, bool accept_multicast)
721bc93e19dSAlexandru Tachici {
722bc93e19dSAlexandru Tachici 	u8 mask[ETH_ALEN] = {0};
723bc93e19dSAlexandru Tachici 	u8 mac[ETH_ALEN] = {0};
724bc93e19dSAlexandru Tachici 	u32 port_rules = 0;
725bc93e19dSAlexandru Tachici 
726bc93e19dSAlexandru Tachici 	mask[0] = BIT(0);
727bc93e19dSAlexandru Tachici 	mac[0] = BIT(0);
728bc93e19dSAlexandru Tachici 
729bc93e19dSAlexandru Tachici 	if (accept_multicast && port_priv->state == BR_STATE_FORWARDING)
730bc93e19dSAlexandru Tachici 		port_rules = adin1110_port_rules(port_priv, true, true);
731bc93e19dSAlexandru Tachici 
732bc93e19dSAlexandru Tachici 	return adin1110_write_mac_address(port_priv, mac_nr, mac,
733bc93e19dSAlexandru Tachici 					  mask, port_rules);
734bc93e19dSAlexandru Tachici }
735bc93e19dSAlexandru Tachici 
adin1110_broadcasts_filter(struct adin1110_port_priv * port_priv,int mac_nr,bool accept_broadcast)736bc93e19dSAlexandru Tachici static int adin1110_broadcasts_filter(struct adin1110_port_priv *port_priv,
737bc93e19dSAlexandru Tachici 				      int mac_nr, bool accept_broadcast)
738bc93e19dSAlexandru Tachici {
739bc93e19dSAlexandru Tachici 	u32 port_rules = 0;
740bc93e19dSAlexandru Tachici 	u8 mask[ETH_ALEN];
741bc93e19dSAlexandru Tachici 
74254024dbeSYang Yingliang 	eth_broadcast_addr(mask);
743bc93e19dSAlexandru Tachici 
744bc93e19dSAlexandru Tachici 	if (accept_broadcast && port_priv->state == BR_STATE_FORWARDING)
745bc93e19dSAlexandru Tachici 		port_rules = adin1110_port_rules(port_priv, true, true);
746bc93e19dSAlexandru Tachici 
747bc93e19dSAlexandru Tachici 	return adin1110_write_mac_address(port_priv, mac_nr, mask,
748bc93e19dSAlexandru Tachici 					  mask, port_rules);
749bc93e19dSAlexandru Tachici }
750bc93e19dSAlexandru Tachici 
adin1110_set_mac_address(struct net_device * netdev,const unsigned char * dev_addr)751bc93e19dSAlexandru Tachici static int adin1110_set_mac_address(struct net_device *netdev,
752bc93e19dSAlexandru Tachici 				    const unsigned char *dev_addr)
753bc93e19dSAlexandru Tachici {
754bc93e19dSAlexandru Tachici 	struct adin1110_port_priv *port_priv = netdev_priv(netdev);
755bc93e19dSAlexandru Tachici 	u8 mask[ETH_ALEN];
756bc93e19dSAlexandru Tachici 	u32 port_rules;
757bc93e19dSAlexandru Tachici 	u32 mac_slot;
758bc93e19dSAlexandru Tachici 
759bc93e19dSAlexandru Tachici 	if (!is_valid_ether_addr(dev_addr))
760bc93e19dSAlexandru Tachici 		return -EADDRNOTAVAIL;
761bc93e19dSAlexandru Tachici 
762bc93e19dSAlexandru Tachici 	eth_hw_addr_set(netdev, dev_addr);
76354024dbeSYang Yingliang 	eth_broadcast_addr(mask);
764bc93e19dSAlexandru Tachici 
765bc93e19dSAlexandru Tachici 	mac_slot = (!port_priv->nr) ?  ADIN_MAC_P1_ADDR_SLOT : ADIN_MAC_P2_ADDR_SLOT;
766bc93e19dSAlexandru Tachici 	port_rules = adin1110_port_rules(port_priv, true, false);
767bc93e19dSAlexandru Tachici 
768bc93e19dSAlexandru Tachici 	return adin1110_write_mac_address(port_priv, mac_slot, netdev->dev_addr,
769bc93e19dSAlexandru Tachici 					  mask, port_rules);
770bc93e19dSAlexandru Tachici }
771bc93e19dSAlexandru Tachici 
adin1110_ndo_set_mac_address(struct net_device * netdev,void * addr)772bc93e19dSAlexandru Tachici static int adin1110_ndo_set_mac_address(struct net_device *netdev, void *addr)
773bc93e19dSAlexandru Tachici {
774bc93e19dSAlexandru Tachici 	struct sockaddr *sa = addr;
775bc93e19dSAlexandru Tachici 	int ret;
776bc93e19dSAlexandru Tachici 
777bc93e19dSAlexandru Tachici 	ret = eth_prepare_mac_addr_change(netdev, addr);
778bc93e19dSAlexandru Tachici 	if (ret < 0)
779bc93e19dSAlexandru Tachici 		return ret;
780bc93e19dSAlexandru Tachici 
781bc93e19dSAlexandru Tachici 	return adin1110_set_mac_address(netdev, sa->sa_data);
782bc93e19dSAlexandru Tachici }
783bc93e19dSAlexandru Tachici 
adin1110_ioctl(struct net_device * netdev,struct ifreq * rq,int cmd)784bc93e19dSAlexandru Tachici static int adin1110_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
785bc93e19dSAlexandru Tachici {
786bc93e19dSAlexandru Tachici 	if (!netif_running(netdev))
787bc93e19dSAlexandru Tachici 		return -EINVAL;
788bc93e19dSAlexandru Tachici 
789bc93e19dSAlexandru Tachici 	return phy_do_ioctl(netdev, rq, cmd);
790bc93e19dSAlexandru Tachici }
791bc93e19dSAlexandru Tachici 
adin1110_set_promisc_mode(struct adin1110_port_priv * port_priv,bool promisc)792bc93e19dSAlexandru Tachici static int adin1110_set_promisc_mode(struct adin1110_port_priv *port_priv,
793bc93e19dSAlexandru Tachici 				     bool promisc)
794bc93e19dSAlexandru Tachici {
795bc93e19dSAlexandru Tachici 	struct adin1110_priv *priv = port_priv->priv;
796bc93e19dSAlexandru Tachici 	u32 mask;
797bc93e19dSAlexandru Tachici 
798bc93e19dSAlexandru Tachici 	if (port_priv->state != BR_STATE_FORWARDING)
799bc93e19dSAlexandru Tachici 		promisc = false;
800bc93e19dSAlexandru Tachici 
801bc93e19dSAlexandru Tachici 	if (!port_priv->nr)
802bc93e19dSAlexandru Tachici 		mask = ADIN1110_FWD_UNK2HOST;
803bc93e19dSAlexandru Tachici 	else
804bc93e19dSAlexandru Tachici 		mask = ADIN2111_P2_FWD_UNK2HOST;
805bc93e19dSAlexandru Tachici 
806bc93e19dSAlexandru Tachici 	return adin1110_set_bits(priv, ADIN1110_CONFIG2,
807bc93e19dSAlexandru Tachici 				 mask, promisc ? mask : 0);
808bc93e19dSAlexandru Tachici }
809bc93e19dSAlexandru Tachici 
adin1110_setup_rx_mode(struct adin1110_port_priv * port_priv)810bc93e19dSAlexandru Tachici static int adin1110_setup_rx_mode(struct adin1110_port_priv *port_priv)
811bc93e19dSAlexandru Tachici {
812bc93e19dSAlexandru Tachici 	int ret;
813bc93e19dSAlexandru Tachici 
814bc93e19dSAlexandru Tachici 	ret = adin1110_set_promisc_mode(port_priv,
815bc93e19dSAlexandru Tachici 					!!(port_priv->flags & IFF_PROMISC));
816bc93e19dSAlexandru Tachici 	if (ret < 0)
817bc93e19dSAlexandru Tachici 		return ret;
818bc93e19dSAlexandru Tachici 
819bc93e19dSAlexandru Tachici 	ret = adin1110_multicast_filter(port_priv, ADIN_MAC_MULTICAST_ADDR_SLOT,
820bc93e19dSAlexandru Tachici 					!!(port_priv->flags & IFF_ALLMULTI));
821bc93e19dSAlexandru Tachici 	if (ret < 0)
822bc93e19dSAlexandru Tachici 		return ret;
823bc93e19dSAlexandru Tachici 
824bc93e19dSAlexandru Tachici 	ret = adin1110_broadcasts_filter(port_priv,
825bc93e19dSAlexandru Tachici 					 ADIN_MAC_BROADCAST_ADDR_SLOT,
826bc93e19dSAlexandru Tachici 					 !!(port_priv->flags & IFF_BROADCAST));
827bc93e19dSAlexandru Tachici 	if (ret < 0)
828bc93e19dSAlexandru Tachici 		return ret;
829bc93e19dSAlexandru Tachici 
830bc93e19dSAlexandru Tachici 	return adin1110_set_bits(port_priv->priv, ADIN1110_CONFIG1,
831bc93e19dSAlexandru Tachici 				 ADIN1110_CONFIG1_SYNC, ADIN1110_CONFIG1_SYNC);
832bc93e19dSAlexandru Tachici }
833bc93e19dSAlexandru Tachici 
adin1110_can_offload_forwarding(struct adin1110_priv * priv)834bc93e19dSAlexandru Tachici static bool adin1110_can_offload_forwarding(struct adin1110_priv *priv)
835bc93e19dSAlexandru Tachici {
836bc93e19dSAlexandru Tachici 	int i;
837bc93e19dSAlexandru Tachici 
838bc93e19dSAlexandru Tachici 	if (priv->cfg->id != ADIN2111_MAC)
839bc93e19dSAlexandru Tachici 		return false;
840bc93e19dSAlexandru Tachici 
841bc93e19dSAlexandru Tachici 	/* Can't enable forwarding if ports do not belong to the same bridge */
842bc93e19dSAlexandru Tachici 	if (priv->ports[0]->bridge != priv->ports[1]->bridge || !priv->ports[0]->bridge)
843bc93e19dSAlexandru Tachici 		return false;
844bc93e19dSAlexandru Tachici 
845bc93e19dSAlexandru Tachici 	/* Can't enable forwarding if there is a port
846bc93e19dSAlexandru Tachici 	 * that has been blocked by STP.
847bc93e19dSAlexandru Tachici 	 */
848bc93e19dSAlexandru Tachici 	for (i = 0; i < priv->cfg->ports_nr; i++) {
849bc93e19dSAlexandru Tachici 		if (priv->ports[i]->state != BR_STATE_FORWARDING)
850bc93e19dSAlexandru Tachici 			return false;
851bc93e19dSAlexandru Tachici 	}
852bc93e19dSAlexandru Tachici 
853bc93e19dSAlexandru Tachici 	return true;
854bc93e19dSAlexandru Tachici }
855bc93e19dSAlexandru Tachici 
adin1110_rx_mode_work(struct work_struct * work)856bc93e19dSAlexandru Tachici static void adin1110_rx_mode_work(struct work_struct *work)
857bc93e19dSAlexandru Tachici {
858bc93e19dSAlexandru Tachici 	struct adin1110_port_priv *port_priv;
859bc93e19dSAlexandru Tachici 	struct adin1110_priv *priv;
860bc93e19dSAlexandru Tachici 
861bc93e19dSAlexandru Tachici 	port_priv = container_of(work, struct adin1110_port_priv, rx_mode_work);
862bc93e19dSAlexandru Tachici 	priv = port_priv->priv;
863bc93e19dSAlexandru Tachici 
864bc93e19dSAlexandru Tachici 	mutex_lock(&priv->lock);
865bc93e19dSAlexandru Tachici 	adin1110_setup_rx_mode(port_priv);
866bc93e19dSAlexandru Tachici 	mutex_unlock(&priv->lock);
867bc93e19dSAlexandru Tachici }
868bc93e19dSAlexandru Tachici 
adin1110_set_rx_mode(struct net_device * dev)869bc93e19dSAlexandru Tachici static void adin1110_set_rx_mode(struct net_device *dev)
870bc93e19dSAlexandru Tachici {
871bc93e19dSAlexandru Tachici 	struct adin1110_port_priv *port_priv = netdev_priv(dev);
872bc93e19dSAlexandru Tachici 	struct adin1110_priv *priv = port_priv->priv;
873bc93e19dSAlexandru Tachici 
874bc93e19dSAlexandru Tachici 	spin_lock(&priv->state_lock);
875bc93e19dSAlexandru Tachici 
876bc93e19dSAlexandru Tachici 	port_priv->flags = dev->flags;
877bc93e19dSAlexandru Tachici 	schedule_work(&port_priv->rx_mode_work);
878bc93e19dSAlexandru Tachici 
879bc93e19dSAlexandru Tachici 	spin_unlock(&priv->state_lock);
880bc93e19dSAlexandru Tachici }
881bc93e19dSAlexandru Tachici 
adin1110_net_open(struct net_device * net_dev)882bc93e19dSAlexandru Tachici static int adin1110_net_open(struct net_device *net_dev)
883bc93e19dSAlexandru Tachici {
884bc93e19dSAlexandru Tachici 	struct adin1110_port_priv *port_priv = netdev_priv(net_dev);
885bc93e19dSAlexandru Tachici 	struct adin1110_priv *priv = port_priv->priv;
886bc93e19dSAlexandru Tachici 	u32 val;
887bc93e19dSAlexandru Tachici 	int ret;
888bc93e19dSAlexandru Tachici 
889bc93e19dSAlexandru Tachici 	mutex_lock(&priv->lock);
890bc93e19dSAlexandru Tachici 
891bc93e19dSAlexandru Tachici 	/* Configure MAC to compute and append the FCS itself. */
892bc93e19dSAlexandru Tachici 	ret = adin1110_write_reg(priv, ADIN1110_CONFIG2, ADIN1110_CRC_APPEND);
893bc93e19dSAlexandru Tachici 	if (ret < 0)
894bc93e19dSAlexandru Tachici 		goto out;
895bc93e19dSAlexandru Tachici 
896bc93e19dSAlexandru Tachici 	val = ADIN1110_TX_RDY_IRQ | ADIN1110_RX_RDY_IRQ | ADIN1110_SPI_ERR_IRQ;
897bc93e19dSAlexandru Tachici 	if (priv->cfg->id == ADIN2111_MAC)
898bc93e19dSAlexandru Tachici 		val |= ADIN2111_RX_RDY_IRQ;
899bc93e19dSAlexandru Tachici 
900bc93e19dSAlexandru Tachici 	priv->irq_mask = val;
901bc93e19dSAlexandru Tachici 	ret = adin1110_write_reg(priv, ADIN1110_IMASK1, ~val);
902bc93e19dSAlexandru Tachici 	if (ret < 0) {
903bc93e19dSAlexandru Tachici 		netdev_err(net_dev, "Failed to enable chip IRQs: %d\n", ret);
904bc93e19dSAlexandru Tachici 		goto out;
905bc93e19dSAlexandru Tachici 	}
906bc93e19dSAlexandru Tachici 
907bc93e19dSAlexandru Tachici 	ret = adin1110_read_reg(priv, ADIN1110_TX_SPACE, &val);
908bc93e19dSAlexandru Tachici 	if (ret < 0) {
909bc93e19dSAlexandru Tachici 		netdev_err(net_dev, "Failed to read TX FIFO space: %d\n", ret);
910bc93e19dSAlexandru Tachici 		goto out;
911bc93e19dSAlexandru Tachici 	}
912bc93e19dSAlexandru Tachici 
913bc93e19dSAlexandru Tachici 	priv->tx_space = 2 * val;
914bc93e19dSAlexandru Tachici 
915bc93e19dSAlexandru Tachici 	port_priv->state = BR_STATE_FORWARDING;
916bc93e19dSAlexandru Tachici 	ret = adin1110_set_mac_address(net_dev, net_dev->dev_addr);
917bc93e19dSAlexandru Tachici 	if (ret < 0) {
918bc93e19dSAlexandru Tachici 		netdev_err(net_dev, "Could not set MAC address: %pM, %d\n",
919bc93e19dSAlexandru Tachici 			   net_dev->dev_addr, ret);
920bc93e19dSAlexandru Tachici 		goto out;
921bc93e19dSAlexandru Tachici 	}
922bc93e19dSAlexandru Tachici 
923bc93e19dSAlexandru Tachici 	ret = adin1110_set_bits(priv, ADIN1110_CONFIG1, ADIN1110_CONFIG1_SYNC,
924bc93e19dSAlexandru Tachici 				ADIN1110_CONFIG1_SYNC);
925bc93e19dSAlexandru Tachici 
926bc93e19dSAlexandru Tachici out:
927bc93e19dSAlexandru Tachici 	mutex_unlock(&priv->lock);
928bc93e19dSAlexandru Tachici 
929bc93e19dSAlexandru Tachici 	if (ret < 0)
930bc93e19dSAlexandru Tachici 		return ret;
931bc93e19dSAlexandru Tachici 
932bc93e19dSAlexandru Tachici 	phy_start(port_priv->phydev);
933bc93e19dSAlexandru Tachici 
934bc93e19dSAlexandru Tachici 	netif_start_queue(net_dev);
935bc93e19dSAlexandru Tachici 
936bc93e19dSAlexandru Tachici 	return 0;
937bc93e19dSAlexandru Tachici }
938bc93e19dSAlexandru Tachici 
adin1110_net_stop(struct net_device * net_dev)939bc93e19dSAlexandru Tachici static int adin1110_net_stop(struct net_device *net_dev)
940bc93e19dSAlexandru Tachici {
941bc93e19dSAlexandru Tachici 	struct adin1110_port_priv *port_priv = netdev_priv(net_dev);
942bc93e19dSAlexandru Tachici 	struct adin1110_priv *priv = port_priv->priv;
943bc93e19dSAlexandru Tachici 	u32 mask;
944bc93e19dSAlexandru Tachici 	int ret;
945bc93e19dSAlexandru Tachici 
946bc93e19dSAlexandru Tachici 	mask = !port_priv->nr ? ADIN2111_RX_RDY_IRQ : ADIN1110_RX_RDY_IRQ;
947bc93e19dSAlexandru Tachici 
948bc93e19dSAlexandru Tachici 	/* Disable RX RDY IRQs */
949bc93e19dSAlexandru Tachici 	mutex_lock(&priv->lock);
950bc93e19dSAlexandru Tachici 	ret = adin1110_set_bits(priv, ADIN1110_IMASK1, mask, mask);
951bc93e19dSAlexandru Tachici 	mutex_unlock(&priv->lock);
952bc93e19dSAlexandru Tachici 	if (ret < 0)
953bc93e19dSAlexandru Tachici 		return ret;
954bc93e19dSAlexandru Tachici 
955bc93e19dSAlexandru Tachici 	netif_stop_queue(port_priv->netdev);
956bc93e19dSAlexandru Tachici 	flush_work(&port_priv->tx_work);
957bc93e19dSAlexandru Tachici 	phy_stop(port_priv->phydev);
958bc93e19dSAlexandru Tachici 
959bc93e19dSAlexandru Tachici 	return 0;
960bc93e19dSAlexandru Tachici }
961bc93e19dSAlexandru Tachici 
adin1110_tx_work(struct work_struct * work)962bc93e19dSAlexandru Tachici static void adin1110_tx_work(struct work_struct *work)
963bc93e19dSAlexandru Tachici {
964bc93e19dSAlexandru Tachici 	struct adin1110_port_priv *port_priv;
965bc93e19dSAlexandru Tachici 	struct adin1110_priv *priv;
966bc93e19dSAlexandru Tachici 	struct sk_buff *txb;
967bc93e19dSAlexandru Tachici 	int ret;
968bc93e19dSAlexandru Tachici 
969bc93e19dSAlexandru Tachici 	port_priv = container_of(work, struct adin1110_port_priv, tx_work);
970bc93e19dSAlexandru Tachici 	priv = port_priv->priv;
971bc93e19dSAlexandru Tachici 
972bc93e19dSAlexandru Tachici 	mutex_lock(&priv->lock);
973bc93e19dSAlexandru Tachici 
974bc93e19dSAlexandru Tachici 	while ((txb = skb_dequeue(&port_priv->txq))) {
975bc93e19dSAlexandru Tachici 		ret = adin1110_write_fifo(port_priv, txb);
976bc93e19dSAlexandru Tachici 		if (ret < 0)
977bc93e19dSAlexandru Tachici 			dev_err_ratelimited(&priv->spidev->dev,
978bc93e19dSAlexandru Tachici 					    "Frame write error: %d\n", ret);
979bc93e19dSAlexandru Tachici 
980bc93e19dSAlexandru Tachici 		dev_kfree_skb(txb);
981bc93e19dSAlexandru Tachici 	}
982bc93e19dSAlexandru Tachici 
983bc93e19dSAlexandru Tachici 	mutex_unlock(&priv->lock);
984bc93e19dSAlexandru Tachici }
985bc93e19dSAlexandru Tachici 
adin1110_start_xmit(struct sk_buff * skb,struct net_device * dev)986bc93e19dSAlexandru Tachici static netdev_tx_t adin1110_start_xmit(struct sk_buff *skb, struct net_device *dev)
987bc93e19dSAlexandru Tachici {
988bc93e19dSAlexandru Tachici 	struct adin1110_port_priv *port_priv = netdev_priv(dev);
989bc93e19dSAlexandru Tachici 	struct adin1110_priv *priv = port_priv->priv;
990bc93e19dSAlexandru Tachici 	netdev_tx_t netdev_ret = NETDEV_TX_OK;
991bc93e19dSAlexandru Tachici 	u32 tx_space_needed;
992bc93e19dSAlexandru Tachici 
993bc93e19dSAlexandru Tachici 	tx_space_needed = skb->len + ADIN1110_FRAME_HEADER_LEN + ADIN1110_INTERNAL_SIZE_HEADER_LEN;
994bc93e19dSAlexandru Tachici 	if (tx_space_needed > priv->tx_space) {
995bc93e19dSAlexandru Tachici 		netif_stop_queue(dev);
996bc93e19dSAlexandru Tachici 		netdev_ret = NETDEV_TX_BUSY;
997bc93e19dSAlexandru Tachici 	} else {
998bc93e19dSAlexandru Tachici 		priv->tx_space -= tx_space_needed;
999bc93e19dSAlexandru Tachici 		skb_queue_tail(&port_priv->txq, skb);
1000bc93e19dSAlexandru Tachici 	}
1001bc93e19dSAlexandru Tachici 
1002bc93e19dSAlexandru Tachici 	schedule_work(&port_priv->tx_work);
1003bc93e19dSAlexandru Tachici 
1004bc93e19dSAlexandru Tachici 	return netdev_ret;
1005bc93e19dSAlexandru Tachici }
1006bc93e19dSAlexandru Tachici 
adin1110_ndo_get_stats64(struct net_device * dev,struct rtnl_link_stats64 * storage)1007bc93e19dSAlexandru Tachici static void adin1110_ndo_get_stats64(struct net_device *dev,
1008bc93e19dSAlexandru Tachici 				     struct rtnl_link_stats64 *storage)
1009bc93e19dSAlexandru Tachici {
1010bc93e19dSAlexandru Tachici 	struct adin1110_port_priv *port_priv = netdev_priv(dev);
1011bc93e19dSAlexandru Tachici 
1012bc93e19dSAlexandru Tachici 	storage->rx_packets = port_priv->rx_packets;
1013bc93e19dSAlexandru Tachici 	storage->tx_packets = port_priv->tx_packets;
1014bc93e19dSAlexandru Tachici 
1015bc93e19dSAlexandru Tachici 	storage->rx_bytes = port_priv->rx_bytes;
1016bc93e19dSAlexandru Tachici 	storage->tx_bytes = port_priv->tx_bytes;
1017bc93e19dSAlexandru Tachici }
1018bc93e19dSAlexandru Tachici 
adin1110_port_get_port_parent_id(struct net_device * dev,struct netdev_phys_item_id * ppid)1019bc93e19dSAlexandru Tachici static int adin1110_port_get_port_parent_id(struct net_device *dev,
1020bc93e19dSAlexandru Tachici 					    struct netdev_phys_item_id *ppid)
1021bc93e19dSAlexandru Tachici {
1022bc93e19dSAlexandru Tachici 	struct adin1110_port_priv *port_priv = netdev_priv(dev);
1023bc93e19dSAlexandru Tachici 	struct adin1110_priv *priv = port_priv->priv;
1024bc93e19dSAlexandru Tachici 
10252b997747SAlexandru Tachici 	ppid->id_len = strnlen(priv->mii_bus_name, MAX_PHYS_ITEM_ID_LEN);
1026bc93e19dSAlexandru Tachici 	memcpy(ppid->id, priv->mii_bus_name, ppid->id_len);
1027bc93e19dSAlexandru Tachici 
1028bc93e19dSAlexandru Tachici 	return 0;
1029bc93e19dSAlexandru Tachici }
1030bc93e19dSAlexandru Tachici 
adin1110_ndo_get_phys_port_name(struct net_device * dev,char * name,size_t len)1031bc93e19dSAlexandru Tachici static int adin1110_ndo_get_phys_port_name(struct net_device *dev,
1032bc93e19dSAlexandru Tachici 					   char *name, size_t len)
1033bc93e19dSAlexandru Tachici {
1034bc93e19dSAlexandru Tachici 	struct adin1110_port_priv *port_priv = netdev_priv(dev);
1035bc93e19dSAlexandru Tachici 	int err;
1036bc93e19dSAlexandru Tachici 
1037bc93e19dSAlexandru Tachici 	err = snprintf(name, len, "p%d", port_priv->nr);
1038bc93e19dSAlexandru Tachici 	if (err >= len)
1039bc93e19dSAlexandru Tachici 		return -EINVAL;
1040bc93e19dSAlexandru Tachici 
1041bc93e19dSAlexandru Tachici 	return 0;
1042bc93e19dSAlexandru Tachici }
1043bc93e19dSAlexandru Tachici 
1044bc93e19dSAlexandru Tachici static const struct net_device_ops adin1110_netdev_ops = {
1045bc93e19dSAlexandru Tachici 	.ndo_open		= adin1110_net_open,
1046bc93e19dSAlexandru Tachici 	.ndo_stop		= adin1110_net_stop,
1047bc93e19dSAlexandru Tachici 	.ndo_eth_ioctl		= adin1110_ioctl,
1048bc93e19dSAlexandru Tachici 	.ndo_start_xmit		= adin1110_start_xmit,
1049bc93e19dSAlexandru Tachici 	.ndo_set_mac_address	= adin1110_ndo_set_mac_address,
1050bc93e19dSAlexandru Tachici 	.ndo_set_rx_mode	= adin1110_set_rx_mode,
1051bc93e19dSAlexandru Tachici 	.ndo_validate_addr	= eth_validate_addr,
1052bc93e19dSAlexandru Tachici 	.ndo_get_stats64	= adin1110_ndo_get_stats64,
1053bc93e19dSAlexandru Tachici 	.ndo_get_port_parent_id	= adin1110_port_get_port_parent_id,
1054bc93e19dSAlexandru Tachici 	.ndo_get_phys_port_name	= adin1110_ndo_get_phys_port_name,
1055bc93e19dSAlexandru Tachici };
1056bc93e19dSAlexandru Tachici 
adin1110_get_drvinfo(struct net_device * dev,struct ethtool_drvinfo * di)1057bc93e19dSAlexandru Tachici static void adin1110_get_drvinfo(struct net_device *dev,
1058bc93e19dSAlexandru Tachici 				 struct ethtool_drvinfo *di)
1059bc93e19dSAlexandru Tachici {
1060bc93e19dSAlexandru Tachici 	strscpy(di->driver, "ADIN1110", sizeof(di->driver));
1061bc93e19dSAlexandru Tachici 	strscpy(di->bus_info, dev_name(dev->dev.parent), sizeof(di->bus_info));
1062bc93e19dSAlexandru Tachici }
1063bc93e19dSAlexandru Tachici 
1064bc93e19dSAlexandru Tachici static const struct ethtool_ops adin1110_ethtool_ops = {
1065bc93e19dSAlexandru Tachici 	.get_drvinfo		= adin1110_get_drvinfo,
1066bc93e19dSAlexandru Tachici 	.get_link		= ethtool_op_get_link,
1067bc93e19dSAlexandru Tachici 	.get_link_ksettings	= phy_ethtool_get_link_ksettings,
1068bc93e19dSAlexandru Tachici 	.set_link_ksettings	= phy_ethtool_set_link_ksettings,
1069bc93e19dSAlexandru Tachici };
1070bc93e19dSAlexandru Tachici 
adin1110_adjust_link(struct net_device * dev)1071bc93e19dSAlexandru Tachici static void adin1110_adjust_link(struct net_device *dev)
1072bc93e19dSAlexandru Tachici {
1073bc93e19dSAlexandru Tachici 	struct phy_device *phydev = dev->phydev;
1074bc93e19dSAlexandru Tachici 
1075bc93e19dSAlexandru Tachici 	if (!phydev->link)
1076bc93e19dSAlexandru Tachici 		phy_print_status(phydev);
1077bc93e19dSAlexandru Tachici }
1078bc93e19dSAlexandru Tachici 
1079bc93e19dSAlexandru Tachici /* PHY ID is stored in the MAC registers too,
1080bc93e19dSAlexandru Tachici  * check spi connection by reading it.
1081bc93e19dSAlexandru Tachici  */
adin1110_check_spi(struct adin1110_priv * priv)1082bc93e19dSAlexandru Tachici static int adin1110_check_spi(struct adin1110_priv *priv)
1083bc93e19dSAlexandru Tachici {
108436934cacSAlexandru Tachici 	struct gpio_desc *reset_gpio;
1085bc93e19dSAlexandru Tachici 	int ret;
1086bc93e19dSAlexandru Tachici 	u32 val;
1087bc93e19dSAlexandru Tachici 
108836934cacSAlexandru Tachici 	reset_gpio = devm_gpiod_get_optional(&priv->spidev->dev, "reset",
108936934cacSAlexandru Tachici 					     GPIOD_OUT_LOW);
109036934cacSAlexandru Tachici 	if (reset_gpio) {
109136934cacSAlexandru Tachici 		/* MISO pin is used for internal configuration, can't have
109236934cacSAlexandru Tachici 		 * anyone else disturbing the SDO line.
109336934cacSAlexandru Tachici 		 */
109436934cacSAlexandru Tachici 		spi_bus_lock(priv->spidev->controller);
109536934cacSAlexandru Tachici 
109636934cacSAlexandru Tachici 		gpiod_set_value(reset_gpio, 1);
109736934cacSAlexandru Tachici 		fsleep(10000);
109836934cacSAlexandru Tachici 		gpiod_set_value(reset_gpio, 0);
109936934cacSAlexandru Tachici 
110036934cacSAlexandru Tachici 		/* Need to wait 90 ms before interacting with
110136934cacSAlexandru Tachici 		 * the MAC after a HW reset.
110236934cacSAlexandru Tachici 		 */
110336934cacSAlexandru Tachici 		fsleep(90000);
110436934cacSAlexandru Tachici 
110536934cacSAlexandru Tachici 		spi_bus_unlock(priv->spidev->controller);
110636934cacSAlexandru Tachici 	}
110736934cacSAlexandru Tachici 
1108bc93e19dSAlexandru Tachici 	ret = adin1110_read_reg(priv, ADIN1110_PHY_ID, &val);
1109bc93e19dSAlexandru Tachici 	if (ret < 0)
1110bc93e19dSAlexandru Tachici 		return ret;
1111bc93e19dSAlexandru Tachici 
1112bc93e19dSAlexandru Tachici 	if (val != priv->cfg->phy_id_val) {
1113bc93e19dSAlexandru Tachici 		dev_err(&priv->spidev->dev, "PHY ID expected: %x, read: %x\n",
1114bc93e19dSAlexandru Tachici 			priv->cfg->phy_id_val, val);
1115bc93e19dSAlexandru Tachici 		return -EIO;
1116bc93e19dSAlexandru Tachici 	}
1117bc93e19dSAlexandru Tachici 
1118bc93e19dSAlexandru Tachici 	return 0;
1119bc93e19dSAlexandru Tachici }
1120bc93e19dSAlexandru Tachici 
adin1110_hw_forwarding(struct adin1110_priv * priv,bool enable)1121bc93e19dSAlexandru Tachici static int adin1110_hw_forwarding(struct adin1110_priv *priv, bool enable)
1122bc93e19dSAlexandru Tachici {
1123bc93e19dSAlexandru Tachici 	int ret;
1124bc93e19dSAlexandru Tachici 	int i;
1125bc93e19dSAlexandru Tachici 
1126bc93e19dSAlexandru Tachici 	priv->forwarding = enable;
1127bc93e19dSAlexandru Tachici 
1128bc93e19dSAlexandru Tachici 	if (!priv->forwarding) {
1129bc93e19dSAlexandru Tachici 		for (i = ADIN_MAC_FDB_ADDR_SLOT; i < ADIN_MAC_MAX_ADDR_SLOTS; i++) {
1130bc93e19dSAlexandru Tachici 			ret = adin1110_clear_mac_address(priv, i);
1131bc93e19dSAlexandru Tachici 			if (ret < 0)
1132bc93e19dSAlexandru Tachici 				return ret;
1133bc93e19dSAlexandru Tachici 		}
1134bc93e19dSAlexandru Tachici 	}
1135bc93e19dSAlexandru Tachici 
1136bc93e19dSAlexandru Tachici 	/* Forwarding is optimised when MAC runs in Cut Through mode. */
1137bc93e19dSAlexandru Tachici 	ret = adin1110_set_bits(priv, ADIN1110_CONFIG2,
1138bc93e19dSAlexandru Tachici 				ADIN2111_PORT_CUT_THRU_EN,
1139bc93e19dSAlexandru Tachici 				priv->forwarding ? ADIN2111_PORT_CUT_THRU_EN : 0);
1140bc93e19dSAlexandru Tachici 	if (ret < 0)
1141bc93e19dSAlexandru Tachici 		return ret;
1142bc93e19dSAlexandru Tachici 
1143bc93e19dSAlexandru Tachici 	for (i = 0; i < priv->cfg->ports_nr; i++) {
1144bc93e19dSAlexandru Tachici 		ret = adin1110_setup_rx_mode(priv->ports[i]);
1145bc93e19dSAlexandru Tachici 		if (ret < 0)
1146bc93e19dSAlexandru Tachici 			return ret;
1147bc93e19dSAlexandru Tachici 	}
1148bc93e19dSAlexandru Tachici 
1149bc93e19dSAlexandru Tachici 	return ret;
1150bc93e19dSAlexandru Tachici }
1151bc93e19dSAlexandru Tachici 
adin1110_port_bridge_join(struct adin1110_port_priv * port_priv,struct net_device * bridge)1152bc93e19dSAlexandru Tachici static int adin1110_port_bridge_join(struct adin1110_port_priv *port_priv,
1153bc93e19dSAlexandru Tachici 				     struct net_device *bridge)
1154bc93e19dSAlexandru Tachici {
1155bc93e19dSAlexandru Tachici 	struct adin1110_priv *priv = port_priv->priv;
1156bc93e19dSAlexandru Tachici 	int ret;
1157bc93e19dSAlexandru Tachici 
1158bc93e19dSAlexandru Tachici 	port_priv->bridge = bridge;
1159bc93e19dSAlexandru Tachici 
1160bc93e19dSAlexandru Tachici 	if (adin1110_can_offload_forwarding(priv)) {
1161bc93e19dSAlexandru Tachici 		mutex_lock(&priv->lock);
1162bc93e19dSAlexandru Tachici 		ret = adin1110_hw_forwarding(priv, true);
1163bc93e19dSAlexandru Tachici 		mutex_unlock(&priv->lock);
1164bc93e19dSAlexandru Tachici 
1165bc93e19dSAlexandru Tachici 		if (ret < 0)
1166bc93e19dSAlexandru Tachici 			return ret;
1167bc93e19dSAlexandru Tachici 	}
1168bc93e19dSAlexandru Tachici 
1169bc93e19dSAlexandru Tachici 	return adin1110_set_mac_address(port_priv->netdev, bridge->dev_addr);
1170bc93e19dSAlexandru Tachici }
1171bc93e19dSAlexandru Tachici 
adin1110_port_bridge_leave(struct adin1110_port_priv * port_priv,struct net_device * bridge)1172bc93e19dSAlexandru Tachici static int adin1110_port_bridge_leave(struct adin1110_port_priv *port_priv,
1173bc93e19dSAlexandru Tachici 				      struct net_device *bridge)
1174bc93e19dSAlexandru Tachici {
1175bc93e19dSAlexandru Tachici 	struct adin1110_priv *priv = port_priv->priv;
1176bc93e19dSAlexandru Tachici 	int ret;
1177bc93e19dSAlexandru Tachici 
1178bc93e19dSAlexandru Tachici 	port_priv->bridge = NULL;
1179bc93e19dSAlexandru Tachici 
1180bc93e19dSAlexandru Tachici 	mutex_lock(&priv->lock);
1181bc93e19dSAlexandru Tachici 	ret = adin1110_hw_forwarding(priv, false);
1182bc93e19dSAlexandru Tachici 	mutex_unlock(&priv->lock);
1183bc93e19dSAlexandru Tachici 
1184bc93e19dSAlexandru Tachici 	return ret;
1185bc93e19dSAlexandru Tachici }
1186bc93e19dSAlexandru Tachici 
adin1110_port_dev_check(const struct net_device * dev)1187f9371935SAlexandru Tachici static bool adin1110_port_dev_check(const struct net_device *dev)
1188f9371935SAlexandru Tachici {
1189f9371935SAlexandru Tachici 	return dev->netdev_ops == &adin1110_netdev_ops;
1190f9371935SAlexandru Tachici }
1191f9371935SAlexandru Tachici 
adin1110_netdevice_event(struct notifier_block * unused,unsigned long event,void * ptr)1192bc93e19dSAlexandru Tachici static int adin1110_netdevice_event(struct notifier_block *unused,
1193bc93e19dSAlexandru Tachici 				    unsigned long event, void *ptr)
1194bc93e19dSAlexandru Tachici {
1195bc93e19dSAlexandru Tachici 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1196bc93e19dSAlexandru Tachici 	struct adin1110_port_priv *port_priv = netdev_priv(dev);
1197bc93e19dSAlexandru Tachici 	struct netdev_notifier_changeupper_info *info = ptr;
1198bc93e19dSAlexandru Tachici 	int ret = 0;
1199bc93e19dSAlexandru Tachici 
1200f9371935SAlexandru Tachici 	if (!adin1110_port_dev_check(dev))
1201f9371935SAlexandru Tachici 		return NOTIFY_DONE;
1202f9371935SAlexandru Tachici 
1203bc93e19dSAlexandru Tachici 	switch (event) {
1204bc93e19dSAlexandru Tachici 	case NETDEV_CHANGEUPPER:
1205bc93e19dSAlexandru Tachici 		if (netif_is_bridge_master(info->upper_dev)) {
1206bc93e19dSAlexandru Tachici 			if (info->linking)
1207bc93e19dSAlexandru Tachici 				ret = adin1110_port_bridge_join(port_priv, info->upper_dev);
1208bc93e19dSAlexandru Tachici 			else
1209bc93e19dSAlexandru Tachici 				ret = adin1110_port_bridge_leave(port_priv, info->upper_dev);
1210bc93e19dSAlexandru Tachici 		}
1211bc93e19dSAlexandru Tachici 		break;
1212bc93e19dSAlexandru Tachici 	default:
1213bc93e19dSAlexandru Tachici 		break;
1214bc93e19dSAlexandru Tachici 	}
1215bc93e19dSAlexandru Tachici 
1216bc93e19dSAlexandru Tachici 	return notifier_from_errno(ret);
1217bc93e19dSAlexandru Tachici }
1218bc93e19dSAlexandru Tachici 
1219bc93e19dSAlexandru Tachici static struct notifier_block adin1110_netdevice_nb = {
1220bc93e19dSAlexandru Tachici 	.notifier_call = adin1110_netdevice_event,
1221bc93e19dSAlexandru Tachici };
1222bc93e19dSAlexandru Tachici 
adin1110_disconnect_phy(void * data)1223bc93e19dSAlexandru Tachici static void adin1110_disconnect_phy(void *data)
1224bc93e19dSAlexandru Tachici {
1225bc93e19dSAlexandru Tachici 	phy_disconnect(data);
1226bc93e19dSAlexandru Tachici }
1227bc93e19dSAlexandru Tachici 
adin1110_port_set_forwarding_state(struct adin1110_port_priv * port_priv)1228bc93e19dSAlexandru Tachici static int adin1110_port_set_forwarding_state(struct adin1110_port_priv *port_priv)
1229bc93e19dSAlexandru Tachici {
1230bc93e19dSAlexandru Tachici 	struct adin1110_priv *priv = port_priv->priv;
1231bc93e19dSAlexandru Tachici 	int ret;
1232bc93e19dSAlexandru Tachici 
1233bc93e19dSAlexandru Tachici 	port_priv->state = BR_STATE_FORWARDING;
1234bc93e19dSAlexandru Tachici 
1235bc93e19dSAlexandru Tachici 	mutex_lock(&priv->lock);
1236bc93e19dSAlexandru Tachici 	ret = adin1110_set_mac_address(port_priv->netdev,
1237bc93e19dSAlexandru Tachici 				       port_priv->netdev->dev_addr);
1238bc93e19dSAlexandru Tachici 	if (ret < 0)
1239bc93e19dSAlexandru Tachici 		goto out;
1240bc93e19dSAlexandru Tachici 
1241bc93e19dSAlexandru Tachici 	if (adin1110_can_offload_forwarding(priv))
1242bc93e19dSAlexandru Tachici 		ret = adin1110_hw_forwarding(priv, true);
1243bc93e19dSAlexandru Tachici 	else
1244bc93e19dSAlexandru Tachici 		ret = adin1110_setup_rx_mode(port_priv);
1245bc93e19dSAlexandru Tachici out:
1246bc93e19dSAlexandru Tachici 	mutex_unlock(&priv->lock);
1247bc93e19dSAlexandru Tachici 
1248bc93e19dSAlexandru Tachici 	return ret;
1249bc93e19dSAlexandru Tachici }
1250bc93e19dSAlexandru Tachici 
adin1110_port_set_blocking_state(struct adin1110_port_priv * port_priv)1251bc93e19dSAlexandru Tachici static int adin1110_port_set_blocking_state(struct adin1110_port_priv *port_priv)
1252bc93e19dSAlexandru Tachici {
1253bc93e19dSAlexandru Tachici 	u8 mac[ETH_ALEN] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x00};
1254bc93e19dSAlexandru Tachici 	struct adin1110_priv *priv = port_priv->priv;
1255bc93e19dSAlexandru Tachici 	u8 mask[ETH_ALEN];
1256bc93e19dSAlexandru Tachici 	u32 port_rules;
1257bc93e19dSAlexandru Tachici 	int mac_slot;
1258bc93e19dSAlexandru Tachici 	int ret;
1259bc93e19dSAlexandru Tachici 
1260bc93e19dSAlexandru Tachici 	port_priv->state = BR_STATE_BLOCKING;
1261bc93e19dSAlexandru Tachici 
1262bc93e19dSAlexandru Tachici 	mutex_lock(&priv->lock);
1263bc93e19dSAlexandru Tachici 
1264bc93e19dSAlexandru Tachici 	mac_slot = (!port_priv->nr) ?  ADIN_MAC_P1_ADDR_SLOT : ADIN_MAC_P2_ADDR_SLOT;
1265bc93e19dSAlexandru Tachici 	ret = adin1110_clear_mac_address(priv, mac_slot);
1266bc93e19dSAlexandru Tachici 	if (ret < 0)
1267bc93e19dSAlexandru Tachici 		goto out;
1268bc93e19dSAlexandru Tachici 
1269bc93e19dSAlexandru Tachici 	ret = adin1110_hw_forwarding(priv, false);
1270bc93e19dSAlexandru Tachici 	if (ret < 0)
1271bc93e19dSAlexandru Tachici 		goto out;
1272bc93e19dSAlexandru Tachici 
1273bc93e19dSAlexandru Tachici 	/* Allow only BPDUs to be passed to the CPU */
127454024dbeSYang Yingliang 	eth_broadcast_addr(mask);
1275bc93e19dSAlexandru Tachici 	port_rules = adin1110_port_rules(port_priv, true, false);
1276bc93e19dSAlexandru Tachici 	ret = adin1110_write_mac_address(port_priv, mac_slot, mac,
1277bc93e19dSAlexandru Tachici 					 mask, port_rules);
1278bc93e19dSAlexandru Tachici out:
1279bc93e19dSAlexandru Tachici 	mutex_unlock(&priv->lock);
1280bc93e19dSAlexandru Tachici 
1281bc93e19dSAlexandru Tachici 	return ret;
1282bc93e19dSAlexandru Tachici }
1283bc93e19dSAlexandru Tachici 
1284bc93e19dSAlexandru Tachici /* ADIN1110/2111 does not have any native STP support.
1285bc93e19dSAlexandru Tachici  * Listen for bridge core state changes and
1286bc93e19dSAlexandru Tachici  * allow all frames to pass or only the BPDUs.
1287bc93e19dSAlexandru Tachici  */
adin1110_port_attr_stp_state_set(struct adin1110_port_priv * port_priv,u8 state)1288bc93e19dSAlexandru Tachici static int adin1110_port_attr_stp_state_set(struct adin1110_port_priv *port_priv,
1289bc93e19dSAlexandru Tachici 					    u8 state)
1290bc93e19dSAlexandru Tachici {
1291bc93e19dSAlexandru Tachici 	switch (state) {
1292bc93e19dSAlexandru Tachici 	case BR_STATE_FORWARDING:
1293bc93e19dSAlexandru Tachici 		return adin1110_port_set_forwarding_state(port_priv);
1294bc93e19dSAlexandru Tachici 	case BR_STATE_LEARNING:
1295bc93e19dSAlexandru Tachici 	case BR_STATE_LISTENING:
1296bc93e19dSAlexandru Tachici 	case BR_STATE_DISABLED:
1297bc93e19dSAlexandru Tachici 	case BR_STATE_BLOCKING:
1298bc93e19dSAlexandru Tachici 		return adin1110_port_set_blocking_state(port_priv);
1299bc93e19dSAlexandru Tachici 	default:
1300bc93e19dSAlexandru Tachici 		return -EINVAL;
1301bc93e19dSAlexandru Tachici 	}
1302bc93e19dSAlexandru Tachici }
1303bc93e19dSAlexandru Tachici 
adin1110_port_attr_set(struct net_device * dev,const void * ctx,const struct switchdev_attr * attr,struct netlink_ext_ack * extack)1304bc93e19dSAlexandru Tachici static int adin1110_port_attr_set(struct net_device *dev, const void *ctx,
1305bc93e19dSAlexandru Tachici 				  const struct switchdev_attr *attr,
1306bc93e19dSAlexandru Tachici 				  struct netlink_ext_ack *extack)
1307bc93e19dSAlexandru Tachici {
1308bc93e19dSAlexandru Tachici 	struct adin1110_port_priv *port_priv = netdev_priv(dev);
1309bc93e19dSAlexandru Tachici 
1310bc93e19dSAlexandru Tachici 	switch (attr->id) {
1311bc93e19dSAlexandru Tachici 	case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
1312bc93e19dSAlexandru Tachici 		return adin1110_port_attr_stp_state_set(port_priv,
1313bc93e19dSAlexandru Tachici 							attr->u.stp_state);
1314bc93e19dSAlexandru Tachici 	default:
1315bc93e19dSAlexandru Tachici 		return -EOPNOTSUPP;
1316bc93e19dSAlexandru Tachici 	}
1317bc93e19dSAlexandru Tachici }
1318bc93e19dSAlexandru Tachici 
adin1110_switchdev_blocking_event(struct notifier_block * unused,unsigned long event,void * ptr)1319bc93e19dSAlexandru Tachici static int adin1110_switchdev_blocking_event(struct notifier_block *unused,
1320bc93e19dSAlexandru Tachici 					     unsigned long event,
1321bc93e19dSAlexandru Tachici 					     void *ptr)
1322bc93e19dSAlexandru Tachici {
1323bc93e19dSAlexandru Tachici 	struct net_device *netdev = switchdev_notifier_info_to_dev(ptr);
1324bc93e19dSAlexandru Tachici 	int ret;
1325bc93e19dSAlexandru Tachici 
1326bc93e19dSAlexandru Tachici 	if (event == SWITCHDEV_PORT_ATTR_SET) {
1327bc93e19dSAlexandru Tachici 		ret = switchdev_handle_port_attr_set(netdev, ptr,
1328bc93e19dSAlexandru Tachici 						     adin1110_port_dev_check,
1329bc93e19dSAlexandru Tachici 						     adin1110_port_attr_set);
1330bc93e19dSAlexandru Tachici 
1331bc93e19dSAlexandru Tachici 		return notifier_from_errno(ret);
1332bc93e19dSAlexandru Tachici 	}
1333bc93e19dSAlexandru Tachici 
1334bc93e19dSAlexandru Tachici 	return NOTIFY_DONE;
1335bc93e19dSAlexandru Tachici }
1336bc93e19dSAlexandru Tachici 
1337bc93e19dSAlexandru Tachici static struct notifier_block adin1110_switchdev_blocking_notifier = {
1338bc93e19dSAlexandru Tachici 	.notifier_call = adin1110_switchdev_blocking_event,
1339bc93e19dSAlexandru Tachici };
1340bc93e19dSAlexandru Tachici 
adin1110_fdb_offload_notify(struct net_device * netdev,struct switchdev_notifier_fdb_info * rcv)1341bc93e19dSAlexandru Tachici static void adin1110_fdb_offload_notify(struct net_device *netdev,
1342bc93e19dSAlexandru Tachici 					struct switchdev_notifier_fdb_info *rcv)
1343bc93e19dSAlexandru Tachici {
1344bc93e19dSAlexandru Tachici 	struct switchdev_notifier_fdb_info info = {};
1345bc93e19dSAlexandru Tachici 
1346bc93e19dSAlexandru Tachici 	info.addr = rcv->addr;
1347bc93e19dSAlexandru Tachici 	info.vid = rcv->vid;
1348bc93e19dSAlexandru Tachici 	info.offloaded = true;
1349bc93e19dSAlexandru Tachici 	call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED,
1350bc93e19dSAlexandru Tachici 				 netdev, &info.info, NULL);
1351bc93e19dSAlexandru Tachici }
1352bc93e19dSAlexandru Tachici 
adin1110_fdb_add(struct adin1110_port_priv * port_priv,struct switchdev_notifier_fdb_info * fdb)1353bc93e19dSAlexandru Tachici static int adin1110_fdb_add(struct adin1110_port_priv *port_priv,
1354bc93e19dSAlexandru Tachici 			    struct switchdev_notifier_fdb_info *fdb)
1355bc93e19dSAlexandru Tachici {
1356bc93e19dSAlexandru Tachici 	struct adin1110_priv *priv = port_priv->priv;
1357bc93e19dSAlexandru Tachici 	struct adin1110_port_priv *other_port;
1358bc93e19dSAlexandru Tachici 	u8 mask[ETH_ALEN];
1359bc93e19dSAlexandru Tachici 	u32 port_rules;
1360bc93e19dSAlexandru Tachici 	int mac_nr;
1361bc93e19dSAlexandru Tachici 	u32 val;
1362bc93e19dSAlexandru Tachici 	int ret;
1363bc93e19dSAlexandru Tachici 
1364bc93e19dSAlexandru Tachici 	netdev_dbg(port_priv->netdev,
1365bc93e19dSAlexandru Tachici 		   "DEBUG: %s: MACID = %pM vid = %u flags = %u %u -- port %d\n",
1366bc93e19dSAlexandru Tachici 		    __func__, fdb->addr, fdb->vid, fdb->added_by_user,
1367bc93e19dSAlexandru Tachici 		    fdb->offloaded, port_priv->nr);
1368bc93e19dSAlexandru Tachici 
1369bc93e19dSAlexandru Tachici 	if (!priv->forwarding)
1370bc93e19dSAlexandru Tachici 		return 0;
1371bc93e19dSAlexandru Tachici 
1372bc93e19dSAlexandru Tachici 	if (fdb->is_local)
1373bc93e19dSAlexandru Tachici 		return -EINVAL;
1374bc93e19dSAlexandru Tachici 
1375bc93e19dSAlexandru Tachici 	/* Find free FDB slot on device. */
1376bc93e19dSAlexandru Tachici 	for (mac_nr = ADIN_MAC_FDB_ADDR_SLOT; mac_nr < ADIN_MAC_MAX_ADDR_SLOTS; mac_nr++) {
1377bc93e19dSAlexandru Tachici 		ret = adin1110_read_reg(priv, ADIN1110_MAC_ADDR_FILTER_UPR + (mac_nr * 2), &val);
1378bc93e19dSAlexandru Tachici 		if (ret < 0)
1379bc93e19dSAlexandru Tachici 			return ret;
1380bc93e19dSAlexandru Tachici 		if (!val)
1381bc93e19dSAlexandru Tachici 			break;
1382bc93e19dSAlexandru Tachici 	}
1383bc93e19dSAlexandru Tachici 
1384bc93e19dSAlexandru Tachici 	if (mac_nr == ADIN_MAC_MAX_ADDR_SLOTS)
1385bc93e19dSAlexandru Tachici 		return -ENOMEM;
1386bc93e19dSAlexandru Tachici 
1387bc93e19dSAlexandru Tachici 	other_port = priv->ports[!port_priv->nr];
138832530dbaSCiprian Regus 	port_rules = adin1110_port_rules(other_port, false, true);
138954024dbeSYang Yingliang 	eth_broadcast_addr(mask);
1390bc93e19dSAlexandru Tachici 
1391bc93e19dSAlexandru Tachici 	return adin1110_write_mac_address(other_port, mac_nr, (u8 *)fdb->addr,
1392bc93e19dSAlexandru Tachici 					  mask, port_rules);
1393bc93e19dSAlexandru Tachici }
1394bc93e19dSAlexandru Tachici 
adin1110_read_mac(struct adin1110_priv * priv,int mac_nr,u8 * addr)1395bc93e19dSAlexandru Tachici static int adin1110_read_mac(struct adin1110_priv *priv, int mac_nr, u8 *addr)
1396bc93e19dSAlexandru Tachici {
1397bc93e19dSAlexandru Tachici 	u32 val;
1398bc93e19dSAlexandru Tachici 	int ret;
1399bc93e19dSAlexandru Tachici 
1400bc93e19dSAlexandru Tachici 	ret = adin1110_read_reg(priv, ADIN1110_MAC_ADDR_FILTER_UPR + (mac_nr * 2), &val);
1401bc93e19dSAlexandru Tachici 	if (ret < 0)
1402bc93e19dSAlexandru Tachici 		return ret;
1403bc93e19dSAlexandru Tachici 
1404bc93e19dSAlexandru Tachici 	put_unaligned_be16(val, addr);
1405bc93e19dSAlexandru Tachici 
1406bc93e19dSAlexandru Tachici 	ret = adin1110_read_reg(priv, ADIN1110_MAC_ADDR_FILTER_LWR + (mac_nr * 2), &val);
1407bc93e19dSAlexandru Tachici 	if (ret < 0)
1408bc93e19dSAlexandru Tachici 		return ret;
1409bc93e19dSAlexandru Tachici 
1410bc93e19dSAlexandru Tachici 	put_unaligned_be32(val, addr + 2);
1411bc93e19dSAlexandru Tachici 
1412bc93e19dSAlexandru Tachici 	return 0;
1413bc93e19dSAlexandru Tachici }
1414bc93e19dSAlexandru Tachici 
adin1110_fdb_del(struct adin1110_port_priv * port_priv,struct switchdev_notifier_fdb_info * fdb)1415bc93e19dSAlexandru Tachici static int adin1110_fdb_del(struct adin1110_port_priv *port_priv,
1416bc93e19dSAlexandru Tachici 			    struct switchdev_notifier_fdb_info *fdb)
1417bc93e19dSAlexandru Tachici {
1418bc93e19dSAlexandru Tachici 	struct adin1110_priv *priv = port_priv->priv;
1419bc93e19dSAlexandru Tachici 	u8 addr[ETH_ALEN];
1420bc93e19dSAlexandru Tachici 	int mac_nr;
1421bc93e19dSAlexandru Tachici 	int ret;
1422bc93e19dSAlexandru Tachici 
1423bc93e19dSAlexandru Tachici 	netdev_dbg(port_priv->netdev,
1424bc93e19dSAlexandru Tachici 		   "DEBUG: %s: MACID = %pM vid = %u flags = %u %u -- port %d\n",
1425bc93e19dSAlexandru Tachici 		   __func__, fdb->addr, fdb->vid, fdb->added_by_user,
1426bc93e19dSAlexandru Tachici 		   fdb->offloaded, port_priv->nr);
1427bc93e19dSAlexandru Tachici 
1428bc93e19dSAlexandru Tachici 	if (fdb->is_local)
1429bc93e19dSAlexandru Tachici 		return -EINVAL;
1430bc93e19dSAlexandru Tachici 
1431bc93e19dSAlexandru Tachici 	for (mac_nr = ADIN_MAC_FDB_ADDR_SLOT; mac_nr < ADIN_MAC_MAX_ADDR_SLOTS; mac_nr++) {
1432bc93e19dSAlexandru Tachici 		ret = adin1110_read_mac(priv, mac_nr, addr);
1433bc93e19dSAlexandru Tachici 		if (ret < 0)
1434bc93e19dSAlexandru Tachici 			return ret;
1435bc93e19dSAlexandru Tachici 
1436bc93e19dSAlexandru Tachici 		if (ether_addr_equal(addr, fdb->addr)) {
1437bc93e19dSAlexandru Tachici 			ret = adin1110_clear_mac_address(priv, mac_nr);
1438bc93e19dSAlexandru Tachici 			if (ret < 0)
1439bc93e19dSAlexandru Tachici 				return ret;
1440bc93e19dSAlexandru Tachici 		}
1441bc93e19dSAlexandru Tachici 	}
1442bc93e19dSAlexandru Tachici 
1443bc93e19dSAlexandru Tachici 	return 0;
1444bc93e19dSAlexandru Tachici }
1445bc93e19dSAlexandru Tachici 
adin1110_switchdev_event_work(struct work_struct * work)1446bc93e19dSAlexandru Tachici static void adin1110_switchdev_event_work(struct work_struct *work)
1447bc93e19dSAlexandru Tachici {
1448bc93e19dSAlexandru Tachici 	struct adin1110_switchdev_event_work *switchdev_work;
1449bc93e19dSAlexandru Tachici 	struct adin1110_port_priv *port_priv;
1450bc93e19dSAlexandru Tachici 	int ret;
1451bc93e19dSAlexandru Tachici 
1452bc93e19dSAlexandru Tachici 	switchdev_work = container_of(work, struct adin1110_switchdev_event_work, work);
1453bc93e19dSAlexandru Tachici 	port_priv = switchdev_work->port_priv;
1454bc93e19dSAlexandru Tachici 
1455bc93e19dSAlexandru Tachici 	mutex_lock(&port_priv->priv->lock);
1456bc93e19dSAlexandru Tachici 
1457bc93e19dSAlexandru Tachici 	switch (switchdev_work->event) {
1458bc93e19dSAlexandru Tachici 	case SWITCHDEV_FDB_ADD_TO_DEVICE:
1459bc93e19dSAlexandru Tachici 		ret = adin1110_fdb_add(port_priv, &switchdev_work->fdb_info);
1460bc93e19dSAlexandru Tachici 		if (!ret)
1461bc93e19dSAlexandru Tachici 			adin1110_fdb_offload_notify(port_priv->netdev,
1462bc93e19dSAlexandru Tachici 						    &switchdev_work->fdb_info);
1463bc93e19dSAlexandru Tachici 		break;
1464bc93e19dSAlexandru Tachici 	case SWITCHDEV_FDB_DEL_TO_DEVICE:
1465bc93e19dSAlexandru Tachici 		adin1110_fdb_del(port_priv, &switchdev_work->fdb_info);
1466bc93e19dSAlexandru Tachici 		break;
1467bc93e19dSAlexandru Tachici 	default:
1468bc93e19dSAlexandru Tachici 		break;
1469bc93e19dSAlexandru Tachici 	}
1470bc93e19dSAlexandru Tachici 
1471bc93e19dSAlexandru Tachici 	mutex_unlock(&port_priv->priv->lock);
1472bc93e19dSAlexandru Tachici 
1473bc93e19dSAlexandru Tachici 	kfree(switchdev_work->fdb_info.addr);
1474bc93e19dSAlexandru Tachici 	kfree(switchdev_work);
1475bc93e19dSAlexandru Tachici 	dev_put(port_priv->netdev);
1476bc93e19dSAlexandru Tachici }
1477bc93e19dSAlexandru Tachici 
1478bc93e19dSAlexandru Tachici /* called under rcu_read_lock() */
adin1110_switchdev_event(struct notifier_block * unused,unsigned long event,void * ptr)1479bc93e19dSAlexandru Tachici static int adin1110_switchdev_event(struct notifier_block *unused,
1480bc93e19dSAlexandru Tachici 				    unsigned long event, void *ptr)
1481bc93e19dSAlexandru Tachici {
1482bc93e19dSAlexandru Tachici 	struct net_device *netdev = switchdev_notifier_info_to_dev(ptr);
1483bc93e19dSAlexandru Tachici 	struct adin1110_port_priv *port_priv = netdev_priv(netdev);
1484bc93e19dSAlexandru Tachici 	struct adin1110_switchdev_event_work *switchdev_work;
1485bc93e19dSAlexandru Tachici 	struct switchdev_notifier_fdb_info *fdb_info = ptr;
1486bc93e19dSAlexandru Tachici 
1487bc93e19dSAlexandru Tachici 	if (!adin1110_port_dev_check(netdev))
1488bc93e19dSAlexandru Tachici 		return NOTIFY_DONE;
1489bc93e19dSAlexandru Tachici 
1490bc93e19dSAlexandru Tachici 	switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
1491bc93e19dSAlexandru Tachici 	if (WARN_ON(!switchdev_work))
1492bc93e19dSAlexandru Tachici 		return NOTIFY_BAD;
1493bc93e19dSAlexandru Tachici 
1494bc93e19dSAlexandru Tachici 	INIT_WORK(&switchdev_work->work, adin1110_switchdev_event_work);
1495bc93e19dSAlexandru Tachici 	switchdev_work->port_priv = port_priv;
1496bc93e19dSAlexandru Tachici 	switchdev_work->event = event;
1497bc93e19dSAlexandru Tachici 
1498bc93e19dSAlexandru Tachici 	switch (event) {
1499bc93e19dSAlexandru Tachici 	case SWITCHDEV_FDB_ADD_TO_DEVICE:
1500bc93e19dSAlexandru Tachici 	case SWITCHDEV_FDB_DEL_TO_DEVICE:
1501bc93e19dSAlexandru Tachici 		memcpy(&switchdev_work->fdb_info, ptr,
1502bc93e19dSAlexandru Tachici 		       sizeof(switchdev_work->fdb_info));
1503bc93e19dSAlexandru Tachici 		switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
1504bc93e19dSAlexandru Tachici 
1505bc93e19dSAlexandru Tachici 		if (!switchdev_work->fdb_info.addr)
1506bc93e19dSAlexandru Tachici 			goto err_addr_alloc;
1507bc93e19dSAlexandru Tachici 
1508bc93e19dSAlexandru Tachici 		ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
1509bc93e19dSAlexandru Tachici 				fdb_info->addr);
1510bc93e19dSAlexandru Tachici 		dev_hold(netdev);
1511bc93e19dSAlexandru Tachici 		break;
1512bc93e19dSAlexandru Tachici 	default:
1513bc93e19dSAlexandru Tachici 		kfree(switchdev_work);
1514bc93e19dSAlexandru Tachici 		return NOTIFY_DONE;
1515bc93e19dSAlexandru Tachici 	}
1516bc93e19dSAlexandru Tachici 
1517bc93e19dSAlexandru Tachici 	queue_work(system_long_wq, &switchdev_work->work);
1518bc93e19dSAlexandru Tachici 
1519bc93e19dSAlexandru Tachici 	return NOTIFY_DONE;
1520bc93e19dSAlexandru Tachici 
1521bc93e19dSAlexandru Tachici err_addr_alloc:
1522bc93e19dSAlexandru Tachici 	kfree(switchdev_work);
1523bc93e19dSAlexandru Tachici 	return NOTIFY_BAD;
1524bc93e19dSAlexandru Tachici }
1525bc93e19dSAlexandru Tachici 
1526bc93e19dSAlexandru Tachici static struct notifier_block adin1110_switchdev_notifier = {
1527bc93e19dSAlexandru Tachici 	.notifier_call = adin1110_switchdev_event,
1528bc93e19dSAlexandru Tachici };
1529bc93e19dSAlexandru Tachici 
adin1110_unregister_notifiers(void)153021ce2c12SAlexandru Tachici static void adin1110_unregister_notifiers(void)
1531bc93e19dSAlexandru Tachici {
1532bc93e19dSAlexandru Tachici 	unregister_switchdev_blocking_notifier(&adin1110_switchdev_blocking_notifier);
1533bc93e19dSAlexandru Tachici 	unregister_switchdev_notifier(&adin1110_switchdev_notifier);
1534bc93e19dSAlexandru Tachici 	unregister_netdevice_notifier(&adin1110_netdevice_nb);
1535bc93e19dSAlexandru Tachici }
1536bc93e19dSAlexandru Tachici 
adin1110_setup_notifiers(void)153721ce2c12SAlexandru Tachici static int adin1110_setup_notifiers(void)
1538bc93e19dSAlexandru Tachici {
1539bc93e19dSAlexandru Tachici 	int ret;
1540bc93e19dSAlexandru Tachici 
1541bc93e19dSAlexandru Tachici 	ret = register_netdevice_notifier(&adin1110_netdevice_nb);
1542bc93e19dSAlexandru Tachici 	if (ret < 0)
1543bc93e19dSAlexandru Tachici 		return ret;
1544bc93e19dSAlexandru Tachici 
1545bc93e19dSAlexandru Tachici 	ret = register_switchdev_notifier(&adin1110_switchdev_notifier);
1546bc93e19dSAlexandru Tachici 	if (ret < 0)
1547bc93e19dSAlexandru Tachici 		goto err_netdev;
1548bc93e19dSAlexandru Tachici 
1549bc93e19dSAlexandru Tachici 	ret = register_switchdev_blocking_notifier(&adin1110_switchdev_blocking_notifier);
1550bc93e19dSAlexandru Tachici 	if (ret < 0)
1551bc93e19dSAlexandru Tachici 		goto err_sdev;
1552bc93e19dSAlexandru Tachici 
155321ce2c12SAlexandru Tachici 	return 0;
1554bc93e19dSAlexandru Tachici 
1555bc93e19dSAlexandru Tachici err_sdev:
1556bc93e19dSAlexandru Tachici 	unregister_switchdev_notifier(&adin1110_switchdev_notifier);
1557bc93e19dSAlexandru Tachici 
1558bc93e19dSAlexandru Tachici err_netdev:
1559bc93e19dSAlexandru Tachici 	unregister_netdevice_notifier(&adin1110_netdevice_nb);
156021ce2c12SAlexandru Tachici 
1561bc93e19dSAlexandru Tachici 	return ret;
1562bc93e19dSAlexandru Tachici }
1563bc93e19dSAlexandru Tachici 
adin1110_probe_netdevs(struct adin1110_priv * priv)1564bc93e19dSAlexandru Tachici static int adin1110_probe_netdevs(struct adin1110_priv *priv)
1565bc93e19dSAlexandru Tachici {
1566bc93e19dSAlexandru Tachici 	struct device *dev = &priv->spidev->dev;
1567bc93e19dSAlexandru Tachici 	struct adin1110_port_priv *port_priv;
1568bc93e19dSAlexandru Tachici 	struct net_device *netdev;
1569bc93e19dSAlexandru Tachici 	int ret;
1570bc93e19dSAlexandru Tachici 	int i;
1571bc93e19dSAlexandru Tachici 
1572bc93e19dSAlexandru Tachici 	for (i = 0; i < priv->cfg->ports_nr; i++) {
1573bc93e19dSAlexandru Tachici 		netdev = devm_alloc_etherdev(dev, sizeof(*port_priv));
1574bc93e19dSAlexandru Tachici 		if (!netdev)
1575bc93e19dSAlexandru Tachici 			return -ENOMEM;
1576bc93e19dSAlexandru Tachici 
1577bc93e19dSAlexandru Tachici 		port_priv = netdev_priv(netdev);
1578bc93e19dSAlexandru Tachici 		port_priv->netdev = netdev;
1579bc93e19dSAlexandru Tachici 		port_priv->priv = priv;
1580bc93e19dSAlexandru Tachici 		port_priv->cfg = priv->cfg;
1581bc93e19dSAlexandru Tachici 		port_priv->nr = i;
1582bc93e19dSAlexandru Tachici 		priv->ports[i] = port_priv;
1583bc93e19dSAlexandru Tachici 		SET_NETDEV_DEV(netdev, dev);
1584bc93e19dSAlexandru Tachici 
1585bc93e19dSAlexandru Tachici 		ret = device_get_ethdev_address(dev, netdev);
1586bc93e19dSAlexandru Tachici 		if (ret < 0)
1587bc93e19dSAlexandru Tachici 			return ret;
1588bc93e19dSAlexandru Tachici 
1589bc93e19dSAlexandru Tachici 		netdev->irq = priv->spidev->irq;
1590bc93e19dSAlexandru Tachici 		INIT_WORK(&port_priv->tx_work, adin1110_tx_work);
1591bc93e19dSAlexandru Tachici 		INIT_WORK(&port_priv->rx_mode_work, adin1110_rx_mode_work);
1592bc93e19dSAlexandru Tachici 		skb_queue_head_init(&port_priv->txq);
1593bc93e19dSAlexandru Tachici 
1594bc93e19dSAlexandru Tachici 		netif_carrier_off(netdev);
1595bc93e19dSAlexandru Tachici 
1596bc93e19dSAlexandru Tachici 		netdev->if_port = IF_PORT_10BASET;
1597bc93e19dSAlexandru Tachici 		netdev->netdev_ops = &adin1110_netdev_ops;
1598bc93e19dSAlexandru Tachici 		netdev->ethtool_ops = &adin1110_ethtool_ops;
1599bc93e19dSAlexandru Tachici 		netdev->priv_flags |= IFF_UNICAST_FLT;
1600bc93e19dSAlexandru Tachici 		netdev->features |= NETIF_F_NETNS_LOCAL;
1601bc93e19dSAlexandru Tachici 
1602bc93e19dSAlexandru Tachici 		port_priv->phydev = get_phy_device(priv->mii_bus, i + 1, false);
16039f1e3378SWei Yongjun 		if (IS_ERR(port_priv->phydev)) {
1604bc93e19dSAlexandru Tachici 			netdev_err(netdev, "Could not find PHY with device address: %d.\n", i);
16059f1e3378SWei Yongjun 			return PTR_ERR(port_priv->phydev);
1606bc93e19dSAlexandru Tachici 		}
1607bc93e19dSAlexandru Tachici 
1608bc93e19dSAlexandru Tachici 		port_priv->phydev = phy_connect(netdev,
1609bc93e19dSAlexandru Tachici 						phydev_name(port_priv->phydev),
1610bc93e19dSAlexandru Tachici 						adin1110_adjust_link,
1611bc93e19dSAlexandru Tachici 						PHY_INTERFACE_MODE_INTERNAL);
1612bc93e19dSAlexandru Tachici 		if (IS_ERR(port_priv->phydev)) {
1613bc93e19dSAlexandru Tachici 			netdev_err(netdev, "Could not connect PHY with device address: %d.\n", i);
1614bc93e19dSAlexandru Tachici 			return PTR_ERR(port_priv->phydev);
1615bc93e19dSAlexandru Tachici 		}
1616bc93e19dSAlexandru Tachici 
1617bc93e19dSAlexandru Tachici 		ret = devm_add_action_or_reset(dev, adin1110_disconnect_phy,
1618bc93e19dSAlexandru Tachici 					       port_priv->phydev);
1619bc93e19dSAlexandru Tachici 		if (ret < 0)
1620bc93e19dSAlexandru Tachici 			return ret;
1621bc93e19dSAlexandru Tachici 	}
1622bc93e19dSAlexandru Tachici 
1623bc93e19dSAlexandru Tachici 	/* ADIN1110 INT_N pin will be used to signal the host */
1624bc93e19dSAlexandru Tachici 	ret = devm_request_threaded_irq(dev, priv->spidev->irq, NULL,
1625bc93e19dSAlexandru Tachici 					adin1110_irq,
1626bc93e19dSAlexandru Tachici 					IRQF_TRIGGER_LOW | IRQF_ONESHOT,
1627bc93e19dSAlexandru Tachici 					dev_name(dev), priv);
1628bc93e19dSAlexandru Tachici 	if (ret < 0)
1629bc93e19dSAlexandru Tachici 		return ret;
1630bc93e19dSAlexandru Tachici 
1631bc93e19dSAlexandru Tachici 	for (i = 0; i < priv->cfg->ports_nr; i++) {
1632bc93e19dSAlexandru Tachici 		ret = devm_register_netdev(dev, priv->ports[i]->netdev);
1633bc93e19dSAlexandru Tachici 		if (ret < 0) {
1634bc93e19dSAlexandru Tachici 			dev_err(dev, "Failed to register network device.\n");
1635bc93e19dSAlexandru Tachici 			return ret;
1636bc93e19dSAlexandru Tachici 		}
1637bc93e19dSAlexandru Tachici 	}
1638bc93e19dSAlexandru Tachici 
1639bc93e19dSAlexandru Tachici 	return 0;
1640bc93e19dSAlexandru Tachici }
1641bc93e19dSAlexandru Tachici 
adin1110_probe(struct spi_device * spi)1642bc93e19dSAlexandru Tachici static int adin1110_probe(struct spi_device *spi)
1643bc93e19dSAlexandru Tachici {
1644bc93e19dSAlexandru Tachici 	const struct spi_device_id *dev_id = spi_get_device_id(spi);
1645bc93e19dSAlexandru Tachici 	struct device *dev = &spi->dev;
1646bc93e19dSAlexandru Tachici 	struct adin1110_priv *priv;
1647bc93e19dSAlexandru Tachici 	int ret;
1648bc93e19dSAlexandru Tachici 
1649bc93e19dSAlexandru Tachici 	priv = devm_kzalloc(dev, sizeof(struct adin1110_priv), GFP_KERNEL);
1650bc93e19dSAlexandru Tachici 	if (!priv)
1651bc93e19dSAlexandru Tachici 		return -ENOMEM;
1652bc93e19dSAlexandru Tachici 
1653bc93e19dSAlexandru Tachici 	priv->spidev = spi;
1654bc93e19dSAlexandru Tachici 	priv->cfg = &adin1110_cfgs[dev_id->driver_data];
1655bc93e19dSAlexandru Tachici 	spi->bits_per_word = 8;
1656bc93e19dSAlexandru Tachici 	spi->mode = SPI_MODE_0;
1657bc93e19dSAlexandru Tachici 
1658bc93e19dSAlexandru Tachici 	mutex_init(&priv->lock);
1659bc93e19dSAlexandru Tachici 	spin_lock_init(&priv->state_lock);
1660bc93e19dSAlexandru Tachici 
1661bc93e19dSAlexandru Tachici 	/* use of CRC on control and data transactions is pin dependent */
1662bc93e19dSAlexandru Tachici 	priv->append_crc = device_property_read_bool(dev, "adi,spi-crc");
1663bc93e19dSAlexandru Tachici 	if (priv->append_crc)
1664bc93e19dSAlexandru Tachici 		crc8_populate_msb(adin1110_crc_table, 0x7);
1665bc93e19dSAlexandru Tachici 
1666bc93e19dSAlexandru Tachici 	ret = adin1110_check_spi(priv);
1667bc93e19dSAlexandru Tachici 	if (ret < 0) {
1668bc93e19dSAlexandru Tachici 		dev_err(dev, "Probe SPI Read check failed: %d\n", ret);
1669bc93e19dSAlexandru Tachici 		return ret;
1670bc93e19dSAlexandru Tachici 	}
1671bc93e19dSAlexandru Tachici 
1672bc93e19dSAlexandru Tachici 	ret = adin1110_write_reg(priv, ADIN1110_RESET, ADIN1110_SWRESET);
1673bc93e19dSAlexandru Tachici 	if (ret < 0)
1674bc93e19dSAlexandru Tachici 		return ret;
1675bc93e19dSAlexandru Tachici 
1676bc93e19dSAlexandru Tachici 	ret = adin1110_register_mdiobus(priv, dev);
1677bc93e19dSAlexandru Tachici 	if (ret < 0) {
1678bc93e19dSAlexandru Tachici 		dev_err(dev, "Could not register MDIO bus %d\n", ret);
1679bc93e19dSAlexandru Tachici 		return ret;
1680bc93e19dSAlexandru Tachici 	}
1681bc93e19dSAlexandru Tachici 
1682bc93e19dSAlexandru Tachici 	return adin1110_probe_netdevs(priv);
1683bc93e19dSAlexandru Tachici }
1684bc93e19dSAlexandru Tachici 
1685bc93e19dSAlexandru Tachici static const struct of_device_id adin1110_match_table[] = {
1686bc93e19dSAlexandru Tachici 	{ .compatible = "adi,adin1110" },
1687bc93e19dSAlexandru Tachici 	{ .compatible = "adi,adin2111" },
1688bc93e19dSAlexandru Tachici 	{ }
1689bc93e19dSAlexandru Tachici };
1690bc93e19dSAlexandru Tachici MODULE_DEVICE_TABLE(of, adin1110_match_table);
1691bc93e19dSAlexandru Tachici 
1692bc93e19dSAlexandru Tachici static const struct spi_device_id adin1110_spi_id[] = {
1693bc93e19dSAlexandru Tachici 	{ .name = "adin1110", .driver_data = ADIN1110_MAC },
1694bc93e19dSAlexandru Tachici 	{ .name = "adin2111", .driver_data = ADIN2111_MAC },
1695bc93e19dSAlexandru Tachici 	{ }
1696bc93e19dSAlexandru Tachici };
1697bb65131bSYang Yingliang MODULE_DEVICE_TABLE(spi, adin1110_spi_id);
1698bc93e19dSAlexandru Tachici 
1699bc93e19dSAlexandru Tachici static struct spi_driver adin1110_driver = {
1700bc93e19dSAlexandru Tachici 	.driver = {
1701bc93e19dSAlexandru Tachici 		.name = "adin1110",
1702bc93e19dSAlexandru Tachici 		.of_match_table = adin1110_match_table,
1703bc93e19dSAlexandru Tachici 	},
1704bc93e19dSAlexandru Tachici 	.probe = adin1110_probe,
1705bc93e19dSAlexandru Tachici 	.id_table = adin1110_spi_id,
1706bc93e19dSAlexandru Tachici };
170721ce2c12SAlexandru Tachici 
adin1110_driver_init(void)170821ce2c12SAlexandru Tachici static int __init adin1110_driver_init(void)
170921ce2c12SAlexandru Tachici {
171021ce2c12SAlexandru Tachici 	int ret;
171121ce2c12SAlexandru Tachici 
171221ce2c12SAlexandru Tachici 	ret = adin1110_setup_notifiers();
171321ce2c12SAlexandru Tachici 	if (ret < 0)
171421ce2c12SAlexandru Tachici 		return ret;
171521ce2c12SAlexandru Tachici 
171621ce2c12SAlexandru Tachici 	ret = spi_register_driver(&adin1110_driver);
171721ce2c12SAlexandru Tachici 	if (ret < 0) {
171821ce2c12SAlexandru Tachici 		adin1110_unregister_notifiers();
171921ce2c12SAlexandru Tachici 		return ret;
172021ce2c12SAlexandru Tachici 	}
172121ce2c12SAlexandru Tachici 
172221ce2c12SAlexandru Tachici 	return 0;
172321ce2c12SAlexandru Tachici }
172421ce2c12SAlexandru Tachici 
adin1110_exit(void)172521ce2c12SAlexandru Tachici static void __exit adin1110_exit(void)
172621ce2c12SAlexandru Tachici {
172721ce2c12SAlexandru Tachici 	adin1110_unregister_notifiers();
172821ce2c12SAlexandru Tachici 	spi_unregister_driver(&adin1110_driver);
172921ce2c12SAlexandru Tachici }
173021ce2c12SAlexandru Tachici module_init(adin1110_driver_init);
173121ce2c12SAlexandru Tachici module_exit(adin1110_exit);
1732bc93e19dSAlexandru Tachici 
1733bc93e19dSAlexandru Tachici MODULE_DESCRIPTION("ADIN1110 Network driver");
1734bc93e19dSAlexandru Tachici MODULE_AUTHOR("Alexandru Tachici <alexandru.tachici@analog.com>");
1735bc93e19dSAlexandru Tachici MODULE_LICENSE("Dual BSD/GPL");
1736