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