summaryrefslogtreecommitdiffstats
path: root/drivers/serial/mxs_auart.c
blob: fc0fa96a0e2e156c48d53dac049341b3580cdfbc (plain)
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
/*
 * Freescale i.MX23/i.MX28 AUART driver
 *
 * Copyright (C) 2013 Andreas Wass <andreas.wass@dalelven.com>
 *
 * Based on the MXC serial driver:
 *
 * (c) 2007 Sascha Hauer <s.hauer@pengutronix.de>
 *
 * Further based on the Linux mxs-auart.c driver:
 *
 * Freescale STMP37XX/STMP38X Application UART drkiver
 * Copyright 2008-2010 Freescale Semiconductor, Inc.
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */
#include <common.h>
#include <asm/io.h>
#include <serial.h>
#include <linux/compiler.h>
#include <asm/arch/regs-base.h>
#include <asm/arch/regs-uartapp.h>
#include <asm/arch/sys_proto.h>

DECLARE_GLOBAL_DATA_PTR;

#ifndef CONFIG_MXS_AUART_BASE
#error "CONFIG_MXS_AUART_BASE must be set to the base UART to use"
#endif

/* AUART clock always supplied by XTAL and always 24MHz */
#define MXS_AUART_CLK 24000000

static struct mxs_uartapp_regs *get_uartapp_registers(void)
{
	return (struct mxs_uartapp_regs *)CONFIG_MXS_AUART_BASE;
}

/**
 * Sets the baud rate and settings.
 * The settings are: 8 data bits, no parit and 1 stop bit.
 */
static void mxs_auart_setbrg(void)
{
	u32 div;
	u32 linectrl = 0;
	struct mxs_uartapp_regs *regs = get_uartapp_registers();

	if (!gd->baudrate)
		gd->baudrate = CONFIG_BAUDRATE;

	/*
	 * From i.MX28 datasheet:
	 * div is calculated by calculating UARTCLK*32/baudrate, rounded to int
	 * div must be between 0xEC and 0x003FFFC0 inclusive
	 * Lowest 6 bits of div goes in BAUD_DIVFRAC part of LINECTRL register
	 * Next 16 bits goes in BAUD_DIVINT part of LINECTRL register
	 */
	div = (MXS_AUART_CLK * 32) / gd->baudrate;
	if (div < 0xEC || div > 0x003FFFC0)
		return;

	linectrl |= ((div & UARTAPP_LINECTRL_EXTRACT_BAUD_DIVFRAC_MASK) <<
		UARTAPP_LINECTRL_BAUD_DIVFRAC_OFFSET) &
		UARTAPP_LINECTRL_BAUD_DIVFRAC_MASK;
	linectrl |= ((div >> UARTAPP_LINECTRL_EXTRACT_BAUD_DIVINT_OFFSET) <<
		UARTAPP_LINECTRL_BAUD_DIVINT_OFFSET) &
		UARTAPP_LINECTRL_BAUD_DIVINT_MASK;

	/* Word length: 8 bits */
	linectrl |= UARTAPP_LINECTRL_WLEN_8BITS;

	/* Enable FIFOs. */
	linectrl |= UARTAPP_LINECTRL_FEN_MASK;

	/* Write above settings, no parity, 1 stop bit */
	writel(linectrl, &regs->hw_uartapp_linectrl);
}

static int mxs_auart_init(void)
{
	struct mxs_uartapp_regs *regs = get_uartapp_registers();
	/* Reset everything */
	mxs_reset_block(&regs->hw_uartapp_ctrl0_reg);
	/* Disable interrupts */
	writel(0, &regs->hw_uartapp_intr);
	/* Set baud rate and settings */
	serial_setbrg();
	/* Disable RTS and CTS, ignore LINECTRL2 register */
	writel(UARTAPP_CTRL2_RTSEN_MASK |
			UARTAPP_CTRL2_CTSEN_MASK |
			UARTAPP_CTRL2_USE_LCR2_MASK,
			&regs->hw_uartapp_ctrl2_clr);
	/* Enable receiver, transmitter and UART */
	writel(UARTAPP_CTRL2_RXE_MASK |
			UARTAPP_CTRL2_TXE_MASK |
			UARTAPP_CTRL2_UARTEN_MASK,
			&regs->hw_uartapp_ctrl2_set);
	return 0;
}

static void mxs_auart_putc(const char c)
{
	struct mxs_uartapp_regs *regs = get_uartapp_registers();
	/* Wait in loop while the transmit FIFO is full */
	while (readl(&regs->hw_uartapp_stat) & UARTAPP_STAT_TXFF_MASK)
		;

	writel(c, &regs->hw_uartapp_data);

	if (c == '\n')
		mxs_auart_putc('\r');
}

static int mxs_auart_tstc(void)
{
	struct mxs_uartapp_regs *regs = get_uartapp_registers();
	/* Checks if receive FIFO is empty */
	return !(readl(&regs->hw_uartapp_stat) & UARTAPP_STAT_RXFE_MASK);
}

static int mxs_auart_getc(void)
{
	struct mxs_uartapp_regs *regs = get_uartapp_registers();
	/* Wait until a character is available to read */
	while (!mxs_auart_tstc())
		;
	/* Read the character from the data register */
	return readl(&regs->hw_uartapp_data) & 0xFF;
}

static struct serial_device mxs_auart_drv = {
	.name = "mxs_auart_serial",
	.start = mxs_auart_init,
	.stop = NULL,
	.setbrg = mxs_auart_setbrg,
	.putc = mxs_auart_putc,
	.puts = default_serial_puts,
	.getc = mxs_auart_getc,
	.tstc = mxs_auart_tstc,
};

void mxs_auart_initialize(void)
{
	serial_register(&mxs_auart_drv);
}

__weak struct serial_device *default_serial_console(void)
{
	return &mxs_auart_drv;
}