New WIP port: Echo R1

The Echo R1 is a new open-hardware music player design, based
on the STM32H743 microcontroller. Schematics and hardware
documentation for it can be found here:

- https://github.com/amachronic/echoplayer

This is an incomplete port. The bootloader can be loaded using
OpenOCD and it can draw to the LCD using SPI. SDRAM is working
but hasn't been extensively tested.

Change-Id: Ifd2bee15c49868fbc989683d3ca14dce48bf3e18
This commit is contained in:
Aidan MacDonald 2025-02-13 13:56:51 +00:00 committed by Solomon Peachy
parent abae4660e2
commit 83950bf233
44 changed files with 3176 additions and 0 deletions

View file

@ -299,4 +299,6 @@ keymaps/keymap-fiiom3k.c
keymaps/keymap-erosq.c
#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
keymaps/keymap-shanlingq1.c
#elif CONFIG_KEYPAD == ECHO_R1_PAD
keymaps/keymap-echor1.c
#endif

View file

@ -0,0 +1,42 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
/* Button Code Definitions for Echo R1 */
#include "config.h"
#include "action.h"
#include "button.h"
#include "settings.h"
/* {Action Code, Button code, Prereq button code } */
static const struct button_mapping button_context_standard[] = {
LAST_ITEM_IN_LIST
}; /* button_context_standard */
const struct button_mapping* get_context_mapping(int context)
{
switch (context)
{
default:
return button_context_standard;
}
}

View file

