Commit | Line | Data |
---|---|---|
15d014d1 LB |
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 | ||
15d014d1 LB |
12 | #include <linux/module.h> |
13 | #include <linux/kernel.h> | |
14 | #include <linux/netdevice.h> | |
15 | #include <linux/etherdevice.h> | |
16 | #include <linux/init.h> | |
17 | #include <linux/moduleparam.h> | |
18ec5c73 | 18 | #include <asm/hardware/uengine.h> |
15d014d1 LB |
19 | #include <asm/mach-types.h> |
20 | #include <asm/io.h> | |
21 | #include "ixpdev.h" | |
22 | #include "caleb.h" | |
23 | #include "ixp2400-msf.h" | |
24 | #include "pm3386.h" | |
25 | ||
26 | /*********************************************************************** | |
27 | * The Radisys ENP2611 is a PCI form factor board with three SFP GBIC | |
28 | * slots, connected via two PMC/Sierra 3386s and an SPI-3 bridge FPGA | |
29 | * to the IXP2400. | |
30 | * | |
31 | * +-------------+ | |
32 | * SFP GBIC #0 ---+ | +---------+ | |
33 | * | PM3386 #0 +-------+ | | |
34 | * SFP GBIC #1 ---+ | | "Caleb" | +---------+ | |
35 | * +-------------+ | | | | | |
36 | * | SPI-3 +---------+ IXP2400 | | |
37 | * +-------------+ | bridge | | | | |
38 | * SFP GBIC #2 ---+ | | FPGA | +---------+ | |
39 | * | PM3386 #1 +-------+ | | |
40 | * | | +---------+ | |
41 | * +-------------+ | |
42 | * ^ ^ ^ | |
43 | * | 1.25Gbaud | 104MHz | 104MHz | |
44 | * | SERDES ea. | SPI-3 ea. | SPI-3 | |
45 | * | |
46 | ***********************************************************************/ | |
47 | static struct ixp2400_msf_parameters enp2611_msf_parameters = | |
48 | { | |
49 | .rx_mode = IXP2400_RX_MODE_UTOPIA_POS | | |
50 | IXP2400_RX_MODE_1x32 | | |
51 | IXP2400_RX_MODE_MPHY | | |
52 | IXP2400_RX_MODE_MPHY_32 | | |
53 | IXP2400_RX_MODE_MPHY_POLLED_STATUS | | |
54 | IXP2400_RX_MODE_MPHY_LEVEL3 | | |
55 | IXP2400_RX_MODE_RBUF_SIZE_64, | |
56 | ||
57 | .rxclk01_multiplier = IXP2400_PLL_MULTIPLIER_16, | |
58 | ||
59 | .rx_poll_ports = 3, | |
60 | ||
61 | .rx_channel_mode = { | |
62 | IXP2400_PORT_RX_MODE_MASTER | | |
63 | IXP2400_PORT_RX_MODE_POS_PHY | | |
64 | IXP2400_PORT_RX_MODE_POS_PHY_L3 | | |
65 | IXP2400_PORT_RX_MODE_ODD_PARITY | | |
66 | IXP2400_PORT_RX_MODE_2_CYCLE_DECODE, | |
67 | ||
68 | IXP2400_PORT_RX_MODE_MASTER | | |
69 | IXP2400_PORT_RX_MODE_POS_PHY | | |
70 | IXP2400_PORT_RX_MODE_POS_PHY_L3 | | |
71 | IXP2400_PORT_RX_MODE_ODD_PARITY | | |
72 | IXP2400_PORT_RX_MODE_2_CYCLE_DECODE, | |
73 | ||
74 | IXP2400_PORT_RX_MODE_MASTER | | |
75 | IXP2400_PORT_RX_MODE_POS_PHY | | |
76 | IXP2400_PORT_RX_MODE_POS_PHY_L3 | | |
77 | IXP2400_PORT_RX_MODE_ODD_PARITY | | |
78 | IXP2400_PORT_RX_MODE_2_CYCLE_DECODE, | |
79 | ||
80 | IXP2400_PORT_RX_MODE_MASTER | | |
81 | IXP2400_PORT_RX_MODE_POS_PHY | | |
82 | IXP2400_PORT_RX_MODE_POS_PHY_L3 | | |
83 | IXP2400_PORT_RX_MODE_ODD_PARITY | | |
84 | IXP2400_PORT_RX_MODE_2_CYCLE_DECODE | |
85 | }, | |
86 | ||
87 | .tx_mode = IXP2400_TX_MODE_UTOPIA_POS | | |
88 | IXP2400_TX_MODE_1x32 | | |
89 | IXP2400_TX_MODE_MPHY | | |
90 | IXP2400_TX_MODE_MPHY_32 | | |
91 | IXP2400_TX_MODE_MPHY_POLLED_STATUS | | |
92 | IXP2400_TX_MODE_MPHY_LEVEL3 | | |
93 | IXP2400_TX_MODE_TBUF_SIZE_64, | |
94 | ||
95 | .txclk01_multiplier = IXP2400_PLL_MULTIPLIER_16, | |
96 | ||
97 | .tx_poll_ports = 3, | |
98 | ||
99 | .tx_channel_mode = { | |
100 | IXP2400_PORT_TX_MODE_MASTER | | |
101 | IXP2400_PORT_TX_MODE_POS_PHY | | |
102 | IXP2400_PORT_TX_MODE_ODD_PARITY | | |
103 | IXP2400_PORT_TX_MODE_2_CYCLE_DECODE, | |
104 | ||
105 | IXP2400_PORT_TX_MODE_MASTER | | |
106 | IXP2400_PORT_TX_MODE_POS_PHY | | |
107 | IXP2400_PORT_TX_MODE_ODD_PARITY | | |
108 | IXP2400_PORT_TX_MODE_2_CYCLE_DECODE, | |
109 | ||
110 | IXP2400_PORT_TX_MODE_MASTER | | |
111 | IXP2400_PORT_TX_MODE_POS_PHY | | |
112 | IXP2400_PORT_TX_MODE_ODD_PARITY | | |
113 | IXP2400_PORT_TX_MODE_2_CYCLE_DECODE, | |
114 | ||
115 | IXP2400_PORT_TX_MODE_MASTER | | |
116 | IXP2400_PORT_TX_MODE_POS_PHY | | |
117 | IXP2400_PORT_TX_MODE_ODD_PARITY | | |
118 | IXP2400_PORT_TX_MODE_2_CYCLE_DECODE | |
119 | } | |
120 | }; | |
121 | ||
122 | struct enp2611_ixpdev_priv | |
123 | { | |
124 | struct ixpdev_priv ixpdev_priv; | |
125 | struct net_device_stats stats; | |
126 | }; | |
127 | ||
128 | static struct net_device *nds[3]; | |
129 | static struct timer_list link_check_timer; | |
130 | ||
131 | static struct net_device_stats *enp2611_get_stats(struct net_device *dev) | |
132 | { | |
133 | struct enp2611_ixpdev_priv *ip = netdev_priv(dev); | |
134 | ||
135 | pm3386_get_stats(ip->ixpdev_priv.channel, &(ip->stats)); | |
136 | ||
137 | return &(ip->stats); | |
138 | } | |
139 | ||
140 | /* @@@ Poll the SFP moddef0 line too. */ | |
141 | /* @@@ Try to use the pm3386 DOOL interrupt as well. */ | |
142 | static void enp2611_check_link_status(unsigned long __dummy) | |
143 | { | |
144 | int i; | |
145 | ||
146 | for (i = 0; i < 3; i++) { | |
147 | struct net_device *dev; | |
148 | int status; | |
149 | ||
15d014d1 | 150 | dev = nds[i]; |
1ea739a5 LB |
151 | if (dev == NULL) |
152 | continue; | |
15d014d1 LB |
153 | |
154 | status = pm3386_is_link_up(i); | |
c44185d4 | 155 | if (status && !netif_carrier_ok(dev)) { |
350f1963 LB |
156 | /* @@@ Should report autonegotiation status. */ |
157 | printk(KERN_INFO "%s: NIC Link is Up\n", dev->name); | |
158 | ||
15d014d1 LB |
159 | pm3386_enable_tx(i); |
160 | caleb_enable_tx(i); | |
c44185d4 LB |
161 | netif_carrier_on(dev); |
162 | } else if (!status && netif_carrier_ok(dev)) { | |
350f1963 LB |
163 | printk(KERN_INFO "%s: NIC Link is Down\n", dev->name); |
164 | ||
c44185d4 | 165 | netif_carrier_off(dev); |
15d014d1 LB |
166 | caleb_disable_tx(i); |
167 | pm3386_disable_tx(i); | |
168 | } | |
169 | } | |
170 | ||
171 | link_check_timer.expires = jiffies + HZ / 10; | |
172 | add_timer(&link_check_timer); | |
173 | } | |
174 | ||
175 | static void enp2611_set_port_admin_status(int port, int up) | |
176 | { | |
177 | if (up) { | |
178 | caleb_enable_rx(port); | |
cffbfcaf LB |
179 | |
180 | pm3386_set_carrier(port, 1); | |
15d014d1 LB |
181 | pm3386_enable_rx(port); |
182 | } else { | |
183 | caleb_disable_tx(port); | |
184 | pm3386_disable_tx(port); | |
cffbfcaf LB |
185 | /* @@@ Flush out pending packets. */ |
186 | pm3386_set_carrier(port, 0); | |
187 | ||
15d014d1 LB |
188 | pm3386_disable_rx(port); |
189 | caleb_disable_rx(port); | |
190 | } | |
191 | } | |
192 | ||
193 | static int __init enp2611_init_module(void) | |
194 | { | |
1ea739a5 | 195 | int ports; |
15d014d1 LB |
196 | int i; |
197 | ||
198 | if (!machine_is_enp2611()) | |
199 | return -ENODEV; | |
200 | ||
201 | caleb_reset(); | |
202 | pm3386_reset(); | |
203 | ||
1ea739a5 LB |
204 | ports = pm3386_port_count(); |
205 | for (i = 0; i < ports; i++) { | |
15d014d1 LB |
206 | nds[i] = ixpdev_alloc(i, sizeof(struct enp2611_ixpdev_priv)); |
207 | if (nds[i] == NULL) { | |
208 | while (--i >= 0) | |
209 | free_netdev(nds[i]); | |
210 | return -ENOMEM; | |
211 | } | |
212 | ||
15d014d1 LB |
213 | nds[i]->get_stats = enp2611_get_stats; |
214 | pm3386_init_port(i); | |
215 | pm3386_get_mac(i, nds[i]->dev_addr); | |
216 | } | |
217 | ||
218 | ixp2400_msf_init(&enp2611_msf_parameters); | |
219 | ||
1ea739a5 LB |
220 | if (ixpdev_init(ports, nds, enp2611_set_port_admin_status)) { |
221 | for (i = 0; i < ports; i++) | |
222 | if (nds[i]) | |
223 | free_netdev(nds[i]); | |
15d014d1 LB |
224 | return -EINVAL; |
225 | } | |
226 | ||
227 | init_timer(&link_check_timer); | |
228 | link_check_timer.function = enp2611_check_link_status; | |
229 | link_check_timer.expires = jiffies; | |
230 | add_timer(&link_check_timer); | |
231 | ||
232 | return 0; | |
233 | } | |
234 | ||
235 | static void __exit enp2611_cleanup_module(void) | |
236 | { | |
237 | int i; | |
238 | ||
239 | del_timer_sync(&link_check_timer); | |
240 | ||
241 | ixpdev_deinit(); | |
242 | for (i = 0; i < 3; i++) | |
243 | free_netdev(nds[i]); | |
244 | } | |
245 | ||
246 | module_init(enp2611_init_module); | |
247 | module_exit(enp2611_cleanup_module); | |
248 | MODULE_LICENSE("GPL"); |