Commit | Line | Data |
---|---|---|
8b1467a3 MS |
1 | /* |
2 | * Ethernet driver for the WIZnet W5100 chip. | |
3 | * | |
4 | * Copyright (C) 2006-2008 WIZnet Co.,Ltd. | |
5 | * Copyright (C) 2012 Mike Sinkovsky <msink@permonline.ru> | |
6 | * | |
7 | * Licensed under the GPL-2 or later. | |
8 | */ | |
9 | ||
10 | #include <linux/kernel.h> | |
11 | #include <linux/module.h> | |
12 | #include <linux/kconfig.h> | |
13 | #include <linux/netdevice.h> | |
14 | #include <linux/etherdevice.h> | |
15 | #include <linux/platform_device.h> | |
16 | #include <linux/platform_data/wiznet.h> | |
17 | #include <linux/ethtool.h> | |
18 | #include <linux/skbuff.h> | |
19 | #include <linux/types.h> | |
20 | #include <linux/errno.h> | |
21 | #include <linux/delay.h> | |
22 | #include <linux/slab.h> | |
23 | #include <linux/spinlock.h> | |
24 | #include <linux/io.h> | |
25 | #include <linux/ioport.h> | |
26 | #include <linux/interrupt.h> | |
64d176fc | 27 | #include <linux/irq.h> |
8b1467a3 MS |
28 | #include <linux/gpio.h> |
29 | ||
850576cf AM |
30 | #include "w5100.h" |
31 | ||
8b1467a3 MS |
32 | #define DRV_NAME "w5100" |
33 | #define DRV_VERSION "2012-04-04" | |
34 | ||
35 | MODULE_DESCRIPTION("WIZnet W5100 Ethernet driver v"DRV_VERSION); | |
36 | MODULE_AUTHOR("Mike Sinkovsky <msink@permonline.ru>"); | |
37 | MODULE_ALIAS("platform:"DRV_NAME); | |
38 | MODULE_LICENSE("GPL"); | |
39 | ||
40 | /* | |
35ef7d68 | 41 | * W5100/W5200/W5500 common registers |
8b1467a3 MS |
42 | */ |
43 | #define W5100_COMMON_REGS 0x0000 | |
44 | #define W5100_MR 0x0000 /* Mode Register */ | |
45 | #define MR_RST 0x80 /* S/W reset */ | |
46 | #define MR_PB 0x10 /* Ping block */ | |
47 | #define MR_AI 0x02 /* Address Auto-Increment */ | |
48 | #define MR_IND 0x01 /* Indirect mode */ | |
49 | #define W5100_SHAR 0x0009 /* Source MAC address */ | |
50 | #define W5100_IR 0x0015 /* Interrupt Register */ | |
8b1467a3 MS |
51 | #define W5100_COMMON_REGS_LEN 0x0040 |
52 | ||
0c165ff2 AM |
53 | #define W5100_Sn_MR 0x0000 /* Sn Mode Register */ |
54 | #define W5100_Sn_CR 0x0001 /* Sn Command Register */ | |
55 | #define W5100_Sn_IR 0x0002 /* Sn Interrupt Register */ | |
56 | #define W5100_Sn_SR 0x0003 /* Sn Status Register */ | |
57 | #define W5100_Sn_TX_FSR 0x0020 /* Sn Transmit free memory size */ | |
58 | #define W5100_Sn_TX_RD 0x0022 /* Sn Transmit memory read pointer */ | |
59 | #define W5100_Sn_TX_WR 0x0024 /* Sn Transmit memory write pointer */ | |
60 | #define W5100_Sn_RX_RSR 0x0026 /* Sn Receive free memory size */ | |
61 | #define W5100_Sn_RX_RD 0x0028 /* Sn Receive memory read pointer */ | |
62 | ||
35ef7d68 | 63 | #define S0_REGS(priv) ((priv)->s0_regs) |
0c165ff2 AM |
64 | |
65 | #define W5100_S0_MR(priv) (S0_REGS(priv) + W5100_Sn_MR) | |
dbedd44e | 66 | #define S0_MR_MACRAW 0x04 /* MAC RAW mode (promiscuous) */ |
8b1467a3 | 67 | #define S0_MR_MACRAW_MF 0x44 /* MAC RAW mode (filtered) */ |
0c165ff2 | 68 | #define W5100_S0_CR(priv) (S0_REGS(priv) + W5100_Sn_CR) |
8b1467a3 MS |
69 | #define S0_CR_OPEN 0x01 /* OPEN command */ |
70 | #define S0_CR_CLOSE 0x10 /* CLOSE command */ | |
71 | #define S0_CR_SEND 0x20 /* SEND command */ | |
72 | #define S0_CR_RECV 0x40 /* RECV command */ | |
0c165ff2 | 73 | #define W5100_S0_IR(priv) (S0_REGS(priv) + W5100_Sn_IR) |
8b1467a3 MS |
74 | #define S0_IR_SENDOK 0x10 /* complete sending */ |
75 | #define S0_IR_RECV 0x04 /* receiving data */ | |
0c165ff2 | 76 | #define W5100_S0_SR(priv) (S0_REGS(priv) + W5100_Sn_SR) |
8b1467a3 | 77 | #define S0_SR_MACRAW 0x42 /* mac raw mode */ |
0c165ff2 AM |
78 | #define W5100_S0_TX_FSR(priv) (S0_REGS(priv) + W5100_Sn_TX_FSR) |
79 | #define W5100_S0_TX_RD(priv) (S0_REGS(priv) + W5100_Sn_TX_RD) | |
80 | #define W5100_S0_TX_WR(priv) (S0_REGS(priv) + W5100_Sn_TX_WR) | |
81 | #define W5100_S0_RX_RSR(priv) (S0_REGS(priv) + W5100_Sn_RX_RSR) | |
82 | #define W5100_S0_RX_RD(priv) (S0_REGS(priv) + W5100_Sn_RX_RD) | |
83 | ||
8b1467a3 MS |
84 | #define W5100_S0_REGS_LEN 0x0040 |
85 | ||
0c165ff2 | 86 | /* |
35ef7d68 AM |
87 | * W5100 and W5200 common registers |
88 | */ | |
89 | #define W5100_IMR 0x0016 /* Interrupt Mask Register */ | |
90 | #define IR_S0 0x01 /* S0 interrupt */ | |
91 | #define W5100_RTR 0x0017 /* Retry Time-value Register */ | |
92 | #define RTR_DEFAULT 2000 /* =0x07d0 (2000) */ | |
93 | ||
94 | /* | |
95 | * W5100 specific register and memory | |
0c165ff2 AM |
96 | */ |
97 | #define W5100_RMSR 0x001a /* Receive Memory Size */ | |
98 | #define W5100_TMSR 0x001b /* Transmit Memory Size */ | |
99 | ||
100 | #define W5100_S0_REGS 0x0400 | |
101 | ||
8b1467a3 | 102 | #define W5100_TX_MEM_START 0x4000 |
850576cf | 103 | #define W5100_TX_MEM_SIZE 0x2000 |
8b1467a3 | 104 | #define W5100_RX_MEM_START 0x6000 |
850576cf | 105 | #define W5100_RX_MEM_SIZE 0x2000 |
8b1467a3 | 106 | |
0c165ff2 | 107 | /* |
35ef7d68 | 108 | * W5200 specific register and memory |
0c165ff2 AM |
109 | */ |
110 | #define W5200_S0_REGS 0x4000 | |
111 | ||
112 | #define W5200_Sn_RXMEM_SIZE(n) (0x401e + (n) * 0x0100) /* Sn RX Memory Size */ | |
113 | #define W5200_Sn_TXMEM_SIZE(n) (0x401f + (n) * 0x0100) /* Sn TX Memory Size */ | |
0c165ff2 AM |
114 | |
115 | #define W5200_TX_MEM_START 0x8000 | |
116 | #define W5200_TX_MEM_SIZE 0x4000 | |
117 | #define W5200_RX_MEM_START 0xc000 | |
118 | #define W5200_RX_MEM_SIZE 0x4000 | |
119 | ||
35ef7d68 AM |
120 | /* |
121 | * W5500 specific register and memory | |
122 | * | |
123 | * W5500 register and memory are organized by multiple blocks. Each one is | |
124 | * selected by 16bits offset address and 5bits block select bits. So we | |
125 | * encode it into 32bits address. (lower 16bits is offset address and | |
126 | * upper 16bits is block select bits) | |
127 | */ | |
128 | #define W5500_SIMR 0x0018 /* Socket Interrupt Mask Register */ | |
129 | #define W5500_RTR 0x0019 /* Retry Time-value Register */ | |
130 | ||
131 | #define W5500_S0_REGS 0x10000 | |
132 | ||
133 | #define W5500_Sn_RXMEM_SIZE(n) \ | |
134 | (0x1001e + (n) * 0x40000) /* Sn RX Memory Size */ | |
135 | #define W5500_Sn_TXMEM_SIZE(n) \ | |
136 | (0x1001f + (n) * 0x40000) /* Sn TX Memory Size */ | |
137 | ||
138 | #define W5500_TX_MEM_START 0x20000 | |
139 | #define W5500_TX_MEM_SIZE 0x04000 | |
140 | #define W5500_RX_MEM_START 0x30000 | |
141 | #define W5500_RX_MEM_SIZE 0x04000 | |
142 | ||
8b1467a3 MS |
143 | /* |
144 | * Device driver private data structure | |
145 | */ | |
850576cf | 146 | |
8b1467a3 | 147 | struct w5100_priv { |
850576cf | 148 | const struct w5100_ops *ops; |
35ef7d68 AM |
149 | |
150 | /* Socket 0 register offset address */ | |
151 | u32 s0_regs; | |
152 | /* Socket 0 TX buffer offset address and size */ | |
153 | u32 s0_tx_buf; | |
154 | u16 s0_tx_buf_size; | |
155 | /* Socket 0 RX buffer offset address and size */ | |
156 | u32 s0_rx_buf; | |
157 | u16 s0_rx_buf_size; | |
158 | ||
8b1467a3 MS |
159 | int irq; |
160 | int link_irq; | |
161 | int link_gpio; | |
162 | ||
163 | struct napi_struct napi; | |
164 | struct net_device *ndev; | |
165 | bool promisc; | |
166 | u32 msg_enable; | |
bf2c6b90 AM |
167 | |
168 | struct workqueue_struct *xfer_wq; | |
169 | struct work_struct rx_work; | |
170 | struct sk_buff *tx_skb; | |
171 | struct work_struct tx_work; | |
172 | struct work_struct setrx_work; | |
173 | struct work_struct restart_work; | |
8b1467a3 MS |
174 | }; |
175 | ||
0c165ff2 AM |
176 | static inline bool is_w5200(struct w5100_priv *priv) |
177 | { | |
178 | return priv->ops->chip_id == W5200; | |
179 | } | |
180 | ||
8b1467a3 MS |
181 | /************************************************************************ |
182 | * | |
183 | * Lowlevel I/O functions | |
184 | * | |
185 | ***********************************************************************/ | |
186 | ||
850576cf AM |
187 | struct w5100_mmio_priv { |
188 | void __iomem *base; | |
189 | /* Serialize access in indirect address mode */ | |
190 | spinlock_t reg_lock; | |
191 | }; | |
192 | ||
193 | static inline struct w5100_mmio_priv *w5100_mmio_priv(struct net_device *dev) | |
194 | { | |
195 | return w5100_ops_priv(dev); | |
196 | } | |
197 | ||
198 | static inline void __iomem *w5100_mmio(struct net_device *ndev) | |
199 | { | |
200 | struct w5100_mmio_priv *mmio_priv = w5100_mmio_priv(ndev); | |
201 | ||
202 | return mmio_priv->base; | |
203 | } | |
204 | ||
8b1467a3 MS |
205 | /* |
206 | * In direct address mode host system can directly access W5100 registers | |
207 | * after mapping to Memory-Mapped I/O space. | |
208 | * | |
209 | * 0x8000 bytes are required for memory space. | |
210 | */ | |
35ef7d68 | 211 | static inline int w5100_read_direct(struct net_device *ndev, u32 addr) |
8b1467a3 | 212 | { |
850576cf | 213 | return ioread8(w5100_mmio(ndev) + (addr << CONFIG_WIZNET_BUS_SHIFT)); |
8b1467a3 MS |
214 | } |
215 | ||
35ef7d68 | 216 | static inline int __w5100_write_direct(struct net_device *ndev, u32 addr, |
850576cf | 217 | u8 data) |
d6586d2e | 218 | { |
850576cf AM |
219 | iowrite8(data, w5100_mmio(ndev) + (addr << CONFIG_WIZNET_BUS_SHIFT)); |
220 | ||
221 | return 0; | |
d6586d2e AM |
222 | } |
223 | ||
35ef7d68 | 224 | static inline int w5100_write_direct(struct net_device *ndev, u32 addr, u8 data) |
8b1467a3 | 225 | { |
850576cf | 226 | __w5100_write_direct(ndev, addr, data); |
d6586d2e | 227 | mmiowb(); |
850576cf AM |
228 | |
229 | return 0; | |
8b1467a3 MS |
230 | } |
231 | ||
35ef7d68 | 232 | static int w5100_read16_direct(struct net_device *ndev, u32 addr) |
8b1467a3 MS |
233 | { |
234 | u16 data; | |
850576cf AM |
235 | data = w5100_read_direct(ndev, addr) << 8; |
236 | data |= w5100_read_direct(ndev, addr + 1); | |
8b1467a3 MS |
237 | return data; |
238 | } | |
239 | ||
35ef7d68 | 240 | static int w5100_write16_direct(struct net_device *ndev, u32 addr, u16 data) |
8b1467a3 | 241 | { |
850576cf AM |
242 | __w5100_write_direct(ndev, addr, data >> 8); |
243 | __w5100_write_direct(ndev, addr + 1, data); | |
d6586d2e | 244 | mmiowb(); |
850576cf AM |
245 | |
246 | return 0; | |
8b1467a3 MS |
247 | } |
248 | ||
35ef7d68 | 249 | static int w5100_readbulk_direct(struct net_device *ndev, u32 addr, u8 *buf, |
850576cf | 250 | int len) |
8b1467a3 | 251 | { |
8b1467a3 MS |
252 | int i; |
253 | ||
850576cf AM |
254 | for (i = 0; i < len; i++, addr++) |
255 | *buf++ = w5100_read_direct(ndev, addr); | |
256 | ||
257 | return 0; | |
8b1467a3 MS |
258 | } |
259 | ||
35ef7d68 | 260 | static int w5100_writebulk_direct(struct net_device *ndev, u32 addr, |
850576cf | 261 | const u8 *buf, int len) |
8b1467a3 | 262 | { |
8b1467a3 MS |
263 | int i; |
264 | ||
850576cf AM |
265 | for (i = 0; i < len; i++, addr++) |
266 | __w5100_write_direct(ndev, addr, *buf++); | |
267 | ||
d6586d2e | 268 | mmiowb(); |
850576cf AM |
269 | |
270 | return 0; | |
271 | } | |
272 | ||
273 | static int w5100_mmio_init(struct net_device *ndev) | |
274 | { | |
275 | struct platform_device *pdev = to_platform_device(ndev->dev.parent); | |
276 | struct w5100_priv *priv = netdev_priv(ndev); | |
277 | struct w5100_mmio_priv *mmio_priv = w5100_mmio_priv(ndev); | |
278 | struct resource *mem; | |
279 | ||
280 | spin_lock_init(&mmio_priv->reg_lock); | |
281 | ||
282 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
283 | mmio_priv->base = devm_ioremap_resource(&pdev->dev, mem); | |
284 | if (IS_ERR(mmio_priv->base)) | |
285 | return PTR_ERR(mmio_priv->base); | |
286 | ||
287 | netdev_info(ndev, "at 0x%llx irq %d\n", (u64)mem->start, priv->irq); | |
288 | ||
289 | return 0; | |
8b1467a3 MS |
290 | } |
291 | ||
850576cf | 292 | static const struct w5100_ops w5100_mmio_direct_ops = { |
0c165ff2 | 293 | .chip_id = W5100, |
850576cf AM |
294 | .read = w5100_read_direct, |
295 | .write = w5100_write_direct, | |
296 | .read16 = w5100_read16_direct, | |
297 | .write16 = w5100_write16_direct, | |
298 | .readbulk = w5100_readbulk_direct, | |
299 | .writebulk = w5100_writebulk_direct, | |
300 | .init = w5100_mmio_init, | |
301 | }; | |
302 | ||
8b1467a3 MS |
303 | /* |
304 | * In indirect address mode host system indirectly accesses registers by | |
305 | * using Indirect Mode Address Register (IDM_AR) and Indirect Mode Data | |
306 | * Register (IDM_DR), which are directly mapped to Memory-Mapped I/O space. | |
307 | * Mode Register (MR) is directly accessible. | |
308 | * | |
309 | * Only 0x04 bytes are required for memory space. | |
310 | */ | |
311 | #define W5100_IDM_AR 0x01 /* Indirect Mode Address Register */ | |
312 | #define W5100_IDM_DR 0x03 /* Indirect Mode Data Register */ | |
313 | ||
35ef7d68 | 314 | static int w5100_read_indirect(struct net_device *ndev, u32 addr) |
8b1467a3 | 315 | { |
850576cf | 316 | struct w5100_mmio_priv *mmio_priv = w5100_mmio_priv(ndev); |
8b1467a3 MS |
317 | unsigned long flags; |
318 | u8 data; | |
319 | ||
850576cf AM |
320 | spin_lock_irqsave(&mmio_priv->reg_lock, flags); |
321 | w5100_write16_direct(ndev, W5100_IDM_AR, addr); | |
322 | data = w5100_read_direct(ndev, W5100_IDM_DR); | |
323 | spin_unlock_irqrestore(&mmio_priv->reg_lock, flags); | |
8b1467a3 MS |
324 | |
325 | return data; | |
326 | } | |
327 | ||
35ef7d68 | 328 | static int w5100_write_indirect(struct net_device *ndev, u32 addr, u8 data) |
8b1467a3 | 329 | { |
850576cf | 330 | struct w5100_mmio_priv *mmio_priv = w5100_mmio_priv(ndev); |
8b1467a3 MS |
331 | unsigned long flags; |
332 | ||
850576cf AM |
333 | spin_lock_irqsave(&mmio_priv->reg_lock, flags); |
334 | w5100_write16_direct(ndev, W5100_IDM_AR, addr); | |
335 | w5100_write_direct(ndev, W5100_IDM_DR, data); | |
336 | spin_unlock_irqrestore(&mmio_priv->reg_lock, flags); | |
337 | ||
338 | return 0; | |
8b1467a3 MS |
339 | } |
340 | ||
35ef7d68 | 341 | static int w5100_read16_indirect(struct net_device *ndev, u32 addr) |
8b1467a3 | 342 | { |
850576cf | 343 | struct w5100_mmio_priv *mmio_priv = w5100_mmio_priv(ndev); |
8b1467a3 MS |
344 | unsigned long flags; |
345 | u16 data; | |
346 | ||
850576cf AM |
347 | spin_lock_irqsave(&mmio_priv->reg_lock, flags); |
348 | w5100_write16_direct(ndev, W5100_IDM_AR, addr); | |
349 | data = w5100_read_direct(ndev, W5100_IDM_DR) << 8; | |
350 | data |= w5100_read_direct(ndev, W5100_IDM_DR); | |
351 | spin_unlock_irqrestore(&mmio_priv->reg_lock, flags); | |
8b1467a3 MS |
352 | |
353 | return data; | |
354 | } | |
355 | ||
35ef7d68 | 356 | static int w5100_write16_indirect(struct net_device *ndev, u32 addr, u16 data) |
8b1467a3 | 357 | { |
850576cf | 358 | struct w5100_mmio_priv *mmio_priv = w5100_mmio_priv(ndev); |
8b1467a3 MS |
359 | unsigned long flags; |
360 | ||
850576cf AM |
361 | spin_lock_irqsave(&mmio_priv->reg_lock, flags); |
362 | w5100_write16_direct(ndev, W5100_IDM_AR, addr); | |
363 | __w5100_write_direct(ndev, W5100_IDM_DR, data >> 8); | |
364 | w5100_write_direct(ndev, W5100_IDM_DR, data); | |
365 | spin_unlock_irqrestore(&mmio_priv->reg_lock, flags); | |
366 | ||
367 | return 0; | |
8b1467a3 MS |
368 | } |
369 | ||
35ef7d68 | 370 | static int w5100_readbulk_indirect(struct net_device *ndev, u32 addr, u8 *buf, |
850576cf | 371 | int len) |
8b1467a3 | 372 | { |
850576cf | 373 | struct w5100_mmio_priv *mmio_priv = w5100_mmio_priv(ndev); |
8b1467a3 MS |
374 | unsigned long flags; |
375 | int i; | |
376 | ||
850576cf AM |
377 | spin_lock_irqsave(&mmio_priv->reg_lock, flags); |
378 | w5100_write16_direct(ndev, W5100_IDM_AR, addr); | |
379 | ||
380 | for (i = 0; i < len; i++) | |
381 | *buf++ = w5100_read_direct(ndev, W5100_IDM_DR); | |
8b1467a3 | 382 | |
8b1467a3 | 383 | mmiowb(); |
850576cf AM |
384 | spin_unlock_irqrestore(&mmio_priv->reg_lock, flags); |
385 | ||
386 | return 0; | |
8b1467a3 MS |
387 | } |
388 | ||
35ef7d68 | 389 | static int w5100_writebulk_indirect(struct net_device *ndev, u32 addr, |
850576cf | 390 | const u8 *buf, int len) |
8b1467a3 | 391 | { |
850576cf | 392 | struct w5100_mmio_priv *mmio_priv = w5100_mmio_priv(ndev); |
8b1467a3 MS |
393 | unsigned long flags; |
394 | int i; | |
395 | ||
850576cf AM |
396 | spin_lock_irqsave(&mmio_priv->reg_lock, flags); |
397 | w5100_write16_direct(ndev, W5100_IDM_AR, addr); | |
398 | ||
399 | for (i = 0; i < len; i++) | |
400 | __w5100_write_direct(ndev, W5100_IDM_DR, *buf++); | |
8b1467a3 | 401 | |
8b1467a3 | 402 | mmiowb(); |
850576cf AM |
403 | spin_unlock_irqrestore(&mmio_priv->reg_lock, flags); |
404 | ||
405 | return 0; | |
406 | } | |
407 | ||
408 | static int w5100_reset_indirect(struct net_device *ndev) | |
409 | { | |
410 | w5100_write_direct(ndev, W5100_MR, MR_RST); | |
411 | mdelay(5); | |
412 | w5100_write_direct(ndev, W5100_MR, MR_PB | MR_AI | MR_IND); | |
413 | ||
414 | return 0; | |
8b1467a3 MS |
415 | } |
416 | ||
850576cf | 417 | static const struct w5100_ops w5100_mmio_indirect_ops = { |
0c165ff2 | 418 | .chip_id = W5100, |
850576cf AM |
419 | .read = w5100_read_indirect, |
420 | .write = w5100_write_indirect, | |
421 | .read16 = w5100_read16_indirect, | |
422 | .write16 = w5100_write16_indirect, | |
423 | .readbulk = w5100_readbulk_indirect, | |
424 | .writebulk = w5100_writebulk_indirect, | |
425 | .init = w5100_mmio_init, | |
426 | .reset = w5100_reset_indirect, | |
427 | }; | |
428 | ||
8b1467a3 | 429 | #if defined(CONFIG_WIZNET_BUS_DIRECT) |
850576cf | 430 | |
35ef7d68 | 431 | static int w5100_read(struct w5100_priv *priv, u32 addr) |
850576cf AM |
432 | { |
433 | return w5100_read_direct(priv->ndev, addr); | |
434 | } | |
435 | ||
35ef7d68 | 436 | static int w5100_write(struct w5100_priv *priv, u32 addr, u8 data) |
850576cf AM |
437 | { |
438 | return w5100_write_direct(priv->ndev, addr, data); | |
439 | } | |
440 | ||
35ef7d68 | 441 | static int w5100_read16(struct w5100_priv *priv, u32 addr) |
850576cf AM |
442 | { |
443 | return w5100_read16_direct(priv->ndev, addr); | |
444 | } | |
445 | ||
35ef7d68 | 446 | static int w5100_write16(struct w5100_priv *priv, u32 addr, u16 data) |
850576cf AM |
447 | { |
448 | return w5100_write16_direct(priv->ndev, addr, data); | |
449 | } | |
450 | ||
35ef7d68 | 451 | static int w5100_readbulk(struct w5100_priv *priv, u32 addr, u8 *buf, int len) |
850576cf AM |
452 | { |
453 | return w5100_readbulk_direct(priv->ndev, addr, buf, len); | |
454 | } | |
455 | ||
35ef7d68 | 456 | static int w5100_writebulk(struct w5100_priv *priv, u32 addr, const u8 *buf, |
850576cf AM |
457 | int len) |
458 | { | |
459 | return w5100_writebulk_direct(priv->ndev, addr, buf, len); | |
460 | } | |
8b1467a3 MS |
461 | |
462 | #elif defined(CONFIG_WIZNET_BUS_INDIRECT) | |
850576cf | 463 | |
35ef7d68 | 464 | static int w5100_read(struct w5100_priv *priv, u32 addr) |
850576cf AM |
465 | { |
466 | return w5100_read_indirect(priv->ndev, addr); | |
467 | } | |
468 | ||
35ef7d68 | 469 | static int w5100_write(struct w5100_priv *priv, u32 addr, u8 data) |
850576cf AM |
470 | { |
471 | return w5100_write_indirect(priv->ndev, addr, data); | |
472 | } | |
473 | ||
35ef7d68 | 474 | static int w5100_read16(struct w5100_priv *priv, u32 addr) |
850576cf AM |
475 | { |
476 | return w5100_read16_indirect(priv->ndev, addr); | |
477 | } | |
478 | ||
35ef7d68 | 479 | static int w5100_write16(struct w5100_priv *priv, u32 addr, u16 data) |
850576cf AM |
480 | { |
481 | return w5100_write16_indirect(priv->ndev, addr, data); | |
482 | } | |
483 | ||
35ef7d68 | 484 | static int w5100_readbulk(struct w5100_priv *priv, u32 addr, u8 *buf, int len) |
850576cf AM |
485 | { |
486 | return w5100_readbulk_indirect(priv->ndev, addr, buf, len); | |
487 | } | |
488 | ||
35ef7d68 | 489 | static int w5100_writebulk(struct w5100_priv *priv, u32 addr, const u8 *buf, |
850576cf AM |
490 | int len) |
491 | { | |
492 | return w5100_writebulk_indirect(priv->ndev, addr, buf, len); | |
493 | } | |
8b1467a3 MS |
494 | |
495 | #else /* CONFIG_WIZNET_BUS_ANY */ | |
850576cf | 496 | |
35ef7d68 | 497 | static int w5100_read(struct w5100_priv *priv, u32 addr) |
850576cf AM |
498 | { |
499 | return priv->ops->read(priv->ndev, addr); | |
500 | } | |
501 | ||
35ef7d68 | 502 | static int w5100_write(struct w5100_priv *priv, u32 addr, u8 data) |
850576cf AM |
503 | { |
504 | return priv->ops->write(priv->ndev, addr, data); | |
505 | } | |
506 | ||
35ef7d68 | 507 | static int w5100_read16(struct w5100_priv *priv, u32 addr) |
850576cf AM |
508 | { |
509 | return priv->ops->read16(priv->ndev, addr); | |
510 | } | |
511 | ||
35ef7d68 | 512 | static int w5100_write16(struct w5100_priv *priv, u32 addr, u16 data) |
850576cf AM |
513 | { |
514 | return priv->ops->write16(priv->ndev, addr, data); | |
515 | } | |
516 | ||
35ef7d68 | 517 | static int w5100_readbulk(struct w5100_priv *priv, u32 addr, u8 *buf, int len) |
850576cf AM |
518 | { |
519 | return priv->ops->readbulk(priv->ndev, addr, buf, len); | |
520 | } | |
521 | ||
35ef7d68 | 522 | static int w5100_writebulk(struct w5100_priv *priv, u32 addr, const u8 *buf, |
850576cf AM |
523 | int len) |
524 | { | |
525 | return priv->ops->writebulk(priv->ndev, addr, buf, len); | |
526 | } | |
527 | ||
8b1467a3 MS |
528 | #endif |
529 | ||
850576cf AM |
530 | static int w5100_readbuf(struct w5100_priv *priv, u16 offset, u8 *buf, int len) |
531 | { | |
35ef7d68 | 532 | u32 addr; |
850576cf AM |
533 | int remain = 0; |
534 | int ret; | |
35ef7d68 AM |
535 | const u32 mem_start = priv->s0_rx_buf; |
536 | const u16 mem_size = priv->s0_rx_buf_size; | |
850576cf | 537 | |
0c165ff2 AM |
538 | offset %= mem_size; |
539 | addr = mem_start + offset; | |
850576cf | 540 | |
0c165ff2 AM |
541 | if (offset + len > mem_size) { |
542 | remain = (offset + len) % mem_size; | |
543 | len = mem_size - offset; | |
850576cf AM |
544 | } |
545 | ||
546 | ret = w5100_readbulk(priv, addr, buf, len); | |
547 | if (ret || !remain) | |
548 | return ret; | |
549 | ||
0c165ff2 | 550 | return w5100_readbulk(priv, mem_start, buf + len, remain); |
850576cf AM |
551 | } |
552 | ||
553 | static int w5100_writebuf(struct w5100_priv *priv, u16 offset, const u8 *buf, | |
554 | int len) | |
555 | { | |
35ef7d68 | 556 | u32 addr; |
850576cf AM |
557 | int ret; |
558 | int remain = 0; | |
35ef7d68 AM |
559 | const u32 mem_start = priv->s0_tx_buf; |
560 | const u16 mem_size = priv->s0_tx_buf_size; | |
850576cf | 561 | |
0c165ff2 AM |
562 | offset %= mem_size; |
563 | addr = mem_start + offset; | |
850576cf | 564 | |
0c165ff2 AM |
565 | if (offset + len > mem_size) { |
566 | remain = (offset + len) % mem_size; | |
567 | len = mem_size - offset; | |
850576cf AM |
568 | } |
569 | ||
570 | ret = w5100_writebulk(priv, addr, buf, len); | |
571 | if (ret || !remain) | |
572 | return ret; | |
573 | ||
0c165ff2 | 574 | return w5100_writebulk(priv, mem_start, buf + len, remain); |
850576cf AM |
575 | } |
576 | ||
577 | static int w5100_reset(struct w5100_priv *priv) | |
578 | { | |
579 | if (priv->ops->reset) | |
580 | return priv->ops->reset(priv->ndev); | |
581 | ||
582 | w5100_write(priv, W5100_MR, MR_RST); | |
583 | mdelay(5); | |
584 | w5100_write(priv, W5100_MR, MR_PB); | |
585 | ||
586 | return 0; | |
587 | } | |
588 | ||
8b1467a3 MS |
589 | static int w5100_command(struct w5100_priv *priv, u16 cmd) |
590 | { | |
bf2c6b90 | 591 | unsigned long timeout; |
8b1467a3 | 592 | |
0c165ff2 | 593 | w5100_write(priv, W5100_S0_CR(priv), cmd); |
8b1467a3 | 594 | |
bf2c6b90 AM |
595 | timeout = jiffies + msecs_to_jiffies(100); |
596 | ||
0c165ff2 | 597 | while (w5100_read(priv, W5100_S0_CR(priv)) != 0) { |
8b1467a3 MS |
598 | if (time_after(jiffies, timeout)) |
599 | return -EIO; | |
600 | cpu_relax(); | |
601 | } | |
602 | ||
603 | return 0; | |
604 | } | |
605 | ||
606 | static void w5100_write_macaddr(struct w5100_priv *priv) | |
607 | { | |
608 | struct net_device *ndev = priv->ndev; | |
8b1467a3 | 609 | |
850576cf | 610 | w5100_writebulk(priv, W5100_SHAR, ndev->dev_addr, ETH_ALEN); |
8b1467a3 MS |
611 | } |
612 | ||
35ef7d68 AM |
613 | static void w5100_socket_intr_mask(struct w5100_priv *priv, u8 mask) |
614 | { | |
615 | u32 imr; | |
616 | ||
617 | if (priv->ops->chip_id == W5500) | |
618 | imr = W5500_SIMR; | |
619 | else | |
620 | imr = W5100_IMR; | |
621 | ||
622 | w5100_write(priv, imr, mask); | |
623 | } | |
624 | ||
625 | static void w5100_enable_intr(struct w5100_priv *priv) | |
626 | { | |
627 | w5100_socket_intr_mask(priv, IR_S0); | |
628 | } | |
629 | ||
630 | static void w5100_disable_intr(struct w5100_priv *priv) | |
631 | { | |
632 | w5100_socket_intr_mask(priv, 0); | |
633 | } | |
634 | ||
0c165ff2 AM |
635 | static void w5100_memory_configure(struct w5100_priv *priv) |
636 | { | |
637 | /* Configure 16K of internal memory | |
638 | * as 8K RX buffer and 8K TX buffer | |
639 | */ | |
640 | w5100_write(priv, W5100_RMSR, 0x03); | |
641 | w5100_write(priv, W5100_TMSR, 0x03); | |
642 | } | |
643 | ||
644 | static void w5200_memory_configure(struct w5100_priv *priv) | |
645 | { | |
646 | int i; | |
647 | ||
648 | /* Configure internal RX memory as 16K RX buffer and | |
649 | * internal TX memory as 16K TX buffer | |
650 | */ | |
651 | w5100_write(priv, W5200_Sn_RXMEM_SIZE(0), 0x10); | |
652 | w5100_write(priv, W5200_Sn_TXMEM_SIZE(0), 0x10); | |
653 | ||
654 | for (i = 1; i < 8; i++) { | |
655 | w5100_write(priv, W5200_Sn_RXMEM_SIZE(i), 0); | |
656 | w5100_write(priv, W5200_Sn_TXMEM_SIZE(i), 0); | |
657 | } | |
658 | } | |
659 | ||
35ef7d68 | 660 | static void w5500_memory_configure(struct w5100_priv *priv) |
8b1467a3 | 661 | { |
35ef7d68 AM |
662 | int i; |
663 | ||
664 | /* Configure internal RX memory as 16K RX buffer and | |
665 | * internal TX memory as 16K TX buffer | |
666 | */ | |
667 | w5100_write(priv, W5500_Sn_RXMEM_SIZE(0), 0x10); | |
668 | w5100_write(priv, W5500_Sn_TXMEM_SIZE(0), 0x10); | |
669 | ||
670 | for (i = 1; i < 8; i++) { | |
671 | w5100_write(priv, W5500_Sn_RXMEM_SIZE(i), 0); | |
672 | w5100_write(priv, W5500_Sn_TXMEM_SIZE(i), 0); | |
673 | } | |
674 | } | |
675 | ||
676 | static int w5100_hw_reset(struct w5100_priv *priv) | |
677 | { | |
678 | u32 rtr; | |
679 | ||
850576cf AM |
680 | w5100_reset(priv); |
681 | ||
35ef7d68 | 682 | w5100_disable_intr(priv); |
8b1467a3 MS |
683 | w5100_write_macaddr(priv); |
684 | ||
35ef7d68 AM |
685 | switch (priv->ops->chip_id) { |
686 | case W5100: | |
0c165ff2 | 687 | w5100_memory_configure(priv); |
35ef7d68 AM |
688 | rtr = W5100_RTR; |
689 | break; | |
690 | case W5200: | |
691 | w5200_memory_configure(priv); | |
692 | rtr = W5100_RTR; | |
693 | break; | |
694 | case W5500: | |
695 | w5500_memory_configure(priv); | |
696 | rtr = W5500_RTR; | |
697 | break; | |
698 | default: | |
699 | return -EINVAL; | |
700 | } | |
701 | ||
702 | if (w5100_read16(priv, rtr) != RTR_DEFAULT) | |
703 | return -ENODEV; | |
704 | ||
705 | return 0; | |
8b1467a3 MS |
706 | } |
707 | ||
708 | static void w5100_hw_start(struct w5100_priv *priv) | |
709 | { | |
0c165ff2 | 710 | w5100_write(priv, W5100_S0_MR(priv), priv->promisc ? |
8b1467a3 | 711 | S0_MR_MACRAW : S0_MR_MACRAW_MF); |
8b1467a3 | 712 | w5100_command(priv, S0_CR_OPEN); |
35ef7d68 | 713 | w5100_enable_intr(priv); |
8b1467a3 MS |
714 | } |
715 | ||
716 | static void w5100_hw_close(struct w5100_priv *priv) | |
717 | { | |
35ef7d68 | 718 | w5100_disable_intr(priv); |
8b1467a3 MS |
719 | w5100_command(priv, S0_CR_CLOSE); |
720 | } | |
721 | ||
722 | /*********************************************************************** | |
723 | * | |
724 | * Device driver functions / callbacks | |
725 | * | |
726 | ***********************************************************************/ | |
727 | ||
728 | static void w5100_get_drvinfo(struct net_device *ndev, | |
729 | struct ethtool_drvinfo *info) | |
730 | { | |
731 | strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); | |
732 | strlcpy(info->version, DRV_VERSION, sizeof(info->version)); | |
733 | strlcpy(info->bus_info, dev_name(ndev->dev.parent), | |
734 | sizeof(info->bus_info)); | |
735 | } | |
736 | ||
737 | static u32 w5100_get_link(struct net_device *ndev) | |
738 | { | |
739 | struct w5100_priv *priv = netdev_priv(ndev); | |
740 | ||
741 | if (gpio_is_valid(priv->link_gpio)) | |
742 | return !!gpio_get_value(priv->link_gpio); | |
743 | ||
744 | return 1; | |
745 | } | |
746 | ||
747 | static u32 w5100_get_msglevel(struct net_device *ndev) | |
748 | { | |
749 | struct w5100_priv *priv = netdev_priv(ndev); | |
750 | ||
751 | return priv->msg_enable; | |
752 | } | |
753 | ||
754 | static void w5100_set_msglevel(struct net_device *ndev, u32 value) | |
755 | { | |
756 | struct w5100_priv *priv = netdev_priv(ndev); | |
757 | ||
758 | priv->msg_enable = value; | |
759 | } | |
760 | ||
761 | static int w5100_get_regs_len(struct net_device *ndev) | |
762 | { | |
763 | return W5100_COMMON_REGS_LEN + W5100_S0_REGS_LEN; | |
764 | } | |
765 | ||
766 | static void w5100_get_regs(struct net_device *ndev, | |
850576cf | 767 | struct ethtool_regs *regs, void *buf) |
8b1467a3 MS |
768 | { |
769 | struct w5100_priv *priv = netdev_priv(ndev); | |
8b1467a3 MS |
770 | |
771 | regs->version = 1; | |
850576cf AM |
772 | w5100_readbulk(priv, W5100_COMMON_REGS, buf, W5100_COMMON_REGS_LEN); |
773 | buf += W5100_COMMON_REGS_LEN; | |
0c165ff2 | 774 | w5100_readbulk(priv, S0_REGS(priv), buf, W5100_S0_REGS_LEN); |
8b1467a3 MS |
775 | } |
776 | ||
bf2c6b90 | 777 | static void w5100_restart(struct net_device *ndev) |
8b1467a3 MS |
778 | { |
779 | struct w5100_priv *priv = netdev_priv(ndev); | |
780 | ||
781 | netif_stop_queue(ndev); | |
782 | w5100_hw_reset(priv); | |
783 | w5100_hw_start(priv); | |
784 | ndev->stats.tx_errors++; | |
860e9538 | 785 | netif_trans_update(ndev); |
8b1467a3 MS |
786 | netif_wake_queue(ndev); |
787 | } | |
788 | ||
bf2c6b90 AM |
789 | static void w5100_restart_work(struct work_struct *work) |
790 | { | |
791 | struct w5100_priv *priv = container_of(work, struct w5100_priv, | |
792 | restart_work); | |
793 | ||
794 | w5100_restart(priv->ndev); | |
795 | } | |
796 | ||
797 | static void w5100_tx_timeout(struct net_device *ndev) | |
8b1467a3 MS |
798 | { |
799 | struct w5100_priv *priv = netdev_priv(ndev); | |
8b1467a3 | 800 | |
bf2c6b90 AM |
801 | if (priv->ops->may_sleep) |
802 | schedule_work(&priv->restart_work); | |
803 | else | |
804 | w5100_restart(ndev); | |
805 | } | |
806 | ||
807 | static void w5100_tx_skb(struct net_device *ndev, struct sk_buff *skb) | |
808 | { | |
809 | struct w5100_priv *priv = netdev_priv(ndev); | |
810 | u16 offset; | |
8b1467a3 | 811 | |
0c165ff2 | 812 | offset = w5100_read16(priv, W5100_S0_TX_WR(priv)); |
8b1467a3 | 813 | w5100_writebuf(priv, offset, skb->data, skb->len); |
0c165ff2 | 814 | w5100_write16(priv, W5100_S0_TX_WR(priv), offset + skb->len); |
8b1467a3 MS |
815 | ndev->stats.tx_bytes += skb->len; |
816 | ndev->stats.tx_packets++; | |
817 | dev_kfree_skb(skb); | |
818 | ||
819 | w5100_command(priv, S0_CR_SEND); | |
bf2c6b90 AM |
820 | } |
821 | ||
822 | static void w5100_tx_work(struct work_struct *work) | |
823 | { | |
824 | struct w5100_priv *priv = container_of(work, struct w5100_priv, | |
825 | tx_work); | |
826 | struct sk_buff *skb = priv->tx_skb; | |
827 | ||
828 | priv->tx_skb = NULL; | |
829 | ||
830 | if (WARN_ON(!skb)) | |
831 | return; | |
832 | w5100_tx_skb(priv->ndev, skb); | |
833 | } | |
834 | ||
835 | static int w5100_start_tx(struct sk_buff *skb, struct net_device *ndev) | |
836 | { | |
837 | struct w5100_priv *priv = netdev_priv(ndev); | |
838 | ||
839 | netif_stop_queue(ndev); | |
840 | ||
841 | if (priv->ops->may_sleep) { | |
842 | WARN_ON(priv->tx_skb); | |
843 | priv->tx_skb = skb; | |
844 | queue_work(priv->xfer_wq, &priv->tx_work); | |
845 | } else { | |
846 | w5100_tx_skb(ndev, skb); | |
847 | } | |
8b1467a3 MS |
848 | |
849 | return NETDEV_TX_OK; | |
850 | } | |
851 | ||
bf2c6b90 | 852 | static struct sk_buff *w5100_rx_skb(struct net_device *ndev) |
8b1467a3 | 853 | { |
bf2c6b90 | 854 | struct w5100_priv *priv = netdev_priv(ndev); |
8b1467a3 | 855 | struct sk_buff *skb; |
8b1467a3 MS |
856 | u16 rx_len; |
857 | u16 offset; | |
858 | u8 header[2]; | |
0c165ff2 | 859 | u16 rx_buf_len = w5100_read16(priv, W5100_S0_RX_RSR(priv)); |
8b1467a3 | 860 | |
bf2c6b90 AM |
861 | if (rx_buf_len == 0) |
862 | return NULL; | |
8b1467a3 | 863 | |
0c165ff2 | 864 | offset = w5100_read16(priv, W5100_S0_RX_RD(priv)); |
bf2c6b90 AM |
865 | w5100_readbuf(priv, offset, header, 2); |
866 | rx_len = get_unaligned_be16(header) - 2; | |
8b1467a3 | 867 | |
bf2c6b90 AM |
868 | skb = netdev_alloc_skb_ip_align(ndev, rx_len); |
869 | if (unlikely(!skb)) { | |
0c165ff2 | 870 | w5100_write16(priv, W5100_S0_RX_RD(priv), offset + rx_buf_len); |
8b1467a3 | 871 | w5100_command(priv, S0_CR_RECV); |
bf2c6b90 AM |
872 | ndev->stats.rx_dropped++; |
873 | return NULL; | |
874 | } | |
875 | ||
876 | skb_put(skb, rx_len); | |
877 | w5100_readbuf(priv, offset + 2, skb->data, rx_len); | |
0c165ff2 | 878 | w5100_write16(priv, W5100_S0_RX_RD(priv), offset + 2 + rx_len); |
bf2c6b90 AM |
879 | w5100_command(priv, S0_CR_RECV); |
880 | skb->protocol = eth_type_trans(skb, ndev); | |
881 | ||
882 | ndev->stats.rx_packets++; | |
883 | ndev->stats.rx_bytes += rx_len; | |
884 | ||
885 | return skb; | |
886 | } | |
887 | ||
888 | static void w5100_rx_work(struct work_struct *work) | |
889 | { | |
890 | struct w5100_priv *priv = container_of(work, struct w5100_priv, | |
891 | rx_work); | |
892 | struct sk_buff *skb; | |
893 | ||
894 | while ((skb = w5100_rx_skb(priv->ndev))) | |
895 | netif_rx_ni(skb); | |
896 | ||
35ef7d68 | 897 | w5100_enable_intr(priv); |
bf2c6b90 AM |
898 | } |
899 | ||
900 | static int w5100_napi_poll(struct napi_struct *napi, int budget) | |
901 | { | |
902 | struct w5100_priv *priv = container_of(napi, struct w5100_priv, napi); | |
903 | int rx_count; | |
8b1467a3 | 904 | |
bf2c6b90 AM |
905 | for (rx_count = 0; rx_count < budget; rx_count++) { |
906 | struct sk_buff *skb = w5100_rx_skb(priv->ndev); | |
907 | ||
908 | if (skb) | |
909 | netif_receive_skb(skb); | |
910 | else | |
911 | break; | |
8b1467a3 MS |
912 | } |
913 | ||
914 | if (rx_count < budget) { | |
5a3dba7a | 915 | napi_complete(napi); |
35ef7d68 | 916 | w5100_enable_intr(priv); |
8b1467a3 MS |
917 | } |
918 | ||
919 | return rx_count; | |
920 | } | |
921 | ||
922 | static irqreturn_t w5100_interrupt(int irq, void *ndev_instance) | |
923 | { | |
924 | struct net_device *ndev = ndev_instance; | |
925 | struct w5100_priv *priv = netdev_priv(ndev); | |
926 | ||
0c165ff2 | 927 | int ir = w5100_read(priv, W5100_S0_IR(priv)); |
8b1467a3 MS |
928 | if (!ir) |
929 | return IRQ_NONE; | |
0c165ff2 | 930 | w5100_write(priv, W5100_S0_IR(priv), ir); |
8b1467a3 | 931 | |
376b16f4 | 932 | if (ir & S0_IR_SENDOK) { |
8b1467a3 MS |
933 | netif_dbg(priv, tx_done, ndev, "tx done\n"); |
934 | netif_wake_queue(ndev); | |
935 | } | |
936 | ||
937 | if (ir & S0_IR_RECV) { | |
35ef7d68 | 938 | w5100_disable_intr(priv); |
bf2c6b90 AM |
939 | |
940 | if (priv->ops->may_sleep) | |
941 | queue_work(priv->xfer_wq, &priv->rx_work); | |
942 | else if (napi_schedule_prep(&priv->napi)) | |
8b1467a3 | 943 | __napi_schedule(&priv->napi); |
8b1467a3 MS |
944 | } |
945 | ||
946 | return IRQ_HANDLED; | |
947 | } | |
948 | ||
949 | static irqreturn_t w5100_detect_link(int irq, void *ndev_instance) | |
950 | { | |
951 | struct net_device *ndev = ndev_instance; | |
952 | struct w5100_priv *priv = netdev_priv(ndev); | |
953 | ||
954 | if (netif_running(ndev)) { | |
955 | if (gpio_get_value(priv->link_gpio) != 0) { | |
956 | netif_info(priv, link, ndev, "link is up\n"); | |
957 | netif_carrier_on(ndev); | |
958 | } else { | |
959 | netif_info(priv, link, ndev, "link is down\n"); | |
960 | netif_carrier_off(ndev); | |
961 | } | |
962 | } | |
963 | ||
964 | return IRQ_HANDLED; | |
965 | } | |
966 | ||
bf2c6b90 AM |
967 | static void w5100_setrx_work(struct work_struct *work) |
968 | { | |
969 | struct w5100_priv *priv = container_of(work, struct w5100_priv, | |
970 | setrx_work); | |
971 | ||
972 | w5100_hw_start(priv); | |
973 | } | |
974 | ||
8b1467a3 MS |
975 | static void w5100_set_rx_mode(struct net_device *ndev) |
976 | { | |
977 | struct w5100_priv *priv = netdev_priv(ndev); | |
978 | bool set_promisc = (ndev->flags & IFF_PROMISC) != 0; | |
979 | ||
980 | if (priv->promisc != set_promisc) { | |
981 | priv->promisc = set_promisc; | |
bf2c6b90 AM |
982 | |
983 | if (priv->ops->may_sleep) | |
984 | schedule_work(&priv->setrx_work); | |
985 | else | |
986 | w5100_hw_start(priv); | |
8b1467a3 MS |
987 | } |
988 | } | |
989 | ||
990 | static int w5100_set_macaddr(struct net_device *ndev, void *addr) | |
991 | { | |
992 | struct w5100_priv *priv = netdev_priv(ndev); | |
993 | struct sockaddr *sock_addr = addr; | |
994 | ||
995 | if (!is_valid_ether_addr(sock_addr->sa_data)) | |
996 | return -EADDRNOTAVAIL; | |
997 | memcpy(ndev->dev_addr, sock_addr->sa_data, ETH_ALEN); | |
8b1467a3 MS |
998 | w5100_write_macaddr(priv); |
999 | return 0; | |
1000 | } | |
1001 | ||
1002 | static int w5100_open(struct net_device *ndev) | |
1003 | { | |
1004 | struct w5100_priv *priv = netdev_priv(ndev); | |
1005 | ||
1006 | netif_info(priv, ifup, ndev, "enabling\n"); | |
8b1467a3 MS |
1007 | w5100_hw_start(priv); |
1008 | napi_enable(&priv->napi); | |
1009 | netif_start_queue(ndev); | |
1010 | if (!gpio_is_valid(priv->link_gpio) || | |
1011 | gpio_get_value(priv->link_gpio) != 0) | |
1012 | netif_carrier_on(ndev); | |
1013 | return 0; | |
1014 | } | |
1015 | ||
1016 | static int w5100_stop(struct net_device *ndev) | |
1017 | { | |
1018 | struct w5100_priv *priv = netdev_priv(ndev); | |
1019 | ||
1020 | netif_info(priv, ifdown, ndev, "shutting down\n"); | |
1021 | w5100_hw_close(priv); | |
1022 | netif_carrier_off(ndev); | |
1023 | netif_stop_queue(ndev); | |
1024 | napi_disable(&priv->napi); | |
1025 | return 0; | |
1026 | } | |
1027 | ||
1028 | static const struct ethtool_ops w5100_ethtool_ops = { | |
1029 | .get_drvinfo = w5100_get_drvinfo, | |
1030 | .get_msglevel = w5100_get_msglevel, | |
1031 | .set_msglevel = w5100_set_msglevel, | |
1032 | .get_link = w5100_get_link, | |
1033 | .get_regs_len = w5100_get_regs_len, | |
1034 | .get_regs = w5100_get_regs, | |
1035 | }; | |
1036 | ||
1037 | static const struct net_device_ops w5100_netdev_ops = { | |
1038 | .ndo_open = w5100_open, | |
1039 | .ndo_stop = w5100_stop, | |
1040 | .ndo_start_xmit = w5100_start_tx, | |
1041 | .ndo_tx_timeout = w5100_tx_timeout, | |
1042 | .ndo_set_rx_mode = w5100_set_rx_mode, | |
1043 | .ndo_set_mac_address = w5100_set_macaddr, | |
1044 | .ndo_validate_addr = eth_validate_addr, | |
1045 | .ndo_change_mtu = eth_change_mtu, | |
1046 | }; | |
1047 | ||
850576cf | 1048 | static int w5100_mmio_probe(struct platform_device *pdev) |
8b1467a3 | 1049 | { |
5988aa61 | 1050 | struct wiznet_platform_data *data = dev_get_platdata(&pdev->dev); |
850576cf | 1051 | u8 *mac_addr = NULL; |
8b1467a3 | 1052 | struct resource *mem; |
850576cf | 1053 | const struct w5100_ops *ops; |
8b1467a3 | 1054 | int irq; |
8b1467a3 | 1055 | |
850576cf AM |
1056 | if (data && is_valid_ether_addr(data->mac_addr)) |
1057 | mac_addr = data->mac_addr; | |
8b1467a3 MS |
1058 | |
1059 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
850576cf AM |
1060 | if (resource_size(mem) < W5100_BUS_DIRECT_SIZE) |
1061 | ops = &w5100_mmio_indirect_ops; | |
1062 | else | |
1063 | ops = &w5100_mmio_direct_ops; | |
8b1467a3 MS |
1064 | |
1065 | irq = platform_get_irq(pdev, 0); | |
1066 | if (irq < 0) | |
1067 | return irq; | |
8b1467a3 | 1068 | |
850576cf AM |
1069 | return w5100_probe(&pdev->dev, ops, sizeof(struct w5100_mmio_priv), |
1070 | mac_addr, irq, data ? data->link_gpio : -EINVAL); | |
1071 | } | |
8b1467a3 | 1072 | |
850576cf AM |
1073 | static int w5100_mmio_remove(struct platform_device *pdev) |
1074 | { | |
1075 | return w5100_remove(&pdev->dev); | |
8b1467a3 MS |
1076 | } |
1077 | ||
850576cf AM |
1078 | void *w5100_ops_priv(const struct net_device *ndev) |
1079 | { | |
1080 | return netdev_priv(ndev) + | |
1081 | ALIGN(sizeof(struct w5100_priv), NETDEV_ALIGN); | |
1082 | } | |
1083 | EXPORT_SYMBOL_GPL(w5100_ops_priv); | |
1084 | ||
1085 | int w5100_probe(struct device *dev, const struct w5100_ops *ops, | |
1086 | int sizeof_ops_priv, u8 *mac_addr, int irq, int link_gpio) | |
8b1467a3 MS |
1087 | { |
1088 | struct w5100_priv *priv; | |
1089 | struct net_device *ndev; | |
1090 | int err; | |
850576cf AM |
1091 | size_t alloc_size; |
1092 | ||
1093 | alloc_size = sizeof(*priv); | |
1094 | if (sizeof_ops_priv) { | |
1095 | alloc_size = ALIGN(alloc_size, NETDEV_ALIGN); | |
1096 | alloc_size += sizeof_ops_priv; | |
1097 | } | |
1098 | alloc_size += NETDEV_ALIGN - 1; | |
8b1467a3 | 1099 | |
850576cf | 1100 | ndev = alloc_etherdev(alloc_size); |
8b1467a3 MS |
1101 | if (!ndev) |
1102 | return -ENOMEM; | |
850576cf AM |
1103 | SET_NETDEV_DEV(ndev, dev); |
1104 | dev_set_drvdata(dev, ndev); | |
8b1467a3 | 1105 | priv = netdev_priv(ndev); |
35ef7d68 AM |
1106 | |
1107 | switch (ops->chip_id) { | |
1108 | case W5100: | |
1109 | priv->s0_regs = W5100_S0_REGS; | |
1110 | priv->s0_tx_buf = W5100_TX_MEM_START; | |
1111 | priv->s0_tx_buf_size = W5100_TX_MEM_SIZE; | |
1112 | priv->s0_rx_buf = W5100_RX_MEM_START; | |
1113 | priv->s0_rx_buf_size = W5100_RX_MEM_SIZE; | |
1114 | break; | |
1115 | case W5200: | |
1116 | priv->s0_regs = W5200_S0_REGS; | |
1117 | priv->s0_tx_buf = W5200_TX_MEM_START; | |
1118 | priv->s0_tx_buf_size = W5200_TX_MEM_SIZE; | |
1119 | priv->s0_rx_buf = W5200_RX_MEM_START; | |
1120 | priv->s0_rx_buf_size = W5200_RX_MEM_SIZE; | |
1121 | break; | |
1122 | case W5500: | |
1123 | priv->s0_regs = W5500_S0_REGS; | |
1124 | priv->s0_tx_buf = W5500_TX_MEM_START; | |
1125 | priv->s0_tx_buf_size = W5500_TX_MEM_SIZE; | |
1126 | priv->s0_rx_buf = W5500_RX_MEM_START; | |
1127 | priv->s0_rx_buf_size = W5500_RX_MEM_SIZE; | |
1128 | break; | |
1129 | default: | |
1130 | err = -EINVAL; | |
1131 | goto err_register; | |
1132 | } | |
1133 | ||
8b1467a3 | 1134 | priv->ndev = ndev; |
850576cf AM |
1135 | priv->ops = ops; |
1136 | priv->irq = irq; | |
1137 | priv->link_gpio = link_gpio; | |
8b1467a3 | 1138 | |
8b1467a3 MS |
1139 | ndev->netdev_ops = &w5100_netdev_ops; |
1140 | ndev->ethtool_ops = &w5100_ethtool_ops; | |
1141 | ndev->watchdog_timeo = HZ; | |
1142 | netif_napi_add(ndev, &priv->napi, w5100_napi_poll, 16); | |
1143 | ||
1144 | /* This chip doesn't support VLAN packets with normal MTU, | |
1145 | * so disable VLAN for this device. | |
1146 | */ | |
1147 | ndev->features |= NETIF_F_VLAN_CHALLENGED; | |
1148 | ||
1149 | err = register_netdev(ndev); | |
1150 | if (err < 0) | |
1151 | goto err_register; | |
1152 | ||
bf2c6b90 AM |
1153 | priv->xfer_wq = create_workqueue(netdev_name(ndev)); |
1154 | if (!priv->xfer_wq) { | |
1155 | err = -ENOMEM; | |
1156 | goto err_wq; | |
1157 | } | |
1158 | ||
1159 | INIT_WORK(&priv->rx_work, w5100_rx_work); | |
1160 | INIT_WORK(&priv->tx_work, w5100_tx_work); | |
1161 | INIT_WORK(&priv->setrx_work, w5100_setrx_work); | |
1162 | INIT_WORK(&priv->restart_work, w5100_restart_work); | |
1163 | ||
850576cf AM |
1164 | if (mac_addr) |
1165 | memcpy(ndev->dev_addr, mac_addr, ETH_ALEN); | |
1166 | else | |
1167 | eth_hw_addr_random(ndev); | |
1168 | ||
1169 | if (priv->ops->init) { | |
1170 | err = priv->ops->init(priv->ndev); | |
1171 | if (err) | |
1172 | goto err_hw; | |
1173 | } | |
1174 | ||
35ef7d68 AM |
1175 | err = w5100_hw_reset(priv); |
1176 | if (err) | |
850576cf | 1177 | goto err_hw; |
850576cf | 1178 | |
bf2c6b90 AM |
1179 | if (ops->may_sleep) { |
1180 | err = request_threaded_irq(priv->irq, NULL, w5100_interrupt, | |
1181 | IRQF_TRIGGER_LOW | IRQF_ONESHOT, | |
1182 | netdev_name(ndev), ndev); | |
1183 | } else { | |
1184 | err = request_irq(priv->irq, w5100_interrupt, | |
1185 | IRQF_TRIGGER_LOW, netdev_name(ndev), ndev); | |
1186 | } | |
850576cf AM |
1187 | if (err) |
1188 | goto err_hw; | |
1189 | ||
1190 | if (gpio_is_valid(priv->link_gpio)) { | |
1191 | char *link_name = devm_kzalloc(dev, 16, GFP_KERNEL); | |
1192 | ||
1193 | if (!link_name) { | |
1194 | err = -ENOMEM; | |
1195 | goto err_gpio; | |
1196 | } | |
1197 | snprintf(link_name, 16, "%s-link", netdev_name(ndev)); | |
1198 | priv->link_irq = gpio_to_irq(priv->link_gpio); | |
1199 | if (request_any_context_irq(priv->link_irq, w5100_detect_link, | |
1200 | IRQF_TRIGGER_RISING | | |
1201 | IRQF_TRIGGER_FALLING, | |
1202 | link_name, priv->ndev) < 0) | |
1203 | priv->link_gpio = -EINVAL; | |
1204 | } | |
8b1467a3 MS |
1205 | |
1206 | return 0; | |
1207 | ||
850576cf AM |
1208 | err_gpio: |
1209 | free_irq(priv->irq, ndev); | |
1210 | err_hw: | |
bf2c6b90 AM |
1211 | destroy_workqueue(priv->xfer_wq); |
1212 | err_wq: | |
8b1467a3 MS |
1213 | unregister_netdev(ndev); |
1214 | err_register: | |
1215 | free_netdev(ndev); | |
8b1467a3 MS |
1216 | return err; |
1217 | } | |
850576cf | 1218 | EXPORT_SYMBOL_GPL(w5100_probe); |
8b1467a3 | 1219 | |
850576cf | 1220 | int w5100_remove(struct device *dev) |
8b1467a3 | 1221 | { |
850576cf | 1222 | struct net_device *ndev = dev_get_drvdata(dev); |
8b1467a3 MS |
1223 | struct w5100_priv *priv = netdev_priv(ndev); |
1224 | ||
1225 | w5100_hw_reset(priv); | |
1226 | free_irq(priv->irq, ndev); | |
1227 | if (gpio_is_valid(priv->link_gpio)) | |
1228 | free_irq(priv->link_irq, ndev); | |
1229 | ||
bf2c6b90 AM |
1230 | flush_work(&priv->setrx_work); |
1231 | flush_work(&priv->restart_work); | |
1232 | flush_workqueue(priv->xfer_wq); | |
1233 | destroy_workqueue(priv->xfer_wq); | |
1234 | ||
8b1467a3 MS |
1235 | unregister_netdev(ndev); |
1236 | free_netdev(ndev); | |
8b1467a3 MS |
1237 | return 0; |
1238 | } | |
850576cf | 1239 | EXPORT_SYMBOL_GPL(w5100_remove); |
8b1467a3 | 1240 | |
4294beba | 1241 | #ifdef CONFIG_PM_SLEEP |
8b1467a3 MS |
1242 | static int w5100_suspend(struct device *dev) |
1243 | { | |
850576cf | 1244 | struct net_device *ndev = dev_get_drvdata(dev); |
8b1467a3 MS |
1245 | struct w5100_priv *priv = netdev_priv(ndev); |
1246 | ||
1247 | if (netif_running(ndev)) { | |
1248 | netif_carrier_off(ndev); | |
1249 | netif_device_detach(ndev); | |
1250 | ||
1251 | w5100_hw_close(priv); | |
1252 | } | |
1253 | return 0; | |
1254 | } | |
1255 | ||
1256 | static int w5100_resume(struct device *dev) | |
1257 | { | |
850576cf | 1258 | struct net_device *ndev = dev_get_drvdata(dev); |
8b1467a3 MS |
1259 | struct w5100_priv *priv = netdev_priv(ndev); |
1260 | ||
1261 | if (netif_running(ndev)) { | |
1262 | w5100_hw_reset(priv); | |
1263 | w5100_hw_start(priv); | |
1264 | ||
1265 | netif_device_attach(ndev); | |
1266 | if (!gpio_is_valid(priv->link_gpio) || | |
1267 | gpio_get_value(priv->link_gpio) != 0) | |
1268 | netif_carrier_on(ndev); | |
1269 | } | |
1270 | return 0; | |
1271 | } | |
4294beba | 1272 | #endif /* CONFIG_PM_SLEEP */ |
8b1467a3 | 1273 | |
850576cf AM |
1274 | SIMPLE_DEV_PM_OPS(w5100_pm_ops, w5100_suspend, w5100_resume); |
1275 | EXPORT_SYMBOL_GPL(w5100_pm_ops); | |
8b1467a3 | 1276 | |
850576cf | 1277 | static struct platform_driver w5100_mmio_driver = { |
8b1467a3 MS |
1278 | .driver = { |
1279 | .name = DRV_NAME, | |
8b1467a3 MS |
1280 | .pm = &w5100_pm_ops, |
1281 | }, | |
850576cf AM |
1282 | .probe = w5100_mmio_probe, |
1283 | .remove = w5100_mmio_remove, | |
8b1467a3 | 1284 | }; |
850576cf | 1285 | module_platform_driver(w5100_mmio_driver); |