aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ixp2000
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@g5.osdl.org>2006-01-04 19:31:56 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-01-04 19:31:56 -0500
commitd779188d2baf436e67fe8816fca2ef53d246900f (patch)
tree9bac75842a5611172860feec3c4019ff874a2b89 /drivers/net/ixp2000
parentf61ea1b0c825a20a1826bb43a226387091934586 (diff)
parentac67c6247361b3b8644b34e5301a46d5069c1373 (diff)
Merge branch 'upstream-linus' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik/netdev-2.6
Diffstat (limited to 'drivers/net/ixp2000')
-rw-r--r--drivers/net/ixp2000/Kconfig6
-rw-r--r--drivers/net/ixp2000/Makefile3
-rw-r--r--drivers/net/ixp2000/caleb.c137
-rw-r--r--drivers/net/ixp2000/caleb.h22
-rw-r--r--drivers/net/ixp2000/enp2611.c245
-rw-r--r--drivers/net/ixp2000/ixp2400-msf.c213
-rw-r--r--drivers/net/ixp2000/ixp2400-msf.h115
-rw-r--r--drivers/net/ixp2000/ixp2400_rx.uc408
-rw-r--r--drivers/net/ixp2000/ixp2400_rx.ucode130
-rw-r--r--drivers/net/ixp2000/ixp2400_tx.uc272
-rw-r--r--drivers/net/ixp2000/ixp2400_tx.ucode98
-rw-r--r--drivers/net/ixp2000/ixpdev.c421
-rw-r--r--drivers/net/ixp2000/ixpdev.h27
-rw-r--r--drivers/net/ixp2000/ixpdev_priv.h57
-rw-r--r--drivers/net/ixp2000/pm3386.c334
-rw-r--r--drivers/net/ixp2000/pm3386.h28
16 files changed, 2516 insertions, 0 deletions
diff --git a/drivers/net/ixp2000/Kconfig b/drivers/net/ixp2000/Kconfig
new file mode 100644
index 000000000000..2fec2415651f
--- /dev/null
+++ b/drivers/net/ixp2000/Kconfig
@@ -0,0 +1,6 @@
1config ENP2611_MSF_NET
2 tristate "Radisys ENP2611 MSF network interface support"
3 depends on ARCH_ENP2611
4 help
5 This is a driver for the MSF network interface unit in
6 the IXP2400 on the Radisys ENP2611 platform.
diff --git a/drivers/net/ixp2000/Makefile b/drivers/net/ixp2000/Makefile
new file mode 100644
index 000000000000..fd38351ceaa7
--- /dev/null
+++ b/drivers/net/ixp2000/Makefile
@@ -0,0 +1,3 @@
1obj-$(CONFIG_ENP2611_MSF_NET) += enp2611_mod.o
2
3enp2611_mod-objs := caleb.o enp2611.o ixp2400-msf.o ixpdev.o pm3386.o
diff --git a/drivers/net/ixp2000/caleb.c b/drivers/net/ixp2000/caleb.c
new file mode 100644
index 000000000000..3595e107df22
--- /dev/null
+++ b/drivers/net/ixp2000/caleb.c
@@ -0,0 +1,137 @@
1/*
2 * Helper functions for the SPI-3 bridge FPGA 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 <asm/io.h>
16#include "caleb.h"
17
18#define CALEB_IDLO 0x00
19#define CALEB_IDHI 0x01
20#define CALEB_RID 0x02
21#define CALEB_RESET 0x03
22#define CALEB_INTREN0 0x04
23#define CALEB_INTREN1 0x05
24#define CALEB_INTRSTAT0 0x06
25#define CALEB_INTRSTAT1 0x07
26#define CALEB_PORTEN 0x08
27#define CALEB_BURST 0x09
28#define CALEB_PORTPAUS 0x0A
29#define CALEB_PORTPAUSD 0x0B
30#define CALEB_PHY0RX 0x10
31#define CALEB_PHY1RX 0x11
32#define CALEB_PHY0TX 0x12
33#define CALEB_PHY1TX 0x13
34#define CALEB_IXPRX_HI_CNTR 0x15
35#define CALEB_PHY0RX_HI_CNTR 0x16
36#define CALEB_PHY1RX_HI_CNTR 0x17
37#define CALEB_IXPRX_CNTR 0x18
38#define CALEB_PHY0RX_CNTR 0x19
39#define CALEB_PHY1RX_CNTR 0x1A
40#define CALEB_IXPTX_CNTR 0x1B
41#define CALEB_PHY0TX_CNTR 0x1C
42#define CALEB_PHY1TX_CNTR 0x1D
43#define CALEB_DEBUG0 0x1E
44#define CALEB_DEBUG1 0x1F
45
46
47static u8 caleb_reg_read(int reg)
48{
49 u8 value;
50
51 value = *((volatile u8 *)(ENP2611_CALEB_VIRT_BASE + reg));
52
53// printk(KERN_INFO "caleb_reg_read(%d) = %.2x\n", reg, value);
54
55 return value;
56}
57
58static void caleb_reg_write(int reg, u8 value)
59{
60 u8 dummy;
61
62// printk(KERN_INFO "caleb_reg_write(%d, %.2x)\n", reg, value);
63
64 *((volatile u8 *)(ENP2611_CALEB_VIRT_BASE + reg)) = value;
65
66 dummy = *((volatile u8 *)ENP2611_CALEB_VIRT_BASE);
67 __asm__ __volatile__("mov %0, %0" : "+r" (dummy));
68}
69
70
71void caleb_reset(void)
72{
73 /*
74 * Perform a chip reset.
75 */
76 caleb_reg_write(CALEB_RESET, 0x02);
77 udelay(1);
78
79 /*
80 * Enable all interrupt sources. This is needed to get
81 * meaningful results out of the status bits (register 6
82 * and 7.)
83 */
84 caleb_reg_write(CALEB_INTREN0, 0xff);
85 caleb_reg_write(CALEB_INTREN1, 0x07);
86
87 /*
88 * Set RX and TX FIFO thresholds to 1.5kb.
89 */
90 caleb_reg_write(CALEB_PHY0RX, 0x11);
91 caleb_reg_write(CALEB_PHY1RX, 0x11);
92 caleb_reg_write(CALEB_PHY0TX, 0x11);
93 caleb_reg_write(CALEB_PHY1TX, 0x11);
94
95 /*
96 * Program SPI-3 burst size.
97 */
98 caleb_reg_write(CALEB_BURST, 0); // 64-byte RBUF mpackets
99// caleb_reg_write(CALEB_BURST, 1); // 128-byte RBUF mpackets
100// caleb_reg_write(CALEB_BURST, 2); // 256-byte RBUF mpackets
101}
102
103void caleb_enable_rx(int port)
104{
105 u8 temp;
106
107 temp = caleb_reg_read(CALEB_PORTEN);
108 temp |= 1 << port;
109 caleb_reg_write(CALEB_PORTEN, temp);
110}
111
112void caleb_disable_rx(int port)
113{
114 u8 temp;
115
116 temp = caleb_reg_read(CALEB_PORTEN);
117 temp &= ~(1 << port);
118 caleb_reg_write(CALEB_PORTEN, temp);
119}
120
121void caleb_enable_tx(int port)
122{
123 u8 temp;
124
125 temp = caleb_reg_read(CALEB_PORTEN);
126 temp |= 1 << (port + 4);
127 caleb_reg_write(CALEB_PORTEN, temp);
128}
129
130void caleb_disable_tx(int port)
131{
132 u8 temp;
133
134 temp = caleb_reg_read(CALEB_PORTEN);
135 temp &= ~(1 << (port + 4));
136 caleb_reg_write(CALEB_PORTEN, temp);
137}
diff --git a/drivers/net/ixp2000/caleb.h b/drivers/net/ixp2000/caleb.h
new file mode 100644
index 000000000000..e93a1ef5b8a3
--- /dev/null
+++ b/drivers/net/ixp2000/caleb.h
@@ -0,0 +1,22 @@
1/*
2 * Helper functions for the SPI-3 bridge FPGA 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#ifndef __CALEB_H
13#define __CALEB_H
14
15void caleb_reset(void);
16void caleb_enable_rx(int port);
17void caleb_disable_rx(int port);
18void caleb_enable_tx(int port);
19void caleb_disable_tx(int port);
20
21
22#endif
diff --git a/drivers/net/ixp2000/enp2611.c b/drivers/net/ixp2000/enp2611.c
new file mode 100644
index 000000000000..d82651a97bae
--- /dev/null
+++ b/drivers/net/ixp2000/enp2611.c
@@ -0,0 +1,245 @@
1/*
2 * IXP2400 MSF network device driver for 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/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 "ixpdev.h"
23#include "caleb.h"
24#include "ixp2400-msf.h"
25#include "pm3386.h"
26
27/***********************************************************************
28 * The Radisys ENP2611 is a PCI form factor board with three SFP GBIC
29 * slots, connected via two PMC/Sierra 3386s and an SPI-3 bridge FPGA
30 * to the IXP2400.
31 *
32 * +-------------+
33 * SFP GBIC #0 ---+ | +---------+
34 * | PM3386 #0 +-------+ |
35 * SFP GBIC #1 ---+ | | "Caleb" | +---------+
36 * +-------------+ | | | |
37 * | SPI-3 +---------+ IXP2400 |
38 * +-------------+ | bridge | | |
39 * SFP GBIC #2 ---+ | | FPGA | +---------+
40 * | PM3386 #1 +-------+ |
41 * | | +---------+
42 * +-------------+
43 * ^ ^ ^
44 * | 1.25Gbaud | 104MHz | 104MHz
45 * | SERDES ea. | SPI-3 ea. | SPI-3
46 *
47 ***********************************************************************/
48static struct ixp2400_msf_parameters enp2611_msf_parameters =
49{
50 .rx_mode = IXP2400_RX_MODE_UTOPIA_POS |
51 IXP2400_RX_MODE_1x32 |
52 IXP2400_RX_MODE_MPHY |
53 IXP2400_RX_MODE_MPHY_32 |
54 IXP2400_RX_MODE_MPHY_POLLED_STATUS |
55 IXP2400_RX_MODE_MPHY_LEVEL3 |
56 IXP2400_RX_MODE_RBUF_SIZE_64,
57
58 .rxclk01_multiplier = IXP2400_PLL_MULTIPLIER_16,
59
60 .rx_poll_ports = 3,
61
62 .rx_channel_mode = {
63 IXP2400_PORT_RX_MODE_MASTER |
64 IXP2400_PORT_RX_MODE_POS_PHY |
65 IXP2400_PORT_RX_MODE_POS_PHY_L3 |
66 IXP2400_PORT_RX_MODE_ODD_PARITY |
67 IXP2400_PORT_RX_MODE_2_CYCLE_DECODE,
68
69 IXP2400_PORT_RX_MODE_MASTER |
70 IXP2400_PORT_RX_MODE_POS_PHY |
71 IXP2400_PORT_RX_MODE_POS_PHY_L3 |
72 IXP2400_PORT_RX_MODE_ODD_PARITY |
73 IXP2400_PORT_RX_MODE_2_CYCLE_DECODE,
74
75 IXP2400_PORT_RX_MODE_MASTER |
76 IXP2400_PORT_RX_MODE_POS_PHY |
77 IXP2400_PORT_RX_MODE_POS_PHY_L3 |
78 IXP2400_PORT_RX_MODE_ODD_PARITY |
79 IXP2400_PORT_RX_MODE_2_CYCLE_DECODE,
80
81 IXP2400_PORT_RX_MODE_MASTER |
82 IXP2400_PORT_RX_MODE_POS_PHY |
83 IXP2400_PORT_RX_MODE_POS_PHY_L3 |
84 IXP2400_PORT_RX_MODE_ODD_PARITY |
85 IXP2400_PORT_RX_MODE_2_CYCLE_DECODE
86 },
87
88 .tx_mode = IXP2400_TX_MODE_UTOPIA_POS |
89 IXP2400_TX_MODE_1x32 |
90 IXP2400_TX_MODE_MPHY |
91 IXP2400_TX_MODE_MPHY_32 |
92 IXP2400_TX_MODE_MPHY_POLLED_STATUS |
93 IXP2400_TX_MODE_MPHY_LEVEL3 |
94 IXP2400_TX_MODE_TBUF_SIZE_64,
95
96 .txclk01_multiplier = IXP2400_PLL_MULTIPLIER_16,
97
98 .tx_poll_ports = 3,
99
100 .tx_channel_mode = {
101 IXP2400_PORT_TX_MODE_MASTER |
102 IXP2400_PORT_TX_MODE_POS_PHY |
103 IXP2400_PORT_TX_MODE_ODD_PARITY |
104 IXP2400_PORT_TX_MODE_2_CYCLE_DECODE,
105
106 IXP2400_PORT_TX_MODE_MASTER |
107 IXP2400_PORT_TX_MODE_POS_PHY |
108 IXP2400_PORT_TX_MODE_ODD_PARITY |
109 IXP2400_PORT_TX_MODE_2_CYCLE_DECODE,
110
111 IXP2400_PORT_TX_MODE_MASTER |
112 IXP2400_PORT_TX_MODE_POS_PHY |
113 IXP2400_PORT_TX_MODE_ODD_PARITY |
114 IXP2400_PORT_TX_MODE_2_CYCLE_DECODE,
115
116 IXP2400_PORT_TX_MODE_MASTER |
117 IXP2400_PORT_TX_MODE_POS_PHY |
118 IXP2400_PORT_TX_MODE_ODD_PARITY |
119 IXP2400_PORT_TX_MODE_2_CYCLE_DECODE
120 }
121};
122
123struct enp2611_ixpdev_priv
124{
125 struct ixpdev_priv ixpdev_priv;
126 struct net_device_stats stats;
127};
128
129static struct net_device *nds[3];
130static struct timer_list link_check_timer;
131
132static struct net_device_stats *enp2611_get_stats(struct net_device *dev)
133{
134 struct enp2611_ixpdev_priv *ip = netdev_priv(dev);
135
136 pm3386_get_stats(ip->ixpdev_priv.channel, &(ip->stats));
137
138 return &(ip->stats);
139}
140
141/* @@@ Poll the SFP moddef0 line too. */
142/* @@@ Try to use the pm3386 DOOL interrupt as well. */
143static void enp2611_check_link_status(unsigned long __dummy)
144{
145 int i;
146
147 for (i = 0; i < 3; i++) {
148 struct net_device *dev;
149 int status;
150
151 dev = nds[i];
152
153 status = pm3386_is_link_up(i);
154 if (status && !netif_carrier_ok(dev)) {
155 /* @@@ Should report autonegotiation status. */
156 printk(KERN_INFO "%s: NIC Link is Up\n", dev->name);
157
158 pm3386_enable_tx(i);
159 caleb_enable_tx(i);
160 netif_carrier_on(dev);
161 } else if (!status && netif_carrier_ok(dev)) {
162 printk(KERN_INFO "%s: NIC Link is Down\n", dev->name);
163
164 netif_carrier_off(dev);
165 caleb_disable_tx(i);
166 pm3386_disable_tx(i);
167 }
168 }
169
170 link_check_timer.expires = jiffies + HZ / 10;
171 add_timer(&link_check_timer);
172}
173
174static void enp2611_set_port_admin_status(int port, int up)
175{
176 if (up) {
177 caleb_enable_rx(port);
178
179 pm3386_set_carrier(port, 1);
180 pm3386_enable_rx(port);
181 } else {
182 caleb_disable_tx(port);
183 pm3386_disable_tx(port);
184 /* @@@ Flush out pending packets. */
185 pm3386_set_carrier(port, 0);
186
187 pm3386_disable_rx(port);
188 caleb_disable_rx(port);
189 }
190}
191
192static int __init enp2611_init_module(void)
193{
194 int i;
195
196 if (!machine_is_enp2611())
197 return -ENODEV;
198
199 caleb_reset();
200 pm3386_reset();
201
202 for (i = 0; i < 3; i++) {
203 nds[i] = ixpdev_alloc(i, sizeof(struct enp2611_ixpdev_priv));
204 if (nds[i] == NULL) {
205 while (--i >= 0)
206 free_netdev(nds[i]);
207 return -ENOMEM;
208 }
209
210 SET_MODULE_OWNER(nds[i]);
211 nds[i]->get_stats = enp2611_get_stats;
212 pm3386_init_port(i);
213 pm3386_get_mac(i, nds[i]->dev_addr);
214 }
215
216 ixp2400_msf_init(&enp2611_msf_parameters);
217
218 if (ixpdev_init(3, nds, enp2611_set_port_admin_status)) {
219 for (i = 0; i < 3; i++)
220 free_netdev(nds[i]);
221 return -EINVAL;
222 }
223
224 init_timer(&link_check_timer);
225 link_check_timer.function = enp2611_check_link_status;
226 link_check_timer.expires = jiffies;
227 add_timer(&link_check_timer);
228
229 return 0;
230}
231
232static void __exit enp2611_cleanup_module(void)
233{
234 int i;
235
236 del_timer_sync(&link_check_timer);
237
238 ixpdev_deinit();
239 for (i = 0; i < 3; i++)
240 free_netdev(nds[i]);
241}
242
243module_init(enp2611_init_module);
244module_exit(enp2611_cleanup_module);
245MODULE_LICENSE("GPL");
diff --git a/drivers/net/ixp2000/ixp2400-msf.c b/drivers/net/ixp2000/ixp2400-msf.c
new file mode 100644
index 000000000000..48a3a891d3a4
--- /dev/null
+++ b/drivers/net/ixp2000/ixp2400-msf.c
@@ -0,0 +1,213 @@
1/*
2 * Generic library functions for the MSF (Media and Switch Fabric) unit
3 * found on the Intel IXP2400 network processor.
4 *
5 * Copyright (C) 2004, 2005 Lennert Buytenhek <buytenh@wantstofly.org>
6 * Dedicated to Marija Kulikova.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as
10 * published by the Free Software Foundation; either version 2.1 of the
11 * License, or (at your option) any later version.
12 */
13
14#include <linux/config.h>
15#include <linux/kernel.h>
16#include <linux/init.h>
17#include <asm/hardware.h>
18#include <asm/arch/ixp2000-regs.h>
19#include <asm/delay.h>
20#include <asm/io.h>
21#include "ixp2400-msf.h"
22
23/*
24 * This is the Intel recommended PLL init procedure as described on
25 * page 340 of the IXP2400/IXP2800 Programmer's Reference Manual.
26 */
27static void ixp2400_pll_init(struct ixp2400_msf_parameters *mp)
28{
29 int rx_dual_clock;
30 int tx_dual_clock;
31 u32 value;
32
33 /*
34 * If the RX mode is not 1x32, we have to enable both RX PLLs
35 * (#0 and #1.) The same thing for the TX direction.
36 */
37 rx_dual_clock = !!(mp->rx_mode & IXP2400_RX_MODE_WIDTH_MASK);
38 tx_dual_clock = !!(mp->tx_mode & IXP2400_TX_MODE_WIDTH_MASK);
39
40 /*
41 * Read initial value.
42 */
43 value = ixp2000_reg_read(IXP2000_MSF_CLK_CNTRL);
44
45 /*
46 * Put PLLs in powerdown and bypass mode.
47 */
48 value |= 0x0000f0f0;
49 ixp2000_reg_write(IXP2000_MSF_CLK_CNTRL, value);
50
51 /*
52 * Set single or dual clock mode bits.
53 */
54 value &= ~0x03000000;
55 value |= (rx_dual_clock << 24) | (tx_dual_clock << 25);
56
57 /*
58 * Set multipliers.
59 */
60 value &= ~0x00ff0000;
61 value |= mp->rxclk01_multiplier << 16;
62 value |= mp->rxclk23_multiplier << 18;
63 value |= mp->txclk01_multiplier << 20;
64 value |= mp->txclk23_multiplier << 22;
65
66 /*
67 * And write value.
68 */
69 ixp2000_reg_write(IXP2000_MSF_CLK_CNTRL, value);
70
71 /*
72 * Disable PLL bypass mode.
73 */
74 value &= ~(0x00005000 | rx_dual_clock << 13 | tx_dual_clock << 15);
75 ixp2000_reg_write(IXP2000_MSF_CLK_CNTRL, value);
76
77 /*
78 * Turn on PLLs.
79 */
80 value &= ~(0x00000050 | rx_dual_clock << 5 | tx_dual_clock << 7);
81 ixp2000_reg_write(IXP2000_MSF_CLK_CNTRL, value);
82
83 /*
84 * Wait for PLLs to lock. There are lock status bits, but IXP2400
85 * erratum #65 says that these lock bits should not be relied upon
86 * as they might not accurately reflect the true state of the PLLs.
87 */
88 udelay(100);
89}
90
91/*
92 * Needed according to p480 of Programmer's Reference Manual.
93 */
94static void ixp2400_msf_free_rbuf_entries(struct ixp2400_msf_parameters *mp)
95{
96 int size_bits;
97 int i;
98
99 /*
100 * Work around IXP2400 erratum #69 (silent RBUF-to-DRAM transfer
101 * corruption) in the Intel-recommended way: do not add the RBUF
102 * elements susceptible to corruption to the freelist.
103 */
104 size_bits = mp->rx_mode & IXP2400_RX_MODE_RBUF_SIZE_MASK;
105 if (size_bits == IXP2400_RX_MODE_RBUF_SIZE_64) {
106 for (i = 1; i < 128; i++) {
107 if (i == 9 || i == 18 || i == 27)
108 continue;
109 ixp2000_reg_write(IXP2000_MSF_RBUF_ELEMENT_DONE, i);
110 }
111 } else if (size_bits == IXP2400_RX_MODE_RBUF_SIZE_128) {
112 for (i = 1; i < 64; i++) {
113 if (i == 4 || i == 9 || i == 13)
114 continue;
115 ixp2000_reg_write(IXP2000_MSF_RBUF_ELEMENT_DONE, i);
116 }
117 } else if (size_bits == IXP2400_RX_MODE_RBUF_SIZE_256) {
118 for (i = 1; i < 32; i++) {
119 if (i == 2 || i == 4 || i == 6)
120 continue;
121 ixp2000_reg_write(IXP2000_MSF_RBUF_ELEMENT_DONE, i);
122 }
123 }
124}
125
126static u32 ixp2400_msf_valid_channels(u32 reg)
127{
128 u32 channels;
129
130 channels = 0;
131 switch (reg & IXP2400_RX_MODE_WIDTH_MASK) {
132 case IXP2400_RX_MODE_1x32:
133 channels = 0x1;
134 if (reg & IXP2400_RX_MODE_MPHY &&
135 !(reg & IXP2400_RX_MODE_MPHY_32))
136 channels = 0xf;
137 break;
138
139 case IXP2400_RX_MODE_2x16:
140 channels = 0x5;
141 break;
142
143 case IXP2400_RX_MODE_4x8:
144 channels = 0xf;
145 break;
146
147 case IXP2400_RX_MODE_1x16_2x8:
148 channels = 0xd;
149 break;
150 }
151
152 return channels;
153}
154
155static void ixp2400_msf_enable_rx(struct ixp2400_msf_parameters *mp)
156{
157 u32 value;
158
159 value = ixp2000_reg_read(IXP2000_MSF_RX_CONTROL) & 0x0fffffff;
160 value |= ixp2400_msf_valid_channels(mp->rx_mode) << 28;
161 ixp2000_reg_write(IXP2000_MSF_RX_CONTROL, value);
162}
163
164static void ixp2400_msf_enable_tx(struct ixp2400_msf_parameters *mp)
165{
166 u32 value;
167
168 value = ixp2000_reg_read(IXP2000_MSF_TX_CONTROL) & 0x0fffffff;
169 value |= ixp2400_msf_valid_channels(mp->tx_mode) << 28;
170 ixp2000_reg_write(IXP2000_MSF_TX_CONTROL, value);
171}
172
173
174void ixp2400_msf_init(struct ixp2400_msf_parameters *mp)
175{
176 u32 value;
177 int i;
178
179 /*
180 * Init the RX/TX PLLs based on the passed parameter block.
181 */
182 ixp2400_pll_init(mp);
183
184 /*
185 * Reset MSF. Bit 7 in IXP_RESET_0 resets the MSF.
186 */
187 value = ixp2000_reg_read(IXP2000_RESET0);
188 ixp2000_reg_write(IXP2000_RESET0, value | 0x80);
189 ixp2000_reg_write(IXP2000_RESET0, value & ~0x80);
190
191 /*
192 * Initialise the RX section.
193 */
194 ixp2000_reg_write(IXP2000_MSF_RX_MPHY_POLL_LIMIT, mp->rx_poll_ports - 1);
195 ixp2000_reg_write(IXP2000_MSF_RX_CONTROL, mp->rx_mode);
196 for (i = 0; i < 4; i++) {
197 ixp2000_reg_write(IXP2000_MSF_RX_UP_CONTROL_0 + i,
198 mp->rx_channel_mode[i]);
199 }
200 ixp2400_msf_free_rbuf_entries(mp);
201 ixp2400_msf_enable_rx(mp);
202
203 /*
204 * Initialise the TX section.
205 */
206 ixp2000_reg_write(IXP2000_MSF_TX_MPHY_POLL_LIMIT, mp->tx_poll_ports - 1);
207 ixp2000_reg_write(IXP2000_MSF_TX_CONTROL, mp->tx_mode);
208 for (i = 0; i < 4; i++) {
209 ixp2000_reg_write(IXP2000_MSF_TX_UP_CONTROL_0 + i,
210 mp->tx_channel_mode[i]);
211 }
212 ixp2400_msf_enable_tx(mp);
213}
diff --git a/drivers/net/ixp2000/ixp2400-msf.h b/drivers/net/ixp2000/ixp2400-msf.h
new file mode 100644
index 000000000000..3ac1af2771da
--- /dev/null
+++ b/drivers/net/ixp2000/ixp2400-msf.h
@@ -0,0 +1,115 @@
1/*
2 * Generic library functions for the MSF (Media and Switch Fabric) unit
3 * found on the Intel IXP2400 network processor.
4 *
5 * Copyright (C) 2004, 2005 Lennert Buytenhek <buytenh@wantstofly.org>
6 * Dedicated to Marija Kulikova.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as
10 * published by the Free Software Foundation; either version 2.1 of the
11 * License, or (at your option) any later version.
12 */
13
14#ifndef __IXP2400_MSF_H
15#define __IXP2400_MSF_H
16
17struct ixp2400_msf_parameters
18{
19 u32 rx_mode;
20 unsigned rxclk01_multiplier:2;
21 unsigned rxclk23_multiplier:2;
22 unsigned rx_poll_ports:6;
23 u32 rx_channel_mode[4];
24
25 u32 tx_mode;
26 unsigned txclk01_multiplier:2;
27 unsigned txclk23_multiplier:2;
28 unsigned tx_poll_ports:6;
29 u32 tx_channel_mode[4];
30};
31
32void ixp2400_msf_init(struct ixp2400_msf_parameters *mp);
33
34#define IXP2400_PLL_MULTIPLIER_48 0x00
35#define IXP2400_PLL_MULTIPLIER_24 0x01
36#define IXP2400_PLL_MULTIPLIER_16 0x02
37#define IXP2400_PLL_MULTIPLIER_12 0x03
38
39#define IXP2400_RX_MODE_CSIX 0x00400000
40#define IXP2400_RX_MODE_UTOPIA_POS 0x00000000
41#define IXP2400_RX_MODE_WIDTH_MASK 0x00300000
42#define IXP2400_RX_MODE_1x16_2x8 0x00300000
43#define IXP2400_RX_MODE_4x8 0x00200000
44#define IXP2400_RX_MODE_2x16 0x00100000
45#define IXP2400_RX_MODE_1x32 0x00000000
46#define IXP2400_RX_MODE_MPHY 0x00080000
47#define IXP2400_RX_MODE_SPHY 0x00000000
48#define IXP2400_RX_MODE_MPHY_32 0x00040000
49#define IXP2400_RX_MODE_MPHY_4 0x00000000
50#define IXP2400_RX_MODE_MPHY_POLLED_STATUS 0x00020000
51#define IXP2400_RX_MODE_MPHY_DIRECT_STATUS 0x00000000
52#define IXP2400_RX_MODE_CBUS_FULL_DUPLEX 0x00010000
53#define IXP2400_RX_MODE_CBUS_SIMPLEX 0x00000000
54#define IXP2400_RX_MODE_MPHY_LEVEL2 0x00004000
55#define IXP2400_RX_MODE_MPHY_LEVEL3 0x00000000
56#define IXP2400_RX_MODE_CBUS_8BIT 0x00002000
57#define IXP2400_RX_MODE_CBUS_4BIT 0x00000000
58#define IXP2400_RX_MODE_CSIX_SINGLE_FREELIST 0x00000200
59#define IXP2400_RX_MODE_CSIX_SPLIT_FREELISTS 0x00000000
60#define IXP2400_RX_MODE_RBUF_SIZE_MASK 0x0000000c
61#define IXP2400_RX_MODE_RBUF_SIZE_256 0x00000008
62#define IXP2400_RX_MODE_RBUF_SIZE_128 0x00000004
63#define IXP2400_RX_MODE_RBUF_SIZE_64 0x00000000
64
65#define IXP2400_PORT_RX_MODE_SLAVE 0x00000040
66#define IXP2400_PORT_RX_MODE_MASTER 0x00000000
67#define IXP2400_PORT_RX_MODE_POS_PHY_L3 0x00000020
68#define IXP2400_PORT_RX_MODE_POS_PHY_L2 0x00000000
69#define IXP2400_PORT_RX_MODE_POS_PHY 0x00000010
70#define IXP2400_PORT_RX_MODE_UTOPIA 0x00000000
71#define IXP2400_PORT_RX_MODE_EVEN_PARITY 0x0000000c
72#define IXP2400_PORT_RX_MODE_ODD_PARITY 0x00000008
73#define IXP2400_PORT_RX_MODE_NO_PARITY 0x00000000
74#define IXP2400_PORT_RX_MODE_UTOPIA_BIG_CELLS 0x00000002
75#define IXP2400_PORT_RX_MODE_UTOPIA_NORMAL_CELLS 0x00000000
76#define IXP2400_PORT_RX_MODE_2_CYCLE_DECODE 0x00000001
77#define IXP2400_PORT_RX_MODE_1_CYCLE_DECODE 0x00000000
78
79#define IXP2400_TX_MODE_CSIX 0x00400000
80#define IXP2400_TX_MODE_UTOPIA_POS 0x00000000
81#define IXP2400_TX_MODE_WIDTH_MASK 0x00300000
82#define IXP2400_TX_MODE_1x16_2x8 0x00300000
83#define IXP2400_TX_MODE_4x8 0x00200000
84#define IXP2400_TX_MODE_2x16 0x00100000
85#define IXP2400_TX_MODE_1x32 0x00000000
86#define IXP2400_TX_MODE_MPHY 0x00080000
87#define IXP2400_TX_MODE_SPHY 0x00000000
88#define IXP2400_TX_MODE_MPHY_32 0x00040000
89#define IXP2400_TX_MODE_MPHY_4 0x00000000
90#define IXP2400_TX_MODE_MPHY_POLLED_STATUS 0x00020000
91#define IXP2400_TX_MODE_MPHY_DIRECT_STATUS 0x00000000
92#define IXP2400_TX_MODE_CBUS_FULL_DUPLEX 0x00010000
93#define IXP2400_TX_MODE_CBUS_SIMPLEX 0x00000000
94#define IXP2400_TX_MODE_MPHY_LEVEL2 0x00004000
95#define IXP2400_TX_MODE_MPHY_LEVEL3 0x00000000
96#define IXP2400_TX_MODE_CBUS_8BIT 0x00002000
97#define IXP2400_TX_MODE_CBUS_4BIT 0x00000000
98#define IXP2400_TX_MODE_TBUF_SIZE_MASK 0x0000000c
99#define IXP2400_TX_MODE_TBUF_SIZE_256 0x00000008
100#define IXP2400_TX_MODE_TBUF_SIZE_128 0x00000004
101#define IXP2400_TX_MODE_TBUF_SIZE_64 0x00000000
102
103#define IXP2400_PORT_TX_MODE_SLAVE 0x00000040
104#define IXP2400_PORT_TX_MODE_MASTER 0x00000000
105#define IXP2400_PORT_TX_MODE_POS_PHY 0x00000010
106#define IXP2400_PORT_TX_MODE_UTOPIA 0x00000000
107#define IXP2400_PORT_TX_MODE_EVEN_PARITY 0x0000000c
108#define IXP2400_PORT_TX_MODE_ODD_PARITY 0x00000008
109#define IXP2400_PORT_TX_MODE_NO_PARITY 0x00000000
110#define IXP2400_PORT_TX_MODE_UTOPIA_BIG_CELLS 0x00000002
111#define IXP2400_PORT_TX_MODE_2_CYCLE_DECODE 0x00000001
112#define IXP2400_PORT_TX_MODE_1_CYCLE_DECODE 0x00000000
113
114
115#endif
diff --git a/drivers/net/ixp2000/ixp2400_rx.uc b/drivers/net/ixp2000/ixp2400_rx.uc
new file mode 100644
index 000000000000..42a73e357afa
--- /dev/null
+++ b/drivers/net/ixp2000/ixp2400_rx.uc
@@ -0,0 +1,408 @@
1/*
2 * RX ucode for the Intel IXP2400 in POS-PHY mode.
3 * Copyright (C) 2004, 2005 Lennert Buytenhek
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 * Assumptions made in this code:
12 * - The IXP2400 MSF is configured for POS-PHY mode, in a mode where
13 * only one full element list is used. This includes, for example,
14 * 1x32 SPHY and 1x32 MPHY32, but not 4x8 SPHY or 1x32 MPHY4. (This
15 * is not an exhaustive list.)
16 * - The RBUF uses 64-byte mpackets.
17 * - RX descriptors reside in SRAM, and have the following format:
18 * struct rx_desc
19 * {
20 * // to uengine
21 * u32 buf_phys_addr;
22 * u32 buf_length;
23 *
24 * // from uengine
25 * u32 channel;
26 * u32 pkt_length;
27 * };
28 * - Packet data resides in DRAM.
29 * - Packet buffer addresses are 8-byte aligned.
30 * - Scratch ring 0 is rx_pending.
31 * - Scratch ring 1 is rx_done, and has status condition 'full'.
32 * - The host triggers rx_done flush and rx_pending refill on seeing INTA.
33 * - This code is run on all eight threads of the microengine it runs on.
34 *
35 * Local memory is used for per-channel RX state.
36 */
37
38#define RX_THREAD_FREELIST_0 0x0030
39#define RBUF_ELEMENT_DONE 0x0044
40
41#define CHANNEL_FLAGS *l$index0[0]
42#define CHANNEL_FLAG_RECEIVING 1
43#define PACKET_LENGTH *l$index0[1]
44#define PACKET_CHECKSUM *l$index0[2]
45#define BUFFER_HANDLE *l$index0[3]
46#define BUFFER_START *l$index0[4]
47#define BUFFER_LENGTH *l$index0[5]
48
49#define CHANNEL_STATE_SIZE 24 // in bytes
50#define CHANNEL_STATE_SHIFT 5 // ceil(log2(state size))
51
52
53 .sig volatile sig1
54 .sig volatile sig2
55 .sig volatile sig3
56
57 .sig mpacket_arrived
58 .reg add_to_rx_freelist
59 .reg read $rsw0, $rsw1
60 .xfer_order $rsw0 $rsw1
61
62 .reg zero
63
64 /*
65 * Initialise add_to_rx_freelist.
66 */
67 .begin
68 .reg temp
69 .reg temp2
70
71 immed[add_to_rx_freelist, RX_THREAD_FREELIST_0]
72 immed_w1[add_to_rx_freelist, (&$rsw0 | (&mpacket_arrived << 12))]
73
74 local_csr_rd[ACTIVE_CTX_STS]
75 immed[temp, 0]
76 alu[temp2, temp, and, 0x1f]
77 alu_shf[add_to_rx_freelist, add_to_rx_freelist, or, temp2, <<20]
78 alu[temp2, temp, and, 0x80]
79 alu_shf[add_to_rx_freelist, add_to_rx_freelist, or, temp2, <<18]
80 .end
81
82 immed[zero, 0]
83
84 /*
85 * Skip context 0 initialisation?
86 */
87 .begin
88 br!=ctx[0, mpacket_receive_loop#]
89 .end
90
91 /*
92 * Initialise local memory.
93 */
94 .begin
95 .reg addr
96 .reg temp
97
98 immed[temp, 0]
99 init_local_mem_loop#:
100 alu_shf[addr, --, b, temp, <<CHANNEL_STATE_SHIFT]
101 local_csr_wr[ACTIVE_LM_ADDR_0, addr]
102 nop
103 nop
104 nop
105
106 immed[CHANNEL_FLAGS, 0]
107
108 alu[temp, temp, +, 1]
109 alu[--, temp, and, 0x20]
110 beq[init_local_mem_loop#]
111 .end
112
113 /*
114 * Initialise signal pipeline.
115 */
116 .begin
117 local_csr_wr[SAME_ME_SIGNAL, (&sig1 << 3)]
118 .set_sig sig1
119
120 local_csr_wr[SAME_ME_SIGNAL, (&sig2 << 3)]
121 .set_sig sig2
122
123 local_csr_wr[SAME_ME_SIGNAL, (&sig3 << 3)]
124 .set_sig sig3
125 .end
126
127mpacket_receive_loop#:
128 /*
129 * Synchronise and wait for mpacket.
130 */
131 .begin
132 ctx_arb[sig1]
133 local_csr_wr[SAME_ME_SIGNAL, (0x80 | (&sig1 << 3))]
134
135 msf[fast_wr, --, add_to_rx_freelist, 0]
136 .set_sig mpacket_arrived
137 ctx_arb[mpacket_arrived]
138 .set $rsw0 $rsw1
139 .end
140
141 /*
142 * We halt if we see {inbparerr,parerr,null,soperror}.
143 */
144 .begin
145 alu_shf[--, 0x1b, and, $rsw0, >>8]
146 bne[abort_rswerr#]
147 .end
148
149 /*
150 * Point local memory pointer to this channel's state area.
151 */
152 .begin
153 .reg chanaddr
154
155 alu[chanaddr, $rsw0, and, 0x1f]
156 alu_shf[chanaddr, --, b, chanaddr, <<CHANNEL_STATE_SHIFT]
157 local_csr_wr[ACTIVE_LM_ADDR_0, chanaddr]
158 nop
159 nop
160 nop
161 .end
162
163 /*
164 * Check whether we received a SOP mpacket while we were already
165 * working on a packet, or a non-SOP mpacket while there was no
166 * packet pending. (SOP == RECEIVING -> abort) If everything's
167 * okay, update the RECEIVING flag to reflect our new state.
168 */
169 .begin
170 .reg temp
171 .reg eop
172
173 #if CHANNEL_FLAG_RECEIVING != 1
174 #error CHANNEL_FLAG_RECEIVING is not 1
175 #endif
176
177 alu_shf[temp, 1, and, $rsw0, >>15]
178 alu[temp, temp, xor, CHANNEL_FLAGS]
179 alu[--, temp, and, CHANNEL_FLAG_RECEIVING]
180 beq[abort_proterr#]
181
182 alu_shf[eop, 1, and, $rsw0, >>14]
183 alu[CHANNEL_FLAGS, temp, xor, eop]
184 .end
185
186 /*
187 * Copy the mpacket into the right spot, and in case of EOP,
188 * write back the descriptor and pass the packet on.
189 */
190 .begin
191 .reg buffer_offset
192 .reg _packet_length
193 .reg _packet_checksum
194 .reg _buffer_handle
195 .reg _buffer_start
196 .reg _buffer_length
197
198 /*
199 * Determine buffer_offset, _packet_length and
200 * _packet_checksum.
201 */
202 .begin
203 .reg temp
204
205 alu[--, 1, and, $rsw0, >>15]
206 beq[not_sop#]
207
208 immed[PACKET_LENGTH, 0]
209 immed[PACKET_CHECKSUM, 0]
210
211 not_sop#:
212 alu[buffer_offset, --, b, PACKET_LENGTH]
213 alu_shf[temp, 0xff, and, $rsw0, >>16]
214 alu[_packet_length, buffer_offset, +, temp]
215 alu[PACKET_LENGTH, --, b, _packet_length]
216
217 immed[temp, 0xffff]
218 alu[temp, $rsw1, and, temp]
219 alu[_packet_checksum, PACKET_CHECKSUM, +, temp]
220 alu[PACKET_CHECKSUM, --, b, _packet_checksum]
221 .end
222
223 /*
224 * Allocate buffer in case of SOP.
225 */
226 .begin
227 .reg temp
228
229 alu[temp, 1, and, $rsw0, >>15]
230 beq[skip_buffer_alloc#]
231
232 .begin
233 .sig zzz
234 .reg read $stemp $stemp2
235 .xfer_order $stemp $stemp2
236
237 rx_nobufs#:
238 scratch[get, $stemp, zero, 0, 1], ctx_swap[zzz]
239 alu[_buffer_handle, --, b, $stemp]
240 beq[rx_nobufs#]
241
242 sram[read, $stemp, _buffer_handle, 0, 2],
243 ctx_swap[zzz]
244 alu[_buffer_start, --, b, $stemp]
245 alu[_buffer_length, --, b, $stemp2]
246 .end
247
248 skip_buffer_alloc#:
249 .end
250
251 /*
252 * Resynchronise.
253 */
254 .begin
255 ctx_arb[sig2]
256 local_csr_wr[SAME_ME_SIGNAL, (0x80 | (&sig2 << 3))]
257 .end
258
259 /*
260 * Synchronise buffer state.
261 */
262 .begin
263 .reg temp
264
265 alu[temp, 1, and, $rsw0, >>15]
266 beq[copy_from_local_mem#]
267
268 alu[BUFFER_HANDLE, --, b, _buffer_handle]
269 alu[BUFFER_START, --, b, _buffer_start]
270 alu[BUFFER_LENGTH, --, b, _buffer_length]
271 br[sync_state_done#]
272
273 copy_from_local_mem#:
274 alu[_buffer_handle, --, b, BUFFER_HANDLE]
275 alu[_buffer_start, --, b, BUFFER_START]
276 alu[_buffer_length, --, b, BUFFER_LENGTH]
277
278 sync_state_done#:
279 .end
280
281#if 0
282 /*
283 * Debug buffer state management.
284 */
285 .begin
286 .reg temp
287
288 alu[temp, 1, and, $rsw0, >>14]
289 beq[no_poison#]
290 immed[BUFFER_HANDLE, 0xdead]
291 immed[BUFFER_START, 0xdead]
292 immed[BUFFER_LENGTH, 0xdead]
293 no_poison#:
294
295 immed[temp, 0xdead]
296 alu[--, _buffer_handle, -, temp]
297 beq[state_corrupted#]
298 alu[--, _buffer_start, -, temp]
299 beq[state_corrupted#]
300 alu[--, _buffer_length, -, temp]
301 beq[state_corrupted#]
302 .end
303#endif
304
305 /*
306 * Check buffer length.
307 */
308 .begin
309 alu[--, _buffer_length, -, _packet_length]
310 blo[buffer_overflow#]
311 .end
312
313 /*
314 * Copy the mpacket and give back the RBUF element.
315 */
316 .begin
317 .reg element
318 .reg xfer_size
319 .reg temp
320 .sig copy_sig
321
322 alu_shf[element, 0x7f, and, $rsw0, >>24]
323 alu_shf[xfer_size, 0xff, and, $rsw0, >>16]
324
325 alu[xfer_size, xfer_size, -, 1]
326 alu_shf[xfer_size, 0x10, or, xfer_size, >>3]
327 alu_shf[temp, 0x10, or, xfer_size, <<21]
328 alu_shf[temp, temp, or, element, <<11]
329 alu_shf[--, temp, or, 1, <<18]
330
331 dram[rbuf_rd, --, _buffer_start, buffer_offset, max_8],
332 indirect_ref, sig_done[copy_sig]
333 ctx_arb[copy_sig]
334
335 alu[temp, RBUF_ELEMENT_DONE, or, element, <<16]
336 msf[fast_wr, --, temp, 0]
337 .end
338
339 /*
340 * If EOP, write back the packet descriptor.
341 */
342 .begin
343 .reg write $stemp $stemp2
344 .xfer_order $stemp $stemp2
345 .sig zzz
346
347 alu_shf[--, 1, and, $rsw0, >>14]
348 beq[no_writeback#]
349
350 alu[$stemp, $rsw0, and, 0x1f]
351 alu[$stemp2, --, b, _packet_length]
352 sram[write, $stemp, _buffer_handle, 8, 2], ctx_swap[zzz]
353
354 no_writeback#:
355 .end
356
357 /*
358 * Resynchronise.
359 */
360 .begin
361 ctx_arb[sig3]
362 local_csr_wr[SAME_ME_SIGNAL, (0x80 | (&sig3 << 3))]
363 .end
364
365 /*
366 * If EOP, put the buffer back onto the scratch ring.
367 */
368 .begin
369 .reg write $stemp
370 .sig zzz
371
372 br_inp_state[SCR_Ring1_Status, rx_done_ring_overflow#]
373
374 alu_shf[--, 1, and, $rsw0, >>14]
375 beq[mpacket_receive_loop#]
376
377 alu[--, 1, and, $rsw0, >>10]
378 bne[rxerr#]
379
380 alu[$stemp, --, b, _buffer_handle]
381 scratch[put, $stemp, zero, 4, 1], ctx_swap[zzz]
382 cap[fast_wr, 0, XSCALE_INT_A]
383 br[mpacket_receive_loop#]
384
385 rxerr#:
386 alu[$stemp, --, b, _buffer_handle]
387 scratch[put, $stemp, zero, 0, 1], ctx_swap[zzz]
388 br[mpacket_receive_loop#]
389 .end
390 .end
391
392
393abort_rswerr#:
394 halt
395
396abort_proterr#:
397 halt
398
399state_corrupted#:
400 halt
401
402buffer_overflow#:
403 halt
404
405rx_done_ring_overflow#:
406 halt
407
408
diff --git a/drivers/net/ixp2000/ixp2400_rx.ucode b/drivers/net/ixp2000/ixp2400_rx.ucode
new file mode 100644
index 000000000000..e8aee2f81aad
--- /dev/null
+++ b/drivers/net/ixp2000/ixp2400_rx.ucode
@@ -0,0 +1,130 @@
1static struct ixp2000_uengine_code ixp2400_rx =
2{
3 .cpu_model_bitmask = 0x000003fe,
4 .cpu_min_revision = 0,
5 .cpu_max_revision = 255,
6
7 .uengine_parameters = IXP2000_UENGINE_8_CONTEXTS |
8 IXP2000_UENGINE_PRN_UPDATE_EVERY |
9 IXP2000_UENGINE_NN_FROM_PREVIOUS |
10 IXP2000_UENGINE_ASSERT_EMPTY_AT_0 |
11 IXP2000_UENGINE_LM_ADDR1_PER_CONTEXT |
12 IXP2000_UENGINE_LM_ADDR0_PER_CONTEXT,
13
14 .initial_reg_values = (struct ixp2000_reg_value []) {
15 { -1, -1 }
16 },
17
18 .num_insns = 109,
19 .insns = (u8 []) {
20 0xf0, 0x00, 0x0c, 0xc0, 0x05,
21 0xf4, 0x44, 0x0c, 0x00, 0x05,
22 0xfc, 0x04, 0x4c, 0x00, 0x00,
23 0xf0, 0x00, 0x00, 0x3b, 0x00,
24 0xb4, 0x40, 0xf0, 0x3b, 0x1f,
25 0x8a, 0xc0, 0x50, 0x3e, 0x05,
26 0xb4, 0x40, 0xf0, 0x3b, 0x80,
27 0x9a, 0xe0, 0x00, 0x3e, 0x05,
28 0xf0, 0x00, 0x00, 0x07, 0x00,
29 0xd8, 0x05, 0xc0, 0x00, 0x11,
30 0xf0, 0x00, 0x00, 0x0f, 0x00,
31 0x91, 0xb0, 0x20, 0x0e, 0x00,
32 0xfc, 0x06, 0x60, 0x0b, 0x00,
33 0xf0, 0x00, 0x0c, 0x03, 0x00,
34 0xf0, 0x00, 0x0c, 0x03, 0x00,
35 0xf0, 0x00, 0x0c, 0x03, 0x00,
36 0xf0, 0x00, 0x0c, 0x02, 0x00,
37 0xb0, 0xc0, 0x30, 0x0f, 0x01,
38 0xa4, 0x70, 0x00, 0x0f, 0x20,
39 0xd8, 0x02, 0xc0, 0x01, 0x00,
40 0xfc, 0x10, 0xac, 0x23, 0x08,
41 0xfc, 0x10, 0xac, 0x43, 0x10,
42 0xfc, 0x10, 0xac, 0x63, 0x18,
43 0xe0, 0x00, 0x00, 0x00, 0x02,
44 0xfc, 0x10, 0xae, 0x23, 0x88,
45 0x3d, 0x00, 0x04, 0x03, 0x20,
46 0xe0, 0x00, 0x00, 0x00, 0x10,
47 0x84, 0x82, 0x02, 0x01, 0x3b,
48 0xd8, 0x1a, 0x00, 0x01, 0x01,
49 0xb4, 0x00, 0x8c, 0x7d, 0x80,
50 0x91, 0xb0, 0x80, 0x22, 0x00,
51 0xfc, 0x06, 0x60, 0x23, 0x00,
52 0xf0, 0x00, 0x0c, 0x03, 0x00,
53 0xf0, 0x00, 0x0c, 0x03, 0x00,
54 0xf0, 0x00, 0x0c, 0x03, 0x00,
55 0x94, 0xf0, 0x92, 0x01, 0x21,
56 0xac, 0x40, 0x60, 0x26, 0x00,
57 0xa4, 0x30, 0x0c, 0x04, 0x06,
58 0xd8, 0x1a, 0x40, 0x01, 0x00,
59 0x94, 0xe0, 0xa2, 0x01, 0x21,
60 0xac, 0x20, 0x00, 0x28, 0x06,
61 0x84, 0xf2, 0x02, 0x01, 0x21,
62 0xd8, 0x0b, 0x40, 0x01, 0x00,
63 0xf0, 0x00, 0x0c, 0x02, 0x01,
64 0xf0, 0x00, 0x0c, 0x02, 0x02,
65 0xa0, 0x00, 0x08, 0x04, 0x00,
66 0x95, 0x00, 0xc6, 0x01, 0xff,
67 0xa0, 0x80, 0x10, 0x30, 0x00,
68 0xa0, 0x60, 0x1c, 0x00, 0x01,
69 0xf0, 0x0f, 0xf0, 0x33, 0xff,
70 0xb4, 0x00, 0xc0, 0x31, 0x81,
71 0xb0, 0x80, 0xb0, 0x32, 0x02,
72 0xa0, 0x20, 0x20, 0x2c, 0x00,
73 0x94, 0xf0, 0xd2, 0x01, 0x21,
74 0xd8, 0x0f, 0x40, 0x01, 0x00,
75 0x19, 0x40, 0x10, 0x04, 0x20,
76 0xa0, 0x00, 0x26, 0x04, 0x00,
77 0xd8, 0x0d, 0xc0, 0x01, 0x00,
78 0x00, 0x42, 0x10, 0x80, 0x02,
79 0xb0, 0x00, 0x46, 0x04, 0x00,
80 0xb0, 0x00, 0x56, 0x08, 0x00,
81 0xe0, 0x00, 0x00, 0x00, 0x04,
82 0xfc, 0x10, 0xae, 0x43, 0x90,
83 0x84, 0xf0, 0x32, 0x01, 0x21,
84 0xd8, 0x11, 0x40, 0x01, 0x00,
85 0xa0, 0x60, 0x3c, 0x00, 0x02,
86 0xa0, 0x20, 0x40, 0x10, 0x00,
87 0xa0, 0x20, 0x50, 0x14, 0x00,
88 0xd8, 0x12, 0x00, 0x00, 0x18,
89 0xa0, 0x00, 0x28, 0x0c, 0x00,
90 0xb0, 0x00, 0x48, 0x10, 0x00,
91 0xb0, 0x00, 0x58, 0x14, 0x00,
92 0xaa, 0xf0, 0x00, 0x14, 0x01,
93 0xd8, 0x1a, 0xc0, 0x01, 0x05,
94 0x85, 0x80, 0x42, 0x01, 0xff,
95 0x95, 0x00, 0x66, 0x01, 0xff,
96 0xba, 0xc0, 0x60, 0x1b, 0x01,
97 0x9a, 0x30, 0x60, 0x19, 0x30,
98 0x9a, 0xb0, 0x70, 0x1a, 0x30,
99 0x9b, 0x50, 0x78, 0x1e, 0x04,
100 0x8a, 0xe2, 0x08, 0x1e, 0x21,
101 0x6a, 0x4e, 0x00, 0x13, 0x00,
102 0xe0, 0x00, 0x00, 0x00, 0x30,
103 0x9b, 0x00, 0x7a, 0x92, 0x04,
104 0x3d, 0x00, 0x04, 0x1f, 0x20,
105 0x84, 0xe2, 0x02, 0x01, 0x21,
106 0xd8, 0x16, 0x80, 0x01, 0x00,
107 0xa4, 0x18, 0x0c, 0x7d, 0x80,
108 0xa0, 0x58, 0x1c, 0x00, 0x01,
109 0x01, 0x42, 0x00, 0xa0, 0x02,
110 0xe0, 0x00, 0x00, 0x00, 0x08,
111 0xfc, 0x10, 0xae, 0x63, 0x98,
112 0xd8, 0x1b, 0x00, 0xc2, 0x14,
113 0x84, 0xe2, 0x02, 0x01, 0x21,
114 0xd8, 0x05, 0xc0, 0x01, 0x00,
115 0x84, 0xa2, 0x02, 0x01, 0x21,
116 0xd8, 0x19, 0x40, 0x01, 0x01,
117 0xa0, 0x58, 0x0c, 0x00, 0x02,
118 0x1a, 0x40, 0x00, 0x04, 0x24,
119 0x33, 0x00, 0x01, 0x2f, 0x20,
120 0xd8, 0x05, 0xc0, 0x00, 0x18,
121 0xa0, 0x58, 0x0c, 0x00, 0x02,
122 0x1a, 0x40, 0x00, 0x04, 0x20,
123 0xd8, 0x05, 0xc0, 0x00, 0x18,
124 0xe0, 0x00, 0x02, 0x00, 0x00,
125 0xe0, 0x00, 0x02, 0x00, 0x00,
126 0xe0, 0x00, 0x02, 0x00, 0x00,
127 0xe0, 0x00, 0x02, 0x00, 0x00,
128 0xe0, 0x00, 0x02, 0x00, 0x00,
129 }
130};
diff --git a/drivers/net/ixp2000/ixp2400_tx.uc b/drivers/net/ixp2000/ixp2400_tx.uc
new file mode 100644
index 000000000000..d090d1884fb7
--- /dev/null
+++ b/drivers/net/ixp2000/ixp2400_tx.uc
@@ -0,0 +1,272 @@
1/*
2 * TX ucode for the Intel IXP2400 in POS-PHY mode.
3 * Copyright (C) 2004, 2005 Lennert Buytenhek
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 * Assumptions made in this code:
12 * - The IXP2400 MSF is configured for POS-PHY mode, in a mode where
13 * only one TBUF partition is used. This includes, for example,
14 * 1x32 SPHY and 1x32 MPHY32, but not 4x8 SPHY or 1x32 MPHY4. (This
15 * is not an exhaustive list.)
16 * - The TBUF uses 64-byte mpackets.
17 * - TX descriptors reside in SRAM, and have the following format:
18 * struct tx_desc
19 * {
20 * // to uengine
21 * u32 buf_phys_addr;
22 * u32 pkt_length;
23 * u32 channel;
24 * };
25 * - Packet data resides in DRAM.
26 * - Packet buffer addresses are 8-byte aligned.
27 * - Scratch ring 2 is tx_pending.
28 * - Scratch ring 3 is tx_done, and has status condition 'full'.
29 * - This code is run on all eight threads of the microengine it runs on.
30 */
31
32#define TX_SEQUENCE_0 0x0060
33#define TBUF_CTRL 0x1800
34
35#define PARTITION_SIZE 128
36#define PARTITION_THRESH 96
37
38
39 .sig volatile sig1
40 .sig volatile sig2
41 .sig volatile sig3
42
43 .reg @old_tx_seq_0
44 .reg @mpkts_in_flight
45 .reg @next_tbuf_mpacket
46
47 .reg @buffer_handle
48 .reg @buffer_start
49 .reg @packet_length
50 .reg @channel
51 .reg @packet_offset
52
53 .reg zero
54
55 immed[zero, 0]
56
57 /*
58 * Skip context 0 initialisation?
59 */
60 .begin
61 br!=ctx[0, mpacket_tx_loop#]
62 .end
63
64 /*
65 * Wait until all pending TBUF elements have been transmitted.
66 */
67 .begin
68 .reg read $tx
69 .sig zzz
70
71 loop_empty#:
72 msf[read, $tx, zero, TX_SEQUENCE_0, 1], ctx_swap[zzz]
73 alu_shf[--, --, b, $tx, >>31]
74 beq[loop_empty#]
75
76 alu[@old_tx_seq_0, --, b, $tx]
77 .end
78
79 immed[@mpkts_in_flight, 0]
80 alu[@next_tbuf_mpacket, @old_tx_seq_0, and, (PARTITION_SIZE - 1)]
81
82 immed[@buffer_handle, 0]
83
84 /*
85 * Initialise signal pipeline.
86 */
87 .begin
88 local_csr_wr[SAME_ME_SIGNAL, (&sig1 << 3)]
89 .set_sig sig1
90
91 local_csr_wr[SAME_ME_SIGNAL, (&sig2 << 3)]
92 .set_sig sig2
93
94 local_csr_wr[SAME_ME_SIGNAL, (&sig3 << 3)]
95 .set_sig sig3
96 .end
97
98mpacket_tx_loop#:
99 .begin
100 .reg tbuf_element_index
101 .reg buffer_handle
102 .reg sop_eop
103 .reg packet_data
104 .reg channel
105 .reg mpacket_size
106
107 /*
108 * If there is no packet currently being transmitted,
109 * dequeue the next TX descriptor, and fetch the buffer
110 * address, packet length and destination channel number.
111 */
112 .begin
113 .reg read $stemp $stemp2 $stemp3
114 .xfer_order $stemp $stemp2 $stemp3
115 .sig zzz
116
117 ctx_arb[sig1]
118
119 alu[--, --, b, @buffer_handle]
120 bne[already_got_packet#]
121
122 tx_nobufs#:
123 scratch[get, $stemp, zero, 8, 1], ctx_swap[zzz]
124 alu[@buffer_handle, --, b, $stemp]
125 beq[tx_nobufs#]
126
127 sram[read, $stemp, $stemp, 0, 3], ctx_swap[zzz]
128 alu[@buffer_start, --, b, $stemp]
129 alu[@packet_length, --, b, $stemp2]
130 beq[zero_byte_packet#]
131 alu[@channel, --, b, $stemp3]
132 immed[@packet_offset, 0]
133
134 already_got_packet#:
135 local_csr_wr[SAME_ME_SIGNAL, (0x80 | (&sig1 << 3))]
136 .end
137
138 /*
139 * Determine tbuf element index, SOP/EOP flags, mpacket
140 * offset and mpacket size and cache buffer_handle and
141 * channel number.
142 */
143 .begin
144 alu[tbuf_element_index, --, b, @next_tbuf_mpacket]
145 alu[@next_tbuf_mpacket, @next_tbuf_mpacket, +, 1]
146 alu[@next_tbuf_mpacket, @next_tbuf_mpacket, and,
147 (PARTITION_SIZE - 1)]
148
149 alu[buffer_handle, --, b, @buffer_handle]
150 immed[@buffer_handle, 0]
151
152 immed[sop_eop, 1]
153
154 alu[packet_data, --, b, @packet_offset]
155 bne[no_sop#]
156 alu[sop_eop, sop_eop, or, 2]
157 no_sop#:
158 alu[packet_data, packet_data, +, @buffer_start]
159
160 alu[channel, --, b, @channel]
161
162 alu[mpacket_size, @packet_length, -, @packet_offset]
163 alu[--, 64, -, mpacket_size]
164 bhs[eop#]
165 alu[@buffer_handle, --, b, buffer_handle]
166 immed[mpacket_size, 64]
167 alu[sop_eop, sop_eop, and, 2]
168 eop#:
169
170 alu[@packet_offset, @packet_offset, +, mpacket_size]
171 .end
172
173 /*
174 * Wait until there's enough space in the TBUF.
175 */
176 .begin
177 .reg read $tx
178 .reg temp
179 .sig zzz
180
181 ctx_arb[sig2]
182
183 br[test_space#]
184
185 loop_space#:
186 msf[read, $tx, zero, TX_SEQUENCE_0, 1], ctx_swap[zzz]
187
188 alu[temp, $tx, -, @old_tx_seq_0]
189 alu[temp, temp, and, 0xff]
190 alu[@mpkts_in_flight, @mpkts_in_flight, -, temp]
191
192 alu[@old_tx_seq_0, --, b, $tx]
193
194 test_space#:
195 alu[--, PARTITION_THRESH, -, @mpkts_in_flight]
196 blo[loop_space#]
197
198 alu[@mpkts_in_flight, @mpkts_in_flight, +, 1]
199
200 local_csr_wr[SAME_ME_SIGNAL, (0x80 | (&sig2 << 3))]
201 .end
202
203 /*
204 * Copy the packet data to the TBUF.
205 */
206 .begin
207 .reg temp
208 .sig copy_sig
209
210 alu[temp, mpacket_size, -, 1]
211 alu_shf[temp, 0x10, or, temp, >>3]
212 alu_shf[temp, 0x10, or, temp, <<21]
213 alu_shf[temp, temp, or, tbuf_element_index, <<11]
214 alu_shf[--, temp, or, 1, <<18]
215
216 dram[tbuf_wr, --, packet_data, 0, max_8],
217 indirect_ref, sig_done[copy_sig]
218 ctx_arb[copy_sig]
219 .end
220
221 /*
222 * Mark TBUF element as ready-to-be-transmitted.
223 */
224 .begin
225 .reg write $tsw $tsw2
226 .xfer_order $tsw $tsw2
227 .reg temp
228 .sig zzz
229
230 alu_shf[temp, channel, or, mpacket_size, <<24]
231 alu_shf[$tsw, temp, or, sop_eop, <<8]
232 immed[$tsw2, 0]
233
234 immed[temp, TBUF_CTRL]
235 alu_shf[temp, temp, or, tbuf_element_index, <<3]
236 msf[write, $tsw, temp, 0, 2], ctx_swap[zzz]
237 .end
238
239 /*
240 * Resynchronise.
241 */
242 .begin
243 ctx_arb[sig3]
244 local_csr_wr[SAME_ME_SIGNAL, (0x80 | (&sig3 << 3))]
245 .end
246
247 /*
248 * If this was an EOP mpacket, recycle the TX buffer
249 * and signal the host.
250 */
251 .begin
252 .reg write $stemp
253 .sig zzz
254
255 alu[--, sop_eop, and, 1]
256 beq[mpacket_tx_loop#]
257
258 tx_done_ring_full#:
259 br_inp_state[SCR_Ring3_Status, tx_done_ring_full#]
260
261 alu[$stemp, --, b, buffer_handle]
262 scratch[put, $stemp, zero, 12, 1], ctx_swap[zzz]
263 cap[fast_wr, 0, XSCALE_INT_A]
264 br[mpacket_tx_loop#]
265 .end
266 .end
267
268
269zero_byte_packet#:
270 halt
271
272
diff --git a/drivers/net/ixp2000/ixp2400_tx.ucode b/drivers/net/ixp2000/ixp2400_tx.ucode
new file mode 100644
index 000000000000..a433e24b0a51
--- /dev/null
+++ b/drivers/net/ixp2000/ixp2400_tx.ucode
@@ -0,0 +1,98 @@
1static struct ixp2000_uengine_code ixp2400_tx =
2{
3 .cpu_model_bitmask = 0x000003fe,
4 .cpu_min_revision = 0,
5 .cpu_max_revision = 255,
6
7 .uengine_parameters = IXP2000_UENGINE_8_CONTEXTS |
8 IXP2000_UENGINE_PRN_UPDATE_EVERY |
9 IXP2000_UENGINE_NN_FROM_PREVIOUS |
10 IXP2000_UENGINE_ASSERT_EMPTY_AT_0 |
11 IXP2000_UENGINE_LM_ADDR1_PER_CONTEXT |
12 IXP2000_UENGINE_LM_ADDR0_PER_CONTEXT,
13
14 .initial_reg_values = (struct ixp2000_reg_value []) {
15 { -1, -1 }
16 },
17
18 .num_insns = 77,
19 .insns = (u8 []) {
20 0xf0, 0x00, 0x00, 0x07, 0x00,
21 0xd8, 0x03, 0x00, 0x00, 0x11,
22 0x3c, 0x40, 0x00, 0x04, 0xe0,
23 0x81, 0xf2, 0x02, 0x01, 0x00,
24 0xd8, 0x00, 0x80, 0x01, 0x00,
25 0xb0, 0x08, 0x06, 0x00, 0x00,
26 0xf0, 0x00, 0x0c, 0x00, 0x80,
27 0xb4, 0x49, 0x02, 0x03, 0x7f,
28 0xf0, 0x00, 0x02, 0x83, 0x00,
29 0xfc, 0x10, 0xac, 0x23, 0x08,
30 0xfc, 0x10, 0xac, 0x43, 0x10,
31 0xfc, 0x10, 0xac, 0x63, 0x18,
32 0xe0, 0x00, 0x00, 0x00, 0x02,
33 0xa0, 0x30, 0x02, 0x80, 0x00,
34 0xd8, 0x06, 0x00, 0x01, 0x01,
35 0x19, 0x40, 0x00, 0x04, 0x28,
36 0xb0, 0x0a, 0x06, 0x00, 0x00,
37 0xd8, 0x03, 0xc0, 0x01, 0x00,
38 0x00, 0x44, 0x00, 0x80, 0x80,
39 0xa0, 0x09, 0x06, 0x00, 0x00,
40 0xb0, 0x0b, 0x06, 0x04, 0x00,
41 0xd8, 0x13, 0x00, 0x01, 0x00,
42 0xb0, 0x0c, 0x06, 0x08, 0x00,
43 0xf0, 0x00, 0x0c, 0x00, 0xa0,
44 0xfc, 0x10, 0xae, 0x23, 0x88,
45 0xa0, 0x00, 0x12, 0x40, 0x00,
46 0xb0, 0xc9, 0x02, 0x43, 0x01,
47 0xb4, 0x49, 0x02, 0x43, 0x7f,
48 0xb0, 0x00, 0x22, 0x80, 0x00,
49 0xf0, 0x00, 0x02, 0x83, 0x00,
50 0xf0, 0x00, 0x0c, 0x04, 0x02,
51 0xb0, 0x40, 0x6c, 0x00, 0xa0,
52 0xd8, 0x08, 0x80, 0x01, 0x01,
53 0xaa, 0x00, 0x2c, 0x08, 0x02,
54 0xa0, 0xc0, 0x30, 0x18, 0x90,
55 0xa0, 0x00, 0x43, 0x00, 0x00,
56 0xba, 0xc0, 0x32, 0xc0, 0xa0,
57 0xaa, 0xb0, 0x00, 0x0f, 0x40,
58 0xd8, 0x0a, 0x80, 0x01, 0x04,
59 0xb0, 0x0a, 0x00, 0x08, 0x00,
60 0xf0, 0x00, 0x00, 0x0f, 0x40,
61 0xa4, 0x00, 0x2c, 0x08, 0x02,
62 0xa0, 0x8a, 0x00, 0x0c, 0xa0,
63 0xe0, 0x00, 0x00, 0x00, 0x04,
64 0xd8, 0x0c, 0x80, 0x00, 0x18,
65 0x3c, 0x40, 0x00, 0x04, 0xe0,
66 0xba, 0x80, 0x42, 0x01, 0x80,
67 0xb4, 0x40, 0x40, 0x13, 0xff,
68 0xaa, 0x88, 0x00, 0x10, 0x80,
69 0xb0, 0x08, 0x06, 0x00, 0x00,
70 0xaa, 0xf0, 0x0d, 0x80, 0x80,
71 0xd8, 0x0b, 0x40, 0x01, 0x05,
72 0xa0, 0x88, 0x0c, 0x04, 0x80,
73 0xfc, 0x10, 0xae, 0x43, 0x90,
74 0xba, 0xc0, 0x50, 0x0f, 0x01,
75 0x9a, 0x30, 0x50, 0x15, 0x30,
76 0x9a, 0xb0, 0x50, 0x16, 0x30,
77 0x9b, 0x50, 0x58, 0x16, 0x01,
78 0x8a, 0xe2, 0x08, 0x16, 0x21,
79 0x6b, 0x4e, 0x00, 0x83, 0x03,
80 0xe0, 0x00, 0x00, 0x00, 0x30,
81 0x9a, 0x80, 0x70, 0x0e, 0x04,
82 0x8b, 0x88, 0x08, 0x1e, 0x02,
83 0xf0, 0x00, 0x0c, 0x01, 0x81,
84 0xf0, 0x01, 0x80, 0x1f, 0x00,
85 0x9b, 0xd0, 0x78, 0x1e, 0x01,
86 0x3d, 0x42, 0x00, 0x1c, 0x20,
87 0xe0, 0x00, 0x00, 0x00, 0x08,
88 0xfc, 0x10, 0xae, 0x63, 0x98,
89 0xa4, 0x30, 0x0c, 0x04, 0x02,
90 0xd8, 0x03, 0x00, 0x01, 0x00,
91 0xd8, 0x11, 0xc1, 0x42, 0x14,
92 0xa0, 0x18, 0x00, 0x08, 0x00,
93 0x1a, 0x40, 0x00, 0x04, 0x2c,
94 0x33, 0x00, 0x01, 0x2f, 0x20,
95 0xd8, 0x03, 0x00, 0x00, 0x18,
96 0xe0, 0x00, 0x02, 0x00, 0x00,
97 }
98};
diff --git a/drivers/net/ixp2000/ixpdev.c b/drivers/net/ixp2000/ixpdev.c
new file mode 100644
index 000000000000..09f03f493bea
--- /dev/null
+++ b/drivers/net/ixp2000/ixpdev.c
@@ -0,0 +1,421 @@
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
27#define DRV_MODULE_VERSION "0.2"
28
29static int nds_count;
30static struct net_device **nds;
31static int nds_open;
32static void (*set_port_admin_status)(int port, int up);
33
34static struct ixpdev_rx_desc * const rx_desc =
35 (struct ixpdev_rx_desc *)(IXP2000_SRAM0_VIRT_BASE + RX_BUF_DESC_BASE);
36static struct ixpdev_tx_desc * const tx_desc =
37 (struct ixpdev_tx_desc *)(IXP2000_SRAM0_VIRT_BASE + TX_BUF_DESC_BASE);
38static int tx_pointer;
39
40
41static int ixpdev_xmit(struct sk_buff *skb, struct net_device *dev)
42{
43 struct ixpdev_priv *ip = netdev_priv(dev);
44 struct ixpdev_tx_desc *desc;
45 int entry;
46
47 if (unlikely(skb->len > PAGE_SIZE)) {
48 /* @@@ Count drops. */
49 dev_kfree_skb(skb);
50 return 0;
51 }
52
53 entry = tx_pointer;
54 tx_pointer = (tx_pointer + 1) % TX_BUF_COUNT;
55
56 desc = tx_desc + entry;
57 desc->pkt_length = skb->len;
58 desc->channel = ip->channel;
59
60 skb_copy_and_csum_dev(skb, phys_to_virt(desc->buf_addr));
61 dev_kfree_skb(skb);
62
63 ixp2000_reg_write(RING_TX_PENDING,
64 TX_BUF_DESC_BASE + (entry * sizeof(struct ixpdev_tx_desc)));
65
66 dev->trans_start = jiffies;
67
68 local_irq_disable();
69 ip->tx_queue_entries++;
70 if (ip->tx_queue_entries == TX_BUF_COUNT_PER_CHAN)
71 netif_stop_queue(dev);
72 local_irq_enable();
73
74 return 0;
75}
76
77
78static int ixpdev_rx(struct net_device *dev, int *budget)
79{
80 while (*budget > 0) {
81 struct ixpdev_rx_desc *desc;
82 struct sk_buff *skb;
83 void *buf;
84 u32 _desc;
85
86 _desc = ixp2000_reg_read(RING_RX_DONE);
87 if (_desc == 0)
88 return 0;
89
90 desc = rx_desc +
91 ((_desc - RX_BUF_DESC_BASE) / sizeof(struct ixpdev_rx_desc));
92 buf = phys_to_virt(desc->buf_addr);
93
94 if (desc->pkt_length < 4 || desc->pkt_length > PAGE_SIZE) {
95 printk(KERN_ERR "ixp2000: rx err, length %d\n",
96 desc->pkt_length);
97 goto err;
98 }
99
100 if (desc->channel < 0 || desc->channel >= nds_count) {
101 printk(KERN_ERR "ixp2000: rx err, channel %d\n",
102 desc->channel);
103 goto err;
104 }
105
106 /* @@@ Make FCS stripping configurable. */
107 desc->pkt_length -= 4;
108
109 if (unlikely(!netif_running(nds[desc->channel])))
110 goto err;
111
112 skb = dev_alloc_skb(desc->pkt_length + 2);
113 if (likely(skb != NULL)) {
114 skb->dev = nds[desc->channel];
115 skb_reserve(skb, 2);
116 eth_copy_and_sum(skb, buf, desc->pkt_length, 0);
117 skb_put(skb, desc->pkt_length);
118 skb->protocol = eth_type_trans(skb, skb->dev);
119
120 skb->dev->last_rx = jiffies;
121
122 netif_receive_skb(skb);
123 }
124
125err:
126 ixp2000_reg_write(RING_RX_PENDING, _desc);
127 dev->quota--;
128 (*budget)--;
129 }
130
131 return 1;
132}
133
134/* dev always points to nds[0]. */
135static int ixpdev_poll(struct net_device *dev, int *budget)
136{
137 /* @@@ Have to stop polling when nds[0] is administratively
138 * downed while we are polling. */
139 do {
140 ixp2000_reg_write(IXP2000_IRQ_THD_RAW_STATUS_A_0, 0x00ff);
141
142 if (ixpdev_rx(dev, budget))
143 return 1;
144 } while (ixp2000_reg_read(IXP2000_IRQ_THD_RAW_STATUS_A_0) & 0x00ff);
145
146 netif_rx_complete(dev);
147 ixp2000_reg_write(IXP2000_IRQ_THD_ENABLE_SET_A_0, 0x00ff);
148
149 return 0;
150}
151
152static void ixpdev_tx_complete(void)
153{
154 int channel;
155 u32 wake;
156
157 wake = 0;
158 while (1) {
159 struct ixpdev_priv *ip;
160 u32 desc;
161 int entry;
162
163 desc = ixp2000_reg_read(RING_TX_DONE);
164 if (desc == 0)
165 break;
166
167 /* @@@ Check whether entries come back in order. */
168 entry = (desc - TX_BUF_DESC_BASE) / sizeof(struct ixpdev_tx_desc);
169 channel = tx_desc[entry].channel;
170
171 if (channel < 0 || channel >= nds_count) {
172 printk(KERN_ERR "ixp2000: txcomp channel index "
173 "out of bounds (%d, %.8i, %d)\n",
174 channel, (unsigned int)desc, entry);
175 continue;
176 }
177
178 ip = netdev_priv(nds[channel]);
179 if (ip->tx_queue_entries == TX_BUF_COUNT_PER_CHAN)
180 wake |= 1 << channel;
181 ip->tx_queue_entries--;
182 }
183
184 for (channel = 0; wake != 0; channel++) {
185 if (wake & (1 << channel)) {
186 netif_wake_queue(nds[channel]);
187 wake &= ~(1 << channel);
188 }
189 }
190}
191
192static irqreturn_t ixpdev_interrupt(int irq, void *dev_id, struct pt_regs *regs)
193{
194 u32 status;
195
196 status = ixp2000_reg_read(IXP2000_IRQ_THD_STATUS_A_0);
197 if (status == 0)
198 return IRQ_NONE;
199
200 /*
201 * Any of the eight receive units signaled RX?
202 */
203 if (status & 0x00ff) {
204 ixp2000_reg_wrb(IXP2000_IRQ_THD_ENABLE_CLEAR_A_0, 0x00ff);
205 if (likely(__netif_rx_schedule_prep(nds[0]))) {
206 __netif_rx_schedule(nds[0]);
207 } else {
208 printk(KERN_CRIT "ixp2000: irq while polling!!\n");
209 }
210 }
211
212 /*
213 * Any of the eight transmit units signaled TXdone?
214 */
215 if (status & 0xff00) {
216 ixp2000_reg_wrb(IXP2000_IRQ_THD_RAW_STATUS_A_0, 0xff00);
217 ixpdev_tx_complete();
218 }
219
220 return IRQ_HANDLED;
221}
222
223#ifdef CONFIG_NET_POLL_CONTROLLER
224static void ixpdev_poll_controller(struct net_device *dev)
225{
226 disable_irq(IRQ_IXP2000_THDA0);
227 ixpdev_interrupt(IRQ_IXP2000_THDA0, dev, NULL);
228 enable_irq(IRQ_IXP2000_THDA0);
229}
230#endif
231
232static int ixpdev_open(struct net_device *dev)
233{
234 struct ixpdev_priv *ip = netdev_priv(dev);
235 int err;
236
237 if (!nds_open++) {
238 err = request_irq(IRQ_IXP2000_THDA0, ixpdev_interrupt,
239 SA_SHIRQ, "ixp2000_eth", nds);
240 if (err) {
241 nds_open--;
242 return err;
243 }
244
245 ixp2000_reg_write(IXP2000_IRQ_THD_ENABLE_SET_A_0, 0xffff);
246 }
247
248 set_port_admin_status(ip->channel, 1);
249 netif_start_queue(dev);
250
251 return 0;
252}
253
254static int ixpdev_close(struct net_device *dev)
255{
256 struct ixpdev_priv *ip = netdev_priv(dev);
257
258 netif_stop_queue(dev);
259 set_port_admin_status(ip->channel, 0);
260
261 if (!--nds_open) {
262 ixp2000_reg_write(IXP2000_IRQ_THD_ENABLE_CLEAR_A_0, 0xffff);
263 free_irq(IRQ_IXP2000_THDA0, nds);
264 }
265
266 return 0;
267}
268
269struct net_device *ixpdev_alloc(int channel, int sizeof_priv)
270{
271 struct net_device *dev;
272 struct ixpdev_priv *ip;
273
274 dev = alloc_etherdev(sizeof_priv);
275 if (dev == NULL)
276 return NULL;
277
278 dev->hard_start_xmit = ixpdev_xmit;
279 dev->poll = ixpdev_poll;
280 dev->open = ixpdev_open;
281 dev->stop = ixpdev_close;
282#ifdef CONFIG_NET_POLL_CONTROLLER
283 dev->poll_controller = ixpdev_poll_controller;
284#endif
285
286 dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM;
287 dev->weight = 64;
288
289 ip = netdev_priv(dev);
290 ip->channel = channel;
291 ip->tx_queue_entries = 0;
292
293 return dev;
294}
295
296int ixpdev_init(int __nds_count, struct net_device **__nds,
297 void (*__set_port_admin_status)(int port, int up))
298{
299 int i;
300 int err;
301
302 if (RX_BUF_COUNT > 192 || TX_BUF_COUNT > 192) {
303 static void __too_many_rx_or_tx_buffers(void);
304 __too_many_rx_or_tx_buffers();
305 }
306
307 printk(KERN_INFO "IXP2000 MSF ethernet driver %s\n", DRV_MODULE_VERSION);
308
309 nds_count = __nds_count;
310 nds = __nds;
311 set_port_admin_status = __set_port_admin_status;
312
313 for (i = 0; i < RX_BUF_COUNT; i++) {
314 void *buf;
315
316 buf = (void *)get_zeroed_page(GFP_KERNEL);
317 if (buf == NULL) {
318 err = -ENOMEM;
319 while (--i >= 0)
320 free_page((unsigned long)phys_to_virt(rx_desc[i].buf_addr));
321 goto err_out;
322 }
323 rx_desc[i].buf_addr = virt_to_phys(buf);
324 rx_desc[i].buf_length = PAGE_SIZE;
325 }
326
327 /* @@@ Maybe we shouldn't be preallocating TX buffers. */
328 for (i = 0; i < TX_BUF_COUNT; i++) {
329 void *buf;
330
331 buf = (void *)get_zeroed_page(GFP_KERNEL);
332 if (buf == NULL) {
333 err = -ENOMEM;
334 while (--i >= 0)
335 free_page((unsigned long)phys_to_virt(tx_desc[i].buf_addr));
336 goto err_free_rx;
337 }
338 tx_desc[i].buf_addr = virt_to_phys(buf);
339 }
340
341 /* 256 entries, ring status set means 'empty', base address 0x0000. */
342 ixp2000_reg_write(RING_RX_PENDING_BASE, 0x44000000);
343 ixp2000_reg_write(RING_RX_PENDING_HEAD, 0x00000000);
344 ixp2000_reg_write(RING_RX_PENDING_TAIL, 0x00000000);
345
346 /* 256 entries, ring status set means 'full', base address 0x0400. */
347 ixp2000_reg_write(RING_RX_DONE_BASE, 0x40000400);
348 ixp2000_reg_write(RING_RX_DONE_HEAD, 0x00000000);
349 ixp2000_reg_write(RING_RX_DONE_TAIL, 0x00000000);
350
351 for (i = 0; i < RX_BUF_COUNT; i++) {
352 ixp2000_reg_write(RING_RX_PENDING,
353 RX_BUF_DESC_BASE + (i * sizeof(struct ixpdev_rx_desc)));
354 }
355
356 ixp2000_uengine_load(0, &ixp2400_rx);
357 ixp2000_uengine_start_contexts(0, 0xff);
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 for (i = 0; i < nds_count; i++) {
373 err = register_netdev(nds[i]);
374 if (err) {
375 while (--i >= 0)
376 unregister_netdev(nds[i]);
377 goto err_free_tx;
378 }
379 }
380
381 for (i = 0; i < nds_count; i++) {
382 printk(KERN_INFO "%s: IXP2000 MSF ethernet (port %d), "
383 "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x.\n", nds[i]->name, i,
384 nds[i]->dev_addr[0], nds[i]->dev_addr[1],
385 nds[i]->dev_addr[2], nds[i]->dev_addr[3],
386 nds[i]->dev_addr[4], nds[i]->dev_addr[5]);
387 }
388
389 return 0;
390
391err_free_tx:
392 for (i = 0; i < TX_BUF_COUNT; i++)
393 free_page((unsigned long)phys_to_virt(tx_desc[i].buf_addr));
394
395err_free_rx:
396 for (i = 0; i < RX_BUF_COUNT; i++)
397 free_page((unsigned long)phys_to_virt(rx_desc[i].buf_addr));
398
399err_out:
400 return err;
401}
402
403void ixpdev_deinit(void)
404{
405 int i;
406
407 /* @@@ Flush out pending packets. */
408
409 for (i = 0; i < nds_count; i++)
410 unregister_netdev(nds[i]);
411
412 ixp2000_uengine_stop_contexts(1, 0xff);
413 ixp2000_uengine_stop_contexts(0, 0xff);
414 ixp2000_uengine_reset(0x3);
415
416 for (i = 0; i < TX_BUF_COUNT; i++)
417 free_page((unsigned long)phys_to_virt(tx_desc[i].buf_addr));
418
419 for (i = 0; i < RX_BUF_COUNT; i++)
420 free_page((unsigned long)phys_to_virt(rx_desc[i].buf_addr));
421}
diff --git a/drivers/net/ixp2000/ixpdev.h b/drivers/net/ixp2000/ixpdev.h
new file mode 100644
index 000000000000..bd686cb63058
--- /dev/null
+++ b/drivers/net/ixp2000/ixpdev.h
@@ -0,0 +1,27 @@
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#ifndef __IXPDEV_H
13#define __IXPDEV_H
14
15struct ixpdev_priv
16{
17 int channel;
18 int tx_queue_entries;
19};
20
21struct net_device *ixpdev_alloc(int channel, int sizeof_priv);
22int ixpdev_init(int num_ports, struct net_device **nds,
23 void (*set_port_admin_status)(int port, int up));
24void ixpdev_deinit(void);
25
26
27#endif
diff --git a/drivers/net/ixp2000/ixpdev_priv.h b/drivers/net/ixp2000/ixpdev_priv.h
new file mode 100644
index 000000000000..86aa08ea0c33
--- /dev/null
+++ b/drivers/net/ixp2000/ixpdev_priv.h
@@ -0,0 +1,57 @@
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#ifndef __IXPDEV_PRIV_H
13#define __IXPDEV_PRIV_H
14
15#define RX_BUF_DESC_BASE 0x00001000
16#define RX_BUF_COUNT ((3 * PAGE_SIZE) / (4 * sizeof(struct ixpdev_rx_desc)))
17#define TX_BUF_DESC_BASE 0x00002000
18#define TX_BUF_COUNT ((3 * PAGE_SIZE) / (4 * sizeof(struct ixpdev_tx_desc)))
19#define TX_BUF_COUNT_PER_CHAN (TX_BUF_COUNT / 4)
20
21#define RING_RX_PENDING ((u32 *)IXP2000_SCRATCH_RING_VIRT_BASE)
22#define RING_RX_DONE ((u32 *)(IXP2000_SCRATCH_RING_VIRT_BASE + 4))
23#define RING_TX_PENDING ((u32 *)(IXP2000_SCRATCH_RING_VIRT_BASE + 8))
24#define RING_TX_DONE ((u32 *)(IXP2000_SCRATCH_RING_VIRT_BASE + 12))
25
26#define SCRATCH_REG(x) ((u32 *)(IXP2000_GLOBAL_REG_VIRT_BASE | 0x0800 | (x)))
27#define RING_RX_PENDING_BASE SCRATCH_REG(0x00)
28#define RING_RX_PENDING_HEAD SCRATCH_REG(0x04)
29#define RING_RX_PENDING_TAIL SCRATCH_REG(0x08)
30#define RING_RX_DONE_BASE SCRATCH_REG(0x10)
31#define RING_RX_DONE_HEAD SCRATCH_REG(0x14)
32#define RING_RX_DONE_TAIL SCRATCH_REG(0x18)
33#define RING_TX_PENDING_BASE SCRATCH_REG(0x20)
34#define RING_TX_PENDING_HEAD SCRATCH_REG(0x24)
35#define RING_TX_PENDING_TAIL SCRATCH_REG(0x28)
36#define RING_TX_DONE_BASE SCRATCH_REG(0x30)
37#define RING_TX_DONE_HEAD SCRATCH_REG(0x34)
38#define RING_TX_DONE_TAIL SCRATCH_REG(0x38)
39
40struct ixpdev_rx_desc
41{
42 u32 buf_addr;
43 u32 buf_length;
44 u32 channel;
45 u32 pkt_length;
46};
47
48struct ixpdev_tx_desc
49{
50 u32 buf_addr;
51 u32 pkt_length;
52 u32 channel;
53 u32 unused;
54};
55
56
57#endif
diff --git a/drivers/net/ixp2000/pm3386.c b/drivers/net/ixp2000/pm3386.c
new file mode 100644
index 000000000000..5c7ab7564053
--- /dev/null
+++ b/drivers/net/ixp2000/pm3386.c
@@ -0,0 +1,334 @@
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#include "pm3386.h"
18
19/*
20 * Read from register 'reg' of PM3386 device 'pm'.
21 */
22static u16 pm3386_reg_read(int pm, int reg)
23{
24 void *_reg;
25 u16 value;
26
27 _reg = (void *)ENP2611_PM3386_0_VIRT_BASE;
28 if (pm == 1)
29 _reg = (void *)ENP2611_PM3386_1_VIRT_BASE;
30
31 value = *((volatile u16 *)(_reg + (reg << 1)));
32
33// printk(KERN_INFO "pm3386_reg_read(%d, %.3x) = %.8x\n", pm, reg, value);
34
35 return value;
36}
37
38/*
39 * Write to register 'reg' of PM3386 device 'pm', and perform
40 * a readback from the identification register.
41 */
42static void pm3386_reg_write(int pm, int reg, u16 value)
43{
44 void *_reg;
45 u16 dummy;
46
47// printk(KERN_INFO "pm3386_reg_write(%d, %.3x, %.8x)\n", pm, reg, value);
48
49 _reg = (void *)ENP2611_PM3386_0_VIRT_BASE;
50 if (pm == 1)
51 _reg = (void *)ENP2611_PM3386_1_VIRT_BASE;
52
53 *((volatile u16 *)(_reg + (reg << 1))) = value;
54
55 dummy = *((volatile u16 *)_reg);
56 __asm__ __volatile__("mov %0, %0" : "+r" (dummy));
57}
58
59/*
60 * Read from port 'port' register 'reg', where the registers
61 * for the different ports are 'spacing' registers apart.
62 */
63static u16 pm3386_port_reg_read(int port, int _reg, int spacing)
64{
65 int reg;
66
67 reg = _reg;
68 if (port & 1)
69 reg += spacing;
70
71 return pm3386_reg_read(port >> 1, reg);
72}
73
74/*
75 * Write to port 'port' register 'reg', where the registers
76 * for the different ports are 'spacing' registers apart.
77 */
78static void pm3386_port_reg_write(int port, int _reg, int spacing, u16 value)
79{
80 int reg;
81
82 reg = _reg;
83 if (port & 1)
84 reg += spacing;
85
86 pm3386_reg_write(port >> 1, reg, value);
87}
88
89
90void pm3386_reset(void)
91{
92 u8 mac[3][6];
93
94 /* Save programmed MAC addresses. */
95 pm3386_get_mac(0, mac[0]);
96 pm3386_get_mac(1, mac[1]);
97 pm3386_get_mac(2, mac[2]);
98
99 /* Assert analog and digital reset. */
100 pm3386_reg_write(0, 0x002, 0x0060);
101 pm3386_reg_write(1, 0x002, 0x0060);
102 mdelay(1);
103
104 /* Deassert analog reset. */
105 pm3386_reg_write(0, 0x002, 0x0062);
106 pm3386_reg_write(1, 0x002, 0x0062);
107 mdelay(10);
108
109 /* Deassert digital reset. */
110 pm3386_reg_write(0, 0x002, 0x0063);
111 pm3386_reg_write(1, 0x002, 0x0063);
112 mdelay(10);
113
114 /* Restore programmed MAC addresses. */
115 pm3386_set_mac(0, mac[0]);
116 pm3386_set_mac(1, mac[1]);
117 pm3386_set_mac(2, mac[2]);
118
119 /* Disable carrier on all ports. */
120 pm3386_set_carrier(0, 0);
121 pm3386_set_carrier(1, 0);
122 pm3386_set_carrier(2, 0);
123}
124
125static u16 swaph(u16 x)
126{
127 return ((x << 8) | (x >> 8)) & 0xffff;
128}
129
130void pm3386_init_port(int port)
131{
132 int pm = port >> 1;
133
134 /*
135 * Work around ENP2611 bootloader programming MAC address
136 * in reverse.
137 */
138 if (pm3386_port_reg_read(port, 0x30a, 0x100) == 0x0000 &&
139 (pm3386_port_reg_read(port, 0x309, 0x100) & 0xff00) == 0x5000) {
140 u16 temp[3];
141
142 temp[0] = pm3386_port_reg_read(port, 0x308, 0x100);
143 temp[1] = pm3386_port_reg_read(port, 0x309, 0x100);
144 temp[2] = pm3386_port_reg_read(port, 0x30a, 0x100);
145 pm3386_port_reg_write(port, 0x308, 0x100, swaph(temp[2]));
146 pm3386_port_reg_write(port, 0x309, 0x100, swaph(temp[1]));
147 pm3386_port_reg_write(port, 0x30a, 0x100, swaph(temp[0]));
148 }
149
150 /*
151 * Initialise narrowbanding mode. See application note 2010486
152 * for more information. (@@@ We also need to issue a reset
153 * when ROOL or DOOL are detected.)
154 */
155 pm3386_port_reg_write(port, 0x708, 0x10, 0xd055);
156 udelay(500);
157 pm3386_port_reg_write(port, 0x708, 0x10, 0x5055);
158
159 /*
160 * SPI-3 ingress block. Set 64 bytes SPI-3 burst size
161 * towards SPI-3 bridge.
162 */
163 pm3386_port_reg_write(port, 0x122, 0x20, 0x0002);
164
165 /*
166 * Enable ingress protocol checking, and soft reset the
167 * SPI-3 ingress block.
168 */
169 pm3386_reg_write(pm, 0x103, 0x0003);
170 while (!(pm3386_reg_read(pm, 0x103) & 0x80))
171 ;
172
173 /*
174 * SPI-3 egress block. Gather 12288 bytes of the current
175 * packet in the TX fifo before initiating transmit on the
176 * SERDES interface. (Prevents TX underflows.)
177 */
178 pm3386_port_reg_write(port, 0x221, 0x20, 0x0007);
179
180 /*
181 * Enforce odd parity from the SPI-3 bridge, and soft reset
182 * the SPI-3 egress block.
183 */
184 pm3386_reg_write(pm, 0x203, 0x000d & ~(4 << (port & 1)));
185 while ((pm3386_reg_read(pm, 0x203) & 0x000c) != 0x000c)
186 ;
187
188 /*
189 * EGMAC block. Set this channels to reject long preambles,
190 * not send or transmit PAUSE frames, enable preamble checking,
191 * disable frame length checking, enable FCS appending, enable
192 * TX frame padding.
193 */
194 pm3386_port_reg_write(port, 0x302, 0x100, 0x0113);
195
196 /*
197 * Soft reset the EGMAC block.
198 */
199 pm3386_port_reg_write(port, 0x301, 0x100, 0x8000);
200 pm3386_port_reg_write(port, 0x301, 0x100, 0x0000);
201
202 /*
203 * Auto-sense autonegotiation status.
204 */
205 pm3386_port_reg_write(port, 0x306, 0x100, 0x0100);
206
207 /*
208 * Allow reception of jumbo frames.
209 */
210 pm3386_port_reg_write(port, 0x310, 0x100, 9018);
211
212 /*
213 * Allow transmission of jumbo frames.
214 */
215 pm3386_port_reg_write(port, 0x336, 0x100, 9018);
216
217 /* @@@ Should set 0x337/0x437 (RX forwarding threshold.) */
218
219 /*
220 * Set autonegotiation parameters to 'no PAUSE, full duplex.'
221 */
222 pm3386_port_reg_write(port, 0x31c, 0x100, 0x0020);
223
224 /*
225 * Enable and restart autonegotiation.
226 */
227 pm3386_port_reg_write(port, 0x318, 0x100, 0x0003);
228 pm3386_port_reg_write(port, 0x318, 0x100, 0x0002);
229}
230
231void pm3386_get_mac(int port, u8 *mac)
232{
233 u16 temp;
234
235 temp = pm3386_port_reg_read(port, 0x308, 0x100);
236 mac[0] = temp & 0xff;
237 mac[1] = (temp >> 8) & 0xff;
238
239 temp = pm3386_port_reg_read(port, 0x309, 0x100);
240 mac[2] = temp & 0xff;
241 mac[3] = (temp >> 8) & 0xff;
242
243 temp = pm3386_port_reg_read(port, 0x30a, 0x100);
244 mac[4] = temp & 0xff;
245 mac[5] = (temp >> 8) & 0xff;
246}
247
248void pm3386_set_mac(int port, u8 *mac)
249{
250 pm3386_port_reg_write(port, 0x308, 0x100, (mac[1] << 8) | mac[0]);
251 pm3386_port_reg_write(port, 0x309, 0x100, (mac[3] << 8) | mac[2]);
252 pm3386_port_reg_write(port, 0x30a, 0x100, (mac[5] << 8) | mac[4]);
253}
254
255static u32 pm3386_get_stat(int port, u16 base)
256{
257 u32 value;
258
259 value = pm3386_port_reg_read(port, base, 0x100);
260 value |= pm3386_port_reg_read(port, base + 1, 0x100) << 16;
261
262 return value;
263}
264
265void pm3386_get_stats(int port, struct net_device_stats *stats)
266{
267 /*
268 * Snapshot statistics counters.
269 */
270 pm3386_port_reg_write(port, 0x500, 0x100, 0x0001);
271 while (pm3386_port_reg_read(port, 0x500, 0x100) & 0x0001)
272 ;
273
274 memset(stats, 0, sizeof(*stats));
275
276 stats->rx_packets = pm3386_get_stat(port, 0x510);
277 stats->tx_packets = pm3386_get_stat(port, 0x590);
278 stats->rx_bytes = pm3386_get_stat(port, 0x514);
279 stats->tx_bytes = pm3386_get_stat(port, 0x594);
280 /* @@@ Add other stats. */
281}
282
283void pm3386_set_carrier(int port, int state)
284{
285 pm3386_port_reg_write(port, 0x703, 0x10, state ? 0x1001 : 0x0000);
286}
287
288int pm3386_is_link_up(int port)
289{
290 u16 temp;
291
292 temp = pm3386_port_reg_read(port, 0x31a, 0x100);
293 temp = pm3386_port_reg_read(port, 0x31a, 0x100);
294
295 return !!(temp & 0x0002);
296}
297
298void pm3386_enable_rx(int port)
299{
300 u16 temp;
301
302 temp = pm3386_port_reg_read(port, 0x303, 0x100);
303 temp |= 0x1000;
304 pm3386_port_reg_write(port, 0x303, 0x100, temp);
305}
306
307void pm3386_disable_rx(int port)
308{
309 u16 temp;
310
311 temp = pm3386_port_reg_read(port, 0x303, 0x100);
312 temp &= 0xefff;
313 pm3386_port_reg_write(port, 0x303, 0x100, temp);
314}
315
316void pm3386_enable_tx(int port)
317{
318 u16 temp;
319
320 temp = pm3386_port_reg_read(port, 0x303, 0x100);
321 temp |= 0x4000;
322 pm3386_port_reg_write(port, 0x303, 0x100, temp);
323}
324
325void pm3386_disable_tx(int port)
326{
327 u16 temp;
328
329 temp = pm3386_port_reg_read(port, 0x303, 0x100);
330 temp &= 0xbfff;
331 pm3386_port_reg_write(port, 0x303, 0x100, temp);
332}
333
334MODULE_LICENSE("GPL");
diff --git a/drivers/net/ixp2000/pm3386.h b/drivers/net/ixp2000/pm3386.h
new file mode 100644
index 000000000000..fe92bb056ac4
--- /dev/null
+++ b/drivers/net/ixp2000/pm3386.h
@@ -0,0 +1,28 @@
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#ifndef __PM3386_H
13#define __PM3386_H
14
15void pm3386_reset(void);
16void pm3386_init_port(int port);
17void pm3386_get_mac(int port, u8 *mac);
18void pm3386_set_mac(int port, u8 *mac);
19void pm3386_get_stats(int port, struct net_device_stats *stats);
20void pm3386_set_carrier(int port, int state);
21int pm3386_is_link_up(int port);
22void pm3386_enable_rx(int port);
23void pm3386_disable_rx(int port);
24void pm3386_enable_tx(int port);
25void pm3386_disable_tx(int port);
26
27
28#endif