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/m41t00.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: m41t00.c,v 1.2 2003/10/20 16:24:10 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/proc.h>
   46 #include <sys/event.h>
   47 
   48 #include <machine/bus.h>
   49 
   50 #include <dev/clock_subr.h>
   51 
   52 #include <dev/i2c/i2cvar.h>
   53 #include <dev/i2c/m41t00reg.h>
   54 
   55 struct m41t00_softc {
   56         struct device sc_dev;
   57         i2c_tag_t sc_tag;
   58         int sc_address;
   59         int sc_open;
   60         struct todr_chip_handle sc_todr;
   61 };
   62 
   63 static int  m41t00_match(struct device *, struct cfdata *, void *);
   64 static void m41t00_attach(struct device *, struct device *, void *);
   65 
   66 CFATTACH_DECL(m41trtc, sizeof(struct m41t00_softc),
   67         m41t00_match, m41t00_attach, NULL, NULL);
   68 extern struct cfdriver m41trtc_cd;
   69 
   70 dev_type_open(m41t00_open);
   71 dev_type_close(m41t00_close);
   72 dev_type_read(m41t00_read);
   73 dev_type_write(m41t00_write);
   74 
   75 const struct cdevsw m41t00_cdevsw = {
   76         m41t00_open, m41t00_close, m41t00_read, m41t00_write, noioctl,
   77         nostop, notty, nopoll, nommap, nokqfilter,
   78 };
   79 
   80 static int m41t00_clock_read(struct m41t00_softc *, struct clock_ymdhms *);
   81 static int m41t00_clock_write(struct m41t00_softc *, struct clock_ymdhms *);
   82 static int m41t00_gettime(struct todr_chip_handle *, struct timeval *);
   83 static int m41t00_settime(struct todr_chip_handle *, struct timeval *);
   84 static int m41t00_getcal(struct todr_chip_handle *, int *);
   85 static int m41t00_setcal(struct todr_chip_handle *, int);
   86 
   87 int
   88 m41t00_match(struct device *parent, struct cfdata *cf, void *aux)
   89 {
   90         struct i2c_attach_args *ia = aux;
   91 
   92         if (ia->ia_addr == M41T00_ADDR) {
   93                 return 1;
   94         }
   95 
   96         return 0;
   97 }
   98 
   99 void
  100 m41t00_attach(struct device *parent, struct device *self, void *aux)
  101 {
  102         struct m41t00_softc *sc = (struct m41t00_softc *)self;
  103         struct i2c_attach_args *ia = aux;
  104 
  105         sc->sc_tag = ia->ia_tag;
  106         sc->sc_address = ia->ia_addr;
  107 
  108         aprint_naive(": Real-time Clock\n");
  109         aprint_normal(": M41T00 Real-time Clock\n");
  110 
  111         sc->sc_open = 0;
  112         sc->sc_todr.cookie = sc;
  113         sc->sc_todr.todr_gettime = m41t00_gettime;
  114         sc->sc_todr.todr_settime = m41t00_settime;
  115         sc->sc_todr.todr_getcal = m41t00_getcal;
  116         sc->sc_todr.todr_setcal = m41t00_setcal;
  117         sc->sc_todr.todr_setwen = NULL;
  118 
  119         todr_attach(&sc->sc_todr);
  120 }
  121 
  122 /*ARGSUSED*/
  123 int
  124 m41t00_open(dev_t dev, int flag, int fmt, struct proc *p)
  125 {
  126         struct m41t00_softc *sc;
  127 
  128         if ((sc = device_lookup(&m41trtc_cd, minor(dev))) == NULL)
  129                 return ENXIO;
  130 
  131         /* XXX: Locking */
  132 
  133         if (sc->sc_open)
  134                 return EBUSY;
  135 
  136         sc->sc_open = 1;
  137         return 0;
  138 }
  139 
  140 /*ARGSUSED*/
  141 int
  142 m41t00_close(dev_t dev, int flag, int fmt, struct proc *p)
  143 {
  144         struct m41t00_softc *sc;
  145 
  146         if ((sc = device_lookup(&m41trtc_cd, minor(dev))) == NULL)
  147                 return ENXIO;
  148 
  149         sc->sc_open = 0;
  150         return 0;
  151 }
  152 
  153 /*ARGSUSED*/
  154 int
  155 m41t00_read(dev_t dev, struct uio *uio, int flags)
  156 {
  157         struct m41t00_softc *sc;
  158         u_int8_t ch, cmdbuf[1];
  159         int a, error;
  160 
  161         if ((sc = device_lookup(&m41trtc_cd, minor(dev))) == NULL)
  162                 return (ENXIO);
  163 
  164         if (uio->uio_offset >= M41T00_NBYTES)
  165                 return (EINVAL);
  166 
  167         if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0)
  168                 return (error);
  169 
  170         while (uio->uio_resid && uio->uio_offset < M41T00_NBYTES) {
  171                 a = (int)uio->uio_offset;
  172                 cmdbuf[0] = a;
  173                 if ((error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
  174                                       sc->sc_address, cmdbuf, 0,
  175                                       &ch, 1, 0)) != 0) {
  176                         iic_release_bus(sc->sc_tag, 0);
  177                         printf("%s: m41t00_read: read failed at 0x%x\n",
  178                             sc->sc_dev.dv_xname, a);
  179                         return (error);
  180                 }
  181                 if ((error = uiomove(&ch, 1, uio)) != 0) {
  182                         iic_release_bus(sc->sc_tag, 0);
  183                         return (error);
  184                 }
  185         }
  186 
  187         iic_release_bus(sc->sc_tag, 0);
  188 
  189         return (0);
  190 }
  191 
  192 /*ARGSUSED*/
  193 int
  194 m41t00_write(dev_t dev, struct uio *uio, int flags)
  195 {
  196         struct m41t00_softc *sc;
  197         u_int8_t cmdbuf[2];
  198         int a, error;
  199 
  200         if ((sc = device_lookup(&m41trtc_cd, minor(dev))) == NULL)
  201                 return (ENXIO);
  202 
  203         if (uio->uio_offset >= M41T00_NBYTES)
  204                 return (EINVAL);
  205 
  206         if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0)
  207                 return (error);
  208 
  209         while (uio->uio_resid && uio->uio_offset < M41T00_NBYTES) {
  210                 a = (int)uio->uio_offset;
  211 
  212                 cmdbuf[0] = a;
  213                 if ((error = uiomove(&cmdbuf[1], 1, uio)) != 0)
  214                         break;
  215 
  216                 if ((error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
  217                                       sc->sc_address,
  218                                       cmdbuf, 1, &cmdbuf[1], 1, 0)) != 0) {
  219                         printf("%s: m41t00_write: write failed at 0x%x\n",
  220                             sc->sc_dev.dv_xname, a);
  221                         break;
  222                 }
  223         }
  224 
  225         iic_release_bus(sc->sc_tag, 0);
  226 
  227         return (error);
  228 }
  229 
  230 static int
  231 m41t00_gettime(struct todr_chip_handle *ch, struct timeval *tv)
  232 {
  233         struct m41t00_softc *sc = ch->cookie;
  234         struct clock_ymdhms dt;
  235 
  236         if (m41t00_clock_read(sc, &dt) == 0)
  237                 return (-1);
  238 
  239         tv->tv_sec = clock_ymdhms_to_secs(&dt);
  240         tv->tv_usec = 0;
  241 
  242         return (0);
  243 }
  244 
  245 static int
  246 m41t00_settime(struct todr_chip_handle *ch, struct timeval *tv)
  247 {
  248         struct m41t00_softc *sc = ch->cookie;
  249         struct clock_ymdhms dt;
  250 
  251         clock_secs_to_ymdhms(tv->tv_sec, &dt);
  252 
  253         if (m41t00_clock_write(sc, &dt) == 0)
  254                 return (-1);
  255 
  256         return (0);
  257 }
  258 
  259 static int
  260 m41t00_setcal(struct todr_chip_handle *ch, int cal)
  261 {
  262 
  263         return (EOPNOTSUPP);
  264 }
  265 
  266 static int
  267 m41t00_getcal(struct todr_chip_handle *ch, int *cal)
  268 {
  269 
  270         return (EOPNOTSUPP);
  271 }
  272 
  273 static int m41t00_rtc_offset[] = {
  274         M41T00_SEC,
  275         M41T00_MIN,
  276         M41T00_CENHR,
  277         M41T00_DAY,
  278         M41T00_DATE,
  279         M41T00_MONTH,
  280         M41T00_YEAR,
  281 };
  282 
  283 static int
  284 m41t00_clock_read(struct m41t00_softc *sc, struct clock_ymdhms *dt)
  285 {
  286         u_int8_t bcd[M41T00_NBYTES], cmdbuf[1];
  287         int i, n;
  288 
  289         if (iic_acquire_bus(sc->sc_tag, I2C_F_POLL)) {
  290                 printf("%s: m41t00_clock_read: failed to acquire I2C bus\n",
  291                     sc->sc_dev.dv_xname);
  292                 return (0);
  293         }
  294 
  295         /* Read each timekeeping register in order. */
  296         n = sizeof(m41t00_rtc_offset) / sizeof(m41t00_rtc_offset[0]);
  297         for (i = 0; i < n ; i++) {
  298                 cmdbuf[0] = m41t00_rtc_offset[i];
  299 
  300                 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
  301                              sc->sc_address, cmdbuf, 1,
  302                              &bcd[i], 1, I2C_F_POLL)) {
  303                         iic_release_bus(sc->sc_tag, I2C_F_POLL);
  304                         printf("%s: m41t00_clock_read: failed to read rtc "
  305                             "at 0x%x\n", sc->sc_dev.dv_xname,
  306                             m41t00_rtc_offset[i]);
  307                         return (0);
  308                 }
  309         }
  310 
  311         /* Done with I2C */
  312         iic_release_bus(sc->sc_tag, I2C_F_POLL);
  313 
  314         /*
  315          * Convert the M41T00's register values into something useable
  316          */
  317         dt->dt_sec = FROMBCD(bcd[M41T00_SEC] & M41T00_SEC_MASK);
  318         dt->dt_min = FROMBCD(bcd[M41T00_MIN] & M41T00_MIN_MASK);
  319         dt->dt_hour = FROMBCD(bcd[M41T00_CENHR] & M41T00_HOUR_MASK);
  320         dt->dt_day = FROMBCD(bcd[M41T00_DATE] & M41T00_DATE_MASK);
  321         dt->dt_wday = FROMBCD(bcd[M41T00_DAY] & M41T00_DAY_MASK);
  322         dt->dt_mon = FROMBCD(bcd[M41T00_MONTH] & M41T00_MONTH_MASK);
  323         dt->dt_year = FROMBCD(bcd[M41T00_YEAR] & M41T00_YEAR_MASK);
  324 
  325         /*
  326          * Since the m41t00 just stores 00-99, and this is 2003 as I write
  327          * this comment, use 2000 as a base year
  328          */
  329         dt->dt_year += 2000;
  330 
  331         return (1);
  332 }
  333 
  334 static int
  335 m41t00_clock_write(struct m41t00_softc *sc, struct clock_ymdhms *dt)
  336 {
  337         uint8_t bcd[M41T00_DATE_BYTES], cmdbuf[2];
  338         uint8_t init_seconds, final_seconds;
  339         int i;
  340 
  341         /*
  342          * Convert our time representation into something the MAX6900
  343          * can understand.
  344          */
  345         bcd[M41T00_SEC] = TOBCD(dt->dt_sec);
  346         bcd[M41T00_MIN] = TOBCD(dt->dt_min);
  347         bcd[M41T00_CENHR] = TOBCD(dt->dt_hour);
  348         bcd[M41T00_DATE] = TOBCD(dt->dt_day);
  349         bcd[M41T00_DAY] = TOBCD(dt->dt_wday);
  350         bcd[M41T00_MONTH] = TOBCD(dt->dt_mon);
  351         bcd[M41T00_YEAR] = TOBCD(dt->dt_year % 100);
  352 
  353         if (iic_acquire_bus(sc->sc_tag, I2C_F_POLL)) {
  354                 printf("%s: m41t00_clock_write: failed to acquire I2C bus\n",
  355                     sc->sc_dev.dv_xname);
  356                 return (0);
  357         }
  358 
  359         /*
  360          * The MAX6900 RTC manual recommends ensuring "atomicity" of
  361          * a non-burst write by:
  362          *
  363          *      - writing SECONDS
  364          *      - reading back SECONDS, remembering it as "initial seconds"
  365          *      - write the remaing RTC registers
  366          *      - read back SECONDS as "final seconds"
  367          *      - if "initial seconds" == 59, ensure "final seconds" == 59
  368          *      - else, ensure "final seconds" is no more than one second
  369          *        beyond "initial seconds".
  370          *
  371          * This sounds reasonable for the M41T00, too.
  372          */
  373  again:
  374         cmdbuf[0] = M41T00_SEC;
  375         if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_address,
  376                      cmdbuf, 1, &bcd[M41T00_SEC], 1, I2C_F_POLL)) {
  377                 iic_release_bus(sc->sc_tag, I2C_F_POLL);
  378                 printf("%s: m41t00_clock_write: failed to write SECONDS\n",
  379                     sc->sc_dev.dv_xname);
  380                 return (0);
  381         }
  382 
  383         cmdbuf[0] = M41T00_SEC;
  384         if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_address,
  385                      cmdbuf, 1, &init_seconds, 1, I2C_F_POLL)) {
  386                 iic_release_bus(sc->sc_tag, I2C_F_POLL);
  387                 printf("%s: m41t00_clock_write: failed to read "
  388                     "INITIAL SECONDS\n", sc->sc_dev.dv_xname);
  389                 return (0);
  390         }
  391         init_seconds = FROMBCD(init_seconds & M41T00_SEC_MASK);
  392 
  393         for (i = 1; i < M41T00_DATE_BYTES; i++) {
  394                 cmdbuf[0] = m41t00_rtc_offset[i];
  395                 if (iic_exec(sc->sc_tag,
  396                              I2C_OP_WRITE_WITH_STOP, sc->sc_address,
  397                              cmdbuf, 1, &bcd[i], 1, I2C_F_POLL)) {
  398                         iic_release_bus(sc->sc_tag, I2C_F_POLL);
  399                         printf("%s: m41t00_clock_write: failed to write rtc "
  400                             " at 0x%x\n", sc->sc_dev.dv_xname,
  401                             m41t00_rtc_offset[i]);
  402                         return (0);
  403                 }
  404         }
  405 
  406         cmdbuf[0] = M41T00_SEC;
  407         if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_address,
  408                      cmdbuf, 1, &final_seconds, 1, I2C_F_POLL)) {
  409                 iic_release_bus(sc->sc_tag, I2C_F_POLL);
  410                 printf("%s: m41t00_clock_write: failed to read "
  411                     "FINAL SECONDS\n", sc->sc_dev.dv_xname);
  412                 return (0);
  413         }
  414         final_seconds = FROMBCD(final_seconds & M41T00_SEC_MASK);
  415 
  416         if ((init_seconds != final_seconds) &&
  417             (((init_seconds + 1) % 60) != final_seconds)) {
  418 #if 1
  419                 printf("%s: m41t00_clock_write: init %d, final %d, try again\n",
  420                     sc->sc_dev.dv_xname, init_seconds, final_seconds);
  421 #endif
  422                 goto again;
  423         }
  424 
  425         iic_release_bus(sc->sc_tag, I2C_F_POLL);
  426 
  427         return (1);
  428 }

Cache object: 13251d4698596e1a8722448b6933411f


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