1/*
2 * Copyright 2017, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13#include <platsupport/mach/pmic_rtc.h>
14#include <platsupport/delay.h>
15#include "../../services.h"
16#include <utils/util.h>
17
18#define RTCREG_INTSTAT    0x00
19#define RTCREG_INTMASK    0x01
20#define RTCREG_CTRLWMASK  0x02
21#define RTCREG_CTRL       0x03
22#define RTCREG_UPDATE     0x04
23#define RTCREG_WATCHDOG   0x06
24
25#define RTCREG_TIME       0x07
26#define RTCREG_ALARM1     0x0E
27#define RTCREG_ALARM(id)  (RTCREG_ALARM1 + (id) * sizeof(struct rtc_time))
28
29#define RTC_NALARMS      2
30
31/* RTCREG_INTSTAT, RTCREG_INTMASK */
32#define RTCINT(x)         ((x) & 0x3f)
33#define RTCINT_READY      (1U << 4)
34/* RTCREG_CTRLWMASK, RTCREG_CTRL */
35#define RTCCTRL_24HOUR    (1U << 1)
36#define RTCCTRL_BCD       (1U << 0)
37/* RTCREG_UPDATE */
38#define RTCUPDATE_READ    (1U << 4)
39#define RTCUPDATE_WRITE   (1U << 0)
40/* RTCREG_WATCHDOG */
41#define RTCWD_SMPL_EN     (1U << 7)
42#define RTCWD_WDT_EN      (1U << 6)
43#define RTCWD_SMPL_CFG(x) ((x) << 2)
44#define RTCWD_WDT_CFG(x)  ((x) << 0)
45
46/* We can set a 24 hour value for the time, but the RTC always gives us back
47 * flag for AM/PM */
48#define RTC_HOUR_PM       (1U << 6)
49
50static int
51id_valid(pmic_rtc_t* dev, int id)
52{
53    return id >= 0 && id < pmic_rtc_nalarms(dev);
54}
55
56static int
57pmic_rtc_reg_read(pmic_rtc_t* dev, uint8_t reg, void* data, int count)
58{
59    return i2c_kvslave_read(&dev->kvslave, reg, data, count);
60}
61
62static int
63pmic_rtc_reg_write(pmic_rtc_t* dev, uint8_t reg, const void* data, int count)
64{
65    return i2c_kvslave_write(&dev->kvslave, reg, data, count);
66}
67
68static int
69pmic_rtc_update(pmic_rtc_t* dev, uint8_t flag)
70{
71    int ret;
72
73    /* Write to the update register */
74    ret = pmic_rtc_reg_write(dev, RTCREG_UPDATE, &flag, 1);
75    if (ret != 1) {
76        ZF_LOGD("Bus error");
77        return -1;
78    }
79    /* Wait for completion */
80    ps_mdelay(16);
81    return 0;
82}
83
84static int
85pmic_rtc_set_tval(pmic_rtc_t* dev, int base, const struct rtc_time* time)
86{
87    int count;
88    count = pmic_rtc_reg_write(dev, base, time, sizeof(*time));
89    return !(count == sizeof(*time));
90}
91
92static int
93pmic_rtc_get_tval(pmic_rtc_t* dev, int base, struct rtc_time* time)
94{
95    int count;
96    count = pmic_rtc_reg_read(dev, base, time, sizeof(*time));
97    time->hour &= ~RTC_HOUR_PM;
98    return !(count == sizeof(*time));
99}
100
101int
102pmic_rtc_init(i2c_bus_t* i2c, pmic_rtc_t* pmic_rtc)
103{
104    uint8_t data[7];
105    int ret;
106    ret = i2c_slave_init(i2c, MAX77686RTC_BUSADDR,
107                           I2C_SLAVE_ADDR_7BIT, I2C_SLAVE_SPEED_FAST,
108                           0, &pmic_rtc->i2c_slave);
109    if (ret) {
110        ZF_LOGD("Failed to register I2C slave");
111        return -1;
112    }
113
114    ret = i2c_kvslave_init(&pmic_rtc->i2c_slave,
115                           LITTLE8, LITTLE8,
116                           &pmic_rtc->kvslave);
117    if (ret) {
118        ZF_LOGD("Failed to initialize I2C KV-slave lib instance.");
119        return -1;
120    }
121
122    data[RTCREG_INTSTAT  ] = 0x00;
123    data[RTCREG_INTMASK  ] = 0x3F;
124    data[RTCREG_CTRLWMASK] = RTCCTRL_24HOUR | RTCCTRL_BCD;
125    data[RTCREG_CTRL     ] = RTCCTRL_24HOUR;
126    data[RTCREG_UPDATE   ] = 0x00;
127    data[RTCREG_WATCHDOG ] = 0x00;
128    ret = pmic_rtc_reg_write(pmic_rtc, RTCREG_INTSTAT, data, sizeof(data));
129    if (ret != sizeof(data)) {
130        ZF_LOGD("Bus error");
131        return -1;
132    }
133
134    return pmic_rtc_update(pmic_rtc, RTCUPDATE_WRITE);
135}
136
137int
138pmic_rtc_get_time(pmic_rtc_t* pmic_rtc, struct rtc_time* time)
139{
140    if (pmic_rtc_update(pmic_rtc, RTCUPDATE_READ)) {
141        return -1;
142    }
143    return pmic_rtc_get_tval(pmic_rtc, RTCREG_TIME, time);
144}
145
146int
147pmic_rtc_set_time(pmic_rtc_t* pmic_rtc, const struct rtc_time* time)
148{
149    if (pmic_rtc_set_tval(pmic_rtc, RTCREG_TIME, time)) {
150        return -1;
151    }
152    return pmic_rtc_update(pmic_rtc, RTCUPDATE_WRITE);
153}
154
155int
156pmic_rtc_nalarms(pmic_rtc_t* pmic_rtc)
157{
158    return RTC_NALARMS;
159}
160
161int
162pmic_rtc_get_alarm(pmic_rtc_t* pmic_rtc, int id, struct rtc_time* alarm)
163{
164    if (!id_valid(pmic_rtc, id)) {
165        return -1;
166    }
167    if (pmic_rtc_update(pmic_rtc, RTCUPDATE_READ)) {
168        return -1;
169    }
170    return pmic_rtc_get_tval(pmic_rtc, RTCREG_ALARM(id), alarm);
171}
172
173int
174pmic_rtc_set_alarm(pmic_rtc_t* pmic_rtc, int id, const struct rtc_time* alarm)
175{
176    if (!id_valid(pmic_rtc, id)) {
177        return -1;
178    }
179    if (pmic_rtc_set_tval(pmic_rtc, RTCREG_ALARM(id), alarm)) {
180        return -1;
181    }
182    return pmic_rtc_update(pmic_rtc, RTCUPDATE_WRITE);
183}
184