summaryrefslogtreecommitdiff
path: root/drivers/serial/serial_owl.c
blob: 3b795785f780711291e72f36b1b7e2b8c95066e8 (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
// SPDX-License-Identifier: GPL-2.0+
/*
 * Actions Semi OWL SoCs UART driver
 *
 * Copyright (C) 2015 Actions Semi Co., Ltd.
 * Copyright (C) 2018 Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
 */

#include <common.h>
#include <clk.h>
#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
#include <serial.h>
#include <asm/io.h>
#include <asm/types.h>
#include <linux/bitops.h>

/* UART Registers */
#define	OWL_UART_CTL			(0x0000)
#define	OWL_UART_RXDAT			(0x0004)
#define	OWL_UART_TXDAT			(0x0008)
#define	OWL_UART_STAT			(0x000C)

/* UART_CTL Register Definitions */
#define	OWL_UART_CTL_PRS_NONE		GENMASK(6, 4)
#define	OWL_UART_CTL_STPS		BIT(2)
#define	OWL_UART_CTL_DWLS		3

/* UART_STAT Register Definitions */
#define	OWL_UART_STAT_TFES		BIT(10)	/* TX FIFO Empty Status	*/
#define	OWL_UART_STAT_RFFS		BIT(9)	/* RX FIFO full	Status */
#define	OWL_UART_STAT_TFFU		BIT(6)	/* TX FIFO full	Status */
#define	OWL_UART_STAT_RFEM		BIT(5)	/* RX FIFO Empty Status	*/

struct owl_serial_priv {
	phys_addr_t base;
};

int owl_serial_setbrg(struct udevice *dev, int baudrate)
{
	/* Driver supports only fixed baudrate */
	return 0;
}

static int owl_serial_getc(struct udevice *dev)
{
	struct owl_serial_priv *priv = dev_get_priv(dev);

	if (readl(priv->base + OWL_UART_STAT) & OWL_UART_STAT_RFEM)
		return -EAGAIN;

	return (int)(readl(priv->base +	OWL_UART_RXDAT));
}

static int owl_serial_putc(struct udevice *dev,	const char ch)
{
	struct owl_serial_priv *priv = dev_get_priv(dev);

	if (readl(priv->base + OWL_UART_STAT) & OWL_UART_STAT_TFFU)
		return -EAGAIN;

	writel(ch, priv->base +	OWL_UART_TXDAT);

	return 0;
}

static int owl_serial_pending(struct udevice *dev, bool	input)
{
	struct owl_serial_priv *priv = dev_get_priv(dev);
	unsigned int stat = readl(priv->base + OWL_UART_STAT);

	if (input)
		return !(stat &	OWL_UART_STAT_RFEM);
	else
		return !(stat &	OWL_UART_STAT_TFES);
}

static int owl_serial_probe(struct udevice *dev)
{
	struct owl_serial_priv *priv = dev_get_priv(dev);
	struct clk clk;
	u32 uart_ctl;
	int ret;

	/* Set data, parity and stop bits */
	uart_ctl = readl(priv->base + OWL_UART_CTL);
	uart_ctl &= ~(OWL_UART_CTL_PRS_NONE);
	uart_ctl &= ~(OWL_UART_CTL_STPS);
	uart_ctl |= OWL_UART_CTL_DWLS;
	writel(uart_ctl, priv->base + OWL_UART_CTL);

	/* Enable UART clock */
	ret = clk_get_by_index(dev, 0, &clk);
	if (ret < 0)
		return ret;

	ret = clk_enable(&clk);
	if (ret < 0)
		return ret;

	return 0;
}

static int owl_serial_of_to_plat(struct	udevice	*dev)
{
	struct owl_serial_priv *priv = dev_get_priv(dev);

	priv->base = dev_read_addr(dev);
	if (priv->base == FDT_ADDR_T_NONE)
		return -EINVAL;

	return 0;
}

static const struct dm_serial_ops owl_serial_ops = {
	.putc =	owl_serial_putc,
	.pending = owl_serial_pending,
	.getc =	owl_serial_getc,
	.setbrg	= owl_serial_setbrg,
};

static const struct udevice_id owl_serial_ids[] = {
	{ .compatible = "actions,owl-uart" },
	{ }
};

U_BOOT_DRIVER(serial_owl) = {
	.name = "serial_owl",
	.id = UCLASS_SERIAL,
	.of_match = owl_serial_ids,
	.of_to_plat = owl_serial_of_to_plat,
	.priv_auto	=	sizeof(struct owl_serial_priv),
	.probe = owl_serial_probe,
	.ops = &owl_serial_ops,
};