source: mainline/uspace/drv/time/cmos-rtc/cmos-rtc.c@ 05ed9d7

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 05ed9d7 was 05ed9d7, checked in by Maurizio Lombardi <m.lombardi85@…>, 13 years ago

rtc: count the months starting from 0, not from 1

  • Property mode set to 100644
File size: 10.2 KB
Line 
1/*
2 * Copyright (c) 2012 Maurizio Lombardi
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/**
30 * @defgroup CMOS RTC driver.
31 * @brief HelenOS RTC driver.
32 * @{
33 */
34
35/** @file
36 */
37
38#include <errno.h>
39#include <ddi.h>
40#include <libarch/ddi.h>
41#include <stdio.h>
42#include <ddf/driver.h>
43#include <ddf/log.h>
44#include <ops/clock.h>
45#include <fibril_synch.h>
46#include <device/hw_res.h>
47#include <devman.h>
48
49#include "cmos-regs.h"
50
51#define NAME "cmos-rtc"
52
53#define REG_COUNT 2
54
55#define RTC_FROM_FNODE(fnode) ((rtc_t *) ((fnode)->dev->driver_data))
56
57typedef struct rtc {
58 /** DDF device node */
59 ddf_dev_t *dev;
60 /** DDF function node */
61 ddf_fun_t *fun;
62 /** The fibril mutex for synchronizing the access to the device */
63 fibril_mutex_t mutex;
64 /** The base I/O address of the device registers */
65 uint32_t io_addr;
66 /** The I/O port used to access the CMOS registers */
67 ioport8_t *port;
68 /** true if a client is connected to the device */
69 bool client_connected;
70} rtc_t;
71
72
73static int rtc_time_get(ddf_fun_t *fun, struct tm *t);
74static int rtc_time_set(ddf_fun_t *fun, struct tm *t);
75static int rtc_dev_add(ddf_dev_t *dev);
76static int rtc_dev_initialize(rtc_t *rtc);
77static bool rtc_pio_enable(rtc_t *rtc);
78static void rtc_dev_cleanup(rtc_t *rtc);
79static int rtc_open(ddf_fun_t *fun);
80static void rtc_close(ddf_fun_t *fun);
81static bool rtc_update_in_progress(rtc_t *rtc);
82static int rtc_register_read(rtc_t *rtc, int reg);
83static int bcd2dec(int bcd);
84
85
86static ddf_dev_ops_t rtc_dev_ops;
87
88/** The RTC device driver's standard operations */
89static driver_ops_t rtc_ops = {
90 .dev_add = rtc_dev_add,
91 .dev_remove = NULL, /* XXX */
92};
93
94/** The RTC device driver structure */
95static driver_t rtc_driver = {
96 .name = NAME,
97 .driver_ops = &rtc_ops,
98};
99
100/** Clock interface */
101static clock_dev_ops_t rtc_clock_dev_ops = {
102 .time_get = rtc_time_get,
103 .time_set = rtc_time_set,
104};
105
106/** Initialize the RTC driver */
107static void
108rtc_init(void)
109{
110 ddf_log_init(NAME, LVL_ERROR);
111
112 rtc_dev_ops.open = rtc_open;
113 rtc_dev_ops.close = rtc_close;
114
115 rtc_dev_ops.interfaces[CLOCK_DEV_IFACE] = &rtc_clock_dev_ops;
116 rtc_dev_ops.default_handler = NULL; /* XXX */
117}
118
119/** Clean up the RTC soft state
120 *
121 * @param rtc The RTC device
122 */
123static void
124rtc_dev_cleanup(rtc_t *rtc)
125{
126 if (rtc->dev->parent_sess) {
127 async_hangup(rtc->dev->parent_sess);
128 rtc->dev->parent_sess = NULL;
129 }
130}
131
132/** Enable the I/O ports of the device
133 *
134 * @param rtc The real time clock device
135 *
136 * @return true in case of success, false otherwise
137 */
138static bool
139rtc_pio_enable(rtc_t *rtc)
140{
141 if (pio_enable((void *)(uintptr_t) rtc->io_addr, REG_COUNT,
142 (void **) &rtc->port)) {
143
144 ddf_msg(LVL_ERROR, "Cannot map the port %#" PRIx32
145 " for device %s", rtc->io_addr, rtc->dev->name);
146 return false;
147 }
148
149 return true;
150}
151
152/** Initialize the RTC device
153 *
154 * @param rtc Pointer to the RTC device
155 *
156 * @return EOK on success or a negative error code
157 */
158static int
159rtc_dev_initialize(rtc_t *rtc)
160{
161 int rc;
162 size_t i;
163 hw_resource_t *res;
164 bool ioport = false;
165
166 ddf_msg(LVL_DEBUG, "rtc_dev_initialize %s", rtc->dev->name);
167
168 hw_resource_list_t hw_resources;
169 memset(&hw_resources, 0, sizeof(hw_resource_list_t));
170
171 /* Connect to the parent's driver */
172
173 rtc->dev->parent_sess = devman_parent_device_connect(EXCHANGE_SERIALIZE,
174 rtc->dev->handle, IPC_FLAG_BLOCKING);
175 if (!rtc->dev->parent_sess) {
176 ddf_msg(LVL_ERROR, "Failed to connect to parent driver\
177 of device %s.", rtc->dev->name);
178 rc = ENOENT;
179 goto error;
180 }
181
182 /* Get the HW resources */
183 rc = hw_res_get_resource_list(rtc->dev->parent_sess, &hw_resources);
184 if (rc != EOK) {
185 ddf_msg(LVL_ERROR, "Failed to get HW resources\
186 for device %s", rtc->dev->name);
187 goto error;
188 }
189
190 for (i = 0; i < hw_resources.count; ++i) {
191 res = &hw_resources.resources[i];
192
193 if (res->type == IO_RANGE) {
194 if (res->res.io_range.size < REG_COUNT) {
195 ddf_msg(LVL_ERROR, "I/O range assigned to \
196 device %s is too small", rtc->dev->name);
197 rc = ELIMIT;
198 goto error;
199 }
200 rtc->io_addr = res->res.io_range.address;
201 ioport = true;
202 ddf_msg(LVL_NOTE, "Device %s was assigned I/O address \
203 0x%x", rtc->dev->name, rtc->io_addr);
204 }
205 }
206
207 if (!ioport) {
208 /* No I/O address assigned to this device */
209 ddf_msg(LVL_ERROR, "Missing HW resource for device %s",
210 rtc->dev->name);
211 rc = ENOENT;
212 goto error;
213 }
214
215 hw_res_clean_resource_list(&hw_resources);
216
217 return EOK;
218
219error:
220 rtc_dev_cleanup(rtc);
221 hw_res_clean_resource_list(&hw_resources);
222
223 return rc;
224}
225
226/** Read a register from the CMOS memory
227 *
228 * @param port The I/O port assigned to the device
229 * @param reg The index of the register to read
230 *
231 * @return The value of the register
232 */
233static int
234rtc_register_read(rtc_t *rtc, int reg)
235{
236 pio_write_8(rtc->port, reg);
237 return pio_read_8(rtc->port + 1);
238}
239
240/** Check if an update is in progress
241 *
242 * @param rtc The rtc device
243 *
244 * @return true if an update is in progress, false otherwise
245 */
246static bool
247rtc_update_in_progress(rtc_t *rtc)
248{
249 return rtc_register_read(rtc, RTC_UPDATE) & RTC_MASK_UPDATE;
250}
251
252/** Read the current time from the CMOS
253 *
254 * @param fun The RTC function
255 * @param t Pointer to the time variable
256 *
257 * @return EOK on success or a negative error code
258 */
259static int
260rtc_time_get(ddf_fun_t *fun, struct tm *t)
261{
262 bool bcd_mode;
263 rtc_t *rtc = RTC_FROM_FNODE(fun);
264
265 fibril_mutex_lock(&rtc->mutex);
266
267 /* now read the registers */
268 do {
269 /* Suspend until the update process has finished */
270 while (rtc_update_in_progress(rtc));
271
272 t->tm_sec = rtc_register_read(rtc, RTC_SEC);
273 t->tm_min = rtc_register_read(rtc, RTC_MIN);
274 t->tm_hour = rtc_register_read(rtc, RTC_HOUR);
275 t->tm_mday = rtc_register_read(rtc, RTC_DAY);
276 t->tm_mon = rtc_register_read(rtc, RTC_MON);
277 t->tm_year = rtc_register_read(rtc, RTC_YEAR);
278
279 /* Now check if it is stable */
280 } while( t->tm_sec != rtc_register_read(rtc, RTC_SEC) ||
281 t->tm_min != rtc_register_read(rtc, RTC_MIN) ||
282 t->tm_mday != rtc_register_read(rtc, RTC_DAY) ||
283 t->tm_mon != rtc_register_read(rtc, RTC_MON) ||
284 t->tm_year != rtc_register_read(rtc, RTC_YEAR));
285
286 /* Check if the RTC is working in BCD mode */
287 bcd_mode = rtc_register_read(rtc, RTC_STATUS_B) & RTC_MASK_BCD;
288
289 if (bcd_mode) {
290 t->tm_sec = bcd2dec(t->tm_sec);
291 t->tm_min = bcd2dec(t->tm_min);
292 t->tm_hour = bcd2dec(t->tm_hour);
293 t->tm_mday = bcd2dec(t->tm_mday);
294 t->tm_mon = bcd2dec(t->tm_mon);
295 t->tm_year = bcd2dec(t->tm_year);
296 }
297
298 /* Count the months starting from 0, not from 1 */
299 t->tm_mon--;
300
301 fibril_mutex_unlock(&rtc->mutex);
302 return EOK;
303}
304
305/** Set the time in the RTC
306 *
307 * @param fun The RTC function
308 * @param t The time value to set
309 *
310 * @return EOK or a negative error code
311 */
312static int
313rtc_time_set(ddf_fun_t *fun, struct tm *t)
314{
315 return EOK;
316}
317
318/** The dev_add callback of the rtc driver
319 *
320 * @param dev The RTC device
321 *
322 * @return EOK on success or a negative error code
323 */
324static int
325rtc_dev_add(ddf_dev_t *dev)
326{
327 rtc_t *rtc;
328 ddf_fun_t *fun = NULL;
329 int rc;
330 bool need_cleanup = false;
331
332 ddf_msg(LVL_DEBUG, "rtc_dev_add %s (handle = %d)",
333 dev->name, (int) dev->handle);
334
335 rtc = ddf_dev_data_alloc(dev, sizeof(rtc_t));
336 if (!rtc)
337 return ENOMEM;
338
339 rtc->dev = dev;
340 fibril_mutex_initialize(&rtc->mutex);
341
342 rc = rtc_dev_initialize(rtc);
343 if (rc != EOK)
344 goto error;
345
346 need_cleanup = true;
347
348 if (!rtc_pio_enable(rtc)) {
349 rc = EADDRNOTAVAIL;
350 goto error;
351 }
352
353 fun = ddf_fun_create(dev, fun_exposed, "a");
354 if (!fun) {
355 ddf_msg(LVL_ERROR, "Failed creating function");
356 rc = ENOENT;
357 goto error;
358 }
359
360 fun->ops = &rtc_dev_ops;
361 rc = ddf_fun_bind(fun);
362 if (rc != EOK) {
363 ddf_msg(LVL_ERROR, "Failed binding function");
364 goto error;
365 }
366
367 rtc->fun = fun;
368
369 ddf_fun_add_to_category(fun, "clock");
370
371 rtc->client_connected = false;
372
373 ddf_msg(LVL_NOTE, "Device %s successfully initialized",
374 dev->name);
375
376 return rc;
377
378error:
379 if (fun)
380 ddf_fun_destroy(fun);
381 if (need_cleanup)
382 rtc_dev_cleanup(rtc);
383 return rc;
384}
385
386/** Open the device
387 *
388 * @param fun The function node
389 *
390 * @return EOK on success or a negative error code
391 */
392static int
393rtc_open(ddf_fun_t *fun)
394{
395 int rc;
396 rtc_t *rtc = RTC_FROM_FNODE(fun);
397
398 fibril_mutex_lock(&rtc->mutex);
399
400 if (rtc->client_connected)
401 rc = EBUSY;
402 else {
403 rc = EOK;
404 rtc->client_connected = true;
405 }
406
407 fibril_mutex_unlock(&rtc->mutex);
408 return rc;
409}
410
411/** Close the device
412 *
413 * @param fun The function node
414 */
415static void
416rtc_close(ddf_fun_t *fun)
417{
418 rtc_t *rtc = RTC_FROM_FNODE(fun);
419
420 fibril_mutex_lock(&rtc->mutex);
421
422 assert(rtc->client_connected);
423 rtc->client_connected = false;
424
425 fibril_mutex_unlock(&rtc->mutex);
426}
427
428/** Convert from BCD mode to binary mode
429 *
430 * @param bcd The number in BCD format to convert
431 *
432 * @return The converted value
433 */
434static int
435bcd2dec(int bcd)
436{
437 return ((bcd & 0xF0) >> 1) + ((bcd & 0xF0) >> 3) + (bcd & 0xf);
438}
439
440int
441main(int argc, char **argv)
442{
443 printf(NAME ": HelenOS RTC driver\n");
444 rtc_init();
445 return ddf_driver_main(&rtc_driver);
446}
447
448/**
449 * @}
450 */
Note: See TracBrowser for help on using the repository browser.