diff options
Diffstat (limited to 'drivers/net/ixp2000/ixp2400-msf.c')
-rw-r--r-- | drivers/net/ixp2000/ixp2400-msf.c | 213 |
1 files changed, 213 insertions, 0 deletions
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 | */ | ||
27 | static 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 | */ | ||
94 | static 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 | |||
126 | static 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 | |||
155 | static 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 | |||
164 | static 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 | |||
174 | void 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 | } | ||