@ -238,6 +238,9 @@ enum { ALARM_START_WPS = 0,
#elif CONFIG_CPU == S3C2440 || defined(CPU_PP) || CONFIG_CPU==IMX31L
/* up to 64MB of DRAM at 0x0 */
#define VIRT_PTR ((unsigned char*)0x4000000)
#elif CONFIG_CPU == STM32H743
/* 64k ITCM at 0x0 */
#define VIRT_PTR ((unsigned char*)0x10000)
#else
/* offset from 0x0 slightly */
#define VIRT_PTR ((unsigned char*)sizeof(char*))

View file

@ -94,4 +94,7 @@ x1000/install.c
x1000/main.c
x1000/recovery.c
x1000/utils.c
#elif defined(ECHO_R1)
echoplayer.c
show_logo.c
#endif

47
bootloader/echoplayer.c Normal file
View file

@ -0,0 +1,47 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 by Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "kernel/kernel-internal.h"
#include "system.h"
#include "power.h"
#include "rtc.h"
#include "lcd.h"
#include "backlight.h"
#include "button.h"
extern void show_logo(void);
void main(void)
{
system_init();
kernel_init();
rtc_init();
lcd_init();
backlight_init();
backlight_on();
show_logo();
while (1) {
lcd_putsxyf(60, 140, "btn: %08x", button_read_device());
lcd_update();
}
}

View file

@ -423,6 +423,8 @@ drivers/rtc/rtc_s35380a.c
drivers/rtc/rtc_d2.c
#elif (CONFIG_RTC == RTC_IMX233)
drivers/rtc/rtc_imx233.c
#elif (CONFIG_RTC == RTC_STM32H743)
drivers/rtc/rtc_stm32h7.c
#endif /* (CONFIG_RTC == RTC_) */
#endif /* PLATFORM_NATIVE */
@ -1970,6 +1972,30 @@ target/arm/rk27xx/ihifi2/audio-ihifi800.c
#endif
#endif
#if CONFIG_CPU == STM32H743
target/arm/stm32/crt0-stm32h7.S
target/arm/stm32/vectors-stm32h7.S
target/arm/stm32/adc-stm32h7.c
target/arm/stm32/debug-stm32h7.c
target/arm/stm32/gpio-stm32h7.c
target/arm/stm32/i2c-stm32h7.c
target/arm/stm32/pcm-stm32h7.c
target/arm/stm32/sd-stm32h7.c
target/arm/stm32/spi-stm32h7.c
target/arm/stm32/system-stm32h7.c
target/arm/stm32/timer-stm32h7.c
target/arm/stm32/usb-stm32h7.c
#endif
#if defined(ECHO_R1)
target/arm/stm32/echoplayer/audiohw-echoplayer.c
target/arm/stm32/echoplayer/backlight-echoplayer.c
target/arm/stm32/echoplayer/button-echoplayer.c
target/arm/stm32/echoplayer/lcd-echoplayer.c
target/arm/stm32/echoplayer/power-echoplayer.c
target/arm/stm32/echoplayer/system-echoplayer.c
#endif
#if (CONFIG_PLATFORM & PLATFORM_ANDROID)
target/hosted/kernel-unix.c
target/hosted/filesystem-unix.c

View file

@ -0,0 +1,172 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "rtc.h"
#include "timefuncs.h"
#include "stm32h7/rtc.h"
#include "stm32h7/rcc.h"
#include "stm32h7/pwr.h"
void rtc_init(void)
{
st_writef(RCC_APB4ENR, RTCAPBEN(1));
/* Initialize RTC if needed */
if (!st_readf(RTC_ISR, INITS))
{
struct tm tm = {
.tm_hour = 0,
.tm_min = 0,
.tm_sec = 0,
.tm_year = 101,
.tm_mon = 0,
.tm_mday = 1,
.tm_wday = 1,
.tm_isdst = 0,
};
rtc_write_datetime(&tm);
}
}
int rtc_read_datetime(struct tm *tm)
{
/*
* Wait for RSF bit to indicate time/date registers are valid.
* But if the calendar is not initialized, then don't wait as
* they will never be valid.
*/
bool valid = false;
while (!valid)
{
uint32_t isr = REG_RTC_ISR;
if (!st_vreadf(isr, RTC_ISR, INITS))
break;
if (st_vreadf(isr, RTC_ISR, RSF))
valid = true;
}
if (valid)
{
uint32_t tr = REG_RTC_TR;
uint32_t dr = REG_RTC_DR;
tm->tm_sec = st_vreadf(tr, RTC_TR, ST)*10 + st_vreadf(tr, RTC_TR, SU);
tm->tm_min = st_vreadf(tr, RTC_TR, MNT)*10 + st_vreadf(tr, RTC_TR, MNU);
tm->tm_hour = st_vreadf(tr, RTC_TR, HT)*10 + st_vreadf(tr, RTC_TR, HU);
tm->tm_mday = st_vreadf(dr, RTC_DR, DT)*10 + st_vreadf(dr, RTC_DR, DU);
tm->tm_mon = st_vreadf(dr, RTC_DR, MT)*10 + st_vreadf(dr, RTC_DR, MU) - 1;
tm->tm_year = 100 + st_vreadf(dr, RTC_DR, YT)*10 + st_vreadf(dr, RTC_DR, YU);
tm->tm_isdst = st_readf(RTC_CR, BKP);
}
else
{
tm->tm_sec = 0;
tm->tm_min = 0;
tm->tm_hour = 0;
tm->tm_mday = 1;
tm->tm_mon = 0;
tm->tm_year = 101;
tm->tm_isdst = 0;
}
set_day_of_week(tm);
set_day_of_year(tm);
return 1;
}
int rtc_write_datetime(const struct tm *tm)
{
/* Allow RTC write protection */
st_writef(PWR_CR1, DBP(1));
/* Unlock registers */
st_writef(RTC_WPR, KEY_V(KEY1));
st_writef(RTC_WPR, KEY_V(KEY2));
/* Enter initialization mode */
st_writef(RTC_ISR, INIT(1));
while (!st_readf(RTC_ISR, INITF));
/* Program calendar and dividers */
st_writef(RTC_PRER, PREDIV_A(127), PREDIV_S(255));
st_writef(RTC_TR,
PM(0),
HT(tm->tm_hour / 10),
HU(tm->tm_hour % 10),
MNT(tm->tm_min / 10),
MNU(tm->tm_min % 10),
ST(tm->tm_sec / 10),
SU(tm->tm_sec % 10));
st_writef(RTC_DR,
WDU(tm->tm_wday == 0 ? 7 : tm->tm_wday),
YT((tm->tm_year / 10) % 10),
YU(tm->tm_year % 10),
MT((tm->tm_mon + 1) / 10),
MU((tm->tm_mon + 1) % 10),
DT(tm->tm_mday / 10),
DU(tm->tm_mday % 10));
st_writef(RTC_CR,
FMT(0),
BKP(!!tm->tm_isdst));
/* Exit initialization */
st_writef(RTC_ISR, INIT(0));
/* Lock registers */
st_writef(RTC_WPR, KEY(0));
st_writef(PWR_CR1, DBP(0));
return 1;
}
#ifdef HAVE_RTC_ALARM
/* TODO */
void rtc_set_alarm(int h, int m)
{
(void)h;
(void)m;
}
void rtc_get_alarm(int *h, int *m)
{
*h = 0;
*m = 0;
}
void rtc_enable_alarm(bool enable)
{
(void)enable;
}
bool rtc_check_alarm_started(bool release_alarm)
{
(void)release_alarm;
return false;
}
bool rtc_check_alarm_flag(void)
{
return false;
}
#endif

View file

@ -76,6 +76,9 @@
#elif CONFIG_CPU == S5L8702 || CONFIG_CPU == S5L8720
# define USB_DW_PHYSADDR(x) S5L8702_PHYSICAL_ADDR(x)
# define USB_DW_UNCACHEDADDR(x) S5L8702_UNCACHED_ADDR(x)
#elif CONFIG_CPU == STM32H743
# define USB_DW_PHYSADDR(x) x
# define NO_UNCACHED_ADDR /* TODO: maybe implement this */
#elif !defined(USB_DW_ARCH_SLAVE)
# error "Must define USB_DW_PHYSADDR / USB_DW_UNCACHEDADDR!"
#endif

View file

@ -238,6 +238,8 @@ struct sound_settings_info
#include "fiiolinux_codec.h"
#elif defined(HAVE_EROSQ_LINUX_CODEC)
#include "erosqlinux_codec.h"
#elif defined(HAVE_TLV320AIC3104)
#include "tlv320aic3104_codec.h"
#endif
#if defined(HAVE_X1000_ICODEC_REC) && !defined(HAVE_X1000_ICODEC_PLAY)

View file

@ -84,6 +84,7 @@
#define IMX233 233
#define RK27XX 2700
#define X1000 1000
#define STM32H743 32743
/* platforms
* bit fields to allow PLATFORM_HOSTED to be OR'ed e.g. with a
@ -163,6 +164,7 @@
#define EROSQ_PAD 72
#define FIIO_M3K_PAD 73
#define SHANLING_Q1_PAD 74
#define ECHO_R1_PAD 75
/* CONFIG_REMOTE_KEYPAD */
#define H100_REMOTE 1
@ -282,6 +284,7 @@
#define LCD_FIIOM3K 69 /* as used by the FiiO M3K */
#define LCD_SHANLING_Q1 70 /* as used by the Shanling Q1 */
#define LCD_EROSQ 71 /* as used by the ErosQ (native) */
#define LCD_ECHO_R1 72 /* ILI9342, as used by the Echo R1 */
/* LCD_PIXELFORMAT */
#define HORIZONTAL_PACKING 1
@ -359,6 +362,7 @@ Lyre prototype 1 */
#define RTC_CONNECT 24 /* Sansa Connect AVR */
#define RTC_NANO3G 25 /* Dialog Semiconductor D1671 ??? */
#define RTC_NANO4G 26 /* Dialog Semiconductor D1759 ??? */
#define RTC_STM32H743 27
/* USB On-the-go */
#define USBOTG_M66591 6591 /* M:Robe 500 */
@ -610,6 +614,8 @@ Lyre prototype 1 */
#include "config/shanlingq1.h"
#elif defined(EROS_QN)
#include "config/erosqnative.h"
#elif defined(ECHO_R1)
#include "config/echor1.h"
#else
//#error "unknown hwardware platform!"
#endif

View file

@ -0,0 +1,114 @@
/* RoLo-related defines */
#define MODEL_NAME "Echo R1"
#define MODEL_NUMBER 119
#define BOOTFILE_EXT "echo"
#define BOOTFILE "rockbox." BOOTFILE_EXT
#define BOOTDIR "/.rockbox"
/* CPU defines */
#define CONFIG_CPU STM32H743
#define STM32_LSE_FREQ 32768
#define STM32_HSE_FREQ 24000000
#define CPU_FREQ 480000000
#ifndef SIMULATOR
#define TIMER_FREQ STM32_HSE_FREQ
#endif
/* Kernel defines */
#define INCLUDE_TIMEOUT_API
#define HAVE_SEMAPHORE_OBJECTS
/* Buffer for plugins and codecs. */
#define PLUGIN_BUFFER_SIZE 0x200000 /* 2 MiB */
#define CODEC_SIZE 0x100000 /* 1 MiB */
/* LCD defines */
#define CONFIG_LCD LCD_ECHO_R1
#define LCD_WIDTH 320
#define LCD_HEIGHT 240
#define LCD_DEPTH 16
#define LCD_PIXELFORMAT RGB565
#define LCD_DPI 240 // FIXME: review this.
#define HAVE_LCD_COLOR
#define HAVE_LCD_BITMAP
#define HAVE_LCD_ENABLE
/* Backlight defines */
#define HAVE_BACKLIGHT
#define HAVE_BACKLIGHT_BRIGHTNESS
#define MIN_BRIGHTNESS_SETTING 1
#define MAX_BRIGHTNESS_SETTING 100
#define BRIGHTNESS_STEP 5
#define DEFAULT_BRIGHTNESS_SETTING 70
#define CONFIG_BACKLIGHT_FADING BACKLIGHT_FADING_SW_SETTING
/* Codec / audio hardware defines */
#define HW_SAMPR_CAPS SAMPR_CAP_ALL_96 // FIXME: check this section
#define REC_SAMPR_CAPS SAMPR_CAP_ALL_96
#define INPUT_SRC_CAPS SRC_CAP_MIC
#define AUDIOHW_CAPS MIC_GAIN_CAP
#define HAVE_RECORDING
#define HAVE_TLV320AIC3104 // TODO: Sansa connect uses the AIC3106, possible code sharing?
#define HAVE_SW_TONE_CONTROLS
#define HAVE_SW_VOLUME_CONTROL
#define DEFAULT_REC_MIC_GAIN 12
/* Button defines */
#define CONFIG_KEYPAD ECHO_R1_PAD
#define HAVE_HEADPHONE_DETECTION
#define HAVE_LINEOUT_DETECTION
/* Storage defines */
#define CONFIG_STORAGE STORAGE_SD
#define HAVE_HOTSWAP
#define HAVE_HOTSWAP_STORAGE_AS_MAIN
#define HAVE_MULTIVOLUME
#define STORAGE_WANTS_ALIGN
#define STORAGE_NEEDS_BOUNCE_BUFFER
/* RTC settings */
#define CONFIG_RTC RTC_STM32H743
#define HAVE_RTC_ALARM
/* Power management */
#define CONFIG_BATTERY_MEASURE VOLTAGE_MEASURE
#define CONFIG_CHARGING CHARGING_MONITOR
#define HAVE_SW_POWEROFF
/* Only one battery type */
#define BATTERY_CAPACITY_DEFAULT 1100
#define BATTERY_CAPACITY_MIN 1100
#define BATTERY_CAPACITY_MAX 1100
#define BATTERY_CAPACITY_INC 0
/* Multiboot */
#define HAVE_BOOTDATA
#define BOOT_REDIR "rockbox_main.echor1"
/* USB support */
#ifndef SIMULATOR
#define CONFIG_USBOTG USBOTG_DESIGNWARE
#define USB_DW_TURNAROUND 5
#define HAVE_USBSTACK
#define USB_VENDOR_ID 0x1
#define USB_PRODUCT_ID 0x2
#define USB_DEVBSS_ATTR __attribute__((aligned(32)))
#define HAVE_USB_POWER
//#define HAVE_USB_CHARGING_ENABLE
//#define HAVE_USB_CHARGING_IN_THREAD
//#define TARGET_USB_CHARGING_DEFAULT USB_CHARGING_FORCE
#define HAVE_BOOTLOADER_USB_MODE
#endif
/* Rockbox capabilities */
#define HAVE_FAT16SUPPORT
#define HAVE_ALBUMART
#define HAVE_BMP_SCALING
#define HAVE_JPEG
#define HAVE_TAGCACHE
#define HAVE_VOLUME_IN_LIST
#define HAVE_QUICKSCREEN
#define HAVE_HOTKEY
#define AB_REPEAT_ENABLE
#define HAVE_BOOTLOADER_SCREENDUMP

View file

@ -77,3 +77,6 @@
#if CONFIG_CPU == X1000
#include "x1000.h"
#endif
#if CONFIG_CPU == STM32H743
#include "cpu-stm32h743.h"
#endif

View file

@ -0,0 +1,29 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef __TLV320AIC3104_CODEC__
#define __TLV320AIC3104_CODEC__
// FIXME: range
AUDIOHW_SETTING(VOLUME, "dB", 0, 2, -74, -2, -40)
AUDIOHW_SETTING(MIC_GAIN, "dB", 0, 1, 0, 63, 12)
#endif /* __TLV320AIC3104_CODEC__ */

View file

@ -0,0 +1,25 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 by Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "adc.h"
void adc_init(void)
{
}

View file

@ -0,0 +1,97 @@
#include "cpu.h"
/*
* TODO: this is temporary and has not been tested
*/
ENTRY(main)
OUTPUT_FORMAT(elf32-littlearm)
OUTPUT_ARCH(arm)
STARTUP(target/arm/stm32/crt0-stm32h7.o)
MEMORY
{
SRAM_AXI (rwx) : ORIGIN = STM32_SRAM_AXI_BASE, LENGTH = STM32_SRAM_AXI_SIZE
DTCM (rwx) : ORIGIN = STM32_DTCM_BASE, LENGTH = STM32_DTCM_SIZE
ITCM (rwx) : ORIGIN = STM32_ITCM_BASE, LENGTH = STM32_ITCM_SIZE
SDRAM (rwx) : ORIGIN = STM32_SDRAM1_BASE, LENGTH = MEMORYSIZE * 1024 * 1024
}
/*
* to control section alignment (only affects on-disk alignment):
* -Wl,-z,max-page-size=0x1
*/
PHDRS
{
sram_rx PT_LOAD ;
sram_ro PT_LOAD ;
sram_rw PT_LOAD ;
itcm PT_LOAD ;
dtcm PT_LOAD ;
sdram_rx PT_LOAD ;
sdram_rw PT_LOAD ;
}
SECTIONS
{
.text :
{
loadaddress = .; /* only needed to keep ROLO happy */
KEEP(*(.bootdata))
*(.init.text*)
*(.text*)
} > SRAM_AXI :sram_rx
.rodata :
{
*(.rodata*)
} > SRAM_AXI :sram_ro
.data :
{
_databegin = .;
*(.data*)
_dataend = .;
} > SRAM_AXI :sram_rw
_datacopy = LOADADDR(.data);
.itext :
{
KEEP(*(.vectors.arm))
KEEP(*(.vectors.platform))
*(.icode*);
} > ITCM :itcm
.stack (NOLOAD) :
{
irqstackbegin = .;
. += 0x400;
irqstackend = .;
stackbegin = .;
. += 0x2000;
stackend = .;
*(.stack);
} > DTCM :dtcm
.bss (NOLOAD) :
{
_bssbegin = .;
*(.bss*);
*(COMMON);
_bssend = .;
} > SDRAM :sdram_rw
audiobuffer = ALIGN(32);
audiobufend = ORIGIN(SDRAM) + LENGTH(SDRAM) - CODEC_SIZE - PLUGIN_BUFFER_SIZE;
codecbuf = audiobufend;
pluginbuf = codecbuf + CODEC_SIZE;
}
EXTERN(__vectors_arm);
EXTERN(__vectors_platform);
ASSERT(DEFINED(__vectors_arm), "ARM exception vectors are not defined.");
ASSERT(DEFINED(__vectors_platform), "Platform exception vectors are not defined.");

View file

@ -0,0 +1,70 @@
#include "cpu.h"
#if 0
/* version for openocd load to ram -- faster to upload than flash */
# define TEXT_MEM SRAM_AXI
# define DATA_MEM SRAM_AXI
#else
# define TEXT_MEM FLASH1
# define DATA_MEM SRAM_AXI
#endif
ENTRY(reset_handler)
OUTPUT_FORMAT(elf32-littlearm)
OUTPUT_ARCH(arm)
STARTUP(target/arm/stm32/crt0-stm32h7.o)
MEMORY
{
SRAM_AXI (rwx) : ORIGIN = STM32_SRAM_AXI_BASE, LENGTH = STM32_SRAM_AXI_SIZE
ITCM (rwx) : ORIGIN = STM32_ITCM_BASE, LENGTH = STM32_ITCM_SIZE
DTCM (rwx) : ORIGIN = STM32_DTCM_BASE, LENGTH = STM32_DTCM_SIZE
FLASH1 (rx) : ORIGIN = STM32_FLASH_BANK1_BASE, LENGTH = STM32_FLASH_BANK1_SIZE
FLASH2 (rx) : ORIGIN = STM32_FLASH_BANK2_BASE, LENGTH = STM32_FLASH_BANK2_SIZE
}
SECTIONS
{
.text :
{
KEEP(*(.vectors.arm))
KEEP(*(.vectors.platform))
*(.init.text*)
*(.text*)
*(.rodata*)
} > TEXT_MEM
.data :
{
_databegin = .;
*(.data*)
_dataend = .;
} > DATA_MEM AT> TEXT_MEM
_datacopy = LOADADDR(.data);
.bss (NOLOAD) :
{
_bssbegin = .;
*(.bss*);
*(COMMON);
_bssend = .;
} > DATA_MEM
.stack (NOLOAD) : ALIGN(4)
{
irqstackbegin = .;
. += 0x400;
irqstackend = .;
stackbegin = .;
. += 0x2000;
stackend = .;
*(.stack);
} > DTCM
}
EXTERN(__vectors_arm);
EXTERN(__vectors_platform);
ASSERT(DEFINED(__vectors_arm), "ARM exception vectors are not defined.");
ASSERT(DEFINED(__vectors_platform), "Platform exception vectors are not defined.");

View file

@ -0,0 +1,73 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef __CPU_STM32H743_H__
#define __CPU_STM32H743_H__
#define CACHE_SIZE (16 * 1024)
#define CACHEALIGN_BITS 5
#define DCACHE_SIZE CACHE_SIZE
#define DCACHE_WAYS 0x4
#define DCACHE_SETS 0x80
#define DCACHE_LINESIZE (1 << CACHEALIGN_BITS)
#define STM32_LSI_FREQ 32000
#define STM32_HSI_FREQ 64000000
#define STM32_CSI_FREQ 4000000
#define STM32_HSI48_FREQ 48000000
#define OTGBASE 0x40080000
#define USB_NUM_ENDPOINTS 9
#define STM32_ITCM_BASE 0x00000000
#define STM32_ITCM_SIZE (64 * 1024)
#define STM32_DTCM_BASE 0x20000000
#define STM32_DTCM_SIZE (128 * 1024)
#define STM32_SRAM_AXI_BASE 0x24000000
#define STM32_SRAM_AXI_SIZE (512 * 1024)
#define STM32_SRAM1_BASE 0x30000000
#define STM32_SRAM1_SIZE (128 * 1024)
#define STM32_SRAM2_BASE 0x30020000
#define STM32_SRAM2_SIZE (128 * 1024)
#define STM32_SRAM3_BASE 0x30040000
#define STM32_SRAM3_SIZE (128 * 1024)
#define STM32_SRAM4_BASE 0x38000000
#define STM32_SRAM4_SIZE (64 * 1024)
#define STM32_SRAM_BKP_BASE 0x38800000
#define STM32_SRAM_BKP_SIZE (4 * 1024)
#define STM32_FLASH_BANK1_BASE 0x08000000
#define STM32_FLASH_BANK1_SIZE (1024 * 1024)
#define STM32_FLASH_BANK2_BASE 0x08100000
#define STM32_FLASH_BANK2_SIZE (1024 * 1024)
/* FIXME: this changes depending on target settings */
#define STM32_SDRAM1_BASE 0x70000000
#endif /* __CPU_STM32H743_H__ */

View file

@ -0,0 +1,124 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 by Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "config.h"
#include "bootdata.h"
#include "stm32h7/pwr.h"
.syntax unified
.text
#if defined(HAVE_BOOTDATA) && !defined(BOOTLOADER)
.section .bootdata
put_boot_data_here
#endif
.section .init.text,"ax",%progbits
.global reset_handler
.type reset_handler, function
reset_handler:
/*
* After power-up the CPU will be in limited Run* mode and many things
* won't work; we need to program the power supply configuration first
* before doing anything else. Below the internal LDO is configured as
* the power supply.
*/
ldr r0, =STA_PWR_CR3
ldr r1, [r0] @ Read PWR_CR3
orr r1, #BM_PWR_CR3_LDOEN @ Set LDOEN=1
bic r1, #BM_PWR_CR3_BYPASS @ Set BYPASS=0
str r1, [r0] @ Write updated value to PWR_CR3
/* Wait for PSR_CSR1.ACTVOSRDY, which indicates we exited Run* mode */
ldr r0, =STA_PWR_CSR1
1:
ldr r1, [r0] @ Read PWR_CSR1
ands r1, #BM_PWR_CSR1_ACTVOSRDY
beq 1b @ Loop back if ACTVOSRDY==0
/* Now jump to the common startup code */
b crt0_start
.global start
.type start, function
crt0_start:
/* Zero out BSS */
ldr a1, =_bssbegin
ldr a2, =_bssend
mov a3, #0
bl crt0_area_clear
/* Copy data section */
ldr a1, =_databegin
ldr a2, =_dataend
ldr a3, =_datacopy
bl crt0_area_copy
/* Clear the main thread stack */
ldr a1, =stackbegin
ldr a2, =stackend
ldr a3, =0xdeadbeef
bl crt0_area_clear
/* Clear the IRQ stack */
ldr a1, =irqstackbegin
ldr a2, =irqstackend
ldr a3, =0xdeadbeef
bl crt0_area_clear
/* Use MSP for IRQ handlers */
ldr r0, =irqstackend
msr msp, r0
/* Use PSP for application code */
ldr r0, =stackend
msr psp, r0
/* Enter thread mode; now sp points to PSP */
mrs r2, control
orr r2, #2
msr control, r2
/* Jump to main */
b main
.local crt0_area_copy
.type crt0_area_copy, function
/* crt0_area_copy(uint32_t *dst, uint32_t *dst_end, const void *src) */
crt0_area_copy:
cmp a2, a1
ldrhi v1, [a3], #4
strhi v1, [a1], #4
bhi crt0_area_copy
bx lr
.local crt0_area_clear
.type crt0_area_clear, function
/* crt0_area_clear(const uint32_t *dst, const uint32_t *end, uint32_t value) */
crt0_area_clear:
cmp a2, a1
strhi a3, [a1], #4
bhi crt0_area_clear
bx lr

View file

@ -0,0 +1,31 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 by Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "system.h"
bool dbg_hw_info(void)
{
return false;
}
bool dbg_ports(void)
{
return false;
}

View file

@ -0,0 +1,24 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef __ECHOPLAYER_ADC_TARGET_H__
#define __ECHOPLAYER_ADC_TARGET_H__
#endif /* __ECHOPLAYER_ADC_TARGET_H__ */

View file

@ -0,0 +1,57 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "audiohw.h"
void audiohw_init(void)
{
}
void audiohw_postinit(void)
{
}
void audiohw_close(void)
{
}
void audio_set_output_source(int source)
{
(void)source;
}
void audiohw_set_recvol(int left, int right, int type)
{
(void)left;
(void)right;
(void)type;
}
void audiohw_set_volume(int vol_l, int vol_r)
{
(void)vol_l;
(void)vol_r;
}
void audio_input_mux(int source, unsigned flags)
{
(void)source;
(void)flags;
}

View file

@ -0,0 +1,46 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "backlight.h"
#include "gpio-stm32h7.h"
bool backlight_hw_init(void)
{
/* FIXME: override PWM config for now, rev1 has broken backlight */
gpio_configure_single(GPIO_BACKLIGHT,
GPIOF_OUTPUT(1, GPIO_TYPE_PUSH_PULL,
GPIO_SPEED_LOW, GPIO_PULL_DISABLED));
return true;
}
void backlight_hw_on(void)
{
gpio_set_level(GPIO_BACKLIGHT, 1);
}
void backlight_hw_off(void)
{
gpio_set_level(GPIO_BACKLIGHT, 0);
}
void backlight_hw_brightness(int brightness)
{
(void)brightness;
}

View file

@ -0,0 +1,32 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef __ECHOPLAYER_BACKLIGHT_TARGET_H__
#define __ECHOPLAYER_BACKLIGHT_TARGET_H__
#include <stdbool.h>
extern bool backlight_hw_init(void);
extern void backlight_hw_on(void);
extern void backlight_hw_off(void);
extern void backlight_hw_brightness(int brightness);
#endif /* __ECHOPLAYER_BACKLIGHT_TARGET_H__ */

View file

@ -0,0 +1,85 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 by Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "button.h"
#include "gpio-stm32h7.h"
void button_init_device(void)
{
}
int button_read_device(void)
{
int buttons = 0;
/* Most buttons are active low, so invert the read */
uint32_t ga = ~REG_GPIO_IDR(GPIO_A);
uint32_t gb = ~REG_GPIO_IDR(GPIO_B);
uint32_t gc = ~REG_GPIO_IDR(GPIO_C);
uint32_t gd = ~REG_GPIO_IDR(GPIO_D);
uint32_t gf = ~REG_GPIO_IDR(GPIO_F);
uint32_t gh = ~REG_GPIO_IDR(GPIO_H);
if (ga & BIT_N(GPION_PIN(GPIO_BUTTON_A)))
buttons |= BUTTON_A;
if (ga & BIT_N(GPION_PIN(GPIO_BUTTON_B)))
buttons |= BUTTON_B;
if (ga & BIT_N(GPION_PIN(GPIO_BUTTON_X)))
buttons |= BUTTON_X;
if (ga & BIT_N(GPION_PIN(GPIO_BUTTON_Y)))
buttons |= BUTTON_A;
if (!(gb & BIT_N(GPION_PIN(GPIO_BUTTON_SELECT))))
buttons |= BUTTON_SELECT;
if (gb & BIT_N(GPION_PIN(GPIO_BUTTON_UP)))
buttons |= BUTTON_UP;
if (gb & BIT_N(GPION_PIN(GPIO_BUTTON_VOL_DOWN)))
buttons |= BUTTON_UP;
if (gc & BIT_N(GPION_PIN(GPIO_BUTTON_RIGHT)))
buttons |= BUTTON_RIGHT;
if (gd & BIT_N(GPION_PIN(GPIO_BUTTON_START)))
buttons |= BUTTON_START;
if (gd & BIT_N(GPION_PIN(GPIO_BUTTON_DOWN)))
buttons |= BUTTON_DOWN;
if (gd & BIT_N(GPION_PIN(GPIO_BUTTON_LEFT)))
buttons |= BUTTON_LEFT;
if (!(gf & BIT_N(GPION_PIN(GPIO_BUTTON_POWER))))
buttons |= BUTTON_POWER;
if (gh & BIT_N(GPION_PIN(GPIO_BUTTON_VOL_UP)))
buttons |= BUTTON_VOL_UP;
if (gh & BIT_N(GPION_PIN(GPIO_BUTTON_HOLD)))
buttons |= BUTTON_HOLD;
return buttons;
}
bool headphones_inserted(void)
{
return true;
}
bool lineout_inserted(void)
{
return false;
}

View file

@ -0,0 +1,44 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef __ECHOPLAYER_BUTTON_TARGET_H__
#define __ECHOPLAYER_BUTTON_TARGET_H__
#define BUTTON_POWER 0x00000001
#define BUTTON_HOLD 0x00000002
#define BUTTON_VOL_UP 0x00000004
#define BUTTON_VOL_DOWN 0x00000008
#define BUTTON_UP 0x00000010
#define BUTTON_DOWN 0x00000020
#define BUTTON_LEFT 0x00000040
#define BUTTON_RIGHT 0x00000080
#define BUTTON_A 0x00000100
#define BUTTON_B 0x00000200
#define BUTTON_X 0x00000400
#define BUTTON_Y 0x00000800
#define BUTTON_START 0x00001000
#define BUTTON_SELECT 0x00002000
#define BUTTON_MAIN 0x00003fff
#define POWEROFF_BUTTON BUTTON_POWER
#define POWEROFF_COUNT 30
#endif /* __ECHOPLAYER_BUTTON_TARGET_H__ */

View file

@ -0,0 +1,56 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef __ECHOPLAYER_GPIO_TARGET_H__
#define __ECHOPLAYER_GPIO_TARGET_H__
#define GPIO_BUTTON_A GPIO_PA(0)
#define GPIO_BUTTON_B GPIO_PA(1)
#define GPIO_BUTTON_X GPIO_PA(6)
#define GPIO_BUTTON_Y GPIO_PA(4)
#define GPIO_BUTTON_START GPIO_PD(12)
#define GPIO_BUTTON_SELECT GPIO_PB(8)
#define GPIO_BUTTON_UP GPIO_PB(14)
#define GPIO_BUTTON_DOWN GPIO_PD(6)
#define GPIO_BUTTON_LEFT GPIO_PD(7)
#define GPIO_BUTTON_RIGHT GPIO_PC(6)
#define GPIO_BUTTON_VOL_UP GPIO_PH(6)
#define GPIO_BUTTON_VOL_DOWN GPIO_PB(15)
#define GPIO_BUTTON_POWER GPIO_PF(8)
#define GPIO_BUTTON_HOLD GPIO_PH(7)
#define GPIO_CPU_POWER_ON GPIO_PA(2)
#define GPIO_POWER_1V8 GPIO_PD(3)
#define GPIO_CODEC_AVDD_EN GPIO_PB(9)
#define GPIO_CODEC_DVDD_EN GPIO_PC(5)
#define GPIO_CODEC_RESET GPIO_PC(4)
#define GPIO_USBPHY_POWER_EN GPIO_PD(4)
#define GPIO_USBPHY_RESET GPIO_PD(5)
#define GPIO_CHARGER_CHARGING GPIO_PA(10)
#define GPIO_CHARGER_ENABLE GPIO_PA(15)
#define GPIO_SDMMC_DETECT GPIO_PC(7)
#define GPIO_LINEOUT_DETECT GPIO_PG(13)
#define GPIO_LCD_RESET GPIO_PD(11)
#define GPIO_BACKLIGHT GPIO_PD(13)
#endif /* __ECHOPLAYER_GPIO_TARGET_H__ */

View file

@ -0,0 +1,134 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "system.h"
#include "kernel.h"
#include "lcd.h"
#include "spi-stm32h7.h"
#include "gpio-stm32h7.h"
#include "stm32h7/rcc.h"
struct stm_spi_config spi_cfg = {
.num = STM_SPI5,
.mode = STM_SPIMODE_HALF_DUPLEX,
.proto = STM_SPIPROTO_MOTOROLA,
.frame_bits = 9,
.hw_cs_output = true,
};
struct stm_spi spi;
#define ili_cmd(cmd, ...) \
do { \
uint16_t arr[] = {cmd, __VA_ARGS__}; \
for (size_t i = 1; i < ARRAYLEN(arr); ++i) \
arr[i] |= 0x100; \
stm_spi_transmit(&spi, arr, sizeof(arr)); \
} while (0)
static void set_row_column_address(int x, int y, int w, int h)
{
ili_cmd(0x2a, x >> 8, x & 0xff, (w-1) >> 8, (w-1) & 0xff);
ili_cmd(0x2b, y >> 8, y & 0xff, (h-1) >> 8, (h-1) & 0xff);
}
void lcd_init_device(void)
{
/* Clock configuration -- should be 12 MHz (SPI clock is 1/2 of HSE) */
st_writef(RCC_D2CCIP1R, SPI45SEL_V(HSE));
st_writef(RCC_APB2ENR, SPI5EN(1));
/* Configure SPI bus */
stm_spi_init(&spi, &spi_cfg);
/* Ensure controller is reset */
gpio_set_level(GPIO_LCD_RESET, 0);
sleep(12);
gpio_set_level(GPIO_LCD_RESET, 1);
sleep(12);
/* Sleep out */
ili_cmd(0x11);
sleep(12);
/* memory access control (X/Y invert, BGR panel) */
ili_cmd(0x36, 0xc8);
/* pixel format set (16bpp) */
ili_cmd(0x3a, 0x55);
/* display ON */
ili_cmd(0x29);
}
bool lcd_active(void)
{
return true;
}
void lcd_update(void)
{
lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT);
}
void lcd_update_rect(int x, int y, int width, int height)
{
/* row buffer to minimize time wasted in post-transaction delay */
static uint16_t row[LCD_WIDTH * 2];
if (x < 0)
x = 0;
else if (x >= LCD_WIDTH)
x = LCD_WIDTH - 1;
if (y < 0)
y = 0;
else if (y >= LCD_HEIGHT)
y = LCD_HEIGHT - 1;
if (width > LCD_WIDTH - x)
width = LCD_WIDTH - x;
if (height > LCD_HEIGHT - y)
height = LCD_HEIGHT - y;
set_row_column_address(x, y, width, height);
ili_cmd(0x2c);
for (int py = y; py < height; ++py)
{
for (int px = x; px < width; ++px)
{
fb_data *fb = FBADDR(px, py);
uint16_t *data = &row[px * 2];
data[0] = 0x100;
data[0] |= (FB_UNPACK_RED(*fb) >> 3) << 3;
data[0] |= (FB_UNPACK_GREEN(*fb) >> 5);
data[1] = 0x100;
data[1] |= ((FB_UNPACK_GREEN(*fb) >> 2) & 0x7) << 5;
data[1] |= (FB_UNPACK_BLUE(*fb) >> 3);
}
stm_spi_transmit(&spi, &row[x * 2], width * sizeof(*row) * 2);
}
}

