mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2026-05-04 20:26:13 +02:00
Currently, the Goldfish RTC driver exclusively relies on device tree to retrieve the base address, failing immediately if dev_read_addr() returns FDT_ADDR_T_NONE. This restriction prevents the driver from being used on platforms that instantiate devices via U_BOOT_DRVINFO() instead of device tree, such as the QEMU m68k virt machine. Add support for platform data to address this limitation. Introduce a new .of_to_plat hook to handle device tree parsing and populate the platform data. Update the probe function to rely exclusively on this platform data, enabling support for both Device Tree and manual instantiation. Introduce a new header file include/goldfish_rtc.h to define the platform data structure. Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com> Reviewed-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
123 lines
2.6 KiB
C
123 lines
2.6 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright 2023, Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
|
|
*
|
|
* This driver supports the Google Goldfish virtual platform RTC device.
|
|
* The device is provided by the RISC-V virt machine in QEMU. It exposes
|
|
* a 64-bit nanosecond timer via two memory-mapped 32-bit registers.
|
|
*/
|
|
|
|
#include <div64.h>
|
|
#include <dm.h>
|
|
#include <goldfish_rtc.h>
|
|
#include <mapmem.h>
|
|
#include <rtc.h>
|
|
#include <linux/io.h>
|
|
|
|
/**
|
|
* struct goldfish_rtc - private data for RTC driver
|
|
*/
|
|
struct goldfish_rtc {
|
|
/**
|
|
* @base: base address for register file
|
|
*/
|
|
void __iomem *base;
|
|
/**
|
|
* @isdst: daylight saving time
|
|
*/
|
|
int isdst;
|
|
};
|
|
|
|
/* Register offsets */
|
|
#define GOLDFISH_TIME_LOW 0x00
|
|
#define GOLDFISH_TIME_HIGH 0x04
|
|
|
|
static int goldfish_rtc_get(struct udevice *dev, struct rtc_time *time)
|
|
{
|
|
struct goldfish_rtc *priv = dev_get_priv(dev);
|
|
void __iomem *base = priv->base;
|
|
u64 time_high;
|
|
u64 time_low;
|
|
u64 now;
|
|
|
|
time_low = ioread32(base + GOLDFISH_TIME_LOW);
|
|
time_high = ioread32(base + GOLDFISH_TIME_HIGH);
|
|
now = (time_high << 32) | time_low;
|
|
|
|
do_div(now, 1000000000U);
|
|
|
|
rtc_to_tm(now, time);
|
|
time->tm_isdst = priv->isdst;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int goldfish_rtc_set(struct udevice *dev, const struct rtc_time *time)
|
|
{
|
|
struct goldfish_rtc *priv = dev_get_priv(dev);
|
|
void __iomem *base = priv->base;
|
|
u64 now;
|
|
|
|
if (time->tm_year < 1970)
|
|
return -EINVAL;
|
|
|
|
now = rtc_mktime(time) * 1000000000ULL;
|
|
iowrite32(now >> 32, base + GOLDFISH_TIME_HIGH);
|
|
iowrite32(now, base + GOLDFISH_TIME_LOW);
|
|
|
|
if (time->tm_isdst > 0)
|
|
priv->isdst = 1;
|
|
else if (time->tm_isdst < 0)
|
|
priv->isdst = -1;
|
|
else
|
|
priv->isdst = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int goldfish_rtc_of_to_plat(struct udevice *dev)
|
|
{
|
|
struct goldfish_rtc_plat *plat = dev_get_plat(dev);
|
|
fdt_addr_t addr;
|
|
|
|
addr = dev_read_addr(dev);
|
|
if (addr != FDT_ADDR_T_NONE)
|
|
plat->reg = addr;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int goldfish_rtc_probe(struct udevice *dev)
|
|
{
|
|
struct goldfish_rtc_plat *plat = dev_get_plat(dev);
|
|
struct goldfish_rtc *priv = dev_get_priv(dev);
|
|
|
|
if (!plat->reg)
|
|
return -EINVAL;
|
|
|
|
priv->base = map_sysmem(plat->reg, 0x20);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct rtc_ops goldfish_rtc_ops = {
|
|
.get = goldfish_rtc_get,
|
|
.set = goldfish_rtc_set,
|
|
};
|
|
|
|
static const struct udevice_id goldfish_rtc_of_match[] = {
|
|
{ .compatible = "google,goldfish-rtc", },
|
|
{},
|
|
};
|
|
|
|
U_BOOT_DRIVER(rtc_goldfish) = {
|
|
.name = "rtc_goldfish",
|
|
.id = UCLASS_RTC,
|
|
.ops = &goldfish_rtc_ops,
|
|
.probe = goldfish_rtc_probe,
|
|
.of_match = goldfish_rtc_of_match,
|
|
.of_to_plat = goldfish_rtc_of_to_plat,
|
|
.plat_auto = sizeof(struct goldfish_rtc_plat),
|
|
.priv_auto = sizeof(struct goldfish_rtc),
|
|
};
|