octeon-pci-console.c revision 210284
1/***********************license start***************
2 *  Copyright (c) 2003-2008 Cavium Networks (support@cavium.com). All rights
3 *  reserved.
4 *
5 *
6 *  Redistribution and use in source and binary forms, with or without
7 *  modification, are permitted provided that the following conditions are
8 *  met:
9 *
10 *      * Redistributions of source code must retain the above copyright
11 *        notice, this list of conditions and the following disclaimer.
12 *
13 *      * Redistributions in binary form must reproduce the above
14 *        copyright notice, this list of conditions and the following
15 *        disclaimer in the documentation and/or other materials provided
16 *        with the distribution.
17 *
18 *      * Neither the name of Cavium Networks nor the names of
19 *        its contributors may be used to endorse or promote products
20 *        derived from this software without specific prior written
21 *        permission.
22 *
23 *  TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
24 *  AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS
25 *  OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH
26 *  RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
27 *  REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
28 *  DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
29 *  OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
30 *  PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET
31 *  POSSESSION OR CORRESPONDENCE TO DESCRIPTION.  THE ENTIRE RISK ARISING OUT
32 *  OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
33 *
34 *
35 *  For any questions regarding licensing please contact marketing@caviumnetworks.com
36 *
37 ***********************license end**************************************/
38
39
40
41
42
43
44#define CVMX_USE_1_TO_1_TLB_MAPPINGS 0
45
46#include "cvmx-platform.h"
47
48#include "cvmx.h"
49#include "cvmx-spinlock.h"
50#define	MIN(a,b) (((a)<(b))?(a):(b))
51
52#if !defined(CVMX_BUILD_FOR_LINUX_KERNEL)
53#include "cvmx-bootmem.h"
54#endif
55
56#include "octeon-pci-console.h"
57
58#if defined(__linux__) && !defined(__KERNEL__) && !defined(OCTEON_TARGET)
59#include "octeon-pci.h"
60#endif
61
62
63/* The following code is only used in standalone CVMX applications. It does
64    not apply for kernel or Linux programming */
65#if defined(OCTEON_TARGET) && !defined(__linux__)
66
67static int cvmx_pci_console_num = 0;
68static int per_core_pci_consoles = 0;
69static uint64_t pci_console_desc_addr = 0;
70/* This function for simple executive internal use only - do not use in any application */
71int  __cvmx_pci_console_write (int fd, char *buf, int nbytes)
72{
73    int console_num;
74    if (fd >= 0x10000000)
75    {
76        console_num = fd & 0xFFFF;
77    }
78    else if (per_core_pci_consoles)
79    {
80        console_num = cvmx_get_core_num();
81    }
82    else
83        console_num = cvmx_pci_console_num;
84
85    if (!pci_console_desc_addr)
86    {
87        cvmx_bootmem_named_block_desc_t *block_desc = cvmx_bootmem_find_named_block(OCTEON_PCI_CONSOLE_BLOCK_NAME);
88        pci_console_desc_addr = block_desc->base_addr;
89    }
90
91
92    return octeon_pci_console_write(pci_console_desc_addr, console_num, buf, nbytes, 0);
93
94}
95
96#endif
97
98
99#if !defined(CONFIG_OCTEON_U_BOOT) || (defined(CONFIG_OCTEON_U_BOOT) && defined(CFG_PCI_CONSOLE))
100int octeon_pci_console_buffer_free_bytes(uint32_t buffer_size, uint32_t wr_idx, uint32_t rd_idx)
101{
102    if (rd_idx >= buffer_size || wr_idx >= buffer_size)
103        return -1;
104
105    return (((buffer_size -1) - (wr_idx - rd_idx))%buffer_size);
106}
107int octeon_pci_console_buffer_avail_bytes(uint32_t buffer_size, uint32_t wr_idx, uint32_t rd_idx)
108{
109    if (rd_idx >= buffer_size || wr_idx >= buffer_size)
110        return -1;
111
112    return (buffer_size - 1 - octeon_pci_console_buffer_free_bytes(buffer_size, wr_idx, rd_idx));
113}
114#endif
115
116
117
118/* The following code is only used under Linux userspace when you are using
119    CVMX */
120#if defined(__linux__) && !defined(__KERNEL__) && !defined(OCTEON_TARGET)
121int octeon_pci_console_host_write(uint64_t console_desc_addr, unsigned int console_num, const char * buffer, int write_reqest_size, uint32_t flags)
122{
123    if (!console_desc_addr)
124        return -1;
125
126    /* Get global pci console information and look up specific console structure. */
127    uint32_t num_consoles = octeon_read_mem32(console_desc_addr + offsetof(octeon_pci_console_desc_t, num_consoles));
128//    printf("Num consoles: %d, buf size: %d\n", num_consoles, console_buffer_size);
129    if (console_num >= num_consoles)
130    {
131        printf("ERROR: attempting to read non-existant console: %d\n", console_num);
132        return(-1);
133    }
134    uint64_t console_addr = octeon_read_mem64(console_desc_addr + offsetof(octeon_pci_console_desc_t, console_addr_array) + console_num *8);
135//    printf("Console %d is at 0x%llx\n", console_num, (long long)console_addr);
136
137    uint32_t console_buffer_size = octeon_read_mem32(console_addr + offsetof(octeon_pci_console_t, buf_size));
138    /* Check to see if any data is available */
139    uint32_t rd_idx, wr_idx;
140    uint64_t base_addr;
141
142    base_addr = octeon_read_mem64(console_addr + offsetof(octeon_pci_console_t, input_base_addr));
143    rd_idx = octeon_read_mem32(console_addr + offsetof(octeon_pci_console_t, input_read_index));
144    wr_idx = octeon_read_mem32(console_addr + offsetof(octeon_pci_console_t, input_write_index));
145
146//    printf("Input base: 0x%llx, rd: %d(0x%x), wr: %d(0x%x)\n", (long long)base_addr, rd_idx, rd_idx, wr_idx, wr_idx);
147    int bytes_to_write = octeon_pci_console_buffer_free_bytes(console_buffer_size, wr_idx, rd_idx);
148    if (bytes_to_write <= 0)
149        return bytes_to_write;
150    bytes_to_write = MIN(bytes_to_write, write_reqest_size);
151    /* Check to see if what we want to write is not contiguous, and limit ourselves to the contiguous block*/
152    if (wr_idx + bytes_to_write >= console_buffer_size)
153        bytes_to_write = console_buffer_size - wr_idx;
154
155//    printf("Attempting to write %d bytes, (buf size: %d)\n", bytes_to_write, write_reqest_size);
156
157    octeon_pci_write_mem(base_addr + wr_idx, buffer, bytes_to_write, OCTEON_PCI_ENDIAN_64BIT_SWAP);
158    octeon_write_mem32(console_addr + offsetof(octeon_pci_console_t, input_write_index), (wr_idx + bytes_to_write)%console_buffer_size);
159
160    return bytes_to_write;
161
162}
163
164int octeon_pci_console_host_read(uint64_t console_desc_addr, unsigned int console_num, char * buffer, int buf_size, uint32_t flags)
165{
166    if (!console_desc_addr)
167        return -1;
168
169    /* Get global pci console information and look up specific console structure. */
170    uint32_t num_consoles = octeon_read_mem32(console_desc_addr + offsetof(octeon_pci_console_desc_t, num_consoles));
171//    printf("Num consoles: %d, buf size: %d\n", num_consoles, console_buffer_size);
172    if (console_num >= num_consoles)
173    {
174        printf("ERROR: attempting to read non-existant console: %d\n", console_num);
175        return(-1);
176    }
177    uint64_t console_addr = octeon_read_mem64(console_desc_addr + offsetof(octeon_pci_console_desc_t, console_addr_array) + console_num *8);
178    uint32_t console_buffer_size = octeon_read_mem32(console_addr + offsetof(octeon_pci_console_t, buf_size));
179//    printf("Console %d is at 0x%llx\n", console_num, (long long)console_addr);
180
181    /* Check to see if any data is available */
182    uint32_t rd_idx, wr_idx;
183    uint64_t base_addr;
184
185    base_addr = octeon_read_mem64(console_addr + offsetof(octeon_pci_console_t, output_base_addr));
186    rd_idx = octeon_read_mem32(console_addr + offsetof(octeon_pci_console_t, output_read_index));
187    wr_idx = octeon_read_mem32(console_addr + offsetof(octeon_pci_console_t, output_write_index));
188
189//    printf("Read buffer base: 0x%llx, rd: %d(0x%x), wr: %d(0x%x)\n", (long long)base_addr, rd_idx, rd_idx, wr_idx, wr_idx);
190    int bytes_to_read = octeon_pci_console_buffer_avail_bytes(console_buffer_size, wr_idx, rd_idx);
191    if (bytes_to_read <= 0)
192        return bytes_to_read;
193
194
195    bytes_to_read = MIN(bytes_to_read, buf_size);
196    /* Check to see if what we want to read is not contiguous, and limit ourselves to the contiguous block*/
197    if (rd_idx + bytes_to_read >= console_buffer_size)
198        bytes_to_read = console_buffer_size - rd_idx;
199
200
201    octeon_pci_read_mem(buffer, base_addr + rd_idx, bytes_to_read,OCTEON_PCI_ENDIAN_64BIT_SWAP);
202    octeon_write_mem32(console_addr + offsetof(octeon_pci_console_t, output_read_index), (rd_idx + bytes_to_read)%console_buffer_size);
203
204    return bytes_to_read;
205}
206
207
208int octeon_pci_console_host_write_avail(uint64_t console_desc_addr, unsigned int console_num)
209{
210    if (!console_desc_addr)
211        return -1;
212
213    /* Get global pci console information and look up specific console structure. */
214    uint32_t num_consoles = octeon_read_mem32(console_desc_addr + offsetof(octeon_pci_console_desc_t, num_consoles));
215//    printf("Num consoles: %d, buf size: %d\n", num_consoles, console_buffer_size);
216    if (console_num >= num_consoles)
217    {
218        printf("ERROR: attempting to read non-existant console: %d\n", console_num);
219        return -1;
220    }
221    uint64_t console_addr = octeon_read_mem64(console_desc_addr + offsetof(octeon_pci_console_desc_t, console_addr_array) + console_num *8);
222//    printf("Console %d is at 0x%llx\n", console_num, (long long)console_addr);
223
224    uint32_t console_buffer_size = octeon_read_mem32(console_addr + offsetof(octeon_pci_console_t, buf_size));
225    /* Check to see if any data is available */
226    uint32_t rd_idx, wr_idx;
227    uint64_t base_addr;
228
229    base_addr = octeon_read_mem64(console_addr + offsetof(octeon_pci_console_t, input_base_addr));
230    rd_idx = octeon_read_mem32(console_addr + offsetof(octeon_pci_console_t, input_read_index));
231    wr_idx = octeon_read_mem32(console_addr + offsetof(octeon_pci_console_t, input_write_index));
232
233//    printf("Input base: 0x%llx, rd: %d(0x%x), wr: %d(0x%x)\n", (long long)base_addr, rd_idx, rd_idx, wr_idx, wr_idx);
234    return octeon_pci_console_buffer_free_bytes(console_buffer_size, wr_idx, rd_idx);
235}
236
237
238int octeon_pci_console_host_read_avail(uint64_t console_desc_addr, unsigned int console_num)
239{
240    if (!console_desc_addr)
241        return -1;
242
243    /* Get global pci console information and look up specific console structure. */
244    uint32_t num_consoles = octeon_read_mem32(console_desc_addr + offsetof(octeon_pci_console_desc_t, num_consoles));
245//    printf("Num consoles: %d, buf size: %d\n", num_consoles, console_buffer_size);
246    if (console_num >= num_consoles)
247    {
248        printf("ERROR: attempting to read non-existant console: %d\n", console_num);
249        return(-1);
250    }
251    uint64_t console_addr = octeon_read_mem64(console_desc_addr + offsetof(octeon_pci_console_desc_t, console_addr_array) + console_num *8);
252    uint32_t console_buffer_size = octeon_read_mem32(console_addr + offsetof(octeon_pci_console_t, buf_size));
253//    printf("Console %d is at 0x%llx\n", console_num, (long long)console_addr);
254
255    /* Check to see if any data is available */
256    uint32_t rd_idx, wr_idx;
257    uint64_t base_addr;
258
259    base_addr = octeon_read_mem64(console_addr + offsetof(octeon_pci_console_t, output_base_addr));
260    rd_idx = octeon_read_mem32(console_addr + offsetof(octeon_pci_console_t, output_read_index));
261    wr_idx = octeon_read_mem32(console_addr + offsetof(octeon_pci_console_t, output_write_index));
262
263//    printf("Read buffer base: 0x%llx, rd: %d(0x%x), wr: %d(0x%x)\n", (long long)base_addr, rd_idx, rd_idx, wr_idx, wr_idx);
264    return octeon_pci_console_buffer_avail_bytes(console_buffer_size, wr_idx, rd_idx);
265}
266
267
268#endif /* TARGET_HOST */
269
270
271
272
273
274
275/* This code is only available in a kernel or CVMX standalone. It can't be used
276    from userspace */
277#if (!defined(CONFIG_OCTEON_U_BOOT) && (!defined(__linux__) || defined(__KERNEL__))) || (defined(CONFIG_OCTEON_U_BOOT) && defined(CFG_PCI_CONSOLE))
278
279static octeon_pci_console_t *octeon_pci_console_get_ptr(uint64_t console_desc_addr, unsigned int console_num)
280{
281    octeon_pci_console_desc_t *cons_desc_ptr;
282
283    if (!console_desc_addr)
284        return NULL;
285
286    cons_desc_ptr = (octeon_pci_console_desc_t *)cvmx_phys_to_ptr(console_desc_addr);
287    if (console_num >= cons_desc_ptr->num_consoles)
288        return NULL;
289
290    return (octeon_pci_console_t *)cvmx_phys_to_ptr(cons_desc_ptr->console_addr_array[console_num]);
291}
292
293
294int octeon_pci_console_write(uint64_t console_desc_addr, unsigned int console_num, const char * buffer, int bytes_to_write, uint32_t flags)
295{
296    octeon_pci_console_t *cons_ptr;
297    cvmx_spinlock_t *lock;
298    int bytes_available;
299    char *buf_ptr;
300    int bytes_written;
301
302    cons_ptr = octeon_pci_console_get_ptr(console_desc_addr, console_num);
303    if (!cons_ptr)
304        return -1;
305
306    lock = (cvmx_spinlock_t *)&cons_ptr->lock;
307
308    buf_ptr = (char*)cvmx_phys_to_ptr(cons_ptr->output_base_addr);
309    bytes_written = 0;
310    cvmx_spinlock_lock(lock);
311    while (bytes_to_write > 0)
312    {
313        bytes_available = octeon_pci_console_buffer_free_bytes(cons_ptr->buf_size, cons_ptr->output_write_index, cons_ptr->output_read_index);
314//        printf("Console %d has %d bytes available for writes\n", console_num, bytes_available);
315        if (bytes_available > 0)
316        {
317            int write_size = MIN(bytes_available, bytes_to_write);
318            /* Limit ourselves to what we can output in a contiguous block */
319            if (cons_ptr->output_write_index + write_size >= cons_ptr->buf_size)
320                write_size = cons_ptr->buf_size - cons_ptr->output_write_index;
321
322            memcpy(buf_ptr + cons_ptr->output_write_index, buffer + bytes_written, write_size);
323            CVMX_SYNCW;  /* Make sure data is visible before changing write index */
324            cons_ptr->output_write_index = (cons_ptr->output_write_index + write_size)%cons_ptr->buf_size;
325            bytes_to_write -= write_size;
326            bytes_written += write_size;
327        }
328        else if (bytes_available == 0)
329        {
330            /* Check to see if we should wait for room, or return after a partial write */
331            if (flags & OCT_PCI_CON_FLAG_NONBLOCK)
332                goto done;
333
334            cvmx_wait(1000000);  /* Delay if we are spinning */
335        }
336        else
337        {
338            bytes_written = -1;
339            goto done;
340        }
341    }
342
343done:
344    cvmx_spinlock_unlock(lock);
345    return(bytes_written);
346}
347
348int octeon_pci_console_read(uint64_t console_desc_addr, unsigned int console_num, char * buffer, int buffer_size, uint32_t flags)
349{
350    int bytes_available;
351    char *buf_ptr;
352    cvmx_spinlock_t *lock;
353    int bytes_read;
354    int read_size;
355    octeon_pci_console_t *cons_ptr = octeon_pci_console_get_ptr(console_desc_addr, console_num);
356    if (!cons_ptr)
357        return -1;
358
359    buf_ptr = (char*)cvmx_phys_to_ptr(cons_ptr->input_base_addr);
360
361    bytes_available = octeon_pci_console_buffer_avail_bytes(cons_ptr->buf_size, cons_ptr->input_write_index, cons_ptr->input_read_index);
362    if (bytes_available < 0)
363        return bytes_available;
364
365    lock = (cvmx_spinlock_t *)&cons_ptr->lock;
366    cvmx_spinlock_lock(lock);
367
368    if (!(flags & OCT_PCI_CON_FLAG_NONBLOCK))
369    {
370        /* Wait for some data to be available */
371        while (0 == (bytes_available = octeon_pci_console_buffer_avail_bytes(cons_ptr->buf_size, cons_ptr->input_write_index, cons_ptr->input_read_index)))
372            cvmx_wait(1000000);
373    }
374
375    bytes_read = 0;
376//        printf("Console %d has %d bytes available for writes\n", console_num, bytes_available);
377
378    /* Don't overflow the buffer passed to us */
379    read_size = MIN(bytes_available, buffer_size);
380
381    /* Limit ourselves to what we can input in a contiguous block */
382    if (cons_ptr->input_read_index + read_size >= cons_ptr->buf_size)
383        read_size = cons_ptr->buf_size - cons_ptr->input_read_index;
384
385    memcpy(buffer, buf_ptr + cons_ptr->input_read_index, read_size);
386    cons_ptr->input_read_index = (cons_ptr->input_read_index + read_size)%cons_ptr->buf_size;
387    bytes_read += read_size;
388
389    cvmx_spinlock_unlock(lock);
390    return(bytes_read);
391}
392
393
394int octeon_pci_console_write_avail(uint64_t console_desc_addr, unsigned int console_num)
395{
396    int bytes_available;
397    octeon_pci_console_t *cons_ptr = octeon_pci_console_get_ptr(console_desc_addr, console_num);
398    if (!cons_ptr)
399        return -1;
400
401    bytes_available = octeon_pci_console_buffer_free_bytes(cons_ptr->buf_size, cons_ptr->input_write_index, cons_ptr->input_read_index);
402    if (bytes_available >= 0)
403        return(bytes_available);
404    else
405        return 0;
406}
407
408
409int octeon_pci_console_read_avail(uint64_t console_desc_addr, unsigned int console_num)
410{
411    int bytes_available;
412    octeon_pci_console_t *cons_ptr = octeon_pci_console_get_ptr(console_desc_addr, console_num);
413    if (!cons_ptr)
414        return -1;
415
416    bytes_available = octeon_pci_console_buffer_avail_bytes(cons_ptr->buf_size, cons_ptr->input_write_index, cons_ptr->input_read_index);
417    if (bytes_available >= 0)
418        return(bytes_available);
419    else
420        return 0;
421}
422
423#endif
424
425
426/* This code can only be used in the bootloader */
427#if defined(CONFIG_OCTEON_U_BOOT) && defined(CFG_PCI_CONSOLE)
428#define DDR0_TOP        0x10000000
429#define DDR2_BASE       0x20000000
430uint64_t  octeon_pci_console_init(int num_consoles, int buffer_size)
431{
432    octeon_pci_console_desc_t *cons_desc_ptr;
433    octeon_pci_console_t *cons_ptr;
434
435    /* Compute size required for pci console structure */
436    int alloc_size = num_consoles * (buffer_size * 2 + sizeof(octeon_pci_console_t) + sizeof(uint64_t)) + sizeof(octeon_pci_console_desc_t);
437
438    /* Allocate memory for the consoles.  This must be in the range addresssible by the bootloader.
439    ** Try to do so in a manner which minimizes fragmentation.  We try to put it at the top of DDR0 or bottom of
440    ** DDR2 first, and only do generic allocation if those fail */
441    int64_t console_block_addr = cvmx_bootmem_phy_named_block_alloc(alloc_size, DDR0_TOP - alloc_size - 128, DDR0_TOP, 128, OCTEON_PCI_CONSOLE_BLOCK_NAME, CVMX_BOOTMEM_FLAG_END_ALLOC);
442    if (console_block_addr < 0)
443        console_block_addr = cvmx_bootmem_phy_named_block_alloc(alloc_size, DDR2_BASE + 1, DDR2_BASE + alloc_size + 128, 128, OCTEON_PCI_CONSOLE_BLOCK_NAME, CVMX_BOOTMEM_FLAG_END_ALLOC);
444    if (console_block_addr < 0)
445        console_block_addr = cvmx_bootmem_phy_named_block_alloc(alloc_size, 0, 0x7fffffff, 128, OCTEON_PCI_CONSOLE_BLOCK_NAME, CVMX_BOOTMEM_FLAG_END_ALLOC);
446    if (console_block_addr < 0)
447        return 0;
448
449    cons_desc_ptr = (void *)(uint32_t)console_block_addr;
450
451    memset(cons_desc_ptr, 0, alloc_size);  /* Clear entire alloc'ed memory */
452
453    cons_desc_ptr->lock = 1; /* initialize as locked until we are done */
454    CVMX_SYNCW;
455    cons_desc_ptr->num_consoles = num_consoles;
456    cons_desc_ptr->flags = 0;
457    cons_desc_ptr->major_version = OCTEON_PCI_CONSOLE_MAJOR_VERSION;
458    cons_desc_ptr->minor_version = OCTEON_PCI_CONSOLE_MINOR_VERSION;
459
460    int i;
461    uint64_t avail_addr = console_block_addr + sizeof(octeon_pci_console_desc_t) + num_consoles * sizeof(uint64_t);
462    for (i = 0; i < num_consoles;i++)
463    {
464        cons_desc_ptr->console_addr_array[i] = avail_addr;
465        cons_ptr = (void *)(uint32_t)cons_desc_ptr->console_addr_array[i];
466        avail_addr += sizeof(octeon_pci_console_t);
467        cons_ptr->input_base_addr = avail_addr;
468        avail_addr += buffer_size;
469        cons_ptr->output_base_addr = avail_addr;
470        avail_addr += buffer_size;
471        cons_ptr->buf_size = buffer_size;
472    }
473    CVMX_SYNCW;
474    cons_desc_ptr->lock = 0;
475
476    return console_block_addr;
477
478
479}
480#endif
481