1*20e69e67SCiprian Regus // SPDX-License-Identifier: GPL-2.0+
2*20e69e67SCiprian Regus /*
3*20e69e67SCiprian Regus * Driver for Analog Devices, Inc. ADIN1140 10BASE-T1S MAC-PHY
4*20e69e67SCiprian Regus *
5*20e69e67SCiprian Regus * Copyright 2026 Analog Devices Inc.
6*20e69e67SCiprian Regus */
7*20e69e67SCiprian Regus
8*20e69e67SCiprian Regus #include <linux/cleanup.h>
9*20e69e67SCiprian Regus #include <linux/etherdevice.h>
10*20e69e67SCiprian Regus #include <linux/kernel.h>
11*20e69e67SCiprian Regus #include <linux/mdio.h>
12*20e69e67SCiprian Regus #include <linux/module.h>
13*20e69e67SCiprian Regus #include <linux/oa_tc6.h>
14*20e69e67SCiprian Regus #include <linux/phy.h>
15*20e69e67SCiprian Regus
16*20e69e67SCiprian Regus #define ADIN1140_MAC_CONFIG2_STAT_CLR_RD BIT(6)
17*20e69e67SCiprian Regus #define ADIN1140_MAC_CONFIG2_FWD_UNK2HOST BIT(2)
18*20e69e67SCiprian Regus
19*20e69e67SCiprian Regus #define ADIN1140_MAC_P1_LOOP_ADDR_REG 0xC4
20*20e69e67SCiprian Regus
21*20e69e67SCiprian Regus #define ADIN1140_MAC_ADDR_FILT_UPR_REG 0x50
22*20e69e67SCiprian Regus #define ADIN1140_MAC_ADDR_FILT_APPLY2PORT1 BIT(30)
23*20e69e67SCiprian Regus #define ADIN1140_MAC_ADDR_FILT_TO_HOST BIT(16)
24*20e69e67SCiprian Regus
25*20e69e67SCiprian Regus #define ADIN1140_MAC_ADDR_FILT_LWR_REG 0x51
26*20e69e67SCiprian Regus
27*20e69e67SCiprian Regus #define ADIN1140_MAC_ADDR_MASK_UPR_REG 0x70
28*20e69e67SCiprian Regus #define ADIN1140_MAC_ADDR_MASK_LWR_REG 0x71
29*20e69e67SCiprian Regus
30*20e69e67SCiprian Regus #define ADIN1140_MAC_FILT_MC_SLOT 0U
31*20e69e67SCiprian Regus #define ADIN1140_MAC_FILT_BC_SLOT 1U
32*20e69e67SCiprian Regus #define ADIN1140_MAC_FILT_UC_SLOT 2U
33*20e69e67SCiprian Regus #define ADIN1140_MAC_FILT_MAX_SLOT 16U
34*20e69e67SCiprian Regus #define ADIN1140_MAC_FILT_MASK_LIMIT 2U
35*20e69e67SCiprian Regus
36*20e69e67SCiprian Regus #define ADIN1140_MAC_RX_FRAME_CNT 0xA1
37*20e69e67SCiprian Regus #define ADIN1140_MAC_RX_BC_FRAME_CNT 0xA2
38*20e69e67SCiprian Regus #define ADIN1140_MAC_RX_MC_FRAME_CNT 0xA3
39*20e69e67SCiprian Regus #define ADIN1140_MAC_RX_CRC_ERR_CNT 0xA5
40*20e69e67SCiprian Regus #define ADIN1140_MAC_RX_ALIGN_ERR_CNT 0xA6
41*20e69e67SCiprian Regus #define ADIN1140_MAC_RX_PREAMBLE_ERR_CNT 0xA7
42*20e69e67SCiprian Regus #define ADIN1140_MAC_RX_SHORT_ERR_CNT 0xA8
43*20e69e67SCiprian Regus #define ADIN1140_MAC_RX_LONG_ERR_CNT 0xA9
44*20e69e67SCiprian Regus #define ADIN1140_MAC_RX_PHY_ERR_CNT 0xAA
45*20e69e67SCiprian Regus #define ADIN1140_MAC_RX_DRP_FULL_CNT 0xAB
46*20e69e67SCiprian Regus #define ADIN1140_MAC_RX_DRP_FILTER_CNT 0xAD
47*20e69e67SCiprian Regus #define ADIN1140_MAC_RX_IFG_ERR_CNT 0xAE
48*20e69e67SCiprian Regus #define ADIN1140_MAC_TX_FRAME_CNT 0xB1
49*20e69e67SCiprian Regus #define ADIN1140_MAC_TX_BC_FRAME_CNT 0xB2
50*20e69e67SCiprian Regus #define ADIN1140_MAC_TX_MC_FRAME_CNT 0xB3
51*20e69e67SCiprian Regus #define ADIN1140_MAC_TX_SINGLE_COL_CNT 0xB5
52*20e69e67SCiprian Regus #define ADIN1140_MAC_TX_MULTI_COL_CNT 0xB6
53*20e69e67SCiprian Regus #define ADIN1140_MAC_TX_DEFERRED_CNT 0xB7
54*20e69e67SCiprian Regus #define ADIN1140_MAC_TX_LATE_COL_CNT 0xB8
55*20e69e67SCiprian Regus #define ADIN1140_MAC_TX_EXCESS_COL_CNT 0xB9
56*20e69e67SCiprian Regus #define ADIN1140_MAC_TX_UNDERRUN_CNT 0xBA
57*20e69e67SCiprian Regus
58*20e69e67SCiprian Regus /* ADIN1140_MAC_FILT_MAX_SLOT - 3 (multicast, broadcast and unicast
59*20e69e67SCiprian Regus * reserved slots)
60*20e69e67SCiprian Regus */
61*20e69e67SCiprian Regus #define ADIN1140_MAC_FILT_AVAIL 13U
62*20e69e67SCiprian Regus
63*20e69e67SCiprian Regus #define ADIN1140_PHY_CTRL_DEFAULT 0x1000
64*20e69e67SCiprian Regus #define ADIN1140_PHY_STATUS_DEFAULT 0x082D
65*20e69e67SCiprian Regus #define ADIN1140_PHY_ID1 0x0283
66*20e69e67SCiprian Regus #define ADIN1140_PHY_ID2 0xBE00
67*20e69e67SCiprian Regus
68*20e69e67SCiprian Regus #define ADIN1140_STATS_CHECK_DELAY (3 * HZ)
69*20e69e67SCiprian Regus
70*20e69e67SCiprian Regus enum adin1140_statistics_entry {
71*20e69e67SCiprian Regus rx_frames,
72*20e69e67SCiprian Regus rx_bc_frames,
73*20e69e67SCiprian Regus rx_mc_frames,
74*20e69e67SCiprian Regus rx_crc_errors,
75*20e69e67SCiprian Regus rx_align_errors,
76*20e69e67SCiprian Regus rx_preamble_errors,
77*20e69e67SCiprian Regus rx_short_frame_errors,
78*20e69e67SCiprian Regus rx_long_frame_errors,
79*20e69e67SCiprian Regus rx_phy_errors,
80*20e69e67SCiprian Regus rx_fifo_full_dropped,
81*20e69e67SCiprian Regus rx_addr_filter_dropped,
82*20e69e67SCiprian Regus rx_ifg_errors,
83*20e69e67SCiprian Regus tx_frames,
84*20e69e67SCiprian Regus tx_bc_frames,
85*20e69e67SCiprian Regus tx_mc_frames,
86*20e69e67SCiprian Regus tx_single_collision,
87*20e69e67SCiprian Regus tx_multi_collision,
88*20e69e67SCiprian Regus tx_deferred,
89*20e69e67SCiprian Regus tx_late_collision,
90*20e69e67SCiprian Regus tx_excess_collision,
91*20e69e67SCiprian Regus tx_underrun,
92*20e69e67SCiprian Regus ADIN1140_STATS_CNT,
93*20e69e67SCiprian Regus };
94*20e69e67SCiprian Regus
95*20e69e67SCiprian Regus struct adin1140_statistics_reg {
96*20e69e67SCiprian Regus const char *name;
97*20e69e67SCiprian Regus enum adin1140_statistics_entry idx;
98*20e69e67SCiprian Regus };
99*20e69e67SCiprian Regus
100*20e69e67SCiprian Regus struct adin1140_priv {
101*20e69e67SCiprian Regus struct net_device *netdev;
102*20e69e67SCiprian Regus struct oa_tc6 *tc6;
103*20e69e67SCiprian Regus struct mii_bus *mdiobus;
104*20e69e67SCiprian Regus struct phy_device *phydev;
105*20e69e67SCiprian Regus struct delayed_work stats_work;
106*20e69e67SCiprian Regus
107*20e69e67SCiprian Regus /* Protects stats[] from concurrent updates in adin1140_stats_work
108*20e69e67SCiprian Regus * and reads in the get_stats functions
109*20e69e67SCiprian Regus */
110*20e69e67SCiprian Regus spinlock_t stat_lock;
111*20e69e67SCiprian Regus u64 stats[ADIN1140_STATS_CNT];
112*20e69e67SCiprian Regus };
113*20e69e67SCiprian Regus
114*20e69e67SCiprian Regus static const u32 adin1140_stat_regs[] = {
115*20e69e67SCiprian Regus [rx_frames] = ADIN1140_MAC_RX_FRAME_CNT,
116*20e69e67SCiprian Regus [rx_bc_frames] = ADIN1140_MAC_RX_BC_FRAME_CNT,
117*20e69e67SCiprian Regus [rx_mc_frames] = ADIN1140_MAC_RX_MC_FRAME_CNT,
118*20e69e67SCiprian Regus [rx_crc_errors] = ADIN1140_MAC_RX_CRC_ERR_CNT,
119*20e69e67SCiprian Regus [rx_align_errors] = ADIN1140_MAC_RX_ALIGN_ERR_CNT,
120*20e69e67SCiprian Regus [rx_preamble_errors] = ADIN1140_MAC_RX_PREAMBLE_ERR_CNT,
121*20e69e67SCiprian Regus [rx_short_frame_errors] = ADIN1140_MAC_RX_SHORT_ERR_CNT,
122*20e69e67SCiprian Regus [rx_long_frame_errors] = ADIN1140_MAC_RX_LONG_ERR_CNT,
123*20e69e67SCiprian Regus [rx_phy_errors] = ADIN1140_MAC_RX_PHY_ERR_CNT,
124*20e69e67SCiprian Regus [rx_fifo_full_dropped] = ADIN1140_MAC_RX_DRP_FULL_CNT,
125*20e69e67SCiprian Regus [rx_addr_filter_dropped] = ADIN1140_MAC_RX_DRP_FILTER_CNT,
126*20e69e67SCiprian Regus [rx_ifg_errors] = ADIN1140_MAC_RX_IFG_ERR_CNT,
127*20e69e67SCiprian Regus [tx_frames] = ADIN1140_MAC_TX_FRAME_CNT,
128*20e69e67SCiprian Regus [tx_bc_frames] = ADIN1140_MAC_TX_BC_FRAME_CNT,
129*20e69e67SCiprian Regus [tx_mc_frames] = ADIN1140_MAC_TX_MC_FRAME_CNT,
130*20e69e67SCiprian Regus [tx_single_collision] = ADIN1140_MAC_TX_SINGLE_COL_CNT,
131*20e69e67SCiprian Regus [tx_multi_collision] = ADIN1140_MAC_TX_MULTI_COL_CNT,
132*20e69e67SCiprian Regus [tx_deferred] = ADIN1140_MAC_TX_DEFERRED_CNT,
133*20e69e67SCiprian Regus [tx_late_collision] = ADIN1140_MAC_TX_LATE_COL_CNT,
134*20e69e67SCiprian Regus [tx_excess_collision] = ADIN1140_MAC_TX_EXCESS_COL_CNT,
135*20e69e67SCiprian Regus [tx_underrun] = ADIN1140_MAC_TX_UNDERRUN_CNT,
136*20e69e67SCiprian Regus };
137*20e69e67SCiprian Regus
138*20e69e67SCiprian Regus static const struct adin1140_statistics_reg adin1140_stats[] = {
139*20e69e67SCiprian Regus {.name = "rx_preamble_errors", .idx = rx_preamble_errors},
140*20e69e67SCiprian Regus {.name = "rx_ifg_errors", .idx = rx_ifg_errors},
141*20e69e67SCiprian Regus };
142*20e69e67SCiprian Regus
adin1140_mac_filter_set(struct adin1140_priv * priv,const u8 * addr,const u8 * mask,u8 slot)143*20e69e67SCiprian Regus static int adin1140_mac_filter_set(struct adin1140_priv *priv,
144*20e69e67SCiprian Regus const u8 *addr, const u8 *mask,
145*20e69e67SCiprian Regus u8 slot)
146*20e69e67SCiprian Regus {
147*20e69e67SCiprian Regus u32 reg_address;
148*20e69e67SCiprian Regus u32 val;
149*20e69e67SCiprian Regus int ret;
150*20e69e67SCiprian Regus
151*20e69e67SCiprian Regus if (slot >= ADIN1140_MAC_FILT_MAX_SLOT)
152*20e69e67SCiprian Regus return -ENOSPC;
153*20e69e67SCiprian Regus
154*20e69e67SCiprian Regus reg_address = ADIN1140_MAC_ADDR_FILT_UPR_REG + 2 * slot;
155*20e69e67SCiprian Regus
156*20e69e67SCiprian Regus ret = oa_tc6_write_register_mms(priv->tc6, OA_TC6_MAC_MMS1,
157*20e69e67SCiprian Regus reg_address,
158*20e69e67SCiprian Regus get_unaligned_be16(&addr[0]) |
159*20e69e67SCiprian Regus ADIN1140_MAC_ADDR_FILT_APPLY2PORT1 |
160*20e69e67SCiprian Regus ADIN1140_MAC_ADDR_FILT_TO_HOST);
161*20e69e67SCiprian Regus if (ret)
162*20e69e67SCiprian Regus return ret;
163*20e69e67SCiprian Regus
164*20e69e67SCiprian Regus reg_address = ADIN1140_MAC_ADDR_FILT_LWR_REG + 2 * slot;
165*20e69e67SCiprian Regus ret = oa_tc6_write_register_mms(priv->tc6, OA_TC6_MAC_MMS1,
166*20e69e67SCiprian Regus reg_address,
167*20e69e67SCiprian Regus get_unaligned_be32(&addr[2]));
168*20e69e67SCiprian Regus if (ret)
169*20e69e67SCiprian Regus return ret;
170*20e69e67SCiprian Regus
171*20e69e67SCiprian Regus /* Only the first 2 destination MAC filter slots support masking.
172*20e69e67SCiprian Regus * For the other entries, the destination address in the received
173*20e69e67SCiprian Regus * frame must match exactly.
174*20e69e67SCiprian Regus */
175*20e69e67SCiprian Regus if (slot >= ADIN1140_MAC_FILT_MASK_LIMIT)
176*20e69e67SCiprian Regus return 0;
177*20e69e67SCiprian Regus
178*20e69e67SCiprian Regus val = get_unaligned_be16(&mask[0]);
179*20e69e67SCiprian Regus reg_address = ADIN1140_MAC_ADDR_MASK_UPR_REG + (2 * slot);
180*20e69e67SCiprian Regus
181*20e69e67SCiprian Regus ret = oa_tc6_write_register_mms(priv->tc6, OA_TC6_MAC_MMS1,
182*20e69e67SCiprian Regus reg_address, val);
183*20e69e67SCiprian Regus if (ret)
184*20e69e67SCiprian Regus return ret;
185*20e69e67SCiprian Regus
186*20e69e67SCiprian Regus val = get_unaligned_be32(&mask[2]);
187*20e69e67SCiprian Regus reg_address = ADIN1140_MAC_ADDR_MASK_LWR_REG + (2 * slot);
188*20e69e67SCiprian Regus
189*20e69e67SCiprian Regus return oa_tc6_write_register_mms(priv->tc6, OA_TC6_MAC_MMS1,
190*20e69e67SCiprian Regus reg_address, val);
191*20e69e67SCiprian Regus }
192*20e69e67SCiprian Regus
adin1140_mac_filter_clear(struct adin1140_priv * priv,u8 slot)193*20e69e67SCiprian Regus static int adin1140_mac_filter_clear(struct adin1140_priv *priv, u8 slot)
194*20e69e67SCiprian Regus {
195*20e69e67SCiprian Regus u8 mask[ETH_ALEN];
196*20e69e67SCiprian Regus u8 addr[ETH_ALEN];
197*20e69e67SCiprian Regus
198*20e69e67SCiprian Regus memset(mask, 0xFF, ETH_ALEN);
199*20e69e67SCiprian Regus memset(addr, 0x0, ETH_ALEN);
200*20e69e67SCiprian Regus
201*20e69e67SCiprian Regus return adin1140_mac_filter_set(priv, addr, mask, slot);
202*20e69e67SCiprian Regus }
203*20e69e67SCiprian Regus
adin1140_filter_unicast(struct adin1140_priv * priv)204*20e69e67SCiprian Regus static int adin1140_filter_unicast(struct adin1140_priv *priv)
205*20e69e67SCiprian Regus {
206*20e69e67SCiprian Regus /* Only the first 2 filter slots support masking, so no unicast
207*20e69e67SCiprian Regus * address will ever need a mask. The first slots are used for the
208*20e69e67SCiprian Regus * all multicast and broadcast filter.
209*20e69e67SCiprian Regus */
210*20e69e67SCiprian Regus return adin1140_mac_filter_set(priv, priv->netdev->dev_addr, NULL,
211*20e69e67SCiprian Regus ADIN1140_MAC_FILT_UC_SLOT);
212*20e69e67SCiprian Regus }
213*20e69e67SCiprian Regus
adin1140_filter_all_multicast(struct adin1140_priv * priv,bool en)214*20e69e67SCiprian Regus static int adin1140_filter_all_multicast(struct adin1140_priv *priv, bool en)
215*20e69e67SCiprian Regus {
216*20e69e67SCiprian Regus u8 multicast_addr[ETH_ALEN] = {1, 0, 0, 0, 0, 0};
217*20e69e67SCiprian Regus
218*20e69e67SCiprian Regus if (en)
219*20e69e67SCiprian Regus return adin1140_mac_filter_set(priv, multicast_addr,
220*20e69e67SCiprian Regus multicast_addr,
221*20e69e67SCiprian Regus ADIN1140_MAC_FILT_MC_SLOT);
222*20e69e67SCiprian Regus
223*20e69e67SCiprian Regus return adin1140_mac_filter_clear(priv, ADIN1140_MAC_FILT_MC_SLOT);
224*20e69e67SCiprian Regus }
225*20e69e67SCiprian Regus
adin1140_filter_broadcast(struct adin1140_priv * priv,bool enabled)226*20e69e67SCiprian Regus static int adin1140_filter_broadcast(struct adin1140_priv *priv, bool enabled)
227*20e69e67SCiprian Regus {
228*20e69e67SCiprian Regus u8 mask[ETH_ALEN];
229*20e69e67SCiprian Regus
230*20e69e67SCiprian Regus if (enabled) {
231*20e69e67SCiprian Regus memset(mask, 0xFF, ETH_ALEN);
232*20e69e67SCiprian Regus return adin1140_mac_filter_set(priv, mask, mask,
233*20e69e67SCiprian Regus ADIN1140_MAC_FILT_BC_SLOT);
234*20e69e67SCiprian Regus }
235*20e69e67SCiprian Regus
236*20e69e67SCiprian Regus return adin1140_mac_filter_clear(priv, ADIN1140_MAC_FILT_BC_SLOT);
237*20e69e67SCiprian Regus }
238*20e69e67SCiprian Regus
adin1140_default_filter_config(struct adin1140_priv * priv)239*20e69e67SCiprian Regus static int adin1140_default_filter_config(struct adin1140_priv *priv)
240*20e69e67SCiprian Regus {
241*20e69e67SCiprian Regus int ret;
242*20e69e67SCiprian Regus
243*20e69e67SCiprian Regus ret = adin1140_filter_broadcast(priv, true);
244*20e69e67SCiprian Regus if (ret)
245*20e69e67SCiprian Regus return ret;
246*20e69e67SCiprian Regus
247*20e69e67SCiprian Regus return adin1140_filter_unicast(priv);
248*20e69e67SCiprian Regus }
249*20e69e67SCiprian Regus
adin1140_promiscuous_mode(struct adin1140_priv * priv,bool enabled)250*20e69e67SCiprian Regus static int adin1140_promiscuous_mode(struct adin1140_priv *priv, bool enabled)
251*20e69e67SCiprian Regus {
252*20e69e67SCiprian Regus int ret;
253*20e69e67SCiprian Regus u32 val;
254*20e69e67SCiprian Regus
255*20e69e67SCiprian Regus ret = oa_tc6_read_register(priv->tc6, OA_TC6_REG_CONFIG2, &val);
256*20e69e67SCiprian Regus if (ret)
257*20e69e67SCiprian Regus return ret;
258*20e69e67SCiprian Regus
259*20e69e67SCiprian Regus if (enabled)
260*20e69e67SCiprian Regus val |= ADIN1140_MAC_CONFIG2_FWD_UNK2HOST;
261*20e69e67SCiprian Regus else
262*20e69e67SCiprian Regus val &= ~ADIN1140_MAC_CONFIG2_FWD_UNK2HOST;
263*20e69e67SCiprian Regus
264*20e69e67SCiprian Regus return oa_tc6_write_register(priv->tc6, OA_TC6_REG_CONFIG2, val);
265*20e69e67SCiprian Regus }
266*20e69e67SCiprian Regus
adin1140_rx_mode(struct net_device * dev,struct netdev_hw_addr_list * uc,struct netdev_hw_addr_list * mc)267*20e69e67SCiprian Regus static int adin1140_rx_mode(struct net_device *dev,
268*20e69e67SCiprian Regus struct netdev_hw_addr_list *uc,
269*20e69e67SCiprian Regus struct netdev_hw_addr_list *mc)
270*20e69e67SCiprian Regus {
271*20e69e67SCiprian Regus struct adin1140_priv *priv = netdev_priv(dev);
272*20e69e67SCiprian Regus struct netdev_hw_addr *ha;
273*20e69e67SCiprian Regus bool all_multi, promisc;
274*20e69e67SCiprian Regus u32 mac_addrs;
275*20e69e67SCiprian Regus u8 slot, i;
276*20e69e67SCiprian Regus int ret;
277*20e69e67SCiprian Regus
278*20e69e67SCiprian Regus /* The ADIN1140 has 16 dest MAC address filter slots:
279*20e69e67SCiprian Regus * 0 - reserved for all multicast filter.
280*20e69e67SCiprian Regus * 1 - reserved for broadcast filter.
281*20e69e67SCiprian Regus * 2 - reserved for the device's own unicast MAC.
282*20e69e67SCiprian Regus * 3 -> 15 - available for other unicast/multicast filters.
283*20e69e67SCiprian Regus */
284*20e69e67SCiprian Regus
285*20e69e67SCiprian Regus mac_addrs = netdev_hw_addr_list_count(uc);
286*20e69e67SCiprian Regus all_multi = false;
287*20e69e67SCiprian Regus promisc = false;
288*20e69e67SCiprian Regus
289*20e69e67SCiprian Regus if (priv->netdev->flags & IFF_PROMISC)
290*20e69e67SCiprian Regus promisc = true;
291*20e69e67SCiprian Regus else if (priv->netdev->flags & IFF_ALLMULTI)
292*20e69e67SCiprian Regus all_multi = true;
293*20e69e67SCiprian Regus else
294*20e69e67SCiprian Regus mac_addrs += netdev_hw_addr_list_count(mc);
295*20e69e67SCiprian Regus
296*20e69e67SCiprian Regus if (mac_addrs > ADIN1140_MAC_FILT_AVAIL) {
297*20e69e67SCiprian Regus /* The filter table is full. Enable promisc mode. */
298*20e69e67SCiprian Regus promisc = true;
299*20e69e67SCiprian Regus }
300*20e69e67SCiprian Regus
301*20e69e67SCiprian Regus ret = adin1140_promiscuous_mode(priv, promisc);
302*20e69e67SCiprian Regus if (ret)
303*20e69e67SCiprian Regus return ret;
304*20e69e67SCiprian Regus
305*20e69e67SCiprian Regus ret = adin1140_filter_all_multicast(priv, all_multi);
306*20e69e67SCiprian Regus if (ret)
307*20e69e67SCiprian Regus return ret;
308*20e69e67SCiprian Regus
309*20e69e67SCiprian Regus slot = ADIN1140_MAC_FILT_UC_SLOT + 1;
310*20e69e67SCiprian Regus if (!promisc) {
311*20e69e67SCiprian Regus netdev_hw_addr_list_for_each(ha, uc) {
312*20e69e67SCiprian Regus ret = adin1140_mac_filter_set(priv, ha->addr, NULL,
313*20e69e67SCiprian Regus slot);
314*20e69e67SCiprian Regus if (ret)
315*20e69e67SCiprian Regus return ret;
316*20e69e67SCiprian Regus
317*20e69e67SCiprian Regus slot++;
318*20e69e67SCiprian Regus }
319*20e69e67SCiprian Regus
320*20e69e67SCiprian Regus if (!all_multi) {
321*20e69e67SCiprian Regus netdev_hw_addr_list_for_each(ha, mc) {
322*20e69e67SCiprian Regus ret = adin1140_mac_filter_set(priv, ha->addr,
323*20e69e67SCiprian Regus NULL, slot);
324*20e69e67SCiprian Regus if (ret)
325*20e69e67SCiprian Regus return ret;
326*20e69e67SCiprian Regus
327*20e69e67SCiprian Regus slot++;
328*20e69e67SCiprian Regus }
329*20e69e67SCiprian Regus }
330*20e69e67SCiprian Regus }
331*20e69e67SCiprian Regus
332*20e69e67SCiprian Regus for (i = slot; i < ADIN1140_MAC_FILT_MAX_SLOT; i++) {
333*20e69e67SCiprian Regus ret = adin1140_mac_filter_clear(priv, i);
334*20e69e67SCiprian Regus if (ret)
335*20e69e67SCiprian Regus return ret;
336*20e69e67SCiprian Regus }
337*20e69e67SCiprian Regus
338*20e69e67SCiprian Regus return 0;
339*20e69e67SCiprian Regus }
340*20e69e67SCiprian Regus
adin1140_stats_work(struct work_struct * work)341*20e69e67SCiprian Regus static void adin1140_stats_work(struct work_struct *work)
342*20e69e67SCiprian Regus {
343*20e69e67SCiprian Regus struct delayed_work *dwork = to_delayed_work(work);
344*20e69e67SCiprian Regus struct adin1140_priv *priv;
345*20e69e67SCiprian Regus u32 reg_val;
346*20e69e67SCiprian Regus int ret;
347*20e69e67SCiprian Regus u32 i;
348*20e69e67SCiprian Regus
349*20e69e67SCiprian Regus priv = container_of(dwork, struct adin1140_priv, stats_work);
350*20e69e67SCiprian Regus
351*20e69e67SCiprian Regus for (i = 0; i < ARRAY_SIZE(adin1140_stat_regs); i++) {
352*20e69e67SCiprian Regus ret = oa_tc6_read_register_mms(priv->tc6, OA_TC6_MAC_MMS1,
353*20e69e67SCiprian Regus adin1140_stat_regs[i],
354*20e69e67SCiprian Regus ®_val);
355*20e69e67SCiprian Regus if (ret)
356*20e69e67SCiprian Regus goto out;
357*20e69e67SCiprian Regus
358*20e69e67SCiprian Regus scoped_guard(spinlock, &priv->stat_lock)
359*20e69e67SCiprian Regus priv->stats[i] += reg_val;
360*20e69e67SCiprian Regus }
361*20e69e67SCiprian Regus
362*20e69e67SCiprian Regus out:
363*20e69e67SCiprian Regus schedule_delayed_work(dwork, ADIN1140_STATS_CHECK_DELAY);
364*20e69e67SCiprian Regus }
365*20e69e67SCiprian Regus
adin1140_configure(struct adin1140_priv * priv)366*20e69e67SCiprian Regus static int adin1140_configure(struct adin1140_priv *priv)
367*20e69e67SCiprian Regus {
368*20e69e67SCiprian Regus u32 reg_val;
369*20e69e67SCiprian Regus int ret;
370*20e69e67SCiprian Regus
371*20e69e67SCiprian Regus ret = oa_tc6_zero_align_receive_frame_enable(priv->tc6);
372*20e69e67SCiprian Regus if (ret)
373*20e69e67SCiprian Regus return ret;
374*20e69e67SCiprian Regus
375*20e69e67SCiprian Regus /* Disable MAC loopback */
376*20e69e67SCiprian Regus ret = oa_tc6_write_register_mms(priv->tc6, OA_TC6_MAC_MMS1,
377*20e69e67SCiprian Regus ADIN1140_MAC_P1_LOOP_ADDR_REG, 0x0);
378*20e69e67SCiprian Regus if (ret)
379*20e69e67SCiprian Regus return ret;
380*20e69e67SCiprian Regus
381*20e69e67SCiprian Regus ret = oa_tc6_read_register(priv->tc6, OA_TC6_REG_CONFIG2, ®_val);
382*20e69e67SCiprian Regus if (ret)
383*20e69e67SCiprian Regus return ret;
384*20e69e67SCiprian Regus
385*20e69e67SCiprian Regus reg_val |= ADIN1140_MAC_CONFIG2_STAT_CLR_RD;
386*20e69e67SCiprian Regus
387*20e69e67SCiprian Regus ret = oa_tc6_write_register(priv->tc6, OA_TC6_REG_CONFIG2, reg_val);
388*20e69e67SCiprian Regus if (ret)
389*20e69e67SCiprian Regus return ret;
390*20e69e67SCiprian Regus
391*20e69e67SCiprian Regus return adin1140_default_filter_config(priv);
392*20e69e67SCiprian Regus }
393*20e69e67SCiprian Regus
adin1140_open(struct net_device * netdev)394*20e69e67SCiprian Regus static int adin1140_open(struct net_device *netdev)
395*20e69e67SCiprian Regus {
396*20e69e67SCiprian Regus struct adin1140_priv *priv = netdev_priv(netdev);
397*20e69e67SCiprian Regus
398*20e69e67SCiprian Regus schedule_delayed_work(&priv->stats_work, ADIN1140_STATS_CHECK_DELAY);
399*20e69e67SCiprian Regus
400*20e69e67SCiprian Regus phy_start(netdev->phydev);
401*20e69e67SCiprian Regus netif_start_queue(netdev);
402*20e69e67SCiprian Regus
403*20e69e67SCiprian Regus return 0;
404*20e69e67SCiprian Regus }
405*20e69e67SCiprian Regus
adin1140_close(struct net_device * netdev)406*20e69e67SCiprian Regus static int adin1140_close(struct net_device *netdev)
407*20e69e67SCiprian Regus {
408*20e69e67SCiprian Regus struct adin1140_priv *priv = netdev_priv(netdev);
409*20e69e67SCiprian Regus
410*20e69e67SCiprian Regus cancel_delayed_work_sync(&priv->stats_work);
411*20e69e67SCiprian Regus
412*20e69e67SCiprian Regus netif_stop_queue(netdev);
413*20e69e67SCiprian Regus phy_stop(netdev->phydev);
414*20e69e67SCiprian Regus
415*20e69e67SCiprian Regus return 0;
416*20e69e67SCiprian Regus }
417*20e69e67SCiprian Regus
adin1140_start_xmit(struct sk_buff * skb,struct net_device * netdev)418*20e69e67SCiprian Regus static netdev_tx_t adin1140_start_xmit(struct sk_buff *skb,
419*20e69e67SCiprian Regus struct net_device *netdev)
420*20e69e67SCiprian Regus {
421*20e69e67SCiprian Regus struct adin1140_priv *priv = netdev_priv(netdev);
422*20e69e67SCiprian Regus
423*20e69e67SCiprian Regus /* The MAC doesn't automatically pad the frame to a 60 byte minimum
424*20e69e67SCiprian Regus * size in case the host sends a shorter skb, so we have to do it in
425*20e69e67SCiprian Regus * the driver. The FCS will be added by the MAC.
426*20e69e67SCiprian Regus */
427*20e69e67SCiprian Regus if (skb_put_padto(skb, ETH_ZLEN))
428*20e69e67SCiprian Regus return NETDEV_TX_OK;
429*20e69e67SCiprian Regus
430*20e69e67SCiprian Regus return oa_tc6_start_xmit(priv->tc6, skb);
431*20e69e67SCiprian Regus }
432*20e69e67SCiprian Regus
adin1140_set_mac_address(struct net_device * netdev,void * addr)433*20e69e67SCiprian Regus static int adin1140_set_mac_address(struct net_device *netdev, void *addr)
434*20e69e67SCiprian Regus {
435*20e69e67SCiprian Regus struct adin1140_priv *priv = netdev_priv(netdev);
436*20e69e67SCiprian Regus struct sockaddr *address = addr;
437*20e69e67SCiprian Regus u8 mask[ETH_ALEN];
438*20e69e67SCiprian Regus int ret;
439*20e69e67SCiprian Regus
440*20e69e67SCiprian Regus ret = eth_prepare_mac_addr_change(netdev, addr);
441*20e69e67SCiprian Regus if (ret < 0)
442*20e69e67SCiprian Regus return ret;
443*20e69e67SCiprian Regus
444*20e69e67SCiprian Regus if (ether_addr_equal(address->sa_data, netdev->dev_addr))
445*20e69e67SCiprian Regus return 0;
446*20e69e67SCiprian Regus
447*20e69e67SCiprian Regus memset(mask, 0xFF, ETH_ALEN);
448*20e69e67SCiprian Regus ret = adin1140_mac_filter_set(priv, address->sa_data, mask,
449*20e69e67SCiprian Regus ADIN1140_MAC_FILT_UC_SLOT);
450*20e69e67SCiprian Regus if (ret)
451*20e69e67SCiprian Regus return ret;
452*20e69e67SCiprian Regus
453*20e69e67SCiprian Regus eth_commit_mac_addr_change(netdev, addr);
454*20e69e67SCiprian Regus
455*20e69e67SCiprian Regus return 0;
456*20e69e67SCiprian Regus }
457*20e69e67SCiprian Regus
__adin1140_ndo_get_stats64(struct adin1140_priv * priv,struct rtnl_link_stats64 * storage)458*20e69e67SCiprian Regus static void __adin1140_ndo_get_stats64(struct adin1140_priv *priv,
459*20e69e67SCiprian Regus struct rtnl_link_stats64 *storage)
460*20e69e67SCiprian Regus {
461*20e69e67SCiprian Regus storage->rx_errors = priv->stats[rx_crc_errors] +
462*20e69e67SCiprian Regus priv->stats[rx_align_errors] +
463*20e69e67SCiprian Regus priv->stats[rx_preamble_errors] +
464*20e69e67SCiprian Regus priv->stats[rx_short_frame_errors] +
465*20e69e67SCiprian Regus priv->stats[rx_long_frame_errors] +
466*20e69e67SCiprian Regus priv->stats[rx_phy_errors] +
467*20e69e67SCiprian Regus priv->stats[rx_ifg_errors];
468*20e69e67SCiprian Regus
469*20e69e67SCiprian Regus storage->tx_errors = priv->stats[tx_excess_collision] +
470*20e69e67SCiprian Regus priv->stats[tx_underrun];
471*20e69e67SCiprian Regus
472*20e69e67SCiprian Regus storage->rx_dropped = priv->stats[rx_fifo_full_dropped] +
473*20e69e67SCiprian Regus priv->stats[rx_addr_filter_dropped];
474*20e69e67SCiprian Regus
475*20e69e67SCiprian Regus storage->multicast = priv->stats[rx_mc_frames];
476*20e69e67SCiprian Regus
477*20e69e67SCiprian Regus storage->collisions = priv->stats[tx_single_collision] +
478*20e69e67SCiprian Regus priv->stats[tx_multi_collision];
479*20e69e67SCiprian Regus
480*20e69e67SCiprian Regus storage->rx_length_errors = priv->stats[rx_short_frame_errors] +
481*20e69e67SCiprian Regus priv->stats[rx_long_frame_errors];
482*20e69e67SCiprian Regus storage->rx_over_errors = priv->stats[rx_fifo_full_dropped];
483*20e69e67SCiprian Regus storage->rx_crc_errors = priv->stats[rx_crc_errors];
484*20e69e67SCiprian Regus storage->rx_frame_errors = priv->stats[rx_align_errors];
485*20e69e67SCiprian Regus storage->rx_missed_errors = priv->stats[rx_fifo_full_dropped];
486*20e69e67SCiprian Regus
487*20e69e67SCiprian Regus storage->tx_aborted_errors = priv->stats[tx_excess_collision];
488*20e69e67SCiprian Regus storage->tx_fifo_errors = priv->stats[tx_underrun];
489*20e69e67SCiprian Regus storage->tx_window_errors = priv->stats[tx_late_collision];
490*20e69e67SCiprian Regus }
491*20e69e67SCiprian Regus
adin1140_ndo_get_stats64(struct net_device * dev,struct rtnl_link_stats64 * storage)492*20e69e67SCiprian Regus static void adin1140_ndo_get_stats64(struct net_device *dev,
493*20e69e67SCiprian Regus struct rtnl_link_stats64 *storage)
494*20e69e67SCiprian Regus {
495*20e69e67SCiprian Regus struct adin1140_priv *priv = netdev_priv(dev);
496*20e69e67SCiprian Regus
497*20e69e67SCiprian Regus storage->rx_packets = priv->netdev->stats.rx_packets;
498*20e69e67SCiprian Regus storage->tx_packets = priv->netdev->stats.tx_packets;
499*20e69e67SCiprian Regus
500*20e69e67SCiprian Regus storage->rx_bytes = priv->netdev->stats.rx_bytes;
501*20e69e67SCiprian Regus storage->tx_bytes = priv->netdev->stats.tx_bytes;
502*20e69e67SCiprian Regus
503*20e69e67SCiprian Regus scoped_guard(spinlock, &priv->stat_lock)
504*20e69e67SCiprian Regus __adin1140_ndo_get_stats64(priv, storage);
505*20e69e67SCiprian Regus }
506*20e69e67SCiprian Regus
adin1140_get_drvinfo(struct net_device * netdev,struct ethtool_drvinfo * info)507*20e69e67SCiprian Regus static void adin1140_get_drvinfo(struct net_device *netdev,
508*20e69e67SCiprian Regus struct ethtool_drvinfo *info)
509*20e69e67SCiprian Regus {
510*20e69e67SCiprian Regus strscpy(info->driver, "ADIN1140", sizeof(info->driver));
511*20e69e67SCiprian Regus strscpy(info->bus_info, dev_name(netdev->dev.parent),
512*20e69e67SCiprian Regus sizeof(info->bus_info));
513*20e69e67SCiprian Regus }
514*20e69e67SCiprian Regus
adin1140_get_ethtool_stats(struct net_device * netdev,struct ethtool_stats * stats,u64 * data)515*20e69e67SCiprian Regus static void adin1140_get_ethtool_stats(struct net_device *netdev,
516*20e69e67SCiprian Regus struct ethtool_stats *stats, u64 *data)
517*20e69e67SCiprian Regus {
518*20e69e67SCiprian Regus struct adin1140_priv *priv = netdev_priv(netdev);
519*20e69e67SCiprian Regus u32 i;
520*20e69e67SCiprian Regus
521*20e69e67SCiprian Regus scoped_guard(spinlock, &priv->stat_lock) {
522*20e69e67SCiprian Regus for (i = 0; i < ARRAY_SIZE(adin1140_stats); i++)
523*20e69e67SCiprian Regus data[i] = priv->stats[adin1140_stats[i].idx];
524*20e69e67SCiprian Regus }
525*20e69e67SCiprian Regus }
526*20e69e67SCiprian Regus
adin1140_get_ethtool_strings(struct net_device * netdev,u32 sset,u8 * p)527*20e69e67SCiprian Regus static void adin1140_get_ethtool_strings(struct net_device *netdev, u32 sset,
528*20e69e67SCiprian Regus u8 *p)
529*20e69e67SCiprian Regus {
530*20e69e67SCiprian Regus u32 i;
531*20e69e67SCiprian Regus
532*20e69e67SCiprian Regus switch (sset) {
533*20e69e67SCiprian Regus case ETH_SS_STATS:
534*20e69e67SCiprian Regus for (i = 0; i < ARRAY_SIZE(adin1140_stats); i++)
535*20e69e67SCiprian Regus ethtool_puts(&p, adin1140_stats[i].name);
536*20e69e67SCiprian Regus
537*20e69e67SCiprian Regus break;
538*20e69e67SCiprian Regus }
539*20e69e67SCiprian Regus }
540*20e69e67SCiprian Regus
adin1140_get_sset_count(struct net_device * netdev,int sset)541*20e69e67SCiprian Regus static int adin1140_get_sset_count(struct net_device *netdev, int sset)
542*20e69e67SCiprian Regus {
543*20e69e67SCiprian Regus switch (sset) {
544*20e69e67SCiprian Regus case ETH_SS_STATS:
545*20e69e67SCiprian Regus return ARRAY_SIZE(adin1140_stats);
546*20e69e67SCiprian Regus default:
547*20e69e67SCiprian Regus return -EOPNOTSUPP;
548*20e69e67SCiprian Regus }
549*20e69e67SCiprian Regus }
550*20e69e67SCiprian Regus
__adin1140_eth_mac_stats(struct adin1140_priv * priv,struct ethtool_eth_mac_stats * mac_stats)551*20e69e67SCiprian Regus static void __adin1140_eth_mac_stats(struct adin1140_priv *priv,
552*20e69e67SCiprian Regus struct ethtool_eth_mac_stats *mac_stats)
553*20e69e67SCiprian Regus {
554*20e69e67SCiprian Regus mac_stats->FramesReceivedOK = priv->stats[rx_frames];
555*20e69e67SCiprian Regus mac_stats->BroadcastFramesReceivedOK = priv->stats[rx_bc_frames];
556*20e69e67SCiprian Regus mac_stats->MulticastFramesReceivedOK = priv->stats[rx_mc_frames];
557*20e69e67SCiprian Regus mac_stats->FrameCheckSequenceErrors = priv->stats[rx_crc_errors];
558*20e69e67SCiprian Regus mac_stats->AlignmentErrors = priv->stats[rx_align_errors];
559*20e69e67SCiprian Regus mac_stats->FrameTooLongErrors = priv->stats[rx_long_frame_errors];
560*20e69e67SCiprian Regus mac_stats->FramesLostDueToIntMACRcvError =
561*20e69e67SCiprian Regus priv->stats[rx_fifo_full_dropped];
562*20e69e67SCiprian Regus mac_stats->FramesTransmittedOK = priv->stats[tx_frames];
563*20e69e67SCiprian Regus mac_stats->BroadcastFramesXmittedOK = priv->stats[tx_bc_frames];
564*20e69e67SCiprian Regus mac_stats->MulticastFramesXmittedOK = priv->stats[tx_mc_frames];
565*20e69e67SCiprian Regus mac_stats->SingleCollisionFrames = priv->stats[tx_single_collision];
566*20e69e67SCiprian Regus mac_stats->MultipleCollisionFrames = priv->stats[tx_multi_collision];
567*20e69e67SCiprian Regus mac_stats->FramesWithDeferredXmissions = priv->stats[tx_deferred];
568*20e69e67SCiprian Regus mac_stats->LateCollisions = priv->stats[tx_late_collision];
569*20e69e67SCiprian Regus mac_stats->FramesAbortedDueToXSColls =
570*20e69e67SCiprian Regus priv->stats[tx_excess_collision];
571*20e69e67SCiprian Regus mac_stats->FramesLostDueToIntMACXmitError = priv->stats[tx_underrun];
572*20e69e67SCiprian Regus }
573*20e69e67SCiprian Regus
adin1140_get_eth_mac_stats(struct net_device * netdev,struct ethtool_eth_mac_stats * mac_stats)574*20e69e67SCiprian Regus static void adin1140_get_eth_mac_stats(struct net_device *netdev,
575*20e69e67SCiprian Regus struct ethtool_eth_mac_stats *mac_stats)
576*20e69e67SCiprian Regus {
577*20e69e67SCiprian Regus struct adin1140_priv *priv = netdev_priv(netdev);
578*20e69e67SCiprian Regus
579*20e69e67SCiprian Regus scoped_guard(spinlock, &priv->stat_lock)
580*20e69e67SCiprian Regus __adin1140_eth_mac_stats(priv, mac_stats);
581*20e69e67SCiprian Regus }
582*20e69e67SCiprian Regus
adin1140_mdiobus_read(struct mii_bus * bus,int addr,int regnum)583*20e69e67SCiprian Regus static int adin1140_mdiobus_read(struct mii_bus *bus, int addr, int regnum)
584*20e69e67SCiprian Regus {
585*20e69e67SCiprian Regus /* The ADIN1140's standard PHY C22 register map (OA TC6 0xFF00 -
586*20e69e67SCiprian Regus * 0xFF1F), of which only 0xFF00 - 0xFF03 are implemented) cannot be
587*20e69e67SCiprian Regus * accessed while frames are being received by the PHY. In case this
588*20e69e67SCiprian Regus * happens the CONFIG0 and CONFIG2 register values will get corrupted,
589*20e69e67SCiprian Regus * getting a random value. Both reads and writes cause the same
590*20e69e67SCiprian Regus * behavior. This is a workaround that avoids MDIO accesses all
591*20e69e67SCiprian Regus * together. Since this is a 10BASE-T1S PHY, only the loopback and
592*20e69e67SCiprian Regus * reset (AN) bits in the control register (0x0) can be written.
593*20e69e67SCiprian Regus * These functionalities have custom implementations in the PHY
594*20e69e67SCiprian Regus * driver. C45 accesses do not cause this issue.
595*20e69e67SCiprian Regus */
596*20e69e67SCiprian Regus
597*20e69e67SCiprian Regus switch (regnum) {
598*20e69e67SCiprian Regus case MII_BMCR:
599*20e69e67SCiprian Regus return ADIN1140_PHY_CTRL_DEFAULT;
600*20e69e67SCiprian Regus case MII_BMSR:
601*20e69e67SCiprian Regus return ADIN1140_PHY_STATUS_DEFAULT;
602*20e69e67SCiprian Regus case MII_PHYSID1:
603*20e69e67SCiprian Regus return ADIN1140_PHY_ID1;
604*20e69e67SCiprian Regus case MII_PHYSID2:
605*20e69e67SCiprian Regus return ADIN1140_PHY_ID2;
606*20e69e67SCiprian Regus default:
607*20e69e67SCiprian Regus return 0xFFFF;
608*20e69e67SCiprian Regus }
609*20e69e67SCiprian Regus }
610*20e69e67SCiprian Regus
adin1140_mdiobus_write(struct mii_bus * bus,int addr,int regnum,u16 val)611*20e69e67SCiprian Regus static int adin1140_mdiobus_write(struct mii_bus *bus, int addr, int regnum,
612*20e69e67SCiprian Regus u16 val)
613*20e69e67SCiprian Regus {
614*20e69e67SCiprian Regus return -EIO;
615*20e69e67SCiprian Regus }
616*20e69e67SCiprian Regus
adin1140_mdio_register(struct adin1140_priv * priv,struct spi_device * spidev)617*20e69e67SCiprian Regus static int adin1140_mdio_register(struct adin1140_priv *priv,
618*20e69e67SCiprian Regus struct spi_device *spidev)
619*20e69e67SCiprian Regus {
620*20e69e67SCiprian Regus priv->mdiobus = devm_mdiobus_alloc(&spidev->dev);
621*20e69e67SCiprian Regus if (!priv->mdiobus)
622*20e69e67SCiprian Regus return dev_err_probe(&spidev->dev, -ENOMEM,
623*20e69e67SCiprian Regus "MDIO bus alloc failed\n");
624*20e69e67SCiprian Regus
625*20e69e67SCiprian Regus priv->mdiobus->name = "adin1140-mdiobus";
626*20e69e67SCiprian Regus priv->mdiobus->priv = priv->tc6;
627*20e69e67SCiprian Regus priv->mdiobus->parent = &spidev->dev;
628*20e69e67SCiprian Regus priv->mdiobus->phy_mask = GENMASK(31, 1);
629*20e69e67SCiprian Regus priv->mdiobus->read = adin1140_mdiobus_read;
630*20e69e67SCiprian Regus priv->mdiobus->write = adin1140_mdiobus_write;
631*20e69e67SCiprian Regus priv->mdiobus->read_c45 = oa_tc6_mdiobus_read_c45;
632*20e69e67SCiprian Regus priv->mdiobus->write_c45 = oa_tc6_mdiobus_write_c45;
633*20e69e67SCiprian Regus
634*20e69e67SCiprian Regus snprintf(priv->mdiobus->id, MII_BUS_ID_SIZE, "adin1140-%s.%u",
635*20e69e67SCiprian Regus dev_name(&spidev->dev), spi_get_chipselect(spidev, 0));
636*20e69e67SCiprian Regus
637*20e69e67SCiprian Regus return devm_mdiobus_register(&spidev->dev, priv->mdiobus);
638*20e69e67SCiprian Regus }
639*20e69e67SCiprian Regus
adin1140_handle_link_change(struct net_device * netdev)640*20e69e67SCiprian Regus static void adin1140_handle_link_change(struct net_device *netdev)
641*20e69e67SCiprian Regus {
642*20e69e67SCiprian Regus phy_print_status(netdev->phydev);
643*20e69e67SCiprian Regus }
644*20e69e67SCiprian Regus
adin1140_phy_remove(void * data)645*20e69e67SCiprian Regus static void adin1140_phy_remove(void *data)
646*20e69e67SCiprian Regus {
647*20e69e67SCiprian Regus phy_disconnect(data);
648*20e69e67SCiprian Regus }
649*20e69e67SCiprian Regus
adin1140_phy_init(struct adin1140_priv * priv,struct spi_device * spidev)650*20e69e67SCiprian Regus static int adin1140_phy_init(struct adin1140_priv *priv,
651*20e69e67SCiprian Regus struct spi_device *spidev)
652*20e69e67SCiprian Regus {
653*20e69e67SCiprian Regus int ret;
654*20e69e67SCiprian Regus
655*20e69e67SCiprian Regus ret = adin1140_mdio_register(priv, spidev);
656*20e69e67SCiprian Regus if (ret)
657*20e69e67SCiprian Regus return ret;
658*20e69e67SCiprian Regus
659*20e69e67SCiprian Regus priv->phydev = phy_find_first(priv->mdiobus);
660*20e69e67SCiprian Regus if (!priv->phydev)
661*20e69e67SCiprian Regus return dev_err_probe(&spidev->dev, -ENODEV, "No PHY found\n");
662*20e69e67SCiprian Regus
663*20e69e67SCiprian Regus priv->phydev->is_internal = true;
664*20e69e67SCiprian Regus ret = phy_connect_direct(priv->netdev, priv->phydev,
665*20e69e67SCiprian Regus &adin1140_handle_link_change,
666*20e69e67SCiprian Regus PHY_INTERFACE_MODE_INTERNAL);
667*20e69e67SCiprian Regus if (ret)
668*20e69e67SCiprian Regus return dev_err_probe(&spidev->dev, ret,
669*20e69e67SCiprian Regus "Can't attach PHY to %s\n",
670*20e69e67SCiprian Regus priv->mdiobus->id);
671*20e69e67SCiprian Regus
672*20e69e67SCiprian Regus ret = devm_add_action_or_reset(&spidev->dev, adin1140_phy_remove,
673*20e69e67SCiprian Regus priv->phydev);
674*20e69e67SCiprian Regus if (ret)
675*20e69e67SCiprian Regus return ret;
676*20e69e67SCiprian Regus
677*20e69e67SCiprian Regus phy_attached_info(priv->phydev);
678*20e69e67SCiprian Regus
679*20e69e67SCiprian Regus return 0;
680*20e69e67SCiprian Regus }
681*20e69e67SCiprian Regus
682*20e69e67SCiprian Regus static const struct ethtool_ops adin1140_ethtool_ops = {
683*20e69e67SCiprian Regus .get_drvinfo = adin1140_get_drvinfo,
684*20e69e67SCiprian Regus .get_link = ethtool_op_get_link,
685*20e69e67SCiprian Regus .get_ethtool_stats = adin1140_get_ethtool_stats,
686*20e69e67SCiprian Regus .get_sset_count = adin1140_get_sset_count,
687*20e69e67SCiprian Regus .get_strings = adin1140_get_ethtool_strings,
688*20e69e67SCiprian Regus .get_link_ksettings = phy_ethtool_get_link_ksettings,
689*20e69e67SCiprian Regus .set_link_ksettings = phy_ethtool_set_link_ksettings,
690*20e69e67SCiprian Regus .get_eth_mac_stats = adin1140_get_eth_mac_stats,
691*20e69e67SCiprian Regus };
692*20e69e67SCiprian Regus
693*20e69e67SCiprian Regus static const struct net_device_ops adin1140_netdev_ops = {
694*20e69e67SCiprian Regus .ndo_open = adin1140_open,
695*20e69e67SCiprian Regus .ndo_stop = adin1140_close,
696*20e69e67SCiprian Regus .ndo_start_xmit = adin1140_start_xmit,
697*20e69e67SCiprian Regus .ndo_set_mac_address = adin1140_set_mac_address,
698*20e69e67SCiprian Regus .ndo_validate_addr = eth_validate_addr,
699*20e69e67SCiprian Regus .ndo_set_rx_mode_async = adin1140_rx_mode,
700*20e69e67SCiprian Regus .ndo_eth_ioctl = phy_do_ioctl_running,
701*20e69e67SCiprian Regus .ndo_get_stats64 = adin1140_ndo_get_stats64,
702*20e69e67SCiprian Regus };
703*20e69e67SCiprian Regus
adin1140_oa_tc6_remove(void * data)704*20e69e67SCiprian Regus static void adin1140_oa_tc6_remove(void *data)
705*20e69e67SCiprian Regus {
706*20e69e67SCiprian Regus oa_tc6_exit(data);
707*20e69e67SCiprian Regus }
708*20e69e67SCiprian Regus
adin1140_probe(struct spi_device * spi)709*20e69e67SCiprian Regus static int adin1140_probe(struct spi_device *spi)
710*20e69e67SCiprian Regus {
711*20e69e67SCiprian Regus struct oa_tc6_quirks tc6_quirks = {};
712*20e69e67SCiprian Regus struct net_device *netdev;
713*20e69e67SCiprian Regus struct adin1140_priv *priv;
714*20e69e67SCiprian Regus int ret;
715*20e69e67SCiprian Regus
716*20e69e67SCiprian Regus netdev = devm_alloc_etherdev(&spi->dev, sizeof(struct adin1140_priv));
717*20e69e67SCiprian Regus if (!netdev)
718*20e69e67SCiprian Regus return -ENOMEM;
719*20e69e67SCiprian Regus
720*20e69e67SCiprian Regus priv = netdev_priv(netdev);
721*20e69e67SCiprian Regus priv->netdev = netdev;
722*20e69e67SCiprian Regus spi_set_drvdata(spi, priv);
723*20e69e67SCiprian Regus spin_lock_init(&priv->stat_lock);
724*20e69e67SCiprian Regus
725*20e69e67SCiprian Regus tc6_quirks.quirk_flags = OA_TC6_BROKEN_PHY;
726*20e69e67SCiprian Regus
727*20e69e67SCiprian Regus priv->tc6 = oa_tc6_init(spi, netdev, &tc6_quirks);
728*20e69e67SCiprian Regus if (!priv->tc6)
729*20e69e67SCiprian Regus return -ENODEV;
730*20e69e67SCiprian Regus
731*20e69e67SCiprian Regus ret = devm_add_action_or_reset(&spi->dev, adin1140_oa_tc6_remove,
732*20e69e67SCiprian Regus priv->tc6);
733*20e69e67SCiprian Regus if (ret)
734*20e69e67SCiprian Regus return ret;
735*20e69e67SCiprian Regus
736*20e69e67SCiprian Regus ret = adin1140_phy_init(priv, spi);
737*20e69e67SCiprian Regus if (ret)
738*20e69e67SCiprian Regus return ret;
739*20e69e67SCiprian Regus
740*20e69e67SCiprian Regus if (device_get_ethdev_address(&spi->dev, netdev))
741*20e69e67SCiprian Regus eth_hw_addr_random(netdev);
742*20e69e67SCiprian Regus
743*20e69e67SCiprian Regus ret = adin1140_configure(priv);
744*20e69e67SCiprian Regus if (ret)
745*20e69e67SCiprian Regus return ret;
746*20e69e67SCiprian Regus
747*20e69e67SCiprian Regus INIT_DELAYED_WORK(&priv->stats_work, adin1140_stats_work);
748*20e69e67SCiprian Regus
749*20e69e67SCiprian Regus netdev->if_port = IF_PORT_10BASET;
750*20e69e67SCiprian Regus netdev->irq = spi->irq;
751*20e69e67SCiprian Regus netdev->netdev_ops = &adin1140_netdev_ops;
752*20e69e67SCiprian Regus netdev->ethtool_ops = &adin1140_ethtool_ops;
753*20e69e67SCiprian Regus netdev->netns_immutable = true;
754*20e69e67SCiprian Regus netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE |
755*20e69e67SCiprian Regus IFF_UNICAST_FLT;
756*20e69e67SCiprian Regus
757*20e69e67SCiprian Regus ret = devm_register_netdev(&spi->dev, netdev);
758*20e69e67SCiprian Regus if (ret)
759*20e69e67SCiprian Regus return dev_err_probe(&spi->dev, ret,
760*20e69e67SCiprian Regus "Failed to register netdev");
761*20e69e67SCiprian Regus
762*20e69e67SCiprian Regus return 0;
763*20e69e67SCiprian Regus }
764*20e69e67SCiprian Regus
765*20e69e67SCiprian Regus static const struct spi_device_id adin1140_spi_id[] = {
766*20e69e67SCiprian Regus { .name = "ad3306" },
767*20e69e67SCiprian Regus { .name = "adin1140" },
768*20e69e67SCiprian Regus {},
769*20e69e67SCiprian Regus };
770*20e69e67SCiprian Regus MODULE_DEVICE_TABLE(spi, adin1140_spi_id);
771*20e69e67SCiprian Regus
772*20e69e67SCiprian Regus static const struct of_device_id adin1140_match_table[] = {
773*20e69e67SCiprian Regus { .compatible = "adi,ad3306" },
774*20e69e67SCiprian Regus { .compatible = "adi,adin1140" },
775*20e69e67SCiprian Regus { }
776*20e69e67SCiprian Regus };
777*20e69e67SCiprian Regus MODULE_DEVICE_TABLE(of, adin1140_match_table);
778*20e69e67SCiprian Regus
779*20e69e67SCiprian Regus static struct spi_driver adin1140_driver = {
780*20e69e67SCiprian Regus .driver = {
781*20e69e67SCiprian Regus .name = "adin1140",
782*20e69e67SCiprian Regus .of_match_table = adin1140_match_table,
783*20e69e67SCiprian Regus },
784*20e69e67SCiprian Regus .probe = adin1140_probe,
785*20e69e67SCiprian Regus .id_table = adin1140_spi_id,
786*20e69e67SCiprian Regus };
787*20e69e67SCiprian Regus module_spi_driver(adin1140_driver);
788*20e69e67SCiprian Regus
789*20e69e67SCiprian Regus MODULE_DESCRIPTION("Analog Devices, Inc. ADIN1140 10BASE-T1S MAC-PHY");
790*20e69e67SCiprian Regus MODULE_AUTHOR("Ciprian Regus <ciprian.regus@analog.com>");
791*20e69e67SCiprian Regus MODULE_LICENSE("GPL");
792