diff options
Diffstat (limited to 'drivers/net/stmmac/dwmac1000_core.c')
-rw-r--r-- | drivers/net/stmmac/dwmac1000_core.c | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/drivers/net/stmmac/dwmac1000_core.c b/drivers/net/stmmac/dwmac1000_core.c new file mode 100644 index 00000000000..0f63b3c83c1 --- /dev/null +++ b/drivers/net/stmmac/dwmac1000_core.c | |||
@@ -0,0 +1,251 @@ | |||
1 | /******************************************************************************* | ||
2 | This is the driver for the GMAC on-chip Ethernet controller for ST SoCs. | ||
3 | DWC Ether MAC 10/100/1000 Universal version 3.41a has been used for | ||
4 | developing this code. | ||
5 | |||
6 | This only implements the mac core functions for this chip. | ||
7 | |||
8 | Copyright (C) 2007-2009 STMicroelectronics Ltd | ||
9 | |||
10 | This program is free software; you can redistribute it and/or modify it | ||
11 | under the terms and conditions of the GNU General Public License, | ||
12 | version 2, as published by the Free Software Foundation. | ||
13 | |||
14 | This program is distributed in the hope it will be useful, but WITHOUT | ||
15 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
16 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
17 | more details. | ||
18 | |||
19 | You should have received a copy of the GNU General Public License along with | ||
20 | this program; if not, write to the Free Software Foundation, Inc., | ||
21 | 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
22 | |||
23 | The full GNU General Public License is included in this distribution in | ||
24 | the file called "COPYING". | ||
25 | |||
26 | Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> | ||
27 | *******************************************************************************/ | ||
28 | |||
29 | #include <linux/crc32.h> | ||
30 | #include <linux/slab.h> | ||
31 | #include <asm/io.h> | ||
32 | #include "dwmac1000.h" | ||
33 | |||
34 | static void dwmac1000_core_init(void __iomem *ioaddr) | ||
35 | { | ||
36 | u32 value = readl(ioaddr + GMAC_CONTROL); | ||
37 | value |= GMAC_CORE_INIT; | ||
38 | writel(value, ioaddr + GMAC_CONTROL); | ||
39 | |||
40 | /* STBus Bridge Configuration */ | ||
41 | /*writel(0xc5608, ioaddr + 0x00007000);*/ | ||
42 | |||
43 | /* Freeze MMC counters */ | ||
44 | writel(0x8, ioaddr + GMAC_MMC_CTRL); | ||
45 | /* Mask GMAC interrupts */ | ||
46 | writel(0x207, ioaddr + GMAC_INT_MASK); | ||
47 | |||
48 | #ifdef STMMAC_VLAN_TAG_USED | ||
49 | /* Tag detection without filtering */ | ||
50 | writel(0x0, ioaddr + GMAC_VLAN_TAG); | ||
51 | #endif | ||
52 | } | ||
53 | |||
54 | static int dwmac1000_rx_coe_supported(void __iomem *ioaddr) | ||
55 | { | ||
56 | u32 value = readl(ioaddr + GMAC_CONTROL); | ||
57 | |||
58 | value |= GMAC_CONTROL_IPC; | ||
59 | writel(value, ioaddr + GMAC_CONTROL); | ||
60 | |||
61 | value = readl(ioaddr + GMAC_CONTROL); | ||
62 | |||
63 | return !!(value & GMAC_CONTROL_IPC); | ||
64 | } | ||
65 | |||
66 | static void dwmac1000_dump_regs(void __iomem *ioaddr) | ||
67 | { | ||
68 | int i; | ||
69 | pr_info("\tDWMAC1000 regs (base addr = 0x%p)\n", ioaddr); | ||
70 | |||
71 | for (i = 0; i < 55; i++) { | ||
72 | int offset = i * 4; | ||
73 | pr_info("\tReg No. %d (offset 0x%x): 0x%08x\n", i, | ||
74 | offset, readl(ioaddr + offset)); | ||
75 | } | ||
76 | } | ||
77 | |||
78 | static void dwmac1000_set_umac_addr(void __iomem *ioaddr, unsigned char *addr, | ||
79 | unsigned int reg_n) | ||
80 | { | ||
81 | stmmac_set_mac_addr(ioaddr, addr, GMAC_ADDR_HIGH(reg_n), | ||
82 | GMAC_ADDR_LOW(reg_n)); | ||
83 | } | ||
84 | |||
85 | static void dwmac1000_get_umac_addr(void __iomem *ioaddr, unsigned char *addr, | ||
86 | unsigned int reg_n) | ||
87 | { | ||
88 | stmmac_get_mac_addr(ioaddr, addr, GMAC_ADDR_HIGH(reg_n), | ||
89 | GMAC_ADDR_LOW(reg_n)); | ||
90 | } | ||
91 | |||
92 | static void dwmac1000_set_filter(struct net_device *dev) | ||
93 | { | ||
94 | void __iomem *ioaddr = (void __iomem *) dev->base_addr; | ||
95 | unsigned int value = 0; | ||
96 | |||
97 | CHIP_DBG(KERN_INFO "%s: # mcasts %d, # unicast %d\n", | ||
98 | __func__, netdev_mc_count(dev), netdev_uc_count(dev)); | ||
99 | |||
100 | if (dev->flags & IFF_PROMISC) | ||
101 | value = GMAC_FRAME_FILTER_PR; | ||
102 | else if ((netdev_mc_count(dev) > HASH_TABLE_SIZE) | ||
103 | || (dev->flags & IFF_ALLMULTI)) { | ||
104 | value = GMAC_FRAME_FILTER_PM; /* pass all multi */ | ||
105 | writel(0xffffffff, ioaddr + GMAC_HASH_HIGH); | ||
106 | writel(0xffffffff, ioaddr + GMAC_HASH_LOW); | ||
107 | } else if (!netdev_mc_empty(dev)) { | ||
108 | u32 mc_filter[2]; | ||
109 | struct netdev_hw_addr *ha; | ||
110 | |||
111 | /* Hash filter for multicast */ | ||
112 | value = GMAC_FRAME_FILTER_HMC; | ||
113 | |||
114 | memset(mc_filter, 0, sizeof(mc_filter)); | ||
115 | netdev_for_each_mc_addr(ha, dev) { | ||
116 | /* The upper 6 bits of the calculated CRC are used to | ||
117 | index the contens of the hash table */ | ||
118 | int bit_nr = | ||
119 | bitrev32(~crc32_le(~0, ha->addr, 6)) >> 26; | ||
120 | /* The most significant bit determines the register to | ||
121 | * use (H/L) while the other 5 bits determine the bit | ||
122 | * within the register. */ | ||
123 | mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); | ||
124 | } | ||
125 | writel(mc_filter[0], ioaddr + GMAC_HASH_LOW); | ||
126 | writel(mc_filter[1], ioaddr + GMAC_HASH_HIGH); | ||
127 | } | ||
128 | |||
129 | /* Handle multiple unicast addresses (perfect filtering)*/ | ||
130 | if (netdev_uc_count(dev) > GMAC_MAX_UNICAST_ADDRESSES) | ||
131 | /* Switch to promiscuous mode is more than 16 addrs | ||
132 | are required */ | ||
133 | value |= GMAC_FRAME_FILTER_PR; | ||
134 | else { | ||
135 | int reg = 1; | ||
136 | struct netdev_hw_addr *ha; | ||
137 | |||
138 | netdev_for_each_uc_addr(ha, dev) { | ||
139 | dwmac1000_set_umac_addr(ioaddr, ha->addr, reg); | ||
140 | reg++; | ||
141 | } | ||
142 | } | ||
143 | |||
144 | #ifdef FRAME_FILTER_DEBUG | ||
145 | /* Enable Receive all mode (to debug filtering_fail errors) */ | ||
146 | value |= GMAC_FRAME_FILTER_RA; | ||
147 | #endif | ||
148 | writel(value, ioaddr + GMAC_FRAME_FILTER); | ||
149 | |||
150 | CHIP_DBG(KERN_INFO "\tFrame Filter reg: 0x%08x\n\tHash regs: " | ||
151 | "HI 0x%08x, LO 0x%08x\n", readl(ioaddr + GMAC_FRAME_FILTER), | ||
152 | readl(ioaddr + GMAC_HASH_HIGH), readl(ioaddr + GMAC_HASH_LOW)); | ||
153 | } | ||
154 | |||
155 | static void dwmac1000_flow_ctrl(void __iomem *ioaddr, unsigned int duplex, | ||
156 | unsigned int fc, unsigned int pause_time) | ||
157 | { | ||
158 | unsigned int flow = 0; | ||
159 | |||
160 | CHIP_DBG(KERN_DEBUG "GMAC Flow-Control:\n"); | ||
161 | if (fc & FLOW_RX) { | ||
162 | CHIP_DBG(KERN_DEBUG "\tReceive Flow-Control ON\n"); | ||
163 | flow |= GMAC_FLOW_CTRL_RFE; | ||
164 | } | ||
165 | if (fc & FLOW_TX) { | ||
166 | CHIP_DBG(KERN_DEBUG "\tTransmit Flow-Control ON\n"); | ||
167 | flow |= GMAC_FLOW_CTRL_TFE; | ||
168 | } | ||
169 | |||
170 | if (duplex) { | ||
171 | CHIP_DBG(KERN_DEBUG "\tduplex mode: PAUSE %d\n", pause_time); | ||
172 | flow |= (pause_time << GMAC_FLOW_CTRL_PT_SHIFT); | ||
173 | } | ||
174 | |||
175 | writel(flow, ioaddr + GMAC_FLOW_CTRL); | ||
176 | } | ||
177 | |||
178 | static void dwmac1000_pmt(void __iomem *ioaddr, unsigned long mode) | ||
179 | { | ||
180 | unsigned int pmt = 0; | ||
181 | |||
182 | if (mode & WAKE_MAGIC) { | ||
183 | CHIP_DBG(KERN_DEBUG "GMAC: WOL Magic frame\n"); | ||
184 | pmt |= power_down | magic_pkt_en; | ||
185 | } | ||
186 | if (mode & WAKE_UCAST) { | ||
187 | CHIP_DBG(KERN_DEBUG "GMAC: WOL on global unicast\n"); | ||
188 | pmt |= global_unicast; | ||
189 | } | ||
190 | |||
191 | writel(pmt, ioaddr + GMAC_PMT); | ||
192 | } | ||
193 | |||
194 | |||
195 | static void dwmac1000_irq_status(void __iomem *ioaddr) | ||
196 | { | ||
197 | u32 intr_status = readl(ioaddr + GMAC_INT_STATUS); | ||
198 | |||
199 | /* Not used events (e.g. MMC interrupts) are not handled. */ | ||
200 | if ((intr_status & mmc_tx_irq)) | ||
201 | CHIP_DBG(KERN_DEBUG "GMAC: MMC tx interrupt: 0x%08x\n", | ||
202 | readl(ioaddr + GMAC_MMC_TX_INTR)); | ||
203 | if (unlikely(intr_status & mmc_rx_irq)) | ||
204 | CHIP_DBG(KERN_DEBUG "GMAC: MMC rx interrupt: 0x%08x\n", | ||
205 | readl(ioaddr + GMAC_MMC_RX_INTR)); | ||
206 | if (unlikely(intr_status & mmc_rx_csum_offload_irq)) | ||
207 | CHIP_DBG(KERN_DEBUG "GMAC: MMC rx csum offload: 0x%08x\n", | ||
208 | readl(ioaddr + GMAC_MMC_RX_CSUM_OFFLOAD)); | ||
209 | if (unlikely(intr_status & pmt_irq)) { | ||
210 | CHIP_DBG(KERN_DEBUG "GMAC: received Magic frame\n"); | ||
211 | /* clear the PMT bits 5 and 6 by reading the PMT | ||
212 | * status register. */ | ||
213 | readl(ioaddr + GMAC_PMT); | ||
214 | } | ||
215 | } | ||
216 | |||
217 | static const struct stmmac_ops dwmac1000_ops = { | ||
218 | .core_init = dwmac1000_core_init, | ||
219 | .rx_coe = dwmac1000_rx_coe_supported, | ||
220 | .dump_regs = dwmac1000_dump_regs, | ||
221 | .host_irq_status = dwmac1000_irq_status, | ||
222 | .set_filter = dwmac1000_set_filter, | ||
223 | .flow_ctrl = dwmac1000_flow_ctrl, | ||
224 | .pmt = dwmac1000_pmt, | ||
225 | .set_umac_addr = dwmac1000_set_umac_addr, | ||
226 | .get_umac_addr = dwmac1000_get_umac_addr, | ||
227 | }; | ||
228 | |||
229 | struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr) | ||
230 | { | ||
231 | struct mac_device_info *mac; | ||
232 | u32 uid = readl(ioaddr + GMAC_VERSION); | ||
233 | |||
234 | pr_info("\tDWMAC1000 - user ID: 0x%x, Synopsys ID: 0x%x\n", | ||
235 | ((uid & 0x0000ff00) >> 8), (uid & 0x000000ff)); | ||
236 | |||
237 | mac = kzalloc(sizeof(const struct mac_device_info), GFP_KERNEL); | ||
238 | if (!mac) | ||
239 | return NULL; | ||
240 | |||
241 | mac->mac = &dwmac1000_ops; | ||
242 | mac->dma = &dwmac1000_dma_ops; | ||
243 | |||
244 | mac->link.port = GMAC_CONTROL_PS; | ||
245 | mac->link.duplex = GMAC_CONTROL_DM; | ||
246 | mac->link.speed = GMAC_CONTROL_FES; | ||
247 | mac->mii.addr = GMAC_MII_ADDR; | ||
248 | mac->mii.data = GMAC_MII_DATA; | ||
249 | |||
250 | return mac; | ||
251 | } | ||