1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /******************************************************************************* |
3 | This is the driver for the MAC 10/100 on-chip Ethernet controller |
4 | currently tested on all the ST boards based on STb7109 and stx7200 SoCs. |
5 | |
6 | DWC Ether MAC 10/100 Universal version 4.0 has been used for developing |
7 | this code. |
8 | |
9 | This only implements the mac core functions for this chip. |
10 | |
11 | Copyright (C) 2007-2009 STMicroelectronics Ltd |
12 | |
13 | |
14 | Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> |
15 | *******************************************************************************/ |
16 | |
17 | #include <linux/crc32.h> |
18 | #include <asm/io.h> |
19 | #include "stmmac.h" |
20 | #include "dwmac100.h" |
21 | |
22 | static void dwmac100_core_init(struct mac_device_info *hw, |
23 | struct net_device *dev) |
24 | { |
25 | void __iomem *ioaddr = hw->pcsr; |
26 | u32 value = readl(addr: ioaddr + MAC_CONTROL); |
27 | |
28 | value |= MAC_CORE_INIT; |
29 | |
30 | writel(val: value, addr: ioaddr + MAC_CONTROL); |
31 | |
32 | #ifdef STMMAC_VLAN_TAG_USED |
33 | writel(ETH_P_8021Q, addr: ioaddr + MAC_VLAN1); |
34 | #endif |
35 | } |
36 | |
37 | static void dwmac100_dump_mac_regs(struct mac_device_info *hw, u32 *reg_space) |
38 | { |
39 | void __iomem *ioaddr = hw->pcsr; |
40 | |
41 | reg_space[MAC_CONTROL / 4] = readl(addr: ioaddr + MAC_CONTROL); |
42 | reg_space[MAC_ADDR_HIGH / 4] = readl(addr: ioaddr + MAC_ADDR_HIGH); |
43 | reg_space[MAC_ADDR_LOW / 4] = readl(addr: ioaddr + MAC_ADDR_LOW); |
44 | reg_space[MAC_HASH_HIGH / 4] = readl(addr: ioaddr + MAC_HASH_HIGH); |
45 | reg_space[MAC_HASH_LOW / 4] = readl(addr: ioaddr + MAC_HASH_LOW); |
46 | reg_space[MAC_FLOW_CTRL / 4] = readl(addr: ioaddr + MAC_FLOW_CTRL); |
47 | reg_space[MAC_VLAN1 / 4] = readl(addr: ioaddr + MAC_VLAN1); |
48 | reg_space[MAC_VLAN2 / 4] = readl(addr: ioaddr + MAC_VLAN2); |
49 | } |
50 | |
51 | static int dwmac100_rx_ipc_enable(struct mac_device_info *hw) |
52 | { |
53 | return 0; |
54 | } |
55 | |
56 | static int dwmac100_irq_status(struct mac_device_info *hw, |
57 | struct stmmac_extra_stats *x) |
58 | { |
59 | return 0; |
60 | } |
61 | |
62 | static void dwmac100_set_umac_addr(struct mac_device_info *hw, |
63 | const unsigned char *addr, |
64 | unsigned int reg_n) |
65 | { |
66 | void __iomem *ioaddr = hw->pcsr; |
67 | stmmac_set_mac_addr(ioaddr, addr, MAC_ADDR_HIGH, MAC_ADDR_LOW); |
68 | } |
69 | |
70 | static void dwmac100_get_umac_addr(struct mac_device_info *hw, |
71 | unsigned char *addr, |
72 | unsigned int reg_n) |
73 | { |
74 | void __iomem *ioaddr = hw->pcsr; |
75 | stmmac_get_mac_addr(ioaddr, addr, MAC_ADDR_HIGH, MAC_ADDR_LOW); |
76 | } |
77 | |
78 | static void dwmac100_set_filter(struct mac_device_info *hw, |
79 | struct net_device *dev) |
80 | { |
81 | void __iomem *ioaddr = (void __iomem *)dev->base_addr; |
82 | u32 value = readl(addr: ioaddr + MAC_CONTROL); |
83 | |
84 | if (dev->flags & IFF_PROMISC) { |
85 | value |= MAC_CONTROL_PR; |
86 | value &= ~(MAC_CONTROL_PM | MAC_CONTROL_IF | MAC_CONTROL_HO | |
87 | MAC_CONTROL_HP); |
88 | } else if ((netdev_mc_count(dev) > HASH_TABLE_SIZE) |
89 | || (dev->flags & IFF_ALLMULTI)) { |
90 | value |= MAC_CONTROL_PM; |
91 | value &= ~(MAC_CONTROL_PR | MAC_CONTROL_IF | MAC_CONTROL_HO); |
92 | writel(val: 0xffffffff, addr: ioaddr + MAC_HASH_HIGH); |
93 | writel(val: 0xffffffff, addr: ioaddr + MAC_HASH_LOW); |
94 | } else if (netdev_mc_empty(dev)) { /* no multicast */ |
95 | value &= ~(MAC_CONTROL_PM | MAC_CONTROL_PR | MAC_CONTROL_IF | |
96 | MAC_CONTROL_HO | MAC_CONTROL_HP); |
97 | } else { |
98 | u32 mc_filter[2]; |
99 | struct netdev_hw_addr *ha; |
100 | |
101 | /* Perfect filter mode for physical address and Hash |
102 | * filter for multicast |
103 | */ |
104 | value |= MAC_CONTROL_HP; |
105 | value &= ~(MAC_CONTROL_PM | MAC_CONTROL_PR | |
106 | MAC_CONTROL_IF | MAC_CONTROL_HO); |
107 | |
108 | memset(mc_filter, 0, sizeof(mc_filter)); |
109 | netdev_for_each_mc_addr(ha, dev) { |
110 | /* The upper 6 bits of the calculated CRC are used to |
111 | * index the contens of the hash table |
112 | */ |
113 | int bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26; |
114 | /* The most significant bit determines the register to |
115 | * use (H/L) while the other 5 bits determine the bit |
116 | * within the register. |
117 | */ |
118 | mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); |
119 | } |
120 | writel(val: mc_filter[0], addr: ioaddr + MAC_HASH_LOW); |
121 | writel(val: mc_filter[1], addr: ioaddr + MAC_HASH_HIGH); |
122 | } |
123 | |
124 | writel(val: value, addr: ioaddr + MAC_CONTROL); |
125 | } |
126 | |
127 | static void dwmac100_flow_ctrl(struct mac_device_info *hw, unsigned int duplex, |
128 | unsigned int fc, unsigned int pause_time, |
129 | u32 tx_cnt) |
130 | { |
131 | void __iomem *ioaddr = hw->pcsr; |
132 | unsigned int flow = MAC_FLOW_CTRL_ENABLE; |
133 | |
134 | if (duplex) |
135 | flow |= (pause_time << MAC_FLOW_CTRL_PT_SHIFT); |
136 | writel(val: flow, addr: ioaddr + MAC_FLOW_CTRL); |
137 | } |
138 | |
139 | /* No PMT module supported on ST boards with this Eth chip. */ |
140 | static void dwmac100_pmt(struct mac_device_info *hw, unsigned long mode) |
141 | { |
142 | return; |
143 | } |
144 | |
145 | static void dwmac100_set_mac_loopback(void __iomem *ioaddr, bool enable) |
146 | { |
147 | u32 value = readl(addr: ioaddr + MAC_CONTROL); |
148 | |
149 | if (enable) |
150 | value |= MAC_CONTROL_OM; |
151 | else |
152 | value &= ~MAC_CONTROL_OM; |
153 | |
154 | writel(val: value, addr: ioaddr + MAC_CONTROL); |
155 | } |
156 | |
157 | const struct stmmac_ops dwmac100_ops = { |
158 | .core_init = dwmac100_core_init, |
159 | .set_mac = stmmac_set_mac, |
160 | .rx_ipc = dwmac100_rx_ipc_enable, |
161 | .dump_regs = dwmac100_dump_mac_regs, |
162 | .host_irq_status = dwmac100_irq_status, |
163 | .set_filter = dwmac100_set_filter, |
164 | .flow_ctrl = dwmac100_flow_ctrl, |
165 | .pmt = dwmac100_pmt, |
166 | .set_umac_addr = dwmac100_set_umac_addr, |
167 | .get_umac_addr = dwmac100_get_umac_addr, |
168 | .set_mac_loopback = dwmac100_set_mac_loopback, |
169 | }; |
170 | |
171 | int dwmac100_setup(struct stmmac_priv *priv) |
172 | { |
173 | struct mac_device_info *mac = priv->hw; |
174 | |
175 | dev_info(priv->device, "\tDWMAC100\n" ); |
176 | |
177 | mac->pcsr = priv->ioaddr; |
178 | mac->link.caps = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | |
179 | MAC_10 | MAC_100; |
180 | mac->link.duplex = MAC_CONTROL_F; |
181 | mac->link.speed10 = 0; |
182 | mac->link.speed100 = 0; |
183 | mac->link.speed1000 = 0; |
184 | mac->link.speed_mask = MAC_CONTROL_PS; |
185 | mac->mii.addr = MAC_MII_ADDR; |
186 | mac->mii.data = MAC_MII_DATA; |
187 | mac->mii.addr_shift = 11; |
188 | mac->mii.addr_mask = 0x0000F800; |
189 | mac->mii.reg_shift = 6; |
190 | mac->mii.reg_mask = 0x000007C0; |
191 | mac->mii.clk_csr_shift = 2; |
192 | mac->mii.clk_csr_mask = GENMASK(5, 2); |
193 | |
194 | return 0; |
195 | } |
196 | |