123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527 |
- #include <linux/kernel.h>
- #include <linux/errno.h>
- #include "ktiva.h"
- #include "ksync.h"
- #include "kfile.h"
- #include "krtc.h"
- /////////////////////////////////////////////////////////////////////////////
- #define _BCD2BIN(b) ((int)(((b) >> 4) * 10 + ((b) & 0x0F)))
- #define _BIN2BCD(b) ((unsigned char)(((b) / 10) << 4 | ((b) % 10)))
- #define _IS_12_HOUR_FORMAT_FLAG (unsigned char)(1 << 6)
- #define _IS_PM_FLAG (unsigned char)(1 << 5)
- #define _IS_12_HOUR_FORMAT(reg) !!((reg) & _IS_12_HOUR_FORMAT_FLAG)
- #define _IS_PM(reg) !!((reg) & _IS_PM_FLAG)
- #define _RTC_STOP_YIELD_DELAY 10
- #define _RTC_STOP_MAX_YIELD_DELAY 1000
- /////////////////////////////////////////////////////////////////////////////
- /////////////////////////////////////////////////////////////////////////////
- static RTCTypes g_rtcType = RTCT_Unknown;
- /////////////////////////////////////////////////////////////////////////////
- /////////////////////////////////////////////////////////////////////////////
- // https://ww1.microchip.com/downloads/en/devicedoc/20005010f.pdf
- // MCP7940
- #define _I2C_SLV_ADDR_MCP7940 0x6f
- #define _IS_MCP7940_OSC_RUN(reg) (!!(reg & 0x20)) // OSCRUN: 1 = Oscillator is enabled and running
- // 0 = Oscillator has stopped or has been disabled
- #define _MCP7940_SRAM_ADDRESS 0x20
- #define _MCP7940_SRAM_SIZE 64
- #define _MCP7940_TEST_I2C_BUFFER_SIZE 28
- /////////////////////////////////////////////////////////////////////////////
- static int _MCP7940_get_date_time(struct file *pf, struct tm *ptm)
- {
- int ret;
- unsigned char rtc[7];
- if((ret = TivaCmdGetI2C(pf, _I2C_SLV_ADDR_MCP7940, 0, sizeof(rtc), rtc, sizeof(rtc))) > 0)
- {
- if(_IS_MCP7940_OSC_RUN(rtc[3]))
- {
- memset(ptm, 0, sizeof(struct tm));
- ptm->tm_year = _BCD2BIN(rtc[6]) + 100;
- ptm->tm_mon = _BCD2BIN(rtc[5] & 0x1F) - 1;
- ptm->tm_mday = _BCD2BIN(rtc[4] & 0x3F);
- if(_IS_12_HOUR_FORMAT(rtc[2])) // 12h format
- ptm->tm_hour = _BCD2BIN(rtc[2] & 0x1F) + (_IS_PM(rtc[2]) ? 12 : 0);
- else // 24h format (default)
- ptm->tm_hour = _BCD2BIN(rtc[2] & 0x3F);
- ptm->tm_min = _BCD2BIN(rtc[1] & 0x7F);
- ptm->tm_sec = _BCD2BIN(rtc[0] & 0x7F);
- ret = 0;
- }
- else
- {
- KALERT("%s: Oscillator not running!\n", __FUNCTION__);
- ret = -EIO;
- }
- }
- else
- {
- KALERT("%s: TivaCmdGetI2C failed (%d)\n", __FUNCTION__, ret);
- ret = -EIO;
- }
-
- return ret;
- }
- /////////////////////////////////////////////////////////////////////////////
- static int _MCP7940_set_date_time(struct file *pf, const struct tm *ptm)
- {
- bool bOscRun;
- unsigned int nMsSleep = 0;
- int ret, y, m;
- unsigned char rtc[9], reg = 0;
- /////////////////////////////////////////////////////////////////////////
- // Initiate a stop of the Oscillator by clearing register 0.
- if((ret = TivaCmdSetI2C(pf, _I2C_SLV_ADDR_MCP7940, 0, ®, 1)))
- {
- KALERT("%s: TivaCmdSetI2C failed (%d)\n", __FUNCTION__, ret);
- return -EIO;
- }
- /////////////////////////////////////////////////////////////////////////
- // Meanwhile prepare the clock data that will be written to the RTC registers
- KALERT("Setting RTC to %04ld-%02d-%02dT%02d:%02d:%02d UTC\n", ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
- y = ptm->tm_year - 100;
- m = ptm->tm_mon + 1;
-
- rtc[0] = _BIN2BCD(ptm->tm_sec) | 0x80; // Write seconds and start Oscillator. This register will be written seperately at last!
- rtc[1] = _BIN2BCD(ptm->tm_min);
- rtc[2] = _BIN2BCD(ptm->tm_hour); // uses 24h format
- rtc[3] = (ptm->tm_wday + 1) | 0x08; // MCP7940 day of week is in range 1-7, struct tm is in range 0-6 (start with Sunday).
- // Setting VBATEN (0x08) enables external battery backup supply.
- rtc[4] = _BIN2BCD(ptm->tm_mday);
- rtc[5] = _BIN2BCD(m);
- rtc[6] = _BIN2BCD(y);
- rtc[7] = 0x80; // Set OUT bit in control register.
- rtc[8] = 0; // Clear OSCTRIM explicitly. Disables Digital Trimming.
- /////////////////////////////////////////////////////////////////////////
- // Wait for the oscillator to stop. This is indicated by OSCRUN = 0 in register 3.
- do
- {
- if((ret = TivaCmdGetI2C(pf, _I2C_SLV_ADDR_MCP7940, 3, 1, ®, 1)) <= 0) // read register 3
- {
- KALERT("%s: TivaCmdGetI2C failed (%d)\n", __FUNCTION__, ret);
- return -EIO;
- }
-
- if((bOscRun = _IS_MCP7940_OSC_RUN(reg))) // if oscillator has not stopped yet
- {
- ksync_sleep_ms(_RTC_STOP_YIELD_DELAY); // yield to another thread for _RTC_STOP_YIELD_DELAY ms
- nMsSleep += _RTC_STOP_YIELD_DELAY; // and sum up the delay to the total yield time.
-
- if(nMsSleep >= _RTC_STOP_MAX_YIELD_DELAY) // if the total yield time has reached _RTC_STOP_MAX_YIELD_DELAY, return a timeout error.
- {
- KALERT("%s: Timeout while trying to stop RTC Oscillator!\n", __FUNCTION__);
- return -ETIMEDOUT;
- }
- }
- }
- while(bOscRun);
- /////////////////////////////////////////////////////////////////////////
- // write registers 1-8 (Minutes to Year and control/status and trim registers)
- if((ret = TivaCmdSetI2C(pf, _I2C_SLV_ADDR_MCP7940, 1, &rtc[1], sizeof(rtc) - 1)))
- {
- KALERT("%s: TivaCmdSetI2C failed (%d)\n", __FUNCTION__, ret);
- return -EIO;
- }
- /////////////////////////////////////////////////////////////////////////
- // write register 0 (Seconds and Start oscillator)
- if((ret = TivaCmdSetI2C(pf, _I2C_SLV_ADDR_MCP7940, 0, rtc, 1)))
- {
- KALERT("%s: TivaCmdSetI2C failed (%d)\n", __FUNCTION__, ret);
- return -EIO;
- }
- return 0; // RTC should now be set and running
- }
- /////////////////////////////////////////////////////////////////////////////
- static int _MCP7940_test_i2c(struct file *pf)
- {
- size_t i;
- int ret;
- unsigned char write[_MCP7940_TEST_I2C_BUFFER_SIZE], read[_MCP7940_TEST_I2C_BUFFER_SIZE];
- for(i = 0; i < sizeof(write); ++i)
- {
- write[i] = i;
- read[i] = 0xFF;
- }
- if((ret = TivaCmdSetI2C(pf, _I2C_SLV_ADDR_MCP7940, _MCP7940_SRAM_ADDRESS, write, sizeof(write))))
- {
- KALERT("%s: TivaCmdSetI2C failed (%d)\n", __FUNCTION__, ret);
- return -EIO;
- }
- if(!(ret = TivaCmdGetI2C(pf, _I2C_SLV_ADDR_MCP7940, _MCP7940_SRAM_ADDRESS, sizeof(read), read, sizeof(read))) <= 0)
- {
- KALERT("%s: TivaCmdGetI2C failed (%d)\n", __FUNCTION__, ret);
- return -EIO;
- }
- if((ret = memcmp(write, read, sizeof(read))))
- {
- KALERT("%s: R/W Data mismatch!\n", __FUNCTION__);
- ret = -EPROTO;
- }
- return ret;
- }
- /////////////////////////////////////////////////////////////////////////////
- /////////////////////////////////////////////////////////////////////////////
- // https://datasheets.maximintegrated.com/en/ds/DS3231-DS3231S.pdf
- // DS3231
- #define _I2C_SLV_ADDR_DS3231 0x68
- #define _IS_DS3231_OSC_RUN(reg) (!(reg & 0x80)) // OSF (Oscillator Stop Flag):
- // 1 indicates that the oscillator either is stopped or was stopped for some period
- /////////////////////////////////////////////////////////////////////////////
- static int _DS3231_get_date_time(struct file *pf, struct tm *ptm)
- {
- int ret;
- unsigned char rtc[16];
- if((ret = TivaCmdGetI2C(pf, _I2C_SLV_ADDR_DS3231, 0, sizeof(rtc), rtc, sizeof(rtc))) > 0)
- {
- if(_IS_DS3231_OSC_RUN(rtc[15]))
- {
- memset(ptm, 0, sizeof(struct tm));
- ptm->tm_year = _BCD2BIN(rtc[6]) + 100;
- ptm->tm_mon = _BCD2BIN(rtc[5] & 0x1F) - 1;
- ptm->tm_mday = _BCD2BIN(rtc[4] & 0x3F);
- if(_IS_12_HOUR_FORMAT(rtc[2])) // 12h format
- ptm->tm_hour = _BCD2BIN(rtc[2] & 0x1F) + (_IS_PM(rtc[2]) ? 12 : 0);
- else // 24h format (default)
- ptm->tm_hour = _BCD2BIN(rtc[2] & 0x3F);
- ptm->tm_min = _BCD2BIN(rtc[1] & 0x7F);
- ptm->tm_sec = _BCD2BIN(rtc[0] & 0x7F);
- ret = 0;
- }
- else
- {
- KALERT("%s: Oscillator not running!\n", __FUNCTION__);
- ret = -EIO;
- }
- }
- else
- {
- KALERT("%s: TivaCmdGetI2C failed (%d)\n", __FUNCTION__, ret);
- ret = -EIO;
- }
-
- return ret;
- }
- /////////////////////////////////////////////////////////////////////////////
- static int _DS3231_set_date_time(struct file *pf, const struct tm *ptm)
- {
- int ret, y, m;
- unsigned char rtc[7], reg = 0;
- /////////////////////////////////////////////////////////////////////////
- // Prepare the clock data to be written to the RTC registers
- KALERT("Setting RTC to %04ld-%02d-%02dT%02d:%02d:%02d UTC\n", ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
- y = ptm->tm_year - 100;
- m = ptm->tm_mon + 1;
-
- rtc[0] = _BIN2BCD(ptm->tm_sec); // seconds
- rtc[1] = _BIN2BCD(ptm->tm_min); // minutes
- rtc[2] = _BIN2BCD(ptm->tm_hour); // hour, uses 24h format
- rtc[3] = (ptm->tm_wday + 1); // DS3231 day of week is in range 1-7, struct tm is in range 0-6 (start with Sunday).
- rtc[4] = _BIN2BCD(ptm->tm_mday); // day
- rtc[5] = _BIN2BCD(m); // month
- rtc[6] = _BIN2BCD(y); // year
- /////////////////////////////////////////////////////////////////////////
- // write registers 0-6 (Seconds to Year)
- // The countdown chain is reset whenever the seconds register is written (0h). Once the countdown chain is
- // reset, to avoid rollover issues the remaining time and date registers must be written within 1 second.
- if((ret = TivaCmdSetI2C(pf, _I2C_SLV_ADDR_DS3231, 0, rtc, sizeof(rtc))))
- {
- KALERT("%s: TivaCmdSetI2C without failed (%d)\n", __FUNCTION__, ret);
- return -EIO;
- }
- /////////////////////////////////////////////////////////////////////////
- // read/write register 15 (Status Register), to clear the OSF flag.
- if((ret = TivaCmdGetI2C(pf, _I2C_SLV_ADDR_DS3231, 15, 1, ®, 1)) > 0) // read register 15
- {
- reg &= 0x7F; // clear OSF bit.
- if((ret = TivaCmdSetI2C(pf, _I2C_SLV_ADDR_DS3231, 15, ®, 1))) // write register 15
- {
- KALERT("%s: TivaCmdSetI2C failed (%d)\n", __FUNCTION__, ret);
- return -EIO;
- }
- }
- else
- {
- KALERT("%s: TivaCmdGetI2C failed (%d)\n", __FUNCTION__, ret);
- return -EIO;
- }
- return 0; // RTC should now be set and running
- }
- /////////////////////////////////////////////////////////////////////////////
- /////////////////////////////////////////////////////////////////////////////
- int krtc_init(void)
- {
- int i, ret = 0;
- struct file *pfSpiDev = NULL;
- g_rtcType = RTCT_Unknown;
- if((pfSpiDev = kf_open_locked(_SPI_DEVICE, O_RDWR, 0)))
- {
- if(KSpiInit(pfSpiDev))
- {
- unsigned char rtc[7];
- if((ret = TivaCmdGetI2C(pfSpiDev, _I2C_SLV_ADDR_MCP7940, 0, sizeof(rtc), rtc, sizeof(rtc))) > 0) // MCP7940
- {
- for(i = 0; i < 7; ++i)
- {
- if(rtc[i] != 0xFF)
- {
- g_rtcType = RTCT_MCP7940;
- break;
- }
- }
-
- if(g_rtcType == RTCT_Unknown)
- {
- if((ret = TivaCmdGetI2C(pfSpiDev, _I2C_SLV_ADDR_DS3231, 0, sizeof(rtc), rtc, sizeof(rtc))) > 0) // DS3231
- {
- for(i = 0; i < 7; ++i)
- {
- if(rtc[i] != 0xFF)
- {
- g_rtcType = RTCT_DS3231;
- break;
- }
- }
- }
- else
- {
- KALERT("%s: TivaCmdGetI2C failed (%d)\n", __FUNCTION__, ret);
- ret = -EIO;
- }
- }
- }
- else
- {
- KALERT("%s: TivaCmdGetI2C failed (%d)\n", __FUNCTION__, ret);
- ret = -EIO;
- }
- }
- else
- {
- KALERT("%s: KSpiInit failed\n", __FUNCTION__);
- ret = -EIO;
- }
- kf_close(pfSpiDev);
- }
- else
- {
- KALERT("%s: kf_open_locked failed\n", __FUNCTION__);
- ret = -EIO;
- }
- if(g_rtcType == RTCT_Unknown)
- {
- KALERT("%s: Unable to determine RTC-Type!\n", __FUNCTION__);
- ret = -ENODEV;
- }
- return ret;
- }
- /////////////////////////////////////////////////////////////////////////////
- RTCTypes krtc_get_type(void)
- {
- return g_rtcType;
- }
- /////////////////////////////////////////////////////////////////////////////
- int krtc_get_date_time(struct tm *ptm)
- {
- int ret;
- struct file *pfSpiDev = NULL;
-
- if(!KRTC_IS_VALID_RTC_TYPE(g_rtcType))
- {
- KALERT("%s: Invalid RTC-Type!\n", __FUNCTION__);
- return -ENODEV;
- }
- if((pfSpiDev = kf_open_locked(_SPI_DEVICE, O_RDWR, 0)))
- {
- if(KSpiInit(pfSpiDev))
- {
- switch(g_rtcType)
- {
- case RTCT_MCP7940:
- if((ret = _MCP7940_get_date_time(pfSpiDev, ptm)))
- KALERT("%s: _MCP7940_get_date_time failed!\n", __FUNCTION__);
- break;
- case RTCT_DS3231:
- if((ret = _DS3231_get_date_time(pfSpiDev, ptm)))
- KALERT("%s: _DS3231_get_date_time failed!\n", __FUNCTION__);
- break;
- default:
- break;
- }
- }
- else
- {
- KALERT("%s: KSpiInit failed\n", __FUNCTION__);
- ret = -EIO;
- }
- kf_close(pfSpiDev);
- }
- else
- {
- KALERT("%s: kf_open_locked failed\n", __FUNCTION__);
- ret = -ENODEV;
- }
- return ret;
- }
- /////////////////////////////////////////////////////////////////////////////
- int krtc_set_date_time(const struct tm *ptm)
- {
- int ret;
- struct file *pfSpiDev = NULL;
- // unsigned long jStart = jiffies, jDiff;
-
- if(!KRTC_IS_VALID_RTC_TYPE(g_rtcType))
- {
- KALERT("%s: Invalid RTC-Type!\n", __FUNCTION__);
- return -ENODEV;
- }
- if((pfSpiDev = kf_open_locked(_SPI_DEVICE, O_RDWR, 0)))
- {
- if(KSpiInit(pfSpiDev))
- {
- switch(g_rtcType)
- {
- case RTCT_MCP7940:
- // jDiff = jiffies - jStart;
- // KALERT("%s: prep _MCP7940_set_date_time took %u us.\n", __FUNCTION__, jiffies_to_usecs(jDiff));
- // jStart = jiffies;
- if((ret = _MCP7940_set_date_time(pfSpiDev, ptm)))
- KALERT("%s: _MCP7940_set_date_time failed!\n", __FUNCTION__);
- // jDiff = jiffies - jStart;
- // KALERT("%s: call _MCP7940_set_date_time took %u us.\n", __FUNCTION__, jiffies_to_usecs(jDiff));
- break;
- case RTCT_DS3231:
- if((ret = _DS3231_set_date_time(pfSpiDev, ptm)))
- KALERT("%s: _DS3231_set_date_time failed!\n", __FUNCTION__);
- break;
- default:
- break;
- }
- }
- else
- {
- KALERT("%s: KSpiInit failed\n", __FUNCTION__);
- ret = -EIO;
- }
- kf_close(pfSpiDev);
- }
- else
- {
- KALERT("%s: kf_open_locked failed\n", __FUNCTION__);
- ret = -ENODEV;
- }
- return ret;
- }
- /////////////////////////////////////////////////////////////////////////////
- int krtc_test_i2c(void)
- {
- int ret;
- struct file *pfSpiDev = NULL;
-
- if(g_rtcType != RTCT_MCP7940)
- {
- if(g_rtcType == RTCT_DS3231)
- {
- KALERT("%s: I2C-Test not implemented in DS3231!\n", __FUNCTION__);
- return -ENOTSUPP;
- }
- else
- {
- KALERT("%s: Invalid RTC-Type!\n", __FUNCTION__);
- return -ENODEV;
- }
- }
- if((pfSpiDev = kf_open_locked(_SPI_DEVICE, O_RDWR, 0)))
- {
- if(KSpiInit(pfSpiDev))
- {
- if((ret = _MCP7940_test_i2c(pfSpiDev)))
- KALERT("%s: _MCP7940_test_i2c failed!\n", __FUNCTION__);
- }
- else
- {
- KALERT("%s: KSpiInit failed\n", __FUNCTION__);
- ret = -EIO;
- }
- kf_close(pfSpiDev);
- }
- else
- {
- KALERT("%s: kf_open_locked failed\n", __FUNCTION__);
- ret = -ENODEV;
- }
- return ret;
- }
|