summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ports/alif/alif.mk3
-rw-r--r--ports/alif/machine_spi.c312
-rw-r--r--ports/alif/mpconfigport.h1
3 files changed, 316 insertions, 0 deletions
diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk
index 5cfd37fc4..63003b58a 100644
--- a/ports/alif/alif.mk
+++ b/ports/alif/alif.mk
@@ -118,6 +118,7 @@ SRC_C = \
fatfs_port.c \
machine_pin.c \
machine_i2c.c \
+ machine_spi.c \
main.c \
modalif.c \
mphalport.c \
@@ -188,6 +189,7 @@ ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\
Device/core/$(MCU_CORE)/source/startup_$(MCU_CORE).c \
drivers/source/adc.c \
drivers/source/i2c.c \
+ drivers/source/spi.c \
drivers/source/mhu_driver.c \
drivers/source/mhu_receiver.c \
drivers/source/mhu_sender.c \
@@ -206,6 +208,7 @@ ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\
)
$(BUILD)/tinyusb_port/tusb_alif_dcd.o: CFLAGS += -Wno-unused-variable -DTUSB_ALIF_NO_IRQ_CFG=1
+$(BUILD)/$(ALIF_DFP_REL_TOP)/drivers/source/spi.o: CFLAGS += -Wno-maybe-uninitialized
$(BUILD)/$(ALIF_DFP_REL_TOP)/se_services/source/services_host_boot.o: CFLAGS += -Wno-stringop-truncation
$(BUILD)/$(ALIF_DFP_REL_TOP)/se_services/source/services_host_system.o: CFLAGS += -Wno-maybe-uninitialized
diff --git a/ports/alif/machine_spi.c b/ports/alif/machine_spi.c
new file mode 100644
index 000000000..d13c6368f
--- /dev/null
+++ b/ports/alif/machine_spi.c
@@ -0,0 +1,312 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024-2025 OpenMV LLC.
+ *
+ * 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 "py/runtime.h"
+#include "py/mphal.h"
+#include "py/mperrno.h"
+#include "extmod/modmachine.h"
+
+#if MICROPY_PY_MACHINE_SPI
+#include "clk.h"
+#include "spi.h"
+#include "sys_ctrl_spi.h"
+
+typedef struct _machine_spi_obj_t {
+ mp_obj_base_t base;
+ uint8_t id;
+ SPI_Type *inst;
+ bool is_lp;
+} machine_spi_obj_t;
+
+static machine_spi_obj_t machine_spi_obj[] = {
+ #if defined(MICROPY_HW_SPI0_SCK)
+ [0] = {{&machine_spi_type}, 0, (SPI_Type *)SPI0_BASE, false},
+ #endif
+ #if defined(MICROPY_HW_SPI1_SCK)
+ [1] = {{&machine_spi_type}, 1, (SPI_Type *)SPI1_BASE, false},
+ #endif
+ #if defined(MICROPY_HW_SPI2_SCK)
+ [2] = {{&machine_spi_type}, 2, (SPI_Type *)SPI2_BASE, false},
+ #endif
+ #if defined(MICROPY_HW_SPI3_SCK)
+ [3] = {{&machine_spi_type}, 3, (SPI_Type *)SPI3_BASE, false},
+ #endif
+ #if defined(MICROPY_HW_LPSPI0_SCK)
+ [4] = {{&machine_spi_type}, 4, (SPI_Type *)LPSPI0_BASE, true},
+ #endif
+
+};
+
+static inline uint32_t spi_get_clk(machine_spi_obj_t *spi) {
+ return spi->is_lp ? GetSystemCoreClock() : GetSystemAHBClock();
+}
+
+static void spi_init(machine_spi_obj_t *spi, uint32_t baudrate,
+ uint32_t polarity, uint32_t phase, uint32_t bits, uint32_t firstbit) {
+ const machine_pin_obj_t *pins[4] = { NULL, NULL, NULL, NULL };
+ switch (spi->id) {
+ #if defined(MICROPY_HW_SPI0_SCK)
+ case 0:
+ pins[0] = MICROPY_HW_SPI0_SCK;
+ pins[1] = MICROPY_HW_SPI0_MISO;
+ pins[2] = MICROPY_HW_SPI0_MOSI;
+ #if defined(MICROPY_HW_SPI0_NSS)
+ pins[3] = MICROPY_HW_SPI0_NSS;
+ #endif
+ break;
+ #endif
+ #if defined(MICROPY_HW_SPI1_SCK)
+ case 1:
+ pins[0] = MICROPY_HW_SPI1_SCK;
+ pins[1] = MICROPY_HW_SPI1_MISO;
+ pins[2] = MICROPY_HW_SPI1_MOSI;
+ #if defined(MICROPY_HW_SPI1_NSS)
+ pins[3] = MICROPY_HW_SPI1_NSS;
+ #endif
+ break;
+ #endif
+ #if defined(MICROPY_HW_SPI2_SCK)
+ case 2:
+ pins[0] = MICROPY_HW_SPI2_SCK;
+ pins[1] = MICROPY_HW_SPI2_MISO;
+ pins[2] = MICROPY_HW_SPI2_MOSI;
+ #if defined(MICROPY_HW_SPI2_NSS)
+ pins[3] = MICROPY_HW_SPI2_NSS;
+ #endif
+ break;
+ #endif
+ #if defined(MICROPY_HW_SPI3_SCK)
+ case 3:
+ pins[0] = MICROPY_HW_SPI3_SCK;
+ pins[1] = MICROPY_HW_SPI3_MISO;
+ pins[2] = MICROPY_HW_SPI3_MOSI;
+ #if defined(MICROPY_HW_SPI3_NSS)
+ pins[3] = MICROPY_HW_SPI3_NSS;
+ #endif
+ break;
+ #endif
+ #if defined(MICROPY_HW_LPSPI0_SCK)
+ case 4: // LPSPI0
+ pins[0] = MICROPY_HW_LPSPI0_SCK;
+ pins[1] = MICROPY_HW_LPSPI0_MISO;
+ pins[2] = MICROPY_HW_LPSPI0_MOSI;
+ #if defined(MICROPY_HW_LPSPI0_NSS)
+ pins[3] = MICROPY_HW_LPSPI0_NSS;
+ #endif
+ break;
+ #endif
+ default:
+ return;
+ }
+
+ // Disable SPI.
+ spi_disable(spi->inst);
+
+ // Enable clocks.
+ if (spi->is_lp) {
+ enable_lpspi_clk();
+ }
+
+ // Configure SPI pins.
+ for (size_t i = 0; i < MP_ARRAY_SIZE(pins) && pins[i]; i++) {
+ mp_hal_pin_config(pins[i], MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE,
+ MP_HAL_PIN_SPEED_HIGH, MP_HAL_PIN_DRIVE_8MA, MP_HAL_PIN_ALT_SPI, true);
+ }
+
+ // Disable all interrupts.
+ spi_mask_interrupts(spi->inst);
+
+ // Configure baudrate clock
+ spi_set_bus_speed(spi->inst, baudrate, spi_get_clk(spi));
+
+ // Configure FIFOs
+ spi_set_tx_threshold(spi->inst, 0);
+ spi_set_rx_threshold(spi->inst, 0);
+ if (!spi->is_lp) {
+ spi_set_rx_sample_delay(spi->inst, 0);
+ spi_set_tx_fifo_start_level(spi->inst, 0);
+ }
+
+ // Configure SPI bus mode.
+ uint32_t spi_mode = (polarity << 1) | phase;
+ if (!spi->is_lp) {
+ spi_set_mode(spi->inst, spi_mode);
+ } else {
+ lpspi_set_mode(spi->inst, spi_mode);
+ }
+
+ // Configure SPI bus protocol.
+ uint32_t spi_proto = SPI_PROTO_SPI;
+ if (!spi->is_lp) {
+ spi_set_protocol(spi->inst, spi_proto);
+ } else {
+ lpspi_set_protocol(spi->inst, spi_proto);
+ }
+
+ // Configure SPI transfer mode.
+ if (!spi->is_lp) {
+ spi_mode_master(spi->inst);
+ }
+
+ // Configure frame size.
+ if (!spi->is_lp) {
+ spi_set_dfs(spi->inst, bits);
+ } else {
+ lpspi_set_dfs(spi->inst, bits);
+ }
+
+ // Configure slave select pin
+ spi_control_ss(spi->inst, 0, true);
+ if (!spi->is_lp) {
+ spi_set_sste(spi->inst, false);
+ } else {
+ lpspi_set_sste(spi->inst, false);
+ }
+
+ // Clear IRQs.
+ (void)spi->inst->SPI_ICR;
+
+ // Enable SPI.
+ spi_enable(spi->inst);
+}
+
+static void machine_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
+ machine_spi_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ uint32_t baudrate = spi_get_bus_speed(self->inst, spi_get_clk(self));
+ mp_printf(print, "SPI(%u, baudrate=%u, lp=%u)", self->id, baudrate, self->is_lp);
+}
+
+mp_obj_t machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
+ enum { ARG_id, ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_sck, ARG_mosi, ARG_miso };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ },
+ { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 500000} },
+ { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
+ { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
+ { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} },
+ { MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
+ { MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
+ { MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
+ { MP_QSTR_miso, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
+ };
+
+ // Parse args.
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ // Get spi bus.
+ int spi_id = mp_obj_get_int(args[ARG_id].u_obj);
+ if (spi_id < 0 || spi_id >= MP_ARRAY_SIZE(machine_spi_obj) || !machine_spi_obj[spi_id].inst) {
+ mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("SPI(%d) doesn't exist"), spi_id);
+ }
+
+ // Get static peripheral object.
+ machine_spi_obj_t *self = &machine_spi_obj[spi_id];
+
+ // here we would check the sck/mosi/miso pins and configure them, but it's not implemented
+ if (args[ARG_sck].u_obj != MP_OBJ_NULL ||
+ args[ARG_mosi].u_obj != MP_OBJ_NULL ||
+ args[ARG_miso].u_obj != MP_OBJ_NULL) {
+ mp_raise_ValueError(MP_ERROR_TEXT("explicit choice of sck/mosi/miso is not implemented"));
+ }
+
+ // Initialize and configure SPI.
+ spi_init(self, args[ARG_baudrate].u_int, args[ARG_polarity].u_int,
+ args[ARG_phase].u_int, args[ARG_bits].u_int, args[ARG_firstbit].u_int);
+
+ return MP_OBJ_FROM_PTR(self);
+}
+
+static void machine_spi_init(mp_obj_base_t *self_in, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ enum { ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
+ { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
+ { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
+ { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
+ { MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
+ };
+
+ // Parse the arguments.
+ machine_spi_obj_t *self = (machine_spi_obj_t *)self_in;
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ // Initialize and configure SPI.
+ spi_init(self, args[ARG_baudrate].u_int, args[ARG_polarity].u_int,
+ args[ARG_phase].u_int, args[ARG_bits].u_int, args[ARG_firstbit].u_int);
+}
+
+static void machine_spi_deinit(mp_obj_base_t *self_in) {
+ machine_spi_obj_t *self = (machine_spi_obj_t *)self_in;
+ // Disable all interrupts.
+ spi_mask_interrupts(self->inst);
+ // Disable SS pin.
+ spi_control_ss(self->inst, 0, 0);
+ // Disable SPI.
+ spi_disable(self->inst);
+ // Deinitialize GPIOs and clocks.
+ if (self->is_lp) {
+ disable_lpspi_clk();
+ }
+}
+
+static void machine_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) {
+ machine_spi_obj_t *self = (machine_spi_obj_t *)self_in;
+ spi_transfer_t spi_xfer = {
+ .tx_buff = src,
+ .tx_total_cnt = len,
+ .rx_buff = dest,
+ .rx_total_cnt = len,
+ .tx_default_val = 0xFF,
+ .tx_default_enable = true,
+ .mode = SPI_TMOD_TX_AND_RX,
+ };
+ // TODO redo transfer_blocking to timeout and poll events.
+ if (!self->is_lp) {
+ spi_transfer_blocking(self->inst, &spi_xfer);
+ } else {
+ lpspi_transfer_blocking(self->inst, &spi_xfer);
+ }
+}
+
+static const mp_machine_spi_p_t machine_spi_p = {
+ .init = machine_spi_init,
+ .deinit = machine_spi_deinit,
+ .transfer = machine_spi_transfer,
+};
+
+MP_DEFINE_CONST_OBJ_TYPE(
+ machine_spi_type,
+ MP_QSTR_SPI,
+ MP_TYPE_FLAG_NONE,
+ make_new, machine_spi_make_new,
+ print, machine_spi_print,
+ protocol, &machine_spi_p,
+ locals_dict, &mp_machine_spi_locals_dict
+ );
+
+#endif // MICROPY_PY_MACHINE_SPI
diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h
index b23886637..83f808b13 100644
--- a/ports/alif/mpconfigport.h
+++ b/ports/alif/mpconfigport.h
@@ -133,6 +133,7 @@
#define MICROPY_PY_MACHINE_I2C (MICROPY_HW_ENABLE_HW_I2C)
#define MICROPY_PY_MACHINE_I2C_TRANSFER_WRITE1 (1)
#define MICROPY_PY_MACHINE_SOFTI2C (1)
+#define MICROPY_PY_MACHINE_SPI (1)
#define MICROPY_PY_MACHINE_SOFTSPI (1)
#define MICROPY_PY_MACHINE_TIMER (1)
#define MICROPY_VFS (1)