Skip to content

Commit ff452dc

Browse files
committed
Adds the ns16550a driver.
Signed-off-by: Amy Ringo <me@remexre.com>
1 parent fae6a32 commit ff452dc

File tree

3 files changed

+218
-0
lines changed

3 files changed

+218
-0
lines changed

src/kernel/drivers/include.mak

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# SPDX-FileCopyrightText: 2025 ukoOS Contributors
2+
#
3+
# SPDX-License-Identifier: GPL-3.0-or-later
4+
5+
kernel-objs-c += drivers/ns16550a

src/kernel/drivers/ns16550a.c

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2025 ukoOS Contributors
3+
*
4+
* SPDX-License-Identifier: GPL-3.0-or-later
5+
*/
6+
7+
#include <devices/uart.h>
8+
#include <devicetree.h>
9+
#include <mm/paging.h>
10+
#include <physical.h>
11+
12+
/**
13+
* The bits of the Interrupt Enable Register.
14+
*/
15+
struct ns16550a_ier {
16+
/**
17+
* Enable Receiver Buffer Full Interrupt.
18+
*/
19+
bool erbfi : 1;
20+
21+
/**
22+
* Enable Transmitter Buffer Empty Interrupt.
23+
*/
24+
bool etbei : 1;
25+
26+
/**
27+
* Enable (Receiver) Line Status Interrupt.
28+
*/
29+
bool elsi : 1;
30+
31+
/**
32+
* Enable Modem Status Interrupt.
33+
*/
34+
bool edssi : 1;
35+
36+
/**
37+
* Reserved, should be zero.
38+
*
39+
* Wikipedia suggests that the 16750 uses some of these bits.
40+
*/
41+
u8 rsvd : 4;
42+
};
43+
44+
/**
45+
* The bits of the Interrupt Identification Register.
46+
*/
47+
struct ns16550a_iir {
48+
/**
49+
* A bit that becomes unset when an interrupt is pending.
50+
*/
51+
bool interrupt_pending : 1;
52+
53+
/**
54+
* The index of the bit in `ns16550a_ier` corresponding to the pending
55+
* interrupt.
56+
*/
57+
u8 interrupt_id : 3;
58+
59+
/**
60+
* Reserved, should be zero.
61+
*
62+
* Wikipedia suggests that the 16750 uses some of these bits.
63+
*/
64+
u8 rsvd : 2;
65+
66+
/**
67+
* A flag for whether the FIFOs are currently functioning.
68+
*/
69+
bool fifo_working : 1;
70+
71+
/**
72+
* A flag for whether the FIFOs are enabled.
73+
*/
74+
bool fifo_enabled : 1;
75+
};
76+
77+
/**
78+
* The UART's registers.
79+
*/
80+
struct ns16550a_regs {
81+
union {
82+
/**
83+
* The Receiver Buffer Register. Read-only. Only valid when DLAB is unset.
84+
*/
85+
u8 rbr;
86+
87+
/**
88+
* The Transmitter Holding Register. Write-only. Only valid when DLAB is
89+
* unset.
90+
*/
91+
u8 thr;
92+
93+
/**
94+
* The Divisor Latch Low byte. Only valid when DLAB is set.
95+
*/
96+
u8 dll;
97+
};
98+
99+
/**
100+
* When DLAB is unset, this is the Interrupt Enable Register. When DLAB is
101+
* set, this is.
102+
*/
103+
union {
104+
/**
105+
* The Interrupt Enable Register. Only valid when DLAB is unset.
106+
*/
107+
struct ns16550a_ier ier;
108+
109+
/**
110+
* The Divisor Latch High byte. Only valid when DLAB is set.
111+
*/
112+
u8 dlh;
113+
};
114+
115+
union {
116+
/**
117+
* The Interrupt Identification Register. Read-only.
118+
*/
119+
struct ns16550a_iir iir;
120+
121+
/**
122+
* The FIFO Control Register. Write-only.
123+
*/
124+
u8 fcr;
125+
};
126+
127+
/**
128+
* The Line Control Register.
129+
*/
130+
u8 lcr;
131+
132+
/**
133+
* The Modem Control Register.
134+
*/
135+
u8 mcr;
136+
137+
/**
138+
* The Line Status Register.
139+
*/
140+
u8 lsr;
141+
142+
/**
143+
* The Modem Status Register.
144+
*/
145+
u8 msr;
146+
147+
/**
148+
* The Scratch Register.
149+
*/
150+
u8 sr;
151+
};
152+
153+
static_assert(sizeof(struct ns16550a_regs) == 8);
154+
155+
struct ns16550a {
156+
struct device device;
157+
struct uart uart;
158+
struct ns16550a_regs *regs;
159+
};
160+
161+
static void ns16550a_write_byte(struct uart *uart, u8 byte) { TODO(); }
162+
163+
static const struct uart_ops ns16550a_uart_ops = {
164+
.write_byte = ns16550a_write_byte,
165+
};
166+
167+
static struct device *ns16550a_enumerate_dt(struct devicetree_node *node) {
168+
struct ns16550a *device = nullptr;
169+
paddr reg_addr;
170+
usize reg_size;
171+
172+
if (!devicetree_reg(node, 0, &reg_addr, &reg_size))
173+
goto fail;
174+
if (reg_size < sizeof(struct ns16550a_regs))
175+
goto fail;
176+
177+
device = alloc(sizeof(struct ns16550a));
178+
if (!device)
179+
goto fail;
180+
181+
*device = (struct ns16550a){
182+
.device = DEVICE_INIT(device->device, "ns16550a@{paddr}", reg_addr),
183+
.uart =
184+
{
185+
.list = LIST_INIT(device->uart.list),
186+
.ops = &ns16550a_uart_ops,
187+
.device = &device->device,
188+
},
189+
.regs = iomem_map(reg_addr, reg_size),
190+
};
191+
if (!device->device.name || !device->regs)
192+
goto fail;
193+
194+
list_push(&uarts, &device->uart.list);
195+
196+
return &device->device;
197+
198+
fail:
199+
if (device) {
200+
free(device->device.name);
201+
iomem_unmap(device->regs, reg_size);
202+
}
203+
free(device);
204+
return nullptr;
205+
}
206+
207+
DEFINE_INIT(INIT_REGISTER_DRIVERS) {
208+
devicetree_register("ns16550a", ns16550a_enumerate_dt);
209+
}

src/kernel/include.mak

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,8 @@ kernel-objs-c += random
4949
kernel-objs-c += selftest
5050
kernel-objs-c += swar_test
5151
kernel-objs-c += symbolicate
52+
include $(srcdir)/src/kernel/drivers/include.mak
53+
54+
# The architecture-specific file needs to be last, since it calls
55+
# compute_component_variables for the kernel.
5256
include $(srcdir)/src/kernel/arch/$(arch)/include.mak

0 commit comments

Comments
 (0)