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/rtc/pcf85063.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  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2021 Alstom Group.
    5  * Copyright (c) 2021 Semihalf.
    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 #include "opt_platform.h"
   32 
   33 #include <sys/param.h>
   34 #include <sys/systm.h>
   35 #include <sys/bus.h>
   36 #include <sys/clock.h>
   37 #include <sys/kernel.h>
   38 #include <sys/lock.h>
   39 #include <sys/module.h>
   40 
   41 #include <dev/ofw/ofw_bus.h>
   42 #include <dev/ofw/ofw_bus_subr.h>
   43 
   44 #include <dev/iicbus/iiconf.h>
   45 #include <dev/iicbus/iicbus.h>
   46 
   47 #include "clock_if.h"
   48 #include "iicbus_if.h"
   49 
   50 #define BIT(x)                          (1 << (x))
   51 
   52 #define PCF85063_CTRL1_REG              0x0
   53 #define PCF85063_TIME_REG               0x4
   54 
   55 #define PCF85063_CTRL1_TIME_FORMAT      BIT(1)
   56 #define PCF85063_CTRL1_RTC_CLK_STOP     BIT(5)
   57 #define PCF85063_TIME_REG_OSC_STOP      BIT(7)
   58 
   59 #define PCF85063_HALF_OF_SEC_NS         500000000
   60 
   61 struct pcf85063_time {
   62         uint8_t         sec;
   63         uint8_t         min;
   64         uint8_t         hour;
   65         uint8_t         day;
   66         uint8_t         dow;
   67         uint8_t         mon;
   68         uint8_t         year;
   69 };
   70 
   71 static int pcf85063_attach(device_t dev);
   72 static int pcf85063_detach(device_t dev);
   73 static int pcf85063_probe(device_t dev);
   74 
   75 static int pcf85063_get_time(device_t dev, struct timespec *ts);
   76 static int pcf85063_set_time(device_t dev, struct timespec *ts);
   77 
   78 static int pcf85063_check_status(device_t dev);
   79 
   80 static struct ofw_compat_data pcf85063_compat_data[] = {
   81         { "nxp,pcf85063",               1},
   82         { NULL,                         0}
   83 };
   84 
   85 static int
   86 pcf85063_check_status(device_t dev)
   87 {
   88         uint8_t flags;
   89         int error;
   90 
   91         error = iicdev_readfrom(dev, PCF85063_TIME_REG, &flags, 1, IIC_WAIT);
   92         if (error != 0)
   93                 return (error);
   94 
   95         if (flags & PCF85063_TIME_REG_OSC_STOP) {
   96                 device_printf(dev,
   97                     "Low voltage flag set, date is incorrect.\n");
   98                 return (ENXIO);
   99         }
  100 
  101         return (0);
  102 }
  103 
  104 static int
  105 pcf85063_attach(device_t dev)
  106 {
  107 
  108         clock_register_flags(dev, 1000000, 0);
  109         clock_schedule(dev, 1);
  110 
  111         return (0);
  112 }
  113 
  114 static int
  115 pcf85063_detach(device_t dev)
  116 {
  117 
  118         clock_unregister(dev);
  119 
  120         return (0);
  121 }
  122 
  123 static int
  124 pcf85063_get_time(device_t dev, struct timespec *ts)
  125 {
  126         struct pcf85063_time data;
  127         struct bcd_clocktime bcd;
  128         uint8_t control_reg;
  129         int error;
  130 
  131         error = pcf85063_check_status(dev);
  132         if (error != 0)
  133                 return (error);
  134 
  135         /* read hour format (12/24 hour mode) */
  136         error = iicdev_readfrom(dev, PCF85063_CTRL1_REG, &control_reg,
  137             sizeof(uint8_t), IIC_WAIT);
  138         if (error != 0)
  139                 return (error);
  140 
  141         /* read current date and time */
  142         error = iicdev_readfrom(dev, PCF85063_TIME_REG, &data,
  143             sizeof(struct pcf85063_time), IIC_WAIT);
  144         if (error != 0)
  145                 return (error);
  146 
  147         bcd.nsec = 0;
  148         bcd.sec = data.sec & 0x7F;
  149         bcd.min = data.min & 0x7F;
  150 
  151         if (control_reg & PCF85063_CTRL1_TIME_FORMAT) {
  152                 /* 12 hour mode */
  153                 bcd.hour = data.hour & 0x1F;
  154                 /* Check if hour is pm */
  155                 bcd.ispm = data.hour & 0x20;
  156         } else {
  157                 /* 24 hour mode */
  158                 bcd.hour = data.hour & 0x3F;
  159                 bcd.ispm = false;
  160         }
  161 
  162         bcd.dow = (data.dow & 0x7) + 1;
  163         bcd.day = data.day & 0x3F;
  164         bcd.mon = data.mon & 0x1F;
  165         bcd.year = data.year;
  166 
  167         clock_dbgprint_bcd(dev, CLOCK_DBG_READ, &bcd);
  168         error = clock_bcd_to_ts(&bcd, ts,
  169             control_reg & PCF85063_CTRL1_TIME_FORMAT);
  170 
  171         return (error);
  172 }
  173 
  174 static int
  175 pcf85063_set_time(device_t dev, struct timespec *ts)
  176 {
  177         uint8_t time_reg, ctrl_reg;
  178         struct pcf85063_time data;
  179         struct bcd_clocktime bcd;
  180         int error;
  181 
  182         error = iicdev_readfrom(dev, PCF85063_TIME_REG, &ctrl_reg,
  183             sizeof(uint8_t), IIC_WAIT);
  184 
  185         ts->tv_sec -= utc_offset();
  186         clock_ts_to_bcd(ts, &bcd, false);
  187         clock_dbgprint_bcd(dev, CLOCK_DBG_WRITE, &bcd);
  188 
  189         data.sec = bcd.sec;
  190         data.min = bcd.min;
  191         data.hour = bcd.hour;
  192         data.dow = bcd.dow - 1;
  193         data.day = bcd.day;
  194         data.mon = bcd.mon;
  195         data.year = bcd.year;
  196 
  197         if (ts->tv_nsec > PCF85063_HALF_OF_SEC_NS)
  198                 data.sec++;
  199 
  200         /* disable clock */
  201         error = iicdev_readfrom(dev, PCF85063_CTRL1_REG, &ctrl_reg,
  202             sizeof(uint8_t), IIC_WAIT);
  203         if (error != 0)
  204                 return (error);
  205 
  206         ctrl_reg |= PCF85063_CTRL1_RTC_CLK_STOP;
  207         /* Explicitly set 24-hour mode. */
  208         ctrl_reg &= ~PCF85063_CTRL1_TIME_FORMAT;
  209 
  210         error = iicdev_writeto(dev, PCF85063_CTRL1_REG, &ctrl_reg,
  211             sizeof(uint8_t), IIC_WAIT);
  212         if (error != 0)
  213                 return (error);
  214 
  215         /* clock is disabled now, write time and date */
  216         error = iicdev_writeto(dev, PCF85063_TIME_REG, &data,
  217             sizeof(struct pcf85063_time), IIC_WAIT);
  218         if (error != 0)
  219                 return (error);
  220 
  221         /* restart clock */
  222         ctrl_reg &= ~PCF85063_CTRL1_RTC_CLK_STOP;
  223         error = iicdev_writeto(dev, PCF85063_CTRL1_REG, &ctrl_reg,
  224             sizeof(uint8_t), IIC_WAIT);
  225         if (error != 0)
  226                 return (error);
  227 
  228         /* reset low voltage flag */
  229         error = iicdev_readfrom(dev, PCF85063_TIME_REG, &time_reg,
  230             sizeof(uint8_t), IIC_WAIT);
  231         if (error != 0)
  232                 return (error);
  233 
  234         time_reg &= ~PCF85063_TIME_REG_OSC_STOP;
  235 
  236         error = iicdev_writeto(dev, PCF85063_TIME_REG, &time_reg,
  237             sizeof(uint8_t), IIC_WAIT);
  238 
  239         return (error);
  240 }
  241 
  242 static int
  243 pcf85063_probe(device_t dev)
  244 {
  245 
  246         if (!ofw_bus_status_okay(dev))
  247                 return (ENXIO);
  248 
  249         if (!ofw_bus_search_compatible(dev, pcf85063_compat_data)->ocd_data)
  250                 return (ENXIO);
  251 
  252         device_set_desc(dev, "NXP pcf85063 Real Time Clock");
  253 
  254         return (BUS_PROBE_GENERIC);
  255 }
  256 
  257 static device_method_t pcf85063_methods [] = {
  258         DEVMETHOD(device_attach, pcf85063_attach),
  259         DEVMETHOD(device_detach, pcf85063_detach),
  260         DEVMETHOD(device_probe, pcf85063_probe),
  261 
  262         DEVMETHOD(clock_gettime, pcf85063_get_time),
  263         DEVMETHOD(clock_settime, pcf85063_set_time),
  264 
  265         DEVMETHOD_END
  266 };
  267 
  268 static driver_t pcf85063_driver = {
  269         "pcf85063",
  270         pcf85063_methods,
  271         0
  272 };
  273 
  274 DRIVER_MODULE(pcf85063, iicbus, pcf85063_driver, NULL, NULL);
  275 IICBUS_FDT_PNP_INFO(pcf85063_compat_data);

Cache object: 51f71a2a55d2e7077db1294189245dff


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