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/pcf8583.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: pcf8583.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 /*
   39  * Driver for the Philips PCF8583 Real Time Clock.
   40  *
   41  * This driver is partially derived from Ben Harris's PCF8583 driver
   42  * for NetBSD/acorn26.
   43  */
   44 
   45 #include <sys/param.h>
   46 #include <sys/systm.h>
   47 #include <sys/device.h>
   48 #include <sys/kernel.h>
   49 #include <sys/fcntl.h>
   50 #include <sys/uio.h>
   51 #include <sys/conf.h>
   52 #include <sys/event.h>
   53 
   54 #include <dev/clock_subr.h>
   55 
   56 #include <dev/i2c/i2cvar.h>
   57 #include <dev/i2c/pcf8583reg.h>
   58 #include <dev/i2c/pcf8583var.h>
   59 
   60 struct pcfrtc_softc {
   61         struct device sc_dev;
   62         i2c_tag_t sc_tag;
   63         int sc_address;
   64         int sc_open;
   65         struct todr_chip_handle sc_todr;
   66 };
   67 
   68 static int  pcfrtc_match(struct device *, struct cfdata *, void *);
   69 static void pcfrtc_attach(struct device *, struct device *, void *);
   70 
   71 CFATTACH_DECL(pcfrtc, sizeof(struct pcfrtc_softc),
   72         pcfrtc_match, pcfrtc_attach, NULL, NULL);
   73 extern struct cfdriver pcfrtc_cd;
   74 
   75 dev_type_open(pcfrtc_open);
   76 dev_type_close(pcfrtc_close);
   77 dev_type_read(pcfrtc_read);
   78 dev_type_write(pcfrtc_write);
   79 
   80 const struct cdevsw pcfrtc_cdevsw = {
   81         pcfrtc_open, pcfrtc_close, pcfrtc_read, pcfrtc_write, noioctl,
   82         nostop, notty, nopoll, nommap, nokqfilter
   83 };
   84 
   85 static int pcfrtc_clock_read(struct pcfrtc_softc *, struct clock_ymdhms *,
   86                              uint8_t *);
   87 static int pcfrtc_clock_write(struct pcfrtc_softc *, struct clock_ymdhms *,
   88                               uint8_t);
   89 static int pcfrtc_gettime(struct todr_chip_handle *, struct timeval *);
   90 static int pcfrtc_settime(struct todr_chip_handle *, struct timeval *);
   91 static int pcfrtc_getcal(struct todr_chip_handle *, int *);
   92 static int pcfrtc_setcal(struct todr_chip_handle *, int);
   93 
   94 int
   95 pcfrtc_match(struct device *parent, struct cfdata *cf, void *aux)
   96 {
   97         struct i2c_attach_args *ia = aux;
   98 
   99         if ((ia->ia_addr & PCF8583_ADDRMASK) == PCF8583_ADDR)
  100                 return (1);
  101 
  102         return (0);
  103 }
  104 
  105 void
  106 pcfrtc_attach(struct device *parent, struct device *self, void *aux)
  107 {
  108         struct pcfrtc_softc *sc = (struct pcfrtc_softc *)self;
  109         struct i2c_attach_args *ia = aux;
  110         uint8_t cmdbuf[1], csr;
  111 
  112         sc->sc_tag = ia->ia_tag;
  113         sc->sc_address = ia->ia_addr;
  114 
  115         aprint_naive(": Real-time Clock/NVRAM\n");
  116         aprint_normal(": PCF8583 Real-time Clock/NVRAM\n");
  117 
  118         cmdbuf[0] = PCF8583_REG_CSR;
  119         if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_address,
  120             cmdbuf, 1, &csr, 1, 0) != 0) {
  121                 aprint_error("%s: unable to read CSR\n", sc->sc_dev.dv_xname);
  122                 return;
  123         }
  124         aprint_normal("%s: ", sc->sc_dev.dv_xname);
  125         switch (csr & PCF8583_CSR_FN_MASK) {
  126         case PCF8583_CSR_FN_32768HZ:
  127                 aprint_normal(" 32.768 kHz clock");
  128                 break;
  129 
  130         case PCF8583_CSR_FN_50HZ:
  131                 aprint_normal(" 50 Hz clock");
  132                 break;
  133 
  134         case PCF8583_CSR_FN_EVENT:
  135                 aprint_normal(" event counter");
  136                 break;
  137 
  138         case PCF8583_CSR_FN_TEST:
  139                 aprint_normal(" test mode");
  140                 break;
  141         }
  142         if (csr & PCF8583_CSR_STOP)
  143                 aprint_normal(", stopped");
  144         if (csr & PCF8583_CSR_ALARMENABLE)
  145                 aprint_normal(", alarm enabled");
  146         aprint_normal("\n");
  147 
  148         sc->sc_open = 0;
  149 
  150         sc->sc_todr.cookie = sc;
  151         sc->sc_todr.todr_gettime = pcfrtc_gettime;
  152         sc->sc_todr.todr_settime = pcfrtc_settime;
  153         sc->sc_todr.todr_getcal = pcfrtc_getcal;
  154         sc->sc_todr.todr_setcal = pcfrtc_setcal;
  155         sc->sc_todr.todr_setwen = NULL;
  156 
  157         todr_attach(&sc->sc_todr);
  158 }
  159 
  160 /*ARGSUSED*/
  161 int
  162 pcfrtc_open(dev_t dev, int flag, int fmt, struct proc *p)
  163 {
  164         struct pcfrtc_softc *sc;
  165 
  166         if ((sc = device_lookup(&pcfrtc_cd, minor(dev))) == NULL)
  167                 return (ENXIO);
  168 
  169         /* XXX: Locking */
  170 
  171         if (sc->sc_open)
  172                 return (EBUSY);
  173 
  174         sc->sc_open = 1;
  175         return (0);
  176 }
  177 
  178 /*ARGSUSED*/
  179 int
  180 pcfrtc_close(dev_t dev, int flag, int fmt, struct proc *p)
  181 {
  182         struct pcfrtc_softc *sc;
  183 
  184         if ((sc = device_lookup(&pcfrtc_cd, minor(dev))) == NULL)
  185                 return (ENXIO);
  186 
  187         sc->sc_open = 0;
  188         return (0);
  189 }
  190 
  191 /*ARGSUSED*/
  192 int
  193 pcfrtc_read(dev_t dev, struct uio *uio, int flags)
  194 {
  195         struct pcfrtc_softc *sc;
  196         u_int8_t ch, cmdbuf[1];
  197         int a, error;
  198 
  199         if ((sc = device_lookup(&pcfrtc_cd, minor(dev))) == NULL)
  200                 return (ENXIO);
  201 
  202         if (uio->uio_offset >= PCF8583_NVRAM_SIZE)
  203                 return (EINVAL);
  204 
  205         if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0)
  206                 return (error);
  207 
  208         while (uio->uio_resid && uio->uio_offset < PCF8583_NVRAM_SIZE) {
  209                 a = (int)uio->uio_offset;
  210                 cmdbuf[0] = a + PCF8583_NVRAM_START;
  211                 if ((error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
  212                                       sc->sc_address, cmdbuf, 1,
  213                                       &ch, 1, 0)) != 0) {
  214                         iic_release_bus(sc->sc_tag, 0);
  215                         printf("%s: pcfrtc_read: read failed at 0x%x\n",
  216                             sc->sc_dev.dv_xname, a);
  217                         return (error);
  218                 }
  219                 if ((error = uiomove(&ch, 1, uio)) != 0) {
  220                         iic_release_bus(sc->sc_tag, 0);
  221                         return (error);
  222                 }
  223         }
  224 
  225         iic_release_bus(sc->sc_tag, 0);
  226 
  227         return (0);
  228 }
  229 
  230 /*ARGSUSED*/
  231 int
  232 pcfrtc_write(dev_t dev, struct uio *uio, int flags)
  233 {
  234         struct pcfrtc_softc *sc;
  235         u_int8_t cmdbuf[2];
  236         int a, error;
  237 
  238         if ((sc = device_lookup(&pcfrtc_cd, minor(dev))) == NULL)
  239                 return (ENXIO);
  240 
  241         if (uio->uio_offset >= PCF8583_NVRAM_SIZE)
  242                 return (EINVAL);
  243 
  244         if ((error = iic_acquire_bus(sc->sc_tag, 0)) != 0)
  245                 return (error);
  246 
  247         while (uio->uio_resid && uio->uio_offset < PCF8583_NVRAM_SIZE) {
  248                 a = (int)uio->uio_offset;
  249                 cmdbuf[0] = a + PCF8583_NVRAM_START;
  250                 if ((error = uiomove(&cmdbuf[1], 1, uio)) != 0)
  251                         break;
  252 
  253                 if ((error = iic_exec(sc->sc_tag,
  254                     uio->uio_resid ? I2C_OP_WRITE : I2C_OP_WRITE_WITH_STOP,
  255                     sc->sc_address, cmdbuf, 1, &cmdbuf[1], 1, 0)) != 0) {
  256                         printf("%s: pcfrtc_write: write failed at 0x%x\n",
  257                             sc->sc_dev.dv_xname, a);
  258                         return (error);
  259                 }
  260         }
  261 
  262         iic_release_bus(sc->sc_tag, 0);
  263 
  264         return (error);
  265 }
  266 
  267 static int
  268 pcfrtc_gettime(struct todr_chip_handle *ch, struct timeval *tv)
  269 {
  270         struct pcfrtc_softc *sc = ch->cookie;
  271         struct clock_ymdhms dt;
  272         uint8_t centi;
  273 
  274         if (pcfrtc_clock_read(sc, &dt, &centi) == 0)
  275                 return (-1);
  276 
  277         tv->tv_sec = clock_ymdhms_to_secs(&dt);
  278         tv->tv_usec = centi * 10000;
  279 
  280         return (0);
  281 }
  282 
  283 static int
  284 pcfrtc_settime(struct todr_chip_handle *ch, struct timeval *tv)
  285 {
  286         struct pcfrtc_softc *sc = ch->cookie;
  287         struct clock_ymdhms dt;
  288 
  289         clock_secs_to_ymdhms(tv->tv_sec, &dt);
  290 
  291         if (pcfrtc_clock_write(sc, &dt, tv->tv_usec / 10000) == 0)
  292                 return (-1);
  293 
  294         return (0);
  295 }
  296 
  297 static int
  298 pcfrtc_setcal(struct todr_chip_handle *ch, int cal)
  299 {
  300 
  301         return (EOPNOTSUPP);
  302 }
  303 
  304 static int
  305 pcfrtc_getcal(struct todr_chip_handle *ch, int *cal)
  306 {
  307 
  308         return (EOPNOTSUPP);
  309 }
  310 
  311 static const int pcf8583_rtc_offset[] = {
  312         PCF8583_REG_CSR,
  313         PCF8583_REG_CENTI,
  314         PCF8583_REG_SEC,
  315         PCF8583_REG_MIN,
  316         PCF8583_REG_HOUR,
  317         PCF8583_REG_YEARDATE,
  318         PCF8583_REG_WKDYMON,
  319         PCF8583_REG_TIMER,
  320         0xc0,                   /* NVRAM -- year stored here */
  321         0xc1,                   /* NVRAM -- century stored here */
  322 };
  323 
  324 static int
  325 pcfrtc_clock_read(struct pcfrtc_softc *sc, struct clock_ymdhms *dt,
  326     uint8_t *centi)
  327 {
  328         u_int8_t bcd[10], cmdbuf[1];
  329         int i;
  330 
  331         if (iic_acquire_bus(sc->sc_tag, I2C_F_POLL)) {
  332                 printf("%s: pcfrtc_clock_read: failed to acquire I2C bus\n",
  333                     sc->sc_dev.dv_xname);
  334                 return (0);
  335         }
  336 
  337         /* Read each timekeeping register in order. */
  338         for (i = 0; i < 10; i++) {
  339                 cmdbuf[0] = pcf8583_rtc_offset[i];
  340 
  341                 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
  342                              sc->sc_address, cmdbuf, 1,
  343                              &bcd[i], 1, I2C_F_POLL)) {
  344                         iic_release_bus(sc->sc_tag, I2C_F_POLL);
  345                         printf("%s: pcfrtc_clock_read: failed to read rtc "
  346                             "at 0x%x\n", sc->sc_dev.dv_xname,
  347                             pcf8583_rtc_offset[i]);
  348                         return (0);
  349                 }
  350         }
  351 
  352         /* Done with I2C */
  353         iic_release_bus(sc->sc_tag, I2C_F_POLL);
  354 
  355         /*
  356          * Convert the PCF8583's register values into something useable
  357          */
  358         *centi      = FROMBCD(bcd[PCF8583_REG_CENTI]);
  359         dt->dt_sec  = FROMBCD(bcd[PCF8583_REG_SEC]);
  360         dt->dt_min  = FROMBCD(bcd[PCF8583_REG_MIN]);
  361         dt->dt_hour = FROMBCD(bcd[PCF8583_REG_HOUR] & PCF8583_HOUR_MASK);
  362         if (bcd[PCF8583_REG_HOUR] & PCF8583_HOUR_12H) {
  363                 dt->dt_hour %= 12;      /* 12AM -> 0, 12PM -> 12 */
  364                 if (bcd[PCF8583_REG_HOUR] & PCF8583_HOUR_PM)
  365                         dt->dt_hour += 12;
  366         }
  367 
  368         dt->dt_day = FROMBCD(bcd[PCF8583_REG_YEARDATE] & PCF8583_DATE_MASK);
  369         dt->dt_mon = FROMBCD(bcd[PCF8583_REG_WKDYMON] & PCF8583_MON_MASK);
  370 
  371         dt->dt_year = bcd[8] + (bcd[9] * 100);
  372         /* Try to notice if the year's rolled over. */
  373         if (bcd[PCF8583_REG_CSR] & PCF8583_CSR_MASK)
  374                 printf("%s: cannot check year in mask mode\n",
  375                     sc->sc_dev.dv_xname);
  376         else {
  377                 while (dt->dt_year % 4 !=
  378                        (bcd[PCF8583_REG_YEARDATE] &
  379                         PCF8583_YEAR_MASK) >> PCF8583_YEAR_SHIFT)
  380                         dt->dt_year++;
  381         }
  382 
  383         return (1);
  384 }
  385 
  386 static int
  387 pcfrtc_clock_write(struct pcfrtc_softc *sc, struct clock_ymdhms *dt,
  388     uint8_t centi)
  389 {
  390         uint8_t bcd[10], cmdbuf[2];
  391         int i;
  392 
  393         /*
  394          * Convert our time representation into something the PCF8583
  395          * can understand.
  396          */
  397         bcd[PCF8583_REG_CENTI]    = centi;
  398         bcd[PCF8583_REG_SEC]      = TOBCD(dt->dt_sec);
  399         bcd[PCF8583_REG_MIN]      = TOBCD(dt->dt_min);
  400         bcd[PCF8583_REG_HOUR]     = TOBCD(dt->dt_hour) & PCF8583_HOUR_MASK;
  401         bcd[PCF8583_REG_YEARDATE] = TOBCD(dt->dt_day) |
  402             ((dt->dt_year % 4) << PCF8583_YEAR_SHIFT);
  403         bcd[PCF8583_REG_WKDYMON]  = TOBCD(dt->dt_mon) |
  404             ((dt->dt_wday % 4) << PCF8583_WKDY_SHIFT);
  405         bcd[8]                    = dt->dt_year % 100;
  406         bcd[9]                    = dt->dt_year / 100;
  407 
  408         if (iic_acquire_bus(sc->sc_tag, I2C_F_POLL)) {
  409                 printf("%s: pcfrtc_clock_write: failed to acquire I2C bus\n",
  410                     sc->sc_dev.dv_xname);
  411                 return (0);
  412         }
  413 
  414         for (i = 1; i < 10; i++) {
  415                 cmdbuf[0] = pcf8583_rtc_offset[i];
  416                 if (iic_exec(sc->sc_tag,
  417                              i != 9 ? I2C_OP_WRITE : I2C_OP_WRITE_WITH_STOP,
  418                              sc->sc_address, cmdbuf, 1,
  419                              &bcd[i], 1, I2C_F_POLL)) {
  420                         iic_release_bus(sc->sc_tag, I2C_F_POLL);
  421                         printf("%s: pcfrtc_clock_write: failed to write rtc "
  422                             " at 0x%x\n", sc->sc_dev.dv_xname,
  423                             pcf8583_rtc_offset[i]);
  424                         return (0);
  425                 }
  426         }
  427 
  428         iic_release_bus(sc->sc_tag, I2C_F_POLL);
  429 
  430         return (1);
  431 }
  432 
  433 int
  434 pcfrtc_bootstrap_read(i2c_tag_t tag, int i2caddr, int offset,
  435     u_int8_t *rvp, size_t len)
  436 {
  437         u_int8_t cmdbuf[1];
  438 
  439         /*
  440          * NOTE: "offset" is an absolute offset into the PCF8583
  441          * address space, not relative to the NVRAM.
  442          */
  443 
  444         if (len == 0)
  445                 return (0);
  446 
  447         if (iic_acquire_bus(tag, I2C_F_POLL) != 0)
  448                 return (-1);
  449 
  450         while (len) {
  451                 /* Read a single byte. */
  452                 cmdbuf[0] = offset;
  453                 if (iic_exec(tag, I2C_OP_READ_WITH_STOP, i2caddr,
  454                              cmdbuf, 1, rvp, 1, I2C_F_POLL)) {
  455                         iic_release_bus(tag, I2C_F_POLL);
  456                         return (-1);
  457                 }
  458 
  459                 len--;
  460                 rvp++;
  461                 offset++;
  462         }
  463 
  464         iic_release_bus(tag, I2C_F_POLL);
  465         return (0);
  466 }
  467 
  468 int
  469 pcfrtc_bootstrap_write(i2c_tag_t tag, int i2caddr, int offset,
  470     u_int8_t *rvp, size_t len)
  471 {
  472         u_int8_t cmdbuf[1];
  473 
  474         /*
  475          * NOTE: "offset" is an absolute offset into the PCF8583
  476          * address space, not relative to the NVRAM.
  477          */
  478 
  479         if (len == 0)
  480                 return (0);
  481 
  482         if (iic_acquire_bus(tag, I2C_F_POLL) != 0)
  483                 return (-1);
  484 
  485         while (len) {
  486                 /* Write a single byte. */
  487                 cmdbuf[0] = offset;
  488                 if (iic_exec(tag, I2C_OP_WRITE_WITH_STOP, i2caddr,
  489                              cmdbuf, 1, rvp, 1, I2C_F_POLL)) {
  490                         iic_release_bus(tag, I2C_F_POLL);
  491                         return (-1);
  492                 }
  493 
  494                 len--;
  495                 rvp++;
  496                 offset++;
  497         }
  498 
  499         iic_release_bus(tag, I2C_F_POLL);
  500         return (0);
  501 }

Cache object: d462bdc7907c079e5cd4dbe13b50a4a3


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