summaryrefslogtreecommitdiffstats
path: root/src/drivers/ti/tps65913/tps65913rtc.c
blob: 1ee0d81462fc5f362f263ed6b905e110fd345398 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/* SPDX-License-Identifier: GPL-2.0-only */

#include <bcd.h>
#include <console/console.h>
#include <device/i2c_simple.h>
#include <rtc.h>
#include <stdint.h>

enum TPS65913_RTC_REG {
	TPS65913_SECONDS_REG		= 0x00,
	TPS65913_MINUTES_REG		= 0x01,
	TPS65913_HOURS_REG		= 0x02,
	TPS65913_DAYS_REG		= 0x03,
	TPS65913_MONTHS_REG		= 0x04,
	TPS65913_YEARS_REG		= 0x05,
	TPS65913_WEEKS_REG		= 0x06,
	TPS65913_RTC_CTRL_REG		= 0x10,
	TPS65913_RTC_STATUS_REG	= 0x11,
	TPS65913_RTC_INTERRUPTS_REG	= 0x12,
};

enum {
	TPS65913_RTC_CTRL_STOP		= (1 << 0),
	TPS65913_RTC_CTRL_GET_TIME	= (1 << 6),

	TPS65913_RTC_STATUS_RUN	= (1 << 1),
	TPS65913_RTC_RUNNING		= (1 << 1),
	TPS65913_RTC_FROZEN		= (0 << 1),
};

static inline uint8_t tps65913_read(enum TPS65913_RTC_REG reg)
{
	uint8_t val;
	i2c_readb(CONFIG_DRIVERS_TI_TPS65913_RTC_BUS,
		  CONFIG_DRIVERS_TI_TPS65913_RTC_ADDR, reg, &val);
	return val;
}

static inline void tps65913_write(enum TPS65913_RTC_REG reg, uint8_t val)
{
	i2c_writeb(CONFIG_DRIVERS_TI_TPS65913_RTC_BUS,
		   CONFIG_DRIVERS_TI_TPS65913_RTC_ADDR, reg, val);
}

static void tps65913_rtc_ctrl_clear(uint8_t bit)
{
	uint8_t control = tps65913_read(TPS65913_RTC_CTRL_REG);

	control &= ~bit;
	tps65913_write(TPS65913_RTC_CTRL_REG, control);
}

static void tps65913_rtc_ctrl_set(uint8_t bit)
{
	uint8_t control = tps65913_read(TPS65913_RTC_CTRL_REG);

	control |= TPS65913_RTC_CTRL_GET_TIME;
	tps65913_write(TPS65913_RTC_CTRL_REG, control);
}

static int tps65913_is_rtc_running(void)
{
	uint8_t status = tps65913_read(TPS65913_RTC_STATUS_REG);
	return ((status & TPS65913_RTC_STATUS_RUN) == TPS65913_RTC_RUNNING);
}

/*
 * This function ensures that current time is copied to shadow registers. Then a
 * normal read on TC registers reads from the shadow instead of current TC
 * registers. This helps prevent the accidental change in counters while
 * reading. In order to ensure that the current TC registers are copied into
 * shadow registers, GET_TIME bit needs to be set to 0 and then to 1.
 */
static void tps65913_rtc_shadow(void)
{
	tps65913_rtc_ctrl_clear(TPS65913_RTC_CTRL_GET_TIME);
	tps65913_rtc_ctrl_set(TPS65913_RTC_CTRL_GET_TIME);
}

static int tps65913_rtc_stop(void)
{
	/* Clearing stop bit freezes RTC */
	tps65913_rtc_ctrl_clear(TPS65913_RTC_CTRL_STOP);

	if (tps65913_is_rtc_running()) {
		printk(BIOS_ERR, "Could not stop RTC\n");
		return 1;
	}

	return 0;
}

static int tps65913_rtc_start(void)
{
	/* Setting stop bit starts RTC */
	tps65913_rtc_ctrl_set(TPS65913_RTC_CTRL_STOP);

	if (!tps65913_is_rtc_running()) {
		printk(BIOS_ERR, "Could not start RTC\n");
		return 1;
	}

	return 0;
}

int rtc_set(const struct rtc_time *time)
{
	/* Before setting the time, ensure that rtc is stopped */
	if (tps65913_rtc_stop())
		return 1;

	tps65913_write(TPS65913_SECONDS_REG, bin2bcd(time->sec));
	tps65913_write(TPS65913_MINUTES_REG, bin2bcd(time->min));
	tps65913_write(TPS65913_HOURS_REG, bin2bcd(time->hour));
	tps65913_write(TPS65913_DAYS_REG, bin2bcd(time->mday));
	tps65913_write(TPS65913_MONTHS_REG, bin2bcd(time->mon));
	tps65913_write(TPS65913_YEARS_REG, bin2bcd(time->year));

	/* Re-start rtc */
	if (tps65913_rtc_start())
		return 1;

	return 0;
}

int rtc_get(struct rtc_time *time)
{
	tps65913_rtc_shadow();

	time->sec = bcd2bin(tps65913_read(TPS65913_SECONDS_REG) & 0x7f);
	time->min = bcd2bin(tps65913_read(TPS65913_MINUTES_REG) & 0x7f);
	time->hour = bcd2bin(tps65913_read(TPS65913_HOURS_REG) & 0x3f);
	time->mday = bcd2bin(tps65913_read(TPS65913_DAYS_REG) & 0x3f);
	time->mon = bcd2bin(tps65913_read(TPS65913_MONTHS_REG) & 0x1f);
	time->year = bcd2bin(tps65913_read(TPS65913_YEARS_REG) & 0xff);

	return 0;
}