aw_rtc: Register the clocks

Since latest DTS update the rtc is supposed to register two clocks :

- osc32k (the 32k oscillator on the board that the RTC uses directly and
that other peripheral can use)
- iosc (the internal oscillator of the RTC when available which frequency
depend on the SoC revision)

Since we need the RTC before the proper clock control unit (because it uses
those clocks) attach it a BUS_PASS_BUS + MIDDLE and attach the clock control
unit at BUS_PASS_BUS + LAST for the SoC that requires it.

Tested On:	     A20, H3, A64

MFC after:	1 month
This commit is contained in:
Emmanuel Vadot 2019-04-16 12:39:31 +00:00
parent b9d89f5e2f
commit 5f50cbd3de
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=346271
5 changed files with 105 additions and 39 deletions

View File

@ -1,4 +1,5 @@
/*-
* Copyright (c) 2019 Emmanuel Vadot <manu@FreeBSD.Org>
* Copyright (c) 2016 Vladimir Belian <fate10@gmail.com>
* All rights reserved.
*
@ -43,6 +44,8 @@ __FBSDID("$FreeBSD$");
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/extres/clk/clk_fixed.h>
#include <arm/allwinner/aw_machdep.h>
#include "clock_if.h"
@ -62,7 +65,7 @@ __FBSDID("$FreeBSD$");
#define LOSC_MAGIC 0x16aa0000
#define LOSC_BUSY_MASK 0x00000380
#define IS_SUN7I (sc->type == A20_RTC)
#define IS_SUN7I (sc->conf->is_a20 == true)
#define YEAR_MIN (IS_SUN7I ? 1970 : 2010)
#define YEAR_MAX (IS_SUN7I ? 2100 : 2073)
@ -92,27 +95,68 @@ __FBSDID("$FreeBSD$");
#define RTC_READ(sc, reg) bus_read_4((sc)->res, (reg))
#define RTC_WRITE(sc, reg, val) bus_write_4((sc)->res, (reg), (val))
#define IS_LEAP_YEAR(y) \
(((y) % 400) == 0 || (((y) % 100) != 0 && ((y) % 4) == 0))
#define IS_LEAP_YEAR(y) (((y) % 400) == 0 || (((y) % 100) != 0 && ((y) % 4) == 0))
#define A10_RTC 1
#define A20_RTC 2
#define A31_RTC 3
struct aw_rtc_conf {
uint64_t iosc_freq;
bus_size_t rtc_date;
bus_size_t rtc_time;
bus_size_t rtc_losc_sta;
bool is_a20;
};
struct aw_rtc_conf a10_conf = {
.rtc_date = A10_RTC_DATE_REG,
.rtc_time = A10_RTC_TIME_REG,
.rtc_losc_sta = LOSC_CTRL_REG,
};
struct aw_rtc_conf a20_conf = {
.rtc_date = A10_RTC_DATE_REG,
.rtc_time = A10_RTC_TIME_REG,
.rtc_losc_sta = LOSC_CTRL_REG,
.is_a20 = true,
};
struct aw_rtc_conf a31_conf = {
.iosc_freq = 650000, /* between 600 and 700 Khz */
.rtc_date = A31_RTC_DATE_REG,
.rtc_time = A31_RTC_TIME_REG,
.rtc_losc_sta = A31_LOSC_AUTO_SWT_STA,
};
struct aw_rtc_conf h3_conf = {
.iosc_freq = 16000000,
.rtc_date = A31_RTC_DATE_REG,
.rtc_time = A31_RTC_TIME_REG,
.rtc_losc_sta = A31_LOSC_AUTO_SWT_STA,
};
static struct ofw_compat_data compat_data[] = {
{ "allwinner,sun4i-a10-rtc", A10_RTC },
{ "allwinner,sun7i-a20-rtc", A20_RTC },
{ "allwinner,sun6i-a31-rtc", A31_RTC },
{ "allwinner,sun4i-a10-rtc", (uintptr_t) &a10_conf },
{ "allwinner,sun7i-a20-rtc", (uintptr_t) &a20_conf },
{ "allwinner,sun6i-a31-rtc", (uintptr_t) &a31_conf },
{ "allwinner,sun8i-h3-rtc", (uintptr_t) &h3_conf },
{ NULL, 0 }
};
struct aw_rtc_softc {
struct resource *res;
struct aw_rtc_conf *conf;
int type;
bus_size_t rtc_date;
bus_size_t rtc_time;
};
static struct clk_fixed_def aw_rtc_osc32k = {
.clkdef.id = 0,
.freq = 32768,
};
static struct clk_fixed_def aw_rtc_iosc = {
.clkdef.id = 2,
};
static void aw_rtc_install_clocks(struct aw_rtc_softc *sc, device_t dev);
static int aw_rtc_probe(device_t dev);
static int aw_rtc_attach(device_t dev);
static int aw_rtc_detach(device_t dev);
@ -140,8 +184,7 @@ static driver_t aw_rtc_driver = {
static devclass_t aw_rtc_devclass;
EARLY_DRIVER_MODULE(aw_rtc, simplebus, aw_rtc_driver, aw_rtc_devclass, 0, 0,
BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE);
BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
static int
aw_rtc_probe(device_t dev)
@ -161,7 +204,6 @@ static int
aw_rtc_attach(device_t dev)
{
struct aw_rtc_softc *sc = device_get_softc(dev);
bus_size_t rtc_losc_sta;
uint32_t val;
int rid = 0;
@ -171,20 +213,7 @@ aw_rtc_attach(device_t dev)
return (ENXIO);
}
sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
switch (sc->type) {
case A10_RTC:
case A20_RTC:
sc->rtc_date = A10_RTC_DATE_REG;
sc->rtc_time = A10_RTC_TIME_REG;
rtc_losc_sta = LOSC_CTRL_REG;
break;
case A31_RTC:
sc->rtc_date = A31_RTC_DATE_REG;
sc->rtc_time = A31_RTC_TIME_REG;
rtc_losc_sta = A31_LOSC_AUTO_SWT_STA;
break;
}
sc->conf = (struct aw_rtc_conf *)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
val = RTC_READ(sc, LOSC_CTRL_REG);
val |= LOSC_AUTO_SW_EN;
val |= LOSC_MAGIC | LOSC_GSM | LOSC_OSC_SRC;
@ -193,13 +222,15 @@ aw_rtc_attach(device_t dev)
DELAY(100);
if (bootverbose) {
val = RTC_READ(sc, rtc_losc_sta);
val = RTC_READ(sc, sc->conf->rtc_losc_sta);
if ((val & LOSC_OSC_SRC) == 0)
device_printf(dev, "Using internal oscillator\n");
else
device_printf(dev, "Using external oscillator\n");
}
aw_rtc_install_clocks(sc, dev);
clock_register(dev, RTC_RES_US);
return (0);
@ -212,6 +243,41 @@ aw_rtc_detach(device_t dev)
return (EBUSY);
}
static void
aw_rtc_install_clocks(struct aw_rtc_softc *sc, device_t dev) {
struct clkdom *clkdom;
const char **clknames;
phandle_t node;
int nclocks;
node = ofw_bus_get_node(dev);
nclocks = ofw_bus_string_list_to_array(node, "clock-output-names", &clknames);
/* No clocks to export */
if (nclocks <= 0)
return;
if (nclocks != 3) {
device_printf(dev, "Having only %d clocks instead of 3, aborting\n", nclocks);
return;
}
clkdom = clkdom_create(dev);
aw_rtc_osc32k.clkdef.name = clknames[0];
if (clknode_fixed_register(clkdom, &aw_rtc_osc32k) != 0)
device_printf(dev, "Cannot register osc32k clock\n");
aw_rtc_iosc.clkdef.name = clknames[2];
aw_rtc_iosc.freq = sc->conf->iosc_freq;
if (clknode_fixed_register(clkdom, &aw_rtc_iosc) != 0)
device_printf(dev, "Cannot register iosc clock\n");
clkdom_finit(clkdom);
if (bootverbose)
clkdom_dump(clkdom);
}
static int
aw_rtc_gettime(device_t dev, struct timespec *ts)
{
@ -219,11 +285,11 @@ aw_rtc_gettime(device_t dev, struct timespec *ts)
struct clocktime ct;
uint32_t rdate, rtime;
rdate = RTC_READ(sc, sc->rtc_date);
rtime = RTC_READ(sc, sc->rtc_time);
rdate = RTC_READ(sc, sc->conf->rtc_date);
rtime = RTC_READ(sc, sc->conf->rtc_time);
if ((rtime & TIME_MASK) == 0)
rdate = RTC_READ(sc, sc->rtc_date);
rdate = RTC_READ(sc, sc->conf->rtc_date);
ct.sec = GET_SEC_VALUE(rtime);
ct.min = GET_MIN_VALUE(rtime);
@ -265,7 +331,7 @@ aw_rtc_settime(device_t dev, struct timespec *ts)
DELAY(1);
}
/* reset time register to avoid unexpected date increment */
RTC_WRITE(sc, sc->rtc_time, 0);
RTC_WRITE(sc, sc->conf->rtc_time, 0);
rdate = SET_DAY_VALUE(ct.day) | SET_MON_VALUE(ct.mon) |
SET_YEAR_VALUE(ct.year - YEAR_OFFSET) |
@ -281,7 +347,7 @@ aw_rtc_settime(device_t dev, struct timespec *ts)
}
DELAY(1);
}
RTC_WRITE(sc, sc->rtc_date, rdate);
RTC_WRITE(sc, sc->conf->rtc_date, rdate);
for (clk = 0; RTC_READ(sc, LOSC_CTRL_REG) & LOSC_BUSY_MASK; clk++) {
if (clk > RTC_TIMEOUT) {
@ -290,7 +356,7 @@ aw_rtc_settime(device_t dev, struct timespec *ts)
}
DELAY(1);
}
RTC_WRITE(sc, sc->rtc_time, rtime);
RTC_WRITE(sc, sc->conf->rtc_time, rtime);
DELAY(RTC_TIMEOUT);

