]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/devs/wallclock/dallas/ds1307/v2_0/src/ds1307.cxx
Initial revision
[karo-tx-redboot.git] / packages / devs / wallclock / dallas / ds1307 / v2_0 / src / ds1307.cxx
1 //==========================================================================
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 Red Hat, Inc.
12 // Copyright (C) 2003 Gary Thomas
13 // Copyright (C) 2004 eCosCentric Ltd
14 //
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.
18 //
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
22 // for more details.
23 //
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.
27 //
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.
34 //
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.
37 //
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####
44 //
45 // Author(s):     gthomas
46 // Contributors:  
47 // Date:          2003-09-19
48 // Purpose:       Wallclock driver for Dallas 1307
49 //
50 //####DESCRIPTIONEND####
51 //
52 //==========================================================================
53
54 #include <pkgconf/hal.h>                // Platform specific configury
55 #include <pkgconf/wallclock.h>          // Wallclock device config
56 #include <pkgconf/devices_wallclock_dallas_ds1307.h>
57
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()
62
63 #include <cyg/io/wallclock.hxx>         // The WallClock API
64 #include <cyg/io/wallclock/wallclock.inl> // Helpers
65
66 #include <cyg/infra/diag.h>
67
68 #if 1
69 # define DEBUG(_format_, ...)
70 #else
71 # define DEBUG(_format_, ...) diag_printf(_format_, ## __VA_ARGS__)
72 #endif
73
74 // Registers.
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
81 #define DS_HOURS           0x02
82 #define DS_DOW             0x03
83 #define DS_DOM             0x04
84 #define DS_MONTH           0x05
85 #define DS_YEAR            0x06
86 #define DS_CONTROL         0x07
87 #define DS_REGS_SIZE       0x08   // Size of register space
88
89 #define DS_SECONDS_CH      0x80   // Clock Halt
90 #define DS_HOURS_24        0x40   // 24 hour clock mode
91
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:
99 //
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
104 //
105 // Using this method, the data in the registers is guaranteed to be
106 // stable (if the access function manipulates the registers in an
107 // single operation)
108 //
109 // If the platform HAL implements the CDL interface
110 // CYGINT_DEVICES_WALLCLOCK_DALLAS_DS1307_I2C then the I2C API will be used.
111
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
115 # endif
116
117 #include <cyg/io/i2c.h>
118
119 static void
120 DS_GET(cyg_uint8* regs)
121 {
122     cyg_uint8   tx_data[1];
123     cyg_bool    ok = true;
124
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.
129         ok = false;
130     } else {
131         // Now fetch the data
132         cyg_i2c_transaction_rx(&cyg_i2c_wallclock_ds1307, true, regs, 8, true, true);
133
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.
137         
138         // Years: 00 - 99, with 70-99 interpreted as 1970 onwards.
139         if ((regs[DS_YEAR] & 0x0F) > 0x09) {
140             ok = false;
141         }
142         // Month: 1 - 12
143         if ((regs[DS_MONTH] == 0x00) ||
144             ((regs[DS_MONTH] > 0x09) && (regs[DS_MONTH] < 0x10)) ||
145             (regs[DS_MONTH] > 0x12)) {
146             ok = false;
147         }
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)) {
152             ok = false;
153         }
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)) {
158             ok = false;
159         }
160         // Ignore the DOW field. The wallclock code does not need it, and
161         // it is hard to calculate.
162         // Minutes: 0 - 59
163         if (((regs[DS_MINUTES] & 0x0F) > 0x09) ||
164             (regs[DS_MINUTES] > 0x59)) {
165             ok = false;
166         }
167         // Seconds: 0 - 59
168         if (((regs[DS_SECONDS] & 0x0F) > 0x09) ||
169             (regs[DS_SECONDS] > 0x59)) {
170             ok = false;
171         }
172     }
173     cyg_i2c_transaction_end(&cyg_i2c_wallclock_ds1307);
174     if (! ok) {
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
177         // value.
178         regs[DS_SECONDS]  = 0x00;
179         regs[DS_MINUTES]  = 0x00;
180         regs[DS_HOURS]    = 0x00;
181         regs[DS_DOW]      = 0x00;
182         regs[DS_DOM]      = 0x01;                                                         
183         regs[DS_MONTH]    = 0x01;                                                        
184         regs[DS_YEAR]     = 0x70;
185         regs[DS_CONTROL]  = 0x00;
186     }
187 }
188
189 static void
190 DS_PUT(cyg_uint8* regs)
191 {
192     cyg_uint8 tx_data[9];
193     tx_data[0] = 0;
194     memcpy(&(tx_data[1]), regs, 8);
195     cyg_i2c_tx(&cyg_i2c_wallclock_ds1307, tx_data, 9);
196 }
197
198 #else
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
202 #endif
203
204 //----------------------------------------------------------------------------
205 // Accessor functions
206
207 static inline void
208 init_ds_hwclock(void)
209 {
210     cyg_uint8 regs[DS_REGS_SIZE];
211
212     // Fetch the current state
213     DS_GET(regs);
214     
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;
223         DS_PUT(regs);
224     }
225 }
226
227 static inline void
228 set_ds_hwclock(cyg_uint32 year, cyg_uint32 month, cyg_uint32 mday,
229                cyg_uint32 hour, cyg_uint32 minute, cyg_uint32 second)
230 {
231     cyg_uint8 regs[DS_REGS_SIZE];
232
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);
243
244     // Send the register set to the hardware
245     DS_PUT(regs);
246
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);
258 }
259
260 static inline void
261 get_ds_hwclock(cyg_uint32* year, cyg_uint32* month, cyg_uint32* mday,
262                cyg_uint32* hour, cyg_uint32* minute, cyg_uint32* second)
263 {
264     cyg_uint8 regs[DS_REGS_SIZE];
265
266     // Fetch the current state
267     DS_GET(regs);
268
269     *year = (cyg_uint32)TO_DEC(regs[DS_YEAR]);
270     // The year field only has the 2 least significant digits :-(
271     if (*year >= 70) {
272         *year += 1900;
273     } else {
274         *year += 2000;
275     }
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);
281
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);
293 }
294
295 //-----------------------------------------------------------------------------
296 // Functions required for the hardware-driver API.
297
298 // Returns the number of seconds elapsed since 1970-01-01 00:00:00.
299 cyg_uint32 
300 Cyg_WallClock::get_hw_seconds(void)
301 {
302     cyg_uint32 year, month, mday, hour, minute, second;
303
304     get_ds_hwclock(&year, &month, &mday, &hour, &minute, &second);
305     cyg_uint32 now = _simple_mktime(year, month, mday, hour, minute, second);
306     return now;
307 }
308
309 #ifdef CYGSEM_WALLCLOCK_SET_GET_MODE
310
311 // Sets the clock. Argument is seconds elapsed since 1970-01-01 00:00:00.
312 void
313 Cyg_WallClock::set_hw_seconds( cyg_uint32 secs )
314 {
315     cyg_uint32 year, month, mday, hour, minute, second;
316
317     _simple_mkdate(secs, &year, &month, &mday, &hour, &minute, &second);
318     set_ds_hwclock(year, month, mday, hour, minute, second);
319 }
320
321 #endif
322
323 void
324 Cyg_WallClock::init_hw_seconds(void)
325 {
326 #ifdef CYGSEM_WALLCLOCK_SET_GET_MODE
327     init_ds_hwclock();
328 #else
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);
334 #endif
335 }
336
337 //-----------------------------------------------------------------------------
338 // End of devs/wallclock/ds1307.inl