]>
Commit | Line | Data |
---|---|---|
6c821bd9 JJ |
1 | /* MOXA ART Ethernet (RTL8201CP) driver. |
2 | * | |
3 | * Copyright (C) 2013 Jonas Jensen | |
4 | * | |
5 | * Jonas Jensen <jonas.jensen@gmail.com> | |
6 | * | |
7 | * Based on code from | |
8 | * Moxa Technology Co., Ltd. <www.moxa.com> | |
9 | * | |
10 | * This file is licensed under the terms of the GNU General Public | |
11 | * License version 2. This program is licensed "as is" without any | |
12 | * warranty of any kind, whether express or implied. | |
13 | */ | |
14 | ||
15 | #include <linux/module.h> | |
6c821bd9 JJ |
16 | #include <linux/netdevice.h> |
17 | #include <linux/etherdevice.h> | |
18 | #include <linux/skbuff.h> | |
19 | #include <linux/dma-mapping.h> | |
20 | #include <linux/ethtool.h> | |
21 | #include <linux/platform_device.h> | |
22 | #include <linux/interrupt.h> | |
23 | #include <linux/irq.h> | |
24 | #include <linux/of_address.h> | |
25 | #include <linux/of_irq.h> | |
26 | #include <linux/crc32.h> | |
27 | #include <linux/crc32c.h> | |
c2b341a6 | 28 | #include <linux/circ_buf.h> |
6c821bd9 JJ |
29 | |
30 | #include "moxart_ether.h" | |
31 | ||
59a557be AB |
32 | static inline void moxart_desc_write(u32 data, u32 *desc) |
33 | { | |
34 | *desc = cpu_to_le32(data); | |
35 | } | |
36 | ||
37 | static inline u32 moxart_desc_read(u32 *desc) | |
38 | { | |
39 | return le32_to_cpu(*desc); | |
40 | } | |
41 | ||
6c821bd9 JJ |
42 | static inline void moxart_emac_write(struct net_device *ndev, |
43 | unsigned int reg, unsigned long value) | |
44 | { | |
45 | struct moxart_mac_priv_t *priv = netdev_priv(ndev); | |
46 | ||
47 | writel(value, priv->base + reg); | |
48 | } | |
49 | ||
50 | static void moxart_update_mac_address(struct net_device *ndev) | |
51 | { | |
52 | moxart_emac_write(ndev, REG_MAC_MS_ADDRESS, | |
53 | ((ndev->dev_addr[0] << 8) | (ndev->dev_addr[1]))); | |
54 | moxart_emac_write(ndev, REG_MAC_MS_ADDRESS + 4, | |
55 | ((ndev->dev_addr[2] << 24) | | |
56 | (ndev->dev_addr[3] << 16) | | |
57 | (ndev->dev_addr[4] << 8) | | |
58 | (ndev->dev_addr[5]))); | |
59 | } | |
60 | ||
61 | static int moxart_set_mac_address(struct net_device *ndev, void *addr) | |
62 | { | |
63 | struct sockaddr *address = addr; | |
64 | ||
65 | if (!is_valid_ether_addr(address->sa_data)) | |
66 | return -EADDRNOTAVAIL; | |
67 | ||
68 | memcpy(ndev->dev_addr, address->sa_data, ndev->addr_len); | |
69 | moxart_update_mac_address(ndev); | |
70 | ||
71 | return 0; | |
72 | } | |
73 | ||
74 | static void moxart_mac_free_memory(struct net_device *ndev) | |
75 | { | |
76 | struct moxart_mac_priv_t *priv = netdev_priv(ndev); | |
77 | int i; | |
78 | ||
79 | for (i = 0; i < RX_DESC_NUM; i++) | |
80 | dma_unmap_single(&ndev->dev, priv->rx_mapping[i], | |
81 | priv->rx_buf_size, DMA_FROM_DEVICE); | |
82 | ||
83 | if (priv->tx_desc_base) | |
5dac33ad CH |
84 | dma_free_coherent(&priv->pdev->dev, |
85 | TX_REG_DESC_SIZE * TX_DESC_NUM, | |
6c821bd9 JJ |
86 | priv->tx_desc_base, priv->tx_base); |
87 | ||
88 | if (priv->rx_desc_base) | |
5dac33ad CH |
89 | dma_free_coherent(&priv->pdev->dev, |
90 | RX_REG_DESC_SIZE * RX_DESC_NUM, | |
6c821bd9 JJ |
91 | priv->rx_desc_base, priv->rx_base); |
92 | ||
93 | kfree(priv->tx_buf_base); | |
94 | kfree(priv->rx_buf_base); | |
95 | } | |
96 | ||
97 | static void moxart_mac_reset(struct net_device *ndev) | |
98 | { | |
99 | struct moxart_mac_priv_t *priv = netdev_priv(ndev); | |
100 | ||
101 | writel(SW_RST, priv->base + REG_MAC_CTRL); | |
102 | while (readl(priv->base + REG_MAC_CTRL) & SW_RST) | |
103 | mdelay(10); | |
104 | ||
105 | writel(0, priv->base + REG_INTERRUPT_MASK); | |
106 | ||
107 | priv->reg_maccr = RX_BROADPKT | FULLDUP | CRC_APD | RX_FTL; | |
108 | } | |
109 | ||
110 | static void moxart_mac_enable(struct net_device *ndev) | |
111 | { | |
112 | struct moxart_mac_priv_t *priv = netdev_priv(ndev); | |
113 | ||
114 | writel(0x00001010, priv->base + REG_INT_TIMER_CTRL); | |
115 | writel(0x00000001, priv->base + REG_APOLL_TIMER_CTRL); | |
116 | writel(0x00000390, priv->base + REG_DMA_BLEN_CTRL); | |
117 | ||
118 | priv->reg_imr |= (RPKT_FINISH_M | XPKT_FINISH_M); | |
119 | writel(priv->reg_imr, priv->base + REG_INTERRUPT_MASK); | |
120 | ||
121 | priv->reg_maccr |= (RCV_EN | XMT_EN | RDMA_EN | XDMA_EN); | |
122 | writel(priv->reg_maccr, priv->base + REG_MAC_CTRL); | |
123 | } | |
124 | ||
125 | static void moxart_mac_setup_desc_ring(struct net_device *ndev) | |
126 | { | |
127 | struct moxart_mac_priv_t *priv = netdev_priv(ndev); | |
59a557be | 128 | void *desc; |
6c821bd9 JJ |
129 | int i; |
130 | ||
131 | for (i = 0; i < TX_DESC_NUM; i++) { | |
132 | desc = priv->tx_desc_base + i * TX_REG_DESC_SIZE; | |
133 | memset(desc, 0, TX_REG_DESC_SIZE); | |
134 | ||
135 | priv->tx_buf[i] = priv->tx_buf_base + priv->tx_buf_size * i; | |
136 | } | |
59a557be | 137 | moxart_desc_write(TX_DESC1_END, desc + TX_REG_OFFSET_DESC1); |
6c821bd9 JJ |
138 | |
139 | priv->tx_head = 0; | |
140 | priv->tx_tail = 0; | |
141 | ||
142 | for (i = 0; i < RX_DESC_NUM; i++) { | |
143 | desc = priv->rx_desc_base + i * RX_REG_DESC_SIZE; | |
144 | memset(desc, 0, RX_REG_DESC_SIZE); | |
59a557be AB |
145 | moxart_desc_write(RX_DESC0_DMA_OWN, desc + RX_REG_OFFSET_DESC0); |
146 | moxart_desc_write(RX_BUF_SIZE & RX_DESC1_BUF_SIZE_MASK, | |
6c821bd9 JJ |
147 | desc + RX_REG_OFFSET_DESC1); |
148 | ||
149 | priv->rx_buf[i] = priv->rx_buf_base + priv->rx_buf_size * i; | |
150 | priv->rx_mapping[i] = dma_map_single(&ndev->dev, | |
151 | priv->rx_buf[i], | |
152 | priv->rx_buf_size, | |
153 | DMA_FROM_DEVICE); | |
154 | if (dma_mapping_error(&ndev->dev, priv->rx_mapping[i])) | |
155 | netdev_err(ndev, "DMA mapping error\n"); | |
156 | ||
59a557be | 157 | moxart_desc_write(priv->rx_mapping[i], |
6c821bd9 | 158 | desc + RX_REG_OFFSET_DESC2 + RX_DESC2_ADDRESS_PHYS); |
59a557be | 159 | moxart_desc_write((uintptr_t)priv->rx_buf[i], |
6c821bd9 JJ |
160 | desc + RX_REG_OFFSET_DESC2 + RX_DESC2_ADDRESS_VIRT); |
161 | } | |
59a557be | 162 | moxart_desc_write(RX_DESC1_END, desc + RX_REG_OFFSET_DESC1); |
6c821bd9 JJ |
163 | |
164 | priv->rx_head = 0; | |
165 | ||
2fcc4402 | 166 | /* reset the MAC controller TX/RX descriptor base address */ |
6c821bd9 JJ |
167 | writel(priv->tx_base, priv->base + REG_TXR_BASE_ADDRESS); |
168 | writel(priv->rx_base, priv->base + REG_RXR_BASE_ADDRESS); | |
169 | } | |
170 | ||
171 | static int moxart_mac_open(struct net_device *ndev) | |
172 | { | |
173 | struct moxart_mac_priv_t *priv = netdev_priv(ndev); | |
174 | ||
175 | if (!is_valid_ether_addr(ndev->dev_addr)) | |
176 | return -EADDRNOTAVAIL; | |
177 | ||
178 | napi_enable(&priv->napi); | |
179 | ||
180 | moxart_mac_reset(ndev); | |
181 | moxart_update_mac_address(ndev); | |
182 | moxart_mac_setup_desc_ring(ndev); | |
183 | moxart_mac_enable(ndev); | |
184 | netif_start_queue(ndev); | |
185 | ||
186 | netdev_dbg(ndev, "%s: IMR=0x%x, MACCR=0x%x\n", | |
187 | __func__, readl(priv->base + REG_INTERRUPT_MASK), | |
188 | readl(priv->base + REG_MAC_CTRL)); | |
189 | ||
190 | return 0; | |
191 | } | |
192 | ||
193 | static int moxart_mac_stop(struct net_device *ndev) | |
194 | { | |
195 | struct moxart_mac_priv_t *priv = netdev_priv(ndev); | |
196 | ||
197 | napi_disable(&priv->napi); | |
198 | ||
199 | netif_stop_queue(ndev); | |
200 | ||
201 | /* disable all interrupts */ | |
202 | writel(0, priv->base + REG_INTERRUPT_MASK); | |
203 | ||
204 | /* disable all functions */ | |
205 | writel(0, priv->base + REG_MAC_CTRL); | |
206 | ||
207 | return 0; | |
208 | } | |
209 | ||
210 | static int moxart_rx_poll(struct napi_struct *napi, int budget) | |
211 | { | |
212 | struct moxart_mac_priv_t *priv = container_of(napi, | |
213 | struct moxart_mac_priv_t, | |
214 | napi); | |
215 | struct net_device *ndev = priv->ndev; | |
216 | struct sk_buff *skb; | |
59a557be | 217 | void *desc; |
6c821bd9 JJ |
218 | unsigned int desc0, len; |
219 | int rx_head = priv->rx_head; | |
220 | int rx = 0; | |
221 | ||
2b7890e7 | 222 | while (rx < budget) { |
6c821bd9 | 223 | desc = priv->rx_desc_base + (RX_REG_DESC_SIZE * rx_head); |
59a557be AB |
224 | desc0 = moxart_desc_read(desc + RX_REG_OFFSET_DESC0); |
225 | rmb(); /* ensure desc0 is up to date */ | |
6c821bd9 JJ |
226 | |
227 | if (desc0 & RX_DESC0_DMA_OWN) | |
228 | break; | |
229 | ||
230 | if (desc0 & (RX_DESC0_ERR | RX_DESC0_CRC_ERR | RX_DESC0_FTL | | |
231 | RX_DESC0_RUNT | RX_DESC0_ODD_NB)) { | |
232 | net_dbg_ratelimited("packet error\n"); | |
8bf66b9d TK |
233 | ndev->stats.rx_dropped++; |
234 | ndev->stats.rx_errors++; | |
2b7890e7 | 235 | goto rx_next; |
6c821bd9 JJ |
236 | } |
237 | ||
238 | len = desc0 & RX_DESC0_FRAME_LEN_MASK; | |
239 | ||
240 | if (len > RX_BUF_SIZE) | |
241 | len = RX_BUF_SIZE; | |
242 | ||
777fbc31 JJ |
243 | dma_sync_single_for_cpu(&ndev->dev, |
244 | priv->rx_mapping[rx_head], | |
245 | priv->rx_buf_size, DMA_FROM_DEVICE); | |
9fe1b3bc JJ |
246 | skb = netdev_alloc_skb_ip_align(ndev, len); |
247 | ||
6c821bd9 | 248 | if (unlikely(!skb)) { |
9fe1b3bc | 249 | net_dbg_ratelimited("netdev_alloc_skb_ip_align failed\n"); |
8bf66b9d TK |
250 | ndev->stats.rx_dropped++; |
251 | ndev->stats.rx_errors++; | |
2b7890e7 | 252 | goto rx_next; |
6c821bd9 JJ |
253 | } |
254 | ||
9fe1b3bc | 255 | memcpy(skb->data, priv->rx_buf[rx_head], len); |
6c821bd9 JJ |
256 | skb_put(skb, len); |
257 | skb->protocol = eth_type_trans(skb, ndev); | |
258 | napi_gro_receive(&priv->napi, skb); | |
259 | rx++; | |
260 | ||
8bf66b9d TK |
261 | ndev->stats.rx_packets++; |
262 | ndev->stats.rx_bytes += len; | |
6c821bd9 | 263 | if (desc0 & RX_DESC0_MULTICAST) |
8bf66b9d | 264 | ndev->stats.multicast++; |
6c821bd9 | 265 | |
2b7890e7 | 266 | rx_next: |
59a557be AB |
267 | wmb(); /* prevent setting ownership back too early */ |
268 | moxart_desc_write(RX_DESC0_DMA_OWN, desc + RX_REG_OFFSET_DESC0); | |
6c821bd9 JJ |
269 | |
270 | rx_head = RX_NEXT(rx_head); | |
271 | priv->rx_head = rx_head; | |
6c821bd9 JJ |
272 | } |
273 | ||
68f70d83 | 274 | if (rx < budget) |
6ad20165 | 275 | napi_complete_done(napi, rx); |
6c821bd9 JJ |
276 | |
277 | priv->reg_imr |= RPKT_FINISH_M; | |
278 | writel(priv->reg_imr, priv->base + REG_INTERRUPT_MASK); | |
279 | ||
280 | return rx; | |
281 | } | |
282 | ||
c2b341a6 JJ |
283 | static int moxart_tx_queue_space(struct net_device *ndev) |
284 | { | |
285 | struct moxart_mac_priv_t *priv = netdev_priv(ndev); | |
286 | ||
287 | return CIRC_SPACE(priv->tx_head, priv->tx_tail, TX_DESC_NUM); | |
288 | } | |
289 | ||
6c821bd9 JJ |
290 | static void moxart_tx_finished(struct net_device *ndev) |
291 | { | |
292 | struct moxart_mac_priv_t *priv = netdev_priv(ndev); | |
d458f4c5 SL |
293 | unsigned int tx_head = priv->tx_head; |
294 | unsigned int tx_tail = priv->tx_tail; | |
6c821bd9 JJ |
295 | |
296 | while (tx_tail != tx_head) { | |
297 | dma_unmap_single(&ndev->dev, priv->tx_mapping[tx_tail], | |
298 | priv->tx_len[tx_tail], DMA_TO_DEVICE); | |
299 | ||
8bf66b9d TK |
300 | ndev->stats.tx_packets++; |
301 | ndev->stats.tx_bytes += priv->tx_skb[tx_tail]->len; | |
6c821bd9 | 302 | |
412261d5 | 303 | dev_consume_skb_irq(priv->tx_skb[tx_tail]); |
6c821bd9 JJ |
304 | priv->tx_skb[tx_tail] = NULL; |
305 | ||
306 | tx_tail = TX_NEXT(tx_tail); | |
307 | } | |
308 | priv->tx_tail = tx_tail; | |
c2b341a6 JJ |
309 | if (netif_queue_stopped(ndev) && |
310 | moxart_tx_queue_space(ndev) >= TX_WAKE_THRESHOLD) | |
311 | netif_wake_queue(ndev); | |
6c821bd9 JJ |
312 | } |
313 | ||
314 | static irqreturn_t moxart_mac_interrupt(int irq, void *dev_id) | |
315 | { | |
c45c5d03 | 316 | struct net_device *ndev = (struct net_device *)dev_id; |
6c821bd9 JJ |
317 | struct moxart_mac_priv_t *priv = netdev_priv(ndev); |
318 | unsigned int ists = readl(priv->base + REG_INTERRUPT_STATUS); | |
319 | ||
320 | if (ists & XPKT_OK_INT_STS) | |
321 | moxart_tx_finished(ndev); | |
322 | ||
323 | if (ists & RPKT_FINISH) { | |
324 | if (napi_schedule_prep(&priv->napi)) { | |
325 | priv->reg_imr &= ~RPKT_FINISH_M; | |
326 | writel(priv->reg_imr, priv->base + REG_INTERRUPT_MASK); | |
327 | __napi_schedule(&priv->napi); | |
328 | } | |
329 | } | |
330 | ||
331 | return IRQ_HANDLED; | |
332 | } | |
333 | ||
334 | static int moxart_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev) | |
335 | { | |
336 | struct moxart_mac_priv_t *priv = netdev_priv(ndev); | |
59a557be | 337 | void *desc; |
6c821bd9 | 338 | unsigned int len; |
c2b341a6 | 339 | unsigned int tx_head; |
6c821bd9 | 340 | u32 txdes1; |
0aa857f8 | 341 | int ret = NETDEV_TX_BUSY; |
6c821bd9 | 342 | |
c2b341a6 JJ |
343 | spin_lock_irq(&priv->txlock); |
344 | ||
345 | tx_head = priv->tx_head; | |
6c821bd9 JJ |
346 | desc = priv->tx_desc_base + (TX_REG_DESC_SIZE * tx_head); |
347 | ||
c2b341a6 JJ |
348 | if (moxart_tx_queue_space(ndev) == 1) |
349 | netif_stop_queue(ndev); | |
350 | ||
59a557be | 351 | if (moxart_desc_read(desc + TX_REG_OFFSET_DESC0) & TX_DESC0_DMA_OWN) { |
6c821bd9 | 352 | net_dbg_ratelimited("no TX space for packet\n"); |
8bf66b9d | 353 | ndev->stats.tx_dropped++; |
0aa857f8 | 354 | goto out_unlock; |
6c821bd9 | 355 | } |
59a557be | 356 | rmb(); /* ensure data is only read that had TX_DESC0_DMA_OWN cleared */ |
6c821bd9 JJ |
357 | |
358 | len = skb->len > TX_BUF_SIZE ? TX_BUF_SIZE : skb->len; | |
359 | ||
360 | priv->tx_mapping[tx_head] = dma_map_single(&ndev->dev, skb->data, | |
361 | len, DMA_TO_DEVICE); | |
362 | if (dma_mapping_error(&ndev->dev, priv->tx_mapping[tx_head])) { | |
363 | netdev_err(ndev, "DMA mapping error\n"); | |
0aa857f8 | 364 | goto out_unlock; |
6c821bd9 JJ |
365 | } |
366 | ||
367 | priv->tx_len[tx_head] = len; | |
368 | priv->tx_skb[tx_head] = skb; | |
369 | ||
59a557be | 370 | moxart_desc_write(priv->tx_mapping[tx_head], |
6c821bd9 | 371 | desc + TX_REG_OFFSET_DESC2 + TX_DESC2_ADDRESS_PHYS); |
59a557be | 372 | moxart_desc_write((uintptr_t)skb->data, |
6c821bd9 JJ |
373 | desc + TX_REG_OFFSET_DESC2 + TX_DESC2_ADDRESS_VIRT); |
374 | ||
375 | if (skb->len < ETH_ZLEN) { | |
376 | memset(&skb->data[skb->len], | |
377 | 0, ETH_ZLEN - skb->len); | |
378 | len = ETH_ZLEN; | |
379 | } | |
380 | ||
777fbc31 JJ |
381 | dma_sync_single_for_device(&ndev->dev, priv->tx_mapping[tx_head], |
382 | priv->tx_buf_size, DMA_TO_DEVICE); | |
383 | ||
b853f319 JJ |
384 | txdes1 = TX_DESC1_LTS | TX_DESC1_FTS | (len & TX_DESC1_BUF_SIZE_MASK); |
385 | if (tx_head == TX_DESC_NUM_MASK) | |
386 | txdes1 |= TX_DESC1_END; | |
59a557be AB |
387 | moxart_desc_write(txdes1, desc + TX_REG_OFFSET_DESC1); |
388 | wmb(); /* flush descriptor before transferring ownership */ | |
389 | moxart_desc_write(TX_DESC0_DMA_OWN, desc + TX_REG_OFFSET_DESC0); | |
6c821bd9 JJ |
390 | |
391 | /* start to send packet */ | |
392 | writel(0xffffffff, priv->base + REG_TX_POLL_DEMAND); | |
393 | ||
394 | priv->tx_head = TX_NEXT(tx_head); | |
395 | ||
860e9538 | 396 | netif_trans_update(ndev); |
0aa857f8 WY |
397 | ret = NETDEV_TX_OK; |
398 | out_unlock: | |
6c821bd9 JJ |
399 | spin_unlock_irq(&priv->txlock); |
400 | ||
0aa857f8 | 401 | return ret; |
6c821bd9 JJ |
402 | } |
403 | ||
6c821bd9 JJ |
404 | static void moxart_mac_setmulticast(struct net_device *ndev) |
405 | { | |
406 | struct moxart_mac_priv_t *priv = netdev_priv(ndev); | |
407 | struct netdev_hw_addr *ha; | |
408 | int crc_val; | |
409 | ||
410 | netdev_for_each_mc_addr(ha, ndev) { | |
411 | crc_val = crc32_le(~0, ha->addr, ETH_ALEN); | |
412 | crc_val = (crc_val >> 26) & 0x3f; | |
413 | if (crc_val >= 32) { | |
414 | writel(readl(priv->base + REG_MCAST_HASH_TABLE1) | | |
415 | (1UL << (crc_val - 32)), | |
416 | priv->base + REG_MCAST_HASH_TABLE1); | |
417 | } else { | |
418 | writel(readl(priv->base + REG_MCAST_HASH_TABLE0) | | |
419 | (1UL << crc_val), | |
420 | priv->base + REG_MCAST_HASH_TABLE0); | |
421 | } | |
422 | } | |
423 | } | |
424 | ||
425 | static void moxart_mac_set_rx_mode(struct net_device *ndev) | |
426 | { | |
427 | struct moxart_mac_priv_t *priv = netdev_priv(ndev); | |
428 | ||
429 | spin_lock_irq(&priv->txlock); | |
430 | ||
431 | (ndev->flags & IFF_PROMISC) ? (priv->reg_maccr |= RCV_ALL) : | |
432 | (priv->reg_maccr &= ~RCV_ALL); | |
433 | ||
434 | (ndev->flags & IFF_ALLMULTI) ? (priv->reg_maccr |= RX_MULTIPKT) : | |
435 | (priv->reg_maccr &= ~RX_MULTIPKT); | |
436 | ||
437 | if ((ndev->flags & IFF_MULTICAST) && netdev_mc_count(ndev)) { | |
438 | priv->reg_maccr |= HT_MULTI_EN; | |
439 | moxart_mac_setmulticast(ndev); | |
440 | } else { | |
441 | priv->reg_maccr &= ~HT_MULTI_EN; | |
442 | } | |
443 | ||
444 | writel(priv->reg_maccr, priv->base + REG_MAC_CTRL); | |
445 | ||
446 | spin_unlock_irq(&priv->txlock); | |
447 | } | |
448 | ||
30bd2f52 | 449 | static const struct net_device_ops moxart_netdev_ops = { |
6c821bd9 JJ |
450 | .ndo_open = moxart_mac_open, |
451 | .ndo_stop = moxart_mac_stop, | |
452 | .ndo_start_xmit = moxart_mac_start_xmit, | |
6c821bd9 JJ |
453 | .ndo_set_rx_mode = moxart_mac_set_rx_mode, |
454 | .ndo_set_mac_address = moxart_set_mac_address, | |
455 | .ndo_validate_addr = eth_validate_addr, | |
6c821bd9 JJ |
456 | }; |
457 | ||
458 | static int moxart_mac_probe(struct platform_device *pdev) | |
459 | { | |
460 | struct device *p_dev = &pdev->dev; | |
461 | struct device_node *node = p_dev->of_node; | |
462 | struct net_device *ndev; | |
463 | struct moxart_mac_priv_t *priv; | |
464 | struct resource *res; | |
465 | unsigned int irq; | |
466 | int ret; | |
467 | ||
468 | ndev = alloc_etherdev(sizeof(struct moxart_mac_priv_t)); | |
469 | if (!ndev) | |
470 | return -ENOMEM; | |
471 | ||
472 | irq = irq_of_parse_and_map(node, 0); | |
473 | if (irq <= 0) { | |
474 | netdev_err(ndev, "irq_of_parse_and_map failed\n"); | |
bdfd6304 WY |
475 | ret = -EINVAL; |
476 | goto irq_map_fail; | |
6c821bd9 JJ |
477 | } |
478 | ||
479 | priv = netdev_priv(ndev); | |
480 | priv->ndev = ndev; | |
5dac33ad | 481 | priv->pdev = pdev; |
6c821bd9 JJ |
482 | |
483 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
484 | ndev->base_addr = res->start; | |
485 | priv->base = devm_ioremap_resource(p_dev, res); | |
1d3cd177 | 486 | if (IS_ERR(priv->base)) { |
6c821bd9 | 487 | dev_err(p_dev, "devm_ioremap_resource failed\n"); |
1d3cd177 | 488 | ret = PTR_ERR(priv->base); |
6c821bd9 JJ |
489 | goto init_fail; |
490 | } | |
491 | ||
492 | spin_lock_init(&priv->txlock); | |
493 | ||
494 | priv->tx_buf_size = TX_BUF_SIZE; | |
9fe1b3bc | 495 | priv->rx_buf_size = RX_BUF_SIZE; |
6c821bd9 | 496 | |
5dac33ad | 497 | priv->tx_desc_base = dma_alloc_coherent(&pdev->dev, TX_REG_DESC_SIZE * |
6c821bd9 JJ |
498 | TX_DESC_NUM, &priv->tx_base, |
499 | GFP_DMA | GFP_KERNEL); | |
e8048b84 | 500 | if (!priv->tx_desc_base) { |
bdfd6304 | 501 | ret = -ENOMEM; |
6c821bd9 | 502 | goto init_fail; |
bdfd6304 | 503 | } |
6c821bd9 | 504 | |
5dac33ad | 505 | priv->rx_desc_base = dma_alloc_coherent(&pdev->dev, RX_REG_DESC_SIZE * |
6c821bd9 JJ |
506 | RX_DESC_NUM, &priv->rx_base, |
507 | GFP_DMA | GFP_KERNEL); | |
e8048b84 | 508 | if (!priv->rx_desc_base) { |
bdfd6304 | 509 | ret = -ENOMEM; |
6c821bd9 | 510 | goto init_fail; |
bdfd6304 | 511 | } |
6c821bd9 | 512 | |
6da2ec56 KC |
513 | priv->tx_buf_base = kmalloc_array(priv->tx_buf_size, TX_DESC_NUM, |
514 | GFP_ATOMIC); | |
bdfd6304 WY |
515 | if (!priv->tx_buf_base) { |
516 | ret = -ENOMEM; | |
6c821bd9 | 517 | goto init_fail; |
bdfd6304 | 518 | } |
6c821bd9 | 519 | |
6da2ec56 KC |
520 | priv->rx_buf_base = kmalloc_array(priv->rx_buf_size, RX_DESC_NUM, |
521 | GFP_ATOMIC); | |
bdfd6304 WY |
522 | if (!priv->rx_buf_base) { |
523 | ret = -ENOMEM; | |
6c821bd9 | 524 | goto init_fail; |
bdfd6304 | 525 | } |
6c821bd9 JJ |
526 | |
527 | platform_set_drvdata(pdev, ndev); | |
528 | ||
529 | ret = devm_request_irq(p_dev, irq, moxart_mac_interrupt, 0, | |
530 | pdev->name, ndev); | |
531 | if (ret) { | |
532 | netdev_err(ndev, "devm_request_irq failed\n"); | |
533 | goto init_fail; | |
534 | } | |
535 | ||
6c821bd9 JJ |
536 | ndev->netdev_ops = &moxart_netdev_ops; |
537 | netif_napi_add(ndev, &priv->napi, moxart_rx_poll, RX_DESC_NUM); | |
538 | ndev->priv_flags |= IFF_UNICAST_FLT; | |
539 | ndev->irq = irq; | |
540 | ||
541 | SET_NETDEV_DEV(ndev, &pdev->dev); | |
542 | ||
543 | ret = register_netdev(ndev); | |
544 | if (ret) { | |
545 | free_netdev(ndev); | |
546 | goto init_fail; | |
547 | } | |
548 | ||
549 | netdev_dbg(ndev, "%s: IRQ=%d address=%pM\n", | |
550 | __func__, ndev->irq, ndev->dev_addr); | |
551 | ||
552 | return 0; | |
553 | ||
554 | init_fail: | |
555 | netdev_err(ndev, "init failed\n"); | |
556 | moxart_mac_free_memory(ndev); | |
bdfd6304 WY |
557 | irq_map_fail: |
558 | free_netdev(ndev); | |
6c821bd9 JJ |
559 | return ret; |
560 | } | |
561 | ||
562 | static int moxart_remove(struct platform_device *pdev) | |
563 | { | |
564 | struct net_device *ndev = platform_get_drvdata(pdev); | |
565 | ||
566 | unregister_netdev(ndev); | |
ee8d2267 | 567 | devm_free_irq(&pdev->dev, ndev->irq, ndev); |
6c821bd9 | 568 | moxart_mac_free_memory(ndev); |
6c821bd9 JJ |
569 | free_netdev(ndev); |
570 | ||
571 | return 0; | |
572 | } | |
573 | ||
574 | static const struct of_device_id moxart_mac_match[] = { | |
575 | { .compatible = "moxa,moxart-mac" }, | |
576 | { } | |
577 | }; | |
ebd8ebf0 | 578 | MODULE_DEVICE_TABLE(of, moxart_mac_match); |
6c821bd9 | 579 | |
437a3ae1 | 580 | static struct platform_driver moxart_mac_driver = { |
6c821bd9 JJ |
581 | .probe = moxart_mac_probe, |
582 | .remove = moxart_remove, | |
583 | .driver = { | |
584 | .name = "moxart-ethernet", | |
6c821bd9 JJ |
585 | .of_match_table = moxart_mac_match, |
586 | }, | |
587 | }; | |
588 | module_platform_driver(moxart_mac_driver); | |
589 | ||
590 | MODULE_DESCRIPTION("MOXART RTL8201CP Ethernet driver"); | |
591 | MODULE_LICENSE("GPL v2"); | |
592 | MODULE_AUTHOR("Jonas Jensen <jonas.jensen@gmail.com>"); |