<commit_before>/*
 * This file is part of the MicroPython project, http://micropython.org/
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 <NAME>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "shared-bindings/busio/SPI.h"
#include "py/mperrno.h"
#include "py/runtime.h"

#include "boards/board.h"
#include "shared-bindings/microcontroller/Pin.h"
#include "supervisor/shared/rgb_led_status.h"

#include "esp_log.h"

static const char* TAG = "CircuitPython SPI";

static bool spi_never_reset[SOC_SPI_PERIPH_NUM];

// Store one lock handle per device so that we can free it.
static spi_bus_lock_dev_handle_t lock_dev_handle[SOC_SPI_PERIPH_NUM];
static intr_handle_t intr_handle[SOC_SPI_PERIPH_NUM];

void spi_reset(void) {
    for (spi_host_device_t host_id = SPI2_HOST; host_id < SOC_SPI_PERIPH_NUM; host_id++) {
        if (spi_never_reset[host_id]) {
            continue;
        }
        bool in_use = false;
        if (lock_dev_handle[host_id] != NULL) {
            spi_bus_lock_unregister_dev(lock_dev_handle[host_id]);
            lock_dev_handle[host_id] = NULL;
            in_use = true;
        }
        if (intr_handle[host_id] != NULL) {
            esp_intr_free(intr_handle[host_id]);
            intr_handle[host_id] = NULL;
            in_use = true;
        }
        if (in_use) {
            spi_bus_free(host_id);
        }
    }
}

// This is copied in from the ESP-IDF because it is static.
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
static bool bus_uses_iomux_pins(spi_host_device_t host, const spi_bus_config_t* bus_config)
{
    if (bus_config->sclk_io_num>=0 &&
        bus_config->sclk_io_num != spi_periph_signal[host].spiclk_iomux_pin) return false;
    if (bus_config->quadwp_io_num>=0 &&
        bus_config->quadwp_io_num != spi_periph_signal[host].spiwp_iomux_pin) return false;
    if (bus_config->quadhd_io_num>=0 &&
        bus_config->quadhd_io_num != spi_periph_signal[host].spihd_iomux_pin) return false;
    if (bus_config->mosi_io_num >= 0 &&
        bus_config->mosi_io_num != spi_periph_signal[host].spid_iomux_pin) return false;
    if (bus_config->miso_io_num>=0 &&
        bus_config->miso_io_num != spi_periph_signal[host].spiq_iomux_pin) return false;

    return true;
}

// End copied code.

static bool spi_bus_is_free(spi_host_device_t host_id) {
    return spi_bus_get_attr(host_id) == NULL;
}

static void spi_interrupt_handler(void *arg) {
    busio_spi_obj_t *self = arg;
    ESP_LOGE(TAG, "SPI interrupt %p", self);
}

// The interrupt may get invoked by the bus lock.
static void spi_bus_intr_enable(void *self)
{
    esp_intr_enable(((busio_spi_obj_t *)self)->interrupt);
}

// The interrupt is always disabled by the ISR itself, not exposed
static void spi_bus_intr_disable(void *self)
{
    esp_intr_disable(((busio_spi_obj_t *)self)->interrupt);
}

void common_hal_busio_spi_construct(busio_spi_obj_t *self,
        const mcu_pin_obj_t * clock, const mcu_pin_obj_t * mosi,
        const mcu_pin_obj_t * miso) {
    spi_bus_config_t bus_config;
    bus_config.mosi_io_num = mosi != NULL ? mosi->number : -1;
    bus_config.miso_io_num = miso != NULL ? miso->number : -1;
    bus_config.sclk_io_num = clock != NULL ? clock->number : -1;
    bus_config.quadwp_io_num = -1;
    bus_config.quadhd_io_num = -1;
    bus_config.max_transfer_sz = 0; // Uses the default
    bus_config.flags = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_SCLK |
                       (mosi != NULL ? SPICOMMON_BUSFLAG_MOSI : 0) |
                       (miso != NULL ? SPICOMMON_BUSFLAG_MISO : 0);
    bus_config.intr_flags = 0;

    // RAM and Flash is often on SPI1 and is unsupported by the IDF so use it as
    // a flag value.
    spi_host_device_t host_id = SPI1_HOST;
    self->connected_through_gpio = true;
    // Try and save SPI2 for pins that are on the IOMUX
    if (bus_uses_iomux_pins(SPI2_HOST, &bus_config) && spi_bus_is_free(SPI2_HOST)) {
        host_id = SPI2_HOST;
        self->connected_through_gpio = false;
    } else if (spi_bus_is_free(SPI3_HOST)) {
        host_id = SPI3_HOST;
    } else if (spi_bus_is_free(SPI2_HOST)) {
        host_id = SPI2_HOST;
    }
    if (host_id == SPI1_HOST) {
        mp_raise_ValueError(translate("All SPI peripherals are in use"));
    }

    esp_err_t result = spi_bus_initialize(host_id, &bus_config, host_id /* dma channel */);
    if (result == ESP_ERR_NO_MEM) {
        mp_raise_msg(&mp_type_MemoryError, translate("ESP-IDF memory allocation failed"));
    } else if (result == ESP_ERR_INVALID_ARG) {
        mp_raise_ValueError(translate("Invalid pins"));
    }

    // After this point, we need to deinit to free IDF memory so fill out self's pins.
    self->clock_pin = clock;
    self->MOSI_pin = mosi;
    self->MISO_pin = miso;

    spi_bus_lock_dev_config_t config = { .flags = 0 };
    // The returned lock is stored in the bus lock but must be freed separately with
    // spi_bus_lock_unregister_dev.
    result = spi_bus_lock_register_dev(spi_bus_get_attr(host_id)->lock,
                                       &config,
                                       &self->lock);
    if (result == ESP_ERR_NO_MEM) {
        common_hal_busio_spi_deinit(self);
        mp_raise_msg(&mp_type_MemoryError, translate("ESP-IDF memory allocation failed"));
    }
    lock_dev_handle[host_id] = self->lock;

    result = esp_intr_alloc(spicommon_irqsource_for_host(host_id),
                            bus_config.intr_flags | ESP_INTR_FLAG_INTRDISABLED,
                            spi_interrupt_handler, self, &self->interrupt);
    if (result == ESP_ERR_NO_MEM) {<commit_msg>Store host_id so that never reset works<commit_after>/*
 * This file is part of the MicroPython project, http://micropython.org/
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 <NAME>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "shared-bindings/busio/SPI.h"
#include "py/mperrno.h"
#include "py/runtime.h"

#include "boards/board.h"
#include "shared-bindings/microcontroller/Pin.h"
#include "supervisor/shared/rgb_led_status.h"

#include "esp_log.h"

static const char* TAG = "CircuitPython SPI";

static bool spi_never_reset[SOC_SPI_PERIPH_NUM];

// Store one lock handle per device so that we can free it.
static spi_bus_lock_dev_handle_t lock_dev_handle[SOC_SPI_PERIPH_NUM];
static intr_handle_t intr_handle[SOC_SPI_PERIPH_NUM];

void spi_reset(void) {
    for (spi_host_device_t host_id = SPI2_HOST; host_id < SOC_SPI_PERIPH_NUM; host_id++) {
        if (spi_never_reset[host_id]) {
            continue;
        }
        bool in_use = false;
        if (lock_dev_handle[host_id] != NULL) {
            spi_bus_lock_unregister_dev(lock_dev_handle[host_id]);
            lock_dev_handle[host_id] = NULL;
            in_use = true;
        }
        if (intr_handle[host_id] != NULL) {
            esp_intr_free(intr_handle[host_id]);
            intr_handle[host_id] = NULL;
            in_use = true;
        }
        if (in_use) {
            spi_bus_free(host_id);
        }
    }
}

// This is copied in from the ESP-IDF because it is static.
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
static bool bus_uses_iomux_pins(spi_host_device_t host, const spi_bus_config_t* bus_config)
{
    if (bus_config->sclk_io_num>=0 &&
        bus_config->sclk_io_num != spi_periph_signal[host].spiclk_iomux_pin) return false;
    if (bus_config->quadwp_io_num>=0 &&
        bus_config->quadwp_io_num != spi_periph_signal[host].spiwp_iomux_pin) return false;
    if (bus_config->quadhd_io_num>=0 &&
        bus_config->quadhd_io_num != spi_periph_signal[host].spihd_iomux_pin) return false;
    if (bus_config->mosi_io_num >= 0 &&
        bus_config->mosi_io_num != spi_periph_signal[host].spid_iomux_pin) return false;
    if (bus_config->miso_io_num>=0 &&
        bus_config->miso_io_num != spi_periph_signal[host].spiq_iomux_pin) return false;

    return true;
}

// End copied code.

static bool spi_bus_is_free(spi_host_device_t host_id) {
    return spi_bus_get_attr(host_id) == NULL;
}

static void spi_interrupt_handler(void *arg) {
    busio_spi_obj_t *self = arg;
    ESP_LOGE(TAG, "SPI interrupt %p", self);
}

// The interrupt may get invoked by the bus lock.
static void spi_bus_intr_enable(void *self)
{
    esp_intr_enable(((busio_spi_obj_t *)self)->interrupt);
}

// The interrupt is always disabled by the ISR itself, not exposed
static void spi_bus_intr_disable(void *self)
{
    esp_intr_disable(((busio_spi_obj_t *)self)->interrupt);
}

void common_hal_busio_spi_construct(busio_spi_obj_t *self,
        const mcu_pin_obj_t * clock, const mcu_pin_obj_t * mosi,
        const mcu_pin_obj_t * miso) {
    spi_bus_config_t bus_config;
    bus_config.mosi_io_num = mosi != NULL ? mosi->number : -1;
    bus_config.miso_io_num = miso != NULL ? miso->number : -1;
    bus_config.sclk_io_num = clock != NULL ? clock->number : -1;
    bus_config.quadwp_io_num = -1;
    bus_config.quadhd_io_num = -1;
    bus_config.max_transfer_sz = 0; // Uses the default
    bus_config.flags = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_SCLK |
                       (mosi != NULL ? SPICOMMON_BUSFLAG_MOSI : 0) |
                       (miso != NULL ? SPICOMMON_BUSFLAG_MISO : 0);
    bus_config.intr_flags = 0;

    // RAM and Flash is often on SPI1 and is unsupported by the IDF so use it as
    // a flag value.
    spi_host_device_t host_id = SPI1_HOST;
    self->connected_through_gpio = true;
    // Try and save SPI2 for pins that are on the IOMUX
    if (bus_uses_iomux_pins(SPI2_HOST, &bus_config) && spi_bus_is_free(SPI2_HOST)) {
        host_id = SPI2_HOST;
        self->connected_through_gpio = false;
    } else if (spi_bus_is_free(SPI3_HOST)) {
        host_id = SPI3_HOST;
    } else if (spi_bus_is_free(SPI2_HOST)) {
        host_id = SPI2_HOST;
    }
    if (host_id == SPI1_HOST) {
        mp_raise_ValueError(translate("All SPI peripherals are in use"));
    }

    esp_err_t result = spi_bus_initialize(host_id, &bus_config, host_id /* dma channel */);
    if (result == ESP_ERR_NO_MEM) {
        mp_raise_msg(&mp_type_MemoryError, translate("ESP-IDF memory allocation failed"));
    } else if (result == ESP_ERR_INVALID_ARG) {
        mp_raise_ValueError(translate("Invalid pins"));
    }

    // After this point, we need to deinit to free IDF memory so fill out self's pins.
    self->clock_pin = clock;
    self->MOSI_pin = mosi;
    self->MISO_pin = miso;
    self->host_id = host_id;

    spi_bus_lock_dev_config_t config = { .flags = 0 };
    // The returned lock is stored in the bus lock but must be freed separately with
    // spi_bus_lock_unregister_dev.
    result = spi_bus_lock_register_dev(spi_bus_get_attr(host_id)->lock,
                                       &config,
                                       &self->lock);
    if (result == ESP_ERR_NO_MEM) {
        common_hal_busio_spi_deinit(self);
        mp_raise_msg(&mp_type_MemoryError, translate("ESP-IDF memory allocation failed"));
    }
    lock_dev_handle[host_id] = self->lock;

    result = esp_intr_alloc(spicommon_irqsource_for_host(host_id),
                            bus_config.intr_flags | ESP_INTR_FLAG_INTRDISABLED,
                            spi_interrupt_handler, self, &self->interrupt);
    if (result == ESP_ERR_NO_MEM) {
{'fraction_non_alphanumeric': 0.09282700421940929, 'fraction_numerical': 0.005948675382167808, 'mean_word_length': 3.2787807043503996, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 64, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '112302', 'n_tokens_mistral': 4855, 'n_tokens_neox': 4730, 'n_words': 1302}