summaryrefslogtreecommitdiff
path: root/drivers/serial/pl011.c
blob: d11aa003fb120b8edb10ba38ca49067bcede8d3d (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
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * pl011.c - ARM PrimeCell PL011 serial, polled io
 *
 * the register layout is shared with the PL010 so we use the combined
 * struct like u-boot does. base comes from the dtb, never hardcoded.
 *
 * Copyright (C) 2026 Bradley Morgan <brads@mainlining.org>
 *
 * 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 <sys/types.h>
#include <stdint.h>
#include <reg.h>
#include <drivers/serial/pl011.h>

void pl011_init(uintptr_t base, uint32 clock, uint32 baud)
{
	struct pl01x_regs *regs = (struct pl01x_regs *)base;
	uint32 divisor;
	uint32 remainder;
	uint32 fraction;

	/* baud divisor, same math as the PL011 TRM section 3.3.3 */
	divisor = clock / (16 * baud);
	remainder = clock % (16 * baud);
	fraction = (8 * remainder + baud / 2) / baud;

	writel(0, &regs->pl011_cr);
	writel(0x7ff, &regs->pl011_icr);
	writel(divisor, &regs->pl011_ibrd);
	writel(fraction, &regs->pl011_fbrd);
	writel((3 << 5) | (1 << 4), &regs->pl011_lcrh);
	writel((1 << 0) | (1 << 8) | (1 << 9), &regs->pl011_cr);
}

void pl011_putc(uintptr_t base, char c)
{
	struct pl01x_regs *regs = (struct pl01x_regs *)base;

	while (readl(&regs->fr) & UART_PL01x_FR_TXFF)
		;
	writel(c, &regs->dr);
}

int pl011_getc(uintptr_t base)
{
	struct pl01x_regs *regs = (struct pl01x_regs *)base;

	if (readl(&regs->fr) & UART_PL01x_FR_RXFE)
		return -1;
	return readl(&regs->dr) & 0xff;
}