aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ixp2000/pm3386.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/pm3386.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/pm3386.c')
-rw-r--r--drivers/net/ixp2000/pm3386.c304
1 files changed, 304 insertions, 0 deletions
diff --git a/drivers/net/ixp2000/pm3386.c b/drivers/net/ixp2000/pm3386.c
new file mode 100644
index 000000000000..cf0681fb1276
--- /dev/null
+++ b/drivers/net/ixp2000/pm3386.c
@@ -0,0 +1,304 @@
1/*
2 * Helper functions for the PM3386s on the Radisys ENP2611
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/delay.h>
15#include <linux/netdevice.h>
16#include <asm/io.h>
17
18/*
19 * Read from register 'reg' of PM3386 device 'pm'.
20 */
21static u16 pm3386_reg_read(int pm, int reg)
22{
23 void *_reg;
24 u16 value;
25
26 _reg = (void *)ENP2611_PM3386_0_VIRT_BASE;
27 if (pm == 1)
28 _reg = (void *)ENP2611_PM3386_1_VIRT_BASE;
29
30 value = *((volatile u16 *)(_reg + (reg << 1)));
31
32// printk(KERN_INFO "pm3386_reg_read(%d, %.3x) = %.8x\n", pm, reg, value);
33
34 return value;
35}
36
37/*
38 * Write to register 'reg' of PM3386 device 'pm', and perform
39 * a readback from the identification register.
40 */
41static void pm3386_reg_write(int pm, int reg, u16 value)
42{
43 void *_reg;
44 u16 dummy;
45
46// printk(KERN_INFO "pm3386_reg_write(%d, %.3x, %.8x)\n", pm, reg, value);
47
48 _reg = (void *)ENP2611_PM3386_0_VIRT_BASE;
49 if (pm == 1)
50 _reg = (void *)ENP2611_PM3386_1_VIRT_BASE;
51
52 *((volatile u16 *)(_reg + (reg << 1))) = value;
53
54 dummy = *((volatile u16 *)_reg);
55 __asm__ __volatile__("mov %0, %0" : "+r" (dummy));
56}
57
58/*
59 * Read from port 'port' register 'reg', where the registers
60 * for the different ports are 'spacing' registers apart.
61 */
62static u16 pm3386_port_reg_read(int port, int _reg, int spacing)
63{
64 int reg;
65
66 reg = _reg;
67 if (port & 1)
68 reg += spacing;
69
70 return pm3386_reg_read(port >> 1, reg);
71}
72
73/*
74 * Write to port 'port' register 'reg', where the registers
75 * for the different ports are 'spacing' registers apart.
76 */
77static void pm3386_port_reg_write(int port, int _reg, int spacing, u16 value)
78{
79 int reg;
80
81 reg = _reg;
82 if (port & 1)
83 reg += spacing;
84
85 pm3386_reg_write(port >> 1, reg, value);
86}
87
88
89void pm3386_reset(void)
90{
91 /* @@@ Implement me. */
92}
93
94static u16 swaph(u16 x)
95{
96 return ((x << 8) | (x >> 8)) & 0xffff;
97}
98
99void pm3386_init_port(int port)
100{
101 int pm = port >> 1;
102
103 /*
104 * Work around ENP2611 bootloader programming MAC address
105 * in reverse.
106 */
107 if (pm3386_port_reg_read(port, 0x30a, 0x100) == 0x0000 &&
108 (pm3386_port_reg_read(port, 0x309, 0x100) & 0xff00) == 0x5000) {
109 u16 temp[3];
110
111 temp[0] = pm3386_port_reg_read(port, 0x308, 0x100);
112 temp[1] = pm3386_port_reg_read(port, 0x309, 0x100);
113 temp[2] = pm3386_port_reg_read(port, 0x30a, 0x100);
114 pm3386_port_reg_write(port, 0x308, 0x100, swaph(temp[2]));
115 pm3386_port_reg_write(port, 0x309, 0x100, swaph(temp[1]));
116 pm3386_port_reg_write(port, 0x30a, 0x100, swaph(temp[0]));
117 }
118
119 /*
120 * Initialise narrowbanding mode. See application note 2010486
121 * for more information. (@@@ We also need to issue a reset
122 * when ROOL or DOOL are detected.)
123 */
124 pm3386_port_reg_write(port, 0x708, 0x10, 0xd055);
125 udelay(500);
126 pm3386_port_reg_write(port, 0x708, 0x10, 0x5055);
127
128 /*
129 * SPI-3 ingress block. Set 64 bytes SPI-3 burst size
130 * towards SPI-3 bridge.
131 */
132 pm3386_port_reg_write(port, 0x122, 0x20, 0x0002);
133
134 /*
135 * Enable ingress protocol checking, and soft reset the
136 * SPI-3 ingress block.
137 */
138 pm3386_reg_write(pm, 0x103, 0x0003);
139 while (!(pm3386_reg_read(pm, 0x103) & 0x80))
140 ;
141
142 /*
143 * SPI-3 egress block. Gather 12288 bytes of the current
144 * packet in the TX fifo before initiating transmit on the
145 * SERDES interface. (Prevents TX underflows.)
146 */
147 pm3386_port_reg_write(port, 0x221, 0x20, 0x0007);
148
149 /*
150 * Enforce odd parity from the SPI-3 bridge, and soft reset
151 * the SPI-3 egress block.
152 */
153 pm3386_reg_write(pm, 0x203, 0x000d & ~(4 << (port & 1)));
154 while ((pm3386_reg_read(pm, 0x203) & 0x000c) != 0x000c)
155 ;
156
157 /*
158 * EGMAC block. Set this channels to reject long preambles,
159 * not send or transmit PAUSE frames, enable preamble checking,
160 * disable frame length checking, enable FCS appending, enable
161 * TX frame padding.
162 */
163 pm3386_port_reg_write(port, 0x302, 0x100, 0x0113);
164
165 /*
166 * Soft reset the EGMAC block.
167 */
168 pm3386_port_reg_write(port, 0x301, 0x100, 0x8000);
169 udelay(10);
170 pm3386_port_reg_write(port, 0x301, 0x100, 0x0000);
171 udelay(10);
172
173 /*
174 * Auto-sense autonegotiation status.
175 */
176 pm3386_port_reg_write(port, 0x306, 0x100, 0x0100);
177
178 /*
179 * Allow reception of jumbo frames.
180 */
181 pm3386_port_reg_write(port, 0x310, 0x100, 9018);
182
183 /*
184 * Allow transmission of jumbo frames.
185 */
186 pm3386_port_reg_write(port, 0x336, 0x100, 9018);
187
188 /* @@@ Should set 0x337/0x437 (RX forwarding threshold.) */
189
190 /*
191 * Set autonegotiation parameters to 'no PAUSE, full duplex.'
192 */
193 pm3386_port_reg_write(port, 0x31c, 0x100, 0x0020);
194 udelay(10);
195
196 /*
197 * Enable and restart autonegotiation.
198 */
199 pm3386_port_reg_write(port, 0x318, 0x100, 0x0003);
200 udelay(1000);
201 pm3386_port_reg_write(port, 0x318, 0x100, 0x0002);
202 udelay(10);
203}
204
205void pm3386_get_mac(int port, u8 *mac)
206{
207 u16 temp;
208
209 temp = pm3386_port_reg_read(port, 0x308, 0x100);
210 mac[0] = temp & 0xff;
211 mac[1] = (temp >> 8) & 0xff;
212
213 temp = pm3386_port_reg_read(port, 0x309, 0x100);
214 mac[2] = temp & 0xff;
215 mac[3] = (temp >> 8) & 0xff;
216
217 temp = pm3386_port_reg_read(port, 0x30a, 0x100);
218 mac[4] = temp & 0xff;
219 mac[5] = (temp >> 8) & 0xff;
220}
221
222static u32 pm3386_get_stat(int port, u16 base)
223{
224 u32 value;
225
226 value = pm3386_port_reg_read(port, base, 0x100);
227 value |= pm3386_port_reg_read(port, base + 1, 0x100) << 16;
228
229 return value;
230}
231
232void pm3386_get_stats(int port, struct net_device_stats *stats)
233{
234 /*
235 * Snapshot statistics counters.
236 */
237 pm3386_port_reg_write(port, 0x500, 0x100, 0x0001);
238 while (pm3386_port_reg_read(port, 0x500, 0x100) & 0x0001)
239 ;
240
241 memset(stats, 0, sizeof(stats));
242
243 stats->rx_packets = pm3386_get_stat(port, 0x510);
244 stats->tx_packets = pm3386_get_stat(port, 0x590);
245 stats->rx_bytes = pm3386_get_stat(port, 0x514);
246 stats->tx_bytes = pm3386_get_stat(port, 0x594);
247 /* @@@ Add other stats. */
248}
249
250int pm3386_is_link_up(int port)
251{
252 u16 temp;
253
254 temp = pm3386_port_reg_read(port, 0x31a, 0x100);
255 temp = pm3386_port_reg_read(port, 0x31a, 0x100);
256
257 return !!(temp & 0x0002);
258}
259
260void pm3386_enable_rx(int port)
261{
262 u16 temp;
263
264 temp = pm3386_port_reg_read(port, 0x303, 0x100);
265 temp |= 0x1000;
266 pm3386_port_reg_write(port, 0x303, 0x100, temp);
267
268 udelay(10);
269}
270
271void pm3386_disable_rx(int port)
272{
273 u16 temp;
274
275 temp = pm3386_port_reg_read(port, 0x303, 0x100);
276 temp &= 0xefff;
277 pm3386_port_reg_write(port, 0x303, 0x100, temp);
278
279 udelay(10);
280}
281
282void pm3386_enable_tx(int port)
283{
284 u16 temp;
285
286 temp = pm3386_port_reg_read(port, 0x303, 0x100);
287 temp |= 0x4000;
288 pm3386_port_reg_write(port, 0x303, 0x100, temp);
289
290 udelay(10);
291}
292
293void pm3386_disable_tx(int port)
294{
295 u16 temp;
296
297 temp = pm3386_port_reg_read(port, 0x303, 0x100);
298 temp &= 0xbfff;
299 pm3386_port_reg_write(port, 0x303, 0x100, temp);
300
301 udelay(10);
302}
303
304MODULE_LICENSE("GPL");