aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ixp2000
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
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')
-rw-r--r--drivers/net/ixp2000/Kconfig6
-rw-r--r--drivers/net/ixp2000/Makefile3
-rw-r--r--drivers/net/ixp2000/caleb.c136
-rw-r--r--drivers/net/ixp2000/caleb.h22
-rw-r--r--drivers/net/ixp2000/enp2611.c238
-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.c404
-rw-r--r--drivers/net/ixp2000/ixpdev.h27
-rw-r--r--drivers/net/ixp2000/ixpdev_priv.h57
-rw-r--r--drivers/net/ixp2000/pm3386.c304
-rw-r--r--drivers/net/ixp2000/pm3386.h26
16 files changed, 2459 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..d70530adb1e2
--- /dev/null
+++ b/drivers/net/ixp2000/caleb.c
@@ -0,0 +1,136 @@
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
17#define CALEB_IDLO 0x00
18#define CALEB_IDHI 0x01
19#define CALEB_RID 0x02
20#define CALEB_RESET 0x03
21#define CALEB_INTREN0 0x04
22#define CALEB_INTREN1 0x05
23#define CALEB_INTRSTAT0 0x06
24#define CALEB_INTRSTAT1 0x07
25#define CALEB_PORTEN 0x08
26#define CALEB_BURST 0x09
27#define CALEB_PORTPAUS 0x0A
28#define CALEB_PORTPAUSD 0x0B
29#define CALEB_PHY0RX 0x10
30#define CALEB_PHY1RX 0x11
31#define CALEB_PHY0TX 0x12
32#define CALEB_PHY1TX 0x13
33#define CALEB_IXPRX_HI_CNTR 0x15
34#define CALEB_PHY0RX_HI_CNTR 0x16
35#define CALEB_PHY1RX_HI_CNTR 0x17
36#define CALEB_IXPRX_CNTR 0x18
37#define CALEB_PHY0RX_CNTR 0x19
38#define CALEB_PHY1RX_CNTR 0x1A
39#define CALEB_IXPTX_CNTR 0x1B
40#define CALEB_PHY0TX_CNTR 0x1C
41#define CALEB_PHY1TX_CNTR 0x1D
42#define CALEB_DEBUG0 0x1E
43#define CALEB_DEBUG1 0x1F
44
45
46static u8 caleb_reg_read(int reg)
47{
48 u8 value;
49
50 value = *((volatile u8 *)(ENP2611_CALEB_VIRT_BASE + reg));
51
52// printk(KERN_INFO "caleb_reg_read(%d) = %.2x\n", reg, value);
53
54 return value;
55}
56
57static void caleb_reg_write(int reg, u8 value)
58{
59 u8 dummy;
60
61// printk(KERN_INFO "caleb_reg_write(%d, %.2x)\n", reg, value);
62
63 *((volatile u8 *)(ENP2611_CALEB_VIRT_BASE + reg)) = value;
64
65 dummy = *((volatile u8 *)ENP2611_CALEB_VIRT_BASE);
66 __asm__ __volatile__("mov %0, %0" : "+r" (dummy));
67}
68
69
70void caleb_reset(void)
71{
72 /*
73 * Perform a chip reset.
74 */
75 caleb_reg_write(CALEB_RESET, 0x02);
76 udelay(1);
77
78 /*
79 * Enable all interrupt sources. This is needed to get
80 * meaningful results out of the status bits (register 6
81 * and 7.)
82 */
83 caleb_reg_write(CALEB_INTREN0, 0xff);
84 caleb_reg_write(CALEB_INTREN1, 0x07);
85
86 /*
87 * Set RX and TX FIFO thresholds to 1.5kb.
88 */
89 caleb_reg_write(CALEB_PHY0RX, 0x11);
90 caleb_reg_write(CALEB_PHY1RX, 0x11);
91 caleb_reg_write(CALEB_PHY0TX, 0x11);
92 caleb_reg_write(CALEB_PHY1TX, 0x11);
93
94 /*
95 * Program SPI-3 burst size.
96 */
97 caleb_reg_write(CALEB_BURST, 0); // 64-byte RBUF mpackets
98// caleb_reg_write(CALEB_BURST, 1); // 128-byte RBUF mpackets
99// caleb_reg_write(CALEB_BURST, 2); // 256-byte RBUF mpackets
100}
101
102void caleb_enable_rx(int port)
103{
104 u8 temp;
105
106 temp = caleb_reg_read(CALEB_PORTEN);
107 temp |= 1 << port;
108 caleb_reg_write(CALEB_PORTEN, temp);
109}
110
111void caleb_disable_rx(int port)
112{
113 u8 temp;
114
115 temp = caleb_reg_read(CALEB_PORTEN);
116 temp &= ~(1 << port);
117 caleb_reg_write(CALEB_PORTEN, temp);
118}
119
120void caleb_enable_tx(int port)
121{
122 u8 temp;
123
124 temp = caleb_reg_read(CALEB_PORTEN);
125 temp |= 1 << (port + 4);
126 caleb_reg_write(CALEB_PORTEN, temp);
127}
128
129void caleb_disable_tx(int port)
130{
131 u8 temp;
132
133 temp = caleb_reg_read(CALEB_PORTEN);
134 temp &= ~(1 << (port + 4));
135 caleb_reg_write(CALEB_PORTEN, temp);
136}
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..3262e70ede61
--- /dev/null
+++ b/drivers/net/ixp2000/enp2611.c
@@ -0,0 +1,238 @@
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 if (!netif_running(nds[i]))
152 continue;
153
154 dev = nds[i];
155
156 status = pm3386_is_link_up(i);
157 if (status && !netif_carrier_ok(nds[i])) {
158 pm3386_enable_tx(i);
159 caleb_enable_tx(i);
160 netif_carrier_on(nds[i]);
161 } else if (!status && netif_carrier_ok(nds[i])) {
162 netif_carrier_off(nds[i]);
163 caleb_disable_tx(i);
164 pm3386_disable_tx(i);
165 }
166 }
167
168 link_check_timer.expires = jiffies + HZ / 10;
169 add_timer(&link_check_timer);
170}
171
172static void enp2611_set_port_admin_status(int port, int up)
173{
174 if (up) {
175 caleb_enable_rx(port);
176 pm3386_enable_rx(port);
177 } else {
178 caleb_disable_tx(port);
179 pm3386_disable_tx(port);
180 pm3386_disable_rx(port);
181 caleb_disable_rx(port);
182 }
183}
184
185static int __init enp2611_init_module(void)
186{
187 int i;
188
189 if (!machine_is_enp2611())
190 return -ENODEV;
191
192 caleb_reset();
193 pm3386_reset();
194
195 for (i = 0; i < 3; i++) {
196 nds[i] = ixpdev_alloc(i, sizeof(struct enp2611_ixpdev_priv));
197 if (nds[i] == NULL) {
198 while (--i >= 0)
199 free_netdev(nds[i]);
200 return -ENOMEM;
201 }
202
203 SET_MODULE_OWNER(nds[i]);
204 nds[i]->get_stats = enp2611_get_stats;
205 pm3386_init_port(i);
206 pm3386_get_mac(i, nds[i]->dev_addr);
207 }
208
209 ixp2400_msf_init(&enp2611_msf_parameters);
210
211 if (ixpdev_init(3, nds, enp2611_set_port_admin_status)) {
212 for (i = 0; i < 3; i++)
213 free_netdev(nds[i]);
214 return -EINVAL;
215 }
216
217 init_timer(&link_check_timer);
218 link_check_timer.function = enp2611_check_link_status;
219 link_check_timer.expires = jiffies;
220 add_timer(&link_check_timer);
221
222 return 0;
223}
224
225static void __exit enp2611_cleanup_module(void)
226{
227 int i;
228
229 del_timer_sync(&link_check_timer);
230
231 ixpdev_deinit();
232 for (i = 0; i < 3; i++)
233 free_netdev(nds[i]);
234}
235
236module_init(enp2611_init_module);
237module_exit(enp2611_cleanup_module);
238MODULE_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..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}
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..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");
diff --git a/drivers/net/ixp2000/pm3386.h b/drivers/net/ixp2000/pm3386.h
new file mode 100644
index 000000000000..55ecb1834219
--- /dev/null
+++ b/drivers/net/ixp2000/pm3386.h
@@ -0,0 +1,26 @@
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_get_stats(int port, struct net_device_stats *stats);
19int pm3386_is_link_up(int port);
20void pm3386_enable_rx(int port);
21void pm3386_disable_rx(int port);
22void pm3386_enable_tx(int port);
23void pm3386_disable_tx(int port);
24
25
26#endif