1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
|
/*
* (C) Copyright 2013-2016 Freescale Semiconductor, Inc.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <watchdog.h>
#include <asm/io.h>
#include <serial.h>
#include <linux/compiler.h>
#include <asm/arch/imx-regs.h>
#include <asm/arch/clock.h>
#define US1_TDRE (1 << 7)
#define US1_RDRF (1 << 5)
#define UC2_TE (1 << 3)
#define LINCR1_INIT (1 << 0)
#define LINCR1_MME (1 << 4)
#define LINCR1_BF (1 << 7)
#define LINSR_LINS_INITMODE (0x00001000)
#define LINSR_LINS_MASK (0x0000F000)
#define UARTCR_UART (1 << 0)
#define UARTCR_WL0 (1 << 1)
#define UARTCR_PCE (1 << 2)
#define UARTCR_PC0 (1 << 3)
#define UARTCR_TXEN (1 << 4)
#define UARTCR_RXEN (1 << 5)
#define UARTCR_PC1 (1 << 6)
#define UARTSR_DTF (1 << 1)
#define UARTSR_DRF (1 << 2)
#define UARTSR_RMB (1 << 9)
DECLARE_GLOBAL_DATA_PTR;
#ifndef CONFIG_DM_SERIAL
#error "The linflex serial driver does not have non-DM support."
#endif
static void _linflex_serial_setbrg(struct linflex_fsl *base, int baudrate)
{
u32 clk = mxc_get_clock(MXC_UART_CLK);
u32 ibr, fbr;
if (!baudrate)
baudrate = CONFIG_BAUDRATE;
ibr = (u32) (clk / (16 * gd->baudrate));
fbr = (u32) (clk % (16 * gd->baudrate)) * 16;
__raw_writel(ibr, &base->linibrr);
__raw_writel(fbr, &base->linfbrr);
}
static int _linflex_serial_getc(struct linflex_fsl *base)
{
char c;
if (!(__raw_readb(&base->uartsr) & UARTSR_DRF))
return -EAGAIN;
if (!(__raw_readl(&base->uartsr) & UARTSR_RMB))
return -EAGAIN;
c = __raw_readl(&base->bdrm);
__raw_writeb((__raw_readb(&base->uartsr) | (UARTSR_DRF | UARTSR_RMB)),
&base->uartsr);
return c;
}
static int _linflex_serial_putc(struct linflex_fsl *base, const char c)
{
__raw_writeb(c, &base->bdrl);
if (!(__raw_readb(&base->uartsr) & UARTSR_DTF))
return -EAGAIN;
__raw_writeb((__raw_readb(&base->uartsr) | UARTSR_DTF), &base->uartsr);
return 0;
}
/*
* Initialise the serial port with the given baudrate. The settings
* are always 8 data bits, no parity, 1 stop bit, no start bits.
*/
static int _linflex_serial_init(struct linflex_fsl *base)
{
volatile u32 ctrl;
/* set the Linflex in master mode amd activate by-pass filter */
ctrl = LINCR1_BF | LINCR1_MME;
__raw_writel(ctrl, &base->lincr1);
/* init mode */
ctrl |= LINCR1_INIT;
__raw_writel(ctrl, &base->lincr1);
/* waiting for init mode entry - TODO: add a timeout */
while ((__raw_readl(&base->linsr) & LINSR_LINS_MASK) !=
LINSR_LINS_INITMODE);
/* set UART bit to allow writing other bits */
__raw_writel(UARTCR_UART, &base->uartcr);
/* provide data bits, parity, stop bit, etc */
serial_setbrg();
/* 8 bit data, no parity, Tx and Rx enabled, UART mode */
__raw_writel(UARTCR_PC1 | UARTCR_RXEN | UARTCR_TXEN | UARTCR_PC0
| UARTCR_WL0 | UARTCR_UART, &base->uartcr);
ctrl = __raw_readl(&base->lincr1);
ctrl &= ~LINCR1_INIT;
__raw_writel(ctrl, &base->lincr1); /* end init mode */
return 0;
}
struct linflex_serial_platdata {
struct linflex_fsl *base_addr;
u8 port_id; /* do we need this? */
};
struct linflex_serial_priv {
struct linflex_fsl *lfuart;
};
int linflex_serial_setbrg(struct udevice *dev, int baudrate)
{
struct linflex_serial_priv *priv = dev_get_priv(dev);
_linflex_serial_setbrg(priv->lfuart, baudrate);
return 0;
}
static int linflex_serial_getc(struct udevice *dev)
{
struct linflex_serial_priv *priv = dev_get_priv(dev);
return _linflex_serial_getc(priv->lfuart);
}
static int linflex_serial_putc(struct udevice *dev, const char ch)
{
struct linflex_serial_priv *priv = dev_get_priv(dev);
return _linflex_serial_putc(priv->lfuart, ch);
}
static int linflex_serial_pending(struct udevice *dev, bool input)
{
struct linflex_serial_priv *priv = dev_get_priv(dev);
uint32_t uartsr = __raw_readl(&priv->lfuart->uartsr);
if (input)
return ((uartsr & UARTSR_DRF) && (uartsr & UARTSR_RMB)) ? 1 : 0;
else
return uartsr & UARTSR_DTF ? 0 : 1;
}
static void linflex_serial_init_internal(struct linflex_fsl *lfuart)
{
_linflex_serial_init(lfuart);
_linflex_serial_setbrg(lfuart, CONFIG_BAUDRATE);
return;
}
static int linflex_serial_probe(struct udevice *dev)
{
struct linflex_serial_platdata *plat = dev->platdata;
struct linflex_serial_priv *priv = dev_get_priv(dev);
priv->lfuart = (struct linflex_fsl *)plat->base_addr;
linflex_serial_init_internal(priv->lfuart);
return 0;
}
static const struct dm_serial_ops linflex_serial_ops = {
.putc = linflex_serial_putc,
.pending = linflex_serial_pending,
.getc = linflex_serial_getc,
.setbrg = linflex_serial_setbrg,
};
U_BOOT_DRIVER(serial_linflex) = {
.name = "serial_linflex",
.id = UCLASS_SERIAL,
.probe = linflex_serial_probe,
.ops = &linflex_serial_ops,
.flags = DM_FLAG_PRE_RELOC,
.priv_auto_alloc_size = sizeof(struct linflex_serial_priv),
};
#ifdef CONFIG_DEBUG_UART_LINFLEXUART
#include <debug_uart.h>
static inline void _debug_uart_init(void)
{
struct linflex_fsl *base = (struct linflex_fsl *)CONFIG_DEBUG_UART_BASE;
linflex_serial_init_internal(base);
}
static inline void _debug_uart_putc(int ch)
{
struct linflex_fsl *base = (struct linflex_fsl *)CONFIG_DEBUG_UART_BASE;
/* XXX: Is this OK? Should this use the non-DM version? */
_linflex_serial_putc(base, ch);
}
DEBUG_UART_FUNCS
#endif /* CONFIG_DEBUG_UART_LINFLEXUART */
|