/* 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 "lib/slist.h" #include "mac/common/timings.h" #include /** Quick access to start bit. */ #define PHY_BRGDMA_START_BIT \ BF_GET (PHY_BRIDGEDMA_CONTROL__START, PHY_BDGDMA_CONTROL_CONFIG) /** Bridge DMA context. */ static phy_bridgedma_t phy_bridgedma_global; /** * 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) { 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); } /** * Start the BRGDMA with the pending DMA list. * \param ctx the bridgedma context. */ static void phy_bridgedma_start_jobs_pending (phy_bridgedma_t *ctx) { /* Set the last bit to true in the last job. */ ctx->jobs_pending.tail->last = true; ctx->jobs_pending.tail->next = (phy_bridgedma_job_t *) PHY_BRGDMA_SPC_ADDRESS ( (u32)ctx->jobs_pending.tail); arch_write_buffer_flush (); phy_bridge_dma_start__configure (ctx, ctx->jobs_pending.head); slist_push_back_range (ctx->bridge.jobs_process., ctx->jobs_pending.head, ctx->jobs_pending.tail, bare); slist_init (ctx->jobs_pending., bare); } /** * Return the BRG DMA status. (see maria:#913, and maria:#971) * \param ctx the bridgedma context. * \return true if pending jobs list is not empty, or start bit or running * bit is true. */ static inline bool _phy_bridgedma_status (phy_bridgedma_t *ctx) { if (ctx->jobs_pending.head) { if (!PHY_BRGDMA_START_BIT) phy_bridgedma_start_jobs_pending (ctx); return true; } else { bool started = PHY_BRGDMA_START_BIT; bool running = BF_GET (PHY_BRIDGEDMA_STATUS_ERROR__RUNNING, PHY_BDGDMA_STATUS_ERROR); return started || running; } } /** 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_bridgedma_status (bridgedma_ctx))) 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); } slist_init (phy_bridgedma_global.bridge.jobs_process., bare); slist_init (phy_bridgedma_global.jobs_pending., bare); return &phy_bridgedma_global; } void phy_bridgedma_uninit (phy_bridgedma_t *ctx) { dbg_assert (ctx); while (_phy_bridgedma_status (ctx)); } 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); slist_push_back_range (ctx->jobs_pending., job_first, job_last, bare); /* Bridge DMA is stopped, add the job and start it. */ if (!PHY_BRGDMA_START_BIT) phy_bridgedma_start_jobs_pending (ctx); } /** * 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_bridgedma_status (ctx); } phy_bridgedma_job_t * phy_bridgedma_jobs_get_ended (phy_bridgedma_t *ctx) { dbg_assert (ctx); phy_bridgedma_job_t *job_head = ctx->bridge.jobs_process.head; if (job_head) { /* Bridgedma is not running. */ if (!_phy_bridgedma_status (ctx)) { ctx->bridge.jobs_process.tail->next = NULL; slist_init (phy_bridgedma_global.bridge.jobs_process., bare); } 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_bridgedma_status (ctx) ; ) ; if ((u32) job_current != PHY_BRGDMA_SPC_ADDRESS ((u32) job_current)) { if ((u32) job_current->next == PHY_BRGDMA_SPC_ADDRESS((u32)job_current)) slist_init ( phy_bridgedma_global.bridge.jobs_process., bare); else ctx->bridge.jobs_process.head = job_current->next; job_current->next = NULL; } else { ctx->bridge.jobs_process.tail->next = NULL; slist_init (phy_bridgedma_global.bridge.jobs_process., bare); } } /* Return the list. */ return job_head; } return NULL; } void phy_bridgedma_stopped (phy_bridgedma_t *ctx) { u32 expired = phy_date () + MAC_MS_TO_TCK (PHY_BRG_POLLING_WAIT_MS); while (_phy_bridgedma_status (ctx) && less_mod2p32 (phy_date (), expired)) ; dbg_assert_print (less_mod2p32 (phy_date (), expired), "Bridge DMA freezed"); }