Commit | Line | Data |
---|---|---|
b886d83c | 1 | // SPDX-License-Identifier: GPL-2.0-only |
a50a97d4 WZ |
2 | /* |
3 | * Copyright (c) 2008-2009 Nuvoton technology corporation. | |
4 | * | |
5 | * Wan ZongShun <mcuos.com@gmail.com> | |
a50a97d4 WZ |
6 | */ |
7 | ||
8 | #include <linux/module.h> | |
9 | #include <linux/init.h> | |
854426ef | 10 | #include <linux/interrupt.h> |
a50a97d4 WZ |
11 | #include <linux/mii.h> |
12 | #include <linux/netdevice.h> | |
13 | #include <linux/etherdevice.h> | |
14 | #include <linux/skbuff.h> | |
15 | #include <linux/ethtool.h> | |
16 | #include <linux/platform_device.h> | |
17 | #include <linux/clk.h> | |
5a0e3ad6 | 18 | #include <linux/gfp.h> |
a50a97d4 WZ |
19 | |
20 | #define DRV_MODULE_NAME "w90p910-emc" | |
21 | #define DRV_MODULE_VERSION "0.1" | |
22 | ||
23 | /* Ethernet MAC Registers */ | |
24 | #define REG_CAMCMR 0x00 | |
25 | #define REG_CAMEN 0x04 | |
26 | #define REG_CAMM_BASE 0x08 | |
27 | #define REG_CAML_BASE 0x0c | |
28 | #define REG_TXDLSA 0x88 | |
29 | #define REG_RXDLSA 0x8C | |
30 | #define REG_MCMDR 0x90 | |
31 | #define REG_MIID 0x94 | |
32 | #define REG_MIIDA 0x98 | |
33 | #define REG_FFTCR 0x9C | |
34 | #define REG_TSDR 0xa0 | |
35 | #define REG_RSDR 0xa4 | |
36 | #define REG_DMARFC 0xa8 | |
37 | #define REG_MIEN 0xac | |
38 | #define REG_MISTA 0xb0 | |
39 | #define REG_CTXDSA 0xcc | |
40 | #define REG_CTXBSA 0xd0 | |
41 | #define REG_CRXDSA 0xd4 | |
42 | #define REG_CRXBSA 0xd8 | |
43 | ||
44 | /* mac controller bit */ | |
45 | #define MCMDR_RXON 0x01 | |
46 | #define MCMDR_ACP (0x01 << 3) | |
47 | #define MCMDR_SPCRC (0x01 << 5) | |
48 | #define MCMDR_TXON (0x01 << 8) | |
49 | #define MCMDR_FDUP (0x01 << 18) | |
50 | #define MCMDR_ENMDC (0x01 << 19) | |
51 | #define MCMDR_OPMOD (0x01 << 20) | |
52 | #define SWR (0x01 << 24) | |
53 | ||
54 | /* cam command regiser */ | |
55 | #define CAMCMR_AUP 0x01 | |
56 | #define CAMCMR_AMP (0x01 << 1) | |
57 | #define CAMCMR_ABP (0x01 << 2) | |
58 | #define CAMCMR_CCAM (0x01 << 3) | |
59 | #define CAMCMR_ECMP (0x01 << 4) | |
60 | #define CAM0EN 0x01 | |
61 | ||
62 | /* mac mii controller bit */ | |
63 | #define MDCCR (0x0a << 20) | |
64 | #define PHYAD (0x01 << 8) | |
65 | #define PHYWR (0x01 << 16) | |
66 | #define PHYBUSY (0x01 << 17) | |
67 | #define PHYPRESP (0x01 << 18) | |
68 | #define CAM_ENTRY_SIZE 0x08 | |
69 | ||
70 | /* rx and tx status */ | |
71 | #define TXDS_TXCP (0x01 << 19) | |
72 | #define RXDS_CRCE (0x01 << 17) | |
73 | #define RXDS_PTLE (0x01 << 19) | |
74 | #define RXDS_RXGD (0x01 << 20) | |
75 | #define RXDS_ALIE (0x01 << 21) | |
76 | #define RXDS_RP (0x01 << 22) | |
77 | ||
78 | /* mac interrupt status*/ | |
79 | #define MISTA_EXDEF (0x01 << 19) | |
80 | #define MISTA_TXBERR (0x01 << 24) | |
81 | #define MISTA_TDU (0x01 << 23) | |
82 | #define MISTA_RDU (0x01 << 10) | |
83 | #define MISTA_RXBERR (0x01 << 11) | |
84 | ||
85 | #define ENSTART 0x01 | |
86 | #define ENRXINTR 0x01 | |
87 | #define ENRXGD (0x01 << 4) | |
88 | #define ENRXBERR (0x01 << 11) | |
89 | #define ENTXINTR (0x01 << 16) | |
90 | #define ENTXCP (0x01 << 18) | |
91 | #define ENTXABT (0x01 << 21) | |
92 | #define ENTXBERR (0x01 << 24) | |
93 | #define ENMDC (0x01 << 19) | |
94 | #define PHYBUSY (0x01 << 17) | |
95 | #define MDCCR_VAL 0xa00000 | |
96 | ||
97 | /* rx and tx owner bit */ | |
98 | #define RX_OWEN_DMA (0x01 << 31) | |
99 | #define RX_OWEN_CPU (~(0x03 << 30)) | |
100 | #define TX_OWEN_DMA (0x01 << 31) | |
101 | #define TX_OWEN_CPU (~(0x01 << 31)) | |
102 | ||
103 | /* tx frame desc controller bit */ | |
104 | #define MACTXINTEN 0x04 | |
105 | #define CRCMODE 0x02 | |
106 | #define PADDINGMODE 0x01 | |
107 | ||
108 | /* fftcr controller bit */ | |
109 | #define TXTHD (0x03 << 8) | |
110 | #define BLENGTH (0x01 << 20) | |
111 | ||
112 | /* global setting for driver */ | |
113 | #define RX_DESC_SIZE 50 | |
114 | #define TX_DESC_SIZE 10 | |
115 | #define MAX_RBUFF_SZ 0x600 | |
116 | #define MAX_TBUFF_SZ 0x600 | |
c63fdf46 | 117 | #define TX_TIMEOUT (HZ/2) |
a50a97d4 WZ |
118 | #define DELAY 1000 |
119 | #define CAM0 0x0 | |
120 | ||
121 | static int w90p910_mdio_read(struct net_device *dev, int phy_id, int reg); | |
122 | ||
123 | struct w90p910_rxbd { | |
124 | unsigned int sl; | |
125 | unsigned int buffer; | |
126 | unsigned int reserved; | |
127 | unsigned int next; | |
128 | }; | |
129 | ||
130 | struct w90p910_txbd { | |
131 | unsigned int mode; | |
132 | unsigned int buffer; | |
133 | unsigned int sl; | |
134 | unsigned int next; | |
135 | }; | |
136 | ||
137 | struct recv_pdesc { | |
138 | struct w90p910_rxbd desclist[RX_DESC_SIZE]; | |
139 | char recv_buf[RX_DESC_SIZE][MAX_RBUFF_SZ]; | |
140 | }; | |
141 | ||
142 | struct tran_pdesc { | |
143 | struct w90p910_txbd desclist[TX_DESC_SIZE]; | |
1e5053b7 | 144 | char tran_buf[TX_DESC_SIZE][MAX_TBUFF_SZ]; |
a50a97d4 WZ |
145 | }; |
146 | ||
147 | struct w90p910_ether { | |
148 | struct recv_pdesc *rdesc; | |
a50a97d4 | 149 | struct tran_pdesc *tdesc; |
1e5053b7 WZ |
150 | dma_addr_t rdesc_phys; |
151 | dma_addr_t tdesc_phys; | |
a50a97d4 | 152 | struct platform_device *pdev; |
1e5053b7 | 153 | struct resource *res; |
a50a97d4 WZ |
154 | struct sk_buff *skb; |
155 | struct clk *clk; | |
156 | struct clk *rmiiclk; | |
157 | struct mii_if_info mii; | |
158 | struct timer_list check_timer; | |
159 | void __iomem *reg; | |
ddb14175 WZ |
160 | int rxirq; |
161 | int txirq; | |
a50a97d4 WZ |
162 | unsigned int cur_tx; |
163 | unsigned int cur_rx; | |
164 | unsigned int finish_tx; | |
165 | unsigned int rx_packets; | |
166 | unsigned int rx_bytes; | |
167 | unsigned int start_tx_ptr; | |
168 | unsigned int start_rx_ptr; | |
169 | unsigned int linkflag; | |
a50a97d4 WZ |
170 | }; |
171 | ||
172 | static void update_linkspeed_register(struct net_device *dev, | |
173 | unsigned int speed, unsigned int duplex) | |
174 | { | |
175 | struct w90p910_ether *ether = netdev_priv(dev); | |
176 | unsigned int val; | |
177 | ||
178 | val = __raw_readl(ether->reg + REG_MCMDR); | |
179 | ||
180 | if (speed == SPEED_100) { | |
181 | /* 100 full/half duplex */ | |
182 | if (duplex == DUPLEX_FULL) { | |
183 | val |= (MCMDR_OPMOD | MCMDR_FDUP); | |
184 | } else { | |
185 | val |= MCMDR_OPMOD; | |
186 | val &= ~MCMDR_FDUP; | |
187 | } | |
188 | } else { | |
189 | /* 10 full/half duplex */ | |
190 | if (duplex == DUPLEX_FULL) { | |
191 | val |= MCMDR_FDUP; | |
192 | val &= ~MCMDR_OPMOD; | |
193 | } else { | |
194 | val &= ~(MCMDR_FDUP | MCMDR_OPMOD); | |
195 | } | |
196 | } | |
197 | ||
198 | __raw_writel(val, ether->reg + REG_MCMDR); | |
199 | } | |
200 | ||
201 | static void update_linkspeed(struct net_device *dev) | |
202 | { | |
203 | struct w90p910_ether *ether = netdev_priv(dev); | |
204 | struct platform_device *pdev; | |
205 | unsigned int bmsr, bmcr, lpa, speed, duplex; | |
206 | ||
207 | pdev = ether->pdev; | |
208 | ||
209 | if (!mii_link_ok(ðer->mii)) { | |
210 | ether->linkflag = 0x0; | |
211 | netif_carrier_off(dev); | |
212 | dev_warn(&pdev->dev, "%s: Link down.\n", dev->name); | |
213 | return; | |
214 | } | |
215 | ||
216 | if (ether->linkflag == 1) | |
217 | return; | |
218 | ||
219 | bmsr = w90p910_mdio_read(dev, ether->mii.phy_id, MII_BMSR); | |
220 | bmcr = w90p910_mdio_read(dev, ether->mii.phy_id, MII_BMCR); | |
221 | ||
222 | if (bmcr & BMCR_ANENABLE) { | |
223 | if (!(bmsr & BMSR_ANEGCOMPLETE)) | |
224 | return; | |
225 | ||
226 | lpa = w90p910_mdio_read(dev, ether->mii.phy_id, MII_LPA); | |
227 | ||
228 | if ((lpa & LPA_100FULL) || (lpa & LPA_100HALF)) | |
229 | speed = SPEED_100; | |
230 | else | |
231 | speed = SPEED_10; | |
232 | ||
233 | if ((lpa & LPA_100FULL) || (lpa & LPA_10FULL)) | |
234 | duplex = DUPLEX_FULL; | |
235 | else | |
236 | duplex = DUPLEX_HALF; | |
237 | ||
238 | } else { | |
239 | speed = (bmcr & BMCR_SPEED100) ? SPEED_100 : SPEED_10; | |
240 | duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF; | |
241 | } | |
242 | ||
243 | update_linkspeed_register(dev, speed, duplex); | |
244 | ||
245 | dev_info(&pdev->dev, "%s: Link now %i-%s\n", dev->name, speed, | |
246 | (duplex == DUPLEX_FULL) ? "FullDuplex" : "HalfDuplex"); | |
247 | ether->linkflag = 0x01; | |
248 | ||
249 | netif_carrier_on(dev); | |
250 | } | |
251 | ||
97815186 | 252 | static void w90p910_check_link(struct timer_list *t) |
a50a97d4 | 253 | { |
97815186 KC |
254 | struct w90p910_ether *ether = from_timer(ether, t, check_timer); |
255 | struct net_device *dev = ether->mii.dev; | |
a50a97d4 WZ |
256 | |
257 | update_linkspeed(dev); | |
258 | mod_timer(ðer->check_timer, jiffies + msecs_to_jiffies(1000)); | |
259 | } | |
260 | ||
261 | static void w90p910_write_cam(struct net_device *dev, | |
262 | unsigned int x, unsigned char *pval) | |
263 | { | |
264 | struct w90p910_ether *ether = netdev_priv(dev); | |
265 | unsigned int msw, lsw; | |
266 | ||
267 | msw = (pval[0] << 24) | (pval[1] << 16) | (pval[2] << 8) | pval[3]; | |
268 | ||
269 | lsw = (pval[4] << 24) | (pval[5] << 16); | |
270 | ||
271 | __raw_writel(lsw, ether->reg + REG_CAML_BASE + x * CAM_ENTRY_SIZE); | |
272 | __raw_writel(msw, ether->reg + REG_CAMM_BASE + x * CAM_ENTRY_SIZE); | |
273 | } | |
274 | ||
1e5053b7 | 275 | static int w90p910_init_desc(struct net_device *dev) |
a50a97d4 WZ |
276 | { |
277 | struct w90p910_ether *ether; | |
1e5053b7 WZ |
278 | struct w90p910_txbd *tdesc; |
279 | struct w90p910_rxbd *rdesc; | |
280 | struct platform_device *pdev; | |
281 | unsigned int i; | |
a50a97d4 WZ |
282 | |
283 | ether = netdev_priv(dev); | |
1e5053b7 | 284 | pdev = ether->pdev; |
a50a97d4 | 285 | |
d0320f75 JP |
286 | ether->tdesc = dma_alloc_coherent(&pdev->dev, sizeof(struct tran_pdesc), |
287 | ðer->tdesc_phys, GFP_KERNEL); | |
288 | if (!ether->tdesc) | |
1e5053b7 | 289 | return -ENOMEM; |
1e5053b7 | 290 | |
d0320f75 JP |
291 | ether->rdesc = dma_alloc_coherent(&pdev->dev, sizeof(struct recv_pdesc), |
292 | ðer->rdesc_phys, GFP_KERNEL); | |
1e5053b7 | 293 | if (!ether->rdesc) { |
1e5053b7 | 294 | dma_free_coherent(&pdev->dev, sizeof(struct tran_pdesc), |
d0320f75 | 295 | ether->tdesc, ether->tdesc_phys); |
1e5053b7 WZ |
296 | return -ENOMEM; |
297 | } | |
a50a97d4 WZ |
298 | |
299 | for (i = 0; i < TX_DESC_SIZE; i++) { | |
1e5053b7 | 300 | unsigned int offset; |
a50a97d4 | 301 | |
1e5053b7 | 302 | tdesc = &(ether->tdesc->desclist[i]); |
a50a97d4 | 303 | |
1e5053b7 WZ |
304 | if (i == TX_DESC_SIZE - 1) |
305 | offset = offsetof(struct tran_pdesc, desclist[0]); | |
306 | else | |
307 | offset = offsetof(struct tran_pdesc, desclist[i + 1]); | |
a50a97d4 | 308 | |
1e5053b7 WZ |
309 | tdesc->next = ether->tdesc_phys + offset; |
310 | tdesc->buffer = ether->tdesc_phys + | |
311 | offsetof(struct tran_pdesc, tran_buf[i]); | |
a50a97d4 WZ |
312 | tdesc->sl = 0; |
313 | tdesc->mode = 0; | |
314 | } | |
315 | ||
1e5053b7 WZ |
316 | ether->start_tx_ptr = ether->tdesc_phys; |
317 | ||
a50a97d4 | 318 | for (i = 0; i < RX_DESC_SIZE; i++) { |
1e5053b7 | 319 | unsigned int offset; |
a50a97d4 | 320 | |
1e5053b7 | 321 | rdesc = &(ether->rdesc->desclist[i]); |
a50a97d4 | 322 | |
1e5053b7 WZ |
323 | if (i == RX_DESC_SIZE - 1) |
324 | offset = offsetof(struct recv_pdesc, desclist[0]); | |
325 | else | |
326 | offset = offsetof(struct recv_pdesc, desclist[i + 1]); | |
a50a97d4 | 327 | |
1e5053b7 | 328 | rdesc->next = ether->rdesc_phys + offset; |
a50a97d4 | 329 | rdesc->sl = RX_OWEN_DMA; |
1e5053b7 WZ |
330 | rdesc->buffer = ether->rdesc_phys + |
331 | offsetof(struct recv_pdesc, recv_buf[i]); | |
a50a97d4 | 332 | } |
1e5053b7 WZ |
333 | |
334 | ether->start_rx_ptr = ether->rdesc_phys; | |
335 | ||
336 | return 0; | |
a50a97d4 WZ |
337 | } |
338 | ||
339 | static void w90p910_set_fifo_threshold(struct net_device *dev) | |
340 | { | |
341 | struct w90p910_ether *ether = netdev_priv(dev); | |
342 | unsigned int val; | |
343 | ||
344 | val = TXTHD | BLENGTH; | |
345 | __raw_writel(val, ether->reg + REG_FFTCR); | |
346 | } | |
347 | ||
348 | static void w90p910_return_default_idle(struct net_device *dev) | |
349 | { | |
350 | struct w90p910_ether *ether = netdev_priv(dev); | |
351 | unsigned int val; | |
352 | ||
353 | val = __raw_readl(ether->reg + REG_MCMDR); | |
354 | val |= SWR; | |
355 | __raw_writel(val, ether->reg + REG_MCMDR); | |
356 | } | |
357 | ||
358 | static void w90p910_trigger_rx(struct net_device *dev) | |
359 | { | |
360 | struct w90p910_ether *ether = netdev_priv(dev); | |
361 | ||
362 | __raw_writel(ENSTART, ether->reg + REG_RSDR); | |
363 | } | |
364 | ||
365 | static void w90p910_trigger_tx(struct net_device *dev) | |
366 | { | |
367 | struct w90p910_ether *ether = netdev_priv(dev); | |
368 | ||
369 | __raw_writel(ENSTART, ether->reg + REG_TSDR); | |
370 | } | |
371 | ||
372 | static void w90p910_enable_mac_interrupt(struct net_device *dev) | |
373 | { | |
374 | struct w90p910_ether *ether = netdev_priv(dev); | |
375 | unsigned int val; | |
376 | ||
377 | val = ENTXINTR | ENRXINTR | ENRXGD | ENTXCP; | |
378 | val |= ENTXBERR | ENRXBERR | ENTXABT; | |
379 | ||
380 | __raw_writel(val, ether->reg + REG_MIEN); | |
381 | } | |
382 | ||
383 | static void w90p910_get_and_clear_int(struct net_device *dev, | |
384 | unsigned int *val) | |
385 | { | |
386 | struct w90p910_ether *ether = netdev_priv(dev); | |
387 | ||
388 | *val = __raw_readl(ether->reg + REG_MISTA); | |
389 | __raw_writel(*val, ether->reg + REG_MISTA); | |
390 | } | |
391 | ||
392 | static void w90p910_set_global_maccmd(struct net_device *dev) | |
393 | { | |
394 | struct w90p910_ether *ether = netdev_priv(dev); | |
395 | unsigned int val; | |
396 | ||
397 | val = __raw_readl(ether->reg + REG_MCMDR); | |
398 | val |= MCMDR_SPCRC | MCMDR_ENMDC | MCMDR_ACP | ENMDC; | |
399 | __raw_writel(val, ether->reg + REG_MCMDR); | |
400 | } | |
401 | ||
402 | static void w90p910_enable_cam(struct net_device *dev) | |
403 | { | |
404 | struct w90p910_ether *ether = netdev_priv(dev); | |
405 | unsigned int val; | |
406 | ||
407 | w90p910_write_cam(dev, CAM0, dev->dev_addr); | |
408 | ||
409 | val = __raw_readl(ether->reg + REG_CAMEN); | |
410 | val |= CAM0EN; | |
411 | __raw_writel(val, ether->reg + REG_CAMEN); | |
412 | } | |
413 | ||
414 | static void w90p910_enable_cam_command(struct net_device *dev) | |
415 | { | |
416 | struct w90p910_ether *ether = netdev_priv(dev); | |
417 | unsigned int val; | |
418 | ||
419 | val = CAMCMR_ECMP | CAMCMR_ABP | CAMCMR_AMP; | |
420 | __raw_writel(val, ether->reg + REG_CAMCMR); | |
421 | } | |
422 | ||
423 | static void w90p910_enable_tx(struct net_device *dev, unsigned int enable) | |
424 | { | |
425 | struct w90p910_ether *ether = netdev_priv(dev); | |
426 | unsigned int val; | |
427 | ||
428 | val = __raw_readl(ether->reg + REG_MCMDR); | |
429 | ||
430 | if (enable) | |
431 | val |= MCMDR_TXON; | |
432 | else | |
433 | val &= ~MCMDR_TXON; | |
434 | ||
435 | __raw_writel(val, ether->reg + REG_MCMDR); | |
436 | } | |
437 | ||
438 | static void w90p910_enable_rx(struct net_device *dev, unsigned int enable) | |
439 | { | |
440 | struct w90p910_ether *ether = netdev_priv(dev); | |
441 | unsigned int val; | |
442 | ||
443 | val = __raw_readl(ether->reg + REG_MCMDR); | |
444 | ||
445 | if (enable) | |
446 | val |= MCMDR_RXON; | |
447 | else | |
448 | val &= ~MCMDR_RXON; | |
449 | ||
450 | __raw_writel(val, ether->reg + REG_MCMDR); | |
451 | } | |
452 | ||
453 | static void w90p910_set_curdest(struct net_device *dev) | |
454 | { | |
455 | struct w90p910_ether *ether = netdev_priv(dev); | |
456 | ||
457 | __raw_writel(ether->start_rx_ptr, ether->reg + REG_RXDLSA); | |
458 | __raw_writel(ether->start_tx_ptr, ether->reg + REG_TXDLSA); | |
459 | } | |
460 | ||
461 | static void w90p910_reset_mac(struct net_device *dev) | |
462 | { | |
463 | struct w90p910_ether *ether = netdev_priv(dev); | |
464 | ||
a50a97d4 WZ |
465 | w90p910_enable_tx(dev, 0); |
466 | w90p910_enable_rx(dev, 0); | |
467 | w90p910_set_fifo_threshold(dev); | |
468 | w90p910_return_default_idle(dev); | |
469 | ||
470 | if (!netif_queue_stopped(dev)) | |
471 | netif_stop_queue(dev); | |
472 | ||
473 | w90p910_init_desc(dev); | |
474 | ||
860e9538 | 475 | netif_trans_update(dev); /* prevent tx timeout */ |
a50a97d4 WZ |
476 | ether->cur_tx = 0x0; |
477 | ether->finish_tx = 0x0; | |
478 | ether->cur_rx = 0x0; | |
479 | ||
480 | w90p910_set_curdest(dev); | |
481 | w90p910_enable_cam(dev); | |
482 | w90p910_enable_cam_command(dev); | |
483 | w90p910_enable_mac_interrupt(dev); | |
484 | w90p910_enable_tx(dev, 1); | |
485 | w90p910_enable_rx(dev, 1); | |
486 | w90p910_trigger_tx(dev); | |
487 | w90p910_trigger_rx(dev); | |
488 | ||
860e9538 | 489 | netif_trans_update(dev); /* prevent tx timeout */ |
a50a97d4 WZ |
490 | |
491 | if (netif_queue_stopped(dev)) | |
492 | netif_wake_queue(dev); | |
a50a97d4 WZ |
493 | } |
494 | ||
495 | static void w90p910_mdio_write(struct net_device *dev, | |
496 | int phy_id, int reg, int data) | |
497 | { | |
498 | struct w90p910_ether *ether = netdev_priv(dev); | |
499 | struct platform_device *pdev; | |
500 | unsigned int val, i; | |
501 | ||
502 | pdev = ether->pdev; | |
503 | ||
504 | __raw_writel(data, ether->reg + REG_MIID); | |
505 | ||
506 | val = (phy_id << 0x08) | reg; | |
507 | val |= PHYBUSY | PHYWR | MDCCR_VAL; | |
508 | __raw_writel(val, ether->reg + REG_MIIDA); | |
509 | ||
510 | for (i = 0; i < DELAY; i++) { | |
511 | if ((__raw_readl(ether->reg + REG_MIIDA) & PHYBUSY) == 0) | |
512 | break; | |
513 | } | |
514 | ||
515 | if (i == DELAY) | |
516 | dev_warn(&pdev->dev, "mdio write timed out\n"); | |
517 | } | |
518 | ||
519 | static int w90p910_mdio_read(struct net_device *dev, int phy_id, int reg) | |
520 | { | |
521 | struct w90p910_ether *ether = netdev_priv(dev); | |
522 | struct platform_device *pdev; | |
523 | unsigned int val, i, data; | |
524 | ||
525 | pdev = ether->pdev; | |
526 | ||
527 | val = (phy_id << 0x08) | reg; | |
528 | val |= PHYBUSY | MDCCR_VAL; | |
529 | __raw_writel(val, ether->reg + REG_MIIDA); | |
530 | ||
531 | for (i = 0; i < DELAY; i++) { | |
532 | if ((__raw_readl(ether->reg + REG_MIIDA) & PHYBUSY) == 0) | |
533 | break; | |
534 | } | |
535 | ||
536 | if (i == DELAY) { | |
537 | dev_warn(&pdev->dev, "mdio read timed out\n"); | |
538 | data = 0xffff; | |
539 | } else { | |
540 | data = __raw_readl(ether->reg + REG_MIID); | |
541 | } | |
542 | ||
543 | return data; | |
544 | } | |
545 | ||
1e5053b7 | 546 | static int w90p910_set_mac_address(struct net_device *dev, void *addr) |
a50a97d4 WZ |
547 | { |
548 | struct sockaddr *address = addr; | |
549 | ||
550 | if (!is_valid_ether_addr(address->sa_data)) | |
551 | return -EADDRNOTAVAIL; | |
552 | ||
553 | memcpy(dev->dev_addr, address->sa_data, dev->addr_len); | |
554 | w90p910_write_cam(dev, CAM0, dev->dev_addr); | |
555 | ||
556 | return 0; | |
557 | } | |
558 | ||
559 | static int w90p910_ether_close(struct net_device *dev) | |
560 | { | |
561 | struct w90p910_ether *ether = netdev_priv(dev); | |
1e5053b7 | 562 | struct platform_device *pdev; |
a50a97d4 | 563 | |
1e5053b7 WZ |
564 | pdev = ether->pdev; |
565 | ||
566 | dma_free_coherent(&pdev->dev, sizeof(struct recv_pdesc), | |
567 | ether->rdesc, ether->rdesc_phys); | |
568 | dma_free_coherent(&pdev->dev, sizeof(struct tran_pdesc), | |
569 | ether->tdesc, ether->tdesc_phys); | |
a50a97d4 WZ |
570 | |
571 | netif_stop_queue(dev); | |
572 | ||
573 | del_timer_sync(ðer->check_timer); | |
574 | clk_disable(ether->rmiiclk); | |
575 | clk_disable(ether->clk); | |
576 | ||
577 | free_irq(ether->txirq, dev); | |
578 | free_irq(ether->rxirq, dev); | |
579 | ||
580 | return 0; | |
581 | } | |
582 | ||
a50a97d4 WZ |
583 | static int w90p910_send_frame(struct net_device *dev, |
584 | unsigned char *data, int length) | |
585 | { | |
586 | struct w90p910_ether *ether; | |
587 | struct w90p910_txbd *txbd; | |
588 | struct platform_device *pdev; | |
589 | unsigned char *buffer; | |
590 | ||
591 | ether = netdev_priv(dev); | |
592 | pdev = ether->pdev; | |
593 | ||
594 | txbd = ðer->tdesc->desclist[ether->cur_tx]; | |
595 | buffer = ether->tdesc->tran_buf[ether->cur_tx]; | |
1e5053b7 | 596 | |
a50a97d4 WZ |
597 | if (length > 1514) { |
598 | dev_err(&pdev->dev, "send data %d bytes, check it\n", length); | |
599 | length = 1514; | |
600 | } | |
601 | ||
602 | txbd->sl = length & 0xFFFF; | |
603 | ||
604 | memcpy(buffer, data, length); | |
605 | ||
606 | txbd->mode = TX_OWEN_DMA | PADDINGMODE | CRCMODE | MACTXINTEN; | |
607 | ||
608 | w90p910_enable_tx(dev, 1); | |
609 | ||
610 | w90p910_trigger_tx(dev); | |
611 | ||
1e5053b7 WZ |
612 | if (++ether->cur_tx >= TX_DESC_SIZE) |
613 | ether->cur_tx = 0; | |
614 | ||
a50a97d4 WZ |
615 | txbd = ðer->tdesc->desclist[ether->cur_tx]; |
616 | ||
a50a97d4 WZ |
617 | if (txbd->mode & TX_OWEN_DMA) |
618 | netif_stop_queue(dev); | |
619 | ||
620 | return 0; | |
621 | } | |
622 | ||
623 | static int w90p910_ether_start_xmit(struct sk_buff *skb, struct net_device *dev) | |
624 | { | |
625 | struct w90p910_ether *ether = netdev_priv(dev); | |
626 | ||
627 | if (!(w90p910_send_frame(dev, skb->data, skb->len))) { | |
628 | ether->skb = skb; | |
ae6279ec | 629 | dev_consume_skb_irq(skb); |
a50a97d4 WZ |
630 | return 0; |
631 | } | |
1e5053b7 | 632 | return -EAGAIN; |
a50a97d4 WZ |
633 | } |
634 | ||
635 | static irqreturn_t w90p910_tx_interrupt(int irq, void *dev_id) | |
636 | { | |
637 | struct w90p910_ether *ether; | |
638 | struct w90p910_txbd *txbd; | |
639 | struct platform_device *pdev; | |
a50a97d4 WZ |
640 | struct net_device *dev; |
641 | unsigned int cur_entry, entry, status; | |
642 | ||
1e5053b7 | 643 | dev = dev_id; |
a50a97d4 WZ |
644 | ether = netdev_priv(dev); |
645 | pdev = ether->pdev; | |
646 | ||
a50a97d4 WZ |
647 | w90p910_get_and_clear_int(dev, &status); |
648 | ||
649 | cur_entry = __raw_readl(ether->reg + REG_CTXDSA); | |
650 | ||
1e5053b7 WZ |
651 | entry = ether->tdesc_phys + |
652 | offsetof(struct tran_pdesc, desclist[ether->finish_tx]); | |
a50a97d4 WZ |
653 | |
654 | while (entry != cur_entry) { | |
655 | txbd = ðer->tdesc->desclist[ether->finish_tx]; | |
656 | ||
1e5053b7 WZ |
657 | if (++ether->finish_tx >= TX_DESC_SIZE) |
658 | ether->finish_tx = 0; | |
a50a97d4 WZ |
659 | |
660 | if (txbd->sl & TXDS_TXCP) { | |
b09a9537 TK |
661 | dev->stats.tx_packets++; |
662 | dev->stats.tx_bytes += txbd->sl & 0xFFFF; | |
a50a97d4 | 663 | } else { |
b09a9537 | 664 | dev->stats.tx_errors++; |
a50a97d4 WZ |
665 | } |
666 | ||
667 | txbd->sl = 0x0; | |
668 | txbd->mode = 0x0; | |
669 | ||
670 | if (netif_queue_stopped(dev)) | |
671 | netif_wake_queue(dev); | |
672 | ||
1e5053b7 WZ |
673 | entry = ether->tdesc_phys + |
674 | offsetof(struct tran_pdesc, desclist[ether->finish_tx]); | |
a50a97d4 WZ |
675 | } |
676 | ||
677 | if (status & MISTA_EXDEF) { | |
678 | dev_err(&pdev->dev, "emc defer exceed interrupt\n"); | |
679 | } else if (status & MISTA_TXBERR) { | |
1e5053b7 WZ |
680 | dev_err(&pdev->dev, "emc bus error interrupt\n"); |
681 | w90p910_reset_mac(dev); | |
682 | } else if (status & MISTA_TDU) { | |
683 | if (netif_queue_stopped(dev)) | |
684 | netif_wake_queue(dev); | |
685 | } | |
a50a97d4 WZ |
686 | |
687 | return IRQ_HANDLED; | |
688 | } | |
689 | ||
690 | static void netdev_rx(struct net_device *dev) | |
691 | { | |
692 | struct w90p910_ether *ether; | |
693 | struct w90p910_rxbd *rxbd; | |
694 | struct platform_device *pdev; | |
a50a97d4 WZ |
695 | struct sk_buff *skb; |
696 | unsigned char *data; | |
697 | unsigned int length, status, val, entry; | |
698 | ||
699 | ether = netdev_priv(dev); | |
700 | pdev = ether->pdev; | |
a50a97d4 WZ |
701 | |
702 | rxbd = ðer->rdesc->desclist[ether->cur_rx]; | |
703 | ||
704 | do { | |
705 | val = __raw_readl(ether->reg + REG_CRXDSA); | |
1e5053b7 WZ |
706 | |
707 | entry = ether->rdesc_phys + | |
708 | offsetof(struct recv_pdesc, desclist[ether->cur_rx]); | |
a50a97d4 WZ |
709 | |
710 | if (val == entry) | |
711 | break; | |
712 | ||
713 | status = rxbd->sl; | |
714 | length = status & 0xFFFF; | |
715 | ||
716 | if (status & RXDS_RXGD) { | |
717 | data = ether->rdesc->recv_buf[ether->cur_rx]; | |
dae2e9f4 | 718 | skb = netdev_alloc_skb(dev, length + 2); |
a50a97d4 | 719 | if (!skb) { |
b09a9537 | 720 | dev->stats.rx_dropped++; |
a50a97d4 WZ |
721 | return; |
722 | } | |
723 | ||
a50a97d4 WZ |
724 | skb_reserve(skb, 2); |
725 | skb_put(skb, length); | |
726 | skb_copy_to_linear_data(skb, data, length); | |
727 | skb->protocol = eth_type_trans(skb, dev); | |
b09a9537 TK |
728 | dev->stats.rx_packets++; |
729 | dev->stats.rx_bytes += length; | |
a50a97d4 WZ |
730 | netif_rx(skb); |
731 | } else { | |
b09a9537 | 732 | dev->stats.rx_errors++; |
a50a97d4 WZ |
733 | |
734 | if (status & RXDS_RP) { | |
735 | dev_err(&pdev->dev, "rx runt err\n"); | |
b09a9537 | 736 | dev->stats.rx_length_errors++; |
a50a97d4 | 737 | } else if (status & RXDS_CRCE) { |
1e5053b7 | 738 | dev_err(&pdev->dev, "rx crc err\n"); |
b09a9537 | 739 | dev->stats.rx_crc_errors++; |
1e5053b7 | 740 | } else if (status & RXDS_ALIE) { |
6b2a314f | 741 | dev_err(&pdev->dev, "rx alignment err\n"); |
b09a9537 | 742 | dev->stats.rx_frame_errors++; |
a50a97d4 | 743 | } else if (status & RXDS_PTLE) { |
1e5053b7 | 744 | dev_err(&pdev->dev, "rx longer err\n"); |
b09a9537 | 745 | dev->stats.rx_over_errors++; |
a50a97d4 | 746 | } |
1e5053b7 | 747 | } |
a50a97d4 WZ |
748 | |
749 | rxbd->sl = RX_OWEN_DMA; | |
750 | rxbd->reserved = 0x0; | |
1e5053b7 WZ |
751 | |
752 | if (++ether->cur_rx >= RX_DESC_SIZE) | |
753 | ether->cur_rx = 0; | |
754 | ||
a50a97d4 WZ |
755 | rxbd = ðer->rdesc->desclist[ether->cur_rx]; |
756 | ||
a50a97d4 WZ |
757 | } while (1); |
758 | } | |
759 | ||
760 | static irqreturn_t w90p910_rx_interrupt(int irq, void *dev_id) | |
761 | { | |
762 | struct net_device *dev; | |
763 | struct w90p910_ether *ether; | |
764 | struct platform_device *pdev; | |
765 | unsigned int status; | |
766 | ||
1e5053b7 | 767 | dev = dev_id; |
a50a97d4 WZ |
768 | ether = netdev_priv(dev); |
769 | pdev = ether->pdev; | |
770 | ||
a50a97d4 WZ |
771 | w90p910_get_and_clear_int(dev, &status); |
772 | ||
773 | if (status & MISTA_RDU) { | |
774 | netdev_rx(dev); | |
a50a97d4 WZ |
775 | w90p910_trigger_rx(dev); |
776 | ||
a50a97d4 WZ |
777 | return IRQ_HANDLED; |
778 | } else if (status & MISTA_RXBERR) { | |
1e5053b7 WZ |
779 | dev_err(&pdev->dev, "emc rx bus error\n"); |
780 | w90p910_reset_mac(dev); | |
781 | } | |
a50a97d4 WZ |
782 | |
783 | netdev_rx(dev); | |
a50a97d4 WZ |
784 | return IRQ_HANDLED; |
785 | } | |
786 | ||
787 | static int w90p910_ether_open(struct net_device *dev) | |
788 | { | |
789 | struct w90p910_ether *ether; | |
790 | struct platform_device *pdev; | |
791 | ||
792 | ether = netdev_priv(dev); | |
793 | pdev = ether->pdev; | |
794 | ||
795 | w90p910_reset_mac(dev); | |
796 | w90p910_set_fifo_threshold(dev); | |
797 | w90p910_set_curdest(dev); | |
798 | w90p910_enable_cam(dev); | |
799 | w90p910_enable_cam_command(dev); | |
800 | w90p910_enable_mac_interrupt(dev); | |
801 | w90p910_set_global_maccmd(dev); | |
802 | w90p910_enable_rx(dev, 1); | |
803 | ||
d1853dc8 WZ |
804 | clk_enable(ether->rmiiclk); |
805 | clk_enable(ether->clk); | |
806 | ||
a50a97d4 WZ |
807 | ether->rx_packets = 0x0; |
808 | ether->rx_bytes = 0x0; | |
809 | ||
810 | if (request_irq(ether->txirq, w90p910_tx_interrupt, | |
811 | 0x0, pdev->name, dev)) { | |
812 | dev_err(&pdev->dev, "register irq tx failed\n"); | |
813 | return -EAGAIN; | |
814 | } | |
815 | ||
816 | if (request_irq(ether->rxirq, w90p910_rx_interrupt, | |
817 | 0x0, pdev->name, dev)) { | |
818 | dev_err(&pdev->dev, "register irq rx failed\n"); | |
1e5053b7 | 819 | free_irq(ether->txirq, dev); |
a50a97d4 WZ |
820 | return -EAGAIN; |
821 | } | |
822 | ||
823 | mod_timer(ðer->check_timer, jiffies + msecs_to_jiffies(1000)); | |
824 | netif_start_queue(dev); | |
825 | w90p910_trigger_rx(dev); | |
826 | ||
827 | dev_info(&pdev->dev, "%s is OPENED\n", dev->name); | |
828 | ||
829 | return 0; | |
830 | } | |
831 | ||
832 | static void w90p910_ether_set_multicast_list(struct net_device *dev) | |
833 | { | |
834 | struct w90p910_ether *ether; | |
835 | unsigned int rx_mode; | |
836 | ||
837 | ether = netdev_priv(dev); | |
838 | ||
839 | if (dev->flags & IFF_PROMISC) | |
840 | rx_mode = CAMCMR_AUP | CAMCMR_AMP | CAMCMR_ABP | CAMCMR_ECMP; | |
3b9a7728 JP |
841 | else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev)) |
842 | rx_mode = CAMCMR_AMP | CAMCMR_ABP | CAMCMR_ECMP; | |
843 | else | |
844 | rx_mode = CAMCMR_ECMP | CAMCMR_ABP; | |
a50a97d4 WZ |
845 | __raw_writel(rx_mode, ether->reg + REG_CAMCMR); |
846 | } | |
847 | ||
848 | static int w90p910_ether_ioctl(struct net_device *dev, | |
849 | struct ifreq *ifr, int cmd) | |
850 | { | |
851 | struct w90p910_ether *ether = netdev_priv(dev); | |
852 | struct mii_ioctl_data *data = if_mii(ifr); | |
853 | ||
854 | return generic_mii_ioctl(ðer->mii, data, cmd, NULL); | |
855 | } | |
856 | ||
857 | static void w90p910_get_drvinfo(struct net_device *dev, | |
858 | struct ethtool_drvinfo *info) | |
859 | { | |
7826d43f JP |
860 | strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); |
861 | strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); | |
a50a97d4 WZ |
862 | } |
863 | ||
2efbe143 PR |
864 | static int w90p910_get_link_ksettings(struct net_device *dev, |
865 | struct ethtool_link_ksettings *cmd) | |
a50a97d4 WZ |
866 | { |
867 | struct w90p910_ether *ether = netdev_priv(dev); | |
82c01a84 | 868 | |
869 | mii_ethtool_get_link_ksettings(ðer->mii, cmd); | |
870 | ||
871 | return 0; | |
a50a97d4 WZ |
872 | } |
873 | ||
2efbe143 PR |
874 | static int w90p910_set_link_ksettings(struct net_device *dev, |
875 | const struct ethtool_link_ksettings *cmd) | |
a50a97d4 WZ |
876 | { |
877 | struct w90p910_ether *ether = netdev_priv(dev); | |
2efbe143 | 878 | return mii_ethtool_set_link_ksettings(ðer->mii, cmd); |
a50a97d4 WZ |
879 | } |
880 | ||
881 | static int w90p910_nway_reset(struct net_device *dev) | |
882 | { | |
883 | struct w90p910_ether *ether = netdev_priv(dev); | |
884 | return mii_nway_restart(ðer->mii); | |
885 | } | |
886 | ||
887 | static u32 w90p910_get_link(struct net_device *dev) | |
888 | { | |
889 | struct w90p910_ether *ether = netdev_priv(dev); | |
890 | return mii_link_ok(ðer->mii); | |
891 | } | |
892 | ||
893 | static const struct ethtool_ops w90p910_ether_ethtool_ops = { | |
a50a97d4 WZ |
894 | .get_drvinfo = w90p910_get_drvinfo, |
895 | .nway_reset = w90p910_nway_reset, | |
896 | .get_link = w90p910_get_link, | |
2efbe143 PR |
897 | .get_link_ksettings = w90p910_get_link_ksettings, |
898 | .set_link_ksettings = w90p910_set_link_ksettings, | |
a50a97d4 WZ |
899 | }; |
900 | ||
901 | static const struct net_device_ops w90p910_ether_netdev_ops = { | |
902 | .ndo_open = w90p910_ether_open, | |
903 | .ndo_stop = w90p910_ether_close, | |
904 | .ndo_start_xmit = w90p910_ether_start_xmit, | |
afc4b13d | 905 | .ndo_set_rx_mode = w90p910_ether_set_multicast_list, |
1e5053b7 | 906 | .ndo_set_mac_address = w90p910_set_mac_address, |
a50a97d4 WZ |
907 | .ndo_do_ioctl = w90p910_ether_ioctl, |
908 | .ndo_validate_addr = eth_validate_addr, | |
a50a97d4 WZ |
909 | }; |
910 | ||
51367e42 | 911 | static void get_mac_address(struct net_device *dev) |
a50a97d4 WZ |
912 | { |
913 | struct w90p910_ether *ether = netdev_priv(dev); | |
914 | struct platform_device *pdev; | |
1409a932 | 915 | char addr[ETH_ALEN]; |
a50a97d4 WZ |
916 | |
917 | pdev = ether->pdev; | |
918 | ||
919 | addr[0] = 0x00; | |
920 | addr[1] = 0x02; | |
921 | addr[2] = 0xac; | |
922 | addr[3] = 0x55; | |
923 | addr[4] = 0x88; | |
924 | addr[5] = 0xa8; | |
925 | ||
926 | if (is_valid_ether_addr(addr)) | |
1409a932 | 927 | memcpy(dev->dev_addr, &addr, ETH_ALEN); |
a50a97d4 WZ |
928 | else |
929 | dev_err(&pdev->dev, "invalid mac address\n"); | |
930 | } | |
931 | ||
932 | static int w90p910_ether_setup(struct net_device *dev) | |
933 | { | |
934 | struct w90p910_ether *ether = netdev_priv(dev); | |
935 | ||
a50a97d4 WZ |
936 | dev->netdev_ops = &w90p910_ether_netdev_ops; |
937 | dev->ethtool_ops = &w90p910_ether_ethtool_ops; | |
938 | ||
939 | dev->tx_queue_len = 16; | |
940 | dev->dma = 0x0; | |
941 | dev->watchdog_timeo = TX_TIMEOUT; | |
942 | ||
943 | get_mac_address(dev); | |
944 | ||
a50a97d4 WZ |
945 | ether->cur_tx = 0x0; |
946 | ether->cur_rx = 0x0; | |
947 | ether->finish_tx = 0x0; | |
948 | ether->linkflag = 0x0; | |
949 | ether->mii.phy_id = 0x01; | |
950 | ether->mii.phy_id_mask = 0x1f; | |
951 | ether->mii.reg_num_mask = 0x1f; | |
952 | ether->mii.dev = dev; | |
953 | ether->mii.mdio_read = w90p910_mdio_read; | |
954 | ether->mii.mdio_write = w90p910_mdio_write; | |
955 | ||
97815186 | 956 | timer_setup(ðer->check_timer, w90p910_check_link, 0); |
a50a97d4 WZ |
957 | |
958 | return 0; | |
959 | } | |
960 | ||
26735f2f | 961 | static int w90p910_ether_probe(struct platform_device *pdev) |
a50a97d4 WZ |
962 | { |
963 | struct w90p910_ether *ether; | |
964 | struct net_device *dev; | |
a50a97d4 WZ |
965 | int error; |
966 | ||
967 | dev = alloc_etherdev(sizeof(struct w90p910_ether)); | |
968 | if (!dev) | |
969 | return -ENOMEM; | |
970 | ||
1e5053b7 WZ |
971 | ether = netdev_priv(dev); |
972 | ||
973 | ether->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
974 | if (ether->res == NULL) { | |
a50a97d4 WZ |
975 | dev_err(&pdev->dev, "failed to get I/O memory\n"); |
976 | error = -ENXIO; | |
977 | goto failed_free; | |
978 | } | |
979 | ||
1e5053b7 WZ |
980 | if (!request_mem_region(ether->res->start, |
981 | resource_size(ether->res), pdev->name)) { | |
a50a97d4 WZ |
982 | dev_err(&pdev->dev, "failed to request I/O memory\n"); |
983 | error = -EBUSY; | |
984 | goto failed_free; | |
985 | } | |
986 | ||
1e5053b7 | 987 | ether->reg = ioremap(ether->res->start, resource_size(ether->res)); |
a50a97d4 WZ |
988 | if (ether->reg == NULL) { |
989 | dev_err(&pdev->dev, "failed to remap I/O memory\n"); | |
990 | error = -ENXIO; | |
991 | goto failed_free_mem; | |
992 | } | |
993 | ||
994 | ether->txirq = platform_get_irq(pdev, 0); | |
995 | if (ether->txirq < 0) { | |
996 | dev_err(&pdev->dev, "failed to get ether tx irq\n"); | |
997 | error = -ENXIO; | |
998 | goto failed_free_io; | |
999 | } | |
1000 | ||
1001 | ether->rxirq = platform_get_irq(pdev, 1); | |
1002 | if (ether->rxirq < 0) { | |
1003 | dev_err(&pdev->dev, "failed to get ether rx irq\n"); | |
1004 | error = -ENXIO; | |
0a171933 | 1005 | goto failed_free_io; |
a50a97d4 WZ |
1006 | } |
1007 | ||
1008 | platform_set_drvdata(pdev, dev); | |
1009 | ||
1010 | ether->clk = clk_get(&pdev->dev, NULL); | |
1011 | if (IS_ERR(ether->clk)) { | |
1012 | dev_err(&pdev->dev, "failed to get ether clock\n"); | |
1013 | error = PTR_ERR(ether->clk); | |
0a171933 | 1014 | goto failed_free_io; |
a50a97d4 WZ |
1015 | } |
1016 | ||
1017 | ether->rmiiclk = clk_get(&pdev->dev, "RMII"); | |
1018 | if (IS_ERR(ether->rmiiclk)) { | |
1019 | dev_err(&pdev->dev, "failed to get ether clock\n"); | |
1020 | error = PTR_ERR(ether->rmiiclk); | |
1021 | goto failed_put_clk; | |
1022 | } | |
1023 | ||
1024 | ether->pdev = pdev; | |
1025 | ||
1026 | w90p910_ether_setup(dev); | |
1027 | ||
1028 | error = register_netdev(dev); | |
1029 | if (error != 0) { | |
fc4fa6e1 | 1030 | dev_err(&pdev->dev, "Register EMC w90p910 FAILED\n"); |
a50a97d4 WZ |
1031 | error = -ENODEV; |
1032 | goto failed_put_rmiiclk; | |
1033 | } | |
1034 | ||
1035 | return 0; | |
1036 | failed_put_rmiiclk: | |
1037 | clk_put(ether->rmiiclk); | |
1038 | failed_put_clk: | |
1039 | clk_put(ether->clk); | |
a50a97d4 WZ |
1040 | failed_free_io: |
1041 | iounmap(ether->reg); | |
1042 | failed_free_mem: | |
1e5053b7 | 1043 | release_mem_region(ether->res->start, resource_size(ether->res)); |
a50a97d4 WZ |
1044 | failed_free: |
1045 | free_netdev(dev); | |
1046 | return error; | |
1047 | } | |
1048 | ||
26735f2f | 1049 | static int w90p910_ether_remove(struct platform_device *pdev) |
a50a97d4 WZ |
1050 | { |
1051 | struct net_device *dev = platform_get_drvdata(pdev); | |
1052 | struct w90p910_ether *ether = netdev_priv(dev); | |
1053 | ||
1054 | unregister_netdev(dev); | |
1e5053b7 | 1055 | |
a50a97d4 WZ |
1056 | clk_put(ether->rmiiclk); |
1057 | clk_put(ether->clk); | |
1e5053b7 WZ |
1058 | |
1059 | iounmap(ether->reg); | |
1060 | release_mem_region(ether->res->start, resource_size(ether->res)); | |
1061 | ||
a50a97d4 | 1062 | del_timer_sync(ðer->check_timer); |
1e5053b7 | 1063 | |
a50a97d4 WZ |
1064 | free_netdev(dev); |
1065 | return 0; | |
1066 | } | |
1067 | ||
1068 | static struct platform_driver w90p910_ether_driver = { | |
1069 | .probe = w90p910_ether_probe, | |
26735f2f | 1070 | .remove = w90p910_ether_remove, |
a50a97d4 | 1071 | .driver = { |
456d8991 | 1072 | .name = "nuc900-emc", |
a50a97d4 WZ |
1073 | }, |
1074 | }; | |
1075 | ||
db62f684 | 1076 | module_platform_driver(w90p910_ether_driver); |
a50a97d4 WZ |
1077 | |
1078 | MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>"); | |
1079 | MODULE_DESCRIPTION("w90p910 MAC driver!"); | |
1080 | MODULE_LICENSE("GPL"); | |
456d8991 | 1081 | MODULE_ALIAS("platform:nuc900-emc"); |
a50a97d4 | 1082 |