/** * @file sdrv_semag.c * @brief SemiDrive Semaphore Gate driver source file. * * User should initialize mbox module before using semag driver. * * @copyright Copyright (c) 2022 Semidrive Semiconductor. * All rights reserved. */ #include #include #include #include #include #include #include "sdrv_semag_reg.h" /** * brief Read the semag gate's register. * * param [in] offset The address offset of the semag gate's register. * return The value of the register. */ static inline uint32_t sdrv_semag_readl(uint32_t offset) { return readl(MB_REG_BASE + offset); } /** * brief Write the semag gate's register. * * param [in] offset The address offset of the semag gate's register. */ static inline void sdrv_semag_writel(uint32_t offset, uint32_t value) { writel(value, MB_REG_BASE + offset); } /** * @brief Tries to lock the SEMAG gate. * * This function tries to lock the specific SEMAG gate. If the gate has been * locked by another processor, this function returns an error code. * * @param [in] gate_idx Gate number to lock. * @return SDRV_STATUS_INVALID_PARAM The gate id is unvalid. * SDRV_STATUS_FAIL Semag gate has been locked by another processor. * SDRV_STATUS_OK Lock the semag gate successfully. */ status_t sdrv_semag_trylock(uint32_t gate_idx) { /* If gate id is unvalid. */ if (gate_idx >= SDRV_SEMAG_NUM) { return SDRV_STATUS_INVALID_PARAM; } volatile uint32_t val; uint32_t offset; /* Get the address offset of current gate id. */ offset = SEMAG_OFF(gate_idx); /* Lock the semag gate. */ val = sdrv_semag_readl(offset); val |= BM_SEMAG_SGC; sdrv_semag_writel(offset, val); /* Check the lock status of the gate. */ if ((sdrv_semag_readl(offset) & BM_SEMAG_SGC) == BM_SEMAG_SGC) { return SDRV_STATUS_OK; } else { return SDRV_STATUS_FAIL; } } /** * @brief Locks the SEMAG gate. * * This function locks the specific SEMAG gate. If the gate has been * locked by other processors, this function waits until it is unlocked and then * lock it. * * @param [in] gate_idx Gate number to lock. * @return SDRV_STATUS_INVALID_PARAM The gate id is unvalid. * SDRV_STATUS_OK Lock the semag gate successfully. */ status_t sdrv_semag_lock(uint32_t gate_idx) { if (gate_idx >= SDRV_SEMAG_NUM) { return SDRV_STATUS_INVALID_PARAM; } uint32_t val; uint32_t offset; /* Get the address offset of current gate id. */ offset = SEMAG_OFF(gate_idx); /* Wait for locking gate successfully. */ do { /* Wait for the semag gate to become unlocked. */ while ((sdrv_semag_readl(offset) & FM_SEMAG_SGLS) > 0U) ; /* Lock the semag gate. */ val = sdrv_semag_readl(offset); val |= BM_SEMAG_SGC; sdrv_semag_writel(offset, val); } while (1U != (sdrv_semag_readl(offset) & BM_SEMAG_SGC)); return SDRV_STATUS_OK; } /** * @brief Unlocks the SEMAG gate. * * This function unlocks the specific SEMAG gate. It only writes unlock value * to the SEMAG gate register. However, it does not check whether the SEMAG gate * is locked by the current processor or not. As a result, if the SEMAG gate is * not locked by the current processor, this function has no effect. * * @param [in] gate_idx Gate number to unlock. * @return SDRV_STATUS_OK Unlock the semag gate successfully. */ status_t sdrv_semag_unlock(uint32_t gate_idx) { if (gate_idx >= SDRV_SEMAG_NUM) { return SDRV_STATUS_INVALID_PARAM; } uint32_t offset; uint32_t val; /* Get the address offset of current gate id. */ offset = SEMAG_OFF(gate_idx); /* Unlock the semag gate. */ val = sdrv_semag_readl(offset); val &= ~BM_SEMAG_SGC; sdrv_semag_writel(offset, val); return SDRV_STATUS_OK; } /** * @brief Gets the status of the SEMAG gate. * * This function checks the lock status of a specific SEMAG gate. * * @param [in] gate_idx Gate number to check lock status. * @return Return -1 if the gate is unlocked, otherwise return the * processor number which has locked the gate. */ int32_t sdrv_semag_get_lock_proc(uint32_t gate_idx) { uint32_t offset; uint32_t val; if (gate_idx < SDRV_SEMAG_NUM) { /* Get the address offset of current gate id. */ offset = SEMAG_OFF(gate_idx); /* check lock status. */ val = sdrv_semag_readl(offset); val = GFV_SEMAG_SGLS(val); for (uint32_t i = 0U; i < SDRV_SEMAG_PROCESSOR_NUM; i++, val >>= 1U) { if ((val & 0x1U) == 0x1U) { return (int32_t)i; } } return -1; } else { return -1; } } /** * @brief Resets all SEMAG gates to an unlocked status. * * This function resets all SEMAG gate to an unlocked status, * it will only unlock gates which locked by itself, can't reset * gates which locked by other processors. * @return SDRV_STATUS_OK Reset all semag gates successfully. */ status_t sdrv_semag_reset_all_gate(void) { uint32_t val; /* Resets all gates to unlocked state which are locked by current processor. */ val = sdrv_semag_readl(SGR_OFF); val |= BM_SGR_PSGR; sdrv_semag_writel(SGR_OFF, val); return SDRV_STATUS_OK; } /** * @brief Enable the gate notification interrupt. * * Gate notification provides such feature, when interrupt is enabled, then core * tried to lock the gate and failed, it could get notification when the gate is * idle. * * @param [in] mask Mask of the gate index, for example: (1<<0U) | (1<<1U) * means gate 0 and gate 1. * @return SDRV_STATUS_OK Enable the notify interrupt of the semag gate * successfully. */ status_t sdrv_semag_enable_gate_notify_interrupt(uint32_t mask) { uint32_t offset; uint32_t val; for (uint32_t i = 0U; i < SDRV_SEMAG_NUM; i++) { if ((1U << i) & mask) { /* Get the address offset of current gate id. */ offset = SEMAG_OFF(i); /* Enable the gate notification interrupt. */ val = sdrv_semag_readl(offset); val |= BM_SEMAG_IESGSC; sdrv_semag_writel(offset, val); } } return SDRV_STATUS_OK; } /** * @brief Disable the gate notification interrupt. * * Gate notification provides such feature, when interrupt is enabled, then core * tried to lock the gate and failed, it could get notification when the gate is * idle. * * @param [in] mask Mask of the gate index, for example: (1<<0U) | (1<<1U) * means gate 0 and gate 1. * @return SDRV_STATUS_OK Disable the notify interrupt of the semag gate * successfully. */ status_t sdrv_semag_disable_gate_notify_interrupt(uint32_t mask) { uint32_t offset; uint32_t val; for (uint32_t i = 0U; i < SDRV_SEMAG_NUM; i++) { if ((1U << i) & mask) { /* Get the address offset of current gate id. */ offset = SEMAG_OFF(i); /* Disable the gate notification interrupt. */ val = sdrv_semag_readl(offset); val &= ~BM_SEMAG_IESGSC; sdrv_semag_writel(offset, val); } } return SDRV_STATUS_OK; } /** * @brief Get the gate notification flags. * * Gate notification provides such feature, when interrupt is enabled, then core * tried to lock the gate and failed, it could get notification when the gate is * idle. * * @return Mask of the gate index, for example: (1<<0U) | (1<<1U) means * gate 0 and gate 1 flags are pending. */ uint32_t sdrv_semag_get_notify_status(void) { return sdrv_semag_readl(SGI_OFF); } /** * @brief Clear the gate notification interrupt status. * * This function clear SEMAG gate IRQ notifications. * * @param [in] mask Mask of the gate index, for example: (1<<0U) | (1<<1U) * means gate 0 and gate 1. * @return SDRV_STATUS_OK Clear the notify interrupt status of the semag gate * successfully. */ status_t sdrv_semag_clear_notify_status(uint32_t mask) { sdrv_semag_writel(SGI_OFF, mask); return SDRV_STATUS_OK; }