Commit | Line | Data |
---|---|---|
dcbf8477 RB |
1 | /* |
2 | * This file is subject to the terms and conditions of the GNU General Public | |
3 | * License. See the file "COPYING" in the main directory of this archive | |
4 | * for more details. | |
5 | */ | |
6 | ||
dcbf8477 | 7 | #include <linux/init.h> |
539d3ee6 | 8 | #include <linux/interrupt.h> |
c2af68e5 | 9 | #include <linux/io.h> |
dcbf8477 RB |
10 | #include <linux/kernel.h> |
11 | #include <linux/module.h> | |
12 | #include <linux/netdevice.h> | |
dcbf8477 | 13 | #include <linux/etherdevice.h> |
d052d1be | 14 | #include <linux/platform_device.h> |
dcbf8477 RB |
15 | #include <asm/mips-boards/simint.h> |
16 | ||
c800c5c9 RB |
17 | #define MIPSNET_VERSION "2007-11-17" |
18 | ||
19 | /* | |
20 | * Net status/control block as seen by sw in the core. | |
21 | */ | |
22 | struct mipsnet_regs { | |
23 | /* | |
24 | * Device info for probing, reads as MIPSNET%d where %d is some | |
25 | * form of version. | |
26 | */ | |
27 | u64 devId; /*0x00 */ | |
dcbf8477 | 28 | |
c800c5c9 RB |
29 | /* |
30 | * read only busy flag. | |
31 | * Set and cleared by the Net Device to indicate that an rx or a tx | |
32 | * is in progress. | |
33 | */ | |
34 | u32 busy; /*0x08 */ | |
dcbf8477 | 35 | |
c800c5c9 RB |
36 | /* |
37 | * Set by the Net Device. | |
38 | * The device will set it once data has been received. | |
39 | * The value is the number of bytes that should be read from | |
40 | * rxDataBuffer. The value will decrease till 0 until all the data | |
41 | * from rxDataBuffer has been read. | |
42 | */ | |
43 | u32 rxDataCount; /*0x0c */ | |
44 | #define MIPSNET_MAX_RXTX_DATACOUNT (1 << 16) | |
45 | ||
46 | /* | |
47 | * Settable from the MIPS core, cleared by the Net Device. | |
48 | * The core should set the number of bytes it wants to send, | |
49 | * then it should write those bytes of data to txDataBuffer. | |
50 | * The device will clear txDataCount has been processed (not | |
51 | * necessarily sent). | |
52 | */ | |
53 | u32 txDataCount; /*0x10 */ | |
54 | ||
55 | /* | |
56 | * Interrupt control | |
57 | * | |
58 | * Used to clear the interrupted generated by this dev. | |
59 | * Write a 1 to clear the interrupt. (except bit31). | |
60 | * | |
61 | * Bit0 is set if it was a tx-done interrupt. | |
62 | * Bit1 is set when new rx-data is available. | |
63 | * Until this bit is cleared there will be no other RXs. | |
64 | * | |
65 | * Bit31 is used for testing, it clears after a read. | |
66 | * Writing 1 to this bit will cause an interrupt to be generated. | |
67 | * To clear the test interrupt, write 0 to this register. | |
68 | */ | |
69 | u32 interruptControl; /*0x14 */ | |
70 | #define MIPSNET_INTCTL_TXDONE (1u << 0) | |
71 | #define MIPSNET_INTCTL_RXDONE (1u << 1) | |
72 | #define MIPSNET_INTCTL_TESTBIT (1u << 31) | |
73 | ||
74 | /* | |
75 | * Readonly core-specific interrupt info for the device to signal | |
76 | * the core. The meaning of the contents of this field might change. | |
77 | */ | |
78 | /* XXX: the whole memIntf interrupt scheme is messy: the device | |
79 | * should have no control what so ever of what VPE/register set is | |
80 | * being used. | |
81 | * The MemIntf should only expose interrupt lines, and something in | |
82 | * the config should be responsible for the line<->core/vpe bindings. | |
83 | */ | |
84 | u32 interruptInfo; /*0x18 */ | |
85 | ||
86 | /* | |
87 | * This is where the received data is read out. | |
88 | * There is more data to read until rxDataReady is 0. | |
89 | * Only 1 byte at this regs offset is used. | |
90 | */ | |
91 | u32 rxDataBuffer; /*0x1c */ | |
92 | ||
93 | /* | |
94 | * This is where the data to transmit is written. | |
95 | * Data should be written for the amount specified in the | |
96 | * txDataCount register. | |
97 | * Only 1 byte at this regs offset is used. | |
98 | */ | |
99 | u32 txDataBuffer; /*0x20 */ | |
100 | }; | |
101 | ||
102 | #define regaddr(dev, field) \ | |
103 | (dev->base_addr + offsetof(struct mipsnet_regs, field)) | |
dcbf8477 | 104 | |
dcbf8477 RB |
105 | static char mipsnet_string[] = "mipsnet"; |
106 | ||
107 | /* | |
108 | * Copy data from the MIPSNET rx data port | |
109 | */ | |
110 | static int ioiocpy_frommipsnet(struct net_device *dev, unsigned char *kdata, | |
111 | int len) | |
112 | { | |
c2af68e5 | 113 | for (; len > 0; len--, kdata++) |
c800c5c9 | 114 | *kdata = inb(regaddr(dev, rxDataBuffer)); |
dcbf8477 | 115 | |
c800c5c9 | 116 | return inl(regaddr(dev, rxDataCount)); |
dcbf8477 RB |
117 | } |
118 | ||
c800c5c9 | 119 | static inline void mipsnet_put_todevice(struct net_device *dev, |
dcbf8477 RB |
120 | struct sk_buff *skb) |
121 | { | |
122 | int count_to_go = skb->len; | |
123 | char *buf_ptr = skb->data; | |
dcbf8477 | 124 | |
c800c5c9 | 125 | outl(skb->len, regaddr(dev, txDataCount)); |
dcbf8477 | 126 | |
c2af68e5 | 127 | for (; count_to_go; buf_ptr++, count_to_go--) |
c800c5c9 | 128 | outb(*buf_ptr, regaddr(dev, txDataBuffer)); |
dcbf8477 | 129 | |
09f75cd7 JG |
130 | dev->stats.tx_packets++; |
131 | dev->stats.tx_bytes += skb->len; | |
dcbf8477 | 132 | |
c800c5c9 | 133 | dev_kfree_skb(skb); |
dcbf8477 RB |
134 | } |
135 | ||
136 | static int mipsnet_xmit(struct sk_buff *skb, struct net_device *dev) | |
137 | { | |
f5e42fba RB |
138 | /* |
139 | * Only one packet at a time. Once TXDONE interrupt is serviced, the | |
dcbf8477 RB |
140 | * queue will be restarted. |
141 | */ | |
142 | netif_stop_queue(dev); | |
143 | mipsnet_put_todevice(dev, skb); | |
144 | ||
6ed10654 | 145 | return NETDEV_TX_OK; |
dcbf8477 RB |
146 | } |
147 | ||
c800c5c9 | 148 | static inline ssize_t mipsnet_get_fromdev(struct net_device *dev, size_t len) |
dcbf8477 RB |
149 | { |
150 | struct sk_buff *skb; | |
dcbf8477 | 151 | |
c800c5c9 RB |
152 | if (!len) |
153 | return len; | |
154 | ||
c056b734 | 155 | skb = netdev_alloc_skb(dev, len + NET_IP_ALIGN); |
c2af68e5 | 156 | if (!skb) { |
09f75cd7 | 157 | dev->stats.rx_dropped++; |
dcbf8477 RB |
158 | return -ENOMEM; |
159 | } | |
160 | ||
c800c5c9 | 161 | skb_reserve(skb, NET_IP_ALIGN); |
dcbf8477 RB |
162 | if (ioiocpy_frommipsnet(dev, skb_put(skb, len), len)) |
163 | return -EFAULT; | |
164 | ||
dcbf8477 RB |
165 | skb->protocol = eth_type_trans(skb, dev); |
166 | skb->ip_summed = CHECKSUM_UNNECESSARY; | |
167 | ||
dcbf8477 RB |
168 | netif_rx(skb); |
169 | ||
09f75cd7 JG |
170 | dev->stats.rx_packets++; |
171 | dev->stats.rx_bytes += len; | |
dcbf8477 | 172 | |
c800c5c9 | 173 | return len; |
dcbf8477 RB |
174 | } |
175 | ||
7d12e780 | 176 | static irqreturn_t mipsnet_interrupt(int irq, void *dev_id) |
dcbf8477 RB |
177 | { |
178 | struct net_device *dev = dev_id; | |
c800c5c9 RB |
179 | u32 int_flags; |
180 | irqreturn_t ret = IRQ_NONE; | |
181 | ||
182 | if (irq != dev->irq) | |
183 | goto out_badirq; | |
184 | ||
185 | /* TESTBIT is cleared on read. */ | |
186 | int_flags = inl(regaddr(dev, interruptControl)); | |
187 | if (int_flags & MIPSNET_INTCTL_TESTBIT) { | |
188 | /* TESTBIT takes effect after a write with 0. */ | |
189 | outl(0, regaddr(dev, interruptControl)); | |
190 | ret = IRQ_HANDLED; | |
191 | } else if (int_flags & MIPSNET_INTCTL_TXDONE) { | |
192 | /* Only one packet at a time, we are done. */ | |
193 | dev->stats.tx_packets++; | |
194 | netif_wake_queue(dev); | |
195 | outl(MIPSNET_INTCTL_TXDONE, | |
196 | regaddr(dev, interruptControl)); | |
197 | ret = IRQ_HANDLED; | |
198 | } else if (int_flags & MIPSNET_INTCTL_RXDONE) { | |
199 | mipsnet_get_fromdev(dev, inl(regaddr(dev, rxDataCount))); | |
200 | outl(MIPSNET_INTCTL_RXDONE, regaddr(dev, interruptControl)); | |
201 | ret = IRQ_HANDLED; | |
dcbf8477 | 202 | } |
c800c5c9 RB |
203 | return ret; |
204 | ||
205 | out_badirq: | |
206 | printk(KERN_INFO "%s: %s(): irq %d for unknown device\n", | |
b39d66a8 | 207 | dev->name, __func__, irq); |
c800c5c9 | 208 | return ret; |
c2af68e5 | 209 | } |
dcbf8477 RB |
210 | |
211 | static int mipsnet_open(struct net_device *dev) | |
212 | { | |
213 | int err; | |
dcbf8477 | 214 | |
a0607fd3 | 215 | err = request_irq(dev->irq, mipsnet_interrupt, |
1fb9df5d | 216 | IRQF_SHARED, dev->name, (void *) dev); |
dcbf8477 | 217 | if (err) { |
c800c5c9 | 218 | release_region(dev->base_addr, sizeof(struct mipsnet_regs)); |
dcbf8477 RB |
219 | return err; |
220 | } | |
221 | ||
dcbf8477 RB |
222 | netif_start_queue(dev); |
223 | ||
c2af68e5 | 224 | /* test interrupt handler */ |
c800c5c9 | 225 | outl(MIPSNET_INTCTL_TESTBIT, regaddr(dev, interruptControl)); |
dcbf8477 RB |
226 | |
227 | return 0; | |
228 | } | |
229 | ||
230 | static int mipsnet_close(struct net_device *dev) | |
231 | { | |
dcbf8477 | 232 | netif_stop_queue(dev); |
c800c5c9 | 233 | free_irq(dev->irq, dev); |
dcbf8477 RB |
234 | return 0; |
235 | } | |
236 | ||
dcbf8477 RB |
237 | static void mipsnet_set_mclist(struct net_device *dev) |
238 | { | |
dcbf8477 RB |
239 | } |
240 | ||
d6771e0b AB |
241 | static const struct net_device_ops mipsnet_netdev_ops = { |
242 | .ndo_open = mipsnet_open, | |
243 | .ndo_stop = mipsnet_close, | |
244 | .ndo_start_xmit = mipsnet_xmit, | |
afc4b13d | 245 | .ndo_set_rx_mode = mipsnet_set_mclist, |
d6771e0b AB |
246 | .ndo_change_mtu = eth_change_mtu, |
247 | .ndo_validate_addr = eth_validate_addr, | |
248 | .ndo_set_mac_address = eth_mac_addr, | |
249 | }; | |
250 | ||
ade2d3db | 251 | static int __devinit mipsnet_probe(struct platform_device *dev) |
dcbf8477 RB |
252 | { |
253 | struct net_device *netdev; | |
254 | int err; | |
255 | ||
09f75cd7 | 256 | netdev = alloc_etherdev(0); |
dcbf8477 RB |
257 | if (!netdev) { |
258 | err = -ENOMEM; | |
259 | goto out; | |
260 | } | |
261 | ||
7a192ec3 | 262 | platform_set_drvdata(dev, netdev); |
dcbf8477 | 263 | |
d6771e0b | 264 | netdev->netdev_ops = &mipsnet_netdev_ops; |
dcbf8477 RB |
265 | |
266 | /* | |
267 | * TODO: probe for these or load them from PARAM | |
268 | */ | |
269 | netdev->base_addr = 0x4200; | |
3b1d4ed5 | 270 | netdev->irq = MIPS_CPU_IRQ_BASE + MIPSCPU_INT_MB0 + |
c800c5c9 | 271 | inl(regaddr(netdev, interruptInfo)); |
dcbf8477 | 272 | |
c2af68e5 | 273 | /* Get the io region now, get irq on open() */ |
c800c5c9 RB |
274 | if (!request_region(netdev->base_addr, sizeof(struct mipsnet_regs), |
275 | "mipsnet")) { | |
dcbf8477 RB |
276 | err = -EBUSY; |
277 | goto out_free_netdev; | |
278 | } | |
279 | ||
280 | /* | |
281 | * Lacking any better mechanism to allocate a MAC address we use a | |
282 | * random one ... | |
283 | */ | |
f2cedb63 | 284 | eth_hw_addr_random(netdev); |
dcbf8477 RB |
285 | |
286 | err = register_netdev(netdev); | |
287 | if (err) { | |
288 | printk(KERN_ERR "MIPSNet: failed to register netdev.\n"); | |
289 | goto out_free_region; | |
290 | } | |
291 | ||
292 | return 0; | |
293 | ||
294 | out_free_region: | |
c800c5c9 | 295 | release_region(netdev->base_addr, sizeof(struct mipsnet_regs)); |
dcbf8477 RB |
296 | |
297 | out_free_netdev: | |
298 | free_netdev(netdev); | |
299 | ||
300 | out: | |
301 | return err; | |
302 | } | |
303 | ||
7a192ec3 | 304 | static int __devexit mipsnet_device_remove(struct platform_device *device) |
dcbf8477 | 305 | { |
7a192ec3 | 306 | struct net_device *dev = platform_get_drvdata(device); |
dcbf8477 RB |
307 | |
308 | unregister_netdev(dev); | |
c800c5c9 | 309 | release_region(dev->base_addr, sizeof(struct mipsnet_regs)); |
dcbf8477 | 310 | free_netdev(dev); |
7a192ec3 | 311 | platform_set_drvdata(device, NULL); |
dcbf8477 RB |
312 | |
313 | return 0; | |
314 | } | |
315 | ||
7a192ec3 ML |
316 | static struct platform_driver mipsnet_driver = { |
317 | .driver = { | |
318 | .name = mipsnet_string, | |
319 | .owner = THIS_MODULE, | |
320 | }, | |
321 | .probe = mipsnet_probe, | |
322 | .remove = __devexit_p(mipsnet_device_remove), | |
dcbf8477 RB |
323 | }; |
324 | ||
dcbf8477 RB |
325 | static int __init mipsnet_init_module(void) |
326 | { | |
dcbf8477 RB |
327 | int err; |
328 | ||
329 | printk(KERN_INFO "MIPSNet Ethernet driver. Version: %s. " | |
330 | "(c)2005 MIPS Technologies, Inc.\n", MIPSNET_VERSION); | |
331 | ||
7a192ec3 | 332 | err = platform_driver_register(&mipsnet_driver); |
1e2b980f | 333 | if (err) |
dcbf8477 | 334 | printk(KERN_ERR "Driver registration failed\n"); |
dcbf8477 | 335 | |
dcbf8477 RB |
336 | return err; |
337 | } | |
338 | ||
339 | static void __exit mipsnet_exit_module(void) | |
340 | { | |
7a192ec3 | 341 | platform_driver_unregister(&mipsnet_driver); |
dcbf8477 RB |
342 | } |
343 | ||
344 | module_init(mipsnet_init_module); | |
345 | module_exit(mipsnet_exit_module); |