|= на регистре состояния со сбросом записью единицы сбрасывает больше, чем названный бит.
#include <stdint.h>
#define UART_SR (*(volatile uint32_t *)0x40004000u)
#define SR_ORE (1u << 3) /* overrun, write 1 to clear */
void clear_overrun_wrong(void) { UART_SR |= SR_ORE; }
void clear_overrun(void) { UART_SR = SR_ORE; }
Флаг переполнения сбрасывается записью 1. Запись 0 ничего не делает. UART_SR |= SR_ORE сначала читает весь регистр. Каждый такой флаг, взведённый в этот момент, читается как 1 и записывается обратно как 1, так что сбрасываются все. Ошибка кадра, до которой обработчик ещё не добрался, пропадает вместе с ними.
На -O2 для Cortex-M неправильный вариант - это ldr, orr, str. Правильный - один str маски. volatile сохраняет оба доступа в программе. Что железо делает с записанными обратно битами, он не говорит.