1/*
2 *Copyright (c) 2007-2011, ETH Zurich.
3 * All rights reserved.
4 *
5 * This file is distributed under the terms in the attached LICENSE file.
6 * If you do not find this file, copies can be found by writing to:
7 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
8 */
9
10
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <barrelfish/barrelfish.h>
15
16#include "sfn5122f.h"
17#include "mcdi_rpc.h"
18#include "sfn5122f_debug.h"
19
20static uint32_t seqno = 0;
21
22static struct thread_mutex mcdi;
23
24void init_mcdi_mutex(void)
25{
26     thread_mutex_init(&mcdi);
27}
28
29
30/* Function to issue MCDI calls
31 *****************************************************************
32 * Each MCDI request starts with an MCDI_HEADER, which is a 32byte
33 * structure, filled in by the client.
34 *
35 *       0       7  8     16    20     22  23  24    31
36 *      | CODE | R | LEN | SEQ | Rsvd | E | R | XFLAGS |
37 *               |                      |   |
38 *               |                      |   \--- Response
39 *               |                      \------- Error
40 *               \------------------------------ Resync (always set)
41*/
42
43errval_t mcdi_rpc(unsigned cmd, const uint8_t *in, uint32_t inlen,
44                     uint8_t *out, uint32_t outlen, uint32_t *outlen_actual,
45                     bool port, sfn5122f_t *d)
46{
47    uint32_t rlen = 0;
48    uint32_t hdr = 0;
49    uint32_t reg = 0;
50    uint32_t offset = MCDI_PDU(port);
51    uint32_t offset2 = MCDI_DORBELL(port);
52    unsigned error;
53    uint32_t* pointer = (uint32_t*) in;
54
55    thread_mutex_lock(&mcdi);
56
57    seqno++;
58
59    // Code
60    hdr |= cmd;
61    // Resync
62    hdr |= 1 << 7;
63    // Len
64    hdr |= inlen << 8;
65    // SEQ
66    hdr |= seqno << 16;
67
68    // write command
69    sfn5122f_mc_treg_smem_wr(d,offset,hdr);
70    // write arguments
71    for (int i = 0; i < inlen/4; i++) {
72        sfn5122f_mc_treg_smem_wr(d, offset+1+i, (uint32_t) pointer[i]);
73        __sync_synchronize();
74    }
75
76    // from driver
77    // ring dorbell with distinct value
78    offset2 = MCDI_DORBELL(port);
79    sfn5122f_mc_treg_smem_wr(d, offset2, 0x45789abc);
80    // Poll for completion
81    while(1){
82        // TODO add backoff ?
83        reg = sfn5122f_mc_treg_smem_rd(d, offset);
84        // If the reg is 0xffffffff the memory resets
85        if (reg != 0xffffffff && ((reg >> 23) & 0x001))
86            break;
87    }
88
89    error = (reg >> 22) & 0x001;
90    rlen = (reg >> 8) & 0x0FF;
91
92    if (cmd == CMD_REBOOT && error && !rlen) {
93        DEBUG("CARD REBOOTED \n");
94    } else if(error && !(cmd == CMD_REBOOT)){
95        // TODO ERROR HANDLING
96        reg = sfn5122f_mc_treg_smem_rd(d,offset+1);
97        DEBUG("AN ERROR OCCURRED: CMD %d, ERROR %d \n",cmd , reg);
98        switch(reg){
99        case 4:
100            return NIC_ERR_INTR;
101        case 5:
102            return NIC_ERR_IO;
103        case 37:
104            return NIC_ERR_NOSYS;
105        default:
106            return NIC_ERR_UNKNOWN;
107        }
108    }
109    // read result
110    // outlen computation from driver
111    outlen = (outlen < rlen+3) ? outlen : rlen+3;
112    outlen = outlen & ~0x3;
113
114    if (outlen_actual != NULL) {
115        *outlen_actual = rlen;
116    }
117
118    memset(&out, outlen, 0);
119
120    for (int i = 0; i < outlen; i+=4 ){
121        reg = sfn5122f_mc_treg_smem_rd(d, offset+1+i/4);
122        memcpy(out+i, &reg, 4);
123    }
124
125    __sync_synchronize();
126    thread_mutex_unlock(&mcdi);
127    return SYS_ERR_OK;
128}
129
130
131
132
133
134
135