aboutsummaryrefslogtreecommitdiff
path: root/drivers/serial/serial_npcm.c
blob: 76ac7cb80db53bf0759226621765d87bba9ecf72 (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
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (c) 2021 Nuvoton Technology Corp.
 */

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

struct npcm_uart {
	union {
		u32	rbr;	/* Receive Buffer Register */
		u32	thr;	/* Transmit Holding Register */
		u32	dll;	/* Divisor Latch (Low Byte) Register */
	};
	union {
		u32	ier;	/* Interrupt Enable Register */
		u32	dlm;	/* Divisor Latch (Low Byte) Register */
	};
	union {
		u32	iir;	/* Interrupt Identification Register */
		u32	fcr;	/* FIFO Control Register */
	};
	u32	lcr;		/* Line Control Register */
	u32	mcr;		/* Modem Control Register */
	u32	lsr;		/* Line Status Control Register */
	u32	msr;		/* Modem Status Register */
	u32	tor;		/* Timeout Register */
};

#define	LCR_WLS_8BITS	3	/* 8-bit word length select */
#define	FCR_TFR		BIT(2)	/* TxFIFO reset */
#define	FCR_RFR		BIT(1)	/* RxFIFO reset */
#define	FCR_FME		BIT(0)	/* FIFO mode enable */
#define	LSR_THRE	BIT(5)	/* Status of TxFIFO empty */
#define	LSR_RFDR	BIT(0)	/* Status of RxFIFO data ready */
#define	LCR_DLAB	BIT(7)	/* Divisor latch access bit */

struct npcm_serial_plat {
	struct npcm_uart *reg;
	u32 uart_clk;		/* frequency of uart clock source */
};

static int npcm_serial_pending(struct udevice *dev, bool input)
{
	struct npcm_serial_plat *plat = dev_get_plat(dev);
	struct npcm_uart *uart = plat->reg;

	if (input)
		return readb(&uart->lsr) & LSR_RFDR ? 1 : 0;
	else
		return readb(&uart->lsr) & LSR_THRE ? 0 : 1;
}

static int npcm_serial_putc(struct udevice *dev, const char ch)
{
	struct npcm_serial_plat *plat = dev_get_plat(dev);
	struct npcm_uart *uart = plat->reg;

	if (!(readb(&uart->lsr) & LSR_THRE))
		return -EAGAIN;

	writeb(ch, &uart->thr);

	return 0;
}

static int npcm_serial_getc(struct udevice *dev)
{
	struct npcm_serial_plat *plat = dev_get_plat(dev);
	struct npcm_uart *uart = plat->reg;

	if (!(readb(&uart->lsr) & LSR_RFDR))
		return -EAGAIN;

	return readb(&uart->rbr);
}

static int npcm_serial_setbrg(struct udevice *dev, int baudrate)
{
	struct npcm_serial_plat *plat = dev_get_plat(dev);
	struct npcm_uart *uart = plat->reg;
	u16 divisor;

	/* BaudOut = UART Clock / (16 * [Divisor + 2]) */
	divisor = DIV_ROUND_CLOSEST(plat->uart_clk, 16 * baudrate + 2) - 2;

	setbits_8(&uart->lcr, LCR_DLAB);
	writeb(divisor & 0xff, &uart->dll);
	writeb(divisor >> 8, &uart->dlm);
	clrbits_8(&uart->lcr, LCR_DLAB);

	return 0;
}

static int npcm_serial_probe(struct udevice *dev)
{
	struct npcm_serial_plat *plat = dev_get_plat(dev);
	struct npcm_uart *uart = plat->reg;
	struct clk clk, parent;
	u32 freq;
	int ret;

	plat->reg = dev_read_addr_ptr(dev);
	freq = dev_read_u32_default(dev, "clock-frequency", 0);

	ret = clk_get_by_index(dev, 0, &clk);
	if (ret < 0)
		return ret;

	ret = clk_get_by_index(dev, 1, &parent);
	if (!ret) {
		ret = clk_set_parent(&clk, &parent);
		if (ret)
			return ret;
	}

	ret = clk_set_rate(&clk, freq);
	if (ret < 0)
		return ret;
	plat->uart_clk = ret;

	/* Disable all interrupt */
	writeb(0, &uart->ier);

	/* Set 8 bit, 1 stop, no parity */
	writeb(LCR_WLS_8BITS, &uart->lcr);

	/* Reset RX/TX FIFO */
	writeb(FCR_FME | FCR_RFR | FCR_TFR, &uart->fcr);

	return 0;
}

static const struct dm_serial_ops npcm_serial_ops = {
	.getc = npcm_serial_getc,
	.setbrg = npcm_serial_setbrg,
	.putc = npcm_serial_putc,
	.pending = npcm_serial_pending,
};

static const struct udevice_id npcm_serial_ids[] = {
	{ .compatible = "nuvoton,npcm750-uart" },
	{ .compatible = "nuvoton,npcm845-uart" },
	{ }
};

U_BOOT_DRIVER(serial_npcm) = {
	.name	= "serial_npcm",
	.id	= UCLASS_SERIAL,
	.of_match = npcm_serial_ids,
	.plat_auto  = sizeof(struct npcm_serial_plat),
	.probe = npcm_serial_probe,
	.ops	= &npcm_serial_ops,
	.flags = DM_FLAG_PRE_RELOC,
};