1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* atlx.c -- common functions for Attansic network drivers |
3 | * |
4 | * Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved. |
5 | * Copyright(c) 2006 - 2007 Chris Snook <csnook@redhat.com> |
6 | * Copyright(c) 2006 - 2008 Jay Cliburn <jcliburn@gmail.com> |
7 | * Copyright(c) 2007 Atheros Corporation. All rights reserved. |
8 | * |
9 | * Derived from Intel e1000 driver |
10 | * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. |
11 | */ |
12 | |
13 | /* Including this file like a header is a temporary hack, I promise. -- CHS */ |
14 | #ifndef ATLX_C |
15 | #define ATLX_C |
16 | |
17 | #include <linux/device.h> |
18 | #include <linux/errno.h> |
19 | #include <linux/etherdevice.h> |
20 | #include <linux/if.h> |
21 | #include <linux/netdevice.h> |
22 | #include <linux/socket.h> |
23 | #include <linux/sockios.h> |
24 | #include <linux/spinlock.h> |
25 | #include <linux/string.h> |
26 | #include <linux/types.h> |
27 | #include <linux/workqueue.h> |
28 | |
29 | #include "atlx.h" |
30 | |
31 | static s32 atlx_read_phy_reg(struct atl1_hw *hw, u16 reg_addr, u16 *phy_data); |
32 | static u32 atlx_hash_mc_addr(struct atl1_hw *hw, u8 *mc_addr); |
33 | static void atlx_set_mac_addr(struct atl1_hw *hw); |
34 | |
35 | static struct atlx_spi_flash_dev flash_table[] = { |
36 | /* MFR_NAME WRSR READ PRGM WREN WRDI RDSR RDID SEC_ERS CHIP_ERS */ |
37 | {"Atmel" , 0x00, 0x03, 0x02, 0x06, 0x04, 0x05, 0x15, 0x52, 0x62}, |
38 | {"SST" , 0x01, 0x03, 0x02, 0x06, 0x04, 0x05, 0x90, 0x20, 0x60}, |
39 | {"ST" , 0x01, 0x03, 0x02, 0x06, 0x04, 0x05, 0xAB, 0xD8, 0xC7}, |
40 | }; |
41 | |
42 | static int atlx_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) |
43 | { |
44 | switch (cmd) { |
45 | case SIOCGMIIPHY: |
46 | case SIOCGMIIREG: |
47 | case SIOCSMIIREG: |
48 | return atlx_mii_ioctl(netdev, ifr, cmd); |
49 | default: |
50 | return -EOPNOTSUPP; |
51 | } |
52 | } |
53 | |
54 | /** |
55 | * atlx_set_mac - Change the Ethernet Address of the NIC |
56 | * @netdev: network interface device structure |
57 | * @p: pointer to an address structure |
58 | * |
59 | * Returns 0 on success, negative on failure |
60 | */ |
61 | static int atlx_set_mac(struct net_device *netdev, void *p) |
62 | { |
63 | struct atlx_adapter *adapter = netdev_priv(dev: netdev); |
64 | struct sockaddr *addr = p; |
65 | |
66 | if (netif_running(dev: netdev)) |
67 | return -EBUSY; |
68 | |
69 | if (!is_valid_ether_addr(addr: addr->sa_data)) |
70 | return -EADDRNOTAVAIL; |
71 | |
72 | eth_hw_addr_set(dev: netdev, addr: addr->sa_data); |
73 | memcpy(adapter->hw.mac_addr, addr->sa_data, netdev->addr_len); |
74 | |
75 | atlx_set_mac_addr(hw: &adapter->hw); |
76 | return 0; |
77 | } |
78 | |
79 | static void atlx_check_for_link(struct atlx_adapter *adapter) |
80 | { |
81 | struct net_device *netdev = adapter->netdev; |
82 | u16 phy_data = 0; |
83 | |
84 | spin_lock(lock: &adapter->lock); |
85 | adapter->phy_timer_pending = false; |
86 | atlx_read_phy_reg(hw: &adapter->hw, MII_BMSR, phy_data: &phy_data); |
87 | atlx_read_phy_reg(hw: &adapter->hw, MII_BMSR, phy_data: &phy_data); |
88 | spin_unlock(lock: &adapter->lock); |
89 | |
90 | /* notify upper layer link down ASAP */ |
91 | if (!(phy_data & BMSR_LSTATUS)) { |
92 | /* Link Down */ |
93 | if (netif_carrier_ok(dev: netdev)) { |
94 | /* old link state: Up */ |
95 | dev_info(&adapter->pdev->dev, "%s link is down\n" , |
96 | netdev->name); |
97 | adapter->link_speed = SPEED_0; |
98 | netif_carrier_off(dev: netdev); |
99 | } |
100 | } |
101 | schedule_work(work: &adapter->link_chg_task); |
102 | } |
103 | |
104 | /** |
105 | * atlx_set_multi - Multicast and Promiscuous mode set |
106 | * @netdev: network interface device structure |
107 | * |
108 | * The set_multi entry point is called whenever the multicast address |
109 | * list or the network interface flags are updated. This routine is |
110 | * responsible for configuring the hardware for proper multicast, |
111 | * promiscuous mode, and all-multi behavior. |
112 | */ |
113 | static void atlx_set_multi(struct net_device *netdev) |
114 | { |
115 | struct atlx_adapter *adapter = netdev_priv(dev: netdev); |
116 | struct atlx_hw *hw = &adapter->hw; |
117 | struct netdev_hw_addr *ha; |
118 | u32 rctl; |
119 | u32 hash_value; |
120 | |
121 | /* Check for Promiscuous and All Multicast modes */ |
122 | rctl = ioread32(hw->hw_addr + REG_MAC_CTRL); |
123 | if (netdev->flags & IFF_PROMISC) |
124 | rctl |= MAC_CTRL_PROMIS_EN; |
125 | else if (netdev->flags & IFF_ALLMULTI) { |
126 | rctl |= MAC_CTRL_MC_ALL_EN; |
127 | rctl &= ~MAC_CTRL_PROMIS_EN; |
128 | } else |
129 | rctl &= ~(MAC_CTRL_PROMIS_EN | MAC_CTRL_MC_ALL_EN); |
130 | |
131 | iowrite32(rctl, hw->hw_addr + REG_MAC_CTRL); |
132 | |
133 | /* clear the old settings from the multicast hash table */ |
134 | iowrite32(0, hw->hw_addr + REG_RX_HASH_TABLE); |
135 | iowrite32(0, (hw->hw_addr + REG_RX_HASH_TABLE) + (1 << 2)); |
136 | |
137 | /* compute mc addresses' hash value ,and put it into hash table */ |
138 | netdev_for_each_mc_addr(ha, netdev) { |
139 | hash_value = atlx_hash_mc_addr(hw, mc_addr: ha->addr); |
140 | atlx_hash_set(hw, hash_value); |
141 | } |
142 | } |
143 | |
144 | static inline void atlx_imr_set(struct atlx_adapter *adapter, |
145 | unsigned int imr) |
146 | { |
147 | iowrite32(imr, adapter->hw.hw_addr + REG_IMR); |
148 | ioread32(adapter->hw.hw_addr + REG_IMR); |
149 | } |
150 | |
151 | /** |
152 | * atlx_irq_enable - Enable default interrupt generation settings |
153 | * @adapter: board private structure |
154 | */ |
155 | static void atlx_irq_enable(struct atlx_adapter *adapter) |
156 | { |
157 | atlx_imr_set(adapter, IMR_NORMAL_MASK); |
158 | adapter->int_enabled = true; |
159 | } |
160 | |
161 | /** |
162 | * atlx_irq_disable - Mask off interrupt generation on the NIC |
163 | * @adapter: board private structure |
164 | */ |
165 | static void atlx_irq_disable(struct atlx_adapter *adapter) |
166 | { |
167 | adapter->int_enabled = false; |
168 | atlx_imr_set(adapter, imr: 0); |
169 | synchronize_irq(irq: adapter->pdev->irq); |
170 | } |
171 | |
172 | static void atlx_clear_phy_int(struct atlx_adapter *adapter) |
173 | { |
174 | u16 phy_data; |
175 | unsigned long flags; |
176 | |
177 | spin_lock_irqsave(&adapter->lock, flags); |
178 | atlx_read_phy_reg(hw: &adapter->hw, reg_addr: 19, phy_data: &phy_data); |
179 | spin_unlock_irqrestore(lock: &adapter->lock, flags); |
180 | } |
181 | |
182 | /** |
183 | * atlx_tx_timeout - Respond to a Tx Hang |
184 | * @netdev: network interface device structure |
185 | */ |
186 | static void atlx_tx_timeout(struct net_device *netdev, unsigned int txqueue) |
187 | { |
188 | struct atlx_adapter *adapter = netdev_priv(dev: netdev); |
189 | /* Do the reset outside of interrupt context */ |
190 | schedule_work(work: &adapter->reset_dev_task); |
191 | } |
192 | |
193 | /* |
194 | * atlx_link_chg_task - deal with link change event Out of interrupt context |
195 | */ |
196 | static void atlx_link_chg_task(struct work_struct *work) |
197 | { |
198 | struct atlx_adapter *adapter; |
199 | unsigned long flags; |
200 | |
201 | adapter = container_of(work, struct atlx_adapter, link_chg_task); |
202 | |
203 | spin_lock_irqsave(&adapter->lock, flags); |
204 | atlx_check_link(adapter); |
205 | spin_unlock_irqrestore(lock: &adapter->lock, flags); |
206 | } |
207 | |
208 | static void __atlx_vlan_mode(netdev_features_t features, u32 *ctrl) |
209 | { |
210 | if (features & NETIF_F_HW_VLAN_CTAG_RX) { |
211 | /* enable VLAN tag insert/strip */ |
212 | *ctrl |= MAC_CTRL_RMV_VLAN; |
213 | } else { |
214 | /* disable VLAN tag insert/strip */ |
215 | *ctrl &= ~MAC_CTRL_RMV_VLAN; |
216 | } |
217 | } |
218 | |
219 | static void atlx_vlan_mode(struct net_device *netdev, |
220 | netdev_features_t features) |
221 | { |
222 | struct atlx_adapter *adapter = netdev_priv(dev: netdev); |
223 | unsigned long flags; |
224 | u32 ctrl; |
225 | |
226 | spin_lock_irqsave(&adapter->lock, flags); |
227 | /* atlx_irq_disable(adapter); FIXME: confirm/remove */ |
228 | ctrl = ioread32(adapter->hw.hw_addr + REG_MAC_CTRL); |
229 | __atlx_vlan_mode(features, ctrl: &ctrl); |
230 | iowrite32(ctrl, adapter->hw.hw_addr + REG_MAC_CTRL); |
231 | /* atlx_irq_enable(adapter); FIXME */ |
232 | spin_unlock_irqrestore(lock: &adapter->lock, flags); |
233 | } |
234 | |
235 | static void atlx_restore_vlan(struct atlx_adapter *adapter) |
236 | { |
237 | atlx_vlan_mode(netdev: adapter->netdev, features: adapter->netdev->features); |
238 | } |
239 | |
240 | static netdev_features_t atlx_fix_features(struct net_device *netdev, |
241 | netdev_features_t features) |
242 | { |
243 | /* |
244 | * Since there is no support for separate rx/tx vlan accel |
245 | * enable/disable make sure tx flag is always in same state as rx. |
246 | */ |
247 | if (features & NETIF_F_HW_VLAN_CTAG_RX) |
248 | features |= NETIF_F_HW_VLAN_CTAG_TX; |
249 | else |
250 | features &= ~NETIF_F_HW_VLAN_CTAG_TX; |
251 | |
252 | return features; |
253 | } |
254 | |
255 | static int atlx_set_features(struct net_device *netdev, |
256 | netdev_features_t features) |
257 | { |
258 | netdev_features_t changed = netdev->features ^ features; |
259 | |
260 | if (changed & NETIF_F_HW_VLAN_CTAG_RX) |
261 | atlx_vlan_mode(netdev, features); |
262 | |
263 | return 0; |
264 | } |
265 | |
266 | #endif /* ATLX_C */ |
267 | |