diff options
Diffstat (limited to 'arch/mips/cavium-octeon/executive')
18 files changed, 6604 insertions, 35 deletions
diff --git a/arch/mips/cavium-octeon/executive/Makefile b/arch/mips/cavium-octeon/executive/Makefile index 7f41c5be2190..b6d6e841a984 100644 --- a/arch/mips/cavium-octeon/executive/Makefile +++ b/arch/mips/cavium-octeon/executive/Makefile | |||
@@ -10,5 +10,10 @@ | |||
10 | # | 10 | # |
11 | 11 | ||
12 | obj-y += cvmx-bootmem.o cvmx-l2c.o cvmx-sysinfo.o octeon-model.o | 12 | obj-y += cvmx-bootmem.o cvmx-l2c.o cvmx-sysinfo.o octeon-model.o |
13 | obj-y += cvmx-pko.o cvmx-spi.o cvmx-cmd-queue.o \ | ||
14 | cvmx-helper-board.o cvmx-helper.o cvmx-helper-xaui.o \ | ||
15 | cvmx-helper-rgmii.o cvmx-helper-sgmii.o cvmx-helper-npi.o \ | ||
16 | cvmx-helper-loop.o cvmx-helper-spi.o cvmx-helper-util.o \ | ||
17 | cvmx-interrupt-decodes.o cvmx-interrupt-rsl.o | ||
13 | 18 | ||
14 | obj-$(CONFIG_CAVIUM_OCTEON_HELPER) += cvmx-helper-errata.o cvmx-helper-jtag.o | 19 | obj-y += cvmx-helper-errata.o cvmx-helper-jtag.o |
diff --git a/arch/mips/cavium-octeon/executive/cvmx-cmd-queue.c b/arch/mips/cavium-octeon/executive/cvmx-cmd-queue.c new file mode 100644 index 000000000000..132bccc66a93 --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-cmd-queue.c | |||
@@ -0,0 +1,306 @@ | |||
1 | /***********************license start*************** | ||
2 | * Author: Cavium Networks | ||
3 | * | ||
4 | * Contact: support@caviumnetworks.com | ||
5 | * This file is part of the OCTEON SDK | ||
6 | * | ||
7 | * Copyright (c) 2003-2008 Cavium Networks | ||
8 | * | ||
9 | * This file is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License, Version 2, as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * This file is distributed in the hope that it will be useful, but | ||
14 | * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty | ||
15 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or | ||
16 | * NONINFRINGEMENT. See the GNU General Public License for more | ||
17 | * details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this file; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
22 | * or visit http://www.gnu.org/licenses/. | ||
23 | * | ||
24 | * This file may also be available under a different license from Cavium. | ||
25 | * Contact Cavium Networks for more information | ||
26 | ***********************license end**************************************/ | ||
27 | |||
28 | /* | ||
29 | * Support functions for managing command queues used for | ||
30 | * various hardware blocks. | ||
31 | */ | ||
32 | |||
33 | #include <linux/kernel.h> | ||
34 | |||
35 | #include <asm/octeon/octeon.h> | ||
36 | |||
37 | #include <asm/octeon/cvmx-config.h> | ||
38 | #include <asm/octeon/cvmx-fpa.h> | ||
39 | #include <asm/octeon/cvmx-cmd-queue.h> | ||
40 | |||
41 | #include <asm/octeon/cvmx-npei-defs.h> | ||
42 | #include <asm/octeon/cvmx-pexp-defs.h> | ||
43 | #include <asm/octeon/cvmx-pko-defs.h> | ||
44 | |||
45 | /** | ||
46 | * This application uses this pointer to access the global queue | ||
47 | * state. It points to a bootmem named block. | ||
48 | */ | ||
49 | __cvmx_cmd_queue_all_state_t *__cvmx_cmd_queue_state_ptr; | ||
50 | |||
51 | /** | ||
52 | * Initialize the Global queue state pointer. | ||
53 | * | ||
54 | * Returns CVMX_CMD_QUEUE_SUCCESS or a failure code | ||
55 | */ | ||
56 | static cvmx_cmd_queue_result_t __cvmx_cmd_queue_init_state_ptr(void) | ||
57 | { | ||
58 | char *alloc_name = "cvmx_cmd_queues"; | ||
59 | #if defined(CONFIG_CAVIUM_RESERVE32) && CONFIG_CAVIUM_RESERVE32 | ||
60 | extern uint64_t octeon_reserve32_memory; | ||
61 | #endif | ||
62 | |||
63 | if (likely(__cvmx_cmd_queue_state_ptr)) | ||
64 | return CVMX_CMD_QUEUE_SUCCESS; | ||
65 | |||
66 | #if defined(CONFIG_CAVIUM_RESERVE32) && CONFIG_CAVIUM_RESERVE32 | ||
67 | if (octeon_reserve32_memory) | ||
68 | __cvmx_cmd_queue_state_ptr = | ||
69 | cvmx_bootmem_alloc_named_range(sizeof(*__cvmx_cmd_queue_state_ptr), | ||
70 | octeon_reserve32_memory, | ||
71 | octeon_reserve32_memory + | ||
72 | (CONFIG_CAVIUM_RESERVE32 << | ||
73 | 20) - 1, 128, alloc_name); | ||
74 | else | ||
75 | #endif | ||
76 | __cvmx_cmd_queue_state_ptr = | ||
77 | cvmx_bootmem_alloc_named(sizeof(*__cvmx_cmd_queue_state_ptr), | ||
78 | 128, | ||
79 | alloc_name); | ||
80 | if (__cvmx_cmd_queue_state_ptr) | ||
81 | memset(__cvmx_cmd_queue_state_ptr, 0, | ||
82 | sizeof(*__cvmx_cmd_queue_state_ptr)); | ||
83 | else { | ||
84 | struct cvmx_bootmem_named_block_desc *block_desc = | ||
85 | cvmx_bootmem_find_named_block(alloc_name); | ||
86 | if (block_desc) | ||
87 | __cvmx_cmd_queue_state_ptr = | ||
88 | cvmx_phys_to_ptr(block_desc->base_addr); | ||
89 | else { | ||
90 | cvmx_dprintf | ||
91 | ("ERROR: cvmx_cmd_queue_initialize: Unable to get named block %s.\n", | ||
92 | alloc_name); | ||
93 | return CVMX_CMD_QUEUE_NO_MEMORY; | ||
94 | } | ||
95 | } | ||
96 | return CVMX_CMD_QUEUE_SUCCESS; | ||
97 | } | ||
98 | |||
99 | /** | ||
100 | * Initialize a command queue for use. The initial FPA buffer is | ||
101 | * allocated and the hardware unit is configured to point to the | ||
102 | * new command queue. | ||
103 | * | ||
104 | * @queue_id: Hardware command queue to initialize. | ||
105 | * @max_depth: Maximum outstanding commands that can be queued. | ||
106 | * @fpa_pool: FPA pool the command queues should come from. | ||
107 | * @pool_size: Size of each buffer in the FPA pool (bytes) | ||
108 | * | ||
109 | * Returns CVMX_CMD_QUEUE_SUCCESS or a failure code | ||
110 | */ | ||
111 | cvmx_cmd_queue_result_t cvmx_cmd_queue_initialize(cvmx_cmd_queue_id_t queue_id, | ||
112 | int max_depth, int fpa_pool, | ||
113 | int pool_size) | ||
114 | { | ||
115 | __cvmx_cmd_queue_state_t *qstate; | ||
116 | cvmx_cmd_queue_result_t result = __cvmx_cmd_queue_init_state_ptr(); | ||
117 | if (result != CVMX_CMD_QUEUE_SUCCESS) | ||
118 | return result; | ||
119 | |||
120 | qstate = __cvmx_cmd_queue_get_state(queue_id); | ||
121 | if (qstate == NULL) | ||
122 | return CVMX_CMD_QUEUE_INVALID_PARAM; | ||
123 | |||
124 | /* | ||
125 | * We artificially limit max_depth to 1<<20 words. It is an | ||
126 | * arbitrary limit. | ||
127 | */ | ||
128 | if (CVMX_CMD_QUEUE_ENABLE_MAX_DEPTH) { | ||
129 | if ((max_depth < 0) || (max_depth > 1 << 20)) | ||
130 | return CVMX_CMD_QUEUE_INVALID_PARAM; | ||
131 | } else if (max_depth != 0) | ||
132 | return CVMX_CMD_QUEUE_INVALID_PARAM; | ||
133 | |||
134 | if ((fpa_pool < 0) || (fpa_pool > 7)) | ||
135 | return CVMX_CMD_QUEUE_INVALID_PARAM; | ||
136 | if ((pool_size < 128) || (pool_size > 65536)) | ||
137 | return CVMX_CMD_QUEUE_INVALID_PARAM; | ||
138 | |||
139 | /* See if someone else has already initialized the queue */ | ||
140 | if (qstate->base_ptr_div128) { | ||
141 | if (max_depth != (int)qstate->max_depth) { | ||
142 | cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: " | ||
143 | "Queue already initialized with different " | ||
144 | "max_depth (%d).\n", | ||
145 | (int)qstate->max_depth); | ||
146 | return CVMX_CMD_QUEUE_INVALID_PARAM; | ||
147 | } | ||
148 | if (fpa_pool != qstate->fpa_pool) { | ||
149 | cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: " | ||
150 | "Queue already initialized with different " | ||
151 | "FPA pool (%u).\n", | ||
152 | qstate->fpa_pool); | ||
153 | return CVMX_CMD_QUEUE_INVALID_PARAM; | ||
154 | } | ||
155 | if ((pool_size >> 3) - 1 != qstate->pool_size_m1) { | ||
156 | cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: " | ||
157 | "Queue already initialized with different " | ||
158 | "FPA pool size (%u).\n", | ||
159 | (qstate->pool_size_m1 + 1) << 3); | ||
160 | return CVMX_CMD_QUEUE_INVALID_PARAM; | ||
161 | } | ||
162 | CVMX_SYNCWS; | ||
163 | return CVMX_CMD_QUEUE_ALREADY_SETUP; | ||
164 | } else { | ||
165 | union cvmx_fpa_ctl_status status; | ||
166 | void *buffer; | ||
167 | |||
168 | status.u64 = cvmx_read_csr(CVMX_FPA_CTL_STATUS); | ||
169 | if (!status.s.enb) { | ||
170 | cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: " | ||
171 | "FPA is not enabled.\n"); | ||
172 | return CVMX_CMD_QUEUE_NO_MEMORY; | ||
173 | } | ||
174 | buffer = cvmx_fpa_alloc(fpa_pool); | ||
175 | if (buffer == NULL) { | ||
176 | cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: " | ||
177 | "Unable to allocate initial buffer.\n"); | ||
178 | return CVMX_CMD_QUEUE_NO_MEMORY; | ||
179 | } | ||
180 | |||
181 | memset(qstate, 0, sizeof(*qstate)); | ||
182 | qstate->max_depth = max_depth; | ||
183 | qstate->fpa_pool = fpa_pool; | ||
184 | qstate->pool_size_m1 = (pool_size >> 3) - 1; | ||
185 | qstate->base_ptr_div128 = cvmx_ptr_to_phys(buffer) / 128; | ||
186 | /* | ||
187 | * We zeroed the now serving field so we need to also | ||
188 | * zero the ticket. | ||
189 | */ | ||
190 | __cvmx_cmd_queue_state_ptr-> | ||
191 | ticket[__cvmx_cmd_queue_get_index(queue_id)] = 0; | ||
192 | CVMX_SYNCWS; | ||
193 | return CVMX_CMD_QUEUE_SUCCESS; | ||
194 | } | ||
195 | } | ||
196 | |||
197 | /** | ||
198 | * Shutdown a queue a free it's command buffers to the FPA. The | ||
199 | * hardware connected to the queue must be stopped before this | ||
200 | * function is called. | ||
201 | * | ||
202 | * @queue_id: Queue to shutdown | ||
203 | * | ||
204 | * Returns CVMX_CMD_QUEUE_SUCCESS or a failure code | ||
205 | */ | ||
206 | cvmx_cmd_queue_result_t cvmx_cmd_queue_shutdown(cvmx_cmd_queue_id_t queue_id) | ||
207 | { | ||
208 | __cvmx_cmd_queue_state_t *qptr = __cvmx_cmd_queue_get_state(queue_id); | ||
209 | if (qptr == NULL) { | ||
210 | cvmx_dprintf("ERROR: cvmx_cmd_queue_shutdown: Unable to " | ||
211 | "get queue information.\n"); | ||
212 | return CVMX_CMD_QUEUE_INVALID_PARAM; | ||
213 | } | ||
214 | |||
215 | if (cvmx_cmd_queue_length(queue_id) > 0) { | ||
216 | cvmx_dprintf("ERROR: cvmx_cmd_queue_shutdown: Queue still " | ||
217 | "has data in it.\n"); | ||
218 | return CVMX_CMD_QUEUE_FULL; | ||
219 | } | ||
220 | |||
221 | __cvmx_cmd_queue_lock(queue_id, qptr); | ||
222 | if (qptr->base_ptr_div128) { | ||
223 | cvmx_fpa_free(cvmx_phys_to_ptr | ||
224 | ((uint64_t) qptr->base_ptr_div128 << 7), | ||
225 | qptr->fpa_pool, 0); | ||
226 | qptr->base_ptr_div128 = 0; | ||
227 | } | ||
228 | __cvmx_cmd_queue_unlock(qptr); | ||
229 | |||
230 | return CVMX_CMD_QUEUE_SUCCESS; | ||
231 | } | ||
232 | |||
233 | /** | ||
234 | * Return the number of command words pending in the queue. This | ||
235 | * function may be relatively slow for some hardware units. | ||
236 | * | ||
237 | * @queue_id: Hardware command queue to query | ||
238 | * | ||
239 | * Returns Number of outstanding commands | ||
240 | */ | ||
241 | int cvmx_cmd_queue_length(cvmx_cmd_queue_id_t queue_id) | ||
242 | { | ||
243 | if (CVMX_ENABLE_PARAMETER_CHECKING) { | ||
244 | if (__cvmx_cmd_queue_get_state(queue_id) == NULL) | ||
245 | return CVMX_CMD_QUEUE_INVALID_PARAM; | ||
246 | } | ||
247 | |||
248 | /* | ||
249 | * The cast is here so gcc with check that all values in the | ||
250 | * cvmx_cmd_queue_id_t enumeration are here. | ||
251 | */ | ||
252 | switch ((cvmx_cmd_queue_id_t) (queue_id & 0xff0000)) { | ||
253 | case CVMX_CMD_QUEUE_PKO_BASE: | ||
254 | /* | ||
255 | * FIXME: Need atomic lock on | ||
256 | * CVMX_PKO_REG_READ_IDX. Right now we are normally | ||
257 | * called with the queue lock, so that is a SLIGHT | ||
258 | * amount of protection. | ||
259 | */ | ||
260 | cvmx_write_csr(CVMX_PKO_REG_READ_IDX, queue_id & 0xffff); | ||
261 | if (OCTEON_IS_MODEL(OCTEON_CN3XXX)) { | ||
262 | union cvmx_pko_mem_debug9 debug9; | ||
263 | debug9.u64 = cvmx_read_csr(CVMX_PKO_MEM_DEBUG9); | ||
264 | return debug9.cn38xx.doorbell; | ||
265 | } else { | ||
266 | union cvmx_pko_mem_debug8 debug8; | ||
267 | debug8.u64 = cvmx_read_csr(CVMX_PKO_MEM_DEBUG8); | ||
268 | return debug8.cn58xx.doorbell; | ||
269 | } | ||
270 | case CVMX_CMD_QUEUE_ZIP: | ||
271 | case CVMX_CMD_QUEUE_DFA: | ||
272 | case CVMX_CMD_QUEUE_RAID: | ||
273 | /* FIXME: Implement other lengths */ | ||
274 | return 0; | ||
275 | case CVMX_CMD_QUEUE_DMA_BASE: | ||
276 | { | ||
277 | union cvmx_npei_dmax_counts dmax_counts; | ||
278 | dmax_counts.u64 = | ||
279 | cvmx_read_csr(CVMX_PEXP_NPEI_DMAX_COUNTS | ||
280 | (queue_id & 0x7)); | ||
281 | return dmax_counts.s.dbell; | ||
282 | } | ||
283 | case CVMX_CMD_QUEUE_END: | ||
284 | return CVMX_CMD_QUEUE_INVALID_PARAM; | ||
285 | } | ||
286 | return CVMX_CMD_QUEUE_INVALID_PARAM; | ||
287 | } | ||
288 | |||
289 | /** | ||
290 | * Return the command buffer to be written to. The purpose of this | ||
291 | * function is to allow CVMX routine access t othe low level buffer | ||
292 | * for initial hardware setup. User applications should not call this | ||
293 | * function directly. | ||
294 | * | ||
295 | * @queue_id: Command queue to query | ||
296 | * | ||
297 | * Returns Command buffer or NULL on failure | ||
298 | */ | ||
299 | void *cvmx_cmd_queue_buffer(cvmx_cmd_queue_id_t queue_id) | ||
300 | { | ||
301 | __cvmx_cmd_queue_state_t *qptr = __cvmx_cmd_queue_get_state(queue_id); | ||
302 | if (qptr && qptr->base_ptr_div128) | ||
303 | return cvmx_phys_to_ptr((uint64_t) qptr->base_ptr_div128 << 7); | ||
304 | else | ||
305 | return NULL; | ||
306 | } | ||
diff --git a/arch/mips/cavium-octeon/executive/cvmx-fpa.c b/arch/mips/cavium-octeon/executive/cvmx-fpa.c new file mode 100644 index 000000000000..ad44b8bd8057 --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-fpa.c | |||
@@ -0,0 +1,183 @@ | |||
1 | /***********************license start*************** | ||
2 | * Author: Cavium Networks | ||
3 | * | ||
4 | * Contact: support@caviumnetworks.com | ||
5 | * This file is part of the OCTEON SDK | ||
6 | * | ||
7 | * Copyright (c) 2003-2008 Cavium Networks | ||
8 | * | ||
9 | * This file is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License, Version 2, as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * This file is distributed in the hope that it will be useful, but | ||
14 | * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty | ||
15 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or | ||
16 | * NONINFRINGEMENT. See the GNU General Public License for more | ||
17 | * details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this file; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
22 | * or visit http://www.gnu.org/licenses/. | ||
23 | * | ||
24 | * This file may also be available under a different license from Cavium. | ||
25 | * Contact Cavium Networks for more information | ||
26 | ***********************license end**************************************/ | ||
27 | |||
28 | /** | ||
29 | * @file | ||
30 | * | ||
31 | * Support library for the hardware Free Pool Allocator. | ||
32 | * | ||
33 | * | ||
34 | */ | ||
35 | |||
36 | #include "cvmx-config.h" | ||
37 | #include "cvmx.h" | ||
38 | #include "cvmx-fpa.h" | ||
39 | #include "cvmx-ipd.h" | ||
40 | |||
41 | /** | ||
42 | * Current state of all the pools. Use access functions | ||
43 | * instead of using it directly. | ||
44 | */ | ||
45 | CVMX_SHARED cvmx_fpa_pool_info_t cvmx_fpa_pool_info[CVMX_FPA_NUM_POOLS]; | ||
46 | |||
47 | /** | ||
48 | * Setup a FPA pool to control a new block of memory. The | ||
49 | * buffer pointer must be a physical address. | ||
50 | * | ||
51 | * @pool: Pool to initialize | ||
52 | * 0 <= pool < 8 | ||
53 | * @name: Constant character string to name this pool. | ||
54 | * String is not copied. | ||
55 | * @buffer: Pointer to the block of memory to use. This must be | ||
56 | * accessible by all processors and external hardware. | ||
57 | * @block_size: Size for each block controlled by the FPA | ||
58 | * @num_blocks: Number of blocks | ||
59 | * | ||
60 | * Returns 0 on Success, | ||
61 | * -1 on failure | ||
62 | */ | ||
63 | int cvmx_fpa_setup_pool(uint64_t pool, const char *name, void *buffer, | ||
64 | uint64_t block_size, uint64_t num_blocks) | ||
65 | { | ||
66 | char *ptr; | ||
67 | if (!buffer) { | ||
68 | cvmx_dprintf | ||
69 | ("ERROR: cvmx_fpa_setup_pool: NULL buffer pointer!\n"); | ||
70 | return -1; | ||
71 | } | ||
72 | if (pool >= CVMX_FPA_NUM_POOLS) { | ||
73 | cvmx_dprintf("ERROR: cvmx_fpa_setup_pool: Illegal pool!\n"); | ||
74 | return -1; | ||
75 | } | ||
76 | |||
77 | if (block_size < CVMX_FPA_MIN_BLOCK_SIZE) { | ||
78 | cvmx_dprintf | ||
79 | ("ERROR: cvmx_fpa_setup_pool: Block size too small.\n"); | ||
80 | return -1; | ||
81 | } | ||
82 | |||
83 | if (((unsigned long)buffer & (CVMX_FPA_ALIGNMENT - 1)) != 0) { | ||
84 | cvmx_dprintf | ||
85 | ("ERROR: cvmx_fpa_setup_pool: Buffer not aligned properly.\n"); | ||
86 | return -1; | ||
87 | } | ||
88 | |||
89 | cvmx_fpa_pool_info[pool].name = name; | ||
90 | cvmx_fpa_pool_info[pool].size = block_size; | ||
91 | cvmx_fpa_pool_info[pool].starting_element_count = num_blocks; | ||
92 | cvmx_fpa_pool_info[pool].base = buffer; | ||
93 | |||
94 | ptr = (char *)buffer; | ||
95 | while (num_blocks--) { | ||
96 | cvmx_fpa_free(ptr, pool, 0); | ||
97 | ptr += block_size; | ||
98 | } | ||
99 | return 0; | ||
100 | } | ||
101 | |||
102 | /** | ||
103 | * Shutdown a Memory pool and validate that it had all of | ||
104 | * the buffers originally placed in it. | ||
105 | * | ||
106 | * @pool: Pool to shutdown | ||
107 | * Returns Zero on success | ||
108 | * - Positive is count of missing buffers | ||
109 | * - Negative is too many buffers or corrupted pointers | ||
110 | */ | ||
111 | uint64_t cvmx_fpa_shutdown_pool(uint64_t pool) | ||
112 | { | ||
113 | uint64_t errors = 0; | ||
114 | uint64_t count = 0; | ||
115 | uint64_t base = cvmx_ptr_to_phys(cvmx_fpa_pool_info[pool].base); | ||
116 | uint64_t finish = | ||
117 | base + | ||
118 | cvmx_fpa_pool_info[pool].size * | ||
119 | cvmx_fpa_pool_info[pool].starting_element_count; | ||
120 | void *ptr; | ||
121 | uint64_t address; | ||
122 | |||
123 | count = 0; | ||
124 | do { | ||
125 | ptr = cvmx_fpa_alloc(pool); | ||
126 | if (ptr) | ||
127 | address = cvmx_ptr_to_phys(ptr); | ||
128 | else | ||
129 | address = 0; | ||
130 | if (address) { | ||
131 | if ((address >= base) && (address < finish) && | ||
132 | (((address - | ||
133 | base) % cvmx_fpa_pool_info[pool].size) == 0)) { | ||
134 | count++; | ||
135 | } else { | ||
136 | cvmx_dprintf | ||
137 | ("ERROR: cvmx_fpa_shutdown_pool: Illegal address 0x%llx in pool %s(%d)\n", | ||
138 | (unsigned long long)address, | ||
139 | cvmx_fpa_pool_info[pool].name, (int)pool); | ||
140 | errors++; | ||
141 | } | ||
142 | } | ||
143 | } while (address); | ||
144 | |||
145 | #ifdef CVMX_ENABLE_PKO_FUNCTIONS | ||
146 | if (pool == 0) | ||
147 | cvmx_ipd_free_ptr(); | ||
148 | #endif | ||
149 | |||
150 | if (errors) { | ||
151 | cvmx_dprintf | ||
152 | ("ERROR: cvmx_fpa_shutdown_pool: Pool %s(%d) started at 0x%llx, ended at 0x%llx, with a step of 0x%llx\n", | ||
153 | cvmx_fpa_pool_info[pool].name, (int)pool, | ||
154 | (unsigned long long)base, (unsigned long long)finish, | ||
155 | (unsigned long long)cvmx_fpa_pool_info[pool].size); | ||
156 | return -errors; | ||
157 | } else | ||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | uint64_t cvmx_fpa_get_block_size(uint64_t pool) | ||
162 | { | ||
163 | switch (pool) { | ||
164 | case 0: | ||
165 | return CVMX_FPA_POOL_0_SIZE; | ||
166 | case 1: | ||
167 | return CVMX_FPA_POOL_1_SIZE; | ||
168 | case 2: | ||
169 | return CVMX_FPA_POOL_2_SIZE; | ||
170 | case 3: | ||
171 | return CVMX_FPA_POOL_3_SIZE; | ||
172 | case 4: | ||
173 | return CVMX_FPA_POOL_4_SIZE; | ||
174 | case 5: | ||
175 | return CVMX_FPA_POOL_5_SIZE; | ||
176 | case 6: | ||
177 | return CVMX_FPA_POOL_6_SIZE; | ||
178 | case 7: | ||
179 | return CVMX_FPA_POOL_7_SIZE; | ||
180 | default: | ||
181 | return 0; | ||
182 | } | ||
183 | } | ||
diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-board.c b/arch/mips/cavium-octeon/executive/cvmx-helper-board.c new file mode 100644 index 000000000000..fd2015331a20 --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-board.c | |||
@@ -0,0 +1,711 @@ | |||
1 | /***********************license start*************** | ||
2 | * Author: Cavium Networks | ||
3 | * | ||
4 | * Contact: support@caviumnetworks.com | ||
5 | * This file is part of the OCTEON SDK | ||
6 | * | ||
7 | * Copyright (c) 2003-2008 Cavium Networks | ||
8 | * | ||
9 | * This file is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License, Version 2, as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * This file is distributed in the hope that it will be useful, but | ||
14 | * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty | ||
15 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or | ||
16 | * NONINFRINGEMENT. See the GNU General Public License for more | ||
17 | * details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this file; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
22 | * or visit http://www.gnu.org/licenses/. | ||
23 | * | ||
24 | * This file may also be available under a different license from Cavium. | ||
25 | * Contact Cavium Networks for more information | ||
26 | ***********************license end**************************************/ | ||
27 | |||
28 | /* | ||
29 | * | ||
30 | * Helper functions to abstract board specific data about | ||
31 | * network ports from the rest of the cvmx-helper files. | ||
32 | */ | ||
33 | |||
34 | #include <asm/octeon/octeon.h> | ||
35 | #include <asm/octeon/cvmx-bootinfo.h> | ||
36 | |||
37 | #include <asm/octeon/cvmx-config.h> | ||
38 | |||
39 | #include <asm/octeon/cvmx-mdio.h> | ||
40 | |||
41 | #include <asm/octeon/cvmx-helper.h> | ||
42 | #include <asm/octeon/cvmx-helper-util.h> | ||
43 | #include <asm/octeon/cvmx-helper-board.h> | ||
44 | |||
45 | #include <asm/octeon/cvmx-gmxx-defs.h> | ||
46 | #include <asm/octeon/cvmx-asxx-defs.h> | ||
47 | |||
48 | /** | ||
49 | * cvmx_override_board_link_get(int ipd_port) is a function | ||
50 | * pointer. It is meant to allow customization of the process of | ||
51 | * talking to a PHY to determine link speed. It is called every | ||
52 | * time a PHY must be polled for link status. Users should set | ||
53 | * this pointer to a function before calling any cvmx-helper | ||
54 | * operations. | ||
55 | */ | ||
56 | cvmx_helper_link_info_t(*cvmx_override_board_link_get) (int ipd_port) = | ||
57 | NULL; | ||
58 | |||
59 | /** | ||
60 | * Return the MII PHY address associated with the given IPD | ||
61 | * port. A result of -1 means there isn't a MII capable PHY | ||
62 | * connected to this port. On chips supporting multiple MII | ||
63 | * busses the bus number is encoded in bits <15:8>. | ||
64 | * | ||
65 | * This function must be modified for every new Octeon board. | ||
66 | * Internally it uses switch statements based on the cvmx_sysinfo | ||
67 | * data to determine board types and revisions. It replies on the | ||
68 | * fact that every Octeon board receives a unique board type | ||
69 | * enumeration from the bootloader. | ||
70 | * | ||
71 | * @ipd_port: Octeon IPD port to get the MII address for. | ||
72 | * | ||
73 | * Returns MII PHY address and bus number or -1. | ||
74 | */ | ||
75 | int cvmx_helper_board_get_mii_address(int ipd_port) | ||
76 | { | ||
77 | switch (cvmx_sysinfo_get()->board_type) { | ||
78 | case CVMX_BOARD_TYPE_SIM: | ||
79 | /* Simulator doesn't have MII */ | ||
80 | return -1; | ||
81 | case CVMX_BOARD_TYPE_EBT3000: | ||
82 | case CVMX_BOARD_TYPE_EBT5800: | ||
83 | case CVMX_BOARD_TYPE_THUNDER: | ||
84 | case CVMX_BOARD_TYPE_NICPRO2: | ||
85 | /* Interface 0 is SPI4, interface 1 is RGMII */ | ||
86 | if ((ipd_port >= 16) && (ipd_port < 20)) | ||
87 | return ipd_port - 16; | ||
88 | else | ||
89 | return -1; | ||
90 | case CVMX_BOARD_TYPE_KODAMA: | ||
91 | case CVMX_BOARD_TYPE_EBH3100: | ||
92 | case CVMX_BOARD_TYPE_HIKARI: | ||
93 | case CVMX_BOARD_TYPE_CN3010_EVB_HS5: | ||
94 | case CVMX_BOARD_TYPE_CN3005_EVB_HS5: | ||
95 | case CVMX_BOARD_TYPE_CN3020_EVB_HS5: | ||
96 | /* | ||
97 | * Port 0 is WAN connected to a PHY, Port 1 is GMII | ||
98 | * connected to a switch | ||
99 | */ | ||
100 | if (ipd_port == 0) | ||
101 | return 4; | ||
102 | else if (ipd_port == 1) | ||
103 | return 9; | ||
104 | else | ||
105 | return -1; | ||
106 | case CVMX_BOARD_TYPE_NAC38: | ||
107 | /* Board has 8 RGMII ports PHYs are 0-7 */ | ||
108 | if ((ipd_port >= 0) && (ipd_port < 4)) | ||
109 | return ipd_port; | ||
110 | else if ((ipd_port >= 16) && (ipd_port < 20)) | ||
111 | return ipd_port - 16 + 4; | ||
112 | else | ||
113 | return -1; | ||
114 | case CVMX_BOARD_TYPE_EBH3000: | ||
115 | /* Board has dual SPI4 and no PHYs */ | ||
116 | return -1; | ||
117 | case CVMX_BOARD_TYPE_EBH5200: | ||
118 | case CVMX_BOARD_TYPE_EBH5201: | ||
119 | case CVMX_BOARD_TYPE_EBT5200: | ||
120 | /* Board has 2 management ports */ | ||
121 | if ((ipd_port >= CVMX_HELPER_BOARD_MGMT_IPD_PORT) && | ||
122 | (ipd_port < (CVMX_HELPER_BOARD_MGMT_IPD_PORT + 2))) | ||
123 | return ipd_port - CVMX_HELPER_BOARD_MGMT_IPD_PORT; | ||
124 | /* | ||
125 | * Board has 4 SGMII ports. The PHYs start right after the MII | ||
126 | * ports MII0 = 0, MII1 = 1, SGMII = 2-5. | ||
127 | */ | ||
128 | if ((ipd_port >= 0) && (ipd_port < 4)) | ||
129 | return ipd_port + 2; | ||
130 | else | ||
131 | return -1; | ||
132 | case CVMX_BOARD_TYPE_EBH5600: | ||
133 | case CVMX_BOARD_TYPE_EBH5601: | ||
134 | case CVMX_BOARD_TYPE_EBH5610: | ||
135 | /* Board has 1 management port */ | ||
136 | if (ipd_port == CVMX_HELPER_BOARD_MGMT_IPD_PORT) | ||
137 | return 0; | ||
138 | /* | ||
139 | * Board has 8 SGMII ports. 4 connect out, two connect | ||
140 | * to a switch, and 2 loop to each other | ||
141 | */ | ||
142 | if ((ipd_port >= 0) && (ipd_port < 4)) | ||
143 | return ipd_port + 1; | ||
144 | else | ||
145 | return -1; | ||
146 | case CVMX_BOARD_TYPE_CUST_NB5: | ||
147 | if (ipd_port == 2) | ||
148 | return 4; | ||
149 | else | ||
150 | return -1; | ||
151 | case CVMX_BOARD_TYPE_NIC_XLE_4G: | ||
152 | /* Board has 4 SGMII ports. connected QLM3(interface 1) */ | ||
153 | if ((ipd_port >= 16) && (ipd_port < 20)) | ||
154 | return ipd_port - 16 + 1; | ||
155 | else | ||
156 | return -1; | ||
157 | case CVMX_BOARD_TYPE_NIC_XLE_10G: | ||
158 | case CVMX_BOARD_TYPE_NIC10E: | ||
159 | return -1; | ||
160 | case CVMX_BOARD_TYPE_NIC4E: | ||
161 | if (ipd_port >= 0 && ipd_port <= 3) | ||
162 | return (ipd_port + 0x1f) & 0x1f; | ||
163 | else | ||
164 | return -1; | ||
165 | case CVMX_BOARD_TYPE_NIC2E: | ||
166 | if (ipd_port >= 0 && ipd_port <= 1) | ||
167 | return ipd_port + 1; | ||
168 | else | ||
169 | return -1; | ||
170 | case CVMX_BOARD_TYPE_BBGW_REF: | ||
171 | /* | ||
172 | * No PHYs are connected to Octeon, everything is | ||
173 | * through switch. | ||
174 | */ | ||
175 | return -1; | ||
176 | |||
177 | case CVMX_BOARD_TYPE_CUST_WSX16: | ||
178 | if (ipd_port >= 0 && ipd_port <= 3) | ||
179 | return ipd_port; | ||
180 | else if (ipd_port >= 16 && ipd_port <= 19) | ||
181 | return ipd_port - 16 + 4; | ||
182 | else | ||
183 | return -1; | ||
184 | } | ||
185 | |||
186 | /* Some unknown board. Somebody forgot to update this function... */ | ||
187 | cvmx_dprintf | ||
188 | ("cvmx_helper_board_get_mii_address: Unknown board type %d\n", | ||
189 | cvmx_sysinfo_get()->board_type); | ||
190 | return -1; | ||
191 | } | ||
192 | |||
193 | /** | ||
194 | * This function is the board specific method of determining an | ||
195 | * ethernet ports link speed. Most Octeon boards have Marvell PHYs | ||
196 | * and are handled by the fall through case. This function must be | ||
197 | * updated for boards that don't have the normal Marvell PHYs. | ||
198 | * | ||
199 | * This function must be modified for every new Octeon board. | ||
200 | * Internally it uses switch statements based on the cvmx_sysinfo | ||
201 | * data to determine board types and revisions. It relies on the | ||
202 | * fact that every Octeon board receives a unique board type | ||
203 | * enumeration from the bootloader. | ||
204 | * | ||
205 | * @ipd_port: IPD input port associated with the port we want to get link | ||
206 | * status for. | ||
207 | * | ||
208 | * Returns The ports link status. If the link isn't fully resolved, this must | ||
209 | * return zero. | ||
210 | */ | ||
211 | cvmx_helper_link_info_t __cvmx_helper_board_link_get(int ipd_port) | ||
212 | { | ||
213 | cvmx_helper_link_info_t result; | ||
214 | int phy_addr; | ||
215 | int is_broadcom_phy = 0; | ||
216 | |||
217 | /* Give the user a chance to override the processing of this function */ | ||
218 | if (cvmx_override_board_link_get) | ||
219 | return cvmx_override_board_link_get(ipd_port); | ||
220 | |||
221 | /* Unless we fix it later, all links are defaulted to down */ | ||
222 | result.u64 = 0; | ||
223 | |||
224 | /* | ||
225 | * This switch statement should handle all ports that either don't use | ||
226 | * Marvell PHYS, or don't support in-band status. | ||
227 | */ | ||
228 | switch (cvmx_sysinfo_get()->board_type) { | ||
229 | case CVMX_BOARD_TYPE_SIM: | ||
230 | /* The simulator gives you a simulated 1Gbps full duplex link */ | ||
231 | result.s.link_up = 1; | ||
232 | result.s.full_duplex = 1; | ||
233 | result.s.speed = 1000; | ||
234 | return result; | ||
235 | case CVMX_BOARD_TYPE_EBH3100: | ||
236 | case CVMX_BOARD_TYPE_CN3010_EVB_HS5: | ||
237 | case CVMX_BOARD_TYPE_CN3005_EVB_HS5: | ||
238 | case CVMX_BOARD_TYPE_CN3020_EVB_HS5: | ||
239 | /* Port 1 on these boards is always Gigabit */ | ||
240 | if (ipd_port == 1) { | ||
241 | result.s.link_up = 1; | ||
242 | result.s.full_duplex = 1; | ||
243 | result.s.speed = 1000; | ||
244 | return result; | ||
245 | } | ||
246 | /* Fall through to the generic code below */ | ||
247 | break; | ||
248 | case CVMX_BOARD_TYPE_CUST_NB5: | ||
249 | /* Port 1 on these boards is always Gigabit */ | ||
250 | if (ipd_port == 1) { | ||
251 | result.s.link_up = 1; | ||
252 | result.s.full_duplex = 1; | ||
253 | result.s.speed = 1000; | ||
254 | return result; | ||
255 | } else /* The other port uses a broadcom PHY */ | ||
256 | is_broadcom_phy = 1; | ||
257 | break; | ||
258 | case CVMX_BOARD_TYPE_BBGW_REF: | ||
259 | /* Port 1 on these boards is always Gigabit */ | ||
260 | if (ipd_port == 2) { | ||
261 | /* Port 2 is not hooked up */ | ||
262 | result.u64 = 0; | ||
263 | return result; | ||
264 | } else { | ||
265 | /* Ports 0 and 1 connect to the switch */ | ||
266 | result.s.link_up = 1; | ||
267 | result.s.full_duplex = 1; | ||
268 | result.s.speed = 1000; | ||
269 | return result; | ||
270 | } | ||
271 | break; | ||
272 | } | ||
273 | |||
274 | phy_addr = cvmx_helper_board_get_mii_address(ipd_port); | ||
275 | if (phy_addr != -1) { | ||
276 | if (is_broadcom_phy) { | ||
277 | /* | ||
278 | * Below we are going to read SMI/MDIO | ||
279 | * register 0x19 which works on Broadcom | ||
280 | * parts | ||
281 | */ | ||
282 | int phy_status = | ||
283 | cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, | ||
284 | 0x19); | ||
285 | switch ((phy_status >> 8) & 0x7) { | ||
286 | case 0: | ||
287 | result.u64 = 0; | ||
288 | break; | ||
289 | case 1: | ||
290 | result.s.link_up = 1; | ||
291 | result.s.full_duplex = 0; | ||
292 | result.s.speed = 10; | ||
293 | break; | ||
294 | case 2: | ||
295 | result.s.link_up = 1; | ||
296 | result.s.full_duplex = 1; | ||
297 | result.s.speed = 10; | ||
298 | break; | ||
299 | case 3: | ||
300 | result.s.link_up = 1; | ||
301 | result.s.full_duplex = 0; | ||
302 | result.s.speed = 100; | ||
303 | break; | ||
304 | case 4: | ||
305 | result.s.link_up = 1; | ||
306 | result.s.full_duplex = 1; | ||
307 | result.s.speed = 100; | ||
308 | break; | ||
309 | case 5: | ||
310 | result.s.link_up = 1; | ||
311 | result.s.full_duplex = 1; | ||
312 | result.s.speed = 100; | ||
313 | break; | ||
314 | case 6: | ||
315 | result.s.link_up = 1; | ||
316 | result.s.full_duplex = 0; | ||
317 | result.s.speed = 1000; | ||
318 | break; | ||
319 | case 7: | ||
320 | result.s.link_up = 1; | ||
321 | result.s.full_duplex = 1; | ||
322 | result.s.speed = 1000; | ||
323 | break; | ||
324 | } | ||
325 | } else { | ||
326 | /* | ||
327 | * This code assumes we are using a Marvell | ||
328 | * Gigabit PHY. All the speed information can | ||
329 | * be read from register 17 in one | ||
330 | * go. Somebody using a different PHY will | ||
331 | * need to handle it above in the board | ||
332 | * specific area. | ||
333 | */ | ||
334 | int phy_status = | ||
335 | cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 17); | ||
336 | |||
337 | /* | ||
338 | * If the resolve bit 11 isn't set, see if | ||
339 | * autoneg is turned off (bit 12, reg 0). The | ||
340 | * resolve bit doesn't get set properly when | ||
341 | * autoneg is off, so force it. | ||
342 | */ | ||
343 | if ((phy_status & (1 << 11)) == 0) { | ||
344 | int auto_status = | ||
345 | cvmx_mdio_read(phy_addr >> 8, | ||
346 | phy_addr & 0xff, 0); | ||
347 | if ((auto_status & (1 << 12)) == 0) | ||
348 | phy_status |= 1 << 11; | ||
349 | } | ||
350 | |||
351 | /* | ||
352 | * Only return a link if the PHY has finished | ||
353 | * auto negotiation and set the resolved bit | ||
354 | * (bit 11) | ||
355 | */ | ||
356 | if (phy_status & (1 << 11)) { | ||
357 | result.s.link_up = 1; | ||
358 | result.s.full_duplex = ((phy_status >> 13) & 1); | ||
359 | switch ((phy_status >> 14) & 3) { | ||
360 | case 0: /* 10 Mbps */ | ||
361 | result.s.speed = 10; | ||
362 | break; | ||
363 | case 1: /* 100 Mbps */ | ||
364 | result.s.speed = 100; | ||
365 | break; | ||
366 | case 2: /* 1 Gbps */ | ||
367 | result.s.speed = 1000; | ||
368 | break; | ||
369 | case 3: /* Illegal */ | ||
370 | result.u64 = 0; | ||
371 | break; | ||
372 | } | ||
373 | } | ||
374 | } | ||
375 | } else if (OCTEON_IS_MODEL(OCTEON_CN3XXX) | ||
376 | || OCTEON_IS_MODEL(OCTEON_CN58XX) | ||
377 | || OCTEON_IS_MODEL(OCTEON_CN50XX)) { | ||
378 | /* | ||
379 | * We don't have a PHY address, so attempt to use | ||
380 | * in-band status. It is really important that boards | ||
381 | * not supporting in-band status never get | ||
382 | * here. Reading broken in-band status tends to do bad | ||
383 | * things | ||
384 | */ | ||
385 | union cvmx_gmxx_rxx_rx_inbnd inband_status; | ||
386 | int interface = cvmx_helper_get_interface_num(ipd_port); | ||
387 | int index = cvmx_helper_get_interface_index_num(ipd_port); | ||
388 | inband_status.u64 = | ||
389 | cvmx_read_csr(CVMX_GMXX_RXX_RX_INBND(index, interface)); | ||
390 | |||
391 | result.s.link_up = inband_status.s.status; | ||
392 | result.s.full_duplex = inband_status.s.duplex; | ||
393 | switch (inband_status.s.speed) { | ||
394 | case 0: /* 10 Mbps */ | ||
395 | result.s.speed = 10; | ||
396 | break; | ||
397 | case 1: /* 100 Mbps */ | ||
398 | result.s.speed = 100; | ||
399 | break; | ||
400 | case 2: /* 1 Gbps */ | ||
401 | result.s.speed = 1000; | ||
402 | break; | ||
403 | case 3: /* Illegal */ | ||
404 | result.u64 = 0; | ||
405 | break; | ||
406 | } | ||
407 | } else { | ||
408 | /* | ||
409 | * We don't have a PHY address and we don't have | ||
410 | * in-band status. There is no way to determine the | ||
411 | * link speed. Return down assuming this port isn't | ||
412 | * wired | ||
413 | */ | ||
414 | result.u64 = 0; | ||
415 | } | ||
416 | |||
417 | /* If link is down, return all fields as zero. */ | ||
418 | if (!result.s.link_up) | ||
419 | result.u64 = 0; | ||
420 | |||
421 | return result; | ||
422 | } | ||
423 | |||
424 | /** | ||
425 | * This function as a board specific method of changing the PHY | ||
426 | * speed, duplex, and auto-negotiation. This programs the PHY and | ||
427 | * not Octeon. This can be used to force Octeon's links to | ||
428 | * specific settings. | ||
429 | * | ||
430 | * @phy_addr: The address of the PHY to program | ||
431 | * @enable_autoneg: | ||
432 | * Non zero if you want to enable auto-negotiation. | ||
433 | * @link_info: Link speed to program. If the speed is zero and auto-negotiation | ||
434 | * is enabled, all possible negotiation speeds are advertised. | ||
435 | * | ||
436 | * Returns Zero on success, negative on failure | ||
437 | */ | ||
438 | int cvmx_helper_board_link_set_phy(int phy_addr, | ||
439 | cvmx_helper_board_set_phy_link_flags_types_t | ||
440 | link_flags, | ||
441 | cvmx_helper_link_info_t link_info) | ||
442 | { | ||
443 | |||
444 | /* Set the flow control settings based on link_flags */ | ||
445 | if ((link_flags & set_phy_link_flags_flow_control_mask) != | ||
446 | set_phy_link_flags_flow_control_dont_touch) { | ||
447 | cvmx_mdio_phy_reg_autoneg_adver_t reg_autoneg_adver; | ||
448 | reg_autoneg_adver.u16 = | ||
449 | cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, | ||
450 | CVMX_MDIO_PHY_REG_AUTONEG_ADVER); | ||
451 | reg_autoneg_adver.s.asymmetric_pause = | ||
452 | (link_flags & set_phy_link_flags_flow_control_mask) == | ||
453 | set_phy_link_flags_flow_control_enable; | ||
454 | reg_autoneg_adver.s.pause = | ||
455 | (link_flags & set_phy_link_flags_flow_control_mask) == | ||
456 | set_phy_link_flags_flow_control_enable; | ||
457 | cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, | ||
458 | CVMX_MDIO_PHY_REG_AUTONEG_ADVER, | ||
459 | reg_autoneg_adver.u16); | ||
460 | } | ||
461 | |||
462 | /* If speed isn't set and autoneg is on advertise all supported modes */ | ||
463 | if ((link_flags & set_phy_link_flags_autoneg) | ||
464 | && (link_info.s.speed == 0)) { | ||
465 | cvmx_mdio_phy_reg_control_t reg_control; | ||
466 | cvmx_mdio_phy_reg_status_t reg_status; | ||
467 | cvmx_mdio_phy_reg_autoneg_adver_t reg_autoneg_adver; | ||
468 | cvmx_mdio_phy_reg_extended_status_t reg_extended_status; | ||
469 | cvmx_mdio_phy_reg_control_1000_t reg_control_1000; | ||
470 | |||
471 | reg_status.u16 = | ||
472 | cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, | ||
473 | CVMX_MDIO_PHY_REG_STATUS); | ||
474 | reg_autoneg_adver.u16 = | ||
475 | cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, | ||
476 | CVMX_MDIO_PHY_REG_AUTONEG_ADVER); | ||
477 | reg_autoneg_adver.s.advert_100base_t4 = | ||
478 | reg_status.s.capable_100base_t4; | ||
479 | reg_autoneg_adver.s.advert_10base_tx_full = | ||
480 | reg_status.s.capable_10_full; | ||
481 | reg_autoneg_adver.s.advert_10base_tx_half = | ||
482 | reg_status.s.capable_10_half; | ||
483 | reg_autoneg_adver.s.advert_100base_tx_full = | ||
484 | reg_status.s.capable_100base_x_full; | ||
485 | reg_autoneg_adver.s.advert_100base_tx_half = | ||
486 | reg_status.s.capable_100base_x_half; | ||
487 | cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, | ||
488 | CVMX_MDIO_PHY_REG_AUTONEG_ADVER, | ||
489 | reg_autoneg_adver.u16); | ||
490 | if (reg_status.s.capable_extended_status) { | ||
491 | reg_extended_status.u16 = | ||
492 | cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, | ||
493 | CVMX_MDIO_PHY_REG_EXTENDED_STATUS); | ||
494 | reg_control_1000.u16 = | ||
495 | cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, | ||
496 | CVMX_MDIO_PHY_REG_CONTROL_1000); | ||
497 | reg_control_1000.s.advert_1000base_t_full = | ||
498 | reg_extended_status.s.capable_1000base_t_full; | ||
499 | reg_control_1000.s.advert_1000base_t_half = | ||
500 | reg_extended_status.s.capable_1000base_t_half; | ||
501 | cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, | ||
502 | CVMX_MDIO_PHY_REG_CONTROL_1000, | ||
503 | reg_control_1000.u16); | ||
504 | } | ||
505 | reg_control.u16 = | ||
506 | cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, | ||
507 | CVMX_MDIO_PHY_REG_CONTROL); | ||
508 | reg_control.s.autoneg_enable = 1; | ||
509 | reg_control.s.restart_autoneg = 1; | ||
510 | cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, | ||
511 | CVMX_MDIO_PHY_REG_CONTROL, reg_control.u16); | ||
512 | } else if ((link_flags & set_phy_link_flags_autoneg)) { | ||
513 | cvmx_mdio_phy_reg_control_t reg_control; | ||
514 | cvmx_mdio_phy_reg_status_t reg_status; | ||
515 | cvmx_mdio_phy_reg_autoneg_adver_t reg_autoneg_adver; | ||
516 | cvmx_mdio_phy_reg_control_1000_t reg_control_1000; | ||
517 | |||
518 | reg_status.u16 = | ||
519 | cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, | ||
520 | CVMX_MDIO_PHY_REG_STATUS); | ||
521 | reg_autoneg_adver.u16 = | ||
522 | cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, | ||
523 | CVMX_MDIO_PHY_REG_AUTONEG_ADVER); | ||
524 | reg_autoneg_adver.s.advert_100base_t4 = 0; | ||
525 | reg_autoneg_adver.s.advert_10base_tx_full = 0; | ||
526 | reg_autoneg_adver.s.advert_10base_tx_half = 0; | ||
527 | reg_autoneg_adver.s.advert_100base_tx_full = 0; | ||
528 | reg_autoneg_adver.s.advert_100base_tx_half = 0; | ||
529 | if (reg_status.s.capable_extended_status) { | ||
530 | reg_control_1000.u16 = | ||
531 | cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, | ||
532 | CVMX_MDIO_PHY_REG_CONTROL_1000); | ||
533 | reg_control_1000.s.advert_1000base_t_full = 0; | ||
534 | reg_control_1000.s.advert_1000base_t_half = 0; | ||
535 | } | ||
536 | switch (link_info.s.speed) { | ||
537 | case 10: | ||
538 | reg_autoneg_adver.s.advert_10base_tx_full = | ||
539 | link_info.s.full_duplex; | ||
540 | reg_autoneg_adver.s.advert_10base_tx_half = | ||
541 | !link_info.s.full_duplex; | ||
542 | break; | ||
543 | case 100: | ||
544 | reg_autoneg_adver.s.advert_100base_tx_full = | ||
545 | link_info.s.full_duplex; | ||
546 | reg_autoneg_adver.s.advert_100base_tx_half = | ||
547 | !link_info.s.full_duplex; | ||
548 | break; | ||
549 | case 1000: | ||
550 | reg_control_1000.s.advert_1000base_t_full = | ||
551 | link_info.s.full_duplex; | ||
552 | reg_control_1000.s.advert_1000base_t_half = | ||
553 | !link_info.s.full_duplex; | ||
554 | break; | ||
555 | } | ||
556 | cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, | ||
557 | CVMX_MDIO_PHY_REG_AUTONEG_ADVER, | ||
558 | reg_autoneg_adver.u16); | ||
559 | if (reg_status.s.capable_extended_status) | ||
560 | cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, | ||
561 | CVMX_MDIO_PHY_REG_CONTROL_1000, | ||
562 | reg_control_1000.u16); | ||
563 | reg_control.u16 = | ||
564 | cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, | ||
565 | CVMX_MDIO_PHY_REG_CONTROL); | ||
566 | reg_control.s.autoneg_enable = 1; | ||
567 | reg_control.s.restart_autoneg = 1; | ||
568 | cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, | ||
569 | CVMX_MDIO_PHY_REG_CONTROL, reg_control.u16); | ||
570 | } else { | ||
571 | cvmx_mdio_phy_reg_control_t reg_control; | ||
572 | reg_control.u16 = | ||
573 | cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, | ||
574 | CVMX_MDIO_PHY_REG_CONTROL); | ||
575 | reg_control.s.autoneg_enable = 0; | ||
576 | reg_control.s.restart_autoneg = 1; | ||
577 | reg_control.s.duplex = link_info.s.full_duplex; | ||
578 | if (link_info.s.speed == 1000) { | ||
579 | reg_control.s.speed_msb = 1; | ||
580 | reg_control.s.speed_lsb = 0; | ||
581 | } else if (link_info.s.speed == 100) { | ||
582 | reg_control.s.speed_msb = 0; | ||
583 | reg_control.s.speed_lsb = 1; | ||
584 | } else if (link_info.s.speed == 10) { | ||
585 | reg_control.s.speed_msb = 0; | ||
586 | reg_control.s.speed_lsb = 0; | ||
587 | } | ||
588 | cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, | ||
589 | CVMX_MDIO_PHY_REG_CONTROL, reg_control.u16); | ||
590 | } | ||
591 | return 0; | ||
592 | } | ||
593 | |||
594 | /** | ||
595 | * This function is called by cvmx_helper_interface_probe() after it | ||
596 | * determines the number of ports Octeon can support on a specific | ||
597 | * interface. This function is the per board location to override | ||
598 | * this value. It is called with the number of ports Octeon might | ||
599 | * support and should return the number of actual ports on the | ||
600 | * board. | ||
601 | * | ||
602 | * This function must be modifed for every new Octeon board. | ||
603 | * Internally it uses switch statements based on the cvmx_sysinfo | ||
604 | * data to determine board types and revisions. It relys on the | ||
605 | * fact that every Octeon board receives a unique board type | ||
606 | * enumeration from the bootloader. | ||
607 | * | ||
608 | * @interface: Interface to probe | ||
609 | * @supported_ports: | ||
610 | * Number of ports Octeon supports. | ||
611 | * | ||
612 | * Returns Number of ports the actual board supports. Many times this will | ||
613 | * simple be "support_ports". | ||
614 | */ | ||
615 | int __cvmx_helper_board_interface_probe(int interface, int supported_ports) | ||
616 | { | ||
617 | switch (cvmx_sysinfo_get()->board_type) { | ||
618 | case CVMX_BOARD_TYPE_CN3005_EVB_HS5: | ||
619 | if (interface == 0) | ||
620 | return 2; | ||
621 | break; | ||
622 | case CVMX_BOARD_TYPE_BBGW_REF: | ||
623 | if (interface == 0) | ||
624 | return 2; | ||
625 | break; | ||
626 | case CVMX_BOARD_TYPE_NIC_XLE_4G: | ||
627 | if (interface == 0) | ||
628 | return 0; | ||
629 | break; | ||
630 | /* The 2nd interface on the EBH5600 is connected to the Marvel switch, | ||
631 | which we don't support. Disable ports connected to it */ | ||
632 | case CVMX_BOARD_TYPE_EBH5600: | ||
633 | if (interface == 1) | ||
634 | return 0; | ||
635 | break; | ||
636 | } | ||
637 | return supported_ports; | ||
638 | } | ||
639 | |||
640 | /** | ||
641 | * Enable packet input/output from the hardware. This function is | ||
642 | * called after by cvmx_helper_packet_hardware_enable() to | ||
643 | * perform board specific initialization. For most boards | ||
644 | * nothing is needed. | ||
645 | * | ||
646 | * @interface: Interface to enable | ||
647 | * | ||
648 | * Returns Zero on success, negative on failure | ||
649 | */ | ||
650 | int __cvmx_helper_board_hardware_enable(int interface) | ||
651 | { | ||
652 | if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_CN3005_EVB_HS5) { | ||
653 | if (interface == 0) { | ||
654 | /* Different config for switch port */ | ||
655 | cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(1, interface), 0); | ||
656 | cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(1, interface), 0); | ||
657 | /* | ||
658 | * Boards with gigabit WAN ports need a | ||
659 | * different setting that is compatible with | ||
660 | * 100 Mbit settings | ||
661 | */ | ||
662 | cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(0, interface), | ||
663 | 0xc); | ||
664 | cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(0, interface), | ||
665 | 0xc); | ||
666 | } | ||
667 | } else if (cvmx_sysinfo_get()->board_type == | ||
668 | CVMX_BOARD_TYPE_CN3010_EVB_HS5) { | ||
669 | /* | ||
670 | * Broadcom PHYs require differnet ASX | ||
671 | * clocks. Unfortunately many boards don't define a | ||
672 | * new board Id and simply mangle the | ||
673 | * CN3010_EVB_HS5 | ||
674 | */ | ||
675 | if (interface == 0) { | ||
676 | /* | ||
677 | * Some boards use a hacked up bootloader that | ||
678 | * identifies them as CN3010_EVB_HS5 | ||
679 | * evaluation boards. This leads to all kinds | ||
680 | * of configuration problems. Detect one | ||
681 | * case, and print warning, while trying to do | ||
682 | * the right thing. | ||
683 | */ | ||
684 | int phy_addr = cvmx_helper_board_get_mii_address(0); | ||
685 | if (phy_addr != -1) { | ||
686 | int phy_identifier = | ||
687 | cvmx_mdio_read(phy_addr >> 8, | ||
688 | phy_addr & 0xff, 0x2); | ||
689 | /* Is it a Broadcom PHY? */ | ||
690 | if (phy_identifier == 0x0143) { | ||
691 | cvmx_dprintf("\n"); | ||
692 | cvmx_dprintf("ERROR:\n"); | ||
693 | cvmx_dprintf | ||
694 | ("ERROR: Board type is CVMX_BOARD_TYPE_CN3010_EVB_HS5, but Broadcom PHY found.\n"); | ||
695 | cvmx_dprintf | ||
696 | ("ERROR: The board type is mis-configured, and software malfunctions are likely.\n"); | ||
697 | cvmx_dprintf | ||
698 | ("ERROR: All boards require a unique board type to identify them.\n"); | ||
699 | cvmx_dprintf("ERROR:\n"); | ||
700 | cvmx_dprintf("\n"); | ||
701 | cvmx_wait(1000000000); | ||
702 | cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX | ||
703 | (0, interface), 5); | ||
704 | cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX | ||
705 | (0, interface), 5); | ||
706 | } | ||
707 | } | ||
708 | } | ||
709 | } | ||
710 | return 0; | ||
711 | } | ||
diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-fpa.c b/arch/mips/cavium-octeon/executive/cvmx-helper-fpa.c new file mode 100644 index 000000000000..c239e5f4ab9a --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-fpa.c | |||
@@ -0,0 +1,243 @@ | |||
1 | /***********************license start*************** | ||
2 | * Author: Cavium Networks | ||
3 | * | ||
4 | * Contact: support@caviumnetworks.com | ||
5 | * This file is part of the OCTEON SDK | ||
6 | * | ||
7 | * Copyright (c) 2003-2008 Cavium Networks | ||
8 | * | ||
9 | * This file is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License, Version 2, as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * This file is distributed in the hope that it will be useful, but | ||
14 | * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty | ||
15 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or | ||
16 | * NONINFRINGEMENT. See the GNU General Public License for more | ||
17 | * details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this file; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
22 | * or visit http://www.gnu.org/licenses/. | ||
23 | * | ||
24 | * This file may also be available under a different license from Cavium. | ||
25 | * Contact Cavium Networks for more information | ||
26 | ***********************license end**************************************/ | ||
27 | |||
28 | /** | ||
29 | * @file | ||
30 | * | ||
31 | * Helper functions for FPA setup. | ||
32 | * | ||
33 | */ | ||
34 | #include "executive-config.h" | ||
35 | #include "cvmx-config.h" | ||
36 | #include "cvmx.h" | ||
37 | #include "cvmx-bootmem.h" | ||
38 | #include "cvmx-fpa.h" | ||
39 | #include "cvmx-helper-fpa.h" | ||
40 | |||
41 | /** | ||
42 | * Allocate memory for and initialize a single FPA pool. | ||
43 | * | ||
44 | * @pool: Pool to initialize | ||
45 | * @buffer_size: Size of buffers to allocate in bytes | ||
46 | * @buffers: Number of buffers to put in the pool. Zero is allowed | ||
47 | * @name: String name of the pool for debugging purposes | ||
48 | * Returns Zero on success, non-zero on failure | ||
49 | */ | ||
50 | static int __cvmx_helper_initialize_fpa_pool(int pool, uint64_t buffer_size, | ||
51 | uint64_t buffers, const char *name) | ||
52 | { | ||
53 | uint64_t current_num; | ||
54 | void *memory; | ||
55 | uint64_t align = CVMX_CACHE_LINE_SIZE; | ||
56 | |||
57 | /* | ||
58 | * Align the allocation so that power of 2 size buffers are | ||
59 | * naturally aligned. | ||
60 | */ | ||
61 | while (align < buffer_size) | ||
62 | align = align << 1; | ||
63 | |||
64 | if (buffers == 0) | ||
65 | return 0; | ||
66 | |||
67 | current_num = cvmx_read_csr(CVMX_FPA_QUEX_AVAILABLE(pool)); | ||
68 | if (current_num) { | ||
69 | cvmx_dprintf("Fpa pool %d(%s) already has %llu buffers. " | ||
70 | "Skipping setup.\n", | ||
71 | pool, name, (unsigned long long)current_num); | ||
72 | return 0; | ||
73 | } | ||
74 | |||
75 | memory = cvmx_bootmem_alloc(buffer_size * buffers, align); | ||
76 | if (memory == NULL) { | ||
77 | cvmx_dprintf("Out of memory initializing fpa pool %d(%s).\n", | ||
78 | pool, name); | ||
79 | return -1; | ||
80 | } | ||
81 | cvmx_fpa_setup_pool(pool, name, memory, buffer_size, buffers); | ||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | /** | ||
86 | * Allocate memory and initialize the FPA pools using memory | ||
87 | * from cvmx-bootmem. Specifying zero for the number of | ||
88 | * buffers will cause that FPA pool to not be setup. This is | ||
89 | * useful if you aren't using some of the hardware and want | ||
90 | * to save memory. Use cvmx_helper_initialize_fpa instead of | ||
91 | * this function directly. | ||
92 | * | ||
93 | * @pip_pool: Should always be CVMX_FPA_PACKET_POOL | ||
94 | * @pip_size: Should always be CVMX_FPA_PACKET_POOL_SIZE | ||
95 | * @pip_buffers: | ||
96 | * Number of packet buffers. | ||
97 | * @wqe_pool: Should always be CVMX_FPA_WQE_POOL | ||
98 | * @wqe_size: Should always be CVMX_FPA_WQE_POOL_SIZE | ||
99 | * @wqe_entries: | ||
100 | * Number of work queue entries | ||
101 | * @pko_pool: Should always be CVMX_FPA_OUTPUT_BUFFER_POOL | ||
102 | * @pko_size: Should always be CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE | ||
103 | * @pko_buffers: | ||
104 | * PKO Command buffers. You should at minimum have two per | ||
105 | * each PKO queue. | ||
106 | * @tim_pool: Should always be CVMX_FPA_TIMER_POOL | ||
107 | * @tim_size: Should always be CVMX_FPA_TIMER_POOL_SIZE | ||
108 | * @tim_buffers: | ||
109 | * TIM ring buffer command queues. At least two per timer bucket | ||
110 | * is recommened. | ||
111 | * @dfa_pool: Should always be CVMX_FPA_DFA_POOL | ||
112 | * @dfa_size: Should always be CVMX_FPA_DFA_POOL_SIZE | ||
113 | * @dfa_buffers: | ||
114 | * DFA command buffer. A relatively small (32 for example) | ||
115 | * number should work. | ||
116 | * Returns Zero on success, non-zero if out of memory | ||
117 | */ | ||
118 | static int __cvmx_helper_initialize_fpa(int pip_pool, int pip_size, | ||
119 | int pip_buffers, int wqe_pool, | ||
120 | int wqe_size, int wqe_entries, | ||
121 | int pko_pool, int pko_size, | ||
122 | int pko_buffers, int tim_pool, | ||
123 | int tim_size, int tim_buffers, | ||
124 | int dfa_pool, int dfa_size, | ||
125 | int dfa_buffers) | ||
126 | { | ||
127 | int status; | ||
128 | |||
129 | cvmx_fpa_enable(); | ||
130 | |||
131 | if ((pip_buffers > 0) && (pip_buffers <= 64)) | ||
132 | cvmx_dprintf | ||
133 | ("Warning: %d packet buffers may not be enough for hardware" | ||
134 | " prefetch. 65 or more is recommended.\n", pip_buffers); | ||
135 | |||
136 | if (pip_pool >= 0) { | ||
137 | status = | ||
138 | __cvmx_helper_initialize_fpa_pool(pip_pool, pip_size, | ||
139 | pip_buffers, | ||
140 | "Packet Buffers"); | ||
141 | if (status) | ||
142 | return status; | ||
143 | } | ||
144 | |||
145 | if (wqe_pool >= 0) { | ||
146 | status = | ||
147 | __cvmx_helper_initialize_fpa_pool(wqe_pool, wqe_size, | ||
148 | wqe_entries, | ||
149 | "Work Queue Entries"); | ||
150 | if (status) | ||
151 | return status; | ||
152 | } | ||
153 | |||
154 | if (pko_pool >= 0) { | ||
155 | status = | ||
156 | __cvmx_helper_initialize_fpa_pool(pko_pool, pko_size, | ||
157 | pko_buffers, | ||
158 | "PKO Command Buffers"); | ||
159 | if (status) | ||
160 | return status; | ||
161 | } | ||
162 | |||
163 | if (tim_pool >= 0) { | ||
164 | status = | ||
165 | __cvmx_helper_initialize_fpa_pool(tim_pool, tim_size, | ||
166 | tim_buffers, | ||
167 | "TIM Command Buffers"); | ||
168 | if (status) | ||
169 | return status; | ||
170 | } | ||
171 | |||
172 | if (dfa_pool >= 0) { | ||
173 | status = | ||
174 | __cvmx_helper_initialize_fpa_pool(dfa_pool, dfa_size, | ||
175 | dfa_buffers, | ||
176 | "DFA Command Buffers"); | ||
177 | if (status) | ||
178 | return status; | ||
179 | } | ||
180 | |||
181 | return 0; | ||
182 | } | ||
183 | |||
184 | /** | ||
185 | * Allocate memory and initialize the FPA pools using memory | ||
186 | * from cvmx-bootmem. Sizes of each element in the pools is | ||
187 | * controlled by the cvmx-config.h header file. Specifying | ||
188 | * zero for any parameter will cause that FPA pool to not be | ||
189 | * setup. This is useful if you aren't using some of the | ||
190 | * hardware and want to save memory. | ||
191 | * | ||
192 | * @packet_buffers: | ||
193 | * Number of packet buffers to allocate | ||
194 | * @work_queue_entries: | ||
195 | * Number of work queue entries | ||
196 | * @pko_buffers: | ||
197 | * PKO Command buffers. You should at minimum have two per | ||
198 | * each PKO queue. | ||
199 | * @tim_buffers: | ||
200 | * TIM ring buffer command queues. At least two per timer bucket | ||
201 | * is recommened. | ||
202 | * @dfa_buffers: | ||
203 | * DFA command buffer. A relatively small (32 for example) | ||
204 | * number should work. | ||
205 | * Returns Zero on success, non-zero if out of memory | ||
206 | */ | ||
207 | int cvmx_helper_initialize_fpa(int packet_buffers, int work_queue_entries, | ||
208 | int pko_buffers, int tim_buffers, | ||
209 | int dfa_buffers) | ||
210 | { | ||
211 | #ifndef CVMX_FPA_PACKET_POOL | ||
212 | #define CVMX_FPA_PACKET_POOL -1 | ||
213 | #define CVMX_FPA_PACKET_POOL_SIZE 0 | ||
214 | #endif | ||
215 | #ifndef CVMX_FPA_WQE_POOL | ||
216 | #define CVMX_FPA_WQE_POOL -1 | ||
217 | #define CVMX_FPA_WQE_POOL_SIZE 0 | ||
218 | #endif | ||
219 | #ifndef CVMX_FPA_OUTPUT_BUFFER_POOL | ||
220 | #define CVMX_FPA_OUTPUT_BUFFER_POOL -1 | ||
221 | #define CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE 0 | ||
222 | #endif | ||
223 | #ifndef CVMX_FPA_TIMER_POOL | ||
224 | #define CVMX_FPA_TIMER_POOL -1 | ||
225 | #define CVMX_FPA_TIMER_POOL_SIZE 0 | ||
226 | #endif | ||
227 | #ifndef CVMX_FPA_DFA_POOL | ||
228 | #define CVMX_FPA_DFA_POOL -1 | ||
229 | #define CVMX_FPA_DFA_POOL_SIZE 0 | ||
230 | #endif | ||
231 | return __cvmx_helper_initialize_fpa(CVMX_FPA_PACKET_POOL, | ||
232 | CVMX_FPA_PACKET_POOL_SIZE, | ||
233 | packet_buffers, CVMX_FPA_WQE_POOL, | ||
234 | CVMX_FPA_WQE_POOL_SIZE, | ||
235 | work_queue_entries, | ||
236 | CVMX_FPA_OUTPUT_BUFFER_POOL, | ||
237 | CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, | ||
238 | pko_buffers, CVMX_FPA_TIMER_POOL, | ||
239 | CVMX_FPA_TIMER_POOL_SIZE, | ||
240 | tim_buffers, CVMX_FPA_DFA_POOL, | ||
241 | CVMX_FPA_DFA_POOL_SIZE, | ||
242 | dfa_buffers); | ||
243 | } | ||
diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-loop.c b/arch/mips/cavium-octeon/executive/cvmx-helper-loop.c new file mode 100644 index 000000000000..bfbd46115e71 --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-loop.c | |||
@@ -0,0 +1,85 @@ | |||
1 | /***********************license start*************** | ||
2 | * Author: Cavium Networks | ||
3 | * | ||
4 | * Contact: support@caviumnetworks.com | ||
5 | * This file is part of the OCTEON SDK | ||
6 | * | ||
7 | * Copyright (c) 2003-2008 Cavium Networks | ||
8 | * | ||
9 | * This file is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License, Version 2, as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * This file is distributed in the hope that it will be useful, but | ||
14 | * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty | ||
15 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or | ||
16 | * NONINFRINGEMENT. See the GNU General Public License for more | ||
17 | * details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this file; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
22 | * or visit http://www.gnu.org/licenses/. | ||
23 | * | ||
24 | * This file may also be available under a different license from Cavium. | ||
25 | * Contact Cavium Networks for more information | ||
26 | ***********************license end**************************************/ | ||
27 | |||
28 | /* | ||
29 | * Functions for LOOP initialization, configuration, | ||
30 | * and monitoring. | ||
31 | */ | ||
32 | #include <asm/octeon/octeon.h> | ||
33 | |||
34 | #include <asm/octeon/cvmx-config.h> | ||
35 | |||
36 | #include <asm/octeon/cvmx-helper.h> | ||
37 | #include <asm/octeon/cvmx-pip-defs.h> | ||
38 | |||
39 | /** | ||
40 | * Probe a LOOP interface and determine the number of ports | ||
41 | * connected to it. The LOOP interface should still be down | ||
42 | * after this call. | ||
43 | * | ||
44 | * @interface: Interface to probe | ||
45 | * | ||
46 | * Returns Number of ports on the interface. Zero to disable. | ||
47 | */ | ||
48 | int __cvmx_helper_loop_probe(int interface) | ||
49 | { | ||
50 | union cvmx_ipd_sub_port_fcs ipd_sub_port_fcs; | ||
51 | int num_ports = 4; | ||
52 | int port; | ||
53 | |||
54 | /* We need to disable length checking so packet < 64 bytes and jumbo | ||
55 | frames don't get errors */ | ||
56 | for (port = 0; port < num_ports; port++) { | ||
57 | union cvmx_pip_prt_cfgx port_cfg; | ||
58 | int ipd_port = cvmx_helper_get_ipd_port(interface, port); | ||
59 | port_cfg.u64 = cvmx_read_csr(CVMX_PIP_PRT_CFGX(ipd_port)); | ||
60 | port_cfg.s.maxerr_en = 0; | ||
61 | port_cfg.s.minerr_en = 0; | ||
62 | cvmx_write_csr(CVMX_PIP_PRT_CFGX(ipd_port), port_cfg.u64); | ||
63 | } | ||
64 | |||
65 | /* Disable FCS stripping for loopback ports */ | ||
66 | ipd_sub_port_fcs.u64 = cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS); | ||
67 | ipd_sub_port_fcs.s.port_bit2 = 0; | ||
68 | cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS, ipd_sub_port_fcs.u64); | ||
69 | return num_ports; | ||
70 | } | ||
71 | |||
72 | /** | ||
73 | * Bringup and enable a LOOP interface. After this call packet | ||
74 | * I/O should be fully functional. This is called with IPD | ||
75 | * enabled but PKO disabled. | ||
76 | * | ||
77 | * @interface: Interface to bring up | ||
78 | * | ||
79 | * Returns Zero on success, negative on failure | ||
80 | */ | ||
81 | int __cvmx_helper_loop_enable(int interface) | ||
82 | { | ||
83 | /* Do nothing. */ | ||
84 | return 0; | ||
85 | } | ||
diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-npi.c b/arch/mips/cavium-octeon/executive/cvmx-helper-npi.c new file mode 100644 index 000000000000..cc94cfa545b4 --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-npi.c | |||
@@ -0,0 +1,113 @@ | |||
1 | /***********************license start*************** | ||
2 | * Author: Cavium Networks | ||
3 | * | ||
4 | * Contact: support@caviumnetworks.com | ||
5 | * This file is part of the OCTEON SDK | ||
6 | * | ||
7 | * Copyright (c) 2003-2008 Cavium Networks | ||
8 | * | ||
9 | * This file is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License, Version 2, as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * This file is distributed in the hope that it will be useful, but | ||
14 | * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty | ||
15 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or | ||
16 | * NONINFRINGEMENT. See the GNU General Public License for more | ||
17 | * details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this file; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
22 | * or visit http://www.gnu.org/licenses/. | ||
23 | * | ||
24 | * This file may also be available under a different license from Cavium. | ||
25 | * Contact Cavium Networks for more information | ||
26 | ***********************license end**************************************/ | ||
27 | |||
28 | /* | ||
29 | * Functions for NPI initialization, configuration, | ||
30 | * and monitoring. | ||
31 | */ | ||
32 | #include <asm/octeon/octeon.h> | ||
33 | |||
34 | #include <asm/octeon/cvmx-config.h> | ||
35 | |||
36 | #include <asm/octeon/cvmx-helper.h> | ||
37 | |||
38 | #include <asm/octeon/cvmx-pip-defs.h> | ||
39 | |||
40 | /** | ||
41 | * Probe a NPI interface and determine the number of ports | ||
42 | * connected to it. The NPI interface should still be down | ||
43 | * after this call. | ||
44 | * | ||
45 | * @interface: Interface to probe | ||
46 | * | ||
47 | * Returns Number of ports on the interface. Zero to disable. | ||
48 | */ | ||
49 | int __cvmx_helper_npi_probe(int interface) | ||
50 | { | ||
51 | #if CVMX_PKO_QUEUES_PER_PORT_PCI > 0 | ||
52 | if (OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX)) | ||
53 | return 4; | ||
54 | else if (OCTEON_IS_MODEL(OCTEON_CN56XX) | ||
55 | && !OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X)) | ||
56 | /* The packet engines didn't exist before pass 2 */ | ||
57 | return 4; | ||
58 | else if (OCTEON_IS_MODEL(OCTEON_CN52XX) | ||
59 | && !OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_X)) | ||
60 | /* The packet engines didn't exist before pass 2 */ | ||
61 | return 4; | ||
62 | #if 0 | ||
63 | /* | ||
64 | * Technically CN30XX, CN31XX, and CN50XX contain packet | ||
65 | * engines, but nobody ever uses them. Since this is the case, | ||
66 | * we disable them here. | ||
67 | */ | ||
68 | else if (OCTEON_IS_MODEL(OCTEON_CN31XX) | ||
69 | || OCTEON_IS_MODEL(OCTEON_CN50XX)) | ||
70 | return 2; | ||
71 | else if (OCTEON_IS_MODEL(OCTEON_CN30XX)) | ||
72 | return 1; | ||
73 | #endif | ||
74 | #endif | ||
75 | return 0; | ||
76 | } | ||
77 | |||
78 | /** | ||
79 | * Bringup and enable a NPI interface. After this call packet | ||
80 | * I/O should be fully functional. This is called with IPD | ||
81 | * enabled but PKO disabled. | ||
82 | * | ||
83 | * @interface: Interface to bring up | ||
84 | * | ||
85 | * Returns Zero on success, negative on failure | ||
86 | */ | ||
87 | int __cvmx_helper_npi_enable(int interface) | ||
88 | { | ||
89 | /* | ||
90 | * On CN50XX, CN52XX, and CN56XX we need to disable length | ||
91 | * checking so packet < 64 bytes and jumbo frames don't get | ||
92 | * errors. | ||
93 | */ | ||
94 | if (!OCTEON_IS_MODEL(OCTEON_CN3XXX) && | ||
95 | !OCTEON_IS_MODEL(OCTEON_CN58XX)) { | ||
96 | int num_ports = cvmx_helper_ports_on_interface(interface); | ||
97 | int port; | ||
98 | for (port = 0; port < num_ports; port++) { | ||
99 | union cvmx_pip_prt_cfgx port_cfg; | ||
100 | int ipd_port = | ||
101 | cvmx_helper_get_ipd_port(interface, port); | ||
102 | port_cfg.u64 = | ||
103 | cvmx_read_csr(CVMX_PIP_PRT_CFGX(ipd_port)); | ||
104 | port_cfg.s.maxerr_en = 0; | ||
105 | port_cfg.s.minerr_en = 0; | ||
106 | cvmx_write_csr(CVMX_PIP_PRT_CFGX(ipd_port), | ||
107 | port_cfg.u64); | ||
108 | } | ||
109 | } | ||
110 | |||
111 | /* Enables are controlled by the remote host, so nothing to do here */ | ||
112 | return 0; | ||
113 | } | ||
diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-rgmii.c b/arch/mips/cavium-octeon/executive/cvmx-helper-rgmii.c new file mode 100644 index 000000000000..82b21843421c --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-rgmii.c | |||
@@ -0,0 +1,526 @@ | |||
1 | /***********************license start*************** | ||
2 | * Author: Cavium Networks | ||
3 | * | ||
4 | * Contact: support@caviumnetworks.com | ||
5 | * This file is part of the OCTEON SDK | ||
6 | * | ||
7 | * Copyright (c) 2003-2008 Cavium Networks | ||
8 | * | ||
9 | * This file is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License, Version 2, as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * This file is distributed in the hope that it will be useful, but | ||
14 | * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty | ||
15 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or | ||
16 | * NONINFRINGEMENT. See the GNU General Public License for more | ||
17 | * details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this file; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
22 | * or visit http://www.gnu.org/licenses/. | ||
23 | * | ||
24 | * This file may also be available under a different license from Cavium. | ||
25 | * Contact Cavium Networks for more information | ||
26 | ***********************license end**************************************/ | ||
27 | |||
28 | /* | ||
29 | * Functions for RGMII/GMII/MII initialization, configuration, | ||
30 | * and monitoring. | ||
31 | */ | ||
32 | #include <asm/octeon/octeon.h> | ||
33 | |||
34 | #include <asm/octeon/cvmx-config.h> | ||
35 | |||
36 | |||
37 | #include <asm/octeon/cvmx-mdio.h> | ||
38 | #include <asm/octeon/cvmx-pko.h> | ||
39 | #include <asm/octeon/cvmx-helper.h> | ||
40 | #include <asm/octeon/cvmx-helper-board.h> | ||
41 | |||
42 | #include <asm/octeon/cvmx-npi-defs.h> | ||
43 | #include <asm/octeon/cvmx-gmxx-defs.h> | ||
44 | #include <asm/octeon/cvmx-asxx-defs.h> | ||
45 | #include <asm/octeon/cvmx-dbg-defs.h> | ||
46 | |||
47 | void __cvmx_interrupt_gmxx_enable(int interface); | ||
48 | void __cvmx_interrupt_asxx_enable(int block); | ||
49 | |||
50 | /** | ||
51 | * Probe RGMII ports and determine the number present | ||
52 | * | ||
53 | * @interface: Interface to probe | ||
54 | * | ||
55 | * Returns Number of RGMII/GMII/MII ports (0-4). | ||
56 | */ | ||
57 | int __cvmx_helper_rgmii_probe(int interface) | ||
58 | { | ||
59 | int num_ports = 0; | ||
60 | union cvmx_gmxx_inf_mode mode; | ||
61 | mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface)); | ||
62 | |||
63 | if (mode.s.type) { | ||
64 | if (OCTEON_IS_MODEL(OCTEON_CN38XX) | ||
65 | || OCTEON_IS_MODEL(OCTEON_CN58XX)) { | ||
66 | cvmx_dprintf("ERROR: RGMII initialize called in " | ||
67 | "SPI interface\n"); | ||
68 | } else if (OCTEON_IS_MODEL(OCTEON_CN31XX) | ||
69 | || OCTEON_IS_MODEL(OCTEON_CN30XX) | ||
70 | || OCTEON_IS_MODEL(OCTEON_CN50XX)) { | ||
71 | /* | ||
72 | * On these chips "type" says we're in | ||
73 | * GMII/MII mode. This limits us to 2 ports | ||
74 | */ | ||
75 | num_ports = 2; | ||
76 | } else { | ||
77 | cvmx_dprintf("ERROR: Unsupported Octeon model in %s\n", | ||
78 | __func__); | ||
79 | } | ||
80 | } else { | ||
81 | if (OCTEON_IS_MODEL(OCTEON_CN38XX) | ||
82 | || OCTEON_IS_MODEL(OCTEON_CN58XX)) { | ||
83 | num_ports = 4; | ||
84 | } else if (OCTEON_IS_MODEL(OCTEON_CN31XX) | ||
85 | || OCTEON_IS_MODEL(OCTEON_CN30XX) | ||
86 | || OCTEON_IS_MODEL(OCTEON_CN50XX)) { | ||
87 | num_ports = 3; | ||
88 | } else { | ||
89 | cvmx_dprintf("ERROR: Unsupported Octeon model in %s\n", | ||
90 | __func__); | ||
91 | } | ||
92 | } | ||
93 | return num_ports; | ||
94 | } | ||
95 | |||
96 | /** | ||
97 | * Put an RGMII interface in loopback mode. Internal packets sent | ||
98 | * out will be received back again on the same port. Externally | ||
99 | * received packets will echo back out. | ||
100 | * | ||
101 | * @port: IPD port number to loop. | ||
102 | */ | ||
103 | void cvmx_helper_rgmii_internal_loopback(int port) | ||
104 | { | ||
105 | int interface = (port >> 4) & 1; | ||
106 | int index = port & 0xf; | ||
107 | uint64_t tmp; | ||
108 | |||
109 | union cvmx_gmxx_prtx_cfg gmx_cfg; | ||
110 | gmx_cfg.u64 = 0; | ||
111 | gmx_cfg.s.duplex = 1; | ||
112 | gmx_cfg.s.slottime = 1; | ||
113 | gmx_cfg.s.speed = 1; | ||
114 | cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 1); | ||
115 | cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 0x200); | ||
116 | cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0x2000); | ||
117 | cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64); | ||
118 | tmp = cvmx_read_csr(CVMX_ASXX_PRT_LOOP(interface)); | ||
119 | cvmx_write_csr(CVMX_ASXX_PRT_LOOP(interface), (1 << index) | tmp); | ||
120 | tmp = cvmx_read_csr(CVMX_ASXX_TX_PRT_EN(interface)); | ||
121 | cvmx_write_csr(CVMX_ASXX_TX_PRT_EN(interface), (1 << index) | tmp); | ||
122 | tmp = cvmx_read_csr(CVMX_ASXX_RX_PRT_EN(interface)); | ||
123 | cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(interface), (1 << index) | tmp); | ||
124 | gmx_cfg.s.en = 1; | ||
125 | cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64); | ||
126 | } | ||
127 | |||
128 | /** | ||
129 | * Workaround ASX setup errata with CN38XX pass1 | ||
130 | * | ||
131 | * @interface: Interface to setup | ||
132 | * @port: Port to setup (0..3) | ||
133 | * @cpu_clock_hz: | ||
134 | * Chip frequency in Hertz | ||
135 | * | ||
136 | * Returns Zero on success, negative on failure | ||
137 | */ | ||
138 | static int __cvmx_helper_errata_asx_pass1(int interface, int port, | ||
139 | int cpu_clock_hz) | ||
140 | { | ||
141 | /* Set hi water mark as per errata GMX-4 */ | ||
142 | if (cpu_clock_hz >= 325000000 && cpu_clock_hz < 375000000) | ||
143 | cvmx_write_csr(CVMX_ASXX_TX_HI_WATERX(port, interface), 12); | ||
144 | else if (cpu_clock_hz >= 375000000 && cpu_clock_hz < 437000000) | ||
145 | cvmx_write_csr(CVMX_ASXX_TX_HI_WATERX(port, interface), 11); | ||
146 | else if (cpu_clock_hz >= 437000000 && cpu_clock_hz < 550000000) | ||
147 | cvmx_write_csr(CVMX_ASXX_TX_HI_WATERX(port, interface), 10); | ||
148 | else if (cpu_clock_hz >= 550000000 && cpu_clock_hz < 687000000) | ||
149 | cvmx_write_csr(CVMX_ASXX_TX_HI_WATERX(port, interface), 9); | ||
150 | else | ||
151 | cvmx_dprintf("Illegal clock frequency (%d). " | ||
152 | "CVMX_ASXX_TX_HI_WATERX not set\n", cpu_clock_hz); | ||
153 | return 0; | ||
154 | } | ||
155 | |||
156 | /** | ||
157 | * Configure all of the ASX, GMX, and PKO regsiters required | ||
158 | * to get RGMII to function on the supplied interface. | ||
159 | * | ||
160 | * @interface: PKO Interface to configure (0 or 1) | ||
161 | * | ||
162 | * Returns Zero on success | ||
163 | */ | ||
164 | int __cvmx_helper_rgmii_enable(int interface) | ||
165 | { | ||
166 | int num_ports = cvmx_helper_ports_on_interface(interface); | ||
167 | int port; | ||
168 | struct cvmx_sysinfo *sys_info_ptr = cvmx_sysinfo_get(); | ||
169 | union cvmx_gmxx_inf_mode mode; | ||
170 | union cvmx_asxx_tx_prt_en asx_tx; | ||
171 | union cvmx_asxx_rx_prt_en asx_rx; | ||
172 | |||
173 | mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface)); | ||
174 | |||
175 | if (mode.s.en == 0) | ||
176 | return -1; | ||
177 | if ((OCTEON_IS_MODEL(OCTEON_CN38XX) || | ||
178 | OCTEON_IS_MODEL(OCTEON_CN58XX)) && mode.s.type == 1) | ||
179 | /* Ignore SPI interfaces */ | ||
180 | return -1; | ||
181 | |||
182 | /* Configure the ASX registers needed to use the RGMII ports */ | ||
183 | asx_tx.u64 = 0; | ||
184 | asx_tx.s.prt_en = cvmx_build_mask(num_ports); | ||
185 | cvmx_write_csr(CVMX_ASXX_TX_PRT_EN(interface), asx_tx.u64); | ||
186 | |||
187 | asx_rx.u64 = 0; | ||
188 | asx_rx.s.prt_en = cvmx_build_mask(num_ports); | ||
189 | cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(interface), asx_rx.u64); | ||
190 | |||
191 | /* Configure the GMX registers needed to use the RGMII ports */ | ||
192 | for (port = 0; port < num_ports; port++) { | ||
193 | /* Setting of CVMX_GMXX_TXX_THRESH has been moved to | ||
194 | __cvmx_helper_setup_gmx() */ | ||
195 | |||
196 | if (cvmx_octeon_is_pass1()) | ||
197 | __cvmx_helper_errata_asx_pass1(interface, port, | ||
198 | sys_info_ptr-> | ||
199 | cpu_clock_hz); | ||
200 | else { | ||
201 | /* | ||
202 | * Configure more flexible RGMII preamble | ||
203 | * checking. Pass 1 doesn't support this | ||
204 | * feature. | ||
205 | */ | ||
206 | union cvmx_gmxx_rxx_frm_ctl frm_ctl; | ||
207 | frm_ctl.u64 = | ||
208 | cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL | ||
209 | (port, interface)); | ||
210 | /* New field, so must be compile time */ | ||
211 | frm_ctl.s.pre_free = 1; | ||
212 | cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(port, interface), | ||
213 | frm_ctl.u64); | ||
214 | } | ||
215 | |||
216 | /* | ||
217 | * Each pause frame transmitted will ask for about 10M | ||
218 | * bit times before resume. If buffer space comes | ||
219 | * available before that time has expired, an XON | ||
220 | * pause frame (0 time) will be transmitted to restart | ||
221 | * the flow. | ||
222 | */ | ||
223 | cvmx_write_csr(CVMX_GMXX_TXX_PAUSE_PKT_TIME(port, interface), | ||
224 | 20000); | ||
225 | cvmx_write_csr(CVMX_GMXX_TXX_PAUSE_PKT_INTERVAL | ||
226 | (port, interface), 19000); | ||
227 | |||
228 | if (OCTEON_IS_MODEL(OCTEON_CN50XX)) { | ||
229 | cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(port, interface), | ||
230 | 16); | ||
231 | cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(port, interface), | ||
232 | 16); | ||
233 | } else { | ||
234 | cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(port, interface), | ||
235 | 24); | ||
236 | cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(port, interface), | ||
237 | 24); | ||
238 | } | ||
239 | } | ||
240 | |||
241 | __cvmx_helper_setup_gmx(interface, num_ports); | ||
242 | |||
243 | /* enable the ports now */ | ||
244 | for (port = 0; port < num_ports; port++) { | ||
245 | union cvmx_gmxx_prtx_cfg gmx_cfg; | ||
246 | cvmx_helper_link_autoconf(cvmx_helper_get_ipd_port | ||
247 | (interface, port)); | ||
248 | gmx_cfg.u64 = | ||
249 | cvmx_read_csr(CVMX_GMXX_PRTX_CFG(port, interface)); | ||
250 | gmx_cfg.s.en = 1; | ||
251 | cvmx_write_csr(CVMX_GMXX_PRTX_CFG(port, interface), | ||
252 | gmx_cfg.u64); | ||
253 | } | ||
254 | __cvmx_interrupt_asxx_enable(interface); | ||
255 | __cvmx_interrupt_gmxx_enable(interface); | ||
256 | |||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | /** | ||
261 | * Return the link state of an IPD/PKO port as returned by | ||
262 | * auto negotiation. The result of this function may not match | ||
263 | * Octeon's link config if auto negotiation has changed since | ||
264 | * the last call to cvmx_helper_link_set(). | ||
265 | * | ||
266 | * @ipd_port: IPD/PKO port to query | ||
267 | * | ||
268 | * Returns Link state | ||
269 | */ | ||
270 | cvmx_helper_link_info_t __cvmx_helper_rgmii_link_get(int ipd_port) | ||
271 | { | ||
272 | int interface = cvmx_helper_get_interface_num(ipd_port); | ||
273 | int index = cvmx_helper_get_interface_index_num(ipd_port); | ||
274 | union cvmx_asxx_prt_loop asxx_prt_loop; | ||
275 | |||
276 | asxx_prt_loop.u64 = cvmx_read_csr(CVMX_ASXX_PRT_LOOP(interface)); | ||
277 | if (asxx_prt_loop.s.int_loop & (1 << index)) { | ||
278 | /* Force 1Gbps full duplex on internal loopback */ | ||
279 | cvmx_helper_link_info_t result; | ||
280 | result.u64 = 0; | ||
281 | result.s.full_duplex = 1; | ||
282 | result.s.link_up = 1; | ||
283 | result.s.speed = 1000; | ||
284 | return result; | ||
285 | } else | ||
286 | return __cvmx_helper_board_link_get(ipd_port); | ||
287 | } | ||
288 | |||
289 | /** | ||
290 | * Configure an IPD/PKO port for the specified link state. This | ||
291 | * function does not influence auto negotiation at the PHY level. | ||
292 | * The passed link state must always match the link state returned | ||
293 | * by cvmx_helper_link_get(). It is normally best to use | ||
294 | * cvmx_helper_link_autoconf() instead. | ||
295 | * | ||
296 | * @ipd_port: IPD/PKO port to configure | ||
297 | * @link_info: The new link state | ||
298 | * | ||
299 | * Returns Zero on success, negative on failure | ||
300 | */ | ||
301 | int __cvmx_helper_rgmii_link_set(int ipd_port, | ||
302 | cvmx_helper_link_info_t link_info) | ||
303 | { | ||
304 | int result = 0; | ||
305 | int interface = cvmx_helper_get_interface_num(ipd_port); | ||
306 | int index = cvmx_helper_get_interface_index_num(ipd_port); | ||
307 | union cvmx_gmxx_prtx_cfg original_gmx_cfg; | ||
308 | union cvmx_gmxx_prtx_cfg new_gmx_cfg; | ||
309 | union cvmx_pko_mem_queue_qos pko_mem_queue_qos; | ||
310 | union cvmx_pko_mem_queue_qos pko_mem_queue_qos_save[16]; | ||
311 | union cvmx_gmxx_tx_ovr_bp gmx_tx_ovr_bp; | ||
312 | union cvmx_gmxx_tx_ovr_bp gmx_tx_ovr_bp_save; | ||
313 | int i; | ||
314 | |||
315 | /* Ignore speed sets in the simulator */ | ||
316 | if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM) | ||
317 | return 0; | ||
318 | |||
319 | /* Read the current settings so we know the current enable state */ | ||
320 | original_gmx_cfg.u64 = | ||
321 | cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); | ||
322 | new_gmx_cfg = original_gmx_cfg; | ||
323 | |||
324 | /* Disable the lowest level RX */ | ||
325 | cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(interface), | ||
326 | cvmx_read_csr(CVMX_ASXX_RX_PRT_EN(interface)) & | ||
327 | ~(1 << index)); | ||
328 | |||
329 | memset(pko_mem_queue_qos_save, 0, sizeof(pko_mem_queue_qos_save)); | ||
330 | /* Disable all queues so that TX should become idle */ | ||
331 | for (i = 0; i < cvmx_pko_get_num_queues(ipd_port); i++) { | ||
332 | int queue = cvmx_pko_get_base_queue(ipd_port) + i; | ||
333 | cvmx_write_csr(CVMX_PKO_REG_READ_IDX, queue); | ||
334 | pko_mem_queue_qos.u64 = cvmx_read_csr(CVMX_PKO_MEM_QUEUE_QOS); | ||
335 | pko_mem_queue_qos.s.pid = ipd_port; | ||
336 | pko_mem_queue_qos.s.qid = queue; | ||
337 | pko_mem_queue_qos_save[i] = pko_mem_queue_qos; | ||
338 | pko_mem_queue_qos.s.qos_mask = 0; | ||
339 | cvmx_write_csr(CVMX_PKO_MEM_QUEUE_QOS, pko_mem_queue_qos.u64); | ||
340 | } | ||
341 | |||
342 | /* Disable backpressure */ | ||
343 | gmx_tx_ovr_bp.u64 = cvmx_read_csr(CVMX_GMXX_TX_OVR_BP(interface)); | ||
344 | gmx_tx_ovr_bp_save = gmx_tx_ovr_bp; | ||
345 | gmx_tx_ovr_bp.s.bp &= ~(1 << index); | ||
346 | gmx_tx_ovr_bp.s.en |= 1 << index; | ||
347 | cvmx_write_csr(CVMX_GMXX_TX_OVR_BP(interface), gmx_tx_ovr_bp.u64); | ||
348 | cvmx_read_csr(CVMX_GMXX_TX_OVR_BP(interface)); | ||
349 | |||
350 | /* | ||
351 | * Poll the GMX state machine waiting for it to become | ||
352 | * idle. Preferably we should only change speed when it is | ||
353 | * idle. If it doesn't become idle we will still do the speed | ||
354 | * change, but there is a slight chance that GMX will | ||
355 | * lockup. | ||
356 | */ | ||
357 | cvmx_write_csr(CVMX_NPI_DBG_SELECT, | ||
358 | interface * 0x800 + index * 0x100 + 0x880); | ||
359 | CVMX_WAIT_FOR_FIELD64(CVMX_DBG_DATA, union cvmx_dbg_data, data & 7, | ||
360 | ==, 0, 10000); | ||
361 | CVMX_WAIT_FOR_FIELD64(CVMX_DBG_DATA, union cvmx_dbg_data, data & 0xf, | ||
362 | ==, 0, 10000); | ||
363 | |||
364 | /* Disable the port before we make any changes */ | ||
365 | new_gmx_cfg.s.en = 0; | ||
366 | cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), new_gmx_cfg.u64); | ||
367 | cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); | ||
368 | |||
369 | /* Set full/half duplex */ | ||
370 | if (cvmx_octeon_is_pass1()) | ||
371 | /* Half duplex is broken for 38XX Pass 1 */ | ||
372 | new_gmx_cfg.s.duplex = 1; | ||
373 | else if (!link_info.s.link_up) | ||
374 | /* Force full duplex on down links */ | ||
375 | new_gmx_cfg.s.duplex = 1; | ||
376 | else | ||
377 | new_gmx_cfg.s.duplex = link_info.s.full_duplex; | ||
378 | |||
379 | /* Set the link speed. Anything unknown is set to 1Gbps */ | ||
380 | if (link_info.s.speed == 10) { | ||
381 | new_gmx_cfg.s.slottime = 0; | ||
382 | new_gmx_cfg.s.speed = 0; | ||
383 | } else if (link_info.s.speed == 100) { | ||
384 | new_gmx_cfg.s.slottime = 0; | ||
385 | new_gmx_cfg.s.speed = 0; | ||
386 | } else { | ||
387 | new_gmx_cfg.s.slottime = 1; | ||
388 | new_gmx_cfg.s.speed = 1; | ||
389 | } | ||
390 | |||
391 | /* Adjust the clocks */ | ||
392 | if (link_info.s.speed == 10) { | ||
393 | cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 50); | ||
394 | cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 0x40); | ||
395 | cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0); | ||
396 | } else if (link_info.s.speed == 100) { | ||
397 | cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 5); | ||
398 | cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 0x40); | ||
399 | cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0); | ||
400 | } else { | ||
401 | cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 1); | ||
402 | cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 0x200); | ||
403 | cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0x2000); | ||
404 | } | ||
405 | |||
406 | if (OCTEON_IS_MODEL(OCTEON_CN30XX) || OCTEON_IS_MODEL(OCTEON_CN50XX)) { | ||
407 | if ((link_info.s.speed == 10) || (link_info.s.speed == 100)) { | ||
408 | union cvmx_gmxx_inf_mode mode; | ||
409 | mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface)); | ||
410 | |||
411 | /* | ||
412 | * Port .en .type .p0mii Configuration | ||
413 | * ---- --- ----- ------ ----------------------------------------- | ||
414 | * X 0 X X All links are disabled. | ||
415 | * 0 1 X 0 Port 0 is RGMII | ||
416 | * 0 1 X 1 Port 0 is MII | ||
417 | * 1 1 0 X Ports 1 and 2 are configured as RGMII ports. | ||
418 | * 1 1 1 X Port 1: GMII/MII; Port 2: disabled. GMII or | ||
419 | * MII port is selected by GMX_PRT1_CFG[SPEED]. | ||
420 | */ | ||
421 | |||
422 | /* In MII mode, CLK_CNT = 1. */ | ||
423 | if (((index == 0) && (mode.s.p0mii == 1)) | ||
424 | || ((index != 0) && (mode.s.type == 1))) { | ||
425 | cvmx_write_csr(CVMX_GMXX_TXX_CLK | ||
426 | (index, interface), 1); | ||
427 | } | ||
428 | } | ||
429 | } | ||
430 | |||
431 | /* Do a read to make sure all setup stuff is complete */ | ||
432 | cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); | ||
433 | |||
434 | /* Save the new GMX setting without enabling the port */ | ||
435 | cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), new_gmx_cfg.u64); | ||
436 | |||
437 | /* Enable the lowest level RX */ | ||
438 | cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(interface), | ||
439 | cvmx_read_csr(CVMX_ASXX_RX_PRT_EN(interface)) | (1 << | ||
440 | index)); | ||
441 | |||
442 | /* Re-enable the TX path */ | ||
443 | for (i = 0; i < cvmx_pko_get_num_queues(ipd_port); i++) { | ||
444 | int queue = cvmx_pko_get_base_queue(ipd_port) + i; | ||
445 | cvmx_write_csr(CVMX_PKO_REG_READ_IDX, queue); | ||
446 | cvmx_write_csr(CVMX_PKO_MEM_QUEUE_QOS, | ||
447 | pko_mem_queue_qos_save[i].u64); | ||
448 | } | ||
449 | |||
450 | /* Restore backpressure */ | ||
451 | cvmx_write_csr(CVMX_GMXX_TX_OVR_BP(interface), gmx_tx_ovr_bp_save.u64); | ||
452 | |||
453 | /* Restore the GMX enable state. Port config is complete */ | ||
454 | new_gmx_cfg.s.en = original_gmx_cfg.s.en; | ||
455 | cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), new_gmx_cfg.u64); | ||
456 | |||
457 | return result; | ||
458 | } | ||
459 | |||
460 | /** | ||
461 | * Configure a port for internal and/or external loopback. Internal loopback | ||
462 | * causes packets sent by the port to be received by Octeon. External loopback | ||
463 | * causes packets received from the wire to sent out again. | ||
464 | * | ||
465 | * @ipd_port: IPD/PKO port to loopback. | ||
466 | * @enable_internal: | ||
467 | * Non zero if you want internal loopback | ||
468 | * @enable_external: | ||
469 | * Non zero if you want external loopback | ||
470 | * | ||
471 | * Returns Zero on success, negative on failure. | ||
472 | */ | ||
473 | int __cvmx_helper_rgmii_configure_loopback(int ipd_port, int enable_internal, | ||
474 | int enable_external) | ||
475 | { | ||
476 | int interface = cvmx_helper_get_interface_num(ipd_port); | ||
477 | int index = cvmx_helper_get_interface_index_num(ipd_port); | ||
478 | int original_enable; | ||
479 | union cvmx_gmxx_prtx_cfg gmx_cfg; | ||
480 | union cvmx_asxx_prt_loop asxx_prt_loop; | ||
481 | |||
482 | /* Read the current enable state and save it */ | ||
483 | gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); | ||
484 | original_enable = gmx_cfg.s.en; | ||
485 | /* Force port to be disabled */ | ||
486 | gmx_cfg.s.en = 0; | ||
487 | if (enable_internal) { | ||
488 | /* Force speed if we're doing internal loopback */ | ||
489 | gmx_cfg.s.duplex = 1; | ||
490 | gmx_cfg.s.slottime = 1; | ||
491 | gmx_cfg.s.speed = 1; | ||
492 | cvmx_write_csr(CVMX_GMXX_TXX_CLK(index, interface), 1); | ||
493 | cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 0x200); | ||
494 | cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0x2000); | ||
495 | } | ||
496 | cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64); | ||
497 | |||
498 | /* Set the loopback bits */ | ||
499 | asxx_prt_loop.u64 = cvmx_read_csr(CVMX_ASXX_PRT_LOOP(interface)); | ||
500 | if (enable_internal) | ||
501 | asxx_prt_loop.s.int_loop |= 1 << index; | ||
502 | else | ||
503 | asxx_prt_loop.s.int_loop &= ~(1 << index); | ||
504 | if (enable_external) | ||
505 | asxx_prt_loop.s.ext_loop |= 1 << index; | ||
506 | else | ||
507 | asxx_prt_loop.s.ext_loop &= ~(1 << index); | ||
508 | cvmx_write_csr(CVMX_ASXX_PRT_LOOP(interface), asxx_prt_loop.u64); | ||
509 | |||
510 | /* Force enables in internal loopback */ | ||
511 | if (enable_internal) { | ||
512 | uint64_t tmp; | ||
513 | tmp = cvmx_read_csr(CVMX_ASXX_TX_PRT_EN(interface)); | ||
514 | cvmx_write_csr(CVMX_ASXX_TX_PRT_EN(interface), | ||
515 | (1 << index) | tmp); | ||
516 | tmp = cvmx_read_csr(CVMX_ASXX_RX_PRT_EN(interface)); | ||
517 | cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(interface), | ||
518 | (1 << index) | tmp); | ||
519 | original_enable = 1; | ||
520 | } | ||
521 | |||
522 | /* Restore the enable state */ | ||
523 | gmx_cfg.s.en = original_enable; | ||
524 | cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64); | ||
525 | return 0; | ||
526 | } | ||
diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-sgmii.c b/arch/mips/cavium-octeon/executive/cvmx-helper-sgmii.c new file mode 100644 index 000000000000..0c0bf5d30e70 --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-sgmii.c | |||
@@ -0,0 +1,554 @@ | |||
1 | /***********************license start*************** | ||
2 | * Author: Cavium Networks | ||
3 | * | ||
4 | * Contact: support@caviumnetworks.com | ||
5 | * This file is part of the OCTEON SDK | ||
6 | * | ||
7 | * Copyright (c) 2003-2008 Cavium Networks | ||
8 | * | ||
9 | * This file is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License, Version 2, as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * This file is distributed in the hope that it will be useful, but | ||
14 | * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty | ||
15 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or | ||
16 | * NONINFRINGEMENT. See the GNU General Public License for more | ||
17 | * details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this file; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
22 | * or visit http://www.gnu.org/licenses/. | ||
23 | * | ||
24 | * This file may also be available under a different license from Cavium. | ||
25 | * Contact Cavium Networks for more information | ||
26 | ***********************license end**************************************/ | ||
27 | |||
28 | /* | ||
29 | * Functions for SGMII initialization, configuration, | ||
30 | * and monitoring. | ||
31 | */ | ||
32 | |||
33 | #include <asm/octeon/octeon.h> | ||
34 | |||
35 | #include <asm/octeon/cvmx-config.h> | ||
36 | |||
37 | #include <asm/octeon/cvmx-mdio.h> | ||
38 | #include <asm/octeon/cvmx-helper.h> | ||
39 | #include <asm/octeon/cvmx-helper-board.h> | ||
40 | |||
41 | #include <asm/octeon/cvmx-gmxx-defs.h> | ||
42 | #include <asm/octeon/cvmx-pcsx-defs.h> | ||
43 | |||
44 | void __cvmx_interrupt_gmxx_enable(int interface); | ||
45 | void __cvmx_interrupt_pcsx_intx_en_reg_enable(int index, int block); | ||
46 | void __cvmx_interrupt_pcsxx_int_en_reg_enable(int index); | ||
47 | |||
48 | /** | ||
49 | * Perform initialization required only once for an SGMII port. | ||
50 | * | ||
51 | * @interface: Interface to init | ||
52 | * @index: Index of prot on the interface | ||
53 | * | ||
54 | * Returns Zero on success, negative on failure | ||
55 | */ | ||
56 | static int __cvmx_helper_sgmii_hardware_init_one_time(int interface, int index) | ||
57 | { | ||
58 | const uint64_t clock_mhz = cvmx_sysinfo_get()->cpu_clock_hz / 1000000; | ||
59 | union cvmx_pcsx_miscx_ctl_reg pcs_misc_ctl_reg; | ||
60 | union cvmx_pcsx_linkx_timer_count_reg pcsx_linkx_timer_count_reg; | ||
61 | union cvmx_gmxx_prtx_cfg gmxx_prtx_cfg; | ||
62 | |||
63 | /* Disable GMX */ | ||
64 | gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); | ||
65 | gmxx_prtx_cfg.s.en = 0; | ||
66 | cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64); | ||
67 | |||
68 | /* | ||
69 | * Write PCS*_LINK*_TIMER_COUNT_REG[COUNT] with the | ||
70 | * appropriate value. 1000BASE-X specifies a 10ms | ||
71 | * interval. SGMII specifies a 1.6ms interval. | ||
72 | */ | ||
73 | pcs_misc_ctl_reg.u64 = | ||
74 | cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface)); | ||
75 | pcsx_linkx_timer_count_reg.u64 = | ||
76 | cvmx_read_csr(CVMX_PCSX_LINKX_TIMER_COUNT_REG(index, interface)); | ||
77 | if (pcs_misc_ctl_reg.s.mode) { | ||
78 | /* 1000BASE-X */ | ||
79 | pcsx_linkx_timer_count_reg.s.count = | ||
80 | (10000ull * clock_mhz) >> 10; | ||
81 | } else { | ||
82 | /* SGMII */ | ||
83 | pcsx_linkx_timer_count_reg.s.count = | ||
84 | (1600ull * clock_mhz) >> 10; | ||
85 | } | ||
86 | cvmx_write_csr(CVMX_PCSX_LINKX_TIMER_COUNT_REG(index, interface), | ||
87 | pcsx_linkx_timer_count_reg.u64); | ||
88 | |||
89 | /* | ||
90 | * Write the advertisement register to be used as the | ||
91 | * tx_Config_Reg<D15:D0> of the autonegotiation. In | ||
92 | * 1000BASE-X mode, tx_Config_Reg<D15:D0> is PCS*_AN*_ADV_REG. | ||
93 | * In SGMII PHY mode, tx_Config_Reg<D15:D0> is | ||
94 | * PCS*_SGM*_AN_ADV_REG. In SGMII MAC mode, | ||
95 | * tx_Config_Reg<D15:D0> is the fixed value 0x4001, so this | ||
96 | * step can be skipped. | ||
97 | */ | ||
98 | if (pcs_misc_ctl_reg.s.mode) { | ||
99 | /* 1000BASE-X */ | ||
100 | union cvmx_pcsx_anx_adv_reg pcsx_anx_adv_reg; | ||
101 | pcsx_anx_adv_reg.u64 = | ||
102 | cvmx_read_csr(CVMX_PCSX_ANX_ADV_REG(index, interface)); | ||
103 | pcsx_anx_adv_reg.s.rem_flt = 0; | ||
104 | pcsx_anx_adv_reg.s.pause = 3; | ||
105 | pcsx_anx_adv_reg.s.hfd = 1; | ||
106 | pcsx_anx_adv_reg.s.fd = 1; | ||
107 | cvmx_write_csr(CVMX_PCSX_ANX_ADV_REG(index, interface), | ||
108 | pcsx_anx_adv_reg.u64); | ||
109 | } else { | ||
110 | union cvmx_pcsx_miscx_ctl_reg pcsx_miscx_ctl_reg; | ||
111 | pcsx_miscx_ctl_reg.u64 = | ||
112 | cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface)); | ||
113 | if (pcsx_miscx_ctl_reg.s.mac_phy) { | ||
114 | /* PHY Mode */ | ||
115 | union cvmx_pcsx_sgmx_an_adv_reg pcsx_sgmx_an_adv_reg; | ||
116 | pcsx_sgmx_an_adv_reg.u64 = | ||
117 | cvmx_read_csr(CVMX_PCSX_SGMX_AN_ADV_REG | ||
118 | (index, interface)); | ||
119 | pcsx_sgmx_an_adv_reg.s.link = 1; | ||
120 | pcsx_sgmx_an_adv_reg.s.dup = 1; | ||
121 | pcsx_sgmx_an_adv_reg.s.speed = 2; | ||
122 | cvmx_write_csr(CVMX_PCSX_SGMX_AN_ADV_REG | ||
123 | (index, interface), | ||
124 | pcsx_sgmx_an_adv_reg.u64); | ||
125 | } else { | ||
126 | /* MAC Mode - Nothing to do */ | ||
127 | } | ||
128 | } | ||
129 | return 0; | ||
130 | } | ||
131 | |||
132 | /** | ||
133 | * Initialize the SERTES link for the first time or after a loss | ||
134 | * of link. | ||
135 | * | ||
136 | * @interface: Interface to init | ||
137 | * @index: Index of prot on the interface | ||
138 | * | ||
139 | * Returns Zero on success, negative on failure | ||
140 | */ | ||
141 | static int __cvmx_helper_sgmii_hardware_init_link(int interface, int index) | ||
142 | { | ||
143 | union cvmx_pcsx_mrx_control_reg control_reg; | ||
144 | |||
145 | /* | ||
146 | * Take PCS through a reset sequence. | ||
147 | * PCS*_MR*_CONTROL_REG[PWR_DN] should be cleared to zero. | ||
148 | * Write PCS*_MR*_CONTROL_REG[RESET]=1 (while not changing the | ||
149 | * value of the other PCS*_MR*_CONTROL_REG bits). Read | ||
150 | * PCS*_MR*_CONTROL_REG[RESET] until it changes value to | ||
151 | * zero. | ||
152 | */ | ||
153 | control_reg.u64 = | ||
154 | cvmx_read_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface)); | ||
155 | if (cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) { | ||
156 | control_reg.s.reset = 1; | ||
157 | cvmx_write_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface), | ||
158 | control_reg.u64); | ||
159 | if (CVMX_WAIT_FOR_FIELD64 | ||
160 | (CVMX_PCSX_MRX_CONTROL_REG(index, interface), | ||
161 | union cvmx_pcsx_mrx_control_reg, reset, ==, 0, 10000)) { | ||
162 | cvmx_dprintf("SGMII%d: Timeout waiting for port %d " | ||
163 | "to finish reset\n", | ||
164 | interface, index); | ||
165 | return -1; | ||
166 | } | ||
167 | } | ||
168 | |||
169 | /* | ||
170 | * Write PCS*_MR*_CONTROL_REG[RST_AN]=1 to ensure a fresh | ||
171 | * sgmii negotiation starts. | ||
172 | */ | ||
173 | control_reg.s.rst_an = 1; | ||
174 | control_reg.s.an_en = 1; | ||
175 | control_reg.s.pwr_dn = 0; | ||
176 | cvmx_write_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface), | ||
177 | control_reg.u64); | ||
178 | |||
179 | /* | ||
180 | * Wait for PCS*_MR*_STATUS_REG[AN_CPT] to be set, indicating | ||
181 | * that sgmii autonegotiation is complete. In MAC mode this | ||
182 | * isn't an ethernet link, but a link between Octeon and the | ||
183 | * PHY. | ||
184 | */ | ||
185 | if ((cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) && | ||
186 | CVMX_WAIT_FOR_FIELD64(CVMX_PCSX_MRX_STATUS_REG(index, interface), | ||
187 | union cvmx_pcsx_mrx_status_reg, an_cpt, ==, 1, | ||
188 | 10000)) { | ||
189 | /* cvmx_dprintf("SGMII%d: Port %d link timeout\n", interface, index); */ | ||
190 | return -1; | ||
191 | } | ||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | /** | ||
196 | * Configure an SGMII link to the specified speed after the SERTES | ||
197 | * link is up. | ||
198 | * | ||
199 | * @interface: Interface to init | ||
200 | * @index: Index of prot on the interface | ||
201 | * @link_info: Link state to configure | ||
202 | * | ||
203 | * Returns Zero on success, negative on failure | ||
204 | */ | ||
205 | static int __cvmx_helper_sgmii_hardware_init_link_speed(int interface, | ||
206 | int index, | ||
207 | cvmx_helper_link_info_t | ||
208 | link_info) | ||
209 | { | ||
210 | int is_enabled; | ||
211 | union cvmx_gmxx_prtx_cfg gmxx_prtx_cfg; | ||
212 | union cvmx_pcsx_miscx_ctl_reg pcsx_miscx_ctl_reg; | ||
213 | |||
214 | /* Disable GMX before we make any changes. Remember the enable state */ | ||
215 | gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); | ||
216 | is_enabled = gmxx_prtx_cfg.s.en; | ||
217 | gmxx_prtx_cfg.s.en = 0; | ||
218 | cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64); | ||
219 | |||
220 | /* Wait for GMX to be idle */ | ||
221 | if (CVMX_WAIT_FOR_FIELD64 | ||
222 | (CVMX_GMXX_PRTX_CFG(index, interface), union cvmx_gmxx_prtx_cfg, | ||
223 | rx_idle, ==, 1, 10000) | ||
224 | || CVMX_WAIT_FOR_FIELD64(CVMX_GMXX_PRTX_CFG(index, interface), | ||
225 | union cvmx_gmxx_prtx_cfg, tx_idle, ==, 1, | ||
226 | 10000)) { | ||
227 | cvmx_dprintf | ||
228 | ("SGMII%d: Timeout waiting for port %d to be idle\n", | ||
229 | interface, index); | ||
230 | return -1; | ||
231 | } | ||
232 | |||
233 | /* Read GMX CFG again to make sure the disable completed */ | ||
234 | gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); | ||
235 | |||
236 | /* | ||
237 | * Get the misc control for PCS. We will need to set the | ||
238 | * duplication amount. | ||
239 | */ | ||
240 | pcsx_miscx_ctl_reg.u64 = | ||
241 | cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface)); | ||
242 | |||
243 | /* | ||
244 | * Use GMXENO to force the link down if the status we get says | ||
245 | * it should be down. | ||
246 | */ | ||
247 | pcsx_miscx_ctl_reg.s.gmxeno = !link_info.s.link_up; | ||
248 | |||
249 | /* Only change the duplex setting if the link is up */ | ||
250 | if (link_info.s.link_up) | ||
251 | gmxx_prtx_cfg.s.duplex = link_info.s.full_duplex; | ||
252 | |||
253 | /* Do speed based setting for GMX */ | ||
254 | switch (link_info.s.speed) { | ||
255 | case 10: | ||
256 | gmxx_prtx_cfg.s.speed = 0; | ||
257 | gmxx_prtx_cfg.s.speed_msb = 1; | ||
258 | gmxx_prtx_cfg.s.slottime = 0; | ||
259 | /* Setting from GMX-603 */ | ||
260 | pcsx_miscx_ctl_reg.s.samp_pt = 25; | ||
261 | cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 64); | ||
262 | cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0); | ||
263 | break; | ||
264 | case 100: | ||
265 | gmxx_prtx_cfg.s.speed = 0; | ||
266 | gmxx_prtx_cfg.s.speed_msb = 0; | ||
267 | gmxx_prtx_cfg.s.slottime = 0; | ||
268 | pcsx_miscx_ctl_reg.s.samp_pt = 0x5; | ||
269 | cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 64); | ||
270 | cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 0); | ||
271 | break; | ||
272 | case 1000: | ||
273 | gmxx_prtx_cfg.s.speed = 1; | ||
274 | gmxx_prtx_cfg.s.speed_msb = 0; | ||
275 | gmxx_prtx_cfg.s.slottime = 1; | ||
276 | pcsx_miscx_ctl_reg.s.samp_pt = 1; | ||
277 | cvmx_write_csr(CVMX_GMXX_TXX_SLOT(index, interface), 512); | ||
278 | cvmx_write_csr(CVMX_GMXX_TXX_BURST(index, interface), 8192); | ||
279 | break; | ||
280 | default: | ||
281 | break; | ||
282 | } | ||
283 | |||
284 | /* Write the new misc control for PCS */ | ||
285 | cvmx_write_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface), | ||
286 | pcsx_miscx_ctl_reg.u64); | ||
287 | |||
288 | /* Write the new GMX settings with the port still disabled */ | ||
289 | cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64); | ||
290 | |||
291 | /* Read GMX CFG again to make sure the config completed */ | ||
292 | gmxx_prtx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); | ||
293 | |||
294 | /* Restore the enabled / disabled state */ | ||
295 | gmxx_prtx_cfg.s.en = is_enabled; | ||
296 | cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmxx_prtx_cfg.u64); | ||
297 | |||
298 | return 0; | ||
299 | } | ||
300 | |||
301 | /** | ||
302 | * Bring up the SGMII interface to be ready for packet I/O but | ||
303 | * leave I/O disabled using the GMX override. This function | ||
304 | * follows the bringup documented in 10.6.3 of the manual. | ||
305 | * | ||
306 | * @interface: Interface to bringup | ||
307 | * @num_ports: Number of ports on the interface | ||
308 | * | ||
309 | * Returns Zero on success, negative on failure | ||
310 | */ | ||
311 | static int __cvmx_helper_sgmii_hardware_init(int interface, int num_ports) | ||
312 | { | ||
313 | int index; | ||
314 | |||
315 | __cvmx_helper_setup_gmx(interface, num_ports); | ||
316 | |||
317 | for (index = 0; index < num_ports; index++) { | ||
318 | int ipd_port = cvmx_helper_get_ipd_port(interface, index); | ||
319 | __cvmx_helper_sgmii_hardware_init_one_time(interface, index); | ||
320 | __cvmx_helper_sgmii_link_set(ipd_port, | ||
321 | __cvmx_helper_sgmii_link_get | ||
322 | (ipd_port)); | ||
323 | |||
324 | } | ||
325 | |||
326 | return 0; | ||
327 | } | ||
328 | |||
329 | int __cvmx_helper_sgmii_enumerate(int interface) | ||
330 | { | ||
331 | return 4; | ||
332 | } | ||
333 | /** | ||
334 | * Probe a SGMII interface and determine the number of ports | ||
335 | * connected to it. The SGMII interface should still be down after | ||
336 | * this call. | ||
337 | * | ||
338 | * @interface: Interface to probe | ||
339 | * | ||
340 | * Returns Number of ports on the interface. Zero to disable. | ||
341 | */ | ||
342 | int __cvmx_helper_sgmii_probe(int interface) | ||
343 | { | ||
344 | union cvmx_gmxx_inf_mode mode; | ||
345 | |||
346 | /* | ||
347 | * Due to errata GMX-700 on CN56XXp1.x and CN52XXp1.x, the | ||
348 | * interface needs to be enabled before IPD otherwise per port | ||
349 | * backpressure may not work properly | ||
350 | */ | ||
351 | mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface)); | ||
352 | mode.s.en = 1; | ||
353 | cvmx_write_csr(CVMX_GMXX_INF_MODE(interface), mode.u64); | ||
354 | return __cvmx_helper_sgmii_enumerate(interface); | ||
355 | } | ||
356 | |||
357 | /** | ||
358 | * Bringup and enable a SGMII interface. After this call packet | ||
359 | * I/O should be fully functional. This is called with IPD | ||
360 | * enabled but PKO disabled. | ||
361 | * | ||
362 | * @interface: Interface to bring up | ||
363 | * | ||
364 | * Returns Zero on success, negative on failure | ||
365 | */ | ||
366 | int __cvmx_helper_sgmii_enable(int interface) | ||
367 | { | ||
368 | int num_ports = cvmx_helper_ports_on_interface(interface); | ||
369 | int index; | ||
370 | |||
371 | __cvmx_helper_sgmii_hardware_init(interface, num_ports); | ||
372 | |||
373 | for (index = 0; index < num_ports; index++) { | ||
374 | union cvmx_gmxx_prtx_cfg gmxx_prtx_cfg; | ||
375 | gmxx_prtx_cfg.u64 = | ||
376 | cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); | ||
377 | gmxx_prtx_cfg.s.en = 1; | ||
378 | cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), | ||
379 | gmxx_prtx_cfg.u64); | ||
380 | __cvmx_interrupt_pcsx_intx_en_reg_enable(index, interface); | ||
381 | } | ||
382 | __cvmx_interrupt_pcsxx_int_en_reg_enable(interface); | ||
383 | __cvmx_interrupt_gmxx_enable(interface); | ||
384 | return 0; | ||
385 | } | ||
386 | |||
387 | /** | ||
388 | * Return the link state of an IPD/PKO port as returned by | ||
389 | * auto negotiation. The result of this function may not match | ||
390 | * Octeon's link config if auto negotiation has changed since | ||
391 | * the last call to cvmx_helper_link_set(). | ||
392 | * | ||
393 | * @ipd_port: IPD/PKO port to query | ||
394 | * | ||
395 | * Returns Link state | ||
396 | */ | ||
397 | cvmx_helper_link_info_t __cvmx_helper_sgmii_link_get(int ipd_port) | ||
398 | { | ||
399 | cvmx_helper_link_info_t result; | ||
400 | union cvmx_pcsx_miscx_ctl_reg pcs_misc_ctl_reg; | ||
401 | int interface = cvmx_helper_get_interface_num(ipd_port); | ||
402 | int index = cvmx_helper_get_interface_index_num(ipd_port); | ||
403 | union cvmx_pcsx_mrx_control_reg pcsx_mrx_control_reg; | ||
404 | |||
405 | result.u64 = 0; | ||
406 | |||
407 | if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM) { | ||
408 | /* The simulator gives you a simulated 1Gbps full duplex link */ | ||
409 | result.s.link_up = 1; | ||
410 | result.s.full_duplex = 1; | ||
411 | result.s.speed = 1000; | ||
412 | return result; | ||
413 | } | ||
414 | |||
415 | pcsx_mrx_control_reg.u64 = | ||
416 | cvmx_read_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface)); | ||
417 | if (pcsx_mrx_control_reg.s.loopbck1) { | ||
418 | /* Force 1Gbps full duplex link for internal loopback */ | ||
419 | result.s.link_up = 1; | ||
420 | result.s.full_duplex = 1; | ||
421 | result.s.speed = 1000; | ||
422 | return result; | ||
423 | } | ||
424 | |||
425 | pcs_misc_ctl_reg.u64 = | ||
426 | cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface)); | ||
427 | if (pcs_misc_ctl_reg.s.mode) { | ||
428 | /* 1000BASE-X */ | ||
429 | /* FIXME */ | ||
430 | } else { | ||
431 | union cvmx_pcsx_miscx_ctl_reg pcsx_miscx_ctl_reg; | ||
432 | pcsx_miscx_ctl_reg.u64 = | ||
433 | cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface)); | ||
434 | if (pcsx_miscx_ctl_reg.s.mac_phy) { | ||
435 | /* PHY Mode */ | ||
436 | union cvmx_pcsx_mrx_status_reg pcsx_mrx_status_reg; | ||
437 | union cvmx_pcsx_anx_results_reg pcsx_anx_results_reg; | ||
438 | |||
439 | /* | ||
440 | * Don't bother continuing if the SERTES low | ||
441 | * level link is down | ||
442 | */ | ||
443 | pcsx_mrx_status_reg.u64 = | ||
444 | cvmx_read_csr(CVMX_PCSX_MRX_STATUS_REG | ||
445 | (index, interface)); | ||
446 | if (pcsx_mrx_status_reg.s.lnk_st == 0) { | ||
447 | if (__cvmx_helper_sgmii_hardware_init_link | ||
448 | (interface, index) != 0) | ||
449 | return result; | ||
450 | } | ||
451 | |||
452 | /* Read the autoneg results */ | ||
453 | pcsx_anx_results_reg.u64 = | ||
454 | cvmx_read_csr(CVMX_PCSX_ANX_RESULTS_REG | ||
455 | (index, interface)); | ||
456 | if (pcsx_anx_results_reg.s.an_cpt) { | ||
457 | /* | ||
458 | * Auto negotiation is complete. Set | ||
459 | * status accordingly. | ||
460 | */ | ||
461 | result.s.full_duplex = | ||
462 | pcsx_anx_results_reg.s.dup; | ||
463 | result.s.link_up = | ||
464 | pcsx_anx_results_reg.s.link_ok; | ||
465 | switch (pcsx_anx_results_reg.s.spd) { | ||
466 | case 0: | ||
467 | result.s.speed = 10; | ||
468 | break; | ||
469 | case 1: | ||
470 | result.s.speed = 100; | ||
471 | break; | ||
472 | case 2: | ||
473 | result.s.speed = 1000; | ||
474 | break; | ||
475 | default: | ||
476 | result.s.speed = 0; | ||
477 | result.s.link_up = 0; | ||
478 | break; | ||
479 | } | ||
480 | } else { | ||
481 | /* | ||
482 | * Auto negotiation isn't | ||
483 | * complete. Return link down. | ||
484 | */ | ||
485 | result.s.speed = 0; | ||
486 | result.s.link_up = 0; | ||
487 | } | ||
488 | } else { /* MAC Mode */ | ||
489 | |||
490 | result = __cvmx_helper_board_link_get(ipd_port); | ||
491 | } | ||
492 | } | ||
493 | return result; | ||
494 | } | ||
495 | |||
496 | /** | ||
497 | * Configure an IPD/PKO port for the specified link state. This | ||
498 | * function does not influence auto negotiation at the PHY level. | ||
499 | * The passed link state must always match the link state returned | ||
500 | * by cvmx_helper_link_get(). It is normally best to use | ||
501 | * cvmx_helper_link_autoconf() instead. | ||
502 | * | ||
503 | * @ipd_port: IPD/PKO port to configure | ||
504 | * @link_info: The new link state | ||
505 | * | ||
506 | * Returns Zero on success, negative on failure | ||
507 | */ | ||
508 | int __cvmx_helper_sgmii_link_set(int ipd_port, | ||
509 | cvmx_helper_link_info_t link_info) | ||
510 | { | ||
511 | int interface = cvmx_helper_get_interface_num(ipd_port); | ||
512 | int index = cvmx_helper_get_interface_index_num(ipd_port); | ||
513 | __cvmx_helper_sgmii_hardware_init_link(interface, index); | ||
514 | return __cvmx_helper_sgmii_hardware_init_link_speed(interface, index, | ||
515 | link_info); | ||
516 | } | ||
517 | |||
518 | /** | ||
519 | * Configure a port for internal and/or external loopback. Internal | ||
520 | * loopback causes packets sent by the port to be received by | ||
521 | * Octeon. External loopback causes packets received from the wire to | ||
522 | * sent out again. | ||
523 | * | ||
524 | * @ipd_port: IPD/PKO port to loopback. | ||
525 | * @enable_internal: | ||
526 | * Non zero if you want internal loopback | ||
527 | * @enable_external: | ||
528 | * Non zero if you want external loopback | ||
529 | * | ||
530 | * Returns Zero on success, negative on failure. | ||
531 | */ | ||
532 | int __cvmx_helper_sgmii_configure_loopback(int ipd_port, int enable_internal, | ||
533 | int enable_external) | ||
534 | { | ||
535 | int interface = cvmx_helper_get_interface_num(ipd_port); | ||
536 | int index = cvmx_helper_get_interface_index_num(ipd_port); | ||
537 | union cvmx_pcsx_mrx_control_reg pcsx_mrx_control_reg; | ||
538 | union cvmx_pcsx_miscx_ctl_reg pcsx_miscx_ctl_reg; | ||
539 | |||
540 | pcsx_mrx_control_reg.u64 = | ||
541 | cvmx_read_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface)); | ||
542 | pcsx_mrx_control_reg.s.loopbck1 = enable_internal; | ||
543 | cvmx_write_csr(CVMX_PCSX_MRX_CONTROL_REG(index, interface), | ||
544 | pcsx_mrx_control_reg.u64); | ||
545 | |||
546 | pcsx_miscx_ctl_reg.u64 = | ||
547 | cvmx_read_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface)); | ||
548 | pcsx_miscx_ctl_reg.s.loopbck2 = enable_external; | ||
549 | cvmx_write_csr(CVMX_PCSX_MISCX_CTL_REG(index, interface), | ||
550 | pcsx_miscx_ctl_reg.u64); | ||
551 | |||
552 | __cvmx_helper_sgmii_hardware_init_link(interface, index); | ||
553 | return 0; | ||
554 | } | ||
diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-spi.c b/arch/mips/cavium-octeon/executive/cvmx-helper-spi.c new file mode 100644 index 000000000000..2830e4bdf7f3 --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-spi.c | |||
@@ -0,0 +1,205 @@ | |||
1 | /***********************license start*************** | ||
2 | * Author: Cavium Networks | ||
3 | * | ||
4 | * Contact: support@caviumnetworks.com | ||
5 | * This file is part of the OCTEON SDK | ||
6 | * | ||
7 | * Copyright (c) 2003-2008 Cavium Networks | ||
8 | * | ||
9 | * This file is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License, Version 2, as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * This file is distributed in the hope that it will be useful, but | ||
14 | * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty | ||
15 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or | ||
16 | * NONINFRINGEMENT. See the GNU General Public License for more | ||
17 | * details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this file; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
22 | * or visit http://www.gnu.org/licenses/. | ||
23 | * | ||
24 | * This file may also be available under a different license from Cavium. | ||
25 | * Contact Cavium Networks for more information | ||
26 | ***********************license end**************************************/ | ||
27 | |||
28 | void __cvmx_interrupt_gmxx_enable(int interface); | ||
29 | void __cvmx_interrupt_spxx_int_msk_enable(int index); | ||
30 | void __cvmx_interrupt_stxx_int_msk_enable(int index); | ||
31 | |||
32 | /* | ||
33 | * Functions for SPI initialization, configuration, | ||
34 | * and monitoring. | ||
35 | */ | ||
36 | #include <asm/octeon/octeon.h> | ||
37 | |||
38 | #include <asm/octeon/cvmx-config.h> | ||
39 | #include <asm/octeon/cvmx-spi.h> | ||
40 | #include <asm/octeon/cvmx-helper.h> | ||
41 | |||
42 | #include <asm/octeon/cvmx-pip-defs.h> | ||
43 | #include <asm/octeon/cvmx-pko-defs.h> | ||
44 | |||
45 | /* | ||
46 | * CVMX_HELPER_SPI_TIMEOUT is used to determine how long the SPI | ||
47 | * initialization routines wait for SPI training. You can override the | ||
48 | * value using executive-config.h if necessary. | ||
49 | */ | ||
50 | #ifndef CVMX_HELPER_SPI_TIMEOUT | ||
51 | #define CVMX_HELPER_SPI_TIMEOUT 10 | ||
52 | #endif | ||
53 | |||
54 | int __cvmx_helper_spi_enumerate(int interface) | ||
55 | { | ||
56 | if ((cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) && | ||
57 | cvmx_spi4000_is_present(interface)) { | ||
58 | return 10; | ||
59 | } else { | ||
60 | return 16; | ||
61 | } | ||
62 | } | ||
63 | |||
64 | /** | ||
65 | * Probe a SPI interface and determine the number of ports | ||
66 | * connected to it. The SPI interface should still be down after | ||
67 | * this call. | ||
68 | * | ||
69 | * @interface: Interface to probe | ||
70 | * | ||
71 | * Returns Number of ports on the interface. Zero to disable. | ||
72 | */ | ||
73 | int __cvmx_helper_spi_probe(int interface) | ||
74 | { | ||
75 | int num_ports = 0; | ||
76 | |||
77 | if ((cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) && | ||
78 | cvmx_spi4000_is_present(interface)) { | ||
79 | num_ports = 10; | ||
80 | } else { | ||
81 | union cvmx_pko_reg_crc_enable enable; | ||
82 | num_ports = 16; | ||
83 | /* | ||
84 | * Unlike the SPI4000, most SPI devices don't | ||
85 | * automatically put on the L2 CRC. For everything | ||
86 | * except for the SPI4000 have PKO append the L2 CRC | ||
87 | * to the packet. | ||
88 | */ | ||
89 | enable.u64 = cvmx_read_csr(CVMX_PKO_REG_CRC_ENABLE); | ||
90 | enable.s.enable |= 0xffff << (interface * 16); | ||
91 | cvmx_write_csr(CVMX_PKO_REG_CRC_ENABLE, enable.u64); | ||
92 | } | ||
93 | __cvmx_helper_setup_gmx(interface, num_ports); | ||
94 | return num_ports; | ||
95 | } | ||
96 | |||
97 | /** | ||
98 | * Bringup and enable a SPI interface. After this call packet I/O | ||
99 | * should be fully functional. This is called with IPD enabled but | ||
100 | * PKO disabled. | ||
101 | * | ||
102 | * @interface: Interface to bring up | ||
103 | * | ||
104 | * Returns Zero on success, negative on failure | ||
105 | */ | ||
106 | int __cvmx_helper_spi_enable(int interface) | ||
107 | { | ||
108 | /* | ||
109 | * Normally the ethernet L2 CRC is checked and stripped in the | ||
110 | * GMX block. When you are using SPI, this isn' the case and | ||
111 | * IPD needs to check the L2 CRC. | ||
112 | */ | ||
113 | int num_ports = cvmx_helper_ports_on_interface(interface); | ||
114 | int ipd_port; | ||
115 | for (ipd_port = interface * 16; ipd_port < interface * 16 + num_ports; | ||
116 | ipd_port++) { | ||
117 | union cvmx_pip_prt_cfgx port_config; | ||
118 | port_config.u64 = cvmx_read_csr(CVMX_PIP_PRT_CFGX(ipd_port)); | ||
119 | port_config.s.crc_en = 1; | ||
120 | cvmx_write_csr(CVMX_PIP_PRT_CFGX(ipd_port), port_config.u64); | ||
121 | } | ||
122 | |||
123 | if (cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM) { | ||
124 | cvmx_spi_start_interface(interface, CVMX_SPI_MODE_DUPLEX, | ||
125 | CVMX_HELPER_SPI_TIMEOUT, num_ports); | ||
126 | if (cvmx_spi4000_is_present(interface)) | ||
127 | cvmx_spi4000_initialize(interface); | ||
128 | } | ||
129 | __cvmx_interrupt_spxx_int_msk_enable(interface); | ||
130 | __cvmx_interrupt_stxx_int_msk_enable(interface); | ||
131 | __cvmx_interrupt_gmxx_enable(interface); | ||
132 | return 0; | ||
133 | } | ||
134 | |||
135 | /** | ||
136 | * Return the link state of an IPD/PKO port as returned by | ||
137 | * auto negotiation. The result of this function may not match | ||
138 | * Octeon's link config if auto negotiation has changed since | ||
139 | * the last call to cvmx_helper_link_set(). | ||
140 | * | ||
141 | * @ipd_port: IPD/PKO port to query | ||
142 | * | ||
143 | * Returns Link state | ||
144 | */ | ||
145 | cvmx_helper_link_info_t __cvmx_helper_spi_link_get(int ipd_port) | ||
146 | { | ||
147 | cvmx_helper_link_info_t result; | ||
148 | int interface = cvmx_helper_get_interface_num(ipd_port); | ||
149 | int index = cvmx_helper_get_interface_index_num(ipd_port); | ||
150 | result.u64 = 0; | ||
151 | |||
152 | if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM) { | ||
153 | /* The simulator gives you a simulated full duplex link */ | ||
154 | result.s.link_up = 1; | ||
155 | result.s.full_duplex = 1; | ||
156 | result.s.speed = 10000; | ||
157 | } else if (cvmx_spi4000_is_present(interface)) { | ||
158 | union cvmx_gmxx_rxx_rx_inbnd inband = | ||
159 | cvmx_spi4000_check_speed(interface, index); | ||
160 | result.s.link_up = inband.s.status; | ||
161 | result.s.full_duplex = inband.s.duplex; | ||
162 | switch (inband.s.speed) { | ||
163 | case 0: /* 10 Mbps */ | ||
164 | result.s.speed = 10; | ||
165 | break; | ||
166 | case 1: /* 100 Mbps */ | ||
167 | result.s.speed = 100; | ||
168 | break; | ||
169 | case 2: /* 1 Gbps */ | ||
170 | result.s.speed = 1000; | ||
171 | break; | ||
172 | case 3: /* Illegal */ | ||
173 | result.s.speed = 0; | ||
174 | result.s.link_up = 0; | ||
175 | break; | ||
176 | } | ||
177 | } else { | ||
178 | /* For generic SPI we can't determine the link, just return some | ||
179 | sane results */ | ||
180 | result.s.link_up = 1; | ||
181 | result.s.full_duplex = 1; | ||
182 | result.s.speed = 10000; | ||
183 | } | ||
184 | return result; | ||
185 | } | ||
186 | |||
187 | /** | ||
188 | * Configure an IPD/PKO port for the specified link state. This | ||
189 | * function does not influence auto negotiation at the PHY level. | ||
190 | * The passed link state must always match the link state returned | ||
191 | * by cvmx_helper_link_get(). It is normally best to use | ||
192 | * cvmx_helper_link_autoconf() instead. | ||
193 | * | ||
194 | * @ipd_port: IPD/PKO port to configure | ||
195 | * @link_info: The new link state | ||
196 | * | ||
197 | * Returns Zero on success, negative on failure | ||
198 | */ | ||
199 | int __cvmx_helper_spi_link_set(int ipd_port, cvmx_helper_link_info_t link_info) | ||
200 | { | ||
201 | /* Nothing to do. If we have a SPI4000 then the setup was already performed | ||
202 | by cvmx_spi4000_check_speed(). If not then there isn't any link | ||
203 | info */ | ||
204 | return 0; | ||
205 | } | ||
diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-util.c b/arch/mips/cavium-octeon/executive/cvmx-helper-util.c new file mode 100644 index 000000000000..116dea17acf5 --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-util.c | |||
@@ -0,0 +1,433 @@ | |||
1 | /***********************license start*************** | ||
2 | * Author: Cavium Networks | ||
3 | * | ||
4 | * Contact: support@caviumnetworks.com | ||
5 | * This file is part of the OCTEON SDK | ||
6 | * | ||
7 | * Copyright (c) 2003-2008 Cavium Networks | ||
8 | * | ||
9 | * This file is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License, Version 2, as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * This file is distributed in the hope that it will be useful, but | ||
14 | * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty | ||
15 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or | ||
16 | * NONINFRINGEMENT. See the GNU General Public License for more | ||
17 | * details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this file; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
22 | * or visit http://www.gnu.org/licenses/. | ||
23 | * | ||
24 | * This file may also be available under a different license from Cavium. | ||
25 | * Contact Cavium Networks for more information | ||
26 | ***********************license end**************************************/ | ||
27 | |||
28 | /* | ||
29 | * Small helper utilities. | ||
30 | */ | ||
31 | #include <linux/kernel.h> | ||
32 | |||
33 | #include <asm/octeon/octeon.h> | ||
34 | |||
35 | #include <asm/octeon/cvmx-config.h> | ||
36 | |||
37 | #include <asm/octeon/cvmx-fpa.h> | ||
38 | #include <asm/octeon/cvmx-pip.h> | ||
39 | #include <asm/octeon/cvmx-pko.h> | ||
40 | #include <asm/octeon/cvmx-ipd.h> | ||
41 | #include <asm/octeon/cvmx-spi.h> | ||
42 | |||
43 | #include <asm/octeon/cvmx-helper.h> | ||
44 | #include <asm/octeon/cvmx-helper-util.h> | ||
45 | |||
46 | #include <asm/octeon/cvmx-ipd-defs.h> | ||
47 | |||
48 | /** | ||
49 | * Convert a interface mode into a human readable string | ||
50 | * | ||
51 | * @mode: Mode to convert | ||
52 | * | ||
53 | * Returns String | ||
54 | */ | ||
55 | const char *cvmx_helper_interface_mode_to_string(cvmx_helper_interface_mode_t | ||
56 | mode) | ||
57 | { | ||
58 | switch (mode) { | ||
59 | case CVMX_HELPER_INTERFACE_MODE_DISABLED: | ||
60 | return "DISABLED"; | ||
61 | case CVMX_HELPER_INTERFACE_MODE_RGMII: | ||
62 | return "RGMII"; | ||
63 | case CVMX_HELPER_INTERFACE_MODE_GMII: | ||
64 | return "GMII"; | ||
65 | case CVMX_HELPER_INTERFACE_MODE_SPI: | ||
66 | return "SPI"; | ||
67 | case CVMX_HELPER_INTERFACE_MODE_PCIE: | ||
68 | return "PCIE"; | ||
69 | case CVMX_HELPER_INTERFACE_MODE_XAUI: | ||
70 | return "XAUI"; | ||
71 | case CVMX_HELPER_INTERFACE_MODE_SGMII: | ||
72 | return "SGMII"; | ||
73 | case CVMX_HELPER_INTERFACE_MODE_PICMG: | ||
74 | return "PICMG"; | ||
75 | case CVMX_HELPER_INTERFACE_MODE_NPI: | ||
76 | return "NPI"; | ||
77 | case CVMX_HELPER_INTERFACE_MODE_LOOP: | ||
78 | return "LOOP"; | ||
79 | } | ||
80 | return "UNKNOWN"; | ||
81 | } | ||
82 | |||
83 | /** | ||
84 | * Debug routine to dump the packet structure to the console | ||
85 | * | ||
86 | * @work: Work queue entry containing the packet to dump | ||
87 | * Returns | ||
88 | */ | ||
89 | int cvmx_helper_dump_packet(cvmx_wqe_t *work) | ||
90 | { | ||
91 | uint64_t count; | ||
92 | uint64_t remaining_bytes; | ||
93 | union cvmx_buf_ptr buffer_ptr; | ||
94 | uint64_t start_of_buffer; | ||
95 | uint8_t *data_address; | ||
96 | uint8_t *end_of_data; | ||
97 | |||
98 | cvmx_dprintf("Packet Length: %u\n", work->len); | ||
99 | cvmx_dprintf(" Input Port: %u\n", work->ipprt); | ||
100 | cvmx_dprintf(" QoS: %u\n", work->qos); | ||
101 | cvmx_dprintf(" Buffers: %u\n", work->word2.s.bufs); | ||
102 | |||
103 | if (work->word2.s.bufs == 0) { | ||
104 | union cvmx_ipd_wqe_fpa_queue wqe_pool; | ||
105 | wqe_pool.u64 = cvmx_read_csr(CVMX_IPD_WQE_FPA_QUEUE); | ||
106 | buffer_ptr.u64 = 0; | ||
107 | buffer_ptr.s.pool = wqe_pool.s.wqe_pool; | ||
108 | buffer_ptr.s.size = 128; | ||
109 | buffer_ptr.s.addr = cvmx_ptr_to_phys(work->packet_data); | ||
110 | if (likely(!work->word2.s.not_IP)) { | ||
111 | union cvmx_pip_ip_offset pip_ip_offset; | ||
112 | pip_ip_offset.u64 = cvmx_read_csr(CVMX_PIP_IP_OFFSET); | ||
113 | buffer_ptr.s.addr += | ||
114 | (pip_ip_offset.s.offset << 3) - | ||
115 | work->word2.s.ip_offset; | ||
116 | buffer_ptr.s.addr += (work->word2.s.is_v6 ^ 1) << 2; | ||
117 | } else { | ||
118 | /* | ||
119 | * WARNING: This code assumes that the packet | ||
120 | * is not RAW. If it was, we would use | ||
121 | * PIP_GBL_CFG[RAW_SHF] instead of | ||
122 | * PIP_GBL_CFG[NIP_SHF]. | ||
123 | */ | ||
124 | union cvmx_pip_gbl_cfg pip_gbl_cfg; | ||
125 | pip_gbl_cfg.u64 = cvmx_read_csr(CVMX_PIP_GBL_CFG); | ||
126 | buffer_ptr.s.addr += pip_gbl_cfg.s.nip_shf; | ||
127 | } | ||
128 | } else | ||
129 | buffer_ptr = work->packet_ptr; | ||
130 | remaining_bytes = work->len; | ||
131 | |||
132 | while (remaining_bytes) { | ||
133 | start_of_buffer = | ||
134 | ((buffer_ptr.s.addr >> 7) - buffer_ptr.s.back) << 7; | ||
135 | cvmx_dprintf(" Buffer Start:%llx\n", | ||
136 | (unsigned long long)start_of_buffer); | ||
137 | cvmx_dprintf(" Buffer I : %u\n", buffer_ptr.s.i); | ||
138 | cvmx_dprintf(" Buffer Back: %u\n", buffer_ptr.s.back); | ||
139 | cvmx_dprintf(" Buffer Pool: %u\n", buffer_ptr.s.pool); | ||
140 | cvmx_dprintf(" Buffer Data: %llx\n", | ||
141 | (unsigned long long)buffer_ptr.s.addr); | ||
142 | cvmx_dprintf(" Buffer Size: %u\n", buffer_ptr.s.size); | ||
143 | |||
144 | cvmx_dprintf("\t\t"); | ||
145 | data_address = (uint8_t *) cvmx_phys_to_ptr(buffer_ptr.s.addr); | ||
146 | end_of_data = data_address + buffer_ptr.s.size; | ||
147 | count = 0; | ||
148 | while (data_address < end_of_data) { | ||
149 | if (remaining_bytes == 0) | ||
150 | break; | ||
151 | else | ||
152 | remaining_bytes--; | ||
153 | cvmx_dprintf("%02x", (unsigned int)*data_address); | ||
154 | data_address++; | ||
155 | if (remaining_bytes && (count == 7)) { | ||
156 | cvmx_dprintf("\n\t\t"); | ||
157 | count = 0; | ||
158 | } else | ||
159 | count++; | ||
160 | } | ||
161 | cvmx_dprintf("\n"); | ||
162 | |||
163 | if (remaining_bytes) | ||
164 | buffer_ptr = *(union cvmx_buf_ptr *) | ||
165 | cvmx_phys_to_ptr(buffer_ptr.s.addr - 8); | ||
166 | } | ||
167 | return 0; | ||
168 | } | ||
169 | |||
170 | /** | ||
171 | * Setup Random Early Drop on a specific input queue | ||
172 | * | ||
173 | * @queue: Input queue to setup RED on (0-7) | ||
174 | * @pass_thresh: | ||
175 | * Packets will begin slowly dropping when there are less than | ||
176 | * this many packet buffers free in FPA 0. | ||
177 | * @drop_thresh: | ||
178 | * All incomming packets will be dropped when there are less | ||
179 | * than this many free packet buffers in FPA 0. | ||
180 | * Returns Zero on success. Negative on failure | ||
181 | */ | ||
182 | int cvmx_helper_setup_red_queue(int queue, int pass_thresh, int drop_thresh) | ||
183 | { | ||
184 | union cvmx_ipd_qosx_red_marks red_marks; | ||
185 | union cvmx_ipd_red_quex_param red_param; | ||
186 | |||
187 | /* Set RED to begin dropping packets when there are pass_thresh buffers | ||
188 | left. It will linearly drop more packets until reaching drop_thresh | ||
189 | buffers */ | ||
190 | red_marks.u64 = 0; | ||
191 | red_marks.s.drop = drop_thresh; | ||
192 | red_marks.s.pass = pass_thresh; | ||
193 | cvmx_write_csr(CVMX_IPD_QOSX_RED_MARKS(queue), red_marks.u64); | ||
194 | |||
195 | /* Use the actual queue 0 counter, not the average */ | ||
196 | red_param.u64 = 0; | ||
197 | red_param.s.prb_con = | ||
198 | (255ul << 24) / (red_marks.s.pass - red_marks.s.drop); | ||
199 | red_param.s.avg_con = 1; | ||
200 | red_param.s.new_con = 255; | ||
201 | red_param.s.use_pcnt = 1; | ||
202 | cvmx_write_csr(CVMX_IPD_RED_QUEX_PARAM(queue), red_param.u64); | ||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | /** | ||
207 | * Setup Random Early Drop to automatically begin dropping packets. | ||
208 | * | ||
209 | * @pass_thresh: | ||
210 | * Packets will begin slowly dropping when there are less than | ||
211 | * this many packet buffers free in FPA 0. | ||
212 | * @drop_thresh: | ||
213 | * All incomming packets will be dropped when there are less | ||
214 | * than this many free packet buffers in FPA 0. | ||
215 | * Returns Zero on success. Negative on failure | ||
216 | */ | ||
217 | int cvmx_helper_setup_red(int pass_thresh, int drop_thresh) | ||
218 | { | ||
219 | union cvmx_ipd_portx_bp_page_cnt page_cnt; | ||
220 | union cvmx_ipd_bp_prt_red_end ipd_bp_prt_red_end; | ||
221 | union cvmx_ipd_red_port_enable red_port_enable; | ||
222 | int queue; | ||
223 | int interface; | ||
224 | int port; | ||
225 | |||
226 | /* Disable backpressure based on queued buffers. It needs SW support */ | ||
227 | page_cnt.u64 = 0; | ||
228 | page_cnt.s.bp_enb = 0; | ||
229 | page_cnt.s.page_cnt = 100; | ||
230 | for (interface = 0; interface < 2; interface++) { | ||
231 | for (port = cvmx_helper_get_first_ipd_port(interface); | ||
232 | port < cvmx_helper_get_last_ipd_port(interface); port++) | ||
233 | cvmx_write_csr(CVMX_IPD_PORTX_BP_PAGE_CNT(port), | ||
234 | page_cnt.u64); | ||
235 | } | ||
236 | |||
237 | for (queue = 0; queue < 8; queue++) | ||
238 | cvmx_helper_setup_red_queue(queue, pass_thresh, drop_thresh); | ||
239 | |||
240 | /* Shutoff the dropping based on the per port page count. SW isn't | ||
241 | decrementing it right now */ | ||
242 | ipd_bp_prt_red_end.u64 = 0; | ||
243 | ipd_bp_prt_red_end.s.prt_enb = 0; | ||
244 | cvmx_write_csr(CVMX_IPD_BP_PRT_RED_END, ipd_bp_prt_red_end.u64); | ||
245 | |||
246 | red_port_enable.u64 = 0; | ||
247 | red_port_enable.s.prt_enb = 0xfffffffffull; | ||
248 | red_port_enable.s.avg_dly = 10000; | ||
249 | red_port_enable.s.prb_dly = 10000; | ||
250 | cvmx_write_csr(CVMX_IPD_RED_PORT_ENABLE, red_port_enable.u64); | ||
251 | |||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | /** | ||
256 | * Setup the common GMX settings that determine the number of | ||
257 | * ports. These setting apply to almost all configurations of all | ||
258 | * chips. | ||
259 | * | ||
260 | * @interface: Interface to configure | ||
261 | * @num_ports: Number of ports on the interface | ||
262 | * | ||
263 | * Returns Zero on success, negative on failure | ||
264 | */ | ||
265 | int __cvmx_helper_setup_gmx(int interface, int num_ports) | ||
266 | { | ||
267 | union cvmx_gmxx_tx_prts gmx_tx_prts; | ||
268 | union cvmx_gmxx_rx_prts gmx_rx_prts; | ||
269 | union cvmx_pko_reg_gmx_port_mode pko_mode; | ||
270 | union cvmx_gmxx_txx_thresh gmx_tx_thresh; | ||
271 | int index; | ||
272 | |||
273 | /* Tell GMX the number of TX ports on this interface */ | ||
274 | gmx_tx_prts.u64 = cvmx_read_csr(CVMX_GMXX_TX_PRTS(interface)); | ||
275 | gmx_tx_prts.s.prts = num_ports; | ||
276 | cvmx_write_csr(CVMX_GMXX_TX_PRTS(interface), gmx_tx_prts.u64); | ||
277 | |||
278 | /* Tell GMX the number of RX ports on this interface. This only | ||
279 | ** applies to *GMII and XAUI ports */ | ||
280 | if (cvmx_helper_interface_get_mode(interface) == | ||
281 | CVMX_HELPER_INTERFACE_MODE_RGMII | ||
282 | || cvmx_helper_interface_get_mode(interface) == | ||
283 | CVMX_HELPER_INTERFACE_MODE_SGMII | ||
284 | || cvmx_helper_interface_get_mode(interface) == | ||
285 | CVMX_HELPER_INTERFACE_MODE_GMII | ||
286 | || cvmx_helper_interface_get_mode(interface) == | ||
287 | CVMX_HELPER_INTERFACE_MODE_XAUI) { | ||
288 | if (num_ports > 4) { | ||
289 | cvmx_dprintf("__cvmx_helper_setup_gmx: Illegal " | ||
290 | "num_ports\n"); | ||
291 | return -1; | ||
292 | } | ||
293 | |||
294 | gmx_rx_prts.u64 = cvmx_read_csr(CVMX_GMXX_RX_PRTS(interface)); | ||
295 | gmx_rx_prts.s.prts = num_ports; | ||
296 | cvmx_write_csr(CVMX_GMXX_RX_PRTS(interface), gmx_rx_prts.u64); | ||
297 | } | ||
298 | |||
299 | /* Skip setting CVMX_PKO_REG_GMX_PORT_MODE on 30XX, 31XX, and 50XX */ | ||
300 | if (!OCTEON_IS_MODEL(OCTEON_CN30XX) && !OCTEON_IS_MODEL(OCTEON_CN31XX) | ||
301 | && !OCTEON_IS_MODEL(OCTEON_CN50XX)) { | ||
302 | /* Tell PKO the number of ports on this interface */ | ||
303 | pko_mode.u64 = cvmx_read_csr(CVMX_PKO_REG_GMX_PORT_MODE); | ||
304 | if (interface == 0) { | ||
305 | if (num_ports == 1) | ||
306 | pko_mode.s.mode0 = 4; | ||
307 | else if (num_ports == 2) | ||
308 | pko_mode.s.mode0 = 3; | ||
309 | else if (num_ports <= 4) | ||
310 | pko_mode.s.mode0 = 2; | ||
311 | else if (num_ports <= 8) | ||
312 | pko_mode.s.mode0 = 1; | ||
313 | else | ||
314 | pko_mode.s.mode0 = 0; | ||
315 | } else { | ||
316 | if (num_ports == 1) | ||
317 | pko_mode.s.mode1 = 4; | ||
318 | else if (num_ports == 2) | ||
319 | pko_mode.s.mode1 = 3; | ||
320 | else if (num_ports <= 4) | ||
321 | pko_mode.s.mode1 = 2; | ||
322 | else if (num_ports <= 8) | ||
323 | pko_mode.s.mode1 = 1; | ||
324 | else | ||
325 | pko_mode.s.mode1 = 0; | ||
326 | } | ||
327 | cvmx_write_csr(CVMX_PKO_REG_GMX_PORT_MODE, pko_mode.u64); | ||
328 | } | ||
329 | |||
330 | /* | ||
331 | * Set GMX to buffer as much data as possible before starting | ||
332 | * transmit. This reduces the chances that we have a TX under | ||
333 | * run due to memory contention. Any packet that fits entirely | ||
334 | * in the GMX FIFO can never have an under run regardless of | ||
335 | * memory load. | ||
336 | */ | ||
337 | gmx_tx_thresh.u64 = cvmx_read_csr(CVMX_GMXX_TXX_THRESH(0, interface)); | ||
338 | if (OCTEON_IS_MODEL(OCTEON_CN30XX) || OCTEON_IS_MODEL(OCTEON_CN31XX) | ||
339 | || OCTEON_IS_MODEL(OCTEON_CN50XX)) { | ||
340 | /* These chips have a fixed max threshold of 0x40 */ | ||
341 | gmx_tx_thresh.s.cnt = 0x40; | ||
342 | } else { | ||
343 | /* Choose the max value for the number of ports */ | ||
344 | if (num_ports <= 1) | ||
345 | gmx_tx_thresh.s.cnt = 0x100 / 1; | ||
346 | else if (num_ports == 2) | ||
347 | gmx_tx_thresh.s.cnt = 0x100 / 2; | ||
348 | else | ||
349 | gmx_tx_thresh.s.cnt = 0x100 / 4; | ||
350 | } | ||
351 | /* | ||
352 | * SPI and XAUI can have lots of ports but the GMX hardware | ||
353 | * only ever has a max of 4. | ||
354 | */ | ||
355 | if (num_ports > 4) | ||
356 | num_ports = 4; | ||
357 | for (index = 0; index < num_ports; index++) | ||
358 | cvmx_write_csr(CVMX_GMXX_TXX_THRESH(index, interface), | ||
359 | gmx_tx_thresh.u64); | ||
360 | |||
361 | return 0; | ||
362 | } | ||
363 | |||
364 | /** | ||
365 | * Returns the IPD/PKO port number for a port on the given | ||
366 | * interface. | ||
367 | * | ||
368 | * @interface: Interface to use | ||
369 | * @port: Port on the interface | ||
370 | * | ||
371 | * Returns IPD/PKO port number | ||
372 | */ | ||
373 | int cvmx_helper_get_ipd_port(int interface, int port) | ||
374 | { | ||
375 | switch (interface) { | ||
376 | case 0: | ||
377 | return port; | ||
378 | case 1: | ||
379 | return port + 16; | ||
380 | case 2: | ||
381 | return port + 32; | ||
382 | case 3: | ||
383 | return port + 36; | ||
384 | } | ||
385 | return -1; | ||
386 | } | ||
387 | |||
388 | /** | ||
389 | * Returns the interface number for an IPD/PKO port number. | ||
390 | * | ||
391 | * @ipd_port: IPD/PKO port number | ||
392 | * | ||
393 | * Returns Interface number | ||
394 | */ | ||
395 | int cvmx_helper_get_interface_num(int ipd_port) | ||
396 | { | ||
397 | if (ipd_port < 16) | ||
398 | return 0; | ||
399 | else if (ipd_port < 32) | ||
400 | return 1; | ||
401 | else if (ipd_port < 36) | ||
402 | return 2; | ||
403 | else if (ipd_port < 40) | ||
404 | return 3; | ||
405 | else | ||
406 | cvmx_dprintf("cvmx_helper_get_interface_num: Illegal IPD " | ||
407 | "port number\n"); | ||
408 | |||
409 | return -1; | ||
410 | } | ||
411 | |||
412 | /** | ||
413 | * Returns the interface index number for an IPD/PKO port | ||
414 | * number. | ||
415 | * | ||
416 | * @ipd_port: IPD/PKO port number | ||
417 | * | ||
418 | * Returns Interface index number | ||
419 | */ | ||
420 | int cvmx_helper_get_interface_index_num(int ipd_port) | ||
421 | { | ||
422 | if (ipd_port < 32) | ||
423 | return ipd_port & 15; | ||
424 | else if (ipd_port < 36) | ||
425 | return ipd_port & 3; | ||
426 | else if (ipd_port < 40) | ||
427 | return ipd_port & 3; | ||
428 | else | ||
429 | cvmx_dprintf("cvmx_helper_get_interface_index_num: " | ||
430 | "Illegal IPD port number\n"); | ||
431 | |||
432 | return -1; | ||
433 | } | ||
diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c b/arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c new file mode 100644 index 000000000000..1723248e987d --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c | |||
@@ -0,0 +1,354 @@ | |||
1 | /***********************license start*************** | ||
2 | * Author: Cavium Networks | ||
3 | * | ||
4 | * Contact: support@caviumnetworks.com | ||
5 | * This file is part of the OCTEON SDK | ||
6 | * | ||
7 | * Copyright (c) 2003-2008 Cavium Networks | ||
8 | * | ||
9 | * This file is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License, Version 2, as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * This file is distributed in the hope that it will be useful, but | ||
14 | * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty | ||
15 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or | ||
16 | * NONINFRINGEMENT. See the GNU General Public License for more | ||
17 | * details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this file; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
22 | * or visit http://www.gnu.org/licenses/. | ||
23 | * | ||
24 | * This file may also be available under a different license from Cavium. | ||
25 | * Contact Cavium Networks for more information | ||
26 | ***********************license end**************************************/ | ||
27 | |||
28 | /* | ||
29 | * Functions for XAUI initialization, configuration, | ||
30 | * and monitoring. | ||
31 | * | ||
32 | */ | ||
33 | |||
34 | #include <asm/octeon/octeon.h> | ||
35 | |||
36 | #include <asm/octeon/cvmx-config.h> | ||
37 | |||
38 | #include <asm/octeon/cvmx-helper.h> | ||
39 | |||
40 | #include <asm/octeon/cvmx-pko-defs.h> | ||
41 | #include <asm/octeon/cvmx-gmxx-defs.h> | ||
42 | #include <asm/octeon/cvmx-pcsxx-defs.h> | ||
43 | |||
44 | void __cvmx_interrupt_gmxx_enable(int interface); | ||
45 | void __cvmx_interrupt_pcsx_intx_en_reg_enable(int index, int block); | ||
46 | void __cvmx_interrupt_pcsxx_int_en_reg_enable(int index); | ||
47 | |||
48 | int __cvmx_helper_xaui_enumerate(int interface) | ||
49 | { | ||
50 | union cvmx_gmxx_hg2_control gmx_hg2_control; | ||
51 | |||
52 | /* If HiGig2 is enabled return 16 ports, otherwise return 1 port */ | ||
53 | gmx_hg2_control.u64 = cvmx_read_csr(CVMX_GMXX_HG2_CONTROL(interface)); | ||
54 | if (gmx_hg2_control.s.hg2tx_en) | ||
55 | return 16; | ||
56 | else | ||
57 | return 1; | ||
58 | } | ||
59 | |||
60 | /** | ||
61 | * Probe a XAUI interface and determine the number of ports | ||
62 | * connected to it. The XAUI interface should still be down | ||
63 | * after this call. | ||
64 | * | ||
65 | * @interface: Interface to probe | ||
66 | * | ||
67 | * Returns Number of ports on the interface. Zero to disable. | ||
68 | */ | ||
69 | int __cvmx_helper_xaui_probe(int interface) | ||
70 | { | ||
71 | int i; | ||
72 | union cvmx_gmxx_inf_mode mode; | ||
73 | |||
74 | /* | ||
75 | * Due to errata GMX-700 on CN56XXp1.x and CN52XXp1.x, the | ||
76 | * interface needs to be enabled before IPD otherwise per port | ||
77 | * backpressure may not work properly. | ||
78 | */ | ||
79 | mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface)); | ||
80 | mode.s.en = 1; | ||
81 | cvmx_write_csr(CVMX_GMXX_INF_MODE(interface), mode.u64); | ||
82 | |||
83 | __cvmx_helper_setup_gmx(interface, 1); | ||
84 | |||
85 | /* | ||
86 | * Setup PKO to support 16 ports for HiGig2 virtual | ||
87 | * ports. We're pointing all of the PKO packet ports for this | ||
88 | * interface to the XAUI. This allows us to use HiGig2 | ||
89 | * backpressure per port. | ||
90 | */ | ||
91 | for (i = 0; i < 16; i++) { | ||
92 | union cvmx_pko_mem_port_ptrs pko_mem_port_ptrs; | ||
93 | pko_mem_port_ptrs.u64 = 0; | ||
94 | /* | ||
95 | * We set each PKO port to have equal priority in a | ||
96 | * round robin fashion. | ||
97 | */ | ||
98 | pko_mem_port_ptrs.s.static_p = 0; | ||
99 | pko_mem_port_ptrs.s.qos_mask = 0xff; | ||
100 | /* All PKO ports map to the same XAUI hardware port */ | ||
101 | pko_mem_port_ptrs.s.eid = interface * 4; | ||
102 | pko_mem_port_ptrs.s.pid = interface * 16 + i; | ||
103 | cvmx_write_csr(CVMX_PKO_MEM_PORT_PTRS, pko_mem_port_ptrs.u64); | ||
104 | } | ||
105 | return __cvmx_helper_xaui_enumerate(interface); | ||
106 | } | ||
107 | |||
108 | /** | ||
109 | * Bringup and enable a XAUI interface. After this call packet | ||
110 | * I/O should be fully functional. This is called with IPD | ||
111 | * enabled but PKO disabled. | ||
112 | * | ||
113 | * @interface: Interface to bring up | ||
114 | * | ||
115 | * Returns Zero on success, negative on failure | ||
116 | */ | ||
117 | int __cvmx_helper_xaui_enable(int interface) | ||
118 | { | ||
119 | union cvmx_gmxx_prtx_cfg gmx_cfg; | ||
120 | union cvmx_pcsxx_control1_reg xauiCtl; | ||
121 | union cvmx_pcsxx_misc_ctl_reg xauiMiscCtl; | ||
122 | union cvmx_gmxx_tx_xaui_ctl gmxXauiTxCtl; | ||
123 | union cvmx_gmxx_rxx_int_en gmx_rx_int_en; | ||
124 | union cvmx_gmxx_tx_int_en gmx_tx_int_en; | ||
125 | union cvmx_pcsxx_int_en_reg pcsx_int_en_reg; | ||
126 | |||
127 | /* (1) Interface has already been enabled. */ | ||
128 | |||
129 | /* (2) Disable GMX. */ | ||
130 | xauiMiscCtl.u64 = cvmx_read_csr(CVMX_PCSXX_MISC_CTL_REG(interface)); | ||
131 | xauiMiscCtl.s.gmxeno = 1; | ||
132 | cvmx_write_csr(CVMX_PCSXX_MISC_CTL_REG(interface), xauiMiscCtl.u64); | ||
133 | |||
134 | /* (3) Disable GMX and PCSX interrupts. */ | ||
135 | gmx_rx_int_en.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(0, interface)); | ||
136 | cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(0, interface), 0x0); | ||
137 | gmx_tx_int_en.u64 = cvmx_read_csr(CVMX_GMXX_TX_INT_EN(interface)); | ||
138 | cvmx_write_csr(CVMX_GMXX_TX_INT_EN(interface), 0x0); | ||
139 | pcsx_int_en_reg.u64 = cvmx_read_csr(CVMX_PCSXX_INT_EN_REG(interface)); | ||
140 | cvmx_write_csr(CVMX_PCSXX_INT_EN_REG(interface), 0x0); | ||
141 | |||
142 | /* (4) Bring up the PCSX and GMX reconciliation layer. */ | ||
143 | /* (4)a Set polarity and lane swapping. */ | ||
144 | /* (4)b */ | ||
145 | gmxXauiTxCtl.u64 = cvmx_read_csr(CVMX_GMXX_TX_XAUI_CTL(interface)); | ||
146 | /* Enable better IFG packing and improves performance */ | ||
147 | gmxXauiTxCtl.s.dic_en = 1; | ||
148 | gmxXauiTxCtl.s.uni_en = 0; | ||
149 | cvmx_write_csr(CVMX_GMXX_TX_XAUI_CTL(interface), gmxXauiTxCtl.u64); | ||
150 | |||
151 | /* (4)c Aply reset sequence */ | ||
152 | xauiCtl.u64 = cvmx_read_csr(CVMX_PCSXX_CONTROL1_REG(interface)); | ||
153 | xauiCtl.s.lo_pwr = 0; | ||
154 | xauiCtl.s.reset = 1; | ||
155 | cvmx_write_csr(CVMX_PCSXX_CONTROL1_REG(interface), xauiCtl.u64); | ||
156 | |||
157 | /* Wait for PCS to come out of reset */ | ||
158 | if (CVMX_WAIT_FOR_FIELD64 | ||
159 | (CVMX_PCSXX_CONTROL1_REG(interface), union cvmx_pcsxx_control1_reg, | ||
160 | reset, ==, 0, 10000)) | ||
161 | return -1; | ||
162 | /* Wait for PCS to be aligned */ | ||
163 | if (CVMX_WAIT_FOR_FIELD64 | ||
164 | (CVMX_PCSXX_10GBX_STATUS_REG(interface), | ||
165 | union cvmx_pcsxx_10gbx_status_reg, alignd, ==, 1, 10000)) | ||
166 | return -1; | ||
167 | /* Wait for RX to be ready */ | ||
168 | if (CVMX_WAIT_FOR_FIELD64 | ||
169 | (CVMX_GMXX_RX_XAUI_CTL(interface), union cvmx_gmxx_rx_xaui_ctl, | ||
170 | status, ==, 0, 10000)) | ||
171 | return -1; | ||
172 | |||
173 | /* (6) Configure GMX */ | ||
174 | gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(0, interface)); | ||
175 | gmx_cfg.s.en = 0; | ||
176 | cvmx_write_csr(CVMX_GMXX_PRTX_CFG(0, interface), gmx_cfg.u64); | ||
177 | |||
178 | /* Wait for GMX RX to be idle */ | ||
179 | if (CVMX_WAIT_FOR_FIELD64 | ||
180 | (CVMX_GMXX_PRTX_CFG(0, interface), union cvmx_gmxx_prtx_cfg, | ||
181 | rx_idle, ==, 1, 10000)) | ||
182 | return -1; | ||
183 | /* Wait for GMX TX to be idle */ | ||
184 | if (CVMX_WAIT_FOR_FIELD64 | ||
185 | (CVMX_GMXX_PRTX_CFG(0, interface), union cvmx_gmxx_prtx_cfg, | ||
186 | tx_idle, ==, 1, 10000)) | ||
187 | return -1; | ||
188 | |||
189 | /* GMX configure */ | ||
190 | gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(0, interface)); | ||
191 | gmx_cfg.s.speed = 1; | ||
192 | gmx_cfg.s.speed_msb = 0; | ||
193 | gmx_cfg.s.slottime = 1; | ||
194 | cvmx_write_csr(CVMX_GMXX_TX_PRTS(interface), 1); | ||
195 | cvmx_write_csr(CVMX_GMXX_TXX_SLOT(0, interface), 512); | ||
196 | cvmx_write_csr(CVMX_GMXX_TXX_BURST(0, interface), 8192); | ||
197 | cvmx_write_csr(CVMX_GMXX_PRTX_CFG(0, interface), gmx_cfg.u64); | ||
198 | |||
199 | /* (7) Clear out any error state */ | ||
200 | cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(0, interface), | ||
201 | cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(0, interface))); | ||
202 | cvmx_write_csr(CVMX_GMXX_TX_INT_REG(interface), | ||
203 | cvmx_read_csr(CVMX_GMXX_TX_INT_REG(interface))); | ||
204 | cvmx_write_csr(CVMX_PCSXX_INT_REG(interface), | ||
205 | cvmx_read_csr(CVMX_PCSXX_INT_REG(interface))); | ||
206 | |||
207 | /* Wait for receive link */ | ||
208 | if (CVMX_WAIT_FOR_FIELD64 | ||
209 | (CVMX_PCSXX_STATUS1_REG(interface), union cvmx_pcsxx_status1_reg, | ||
210 | rcv_lnk, ==, 1, 10000)) | ||
211 | return -1; | ||
212 | if (CVMX_WAIT_FOR_FIELD64 | ||
213 | (CVMX_PCSXX_STATUS2_REG(interface), union cvmx_pcsxx_status2_reg, | ||
214 | xmtflt, ==, 0, 10000)) | ||
215 | return -1; | ||
216 | if (CVMX_WAIT_FOR_FIELD64 | ||
217 | (CVMX_PCSXX_STATUS2_REG(interface), union cvmx_pcsxx_status2_reg, | ||
218 | rcvflt, ==, 0, 10000)) | ||
219 | return -1; | ||
220 | |||
221 | cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(0, interface), gmx_rx_int_en.u64); | ||
222 | cvmx_write_csr(CVMX_GMXX_TX_INT_EN(interface), gmx_tx_int_en.u64); | ||
223 | cvmx_write_csr(CVMX_PCSXX_INT_EN_REG(interface), pcsx_int_en_reg.u64); | ||
224 | |||
225 | cvmx_helper_link_autoconf(cvmx_helper_get_ipd_port(interface, 0)); | ||
226 | |||
227 | /* (8) Enable packet reception */ | ||
228 | xauiMiscCtl.s.gmxeno = 0; | ||
229 | cvmx_write_csr(CVMX_PCSXX_MISC_CTL_REG(interface), xauiMiscCtl.u64); | ||
230 | |||
231 | gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(0, interface)); | ||
232 | gmx_cfg.s.en = 1; | ||
233 | cvmx_write_csr(CVMX_GMXX_PRTX_CFG(0, interface), gmx_cfg.u64); | ||
234 | |||
235 | __cvmx_interrupt_pcsx_intx_en_reg_enable(0, interface); | ||
236 | __cvmx_interrupt_pcsx_intx_en_reg_enable(1, interface); | ||
237 | __cvmx_interrupt_pcsx_intx_en_reg_enable(2, interface); | ||
238 | __cvmx_interrupt_pcsx_intx_en_reg_enable(3, interface); | ||
239 | __cvmx_interrupt_pcsxx_int_en_reg_enable(interface); | ||
240 | __cvmx_interrupt_gmxx_enable(interface); | ||
241 | |||
242 | return 0; | ||
243 | } | ||
244 | |||
245 | /** | ||
246 | * Return the link state of an IPD/PKO port as returned by | ||
247 | * auto negotiation. The result of this function may not match | ||
248 | * Octeon's link config if auto negotiation has changed since | ||
249 | * the last call to cvmx_helper_link_set(). | ||
250 | * | ||
251 | * @ipd_port: IPD/PKO port to query | ||
252 | * | ||
253 | * Returns Link state | ||
254 | */ | ||
255 | cvmx_helper_link_info_t __cvmx_helper_xaui_link_get(int ipd_port) | ||
256 | { | ||
257 | int interface = cvmx_helper_get_interface_num(ipd_port); | ||
258 | union cvmx_gmxx_tx_xaui_ctl gmxx_tx_xaui_ctl; | ||
259 | union cvmx_gmxx_rx_xaui_ctl gmxx_rx_xaui_ctl; | ||
260 | union cvmx_pcsxx_status1_reg pcsxx_status1_reg; | ||
261 | cvmx_helper_link_info_t result; | ||
262 | |||
263 | gmxx_tx_xaui_ctl.u64 = cvmx_read_csr(CVMX_GMXX_TX_XAUI_CTL(interface)); | ||
264 | gmxx_rx_xaui_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RX_XAUI_CTL(interface)); | ||
265 | pcsxx_status1_reg.u64 = | ||
266 | cvmx_read_csr(CVMX_PCSXX_STATUS1_REG(interface)); | ||
267 | result.u64 = 0; | ||
268 | |||
269 | /* Only return a link if both RX and TX are happy */ | ||
270 | if ((gmxx_tx_xaui_ctl.s.ls == 0) && (gmxx_rx_xaui_ctl.s.status == 0) && | ||
271 | (pcsxx_status1_reg.s.rcv_lnk == 1)) { | ||
272 | result.s.link_up = 1; | ||
273 | result.s.full_duplex = 1; | ||
274 | result.s.speed = 10000; | ||
275 | } else { | ||
276 | /* Disable GMX and PCSX interrupts. */ | ||
277 | cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(0, interface), 0x0); | ||
278 | cvmx_write_csr(CVMX_GMXX_TX_INT_EN(interface), 0x0); | ||
279 | cvmx_write_csr(CVMX_PCSXX_INT_EN_REG(interface), 0x0); | ||
280 | } | ||
281 | return result; | ||
282 | } | ||
283 | |||
284 | /** | ||
285 | * Configure an IPD/PKO port for the specified link state. This | ||
286 | * function does not influence auto negotiation at the PHY level. | ||
287 | * The passed link state must always match the link state returned | ||
288 | * by cvmx_helper_link_get(). It is normally best to use | ||
289 | * cvmx_helper_link_autoconf() instead. | ||
290 | * | ||
291 | * @ipd_port: IPD/PKO port to configure | ||
292 | * @link_info: The new link state | ||
293 | * | ||
294 | * Returns Zero on success, negative on failure | ||
295 | */ | ||
296 | int __cvmx_helper_xaui_link_set(int ipd_port, cvmx_helper_link_info_t link_info) | ||
297 | { | ||
298 | int interface = cvmx_helper_get_interface_num(ipd_port); | ||
299 | union cvmx_gmxx_tx_xaui_ctl gmxx_tx_xaui_ctl; | ||
300 | union cvmx_gmxx_rx_xaui_ctl gmxx_rx_xaui_ctl; | ||
301 | |||
302 | gmxx_tx_xaui_ctl.u64 = cvmx_read_csr(CVMX_GMXX_TX_XAUI_CTL(interface)); | ||
303 | gmxx_rx_xaui_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RX_XAUI_CTL(interface)); | ||
304 | |||
305 | /* If the link shouldn't be up, then just return */ | ||
306 | if (!link_info.s.link_up) | ||
307 | return 0; | ||
308 | |||
309 | /* Do nothing if both RX and TX are happy */ | ||
310 | if ((gmxx_tx_xaui_ctl.s.ls == 0) && (gmxx_rx_xaui_ctl.s.status == 0)) | ||
311 | return 0; | ||
312 | |||
313 | /* Bring the link up */ | ||
314 | return __cvmx_helper_xaui_enable(interface); | ||
315 | } | ||
316 | |||
317 | /** | ||
318 | * Configure a port for internal and/or external loopback. Internal loopback | ||
319 | * causes packets sent by the port to be received by Octeon. External loopback | ||
320 | * causes packets received from the wire to sent out again. | ||
321 | * | ||
322 | * @ipd_port: IPD/PKO port to loopback. | ||
323 | * @enable_internal: | ||
324 | * Non zero if you want internal loopback | ||
325 | * @enable_external: | ||
326 | * Non zero if you want external loopback | ||
327 | * | ||
328 | * Returns Zero on success, negative on failure. | ||
329 | */ | ||
330 | extern int __cvmx_helper_xaui_configure_loopback(int ipd_port, | ||
331 | int enable_internal, | ||
332 | int enable_external) | ||
333 | { | ||
334 | int interface = cvmx_helper_get_interface_num(ipd_port); | ||
335 | union cvmx_pcsxx_control1_reg pcsxx_control1_reg; | ||
336 | union cvmx_gmxx_xaui_ext_loopback gmxx_xaui_ext_loopback; | ||
337 | |||
338 | /* Set the internal loop */ | ||
339 | pcsxx_control1_reg.u64 = | ||
340 | cvmx_read_csr(CVMX_PCSXX_CONTROL1_REG(interface)); | ||
341 | pcsxx_control1_reg.s.loopbck1 = enable_internal; | ||
342 | cvmx_write_csr(CVMX_PCSXX_CONTROL1_REG(interface), | ||
343 | pcsxx_control1_reg.u64); | ||
344 | |||
345 | /* Set the external loop */ | ||
346 | gmxx_xaui_ext_loopback.u64 = | ||
347 | cvmx_read_csr(CVMX_GMXX_XAUI_EXT_LOOPBACK(interface)); | ||
348 | gmxx_xaui_ext_loopback.s.en = enable_external; | ||
349 | cvmx_write_csr(CVMX_GMXX_XAUI_EXT_LOOPBACK(interface), | ||
350 | gmxx_xaui_ext_loopback.u64); | ||
351 | |||
352 | /* Take the link through a reset */ | ||
353 | return __cvmx_helper_xaui_enable(interface); | ||
354 | } | ||
diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper.c b/arch/mips/cavium-octeon/executive/cvmx-helper.c new file mode 100644 index 000000000000..fa4963856353 --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-helper.c | |||
@@ -0,0 +1,1116 @@ | |||
1 | /***********************license start*************** | ||
2 | * Author: Cavium Networks | ||
3 | * | ||
4 | * Contact: support@caviumnetworks.com | ||
5 | * This file is part of the OCTEON SDK | ||
6 | * | ||
7 | * Copyright (c) 2003-2008 Cavium Networks | ||
8 | * | ||
9 | * This file is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License, Version 2, as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * This file is distributed in the hope that it will be useful, but | ||
14 | * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty | ||
15 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or | ||
16 | * NONINFRINGEMENT. See the GNU General Public License for more | ||
17 | * details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this file; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
22 | * or visit http://www.gnu.org/licenses/. | ||
23 | * | ||
24 | * This file may also be available under a different license from Cavium. | ||
25 | * Contact Cavium Networks for more information | ||
26 | ***********************license end**************************************/ | ||
27 | |||
28 | /* | ||
29 | * | ||
30 | * Helper functions for common, but complicated tasks. | ||
31 | * | ||
32 | */ | ||
33 | #include <asm/octeon/octeon.h> | ||
34 | |||
35 | #include <asm/octeon/cvmx-config.h> | ||
36 | |||
37 | #include <asm/octeon/cvmx-fpa.h> | ||
38 | #include <asm/octeon/cvmx-pip.h> | ||
39 | #include <asm/octeon/cvmx-pko.h> | ||
40 | #include <asm/octeon/cvmx-ipd.h> | ||
41 | #include <asm/octeon/cvmx-spi.h> | ||
42 | #include <asm/octeon/cvmx-helper.h> | ||
43 | #include <asm/octeon/cvmx-helper-board.h> | ||
44 | |||
45 | #include <asm/octeon/cvmx-pip-defs.h> | ||
46 | #include <asm/octeon/cvmx-smix-defs.h> | ||
47 | #include <asm/octeon/cvmx-asxx-defs.h> | ||
48 | |||
49 | /** | ||
50 | * cvmx_override_pko_queue_priority(int ipd_port, uint64_t | ||
51 | * priorities[16]) is a function pointer. It is meant to allow | ||
52 | * customization of the PKO queue priorities based on the port | ||
53 | * number. Users should set this pointer to a function before | ||
54 | * calling any cvmx-helper operations. | ||
55 | */ | ||
56 | void (*cvmx_override_pko_queue_priority) (int pko_port, | ||
57 | uint64_t priorities[16]); | ||
58 | |||
59 | /** | ||
60 | * cvmx_override_ipd_port_setup(int ipd_port) is a function | ||
61 | * pointer. It is meant to allow customization of the IPD port | ||
62 | * setup before packet input/output comes online. It is called | ||
63 | * after cvmx-helper does the default IPD configuration, but | ||
64 | * before IPD is enabled. Users should set this pointer to a | ||
65 | * function before calling any cvmx-helper operations. | ||
66 | */ | ||
67 | void (*cvmx_override_ipd_port_setup) (int ipd_port); | ||
68 | |||
69 | /* Port count per interface */ | ||
70 | static int interface_port_count[4] = { 0, 0, 0, 0 }; | ||
71 | |||
72 | /* Port last configured link info index by IPD/PKO port */ | ||
73 | static cvmx_helper_link_info_t | ||
74 | port_link_info[CVMX_PIP_NUM_INPUT_PORTS]; | ||
75 | |||
76 | /** | ||
77 | * Return the number of interfaces the chip has. Each interface | ||
78 | * may have multiple ports. Most chips support two interfaces, | ||
79 | * but the CNX0XX and CNX1XX are exceptions. These only support | ||
80 | * one interface. | ||
81 | * | ||
82 | * Returns Number of interfaces on chip | ||
83 | */ | ||
84 | int cvmx_helper_get_number_of_interfaces(void) | ||
85 | { | ||
86 | if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN52XX)) | ||
87 | return 4; | ||
88 | else | ||
89 | return 3; | ||
90 | } | ||
91 | |||
92 | /** | ||
93 | * Return the number of ports on an interface. Depending on the | ||
94 | * chip and configuration, this can be 1-16. A value of 0 | ||
95 | * specifies that the interface doesn't exist or isn't usable. | ||
96 | * | ||
97 | * @interface: Interface to get the port count for | ||
98 | * | ||
99 | * Returns Number of ports on interface. Can be Zero. | ||
100 | */ | ||
101 | int cvmx_helper_ports_on_interface(int interface) | ||
102 | { | ||
103 | return interface_port_count[interface]; | ||
104 | } | ||
105 | |||
106 | /** | ||
107 | * Get the operating mode of an interface. Depending on the Octeon | ||
108 | * chip and configuration, this function returns an enumeration | ||
109 | * of the type of packet I/O supported by an interface. | ||
110 | * | ||
111 | * @interface: Interface to probe | ||
112 | * | ||
113 | * Returns Mode of the interface. Unknown or unsupported interfaces return | ||
114 | * DISABLED. | ||
115 | */ | ||
116 | cvmx_helper_interface_mode_t cvmx_helper_interface_get_mode(int interface) | ||
117 | { | ||
118 | union cvmx_gmxx_inf_mode mode; | ||
119 | if (interface == 2) | ||
120 | return CVMX_HELPER_INTERFACE_MODE_NPI; | ||
121 | |||
122 | if (interface == 3) { | ||
123 | if (OCTEON_IS_MODEL(OCTEON_CN56XX) | ||
124 | || OCTEON_IS_MODEL(OCTEON_CN52XX)) | ||
125 | return CVMX_HELPER_INTERFACE_MODE_LOOP; | ||
126 | else | ||
127 | return CVMX_HELPER_INTERFACE_MODE_DISABLED; | ||
128 | } | ||
129 | |||
130 | if (interface == 0 | ||
131 | && cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_CN3005_EVB_HS5 | ||
132 | && cvmx_sysinfo_get()->board_rev_major == 1) { | ||
133 | /* | ||
134 | * Lie about interface type of CN3005 board. This | ||
135 | * board has a switch on port 1 like the other | ||
136 | * evaluation boards, but it is connected over RGMII | ||
137 | * instead of GMII. Report GMII mode so that the | ||
138 | * speed is forced to 1 Gbit full duplex. Other than | ||
139 | * some initial configuration (which does not use the | ||
140 | * output of this function) there is no difference in | ||
141 | * setup between GMII and RGMII modes. | ||
142 | */ | ||
143 | return CVMX_HELPER_INTERFACE_MODE_GMII; | ||
144 | } | ||
145 | |||
146 | /* Interface 1 is always disabled on CN31XX and CN30XX */ | ||
147 | if ((interface == 1) | ||
148 | && (OCTEON_IS_MODEL(OCTEON_CN31XX) || OCTEON_IS_MODEL(OCTEON_CN30XX) | ||
149 | || OCTEON_IS_MODEL(OCTEON_CN50XX) | ||
150 | || OCTEON_IS_MODEL(OCTEON_CN52XX))) | ||
151 | return CVMX_HELPER_INTERFACE_MODE_DISABLED; | ||
152 | |||
153 | mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface)); | ||
154 | |||
155 | if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN52XX)) { | ||
156 | switch (mode.cn56xx.mode) { | ||
157 | case 0: | ||
158 | return CVMX_HELPER_INTERFACE_MODE_DISABLED; | ||
159 | case 1: | ||
160 | return CVMX_HELPER_INTERFACE_MODE_XAUI; | ||
161 | case 2: | ||
162 | return CVMX_HELPER_INTERFACE_MODE_SGMII; | ||
163 | case 3: | ||
164 | return CVMX_HELPER_INTERFACE_MODE_PICMG; | ||
165 | default: | ||
166 | return CVMX_HELPER_INTERFACE_MODE_DISABLED; | ||
167 | } | ||
168 | } else { | ||
169 | if (!mode.s.en) | ||
170 | return CVMX_HELPER_INTERFACE_MODE_DISABLED; | ||
171 | |||
172 | if (mode.s.type) { | ||
173 | if (OCTEON_IS_MODEL(OCTEON_CN38XX) | ||
174 | || OCTEON_IS_MODEL(OCTEON_CN58XX)) | ||
175 | return CVMX_HELPER_INTERFACE_MODE_SPI; | ||
176 | else | ||
177 | return CVMX_HELPER_INTERFACE_MODE_GMII; | ||
178 | } else | ||
179 | return CVMX_HELPER_INTERFACE_MODE_RGMII; | ||
180 | } | ||
181 | } | ||
182 | |||
183 | /** | ||
184 | * Configure the IPD/PIP tagging and QoS options for a specific | ||
185 | * port. This function determines the POW work queue entry | ||
186 | * contents for a port. The setup performed here is controlled by | ||
187 | * the defines in executive-config.h. | ||
188 | * | ||
189 | * @ipd_port: Port to configure. This follows the IPD numbering, not the | ||
190 | * per interface numbering | ||
191 | * | ||
192 | * Returns Zero on success, negative on failure | ||
193 | */ | ||
194 | static int __cvmx_helper_port_setup_ipd(int ipd_port) | ||
195 | { | ||
196 | union cvmx_pip_prt_cfgx port_config; | ||
197 | union cvmx_pip_prt_tagx tag_config; | ||
198 | |||
199 | port_config.u64 = cvmx_read_csr(CVMX_PIP_PRT_CFGX(ipd_port)); | ||
200 | tag_config.u64 = cvmx_read_csr(CVMX_PIP_PRT_TAGX(ipd_port)); | ||
201 | |||
202 | /* Have each port go to a different POW queue */ | ||
203 | port_config.s.qos = ipd_port & 0x7; | ||
204 | |||
205 | /* Process the headers and place the IP header in the work queue */ | ||
206 | port_config.s.mode = CVMX_HELPER_INPUT_PORT_SKIP_MODE; | ||
207 | |||
208 | tag_config.s.ip6_src_flag = CVMX_HELPER_INPUT_TAG_IPV6_SRC_IP; | ||
209 | tag_config.s.ip6_dst_flag = CVMX_HELPER_INPUT_TAG_IPV6_DST_IP; | ||
210 | tag_config.s.ip6_sprt_flag = CVMX_HELPER_INPUT_TAG_IPV6_SRC_PORT; | ||
211 | tag_config.s.ip6_dprt_flag = CVMX_HELPER_INPUT_TAG_IPV6_DST_PORT; | ||
212 | tag_config.s.ip6_nxth_flag = CVMX_HELPER_INPUT_TAG_IPV6_NEXT_HEADER; | ||
213 | tag_config.s.ip4_src_flag = CVMX_HELPER_INPUT_TAG_IPV4_SRC_IP; | ||
214 | tag_config.s.ip4_dst_flag = CVMX_HELPER_INPUT_TAG_IPV4_DST_IP; | ||
215 | tag_config.s.ip4_sprt_flag = CVMX_HELPER_INPUT_TAG_IPV4_SRC_PORT; | ||
216 | tag_config.s.ip4_dprt_flag = CVMX_HELPER_INPUT_TAG_IPV4_DST_PORT; | ||
217 | tag_config.s.ip4_pctl_flag = CVMX_HELPER_INPUT_TAG_IPV4_PROTOCOL; | ||
218 | tag_config.s.inc_prt_flag = CVMX_HELPER_INPUT_TAG_INPUT_PORT; | ||
219 | tag_config.s.tcp6_tag_type = CVMX_HELPER_INPUT_TAG_TYPE; | ||
220 | tag_config.s.tcp4_tag_type = CVMX_HELPER_INPUT_TAG_TYPE; | ||
221 | tag_config.s.ip6_tag_type = CVMX_HELPER_INPUT_TAG_TYPE; | ||
222 | tag_config.s.ip4_tag_type = CVMX_HELPER_INPUT_TAG_TYPE; | ||
223 | tag_config.s.non_tag_type = CVMX_HELPER_INPUT_TAG_TYPE; | ||
224 | /* Put all packets in group 0. Other groups can be used by the app */ | ||
225 | tag_config.s.grp = 0; | ||
226 | |||
227 | cvmx_pip_config_port(ipd_port, port_config, tag_config); | ||
228 | |||
229 | /* Give the user a chance to override our setting for each port */ | ||
230 | if (cvmx_override_ipd_port_setup) | ||
231 | cvmx_override_ipd_port_setup(ipd_port); | ||
232 | |||
233 | return 0; | ||
234 | } | ||
235 | |||
236 | /** | ||
237 | * This function sets the interface_port_count[interface] correctly, | ||
238 | * without modifying any hardware configuration. Hardware setup of | ||
239 | * the ports will be performed later. | ||
240 | * | ||
241 | * @interface: Interface to probe | ||
242 | * | ||
243 | * Returns Zero on success, negative on failure | ||
244 | */ | ||
245 | int cvmx_helper_interface_enumerate(int interface) | ||
246 | { | ||
247 | switch (cvmx_helper_interface_get_mode(interface)) { | ||
248 | /* These types don't support ports to IPD/PKO */ | ||
249 | case CVMX_HELPER_INTERFACE_MODE_DISABLED: | ||
250 | case CVMX_HELPER_INTERFACE_MODE_PCIE: | ||
251 | interface_port_count[interface] = 0; | ||
252 | break; | ||
253 | /* XAUI is a single high speed port */ | ||
254 | case CVMX_HELPER_INTERFACE_MODE_XAUI: | ||
255 | interface_port_count[interface] = | ||
256 | __cvmx_helper_xaui_enumerate(interface); | ||
257 | break; | ||
258 | /* | ||
259 | * RGMII/GMII/MII are all treated about the same. Most | ||
260 | * functions refer to these ports as RGMII. | ||
261 | */ | ||
262 | case CVMX_HELPER_INTERFACE_MODE_RGMII: | ||
263 | case CVMX_HELPER_INTERFACE_MODE_GMII: | ||
264 | interface_port_count[interface] = | ||
265 | __cvmx_helper_rgmii_enumerate(interface); | ||
266 | break; | ||
267 | /* | ||
268 | * SPI4 can have 1-16 ports depending on the device at | ||
269 | * the other end. | ||
270 | */ | ||
271 | case CVMX_HELPER_INTERFACE_MODE_SPI: | ||
272 | interface_port_count[interface] = | ||
273 | __cvmx_helper_spi_enumerate(interface); | ||
274 | break; | ||
275 | /* | ||
276 | * SGMII can have 1-4 ports depending on how many are | ||
277 | * hooked up. | ||
278 | */ | ||
279 | case CVMX_HELPER_INTERFACE_MODE_SGMII: | ||
280 | case CVMX_HELPER_INTERFACE_MODE_PICMG: | ||
281 | interface_port_count[interface] = | ||
282 | __cvmx_helper_sgmii_enumerate(interface); | ||
283 | break; | ||
284 | /* PCI target Network Packet Interface */ | ||
285 | case CVMX_HELPER_INTERFACE_MODE_NPI: | ||
286 | interface_port_count[interface] = | ||
287 | __cvmx_helper_npi_enumerate(interface); | ||
288 | break; | ||
289 | /* | ||
290 | * Special loopback only ports. These are not the same | ||
291 | * as other ports in loopback mode. | ||
292 | */ | ||
293 | case CVMX_HELPER_INTERFACE_MODE_LOOP: | ||
294 | interface_port_count[interface] = | ||
295 | __cvmx_helper_loop_enumerate(interface); | ||
296 | break; | ||
297 | } | ||
298 | |||
299 | interface_port_count[interface] = | ||
300 | __cvmx_helper_board_interface_probe(interface, | ||
301 | interface_port_count | ||
302 | [interface]); | ||
303 | |||
304 | /* Make sure all global variables propagate to other cores */ | ||
305 | CVMX_SYNCWS; | ||
306 | |||
307 | return 0; | ||
308 | } | ||
309 | |||
310 | /** | ||
311 | * This function probes an interface to determine the actual | ||
312 | * number of hardware ports connected to it. It doesn't setup the | ||
313 | * ports or enable them. The main goal here is to set the global | ||
314 | * interface_port_count[interface] correctly. Hardware setup of the | ||
315 | * ports will be performed later. | ||
316 | * | ||
317 | * @interface: Interface to probe | ||
318 | * | ||
319 | * Returns Zero on success, negative on failure | ||
320 | */ | ||
321 | int cvmx_helper_interface_probe(int interface) | ||
322 | { | ||
323 | cvmx_helper_interface_enumerate(interface); | ||
324 | /* At this stage in the game we don't want packets to be moving yet. | ||
325 | The following probe calls should perform hardware setup | ||
326 | needed to determine port counts. Receive must still be disabled */ | ||
327 | switch (cvmx_helper_interface_get_mode(interface)) { | ||
328 | /* These types don't support ports to IPD/PKO */ | ||
329 | case CVMX_HELPER_INTERFACE_MODE_DISABLED: | ||
330 | case CVMX_HELPER_INTERFACE_MODE_PCIE: | ||
331 | break; | ||
332 | /* XAUI is a single high speed port */ | ||
333 | case CVMX_HELPER_INTERFACE_MODE_XAUI: | ||
334 | __cvmx_helper_xaui_probe(interface); | ||
335 | break; | ||
336 | /* | ||
337 | * RGMII/GMII/MII are all treated about the same. Most | ||
338 | * functions refer to these ports as RGMII. | ||
339 | */ | ||
340 | case CVMX_HELPER_INTERFACE_MODE_RGMII: | ||
341 | case CVMX_HELPER_INTERFACE_MODE_GMII: | ||
342 | __cvmx_helper_rgmii_probe(interface); | ||
343 | break; | ||
344 | /* | ||
345 | * SPI4 can have 1-16 ports depending on the device at | ||
346 | * the other end. | ||
347 | */ | ||
348 | case CVMX_HELPER_INTERFACE_MODE_SPI: | ||
349 | __cvmx_helper_spi_probe(interface); | ||
350 | break; | ||
351 | /* | ||
352 | * SGMII can have 1-4 ports depending on how many are | ||
353 | * hooked up. | ||
354 | */ | ||
355 | case CVMX_HELPER_INTERFACE_MODE_SGMII: | ||
356 | case CVMX_HELPER_INTERFACE_MODE_PICMG: | ||
357 | __cvmx_helper_sgmii_probe(interface); | ||
358 | break; | ||
359 | /* PCI target Network Packet Interface */ | ||
360 | case CVMX_HELPER_INTERFACE_MODE_NPI: | ||
361 | __cvmx_helper_npi_probe(interface); | ||
362 | break; | ||
363 | /* | ||
364 | * Special loopback only ports. These are not the same | ||
365 | * as other ports in loopback mode. | ||
366 | */ | ||
367 | case CVMX_HELPER_INTERFACE_MODE_LOOP: | ||
368 | __cvmx_helper_loop_probe(interface); | ||
369 | break; | ||
370 | } | ||
371 | |||
372 | /* Make sure all global variables propagate to other cores */ | ||
373 | CVMX_SYNCWS; | ||
374 | |||
375 | return 0; | ||
376 | } | ||
377 | |||
378 | /** | ||
379 | * Setup the IPD/PIP for the ports on an interface. Packet | ||
380 | * classification and tagging are set for every port on the | ||
381 | * interface. The number of ports on the interface must already | ||
382 | * have been probed. | ||
383 | * | ||
384 | * @interface: Interface to setup IPD/PIP for | ||
385 | * | ||
386 | * Returns Zero on success, negative on failure | ||
387 | */ | ||
388 | static int __cvmx_helper_interface_setup_ipd(int interface) | ||
389 | { | ||
390 | int ipd_port = cvmx_helper_get_ipd_port(interface, 0); | ||
391 | int num_ports = interface_port_count[interface]; | ||
392 | |||
393 | while (num_ports--) { | ||
394 | __cvmx_helper_port_setup_ipd(ipd_port); | ||
395 | ipd_port++; | ||
396 | } | ||
397 | return 0; | ||
398 | } | ||
399 | |||
400 | /** | ||
401 | * Setup global setting for IPD/PIP not related to a specific | ||
402 | * interface or port. This must be called before IPD is enabled. | ||
403 | * | ||
404 | * Returns Zero on success, negative on failure. | ||
405 | */ | ||
406 | static int __cvmx_helper_global_setup_ipd(void) | ||
407 | { | ||
408 | /* Setup the global packet input options */ | ||
409 | cvmx_ipd_config(CVMX_FPA_PACKET_POOL_SIZE / 8, | ||
410 | CVMX_HELPER_FIRST_MBUFF_SKIP / 8, | ||
411 | CVMX_HELPER_NOT_FIRST_MBUFF_SKIP / 8, | ||
412 | /* The +8 is to account for the next ptr */ | ||
413 | (CVMX_HELPER_FIRST_MBUFF_SKIP + 8) / 128, | ||
414 | /* The +8 is to account for the next ptr */ | ||
415 | (CVMX_HELPER_NOT_FIRST_MBUFF_SKIP + 8) / 128, | ||
416 | CVMX_FPA_WQE_POOL, | ||
417 | CVMX_IPD_OPC_MODE_STT, | ||
418 | CVMX_HELPER_ENABLE_BACK_PRESSURE); | ||
419 | return 0; | ||
420 | } | ||
421 | |||
422 | /** | ||
423 | * Setup the PKO for the ports on an interface. The number of | ||
424 | * queues per port and the priority of each PKO output queue | ||
425 | * is set here. PKO must be disabled when this function is called. | ||
426 | * | ||
427 | * @interface: Interface to setup PKO for | ||
428 | * | ||
429 | * Returns Zero on success, negative on failure | ||
430 | */ | ||
431 | static int __cvmx_helper_interface_setup_pko(int interface) | ||
432 | { | ||
433 | /* | ||
434 | * Each packet output queue has an associated priority. The | ||
435 | * higher the priority, the more often it can send a packet. A | ||
436 | * priority of 8 means it can send in all 8 rounds of | ||
437 | * contention. We're going to make each queue one less than | ||
438 | * the last. The vector of priorities has been extended to | ||
439 | * support CN5xxx CPUs, where up to 16 queues can be | ||
440 | * associated to a port. To keep backward compatibility we | ||
441 | * don't change the initial 8 priorities and replicate them in | ||
442 | * the second half. With per-core PKO queues (PKO lockless | ||
443 | * operation) all queues have the same priority. | ||
444 | */ | ||
445 | uint64_t priorities[16] = | ||
446 | { 8, 7, 6, 5, 4, 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, 1 }; | ||
447 | |||
448 | /* | ||
449 | * Setup the IPD/PIP and PKO for the ports discovered | ||
450 | * above. Here packet classification, tagging and output | ||
451 | * priorities are set. | ||
452 | */ | ||
453 | int ipd_port = cvmx_helper_get_ipd_port(interface, 0); | ||
454 | int num_ports = interface_port_count[interface]; | ||
455 | while (num_ports--) { | ||
456 | /* | ||
457 | * Give the user a chance to override the per queue | ||
458 | * priorities. | ||
459 | */ | ||
460 | if (cvmx_override_pko_queue_priority) | ||
461 | cvmx_override_pko_queue_priority(ipd_port, priorities); | ||
462 | |||
463 | cvmx_pko_config_port(ipd_port, | ||
464 | cvmx_pko_get_base_queue_per_core(ipd_port, | ||
465 | 0), | ||
466 | cvmx_pko_get_num_queues(ipd_port), | ||
467 | priorities); | ||
468 | ipd_port++; | ||
469 | } | ||
470 | return 0; | ||
471 | } | ||
472 | |||
473 | /** | ||
474 | * Setup global setting for PKO not related to a specific | ||
475 | * interface or port. This must be called before PKO is enabled. | ||
476 | * | ||
477 | * Returns Zero on success, negative on failure. | ||
478 | */ | ||
479 | static int __cvmx_helper_global_setup_pko(void) | ||
480 | { | ||
481 | /* | ||
482 | * Disable tagwait FAU timeout. This needs to be done before | ||
483 | * anyone might start packet output using tags. | ||
484 | */ | ||
485 | union cvmx_iob_fau_timeout fau_to; | ||
486 | fau_to.u64 = 0; | ||
487 | fau_to.s.tout_val = 0xfff; | ||
488 | fau_to.s.tout_enb = 0; | ||
489 | cvmx_write_csr(CVMX_IOB_FAU_TIMEOUT, fau_to.u64); | ||
490 | return 0; | ||
491 | } | ||
492 | |||
493 | /** | ||
494 | * Setup global backpressure setting. | ||
495 | * | ||
496 | * Returns Zero on success, negative on failure | ||
497 | */ | ||
498 | static int __cvmx_helper_global_setup_backpressure(void) | ||
499 | { | ||
500 | #if CVMX_HELPER_DISABLE_RGMII_BACKPRESSURE | ||
501 | /* Disable backpressure if configured to do so */ | ||
502 | /* Disable backpressure (pause frame) generation */ | ||
503 | int num_interfaces = cvmx_helper_get_number_of_interfaces(); | ||
504 | int interface; | ||
505 | for (interface = 0; interface < num_interfaces; interface++) { | ||
506 | switch (cvmx_helper_interface_get_mode(interface)) { | ||
507 | case CVMX_HELPER_INTERFACE_MODE_DISABLED: | ||
508 | case CVMX_HELPER_INTERFACE_MODE_PCIE: | ||
509 | case CVMX_HELPER_INTERFACE_MODE_NPI: | ||
510 | case CVMX_HELPER_INTERFACE_MODE_LOOP: | ||
511 | case CVMX_HELPER_INTERFACE_MODE_XAUI: | ||
512 | break; | ||
513 | case CVMX_HELPER_INTERFACE_MODE_RGMII: | ||
514 | case CVMX_HELPER_INTERFACE_MODE_GMII: | ||
515 | case CVMX_HELPER_INTERFACE_MODE_SPI: | ||
516 | case CVMX_HELPER_INTERFACE_MODE_SGMII: | ||
517 | case CVMX_HELPER_INTERFACE_MODE_PICMG: | ||
518 | cvmx_gmx_set_backpressure_override(interface, 0xf); | ||
519 | break; | ||
520 | } | ||
521 | } | ||
522 | #endif | ||
523 | |||
524 | return 0; | ||
525 | } | ||
526 | |||
527 | /** | ||
528 | * Enable packet input/output from the hardware. This function is | ||
529 | * called after all internal setup is complete and IPD is enabled. | ||
530 | * After this function completes, packets will be accepted from the | ||
531 | * hardware ports. PKO should still be disabled to make sure packets | ||
532 | * aren't sent out partially setup hardware. | ||
533 | * | ||
534 | * @interface: Interface to enable | ||
535 | * | ||
536 | * Returns Zero on success, negative on failure | ||
537 | */ | ||
538 | static int __cvmx_helper_packet_hardware_enable(int interface) | ||
539 | { | ||
540 | int result = 0; | ||
541 | switch (cvmx_helper_interface_get_mode(interface)) { | ||
542 | /* These types don't support ports to IPD/PKO */ | ||
543 | case CVMX_HELPER_INTERFACE_MODE_DISABLED: | ||
544 | case CVMX_HELPER_INTERFACE_MODE_PCIE: | ||
545 | /* Nothing to do */ | ||
546 | break; | ||
547 | /* XAUI is a single high speed port */ | ||
548 | case CVMX_HELPER_INTERFACE_MODE_XAUI: | ||
549 | result = __cvmx_helper_xaui_enable(interface); | ||
550 | break; | ||
551 | /* | ||
552 | * RGMII/GMII/MII are all treated about the same. Most | ||
553 | * functions refer to these ports as RGMII | ||
554 | */ | ||
555 | case CVMX_HELPER_INTERFACE_MODE_RGMII: | ||
556 | case CVMX_HELPER_INTERFACE_MODE_GMII: | ||
557 | result = __cvmx_helper_rgmii_enable(interface); | ||
558 | break; | ||
559 | /* | ||
560 | * SPI4 can have 1-16 ports depending on the device at | ||
561 | * the other end | ||
562 | */ | ||
563 | case CVMX_HELPER_INTERFACE_MODE_SPI: | ||
564 | result = __cvmx_helper_spi_enable(interface); | ||
565 | break; | ||
566 | /* | ||
567 | * SGMII can have 1-4 ports depending on how many are | ||
568 | * hooked up | ||
569 | */ | ||
570 | case CVMX_HELPER_INTERFACE_MODE_SGMII: | ||
571 | case CVMX_HELPER_INTERFACE_MODE_PICMG: | ||
572 | result = __cvmx_helper_sgmii_enable(interface); | ||
573 | break; | ||
574 | /* PCI target Network Packet Interface */ | ||
575 | case CVMX_HELPER_INTERFACE_MODE_NPI: | ||
576 | result = __cvmx_helper_npi_enable(interface); | ||
577 | break; | ||
578 | /* | ||
579 | * Special loopback only ports. These are not the same | ||
580 | * as other ports in loopback mode | ||
581 | */ | ||
582 | case CVMX_HELPER_INTERFACE_MODE_LOOP: | ||
583 | result = __cvmx_helper_loop_enable(interface); | ||
584 | break; | ||
585 | } | ||
586 | result |= __cvmx_helper_board_hardware_enable(interface); | ||
587 | return result; | ||
588 | } | ||
589 | |||
590 | /** | ||
591 | * Function to adjust internal IPD pointer alignments | ||
592 | * | ||
593 | * Returns 0 on success | ||
594 | * !0 on failure | ||
595 | */ | ||
596 | int __cvmx_helper_errata_fix_ipd_ptr_alignment(void) | ||
597 | { | ||
598 | #define FIX_IPD_FIRST_BUFF_PAYLOAD_BYTES \ | ||
599 | (CVMX_FPA_PACKET_POOL_SIZE-8-CVMX_HELPER_FIRST_MBUFF_SKIP) | ||
600 | #define FIX_IPD_NON_FIRST_BUFF_PAYLOAD_BYTES \ | ||
601 | (CVMX_FPA_PACKET_POOL_SIZE-8-CVMX_HELPER_NOT_FIRST_MBUFF_SKIP) | ||
602 | #define FIX_IPD_OUTPORT 0 | ||
603 | /* Ports 0-15 are interface 0, 16-31 are interface 1 */ | ||
604 | #define INTERFACE(port) (port >> 4) | ||
605 | #define INDEX(port) (port & 0xf) | ||
606 | uint64_t *p64; | ||
607 | cvmx_pko_command_word0_t pko_command; | ||
608 | union cvmx_buf_ptr g_buffer, pkt_buffer; | ||
609 | cvmx_wqe_t *work; | ||
610 | int size, num_segs = 0, wqe_pcnt, pkt_pcnt; | ||
611 | union cvmx_gmxx_prtx_cfg gmx_cfg; | ||
612 | int retry_cnt; | ||
613 | int retry_loop_cnt; | ||
614 | int i; | ||
615 | cvmx_helper_link_info_t link_info; | ||
616 | |||
617 | /* Save values for restore at end */ | ||
618 | uint64_t prtx_cfg = | ||
619 | cvmx_read_csr(CVMX_GMXX_PRTX_CFG | ||
620 | (INDEX(FIX_IPD_OUTPORT), INTERFACE(FIX_IPD_OUTPORT))); | ||
621 | uint64_t tx_ptr_en = | ||
622 | cvmx_read_csr(CVMX_ASXX_TX_PRT_EN(INTERFACE(FIX_IPD_OUTPORT))); | ||
623 | uint64_t rx_ptr_en = | ||
624 | cvmx_read_csr(CVMX_ASXX_RX_PRT_EN(INTERFACE(FIX_IPD_OUTPORT))); | ||
625 | uint64_t rxx_jabber = | ||
626 | cvmx_read_csr(CVMX_GMXX_RXX_JABBER | ||
627 | (INDEX(FIX_IPD_OUTPORT), INTERFACE(FIX_IPD_OUTPORT))); | ||
628 | uint64_t frame_max = | ||
629 | cvmx_read_csr(CVMX_GMXX_RXX_FRM_MAX | ||
630 | (INDEX(FIX_IPD_OUTPORT), INTERFACE(FIX_IPD_OUTPORT))); | ||
631 | |||
632 | /* Configure port to gig FDX as required for loopback mode */ | ||
633 | cvmx_helper_rgmii_internal_loopback(FIX_IPD_OUTPORT); | ||
634 | |||
635 | /* | ||
636 | * Disable reception on all ports so if traffic is present it | ||
637 | * will not interfere. | ||
638 | */ | ||
639 | cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(INTERFACE(FIX_IPD_OUTPORT)), 0); | ||
640 | |||
641 | cvmx_wait(100000000ull); | ||
642 | |||
643 | for (retry_loop_cnt = 0; retry_loop_cnt < 10; retry_loop_cnt++) { | ||
644 | retry_cnt = 100000; | ||
645 | wqe_pcnt = cvmx_read_csr(CVMX_IPD_PTR_COUNT); | ||
646 | pkt_pcnt = (wqe_pcnt >> 7) & 0x7f; | ||
647 | wqe_pcnt &= 0x7f; | ||
648 | |||
649 | num_segs = (2 + pkt_pcnt - wqe_pcnt) & 3; | ||
650 | |||
651 | if (num_segs == 0) | ||
652 | goto fix_ipd_exit; | ||
653 | |||
654 | num_segs += 1; | ||
655 | |||
656 | size = | ||
657 | FIX_IPD_FIRST_BUFF_PAYLOAD_BYTES + | ||
658 | ((num_segs - 1) * FIX_IPD_NON_FIRST_BUFF_PAYLOAD_BYTES) - | ||
659 | (FIX_IPD_NON_FIRST_BUFF_PAYLOAD_BYTES / 2); | ||
660 | |||
661 | cvmx_write_csr(CVMX_ASXX_PRT_LOOP(INTERFACE(FIX_IPD_OUTPORT)), | ||
662 | 1 << INDEX(FIX_IPD_OUTPORT)); | ||
663 | CVMX_SYNC; | ||
664 | |||
665 | g_buffer.u64 = 0; | ||
666 | g_buffer.s.addr = | ||
667 | cvmx_ptr_to_phys(cvmx_fpa_alloc(CVMX_FPA_WQE_POOL)); | ||
668 | if (g_buffer.s.addr == 0) { | ||
669 | cvmx_dprintf("WARNING: FIX_IPD_PTR_ALIGNMENT " | ||
670 | "buffer allocation failure.\n"); | ||
671 | goto fix_ipd_exit; | ||
672 | } | ||
673 | |||
674 | g_buffer.s.pool = CVMX_FPA_WQE_POOL; | ||
675 | g_buffer.s.size = num_segs; | ||
676 | |||
677 | pkt_buffer.u64 = 0; | ||
678 | pkt_buffer.s.addr = | ||
679 | cvmx_ptr_to_phys(cvmx_fpa_alloc(CVMX_FPA_PACKET_POOL)); | ||
680 | if (pkt_buffer.s.addr == 0) { | ||
681 | cvmx_dprintf("WARNING: FIX_IPD_PTR_ALIGNMENT " | ||
682 | "buffer allocation failure.\n"); | ||
683 | goto fix_ipd_exit; | ||
684 | } | ||
685 | pkt_buffer.s.i = 1; | ||
686 | pkt_buffer.s.pool = CVMX_FPA_PACKET_POOL; | ||
687 | pkt_buffer.s.size = FIX_IPD_FIRST_BUFF_PAYLOAD_BYTES; | ||
688 | |||
689 | p64 = (uint64_t *) cvmx_phys_to_ptr(pkt_buffer.s.addr); | ||
690 | p64[0] = 0xffffffffffff0000ull; | ||
691 | p64[1] = 0x08004510ull; | ||
692 | p64[2] = ((uint64_t) (size - 14) << 48) | 0x5ae740004000ull; | ||
693 | p64[3] = 0x3a5fc0a81073c0a8ull; | ||
694 | |||
695 | for (i = 0; i < num_segs; i++) { | ||
696 | if (i > 0) | ||
697 | pkt_buffer.s.size = | ||
698 | FIX_IPD_NON_FIRST_BUFF_PAYLOAD_BYTES; | ||
699 | |||
700 | if (i == (num_segs - 1)) | ||
701 | pkt_buffer.s.i = 0; | ||
702 | |||
703 | *(uint64_t *) cvmx_phys_to_ptr(g_buffer.s.addr + | ||
704 | 8 * i) = pkt_buffer.u64; | ||
705 | } | ||
706 | |||
707 | /* Build the PKO command */ | ||
708 | pko_command.u64 = 0; | ||
709 | pko_command.s.segs = num_segs; | ||
710 | pko_command.s.total_bytes = size; | ||
711 | pko_command.s.dontfree = 0; | ||
712 | pko_command.s.gather = 1; | ||
713 | |||
714 | gmx_cfg.u64 = | ||
715 | cvmx_read_csr(CVMX_GMXX_PRTX_CFG | ||
716 | (INDEX(FIX_IPD_OUTPORT), | ||
717 | INTERFACE(FIX_IPD_OUTPORT))); | ||
718 | gmx_cfg.s.en = 1; | ||
719 | cvmx_write_csr(CVMX_GMXX_PRTX_CFG | ||
720 | (INDEX(FIX_IPD_OUTPORT), | ||
721 | INTERFACE(FIX_IPD_OUTPORT)), gmx_cfg.u64); | ||
722 | cvmx_write_csr(CVMX_ASXX_TX_PRT_EN(INTERFACE(FIX_IPD_OUTPORT)), | ||
723 | 1 << INDEX(FIX_IPD_OUTPORT)); | ||
724 | cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(INTERFACE(FIX_IPD_OUTPORT)), | ||
725 | 1 << INDEX(FIX_IPD_OUTPORT)); | ||
726 | |||
727 | cvmx_write_csr(CVMX_GMXX_RXX_JABBER | ||
728 | (INDEX(FIX_IPD_OUTPORT), | ||
729 | INTERFACE(FIX_IPD_OUTPORT)), 65392 - 14 - 4); | ||
730 | cvmx_write_csr(CVMX_GMXX_RXX_FRM_MAX | ||
731 | (INDEX(FIX_IPD_OUTPORT), | ||
732 | INTERFACE(FIX_IPD_OUTPORT)), 65392 - 14 - 4); | ||
733 | |||
734 | cvmx_pko_send_packet_prepare(FIX_IPD_OUTPORT, | ||
735 | cvmx_pko_get_base_queue | ||
736 | (FIX_IPD_OUTPORT), | ||
737 | CVMX_PKO_LOCK_CMD_QUEUE); | ||
738 | cvmx_pko_send_packet_finish(FIX_IPD_OUTPORT, | ||
739 | cvmx_pko_get_base_queue | ||
740 | (FIX_IPD_OUTPORT), pko_command, | ||
741 | g_buffer, CVMX_PKO_LOCK_CMD_QUEUE); | ||
742 | |||
743 | CVMX_SYNC; | ||
744 | |||
745 | do { | ||
746 | work = cvmx_pow_work_request_sync(CVMX_POW_WAIT); | ||
747 | retry_cnt--; | ||
748 | } while ((work == NULL) && (retry_cnt > 0)); | ||
749 | |||
750 | if (!retry_cnt) | ||
751 | cvmx_dprintf("WARNING: FIX_IPD_PTR_ALIGNMENT " | ||
752 | "get_work() timeout occurred.\n"); | ||
753 | |||
754 | /* Free packet */ | ||
755 | if (work) | ||
756 | cvmx_helper_free_packet_data(work); | ||
757 | } | ||
758 | |||
759 | fix_ipd_exit: | ||
760 | |||
761 | /* Return CSR configs to saved values */ | ||
762 | cvmx_write_csr(CVMX_GMXX_PRTX_CFG | ||
763 | (INDEX(FIX_IPD_OUTPORT), INTERFACE(FIX_IPD_OUTPORT)), | ||
764 | prtx_cfg); | ||
765 | cvmx_write_csr(CVMX_ASXX_TX_PRT_EN(INTERFACE(FIX_IPD_OUTPORT)), | ||
766 | tx_ptr_en); | ||
767 | cvmx_write_csr(CVMX_ASXX_RX_PRT_EN(INTERFACE(FIX_IPD_OUTPORT)), | ||
768 | rx_ptr_en); | ||
769 | cvmx_write_csr(CVMX_GMXX_RXX_JABBER | ||
770 | (INDEX(FIX_IPD_OUTPORT), INTERFACE(FIX_IPD_OUTPORT)), | ||
771 | rxx_jabber); | ||
772 | cvmx_write_csr(CVMX_GMXX_RXX_FRM_MAX | ||
773 | (INDEX(FIX_IPD_OUTPORT), INTERFACE(FIX_IPD_OUTPORT)), | ||
774 | frame_max); | ||
775 | cvmx_write_csr(CVMX_ASXX_PRT_LOOP(INTERFACE(FIX_IPD_OUTPORT)), 0); | ||
776 | /* Set link to down so autonegotiation will set it up again */ | ||
777 | link_info.u64 = 0; | ||
778 | cvmx_helper_link_set(FIX_IPD_OUTPORT, link_info); | ||
779 | |||
780 | /* | ||
781 | * Bring the link back up as autonegotiation is not done in | ||
782 | * user applications. | ||
783 | */ | ||
784 | cvmx_helper_link_autoconf(FIX_IPD_OUTPORT); | ||
785 | |||
786 | CVMX_SYNC; | ||
787 | if (num_segs) | ||
788 | cvmx_dprintf("WARNING: FIX_IPD_PTR_ALIGNMENT failed.\n"); | ||
789 | |||
790 | return !!num_segs; | ||
791 | |||
792 | } | ||
793 | |||
794 | /** | ||
795 | * Called after all internal packet IO paths are setup. This | ||
796 | * function enables IPD/PIP and begins packet input and output. | ||
797 | * | ||
798 | * Returns Zero on success, negative on failure | ||
799 | */ | ||
800 | int cvmx_helper_ipd_and_packet_input_enable(void) | ||
801 | { | ||
802 | int num_interfaces; | ||
803 | int interface; | ||
804 | |||
805 | /* Enable IPD */ | ||
806 | cvmx_ipd_enable(); | ||
807 | |||
808 | /* | ||
809 | * Time to enable hardware ports packet input and output. Note | ||
810 | * that at this point IPD/PIP must be fully functional and PKO | ||
811 | * must be disabled | ||
812 | */ | ||
813 | num_interfaces = cvmx_helper_get_number_of_interfaces(); | ||
814 | for (interface = 0; interface < num_interfaces; interface++) { | ||
815 | if (cvmx_helper_ports_on_interface(interface) > 0) | ||
816 | __cvmx_helper_packet_hardware_enable(interface); | ||
817 | } | ||
818 | |||
819 | /* Finally enable PKO now that the entire path is up and running */ | ||
820 | cvmx_pko_enable(); | ||
821 | |||
822 | if ((OCTEON_IS_MODEL(OCTEON_CN31XX_PASS1) | ||
823 | || OCTEON_IS_MODEL(OCTEON_CN30XX_PASS1)) | ||
824 | && (cvmx_sysinfo_get()->board_type != CVMX_BOARD_TYPE_SIM)) | ||
825 | __cvmx_helper_errata_fix_ipd_ptr_alignment(); | ||
826 | return 0; | ||
827 | } | ||
828 | |||
829 | /** | ||
830 | * Initialize the PIP, IPD, and PKO hardware to support | ||
831 | * simple priority based queues for the ethernet ports. Each | ||
832 | * port is configured with a number of priority queues based | ||
833 | * on CVMX_PKO_QUEUES_PER_PORT_* where each queue is lower | ||
834 | * priority than the previous. | ||
835 | * | ||
836 | * Returns Zero on success, non-zero on failure | ||
837 | */ | ||
838 | int cvmx_helper_initialize_packet_io_global(void) | ||
839 | { | ||
840 | int result = 0; | ||
841 | int interface; | ||
842 | union cvmx_l2c_cfg l2c_cfg; | ||
843 | union cvmx_smix_en smix_en; | ||
844 | const int num_interfaces = cvmx_helper_get_number_of_interfaces(); | ||
845 | |||
846 | /* | ||
847 | * CN52XX pass 1: Due to a bug in 2nd order CDR, it needs to | ||
848 | * be disabled. | ||
849 | */ | ||
850 | if (OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_0)) | ||
851 | __cvmx_helper_errata_qlm_disable_2nd_order_cdr(1); | ||
852 | |||
853 | /* | ||
854 | * Tell L2 to give the IOB statically higher priority compared | ||
855 | * to the cores. This avoids conditions where IO blocks might | ||
856 | * be starved under very high L2 loads. | ||
857 | */ | ||
858 | l2c_cfg.u64 = cvmx_read_csr(CVMX_L2C_CFG); | ||
859 | l2c_cfg.s.lrf_arb_mode = 0; | ||
860 | l2c_cfg.s.rfb_arb_mode = 0; | ||
861 | cvmx_write_csr(CVMX_L2C_CFG, l2c_cfg.u64); | ||
862 | |||
863 | /* Make sure SMI/MDIO is enabled so we can query PHYs */ | ||
864 | smix_en.u64 = cvmx_read_csr(CVMX_SMIX_EN(0)); | ||
865 | if (!smix_en.s.en) { | ||
866 | smix_en.s.en = 1; | ||
867 | cvmx_write_csr(CVMX_SMIX_EN(0), smix_en.u64); | ||
868 | } | ||
869 | |||
870 | /* Newer chips actually have two SMI/MDIO interfaces */ | ||
871 | if (!OCTEON_IS_MODEL(OCTEON_CN3XXX) && | ||
872 | !OCTEON_IS_MODEL(OCTEON_CN58XX) && | ||
873 | !OCTEON_IS_MODEL(OCTEON_CN50XX)) { | ||
874 | smix_en.u64 = cvmx_read_csr(CVMX_SMIX_EN(1)); | ||
875 | if (!smix_en.s.en) { | ||
876 | smix_en.s.en = 1; | ||
877 | cvmx_write_csr(CVMX_SMIX_EN(1), smix_en.u64); | ||
878 | } | ||
879 | } | ||
880 | |||
881 | cvmx_pko_initialize_global(); | ||
882 | for (interface = 0; interface < num_interfaces; interface++) { | ||
883 | result |= cvmx_helper_interface_probe(interface); | ||
884 | if (cvmx_helper_ports_on_interface(interface) > 0) | ||
885 | cvmx_dprintf("Interface %d has %d ports (%s)\n", | ||
886 | interface, | ||
887 | cvmx_helper_ports_on_interface(interface), | ||
888 | cvmx_helper_interface_mode_to_string | ||
889 | (cvmx_helper_interface_get_mode | ||
890 | (interface))); | ||
891 | result |= __cvmx_helper_interface_setup_ipd(interface); | ||
892 | result |= __cvmx_helper_interface_setup_pko(interface); | ||
893 | } | ||
894 | |||
895 | result |= __cvmx_helper_global_setup_ipd(); | ||
896 | result |= __cvmx_helper_global_setup_pko(); | ||
897 | |||
898 | /* Enable any flow control and backpressure */ | ||
899 | result |= __cvmx_helper_global_setup_backpressure(); | ||
900 | |||
901 | #if CVMX_HELPER_ENABLE_IPD | ||
902 | result |= cvmx_helper_ipd_and_packet_input_enable(); | ||
903 | #endif | ||
904 | return result; | ||
905 | } | ||
906 | |||
907 | /** | ||
908 | * Does core local initialization for packet io | ||
909 | * | ||
910 | * Returns Zero on success, non-zero on failure | ||
911 | */ | ||
912 | int cvmx_helper_initialize_packet_io_local(void) | ||
913 | { | ||
914 | return cvmx_pko_initialize_local(); | ||
915 | } | ||
916 | |||
917 | /** | ||
918 | * Auto configure an IPD/PKO port link state and speed. This | ||
919 | * function basically does the equivalent of: | ||
920 | * cvmx_helper_link_set(ipd_port, cvmx_helper_link_get(ipd_port)); | ||
921 | * | ||
922 | * @ipd_port: IPD/PKO port to auto configure | ||
923 | * | ||
924 | * Returns Link state after configure | ||
925 | */ | ||
926 | cvmx_helper_link_info_t cvmx_helper_link_autoconf(int ipd_port) | ||
927 | { | ||
928 | cvmx_helper_link_info_t link_info; | ||
929 | int interface = cvmx_helper_get_interface_num(ipd_port); | ||
930 | int index = cvmx_helper_get_interface_index_num(ipd_port); | ||
931 | |||
932 | if (index >= cvmx_helper_ports_on_interface(interface)) { | ||
933 | link_info.u64 = 0; | ||
934 | return link_info; | ||
935 | } | ||
936 | |||
937 | link_info = cvmx_helper_link_get(ipd_port); | ||
938 | if (link_info.u64 == port_link_info[ipd_port].u64) | ||
939 | return link_info; | ||
940 | |||
941 | /* If we fail to set the link speed, port_link_info will not change */ | ||
942 | cvmx_helper_link_set(ipd_port, link_info); | ||
943 | |||
944 | /* | ||
945 | * port_link_info should be the current value, which will be | ||
946 | * different than expect if cvmx_helper_link_set() failed. | ||
947 | */ | ||
948 | return port_link_info[ipd_port]; | ||
949 | } | ||
950 | |||
951 | /** | ||
952 | * Return the link state of an IPD/PKO port as returned by | ||
953 | * auto negotiation. The result of this function may not match | ||
954 | * Octeon's link config if auto negotiation has changed since | ||
955 | * the last call to cvmx_helper_link_set(). | ||
956 | * | ||
957 | * @ipd_port: IPD/PKO port to query | ||
958 | * | ||
959 | * Returns Link state | ||
960 | */ | ||
961 | cvmx_helper_link_info_t cvmx_helper_link_get(int ipd_port) | ||
962 | { | ||
963 | cvmx_helper_link_info_t result; | ||
964 | int interface = cvmx_helper_get_interface_num(ipd_port); | ||
965 | int index = cvmx_helper_get_interface_index_num(ipd_port); | ||
966 | |||
967 | /* The default result will be a down link unless the code below | ||
968 | changes it */ | ||
969 | result.u64 = 0; | ||
970 | |||
971 | if (index >= cvmx_helper_ports_on_interface(interface)) | ||
972 | return result; | ||
973 | |||
974 | switch (cvmx_helper_interface_get_mode(interface)) { | ||
975 | case CVMX_HELPER_INTERFACE_MODE_DISABLED: | ||
976 | case CVMX_HELPER_INTERFACE_MODE_PCIE: | ||
977 | /* Network links are not supported */ | ||
978 | break; | ||
979 | case CVMX_HELPER_INTERFACE_MODE_XAUI: | ||
980 | result = __cvmx_helper_xaui_link_get(ipd_port); | ||
981 | break; | ||
982 | case CVMX_HELPER_INTERFACE_MODE_GMII: | ||
983 | if (index == 0) | ||
984 | result = __cvmx_helper_rgmii_link_get(ipd_port); | ||
985 | else { | ||
986 | result.s.full_duplex = 1; | ||
987 | result.s.link_up = 1; | ||
988 | result.s.speed = 1000; | ||
989 | } | ||
990 | break; | ||
991 | case CVMX_HELPER_INTERFACE_MODE_RGMII: | ||
992 | result = __cvmx_helper_rgmii_link_get(ipd_port); | ||
993 | break; | ||
994 | case CVMX_HELPER_INTERFACE_MODE_SPI: | ||
995 | result = __cvmx_helper_spi_link_get(ipd_port); | ||
996 | break; | ||
997 | case CVMX_HELPER_INTERFACE_MODE_SGMII: | ||
998 | case CVMX_HELPER_INTERFACE_MODE_PICMG: | ||
999 | result = __cvmx_helper_sgmii_link_get(ipd_port); | ||
1000 | break; | ||
1001 | case CVMX_HELPER_INTERFACE_MODE_NPI: | ||
1002 | case CVMX_HELPER_INTERFACE_MODE_LOOP: | ||
1003 | /* Network links are not supported */ | ||
1004 | break; | ||
1005 | } | ||
1006 | return result; | ||
1007 | } | ||
1008 | |||
1009 | /** | ||
1010 | * Configure an IPD/PKO port for the specified link state. This | ||
1011 | * function does not influence auto negotiation at the PHY level. | ||
1012 | * The passed link state must always match the link state returned | ||
1013 | * by cvmx_helper_link_get(). It is normally best to use | ||
1014 | * cvmx_helper_link_autoconf() instead. | ||
1015 | * | ||
1016 | * @ipd_port: IPD/PKO port to configure | ||
1017 | * @link_info: The new link state | ||
1018 | * | ||
1019 | * Returns Zero on success, negative on failure | ||
1020 | */ | ||
1021 | int cvmx_helper_link_set(int ipd_port, cvmx_helper_link_info_t link_info) | ||
1022 | { | ||
1023 | int result = -1; | ||
1024 | int interface = cvmx_helper_get_interface_num(ipd_port); | ||
1025 | int index = cvmx_helper_get_interface_index_num(ipd_port); | ||
1026 | |||
1027 | if (index >= cvmx_helper_ports_on_interface(interface)) | ||
1028 | return -1; | ||
1029 | |||
1030 | switch (cvmx_helper_interface_get_mode(interface)) { | ||
1031 | case CVMX_HELPER_INTERFACE_MODE_DISABLED: | ||
1032 | case CVMX_HELPER_INTERFACE_MODE_PCIE: | ||
1033 | break; | ||
1034 | case CVMX_HELPER_INTERFACE_MODE_XAUI: | ||
1035 | result = __cvmx_helper_xaui_link_set(ipd_port, link_info); | ||
1036 | break; | ||
1037 | /* | ||
1038 | * RGMII/GMII/MII are all treated about the same. Most | ||
1039 | * functions refer to these ports as RGMII. | ||
1040 | */ | ||
1041 | case CVMX_HELPER_INTERFACE_MODE_RGMII: | ||
1042 | case CVMX_HELPER_INTERFACE_MODE_GMII: | ||
1043 | result = __cvmx_helper_rgmii_link_set(ipd_port, link_info); | ||
1044 | break; | ||
1045 | case CVMX_HELPER_INTERFACE_MODE_SPI: | ||
1046 | result = __cvmx_helper_spi_link_set(ipd_port, link_info); | ||
1047 | break; | ||
1048 | case CVMX_HELPER_INTERFACE_MODE_SGMII: | ||
1049 | case CVMX_HELPER_INTERFACE_MODE_PICMG: | ||
1050 | result = __cvmx_helper_sgmii_link_set(ipd_port, link_info); | ||
1051 | break; | ||
1052 | case CVMX_HELPER_INTERFACE_MODE_NPI: | ||
1053 | case CVMX_HELPER_INTERFACE_MODE_LOOP: | ||
1054 | break; | ||
1055 | } | ||
1056 | /* Set the port_link_info here so that the link status is updated | ||
1057 | no matter how cvmx_helper_link_set is called. We don't change | ||
1058 | the value if link_set failed */ | ||
1059 | if (result == 0) | ||
1060 | port_link_info[ipd_port].u64 = link_info.u64; | ||
1061 | return result; | ||
1062 | } | ||
1063 | |||
1064 | /** | ||
1065 | * Configure a port for internal and/or external loopback. Internal loopback | ||
1066 | * causes packets sent by the port to be received by Octeon. External loopback | ||
1067 | * causes packets received from the wire to sent out again. | ||
1068 | * | ||
1069 | * @ipd_port: IPD/PKO port to loopback. | ||
1070 | * @enable_internal: | ||
1071 | * Non zero if you want internal loopback | ||
1072 | * @enable_external: | ||
1073 | * Non zero if you want external loopback | ||
1074 | * | ||
1075 | * Returns Zero on success, negative on failure. | ||
1076 | */ | ||
1077 | int cvmx_helper_configure_loopback(int ipd_port, int enable_internal, | ||
1078 | int enable_external) | ||
1079 | { | ||
1080 | int result = -1; | ||
1081 | int interface = cvmx_helper_get_interface_num(ipd_port); | ||
1082 | int index = cvmx_helper_get_interface_index_num(ipd_port); | ||
1083 | |||
1084 | if (index >= cvmx_helper_ports_on_interface(interface)) | ||
1085 | return -1; | ||
1086 | |||
1087 | switch (cvmx_helper_interface_get_mode(interface)) { | ||
1088 | case CVMX_HELPER_INTERFACE_MODE_DISABLED: | ||
1089 | case CVMX_HELPER_INTERFACE_MODE_PCIE: | ||
1090 | case CVMX_HELPER_INTERFACE_MODE_SPI: | ||
1091 | case CVMX_HELPER_INTERFACE_MODE_NPI: | ||
1092 | case CVMX_HELPER_INTERFACE_MODE_LOOP: | ||
1093 | break; | ||
1094 | case CVMX_HELPER_INTERFACE_MODE_XAUI: | ||
1095 | result = | ||
1096 | __cvmx_helper_xaui_configure_loopback(ipd_port, | ||
1097 | enable_internal, | ||
1098 | enable_external); | ||
1099 | break; | ||
1100 | case CVMX_HELPER_INTERFACE_MODE_RGMII: | ||
1101 | case CVMX_HELPER_INTERFACE_MODE_GMII: | ||
1102 | result = | ||
1103 | __cvmx_helper_rgmii_configure_loopback(ipd_port, | ||
1104 | enable_internal, | ||
1105 | enable_external); | ||
1106 | break; | ||
1107 | case CVMX_HELPER_INTERFACE_MODE_SGMII: | ||
1108 | case CVMX_HELPER_INTERFACE_MODE_PICMG: | ||
1109 | result = | ||
1110 | __cvmx_helper_sgmii_configure_loopback(ipd_port, | ||
1111 | enable_internal, | ||
1112 | enable_external); | ||
1113 | break; | ||
1114 | } | ||
1115 | return result; | ||
1116 | } | ||
diff --git a/arch/mips/cavium-octeon/executive/cvmx-interrupt-decodes.c b/arch/mips/cavium-octeon/executive/cvmx-interrupt-decodes.c new file mode 100644 index 000000000000..e59d1b79f24c --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-interrupt-decodes.c | |||
@@ -0,0 +1,371 @@ | |||
1 | /***********************license start*************** | ||
2 | * Author: Cavium Networks | ||
3 | * | ||
4 | * Contact: support@caviumnetworks.com | ||
5 | * This file is part of the OCTEON SDK | ||
6 | * | ||
7 | * Copyright (c) 2003-2009 Cavium Networks | ||
8 | * | ||
9 | * This file is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License, Version 2, as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * This file is distributed in the hope that it will be useful, but | ||
14 | * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty | ||
15 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or | ||
16 | * NONINFRINGEMENT. See the GNU General Public License for more | ||
17 | * details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this file; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
22 | * or visit http://www.gnu.org/licenses/. | ||
23 | * | ||
24 | * This file may also be available under a different license from Cavium. | ||
25 | * Contact Cavium Networks for more information | ||
26 | ***********************license end**************************************/ | ||
27 | |||
28 | /* | ||
29 | * | ||
30 | * Automatically generated functions useful for enabling | ||
31 | * and decoding RSL_INT_BLOCKS interrupts. | ||
32 | * | ||
33 | */ | ||
34 | |||
35 | #include <asm/octeon/octeon.h> | ||
36 | |||
37 | #include <asm/octeon/cvmx-gmxx-defs.h> | ||
38 | #include <asm/octeon/cvmx-pcsx-defs.h> | ||
39 | #include <asm/octeon/cvmx-pcsxx-defs.h> | ||
40 | #include <asm/octeon/cvmx-spxx-defs.h> | ||
41 | #include <asm/octeon/cvmx-stxx-defs.h> | ||
42 | |||
43 | #ifndef PRINT_ERROR | ||
44 | #define PRINT_ERROR(format, ...) | ||
45 | #endif | ||
46 | |||
47 | |||
48 | /** | ||
49 | * __cvmx_interrupt_gmxx_rxx_int_en_enable enables all interrupt bits in cvmx_gmxx_rxx_int_en_t | ||
50 | */ | ||
51 | void __cvmx_interrupt_gmxx_rxx_int_en_enable(int index, int block) | ||
52 | { | ||
53 | union cvmx_gmxx_rxx_int_en gmx_rx_int_en; | ||
54 | cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, block), | ||
55 | cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, block))); | ||
56 | gmx_rx_int_en.u64 = 0; | ||
57 | if (OCTEON_IS_MODEL(OCTEON_CN56XX)) { | ||
58 | /* Skipping gmx_rx_int_en.s.reserved_29_63 */ | ||
59 | gmx_rx_int_en.s.hg2cc = 1; | ||
60 | gmx_rx_int_en.s.hg2fld = 1; | ||
61 | gmx_rx_int_en.s.undat = 1; | ||
62 | gmx_rx_int_en.s.uneop = 1; | ||
63 | gmx_rx_int_en.s.unsop = 1; | ||
64 | gmx_rx_int_en.s.bad_term = 1; | ||
65 | gmx_rx_int_en.s.bad_seq = 1; | ||
66 | gmx_rx_int_en.s.rem_fault = 1; | ||
67 | gmx_rx_int_en.s.loc_fault = 1; | ||
68 | gmx_rx_int_en.s.pause_drp = 1; | ||
69 | /* Skipping gmx_rx_int_en.s.reserved_16_18 */ | ||
70 | /*gmx_rx_int_en.s.ifgerr = 1; */ | ||
71 | /*gmx_rx_int_en.s.coldet = 1; // Collsion detect */ | ||
72 | /*gmx_rx_int_en.s.falerr = 1; // False carrier error or extend error after slottime */ | ||
73 | /*gmx_rx_int_en.s.rsverr = 1; // RGMII reserved opcodes */ | ||
74 | /*gmx_rx_int_en.s.pcterr = 1; // Bad Preamble / Protocol */ | ||
75 | gmx_rx_int_en.s.ovrerr = 1; | ||
76 | /* Skipping gmx_rx_int_en.s.reserved_9_9 */ | ||
77 | gmx_rx_int_en.s.skperr = 1; | ||
78 | gmx_rx_int_en.s.rcverr = 1; | ||
79 | /* Skipping gmx_rx_int_en.s.reserved_5_6 */ | ||
80 | /*gmx_rx_int_en.s.fcserr = 1; // FCS errors are handled when we get work */ | ||
81 | gmx_rx_int_en.s.jabber = 1; | ||
82 | /* Skipping gmx_rx_int_en.s.reserved_2_2 */ | ||
83 | gmx_rx_int_en.s.carext = 1; | ||
84 | /* Skipping gmx_rx_int_en.s.reserved_0_0 */ | ||
85 | } | ||
86 | if (OCTEON_IS_MODEL(OCTEON_CN30XX)) { | ||
87 | /* Skipping gmx_rx_int_en.s.reserved_19_63 */ | ||
88 | /*gmx_rx_int_en.s.phy_dupx = 1; */ | ||
89 | /*gmx_rx_int_en.s.phy_spd = 1; */ | ||
90 | /*gmx_rx_int_en.s.phy_link = 1; */ | ||
91 | /*gmx_rx_int_en.s.ifgerr = 1; */ | ||
92 | /*gmx_rx_int_en.s.coldet = 1; // Collsion detect */ | ||
93 | /*gmx_rx_int_en.s.falerr = 1; // False carrier error or extend error after slottime */ | ||
94 | /*gmx_rx_int_en.s.rsverr = 1; // RGMII reserved opcodes */ | ||
95 | /*gmx_rx_int_en.s.pcterr = 1; // Bad Preamble / Protocol */ | ||
96 | gmx_rx_int_en.s.ovrerr = 1; | ||
97 | gmx_rx_int_en.s.niberr = 1; | ||
98 | gmx_rx_int_en.s.skperr = 1; | ||
99 | gmx_rx_int_en.s.rcverr = 1; | ||
100 | /*gmx_rx_int_en.s.lenerr = 1; // Length errors are handled when we get work */ | ||
101 | gmx_rx_int_en.s.alnerr = 1; | ||
102 | /*gmx_rx_int_en.s.fcserr = 1; // FCS errors are handled when we get work */ | ||
103 | gmx_rx_int_en.s.jabber = 1; | ||
104 | gmx_rx_int_en.s.maxerr = 1; | ||
105 | gmx_rx_int_en.s.carext = 1; | ||
106 | gmx_rx_int_en.s.minerr = 1; | ||
107 | } | ||
108 | if (OCTEON_IS_MODEL(OCTEON_CN50XX)) { | ||
109 | /* Skipping gmx_rx_int_en.s.reserved_20_63 */ | ||
110 | gmx_rx_int_en.s.pause_drp = 1; | ||
111 | /*gmx_rx_int_en.s.phy_dupx = 1; */ | ||
112 | /*gmx_rx_int_en.s.phy_spd = 1; */ | ||
113 | /*gmx_rx_int_en.s.phy_link = 1; */ | ||
114 | /*gmx_rx_int_en.s.ifgerr = 1; */ | ||
115 | /*gmx_rx_int_en.s.coldet = 1; // Collsion detect */ | ||
116 | /*gmx_rx_int_en.s.falerr = 1; // False carrier error or extend error after slottime */ | ||
117 | /*gmx_rx_int_en.s.rsverr = 1; // RGMII reserved opcodes */ | ||
118 | /*gmx_rx_int_en.s.pcterr = 1; // Bad Preamble / Protocol */ | ||
119 | gmx_rx_int_en.s.ovrerr = 1; | ||
120 | gmx_rx_int_en.s.niberr = 1; | ||
121 | gmx_rx_int_en.s.skperr = 1; | ||
122 | gmx_rx_int_en.s.rcverr = 1; | ||
123 | /* Skipping gmx_rx_int_en.s.reserved_6_6 */ | ||
124 | gmx_rx_int_en.s.alnerr = 1; | ||
125 | /*gmx_rx_int_en.s.fcserr = 1; // FCS errors are handled when we get work */ | ||
126 | gmx_rx_int_en.s.jabber = 1; | ||
127 | /* Skipping gmx_rx_int_en.s.reserved_2_2 */ | ||
128 | gmx_rx_int_en.s.carext = 1; | ||
129 | /* Skipping gmx_rx_int_en.s.reserved_0_0 */ | ||
130 | } | ||
131 | if (OCTEON_IS_MODEL(OCTEON_CN38XX)) { | ||
132 | /* Skipping gmx_rx_int_en.s.reserved_19_63 */ | ||
133 | /*gmx_rx_int_en.s.phy_dupx = 1; */ | ||
134 | /*gmx_rx_int_en.s.phy_spd = 1; */ | ||
135 | /*gmx_rx_int_en.s.phy_link = 1; */ | ||
136 | /*gmx_rx_int_en.s.ifgerr = 1; */ | ||
137 | /*gmx_rx_int_en.s.coldet = 1; // Collsion detect */ | ||
138 | /*gmx_rx_int_en.s.falerr = 1; // False carrier error or extend error after slottime */ | ||
139 | /*gmx_rx_int_en.s.rsverr = 1; // RGMII reserved opcodes */ | ||
140 | /*gmx_rx_int_en.s.pcterr = 1; // Bad Preamble / Protocol */ | ||
141 | gmx_rx_int_en.s.ovrerr = 1; | ||
142 | gmx_rx_int_en.s.niberr = 1; | ||
143 | gmx_rx_int_en.s.skperr = 1; | ||
144 | gmx_rx_int_en.s.rcverr = 1; | ||
145 | /*gmx_rx_int_en.s.lenerr = 1; // Length errors are handled when we get work */ | ||
146 | gmx_rx_int_en.s.alnerr = 1; | ||
147 | /*gmx_rx_int_en.s.fcserr = 1; // FCS errors are handled when we get work */ | ||
148 | gmx_rx_int_en.s.jabber = 1; | ||
149 | gmx_rx_int_en.s.maxerr = 1; | ||
150 | gmx_rx_int_en.s.carext = 1; | ||
151 | gmx_rx_int_en.s.minerr = 1; | ||
152 | } | ||
153 | if (OCTEON_IS_MODEL(OCTEON_CN31XX)) { | ||
154 | /* Skipping gmx_rx_int_en.s.reserved_19_63 */ | ||
155 | /*gmx_rx_int_en.s.phy_dupx = 1; */ | ||
156 | /*gmx_rx_int_en.s.phy_spd = 1; */ | ||
157 | /*gmx_rx_int_en.s.phy_link = 1; */ | ||
158 | /*gmx_rx_int_en.s.ifgerr = 1; */ | ||
159 | /*gmx_rx_int_en.s.coldet = 1; // Collsion detect */ | ||
160 | /*gmx_rx_int_en.s.falerr = 1; // False carrier error or extend error after slottime */ | ||
161 | /*gmx_rx_int_en.s.rsverr = 1; // RGMII reserved opcodes */ | ||
162 | /*gmx_rx_int_en.s.pcterr = 1; // Bad Preamble / Protocol */ | ||
163 | gmx_rx_int_en.s.ovrerr = 1; | ||
164 | gmx_rx_int_en.s.niberr = 1; | ||
165 | gmx_rx_int_en.s.skperr = 1; | ||
166 | gmx_rx_int_en.s.rcverr = 1; | ||
167 | /*gmx_rx_int_en.s.lenerr = 1; // Length errors are handled when we get work */ | ||
168 | gmx_rx_int_en.s.alnerr = 1; | ||
169 | /*gmx_rx_int_en.s.fcserr = 1; // FCS errors are handled when we get work */ | ||
170 | gmx_rx_int_en.s.jabber = 1; | ||
171 | gmx_rx_int_en.s.maxerr = 1; | ||
172 | gmx_rx_int_en.s.carext = 1; | ||
173 | gmx_rx_int_en.s.minerr = 1; | ||
174 | } | ||
175 | if (OCTEON_IS_MODEL(OCTEON_CN58XX)) { | ||
176 | /* Skipping gmx_rx_int_en.s.reserved_20_63 */ | ||
177 | gmx_rx_int_en.s.pause_drp = 1; | ||
178 | /*gmx_rx_int_en.s.phy_dupx = 1; */ | ||
179 | /*gmx_rx_int_en.s.phy_spd = 1; */ | ||
180 | /*gmx_rx_int_en.s.phy_link = 1; */ | ||
181 | /*gmx_rx_int_en.s.ifgerr = 1; */ | ||
182 | /*gmx_rx_int_en.s.coldet = 1; // Collsion detect */ | ||
183 | /*gmx_rx_int_en.s.falerr = 1; // False carrier error or extend error after slottime */ | ||
184 | /*gmx_rx_int_en.s.rsverr = 1; // RGMII reserved opcodes */ | ||
185 | /*gmx_rx_int_en.s.pcterr = 1; // Bad Preamble / Protocol */ | ||
186 | gmx_rx_int_en.s.ovrerr = 1; | ||
187 | gmx_rx_int_en.s.niberr = 1; | ||
188 | gmx_rx_int_en.s.skperr = 1; | ||
189 | gmx_rx_int_en.s.rcverr = 1; | ||
190 | /*gmx_rx_int_en.s.lenerr = 1; // Length errors are handled when we get work */ | ||
191 | gmx_rx_int_en.s.alnerr = 1; | ||
192 | /*gmx_rx_int_en.s.fcserr = 1; // FCS errors are handled when we get work */ | ||
193 | gmx_rx_int_en.s.jabber = 1; | ||
194 | gmx_rx_int_en.s.maxerr = 1; | ||
195 | gmx_rx_int_en.s.carext = 1; | ||
196 | gmx_rx_int_en.s.minerr = 1; | ||
197 | } | ||
198 | if (OCTEON_IS_MODEL(OCTEON_CN52XX)) { | ||
199 | /* Skipping gmx_rx_int_en.s.reserved_29_63 */ | ||
200 | gmx_rx_int_en.s.hg2cc = 1; | ||
201 | gmx_rx_int_en.s.hg2fld = 1; | ||
202 | gmx_rx_int_en.s.undat = 1; | ||
203 | gmx_rx_int_en.s.uneop = 1; | ||
204 | gmx_rx_int_en.s.unsop = 1; | ||
205 | gmx_rx_int_en.s.bad_term = 1; | ||
206 | gmx_rx_int_en.s.bad_seq = 0; | ||
207 | gmx_rx_int_en.s.rem_fault = 1; | ||
208 | gmx_rx_int_en.s.loc_fault = 0; | ||
209 | gmx_rx_int_en.s.pause_drp = 1; | ||
210 | /* Skipping gmx_rx_int_en.s.reserved_16_18 */ | ||
211 | /*gmx_rx_int_en.s.ifgerr = 1; */ | ||
212 | /*gmx_rx_int_en.s.coldet = 1; // Collsion detect */ | ||
213 | /*gmx_rx_int_en.s.falerr = 1; // False carrier error or extend error after slottime */ | ||
214 | /*gmx_rx_int_en.s.rsverr = 1; // RGMII reserved opcodes */ | ||
215 | /*gmx_rx_int_en.s.pcterr = 1; // Bad Preamble / Protocol */ | ||
216 | gmx_rx_int_en.s.ovrerr = 1; | ||
217 | /* Skipping gmx_rx_int_en.s.reserved_9_9 */ | ||
218 | gmx_rx_int_en.s.skperr = 1; | ||
219 | gmx_rx_int_en.s.rcverr = 1; | ||
220 | /* Skipping gmx_rx_int_en.s.reserved_5_6 */ | ||
221 | /*gmx_rx_int_en.s.fcserr = 1; // FCS errors are handled when we get work */ | ||
222 | gmx_rx_int_en.s.jabber = 1; | ||
223 | /* Skipping gmx_rx_int_en.s.reserved_2_2 */ | ||
224 | gmx_rx_int_en.s.carext = 1; | ||
225 | /* Skipping gmx_rx_int_en.s.reserved_0_0 */ | ||
226 | } | ||
227 | cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, block), gmx_rx_int_en.u64); | ||
228 | } | ||
229 | /** | ||
230 | * __cvmx_interrupt_pcsx_intx_en_reg_enable enables all interrupt bits in cvmx_pcsx_intx_en_reg_t | ||
231 | */ | ||
232 | void __cvmx_interrupt_pcsx_intx_en_reg_enable(int index, int block) | ||
233 | { | ||
234 | union cvmx_pcsx_intx_en_reg pcs_int_en_reg; | ||
235 | cvmx_write_csr(CVMX_PCSX_INTX_REG(index, block), | ||
236 | cvmx_read_csr(CVMX_PCSX_INTX_REG(index, block))); | ||
237 | pcs_int_en_reg.u64 = 0; | ||
238 | if (OCTEON_IS_MODEL(OCTEON_CN56XX)) { | ||
239 | /* Skipping pcs_int_en_reg.s.reserved_12_63 */ | ||
240 | /*pcs_int_en_reg.s.dup = 1; // This happens during normal operation */ | ||
241 | pcs_int_en_reg.s.sync_bad_en = 1; | ||
242 | pcs_int_en_reg.s.an_bad_en = 1; | ||
243 | pcs_int_en_reg.s.rxlock_en = 1; | ||
244 | pcs_int_en_reg.s.rxbad_en = 1; | ||
245 | /*pcs_int_en_reg.s.rxerr_en = 1; // This happens during normal operation */ | ||
246 | pcs_int_en_reg.s.txbad_en = 1; | ||
247 | pcs_int_en_reg.s.txfifo_en = 1; | ||
248 | pcs_int_en_reg.s.txfifu_en = 1; | ||
249 | pcs_int_en_reg.s.an_err_en = 1; | ||
250 | /*pcs_int_en_reg.s.xmit_en = 1; // This happens during normal operation */ | ||
251 | /*pcs_int_en_reg.s.lnkspd_en = 1; // This happens during normal operation */ | ||
252 | } | ||
253 | if (OCTEON_IS_MODEL(OCTEON_CN52XX)) { | ||
254 | /* Skipping pcs_int_en_reg.s.reserved_12_63 */ | ||
255 | /*pcs_int_en_reg.s.dup = 1; // This happens during normal operation */ | ||
256 | pcs_int_en_reg.s.sync_bad_en = 1; | ||
257 | pcs_int_en_reg.s.an_bad_en = 1; | ||
258 | pcs_int_en_reg.s.rxlock_en = 1; | ||
259 | pcs_int_en_reg.s.rxbad_en = 1; | ||
260 | /*pcs_int_en_reg.s.rxerr_en = 1; // This happens during normal operation */ | ||
261 | pcs_int_en_reg.s.txbad_en = 1; | ||
262 | pcs_int_en_reg.s.txfifo_en = 1; | ||
263 | pcs_int_en_reg.s.txfifu_en = 1; | ||
264 | pcs_int_en_reg.s.an_err_en = 1; | ||
265 | /*pcs_int_en_reg.s.xmit_en = 1; // This happens during normal operation */ | ||
266 | /*pcs_int_en_reg.s.lnkspd_en = 1; // This happens during normal operation */ | ||
267 | } | ||
268 | cvmx_write_csr(CVMX_PCSX_INTX_EN_REG(index, block), pcs_int_en_reg.u64); | ||
269 | } | ||
270 | /** | ||
271 | * __cvmx_interrupt_pcsxx_int_en_reg_enable enables all interrupt bits in cvmx_pcsxx_int_en_reg_t | ||
272 | */ | ||
273 | void __cvmx_interrupt_pcsxx_int_en_reg_enable(int index) | ||
274 | { | ||
275 | union cvmx_pcsxx_int_en_reg pcsx_int_en_reg; | ||
276 | cvmx_write_csr(CVMX_PCSXX_INT_REG(index), | ||
277 | cvmx_read_csr(CVMX_PCSXX_INT_REG(index))); | ||
278 | pcsx_int_en_reg.u64 = 0; | ||
279 | if (OCTEON_IS_MODEL(OCTEON_CN56XX)) { | ||
280 | /* Skipping pcsx_int_en_reg.s.reserved_6_63 */ | ||
281 | pcsx_int_en_reg.s.algnlos_en = 1; | ||
282 | pcsx_int_en_reg.s.synlos_en = 1; | ||
283 | pcsx_int_en_reg.s.bitlckls_en = 1; | ||
284 | pcsx_int_en_reg.s.rxsynbad_en = 1; | ||
285 | pcsx_int_en_reg.s.rxbad_en = 1; | ||
286 | pcsx_int_en_reg.s.txflt_en = 1; | ||
287 | } | ||
288 | if (OCTEON_IS_MODEL(OCTEON_CN52XX)) { | ||
289 | /* Skipping pcsx_int_en_reg.s.reserved_6_63 */ | ||
290 | pcsx_int_en_reg.s.algnlos_en = 1; | ||
291 | pcsx_int_en_reg.s.synlos_en = 1; | ||
292 | pcsx_int_en_reg.s.bitlckls_en = 0; /* Happens if XAUI module is not installed */ | ||
293 | pcsx_int_en_reg.s.rxsynbad_en = 1; | ||
294 | pcsx_int_en_reg.s.rxbad_en = 1; | ||
295 | pcsx_int_en_reg.s.txflt_en = 1; | ||
296 | } | ||
297 | cvmx_write_csr(CVMX_PCSXX_INT_EN_REG(index), pcsx_int_en_reg.u64); | ||
298 | } | ||
299 | |||
300 | /** | ||
301 | * __cvmx_interrupt_spxx_int_msk_enable enables all interrupt bits in cvmx_spxx_int_msk_t | ||
302 | */ | ||
303 | void __cvmx_interrupt_spxx_int_msk_enable(int index) | ||
304 | { | ||
305 | union cvmx_spxx_int_msk spx_int_msk; | ||
306 | cvmx_write_csr(CVMX_SPXX_INT_REG(index), | ||
307 | cvmx_read_csr(CVMX_SPXX_INT_REG(index))); | ||
308 | spx_int_msk.u64 = 0; | ||
309 | if (OCTEON_IS_MODEL(OCTEON_CN38XX)) { | ||
310 | /* Skipping spx_int_msk.s.reserved_12_63 */ | ||
311 | spx_int_msk.s.calerr = 1; | ||
312 | spx_int_msk.s.syncerr = 1; | ||
313 | spx_int_msk.s.diperr = 1; | ||
314 | spx_int_msk.s.tpaovr = 1; | ||
315 | spx_int_msk.s.rsverr = 1; | ||
316 | spx_int_msk.s.drwnng = 1; | ||
317 | spx_int_msk.s.clserr = 1; | ||
318 | spx_int_msk.s.spiovr = 1; | ||
319 | /* Skipping spx_int_msk.s.reserved_2_3 */ | ||
320 | spx_int_msk.s.abnorm = 1; | ||
321 | spx_int_msk.s.prtnxa = 1; | ||
322 | } | ||
323 | if (OCTEON_IS_MODEL(OCTEON_CN58XX)) { | ||
324 | /* Skipping spx_int_msk.s.reserved_12_63 */ | ||
325 | spx_int_msk.s.calerr = 1; | ||
326 | spx_int_msk.s.syncerr = 1; | ||
327 | spx_int_msk.s.diperr = 1; | ||
328 | spx_int_msk.s.tpaovr = 1; | ||
329 | spx_int_msk.s.rsverr = 1; | ||
330 | spx_int_msk.s.drwnng = 1; | ||
331 | spx_int_msk.s.clserr = 1; | ||
332 | spx_int_msk.s.spiovr = 1; | ||
333 | /* Skipping spx_int_msk.s.reserved_2_3 */ | ||
334 | spx_int_msk.s.abnorm = 1; | ||
335 | spx_int_msk.s.prtnxa = 1; | ||
336 | } | ||
337 | cvmx_write_csr(CVMX_SPXX_INT_MSK(index), spx_int_msk.u64); | ||
338 | } | ||
339 | /** | ||
340 | * __cvmx_interrupt_stxx_int_msk_enable enables all interrupt bits in cvmx_stxx_int_msk_t | ||
341 | */ | ||
342 | void __cvmx_interrupt_stxx_int_msk_enable(int index) | ||
343 | { | ||
344 | union cvmx_stxx_int_msk stx_int_msk; | ||
345 | cvmx_write_csr(CVMX_STXX_INT_REG(index), | ||
346 | cvmx_read_csr(CVMX_STXX_INT_REG(index))); | ||
347 | stx_int_msk.u64 = 0; | ||
348 | if (OCTEON_IS_MODEL(OCTEON_CN38XX)) { | ||
349 | /* Skipping stx_int_msk.s.reserved_8_63 */ | ||
350 | stx_int_msk.s.frmerr = 1; | ||
351 | stx_int_msk.s.unxfrm = 1; | ||
352 | stx_int_msk.s.nosync = 1; | ||
353 | stx_int_msk.s.diperr = 1; | ||
354 | stx_int_msk.s.datovr = 1; | ||
355 | stx_int_msk.s.ovrbst = 1; | ||
356 | stx_int_msk.s.calpar1 = 1; | ||
357 | stx_int_msk.s.calpar0 = 1; | ||
358 | } | ||
359 | if (OCTEON_IS_MODEL(OCTEON_CN58XX)) { | ||
360 | /* Skipping stx_int_msk.s.reserved_8_63 */ | ||
361 | stx_int_msk.s.frmerr = 1; | ||
362 | stx_int_msk.s.unxfrm = 1; | ||
363 | stx_int_msk.s.nosync = 1; | ||
364 | stx_int_msk.s.diperr = 1; | ||
365 | stx_int_msk.s.datovr = 1; | ||
366 | stx_int_msk.s.ovrbst = 1; | ||
367 | stx_int_msk.s.calpar1 = 1; | ||
368 | stx_int_msk.s.calpar0 = 1; | ||
369 | } | ||
370 | cvmx_write_csr(CVMX_STXX_INT_MSK(index), stx_int_msk.u64); | ||
371 | } | ||
diff --git a/arch/mips/cavium-octeon/executive/cvmx-interrupt-rsl.c b/arch/mips/cavium-octeon/executive/cvmx-interrupt-rsl.c new file mode 100644 index 000000000000..bea7538ea4e9 --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-interrupt-rsl.c | |||
@@ -0,0 +1,140 @@ | |||
1 | /***********************license start*************** | ||
2 | * Author: Cavium Networks | ||
3 | * | ||
4 | * Contact: support@caviumnetworks.com | ||
5 | * This file is part of the OCTEON SDK | ||
6 | * | ||
7 | * Copyright (c) 2003-2008 Cavium Networks | ||
8 | * | ||
9 | * This file is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License, Version 2, as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * This file is distributed in the hope that it will be useful, but | ||
14 | * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty | ||
15 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or | ||
16 | * NONINFRINGEMENT. See the GNU General Public License for more | ||
17 | * details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this file; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
22 | * or visit http://www.gnu.org/licenses/. | ||
23 | * | ||
24 | * This file may also be available under a different license from Cavium. | ||
25 | * Contact Cavium Networks for more information | ||
26 | ***********************license end**************************************/ | ||
27 | |||
28 | /* | ||
29 | * Utility functions to decode Octeon's RSL_INT_BLOCKS | ||
30 | * interrupts into error messages. | ||
31 | */ | ||
32 | |||
33 | #include <asm/octeon/octeon.h> | ||
34 | |||
35 | #include <asm/octeon/cvmx-asxx-defs.h> | ||
36 | #include <asm/octeon/cvmx-gmxx-defs.h> | ||
37 | |||
38 | #ifndef PRINT_ERROR | ||
39 | #define PRINT_ERROR(format, ...) | ||
40 | #endif | ||
41 | |||
42 | void __cvmx_interrupt_gmxx_rxx_int_en_enable(int index, int block); | ||
43 | |||
44 | /** | ||
45 | * Enable ASX error interrupts that exist on CN3XXX, CN50XX, and | ||
46 | * CN58XX. | ||
47 | * | ||
48 | * @block: Interface to enable 0-1 | ||
49 | */ | ||
50 | void __cvmx_interrupt_asxx_enable(int block) | ||
51 | { | ||
52 | int mask; | ||
53 | union cvmx_asxx_int_en csr; | ||
54 | /* | ||
55 | * CN38XX and CN58XX have two interfaces with 4 ports per | ||
56 | * interface. All other chips have a max of 3 ports on | ||
57 | * interface 0 | ||
58 | */ | ||
59 | if (OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX)) | ||
60 | mask = 0xf; /* Set enables for 4 ports */ | ||
61 | else | ||
62 | mask = 0x7; /* Set enables for 3 ports */ | ||
63 | |||
64 | /* Enable interface interrupts */ | ||
65 | csr.u64 = cvmx_read_csr(CVMX_ASXX_INT_EN(block)); | ||
66 | csr.s.txpsh = mask; | ||
67 | csr.s.txpop = mask; | ||
68 | csr.s.ovrflw = mask; | ||
69 | cvmx_write_csr(CVMX_ASXX_INT_EN(block), csr.u64); | ||
70 | } | ||
71 | /** | ||
72 | * Enable GMX error reporting for the supplied interface | ||
73 | * | ||
74 | * @interface: Interface to enable | ||
75 | */ | ||
76 | void __cvmx_interrupt_gmxx_enable(int interface) | ||
77 | { | ||
78 | union cvmx_gmxx_inf_mode mode; | ||
79 | union cvmx_gmxx_tx_int_en gmx_tx_int_en; | ||
80 | int num_ports; | ||
81 | int index; | ||
82 | |||
83 | mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(interface)); | ||
84 | |||
85 | if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN52XX)) { | ||
86 | if (mode.s.en) { | ||
87 | switch (mode.cn56xx.mode) { | ||
88 | case 1: /* XAUI */ | ||
89 | num_ports = 1; | ||
90 | break; | ||
91 | case 2: /* SGMII */ | ||
92 | case 3: /* PICMG */ | ||
93 | num_ports = 4; | ||
94 | break; | ||
95 | default: /* Disabled */ | ||
96 | num_ports = 0; | ||
97 | break; | ||
98 | } | ||
99 | } else | ||
100 | num_ports = 0; | ||
101 | } else { | ||
102 | if (mode.s.en) { | ||
103 | if (OCTEON_IS_MODEL(OCTEON_CN38XX) | ||
104 | || OCTEON_IS_MODEL(OCTEON_CN58XX)) { | ||
105 | /* | ||
106 | * SPI on CN38XX and CN58XX report all | ||
107 | * errors through port 0. RGMII needs | ||
108 | * to check all 4 ports | ||
109 | */ | ||
110 | if (mode.s.type) | ||
111 | num_ports = 1; | ||
112 | else | ||
113 | num_ports = 4; | ||
114 | } else { | ||
115 | /* | ||
116 | * CN30XX, CN31XX, and CN50XX have two | ||
117 | * or three ports. GMII and MII has 2, | ||
118 | * RGMII has three | ||
119 | */ | ||
120 | if (mode.s.type) | ||
121 | num_ports = 2; | ||
122 | else | ||
123 | num_ports = 3; | ||
124 | } | ||
125 | } else | ||
126 | num_ports = 0; | ||
127 | } | ||
128 | |||
129 | gmx_tx_int_en.u64 = 0; | ||
130 | if (num_ports) { | ||
131 | if (OCTEON_IS_MODEL(OCTEON_CN38XX) | ||
132 | || OCTEON_IS_MODEL(OCTEON_CN58XX)) | ||
133 | gmx_tx_int_en.s.ncb_nxa = 1; | ||
134 | gmx_tx_int_en.s.pko_nxa = 1; | ||
135 | } | ||
136 | gmx_tx_int_en.s.undflw = (1 << num_ports) - 1; | ||
137 | cvmx_write_csr(CVMX_GMXX_TX_INT_EN(interface), gmx_tx_int_en.u64); | ||
138 | for (index = 0; index < num_ports; index++) | ||
139 | __cvmx_interrupt_gmxx_rxx_int_en_enable(index, interface); | ||
140 | } | ||
diff --git a/arch/mips/cavium-octeon/executive/cvmx-pko.c b/arch/mips/cavium-octeon/executive/cvmx-pko.c new file mode 100644 index 000000000000..f557084b1092 --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-pko.c | |||
@@ -0,0 +1,506 @@ | |||
1 | /***********************license start*************** | ||
2 | * Author: Cavium Networks | ||
3 | * | ||
4 | * Contact: support@caviumnetworks.com | ||
5 | * This file is part of the OCTEON SDK | ||
6 | * | ||
7 | * Copyright (c) 2003-2008 Cavium Networks | ||
8 | * | ||
9 | * This file is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License, Version 2, as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * This file is distributed in the hope that it will be useful, but | ||
14 | * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty | ||
15 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or | ||
16 | * NONINFRINGEMENT. See the GNU General Public License for more | ||
17 | * details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this file; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
22 | * or visit http://www.gnu.org/licenses/. | ||
23 | * | ||
24 | * This file may also be available under a different license from Cavium. | ||
25 | * Contact Cavium Networks for more information | ||
26 | ***********************license end**************************************/ | ||
27 | |||
28 | /* | ||
29 | * Support library for the hardware Packet Output unit. | ||
30 | */ | ||
31 | |||
32 | #include <asm/octeon/octeon.h> | ||
33 | |||
34 | #include <asm/octeon/cvmx-config.h> | ||
35 | #include <asm/octeon/cvmx-pko.h> | ||
36 | #include <asm/octeon/cvmx-helper.h> | ||
37 | |||
38 | /** | ||
39 | * Internal state of packet output | ||
40 | */ | ||
41 | |||
42 | /** | ||
43 | * Call before any other calls to initialize the packet | ||
44 | * output system. This does chip global config, and should only be | ||
45 | * done by one core. | ||
46 | */ | ||
47 | |||
48 | void cvmx_pko_initialize_global(void) | ||
49 | { | ||
50 | int i; | ||
51 | uint64_t priority = 8; | ||
52 | union cvmx_pko_reg_cmd_buf config; | ||
53 | |||
54 | /* | ||
55 | * Set the size of the PKO command buffers to an odd number of | ||
56 | * 64bit words. This allows the normal two word send to stay | ||
57 | * aligned and never span a comamnd word buffer. | ||
58 | */ | ||
59 | config.u64 = 0; | ||
60 | config.s.pool = CVMX_FPA_OUTPUT_BUFFER_POOL; | ||
61 | config.s.size = CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE / 8 - 1; | ||
62 | |||
63 | cvmx_write_csr(CVMX_PKO_REG_CMD_BUF, config.u64); | ||
64 | |||
65 | for (i = 0; i < CVMX_PKO_MAX_OUTPUT_QUEUES; i++) | ||
66 | cvmx_pko_config_port(CVMX_PKO_MEM_QUEUE_PTRS_ILLEGAL_PID, i, 1, | ||
67 | &priority); | ||
68 | |||
69 | /* | ||
70 | * If we aren't using all of the queues optimize PKO's | ||
71 | * internal memory. | ||
72 | */ | ||
73 | if (OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX) | ||
74 | || OCTEON_IS_MODEL(OCTEON_CN56XX) | ||
75 | || OCTEON_IS_MODEL(OCTEON_CN52XX)) { | ||
76 | int num_interfaces = cvmx_helper_get_number_of_interfaces(); | ||
77 | int last_port = | ||
78 | cvmx_helper_get_last_ipd_port(num_interfaces - 1); | ||
79 | int max_queues = | ||
80 | cvmx_pko_get_base_queue(last_port) + | ||
81 | cvmx_pko_get_num_queues(last_port); | ||
82 | if (OCTEON_IS_MODEL(OCTEON_CN38XX)) { | ||
83 | if (max_queues <= 32) | ||
84 | cvmx_write_csr(CVMX_PKO_REG_QUEUE_MODE, 2); | ||
85 | else if (max_queues <= 64) | ||
86 | cvmx_write_csr(CVMX_PKO_REG_QUEUE_MODE, 1); | ||
87 | } else { | ||
88 | if (max_queues <= 64) | ||
89 | cvmx_write_csr(CVMX_PKO_REG_QUEUE_MODE, 2); | ||
90 | else if (max_queues <= 128) | ||
91 | cvmx_write_csr(CVMX_PKO_REG_QUEUE_MODE, 1); | ||
92 | } | ||
93 | } | ||
94 | } | ||
95 | |||
96 | /** | ||
97 | * This function does per-core initialization required by the PKO routines. | ||
98 | * This must be called on all cores that will do packet output, and must | ||
99 | * be called after the FPA has been initialized and filled with pages. | ||
100 | * | ||
101 | * Returns 0 on success | ||
102 | * !0 on failure | ||
103 | */ | ||
104 | int cvmx_pko_initialize_local(void) | ||
105 | { | ||
106 | /* Nothing to do */ | ||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | /** | ||
111 | * Enables the packet output hardware. It must already be | ||
112 | * configured. | ||
113 | */ | ||
114 | void cvmx_pko_enable(void) | ||
115 | { | ||
116 | union cvmx_pko_reg_flags flags; | ||
117 | |||
118 | flags.u64 = cvmx_read_csr(CVMX_PKO_REG_FLAGS); | ||
119 | if (flags.s.ena_pko) | ||
120 | cvmx_dprintf | ||
121 | ("Warning: Enabling PKO when PKO already enabled.\n"); | ||
122 | |||
123 | flags.s.ena_dwb = 1; | ||
124 | flags.s.ena_pko = 1; | ||
125 | /* | ||
126 | * always enable big endian for 3-word command. Does nothing | ||
127 | * for 2-word. | ||
128 | */ | ||
129 | flags.s.store_be = 1; | ||
130 | cvmx_write_csr(CVMX_PKO_REG_FLAGS, flags.u64); | ||
131 | } | ||
132 | |||
133 | /** | ||
134 | * Disables the packet output. Does not affect any configuration. | ||
135 | */ | ||
136 | void cvmx_pko_disable(void) | ||
137 | { | ||
138 | union cvmx_pko_reg_flags pko_reg_flags; | ||
139 | pko_reg_flags.u64 = cvmx_read_csr(CVMX_PKO_REG_FLAGS); | ||
140 | pko_reg_flags.s.ena_pko = 0; | ||
141 | cvmx_write_csr(CVMX_PKO_REG_FLAGS, pko_reg_flags.u64); | ||
142 | } | ||
143 | |||
144 | |||
145 | /** | ||
146 | * Reset the packet output. | ||
147 | */ | ||
148 | static void __cvmx_pko_reset(void) | ||
149 | { | ||
150 | union cvmx_pko_reg_flags pko_reg_flags; | ||
151 | pko_reg_flags.u64 = cvmx_read_csr(CVMX_PKO_REG_FLAGS); | ||
152 | pko_reg_flags.s.reset = 1; | ||
153 | cvmx_write_csr(CVMX_PKO_REG_FLAGS, pko_reg_flags.u64); | ||
154 | } | ||
155 | |||
156 | /** | ||
157 | * Shutdown and free resources required by packet output. | ||
158 | */ | ||
159 | void cvmx_pko_shutdown(void) | ||
160 | { | ||
161 | union cvmx_pko_mem_queue_ptrs config; | ||
162 | int queue; | ||
163 | |||
164 | cvmx_pko_disable(); | ||
165 | |||
166 | for (queue = 0; queue < CVMX_PKO_MAX_OUTPUT_QUEUES; queue++) { | ||
167 | config.u64 = 0; | ||
168 | config.s.tail = 1; | ||
169 | config.s.index = 0; | ||
170 | config.s.port = CVMX_PKO_MEM_QUEUE_PTRS_ILLEGAL_PID; | ||
171 | config.s.queue = queue & 0x7f; | ||
172 | config.s.qos_mask = 0; | ||
173 | config.s.buf_ptr = 0; | ||
174 | if (!OCTEON_IS_MODEL(OCTEON_CN3XXX)) { | ||
175 | union cvmx_pko_reg_queue_ptrs1 config1; | ||
176 | config1.u64 = 0; | ||
177 | config1.s.qid7 = queue >> 7; | ||
178 | cvmx_write_csr(CVMX_PKO_REG_QUEUE_PTRS1, config1.u64); | ||
179 | } | ||
180 | cvmx_write_csr(CVMX_PKO_MEM_QUEUE_PTRS, config.u64); | ||
181 | cvmx_cmd_queue_shutdown(CVMX_CMD_QUEUE_PKO(queue)); | ||
182 | } | ||
183 | __cvmx_pko_reset(); | ||
184 | } | ||
185 | |||
186 | /** | ||
187 | * Configure a output port and the associated queues for use. | ||
188 | * | ||
189 | * @port: Port to configure. | ||
190 | * @base_queue: First queue number to associate with this port. | ||
191 | * @num_queues: Number of queues to associate with this port | ||
192 | * @priority: Array of priority levels for each queue. Values are | ||
193 | * allowed to be 0-8. A value of 8 get 8 times the traffic | ||
194 | * of a value of 1. A value of 0 indicates that no rounds | ||
195 | * will be participated in. These priorities can be changed | ||
196 | * on the fly while the pko is enabled. A priority of 9 | ||
197 | * indicates that static priority should be used. If static | ||
198 | * priority is used all queues with static priority must be | ||
199 | * contiguous starting at the base_queue, and lower numbered | ||
200 | * queues have higher priority than higher numbered queues. | ||
201 | * There must be num_queues elements in the array. | ||
202 | */ | ||
203 | cvmx_pko_status_t cvmx_pko_config_port(uint64_t port, uint64_t base_queue, | ||
204 | uint64_t num_queues, | ||
205 | const uint64_t priority[]) | ||
206 | { | ||
207 | cvmx_pko_status_t result_code; | ||
208 | uint64_t queue; | ||
209 | union cvmx_pko_mem_queue_ptrs config; | ||
210 | union cvmx_pko_reg_queue_ptrs1 config1; | ||
211 | int static_priority_base = -1; | ||
212 | int static_priority_end = -1; | ||
213 | |||
214 | if ((port >= CVMX_PKO_NUM_OUTPUT_PORTS) | ||
215 | && (port != CVMX_PKO_MEM_QUEUE_PTRS_ILLEGAL_PID)) { | ||
216 | cvmx_dprintf("ERROR: cvmx_pko_config_port: Invalid port %llu\n", | ||
217 | (unsigned long long)port); | ||
218 | return CVMX_PKO_INVALID_PORT; | ||
219 | } | ||
220 | |||
221 | if (base_queue + num_queues > CVMX_PKO_MAX_OUTPUT_QUEUES) { | ||
222 | cvmx_dprintf | ||
223 | ("ERROR: cvmx_pko_config_port: Invalid queue range %llu\n", | ||
224 | (unsigned long long)(base_queue + num_queues)); | ||
225 | return CVMX_PKO_INVALID_QUEUE; | ||
226 | } | ||
227 | |||
228 | if (port != CVMX_PKO_MEM_QUEUE_PTRS_ILLEGAL_PID) { | ||
229 | /* | ||
230 | * Validate the static queue priority setup and set | ||
231 | * static_priority_base and static_priority_end | ||
232 | * accordingly. | ||
233 | */ | ||
234 | for (queue = 0; queue < num_queues; queue++) { | ||
235 | /* Find first queue of static priority */ | ||
236 | if (static_priority_base == -1 | ||
237 | && priority[queue] == | ||
238 | CVMX_PKO_QUEUE_STATIC_PRIORITY) | ||
239 | static_priority_base = queue; | ||
240 | /* Find last queue of static priority */ | ||
241 | if (static_priority_base != -1 | ||
242 | && static_priority_end == -1 | ||
243 | && priority[queue] != CVMX_PKO_QUEUE_STATIC_PRIORITY | ||
244 | && queue) | ||
245 | static_priority_end = queue - 1; | ||
246 | else if (static_priority_base != -1 | ||
247 | && static_priority_end == -1 | ||
248 | && queue == num_queues - 1) | ||
249 | /* all queues are static priority */ | ||
250 | static_priority_end = queue; | ||
251 | /* | ||
252 | * Check to make sure all static priority | ||
253 | * queues are contiguous. Also catches some | ||
254 | * cases of static priorites not starting at | ||
255 | * queue 0. | ||
256 | */ | ||
257 | if (static_priority_end != -1 | ||
258 | && (int)queue > static_priority_end | ||
259 | && priority[queue] == | ||
260 | CVMX_PKO_QUEUE_STATIC_PRIORITY) { | ||
261 | cvmx_dprintf("ERROR: cvmx_pko_config_port: " | ||
262 | "Static priority queues aren't " | ||
263 | "contiguous or don't start at " | ||
264 | "base queue. q: %d, eq: %d\n", | ||
265 | (int)queue, static_priority_end); | ||
266 | return CVMX_PKO_INVALID_PRIORITY; | ||
267 | } | ||
268 | } | ||
269 | if (static_priority_base > 0) { | ||
270 | cvmx_dprintf("ERROR: cvmx_pko_config_port: Static " | ||
271 | "priority queues don't start at base " | ||
272 | "queue. sq: %d\n", | ||
273 | static_priority_base); | ||
274 | return CVMX_PKO_INVALID_PRIORITY; | ||
275 | } | ||
276 | #if 0 | ||
277 | cvmx_dprintf("Port %d: Static priority queue base: %d, " | ||
278 | "end: %d\n", port, | ||
279 | static_priority_base, static_priority_end); | ||
280 | #endif | ||
281 | } | ||
282 | /* | ||
283 | * At this point, static_priority_base and static_priority_end | ||
284 | * are either both -1, or are valid start/end queue | ||
285 | * numbers. | ||
286 | */ | ||
287 | |||
288 | result_code = CVMX_PKO_SUCCESS; | ||
289 | |||
290 | #ifdef PKO_DEBUG | ||
291 | cvmx_dprintf("num queues: %d (%lld,%lld)\n", num_queues, | ||
292 | CVMX_PKO_QUEUES_PER_PORT_INTERFACE0, | ||
293 | CVMX_PKO_QUEUES_PER_PORT_INTERFACE1); | ||
294 | #endif | ||
295 | |||
296 | for (queue = 0; queue < num_queues; queue++) { | ||
297 | uint64_t *buf_ptr = NULL; | ||
298 | |||
299 | config1.u64 = 0; | ||
300 | config1.s.idx3 = queue >> 3; | ||
301 | config1.s.qid7 = (base_queue + queue) >> 7; | ||
302 | |||
303 | config.u64 = 0; | ||
304 | config.s.tail = queue == (num_queues - 1); | ||
305 | config.s.index = queue; | ||
306 | config.s.port = port; | ||
307 | config.s.queue = base_queue + queue; | ||
308 | |||
309 | if (!cvmx_octeon_is_pass1()) { | ||
310 | config.s.static_p = static_priority_base >= 0; | ||
311 | config.s.static_q = (int)queue <= static_priority_end; | ||
312 | config.s.s_tail = (int)queue == static_priority_end; | ||
313 | } | ||
314 | /* | ||
315 | * Convert the priority into an enable bit field. Try | ||
316 | * to space the bits out evenly so the packet don't | ||
317 | * get grouped up | ||
318 | */ | ||
319 | switch ((int)priority[queue]) { | ||
320 | case 0: | ||
321 | config.s.qos_mask = 0x00; | ||
322 | break; | ||
323 | case 1: | ||
324 | config.s.qos_mask = 0x01; | ||
325 | break; | ||
326 | case 2: | ||
327 | config.s.qos_mask = 0x11; | ||
328 | break; | ||
329 | case 3: | ||
330 | config.s.qos_mask = 0x49; | ||
331 | break; | ||
332 | case 4: | ||
333 | config.s.qos_mask = 0x55; | ||
334 | break; | ||
335 | case 5: | ||
336 | config.s.qos_mask = 0x57; | ||
337 | break; | ||
338 | case 6: | ||
339 | config.s.qos_mask = 0x77; | ||
340 | break; | ||
341 | case 7: | ||
342 | config.s.qos_mask = 0x7f; | ||
343 | break; | ||
344 | case 8: | ||
345 | config.s.qos_mask = 0xff; | ||
346 | break; | ||
347 | case CVMX_PKO_QUEUE_STATIC_PRIORITY: | ||
348 | /* Pass 1 will fall through to the error case */ | ||
349 | if (!cvmx_octeon_is_pass1()) { | ||
350 | config.s.qos_mask = 0xff; | ||
351 | break; | ||
352 | } | ||
353 | default: | ||
354 | cvmx_dprintf("ERROR: cvmx_pko_config_port: Invalid " | ||
355 | "priority %llu\n", | ||
356 | (unsigned long long)priority[queue]); | ||
357 | config.s.qos_mask = 0xff; | ||
358 | result_code = CVMX_PKO_INVALID_PRIORITY; | ||
359 | break; | ||
360 | } | ||
361 | |||
362 | if (port != CVMX_PKO_MEM_QUEUE_PTRS_ILLEGAL_PID) { | ||
363 | cvmx_cmd_queue_result_t cmd_res = | ||
364 | cvmx_cmd_queue_initialize(CVMX_CMD_QUEUE_PKO | ||
365 | (base_queue + queue), | ||
366 | CVMX_PKO_MAX_QUEUE_DEPTH, | ||
367 | CVMX_FPA_OUTPUT_BUFFER_POOL, | ||
368 | CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE | ||
369 | - | ||
370 | CVMX_PKO_COMMAND_BUFFER_SIZE_ADJUST | ||
371 | * 8); | ||
372 | if (cmd_res != CVMX_CMD_QUEUE_SUCCESS) { | ||
373 | switch (cmd_res) { | ||
374 | case CVMX_CMD_QUEUE_NO_MEMORY: | ||
375 | cvmx_dprintf("ERROR: " | ||
376 | "cvmx_pko_config_port: " | ||
377 | "Unable to allocate " | ||
378 | "output buffer.\n"); | ||
379 | return CVMX_PKO_NO_MEMORY; | ||
380 | case CVMX_CMD_QUEUE_ALREADY_SETUP: | ||
381 | cvmx_dprintf | ||
382 | ("ERROR: cvmx_pko_config_port: Port already setup.\n"); | ||
383 | return CVMX_PKO_PORT_ALREADY_SETUP; | ||
384 | case CVMX_CMD_QUEUE_INVALID_PARAM: | ||
385 | default: | ||
386 | cvmx_dprintf | ||
387 | ("ERROR: cvmx_pko_config_port: Command queue initialization failed.\n"); | ||
388 | return CVMX_PKO_CMD_QUEUE_INIT_ERROR; | ||
389 | } | ||
390 | } | ||
391 | |||
392 | buf_ptr = | ||
393 | (uint64_t *) | ||
394 | cvmx_cmd_queue_buffer(CVMX_CMD_QUEUE_PKO | ||
395 | (base_queue + queue)); | ||
396 | config.s.buf_ptr = cvmx_ptr_to_phys(buf_ptr); | ||
397 | } else | ||
398 | config.s.buf_ptr = 0; | ||
399 | |||
400 | CVMX_SYNCWS; | ||
401 | |||
402 | if (!OCTEON_IS_MODEL(OCTEON_CN3XXX)) | ||
403 | cvmx_write_csr(CVMX_PKO_REG_QUEUE_PTRS1, config1.u64); | ||
404 | cvmx_write_csr(CVMX_PKO_MEM_QUEUE_PTRS, config.u64); | ||
405 | } | ||
406 | |||
407 | return result_code; | ||
408 | } | ||
409 | |||
410 | #ifdef PKO_DEBUG | ||
411 | /** | ||
412 | * Show map of ports -> queues for different cores. | ||
413 | */ | ||
414 | void cvmx_pko_show_queue_map() | ||
415 | { | ||
416 | int core, port; | ||
417 | int pko_output_ports = 36; | ||
418 | |||
419 | cvmx_dprintf("port"); | ||
420 | for (port = 0; port < pko_output_ports; port++) | ||
421 | cvmx_dprintf("%3d ", port); | ||
422 | cvmx_dprintf("\n"); | ||
423 | |||
424 | for (core = 0; core < CVMX_MAX_CORES; core++) { | ||
425 | cvmx_dprintf("\n%2d: ", core); | ||
426 | for (port = 0; port < pko_output_ports; port++) { | ||
427 | cvmx_dprintf("%3d ", | ||
428 | cvmx_pko_get_base_queue_per_core(port, | ||
429 | core)); | ||
430 | } | ||
431 | } | ||
432 | cvmx_dprintf("\n"); | ||
433 | } | ||
434 | #endif | ||
435 | |||
436 | /** | ||
437 | * Rate limit a PKO port to a max packets/sec. This function is only | ||
438 | * supported on CN51XX and higher, excluding CN58XX. | ||
439 | * | ||
440 | * @port: Port to rate limit | ||
441 | * @packets_s: Maximum packet/sec | ||
442 | * @burst: Maximum number of packets to burst in a row before rate | ||
443 | * limiting cuts in. | ||
444 | * | ||
445 | * Returns Zero on success, negative on failure | ||
446 | */ | ||
447 | int cvmx_pko_rate_limit_packets(int port, int packets_s, int burst) | ||
448 | { | ||
449 | union cvmx_pko_mem_port_rate0 pko_mem_port_rate0; | ||
450 | union cvmx_pko_mem_port_rate1 pko_mem_port_rate1; | ||
451 | |||
452 | pko_mem_port_rate0.u64 = 0; | ||
453 | pko_mem_port_rate0.s.pid = port; | ||
454 | pko_mem_port_rate0.s.rate_pkt = | ||
455 | cvmx_sysinfo_get()->cpu_clock_hz / packets_s / 16; | ||
456 | /* No cost per word since we are limited by packets/sec, not bits/sec */ | ||
457 | pko_mem_port_rate0.s.rate_word = 0; | ||
458 | |||
459 | pko_mem_port_rate1.u64 = 0; | ||
460 | pko_mem_port_rate1.s.pid = port; | ||
461 | pko_mem_port_rate1.s.rate_lim = | ||
462 | ((uint64_t) pko_mem_port_rate0.s.rate_pkt * burst) >> 8; | ||
463 | |||
464 | cvmx_write_csr(CVMX_PKO_MEM_PORT_RATE0, pko_mem_port_rate0.u64); | ||
465 | cvmx_write_csr(CVMX_PKO_MEM_PORT_RATE1, pko_mem_port_rate1.u64); | ||
466 | return 0; | ||
467 | } | ||
468 | |||
469 | /** | ||
470 | * Rate limit a PKO port to a max bits/sec. This function is only | ||
471 | * supported on CN51XX and higher, excluding CN58XX. | ||
472 | * | ||
473 | * @port: Port to rate limit | ||
474 | * @bits_s: PKO rate limit in bits/sec | ||
475 | * @burst: Maximum number of bits to burst before rate | ||
476 | * limiting cuts in. | ||
477 | * | ||
478 | * Returns Zero on success, negative on failure | ||
479 | */ | ||
480 | int cvmx_pko_rate_limit_bits(int port, uint64_t bits_s, int burst) | ||
481 | { | ||
482 | union cvmx_pko_mem_port_rate0 pko_mem_port_rate0; | ||
483 | union cvmx_pko_mem_port_rate1 pko_mem_port_rate1; | ||
484 | uint64_t clock_rate = cvmx_sysinfo_get()->cpu_clock_hz; | ||
485 | uint64_t tokens_per_bit = clock_rate * 16 / bits_s; | ||
486 | |||
487 | pko_mem_port_rate0.u64 = 0; | ||
488 | pko_mem_port_rate0.s.pid = port; | ||
489 | /* | ||
490 | * Each packet has a 12 bytes of interframe gap, an 8 byte | ||
491 | * preamble, and a 4 byte CRC. These are not included in the | ||
492 | * per word count. Multiply by 8 to covert to bits and divide | ||
493 | * by 256 for limit granularity. | ||
494 | */ | ||
495 | pko_mem_port_rate0.s.rate_pkt = (12 + 8 + 4) * 8 * tokens_per_bit / 256; | ||
496 | /* Each 8 byte word has 64bits */ | ||
497 | pko_mem_port_rate0.s.rate_word = 64 * tokens_per_bit; | ||
498 | |||
499 | pko_mem_port_rate1.u64 = 0; | ||
500 | pko_mem_port_rate1.s.pid = port; | ||
501 | pko_mem_port_rate1.s.rate_lim = tokens_per_bit * burst / 256; | ||
502 | |||
503 | cvmx_write_csr(CVMX_PKO_MEM_PORT_RATE0, pko_mem_port_rate0.u64); | ||
504 | cvmx_write_csr(CVMX_PKO_MEM_PORT_RATE1, pko_mem_port_rate1.u64); | ||
505 | return 0; | ||
506 | } | ||
diff --git a/arch/mips/cavium-octeon/executive/cvmx-spi.c b/arch/mips/cavium-octeon/executive/cvmx-spi.c new file mode 100644 index 000000000000..74afb1710cd9 --- /dev/null +++ b/arch/mips/cavium-octeon/executive/cvmx-spi.c | |||
@@ -0,0 +1,667 @@ | |||
1 | /***********************license start*************** | ||
2 | * Author: Cavium Networks | ||
3 | * | ||
4 | * Contact: support@caviumnetworks.com | ||
5 | * This file is part of the OCTEON SDK | ||
6 | * | ||
7 | * Copyright (c) 2003-2008 Cavium Networks | ||
8 | * | ||
9 | * This file is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License, Version 2, as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * This file is distributed in the hope that it will be useful, but | ||
14 | * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty | ||
15 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or | ||
16 | * NONINFRINGEMENT. See the GNU General Public License for more | ||
17 | * details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this file; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
22 | * or visit http://www.gnu.org/licenses/. | ||
23 | * | ||
24 | * This file may also be available under a different license from Cavium. | ||
25 | * Contact Cavium Networks for more information | ||
26 | ***********************license end**************************************/ | ||
27 | |||
28 | /* | ||
29 | * | ||
30 | * Support library for the SPI | ||
31 | */ | ||
32 | #include <asm/octeon/octeon.h> | ||
33 | |||
34 | #include <asm/octeon/cvmx-config.h> | ||
35 | |||
36 | #include <asm/octeon/cvmx-pko.h> | ||
37 | #include <asm/octeon/cvmx-spi.h> | ||
38 | |||
39 | #include <asm/octeon/cvmx-spxx-defs.h> | ||
40 | #include <asm/octeon/cvmx-stxx-defs.h> | ||
41 | #include <asm/octeon/cvmx-srxx-defs.h> | ||
42 | |||
43 | #define INVOKE_CB(function_p, args...) \ | ||
44 | do { \ | ||
45 | if (function_p) { \ | ||
46 | res = function_p(args); \ | ||
47 | if (res) \ | ||
48 | return res; \ | ||
49 | } \ | ||
50 | } while (0) | ||
51 | |||
52 | #if CVMX_ENABLE_DEBUG_PRINTS | ||
53 | static const char *modes[] = | ||
54 | { "UNKNOWN", "TX Halfplex", "Rx Halfplex", "Duplex" }; | ||
55 | #endif | ||
56 | |||
57 | /* Default callbacks, can be overridden | ||
58 | * using cvmx_spi_get_callbacks/cvmx_spi_set_callbacks | ||
59 | */ | ||
60 | static cvmx_spi_callbacks_t cvmx_spi_callbacks = { | ||
61 | .reset_cb = cvmx_spi_reset_cb, | ||
62 | .calendar_setup_cb = cvmx_spi_calendar_setup_cb, | ||
63 | .clock_detect_cb = cvmx_spi_clock_detect_cb, | ||
64 | .training_cb = cvmx_spi_training_cb, | ||
65 | .calendar_sync_cb = cvmx_spi_calendar_sync_cb, | ||
66 | .interface_up_cb = cvmx_spi_interface_up_cb | ||
67 | }; | ||
68 | |||
69 | /** | ||
70 | * Get current SPI4 initialization callbacks | ||
71 | * | ||
72 | * @callbacks: Pointer to the callbacks structure.to fill | ||
73 | * | ||
74 | * Returns Pointer to cvmx_spi_callbacks_t structure. | ||
75 | */ | ||
76 | void cvmx_spi_get_callbacks(cvmx_spi_callbacks_t *callbacks) | ||
77 | { | ||
78 | memcpy(callbacks, &cvmx_spi_callbacks, sizeof(cvmx_spi_callbacks)); | ||
79 | } | ||
80 | |||
81 | /** | ||
82 | * Set new SPI4 initialization callbacks | ||
83 | * | ||
84 | * @new_callbacks: Pointer to an updated callbacks structure. | ||
85 | */ | ||
86 | void cvmx_spi_set_callbacks(cvmx_spi_callbacks_t *new_callbacks) | ||
87 | { | ||
88 | memcpy(&cvmx_spi_callbacks, new_callbacks, sizeof(cvmx_spi_callbacks)); | ||
89 | } | ||
90 | |||
91 | /** | ||
92 | * Initialize and start the SPI interface. | ||
93 | * | ||
94 | * @interface: The identifier of the packet interface to configure and | ||
95 | * use as a SPI interface. | ||
96 | * @mode: The operating mode for the SPI interface. The interface | ||
97 | * can operate as a full duplex (both Tx and Rx data paths | ||
98 | * active) or as a halfplex (either the Tx data path is | ||
99 | * active or the Rx data path is active, but not both). | ||
100 | * @timeout: Timeout to wait for clock synchronization in seconds | ||
101 | * @num_ports: Number of SPI ports to configure | ||
102 | * | ||
103 | * Returns Zero on success, negative of failure. | ||
104 | */ | ||
105 | int cvmx_spi_start_interface(int interface, cvmx_spi_mode_t mode, int timeout, | ||
106 | int num_ports) | ||
107 | { | ||
108 | int res = -1; | ||
109 | |||
110 | if (!(OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX))) | ||
111 | return res; | ||
112 | |||
113 | /* Callback to perform SPI4 reset */ | ||
114 | INVOKE_CB(cvmx_spi_callbacks.reset_cb, interface, mode); | ||
115 | |||
116 | /* Callback to perform calendar setup */ | ||
117 | INVOKE_CB(cvmx_spi_callbacks.calendar_setup_cb, interface, mode, | ||
118 | num_ports); | ||
119 | |||
120 | /* Callback to perform clock detection */ | ||
121 | INVOKE_CB(cvmx_spi_callbacks.clock_detect_cb, interface, mode, timeout); | ||
122 | |||
123 | /* Callback to perform SPI4 link training */ | ||
124 | INVOKE_CB(cvmx_spi_callbacks.training_cb, interface, mode, timeout); | ||
125 | |||
126 | /* Callback to perform calendar sync */ | ||
127 | INVOKE_CB(cvmx_spi_callbacks.calendar_sync_cb, interface, mode, | ||
128 | timeout); | ||
129 | |||
130 | /* Callback to handle interface coming up */ | ||
131 | INVOKE_CB(cvmx_spi_callbacks.interface_up_cb, interface, mode); | ||
132 | |||
133 | return res; | ||
134 | } | ||
135 | |||
136 | /** | ||
137 | * This routine restarts the SPI interface after it has lost synchronization | ||
138 | * with its correspondent system. | ||
139 | * | ||
140 | * @interface: The identifier of the packet interface to configure and | ||
141 | * use as a SPI interface. | ||
142 | * @mode: The operating mode for the SPI interface. The interface | ||
143 | * can operate as a full duplex (both Tx and Rx data paths | ||
144 | * active) or as a halfplex (either the Tx data path is | ||
145 | * active or the Rx data path is active, but not both). | ||
146 | * @timeout: Timeout to wait for clock synchronization in seconds | ||
147 | * | ||
148 | * Returns Zero on success, negative of failure. | ||
149 | */ | ||
150 | int cvmx_spi_restart_interface(int interface, cvmx_spi_mode_t mode, int timeout) | ||
151 | { | ||
152 | int res = -1; | ||
153 | |||
154 | if (!(OCTEON_IS_MODEL(OCTEON_CN38XX) || OCTEON_IS_MODEL(OCTEON_CN58XX))) | ||
155 | return res; | ||
156 | |||
157 | cvmx_dprintf("SPI%d: Restart %s\n", interface, modes[mode]); | ||
158 | |||
159 | /* Callback to perform SPI4 reset */ | ||
160 | INVOKE_CB(cvmx_spi_callbacks.reset_cb, interface, mode); | ||
161 | |||
162 | /* NOTE: Calendar setup is not performed during restart */ | ||
163 | /* Refer to cvmx_spi_start_interface() for the full sequence */ | ||
164 | |||
165 | /* Callback to perform clock detection */ | ||
166 | INVOKE_CB(cvmx_spi_callbacks.clock_detect_cb, interface, mode, timeout); | ||
167 | |||
168 | /* Callback to perform SPI4 link training */ | ||
169 | INVOKE_CB(cvmx_spi_callbacks.training_cb, interface, mode, timeout); | ||
170 | |||
171 | /* Callback to perform calendar sync */ | ||
172 | INVOKE_CB(cvmx_spi_callbacks.calendar_sync_cb, interface, mode, | ||
173 | timeout); | ||
174 | |||
175 | /* Callback to handle interface coming up */ | ||
176 | INVOKE_CB(cvmx_spi_callbacks.interface_up_cb, interface, mode); | ||
177 | |||
178 | return res; | ||
179 | } | ||
180 | |||
181 | /** | ||
182 | * Callback to perform SPI4 reset | ||
183 | * | ||
184 | * @interface: The identifier of the packet interface to configure and | ||
185 | * use as a SPI interface. | ||
186 | * @mode: The operating mode for the SPI interface. The interface | ||
187 | * can operate as a full duplex (both Tx and Rx data paths | ||
188 | * active) or as a halfplex (either the Tx data path is | ||
189 | * active or the Rx data path is active, but not both). | ||
190 | * | ||
191 | * Returns Zero on success, non-zero error code on failure (will cause | ||
192 | * SPI initialization to abort) | ||
193 | */ | ||
194 | int cvmx_spi_reset_cb(int interface, cvmx_spi_mode_t mode) | ||
195 | { | ||
196 | union cvmx_spxx_dbg_deskew_ctl spxx_dbg_deskew_ctl; | ||
197 | union cvmx_spxx_clk_ctl spxx_clk_ctl; | ||
198 | union cvmx_spxx_bist_stat spxx_bist_stat; | ||
199 | union cvmx_spxx_int_msk spxx_int_msk; | ||
200 | union cvmx_stxx_int_msk stxx_int_msk; | ||
201 | union cvmx_spxx_trn4_ctl spxx_trn4_ctl; | ||
202 | int index; | ||
203 | uint64_t MS = cvmx_sysinfo_get()->cpu_clock_hz / 1000; | ||
204 | |||
205 | /* Disable SPI error events while we run BIST */ | ||
206 | spxx_int_msk.u64 = cvmx_read_csr(CVMX_SPXX_INT_MSK(interface)); | ||
207 | cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), 0); | ||
208 | stxx_int_msk.u64 = cvmx_read_csr(CVMX_STXX_INT_MSK(interface)); | ||
209 | cvmx_write_csr(CVMX_STXX_INT_MSK(interface), 0); | ||
210 | |||
211 | /* Run BIST in the SPI interface */ | ||
212 | cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), 0); | ||
213 | cvmx_write_csr(CVMX_STXX_COM_CTL(interface), 0); | ||
214 | spxx_clk_ctl.u64 = 0; | ||
215 | spxx_clk_ctl.s.runbist = 1; | ||
216 | cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64); | ||
217 | cvmx_wait(10 * MS); | ||
218 | spxx_bist_stat.u64 = cvmx_read_csr(CVMX_SPXX_BIST_STAT(interface)); | ||
219 | if (spxx_bist_stat.s.stat0) | ||
220 | cvmx_dprintf | ||
221 | ("ERROR SPI%d: BIST failed on receive datapath FIFO\n", | ||
222 | interface); | ||
223 | if (spxx_bist_stat.s.stat1) | ||
224 | cvmx_dprintf("ERROR SPI%d: BIST failed on RX calendar table\n", | ||
225 | interface); | ||
226 | if (spxx_bist_stat.s.stat2) | ||
227 | cvmx_dprintf("ERROR SPI%d: BIST failed on TX calendar table\n", | ||
228 | interface); | ||
229 | |||
230 | /* Clear the calendar table after BIST to fix parity errors */ | ||
231 | for (index = 0; index < 32; index++) { | ||
232 | union cvmx_srxx_spi4_calx srxx_spi4_calx; | ||
233 | union cvmx_stxx_spi4_calx stxx_spi4_calx; | ||
234 | |||
235 | srxx_spi4_calx.u64 = 0; | ||
236 | srxx_spi4_calx.s.oddpar = 1; | ||
237 | cvmx_write_csr(CVMX_SRXX_SPI4_CALX(index, interface), | ||
238 | srxx_spi4_calx.u64); | ||
239 | |||
240 | stxx_spi4_calx.u64 = 0; | ||
241 | stxx_spi4_calx.s.oddpar = 1; | ||
242 | cvmx_write_csr(CVMX_STXX_SPI4_CALX(index, interface), | ||
243 | stxx_spi4_calx.u64); | ||
244 | } | ||
245 | |||
246 | /* Re enable reporting of error interrupts */ | ||
247 | cvmx_write_csr(CVMX_SPXX_INT_REG(interface), | ||
248 | cvmx_read_csr(CVMX_SPXX_INT_REG(interface))); | ||
249 | cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), spxx_int_msk.u64); | ||
250 | cvmx_write_csr(CVMX_STXX_INT_REG(interface), | ||
251 | cvmx_read_csr(CVMX_STXX_INT_REG(interface))); | ||
252 | cvmx_write_csr(CVMX_STXX_INT_MSK(interface), stxx_int_msk.u64); | ||
253 | |||
254 | /* Setup the CLKDLY right in the middle */ | ||
255 | spxx_clk_ctl.u64 = 0; | ||
256 | spxx_clk_ctl.s.seetrn = 0; | ||
257 | spxx_clk_ctl.s.clkdly = 0x10; | ||
258 | spxx_clk_ctl.s.runbist = 0; | ||
259 | spxx_clk_ctl.s.statdrv = 0; | ||
260 | /* This should always be on the opposite edge as statdrv */ | ||
261 | spxx_clk_ctl.s.statrcv = 1; | ||
262 | spxx_clk_ctl.s.sndtrn = 0; | ||
263 | spxx_clk_ctl.s.drptrn = 0; | ||
264 | spxx_clk_ctl.s.rcvtrn = 0; | ||
265 | spxx_clk_ctl.s.srxdlck = 0; | ||
266 | cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64); | ||
267 | cvmx_wait(100 * MS); | ||
268 | |||
269 | /* Reset SRX0 DLL */ | ||
270 | spxx_clk_ctl.s.srxdlck = 1; | ||
271 | cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64); | ||
272 | |||
273 | /* Waiting for Inf0 Spi4 RX DLL to lock */ | ||
274 | cvmx_wait(100 * MS); | ||
275 | |||
276 | /* Enable dynamic alignment */ | ||
277 | spxx_trn4_ctl.s.trntest = 0; | ||
278 | spxx_trn4_ctl.s.jitter = 1; | ||
279 | spxx_trn4_ctl.s.clr_boot = 1; | ||
280 | spxx_trn4_ctl.s.set_boot = 0; | ||
281 | if (OCTEON_IS_MODEL(OCTEON_CN58XX)) | ||
282 | spxx_trn4_ctl.s.maxdist = 3; | ||
283 | else | ||
284 | spxx_trn4_ctl.s.maxdist = 8; | ||
285 | spxx_trn4_ctl.s.macro_en = 1; | ||
286 | spxx_trn4_ctl.s.mux_en = 1; | ||
287 | cvmx_write_csr(CVMX_SPXX_TRN4_CTL(interface), spxx_trn4_ctl.u64); | ||
288 | |||
289 | spxx_dbg_deskew_ctl.u64 = 0; | ||
290 | cvmx_write_csr(CVMX_SPXX_DBG_DESKEW_CTL(interface), | ||
291 | spxx_dbg_deskew_ctl.u64); | ||
292 | |||
293 | return 0; | ||
294 | } | ||
295 | |||
296 | /** | ||
297 | * Callback to setup calendar and miscellaneous settings before clock detection | ||
298 | * | ||
299 | * @interface: The identifier of the packet interface to configure and | ||
300 | * use as a SPI interface. | ||
301 | * @mode: The operating mode for the SPI interface. The interface | ||
302 | * can operate as a full duplex (both Tx and Rx data paths | ||
303 | * active) or as a halfplex (either the Tx data path is | ||
304 | * active or the Rx data path is active, but not both). | ||
305 | * @num_ports: Number of ports to configure on SPI | ||
306 | * | ||
307 | * Returns Zero on success, non-zero error code on failure (will cause | ||
308 | * SPI initialization to abort) | ||
309 | */ | ||
310 | int cvmx_spi_calendar_setup_cb(int interface, cvmx_spi_mode_t mode, | ||
311 | int num_ports) | ||
312 | { | ||
313 | int port; | ||
314 | int index; | ||
315 | if (mode & CVMX_SPI_MODE_RX_HALFPLEX) { | ||
316 | union cvmx_srxx_com_ctl srxx_com_ctl; | ||
317 | union cvmx_srxx_spi4_stat srxx_spi4_stat; | ||
318 | |||
319 | /* SRX0 number of Ports */ | ||
320 | srxx_com_ctl.u64 = 0; | ||
321 | srxx_com_ctl.s.prts = num_ports - 1; | ||
322 | srxx_com_ctl.s.st_en = 0; | ||
323 | srxx_com_ctl.s.inf_en = 0; | ||
324 | cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64); | ||
325 | |||
326 | /* SRX0 Calendar Table. This round robbins through all ports */ | ||
327 | port = 0; | ||
328 | index = 0; | ||
329 | while (port < num_ports) { | ||
330 | union cvmx_srxx_spi4_calx srxx_spi4_calx; | ||
331 | srxx_spi4_calx.u64 = 0; | ||
332 | srxx_spi4_calx.s.prt0 = port++; | ||
333 | srxx_spi4_calx.s.prt1 = port++; | ||
334 | srxx_spi4_calx.s.prt2 = port++; | ||
335 | srxx_spi4_calx.s.prt3 = port++; | ||
336 | srxx_spi4_calx.s.oddpar = | ||
337 | ~(cvmx_dpop(srxx_spi4_calx.u64) & 1); | ||
338 | cvmx_write_csr(CVMX_SRXX_SPI4_CALX(index, interface), | ||
339 | srxx_spi4_calx.u64); | ||
340 | index++; | ||
341 | } | ||
342 | srxx_spi4_stat.u64 = 0; | ||
343 | srxx_spi4_stat.s.len = num_ports; | ||
344 | srxx_spi4_stat.s.m = 1; | ||
345 | cvmx_write_csr(CVMX_SRXX_SPI4_STAT(interface), | ||
346 | srxx_spi4_stat.u64); | ||
347 | } | ||
348 | |||
349 | if (mode & CVMX_SPI_MODE_TX_HALFPLEX) { | ||
350 | union cvmx_stxx_arb_ctl stxx_arb_ctl; | ||
351 | union cvmx_gmxx_tx_spi_max gmxx_tx_spi_max; | ||
352 | union cvmx_gmxx_tx_spi_thresh gmxx_tx_spi_thresh; | ||
353 | union cvmx_gmxx_tx_spi_ctl gmxx_tx_spi_ctl; | ||
354 | union cvmx_stxx_spi4_stat stxx_spi4_stat; | ||
355 | union cvmx_stxx_spi4_dat stxx_spi4_dat; | ||
356 | |||
357 | /* STX0 Config */ | ||
358 | stxx_arb_ctl.u64 = 0; | ||
359 | stxx_arb_ctl.s.igntpa = 0; | ||
360 | stxx_arb_ctl.s.mintrn = 0; | ||
361 | cvmx_write_csr(CVMX_STXX_ARB_CTL(interface), stxx_arb_ctl.u64); | ||
362 | |||
363 | gmxx_tx_spi_max.u64 = 0; | ||
364 | gmxx_tx_spi_max.s.max1 = 8; | ||
365 | gmxx_tx_spi_max.s.max2 = 4; | ||
366 | gmxx_tx_spi_max.s.slice = 0; | ||
367 | cvmx_write_csr(CVMX_GMXX_TX_SPI_MAX(interface), | ||
368 | gmxx_tx_spi_max.u64); | ||
369 | |||
370 | gmxx_tx_spi_thresh.u64 = 0; | ||
371 | gmxx_tx_spi_thresh.s.thresh = 4; | ||
372 | cvmx_write_csr(CVMX_GMXX_TX_SPI_THRESH(interface), | ||
373 | gmxx_tx_spi_thresh.u64); | ||
374 | |||
375 | gmxx_tx_spi_ctl.u64 = 0; | ||
376 | gmxx_tx_spi_ctl.s.tpa_clr = 0; | ||
377 | gmxx_tx_spi_ctl.s.cont_pkt = 0; | ||
378 | cvmx_write_csr(CVMX_GMXX_TX_SPI_CTL(interface), | ||
379 | gmxx_tx_spi_ctl.u64); | ||
380 | |||
381 | /* STX0 Training Control */ | ||
382 | stxx_spi4_dat.u64 = 0; | ||
383 | /*Minimum needed by dynamic alignment */ | ||
384 | stxx_spi4_dat.s.alpha = 32; | ||
385 | stxx_spi4_dat.s.max_t = 0xFFFF; /*Minimum interval is 0x20 */ | ||
386 | cvmx_write_csr(CVMX_STXX_SPI4_DAT(interface), | ||
387 | stxx_spi4_dat.u64); | ||
388 | |||
389 | /* STX0 Calendar Table. This round robbins through all ports */ | ||
390 | port = 0; | ||
391 | index = 0; | ||
392 | while (port < num_ports) { | ||
393 | union cvmx_stxx_spi4_calx stxx_spi4_calx; | ||
394 | stxx_spi4_calx.u64 = 0; | ||
395 | stxx_spi4_calx.s.prt0 = port++; | ||
396 | stxx_spi4_calx.s.prt1 = port++; | ||
397 | stxx_spi4_calx.s.prt2 = port++; | ||
398 | stxx_spi4_calx.s.prt3 = port++; | ||
399 | stxx_spi4_calx.s.oddpar = | ||
400 | ~(cvmx_dpop(stxx_spi4_calx.u64) & 1); | ||
401 | cvmx_write_csr(CVMX_STXX_SPI4_CALX(index, interface), | ||
402 | stxx_spi4_calx.u64); | ||
403 | index++; | ||
404 | } | ||
405 | stxx_spi4_stat.u64 = 0; | ||
406 | stxx_spi4_stat.s.len = num_ports; | ||
407 | stxx_spi4_stat.s.m = 1; | ||
408 | cvmx_write_csr(CVMX_STXX_SPI4_STAT(interface), | ||
409 | stxx_spi4_stat.u64); | ||
410 | } | ||
411 | |||
412 | return 0; | ||
413 | } | ||
414 | |||
415 | /** | ||
416 | * Callback to perform clock detection | ||
417 | * | ||
418 | * @interface: The identifier of the packet interface to configure and | ||
419 | * use as a SPI interface. | ||
420 | * @mode: The operating mode for the SPI interface. The interface | ||
421 | * can operate as a full duplex (both Tx and Rx data paths | ||
422 | * active) or as a halfplex (either the Tx data path is | ||
423 | * active or the Rx data path is active, but not both). | ||
424 | * @timeout: Timeout to wait for clock synchronization in seconds | ||
425 | * | ||
426 | * Returns Zero on success, non-zero error code on failure (will cause | ||
427 | * SPI initialization to abort) | ||
428 | */ | ||
429 | int cvmx_spi_clock_detect_cb(int interface, cvmx_spi_mode_t mode, int timeout) | ||
430 | { | ||
431 | int clock_transitions; | ||
432 | union cvmx_spxx_clk_stat stat; | ||
433 | uint64_t timeout_time; | ||
434 | uint64_t MS = cvmx_sysinfo_get()->cpu_clock_hz / 1000; | ||
435 | |||
436 | /* | ||
437 | * Regardless of operating mode, both Tx and Rx clocks must be | ||
438 | * present for the SPI interface to operate. | ||
439 | */ | ||
440 | cvmx_dprintf("SPI%d: Waiting to see TsClk...\n", interface); | ||
441 | timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout; | ||
442 | /* | ||
443 | * Require 100 clock transitions in order to avoid any noise | ||
444 | * in the beginning. | ||
445 | */ | ||
446 | clock_transitions = 100; | ||
447 | do { | ||
448 | stat.u64 = cvmx_read_csr(CVMX_SPXX_CLK_STAT(interface)); | ||
449 | if (stat.s.s4clk0 && stat.s.s4clk1 && clock_transitions) { | ||
450 | /* | ||
451 | * We've seen a clock transition, so decrement | ||
452 | * the number we still need. | ||
453 | */ | ||
454 | clock_transitions--; | ||
455 | cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64); | ||
456 | stat.s.s4clk0 = 0; | ||
457 | stat.s.s4clk1 = 0; | ||
458 | } | ||
459 | if (cvmx_get_cycle() > timeout_time) { | ||
460 | cvmx_dprintf("SPI%d: Timeout\n", interface); | ||
461 | return -1; | ||
462 | } | ||
463 | } while (stat.s.s4clk0 == 0 || stat.s.s4clk1 == 0); | ||
464 | |||
465 | cvmx_dprintf("SPI%d: Waiting to see RsClk...\n", interface); | ||
466 | timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout; | ||
467 | /* | ||
468 | * Require 100 clock transitions in order to avoid any noise in the | ||
469 | * beginning. | ||
470 | */ | ||
471 | clock_transitions = 100; | ||
472 | do { | ||
473 | stat.u64 = cvmx_read_csr(CVMX_SPXX_CLK_STAT(interface)); | ||
474 | if (stat.s.d4clk0 && stat.s.d4clk1 && clock_transitions) { | ||
475 | /* | ||
476 | * We've seen a clock transition, so decrement | ||
477 | * the number we still need | ||
478 | */ | ||
479 | clock_transitions--; | ||
480 | cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64); | ||
481 | stat.s.d4clk0 = 0; | ||
482 | stat.s.d4clk1 = 0; | ||
483 | } | ||
484 | if (cvmx_get_cycle() > timeout_time) { | ||
485 | cvmx_dprintf("SPI%d: Timeout\n", interface); | ||
486 | return -1; | ||
487 | } | ||
488 | } while (stat.s.d4clk0 == 0 || stat.s.d4clk1 == 0); | ||
489 | |||
490 | return 0; | ||
491 | } | ||
492 | |||
493 | /** | ||
494 | * Callback to perform link training | ||
495 | * | ||
496 | * @interface: The identifier of the packet interface to configure and | ||
497 | * use as a SPI interface. | ||
498 | * @mode: The operating mode for the SPI interface. The interface | ||
499 | * can operate as a full duplex (both Tx and Rx data paths | ||
500 | * active) or as a halfplex (either the Tx data path is | ||
501 | * active or the Rx data path is active, but not both). | ||
502 | * @timeout: Timeout to wait for link to be trained (in seconds) | ||
503 | * | ||
504 | * Returns Zero on success, non-zero error code on failure (will cause | ||
505 | * SPI initialization to abort) | ||
506 | */ | ||
507 | int cvmx_spi_training_cb(int interface, cvmx_spi_mode_t mode, int timeout) | ||
508 | { | ||
509 | union cvmx_spxx_trn4_ctl spxx_trn4_ctl; | ||
510 | union cvmx_spxx_clk_stat stat; | ||
511 | uint64_t MS = cvmx_sysinfo_get()->cpu_clock_hz / 1000; | ||
512 | uint64_t timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout; | ||
513 | int rx_training_needed; | ||
514 | |||
515 | /* SRX0 & STX0 Inf0 Links are configured - begin training */ | ||
516 | union cvmx_spxx_clk_ctl spxx_clk_ctl; | ||
517 | spxx_clk_ctl.u64 = 0; | ||
518 | spxx_clk_ctl.s.seetrn = 0; | ||
519 | spxx_clk_ctl.s.clkdly = 0x10; | ||
520 | spxx_clk_ctl.s.runbist = 0; | ||
521 | spxx_clk_ctl.s.statdrv = 0; | ||
522 | /* This should always be on the opposite edge as statdrv */ | ||
523 | spxx_clk_ctl.s.statrcv = 1; | ||
524 | spxx_clk_ctl.s.sndtrn = 1; | ||
525 | spxx_clk_ctl.s.drptrn = 1; | ||
526 | spxx_clk_ctl.s.rcvtrn = 1; | ||
527 | spxx_clk_ctl.s.srxdlck = 1; | ||
528 | cvmx_write_csr(CVMX_SPXX_CLK_CTL(interface), spxx_clk_ctl.u64); | ||
529 | cvmx_wait(1000 * MS); | ||
530 | |||
531 | /* SRX0 clear the boot bit */ | ||
532 | spxx_trn4_ctl.u64 = cvmx_read_csr(CVMX_SPXX_TRN4_CTL(interface)); | ||
533 | spxx_trn4_ctl.s.clr_boot = 1; | ||
534 | cvmx_write_csr(CVMX_SPXX_TRN4_CTL(interface), spxx_trn4_ctl.u64); | ||
535 | |||
536 | /* Wait for the training sequence to complete */ | ||
537 | cvmx_dprintf("SPI%d: Waiting for training\n", interface); | ||
538 | cvmx_wait(1000 * MS); | ||
539 | /* Wait a really long time here */ | ||
540 | timeout_time = cvmx_get_cycle() + 1000ull * MS * 600; | ||
541 | /* | ||
542 | * The HRM says we must wait for 34 + 16 * MAXDIST training sequences. | ||
543 | * We'll be pessimistic and wait for a lot more. | ||
544 | */ | ||
545 | rx_training_needed = 500; | ||
546 | do { | ||
547 | stat.u64 = cvmx_read_csr(CVMX_SPXX_CLK_STAT(interface)); | ||
548 | if (stat.s.srxtrn && rx_training_needed) { | ||
549 | rx_training_needed--; | ||
550 | cvmx_write_csr(CVMX_SPXX_CLK_STAT(interface), stat.u64); | ||
551 | stat.s.srxtrn = 0; | ||
552 | } | ||
553 | if (cvmx_get_cycle() > timeout_time) { | ||
554 | cvmx_dprintf("SPI%d: Timeout\n", interface); | ||
555 | return -1; | ||
556 | } | ||
557 | } while (stat.s.srxtrn == 0); | ||
558 | |||
559 | return 0; | ||
560 | } | ||
561 | |||
562 | /** | ||
563 | * Callback to perform calendar data synchronization | ||
564 | * | ||
565 | * @interface: The identifier of the packet interface to configure and | ||
566 | * use as a SPI interface. | ||
567 | * @mode: The operating mode for the SPI interface. The interface | ||
568 | * can operate as a full duplex (both Tx and Rx data paths | ||
569 | * active) or as a halfplex (either the Tx data path is | ||
570 | * active or the Rx data path is active, but not both). | ||
571 | * @timeout: Timeout to wait for calendar data in seconds | ||
572 | * | ||
573 | * Returns Zero on success, non-zero error code on failure (will cause | ||
574 | * SPI initialization to abort) | ||
575 | */ | ||
576 | int cvmx_spi_calendar_sync_cb(int interface, cvmx_spi_mode_t mode, int timeout) | ||
577 | { | ||
578 | uint64_t MS = cvmx_sysinfo_get()->cpu_clock_hz / 1000; | ||
579 | if (mode & CVMX_SPI_MODE_RX_HALFPLEX) { | ||
580 | /* SRX0 interface should be good, send calendar data */ | ||
581 | union cvmx_srxx_com_ctl srxx_com_ctl; | ||
582 | cvmx_dprintf | ||
583 | ("SPI%d: Rx is synchronized, start sending calendar data\n", | ||
584 | interface); | ||
585 | srxx_com_ctl.u64 = cvmx_read_csr(CVMX_SRXX_COM_CTL(interface)); | ||
586 | srxx_com_ctl.s.inf_en = 1; | ||
587 | srxx_com_ctl.s.st_en = 1; | ||
588 | cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64); | ||
589 | } | ||
590 | |||
591 | if (mode & CVMX_SPI_MODE_TX_HALFPLEX) { | ||
592 | /* STX0 has achieved sync */ | ||
593 | /* The corespondant board should be sending calendar data */ | ||
594 | /* Enable the STX0 STAT receiver. */ | ||
595 | union cvmx_spxx_clk_stat stat; | ||
596 | uint64_t timeout_time; | ||
597 | union cvmx_stxx_com_ctl stxx_com_ctl; | ||
598 | stxx_com_ctl.u64 = 0; | ||
599 | stxx_com_ctl.s.st_en = 1; | ||
600 | cvmx_write_csr(CVMX_STXX_COM_CTL(interface), stxx_com_ctl.u64); | ||
601 | |||
602 | /* Waiting for calendar sync on STX0 STAT */ | ||
603 | cvmx_dprintf("SPI%d: Waiting to sync on STX[%d] STAT\n", | ||
604 | interface, interface); | ||
605 | timeout_time = cvmx_get_cycle() + 1000ull * MS * timeout; | ||
606 | /* SPX0_CLK_STAT - SPX0_CLK_STAT[STXCAL] should be 1 (bit10) */ | ||
607 | do { | ||
608 | stat.u64 = cvmx_read_csr(CVMX_SPXX_CLK_STAT(interface)); | ||
609 | if (cvmx_get_cycle() > timeout_time) { | ||
610 | cvmx_dprintf("SPI%d: Timeout\n", interface); | ||
611 | return -1; | ||
612 | } | ||
613 | } while (stat.s.stxcal == 0); | ||
614 | } | ||
615 | |||
616 | return 0; | ||
617 | } | ||
618 | |||
619 | /** | ||
620 | * Callback to handle interface up | ||
621 | * | ||
622 | * @interface: The identifier of the packet interface to configure and | ||
623 | * use as a SPI interface. | ||
624 | * @mode: The operating mode for the SPI interface. The interface | ||
625 | * can operate as a full duplex (both Tx and Rx data paths | ||
626 | * active) or as a halfplex (either the Tx data path is | ||
627 | * active or the Rx data path is active, but not both). | ||
628 | * | ||
629 | * Returns Zero on success, non-zero error code on failure (will cause | ||
630 | * SPI initialization to abort) | ||
631 | */ | ||
632 | int cvmx_spi_interface_up_cb(int interface, cvmx_spi_mode_t mode) | ||
633 | { | ||
634 | union cvmx_gmxx_rxx_frm_min gmxx_rxx_frm_min; | ||
635 | union cvmx_gmxx_rxx_frm_max gmxx_rxx_frm_max; | ||
636 | union cvmx_gmxx_rxx_jabber gmxx_rxx_jabber; | ||
637 | |||
638 | if (mode & CVMX_SPI_MODE_RX_HALFPLEX) { | ||
639 | union cvmx_srxx_com_ctl srxx_com_ctl; | ||
640 | srxx_com_ctl.u64 = cvmx_read_csr(CVMX_SRXX_COM_CTL(interface)); | ||
641 | srxx_com_ctl.s.inf_en = 1; | ||
642 | cvmx_write_csr(CVMX_SRXX_COM_CTL(interface), srxx_com_ctl.u64); | ||
643 | cvmx_dprintf("SPI%d: Rx is now up\n", interface); | ||
644 | } | ||
645 | |||
646 | if (mode & CVMX_SPI_MODE_TX_HALFPLEX) { | ||
647 | union cvmx_stxx_com_ctl stxx_com_ctl; | ||
648 | stxx_com_ctl.u64 = cvmx_read_csr(CVMX_STXX_COM_CTL(interface)); | ||
649 | stxx_com_ctl.s.inf_en = 1; | ||
650 | cvmx_write_csr(CVMX_STXX_COM_CTL(interface), stxx_com_ctl.u64); | ||
651 | cvmx_dprintf("SPI%d: Tx is now up\n", interface); | ||
652 | } | ||
653 | |||
654 | gmxx_rxx_frm_min.u64 = 0; | ||
655 | gmxx_rxx_frm_min.s.len = 64; | ||
656 | cvmx_write_csr(CVMX_GMXX_RXX_FRM_MIN(0, interface), | ||
657 | gmxx_rxx_frm_min.u64); | ||
658 | gmxx_rxx_frm_max.u64 = 0; | ||
659 | gmxx_rxx_frm_max.s.len = 64 * 1024 - 4; | ||
660 | cvmx_write_csr(CVMX_GMXX_RXX_FRM_MAX(0, interface), | ||
661 | gmxx_rxx_frm_max.u64); | ||
662 | gmxx_rxx_jabber.u64 = 0; | ||
663 | gmxx_rxx_jabber.s.cnt = 64 * 1024 - 4; | ||
664 | cvmx_write_csr(CVMX_GMXX_RXX_JABBER(0, interface), gmxx_rxx_jabber.u64); | ||
665 | |||
666 | return 0; | ||
667 | } | ||
diff --git a/arch/mips/cavium-octeon/executive/octeon-model.c b/arch/mips/cavium-octeon/executive/octeon-model.c index c8d35684504e..f4c1b36fdf65 100644 --- a/arch/mips/cavium-octeon/executive/octeon-model.c +++ b/arch/mips/cavium-octeon/executive/octeon-model.c | |||
@@ -4,7 +4,7 @@ | |||
4 | * Contact: support@caviumnetworks.com | 4 | * Contact: support@caviumnetworks.com |
5 | * This file is part of the OCTEON SDK | 5 | * This file is part of the OCTEON SDK |
6 | * | 6 | * |
7 | * Copyright (c) 2003-2008 Cavium Networks | 7 | * Copyright (c) 2003-2010 Cavium Networks |
8 | * | 8 | * |
9 | * This file is free software; you can redistribute it and/or modify | 9 | * This file is free software; you can redistribute it and/or modify |
10 | * it under the terms of the GNU General Public License, Version 2, as | 10 | * it under the terms of the GNU General Public License, Version 2, as |
@@ -25,10 +25,6 @@ | |||
25 | * Contact Cavium Networks for more information | 25 | * Contact Cavium Networks for more information |
26 | ***********************license end**************************************/ | 26 | ***********************license end**************************************/ |
27 | 27 | ||
28 | /* | ||
29 | * File defining functions for working with different Octeon | ||
30 | * models. | ||
31 | */ | ||
32 | #include <asm/octeon/octeon.h> | 28 | #include <asm/octeon/octeon.h> |
33 | 29 | ||
34 | /** | 30 | /** |
@@ -69,11 +65,12 @@ const char *octeon_model_get_string_buffer(uint32_t chip_id, char *buffer) | |||
69 | char fuse_model[10]; | 65 | char fuse_model[10]; |
70 | uint32_t fuse_data = 0; | 66 | uint32_t fuse_data = 0; |
71 | 67 | ||
72 | fus3.u64 = cvmx_read_csr(CVMX_L2D_FUS3); | 68 | fus3.u64 = 0; |
69 | if (!OCTEON_IS_MODEL(OCTEON_CN6XXX)) | ||
70 | fus3.u64 = cvmx_read_csr(CVMX_L2D_FUS3); | ||
73 | fus_dat2.u64 = cvmx_read_csr(CVMX_MIO_FUS_DAT2); | 71 | fus_dat2.u64 = cvmx_read_csr(CVMX_MIO_FUS_DAT2); |
74 | fus_dat3.u64 = cvmx_read_csr(CVMX_MIO_FUS_DAT3); | 72 | fus_dat3.u64 = cvmx_read_csr(CVMX_MIO_FUS_DAT3); |
75 | 73 | num_cores = cvmx_pop(cvmx_read_csr(CVMX_CIU_FUSE)); | |
76 | num_cores = cvmx_octeon_num_cores(); | ||
77 | 74 | ||
78 | /* Make sure the non existent devices look disabled */ | 75 | /* Make sure the non existent devices look disabled */ |
79 | switch ((chip_id >> 8) & 0xff) { | 76 | switch ((chip_id >> 8) & 0xff) { |
@@ -108,7 +105,7 @@ const char *octeon_model_get_string_buffer(uint32_t chip_id, char *buffer) | |||
108 | * Assume pass number is encoded using <5:3><2:0>. Exceptions | 105 | * Assume pass number is encoded using <5:3><2:0>. Exceptions |
109 | * will be fixed later. | 106 | * will be fixed later. |
110 | */ | 107 | */ |
111 | sprintf(pass, "%u.%u", ((chip_id >> 3) & 7) + 1, chip_id & 7); | 108 | sprintf(pass, "%d.%d", (int)((chip_id >> 3) & 7) + 1, (int)chip_id & 7); |
112 | 109 | ||
113 | /* | 110 | /* |
114 | * Use the number of cores to determine the last 2 digits of | 111 | * Use the number of cores to determine the last 2 digits of |
@@ -116,6 +113,12 @@ const char *octeon_model_get_string_buffer(uint32_t chip_id, char *buffer) | |||
116 | * later. | 113 | * later. |
117 | */ | 114 | */ |
118 | switch (num_cores) { | 115 | switch (num_cores) { |
116 | case 32: | ||
117 | core_model = "80"; | ||
118 | break; | ||
119 | case 24: | ||
120 | core_model = "70"; | ||
121 | break; | ||
119 | case 16: | 122 | case 16: |
120 | core_model = "60"; | 123 | core_model = "60"; |
121 | break; | 124 | break; |
@@ -246,8 +249,8 @@ const char *octeon_model_get_string_buffer(uint32_t chip_id, char *buffer) | |||
246 | break; | 249 | break; |
247 | case 3: /* CN58XX */ | 250 | case 3: /* CN58XX */ |
248 | family = "58"; | 251 | family = "58"; |
249 | /* Special case. 4 core, no crypto */ | 252 | /* Special case. 4 core, half cache (CP with half cache) */ |
250 | if ((num_cores == 4) && fus_dat2.cn38xx.nocrypto) | 253 | if ((num_cores == 4) && fus3.cn58xx.crip_1024k && !strncmp(suffix, "CP", 2)) |
251 | core_model = "29"; | 254 | core_model = "29"; |
252 | 255 | ||
253 | /* Pass 1 uses different encodings for pass numbers */ | 256 | /* Pass 1 uses different encodings for pass numbers */ |
@@ -285,6 +288,9 @@ const char *octeon_model_get_string_buffer(uint32_t chip_id, char *buffer) | |||
285 | suffix = "NSP"; | 288 | suffix = "NSP"; |
286 | if (fus_dat3.s.nozip) | 289 | if (fus_dat3.s.nozip) |
287 | suffix = "SCP"; | 290 | suffix = "SCP"; |
291 | |||
292 | if (fus_dat3.s.bar2_en) | ||
293 | suffix = "NSPB2"; | ||
288 | } | 294 | } |
289 | if (fus3.cn56xx.crip_1024k) | 295 | if (fus3.cn56xx.crip_1024k) |
290 | family = "54"; | 296 | family = "54"; |
@@ -301,6 +307,60 @@ const char *octeon_model_get_string_buffer(uint32_t chip_id, char *buffer) | |||
301 | else | 307 | else |
302 | family = "52"; | 308 | family = "52"; |
303 | break; | 309 | break; |
310 | case 0x93: /* CN61XX */ | ||
311 | family = "61"; | ||
312 | if (fus_dat2.cn61xx.nocrypto && fus_dat2.cn61xx.dorm_crypto) | ||
313 | suffix = "AP"; | ||
314 | if (fus_dat2.cn61xx.nocrypto) | ||
315 | suffix = "CP"; | ||
316 | else if (fus_dat2.cn61xx.dorm_crypto) | ||
317 | suffix = "DAP"; | ||
318 | else if (fus_dat3.cn61xx.nozip) | ||
319 | suffix = "SCP"; | ||
320 | break; | ||
321 | case 0x90: /* CN63XX */ | ||
322 | family = "63"; | ||
323 | if (fus_dat3.s.l2c_crip == 2) | ||
324 | family = "62"; | ||
325 | if (num_cores == 6) /* Other core counts match generic */ | ||
326 | core_model = "35"; | ||
327 | if (fus_dat2.cn63xx.nocrypto) | ||
328 | suffix = "CP"; | ||
329 | else if (fus_dat2.cn63xx.dorm_crypto) | ||
330 | suffix = "DAP"; | ||
331 | else if (fus_dat3.cn63xx.nozip) | ||
332 | suffix = "SCP"; | ||
333 | else | ||
334 | suffix = "AAP"; | ||
335 | break; | ||
336 | case 0x92: /* CN66XX */ | ||
337 | family = "66"; | ||
338 | if (num_cores == 6) /* Other core counts match generic */ | ||
339 | core_model = "35"; | ||
340 | if (fus_dat2.cn66xx.nocrypto && fus_dat2.cn66xx.dorm_crypto) | ||
341 | suffix = "AP"; | ||
342 | if (fus_dat2.cn66xx.nocrypto) | ||
343 | suffix = "CP"; | ||
344 | else if (fus_dat2.cn66xx.dorm_crypto) | ||
345 | suffix = "DAP"; | ||
346 | else if (fus_dat3.cn66xx.nozip) | ||
347 | suffix = "SCP"; | ||
348 | else | ||
349 | suffix = "AAP"; | ||
350 | break; | ||
351 | case 0x91: /* CN68XX */ | ||
352 | family = "68"; | ||
353 | if (fus_dat2.cn68xx.nocrypto && fus_dat3.cn68xx.nozip) | ||
354 | suffix = "CP"; | ||
355 | else if (fus_dat2.cn68xx.dorm_crypto) | ||
356 | suffix = "DAP"; | ||
357 | else if (fus_dat3.cn68xx.nozip) | ||
358 | suffix = "SCP"; | ||
359 | else if (fus_dat2.cn68xx.nocrypto) | ||
360 | suffix = "SP"; | ||
361 | else | ||
362 | suffix = "AAP"; | ||
363 | break; | ||
304 | default: | 364 | default: |
305 | family = "XX"; | 365 | family = "XX"; |
306 | core_model = "XX"; | 366 | core_model = "XX"; |
@@ -310,49 +370,40 @@ const char *octeon_model_get_string_buffer(uint32_t chip_id, char *buffer) | |||
310 | } | 370 | } |
311 | 371 | ||
312 | clock_mhz = octeon_get_clock_rate() / 1000000; | 372 | clock_mhz = octeon_get_clock_rate() / 1000000; |
313 | |||
314 | if (family[0] != '3') { | 373 | if (family[0] != '3') { |
374 | int fuse_base = 384 / 8; | ||
375 | if (family[0] == '6') | ||
376 | fuse_base = 832 / 8; | ||
377 | |||
315 | /* Check for model in fuses, overrides normal decode */ | 378 | /* Check for model in fuses, overrides normal decode */ |
316 | /* This is _not_ valid for Octeon CN3XXX models */ | 379 | /* This is _not_ valid for Octeon CN3XXX models */ |
317 | fuse_data |= cvmx_fuse_read_byte(51); | 380 | fuse_data |= cvmx_fuse_read_byte(fuse_base + 3); |
318 | fuse_data = fuse_data << 8; | 381 | fuse_data = fuse_data << 8; |
319 | fuse_data |= cvmx_fuse_read_byte(50); | 382 | fuse_data |= cvmx_fuse_read_byte(fuse_base + 2); |
320 | fuse_data = fuse_data << 8; | 383 | fuse_data = fuse_data << 8; |
321 | fuse_data |= cvmx_fuse_read_byte(49); | 384 | fuse_data |= cvmx_fuse_read_byte(fuse_base + 1); |
322 | fuse_data = fuse_data << 8; | 385 | fuse_data = fuse_data << 8; |
323 | fuse_data |= cvmx_fuse_read_byte(48); | 386 | fuse_data |= cvmx_fuse_read_byte(fuse_base); |
324 | if (fuse_data & 0x7ffff) { | 387 | if (fuse_data & 0x7ffff) { |
325 | int model = fuse_data & 0x3fff; | 388 | int model = fuse_data & 0x3fff; |
326 | int suffix = (fuse_data >> 14) & 0x1f; | 389 | int suffix = (fuse_data >> 14) & 0x1f; |
327 | if (suffix && model) { | 390 | if (suffix && model) { |
328 | /* | 391 | /* Have both number and suffix in fuses, so both */ |
329 | * Have both number and suffix in | 392 | sprintf(fuse_model, "%d%c", model, 'A' + suffix - 1); |
330 | * fuses, so both | ||
331 | */ | ||
332 | sprintf(fuse_model, "%d%c", | ||
333 | model, 'A' + suffix - 1); | ||
334 | core_model = ""; | 393 | core_model = ""; |
335 | family = fuse_model; | 394 | family = fuse_model; |
336 | } else if (suffix && !model) { | 395 | } else if (suffix && !model) { |
337 | /* | 396 | /* Only have suffix, so add suffix to 'normal' model number */ |
338 | * Only have suffix, so add suffix to | 397 | sprintf(fuse_model, "%s%c", core_model, 'A' + suffix - 1); |
339 | * 'normal' model number. | ||
340 | */ | ||
341 | sprintf(fuse_model, "%s%c", core_model, | ||
342 | 'A' + suffix - 1); | ||
343 | core_model = fuse_model; | 398 | core_model = fuse_model; |
344 | } else { | 399 | } else { |
345 | /* | 400 | /* Don't have suffix, so just use model from fuses */ |
346 | * Don't have suffix, so just use | ||
347 | * model from fuses. | ||
348 | */ | ||
349 | sprintf(fuse_model, "%d", model); | 401 | sprintf(fuse_model, "%d", model); |
350 | core_model = ""; | 402 | core_model = ""; |
351 | family = fuse_model; | 403 | family = fuse_model; |
352 | } | 404 | } |
353 | } | 405 | } |
354 | } | 406 | } |
355 | sprintf(buffer, "CN%s%sp%s-%d-%s", | 407 | sprintf(buffer, "CN%s%sp%s-%d-%s", family, core_model, pass, clock_mhz, suffix); |
356 | family, core_model, pass, clock_mhz, suffix); | ||
357 | return buffer; | 408 | return buffer; |
358 | } | 409 | } |