1 |
786 |
skrzyp |
//==========================================================================
|
2 |
|
|
//
|
3 |
|
|
// devs/wallclock/ds1307.inl
|
4 |
|
|
//
|
5 |
|
|
// Wallclock implementation for Dallas 1307
|
6 |
|
|
//
|
7 |
|
|
//==========================================================================
|
8 |
|
|
// ####ECOSGPLCOPYRIGHTBEGIN####
|
9 |
|
|
// -------------------------------------------
|
10 |
|
|
// This file is part of eCos, the Embedded Configurable Operating System.
|
11 |
|
|
// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
|
12 |
|
|
//
|
13 |
|
|
// eCos is free software; you can redistribute it and/or modify it under
|
14 |
|
|
// the terms of the GNU General Public License as published by the Free
|
15 |
|
|
// Software Foundation; either version 2 or (at your option) any later
|
16 |
|
|
// version.
|
17 |
|
|
//
|
18 |
|
|
// eCos is distributed in the hope that it will be useful, but WITHOUT
|
19 |
|
|
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
20 |
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
21 |
|
|
// for more details.
|
22 |
|
|
//
|
23 |
|
|
// You should have received a copy of the GNU General Public License
|
24 |
|
|
// along with eCos; if not, write to the Free Software Foundation, Inc.,
|
25 |
|
|
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
26 |
|
|
//
|
27 |
|
|
// As a special exception, if other files instantiate templates or use
|
28 |
|
|
// macros or inline functions from this file, or you compile this file
|
29 |
|
|
// and link it with other works to produce a work based on this file,
|
30 |
|
|
// this file does not by itself cause the resulting work to be covered by
|
31 |
|
|
// the GNU General Public License. However the source code for this file
|
32 |
|
|
// must still be made available in accordance with section (3) of the GNU
|
33 |
|
|
// General Public License v2.
|
34 |
|
|
//
|
35 |
|
|
// This exception does not invalidate any other reasons why a work based
|
36 |
|
|
// on this file might be covered by the GNU General Public License.
|
37 |
|
|
// -------------------------------------------
|
38 |
|
|
// ####ECOSGPLCOPYRIGHTEND####
|
39 |
|
|
//==========================================================================
|
40 |
|
|
//#####DESCRIPTIONBEGIN####
|
41 |
|
|
//
|
42 |
|
|
// Author(s): gthomas
|
43 |
|
|
// Contributors:
|
44 |
|
|
// Date: 2003-09-19
|
45 |
|
|
// Purpose: Wallclock driver for Dallas 1307
|
46 |
|
|
//
|
47 |
|
|
//####DESCRIPTIONEND####
|
48 |
|
|
//
|
49 |
|
|
//==========================================================================
|
50 |
|
|
|
51 |
|
|
#include <pkgconf/hal.h> // Platform specific configury
|
52 |
|
|
#include <pkgconf/wallclock.h> // Wallclock device config
|
53 |
|
|
#include <pkgconf/devices_wallclock_dallas_ds1307.h>
|
54 |
|
|
|
55 |
|
|
#include <cyg/hal/hal_io.h> // IO macros
|
56 |
|
|
#include <cyg/hal/hal_intr.h> // interrupt enable/disable
|
57 |
|
|
#include <cyg/infra/cyg_type.h> // Common type definitions and support
|
58 |
|
|
#include <string.h> // memcpy()
|
59 |
|
|
|
60 |
|
|
#include <cyg/io/wallclock.hxx> // The WallClock API
|
61 |
|
|
#include <cyg/io/wallclock/wallclock.inl> // Helpers
|
62 |
|
|
|
63 |
|
|
#include <cyg/infra/diag.h>
|
64 |
|
|
|
65 |
|
|
#if 1
|
66 |
|
|
# define DEBUG(_format_, ...)
|
67 |
|
|
#else
|
68 |
|
|
# define DEBUG(_format_, ...) diag_printf(_format_, ## __VA_ARGS__)
|
69 |
|
|
#endif
|
70 |
|
|
|
71 |
|
|
// Registers.
|
72 |
|
|
// FIXME: there is no need to include the control register here, it
|
73 |
|
|
// controls a square wave output which is independent from the wallclock.
|
74 |
|
|
// However fixing it would require changing any platforms that use the
|
75 |
|
|
// old DS_GET()/DS_PUT() functionality.
|
76 |
|
|
#define DS_SECONDS 0x00
|
77 |
|
|
#define DS_MINUTES 0x01
|
78 |
|
|
#define DS_HOURS 0x02
|
79 |
|
|
#define DS_DOW 0x03
|
80 |
|
|
#define DS_DOM 0x04
|
81 |
|
|
#define DS_MONTH 0x05
|
82 |
|
|
#define DS_YEAR 0x06
|
83 |
|
|
#define DS_CONTROL 0x07
|
84 |
|
|
#define DS_REGS_SIZE 0x08 // Size of register space
|
85 |
|
|
|
86 |
|
|
#define DS_SECONDS_CH 0x80 // Clock Halt
|
87 |
|
|
#define DS_HOURS_24 0x40 // 24 hour clock mode
|
88 |
|
|
|
89 |
|
|
// The DS1307 chip is accessed via I2C (2-wire protocol). This can be
|
90 |
|
|
// implemented in one of two ways. If the platform supports the generic
|
91 |
|
|
// I2C API then it should also export a cyg_i2c_device structure
|
92 |
|
|
// cyg_i2c_wallclock_ds1307, and this can be manipulated via the
|
93 |
|
|
// usual cyg_i2c_tx() and cyg_i2c_rx() functions. Alternatively (and
|
94 |
|
|
// primarily for older ports predating the generic I2C package)
|
95 |
|
|
// the platform HAL can provide the following two macros/functions:
|
96 |
|
|
//
|
97 |
|
|
// void DS_GET(cyg_uint8 *regs)
|
98 |
|
|
// Reads the entire set of registers (8 bytes) into *regs
|
99 |
|
|
// void DS_PUT(cyg_uint8 *regs)
|
100 |
|
|
// Updated the entire set of registers (8 bytes) from *regs
|
101 |
|
|
//
|
102 |
|
|
// Using this method, the data in the registers is guaranteed to be
|
103 |
|
|
// stable (if the access function manipulates the registers in an
|
104 |
|
|
// single operation)
|
105 |
|
|
//
|
106 |
|
|
// If the platform HAL implements the CDL interface
|
107 |
|
|
// CYGINT_DEVICES_WALLCLOCK_DALLAS_DS1307_I2C then the I2C API will be used.
|
108 |
|
|
|
109 |
|
|
#ifdef CYGINT_DEVICES_WALLCLOCK_DALLAS_DS1307_I2C
|
110 |
|
|
# if defined(DS_GET) || defined(DS_PUT)
|
111 |
|
|
# error The macros DS_GET and DS_PUT should not be defined if the generic I2C API is used
|
112 |
|
|
# endif
|
113 |
|
|
|
114 |
|
|
#include <cyg/io/i2c.h>
|
115 |
|
|
|
116 |
|
|
static void
|
117 |
|
|
DS_GET(cyg_uint8* regs)
|
118 |
|
|
{
|
119 |
|
|
cyg_uint8 tx_data[1];
|
120 |
|
|
cyg_bool ok = true;
|
121 |
|
|
|
122 |
|
|
tx_data[0] = 0x00; // Initial register to read
|
123 |
|
|
cyg_i2c_transaction_begin(&cyg_i2c_wallclock_ds1307);
|
124 |
|
|
if (1 != cyg_i2c_transaction_tx(&cyg_i2c_wallclock_ds1307, true, tx_data, 1, false)) {
|
125 |
|
|
// The device has not responded to the address byte.
|
126 |
|
|
ok = false;
|
127 |
|
|
} else {
|
128 |
|
|
// Now fetch the data
|
129 |
|
|
cyg_i2c_transaction_rx(&cyg_i2c_wallclock_ds1307, true, regs, 8, true, true);
|
130 |
|
|
|
131 |
|
|
// Verify that there are reasonable default settings. The
|
132 |
|
|
// register values can be used as array indices so bogus
|
133 |
|
|
// values can lead to bus errors or similar problems.
|
134 |
|
|
|
135 |
|
|
// Years: 00 - 99, with 70-99 interpreted as 1970 onwards.
|
136 |
|
|
if ((regs[DS_YEAR] & 0x0F) > 0x09) {
|
137 |
|
|
ok = false;
|
138 |
|
|
}
|
139 |
|
|
// Month: 1 - 12
|
140 |
|
|
if ((regs[DS_MONTH] == 0x00) ||
|
141 |
|
|
((regs[DS_MONTH] > 0x09) && (regs[DS_MONTH] < 0x10)) ||
|
142 |
|
|
(regs[DS_MONTH] > 0x12)) {
|
143 |
|
|
ok = false;
|
144 |
|
|
}
|
145 |
|
|
// Day: 1 - 31. This check does not allow for 28-30 day months.
|
146 |
|
|
if ((regs[DS_DOM] == 0x00) ||
|
147 |
|
|
((regs[DS_DOM] & 0x0F) > 0x09) ||
|
148 |
|
|
(regs[DS_DOM] > 0x31)) {
|
149 |
|
|
ok = false;
|
150 |
|
|
}
|
151 |
|
|
// Hours: 0 - 23. Always run in 24-hour mode
|
152 |
|
|
if ((0 != (regs[DS_HOURS] & DS_HOURS_24)) ||
|
153 |
|
|
((regs[DS_HOURS] & 0x0F) > 0x09) ||
|
154 |
|
|
((regs[DS_HOURS] & 0x3F) > 0x023)) {
|
155 |
|
|
ok = false;
|
156 |
|
|
}
|
157 |
|
|
// Ignore the DOW field. The wallclock code does not need it, and
|
158 |
|
|
// it is hard to calculate.
|
159 |
|
|
// Minutes: 0 - 59
|
160 |
|
|
if (((regs[DS_MINUTES] & 0x0F) > 0x09) ||
|
161 |
|
|
(regs[DS_MINUTES] > 0x59)) {
|
162 |
|
|
ok = false;
|
163 |
|
|
}
|
164 |
|
|
// Seconds: 0 - 59
|
165 |
|
|
if (((regs[DS_SECONDS] & 0x0F) > 0x09) ||
|
166 |
|
|
(regs[DS_SECONDS] > 0x59)) {
|
167 |
|
|
ok = false;
|
168 |
|
|
}
|
169 |
|
|
}
|
170 |
|
|
cyg_i2c_transaction_end(&cyg_i2c_wallclock_ds1307);
|
171 |
|
|
if (! ok) {
|
172 |
|
|
// Any problems, return Jan 1 1970 but do not update the hardware.
|
173 |
|
|
// Leave it to the user or other code to set the clock to a sensible
|
174 |
|
|
// value.
|
175 |
|
|
regs[DS_SECONDS] = 0x00;
|
176 |
|
|
regs[DS_MINUTES] = 0x00;
|
177 |
|
|
regs[DS_HOURS] = 0x00;
|
178 |
|
|
regs[DS_DOW] = 0x00;
|
179 |
|
|
regs[DS_DOM] = 0x01;
|
180 |
|
|
regs[DS_MONTH] = 0x01;
|
181 |
|
|
regs[DS_YEAR] = 0x70;
|
182 |
|
|
regs[DS_CONTROL] = 0x00;
|
183 |
|
|
}
|
184 |
|
|
}
|
185 |
|
|
|
186 |
|
|
static void
|
187 |
|
|
DS_PUT(cyg_uint8* regs)
|
188 |
|
|
{
|
189 |
|
|
cyg_uint8 tx_data[9];
|
190 |
|
|
tx_data[0] = 0;
|
191 |
|
|
memcpy(&(tx_data[1]), regs, 8);
|
192 |
|
|
cyg_i2c_tx(&cyg_i2c_wallclock_ds1307, tx_data, 9);
|
193 |
|
|
}
|
194 |
|
|
|
195 |
|
|
#else
|
196 |
|
|
// Platform details. The platform HAL or some other package should
|
197 |
|
|
// provide this header, containing the required macros
|
198 |
|
|
# include CYGDAT_DEVS_WALLCLOCK_DALLAS_1307_INL
|
199 |
|
|
#endif
|
200 |
|
|
|
201 |
|
|
//----------------------------------------------------------------------------
|
202 |
|
|
// Accessor functions
|
203 |
|
|
|
204 |
|
|
static inline void
|
205 |
|
|
init_ds_hwclock(void)
|
206 |
|
|
{
|
207 |
|
|
cyg_uint8 regs[DS_REGS_SIZE];
|
208 |
|
|
|
209 |
|
|
// Fetch the current state
|
210 |
|
|
DS_GET(regs);
|
211 |
|
|
|
212 |
|
|
// If the clock is not currently running or is not in 24-hours mode,
|
213 |
|
|
// update it. Otherwise skip the update because the clock may have
|
214 |
|
|
// ticked between DS_GET() and DS_PUT() and we could be losing the
|
215 |
|
|
// occasional second.
|
216 |
|
|
if ((0 != (regs[DS_HOURS] & DS_HOURS_24)) ||
|
217 |
|
|
(0 != (regs[DS_SECONDS] & DS_SECONDS_CH))) {
|
218 |
|
|
regs[DS_SECONDS] &= ~DS_SECONDS_CH;
|
219 |
|
|
regs[DS_HOURS] &= ~DS_HOURS_24;
|
220 |
|
|
DS_PUT(regs);
|
221 |
|
|
}
|
222 |
|
|
}
|
223 |
|
|
|
224 |
|
|
static inline void
|
225 |
|
|
set_ds_hwclock(cyg_uint32 year, cyg_uint32 month, cyg_uint32 mday,
|
226 |
|
|
cyg_uint32 hour, cyg_uint32 minute, cyg_uint32 second)
|
227 |
|
|
{
|
228 |
|
|
cyg_uint8 regs[DS_REGS_SIZE];
|
229 |
|
|
|
230 |
|
|
// Set up the registers
|
231 |
|
|
regs[DS_CONTROL] = 0x00;
|
232 |
|
|
regs[DS_YEAR] = TO_BCD((cyg_uint8)(year % 100));
|
233 |
|
|
regs[DS_MONTH] = TO_BCD((cyg_uint8)month);
|
234 |
|
|
regs[DS_DOM] = TO_BCD((cyg_uint8)mday);
|
235 |
|
|
regs[DS_DOW] = TO_BCD(0x01); // Not accurate, but not used by this driver either
|
236 |
|
|
regs[DS_HOURS] = TO_BCD((cyg_uint8)hour);
|
237 |
|
|
regs[DS_MINUTES] = TO_BCD((cyg_uint8)minute);
|
238 |
|
|
// This also starts the clock
|
239 |
|
|
regs[DS_SECONDS] = TO_BCD((cyg_uint8)second);
|
240 |
|
|
|
241 |
|
|
// Send the register set to the hardware
|
242 |
|
|
DS_PUT(regs);
|
243 |
|
|
|
244 |
|
|
// These debugs will cause the test to eventually fail due to
|
245 |
|
|
// the printouts causing timer interrupts to be lost...
|
246 |
|
|
DEBUG("DS1307 set -------------\n");
|
247 |
|
|
DEBUG("regs %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
248 |
|
|
regs[0], regs[1], regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]);
|
249 |
|
|
DEBUG("year %02d\n", year);
|
250 |
|
|
DEBUG("month %02d\n", month);
|
251 |
|
|
DEBUG("mday %02d\n", mday);
|
252 |
|
|
DEBUG("hour %02d\n", hour);
|
253 |
|
|
DEBUG("minute %02d\n", minute);
|
254 |
|
|
DEBUG("second %02d\n", second);
|
255 |
|
|
}
|
256 |
|
|
|
257 |
|
|
static inline void
|
258 |
|
|
get_ds_hwclock(cyg_uint32* year, cyg_uint32* month, cyg_uint32* mday,
|
259 |
|
|
cyg_uint32* hour, cyg_uint32* minute, cyg_uint32* second)
|
260 |
|
|
{
|
261 |
|
|
cyg_uint8 regs[DS_REGS_SIZE];
|
262 |
|
|
|
263 |
|
|
// Fetch the current state
|
264 |
|
|
DS_GET(regs);
|
265 |
|
|
|
266 |
|
|
*year = (cyg_uint32)TO_DEC(regs[DS_YEAR]);
|
267 |
|
|
// The year field only has the 2 least significant digits :-(
|
268 |
|
|
if (*year >= 70) {
|
269 |
|
|
*year += 1900;
|
270 |
|
|
} else {
|
271 |
|
|
*year += 2000;
|
272 |
|
|
}
|
273 |
|
|
*month = (cyg_uint32)TO_DEC(regs[DS_MONTH]);
|
274 |
|
|
*mday = (cyg_uint32)TO_DEC(regs[DS_DOM]);
|
275 |
|
|
*hour = (cyg_uint32)TO_DEC(regs[DS_HOURS] & 0x3F);
|
276 |
|
|
*minute = (cyg_uint32)TO_DEC(regs[DS_MINUTES]);
|
277 |
|
|
*second = (cyg_uint32)TO_DEC(regs[DS_SECONDS] & 0x7F);
|
278 |
|
|
|
279 |
|
|
// These debugs will cause the test to eventually fail due to
|
280 |
|
|
// the printouts causing timer interrupts to be lost...
|
281 |
|
|
DEBUG("DS1307 get -------------\n");
|
282 |
|
|
DEBUG("regs %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
283 |
|
|
regs[0], regs[1], regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]);
|
284 |
|
|
DEBUG("year %02d\n", *year);
|
285 |
|
|
DEBUG("month %02d\n", *month);
|
286 |
|
|
DEBUG("mday %02d\n", *mday);
|
287 |
|
|
DEBUG("hour %02d\n", *hour);
|
288 |
|
|
DEBUG("minute %02d\n", *minute);
|
289 |
|
|
DEBUG("second %02d\n", *second);
|
290 |
|
|
}
|
291 |
|
|
|
292 |
|
|
//-----------------------------------------------------------------------------
|
293 |
|
|
// Functions required for the hardware-driver API.
|
294 |
|
|
|
295 |
|
|
// Returns the number of seconds elapsed since 1970-01-01 00:00:00.
|
296 |
|
|
cyg_uint32
|
297 |
|
|
Cyg_WallClock::get_hw_seconds(void)
|
298 |
|
|
{
|
299 |
|
|
cyg_uint32 year, month, mday, hour, minute, second;
|
300 |
|
|
|
301 |
|
|
get_ds_hwclock(&year, &month, &mday, &hour, &minute, &second);
|
302 |
|
|
cyg_uint32 now = _simple_mktime(year, month, mday, hour, minute, second);
|
303 |
|
|
return now;
|
304 |
|
|
}
|
305 |
|
|
|
306 |
|
|
#ifdef CYGSEM_WALLCLOCK_SET_GET_MODE
|
307 |
|
|
|
308 |
|
|
// Sets the clock. Argument is seconds elapsed since 1970-01-01 00:00:00.
|
309 |
|
|
void
|
310 |
|
|
Cyg_WallClock::set_hw_seconds( cyg_uint32 secs )
|
311 |
|
|
{
|
312 |
|
|
cyg_uint32 year, month, mday, hour, minute, second;
|
313 |
|
|
|
314 |
|
|
_simple_mkdate(secs, &year, &month, &mday, &hour, &minute, &second);
|
315 |
|
|
set_ds_hwclock(year, month, mday, hour, minute, second);
|
316 |
|
|
}
|
317 |
|
|
|
318 |
|
|
#endif
|
319 |
|
|
|
320 |
|
|
void
|
321 |
|
|
Cyg_WallClock::init_hw_seconds(void)
|
322 |
|
|
{
|
323 |
|
|
#ifdef CYGSEM_WALLCLOCK_SET_GET_MODE
|
324 |
|
|
init_ds_hwclock();
|
325 |
|
|
#else
|
326 |
|
|
// This is our base: 1970-01-01 00:00:00
|
327 |
|
|
// Set the HW clock - if for nothing else, just to be sure it's in a
|
328 |
|
|
// legal range. Any arbitrary base could be used.
|
329 |
|
|
// After this the hardware clock is only read.
|
330 |
|
|
set_ds_hwclock(1970,1,1,0,0,0);
|
331 |
|
|
#endif
|
332 |
|
|
}
|
333 |
|
|
|
334 |
|
|
//-----------------------------------------------------------------------------
|
335 |
|
|
// End of devs/wallclock/ds1307.inl
|