View file

@ -0,0 +1,64 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 by Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "power.h"
unsigned short battery_level_disksafe = 3500;
unsigned short battery_level_shutoff = 3400;
/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */
unsigned short percent_to_volt_discharge[11] =
{
3400, 3639, 3697, 3723, 3757, 3786, 3836, 3906, 3980, 4050, 4159
};
/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */
unsigned short percent_to_volt_charge[11] =
{
3485, 3780, 3836, 3857, 3890, 3930, 3986, 4062, 4158, 4185, 4196
};
/* Power management */
void power_init(void)
{
}
void power_off(void)
{
}
void system_reboot(void)
{
}
unsigned int power_input_status(void)
{
return POWER_INPUT_USB_CHARGER;
}
bool charging_state(void)
{
return true;
}
int _battery_voltage(void)
{
return 4000;
}

View file

@ -0,0 +1,173 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "system.h"
#include "gpio-stm32h7.h"
#include "stm32h7/rcc.h"
#include "stm32h7/fmc.h"
#define F_INPUT GPIOF_INPUT(GPIO_PULL_DISABLED)
#define F_INPUT_PU GPIOF_INPUT(GPIO_PULL_UP)
#define F_INPUT_PD GPIOF_INPUT(GPIO_PULL_DOWN)
#define F_OUT_LS(x) GPIOF_OUTPUT(x, GPIO_TYPE_PUSH_PULL, GPIO_SPEED_LOW, GPIO_PULL_DISABLED)
#define F_SDRAM GPIOF_FUNCTION(12, GPIO_TYPE_PUSH_PULL, GPIO_SPEED_VERYHIGH, GPIO_PULL_DISABLED)
#define F_OTG_FS GPIOF_ANALOG()
#define F_ULPI GPIOF_FUNCTION(10, GPIO_TYPE_PUSH_PULL, GPIO_SPEED_VERYHIGH, GPIO_PULL_DISABLED)
#define F_MCO1 GPIOF_FUNCTION(0, GPIO_TYPE_PUSH_PULL, GPIO_SPEED_VERYHIGH, GPIO_PULL_DISABLED)
#define F_I2C1 GPIOF_FUNCTION(4, GPIO_TYPE_OPEN_DRAIN, GPIO_SPEED_LOW, GPIO_PULL_DISABLED)
#define F_SDMMC1 GPIOF_FUNCTION(12, GPIO_TYPE_PUSH_PULL, GPIO_SPEED_VERYHIGH, GPIO_PULL_DISABLED)
#define F_SAI1 GPIOF_FUNCTION(6, GPIO_TYPE_PUSH_PULL, GPIO_SPEED_VERYHIGH, GPIO_PULL_DISABLED)
#define F_SPI5 GPIOF_FUNCTION(5, GPIO_TYPE_PUSH_PULL, GPIO_SPEED_VERYHIGH, GPIO_PULL_DISABLED)
#define F_LCD_AF14 GPIOF_FUNCTION(14, GPIO_TYPE_PUSH_PULL, GPIO_SPEED_VERYHIGH, GPIO_PULL_DISABLED)
#define F_LCD_AF9 GPIOF_FUNCTION(9, GPIO_TYPE_PUSH_PULL, GPIO_SPEED_VERYHIGH, GPIO_PULL_DISABLED)
#define F_LPTIM4_OUT GPIOF_FUNCTION(3, GPIO_TYPE_PUSH_PULL, GPIO_SPEED_LOW, GPIO_PULL_DISABLED)
#define F_LPTIM1_OUT GPIOF_FUNCTION(1, GPIO_TYPE_PUSH_PULL, GPIO_SPEED_VERYHIGH, GPIO_PULL_DISABLED)
static const struct gpio_setting gpios[] = {
STM_DEFGPIO(GPIO_BUTTON_A, F_INPUT_PU),
STM_DEFGPIO(GPIO_BUTTON_B, F_INPUT_PU),
STM_DEFGPIO(GPIO_BUTTON_X, F_INPUT_PU),
STM_DEFGPIO(GPIO_BUTTON_Y, F_INPUT_PU),
STM_DEFGPIO(GPIO_BUTTON_START, F_INPUT_PU),
STM_DEFGPIO(GPIO_BUTTON_SELECT, F_INPUT), /* external pulldown */
STM_DEFGPIO(GPIO_BUTTON_UP, F_INPUT_PU),
STM_DEFGPIO(GPIO_BUTTON_DOWN, F_INPUT_PU),
STM_DEFGPIO(GPIO_BUTTON_LEFT, F_INPUT_PU),
STM_DEFGPIO(GPIO_BUTTON_RIGHT, F_INPUT_PU),
STM_DEFGPIO(GPIO_BUTTON_VOL_UP, F_INPUT_PU),
STM_DEFGPIO(GPIO_BUTTON_VOL_DOWN, F_INPUT_PU),
STM_DEFGPIO(GPIO_BUTTON_POWER, F_INPUT_PD),
STM_DEFGPIO(GPIO_BUTTON_HOLD, F_INPUT_PU),
STM_DEFGPIO(GPIO_CPU_POWER_ON, F_LPTIM4_OUT),
STM_DEFGPIO(GPIO_POWER_1V8, F_OUT_LS(0)), /* active high */
STM_DEFGPIO(GPIO_CODEC_AVDD_EN, F_OUT_LS(1)), /* active low */
STM_DEFGPIO(GPIO_CODEC_DVDD_EN, F_OUT_LS(1)), /* active low */
STM_DEFGPIO(GPIO_CODEC_RESET, F_OUT_LS(0)), /* active low */
STM_DEFGPIO(GPIO_USBPHY_POWER_EN, F_OUT_LS(1)), /* active low */
STM_DEFGPIO(GPIO_USBPHY_RESET, F_OUT_LS(0)), /* active low */
STM_DEFGPIO(GPIO_CHARGER_CHARGING, F_INPUT_PU), /* active low */
STM_DEFGPIO(GPIO_CHARGER_ENABLE, F_INPUT), /* hi-z: off, gnd: on */
STM_DEFGPIO(GPIO_SDMMC_DETECT, F_INPUT_PU), /* active low */
STM_DEFGPIO(GPIO_LINEOUT_DETECT, F_INPUT), /* external pullup */
STM_DEFGPIO(GPIO_LCD_RESET, F_OUT_LS(0)), /* active low */
STM_DEFGPIO(GPIO_BACKLIGHT, F_LPTIM1_OUT),
};
/* TODO - replace hex constants - there are probably mistakes here */
static const struct pingroup_setting pingroups[] = {
/* FMC - SDRAM1 */
STM_DEFPINS(GPIO_A, 0x0080, F_SDRAM),
STM_DEFPINS(GPIO_C, 0x000c, F_SDRAM),
STM_DEFPINS(GPIO_D, 0xc703, F_SDRAM),
STM_DEFPINS(GPIO_E, 0xff83, F_SDRAM),
STM_DEFPINS(GPIO_F, 0xf83f, F_SDRAM),
STM_DEFPINS(GPIO_G, 0x8137, F_SDRAM),
/* OTG_FS */
STM_DEFPINS(GPIO_A, 0x1a00, F_OTG_FS),
/* OTG_HS - ULPI */
STM_DEFPINS(GPIO_A, 0x0028, F_ULPI),
STM_DEFPINS(GPIO_B, 0x3c23, F_ULPI),
STM_DEFPINS(GPIO_C, 0x0001, F_ULPI),
STM_DEFPINS(GPIO_H, 0x0010, F_ULPI),
STM_DEFPINS(GPIO_I, 0x0100, F_ULPI),
/* MCO1 for USB PHY */
STM_DEFPINS(GPIO_A, 0x0100, F_MCO1),
/* I2C1 */
STM_DEFPINS(GPIO_B, 0x00c0, F_I2C1),
/* SDMMC1 */
STM_DEFPINS(GPIO_C, 0x1f00, F_SDMMC1),
STM_DEFPINS(GPIO_D, 0x0004, F_SDMMC1),
/* SAI1 */
STM_DEFPINS(GPIO_E, 0x007c, F_SAI1),
/* SPI5 */
STM_DEFPINS(GPIO_F, 0x02c0, F_SPI5),
/* LCD */
STM_DEFPINS(GPIO_F, 0x0400, F_LCD_AF14),
STM_DEFPINS(GPIO_G, 0x0cc0, F_LCD_AF14),
STM_DEFPINS(GPIO_G, 0x1000, F_LCD_AF9),
STM_DEFPINS(GPIO_H, 0xff00, F_LCD_AF14),
STM_DEFPINS(GPIO_I, 0x06e7, F_LCD_AF14),
};
void gpio_init(void)
{
/* Enable clocks for all used GPIO banks */
st_writef(RCC_AHB4ENR,
GPIOAEN(1), GPIOBEN(1), GPIOCEN(1), GPIODEN(1),
GPIOEEN(1), GPIOFEN(1), GPIOGEN(1), GPIOHEN(1), GPIOIEN(1));
/*
* NOTE: I think it's possible to disable clocks for the banks which
* we don't need to access at runtime because these are only clocking
* register access. Probably a micro-optimization but it supposedly
* does save a few uA/MHz.
*/
gpio_configure_all(gpios, ARRAYLEN(gpios),
pingroups, ARRAYLEN(pingroups));
}
void fmc_init(void)
{
/* configure clock */
st_writef(RCC_D1CCIPR, FMCSEL_V(AHB));
/* ungate FMC peripheral */
st_writef(RCC_AHB3ENR, FMCEN(1));
/* configure FMC */
st_writef(FMC_SDCR(0),
RPIPE(0), /* Not clear why this would be useful so leave at 0 */
RBURST(1), /* Enable burst read optimization */
SDCLK(2), /* 2x fmc_ker_ck per sdclk (120 MHz, tCK = 8.33ns) */
WP(0), /* Write protect off */
CAS(2), /* 2 cycle CAS latency */
NB(1), /* 4 internal banks */
MWID(1), /* 16-bit data bus width */
NR(2), /* 13 row address bits */
NC(1)); /* 9 column address bits */
st_writef(FMC_SDTR(0),
TRCD(2), /* 15-20 ns <= 3 tCK */
TRP(2), /* 15-20 ns <= 3 tCK */
TWR(2), /* WR delay >= 2 tCK, but must be >= RAS - RCD (3 tCK) */
TRC(7), /* 55-65 ns <= 7-8 tCK */
TRAS(5), /* 40-45 ns <= 5-6 tCK */
TXSR(8), /* 70-75 ns <= 9 tCK */
TMRD(1)); /* MR delay == 2 tCK */
st_writef(FMC_BCR(0), FMCEN(1));
/* send SDRAM initialization commands */
st_writef(FMC_SDCMR, CTB1(1), CTB2(0), MODE(1), NRFS(1));
udelay(100);
st_writef(FMC_SDCMR, CTB1(1), CTB2(0), MODE(2), NRFS(1));
st_writef(FMC_SDCMR, CTB1(1), CTB2(0), MODE(3), NRFS(8));
st_writef(FMC_SDCMR, CTB1(1), CTB2(0), MODE(4), NRFS(1), MRD(0x220));
st_writef(FMC_SDCMR, CTB1(1), CTB2(0), MODE(3), NRFS(8));
/*
* Refresh time calculation:
* -> 64ms/8192 rows = 7.81 us/row
* -> 937 tCK per row, minus 20 tCK margin from datasheet
*/
st_writef(FMC_SDRTR, REIE(0), COUNT(917), CRE(0));
}

