Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:05

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  * @file
0005  *
0006  * @ingroup shared_tod_i2c_rtc
0007  *
0008  * @brief This file provides the implementation of @ref shared_tod_i2c_rtc.
0009  */
0010 
0011 /*
0012  * Copyright (C) 2023-2024 embedded brains GmbH & Co. KG
0013  *
0014  * Redistribution and use in source and binary forms, with or without
0015  * modification, are permitted provided that the following conditions
0016  * are met:
0017  * 1. Redistributions of source code must retain the above copyright
0018  *    notice, this list of conditions and the following disclaimer.
0019  * 2. Redistributions in binary form must reproduce the above copyright
0020  *    notice, this list of conditions and the following disclaimer in the
0021  *    documentation and/or other materials provided with the distribution.
0022  *
0023  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0024  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0025  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0026  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0027  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0028  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0029  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0030  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0031  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0032  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0033  * POSSIBILITY OF SUCH DAMAGE.
0034  */
0035 
0036 #include <dev/i2c/i2c.h>
0037 #include <libchip/i2c-rtc.h>
0038 #include <rtems/score/sysstate.h>
0039 #include <rtems/score/todimpl.h>
0040 
0041 #include <fcntl.h>
0042 #include <string.h>
0043 #include <sys/stat.h>
0044 #include <unistd.h>
0045 
0046 #define RTCSEC_SECBCD_SHIFT  0u
0047 #define RTCSEC_SECBCD_MASK   (0x7fu << RTCSEC_SECBCD_SHIFT)
0048 #define RTCSEC_SECBCD(x)     (((x) << RTCSEC_SECBCD_SHIFT) & RTCSEC_SECBCD_MASK)
0049 #define RTCSEC_SECBCD_GET(x) (((x) & RTCSEC_SECBCD_MASK) >> RTCSEC_SECBCD_SHIFT)
0050 
0051 #define RTCMIN_MINBCD_SHIFT  0u
0052 #define RTCMIN_MINBCD_MASK   (0x7fu << RTCMIN_MINBCD_SHIFT)
0053 #define RTCMIN_MINBCD(x)     (((x) << RTCMIN_MINBCD_SHIFT) & RTCMIN_MINBCD_MASK)
0054 #define RTCMIN_MINBCD_GET(x) (((x) & RTCMIN_MINBCD_MASK) >> RTCMIN_MINBCD_SHIFT)
0055 
0056 #define RTCHOUR_HRBCD12_SHIFT  0u
0057 #define RTCHOUR_HRBCD12_MASK   (0x1fu << RTCHOUR_HRBCD12_SHIFT)
0058 #define RTCHOUR_HRBCD12(x)     (((x) << RTCHOUR_HRBCD12_SHIFT) & RTCHOUR_HRBCD12_MASK)
0059 #define RTCHOUR_HRBCD12_GET(x) (((x) & RTCHOUR_HRBCD12_MASK) >> RTCHOUR_HRBCD12_SHIFT)
0060 
0061 #define RTCHOUR_HRBCD24_SHIFT  0u
0062 #define RTCHOUR_HRBCD24_MASK   (0x3fu << RTCHOUR_HRBCD24_SHIFT)
0063 #define RTCHOUR_HRBCD24(x)     (((x) << RTCHOUR_HRBCD24_SHIFT) & RTCHOUR_HRBCD24_MASK)
0064 #define RTCHOUR_HRBCD24_GET(x) (((x) & RTCHOUR_HRBCD24_MASK) >> RTCHOUR_HRBCD24_SHIFT)
0065 
0066 #define RTCHOUR_AMPM           (0x01u << 5)
0067 #define RTCHOUR_1224           (0x01u << 6)
0068 
0069 #define RTCWKDAY_WKDAY_SHIFT   0u
0070 #define RTCWKDAY_WKDAY_MASK    (0x7u << RTCWKDAY_WKDAY_SHIFT)
0071 #define RTCWKDAY_WKDAY(x)      (((x) << RTCWKDAY_WKDAY_SHIFT) & RTCWKDAY_WKDAY_MASK)
0072 #define RTCWKDAY_WKDAY_GET(x)  (((x) & RTCWKDAY_WKDAY_MASK) >> RTCWKDAY_WKDAY_SHIFT)
0073 
0074 #define RTCDATE_DATEBCD_SHIFT  0u
0075 #define RTCDATE_DATEBCD_MASK   (0x3fu << RTCDATE_DATEBCD_SHIFT)
0076 #define RTCDATE_DATEBCD(x)     (((x) << RTCDATE_DATEBCD_SHIFT) & RTCDATE_DATEBCD_MASK)
0077 #define RTCDATE_DATEBCD_GET(x) (((x) & RTCDATE_DATEBCD_MASK) >> RTCDATE_DATEBCD_SHIFT)
0078 
0079 #define RTCMTH_MTHBCD_SHIFT    0u
0080 #define RTCMTH_MTHBCD_MASK     (0x1fu << RTCMTH_MTHBCD_SHIFT)
0081 #define RTCMTH_MTHBCD(x)       (((x) << RTCMTH_MTHBCD_SHIFT) & RTCMTH_MTHBCD_MASK)
0082 #define RTCMTH_MTHBCD_GET(x)   (((x) & RTCMTH_MTHBCD_MASK) >> RTCMTH_MTHBCD_SHIFT)
0083 
0084 #define RTCYEAR_YRBCD_SHIFT    0u
0085 #define RTCYEAR_YRBCD_MASK     (0xffu << RTCYEAR_YRBCD_SHIFT)
0086 #define RTCYEAR_YRBCD(x)       (((x) << RTCYEAR_YRBCD_SHIFT) & RTCYEAR_YRBCD_MASK)
0087 #define RTCYEAR_YRBCD_GET(x)   (((x) & RTCYEAR_YRBCD_MASK) >> RTCYEAR_YRBCD_SHIFT)
0088 
0089 #define NR_RTC_REGISTERS 0x07u
0090 
0091 static inline uint8_t bcd_to_bin(uint8_t bcd)
0092 {
0093   uint8_t bin;
0094   bin = bcd & 0x0f;
0095   bin += ((bcd >> 4) & 0x0f) * 10;
0096   return bin;
0097 }
0098 
0099 static inline uint8_t bin_to_bcd(uint8_t bin)
0100 {
0101   uint8_t bcd;
0102   bcd = bin % 10;
0103   bcd |= (bin / 10) << 4;
0104   return bcd;
0105 }
0106 
0107 static struct i2c_rtc_base *i2c_rtc_get_context(int minor)
0108 {
0109   return (struct i2c_rtc_base *) RTC_Table[minor].pDeviceParams;
0110 }
0111 
0112 int i2c_rtc_read(
0113     struct i2c_rtc_base *ctx,
0114     uint8_t addr,
0115     uint8_t *buf,
0116     size_t len
0117 )
0118 {
0119   int fd;
0120   int rv;
0121   struct i2c_msg msgs[] = {{
0122     .addr = ctx->i2c_addr,
0123     .flags = 0,
0124     .buf = &addr,
0125     .len = 1,
0126   }, {
0127     .addr = ctx->i2c_addr,
0128     .flags = I2C_M_RD,
0129     .buf = buf,
0130     .len = len,
0131   }};
0132   struct i2c_rdwr_ioctl_data payload = {
0133     .msgs = msgs,
0134     .nmsgs = sizeof(msgs)/sizeof(msgs[0]),
0135   };
0136 
0137   fd = open(ctx->i2c_bus_path, O_RDWR);
0138   if (fd < 0) {
0139     return fd;
0140   }
0141 
0142   rv = ioctl(fd, I2C_RDWR, &payload);
0143 
0144   close(fd);
0145 
0146   return rv;
0147 }
0148 
0149 int i2c_rtc_write(
0150     struct i2c_rtc_base *ctx,
0151     uint8_t addr,
0152     const uint8_t *buf,
0153     size_t len
0154 )
0155 {
0156   int fd;
0157   int rv;
0158   uint8_t writebuf[len + 1];
0159   struct i2c_msg msgs[] = {{
0160     .addr = ctx->i2c_addr,
0161     .flags = 0,
0162     .buf = writebuf,
0163     .len = len + 1,
0164   }};
0165   struct i2c_rdwr_ioctl_data payload = {
0166     .msgs = msgs,
0167     .nmsgs = sizeof(msgs)/sizeof(msgs[0]),
0168   };
0169 
0170   writebuf[0] = addr;
0171   memcpy(&writebuf[1], buf, len);
0172 
0173   fd = open(ctx->i2c_bus_path, O_RDWR);
0174   if (fd < 0) {
0175     return fd;
0176   }
0177 
0178   rv = ioctl(fd, I2C_RDWR, &payload);
0179 
0180   close(fd);
0181 
0182   return rv;
0183 }
0184 
0185 static int i2c_rtc_initialize_once(struct i2c_rtc_base *ctx)
0186 {
0187   int rv;
0188 
0189   if (ctx->initialized || ctx->hw_init == NULL) {
0190     return 0;
0191   }
0192 
0193   rv = ctx->hw_init(ctx);
0194 
0195   if (rv == 0) {
0196     ctx->initialized = true;
0197   }
0198 
0199   return rv;
0200 }
0201 
0202 static int i2c_rtc_get_time(int minor, rtems_time_of_day *time)
0203 {
0204   int rv = 0;
0205   uint8_t buf[NR_RTC_REGISTERS];
0206   struct i2c_rtc_base *ctx = i2c_rtc_get_context(minor);
0207 
0208   if (!_System_state_Is_up(_System_state_Get())) {
0209       return -1;
0210   }
0211 
0212   rtems_mutex_lock(&ctx->mutex);
0213 
0214   rv = i2c_rtc_initialize_once(ctx);
0215 
0216   if (rv == 0) {
0217     rv = i2c_rtc_read(ctx, ctx->clock_offset, buf, sizeof(buf));
0218   }
0219 
0220   if (rv == 0) {
0221     unsigned year = bcd_to_bin(RTCYEAR_YRBCD_GET(buf[ctx->order.year])) +
0222                     (TOD_BASE_YEAR / 100 * 100);
0223     if (year < TOD_BASE_YEAR) {
0224       year += 100;
0225     }
0226     time->year = year;
0227     time->month = bcd_to_bin(RTCMTH_MTHBCD_GET(buf[ctx->order.month]));
0228     time->day = bcd_to_bin(RTCDATE_DATEBCD_GET(buf[ctx->order.day]));
0229     time->hour = bcd_to_bin(RTCHOUR_HRBCD24_GET(buf[ctx->order.hour]));
0230     time->minute = bcd_to_bin(RTCMIN_MINBCD_GET(buf[ctx->order.min]));
0231     time->second = bcd_to_bin(RTCSEC_SECBCD_GET(buf[ctx->order.sec]));
0232     time->ticks  = 0;
0233   }
0234 
0235   rtems_mutex_unlock(&ctx->mutex);
0236 
0237   return rv;
0238 }
0239 
0240 static int i2c_rtc_set_time(int minor, const rtems_time_of_day *time)
0241 {
0242   int rv = 0;
0243   uint8_t buf[NR_RTC_REGISTERS];
0244   struct i2c_rtc_base *ctx = i2c_rtc_get_context(minor);
0245 
0246   if (!_System_state_Is_up(_System_state_Get())) {
0247       return -1;
0248   }
0249 
0250   rtems_mutex_lock(&ctx->mutex);
0251 
0252   rv = i2c_rtc_initialize_once(ctx);
0253 
0254   if (rv == 0) {
0255     rv = i2c_rtc_read(ctx, ctx->clock_offset, buf, sizeof(buf));
0256   }
0257 
0258   if (rv == 0) {
0259     /* Make sure weekday is not 0 (out of range). Otherwise it's not used. */
0260     if (RTCWKDAY_WKDAY_GET(buf[ctx->order.wkday]) < 1) {
0261       buf[ctx->order.wkday] &= ~RTCWKDAY_WKDAY_MASK;
0262       buf[ctx->order.wkday] |= RTCWKDAY_WKDAY(1);
0263     }
0264 
0265     buf[ctx->order.year] &= ~RTCYEAR_YRBCD_MASK;
0266     buf[ctx->order.year] |= RTCYEAR_YRBCD(bin_to_bcd(time->year % 100));
0267 
0268     buf[ctx->order.month] &= ~RTCMTH_MTHBCD_MASK;
0269     buf[ctx->order.month] |= RTCMTH_MTHBCD(bin_to_bcd(time->month));
0270 
0271     buf[ctx->order.day] &= ~RTCDATE_DATEBCD_MASK;
0272     buf[ctx->order.day] |= RTCDATE_DATEBCD(bin_to_bcd(time->day));
0273 
0274     buf[ctx->order.hour] &= ~(RTCHOUR_HRBCD24_MASK | RTCHOUR_1224);
0275     buf[ctx->order.hour] |= RTCHOUR_HRBCD24(bin_to_bcd(time->hour));
0276 
0277     buf[ctx->order.min] &= ~RTCMIN_MINBCD_MASK;
0278     buf[ctx->order.min] |= RTCMIN_MINBCD(bin_to_bcd(time->minute));
0279 
0280     buf[ctx->order.sec] &= ~RTCSEC_SECBCD_MASK;
0281     buf[ctx->order.sec] |= RTCSEC_SECBCD(bin_to_bcd(time->second));
0282 
0283     rv = i2c_rtc_write(ctx, ctx->clock_offset, buf, sizeof(buf));
0284   }
0285 
0286   rtems_mutex_unlock(&ctx->mutex);
0287 
0288   return rv;
0289 }
0290 
0291 static void i2c_rtc_init(int minor)
0292 {
0293   (void) minor;
0294 }
0295 
0296 bool i2c_rtc_probe(int minor)
0297 {
0298   return true;
0299 }
0300 
0301 const rtc_fns i2c_rtc_fns = {
0302   .deviceInitialize = i2c_rtc_init,
0303   .deviceGetTime =    i2c_rtc_get_time,
0304   .deviceSetTime =    i2c_rtc_set_time,
0305 };