summaryrefslogtreecommitdiff
path: root/cesar/hal/phy/src/bridgedma.c
blob: 7bdda4677e5e83909c0f9299cbcc5a8f0222e1d7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/* Cesar project {{{
 *
 * Copyright (C) 2008 Spidcom
 *
 * <<<Licence>>>
 *
 * }}} */
/**
 * \file    hal/phy/src/bridgedma.c
 * \brief   Phy bridgedma HAL functions.
 * \ingroup hal_phy
 *
 */
#include "common/std.h"

#include "hal/arch/arch.h"
#include "hal/leon/itc2.h"

#include "hal/phy/bridgedma.h"
#include "hal/phy/inc/bridgedma.h"
#include "hal/phy/inc/bridgedma_regs.h"

#include "string.h"

/** Bridge DMA context. */
static phy_bridgedma_t phy_bridgedma_global;

/** eCos ISR called by eCos each time the bridgedma ends a job with the it
 * flag setted.
 * \param  vector  the IT vector.
 * \param  data  the user data.
 */
static cyg_uint32
_bridgedma_ecos_isr(cyg_vector_t vector, cyg_addrword_t data)
{
    /* nothing to do except calling the bridgedma callback */
    phy_bridgedma_t *bridgedma_ctx;

    cyg_interrupt_mask(PHY_BRIDGEDMA_END_INTERRUPT);

    bridgedma_ctx = (phy_bridgedma_t *)data;

    if((*bridgedma_ctx->bridgedma_cb)(bridgedma_ctx->user_data,
                                      *((u32 *)((void *)&bridgedma_ctx->status))))
        return CYG_ISR_CALL_DSR;  // Cause DSR to be run
    else
    {
        cyg_interrupt_unmask(PHY_BRIDGEDMA_END_INTERRUPT);
        return CYG_ISR_HANDLED;
    }
}

/** eCos DSR called by eCos each time the bridgedma ends a job with the it
 * flag setted.
 * \param  vector  the IT vector.
 * \param  data  the user data.
 */
static void
_bridgedma_ecos_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
{

    /* nothing to do except calling the phy dsr */
    phy_bridgedma_t *bridgedma_ctx;
    bridgedma_ctx = (phy_bridgedma_t *)data;
    (*bridgedma_ctx->deferred_cb)(bridgedma_ctx->user_data);

    /* ACK and unmask. */
    cyg_interrupt_acknowledge(PHY_BRIDGEDMA_END_INTERRUPT);
    cyg_interrupt_unmask(PHY_BRIDGEDMA_END_INTERRUPT);

    return;
}

/**
 * Initialise the Bridge DMA.
 * \param  user_data  User data passed to any callback
 * \param  bridgedma_cb  Bridge DMA interrupt callback
 * \param  deferred_cb  DSR callback
 * \return  the newly created context
 */
phy_bridgedma_t *
phy_bridgedma_init (void *user_data, phy_bridgedma_cb_t bridgedma_cb,
                    phy_deferred_cb_t deferred_cb)
{
    dbg_assert (bridgedma_cb);
    dbg_assert (deferred_cb);

    memset (&phy_bridgedma_global, 0, sizeof (phy_bridgedma_t));

    phy_bridgedma_global.user_data = user_data;
    phy_bridgedma_global.bridgedma_cb = bridgedma_cb;
    phy_bridgedma_global.deferred_cb = deferred_cb;

    /* register ISR et DSR to eCos */
    cyg_interrupt_create(PHY_BRIDGEDMA_END_INTERRUPT,
                         0,
                         (cyg_addrword_t)&phy_bridgedma_global,
                         _bridgedma_ecos_isr,
                         _bridgedma_ecos_dsr,
                         &phy_bridgedma_global.it_mgr.interrupt_handle,
                         &phy_bridgedma_global.it_mgr.interrupt);
    cyg_interrupt_attach(phy_bridgedma_global.it_mgr.interrupt_handle);

    cyg_interrupt_acknowledge(PHY_BRIDGEDMA_END_INTERRUPT);
    cyg_interrupt_unmask(PHY_BRIDGEDMA_END_INTERRUPT);

    return &phy_bridgedma_global;
}

/**
 * Reset and uninitialise the Bridge DMA.
 * \param  ctx  Bridge DMA context
 */
void
phy_bridgedma_uninit (phy_bridgedma_t *ctx)
{
    volatile u32 *control;
    dbg_assert (ctx);

    control = (u32 *)PHY_BRIDGEDMA_CONTROL;

    // Set the current job as the last one.
    ctx->job_first->last = true;

    while (((phy_bridgedma_ctrl_t *)control)->start);
}

/**
 * Enqueue and start a list of jobs.
 * \param  ctx  Bridge DMA context
 * \param  job_first  first job to enqueue
 * \param  job_last  last job to enqueue
 *
 * The new jobs are added to the Bridge DMA queue and the Bridge DMA is
 * restarted if it was stopped.  The \c last flag must be set in the last
 * enqueued job.
 */
void
phy_bridgedma_start (phy_bridgedma_t *ctx, phy_bridgedma_job_t *job_first,
                     phy_bridgedma_job_t *job_last)
{
    volatile u32* control;
    volatile u32* job_current;
    dbg_assert (ctx);
    dbg_assert (job_first);
    dbg_assert (job_last);

    control = (u32*) PHY_BRIDGEDMA_CONTROL;
    job_current = (u32 *)PHY_BRIDGEDMA_CURR_JOB_PTR;
    arch_reorder_barrier ();

    ctx->job_last->next = job_first;
    ctx->job_last->last = false;

    if (((phy_bridgedma_job_t *) job_current) != ctx->job_last)
    {
	ctx->job_last->next = job_first;
        ctx->job_last = job_last;
        job_last->last = true;

        if (!((phy_bridgedma_ctrl_t *) control)->start)
            ((phy_bridgedma_ctrl_t *) control)->start = true;
    }
    else
    {
	if (!((phy_bridgedma_job_t *)job_current)->last)
        {
            ctx->job_last->next = job_first;
            ctx->job_last = job_last;
            job_last->last = true;
        }
	else
        {
            job_current = (u32 *) job_first;
            ctx->job_last = job_last;
            ((phy_bridgedma_ctrl_t *) control)->start = true;
        }
    }
}