View file

@ -0,0 +1,66 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "gpio-stm32h7.h"
void gpio_configure_single(int gpio, int confbits)
{
int mode = GPIO_F_GETMODE(confbits);
if (mode == GPIO_MODE_OUTPUT)
gpio_set_level(gpio, GPIO_F_GETINITLEVEL(confbits));
else if (mode == GPIO_MODE_FUNCTION)
gpio_set_function(gpio, GPIO_F_GETAFNUM(confbits));
gpio_set_pull(gpio, GPIO_F_GETPULL(confbits));
gpio_set_type(gpio, GPIO_F_GETTYPE(confbits));
gpio_set_speed(gpio, GPIO_F_GETSPEED(confbits));
gpio_set_mode(gpio, mode);
}
void gpio_configure_port(int port, uint16_t mask, int confbits)
{
for (int pin = 0; pin < 16; ++pin)
{
if (mask & BIT_N(pin))
{
int gpio = GPION_CREATE(port, pin);
gpio_configure_single(gpio, confbits);
}
}
}
void gpio_configure_all(
const struct gpio_setting *gpios, size_t ngpios,
const struct pingroup_setting *pingroups, size_t ngroups)
{
for (size_t i = 0; i < ngpios; ++i)
{
gpio_configure_single(gpios[i].gpio, gpios[i].func);
}
for (size_t i = 0; i < ngroups; ++i)
{
gpio_configure_port(pingroups[i].port,
pingroups[i].pins,
pingroups[i].func);
}
}

