1 //==========================================================================
3 // devs/wallclock/ds1307.inl
5 // Wallclock implementation for Dallas 1307
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
12 // Copyright (C) 2003 Gary Thomas
13 // Copyright (C) 2004 eCosCentric Ltd
15 // eCos is free software; you can redistribute it and/or modify it under
16 // the terms of the GNU General Public License as published by the Free
17 // Software Foundation; either version 2 or (at your option) any later version.
19 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
20 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
24 // You should have received a copy of the GNU General Public License along
25 // with eCos; if not, write to the Free Software Foundation, Inc.,
26 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
28 // As a special exception, if other files instantiate templates or use macros
29 // or inline functions from this file, or you compile this file and link it
30 // with other works to produce a work based on this file, this file does not
31 // by itself cause the resulting work to be covered by the GNU General Public
32 // License. However the source code for this file must still be made available
33 // in accordance with section (3) of the GNU General Public License.
35 // This exception does not invalidate any other reasons why a work based on
36 // this file might be covered by the GNU General Public License.
38 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
39 // at http://sources.redhat.com/ecos/ecos-license/
40 // -------------------------------------------
41 //####ECOSGPLCOPYRIGHTEND####
42 //==========================================================================
43 //#####DESCRIPTIONBEGIN####
48 // Purpose: Wallclock driver for Dallas 1307
50 //####DESCRIPTIONEND####
52 //==========================================================================
54 #include <pkgconf/hal.h> // Platform specific configury
55 #include <pkgconf/wallclock.h> // Wallclock device config
56 #include <pkgconf/devices_wallclock_dallas_ds1307.h>
58 #include <cyg/hal/hal_io.h> // IO macros
59 #include <cyg/hal/hal_intr.h> // interrupt enable/disable
60 #include <cyg/infra/cyg_type.h> // Common type definitions and support
61 #include <string.h> // memcpy()
63 #include <cyg/io/wallclock.hxx> // The WallClock API
64 #include <cyg/io/wallclock/wallclock.inl> // Helpers
66 #include <cyg/infra/diag.h>
69 # define DEBUG(_format_, ...)
71 # define DEBUG(_format_, ...) diag_printf(_format_, ## __VA_ARGS__)
75 // FIXME: there is no need to include the control register here, it
76 // controls a square wave output which is independent from the wallclock.
77 // However fixing it would require changing any platforms that use the
78 // old DS_GET()/DS_PUT() functionality.
79 #define DS_SECONDS 0x00
80 #define DS_MINUTES 0x01
86 #define DS_CONTROL 0x07
87 #define DS_REGS_SIZE 0x08 // Size of register space
89 #define DS_SECONDS_CH 0x80 // Clock Halt
90 #define DS_HOURS_24 0x40 // 24 hour clock mode
92 // The DS1307 chip is accessed via I2C (2-wire protocol). This can be
93 // implemented in one of two ways. If the platform supports the generic
94 // I2C API then it should also export a cyg_i2c_device structure
95 // cyg_i2c_wallclock_ds1307, and this can be manipulated via the
96 // usual cyg_i2c_tx() and cyg_i2c_rx() functions. Alternatively (and
97 // primarily for older ports predating the generic I2C package)
98 // the platform HAL can provide the following two macros/functions:
100 // void DS_GET(cyg_uint8 *regs)
101 // Reads the entire set of registers (8 bytes) into *regs
102 // void DS_PUT(cyg_uint8 *regs)
103 // Updated the entire set of registers (8 bytes) from *regs
105 // Using this method, the data in the registers is guaranteed to be
106 // stable (if the access function manipulates the registers in an
109 // If the platform HAL implements the CDL interface
110 // CYGINT_DEVICES_WALLCLOCK_DALLAS_DS1307_I2C then the I2C API will be used.
112 #ifdef CYGINT_DEVICES_WALLCLOCK_DALLAS_DS1307_I2C
113 # if defined(DS_GET) || defined(DS_PUT)
114 # error The macros DS_GET and DS_PUT should not be defined if the generic I2C API is used
117 #include <cyg/io/i2c.h>
120 DS_GET(cyg_uint8* regs)
122 cyg_uint8 tx_data[1];
125 tx_data[0] = 0x00; // Initial register to read
126 cyg_i2c_transaction_begin(&cyg_i2c_wallclock_ds1307);
127 if (1 != cyg_i2c_transaction_tx(&cyg_i2c_wallclock_ds1307, true, tx_data, 1, false)) {
128 // The device has not responded to the address byte.
131 // Now fetch the data
132 cyg_i2c_transaction_rx(&cyg_i2c_wallclock_ds1307, true, regs, 8, true, true);
134 // Verify that there are reasonable default settings. The
135 // register values can be used as array indices so bogus
136 // values can lead to bus errors or similar problems.
138 // Years: 00 - 99, with 70-99 interpreted as 1970 onwards.
139 if ((regs[DS_YEAR] & 0x0F) > 0x09) {
143 if ((regs[DS_MONTH] == 0x00) ||
144 ((regs[DS_MONTH] > 0x09) && (regs[DS_MONTH] < 0x10)) ||
145 (regs[DS_MONTH] > 0x12)) {
148 // Day: 1 - 31. This check does not allow for 28-30 day months.
149 if ((regs[DS_DOM] == 0x00) ||
150 ((regs[DS_DOM] & 0x0F) > 0x09) ||
151 (regs[DS_DOM] > 0x31)) {
154 // Hours: 0 - 23. Always run in 24-hour mode
155 if ((0 != (regs[DS_HOURS] & DS_HOURS_24)) ||
156 ((regs[DS_HOURS] & 0x0F) > 0x09) ||
157 ((regs[DS_HOURS] & 0x3F) > 0x023)) {
160 // Ignore the DOW field. The wallclock code does not need it, and
161 // it is hard to calculate.
163 if (((regs[DS_MINUTES] & 0x0F) > 0x09) ||
164 (regs[DS_MINUTES] > 0x59)) {
168 if (((regs[DS_SECONDS] & 0x0F) > 0x09) ||
169 (regs[DS_SECONDS] > 0x59)) {
173 cyg_i2c_transaction_end(&cyg_i2c_wallclock_ds1307);
175 // Any problems, return Jan 1 1970 but do not update the hardware.
176 // Leave it to the user or other code to set the clock to a sensible
178 regs[DS_SECONDS] = 0x00;
179 regs[DS_MINUTES] = 0x00;
180 regs[DS_HOURS] = 0x00;
183 regs[DS_MONTH] = 0x01;
184 regs[DS_YEAR] = 0x70;
185 regs[DS_CONTROL] = 0x00;
190 DS_PUT(cyg_uint8* regs)
192 cyg_uint8 tx_data[9];
194 memcpy(&(tx_data[1]), regs, 8);
195 cyg_i2c_tx(&cyg_i2c_wallclock_ds1307, tx_data, 9);
199 // Platform details. The platform HAL or some other package should
200 // provide this header, containing the required macros
201 # include CYGDAT_DEVS_WALLCLOCK_DALLAS_1307_INL
204 //----------------------------------------------------------------------------
205 // Accessor functions
208 init_ds_hwclock(void)
210 cyg_uint8 regs[DS_REGS_SIZE];
212 // Fetch the current state
215 // If the clock is not currently running or is not in 24-hours mode,
216 // update it. Otherwise skip the update because the clock may have
217 // ticked between DS_GET() and DS_PUT() and we could be losing the
218 // occasional second.
219 if ((0 != (regs[DS_HOURS] & DS_HOURS_24)) ||
220 (0 != (regs[DS_SECONDS] & DS_SECONDS_CH))) {
221 regs[DS_SECONDS] &= ~DS_SECONDS_CH;
222 regs[DS_HOURS] &= ~DS_HOURS_24;
228 set_ds_hwclock(cyg_uint32 year, cyg_uint32 month, cyg_uint32 mday,
229 cyg_uint32 hour, cyg_uint32 minute, cyg_uint32 second)
231 cyg_uint8 regs[DS_REGS_SIZE];
233 // Set up the registers
234 regs[DS_CONTROL] = 0x00;
235 regs[DS_YEAR] = TO_BCD((cyg_uint8)(year % 100));
236 regs[DS_MONTH] = TO_BCD((cyg_uint8)month);
237 regs[DS_DOM] = TO_BCD((cyg_uint8)mday);
238 regs[DS_DOW] = TO_BCD(0x01); // Not accurate, but not used by this driver either
239 regs[DS_HOURS] = TO_BCD((cyg_uint8)hour);
240 regs[DS_MINUTES] = TO_BCD((cyg_uint8)minute);
241 // This also starts the clock
242 regs[DS_SECONDS] = TO_BCD((cyg_uint8)second);
244 // Send the register set to the hardware
247 // These debugs will cause the test to eventually fail due to
248 // the printouts causing timer interrupts to be lost...
249 DEBUG("DS1307 set -------------\n");
250 DEBUG("regs %02x %02x %02x %02x %02x %02x %02x %02x\n",
251 regs[0], regs[1], regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]);
252 DEBUG("year %02d\n", year);
253 DEBUG("month %02d\n", month);
254 DEBUG("mday %02d\n", mday);
255 DEBUG("hour %02d\n", hour);
256 DEBUG("minute %02d\n", minute);
257 DEBUG("second %02d\n", second);
261 get_ds_hwclock(cyg_uint32* year, cyg_uint32* month, cyg_uint32* mday,
262 cyg_uint32* hour, cyg_uint32* minute, cyg_uint32* second)
264 cyg_uint8 regs[DS_REGS_SIZE];
266 // Fetch the current state
269 *year = (cyg_uint32)TO_DEC(regs[DS_YEAR]);
270 // The year field only has the 2 least significant digits :-(
276 *month = (cyg_uint32)TO_DEC(regs[DS_MONTH]);
277 *mday = (cyg_uint32)TO_DEC(regs[DS_DOM]);
278 *hour = (cyg_uint32)TO_DEC(regs[DS_HOURS] & 0x3F);
279 *minute = (cyg_uint32)TO_DEC(regs[DS_MINUTES]);
280 *second = (cyg_uint32)TO_DEC(regs[DS_SECONDS] & 0x7F);
282 // These debugs will cause the test to eventually fail due to
283 // the printouts causing timer interrupts to be lost...
284 DEBUG("DS1307 get -------------\n");
285 DEBUG("regs %02x %02x %02x %02x %02x %02x %02x %02x\n",
286 regs[0], regs[1], regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]);
287 DEBUG("year %02d\n", *year);
288 DEBUG("month %02d\n", *month);
289 DEBUG("mday %02d\n", *mday);
290 DEBUG("hour %02d\n", *hour);
291 DEBUG("minute %02d\n", *minute);
292 DEBUG("second %02d\n", *second);
295 //-----------------------------------------------------------------------------
296 // Functions required for the hardware-driver API.
298 // Returns the number of seconds elapsed since 1970-01-01 00:00:00.
300 Cyg_WallClock::get_hw_seconds(void)
302 cyg_uint32 year, month, mday, hour, minute, second;
304 get_ds_hwclock(&year, &month, &mday, &hour, &minute, &second);
305 cyg_uint32 now = _simple_mktime(year, month, mday, hour, minute, second);
309 #ifdef CYGSEM_WALLCLOCK_SET_GET_MODE
311 // Sets the clock. Argument is seconds elapsed since 1970-01-01 00:00:00.
313 Cyg_WallClock::set_hw_seconds( cyg_uint32 secs )
315 cyg_uint32 year, month, mday, hour, minute, second;
317 _simple_mkdate(secs, &year, &month, &mday, &hour, &minute, &second);
318 set_ds_hwclock(year, month, mday, hour, minute, second);
324 Cyg_WallClock::init_hw_seconds(void)
326 #ifdef CYGSEM_WALLCLOCK_SET_GET_MODE
329 // This is our base: 1970-01-01 00:00:00
330 // Set the HW clock - if for nothing else, just to be sure it's in a
331 // legal range. Any arbitrary base could be used.
332 // After this the hardware clock is only read.
333 set_ds_hwclock(1970,1,1,0,0,0);
337 //-----------------------------------------------------------------------------
338 // End of devs/wallclock/ds1307.inl