/* Cesar project {{{ * * Copyright (C) 2008 Spidcom * * <<>> * * }}} */ /** * \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 /** Quick access to Running bit. */ #define PHY_BRGDMA_RUNNING_BIT \ BF_GET (PHY_BRIDGEDMA_STATUS_ERROR__RUNNING, PHY_BDGDMA_STATUS_ERROR) /** Quick access to the last bit. */ #define PHY_BRGDMA_LAST_BIT \ BF_GET (PHY_BRIDGEDMA_JOB_CONF__LAST, PHY_BDGDMA_JOB_CONF) /** 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 = (phy_bridgedma_t *) data; /* ACK and unmask. */ cyg_interrupt_acknowledge(PHY_BRIDGEDMA_END_INTERRUPT); cyg_interrupt_unmask(PHY_BRIDGEDMA_END_INTERRUPT); if((*bridgedma_ctx->bridge.bridgedma_cb) (bridgedma_ctx->bridge.user_data, PHY_BRGDMA_RUNNING_BIT)) 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->bridge.deferred_cb)(bridgedma_ctx->bridge.user_data); } phy_bridgedma_t * phy_bridgedma_init (void *user_data, phy_bridgedma_cb_t bridgedma_cb, phy_deferred_cb_t deferred_cb) { /* DSR is only valid if ISR is set. */ dbg_assert ((bridgedma_cb && deferred_cb) || !bridgedma_cb); memset (&phy_bridgedma_global, 0, sizeof (phy_bridgedma_t)); phy_bridgedma_global.bridge.user_data = user_data; phy_bridgedma_global.bridge.bridgedma_cb = bridgedma_cb; phy_bridgedma_global.bridge.deferred_cb = deferred_cb; /* Reset bridge DMA. */ PHY_RB_RST_MODULE = BF_SET (PHY_RB_RST_MODULE, PHY_RB_RST_MODULE__BDGDMA_RESET, 1); PHY_RB_RST_MODULE = BF_SET (PHY_RB_RST_MODULE, PHY_RB_RST_MODULE__BDGDMA_RESET, 0); /* ISR function callbacks is defined, register the ISR request to eCos. */ if (bridgedma_cb) { /* register ISR and 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; } void phy_bridgedma_uninit (phy_bridgedma_t *ctx) { dbg_assert (ctx); while (phy_bridgedma_status (ctx)); } /** * Start the bridgedma. * \param ctx the bridgedma context. * \param job the job to use for the start. */ static inline void phy_bridge_dma_start__configure (phy_bridgedma_t *ctx, phy_bridgedma_job_t *job) { dbg_assert (ctx); dbg_assert (job); PHY_BDGDMA_FIRST_JOBD_PTR = (u32) job; /* Start the bridgedma. */ PHY_BDGDMA_CONTROL_CONFIG = BF_SET (PHY_BDGDMA_CONTROL_CONFIG, PHY_BRIDGEDMA_CONTROL__START, true); } void phy_bridgedma_start (phy_bridgedma_t *ctx, phy_bridgedma_job_t *job_first, phy_bridgedma_job_t *job_last) { dbg_assert (ctx); dbg_assert (job_first); dbg_assert (job_last); dbg_assert (job_last->next == NULL); /* Set the last bit to true in the last job. */ job_last->last = true; job_last->next = (phy_bridgedma_job_t *) PHY_BRGDMA_SPC_ADDRESS ((u32)job_last); /* Bridge DMA is stopped, add the job and start it. */ if (!PHY_BRGDMA_RUNNING_BIT) { arch_write_buffer_flush (); phy_bridge_dma_start__configure (ctx, job_first); /* Bridge DMA head is not null, chain the list to the tail. */ if (ctx->bridge.job_head) ctx->bridge.job_tail->next = job_first; else ctx->bridge.job_head = job_first; } else { ctx->bridge.job_tail->next = job_first; arch_reorder_barrier (); ctx->bridge.job_tail->last = false; arch_write_buffer_flush (); /* if last_job is loaded before last has been written. */ if (((PHY_BDGDMA_CURRENT_JOBD_PTR & ~3) == (u32) ctx->bridge.job_tail && PHY_BRGDMA_LAST_BIT) || (!PHY_BRGDMA_RUNNING_BIT && (PHY_BDGDMA_CURRENT_JOBD_PTR & ~3) == (u32) job_first)) phy_bridge_dma_start__configure (ctx, job_first); } ctx->bridge.job_tail = job_last; } /** * Get the current job descriptor from the bridgedma. * \param ctx the Bridge DMA context. * \return the address of the current job descriptor beeing processed by the * bridge DMA. * * It corresponds to the current job which is being processed by the * bridgedma when the Interruption arrived. */ static inline phy_bridgedma_job_t * phy_bridgedma_current_job (phy_bridgedma_t *ctx) { dbg_assert (ctx); return (phy_bridgedma_job_t *) PHY_BDGDMA_CURRENT_JOBD_PTR; } bool phy_bridgedma_status (phy_bridgedma_t *ctx) { return PHY_BRGDMA_RUNNING_BIT; } phy_bridgedma_job_t * phy_bridgedma_jobs_get_ended (phy_bridgedma_t *ctx) { dbg_assert (ctx); phy_bridgedma_job_t *job_head = ctx->bridge.job_head; if (job_head) { /* Bridgedma is not running. */ if (!PHY_BRGDMA_RUNNING_BIT) { ctx->bridge.job_tail->next = NULL; ctx->bridge.job_head = ctx->bridge.job_tail = NULL; } else { phy_bridgedma_job_t *job_current; /* Wait current jobs to be fully processed. */ for (job_current = phy_bridgedma_current_job (ctx); job_current == phy_bridgedma_current_job (ctx) && PHY_BRGDMA_RUNNING_BIT ; ) ; if ((u32) job_current != PHY_BRGDMA_SPC_ADDRESS ((u32) job_current)) { if ((u32) job_current->next == PHY_BRGDMA_SPC_ADDRESS((u32)job_current)) ctx->bridge.job_head = ctx->bridge.job_tail = NULL; else ctx->bridge.job_head = job_current->next; job_current->next = NULL; } else { ctx->bridge.job_tail->next = NULL; ctx->bridge.job_head = ctx->bridge.job_tail = NULL; } } /* Return the list. */ return job_head; } return NULL; }