summaryrefslogtreecommitdiff
path: root/drivers/serial/serial_arc.c
blob: 326a536a26ad47801b40eee9b44a7f04ce084641 (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
/*
 * Copyright (C) 2013 Synopsys, Inc. (www.synopsys.com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 */

#include <common.h>
#include <dm.h>
#include <serial.h>

DECLARE_GLOBAL_DATA_PTR;

struct arc_serial_regs {
	unsigned int id0;
	unsigned int id1;
	unsigned int id2;
	unsigned int id3;
	unsigned int data;
	unsigned int status;
	unsigned int baudl;
	unsigned int baudh;
};


struct arc_serial_platdata {
	struct arc_serial_regs *reg;
	unsigned int uartclk;
};

/* Bit definitions of STATUS register */
#define UART_RXEMPTY		(1 << 5)
#define UART_OVERFLOW_ERR	(1 << 1)
#define UART_TXEMPTY		(1 << 7)

static int arc_serial_setbrg(struct udevice *dev, int baudrate)
{
	struct arc_serial_platdata *plat = dev->platdata;
	struct arc_serial_regs *const regs = plat->reg;
	int arc_console_baud = gd->cpu_clk / (baudrate * 4) - 1;

	writeb(arc_console_baud & 0xff, &regs->baudl);
	writeb((arc_console_baud & 0xff00) >> 8, &regs->baudh);

	return 0;
}

static int arc_serial_putc(struct udevice *dev, const char c)
{
	struct arc_serial_platdata *plat = dev->platdata;
	struct arc_serial_regs *const regs = plat->reg;

	while (!(readb(&regs->status) & UART_TXEMPTY))
		;

	writeb(c, &regs->data);

	return 0;
}

static int arc_serial_tstc(struct arc_serial_regs *const regs)
{
	return !(readb(&regs->status) & UART_RXEMPTY);
}

static int arc_serial_pending(struct udevice *dev, bool input)
{
	struct arc_serial_platdata *plat = dev->platdata;
	struct arc_serial_regs *const regs = plat->reg;
	uint32_t status = readb(&regs->status);

	if (input)
		return status & UART_RXEMPTY ? 0 : 1;
	else
		return status & UART_TXEMPTY ? 0 : 1;
}

static int arc_serial_getc(struct udevice *dev)
{
	struct arc_serial_platdata *plat = dev->platdata;
	struct arc_serial_regs *const regs = plat->reg;

	while (!arc_serial_tstc(regs))
		;

	/* Check for overflow errors */
	if (readb(&regs->status) & UART_OVERFLOW_ERR)
		return 0;

	return readb(&regs->data) & 0xFF;
}

static int arc_serial_probe(struct udevice *dev)
{
	return 0;
}

static const struct dm_serial_ops arc_serial_ops = {
	.putc = arc_serial_putc,
	.pending = arc_serial_pending,
	.getc = arc_serial_getc,
	.setbrg = arc_serial_setbrg,
};

static const struct udevice_id arc_serial_ids[] = {
	{ .compatible = "snps,arc-uart" },
	{ }
};

static int arc_serial_ofdata_to_platdata(struct udevice *dev)
{
	struct arc_serial_platdata *plat = dev_get_platdata(dev);
	DECLARE_GLOBAL_DATA_PTR;

	plat->reg = (struct arc_serial_regs *)dev_get_addr(dev);
	plat->uartclk = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
				       "clock-frequency", 0);

	return 0;
}

U_BOOT_DRIVER(serial_arc) = {
	.name	= "serial_arc",
	.id	= UCLASS_SERIAL,
	.of_match = arc_serial_ids,
	.ofdata_to_platdata = arc_serial_ofdata_to_platdata,
	.probe = arc_serial_probe,
	.ops	= &arc_serial_ops,
	.flags = DM_FLAG_PRE_RELOC,
};