| /* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| /* Interrupt based USART TX driver for STM32 */ |
| |
| #include "usart.h" |
| |
| #include "common.h" |
| #include "registers.h" |
| #include "system.h" |
| #include "util.h" |
| |
| static void usart_tx_init(struct usart_config const *config) |
| { |
| intptr_t base = config->hw->base; |
| |
| STM32_USART_CR1(base) |= STM32_USART_CR1_TE; |
| } |
| |
| static void usart_written(struct consumer const *consumer, size_t count) |
| { |
| struct usart_config const *config = |
| DOWNCAST(consumer, struct usart_config, consumer); |
| |
| /* |
| * Enable USART interrupt. This causes the USART interrupt handler to |
| * start fetching from the TX queue if it wasn't already. |
| */ |
| if (count) |
| STM32_USART_CR1(config->hw->base) |= STM32_USART_CR1_TXEIE; |
| } |
| |
| static void usart_tx_interrupt_handler(struct usart_config const *config) |
| { |
| intptr_t base = config->hw->base; |
| uint8_t byte; |
| |
| if (!(STM32_USART_SR(base) & STM32_USART_SR_TXE)) |
| return; |
| |
| if (queue_remove_unit(config->consumer.queue, &byte)) { |
| STM32_USART_TDR(base) = byte; |
| |
| /* |
| * Make sure the TXE interrupt is enabled and that we won't go |
| * into deep sleep. This invocation of the USART interrupt |
| * handler may have been manually triggered to start |
| * transmission. |
| */ |
| disable_sleep(SLEEP_MASK_UART); |
| |
| STM32_USART_CR1(base) |= STM32_USART_CR1_TXEIE; |
| } else { |
| /* |
| * The TX queue is empty, disable the TXE interrupt and enable |
| * deep sleep mode. The TXE interrupt will remain disabled |
| * until a write call happens. |
| */ |
| enable_sleep(SLEEP_MASK_UART); |
| |
| STM32_USART_CR1(base) &= ~STM32_USART_CR1_TXEIE; |
| } |
| } |
| |
| struct usart_tx const usart_tx_interrupt = { |
| .consumer_ops = { |
| .written = usart_written, |
| }, |
| |
| .init = usart_tx_init, |
| .interrupt = usart_tx_interrupt_handler, |
| .info = NULL, |
| }; |