<commit_before>/*
 * battclock.c
 * 
 * Test the Oki MSM6242B / Ricoh RP5C01 battery-backed clock.
 * 
 * Written & released by <NAME> <<EMAIL>>
 * 
 * This is free and unencumbered software released into the public domain.
 * See the file COPYING for more details, or visit <http://unlicense.org>.
 */

#include "systest.h"

struct msm6242 {
    uint8_t _0[3], sec1;
    uint8_t _1[3], sec10;
    uint8_t _2[3], min1;
    uint8_t _3[3], min10;
    uint8_t _4[3], hr1;
    uint8_t _5[3], hr10;
    uint8_t _6[3], day1;
    uint8_t _7[3], day10;
    uint8_t _8[3], mon1;
    uint8_t _9[3], mon10;
    uint8_t _a[3], yr1;
    uint8_t _b[3], yr10;
    uint8_t _c[3], day_of_week;
    uint8_t _d[3], ctl_d;
    uint8_t _e[3], ctl_e;
    uint8_t _f[3], ctl_f;
};

struct rp5c01 {
    uint8_t _0[3], sec1;
    uint8_t _1[3], sec10;
    uint8_t _2[3], min1;
    uint8_t _3[3], min10;
    uint8_t _4[3], hr1;
    uint8_t _5[3], hr10;
    uint8_t _6[3], day_of_week;
    uint8_t _7[3], day1;
    uint8_t _8[3], day10;
    uint8_t _9[3], mon1;
    uint8_t _a[3], mon10;
    uint8_t _b[3], yr1;
    uint8_t _c[3], yr10;
    uint8_t _d[3], ctl_d;
    uint8_t _e[3], ctl_e;
    uint8_t _f[3], ctl_f;
};

enum bc_type {
    BC_RP5C01 = 0,
    BC_MSM6242,
    BC_NONE
};

/* This function has to be careful: 
 * 1. We do not know whether we have a RP5C01 or MSM6242 chip, and they are 
 *    not register compatible. 
 * 2. We don't want to mess up the existing date/time setting. */
static enum bc_type detect_clock_at(uint32_t base)
{
    volatile struct rp5c01 *rp5c01 = (struct rp5c01 *)base;
    uint8_t t;

    /* Clear all test/stop/reset flags, preserve MSM6242 24h/12h flag. */
    rp5c01->ctl_e = 0;
    rp5c01->ctl_f = rp5c01->ctl_f & 4;
    /* Check that flags really are cleared. If not, there's no clock chip. */
    if (((rp5c01->ctl_e & 0xf) != 0) || ((rp5c01->ctl_f & 0xb) != 0))
        return BC_NONE;

    /* Put RP5C01 into MODE 01 (MSM6242: set HOLD flag). */
    rp5c01->ctl_d = 1;
    if ((rp5c01->ctl_d & 0x9) != 1)
        return BC_NONE;

    /* MSM6242: Check BUSY flag and wait up to 10ms for it to clear. */
    t = 10;
    while ((rp5c01->ctl_d & 2) && --t) {
        rp5c01->ctl_d = 0;
        delay_ms(1);
        rp5c01->ctl_d = 1;
    }
    /* BUSY can't still be set. Should only be set a few usec per second. */
    if ((rp5c01->ctl_d & 0xb) != 1) /* checks ADJ,BUSY,HOLD flags */
        return BC_NONE;

    /* Write a non-existent register (in RP5C01 MODE 01) */
    t = rp5c01->yr10;
    rp5c01->yr10 = 5;
    if (((rp5c01->ctl_d & 0xf) == 1) && ((rp5c01->yr10 & 0xf) == 0)) {
        /* RP5C01? */
        rp5c01->ctl_d = 8; /* MODE 00, TIMER_EN */
        if ((rp5c01->ctl_d & 0xf) == 8)
            return BC_RP5C01;
    } else {
        /* MSM6242? */
        rp5c01->yr10 = t; /* restore day-of-week */
        rp5c01->ctl_d = 0; /* clear HOLD */
        if ((rp5c01->ctl_d & 0x9) == 0) /* check ADJ & HOLD are clear */
            return BC_MSM6242;
    }

