/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <plat/gpio.h>
#include <plat/usart.h>
#include <plat/pwr.h>
#include <usart.h>
#include <gpio.h>
struct StmUsart {
volatile uint16_t SR;
uint8_t unused0[2];
volatile uint16_t DR;
uint8_t unused1[2];
volatile uint16_t BRR;
uint8_t unused2[2];
volatile uint16_t CR1;
uint8_t unused3[2];
volatile uint16_t CR2;
uint8_t unused4[2];
volatile uint16_t CR3;
uint8_t unused5[2];
volatile uint16_t GTPR;
uint8_t unused6[2];
};
static const uint32_t mUsartPorts[] = {
USART1_BASE,
USART2_BASE,
USART3_BASE,
UART4_BASE,
UART5_BASE,
USART6_BASE,
};
static const uint32_t mUsartPeriphs[] = {
PERIPH_APB2_USART1,
PERIPH_APB1_USART2,
PERIPH_APB1_USART3,
PERIPH_APB1_UART4,
PERIPH_APB1_UART5,
PERIPH_APB2_USART6,
};
static uint8_t mUsartBusses[] = {
PERIPH_BUS_APB2,
PERIPH_BUS_APB1,
PERIPH_BUS_APB1,
PERIPH_BUS_APB1,
PERIPH_BUS_APB1,
PERIPH_BUS_APB2,
};
static bool mUsartHasFlowControl[] = {
true,
true,
true,
false,
false,
true,
};
static enum StmGpioAltFunc mUsartAlt[] = {
GPIO_AF_USART1,
GPIO_AF_USART2,
GPIO_AF00,
GPIO_AF00,
GPIO_AF00,
GPIO_AF_USART6,
};
void usartOpen(struct usart* __restrict usart, UsartPort port,
uint32_t txGpioNum, uint32_t rxGpioNum,
uint32_t baud, UsartDataBitsCfg data_bits,
UsatStopBitsCfg stop_bits, UsartParityCfg parity,
UsartFlowControlCfg flow_control)
{
static const uint16_t stopBitsVals[] = {0x1000, 0x0000, 0x3000, 0x2000}; // indexed by UsatStopBitsCfg
static const uint16_t wordLengthVals[] = {0x0000, 0x1000}; // indexed by UsartDataBitsCfg
static const uint16_t parityVals[] = {0x0000, 0x0400, 0x0600}; // indexed by UsartParityCfg
static const uint16_t flowCtrlVals[] = {0x0000, 0x0100, 0x0200, 0x0300}; // indexed by UsartFlowControlCfg
struct StmUsart *block = (struct StmUsart*)mUsartPorts[usart->unit = --port];
uint32_t baseClk, div, intPart, fraPart;
/* configure tx/rx gpios */
usart->rx = gpioRequest(rxGpioNum); /* rx */
gpioConfigAlt(usart->rx, GPIO_SPEED_LOW, GPIO_PULL_UP, GPIO_OUT_PUSH_PULL, mUsartAlt[port]);
usart->tx = gpioRequest(txGpioNum); /* tx */
gpioConfigAlt(usart->tx, GPIO_SPEED_LOW, GPIO_PULL_UP, GPIO_OUT_PUSH_PULL, mUsartAlt[port]);
/* enable clock */
pwrUnitClock(mUsartBusses[port], mUsartPeriphs[port], true);
/* sanity checks */
if (!mUsartHasFlowControl[port])
flow_control = USART_FLOW_CONTROL_NONE;
/* basic config as required + oversample by 8, tx+rx on */
block->CR2 = (block->CR2 &~ 0x3000) | stopBitsVals[stop_bits];
block->CR1 = (block->CR1 &~ 0x1600) | wordLengthVals[data_bits] | parityVals[parity] | 0x800C;
block->CR3 = (block->CR3 &~ 0x0300) | flowCtrlVals[flow_control];
/* clocking calc */
baseClk = pwrGetBusSpeed(mUsartBusses[port]);
div = (baseClk * 25) / (baud * 2);
intPart = div / 100;
fraPart = div % 100;
/* clocking munging */
intPart = intPart << 4;
fraPart = ((fraPart * 8 + 50) / 100) & 7;
block->BRR = intPart | fraPart;
/* enable */
block->CR1 |= 0x2000;
}
void usartClose(const struct usart* __restrict usart)
{
struct StmUsart *block = (struct StmUsart*)mUsartPorts[usart->unit];
/* Disable USART */
block->CR1 &=~ 0x2000;
/* Disable USART clock */
pwrUnitClock(mUsartBusses[usart->unit], mUsartPeriphs[usart->unit], false);
/* Release gpios */
gpioRelease(usart->rx);
gpioRelease(usart->tx);
}
void usartPutchar(const struct usart* __restrict usart, char c)
{
struct StmUsart *block = (struct StmUsart*)mUsartPorts[usart->unit];
/* wait for ready */
while (!(block->SR & 0x0080));
/* send */
block->DR = (uint8_t)c;
}