/* * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include #include "sdkconfig.h" #if CONFIG_PARLIO_ENABLE_DEBUG_LOG // The local log level must be defined before including esp_log.h // Set the maximum log level for this source file #define LOG_LOCAL_LEVEL ESP_LOG_DEBUG #endif #include "esp_log.h" #include "esp_check.h" #include "clk_ctrl_os.h" #include "soc/rtc.h" #include "soc/parlio_periph.h" #include "hal/parlio_ll.h" #include "esp_private/esp_clk.h" #include "parlio_private.h" static const char *TAG = "parlio"; typedef struct parlio_platform_t { _lock_t mutex; // platform level mutex lock parlio_group_t *groups[SOC_PARLIO_GROUPS]; // array of parallel IO group instances int group_ref_counts[SOC_PARLIO_GROUPS]; // reference count used to protect group install/uninstall } parlio_platform_t; static parlio_platform_t s_platform; // singleton platform parlio_group_t *parlio_acquire_group_handle(int group_id) { bool new_group = false; parlio_group_t *group = NULL; // prevent install parlio group concurrently _lock_acquire(&s_platform.mutex); if (!s_platform.groups[group_id]) { group = heap_caps_calloc(1, sizeof(parlio_group_t), PARLIO_MEM_ALLOC_CAPS); if (group) { new_group = true; s_platform.groups[group_id] = group; group->group_id = group_id; group->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; PARLIO_RCC_ATOMIC() { parlio_ll_enable_bus_clock(group_id, true); parlio_ll_reset_register(group_id); } // hal layer initialize parlio_hal_init(&group->hal); } } else { // group already install group = s_platform.groups[group_id]; } if (group) { // someone acquired the group handle means we have a new object that refer to this group s_platform.group_ref_counts[group_id]++; } _lock_release(&s_platform.mutex); if (new_group) { ESP_LOGD(TAG, "new group(%d) at %p", group_id, group); } return group; } void parlio_release_group_handle(parlio_group_t *group) { int group_id = group->group_id; bool do_deinitialize = false; _lock_acquire(&s_platform.mutex); s_platform.group_ref_counts[group_id]--; if (s_platform.group_ref_counts[group_id] == 0) { do_deinitialize = true; s_platform.groups[group_id] = NULL; // hal layer deinitialize parlio_hal_deinit(&group->hal); PARLIO_RCC_ATOMIC() { parlio_ll_enable_bus_clock(group_id, false); } free(group); } _lock_release(&s_platform.mutex); if (do_deinitialize) { ESP_LOGD(TAG, "del group(%d)", group_id); } } esp_err_t parlio_register_unit_to_group(parlio_unit_base_handle_t unit) { parlio_group_t *group = NULL; int unit_id = -1; for (int i = 0; i < SOC_PARLIO_GROUPS; i++) { group = parlio_acquire_group_handle(i); parlio_unit_base_handle_t *group_unit = NULL; ESP_RETURN_ON_FALSE(group, ESP_ERR_NO_MEM, TAG, "no memory for group (%d)", i); portENTER_CRITICAL(&group->spinlock); if (unit->dir == PARLIO_DIR_TX) { for (int j = 0; j < SOC_PARLIO_TX_UNITS_PER_GROUP; j++) { group_unit = &group->tx_units[j]; if (*group_unit == NULL) { *group_unit = unit; unit_id = j; break; } } } else { for (int j = 0; j < SOC_PARLIO_RX_UNITS_PER_GROUP; j++) { group_unit = &group->rx_units[j]; if (*group_unit == NULL) { *group_unit = unit; unit_id = j; break; } } } portEXIT_CRITICAL(&group->spinlock); if (unit_id < 0) { /* didn't find a free unit slot in the group */ parlio_release_group_handle(group); group = NULL; } else { unit->unit_id = unit_id; unit->group = group; break; } } ESP_RETURN_ON_FALSE(unit_id >= 0, ESP_ERR_NOT_FOUND, TAG, "no free %s unit", unit->dir == PARLIO_DIR_TX ? "tx" : "rx"); return ESP_OK; } void parlio_unregister_unit_from_group(parlio_unit_base_handle_t unit) { assert(unit); parlio_group_t *group = unit->group; portENTER_CRITICAL(&group->spinlock); if (unit->dir == PARLIO_DIR_TX) { group->tx_units[unit->unit_id] = NULL; } else { group->rx_units[unit->unit_id] = NULL; } portEXIT_CRITICAL(&group->spinlock); /* the parlio unit has a reference of the group, release it now */ parlio_release_group_handle(group); }