View file

@ -0,0 +1,256 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef __GPIO_STM32_H__
#define __GPIO_STM32_H__
#include "system.h"
#include "gpio-target.h"
#include "stm32h7/gpio.h"
#include <stddef.h>
#include <stdint.h>
/* GPIO port numbers */
#define GPIO_A 0
#define GPIO_B 1
#define GPIO_C 2
#define GPIO_D 3
#define GPIO_E 4
#define GPIO_F 5
#define GPIO_G 6
#define GPIO_H 7
#define GPIO_I 8
#define GPIO_J 9
#define GPIO_K 10
/* GPIO pin numbers */
#define GPION_CREATE(port, pin) ((((port) & 0xf) << 4) | ((pin) & 0xf))
#define GPION_PORT(gpio) (((gpio) >> 4) & 0xf)
#define GPION_PIN(gpio) ((gpio) & 0xf)
/* Easy pin number macros */
#define GPIO_PA(x) GPION_CREATE(GPIO_A, x)
#define GPIO_PB(x) GPION_CREATE(GPIO_B, x)
#define GPIO_PC(x) GPION_CREATE(GPIO_C, x)
#define GPIO_PD(x) GPION_CREATE(GPIO_D, x)
#define GPIO_PE(x) GPION_CREATE(GPIO_E, x)
#define GPIO_PF(x) GPION_CREATE(GPIO_F, x)
#define GPIO_PG(x) GPION_CREATE(GPIO_G, x)
#define GPIO_PH(x) GPION_CREATE(GPIO_H, x)
#define GPIO_PI(x) GPION_CREATE(GPIO_I, x)
#define GPIO_PJ(x) GPION_CREATE(GPIO_J, x)
#define GPIO_PK(x) GPION_CREATE(GPIO_K, x)
/* GPIO modes */
#define GPIO_MODE_INPUT 0
#define GPIO_MODE_OUTPUT 1
#define GPIO_MODE_FUNCTION 2
#define GPIO_MODE_ANALOG 3
/* GPIO output types */
#define GPIO_TYPE_PUSH_PULL 0
#define GPIO_TYPE_OPEN_DRAIN 1
/* GPIO output speeds */
#define GPIO_SPEED_LOW 0
#define GPIO_SPEED_MEDIUM 1
#define GPIO_SPEED_HIGH 2
#define GPIO_SPEED_VERYHIGH 3
/* GPIO pullup settings */
#define GPIO_PULL_DISABLED 0
#define GPIO_PULL_UP 1
#define GPIO_PULL_DOWN 2
/* Helpers for GPIO library functions */
#define GPIO_SETTING_MASK(n) (0x3u << GPIO_SETTING_LSB(n))
#define GPIO_SETTING_LSB(n) ((n)*2)
#define GPIO_AF_MASK(n) (0xFu << GPIO_AF_LSB(n))
#define GPIO_AF_LSB(n) ((n)*4)
/* GPIO function attributes */
#define GPIO_F_MODE(n) (((n) & 0x3) << 0)
#define GPIO_F_TYPE(n) (((n) & 0x1) << 2)
#define GPIO_F_SPEED(n) (((n) & 0x3) << 3)
#define GPIO_F_PULL(n) (((n) & 0x3) << 5)
#define GPIO_F_AFNUM(n) (((n) & 0xf) << 7)
#define GPIO_F_INITLEVEL(n) (((n) & 0x1) << 11)
#define GPIO_F_GETMODE(x) (((x) >> 0) & 0x3)
#define GPIO_F_GETTYPE(x) (((x) >> 2) & 0x1)
#define GPIO_F_GETSPEED(x) (((x) >> 3) & 0x3)
#define GPIO_F_GETPULL(x) (((x) >> 5) & 0x3)
#define GPIO_F_GETAFNUM(x) (((x) >> 7) & 0xf)
#define GPIO_F_GETINITLEVEL(x) (((x) >> 11) & 0x1)
#define GPIOF_ANALOG() \
(GPIO_F_MODE(GPIO_MODE_ANALOG) | \
GPIO_F_TYPE(GPIO_TYPE_PUSH_PULL) | \
GPIO_F_SPEED(GPIO_SPEED_LOW) | \
GPIO_F_PULL(GPIO_PULL_DISABLED))
#define GPIOF_INPUT(pull) \
(GPIO_F_MODE(GPIO_MODE_INPUT) | \
GPIO_F_PULL(pull))
#define GPIOF_OUTPUT(initlevel, type, speed, pull) \
(GPIO_F_MODE(GPIO_MODE_OUTPUT) | \
GPIO_F_INITLEVEL(initlevel) | \
GPIO_F_TYPE(type) | \
GPIO_F_SPEED(speed) | \
GPIO_F_PULL(pull))
#define GPIOF_FUNCTION(afnum, type, speed, pull) \
(GPIO_F_MODE(GPIO_MODE_FUNCTION) | \
GPIO_F_AFNUM(afnum) | \
GPIO_F_TYPE(type) | \
GPIO_F_SPEED(speed) | \
GPIO_F_PULL(pull))
struct gpio_setting
{
uint16_t gpio;
uint16_t func;
};
struct pingroup_setting
{
int port;
uint16_t pins;
uint16_t func;
};
#define STM_DEFGPIO(_gpio, _func) \
{.gpio = (_gpio), .func = (_func)}
#define STM_DEFPINS(_port, _pins, _func) \
{.port = (_port), .pins = (_pins), .func = (_func)}
/*
* Configure a single GPIO or multiple GPIOs on one port.
* 'confbits' describe the configuration to apply: see GPIO_F macros above.
*/
void gpio_configure_single(int gpio, int confbits);
void gpio_configure_port(int port, uint16_t mask, int confbits);
/*
* Configure multiple GPIO ports according to a list of settings.
*
* Identical to calling gpio_configure_single() with each GPIO setting
* and gpio_configure_port() with each pingroup setting. It is mainly
* to make the organization of the initial GPIO configuration tidier
* and intended to be called from gpio_init().
*/
void gpio_configure_all(
const struct gpio_setting *gpios, size_t ngpios,
const struct pingroup_setting *pingroups, size_t ngroups) INIT_ATTR;
static inline void gpio_set_mode(int gpio, int mode)
{
int port = GPION_PORT(gpio);
int pin = GPION_PIN(gpio);
uint32_t val = REG_GPIO_MODER(port);
uint32_t mask = GPIO_SETTING_MASK(pin);
val &= ~mask;
val |= (mode << GPIO_SETTING_LSB(pin)) & mask;
REG_GPIO_MODER(port) = val;
}
static inline void gpio_set_type(int gpio, int type)
{
int port = GPION_PORT(gpio);
int pin = GPION_PIN(gpio);
uint32_t val = REG_GPIO_OTYPER(port);
uint32_t mask = BIT_N(pin);
val &= ~mask;
val |= (type << pin) & mask;
REG_GPIO_OTYPER(port) = val;
}
static inline void gpio_set_speed(int gpio, int speed)
{
int port = GPION_PORT(gpio);
int pin = GPION_PIN(gpio);
uint32_t val = REG_GPIO_OSPEEDR(port);
uint32_t mask = GPIO_SETTING_MASK(pin);
val &= ~mask;
val |= (speed << GPIO_SETTING_LSB(pin)) & mask;
REG_GPIO_OSPEEDR(port) = val;
}
static inline void gpio_set_pull(int gpio, int pull)
{
int port = GPION_PORT(gpio);
int pin = GPION_PIN(gpio);
uint32_t val = REG_GPIO_PUPDR(port);
uint32_t mask = GPIO_SETTING_MASK(pin);
val &= ~mask;
val |= (pull << GPIO_SETTING_LSB(pin)) & mask;
REG_GPIO_PUPDR(port) = val;
}
static inline void gpio_set_function(int gpio, int func)
{
int port = GPION_PORT(gpio);
int pin = GPION_PIN(gpio);
volatile uint32_t *addr = &REG_GPIO_AFRL(port);
if (pin >= 8) {
addr += &REG_GPIO_AFRH(0) - &REG_GPIO_AFRL(0);
pin -= 8;
}
uint32_t val = *addr;
uint32_t mask = GPIO_AF_MASK(pin);
val &= ~mask;
val |= (func << GPIO_AF_LSB(pin)) & mask;
*addr = val;
}
static inline int gpio_get_level(int gpio)
{
int port = GPION_PORT(gpio);
int pin = GPION_PIN(gpio);
return REG_GPIO_IDR(port) & BIT_N(pin);
}
static inline void gpio_set_level(int gpio, int value)
{
int port = GPION_PORT(gpio);
int pin = GPION_PIN(gpio);
if (value)
REG_GPIO_BSRR(port) = BIT_N(pin);
else
REG_GPIO_BSRR(port) = BIT_N(pin + 16);
}
#endif /* __GPIO_STM32_H__ */

