aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ixp2000/ixpdev.c
diff options
context:
space:
mode:
authorLennert Buytenhek <buytenh@wantstofly.org>2005-11-11 12:23:13 -0500
committerJeff Garzik <jgarzik@pobox.com>2005-11-18 13:32:22 -0500
commit15d014d13149aedd76cbff1b5c3bbfe839391457 (patch)
tree748af38a44021cf286016b179e58190b685c1630 /drivers/net/ixp2000/ixpdev.c
parent7f7f53168dbee6d6a462acea666fddd18aad4f08 (diff)
[PATCH] intel ixp2000 network driver
The way the hardware and firmware work is that there is one shared RX queue and IRQ for a number of different network interfaces. Due to this, we would like to process received packets for every interface in the same NAPI poll handler, so we need a pseudo-device to schedule polling on. What the driver currently does is that it always schedules polling for the first network interface in the list, and processes packets for every interface in the poll handler for that first interface -- however, this scheme breaks down if the first network interface happens to not be up, since netif_rx_schedule_prep() checks netif_running(). sky2 apparently has the same issue, and Stephen Hemminger suggested a way to work around this: create a variant of netif_rx_schedule_prep() that does not check netif_running(). I implemented this locally and called it netif_rx_schedule_prep_notup(), and it seems to work well, but it's something that probably not everyone would be happy with. The ixp2000 is an ARM CPU with a high-speed network interface in the CPU itself (full duplex 4Gb/s or 10Gb/s depending on the IXP model.) The CPU package also contains 8 or 16 (again depending on the IXP model) 'microengines', which are somewhat primitive but very fast and efficient processor cores which can be used to offload various things from the main CPU. This driver makes the high-speed network interface in the CPU visible and usable as a regular linux network device. Currently, it only supports the Radisys ENP2611 IXP board, but adding support for other board types should be fairly easy. Signed-off-by: Lennert Buytenhek <buytenh@wantstofly.org> Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
Diffstat (limited to 'drivers/net/ixp2000/ixpdev.c')
-rw-r--r--drivers/net/ixp2000/ixpdev.c404
1 files changed, 404 insertions, 0 deletions
diff --git a/drivers/net/ixp2000/ixpdev.c b/drivers/net/ixp2000/ixpdev.c
new file mode 100644
index 000000000000..216aad1911e6
--- /dev/null
+++ b/drivers/net/ixp2000/ixpdev.c
@@ -0,0 +1,404 @@
1/*
2 * IXP2000 MSF network device driver
3 * Copyright (C) 2004, 2005 Lennert Buytenhek <buytenh@wantstofly.org>
4 * Dedicated to Marija Kulikova.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12#include <linux/config.h>
13#include <linux/module.h>
14#include <linux/kernel.h>
15#include <linux/netdevice.h>
16#include <linux/etherdevice.h>
17#include <linux/init.h>
18#include <linux/moduleparam.h>
19#include <asm/arch/uengine.h>
20#include <asm/mach-types.h>
21#include <asm/io.h>
22#include "ixp2400_rx.ucode"
23#include "ixp2400_tx.ucode"
24#include "ixpdev_priv.h"
25#include "ixpdev.h"
26
27static int nds_count;
28static struct net_device **nds;
29static int nds_open;
30static void (*set_port_admin_status)(int port, int up);
31
32static struct ixpdev_rx_desc * const rx_desc =
33 (struct ixpdev_rx_desc *)(IXP2000_SRAM0_VIRT_BASE + RX_BUF_DESC_BASE);
34static struct ixpdev_tx_desc * const tx_desc =
35 (struct ixpdev_tx_desc *)(IXP2000_SRAM0_VIRT_BASE + TX_BUF_DESC_BASE);
36static int tx_pointer;
37
38
39static int ixpdev_xmit(struct sk_buff *skb, struct net_device *dev)
40{
41 struct ixpdev_priv *ip = netdev_priv(dev);
42 struct ixpdev_tx_desc *desc;
43 int entry;
44
45 if (unlikely(skb->len > PAGE_SIZE)) {
46 /* @@@ Count drops. */
47 dev_kfree_skb(skb);
48 return 0;
49 }
50
51 entry = tx_pointer;
52 tx_pointer = (tx_pointer + 1) % TX_BUF_COUNT;
53
54 desc = tx_desc + entry;
55 desc->pkt_length = skb->len;
56 desc->channel = ip->channel;
57
58 skb_copy_and_csum_dev(skb, phys_to_virt(desc->buf_addr));
59 dev_kfree_skb(skb);
60
61 ixp2000_reg_write(RING_TX_PENDING,
62 TX_BUF_DESC_BASE + (entry * sizeof(struct ixpdev_tx_desc)));
63
64 dev->trans_start = jiffies;
65
66 local_irq_disable();
67 ip->tx_queue_entries++;
68 if (ip->tx_queue_entries == TX_BUF_COUNT_PER_CHAN)
69 netif_stop_queue(dev);
70 local_irq_enable();
71
72 return 0;
73}
74
75
76static int ixpdev_rx(struct net_device *dev, int *budget)
77{
78 while (*budget > 0) {
79 struct ixpdev_rx_desc *desc;
80 struct sk_buff *skb;
81 void *buf;
82 u32 _desc;
83
84 _desc = ixp2000_reg_read(RING_RX_DONE);
85 if (_desc == 0)
86 return 0;
87
88 desc = rx_desc +
89 ((_desc - RX_BUF_DESC_BASE) / sizeof(struct ixpdev_rx_desc));
90 buf = phys_to_virt(desc->buf_addr);
91
92 if (desc->pkt_length < 4 || desc->pkt_length > PAGE_SIZE) {
93 printk(KERN_ERR "ixp2000: rx err, length %d\n",
94 desc->pkt_length);
95 goto err;
96 }
97
98 if (desc->channel < 0 || desc->channel >= nds_count) {
99 printk(KERN_ERR "ixp2000: rx err, channel %d\n",
100 desc->channel);
101 goto err;
102 }
103
104 /* @@@ Make FCS stripping configurable. */
105 desc->pkt_length -= 4;
106
107 if (unlikely(!netif_running(nds[desc->channel])))
108 goto err;
109
110 skb = dev_alloc_skb(desc->pkt_length + 2);
111 if (likely(skb != NULL)) {
112 skb->dev = nds[desc->channel];
113 skb_reserve(skb, 2);
114 eth_copy_and_sum(skb, buf, desc->pkt_length, 0);
115 skb_put(skb, desc->pkt_length);
116 skb->protocol = eth_type_trans(skb, skb->dev);
117
118 skb->dev->last_rx = jiffies;
119
120 netif_receive_skb(skb);
121 }
122
123err:
124 ixp2000_reg_write(RING_RX_PENDING, _desc);
125 dev->quota--;
126 (*budget)--;
127 }
128
129 return 1;
130}
131
132/* dev always points to nds[0]. */
133static int ixpdev_poll(struct net_device *dev, int *budget)
134{
135 /* @@@ Have to stop polling when nds[0] is administratively
136 * downed while we are polling. */
137 do {
138 ixp2000_reg_write(IXP2000_IRQ_THD_RAW_STATUS_A_0, 0x00ff);
139
140 if (ixpdev_rx(dev, budget))
141 return 1;
142 } while (ixp2000_reg_read(IXP2000_IRQ_THD_RAW_STATUS_A_0) & 0x00ff);
143
144 netif_rx_complete(dev);
145 ixp2000_reg_write(IXP2000_IRQ_THD_ENABLE_SET_A_0, 0x00ff);
146
147 return 0;
148}
149
150/* @@@ Ugly hack. */
151static inline int netif_rx_schedule_prep_notup(struct net_device *dev)
152{
153 return !test_and_set_bit(__LINK_STATE_RX_SCHED, &dev->state);
154}
155
156static void ixpdev_tx_complete(void)
157{
158 int channel;
159 u32 wake;
160
161 wake = 0;
162 while (1) {
163 struct ixpdev_priv *ip;
164 u32 desc;
165 int entry;
166
167 desc = ixp2000_reg_read(RING_TX_DONE);
168 if (desc == 0)
169 break;
170
171 /* @@@ Check whether entries come back in order. */
172 entry = (desc - TX_BUF_DESC_BASE) / sizeof(struct ixpdev_tx_desc);
173 channel = tx_desc[entry].channel;
174
175 if (channel < 0 || channel >= nds_count) {
176 printk(KERN_ERR "ixp2000: txcomp channel index "
177 "out of bounds (%d, %.8i, %d)\n",
178 channel, (unsigned int)desc, entry);
179 continue;
180 }
181
182 ip = netdev_priv(nds[channel]);
183 if (ip->tx_queue_entries == TX_BUF_COUNT_PER_CHAN)
184 wake |= 1 << channel;
185 ip->tx_queue_entries--;
186 }
187
188 for (channel = 0; wake != 0; channel++) {
189 if (wake & (1 << channel)) {
190 netif_wake_queue(nds[channel]);
191 wake &= ~(1 << channel);
192 }
193 }
194}
195
196static irqreturn_t ixpdev_interrupt(int irq, void *dev_id, struct pt_regs *regs)
197{
198 u32 status;
199
200 status = ixp2000_reg_read(IXP2000_IRQ_THD_STATUS_A_0);
201 if (status == 0)
202 return IRQ_NONE;
203
204 /*
205 * Any of the eight receive units signaled RX?
206 */
207 if (status & 0x00ff) {
208 ixp2000_reg_wrb(IXP2000_IRQ_THD_ENABLE_CLEAR_A_0, 0x00ff);
209 if (likely(netif_rx_schedule_prep_notup(nds[0]))) {
210 __netif_rx_schedule(nds[0]);
211 } else {
212 printk(KERN_CRIT "ixp2000: irq while polling!!\n");
213 }
214 }
215
216 /*
217 * Any of the eight transmit units signaled TXdone?
218 */
219 if (status & 0xff00) {
220 ixp2000_reg_wrb(IXP2000_IRQ_THD_RAW_STATUS_A_0, 0xff00);
221 ixpdev_tx_complete();
222 }
223
224 return IRQ_HANDLED;
225}
226
227static int ixpdev_open(struct net_device *dev)
228{
229 struct ixpdev_priv *ip = netdev_priv(dev);
230 int err;
231
232 if (!nds_open++) {
233 err = request_irq(IRQ_IXP2000_THDA0, ixpdev_interrupt,
234 SA_SHIRQ, "ixp2000_eth", nds);
235 if (err) {
236 nds_open--;
237 return err;
238 }
239
240 ixp2000_reg_write(IXP2000_IRQ_THD_ENABLE_SET_A_0, 0xffff);
241 }
242
243 set_port_admin_status(ip->channel, 1);
244 netif_start_queue(dev);
245
246 return 0;
247}
248
249static int ixpdev_close(struct net_device *dev)
250{
251 struct ixpdev_priv *ip = netdev_priv(dev);
252
253 netif_stop_queue(dev);
254 set_port_admin_status(ip->channel, 0);
255
256 if (!--nds_open) {
257 ixp2000_reg_write(IXP2000_IRQ_THD_ENABLE_CLEAR_A_0, 0xffff);
258 free_irq(IRQ_IXP2000_THDA0, nds);
259 }
260
261 return 0;
262}
263
264struct net_device *ixpdev_alloc(int channel, int sizeof_priv)
265{
266 struct net_device *dev;
267 struct ixpdev_priv *ip;
268
269 dev = alloc_etherdev(sizeof_priv);
270 if (dev == NULL)
271 return NULL;
272
273 dev->hard_start_xmit = ixpdev_xmit;
274 dev->poll = ixpdev_poll;
275 dev->open = ixpdev_open;
276 dev->stop = ixpdev_close;
277
278 dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM;
279 dev->weight = 64;
280
281 ip = netdev_priv(dev);
282 ip->channel = channel;
283 ip->tx_queue_entries = 0;
284
285 return dev;
286}
287
288int ixpdev_init(int __nds_count, struct net_device **__nds,
289 void (*__set_port_admin_status)(int port, int up))
290{
291 int i;
292 int err;
293
294 if (RX_BUF_COUNT > 192 || TX_BUF_COUNT > 192) {
295 static void __too_many_rx_or_tx_buffers(void);
296 __too_many_rx_or_tx_buffers();
297 }
298
299 nds_count = __nds_count;
300 nds = __nds;
301 set_port_admin_status = __set_port_admin_status;
302
303 for (i = 0; i < nds_count; i++) {
304 err = register_netdev(nds[i]);
305 if (err) {
306 while (--i >= 0)
307 unregister_netdev(nds[i]);
308 goto err_out;
309 }
310 }
311
312 for (i = 0; i < RX_BUF_COUNT; i++) {
313 void *buf;
314
315 buf = (void *)get_zeroed_page(GFP_KERNEL);
316 if (buf == NULL) {
317 err = -ENOMEM;
318 while (--i >= 0)
319 free_page((unsigned long)phys_to_virt(rx_desc[i].buf_addr));
320 goto err_unregister;
321 }
322 rx_desc[i].buf_addr = virt_to_phys(buf);
323 rx_desc[i].buf_length = PAGE_SIZE;
324 }
325
326 /* @@@ Maybe we shouldn't be preallocating TX buffers. */
327 for (i = 0; i < TX_BUF_COUNT; i++) {
328 void *buf;
329
330 buf = (void *)get_zeroed_page(GFP_KERNEL);
331 if (buf == NULL) {
332 err = -ENOMEM;
333 while (--i >= 0)
334 free_page((unsigned long)phys_to_virt(tx_desc[i].buf_addr));
335 goto err_free_rx;
336 }
337 tx_desc[i].buf_addr = virt_to_phys(buf);
338 }
339
340 /* 256 entries, ring status set means 'empty', base address 0x0000. */
341 ixp2000_reg_write(RING_RX_PENDING_BASE, 0x44000000);
342 ixp2000_reg_write(RING_RX_PENDING_HEAD, 0x00000000);
343 ixp2000_reg_write(RING_RX_PENDING_TAIL, 0x00000000);
344
345 /* 256 entries, ring status set means 'full', base address 0x0400. */
346 ixp2000_reg_write(RING_RX_DONE_BASE, 0x40000400);
347 ixp2000_reg_write(RING_RX_DONE_HEAD, 0x00000000);
348 ixp2000_reg_write(RING_RX_DONE_TAIL, 0x00000000);
349
350 for (i = 0; i < RX_BUF_COUNT; i++) {
351 ixp2000_reg_write(RING_RX_PENDING,
352 RX_BUF_DESC_BASE + (i * sizeof(struct ixpdev_rx_desc)));
353 }
354
355 ixp2000_uengine_load(0, &ixp2400_rx);
356 ixp2000_uengine_start_contexts(0, 0xff);
357
358
359 /* 256 entries, ring status set means 'empty', base address 0x0800. */
360 ixp2000_reg_write(RING_TX_PENDING_BASE, 0x44000800);
361 ixp2000_reg_write(RING_TX_PENDING_HEAD, 0x00000000);
362 ixp2000_reg_write(RING_TX_PENDING_TAIL, 0x00000000);
363
364 /* 256 entries, ring status set means 'full', base address 0x0c00. */
365 ixp2000_reg_write(RING_TX_DONE_BASE, 0x40000c00);
366 ixp2000_reg_write(RING_TX_DONE_HEAD, 0x00000000);
367 ixp2000_reg_write(RING_TX_DONE_TAIL, 0x00000000);
368
369 ixp2000_uengine_load(1, &ixp2400_tx);
370 ixp2000_uengine_start_contexts(1, 0xff);
371
372 return 0;
373
374err_free_rx:
375 for (i = 0; i < RX_BUF_COUNT; i++)
376 free_page((unsigned long)phys_to_virt(rx_desc[i].buf_addr));
377
378err_unregister:
379 for (i = 0; i < nds_count; i++)
380 unregister_netdev(nds[i]);
381
382err_out:
383 return err;
384}
385
386void ixpdev_deinit(void)
387{
388 int i;
389
390 /* @@@ Flush out pending packets. */
391
392 ixp2000_uengine_stop_contexts(1, 0xff);
393 ixp2000_uengine_stop_contexts(0, 0xff);
394 ixp2000_uengine_reset(0x3);
395
396 for (i = 0; i < TX_BUF_COUNT; i++)
397 free_page((unsigned long)phys_to_virt(tx_desc[i].buf_addr));
398
399 for (i = 0; i < RX_BUF_COUNT; i++)
400 free_page((unsigned long)phys_to_virt(rx_desc[i].buf_addr));
401
402 for (i = 0; i < nds_count; i++)
403 unregister_netdev(nds[i]);
404}