source: rtems/c/src/lib/libbsp/arm/altera-cyclone-v/rtc/rtc.c @ 81329f9

4.11
Last change on this file since 81329f9 was 81329f9, checked in by Christian Mauderer <Christian.Mauderer@…>, on Jul 15, 2014 at 2:20:37 PM

bsp/altera-cyclone-v: Add RTC driver.

  • Property mode set to 100644
File size: 8.3 KB
Line 
1/*
2 * Copyright (c) 2014 embedded brains GmbH.  All rights reserved.
3 *
4 *  embedded brains GmbH
5 *  Dornierstr. 4
6 *  82178 Puchheim
7 *  Germany
8 *  <info@embedded-brains.de>
9 *
10 * The license and distribution terms for this file may be
11 * found in the file LICENSE in this distribution or at
12 * http://www.rtems.org/license/LICENSE.
13 */
14
15/*
16 * Driver for the DS1339 RTC.
17 *
18 * Please note the following points:
19 * - The day of week is ignored.
20 * - The century bit is interpreted the following way:
21 *   - century not set: TOD_BASE_YEAR .. 1999
22 *   - century set: 2000 .. 2099
23 *   - century not set: 2100 .. (TOD_BASE_YEAR + 200)
24 */
25
26#include <libchip/rtc.h>
27#include <assert.h>
28#include <rtems/score/todimpl.h>
29#include <sys/filio.h>
30#include <fcntl.h>
31#include <unistd.h>
32#include <bsp/i2cdrv.h>
33
34#define ALTERA_CYCLONE_V_RTC_NUMBER 1
35
36#define DS1339_I2C_ADDRESS 0x68
37#define DS1339_I2C_BUS_DEVICE "/dev/i2c0"
38
39#define DS1339_ADDR_CTRL 0x0E
40#define DS1339_CTRL_EOSC 0x80
41#define DS1339_CTRL_BBSQI 0x20
42#define DS1339_CTRL_RS2 0x10
43#define DS1339_CTRL_RS1 0x08
44#define DS1339_CTRL_INTCN 0x04
45#define DS1339_CTRL_A2IE 0x02
46#define DS1339_CTRL_A1IE 0x01
47
48#define DS1339_CTRL_DEFAULT (0x00)
49
50#define DS1339_ADDR_TIME 0x00
51#define DS1339_ADDR_STATUS 0x0F
52#define DS1339_STATUS_OSF 0x80
53#define DS1339_STATUS_A2F 0x02
54#define DS1339_STATUS_A1F 0x01
55
56#define DS1339_STATUS_CLEAR (0x00)
57
58typedef struct {
59  uint8_t seconds;
60  uint8_t minutes;
61  uint8_t hours;
62#define DS1339_HOURS_12_24_FLAG 0x40
63#define DS1339_HOURS_AM_PM_FLAG_OR_20_HOURS 0x20
64#define DS1339_HOURS_10_HOURS 0x10
65  uint8_t weekday;
66  uint8_t date;
67  uint8_t month;
68#define DS1339_MONTH_CENTURY 0x80
69  uint8_t year;
70} ds1339_time_t;
71
72/* The longest write transmission is writing the time + one address bit */
73#define DS1339_MAX_WRITE_SIZE (sizeof(ds1339_time_t) + 1)
74
75/* Functions for converting the fields */
76static unsigned int get_seconds (ds1339_time_t *time) {
77  uint8_t tens = time->seconds >> 4;
78  uint8_t ones = time->seconds & 0x0F;
79  return tens * 10 + ones;
80}
81
82static unsigned int get_minutes (ds1339_time_t *time) {
83  uint8_t tens = time->minutes >> 4;
84  uint8_t ones = time->minutes & 0x0F;
85  return tens * 10 + ones;
86}
87
88static unsigned int get_hours (ds1339_time_t *time) {
89  uint8_t value = time->hours & 0x0F;
90
91  if(time->hours & DS1339_HOURS_10_HOURS) {
92    value += 10;
93  }
94  if(time->hours & DS1339_HOURS_AM_PM_FLAG_OR_20_HOURS) {
95    if(time->hours & DS1339_HOURS_12_24_FLAG) {
96      value += 12;
97    } else {
98      value += 20;
99    }
100  }
101
102  return value;
103}
104
105static unsigned int get_day_of_month (ds1339_time_t *time) {
106  uint8_t tens = time->date >> 4;
107  uint8_t ones = time->date & 0x0F;
108  return tens * 10 + ones;
109}
110
111static unsigned int get_month (ds1339_time_t *time) {
112  uint8_t tens = (time->month >> 4) & 0x07;
113  uint8_t ones = time->month & 0x0F;
114  return tens * 10 + ones;
115}
116
117static unsigned int get_year (ds1339_time_t *time) {
118  unsigned int year = 1900;
119  year += (time->year >> 4) * 10;
120  year += time->year & 0x0F;
121  if(time->month & DS1339_MONTH_CENTURY) {
122    year += 100;
123  }
124  if(year < TOD_BASE_YEAR) {
125    year += 200;
126  }
127  return year;
128}
129
130static void set_time (
131  ds1339_time_t *time,
132  unsigned int second,
133  unsigned int minute,
134  unsigned int hour,
135  unsigned int day,
136  unsigned int month,
137  unsigned int year
138) {
139  unsigned int tens;
140  unsigned int ones;
141  uint8_t century = 0;
142
143  tens = second / 10;
144  ones = second % 10;
145  time->seconds = tens << 4 | ones;
146
147  tens = minute / 10;
148  ones = minute % 10;
149  time->minutes = tens << 4 | ones;
150
151  tens = hour / 10;
152  ones = hour % 10;
153  time->hours = tens << 4 | ones;
154
155  /* Weekday is not used. Therefore it can be set to an arbitrary valid value */
156  time->weekday = 1;
157
158  tens = day / 10;
159  ones = day % 10;
160  time->date = tens << 4 | ones;
161
162  tens = month / 10;
163  ones = month % 10;
164  if(year >= 2000 && year < 2100) {
165    century = DS1339_MONTH_CENTURY;
166  }
167  time->month = century | tens << 4 | ones;
168
169  tens = (year % 100) / 10;
170  ones = year % 10;
171  time->year = tens << 4 | ones;
172}
173
174static rtems_status_code ds1339_open_file(int *fd)
175{
176  int rv = 0;
177  rtems_status_code sc = RTEMS_SUCCESSFUL;
178
179  *fd = open(DS1339_I2C_BUS_DEVICE, O_RDWR);
180  if ( *fd == -1 ) {
181    sc = RTEMS_IO_ERROR;
182  }
183
184  if ( sc == RTEMS_SUCCESSFUL ) {
185    rv = ioctl(*fd, I2C_IOC_SET_SLAVE_ADDRESS, DS1339_I2C_ADDRESS);
186    if ( rv == -1 ) {
187      sc = RTEMS_IO_ERROR;
188    }
189  }
190
191  return sc;
192}
193
194/* Read size bytes from ds1339 register address addr to buf. */
195static rtems_status_code ds1339_read(uint8_t addr, void *buf, size_t size)
196{
197  int fd = -1;
198  int rv = 0;
199  rtems_status_code sc = RTEMS_SUCCESSFUL;
200
201  sc = ds1339_open_file(&fd);
202
203  if ( sc == RTEMS_SUCCESSFUL ) {
204    rv = write(fd, &addr, sizeof(addr));
205    if ( rv != sizeof(addr) ) {
206      sc = RTEMS_IO_ERROR;
207    }
208  }
209
210  if ( sc == RTEMS_SUCCESSFUL ) {
211    rv = read(fd, buf, size);
212    if ( rv != size ) {
213      sc = RTEMS_IO_ERROR;
214    }
215  }
216
217  rv = close(fd);
218  if ( rv != 0 ) {
219    sc = RTEMS_IO_ERROR;
220  }
221
222  return sc;
223}
224
225/* Write size bytes from buf to ds1339 register address addr. */
226static rtems_status_code ds1339_write(uint8_t addr, void *buf, size_t size)
227{
228  int fd = -1;
229  int rv = 0;
230  rtems_status_code sc = RTEMS_SUCCESSFUL;
231  /* The driver never writes many bytes. Therefore it should be less expensive
232   * to reserve the maximum number of bytes that will be written in one go than
233   * use a malloc. */
234  uint8_t local_buf[DS1339_MAX_WRITE_SIZE];
235  int write_size = size + 1;
236
237  assert(write_size <= DS1339_MAX_WRITE_SIZE);
238
239  local_buf[0] = addr;
240  memcpy(&local_buf[1], buf, size);
241
242  sc = ds1339_open_file(&fd);
243
244  if ( sc == RTEMS_SUCCESSFUL ) {
245    rv = write(fd, local_buf, write_size);
246    if ( rv != write_size ) {
247      sc = RTEMS_IO_ERROR;
248    }
249  }
250
251  rv = close(fd);
252  if ( rv != 0 ) {
253    sc = RTEMS_IO_ERROR;
254  }
255
256  return RTEMS_SUCCESSFUL;
257}
258
259static void altera_cyclone_v_rtc_initialize(int minor)
260{
261  rtems_status_code sc = RTEMS_SUCCESSFUL;
262  uint8_t status = 0;
263
264  /* Check RTC valid */
265  sc = ds1339_read(DS1339_ADDR_STATUS, &status, sizeof(status));
266  assert(sc == RTEMS_SUCCESSFUL);
267  if(status & DS1339_STATUS_OSF) {
268    /* RTC has been stopped. Initialise it. */
269    ds1339_time_t time;
270
271    uint8_t write = DS1339_CTRL_DEFAULT;
272    sc = ds1339_write(DS1339_ADDR_CTRL, &write, sizeof(write));
273    assert(sc == RTEMS_SUCCESSFUL);
274
275    write = DS1339_STATUS_CLEAR;
276    sc = ds1339_write(DS1339_ADDR_STATUS, &write, sizeof(write));
277    assert(sc == RTEMS_SUCCESSFUL);
278
279    set_time(&time, 0, 0, 0, 1, 1, TOD_BASE_YEAR);
280    sc = ds1339_write(DS1339_ADDR_TIME, &time, sizeof(time));
281    assert(sc == RTEMS_SUCCESSFUL);
282  }
283}
284
285static int altera_cyclone_v_rtc_get_time(int minor, rtems_time_of_day *tod)
286{
287  ds1339_time_t time;
288  rtems_status_code sc = RTEMS_SUCCESSFUL;
289  rtems_time_of_day temp_tod;
290
291  sc = ds1339_read(DS1339_ADDR_TIME, &time, sizeof(time));
292
293  if ( sc == RTEMS_SUCCESSFUL ) {
294    temp_tod.ticks = 0;
295    temp_tod.second = get_seconds(&time);
296    temp_tod.minute = get_minutes(&time);
297    temp_tod.hour = get_hours(&time);
298    temp_tod.day = get_day_of_month(&time);
299    temp_tod.month = get_month(&time);
300    temp_tod.year = get_year(&time);
301
302    if ( _TOD_Validate(&temp_tod) ) {
303      memcpy(tod, &temp_tod, sizeof(temp_tod));
304    } else {
305      sc = RTEMS_INVALID_CLOCK;
306    }
307  }
308
309  return -sc;
310}
311
312static int altera_cyclone_v_rtc_set_time(int minor, const rtems_time_of_day *tod)
313{
314  ds1339_time_t time;
315  rtems_status_code sc = RTEMS_SUCCESSFUL;
316
317  set_time (
318    &time,
319    tod->second,
320    tod->minute,
321    tod->hour,
322    tod->day,
323    tod->month,
324    tod->year
325  );
326
327  sc = ds1339_write(DS1339_ADDR_TIME, &time, sizeof(time));
328
329  return -sc;
330}
331
332static bool altera_cyclone_v_rtc_probe(int minor)
333{
334  /* FIXME: Probe for i2c device */
335  return true;
336}
337
338const rtc_fns altera_cyclone_v_rtc_ops = {
339  .deviceInitialize = altera_cyclone_v_rtc_initialize,
340  .deviceGetTime = altera_cyclone_v_rtc_get_time,
341  .deviceSetTime = altera_cyclone_v_rtc_set_time
342};
343
344size_t RTC_Count = ALTERA_CYCLONE_V_RTC_NUMBER;
345
346rtems_device_minor_number RTC_Minor = 0;
347
348rtc_tbl RTC_Table [ALTERA_CYCLONE_V_RTC_NUMBER] = {
349  {
350    .sDeviceName = "/dev/rtc",
351    .deviceType = RTC_CUSTOM,
352    .pDeviceFns = &altera_cyclone_v_rtc_ops,
353    .deviceProbe = altera_cyclone_v_rtc_probe,
354    .pDeviceParams = NULL,
355    .ulCtrlPort1 = 0,
356    .ulDataPort = 0,
357    .getRegister = NULL,
358    .setRegister = NULL
359  }
360};
Note: See TracBrowser for help on using the repository browser.