# FreeBSD doesn't read RTC on boot



## JohnnySorocil (Aug 12, 2020)

I am using external I2C RTC module (DS3231) with armv7 Orange Pi Zero board (which doesn't have RTC onboard, hardware is there but there is no RTC battery socket). Module is recognized and works (after patching device tree):


```
root@arm2:~ # sysctl dev.ds3231
dev.ds3231.0.32khz_enable: 1
dev.ds3231.0.sqw_mode: interrupt
dev.ds3231.0.sqw_freq: 1024
dev.ds3231.0.bbsqw: 0
dev.ds3231.0.temp_conv: 0
dev.ds3231.0.temperature: 30.0C
dev.ds3231.0.%parent: iicbus0
dev.ds3231.0.%pnpinfo: name=ds3231@68 compat=maxim,ds3231
dev.ds3231.0.%location: addr=0xd0
dev.ds3231.0.%driver: ds3231
dev.ds3231.0.%desc: Maxim DS3231 RTC
dev.ds3231.%parent:
```

Setting date works - system will have correct date until power is disconnected. After power is connected again, system time is not read from RTC.

After powercycle:

```
root@arm2:~ # date
Fri Jan  1 00:22:25 UTC 2010
root@arm2:~ # ./ds1307 -a 0x68 -r
09:37:59 12/08/2020
```

NTP is not used.  ds1307 program is used to directly read registers from I2C module.


----------



## JohnnySorocil (Aug 12, 2020)

Current idea is that kernel uses onboard rtc (which doesn't have battery) and then external i2c module.

```
dmesg | grep registered
FreeBSD is a registered trademark of The FreeBSD Foundation.
rtc0: registered as a time-of-day clock, resolution 1.000000s
ds32310: registered as a time-of-day clock, resolution 1.000000s
```

Disabling internal rtc (via device tree overlay) results in kernel panic & hang:

```
---<<BOOT>>---                                                                                                                                                                                
Copyright (c) 1992-2019 The FreeBSD Project.                                                                                                                                                  
Copyright (c) 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994                                                                                                                      
        The Regents of the University of California. All rights reserved.                                                                                                                     
FreeBSD is a registered trademark of The FreeBSD Foundation.                                                                                                                                  
FreeBSD 12.1-RELEASE r354233 GENERIC arm                                                                                                                                                      
FreeBSD clang version 8.0.1 (tags/RELEASE_801/final 366581) (based on LLVM 8.0.1)                                                                                                             
VT: init without driver.                                                                                                                                                                      
module_register: cannot register ofwbus/pcib from kernel; already loaded from kernel                                                                                                          
Module ofwbus/pcib failed to register: 17                                                      
module_register: cannot register simplebus/pcib from kernel; already loaded from kernel
Module simplebus/pcib failed to register: 17
CPU: ARM Cortex-A7 r0p5 (ECO: 0x00000000)
CPU Features:        
  Multiprocessing, Thumb2, Security, Virtualization, Generic Timer, VMSAv7,
  PXN, LPAE, Coherent Walk                                                                     
Optional instructions:                      
  SDIV/UDIV, UMULL, SMULL, SIMD(ext)
LoUU:2 LoC:3 LoUIS:2                                                                           
Cache level 1:                   
 32KB/64B 4-way data cache WB Read-Alloc Write-Alloc
 32KB/32B 2-way instruction cache Read-Alloc
Cache level 2:                                                                                 
 512KB/64B 8-way unified cache WB Read-Alloc Write-Alloc
real memory  = 536289280 (511 MB)        
avail memory = 509890560 (486 MB)
No PSCI/SMCCC call function found   
FreeBSD/SMP: Multiprocessor System Detected: 4 CPUs
random: unblocking device.              
random: entropy device external interface
kbd0 at kbdmux0                                                                                
ofwbus0: <Open Firmware Device Tree> 
ofw_clkbus0: <OFW clocks bus> on ofwbus0
clk_fixed0: <Fixed clock> on ofw_clkbus0
clk_fixed1: <Fixed clock> on ofw_clkbus0
simplebus0: <Flattened device tree simple bus> on ofwbus0
regfix0: <Fixed Regulator> on ofwbus0
regfix1: <Fixed Regulator> on ofwbus0
regfix2: <Fixed Regulator> on ofwbus0
regfix3: <Fixed Regulator> on ofwbus0
ccu_h3ng0: <Allwinner H3/H5 Clock Control Unit NG> mem 0x1c20000-0x1c203ff on simplebus0
ccu_h3ng0: Clock apb2 have unknown parent: osc32k
ccu_h3ng0: Clock ahb1 have unknown parent: osc32k
ccu_h3ng0: Clock cpux have unknown parent: osc32k
panic: cannot finalize clkdom initialization

cpuid = 0
time = 1
Uptime: 1s
Automatic reboot in 15 seconds - press a key on the console to abort
Rebooting...
Reset: watchdog device has not been initialized
Reset failed!
```

Is it possible to tell kernel to use external RTC instead of internal?


----------



## mark_j (Aug 14, 2020)

JohnnySorocil said:


> I am using external I2C RTC module (DS3231) with armv7 Orange Pi Zero board (which doesn't have RTC onboard, hardware is there but there is no RTC battery socket). Module is recognized and works (after patching device tree):
> 
> 
> ```
> ...


Have you enabled i2c0 in efi.config.txt?
What was your dtc command?


----------



## obsigna (Aug 14, 2020)

Quite probably you need to compile a custom kernel with the aw_rtc device removed:

```
...
nodevice        aw_rtc            # Allwinner On-chip RTC
...
```


----------



## mark_j (Aug 14, 2020)

I assumed he'd already compiled that in; probably a wrong assumption.


----------



## JohnnySorocil (Aug 14, 2020)

mark_j said:


> Have you enabled i2c0 in efi.config.txt?
> What was your dtc command?


Do I need that file if I am not using EFI for booting?

DT files for enabling i2c0 (on which module is connected) and DS3231 module:

```
/dts-v1/;
/plugin/;

/ {
        compatible = "xunlong,orangepi-zero", "allwinner,sun8i-h2-plus";

        fragment@0 {
                target-path = "/aliases";
                __overlay__ {
                        i2c0 = "/soc/i2c@01c2ac00";
                };
        };

        fragment@1 {
                target = <&i2c0>;
                __overlay__ {
                        status = "okay";
                };
        };
};
```


```
/dts-v1/;
/plugin/;

/ {
        compatible = "xunlong,orangepi-zero", "allwinner,sun8i-h2-plus";

        fragment@0 {
                target = <&i2c0>;
                __overlay__ {
                        #address-cells = <1>;
                        #size-cells = <0>;
                        status = "okay";

                        ds3231: ds3231@68 {
                                                compatible = "maxim,ds3231";
                                                reg = <0x68>;
                                                status = "okay";
                        };
                };
        };

        __overrides__ {
                ds3231 = <&ds3231>,"status";
        };
};
```

Files are compiled with:

```
dtc -I dts -O dtb -@ -o /boot/dtb/overlays/ds3231.dtbo /opt/dtb/ds3231.dts
dtc -I dts -O dtb -@ -o /boot/dtb/overlays/sun8i-h3-i2c0.dtbo /opt/dtb/sun8i-h3-i2c0.dts
```

They are enabled in /boot/loader.conf:

```
fdt_overlays="sun8i-h3-sid.dtbo,sun8i-h3-ths.dtbo,sun8i-h3-i2c0.dtbo,sun8i-h3-i2c1.dtbo,ds3231.dtbo"
```

and loaded during boot:

```
/boot/dtb/overlays/sun8i-h3-i2c0.dtbo size=0x180                                                                                                                                             
/boot/dtb/overlays/sun8i-h3-i2c1.dtbo size=0x1a6                                                                                                                                            
/boot/dtb/overlays/ds3231.dtbo size=0x361                                                                                                                                                    
/boot/dtb/overlays/flash.dtbo size=0x284                                                                                                                                                     
Kernel entry at 0x55000180...
...
```



obsigna said:


> Quite probably you need to compile a custom kernel with the aw_rtc device removed:
> 
> ```
> ...
> ...



Did not try that before, assumption was that driver is not active if there is not device tree to activate that driver.
Anyway, tried it now, -CURRENT kernel compiled on that board (not cross compile):

```
% cat usr/src/sys/arm/conf/MYKERNEL
include GENERIC
ident   MYKERNEL

nodevice        aw_rtc            # Allwinner On-chip RTC
```

Device tree overlay for disabling internal RTC is not enabled and after connecting serial console and booting that kernel:

```
...
simplebus0: <Flattened device tree simple bus> on ofwbus0                                                                                                                                     
regfix0: <Fixed Regulator> on ofwbus0                                                                                                                                                         
regfix1: <Fixed Regulator> on ofwbus0                                                                                                                                                         
regfix2: <Fixed Regulator> on ofwbus0                                                                                                                                                         
regfix3: <Fixed Regulator> on ofwbus0                                                                                                                                                         
ccu_h3ng0: <Allwinner H3/H5 Clock Control Unit NG> mem 0x1c20000-0x1c203ff on simplebus0
ccu_h3ng0: Clock apb2 have unknown parent: osc32k
ccu_h3ng0: Clock ahb1 have unknown parent: osc32k
ccu_h3ng0: Clock cpux have unknown parent: osc32k
panic: cannot finalize clkdom initialization
                      
cpuid = 0                                                                                                                                                                                     
time = 1  
KDB: stack backtrace:                                                                                                                                                                         
db_trace_self() at db_trace_self                                                                                                                                                              
         pc = 0xc0578934  lr = 0xc0079eb0 (db_trace_self_wrapper+0x30)                                                                                                                        
         sp = 0xc0d14b98  fp = 0xc0d14cb0                                                                                                                                                     
db_trace_self_wrapper() at db_trace_self_wrapper+0x30                                                                                                                                         
         pc = 0xc0079eb0  lr = 0xc02bf54c (vpanic+0x174)                                                                                                                                      
         sp = 0xc0d14cb8  fp = 0xc0d14cd8                                                                                                                                                     
         r4 = 0x00000100  r5 = 0xc0a42b20                                                                                                                                                     
         r6 = 0xc06a0af6  r7 = 0x00000000                                                                                                                                                     
vpanic() at vpanic+0x174                                                                                                                                                                      
         pc = 0xc02bf54c  lr = 0xc02bf308 (doadump)                                                                                                                                           
         sp = 0xc0d14ce0  fp = 0xc0d14ce4                                                                                                                                                     
         r4 = 0x000008c0  r5 = 0xd02f9800                                                                                                                                                     
         r6 = 0x00000071  r7 = 0xc0d14d18                                                                                                                                                     
         r8 = 0xc0d14d08  r9 = 0x00000001                                                                                                                                                     
        r10 = 0x00000000                                                                                                                                                                      
doadump() at doadump                                                                                                                                                                          
         pc = 0xc02bf308  lr = 0xc05be870 (aw_ccung_write_4)                                                                                                                                  
         sp = 0xc0d14cec  fp = 0xc0d14d50                                                                                                                                                     
         r4 = 0x00000000  r5 = 0xc0d14ce4                                                                                                                                                     
         r6 = 0xc02bf308 r10 = 0xc0d14cec                                                                                                                                                     
aw_ccung_write_4() at aw_ccung_write_4                                                                                                                                                        
         pc = 0xc05be870  lr = 0xc02fc1b8 (device_attach+0x50c)                                                                                                                               
         sp = 0xc0d14d58  fp = 0xc0d14da0                                                                                                                                                     
         r4 = 0x00000000  r5 = 0xc0ae30a4                                                                                                                                                     
         r6 = 0x80040001 r10 = 0x80000803                                                                                                                                                     
device_attach() at device_attach+0x50c                                                                                                                                                        
         pc = 0xc02fc1b8  lr = 0xc02fbc10 (device_probe_and_attach+0x8c)                                                                                                                      
         sp = 0xc0d14da8  fp = 0xc0d14dc0                                                                                                                                                     
         r4 = 0xd00de400  r5 = 0xc2471f80                                                                                                                                                     
         r6 = 0x5e4a6f28  r7 = 0x00000000                                                                                                                                                     
         r8 = 0xc0a615a8  r9 = 0xc0a615ac                                                                                                                                                     
        r10 = 0xc0a4278c                                                                                                                                                                      
device_probe_and_attach() at device_probe_and_attach+0x8c                                                                                                                                     
         pc = 0xc02fbc10  lr = 0xc02fdf5c (bus_generic_new_pass+0xb0)                                                                                                                         
         sp = 0xc0d14dc8  fp = 0xc0d14de0                                                                                                                                                     
         r4 = 0xd00de400  r5 = 0xc0848ef8                                                                                                                                                     
         r6 = 0xc088cba0 r10 = 0xc0a4278c                                                                                                                                                     
bus_generic_new_pass() at bus_generic_new_pass+0xb0                                                                                                                                           
         pc = 0xc02fdf5c  lr = 0xc02fdfa8 (bus_generic_new_pass+0xfc)                                                                                                                         
         sp = 0xc0d14de8  fp = 0xc0d14e00                                                                                                                                                     
         r4 = 0xd00df980  r5 = 0xc0848ef8                                                                                                                                                     
         r6 = 0xd00e0000  r7 = 0x00000000                                                                                                                                                     
         r8 = 0xc0a615a8 r10 = 0xc0a4278c 
bus_generic_new_pass() at bus_generic_new_pass+0xfc                                                                                                                                           
         pc = 0xc02fdfa8  lr = 0xc02fdfa8 (bus_generic_new_pass+0xfc)                                                                                                                         
         sp = 0xc0d14e08  fp = 0xc0d14e20                                                                                                                                                     
         r4 = 0xd00dfc00  r5 = 0xc0848ef8                                                                                                                                                     
         r6 = 0xd00e0000  r7 = 0x00000000                                                                                                                                                     
         r8 = 0xc0a615a8 r10 = 0xc0a4278c                                                                                                                                                     
bus_generic_new_pass() at bus_generic_new_pass+0xfc                                                                                                                                           
         pc = 0xc02fdfa8  lr = 0xc02fdfa8 (bus_generic_new_pass+0xfc)                                                                                                                         
         sp = 0xc0d14e28  fp = 0xc0d14e40                                                                                                                                                     
         r4 = 0xd00dfc80  r5 = 0xc0848ef8                                                                                                                                                     
         r6 = 0xd00e0000  r7 = 0x00000000                                                                                                                                                     
         r8 = 0xc0a615a8 r10 = 0xc0a4278c                                                                                                                                                     
bus_generic_new_pass() at bus_generic_new_pass+0xfc                                                                                                                                           
         pc = 0xc02fdfa8  lr = 0xc02f9490 (bus_set_pass+0x54)                                                                                                                                 
         sp = 0xc0d14e48  fp = 0xc0d14e60                                                                                                                                                     
         r4 = 0xd00e78a0  r5 = 0xc0848ef8                                                                                                                                                     
         r6 = 0xd00e0000  r7 = 0xc0a615a8                                                                                                                                                     
         r8 = 0x7fffffff r10 = 0xc0a4278c                                                                                                                                                     
bus_set_pass() at bus_set_pass+0x54                                                                                                                                                           
         pc = 0xc02f9490  lr = 0xc0255904 (mi_startup+0x2a4)                                                                                                                                  
         sp = 0xc0d14e68  fp = 0xc0d14e90                                                                                                                                                     
         r4 = 0xc08a48b4  r5 = 0xc0a42788
         r6 = 0x00800001  r7 = 0x00000000
         r8 = 0x03800000  r9 = 0xc0a42794
mi_startup() at mi_startup+0x2a4
         pc = 0xc0255904  lr = 0xc00002c4 (_start+0x144)
         sp = 0xc0d14e98  fp = 0x00000000
         r4 = 0xc00003f8  r5 = 0xc0b1c000
         r6 = 0x00000000  r7 = 0x00c52078
         r8 = 0xc0cdd000  r9 = 0x5af1e780
        r10 = 0x00000000
_start() at _start+0x144
         pc = 0xc00002c4  lr = 0xc00002c4 (_start+0x144)
         sp = 0xc0d14e98  fp = 0x00000000
KDB: enter: panic
[ thread pid 0 tid 100000 ]
Stopped at      kdb_enter+0x58: ldrb    r15, [r15, r15, ror r15]!
```


----------



## mark_j (Aug 15, 2020)

JohnnySorocil said:


> Do I need that file if I am not using EFI for booting?



Sorry, I don't know the Orange Pi but I was assuming if it uses the term "Pi" it's emulating/copying the boot of the Raspberry Pi. My mistake.

If you're saying that the ds1307 program is reading that address correctly on the i2c then I have to suspect it's your RTC.

Is the battery up to voltage? What's the name of the breakout with the DS3231 on it?


----------



## obsigna (Aug 15, 2020)

JohnnySorocil said:


> ...
> Did not try that before, assumption was that driver is not active if there is not device tree to activate that driver.
> Anyway, tried it now, -CURRENT kernel compiled on that board (not cross compile):
> 
> ...



Well, I see now, that there are more than one Allwinner RTC drivers in the kernel sources, and `man /usr/src/share/man/man4/man4.arm/aw_rtc.4` is not the driver for <Allwinner H3/H5 Clock Control Unit NG> chips, but for different devices, and now I see that removing this driver from the kernel would of course make no difference, it wasn’t used anyway - sorry for the fake news.

The driver for the ccu_h3ng device is not mentioned in the kernel configuration file. Instead it is referred to here:
/usr/src/sys/arm/allwinner/h3/files.h3

The actual source of the driver is here:
/usr/src/sys/arm/allwinner/clkng/ccu_h3.c

Emanual Vadot (manu) seems to be (have/had) working on all this AW RTCs. He is quite active on the ARM mailing list - https://lists.freebsd.org/mailman/listinfo/freebsd-arm

Perhaps it might be worth to remove the entry of clkng/ccu_h3.c from h3/files.h3 and then try building the kernel once again. In parallel, you want to describe your issue on said mailing list, and probably receive a quick hint or even solution from Emanuel.

Note, I have the DS3231 module running on a Beaglebone Black, and there it simply does its job. I had only an issue with a time offset of 20 h upon restart, but this has been resolved very quickly thanks to Ian Lepore.



			DS3231 on BeagleBone Black with FreeBSD 13-CURRENT exactly 20 h off  backwards


----------



## JohnnySorocil (Sep 18, 2020)

Reporting back:
Asked on mail list
Workaround can be running userland program which reads RTC early in the boot procedure and sets system time:

Apply device tree overlays from post #6 in this thread

ds3231.sh

```
#!/bin/sh

OUTPUT=$(/opt/ds1307 -a 0x68 -r)

DATE=$(echo $OUTPUT | awk '{print $2}')
TIME=$(echo $OUTPUT | awk '{print $1}')

YEAR=$(echo $DATE | cut -d '/' -f 3)
MONTH=$(echo $DATE | cut -d '/' -f 2)
DAY=$(echo $DATE | cut -d '/' -f 1)

HOUR=$(echo $TIME | cut -d ':' -f 1)
MIN=$(echo $TIME | cut -d ':' -f 2)
SEC=$(echo $TIME | cut -d ':' -f 3)

echo "----------------------------------------------------------------------"
echo "Current system time: $(date)"
echo "Current I2C time: $OUTPUT"

date $YEAR$MONTH$DAY$HOUR$MIN.$SEC

echo "----------------------------------------------------------------------"
echo "New system time: $(date)"
```

C program which reads time:

```
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <dev/iicbus/iic.h>
#include <time.h>

#define READ 1
#define WRITE 0


static uint8_t bcd2bin (uint8_t val) { return val - 6 * (val >> 4); }
static uint8_t bin2bcd (uint8_t val) { return val + 6 * (val / 10); }


void usage ( char *pname )  {
  
  printf( "Usage: %s -r [-a <addr>] [-f <iic-dev>]\n", pname );
  printf( "       %s -s [-a <addr>] [-f <iic-dev>]\n", pname );
  exit(1);
}


char i2c_read ( int fd, uint16_t slave, uint8_t offset, uint8_t *buf )  {

  struct iic_msg msg[2];
  struct iic_rdwr_data rdwr;

  msg[0].slave = slave << 1 | !IIC_M_RD;
  msg[0].flags = !IIC_M_RD;
  msg[0].len = sizeof( uint8_t );
  msg[0].buf = &offset;

  msg[1].slave = slave << 1 | IIC_M_RD;
  msg[1].flags = IIC_M_RD;
  msg[1].len = sizeof( uint8_t );
  msg[1].buf = buf;

  rdwr.msgs = msg;
  rdwr.nmsgs = 2;

  if ( ioctl(fd, I2CRDWR, &rdwr) < 0 )  {
    perror("I2CRDWR");
    return(-1);
  }
  
  return(0);
}


char i2c_write ( int fd, uint16_t slave, uint8_t offset, uint8_t val )  {

  uint8_t buf[2];
  struct iic_msg msg;
  struct iic_rdwr_data rdwr;

  buf[0] = offset;
  buf[1] = val;
  msg.slave = slave << 1 | !IIC_M_RD;
  msg.flags = !IIC_M_RD;
  msg.len = 2*sizeof( uint8_t );
  msg.buf = buf;

  rdwr.msgs = &msg;
  rdwr.nmsgs = 1;

  if ( ioctl(fd, I2CRDWR, &rdwr) < 0 )  {
    perror("I2CRDWR");
    return(-1);
  }
  
  return(0);
}


int main ( int argc, char *argv[] )  {

  int i, ch, fd, fl;
  int ss, mm, hh, wd, d, m, y;
  char dev[] = "/dev/iic0";
  uint8_t buf;
  uint16_t slave = 0x68;
  time_t tloc;
  struct tm *ptm;


  if ( argc == 1 )  usage(argv[0]);

  while ( (ch = getopt(argc, argv, "hrsa:f:")) != -1 )  {
    switch (ch)  {
      case 'r': fl = READ;
                break;
      case 's': fl = WRITE;
                break;
      case 'a': sscanf( optarg, "%hX", &slave );
                break;
      case 'f': strcpy( dev, optarg );
                break;
      case 'h':
      default:  usage(argv[0]);
    }
  }
  
  if ( (fd = open(dev, O_RDWR)) < 0 )  {
    perror("open");
    exit(-1);
  }

  if ( fl == WRITE )  {

    tloc = time( &tloc );
    ptm = localtime( &tloc );

    i2c_write( fd, slave, 0, bin2bcd(ptm->tm_sec) );
    i2c_write( fd, slave, 1, bin2bcd(ptm->tm_min) );
    i2c_write( fd, slave, 2, bin2bcd(ptm->tm_hour) );
    i2c_write( fd, slave, 3, bin2bcd(ptm->tm_wday+1) );
    i2c_write( fd, slave, 4, bin2bcd(ptm->tm_mday) );
    i2c_write( fd, slave, 5, bin2bcd(ptm->tm_mon+1) );
    i2c_write( fd, slave, 6, bin2bcd(ptm->tm_year-100) );

    exit(0);
  }

  if ( fl == READ )  {

    i2c_read( fd, slave, 0, &buf );
    ss = bcd2bin(buf & 0x7F);

    i2c_read( fd, slave, 1, &buf );
    mm = bcd2bin(buf & 0x7F);

    i2c_read( fd, slave, 2, &buf );
    hh = bcd2bin(buf & 0x3F);

    i2c_read( fd, slave, 3, &buf );
    wd = bcd2bin((buf & 0x07) - 1);

    i2c_read( fd, slave, 4, &buf );
    d = bcd2bin(buf & 0x3F);

    i2c_read( fd, slave, 5, &buf );
    m = bcd2bin(buf & 0x1F);

    i2c_read( fd, slave, 6, &buf );
    y = 2000 + bcd2bin(buf);

    printf("%.2d:%.2d:%.2d %.2d/%.2d/%.4d\n", hh, mm, ss, d, m, y );
    
    exit(0);
  }

  exit(0);

}
```

Compile it on ARM board with:

```
cc ds1307.c -o /opt/ds1307
```

And then run it early in boot procedure - put  "/opt/ds3231.sh" somewhere in /etc/rc file. Forget correct line, didn't make backup and SD card is repurposed but it was working reliably 
All this fuss could be avoided if Orange Pi Zero board creators routed out battery connector for internal RTC


----------

