xref: /titanic_44/usr/src/uts/sun4u/snowbird/io/todds1307/todds1307.c (revision b9238976491622ad75a67ab0c12edf99e36212b9)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/types.h>
29 #include <sys/conf.h>
30 #include <sys/devops.h>
31 #include <sys/kmem.h>
32 #include <sys/open.h>
33 #include <sys/file.h>
34 #include <sys/note.h>
35 #include <sys/ddi.h>
36 #include <sys/sunddi.h>
37 
38 #include <sys/modctl.h>
39 #include <sys/stat.h>
40 #include <sys/clock.h>
41 #include <sys/reboot.h>
42 #include <sys/machsystm.h>
43 #include <sys/poll.h>
44 #include <sys/pbio.h>
45 #include <sys/sysmacros.h>
46 
47 /* Added for prom interface */
48 #include <sys/promif.h>
49 #include <sys/promimpl.h>
50 
51 #include <sys/i2c/misc/i2c_svc.h>
52 #include <sys/todds1307.h>
53 
54 #define	I2C_DELAY	20000
55 #define	DS1307_DEVICE_TYPE	"rtc"
56 
57 /*
58  * Driver enrty routines
59  */
60 static int todds1307_attach(dev_info_t *, ddi_attach_cmd_t);
61 static int todds1307_detach(dev_info_t *, ddi_detach_cmd_t);
62 static int todds1307_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
63 
64 /*
65  * tod_ops entry routines
66  */
67 static timestruc_t	todds1307_get(void);
68 static void		todds1307_set(timestruc_t);
69 static uint_t		todds1307_set_watchdog_timer(uint_t);
70 static uint_t		todds1307_clear_watchdog_timer(void);
71 static void		todds1307_set_power_alarm(timestruc_t);
72 static void		todds1307_clear_power_alarm(void);
73 static int todds1307_setup_prom();
74 static void todds1307_rele_prom();
75 static int todds1307_prom_getdate(struct rtc_t *rtc);
76 static int todds1307_prom_setdate(struct rtc_t *rtc);
77 
78 /*
79  * Local functions
80  */
81 static int	todds1307_read_rtc(struct rtc_t *);
82 static int	todds1307_write_rtc(struct rtc_t *);
83 
84 /* Anchor for soft state structure */
85 static void	*ds1307_statep;
86 static int	instance = -1;
87 static int	todds1307_attach_done = 0;
88 static kmutex_t	todds1307_rd_lock;
89 static ihandle_t todds1307_ihandle = 0;
90 
91 /* one second time out */
92 #define	I2c_CYCLIC_TIMEOUT	1000000000
93 uint_t i2c_cyclic_timeout = I2c_CYCLIC_TIMEOUT;
94 static int sync_clock_once = 1;
95 static 	struct	rtc_t	 soft_rtc;
96 
97 /*
98  * For debugging only
99  */
100 static unsigned char int2bcd(int num);
101 static int bcd2int(unsigned char num);
102 
103 /*
104  * cp_ops structure
105  */
106 static struct cb_ops ds1307_cbops = {
107 	nodev,				/* open */
108 	nodev,				/* close */
109 	nodev,				/* strategy */
110 	nodev,				/* print */
111 	nodev,				/* dump */
112 	nodev,				/* read */
113 	nodev,				/* write */
114 	nodev,				/* ioctl */
115 	nodev,				/* devmap */
116 	nodev,				/* mmap */
117 	nodev,				/* segmap */
118 	NULL,				/* poll */
119 	ddi_prop_op,			/* cb_prop_op */
120 	NULL,				/* streamtab */
121 	D_NEW | D_MP,			/* Driver compatibility flag */
122 	CB_REV,				/* rev */
123 	nodev,				/* int (*cb_aread)() */
124 	nodev				/* int (*cb_awrite)() */
125 };
126 
127 /*
128  * dev_ops structure
129  */
130 static struct dev_ops ds1307_ops = {
131 	DEVO_REV,		/* devo_rev */
132 	0,			/* refcnt - reference cnt always set to 0 */
133 	todds1307_getinfo,	/* getinfo - Maybe requred */
134 	nulldev,		/* identify */
135 	nulldev,		/* probe */
136 	todds1307_attach,	/* attach */
137 	todds1307_detach,	/* detach */
138 	nodev,			/* reset */
139 	&ds1307_cbops,		/* cb_ops - ds1307 does not need this(?) */
140 	NULL
141 };
142 
143 static struct modldrv todds1307_modldrv = {
144 	&mod_driverops,		/* Type of module. This one is a driver */
145 	"tod driver for DS1307 v%I%",	/* Name of the module */
146 	&ds1307_ops,			/* Pointer to dev_ops */
147 };
148 
149 /*
150  * Module linkage structure
151  */
152 static struct modlinkage todds1307_modlinkage = {
153 	MODREV_1,
154 	&todds1307_modldrv,
155 	0
156 };
157 
158 int
159 _init(void)
160 {
161 	int error;
162 
163 	if (strcmp(tod_module_name, "todds1307") == 0) {
164 		if ((error = ddi_soft_state_init(&ds1307_statep,
165 		    sizeof (ds1307_state_t), 0)) != DDI_SUCCESS) {
166 			return (error);
167 		}
168 
169 		tod_ops.tod_get = todds1307_get;
170 		tod_ops.tod_set = todds1307_set;
171 		tod_ops.tod_set_watchdog_timer = todds1307_set_watchdog_timer;
172 		tod_ops.tod_clear_watchdog_timer =
173 		    todds1307_clear_watchdog_timer;
174 		tod_ops.tod_set_power_alarm = todds1307_set_power_alarm;
175 		tod_ops.tod_clear_power_alarm = todds1307_clear_power_alarm;
176 	}
177 
178 	(void) todds1307_setup_prom();
179 
180 	/*
181 	 * Install the module
182 	 */
183 	if ((error = mod_install(&todds1307_modlinkage)) != 0) {
184 		ddi_soft_state_fini(&ds1307_statep);
185 		return (error);
186 	}
187 	mutex_init(&todds1307_rd_lock, NULL, MUTEX_DEFAULT, NULL);
188 
189 	return (0);
190 }
191 
192 int
193 _fini(void)
194 {
195 	int error = 0;
196 
197 	if (strcmp(tod_module_name, "todds1307") == 0) {
198 		error = EBUSY;
199 	} else {
200 		if ((error = mod_remove(&todds1307_modlinkage)) == 0) {
201 			ddi_soft_state_fini(&ds1307_statep);
202 			mutex_destroy(&todds1307_rd_lock);
203 			todds1307_rele_prom();
204 		}
205 	}
206 
207 	return (error);
208 }
209 
210 int
211 _info(struct modinfo *modinfop)
212 {
213 	return (mod_info(&todds1307_modlinkage, modinfop));
214 }
215 
216 /*
217  * cyclical call to get tod.
218  */
219 static void
220 todds1307_cyclic(void *arg)
221 {
222 
223 	todds1307_read_rtc((struct rtc_t *)arg);
224 
225 }
226 
227 /*
228  * register ds1307 client device with i2c services, and
229  * allocate & initialize soft state structure.
230  */
231 static int
232 todds1307_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
233 {
234 	static ds1307_state_t	*statep = NULL;
235 	i2c_transfer_t	*i2c_tp = NULL;
236 	uint8_t tempVal = (uint8_t)0;
237 	switch (cmd) {
238 
239 		case DDI_ATTACH:
240 			break;
241 		case DDI_RESUME:
242 			return (DDI_SUCCESS);
243 		default:
244 			return (DDI_FAILURE);
245 	}
246 
247 	if (instance != -1) {
248 		return (DDI_FAILURE);
249 	}
250 
251 	instance = ddi_get_instance(dip);
252 
253 	/*
254 	 * Allocate soft state structure
255 	 */
256 	if (ddi_soft_state_zalloc(ds1307_statep, instance) != DDI_SUCCESS) {
257 		return (DDI_FAILURE);
258 	}
259 
260 	statep = ddi_get_soft_state(ds1307_statep, instance);
261 	if (statep == NULL) {
262 		return (DDI_FAILURE);
263 	}
264 
265 	statep->dip = dip;
266 
267 	if (i2c_client_register(dip, &statep->ds1307_i2c_hdl) != I2C_SUCCESS) {
268 		ddi_soft_state_free(ds1307_statep, instance);
269 		delay(drv_usectohz(I2C_DELAY));
270 		return (DDI_FAILURE);
271 	}
272 
273 	/* check and initialize the oscillator */
274 
275 	(void) i2c_transfer_alloc(statep->ds1307_i2c_hdl,
276 	    &i2c_tp, 1, 1, I2C_SLEEP);
277 	i2c_tp->i2c_version = I2C_XFER_REV;
278 	i2c_tp->i2c_flags = I2C_WR_RD;
279 	i2c_tp->i2c_wbuf[0] = (uchar_t)0x00; /* Read 00h */
280 	i2c_tp->i2c_wlen = 1;
281 	i2c_tp->i2c_rlen = 1;
282 
283 	if ((i2c_transfer(statep->ds1307_i2c_hdl, i2c_tp)) != I2C_SUCCESS) {
284 		(void) i2c_transfer_free(statep->ds1307_i2c_hdl, i2c_tp);
285 		ddi_soft_state_free(ds1307_statep, instance);
286 		delay(drv_usectohz(I2C_DELAY));
287 		return (DDI_FAILURE);
288 	}
289 
290 	tempVal = i2c_tp->i2c_rbuf[0];
291 
292 	(void) i2c_transfer_free(statep->ds1307_i2c_hdl, i2c_tp);
293 
294 	if (tempVal & 0x80) {			 /* check Oscillator */
295 		(void) i2c_transfer_alloc(statep->ds1307_i2c_hdl, &i2c_tp,
296 		    2, 1, I2C_SLEEP);
297 		i2c_tp->i2c_version = I2C_XFER_REV;
298 		i2c_tp->i2c_flags = I2C_WR;
299 		i2c_tp->i2c_wbuf[0] = 0x00;
300 		i2c_tp->i2c_wbuf[1] =
301 		    (uchar_t)(i2c_tp->i2c_rbuf[0]& 0x7f);
302 		i2c_tp->i2c_wlen = 2;
303 					/* Enable oscillator */
304 		if ((i2c_transfer(statep->ds1307_i2c_hdl, i2c_tp))
305 		    != I2C_SUCCESS) {
306 			(void) i2c_transfer_free(statep->ds1307_i2c_hdl,
307 			    i2c_tp);
308 			ddi_soft_state_free(ds1307_statep, instance);
309 			return (DDI_FAILURE);
310 		}
311 		(void) i2c_transfer_free(statep->ds1307_i2c_hdl, i2c_tp);
312 	}
313 
314 	/*
315 	 * Create a periodical handler to read TOD.
316 	 */
317 	ASSERT(statep->cycid == NULL);
318 	statep->cycid = ddi_periodic_add(todds1307_cyclic, &soft_rtc,
319 	    i2c_cyclic_timeout, DDI_IPL_1);
320 
321 	statep->state = TOD_ATTACHED;
322 	todds1307_attach_done = 1;
323 	ddi_report_dev(dip);
324 
325 	return (DDI_SUCCESS);
326 }
327 
328 /*
329  * Unregister ds1307 client device with i2c services and free
330  * soft state structure.
331  */
332 /*ARGSUSED*/
333 static int
334 todds1307_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
335 {
336 	switch (cmd) {
337 
338 	/*
339 	 * Once attached, do not allow detach because the system constantly
340 	 * calling todds1307_get() to get the time.  If the driver is detached
341 	 * and the system try to get the time, the system will have memory
342 	 * problem.
343 	 *
344 	 *	ds1307_state_t	*statep = NULL;
345 	 *	case DDI_DETACH:
346 	 *		if ((statep = ddi_get_soft_state(ds1307_statep,
347 	 *					instance)) == NULL) {
348 	 *			return (ENOMEM);
349 	 *		}
350 	 *		i2c_client_unregister(statep->ds1307_i2c_hdl);
351 	 *		ddi_soft_state_free(ds1307_statep, instance);
352 	 *		return (DDI_SUCCESS);
353 	 */
354 		case DDI_SUSPEND:
355 			return (DDI_SUCCESS);
356 
357 		default:
358 			return (DDI_FAILURE);
359 	}
360 }
361 
362 /* *********************** tod_ops entry points ******************** */
363 
364 /*
365  * Read the current time from the DS1307 chip and convert to UNIX form.
366  * Should be called with tod_clock held.
367  */
368 
369 static timestruc_t
370 todds1307_get(void)
371 {
372 	timestruc_t	ts;
373 	todinfo_t	tod;
374 	struct	rtc_t	rtc;
375 
376 	ASSERT(MUTEX_HELD(&tod_lock));
377 
378 	if (sync_clock_once) {
379 		todds1307_read_rtc(&soft_rtc);
380 		sync_clock_once = 0;
381 	} else {
382 		tod_fault_reset();
383 		return (hrestime);
384 	}
385 
386 	bcopy(&soft_rtc, &rtc, sizeof (rtc));
387 
388 	/*
389 	 * 00 - 68 = 2000 thru 2068
390 	 * 69-99 = 1969 thru 1999
391 	 */
392 	tod.tod_year    = rtc.rtc_year;
393 	if (rtc.rtc_year <= 68)
394 		tod.tod_year += 100;
395 	tod.tod_month	= rtc.rtc_mon;
396 	tod.tod_day	= rtc.rtc_dom;
397 	tod.tod_dow	= rtc.rtc_dow;
398 	tod.tod_hour	= rtc.rtc_hrs;
399 	tod.tod_min	= rtc.rtc_min;
400 	tod.tod_sec	= rtc.rtc_sec;
401 
402 	ts.tv_sec = tod_to_utc(tod);
403 	ts.tv_nsec = 0;
404 	return (ts);
405 }
406 
407 /*
408  * Program DS1307 with the specified time.
409  * Must be called with tod_lock held. The TOD
410  * chip supports date from 1969-2068 only. We must
411  * reject requests to set date below 2000.
412  */
413 static void
414 todds1307_set(timestruc_t ts)
415 {
416 	struct rtc_t	rtc;
417 	todinfo_t	tod = utc_to_tod(ts.tv_sec);
418 	int		year;
419 
420 
421 	ASSERT(MUTEX_HELD(&tod_lock));
422 
423 	/*
424 	 * Year is base 1900, valid year range 1969-2068
425 	 */
426 	if ((tod.tod_year < 69) || (tod.tod_year > 168))
427 		return;
428 
429 	year = tod.tod_year;
430 	if (year >= 100)
431 		year -= 100;
432 
433 	rtc.rtc_year	= int2bcd(year);
434 	rtc.rtc_mon	= int2bcd(tod.tod_month);
435 	rtc.rtc_dom	= int2bcd(tod.tod_day);
436 	rtc.rtc_dow	= int2bcd(tod.tod_dow);
437 	rtc.rtc_hrs	= int2bcd(tod.tod_hour);
438 	rtc.rtc_min	= int2bcd(tod.tod_min);
439 	rtc.rtc_sec	= int2bcd(tod.tod_sec);
440 
441 	todds1307_write_rtc(&rtc);
442 }
443 
444 /* ARGSUSED */
445 static void
446 todds1307_set_power_alarm(timestruc_t ts)
447 {
448 	ASSERT(MUTEX_HELD(&tod_lock));
449 }
450 
451 /* ARGSUSED */
452 static void
453 todds1307_clear_power_alarm(void)
454 {
455 	ASSERT(MUTEX_HELD(&tod_lock));
456 }
457 
458 /* ARGSUSED */
459 static uint_t
460 todds1307_set_watchdog_timer(uint_t timeoutval)
461 {
462 	ASSERT(MUTEX_HELD(&tod_lock));
463 	return (0);
464 }
465 
466 /* ARGSUSED */
467 static uint_t
468 todds1307_clear_watchdog_timer(void)
469 {
470 	ASSERT(MUTEX_HELD(&tod_lock));
471 	return (0);
472 }
473 
474 /* ********************** Local functions ***************************** */
475 
476 static char tod_read[7] = {-1, -1, -1, -1, -1, -1, -1};
477 static int
478 todds1307_read_rtc(struct rtc_t *rtc)
479 {
480 	static	ds1307_state_t	*statep = NULL;
481 	i2c_transfer_t	*i2c_tp = NULL;
482 	int i2c_cmd_status = I2C_FAILURE;
483 	int counter = 4;
484 
485 	if (!todds1307_attach_done) {
486 		return (todds1307_prom_getdate(rtc));
487 	}
488 
489 	statep = ddi_get_soft_state(ds1307_statep, instance);
490 	if (statep == NULL) {
491 		cmn_err(CE_WARN, "todds1307: ddi_get_soft_state failed");
492 		return (DDI_FAILURE);
493 	}
494 
495 	mutex_enter(&todds1307_rd_lock);
496 
497 	/*
498 	 * Allocate 1 byte for write buffer and 7 bytes for read buffer to
499 	 * to accomodate sec, min, hrs, dayOfWeek, dayOfMonth, year
500 	 */
501 	if ((i2c_transfer_alloc(statep->ds1307_i2c_hdl, &i2c_tp, 1,
502 	    7, I2C_SLEEP)) != I2C_SUCCESS) {
503 		mutex_exit(&todds1307_rd_lock);
504 		return (DDI_FAILURE);
505 	}
506 
507 	do {
508 		i2c_tp->i2c_version = I2C_XFER_REV;
509 		i2c_tp->i2c_flags = I2C_WR_RD;
510 		i2c_tp->i2c_wbuf[0] = (uchar_t)0x00; /* Start from reg 0x00 */
511 		i2c_tp->i2c_wlen = 1;	/* Write one byte address */
512 		i2c_tp->i2c_rlen = 7;	/* Read 7 regs */
513 
514 		if ((i2c_cmd_status = i2c_transfer(statep->ds1307_i2c_hdl,
515 		    i2c_tp)) != I2C_SUCCESS) {
516 			drv_usecwait(I2C_DELAY);
517 			goto done;
518 		}
519 		/* for first read, need to get valid data */
520 		while (tod_read[0] == -1 && counter > 0) {
521 		/* move data to static buffer */
522 		bcopy(i2c_tp->i2c_rbuf, tod_read, 7);
523 
524 		/* now read again */
525 		/* Start reading reg from 0x00 */
526 		i2c_tp->i2c_wbuf[0] = (uchar_t)0x00;
527 		i2c_tp->i2c_wlen = 1;	/* Write one byte address */
528 		i2c_tp->i2c_rlen = 7;	/* Read 7 regs */
529 		if ((i2c_cmd_status = i2c_transfer(statep->ds1307_i2c_hdl,
530 		    i2c_tp)) != I2C_SUCCESS) {
531 			drv_usecwait(I2C_DELAY);
532 			goto done;
533 		}
534 		/* if they are not the same, then read again */
535 		if (bcmp(tod_read, i2c_tp->i2c_rbuf, 7) != 0) {
536 			tod_read[0] = -1;
537 			counter--;
538 		}
539 	}
540 
541 	} while (i2c_tp->i2c_rbuf[0] == 0x59 &&
542 	    /* if seconds register is 0x59 (BCD), add data should match */
543 	    bcmp(&tod_read[1], &i2c_tp->i2c_rbuf[1], 6) != 0 &&
544 	    counter-- > 0);
545 
546 	if (counter < 0)
547 		cmn_err(CE_WARN, "i2ctod: TOD Chip failed ??");
548 
549 	/* move data to static buffer */
550 	bcopy(i2c_tp->i2c_rbuf, tod_read, 7);
551 
552 
553 	rtc->rtc_year	= bcd2int(i2c_tp->i2c_rbuf[6]);
554 	rtc->rtc_mon	= bcd2int(i2c_tp->i2c_rbuf[5]);
555 	rtc->rtc_dom	= bcd2int(i2c_tp->i2c_rbuf[4]);
556 	rtc->rtc_dow	= bcd2int(i2c_tp->i2c_rbuf[3]);
557 	rtc->rtc_hrs	= bcd2int(i2c_tp->i2c_rbuf[2]);
558 	rtc->rtc_min	= bcd2int(i2c_tp->i2c_rbuf[1]);
559 	rtc->rtc_sec	= bcd2int(i2c_tp->i2c_rbuf[0]);
560 
561 done:
562 	(void) i2c_transfer_free(statep->ds1307_i2c_hdl, i2c_tp);
563 
564 	mutex_exit(&todds1307_rd_lock);
565 	return (i2c_cmd_status);
566 }
567 
568 
569 static int
570 todds1307_write_rtc(struct rtc_t *rtc)
571 {
572 	ds1307_state_t	*statep = NULL;
573 	i2c_transfer_t	*i2c_tp = NULL;
574 	int i2c_cmd_status = I2C_SUCCESS;
575 
576 
577 	if (!todds1307_attach_done) {
578 		return (todds1307_prom_setdate(rtc));
579 	}
580 
581 	statep = ddi_get_soft_state(ds1307_statep, instance);
582 	if (statep == NULL) {
583 		return (DDI_FAILURE);
584 	}
585 
586 	if ((i2c_cmd_status = i2c_transfer_alloc(statep->ds1307_i2c_hdl,
587 	    &i2c_tp, 8, 0, I2C_SLEEP)) != I2C_SUCCESS) {
588 		return (i2c_cmd_status);
589 	}
590 
591 	i2c_tp->i2c_version = I2C_XFER_REV;
592 	i2c_tp->i2c_flags = I2C_WR;
593 	i2c_tp->i2c_wbuf[0] = (uchar_t)0x00;
594 	i2c_tp->i2c_wbuf[1] = rtc->rtc_sec;
595 	i2c_tp->i2c_wbuf[2] = rtc->rtc_min;
596 	i2c_tp->i2c_wbuf[3] = rtc->rtc_hrs;
597 	i2c_tp->i2c_wbuf[4] = rtc->rtc_dow;
598 	i2c_tp->i2c_wbuf[5] = rtc->rtc_dom;
599 	i2c_tp->i2c_wbuf[6] = rtc->rtc_mon;
600 	i2c_tp->i2c_wbuf[7] = rtc->rtc_year;
601 	i2c_tp->i2c_wlen = 8;
602 
603 	if ((i2c_cmd_status = i2c_transfer(statep->ds1307_i2c_hdl,
604 	    i2c_tp)) != I2C_SUCCESS) {
605 		(void) i2c_transfer_free(statep->ds1307_i2c_hdl, i2c_tp);
606 		/* delay(drv_usectohz(I2C_DELAY)); */
607 		drv_usecwait(I2C_DELAY);
608 		return (i2c_cmd_status);
609 	}
610 
611 	tod_read[0] = -1;  /* invalidate saved data from read routine */
612 
613 	(void) i2c_transfer_free(statep->ds1307_i2c_hdl, i2c_tp);
614 
615 	return (i2c_cmd_status);
616 }
617 
618 
619 /*ARGSUSED*/
620 static int
621 todds1307_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
622     void **result)
623 {
624 	ds1307_state_t *softsp;
625 
626 	if (instance == -1) {
627 		return (DDI_FAILURE);
628 	}
629 
630 	switch (infocmd) {
631 	case DDI_INFO_DEVT2DEVINFO:
632 		if ((softsp = ddi_get_soft_state(ds1307_statep, instance))
633 		    == NULL)
634 			return (DDI_FAILURE);
635 		*result = (void *)softsp->dip;
636 		return (DDI_SUCCESS);
637 
638 	case DDI_INFO_DEVT2INSTANCE:
639 		*result = (void *)(uintptr_t)instance;
640 		return (DDI_SUCCESS);
641 
642 	default:
643 		return (DDI_FAILURE);
644 	}
645 }
646 
647 /*
648  * Conversion functions
649  */
650 static unsigned char
651 int2bcd(int num) {
652 	return (((num / 10) << 4)	/* tens BCD digit in high four bits */
653 	+ (num % 10));		/* units digit goes in low four bits */
654 }
655 
656 static int
657 bcd2int(unsigned char num) {
658 	return (((num >> 4) * 10)	/* 10 times high-order four bits */
659 	+ (num & 0x0f));		/* plus low-order four bits */
660 }
661 
662 /*
663  * Finds the device node with device_type "rtc" and opens it to
664  * execute the get-time method
665  */
666 static int
667 todds1307_setup_prom()
668 {
669 	pnode_t todnode;
670 	char tod1307_devpath[MAXNAMELEN];
671 
672 	if ((todnode = prom_findnode_bydevtype(prom_rootnode(),
673 	    DS1307_DEVICE_TYPE)) == OBP_NONODE)
674 		return (DDI_FAILURE);
675 
676 	/*
677 	 * We now have the phandle of the rtc node, we need to open the
678 	 * node and get the ihandle
679 	 */
680 	if (prom_phandle_to_path(todnode, tod1307_devpath,
681 	    sizeof (tod1307_devpath)) < 0) {
682 		cmn_err(CE_WARN, "prom_phandle_to_path failed");
683 		return (DDI_FAILURE);
684 	}
685 
686 	/*
687 	 * Now open the node and store it's ihandle
688 	 */
689 	if ((todds1307_ihandle = prom_open(tod1307_devpath)) == NULL) {
690 		cmn_err(CE_WARN, "prom_open failed");
691 		return (DDI_FAILURE);
692 	}
693 
694 	return (DDI_SUCCESS);
695 }
696 
697 /*
698  * Closes the prom interface
699  */
700 static void
701 todds1307_rele_prom()
702 {
703 	(void) prom_close(todds1307_ihandle);
704 }
705 
706 /*
707  * Read the date using "get-time" method in rtc node
708  * PROM returns 1969-1999 when reading 69-99 and
709  * 2000-2068 when reading 00-68
710  */
711 static int
712 todds1307_prom_getdate(struct rtc_t *rtc)
713 {
714 	int year;
715 	cell_t ci[12];
716 
717 	ci[0] = p1275_ptr2cell("call-method");  /* Service name */
718 	ci[1] = 2; /* # of arguments */
719 	ci[2] = 7; /* # of result cells */
720 	ci[3] = p1275_ptr2cell("get-time");
721 	ci[4] = p1275_ihandle2cell(todds1307_ihandle);
722 
723 	promif_preprom();
724 	(void) p1275_cif_handler(&ci);
725 	promif_postprom();
726 
727 	year 		= p1275_cell2int(ci[6]);
728 	rtc->rtc_mon	= p1275_cell2int(ci[7]);
729 	rtc->rtc_dom	= p1275_cell2int(ci[8]);
730 	rtc->rtc_dow	= 0;
731 	rtc->rtc_hrs	= p1275_cell2int(ci[9]);
732 	rtc->rtc_min	= p1275_cell2int(ci[10]);
733 	rtc->rtc_sec	= p1275_cell2int(ci[11]);
734 	if (year >= 2000)
735 		year -= 2000;
736 	else
737 		year -= 1900;
738 	rtc->rtc_year	= year;
739 
740 	return (DDI_SUCCESS);
741 }
742 
743 /*
744  * Read the date using "set-time" method in rtc node
745  * For values 00 - 68, write 2000-2068, and for 69-99,
746  * write 1969-1999
747  */
748 static int
749 todds1307_prom_setdate(struct rtc_t *rtc)
750 {
751 	int year;
752 	cell_t ci[12];
753 
754 	year = rtc->rtc_year;
755 
756 	if ((year < 0) || (year > 99))
757 		return (DDI_FAILURE);
758 
759 	if (year <= 68)
760 		year = rtc->rtc_year + 2000;
761 	else
762 		year = rtc->rtc_year + 1900;
763 
764 	ci[0] = p1275_ptr2cell("call-method");  /* Service name */
765 	ci[1] = 8; /* # of arguments */
766 	ci[2] = 0; /* # of result cells */
767 	ci[3] = p1275_ptr2cell("set-time");
768 	ci[4] = p1275_ihandle2cell(todds1307_ihandle);
769 	ci[5] = p1275_int2cell(year);
770 	ci[6] = p1275_int2cell(rtc->rtc_mon);
771 	ci[7] = p1275_int2cell(rtc->rtc_dom);
772 	ci[8] = p1275_int2cell(rtc->rtc_hrs);
773 	ci[9] = p1275_int2cell(rtc->rtc_min);
774 	ci[10] = p1275_int2cell(rtc->rtc_sec);
775 
776 	promif_preprom();
777 	(void) p1275_cif_handler(&ci);
778 	promif_postprom();
779 
780 	return (DDI_SUCCESS);
781 }
782