    return BC_NONE;
}

static enum bc_type detect_clock(uint32_t *base)
{
    uint32_t bases[] = { 0xdc0000, 0xd80000 };
    enum bc_type bc_type;
    unsigned int i, nr = ARRAY_SIZE(bases);

    /* AGA systems never have RTC at 0xD80000 (only early A2000). Indeed A4000 
     * asserts a Bus Error on accesses in the region 0xD00000-0xDFFFFF. */
    if (chipset_type == CHIPSET_aga)
        nr--;

    for (i = 0; i < nr; i++) {
        bc_type = detect_clock_at(*base = bases[i]);
        if (bc_type != BC_NONE)
            break;
    }

    return bc_type;
}

/* Look for out-of-range register values indicating that the configured 
 * time/date is invalid and requires reset. This will often be the case 
 * when running a clock with no backup battery. */
static int bc_time_is_bogus(enum bc_type bc_type, uint32_t base)
{
    int good = 0;
    uint8_t t;

    switch (bc_type) {
    case BC_RP5C01: {
        volatile struct rp5c01 *rp5c01 = (struct rp5c01 *)base;
        rp5c01->ctl_d = 0; /* stop timer */
        good = ((rp5c01->sec1 & 0xf) <= 9)
            && ((rp5c01->sec10 & 0xf) <= 5)
            && ((rp5c01->min1 & 0xf) <= 9)
            && ((rp5c01->min10 & 0xf) <= 5)
            && ((rp5c01->hr1 & 0xf) <= 9)
            && ((rp5c01->hr10 & 0xf) <= 3)
            && ((rp5c01->day1 & 0xf) <= 9)
            && ((rp5c01->day10 & 0xf) <= 3)
            && ((rp5c01->yr1 & 0xf) <= 9);
        rp5c01->ctl_d = 8; /* start timer */
        break;
    }
    case BC_MSM6242: {
        volatile struct msm6242 *msm6242 = (struct msm6242 *)base;
        msm6242->ctl_d = 1; /* set HOLD */
        t = 10;
        while ((msm6242->ctl_d & 2) && --t) { /* wait 10ms for !BUSY */
            msm6242->ctl_d = 0;
            delay_ms(1);
            msm6242->ctl_d = 1;
        }
        good = ((msm6242->sec1 & 0xf) <= 9)
            && ((msm6242->sec10 & 0xf) <= 5)
            && ((msm6242->min1 & 0xf) <= 9)
            && ((msm6242->min10 & 0xf) <= 5)
            && ((msm6242->hr1 & 0xf) <= 9)
            && ((msm6242->hr10 & 0xf) <= 3)
            && ((msm6242->day1 & 0xf) <= 9)
            && ((msm6242->day10 & 0xf) <= 3)
            && ((msm6242->yr1 & 0xf) <= 9);
        msm6242->ctl_d = 0; /* clear HOLD */
        break;
    }
    default:
        break;
    }
    return !good;
}

