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/iicbus/ds133x.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 /*-
    2  * Copyright (c) 2008 Stanislav Sedov <stas@FreeBSD.org>,
    3  *                    Rafal Jaworowski <raj@FreeBSD.org>,
    4  *                    Piotr Ziecik <kosmo@semihalf.com>.
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   26  */
   27 
   28 #include <sys/cdefs.h>
   29 __FBSDID("$FreeBSD$");
   30 /*
   31  * Dallas Semiconductor DS133X RTC sitting on the I2C bus.
   32  */
   33 #include <sys/param.h>
   34 #include <sys/systm.h>
   35 #include <sys/kernel.h>
   36 #include <sys/module.h>
   37 #include <sys/clock.h>
   38 #include <sys/time.h>
   39 #include <sys/bus.h>
   40 #include <sys/resource.h>
   41 #include <sys/rman.h>
   42 
   43 #include <dev/iicbus/iicbus.h>
   44 #include <dev/iicbus/iiconf.h>
   45 
   46 #include "iicbus_if.h"
   47 #include "clock_if.h"
   48 
   49 #define DS133X_DEVNAME          "ds133x_rtc"
   50 
   51 #define DS133X_ADDR             0xd0    /* slave address */
   52 #define DS133X_DATE_REG         0x0
   53 #define DS133X_CTRL_REG         0x0e
   54 #define DS133X_OSCD_FLAG        0x80
   55 #define DS133X_OSF_FLAG         0x80
   56 
   57 #define DS133X_24H_FLAG         0x40    /* 24 hours mode. */
   58 #define DS133X_PM_FLAG          0x20    /* AM/PM bit. */
   59 #define DS133X_CENT_FLAG        0x80    /* Century selector. */
   60 #define DS133X_CENT_SHIFT       7
   61 
   62 #define DS1338_REG_CLOCK_HALT   0x00
   63 #define DS1338_REG_CONTROL      0x07
   64 #define DS1338_CLOCK_HALT       (1 << 7)
   65 #define DS1338_OSC_STOP         (1 << 5)
   66 
   67 #define DS1339_REG_CONTROL      0x0E
   68 #define DS1339_REG_STATUS       0x0F
   69 #define DS1339_OSC_STOP         (1 << 7)
   70 #define DS1339_ENABLE_OSC       (1 << 7)
   71 #define DS1339_BBSQI            (1 << 5)
   72 
   73 #define HALFSEC                 500000000       /* 1/2 of second. */
   74 
   75 #define MAX_IIC_DATA_SIZE       7
   76 
   77 enum {
   78         DS1337,
   79         DS1338,
   80         DS1339,
   81 };
   82 
   83 struct ds133x_softc {
   84         int             sc_type;
   85         device_t        sc_dev;
   86 };
   87 
   88 static int
   89 ds133x_read(device_t dev, uint8_t address, uint8_t *data, uint8_t size)
   90 {
   91         struct iic_msg msg[] = {
   92             { DS133X_ADDR, IIC_M_WR, 1, &address },
   93             { DS133X_ADDR, IIC_M_RD, size, data },
   94         };
   95 
   96         return (iicbus_transfer(dev, msg, 2));
   97 }
   98 
   99 static int
  100 ds133x_write(device_t dev, uint8_t address, uint8_t *data, uint8_t size)
  101 {
  102         uint8_t buffer[MAX_IIC_DATA_SIZE + 1];
  103         struct iic_msg msg[] = {
  104                 { DS133X_ADDR, IIC_M_WR, size + 1, buffer },
  105         };
  106 
  107         if (size > MAX_IIC_DATA_SIZE)
  108                 return (ENOMEM);
  109 
  110         buffer[0] = address;
  111         memcpy(buffer + 1, data, size);
  112 
  113         return (iicbus_transfer(dev, msg, 1));
  114 }
  115 
  116 static int
  117 ds133x_detect(device_t dev, int *sc_type)
  118 {
  119         int error;
  120         uint8_t reg, orig;
  121 
  122         /*
  123          * Check for DS1338. At address 0x0F this chip has RAM, however
  124          * DS1337 and DS1339 have status register. Bits 6-2 in status
  125          * register will be always read as 0.
  126          */
  127 
  128         if ((error = ds133x_read(dev, DS1339_REG_STATUS, &reg, 1)))
  129                 return (error);
  130 
  131         orig = reg;
  132         reg |= 0x7C;
  133 
  134         if ((error = ds133x_write(dev, DS1339_REG_STATUS, &reg, 1)))
  135                 return (error);
  136 
  137         if ((error = ds133x_read(dev, DS1339_REG_STATUS, &reg, 1)))
  138                 return (error);
  139 
  140         if ((reg & 0x7C) != 0) {
  141                 /* This is DS1338 */
  142 
  143                 if ((error = ds133x_write(dev, DS1339_REG_STATUS, &orig, 1)))
  144                         return (error);
  145 
  146                 *sc_type = DS1338;
  147 
  148                 return (0);
  149         }
  150 
  151         /*
  152          * Now Check for DS1337. Bit 5 in control register of this chip will be
  153          * allways read as 0. In DS1339 changing of this bit is safe until
  154          * chip is powered up.
  155          */
  156 
  157         if ((error = ds133x_read(dev, DS1339_REG_CONTROL, &reg, 1)))
  158                 return (error);
  159 
  160         orig = reg;
  161         reg |= DS1339_BBSQI;
  162 
  163         if ((error = ds133x_write(dev, DS1339_REG_CONTROL, &reg, 1)))
  164                 return (error);
  165 
  166         if ((error = ds133x_read(dev, DS1339_REG_CONTROL, &reg, 1)))
  167                 return (error);
  168 
  169         if ((reg & DS1339_BBSQI) != 0) {
  170                 /* This is DS1339 */
  171 
  172                 if ((error = ds133x_write(dev, DS1339_REG_CONTROL, &orig, 1)))
  173                         return (error);
  174 
  175                 *sc_type = DS1339;
  176                 return (0);
  177         }
  178 
  179         /* This is DS1337 */
  180         *sc_type = DS1337;
  181 
  182         return (0);
  183 }
  184 
  185 static int
  186 ds133x_init(device_t dev, uint8_t cs_reg, uint8_t cs_bit, uint8_t osf_reg,
  187     uint8_t osf_bit)
  188 {
  189         int error;
  190         uint8_t reg;
  191 
  192         if ((error = ds133x_read(dev, cs_reg, &reg, 1)))
  193                 return (error);
  194 
  195         if (reg & cs_bit) {     /* If clock is stopped - start it */
  196                 reg &= ~cs_bit;
  197                 if ((error = ds133x_write(dev, cs_reg, &reg, 1)))
  198                         return (error);
  199         }
  200 
  201         if ((error = ds133x_read(dev, osf_reg, &reg, 1)))
  202                 return (error);
  203 
  204         if (reg & osf_bit) {    /* Clear oscillator stop flag */
  205                 device_printf(dev, "RTC oscillator was stopped. Check system"
  206                     " time and RTC battery.\n");
  207                 reg &= ~osf_bit;
  208                 if ((error = ds133x_write(dev, osf_reg, &reg, 1)))
  209                         return (error);
  210         }
  211 
  212         return (0);
  213 }
  214 
  215 
  216 static void
  217 ds133x_identify(driver_t *driver, device_t parent)
  218 {
  219 
  220         if (device_find_child(parent, DS133X_DEVNAME, -1) == NULL)
  221                 BUS_ADD_CHILD(parent, 0, DS133X_DEVNAME, -1);
  222 }
  223 
  224 static int
  225 ds133x_probe(device_t dev)
  226 {
  227         struct ds133x_softc *sc;
  228         int error;
  229 
  230         sc = device_get_softc(dev);
  231 
  232         if ((error = ds133x_detect(dev, &sc->sc_type)))
  233                 return (error);
  234 
  235         switch (sc->sc_type) {
  236         case DS1337:
  237                 device_set_desc(dev, "Dallas Semiconductor DS1337 RTC");
  238                 break;
  239         case DS1338:
  240                 device_set_desc(dev, "Dallas Semiconductor DS1338 RTC");
  241                 break;
  242         case DS1339:
  243                 device_set_desc(dev, "Dallas Semiconductor DS1339 RTC");
  244                 break;
  245         default:
  246                 break;
  247         }
  248 
  249         return (0);
  250 }
  251 
  252 static int
  253 ds133x_attach(device_t dev)
  254 {
  255         struct ds133x_softc *sc = device_get_softc(dev);
  256 
  257         sc->sc_dev = dev;
  258 
  259         if (sc->sc_type == DS1338)
  260                 ds133x_init(dev, DS1338_REG_CLOCK_HALT, DS1338_CLOCK_HALT,
  261                     DS1338_REG_CONTROL, DS1338_OSC_STOP);
  262         else
  263                 ds133x_init(dev, DS1339_REG_CONTROL, DS1339_ENABLE_OSC,
  264                     DS1339_REG_STATUS, DS1339_OSC_STOP);
  265 
  266         clock_register(dev, 1000000);
  267 
  268         return (0);
  269 }
  270 
  271 static uint8_t
  272 ds133x_get_hours(uint8_t val)
  273 {
  274         uint8_t ret;
  275 
  276         if (!(val & DS133X_24H_FLAG))
  277                 ret = FROMBCD(val & 0x3f);
  278         else if (!(val & DS133X_PM_FLAG))
  279                 ret = FROMBCD(val & 0x1f);
  280         else
  281                 ret = FROMBCD(val & 0x1f) + 12;
  282 
  283         return (ret);
  284 }
  285 
  286 static int
  287 ds133x_gettime(device_t dev, struct timespec *ts)
  288 {
  289         struct ds133x_softc *sc = device_get_softc(dev);
  290         struct clocktime ct;
  291         uint8_t date[7];
  292         int error;
  293 
  294         error = ds133x_read(dev, DS133X_DATE_REG, date, 7);
  295         if (error == 0) {
  296                 ct.nsec = 0;
  297                 ct.sec = FROMBCD(date[0] & 0x7f);
  298                 ct.min = FROMBCD(date[1] & 0x7f);
  299                 ct.hour = ds133x_get_hours(date[2]);
  300                 ct.dow = FROMBCD(date[3] & 0x07) - 1;
  301                 ct.day = FROMBCD(date[4] & 0x3f);
  302                 ct.mon = FROMBCD(date[5] & 0x1f);
  303 
  304                 if (sc->sc_type == DS1338)
  305                         ct.year = 2000 + FROMBCD(date[6]);
  306                 else
  307                         ct.year = 1900 + FROMBCD(date[6]) +
  308                             ((date[5] & DS133X_CENT_FLAG) >> DS133X_CENT_SHIFT) * 100;
  309 
  310                 error = clock_ct_to_ts(&ct, ts);
  311         }
  312 
  313         return (error);
  314 }
  315 
  316 static int
  317 ds133x_settime(device_t dev, struct timespec *ts)
  318 {
  319         struct ds133x_softc *sc = device_get_softc(dev);
  320         struct clocktime ct;
  321         uint8_t date[7];
  322 
  323         clock_ts_to_ct(ts, &ct);
  324 
  325         date[0] = TOBCD(ct.nsec >= HALFSEC ? ct.sec + 1 : ct.sec) & 0x7f;
  326         date[1] = TOBCD(ct.min) & 0x7f;
  327         date[2] = TOBCD(ct.hour) & 0x3f;        /* We use 24-hours mode. */
  328         date[3] = TOBCD(ct.dow + 1) & 0x07;
  329         date[4] = TOBCD(ct.day) & 0x3f;
  330         date[5] = TOBCD(ct.mon) & 0x1f;
  331         if (sc->sc_type == DS1338)
  332                 date[6] = TOBCD(ct.year - 2000);
  333         else if (ct.year >= 2000) {
  334                 date[5] |= DS133X_CENT_FLAG;
  335                 date[6] = TOBCD(ct.year - 2000);
  336         } else
  337                 date[6] = TOBCD(ct.year - 1900);
  338 
  339         return (ds133x_write(dev, DS133X_DATE_REG, date, 7));
  340 }
  341 
  342 static device_method_t ds133x_methods[] = {
  343         DEVMETHOD(device_identify,      ds133x_identify),
  344         DEVMETHOD(device_probe,         ds133x_probe),
  345         DEVMETHOD(device_attach,        ds133x_attach),
  346 
  347         DEVMETHOD(clock_gettime,        ds133x_gettime),
  348         DEVMETHOD(clock_settime,        ds133x_settime),
  349 
  350         {0, 0},
  351 };
  352 
  353 static driver_t ds133x_driver = {
  354         DS133X_DEVNAME,
  355         ds133x_methods,
  356         sizeof(struct ds133x_softc),
  357 };
  358 
  359 static devclass_t ds133x_devclass;
  360 
  361 DRIVER_MODULE(ds133x, iicbus, ds133x_driver, ds133x_devclass, 0, 0);
  362 MODULE_VERSION(ds133x, 1);
  363 MODULE_DEPEND(ds133x, iicbus, 1, 1, 1);

Cache object: f8e40eb52715210305594bc8d4140183


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