View File

@ -973,4 +973,4 @@ DEFINE_CLASS_1(ccu_a31ng, ccu_a31ng_driver, ccu_a31ng_methods,
sizeof(struct aw_ccung_softc), aw_ccung_driver);
EARLY_DRIVER_MODULE(ccu_a31ng, simplebus, ccu_a31ng_driver,
ccu_a31ng_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
ccu_a31ng_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_LAST);

View File

@ -825,4 +825,4 @@ DEFINE_CLASS_1(ccu_a64ng, ccu_a64ng_driver, ccu_a64ng_methods,
sizeof(struct aw_ccung_softc), aw_ccung_driver);
EARLY_DRIVER_MODULE(ccu_a64ng, simplebus, ccu_a64ng_driver,
ccu_a64ng_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
ccu_a64ng_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_LAST);

View File

@ -786,4 +786,4 @@ DEFINE_CLASS_1(ccu_a83tng, ccu_a83tng_driver, ccu_a83tng_methods,
sizeof(struct aw_ccung_softc), aw_ccung_driver);
EARLY_DRIVER_MODULE(ccu_a83tng, simplebus, ccu_a83tng_driver,
ccu_a83tng_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
ccu_a83tng_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_LAST);

View File

@ -787,4 +787,4 @@ DEFINE_CLASS_1(ccu_h3ng, ccu_h3ng_driver, ccu_h3ng_methods,
sizeof(struct aw_ccung_softc), aw_ccung_driver);
EARLY_DRIVER_MODULE(ccu_h3ng, simplebus, ccu_h3ng_driver,
ccu_h3ng_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
ccu_h3ng_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_LAST);