The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/i2c/ds1307.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*      $NetBSD: ds1307.c,v 1.1 2003/09/30 00:35:31 thorpej Exp $       */
    2 
    3 /*
    4  * Copyright (c) 2003 Wasabi Systems, Inc.
    5  * All rights reserved.
    6  *
    7  * Written by Steve C. Woodford and Jason R. Thorpe for Wasabi Systems, Inc.
    8  *
    9  * Redistribution and use in source and binary forms, with or without
   10  * modification, are permitted provided that the following conditions
   11  * are met:
   12  * 1. Redistributions of source code must retain the above copyright
   13  *    notice, this list of conditions and the following disclaimer.
   14  * 2. Redistributions in binary form must reproduce the above copyright
   15  *    notice, this list of conditions and the following disclaimer in the
   16  *    documentation and/or other materials provided with the distribution.
   17  * 3. All advertising materials mentioning features or use of this software
   18  *    must display the following acknowledgement:
   19  *      This product includes software developed for the NetBSD Project by
   20  *      Wasabi Systems, Inc.
   21  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
   22  *    or promote products derived from this software without specific prior
   23  *    written permission.
   24  *
   25  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
   26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   27  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   28  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
   29  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   30  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   31  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   33  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   35  * POSSIBILITY OF SUCH DAMAGE.
   36  */
   37 
   38 #include <sys/param.h>
   39 #include <sys/systm.h>
   40 #include <sys/device.h>
   41 #include <sys/kernel.h>
   42 #include <sys/fcntl.h>
   43 #include <sys/uio.h>
   44 #include <sys/conf.h>
   45 #include <sys/event.h>
   46 
   47 #include <dev/clock_subr.h>
   48 
   49 #include <dev/i2c/i2cvar.h>
   50 #include <dev/i2c/ds1307reg.h>
   51 
   52 struct dsrtc_softc {
   53         struct device sc_dev;
   54         i2c_tag_t sc_tag;
   55         int sc_address;
   56         int sc_open;
   57         struct todr_chip_handle sc_todr;
   58 };
   59 
   60 static void     dsrtc_attach(struct device *, struct device *, void *);
   61 static int      dsrtc_match(struct device *, struct cfdata *, void *);
   62 
   63 CFATTACH_DECL(dsrtc, sizeof(struct dsrtc_softc),
   64     dsrtc_match, dsrtc_attach, NULL, NULL);
   65 extern struct cfdriver dsrtc_cd;
   66 
   67 dev_type_open(dsrtc_open);
   68 dev_type_close(dsrtc_close);
   69 dev_type_read(dsrtc_read);
   70 dev_type_write(dsrtc_write);
   71 
   72 const struct cdevsw dsrtc_cdevsw = {
   73         dsrtc_open, dsrtc_close, dsrtc_read, dsrtc_write, noioctl,
   74         nostop, notty, nopoll, nommap, nokqfilter
   75 };
   76 
   77 static int dsrtc_clock_read(struct dsrtc_softc *, struct clock_ymdhms *);
   78 static int dsrtc_clock_write(struct dsrtc_softc *, struct clock_ymdhms *);
   79 static int dsrtc_gettime(struct todr_chip_handle *, struct timeval *);
   80 static int dsrtc_settime(struct todr_chip_handle *, struct timeval *);
   81 static int dsrtc_getcal(struct todr_chip_handle *, int *);
   82 static int dsrtc_setcal(struct todr_chip_handle *, int);
   83 
   84 static int
   85 dsrtc_match(struct device *parent, struct cfdata *cf, void *arg)
   86 {
   87         struct i2c_attach_args *ia = arg;
   88 
   89         if (ia->ia_addr == DS1307_ADDR)
   90                 return (1);
   91 
   92         return (0);
   93 }
   94 
   95 static void
   96 dsrtc_attach(struct device *parent, struct device *self, void *arg)
   97 {
   98         struct dsrtc_softc *sc = (struct dsrtc_softc *)self;
   99         struct i2c_attach_args *ia = arg;
  100 
  101         aprint_naive(": Real-time Clock/NVRAM\n");
  102         aprint_normal(": DS1307 Real-time Clock/NVRAM\n");
  103 
  104         sc->sc_tag = ia->ia_tag;
  105         sc->sc_address = ia->ia_addr;
  106         sc->sc_open = 0;
  107         sc->sc_todr.cookie = sc;
  108         sc->sc_todr.todr_gettime = dsrtc_gettime;
  109         sc->sc_todr.todr_settime = dsrtc_settime;
  110         sc->sc_todr.todr_getcal = dsrtc_getcal;
  111         sc->sc_todr.todr_setcal = dsrtc_setcal;
  112         sc->sc_todr.todr_setwen = NULL;
  113 
  114         todr_attach(&sc->sc_todr);
  115 }
  116 
  117 /*ARGSUSED*/
  118 int
  119 dsrtc_open(dev_t dev, int flag, int fmt, struct proc *p)
  120 {
  121         struct dsrtc_softc *sc;
  122 
  123         if ((sc = device_lookup(&dsrtc_cd, minor(dev))) == NULL)
  124                 return (ENXIO);
  125 
  126         /* XXX: Locking */
  127 
  128         if (sc->sc_open)
  129                 return (EBUSY);
  130 
  131         sc->sc_open = 1;
  132         return (0);
  133 }
  134 
  135 /*ARGSUSED*/
  136 int
  137 dsrtc_close(dev_t dev, int flag, int fmt, struct proc *p)
  138 {
  139         struct dsrtc_softc *sc;
  140 
  141         if ((sc = device_lookup(&dsrtc_cd, minor(dev))) == NULL)
  142                 return (ENXIO);
  143 
  144         sc->sc_open = 0;
  145         return (0);
  146 }
  147 
  148 /*ARGSUSED*/
  149 int
  150 dsrtc_read(dev_t dev, struct uio *uio, int flags)
  151 {
  152         struct dsrtc_softc *sc;
  153         u_int8_t ch, cmdbuf[1];
  154         int a, error;
  155 
  156         if ((sc = device_lookup(&dsrtc_cd, minor(dev))) == NULL)
  157                 return (ENXIO);
  158 
  159         if (uio->uio_offset >= DS1307_NVRAM_SIZE)
  160                 return (EINVAL);
  161 
  162         if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0)
  163                 return (error);
  164 
  165         while (uio->uio_resid && uio->uio_offset < DS1307_NVRAM_SIZE) {
  166                 a = (int)uio->uio_offset;
  167                 cmdbuf[0] = a + DS1307_NVRAM_START;
  168                 if ((error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
  169                                       sc->sc_address, cmdbuf, 1,
  170                                       &ch, 1, 0)) != 0) {
  171                         iic_release_bus(sc->sc_tag, 0);
  172                         printf("%s: dsrtc_read: read failed at 0x%x\n",
  173                             sc->sc_dev.dv_xname, a);
  174                         return (error);
  175                 }
  176                 if ((error = uiomove(&ch, 1, uio)) != 0) {
  177                         iic_release_bus(sc->sc_tag, 0);
  178                         return (error);
  179                 }
  180         }
  181 
  182         iic_release_bus(sc->sc_tag, 0);
  183 
  184         return (0);
  185 }
  186 
  187 /*ARGSUSED*/
  188 int
  189 dsrtc_write(dev_t dev, struct uio *uio, int flags)
  190 {
  191         struct dsrtc_softc *sc;
  192         u_int8_t cmdbuf[2];
  193         int a, error;
  194 
  195         if ((sc = device_lookup(&dsrtc_cd, minor(dev))) == NULL)
  196                 return (ENXIO);
  197 
  198         if (uio->uio_offset >= DS1307_NVRAM_SIZE)
  199                 return (EINVAL);
  200 
  201         if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0)
  202                 return (error);
  203 
  204         while (uio->uio_resid && uio->uio_offset < DS1307_NVRAM_SIZE) {
  205                 a = (int)uio->uio_offset;
  206                 cmdbuf[0] = a + DS1307_NVRAM_START;
  207                 if ((error = uiomove(&cmdbuf[1], 1, uio)) != 0)
  208                         break;
  209 
  210                 if ((error = iic_exec(sc->sc_tag,
  211                     uio->uio_resid ? I2C_OP_WRITE : I2C_OP_WRITE_WITH_STOP,
  212                     sc->sc_address, cmdbuf, 1, &cmdbuf[1], 1, 0)) != 0) {
  213                         printf("%s: dsrtc_write: write failed at 0x%x\n",
  214                             sc->sc_dev.dv_xname, a);
  215                         break;
  216                 }
  217         }
  218 
  219         iic_release_bus(sc->sc_tag, 0);
  220 
  221         return (error);
  222 }
  223 
  224 static int
  225 dsrtc_gettime(struct todr_chip_handle *ch, struct timeval *tv)
  226 {
  227         struct dsrtc_softc *sc = ch->cookie;
  228         struct clock_ymdhms dt, check;
  229         int retries;
  230 
  231         memset(&dt, 0, sizeof(dt));
  232         memset(&check, 0, sizeof(check));
  233 
  234         /*
  235          * Since we don't support Burst Read, we have to read the clock twice
  236          * until we get two consecutive identical results.
  237          */
  238         retries = 5;
  239         do {
  240                 dsrtc_clock_read(sc, &dt);
  241                 dsrtc_clock_read(sc, &check);
  242         } while (memcmp(&dt, &check, sizeof(check)) != 0 && --retries);
  243 
  244         tv->tv_sec = clock_ymdhms_to_secs(&dt);
  245         tv->tv_usec = 0;
  246 
  247         return (0);
  248 }
  249 
  250 static int
  251 dsrtc_settime(struct todr_chip_handle *ch, struct timeval *tv)
  252 {
  253         struct dsrtc_softc *sc = ch->cookie;
  254         struct clock_ymdhms dt;
  255 
  256         clock_secs_to_ymdhms(tv->tv_sec, &dt);
  257 
  258         if (dsrtc_clock_write(sc, &dt) == 0)
  259                 return (-1);
  260 
  261         return (0);
  262 }
  263 
  264 static int
  265 dsrtc_setcal(struct todr_chip_handle *ch, int cal)
  266 {
  267 
  268         return (EOPNOTSUPP);
  269 }
  270 
  271 static int
  272 dsrtc_getcal(struct todr_chip_handle *ch, int *cal)
  273 {
  274 
  275         return (EOPNOTSUPP);
  276 }
  277 
  278 static int
  279 dsrtc_clock_read(struct dsrtc_softc *sc, struct clock_ymdhms *dt)
  280 {
  281         u_int8_t bcd[DS1307_NRTC_REGS], cmdbuf[1];
  282         int i;
  283 
  284         if (iic_acquire_bus(sc->sc_tag, I2C_F_POLL)) {
  285                 printf("%s: dsrtc_clock_read: failed to acquire I2C bus\n",
  286                     sc->sc_dev.dv_xname);
  287                 return (0);
  288         }
  289 
  290         /* Read each RTC register in order. */
  291         for (i = DS1307_SECONDS; i < DS1307_NRTC_REGS; i++) {
  292                 cmdbuf[0] = i;
  293 
  294                 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
  295                              sc->sc_address, cmdbuf, 1,
  296                              &bcd[i], 1, I2C_F_POLL)) {
  297                         iic_release_bus(sc->sc_tag, I2C_F_POLL);
  298                         printf("%s: dsrtc_clock_read: failed to read rtc "
  299                             "at 0x%x\n", sc->sc_dev.dv_xname, i);
  300                         return (0);
  301                 }
  302         }
  303 
  304         /* Done with I2C */
  305         iic_release_bus(sc->sc_tag, I2C_F_POLL);
  306 
  307         /*
  308          * Convert the DS1307's register values into something useable
  309          */
  310         dt->dt_sec = FROMBCD(bcd[DS1307_SECONDS] & DS1307_SECONDS_MASK);
  311         dt->dt_min = FROMBCD(bcd[DS1307_MINUTES] & DS1307_MINUTES_MASK);
  312 
  313         if ((bcd[DS1307_HOURS] & DS1307_HOURS_24HRS) == 0) {
  314                 dt->dt_hour = FROMBCD(bcd[DS1307_HOURS] &
  315                     DS1307_HOURS_12MASK);
  316                 if (bcd[DS1307_HOURS] & DS1307_HOURS_12HRS_PM)
  317                         dt->dt_hour += 12;
  318         } else {
  319                 dt->dt_hour = FROMBCD(bcd[DS1307_HOURS] &
  320                     DS1307_HOURS_24MASK);
  321         }
  322 
  323         dt->dt_day = FROMBCD(bcd[DS1307_DATE] & DS1307_DATE_MASK);
  324         dt->dt_mon = FROMBCD(bcd[DS1307_MONTH] & DS1307_MONTH_MASK);
  325 
  326         /* XXX: Should be an MD way to specify EPOCH used by BIOS/Firmware */
  327         dt->dt_year = FROMBCD(bcd[DS1307_YEAR]) + POSIX_BASE_YEAR;
  328 
  329         return (1);
  330 }
  331 
  332 static int
  333 dsrtc_clock_write(struct dsrtc_softc *sc, struct clock_ymdhms *dt)
  334 {
  335         uint8_t bcd[DS1307_NRTC_REGS], cmdbuf[2];
  336         int i;
  337 
  338         /*
  339          * Convert our time representation into something the DS1307
  340          * can understand.
  341          */
  342         bcd[DS1307_SECONDS] = TOBCD(dt->dt_sec);
  343         bcd[DS1307_MINUTES] = TOBCD(dt->dt_min);
  344         bcd[DS1307_HOURS] = TOBCD(dt->dt_hour) | DS1307_HOURS_24HRS;
  345         bcd[DS1307_DATE] = TOBCD(dt->dt_day);
  346         bcd[DS1307_DAY] = TOBCD(dt->dt_wday);
  347         bcd[DS1307_MONTH] = TOBCD(dt->dt_mon);
  348         bcd[DS1307_YEAR] = TOBCD((dt->dt_year - POSIX_BASE_YEAR) % 100);
  349 
  350         if (iic_acquire_bus(sc->sc_tag, I2C_F_POLL)) {
  351                 printf("%s: dsrtc_clock_write: failed to acquire I2C bus\n",
  352                     sc->sc_dev.dv_xname);
  353                 return (0);
  354         }
  355 
  356         /* Stop the clock */
  357         cmdbuf[0] = DS1307_SECONDS;
  358         cmdbuf[1] = DS1307_SECONDS_CH;
  359 
  360         if (iic_exec(sc->sc_tag, I2C_OP_WRITE, sc->sc_address,
  361                      cmdbuf, 1, &cmdbuf[1], 1, I2C_F_POLL)) {
  362                 iic_release_bus(sc->sc_tag, I2C_F_POLL);
  363                 printf("%s: dsrtc_clock_write: failed to Hold Clock\n",
  364                     sc->sc_dev.dv_xname);
  365                 return (0);
  366         }
  367 
  368         /*
  369          * Write registers in reverse order. The last write (to the Seconds
  370          * register) will undo the Clock Hold, above.
  371          */
  372         for (i = DS1307_NRTC_REGS - 1; i >= 0; i--) {
  373                 cmdbuf[0] = i;
  374                 if (iic_exec(sc->sc_tag,
  375                              i ? I2C_OP_WRITE : I2C_OP_WRITE_WITH_STOP,
  376                              sc->sc_address, cmdbuf, 1, &bcd[i], 1,
  377                              I2C_F_POLL)) {
  378                         iic_release_bus(sc->sc_tag, I2C_F_POLL);
  379                         printf("%s: dsrtc_clock_write: failed to write rtc "
  380                             " at 0x%x\n", sc->sc_dev.dv_xname, i);
  381                         /* XXX: Clock Hold is likely still asserted! */
  382                         return (0);
  383                 }
  384         }
  385 
  386         iic_release_bus(sc->sc_tag, I2C_F_POLL);
  387 
  388         return (1);
  389 }

Cache object: da0c530beaf7d843360a92a3a0da305e


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.