View file

@ -0,0 +1,25 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 by Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "i2c.h"
void i2c_init(void)
{
}

View file

@ -0,0 +1,32 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 by Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
/*
* NOTE: This file must be included from system-arm-micro.c!
*/
void dma_irq_handler(void) ATTR_IRQ_HANDLER;
void i2c_irq_handler(void) ATTR_IRQ_HANDLER;
void lcdc_irq_handler(void) ATTR_IRQ_HANDLER;
void otg_hs_irq_handler(void) ATTR_IRQ_HANDLER;
void sai_irq_handler(void) ATTR_IRQ_HANDLER;
void sdmmc_irq_handler(void) ATTR_IRQ_HANDLER;
void spi_irq_handler(void) ATTR_IRQ_HANDLER;

View file

@ -0,0 +1,85 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "pcm.h"
#include "pcm-internal.h"
void pcm_dma_apply_settings(void)
{
}
void pcm_play_dma_init(void)
{
}
void pcm_play_dma_postinit(void)
{
}
void pcm_play_dma_start(const void *addr, size_t size)
{
(void)addr;
(void)size;
}
void pcm_play_dma_stop(void)
{
}
void pcm_play_lock(void)
{
}
void pcm_play_unlock(void)
{
}
#ifdef HAVE_RECORDING
void pcm_rec_dma_init(void)
{
}
void pcm_rec_dma_close(void)
{
}
void pcm_rec_dma_start(void *addr, size_t size)
{
(void)addr;
(void)size;
}
void pcm_rec_dma_stop(void)
{
}
void pcm_rec_lock(void)
{
}
void pcm_rec_unlock(void)
{
}
const void *pcm_rec_dma_get_peak_buffer(void)
{
return NULL;
}
#endif

View file

@ -0,0 +1,81 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 by Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "storage.h"
#include "sdmmc.h"
#include "sd.h"
int sd_init(void)
{
return 0;
}
void sd_enable(bool on)
{
(void)on;
}
int sd_event(long id, intptr_t data)
{
return storage_event_default_handler(id, data, 0, STORAGE_SD);
}
long sd_last_disk_activity(void)
{
return 0;
}
bool sd_present(IF_MD_NONVOID(int drive))
{
IF_MD((void)drive);
return true;
}
bool sd_removable(IF_MD_NONVOID(int card_no))
{
IF_MD((void)card_no);
return true;
}
tCardInfo *card_get_info_target(int card_no)
{
(void)card_no;
static tCardInfo null_info = {0};
return &null_info;
}
int sd_read_sectors(IF_MD(int drive,) sector_t start, int count, void *buf)
{
IF_MD((void)drive);
(void)start;
(void)count;
(void)buf;
return -1;
}
int sd_write_sectors(IF_MD(int drive,) sector_t start, int count, const void *buf)
{
IF_MD((void)drive);
(void)start;
(void)count;
(void)buf;
return -1;
}

View file

@ -0,0 +1,302 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "spi-stm32h7.h"
#include "stm32h7/spi.h"
/*
* Align the max transfer size to ensure it will always be a multiple
* of 32 bits. This is necessary because an unaligned TSIZE will cause
* some data written to the FIFO to be ignored -- this is OK and the
* intended behavior when it happens at the end of the transfer. But
* we don't want this to happen when TSER > 0 when we're still in the
* middle of the transfer, as it will throw away valid data.
*/
#define TSIZE_MAX \
ALIGN_DOWN(st_vreadf(BM_SPI_CR2_TSIZE, SPI_CR2, TSIZE), sizeof(uint32_t))
static struct stm_spi *spi_map[STM_SPI_COUNT];
static const uint32_t spi_addr[STM_SPI_COUNT] INITDATA_ATTR = {
[STM_SPI1] = STA_SPI1,
[STM_SPI2] = STA_SPI2,
[STM_SPI3] = STA_SPI3,
[STM_SPI4] = STA_SPI4,
[STM_SPI5] = STA_SPI5,
[STM_SPI6] = STA_SPI6,
};
static void stm_spi_set_cs(struct stm_spi *spi, bool enable)
{
if (spi->set_cs)
spi->set_cs(spi, enable);
st_writelf(spi->regs, SPI_CR1, SSI(1));
}
static void stm_spi_enable(struct stm_spi *spi, bool hd_tx, size_t size)
{
size_t tsize = size / spi->frame_size;
size_t tser = 0;
size_t left = 0;
if (tsize > TSIZE_MAX)
{
tser = tsize - TSIZE_MAX;
tsize = TSIZE_MAX;
if (tser > TSIZE_MAX)
{
left = tser - TSIZE_MAX;
tser = TSIZE_MAX;
}
}
/*
* Save number of bytes left for next TSER load, tracked
* separately from the overall transfer size because the
* timing of the SPI_SR.TSERF interrupt isn't clear. We'll
* decrement this by TSIZE_MAX whenever we load TSER in the
* middle of a transfer.
*/
spi->tser_left = left;
/* TSIZE must be programmed before setting SPE. */
st_overwritelf(spi->regs, SPI_CR2, TSIZE(tsize), TSER(tser));
st_writelf(spi->regs, SPI_CR1, HDDIR(hd_tx), SPE(1));
}
static void stm_spi_disable(struct stm_spi *spi)
{
st_overwritelf(spi->regs, SPI_IFCR, TSERFC(1), TXTFC(1), EOTC(1));
st_writelf(spi->regs, SPI_CR1, SPE(0));
}
static void stm_spi_start(struct stm_spi *spi)
{
st_writelf(spi->regs, SPI_CR1, CSTART(1));
}
static uint32_t stm_spi_pack(const void **bufp, size_t *sizep)
{
const uint8_t *buf = *bufp;
uint32_t data = 0;
if (LIKELY(*sizep >= sizeof(uint32_t)))
{
data = load_le32(buf);
*bufp += sizeof(uint32_t);
*sizep -= sizeof(uint32_t);
return data;
}
switch (*sizep) {
case 3: data |= buf[2] << 16; /* fallthrough */
case 2: data |= buf[1] << 8; /* fallthrough */
case 1: data |= buf[0]; /* fallthrough */
}
*bufp += *sizep;
*sizep = 0;
return data;
}
static void stm_spi_unpack(void **bufp, size_t *sizep, uint32_t data)
{
uint8_t *buf = *bufp;
if (LIKELY(*sizep >= sizeof(uint32_t)))
{
store_le32(buf, data);
*bufp += sizeof(uint32_t);
*sizep -= sizeof(uint32_t);
return;
}
switch (*sizep) {
case 3: buf[2] = (data >> 16) & 0xff; /* fallthrough */
case 2: buf[1] = (data >> 8) & 0xff; /* fallthrough */
case 1: buf[0] = data & 0xff; /* fallthrough */
}
*bufp += *sizep;
*sizep = 0;
}
void stm_spi_init(struct stm_spi *spi,
const struct stm_spi_config *config)
{
uint32_t ftlevel;
spi->regs = spi_addr[config->num];
spi->mode = config->mode;
spi->set_cs = config->set_cs;
/* Set FIFO level based on 32-bit packed writes */
if (config->frame_bits > 16)
{
spi->frame_size = 4;
ftlevel = 1;
}
else if (config->frame_bits > 8)
{
spi->frame_size = 2;
ftlevel = 2;
}
else
{
spi->frame_size = 1;
ftlevel = 4;
}
/*
* SPI1-3 have a 16-byte FIFO, SPI4-6 have only 8 bytes.
* So we can double the threshold setting for SPI1-3.
* (Maximum allowable threshold is 1/2 the FIFO size.)
*/
if (config->num <= STM_SPI3)
ftlevel *= 2;
/* TODO: allow setting MBR here */
st_writelf(spi->regs, SPI_CFG1,
MBR(0),
CRCEN(0),
CRCSIZE(7),
TXDMAEN(0),
RXDMAEN(0),
UDRDET(0),
UDRCFG(0),
FTHLV(ftlevel - 1),
DSIZE(config->frame_bits - 1));
st_writelf(spi->regs, SPI_CFG2,
AFCNTR(1),
SSM(config->hw_cs_input ? 0 : 1),
SSOE(config->hw_cs_output),
SSIOP(config->hw_cs_polarity),
CPOL(config->cpol),
CPHA(config->cpha),
LSBFIRST(config->send_lsb_first),
COMM(config->mode),
SP(config->proto),
MASTER(1),
SSOM(0),
IOSWP(config->swap_mosi_miso),
MIDI(0),
MSSI(0));
spi_map[config->num] = spi;
}
int stm_spi_xfer(struct stm_spi *spi, size_t size,
const void *tx_buf, void *rx_buf)
{
bool hd_tx = false;
size_t size_tx = tx_buf ? size : 0;
size_t size_rx = rx_buf ? size : 0;
/* Ignore zero-length transfers. */
if (size == 0)
return 0;
/* Reject nonsensical transfers (no data or less than 1 frame) */
if (!tx_buf && !rx_buf)
return -1;
if (size < spi->frame_size)
return -1;
if (spi->mode == STM_SPIMODE_HALF_DUPLEX)
{
/* Half duplex mode can't support duplex transfers. */
if (tx_buf && rx_buf)
return -1;
if (tx_buf)
hd_tx = true;
}
stm_spi_set_cs(spi, true);
stm_spi_enable(spi, hd_tx, size);
stm_spi_start(spi);
while (size_tx > 0 || size_rx > 0)
{
uint32_t sr = st_readl(spi->regs, SPI_SR);
/*
* Handle continuation of large transfers
*
* TODO - something is not right with this code
*/
if (spi->tser_left > 0 && st_vreadf(sr, SPI_SR, TSERF))
{
if (spi->tser_left < TSIZE_MAX)
{
st_writelf(spi->regs, SPI_CR2, TSER(spi->tser_left));
spi->tser_left = 0;
}
else
{
st_writelf(spi->regs, SPI_CR2, TSER(TSIZE_MAX));
spi->tser_left -= TSIZE_MAX;
}
}
/* Handle FIFO write */
if (size_tx > 0 && st_vreadf(sr, SPI_SR, TXP))
{
uint32_t data = stm_spi_pack(&tx_buf, &size_tx);
st_writel(spi->regs, SPI_TXDR32, data);
}
/*
* Handle FIFO read. Since RXP is set only if the FIFO level
* exceeds the threshold we can't rely on it at the end of a
* transfer, and must check EOT as well.
*/
if (size_rx > 0 &&
(st_vreadf(sr, SPI_SR, RXP) || st_vreadf(sr, SPI_SR, EOT)))
{
uint32_t data = st_readl(spi->regs, SPI_RXDR32);
stm_spi_unpack(&rx_buf, &size_rx, data);
}
}
/*
* Errata 2.22.2: Master data transfer stall at system clock much faster than SCK
* Errata 2.22.6: Truncation of SPI output signals after EOT event
*
* For 2.22.2 we need to wait at least 1 SCK cycle before starting the
* next transaction. For 2.22.6, waiting 1/2 SCK cycle should be enough
* since the EOT event is raised on a clock edge.
*
* TODO: calculate this delay time. doesn't seem to match assumptions above
*/
udelay(5);
stm_spi_disable(spi);
stm_spi_set_cs(spi, false);
return 0;
}
void spi_irq_handler(void)
{
while (1);
}