static void bc_get_time(enum bc_type bc_type, uint32_t base, char *s)
{
    const static uint8_t days_in_month[] = {
        31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
    };
    const static char *mon_str[] = {
        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
    };
    const static char *day_week_str[] = {
        "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
    };

    uint32_t days_since_1900;
    uint8_t sec, min, hr, day, day_week, mon, hr24, t;
    uint16_t yr;
    switch (bc_type) {
    case BC_RP5C01: {
        volatile struct rp5c01 *rp5c01 = (struct rp5c01 *)base;
        rp5c01->ctl_d = 9;
        hr24 = rp5c01->mon10 & 1;
        rp5c01->ctl_d = 0; /* stop timer */
        sec = ((rp5c01->sec10 & 0xf) * 10) + (rp5c01->sec1 & 0xf);
        min = ((rp5c01->min10 & 0xf) * 10) + (rp5c01->min1 & 0xf);
        if (hr24) {<commit_msg>systest: Do not warn that non-existent RTC shows bogus time<commit_after>/*
 * battclock.c
 * 
 * Test the Oki MSM6242B / Ricoh RP5C01 battery-backed clock.
 * 
 * Written & released by <NAME> <<EMAIL>>
 * 
 * This is free and unencumbered software released into the public domain.
 * See the file COPYING for more details, or visit <http://unlicense.org>.
 */

#include "systest.h"

struct msm6242 {
    uint8_t _0[3], sec1;
    uint8_t _1[3], sec10;
    uint8_t _2[3], min1;
    uint8_t _3[3], min10;
    uint8_t _4[3], hr1;
    uint8_t _5[3], hr10;
    uint8_t _6[3], day1;
    uint8_t _7[3], day10;
    uint8_t _8[3], mon1;
    uint8_t _9[3], mon10;
    uint8_t _a[3], yr1;
    uint8_t _b[3], yr10;
    uint8_t _c[3], day_of_week;
    uint8_t _d[3], ctl_d;
    uint8_t _e[3], ctl_e;
    uint8_t _f[3], ctl_f;
};

struct rp5c01 {
    uint8_t _0[3], sec1;
    uint8_t _1[3], sec10;
    uint8_t _2[3], min1;
    uint8_t _3[3], min10;
    uint8_t _4[3], hr1;
    uint8_t _5[3], hr10;
    uint8_t _6[3], day_of_week;
    uint8_t _7[3], day1;
    uint8_t _8[3], day10;
    uint8_t _9[3], mon1;
    uint8_t _a[3], mon10;
    uint8_t _b[3], yr1;
    uint8_t _c[3], yr10;
    uint8_t _d[3], ctl_d;
    uint8_t _e[3], ctl_e;
    uint8_t _f[3], ctl_f;
};

enum bc_type {
    BC_RP5C01 = 0,
    BC_MSM6242,
    BC_NONE
};

/* This function has to be careful: 
 * 1. We do not know whether we have a RP5C01 or MSM6242 chip, and they are 
 *    not register compatible. 
 * 2. We don't want to mess up the existing date/time setting. */
static enum bc_type detect_clock_at(uint32_t base)
{
    volatile struct rp5c01 *rp5c01 = (struct rp5c01 *)base;
    uint8_t t;

    /* Clear all test/stop/reset flags, preserve MSM6242 24h/12h flag. */
    rp5c01->ctl_e = 0;
    rp5c01->ctl_f = rp5c01->ctl_f & 4;
    /* Check that flags really are cleared. If not, there's no clock chip. */
    if (((rp5c01->ctl_e & 0xf) != 0) || ((rp5c01->ctl_f & 0xb) != 0))
        return BC_NONE;

    /* Put RP5C01 into MODE 01 (MSM6242: set HOLD flag). */
    rp5c01->ctl_d = 1;
    if ((rp5c01->ctl_d & 0x9) != 1)
        return BC_NONE;

    /* MSM6242: Check BUSY flag and wait up to 10ms for it to clear. */
    t = 10;
    while ((rp5c01->ctl_d & 2) && --t) {
        rp5c01->ctl_d = 0;
        delay_ms(1);
        rp5c01->ctl_d = 1;
    }
    /* BUSY can't still be set. Should only be set a few usec per second. */
    if ((rp5c01->ctl_d & 0xb) != 1) /* checks ADJ,BUSY,HOLD flags */
        return BC_NONE;

    /* Write a non-existent register (in RP5C01 MODE 01) */
    t = rp5c01->yr10;
    rp5c01->yr10 = 5;
    if (((rp5c01->ctl_d & 0xf) == 1) && ((rp5c01->yr10 & 0xf) == 0)) {
        /* RP5C01? */
        rp5c01->ctl_d = 8; /* MODE 00, TIMER_EN */
        if ((rp5c01->ctl_d & 0xf) == 8)
            return BC_RP5C01;
    } else {
        /* MSM6242? */
        rp5c01->yr10 = t; /* restore day-of-week */
        rp5c01->ctl_d = 0; /* clear HOLD */
        if ((rp5c01->ctl_d & 0x9) == 0) /* check ADJ & HOLD are clear */
            return BC_MSM6242;
    }

    return BC_NONE;
}

static enum bc_type detect_clock(uint32_t *base)
{
    uint32_t bases[] = { 0xdc0000, 0xd80000 };
    enum bc_type bc_type;
    unsigned int i, nr = ARRAY_SIZE(bases);

    /* AGA systems never have RTC at 0xD80000 (only early A2000). Indeed A4000 
     * asserts a Bus Error on accesses in the region 0xD00000-0xDFFFFF. */
    if (chipset_type == CHIPSET_aga)
        nr--;

    for (i = 0; i < nr; i++) {
        bc_type = detect_clock_at(*base = bases[i]);
        if (bc_type != BC_NONE)
            break;
    }

    return bc_type;
}

/* Look for out-of-range register values indicating that the configured 
 * time/date is invalid and requires reset. This will often be the case 
 * when running a clock with no backup battery. */
static int bc_time_is_bogus(enum bc_type bc_type, uint32_t base)
{
    int good = 0;
    uint8_t t;

    switch (bc_type) {
    case BC_RP5C01: {
        volatile struct rp5c01 *rp5c01 = (struct rp5c01 *)base;
        rp5c01->ctl_d = 0; /* stop timer */
        good = ((rp5c01->sec1 & 0xf) <= 9)
            && ((rp5c01->sec10 & 0xf) <= 5)
            && ((rp5c01->min1 & 0xf) <= 9)
            && ((rp5c01->min10 & 0xf) <= 5)
            && ((rp5c01->hr1 & 0xf) <= 9)
            && ((rp5c01->hr10 & 0xf) <= 3)
            && ((rp5c01->day1 & 0xf) <= 9)
            && ((rp5c01->day10 & 0xf) <= 3)
            && ((rp5c01->yr1 & 0xf) <= 9);
        rp5c01->ctl_d = 8; /* start timer */
        break;
    }
    case BC_MSM6242: {
        volatile struct msm6242 *msm6242 = (struct msm6242 *)base;
        msm6242->ctl_d = 1; /* set HOLD */
        t = 10;
        while ((msm6242->ctl_d & 2) && --t) { /* wait 10ms for !BUSY */
            msm6242->ctl_d = 0;
            delay_ms(1);
            msm6242->ctl_d = 1;
        }
        good = ((msm6242->sec1 & 0xf) <= 9)
            && ((msm6242->sec10 & 0xf) <= 5)
            && ((msm6242->min1 & 0xf) <= 9)
            && ((msm6242->min10 & 0xf) <= 5)
            && ((msm6242->hr1 & 0xf) <= 9)
            && ((msm6242->hr10 & 0xf) <= 3)
            && ((msm6242->day1 & 0xf) <= 9)
            && ((msm6242->day10 & 0xf) <= 3)
            && ((msm6242->yr1 & 0xf) <= 9);
        msm6242->ctl_d = 0; /* clear HOLD */
        break;
    }
    default:
        good = 1;
        break;
    }
    return !good;
}

static void bc_get_time(enum bc_type bc_type, uint32_t base, char *s)
{
    const static uint8_t days_in_month[] = {
        31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
    };
    const static char *mon_str[] = {
        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
    };
    const static char *day_week_str[] = {
        "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
    };

    uint32_t days_since_1900;
    uint8_t sec, min, hr, day, day_week, mon, hr24, t;
    uint16_t yr;
    switch (bc_type) {
    case BC_RP5C01: {
        volatile struct rp5c01 *rp5c01 = (struct rp5c01 *)base;
        rp5c01->ctl_d = 9;
        hr24 = rp5c01->mon10 & 1;
        rp5c01->ctl_d = 0; /* stop timer */
        sec = ((rp5c01->sec10 & 0xf) * 10) + (rp5c01->sec1 & 0xf);
        min = ((rp5c01->min10 & 0xf) * 10) + (rp5c01->min1 & 0xf);
        if (hr24) {
{'fraction_non_alphanumeric': 0.15563391569161725, 'fraction_numerical': 0.10121814393332265, 'mean_word_length': 2.0827569169960474, 'pattern_counts': {'":': 0, '<': 49, '<?xml version=': 0, '>': 115, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2182724', 'n_tokens_mistral': 5829, 'n_tokens_neox': 5125, 'n_words': 1325}