source: mainline/uspace/drv/time/cmos-rtc/cmos-rtc.c@ 3b79ba5

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

rtc: add a function to read a generic register from the CMOS memory

  • Property mode set to 100644
File size: 8.8 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(ioport8_t *port, int reg);
83
84
85static ddf_dev_ops_t rtc_dev_ops;
86
87/** The RTC device driver's standard operations */
88static driver_ops_t rtc_ops = {
89 .dev_add = rtc_dev_add,
90 .dev_remove = NULL, /* XXX */
91};
92
93/** The RTC device driver structure */
94static driver_t rtc_driver = {
95 .name = NAME,
96 .driver_ops = &rtc_ops,
97};
98
99/** Clock interface */
100static clock_dev_ops_t rtc_clock_dev_ops = {
101 .time_get = rtc_time_get,
102 .time_set = rtc_time_set,
103};
104
105/** Initialize the RTC driver */
106static void
107rtc_init(void)
108{
109 ddf_log_init(NAME, LVL_ERROR);
110
111 rtc_dev_ops.open = rtc_open;
112 rtc_dev_ops.close = rtc_close;
113
114 rtc_dev_ops.interfaces[CLOCK_DEV_IFACE] = &rtc_clock_dev_ops;
115 rtc_dev_ops.default_handler = NULL; /* XXX */
116}
117
118/** Clean up the RTC soft state
119 *
120 * @param rtc The RTC device
121 */
122static void
123rtc_dev_cleanup(rtc_t *rtc)
124{
125 if (rtc->dev->parent_sess) {
126 async_hangup(rtc->dev->parent_sess);
127 rtc->dev->parent_sess = NULL;
128 }
129}
130
131/** Enable the I/O ports of the device
132 *
133 * @param rtc The real time clock device
134 *
135 * @return true in case of success, false otherwise
136 */
137static bool
138rtc_pio_enable(rtc_t *rtc)
139{
140 if (pio_enable((void *)(uintptr_t) rtc->io_addr, REG_COUNT,
141 (void **) &rtc->port)) {
142
143 ddf_msg(LVL_ERROR, "Cannot map the port %#" PRIx32
144 " for device %s", rtc->io_addr, rtc->dev->name);
145 return false;
146 }
147
148 return true;
149}
150
151/** Initialize the RTC device
152 *
153 * @param rtc Pointer to the RTC device
154 *
155 * @return EOK on success or a negative error code
156 */
157static int
158rtc_dev_initialize(rtc_t *rtc)
159{
160 int rc;
161 size_t i;
162 hw_resource_t *res;
163 bool ioport = false;
164
165 ddf_msg(LVL_DEBUG, "rtc_dev_initialize %s", rtc->dev->name);
166
167 hw_resource_list_t hw_resources;
168 memset(&hw_resources, 0, sizeof(hw_resource_list_t));
169
170 /* Connect to the parent's driver */
171
172 rtc->dev->parent_sess = devman_parent_device_connect(EXCHANGE_SERIALIZE,
173 rtc->dev->handle, IPC_FLAG_BLOCKING);
174 if (!rtc->dev->parent_sess) {
175 ddf_msg(LVL_ERROR, "Failed to connect to parent driver\
176 of device %s.", rtc->dev->name);
177 rc = ENOENT;
178 goto error;
179 }
180
181 /* Get the HW resources */
182 rc = hw_res_get_resource_list(rtc->dev->parent_sess, &hw_resources);
183 if (rc != EOK) {
184 ddf_msg(LVL_ERROR, "Failed to get HW resources\
185 for device %s", rtc->dev->name);
186 goto error;
187 }
188
189 for (i = 0; i < hw_resources.count; ++i) {
190 res = &hw_resources.resources[i];
191
192 if (res->type == IO_RANGE) {
193 if (res->res.io_range.size < REG_COUNT) {
194 ddf_msg(LVL_ERROR, "I/O range assigned to \
195 device %s is too small", rtc->dev->name);
196 rc = ELIMIT;
197 goto error;
198 }
199 rtc->io_addr = res->res.io_range.address;
200 ioport = true;
201 ddf_msg(LVL_NOTE, "Device %s was assigned I/O address \
202 0x%x", rtc->dev->name, rtc->io_addr);
203 }
204 }
205
206 if (!ioport) {
207 /* No I/O address assigned to this device */
208 ddf_msg(LVL_ERROR, "Missing HW resource for device %s",
209 rtc->dev->name);
210 rc = ENOENT;
211 goto error;
212 }
213
214 hw_res_clean_resource_list(&hw_resources);
215
216 return EOK;
217
218error:
219 rtc_dev_cleanup(rtc);
220 hw_res_clean_resource_list(&hw_resources);
221
222 return rc;
223}
224
225/** Read a register from the CMOS memory
226 *
227 * @param port The I/O port assigned to the device
228 * @param reg The index of the register to read
229 *
230 * @return The value of the register
231 */
232static int
233rtc_register_read(ioport8_t *port, int reg)
234{
235 pio_write_8(port, reg);
236 return pio_read_8(port + 1);
237}
238
239/** Check if an update is in progress
240 *
241 * @param rtc The rtc device
242 *
243 * @return true if an update is in progress, false otherwise
244 */
245static bool
246rtc_update_in_progress(rtc_t *rtc)
247{
248 return rtc_register_read(rtc->port, RTC_UPDATE) & RTC_MASK_UPDATE;
249}
250
251/** Read the current time from the CMOS
252 *
253 * @param fun The RTC function
254 * @param t Pointer to the time variable
255 *
256 * @return EOK on success or a negative error code
257 */
258static int
259rtc_time_get(ddf_fun_t *fun, struct tm *t)
260{
261 rtc_t *rtc = RTC_FROM_FNODE(fun);
262
263 fibril_mutex_lock(&rtc->mutex);
264
265 while (rtc_update_in_progress(rtc));
266
267 fibril_mutex_unlock(&rtc->mutex);
268 return EOK;
269}
270
271/** Set the time in the RTC
272 *
273 * @param fun The RTC function
274 * @param t The time value to set
275 *
276 * @return EOK or a negative error code
277 */
278static int
279rtc_time_set(ddf_fun_t *fun, struct tm *t)
280{
281 return EOK;
282}
283
284/** The dev_add callback of the rtc driver
285 *
286 * @param dev The RTC device
287 *
288 * @return EOK on success or a negative error code
289 */
290static int
291rtc_dev_add(ddf_dev_t *dev)
292{
293 rtc_t *rtc;
294 ddf_fun_t *fun = NULL;
295 int rc;
296 bool need_cleanup = false;
297
298 ddf_msg(LVL_DEBUG, "rtc_dev_add %s (handle = %d)",
299 dev->name, (int) dev->handle);
300
301 rtc = ddf_dev_data_alloc(dev, sizeof(rtc_t));
302 if (!rtc)
303 return ENOMEM;
304
305 rtc->dev = dev;
306 fibril_mutex_initialize(&rtc->mutex);
307
308 rc = rtc_dev_initialize(rtc);
309 if (rc != EOK)
310 goto error;
311
312 need_cleanup = true;
313
314 if (!rtc_pio_enable(rtc)) {
315 rc = EADDRNOTAVAIL;
316 goto error;
317 }
318
319 fun = ddf_fun_create(dev, fun_exposed, "a");
320 if (!fun) {
321 ddf_msg(LVL_ERROR, "Failed creating function");
322 rc = ENOENT;
323 goto error;
324 }
325
326 fun->ops = &rtc_dev_ops;
327 rc = ddf_fun_bind(fun);
328 if (rc != EOK) {
329 ddf_msg(LVL_ERROR, "Failed binding function");
330 goto error;
331 }
332
333 rtc->fun = fun;
334
335 ddf_fun_add_to_category(fun, "clock");
336
337 rtc->client_connected = false;
338
339 ddf_msg(LVL_NOTE, "Device %s successfully initialized",
340 dev->name);
341
342 return rc;
343
344error:
345 if (fun)
346 ddf_fun_destroy(fun);
347 if (need_cleanup)
348 rtc_dev_cleanup(rtc);
349 return rc;
350}
351
352/** Open the device
353 *
354 * @param fun The function node
355 *
356 * @return EOK on success or a negative error code
357 */
358static int
359rtc_open(ddf_fun_t *fun)
360{
361 int rc;
362 rtc_t *rtc = RTC_FROM_FNODE(fun);
363
364 fibril_mutex_lock(&rtc->mutex);
365
366 if (rtc->client_connected)
367 rc = EBUSY;
368 else {
369 rc = EOK;
370 rtc->client_connected = true;
371 }
372
373 fibril_mutex_unlock(&rtc->mutex);
374 return rc;
375}
376
377/** Close the device
378 *
379 * @param fun The function node
380 */
381static void
382rtc_close(ddf_fun_t *fun)
383{
384 rtc_t *rtc = RTC_FROM_FNODE(fun);
385
386 fibril_mutex_lock(&rtc->mutex);
387
388 assert(rtc->client_connected);
389 rtc->client_connected = false;
390
391 fibril_mutex_unlock(&rtc->mutex);
392}
393
394int
395main(int argc, char **argv)
396{
397 printf(NAME ": HelenOS RTC driver\n");
398 rtc_init();
399 return ddf_driver_main(&rtc_driver);
400}
401
402/**
403 * @}
404 */
Note: See TracBrowser for help on using the repository browser.