1 /*
2  * Copyright (c) 2019 Intel Corp.
3  * SPDX-License-Identifier: Apache-2.0
4  *
5  * This barebones driver enables the use of the PC AT-style RTC
6  * (the so-called "CMOS" clock) as a primitive, 1Hz monotonic counter.
7  *
8  * Reading a reliable value from the RTC is a fairly slow process, because
9  * we use legacy I/O ports and do a lot of iterations with spinlocks to read
10  * the RTC state. Plus we have to read the state multiple times because we're
11  * crossing clock domains (no pun intended). Use accordingly.
12  */
13 
14 #define DT_DRV_COMPAT motorola_mc146818
15 
16 #include <zephyr/drivers/counter.h>
17 #include <zephyr/device.h>
18 #include <soc.h>
19 
20 /* The "CMOS" device is accessed via an address latch and data port. */
21 
22 #define X86_CMOS_ADDR (DT_INST_REG_ADDR_BY_IDX(0, 0))
23 #define X86_CMOS_DATA (DT_INST_REG_ADDR_BY_IDX(0, 1))
24 
25 /*
26  * A snapshot of the RTC state, or at least the state we're
27  * interested in. This struct should not be modified without
28  * serious consideration, for two reasons:
29  *
30  *	1. Order of the element is important, and must correlate
31  *	   with addrs[] and NR_BCD_VALS (see below), and
32  *	2. if it doesn't remain exactly 8 bytes long, the
33  *	   type-punning to compare states will break.
34  */
35 
36 struct state {
37 	uint8_t second,
38 	     minute,
39 	     hour,
40 	     day,
41 	     month,
42 	     year,
43 	     status_a,
44 	     status_b;
45 };
46 
47 /*
48  * If the clock is in BCD mode, the first NR_BCD_VALS
49  * values in 'struct state' are BCD-encoded.
50  */
51 
52 #define NR_BCD_VALS 6
53 
54 /*
55  * Indices into the CMOS address space that correspond to
56  * the members of 'struct state'.
57  */
58 
59 const uint8_t addrs[] = { 0, 2, 4, 7, 8, 9, 10, 11 };
60 
61 /*
62  * Interesting bits in 'struct state'.
63  */
64 
65 #define STATUS_B_24HR	0x02	/* 24-hour (vs 12-hour) mode */
66 #define STATUS_B_BIN	0x01	/* binary (vs BCD) mode */
67 #define HOUR_PM		0x80	/* high bit of hour set = PM */
68 
69 /*
70  * Read a value from the CMOS. Because of the address latch,
71  * we have to spinlock to make the access atomic.
72  */
73 
read_register(uint8_t addr)74 static uint8_t read_register(uint8_t addr)
75 {
76 	static struct k_spinlock lock;
77 	k_spinlock_key_t k;
78 	uint8_t val;
79 
80 	k = k_spin_lock(&lock);
81 	sys_out8(addr, X86_CMOS_ADDR);
82 	val = sys_in8(X86_CMOS_DATA);
83 	k_spin_unlock(&lock, k);
84 
85 	return val;
86 }
87 
88 /* Populate 'state' with current RTC state. */
89 
read_state(struct state * state)90 void read_state(struct state *state)
91 {
92 	int i;
93 	uint8_t *p;
94 
95 	p = (uint8_t *) state;
96 	for (i = 0; i < sizeof(*state); ++i) {
97 		*p++ = read_register(addrs[i]);
98 	}
99 }
100 
101 /* Convert 8-bit (2-digit) BCD to binary equivalent. */
102 
decode_bcd(uint8_t val)103 static inline uint8_t decode_bcd(uint8_t val)
104 {
105 	return (((val >> 4) & 0x0F) * 10) + (val & 0x0F);
106 }
107 
108 /*
109  * Hinnant's algorithm to calculate the number of days offset from the epoch.
110  */
111 
hinnant(int y,int m,int d)112 static uint32_t hinnant(int y, int m, int d)
113 {
114 	unsigned yoe;
115 	unsigned doy;
116 	unsigned doe;
117 	int era;
118 
119 	y -= (m <= 2);
120 	era = ((y >= 0) ? y : (y - 399)) / 400;
121 	yoe = y - era * 400;
122 	doy = (153 * (m + ((m > 2) ? -3 : 9)) + 2)/5 + d - 1;
123 	doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
124 
125 	return era * 146097 + ((int) doe) - 719468;
126 }
127 
128 /*
129  * Get the Unix epoch time (assuming UTC) read from the CMOS RTC.
130  * This function is long, but linear and easy to follow.
131  */
132 
get_value(const struct device * dev,uint32_t * ticks)133 int get_value(const struct device *dev, uint32_t *ticks)
134 {
135 	struct state state, state2;
136 	uint64_t *pun = (uint64_t *) &state;
137 	uint64_t *pun2 = (uint64_t *) &state2;
138 	bool pm;
139 	uint32_t epoch;
140 
141 	ARG_UNUSED(dev);
142 
143 	/*
144 	 * Read the state until we see the same state twice in a row.
145 	 */
146 
147 	read_state(&state2);
148 	do {
149 		state = state2;
150 		read_state(&state2);
151 	} while (*pun != *pun2);
152 
153 	/*
154 	 * Normalize the state; 12hr -> 24hr, BCD -> decimal.
155 	 * The order is a bit awkward because we need to interpret
156 	 * the HOUR_PM flag before we adjust for BCD.
157 	 */
158 
159 	if ((state.status_b & STATUS_B_24HR) != 0U) {
160 		pm = false;
161 	} else {
162 		pm = ((state.hour & HOUR_PM) == HOUR_PM);
163 		state.hour &= ~HOUR_PM;
164 	}
165 
166 	if ((state.status_b & STATUS_B_BIN) == 0U) {
167 		uint8_t *cp = (uint8_t *) &state;
168 		int i;
169 
170 		for (i = 0; i < NR_BCD_VALS; ++i) {
171 			*cp = decode_bcd(*cp);
172 			++cp;
173 		}
174 	}
175 
176 	if (pm) {
177 		state.hour = (state.hour + 12) % 24;
178 	}
179 
180 	/*
181 	 * Convert date/time to epoch time. We don't care about
182 	 * timezones here, because we're just creating a mapping
183 	 * that results in a monotonic clock; the absolute value
184 	 * is irrelevant.
185 	 */
186 
187 	epoch = hinnant(state.year + 2000, state.month, state.day);
188 	epoch *= 86400; /* seconds per day */
189 	epoch += state.hour * 3600; /* seconds per hour */
190 	epoch += state.minute * 60; /* seconds per minute */
191 	epoch += state.second;
192 
193 	*ticks = epoch;
194 	return 0;
195 }
196 
197 static const struct counter_config_info info = {
198 	.max_top_value = UINT_MAX,
199 	.freq = 1
200 };
201 
202 static const struct counter_driver_api api = {
203 	.get_value = get_value
204 };
205 
206 DEVICE_DT_INST_DEFINE(0, NULL, NULL, NULL, &info, POST_KERNEL,
207 		      CONFIG_COUNTER_INIT_PRIORITY, &api);
208