summaryrefslogtreecommitdiffstats
path: root/drivers/serial/serial_pxa.c
blob: 1a4d7b4303cfdd622eede48a757c6141ed134b45 (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
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
/*
 * (c) 2009 Sascha Hauer <s.hauer@pengutronix.de>
 *     2010 by Marc Kleine-Budde <kernel@pengutronix.de>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 */

#include <common.h>
#include <driver.h>
#include <init.h>
#include <malloc.h>

#include <mach/clock.h>
#include <asm/io.h>

#define RBR		0x00	/* Receive Buffer Register (read only) */
#define THR		0x00	/* Transmit Holding Register (write only) */
#define IER		0x04	/* Interrupt Enable Register (read/write) */
#define IIR		0x08	/* Interrupt ID Register (read only) */
#define FCR		0x08	/* FIFO Control Register (write only) */
#define LCR		0x0c	/* Line Control Register (read/write) */
#define MCR		0x10	/* Modem Control Register (read/write) */
#define LSR		0x14	/* Line Status Register (read only) */
#define MSR		0x18	/* Modem Status Register (read only) */
#define SPR		0x1c	/* Scratch Pad Register (read/write) */
#define ISR		0x20	/* Infrared Selection Register (read/write) */
#define DLL		0x00	/* Divisor Latch Low Register (DLAB = 1) (read/write) */
#define DLH		0x04	/* Divisor Latch High Register (DLAB = 1) (read/write) */

#define IER_DMAE	(1 << 7)	/* DMA Requests Enable */
#define IER_UUE		(1 << 6)	/* UART Unit Enable */
#define IER_NRZE	(1 << 5)	/* NRZ coding Enable */
#define IER_RTIOE	(1 << 4)	/* Receiver Time Out Interrupt Enable */
#define IER_MIE		(1 << 3)	/* Modem Interrupt Enable */
#define IER_RLSE	(1 << 2)	/* Receiver Line Status Interrupt Enable */
#define IER_TIE		(1 << 1)	/* Transmit Data request Interrupt Enable */
#define IER_RAVIE	(1 << 0)	/* Receiver Data Available Interrupt Enable */

#define IIR_FIFOES1	(1 << 7)	/* FIFO Mode Enable Status */
#define IIR_FIFOES0	(1 << 6)	/* FIFO Mode Enable Status */
#define IIR_TOD		(1 << 3)	/* Time Out Detected */
#define IIR_IID2	(1 << 2)	/* Interrupt Source Encoded */
#define IIR_IID1	(1 << 1)	/* Interrupt Source Encoded */
#define IIR_IP		(1 << 0)	/* Interrupt Pending (active low) */

#define FCR_ITL2	(1 << 7)	/* Interrupt Trigger Level */
#define FCR_ITL1	(1 << 6)	/* Interrupt Trigger Level */
#define FCR_RESETTF	(1 << 2)	/* Reset Transmitter FIFO */
#define FCR_RESETRF	(1 << 1)	/* Reset Receiver FIFO */
#define FCR_TRFIFOE	(1 << 0)	/* Transmit and Receive FIFO Enable */
#define FCR_ITL_1	(0)
#define FCR_ITL_8	(FCR_ITL1)
#define FCR_ITL_16	(FCR_ITL2)
#define FCR_ITL_32	(FCR_ITL2 | F CR_ITL1)

#define LCR_DLAB	(1 << 7)	/* Divisor Latch Access Bit */
#define LCR_SB		(1 << 6)	/* Set Break */
#define LCR_STKYP	(1 << 5)	/* Sticky Parity */
#define LCR_EPS		(1 << 4)	/* Even Parity Select */
#define LCR_PEN		(1 << 3)	/* Parity Enable */
#define LCR_STB		(1 << 2)	/* Stop Bit */
#define LCR_WLS1	(1 << 1)	/* Word Length Select */
#define LCR_WLS0	(1 << 0)	/* Word Length Select */
#define LCR_WLEN8	(LCR_WLS1 | LCR_WLS0)
					/* Wordlength: 8 bits */

#define LSR_FIFOE	(1 << 7)	/* FIFO Error Status */
#define LSR_TEMT	(1 << 6)	/* Transmitter Empty */
#define LSR_TDRQ	(1 << 5)	/* Transmit Data Request */
#define LSR_BI		(1 << 4)	/* Break Interrupt */
#define LSR_FE		(1 << 3)	/* Framing Error */
#define LSR_PE		(1 << 2)	/* Parity Error */
#define LSR_OE		(1 << 1)	/* Overrun Error */
#define LSR_DR		(1 << 0)	/* Data Ready */

#define MCR_LOOP	(1 << 4)	/* */
#define MCR_OUT2	(1 << 3)	/* force MSR_DCD in loopback mode */
#define MCR_OUT1	(1 << 2)	/* force MSR_RI in loopback mode */
#define MCR_RTS		(1 << 1)	/* Request to Send */
#define MCR_DTR		(1 << 0)	/* Data Terminal Ready */

#define MSR_DCD		(1 << 7)	/* Data Carrier Detect */
#define MSR_RI		(1 << 6)	/* Ring Indicator */
#define MSR_DSR		(1 << 5)	/* Data Set Ready */
#define MSR_CTS		(1 << 4)	/* Clear To Send */
#define MSR_DDCD	(1 << 3)	/* Delta Data Carrier Detect */
#define MSR_TERI	(1 << 2)	/* Trailing Edge Ring Indicator */
#define MSR_DDSR	(1 << 1)	/* Delta Data Set Ready */
#define MSR_DCTS	(1 << 0)	/* Delta Clear To Send */

struct pxa_serial_priv {
	void __iomem *regs;
	struct console_device cdev;
};

static void __iomem *to_regs(struct console_device *cdev)
{
	struct pxa_serial_priv *priv =
		container_of(cdev, struct pxa_serial_priv, cdev);
	return priv->regs;
}

static void pxa_serial_putc(struct console_device *cdev, char c)
{
	while (!(readl(to_regs(cdev) + LSR) & LSR_TEMT));

	writel(c, to_regs(cdev) + THR);
}

static int pxa_serial_tstc(struct console_device *cdev)
{
	return readl(to_regs(cdev) + LSR) & LSR_DR;
}

static int pxa_serial_getc(struct console_device *cdev)
{
	while (!(readl(to_regs(cdev) + LSR) & LSR_DR));

	return readl(to_regs(cdev) + RBR) & 0xff;
}

static void pxa_serial_flush(struct console_device *cdev)
{
}

static int pxa_serial_setbaudrate(struct console_device *cdev, int baudrate)
{
	unsigned char cval = LCR_WLEN8;			/* 8N1 */
	unsigned int quot;

	/* enable uart */
	writel(IER_UUE, to_regs(cdev) + IER);

	/* write divisor */
	quot = (pxa_get_uartclk() + (8 * baudrate)) / (16 * baudrate);

	writel(cval | LCR_DLAB, to_regs(cdev) + LCR);	/* set DLAB */
	writel(quot & 0xff, to_regs(cdev) + DLL);
	/*
	 * work around Errata #75 according to Intel(R) PXA27x
	 * Processor Family Specification Update (Nov 2005)
	 */
	readl(to_regs(cdev) + DLL);
	writel(quot >> 8, to_regs(cdev) + DLH);
	writel(cval, to_regs(cdev) + LCR);		/* reset DLAB */

	/* enable fifos */
	writel(FCR_TRFIFOE, to_regs(cdev) + FCR);

	return 0;
}

static int pxa_serial_probe(struct device_d *dev)
{
	struct resource *iores;
	struct console_device *cdev;
	struct pxa_serial_priv *priv;

	priv = xzalloc(sizeof(*priv));
	cdev = &priv->cdev;
	iores = dev_request_mem_resource(dev, 0);
	if (IS_ERR(iores))
		return PTR_ERR(iores);
	priv->regs = IOMEM(iores->start);

	dev->priv = priv;
	cdev->dev = dev;
	cdev->tstc = pxa_serial_tstc;
	cdev->putc = pxa_serial_putc;
	cdev->getc = pxa_serial_getc;
	cdev->flush = pxa_serial_flush;
	cdev->setbrg = pxa_serial_setbaudrate;

	console_register(cdev);

	return 0;
}

static void pxa_serial_remove(struct device_d *dev)
{
	struct pxa_serial_priv *priv = dev->priv;

	console_unregister(&priv->cdev);
	free(priv);
}

static struct driver_d pxa_serial_driver = {
	.name = "pxa_serial",
	.probe = pxa_serial_probe,
	.remove = pxa_serial_remove,
};
console_platform_driver(pxa_serial_driver);