Skip to content

Commit 5471213

Browse files
committed
pl031: Add pl031 rtc support
Add support for the pl031, a popular RTC chip used by many devices, including qemu-virt. Signed-off-by: Pedro Falcato <pedro.falcato@gmail.com>
1 parent 29a4bbb commit 5471213

File tree

3 files changed

+116
-0
lines changed

3 files changed

+116
-0
lines changed

kernel/configs/kernel.config.arm64

+1
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@ CONFIG_RTL8168=y
2525
CONFIG_RISCV=n
2626
CONFIG_ARM64=y
2727
CONFIG_SERIAL_PL011=y
28+
CONFIG_PL031_RTC=y

kernel/drivers/rtc/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
obj-$(CONFIG_X86)+= drivers/rtc/rtc.o
22
obj-$(CONFIG_GOLDFISH_RTC)+= drivers/rtc/goldfish-rtc.o
3+
obj-$(CONFIG_PL031_RTC)+= drivers/rtc/pl031-rtc.o

kernel/drivers/rtc/pl031-rtc.cpp

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*
2+
* Copyright (c) 2023 Pedro Falcato
3+
* This file is part of Onyx, and is released under the terms of the MIT License
4+
* check LICENSE at the root directory for more information
5+
*
6+
* SPDX-License-Identifier: MIT
7+
*/
8+
9+
#include <onyx/clock.h>
10+
#include <onyx/dev.h>
11+
#include <onyx/device_tree.h>
12+
#include <onyx/types.h>
13+
14+
#include <onyx/hwregister.hpp>
15+
16+
#define RTC_DR 0x0
17+
#define RTC_LR 0x8
18+
#define RTC_CR 0x0c
19+
20+
class pl031_dev
21+
{
22+
private:
23+
dev_resource *rsrc_;
24+
unsigned int irq_;
25+
hw_range range_;
26+
27+
public:
28+
pl031_dev(dev_resource *io, unsigned int irq) : rsrc_{io}, irq_{irq}, range_{rsrc_}
29+
{
30+
}
31+
32+
int init();
33+
34+
u64 get_time()
35+
{
36+
return range_.read32(RTC_DR);
37+
}
38+
39+
int set_time(u64 time_epoch)
40+
{
41+
if (time_epoch > UINT32_MAX)
42+
{
43+
printf("pl031: The device does not support 64-bit time (tried to set time=%lx)\n",
44+
time_epoch);
45+
return -EIO;
46+
}
47+
48+
range_.write32(RTC_LR, (u32) time_epoch);
49+
return 0;
50+
}
51+
};
52+
53+
#define RTC_CR_START (1 << 0)
54+
55+
int pl031_dev::init()
56+
{
57+
if (!range_.init(rsrc_))
58+
return -ENOMEM;
59+
printf("pl031: initializing...\n");
60+
61+
if (!(range_.read32(RTC_CR) & RTC_CR_START))
62+
{
63+
// We must only write the RTC start bit
64+
// *IF* it is not started already, else we reset the clock.
65+
range_.write32(RTC_CR, RTC_CR_START);
66+
}
67+
68+
struct clock_time clk;
69+
clk.epoch = get_time();
70+
clk.measurement_timestamp = clocksource_get_time();
71+
time_set(CLOCK_REALTIME, &clk);
72+
return 0;
73+
}
74+
75+
int pl031_dt_probe(device *fake_dev)
76+
{
77+
auto dev = (device_tree::node *) fake_dev;
78+
79+
// Find IRQ, IO resources
80+
auto irq_rc = dev->get_resource(DEV_RESOURCE_FLAG_IRQ);
81+
if (!irq_rc)
82+
return -1;
83+
84+
auto iorsrc = dev->get_resource(DEV_RESOURCE_FLAG_MEM | DEV_RESOURCE_FLAG_IO_PORT);
85+
if (!iorsrc)
86+
return -1;
87+
88+
unique_ptr<pl031_dev> gdev =
89+
make_unique<pl031_dev>(iorsrc, static_cast<uint16_t>(irq_rc->start()));
90+
if (!gdev)
91+
return -ENOMEM;
92+
93+
if (int st = gdev->init(); st < 0)
94+
return st;
95+
gdev.release();
96+
return 0;
97+
}
98+
99+
static const char *pl031_compatible_ids[] = {"arm,pl031", nullptr};
100+
101+
static driver pl031_driver = {
102+
.name = "pl031",
103+
.devids = pl031_compatible_ids,
104+
.probe = pl031_dt_probe,
105+
.bus_type_node = &pl031_driver,
106+
};
107+
108+
int pl031_dt_init()
109+
{
110+
device_tree::register_driver(&pl031_driver);
111+
return 0;
112+
}
113+
114+
DRIVER_INIT(pl031_dt_init);

0 commit comments

Comments
 (0)