View file

@ -0,0 +1,101 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef __SPI_STM32H743_H__
#define __SPI_STM32H743_H__
#include "system.h"
#include <stddef.h>
struct stm_spi;
enum stm_spi_num
{
STM_SPI1,
STM_SPI2,
STM_SPI3,
STM_SPI4,
STM_SPI5,
STM_SPI6,
STM_SPI_COUNT,
};
/* Must match the SPI_CFG2.COMM register */
enum stm_spi_mode
{
STM_SPIMODE_DUPLEX,
STM_SPIMODE_TXONLY,
STM_SPIMODE_RXONLY,
STM_SPIMODE_HALF_DUPLEX,
};
/* Must match the SPI_CFG2.SP register */
enum stm_spi_protocol
{
STM_SPIPROTO_MOTOROLA,
STM_SPIPROTO_TI,
};
typedef void (*stm_spi_set_cs_t) (struct stm_spi *spi, bool enable);
struct stm_spi_config
{
enum stm_spi_num num;
enum stm_spi_mode mode;
enum stm_spi_protocol proto;
stm_spi_set_cs_t set_cs;
uint32_t frame_bits: 6;
uint32_t cpha: 1;
uint32_t cpol: 1;
uint32_t hw_cs_input: 1;
uint32_t hw_cs_output: 1;
uint32_t hw_cs_polarity: 1;
uint32_t send_lsb_first: 1;
uint32_t swap_mosi_miso: 1;
};
struct stm_spi
{
uint32_t regs;
enum stm_spi_mode mode;
stm_spi_set_cs_t set_cs;
uint32_t frame_size;
size_t tser_left;
};
void stm_spi_init(struct stm_spi *spi,
const struct stm_spi_config *config) INIT_ATTR;
int stm_spi_xfer(struct stm_spi *spi, size_t size,
const void *tx_buf, void *rx_buf);
static inline int stm_spi_transmit(struct stm_spi *spi,
const void *buf, size_t size)
{
return stm_spi_xfer(spi, size, buf, NULL);
}
static inline int stm_spi_receive(struct stm_spi *spi,
void *buf, size_t size)
{
return stm_spi_xfer(spi, size, NULL, buf);
}
#endif /* __SPI_STM32H743_H__ */

View file

@ -0,0 +1,261 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "system.h"
#include "tick.h"
#include "gpio-stm32h7.h"
#include "cortex-m/scb.h"
#include "cortex-m/systick.h"
#include "stm32h7/rcc.h"
#include "stm32h7/pwr.h"
#include "stm32h7/syscfg.h"
#include "stm32h7/flash.h"
/* EXT timer is 1/8th of CPU clock */
#define SYSTICK_FREQ (CPU_FREQ / 8)
#define SYSTICK_PER_MS (SYSTICK_FREQ / 1000)
#define SYSTICK_PER_US (SYSTICK_FREQ / 1000000)
/* Max delay is limited by kernel tick interval + safety margin */
#define SYSTICK_DELAY_MAX_US (1000000 / HZ / 2)
#define SYSTICK_DELAY_MAX_MS (SYSTICK_DELAY_MAX_US / 1000)
/* Flag to use VOS0 */
#define STM32H743_USE_VOS0 (CPU_FREQ > 400000000)
/* Base address of vector table */
extern char __vectors_arm[];
static void systick_init(unsigned int interval_in_ms)
{
cm_writef(SYSTICK_RVR, VALUE(SYSTICK_PER_MS * interval_in_ms - 1));
cm_writef(SYSTICK_CVR, VALUE(0));
cm_writef(SYSTICK_CSR, CLKSOURCE_V(EXT), ENABLE(1));
}
static void stm_enable_caches(void)
{
__discard_idcache();
cm_writef(SCB_CCR, IC(1), DC(1));
arm_dsb();
arm_isb();
}
static void stm_init_hse(void)
{
st_writef(RCC_CR, HSEON(1));
while (!st_readf(RCC_CR, HSERDY));
}
static void stm_init_pll(void)
{
/* TODO - this should be determined by the target in some way. */
/* Select HSE/4 input for PLL1 (6 MHz) */
st_writef(RCC_PLLCKSELR,
PLLSRC_V(HSE),
DIVM1(4),
DIVM2(0),
DIVM3(0));
/* Enable PLL1P and PLL1Q */
st_writef(RCC_PLLCFGR,
DIVP1EN(1),
DIVQ1EN(1),
DIVR1EN(0),
DIVP2EN(0),
DIVQ2EN(0),
DIVR2EN(0),
DIVP3EN(0),
DIVQ3EN(0),
DIVR3EN(0),
PLL1RGE_V(4_8MHz),
PLL1VCOSEL_V(WIDE));
st_writef(RCC_PLL1DIVR,
DIVN(80 - 1), /* 6 * 80 = 480 MHz */
DIVP(1 - 1), /* 480 / 1 = 480 MHz */
DIVQ(8 - 1), /* 480 / 8 = 60 MHz */
DIVR(1 - 1));
st_writef(RCC_CR, PLL1ON(1));
while (!st_readf(RCC_CR, PLL1RDY));
}
static void stm_init_vos(void)
{
st_writef(PWR_D3CR, VOS_V(VOS1));
while (!st_readf(PWR_D3CR, VOSRDY));
if (STM32H743_USE_VOS0)
{
st_writef(RCC_APB4ENR, SYSCFGEN(1));
/* Set ODEN bit to enter VOS0 */
st_writef(SYSCFG_PWRCFG, ODEN(1));
while (!st_readf(PWR_D3CR, VOSRDY));
st_writef(RCC_APB4ENR, SYSCFGEN(0));
}
}
static void stm_init_system_clock(void)
{
/* Enable HCLK /2 divider (CPU is at 480 MHz, HCLK limit is 240 MHz) */
st_writef(RCC_D1CFGR, HPRE(8));
while (st_readf(RCC_D1CFGR, HPRE) != 8);
/* Enable ABP /2 dividers (HCLK/2, APB limit is 120 MHz) */
st_writef(RCC_D1CFGR, D1PPRE(4));
st_writef(RCC_D2CFGR, D2PPRE1(4), D2PPRE2(4));
st_writef(RCC_D3CFGR, D3PPRE(4));
/* Switch to PLL1P system clock source */
st_writef(RCC_CFGR, SW_V(PLL1P));
while (st_readf(RCC_CFGR, SWS) != BV_RCC_CFGR_SW__PLL1P);
/* Reduce flash access latency */
st_writef(FLASH_ACR, LATENCY(4), WRHIGHFREQ(2));
while (st_readf(FLASH_ACR, LATENCY) != 4);
}
static void stm_init_lse(void)
{
/*
* Skip if LSE and RTC are already enabled.
*/
if (st_readf(RCC_BDCR, LSERDY) &&
st_readf(RCC_BDCR, RTCEN))
return;
/*
* Enable LSE and set it as RTC clock source,
* then re-enable backup domain write protection.
*/
st_writef(PWR_CR1, DBP(1));
/* Reset backup domain */
st_writef(RCC_BDCR, BDRST(1));
st_writef(RCC_BDCR, BDRST(0));
st_writef(RCC_BDCR, LSEON(1), LSEDRV(3));
while (!st_readf(RCC_BDCR, LSERDY));
st_writef(RCC_BDCR, RTCEN(1), RTCSEL_V(LSE));
st_writef(PWR_CR1, DBP(0));
}
void system_init(void)
{
/* Ensure IRQs are disabled and set vector table address */
disable_irq();
cm_write(SCB_VTOR, (uint32_t)__vectors_arm);
/* Enable CPU caches */
stm_enable_caches();
/* Start up clocks and get running at max CPU frequency */
stm_init_hse();
stm_init_pll();
stm_init_vos();
stm_init_system_clock();
/* Start up LSE and RTC if necessary so backup domain works */
stm_init_lse();
/* TODO: move this */
systick_init(1000/HZ);
/* Call target-specific initialization */
gpio_init();
fmc_init();
}
void tick_start(unsigned int interval_in_ms)
{
(void)interval_in_ms;
cm_writef(SYSTICK_CSR, TICKINT(1));
}
void systick_handler(void)
{
call_tick_tasks();
}
/*
* NOTE: This assumes that the CPU cannot be reclocked during an interrupt.
* If that happens, the systick interval and reload value would be modified
* to maintain the kernel tick interval and the code here will break.
*/
static void __udelay(uint32_t us)
{
uint32_t start = cm_readf(SYSTICK_CVR, VALUE);
uint32_t max = cm_readf(SYSTICK_RVR, VALUE);
uint32_t delay = us * SYSTICK_PER_US;
for (;;)
{
uint32_t value = cm_readf(SYSTICK_CVR, VALUE);
uint32_t diff = start - value;
if (value > start)
diff += max;
if (diff >= delay)
break;
}
}
void udelay(uint32_t us)
{
while (us > SYSTICK_DELAY_MAX_US)
{
__udelay(SYSTICK_DELAY_MAX_US);
us -= SYSTICK_DELAY_MAX_US;
}
__udelay(us);
}
void mdelay(uint32_t ms)
{
while (ms > SYSTICK_DELAY_MAX_MS)
{
__udelay(SYSTICK_DELAY_MAX_MS * 1000);
ms -= SYSTICK_DELAY_MAX_MS;
}
__udelay(ms * 1000);
}
void system_exception_wait(void)
{
while (1);
}
int system_memory_guard(int newmode)
{
/* TODO -- maybe use MPU here to give some basic protection */
(void)newmode;
return MEMGUARD_NONE;
}

