source: trunk/kernel/drivers/soclib/soclib_nic.c @ 58

Last change on this file since 58 was 4, checked in by alain, 8 years ago

Introduce the chdev_t structure in place of device_t.

File size: 11.3 KB
Line 
1/*
2 * soclib_nic.c - SOCLIB_NIC (Network Interface Controler) driver implementation.
3 *
4 * Author     Alain Greiner (2016)
5 *
6 * Copyright (c) UPMC Sorbonne Universites
7 *
8 * This file is part of ALMOS-MKH.
9 *
10 * ALMOS-MKH.is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; version 2.0 of the License.
13 *
14 * ALMOS-MKH.is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with ALMOS-MKH.; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24#include <hal_types.h>
25#include <hal_remote.h>
26#include <hal_special.h>
27#include <chdev.h>
28#include <dev_nic.h>
29#include <spinlock.h>
30#include <kmem.h>
31#include <printk.h>
32#include <memcpy.h>
33#include <thread.h>
34#include <soclib_nic.h>
35
36///////////////////////////////////////
37void soclib_nic_init( chdev_t * chdev )
38{
39    uint32_t    i;
40    kmem_req_t  req;
41   
42    // get hardware device cluster and local pointer
43    cxy_t      nic_cxy  = GET_CXY( chdev->base );
44    uint32_t * nic_ptr  = (uint32_t *)GET_PTR( chdev->base );
45
46    // initialize Soclib NIC global registers
47    hal_remote_sw( XPTR( nic_cxy , &nic_ptr + NIC_G_BC_ENABLE ) , 0 );
48    hal_remote_sw( XPTR( nic_cxy , &nic_ptr + NIC_G_RUN       ) , 0 );
49
50    // allocate memory for chbuf descriptor (one page)
51    assert( (sizeof(nic_chbuf_t) <= CONFIG_PPM_PAGE_SIZE ) , __FUNCTION__ ,
52            "chbuf descriptor exceeds one page" );
53
54    req.type   = KMEM_PAGE;
55    req.size   = 0;
56    req.flags  = AF_KERNEL;
57
58    nic_chbuf_t * chbuf = (nic_chbuf_t *)kmem_alloc( &req );
59
60    assert( (chbuf != NULL) , __FUNCTION__ , 
61             "cannot allocate chbuf descriptor" );
62
63    // initialise chbuf state
64    chbuf->cont_id  = 0;
65    chbuf->pkt_id   = 0;
66    chbuf->word_id  = 34;
67   
68    // allocate containers (one page per container)
69    // and complete chbuf descriptor initialization
70    assert( (CONFIG_PPM_PAGE_SIZE == 4096) , __FUNCTION__ ,
71            "chbuf container must be 4 Kbytes" );
72
73    for( i = 0 ; i < CONFIG_NIC_CHBUF_DEPTH ; i++ )
74    {
75        uint32_t * container = (uint32_t *)kmem_alloc( &req );   
76
77        assert( (container != NULL) , __FUNCTION__ , 
78                "cannot allocate container" );
79       
80        chbuf->cont[i] = container;
81        chbuf->full[i] = (paddr_t)XPTR( local_cxy , container );
82    }
83} // end soclib_nic_init()
84
85
86//////////////////////////////////////////////////////////////////
87void __attribute__ ((noinline)) soclib_nic_cmd( xptr_t thread_xp )
88{
89    uint32_t       cmd;          // command type   
90    char         * buffer;       // pointer on command buffer   
91    uint32_t       length;       // Ethernet packet length
92    xptr_t         dev_xp;       // extended pointer on NIC device
93    nic_chbuf_t  * chbuf;        // pointer on chbuf descriptor
94    uint32_t       cont_id;      // index of current container in chbuf
95    uint32_t       pkt_id;       // index of current packet in container
96    uint32_t       word_id;      // index of first word of current packet in container
97    uint32_t     * container;    // pointer on container (array of uint32_t)
98    uint16_t     * header;       // pointer on container header (array of uint16_t)
99    uint32_t       npackets;     // number of packets in current container
100
101    // get local pointer for client thread
102    thread_t * thread_ptr = (thread_t *)GET_PTR( thread_xp );
103
104    // get command arguments
105    cmd    = thread_ptr->command.nic.cmd;
106    buffer = thread_ptr->command.nic.buffer;
107    length = thread_ptr->command.nic.length;
108    dev_xp = thread_ptr->command.nic.dev_xp;
109
110    // get local pointer for device
111    chdev_t * dev_ptr = (chdev_t *)GET_PTR( dev_xp );
112
113    // get chbuf descriptor pointer
114    chbuf = (nic_chbuf_t *)dev_ptr->ext.nic.queue;
115
116    // analyse command type
117    switch( cmd )
118    {
119        /////////////////////////////////////////////////////////////////////////////
120        case NIC_CMD_READ:   // transfer one packet from RX queue to command buffer
121        {
122            // get current container index
123            cont_id = chbuf->cont_id;
124           
125            if( chbuf->full[cont_id] == 0 )  // container empty
126            {
127                printk("PANIC in %s : read an empty container\n", __FUNCTION__ );
128                hal_core_sleep();
129            }
130
131            // get pointer on container and header
132            container = chbuf->cont[cont_id];
133            header    = (uint16_t *)container;
134
135            // get  expected packet index and first word index in container
136            pkt_id  = chbuf->pkt_id;
137            word_id = chbuf->word_id;
138
139            // get packet length and number of packets from container header
140            length    = header[pkt_id + 2];
141            npackets  = header[0];
142
143            if( pkt_id >= npackets )  // packet index too large
144            {
145                printk("PANIC in %s : read a non readable container\n", __FUNCTION__ );
146                hal_core_sleep();
147            }
148
149            // move the packet from container to buffer
150            memcpy( buffer , container + word_id , length );
151
152            // update current packet index and first word index
153            chbuf->pkt_id  = pkt_id + 1;
154            if( length & 0x3 ) chbuf->word_id = word_id + (length>>2) + 1;
155            else               chbuf->word_id = word_id + (length>>2);
156        }
157        break;    // end READ
158           
159        //////////////////////////////////////////////////////////////////////////
160        case NIC_CMD_WRITE:  // move one packet from command buffer to TX queue
161        {
162            // get current TX container indexes
163            cont_id = chbuf->cont_id;
164            pkt_id  = chbuf->pkt_id;
165            word_id = chbuf->word_id;
166
167            if( chbuf->full[cont_id] != 0 )  // container full
168            {
169                printk("PANIC in %s : write to a full container\n", __FUNCTION__ );
170                hal_core_sleep();
171            }
172
173            // get pointer on container and header
174            container = chbuf->cont[cont_id];
175            header    = (uint16_t *)container;
176
177            if( length > ((1024 - word_id)<<2) )  // packet length too large
178            {
179                printk("PANIC in %s : write to a non writable container\n", __FUNCTION__ );
180                hal_core_sleep();
181            }
182
183            // update packet length in container header
184            header[pkt_id + 2] = (uint16_t)length;
185
186            // move the packet from buffer to container
187            memcpy( container + word_id , buffer , length );
188
189            // update current packet index and first word index
190            chbuf->pkt_id  = pkt_id + 1;
191            if( length & 0x3 ) chbuf->word_id = word_id + (length>>2) + 1;
192            else               chbuf->word_id = word_id + (length>>2);
193        }
194        break;  // end WRITE
195
196        ////////////////////////////////////////////////////////////////////////////
197        case NIC_CMD_WRITABLE:  // analyse chbuf status / update status if required
198        {
199            // get current container state
200            cont_id = chbuf->cont_id;
201            word_id = chbuf->word_id;
202
203            // compute current container writable
204            bool_t ok = ( chbuf->full[cont_id] == 0 ) &&
205                        ( length <= ((1024 - word_id)<<2) );
206
207            if( ok )                // current container writable
208            {
209                // return chbuf writable
210                thread_ptr->command.nic.status = true;
211            }
212            else                    // current container not writable
213            {
214                // release current container
215                chbuf->full[cont_id] = 1;
216
217                // check next container
218                cont_id = (cont_id + 1) % CONFIG_NIC_CHBUF_DEPTH;
219
220                if( chbuf->full[cont_id] == 0 ) // next container empty
221                {
222                    // update chbuf status
223                    chbuf->word_id = 34;
224                    chbuf->cont_id = cont_id;
225                    chbuf->pkt_id  = 0;
226                     
227                    // return chbuf writable
228                    thread_ptr->command.nic.status = true;
229                }
230                else                            // next container full     
231                {
232                    // return chbuf non writable
233                    thread_ptr->command.nic.status = false;
234                }
235            }
236        }
237        break;  // end WRITABLE
238
239        /////////////////////////////////////////////////////////////////////////////
240        case NIC_CMD_READABLE:  // analyse chbuf status / update status if required
241        {
242            // get current container state
243            cont_id  = chbuf->cont_id;
244            pkt_id   = chbuf->pkt_id;
245            npackets = chbuf->cont[cont_id][0] & 0x0000FFFF; 
246           
247            // compute current container readable
248            bool_t ok = ( chbuf->full[cont_id] == 1 ) &&
249                        ( pkt_id < npackets );
250
251            if( ok )                    // current container readable
252            {
253                // return chbuf readable     
254                thread_ptr->command.nic.status = true;
255            }
256            else                        // current container non readable
257            {
258                // release current container
259                chbuf->full[cont_id] = 0;
260
261                // check next container
262                cont_id = (cont_id + 1) % CONFIG_NIC_CHBUF_DEPTH;
263
264                if( chbuf->full[cont_id] == 1 ) // next container full
265                {
266                    // update chbuf status
267                    chbuf->word_id = 34;
268                    chbuf->cont_id = cont_id;
269                    chbuf->pkt_id  = 0;
270                     
271                    // return chbuf readable
272                    thread_ptr->command.nic.status = true;
273                }
274                else                            // next container empty   
275                {
276                    // return chbuf non readable
277                    thread_ptr->command.nic.status = false;
278                }
279            }
280   
281        }
282        break;  // end READABLE
283    } 
284} // end soclib_nic_cmd()
285
286
287/////////////////////////////////////////////////////////////////
288void __attribute__ ((noinline)) soclib_nic_isr( chdev_t * chdev )
289{
290    // get base, size, channel, is_rx from NIC channel device NIC
291    xptr_t     base    = chdev->base;
292    uint32_t   channel = chdev->channel;
293    bool_t     is_rx   = chdev->is_rx;
294
295    // get NIC peripheral cluster and local pointer
296    cxy_t      cxy_nic = GET_CXY( base );
297    uint32_t * ptr_nic = (uint32_t *)GET_PTR( base );
298
299    // compute local pointer on status register
300    uint32_t * offset;
301    if( is_rx ) offset = ptr_nic + (NIC_CHANNEL_SPAN * (channel + 1)) + NIC_RX_STATUS;
302    else        offset = ptr_nic + (NIC_CHANNEL_SPAN * (channel + 1)) + NIC_TX_STATUS;
303
304    // read NIC channel status and acknowledge IRQ
305    uint32_t status = hal_remote_lw( XPTR( cxy_nic , offset ) );
306
307    if( status != 0 ) hal_core_sleep("%s : illegal address\n", __FUNCTION__ );
308   
309    // unblock server thread
310    thread_t * server = chdev->server;
311    thread_unblock( XPTR( local_cxy , server ) , THREAD_BLOCKED_IO );
312
313} // end soclib_nic_isr()
314
315
316
Note: See TracBrowser for help on using the repository browser.