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/m41st84.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: m41st84.c,v 1.2 2004/01/20 19:12:54 briggs 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/m41st84reg.h>
   51 
   52 struct strtc_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     strtc_attach(struct device *, struct device *, void *);
   61 static int      strtc_match(struct device *, struct cfdata *, void *);
   62 
   63 CFATTACH_DECL(strtc, sizeof(struct strtc_softc),
   64     strtc_match, strtc_attach, NULL, NULL);
   65 extern struct cfdriver strtc_cd;
   66 
   67 dev_type_open(strtc_open);
   68 dev_type_close(strtc_close);
   69 dev_type_read(strtc_read);
   70 dev_type_write(strtc_write);
   71 
   72 const struct cdevsw strtc_cdevsw = {
   73         strtc_open, strtc_close, strtc_read, strtc_write, noioctl,
   74         nostop, notty, nopoll, nommap, nokqfilter
   75 };
   76 
   77 static int strtc_clock_read(struct strtc_softc *, struct clock_ymdhms *);
   78 static int strtc_clock_write(struct strtc_softc *, struct clock_ymdhms *);
   79 static int strtc_gettime(struct todr_chip_handle *, struct timeval *);
   80 static int strtc_settime(struct todr_chip_handle *, struct timeval *);
   81 static int strtc_getcal(struct todr_chip_handle *, int *);
   82 static int strtc_setcal(struct todr_chip_handle *, int);
   83 
   84 static int
   85 strtc_match(struct device *parent, struct cfdata *cf, void *arg)
   86 {
   87         struct i2c_attach_args *ia = arg;
   88 
   89         if (ia->ia_addr == M41ST84_ADDR)
   90                 return (1);
   91 
   92         return (0);
   93 }
   94 
   95 static void
   96 strtc_attach(struct device *parent, struct device *self, void *arg)
   97 {
   98         struct strtc_softc *sc = (struct strtc_softc *)self;
   99         struct i2c_attach_args *ia = arg;
  100 
  101         aprint_naive(": Real-time Clock/NVRAM\n");
  102         aprint_normal(": M41ST84 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 = strtc_gettime;
  109         sc->sc_todr.todr_settime = strtc_settime;
  110         sc->sc_todr.todr_getcal = strtc_getcal;
  111         sc->sc_todr.todr_setcal = strtc_setcal;
  112         sc->sc_todr.todr_setwen = NULL;
  113 
  114         todr_attach(&sc->sc_todr);
  115 }
  116 
  117 /*ARGSUSED*/
  118 int
  119 strtc_open(dev_t dev, int flag, int fmt, struct proc *p)
  120 {
  121         struct strtc_softc *sc;
  122 
  123         if ((sc = device_lookup(&strtc_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 strtc_close(dev_t dev, int flag, int fmt, struct proc *p)
  138 {
  139         struct strtc_softc *sc;
  140 
  141         if ((sc = device_lookup(&strtc_cd, minor(dev))) == NULL)
  142                 return (ENXIO);
  143 
  144         sc->sc_open = 0;
  145         return (0);
  146 }
  147 
  148 /*ARGSUSED*/
  149 int
  150 strtc_read(dev_t dev, struct uio *uio, int flags)
  151 {
  152         struct strtc_softc *sc;
  153         u_int8_t ch, cmdbuf[1];
  154         int a, error;
  155 
  156         if ((sc = device_lookup(&strtc_cd, minor(dev))) == NULL)
  157                 return (ENXIO);
  158 
  159         if (uio->uio_offset >= M41ST84_USER_RAM_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 < M41ST84_USER_RAM_SIZE) {
  166                 a = (int)uio->uio_offset;
  167                 cmdbuf[0] = a + M41ST84_USER_RAM;
  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: strtc_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 strtc_write(dev_t dev, struct uio *uio, int flags)
  190 {
  191         struct strtc_softc *sc;
  192         u_int8_t cmdbuf[2];
  193         int a, error;
  194 
  195         if ((sc = device_lookup(&strtc_cd, minor(dev))) == NULL)
  196                 return (ENXIO);
  197 
  198         if (uio->uio_offset >= M41ST84_USER_RAM_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 < M41ST84_USER_RAM_SIZE) {
  205                 a = (int)uio->uio_offset;
  206                 cmdbuf[0] = a + M41ST84_USER_RAM;
  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: strtc_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 strtc_gettime(struct todr_chip_handle *ch, struct timeval *tv)
  226 {
  227         struct strtc_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                 strtc_clock_read(sc, &dt);
  241                 strtc_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 strtc_settime(struct todr_chip_handle *ch, struct timeval *tv)
  252 {
  253         struct strtc_softc *sc = ch->cookie;
  254         struct clock_ymdhms dt;
  255 
  256         clock_secs_to_ymdhms(tv->tv_sec, &dt);
  257 
  258         if (strtc_clock_write(sc, &dt) == 0)
  259                 return (-1);
  260 
  261         return (0);
  262 }
  263 
  264 static int
  265 strtc_setcal(struct todr_chip_handle *ch, int cal)
  266 {
  267 
  268         return (EOPNOTSUPP);
  269 }
  270 
  271 static int
  272 strtc_getcal(struct todr_chip_handle *ch, int *cal)
  273 {
  274 
  275         return (EOPNOTSUPP);
  276 }
  277 
  278 static int
  279 strtc_clock_read(struct strtc_softc *sc, struct clock_ymdhms *dt)
  280 {
  281         u_int8_t bcd[M41ST84_REG_DATE_BYTES], cmdbuf[1];
  282         int i;
  283 
  284         if (iic_acquire_bus(sc->sc_tag, I2C_F_POLL)) {
  285                 printf("%s: strtc_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 = M41ST84_REG_CSEC; i < M41ST84_REG_DATE_BYTES; 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: strtc_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 M41ST84's register values into something useable
  309          */
  310         dt->dt_sec = FROMBCD(bcd[M41ST84_REG_SEC] & M41ST84_SEC_MASK);
  311         dt->dt_min = FROMBCD(bcd[M41ST84_REG_MIN] & M41ST84_MIN_MASK);
  312         dt->dt_hour = FROMBCD(bcd[M41ST84_REG_CENHR] & M41ST84_HOUR_MASK);
  313         dt->dt_day = FROMBCD(bcd[M41ST84_REG_DATE] & M41ST84_DATE_MASK);
  314         dt->dt_mon = FROMBCD(bcd[M41ST84_REG_MONTH] & M41ST84_MONTH_MASK);
  315 
  316         /* XXX: Should be an MD way to specify EPOCH used by BIOS/Firmware */
  317         dt->dt_year = FROMBCD(bcd[M41ST84_REG_YEAR]) + POSIX_BASE_YEAR;
  318 
  319         return (1);
  320 }
  321 
  322 static int
  323 strtc_clock_write(struct strtc_softc *sc, struct clock_ymdhms *dt)
  324 {
  325         uint8_t bcd[M41ST84_REG_DATE_BYTES], cmdbuf[2];
  326         int i;
  327 
  328         /*
  329          * Convert our time representation into something the M41ST84
  330          * can understand.
  331          */
  332         bcd[M41ST84_REG_CSEC] = TOBCD(0);       /* must always write as 0 */
  333         bcd[M41ST84_REG_SEC] = TOBCD(dt->dt_sec);
  334         bcd[M41ST84_REG_MIN] = TOBCD(dt->dt_min);
  335         bcd[M41ST84_REG_CENHR] = TOBCD(dt->dt_hour);
  336         bcd[M41ST84_REG_DATE] = TOBCD(dt->dt_day);
  337         bcd[M41ST84_REG_DAY] = TOBCD(dt->dt_wday);
  338         bcd[M41ST84_REG_MONTH] = TOBCD(dt->dt_mon);
  339         bcd[M41ST84_REG_YEAR] = TOBCD((dt->dt_year - POSIX_BASE_YEAR) % 100);
  340 
  341         if (iic_acquire_bus(sc->sc_tag, I2C_F_POLL)) {
  342                 printf("%s: strtc_clock_write: failed to acquire I2C bus\n",
  343                     sc->sc_dev.dv_xname);
  344                 return (0);
  345         }
  346 
  347         /* Stop the clock */
  348         cmdbuf[0] = M41ST84_REG_SEC;
  349         cmdbuf[1] = M41ST84_SEC_ST;
  350 
  351         if (iic_exec(sc->sc_tag, I2C_OP_WRITE, sc->sc_address,
  352                      cmdbuf, 1, &cmdbuf[1], 1, I2C_F_POLL)) {
  353                 iic_release_bus(sc->sc_tag, I2C_F_POLL);
  354                 printf("%s: strtc_clock_write: failed to Hold Clock\n",
  355                     sc->sc_dev.dv_xname);
  356                 return (0);
  357         }
  358 
  359         /*
  360          * Check for the HT bit -- if set, then clock lost power & stopped
  361          * If that happened, then clear the bit so that the clock will have
  362          * a chance to run again.
  363          */
  364         cmdbuf[0] = M41ST84_REG_AL_HOUR;
  365         if (iic_exec(sc->sc_tag, I2C_OP_READ, sc->sc_address,
  366                      cmdbuf, 1, &cmdbuf[1], 1, I2C_F_POLL)) {
  367                 iic_release_bus(sc->sc_tag, I2C_F_POLL);
  368                 printf("%s: strtc_clock_write: failed to read HT\n",
  369                     sc->sc_dev.dv_xname);
  370                 return (0);
  371         }
  372         if (cmdbuf[1] & M41ST84_AL_HOUR_HT) {
  373                 cmdbuf[1] &= ~M41ST84_AL_HOUR_HT;
  374                 if (iic_exec(sc->sc_tag, I2C_OP_WRITE, sc->sc_address,
  375                              cmdbuf, 1, &cmdbuf[1], 1, I2C_F_POLL)) {
  376                         iic_release_bus(sc->sc_tag, I2C_F_POLL);
  377                         printf("%s: strtc_clock_write: failed to reset HT\n",
  378                             sc->sc_dev.dv_xname);
  379                         return (0);
  380                 }
  381         }
  382 
  383         /*
  384          * Write registers in reverse order. The last write (to the Seconds
  385          * register) will undo the Clock Hold, above.
  386          */
  387         for (i = M41ST84_REG_DATE_BYTES - 1; i >= 0; i--) {
  388                 cmdbuf[0] = i;
  389                 if (iic_exec(sc->sc_tag,
  390                              i ? I2C_OP_WRITE : I2C_OP_WRITE_WITH_STOP,
  391                              sc->sc_address, cmdbuf, 1, &bcd[i], 1,
  392                              I2C_F_POLL)) {
  393                         iic_release_bus(sc->sc_tag, I2C_F_POLL);
  394                         printf("%s: strtc_clock_write: failed to write rtc "
  395                             " at 0x%x\n", sc->sc_dev.dv_xname, i);
  396                         /* XXX: Clock Hold is likely still asserted! */
  397                         return (0);
  398                 }
  399         }
  400 
  401         iic_release_bus(sc->sc_tag, I2C_F_POLL);
  402 
  403         return (1);
  404 }

Cache object: ad98888400ede770260f94a8c78141a6


[ 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.