View file

@ -0,0 +1,34 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef __STM32_SYSTEM_TARGET_H__
#define __STM32_SYSTEM_TARGET_H__
#include "system-arm.h"
#include "cpucache-armv7m.h"
/* Implemented by the target -- can be a no-op if not needed */
void gpio_init(void) INIT_ATTR;
void fmc_init(void) INIT_ATTR;
void udelay(uint32_t us);
void mdelay(uint32_t ms);
#endif /* __STM32_SYSTEM_TARGET_H__ */

View file

@ -0,0 +1,37 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "timer.h"
bool timer_set(long cycles, bool start)
{
(void)cycles;
(void)start;
return false;
}
bool timer_start(void)
{
return false;
}
void timer_stop(void)
{
}

View file

@ -0,0 +1,86 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "system.h"
#include "usb.h"
#include "usb_core.h"
#include "usb_drv.h"
#include "usb-designware.h"
const struct usb_dw_config usb_dw_config = {
.phytype = DWC_PHYTYPE_ULPI_SDR,
/*
* Available FIFO memory: 1024 words (TODO: check this)
* Number of endpoints: 9
* Max packet size: 512 bytes
*/
.rx_fifosz = 224, /* shared RxFIFO */
.nptx_fifosz = 32, /* only used for EP0 IN */
.ptx_fifosz = 192, /* room for 4 IN EPs */
#ifndef USB_DW_ARCH_SLAVE
.ahb_burst_len = HBSTLEN_INCR8,
/* Disable Rx FIFO thresholding. It appears to cause problems,
* apparently a known issue -- Synopsys recommends disabling it
* because it can cause issues during certain error conditions.
*/
.ahb_threshold = 0,
#else
.disable_double_buffering = false,
#endif
};
void usb_enable(bool on)
{
if (on)
usb_core_init();
else
usb_core_exit();
}
void usb_init_device(void)
{
/* TODO - this is highly target specific */
}
int usb_detect(void)
{
return USB_EXTRACTED;
}
void usb_dw_target_enable_clocks(void)
{
}
void usb_dw_target_disable_clocks(void)
{
}
void usb_dw_target_enable_irq(void)
{
}
void usb_dw_target_disable_irq(void)
{
}
void usb_dw_target_clear_irq(void)
{
}

View file

@ -0,0 +1,179 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 by Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "config.h"
.syntax unified
.text
.section .vectors.platform,"ax",%progbits
.global __vectors_platform
__vectors_platform:
.word UIE /* [ 0] */
.word UIE /* [ 1] */
.word UIE /* [ 2] */
.word UIE /* [ 3] */
.word UIE /* [ 4] */
.word UIE /* [ 5] */
.word UIE /* [ 6] */
.word UIE /* [ 7] */
.word UIE /* [ 8] */
.word UIE /* [ 9] */
.word UIE /* [ 10] */
.word dma_irq_handler /* [ 11] DMA1 Stream0 */
.word dma_irq_handler /* [ 12] DMA1 Stream1 */
.word dma_irq_handler /* [ 13] DMA1 Stream2 */
.word dma_irq_handler /* [ 14] DMA1 Stream3 */
.word dma_irq_handler /* [ 15] DMA1 Stream4 */
.word dma_irq_handler /* [ 16] DMA1 Stream5 */
.word dma_irq_handler /* [ 17] DMA1 Stream6 */
.word UIE /* [ 18] */
.word UIE /* [ 19] */
.word UIE /* [ 20] */
.word UIE /* [ 21] */
.word UIE /* [ 22] */
.word UIE /* [ 23] */
.word UIE /* [ 24] */
.word UIE /* [ 25] */
.word UIE /* [ 26] */
.word UIE /* [ 27] */
.word UIE /* [ 28] */
.word UIE /* [ 29] */
.word UIE /* [ 30] */
.word i2c_irq_handler /* [ 31] I2C1 event */
.word i2c_irq_handler /* [ 32] I2C1 error */
.word i2c_irq_handler /* [ 33] I2C2 event */
.word i2c_irq_handler /* [ 34] I2C2 error */
.word spi_irq_handler /* [ 35] SPI1 */
.word spi_irq_handler /* [ 36] SPI2 */
.word UIE /* [ 37] */
.word UIE /* [ 38] */
.word UIE /* [ 39] */
.word UIE /* [ 40] */
.word UIE /* [ 41] */
.word UIE /* [ 42] */
.word UIE /* [ 43] */
.word UIE /* [ 44] */
.word UIE /* [ 45] */
.word UIE /* [ 46] */
.word dma_irq_handler /* [ 47] DMA1 Stream7 */
.word UIE /* [ 48] */
.word sdmmc_irq_handler /* [ 49] SDMMC1 */
.word UIE /* [ 50] */
.word spi_irq_handler /* [ 51] SPI3 */
.word UIE /* [ 52] */
.word UIE /* [ 53] */
.word UIE /* [ 54] */
.word UIE /* [ 55] */
.word dma_irq_handler /* [ 56] DMA2 Stream0 */
.word dma_irq_handler /* [ 57] DMA2 Stream1 */
.word dma_irq_handler /* [ 58] DMA2 Stream2 */
.word dma_irq_handler /* [ 59] DMA2 Stream3 */
.word dma_irq_handler /* [ 60] DMA2 Stream4 */
.word UIE /* [ 61] */
.word UIE /* [ 62] */
.word UIE /* [ 63] */
.word UIE /* [ 64] */
.word UIE /* [ 65] */
.word UIE /* [ 66] */
.word UIE /* [ 67] */
.word dma_irq_handler /* [ 68] DMA2 Stream5 */
.word dma_irq_handler /* [ 69] DMA2 Stream6 */
.word dma_irq_handler /* [ 70] DMA2 Stream7 */
.word UIE /* [ 71] */
.word i2c_irq_handler /* [ 72] I2C3 event */
.word i2c_irq_handler /* [ 73] I2C3 error */
.word UIE /* [ 74] */
.word UIE /* [ 75] */
.word UIE /* [ 76] */
.word otg_hs_irq_handler /* [ 77] OTG HS */
.word UIE /* [ 78] */
.word UIE /* [ 79] */
.word UIE /* [ 80] */
.word UIE /* [ 81] */
.word UIE /* [ 82] */
.word UIE /* [ 83] */
.word spi_irq_handler /* [ 84] SPI4 */
.word spi_irq_handler /* [ 85] SPI5 */
.word spi_irq_handler /* [ 86] SPI6 */
.word sai_irq_handler /* [ 87] SAI1 */
.word lcdc_irq_handler /* [ 88] LCDC */
.word lcdc_irq_handler /* [ 89] LCDC error */
.word dma_irq_handler /* [ 90] DMA2D */
.word sai_irq_handler /* [ 91] SAI2 */
.word UIE /* [ 92] */
.word UIE /* [ 93] */
.word UIE /* [ 94] */
.word i2c_irq_handler /* [ 95] I2C4 event */
.word i2c_irq_handler /* [ 96] I2C4 error */
.word UIE /* [ 97] */
.word UIE /* [ 98] */
.word UIE /* [ 99] */
.word UIE /* [100] */
.word UIE /* [101] */
.word dma_irq_handler /* [102] DMAMUX1 overrun */
.word UIE /* [103] */
.word UIE /* [104] */
.word UIE /* [105] */
.word UIE /* [106] */
.word UIE /* [107] */
.word UIE /* [108] */
.word UIE /* [109] */
.word UIE /* [110] */
.word UIE /* [111] */
.word UIE /* [112] */
.word UIE /* [113] */
.word sai_irq_handler /* [114] SAI3 */
.word UIE /* [115] */
.word UIE /* [116] */
.word UIE /* [117] */
.word UIE /* [118] */
.word UIE /* [119] */
.word UIE /* [120] */
.word UIE /* [121] */
.word dma_irq_handler /* [122] MDMA */
.word UIE /* [123] */
.word sdmmc_irq_handler /* [124] SDMMC2 */
.word UIE /* [125] */
.word UIE /* [126] */
.word UIE /* [127] */
.word dma_irq_handler /* [128] DMAMUX2 overrun */
.word dma_irq_handler /* [129] BDMA channel 0 */
.word dma_irq_handler /* [130] BDMA channel 1 */
.word dma_irq_handler /* [131] BDMA channel 2 */
.word dma_irq_handler /* [132] BDMA channel 3 */
.word dma_irq_handler /* [133] BDMA channel 4 */
.word dma_irq_handler /* [134] BDMA channel 5 */
.word dma_irq_handler /* [135] BDMA channel 6 */
.word dma_irq_handler /* [136] BDMA channel 7 */
.word UIE /* [137] */
.word UIE /* [138] */
.word UIE /* [139] */
.word UIE /* [140] */
.word UIE /* [141] */
.word UIE /* [142] */
.word UIE /* [143] */
.word UIE /* [144] */
.word UIE /* [145] */
.word sai_irq_handler /* [146] SAI4 */
.word UIE /* [147] */
.word UIE /* [148] */
.word UIE /* [149] */

View file

@ -64,3 +64,13 @@ void debugmonitor_handler(void) ATTR_IRQ_HANDLER;
#if ARCH_VERSION >= 8
void securefault_handler(void) ATTR_IRQ_HANDLER;
#endif
/*
* The weak alias attribute above only works if the alias is defined
* in the same translation unit. So each platform should declare its
* IRQ handlers in a file and include it below, so that unused ones
* can be aliased to UIE.
*/
#if CONFIG_CPU == STM32H743
# include "irqhandlers-stm32h743.c"
#endif

34
tools/configure vendored
View file

@ -609,6 +609,13 @@ arm7ejscc () {
endian="little"
}
armcortexm7cc () {
findarmgcc
GCCOPTS="$CCOPTS -march=armv7e-m -mcpu=cortex-m7"
GCCOPTIMIZE="-fomit-frame-pointer"
endian="little"
}
mipselcc () {
prefixtools mipsel-elf-
# mips is predefined, but we want it for paths. use __mips instead
@ -1724,6 +1731,8 @@ cat <<EOF
260) Q1 243) X20 (hw3 bl only)
249) Eros Q / K native
(hw4 bl only)
==Echo project==
270) Echo R1 (WIP)
EOF
buildfor=`input`;
@ -4339,6 +4348,31 @@ fi
t_model="shanlingq1"
;;
270|echor1)
target_id=119
modelname="echor1"
target="ECHO_R1"
memory=32
armcortexm7cc
appextra="recorder:gui"
plugins="no"
tool="cp "
boottool="cp "
output="rockbox.echo"
bootoutput="bootloader.echo"
sysfontbl="16-Terminus"
sysfont="14-Rockbox-Mix"
# toolset is the tools within the tools directory that we build for
# this particular target.
toolset="$genericbitmaptools scramble uclpack"
bmp2rb_mono="$rootdir/tools/bmp2rb -f 0"
bmp2rb_native="$rootdir/tools/bmp2rb -f 4"
# architecture, manufacturer and model for the target-tree build
t_cpu="arm"
t_manufacturer="stm32"
t_model="echoplayer"
;;
*)
echo "Please select a supported target platform!"
exit 7