A board support package for the LPC4088 Display Module.
Dependencies: DM_HttpServer DM_USBHost
Dependents: lpc4088_displaymodule_emwin lpc4088_displaymodule_demo_sphere sampleGUI sampleEmptyGUI ... more
Fork of DMSupport by
Revision 42:bbfe299d4a0c, committed 2019-11-04
- Comitter:
- embeddedartists
- Date:
- Mon Nov 04 14:32:50 2019 +0000
- Parent:
- 41:e06e764ff4fd
- Commit message:
- More updates related to mbed OS 5
Changed in this revision
diff -r e06e764ff4fd -r bbfe299d4a0c Bios/BiosLoader.cpp --- a/Bios/BiosLoader.cpp Wed Oct 23 06:59:29 2019 +0000 +++ b/Bios/BiosLoader.cpp Mon Nov 04 14:32:50 2019 +0000 @@ -222,12 +222,7 @@ DMBoard::BoardError err = DMBoard::Ok; if (!_initialized) { do { - - // With mbed OS 5 the MPU has been enabled and by default execution from - // RAM is not allowed. Calling this function to allow execution since - // the BIOS code will be executed from RAM. - mbed_mpu_manager_lock_ram_execution(); - + // Get the display bios from the DMBoard. DMBoard will have verified it // and will keep it in RAM so there is no need to copy it. uint8_t* p = NULL;
diff -r e06e764ff4fd -r bbfe299d4a0c DMBoard.cpp --- a/DMBoard.cpp Wed Oct 23 06:59:29 2019 +0000 +++ b/DMBoard.cpp Mon Nov 04 14:32:50 2019 +0000 @@ -26,7 +26,9 @@ #if defined(DM_BOARD_ENABLE_MEASSURING_PINS) #include "meas.h" -#endif +#endif + +#include "dm_rtc.h" /****************************************************************************** * Configuration Compatibility Control @@ -74,7 +76,8 @@ DMBoard::DMBoard() : _initialized(false), #if defined(DM_BOARD_USE_MCI_FS) - _mcifs("mci", P4_16), + _mcifs(P4_16), + _mciFatFs("mci"), #endif #if defined(DM_BOARD_USE_QSPI_FS) _qspifs("qspi"), @@ -101,6 +104,17 @@ BoardError err = Ok; if (!_initialized) { do { + + /* + * By default mbed OS 5 comes with the MPU enabled and prevents execution + * from RAM and writes to ROM. We need to be able to execute from EAM + * (e.g. the BIOS functionality) and also write to ROM area (we are e.g. + * using the EEPROM peripheral in the LPC4088 which has an address space + * within the ROM area). + */ + mbed_mpu_manager_lock_ram_execution(); + mbed_mpu_manager_lock_rom_write(); + // Turn off the buzzer _buzzer.period_ms(1); _buzzer = 0; @@ -129,6 +143,10 @@ freopen("/null", "w", stdout); #endif +#if defined(DM_BOARD_USE_MCI_FS) + _mciFatFs.mount(&_mcifs); +#endif + #if defined(DM_BOARD_USE_QSPI) || defined(DM_BOARD_USE_QSPI_FS) if (SPIFI::instance().init() != SPIFI::Ok) { err = SpifiError; @@ -156,6 +174,12 @@ break; } #endif + /* + * With mbed OS 5 the rtc driver in target/hal has been disabled + * Adding an external rtc driver instead. + */ + attach_rtc(&dm_read_rtc, &dm_write_rtc, &dm_init_rtc, &dm_isenabled_rtc); + _initialized = true; } while(0); }
diff -r e06e764ff4fd -r bbfe299d4a0c DMBoard.h --- a/DMBoard.h Wed Oct 23 06:59:29 2019 +0000 +++ b/DMBoard.h Mon Nov 04 14:32:50 2019 +0000 @@ -36,6 +36,18 @@ #include "Registry.h" #endif +#if defined(DM_BOARD_USE_USB_HOST) && defined(DM_BOARD_USE_ETHERNET) +/* + * It is not possible to use USB Host and Ethernet at the same time due to a + * conflict in memory areas. + * + * In OS 5 memory area AHBSRAM0 is used by the EMAC driver (lpc17_emac.cpp) and + * AHBSRAM1 is used by LWIP as heap (lwip_sys_arch.c). The USB host + * implementation is using AHBSRAM1 (USBHALHost.cpp). + */ +#error "Cannot have both USB Host and Ethernet enabled at the same time. See DMBoard.h" +#endif + /** * Example of using the Board class: @@ -181,6 +193,7 @@ #if defined(DM_BOARD_USE_MCI_FS) MCIFileSystem _mcifs; + FATFileSystem _mciFatFs; #endif #if defined(DM_BOARD_USE_QSPI_FS) QSPIFileSystem _qspifs;
diff -r e06e764ff4fd -r bbfe299d4a0c DM_HttpServer.lib --- a/DM_HttpServer.lib Wed Oct 23 06:59:29 2019 +0000 +++ b/DM_HttpServer.lib Mon Nov 04 14:32:50 2019 +0000 @@ -1,1 +1,1 @@ -http://developer.mbed.org/teams/Embedded-Artists/code/DM_HttpServer/#c1c8276af541 +http://developer.mbed.org/teams/Embedded-Artists/code/DM_HttpServer/#9dcff8cf906a
diff -r e06e764ff4fd -r bbfe299d4a0c DM_USBHost.lib --- a/DM_USBHost.lib Wed Oct 23 06:59:29 2019 +0000 +++ b/DM_USBHost.lib Mon Nov 04 14:32:50 2019 +0000 @@ -1,1 +1,1 @@ -http://developer.mbed.org/teams/Embedded-Artists/code/DM_USBHost/#f2d129436056 +http://developer.mbed.org/teams/Embedded-Artists/code/DM_USBHost/#819bbf04163b
diff -r e06e764ff4fd -r bbfe299d4a0c FileSystems/MCIFileSystem.cpp --- a/FileSystems/MCIFileSystem.cpp Wed Oct 23 06:59:29 2019 +0000 +++ b/FileSystems/MCIFileSystem.cpp Mon Nov 04 14:32:50 2019 +0000 @@ -581,12 +581,11 @@ * Public Functions *****************************************************************************/ -MCIFileSystem::MCIFileSystem(const char* name, PinName cd) : - FATFileSystem(name) +MCIFileSystem::MCIFileSystem(PinName cd) : + _init_ref_count(0), _is_initialized(false) { pUglyForIRQ = this; - _Stat = STA_NOINIT; memset(&_sdCardInfo, 0, sizeof(SDMMC_CARD_T)); _eventReceived = false; _eventSuccess = false; @@ -670,90 +669,131 @@ _eventDmaChannel = GPDMA::instance().acquireChannel(mydmairq); } -int MCIFileSystem::disk_initialize() { +int MCIFileSystem::init() { + uint32_t val = core_util_atomic_incr_u32(&_init_ref_count, 1); + + if (val != 1) { + return BD_ERROR_OK; + } - debug_if(MCI_DBG, "mcifs:disk_initialize(), _Stat = %#x\n", _Stat); + debug_if(MCI_DBG, "mcifs:disk_initialize()\n"); if (!cardInserted()) { - /* No card in the socket */ - _Stat = STA_NODISK | STA_NOINIT; + debug("No card detected\r\n"); + return BD_ERROR_DEVICE_ERROR; } - if (_Stat != STA_NOINIT) { - return _Stat; /* card is already enumerated */ - } - - //rtc_init(); - /* Initialize the Card Data Strucutre */ memset(&_sdCardInfo, 0, sizeof(SDMMC_CARD_T)); - /* Reset */ - _Stat = STA_NOINIT; - /* Enumerate the card once detected. Note this function may block for a little while. */ int ret = mci_Acquire(); if (ret != 1) { debug("Card Acquire failed... got %d, but expected 1\r\n", ret); - return 1;//Stat; + return BD_ERROR_DEVICE_ERROR; } - _Stat &= ~STA_NOINIT; - return _Stat; + _is_initialized = true; + return BD_ERROR_OK; } -int MCIFileSystem::disk_write(const uint8_t *buffer, uint64_t block_number, uint8_t count) { - debug_if(MCI_DBG, "mcifs:disk_write(%#x, %llu, %u), _Stat = %#x\n", (uint32_t)buffer, block_number, count, _Stat); - if (_Stat & STA_NOINIT) { - // not ready - return 1; - } - if (mci_WriteBlocks((void*)buffer, block_number, count) == SDC_RET_OK) { - return 0; +int MCIFileSystem::deinit() { + + if (!_is_initialized) { + return BD_ERROR_OK; + } + + uint32_t val = core_util_atomic_decr_u32(&_init_ref_count, 1); + + if (val) { + return BD_ERROR_OK; } - return 1; + _is_initialized = false; + return BD_ERROR_OK; } -int MCIFileSystem::disk_read(uint8_t *buffer, uint64_t block_number, uint8_t count) { - debug_if(MCI_DBG, "mcifs:disk_read(%#x, %llu, %u), _Stat = %#x\n", (uint32_t)buffer, block_number, count, _Stat); - if (_Stat & STA_NOINIT) { - // not ready - return _Stat; +int MCIFileSystem::program(const void *buffer, bd_addr_t addr, bd_size_t size) { + ReturnCode status; + + debug_if(MCI_DBG, "mcifs:disk_write(%#x, %llu, %llu)\n", (uint32_t)buffer, addr, size); + + if (!_is_initialized) { + return BD_ERROR_DEVICE_ERROR; } - if (mci_ReadBlocks(buffer, block_number, count) == SDC_RET_OK) { - return 0; + + /* need to convert from number of bytes to blocks */ + addr = addr / MMC_SECTOR_SIZE; + size = size / MMC_SECTOR_SIZE; + + status = mci_WriteBlocks((void*)buffer, addr, size); + if (status != SDC_RET_OK) { + return status; } - return 1; + return BD_ERROR_OK; } -int MCIFileSystem::disk_status() -{ - debug_if(MCI_DBG, "mcifs:disk_status(), _Stat = %#x\n", _Stat); - return _Stat; +int MCIFileSystem::read(void *buffer, bd_addr_t addr, bd_size_t size) { + ReturnCode status; + debug_if(MCI_DBG, "mcifs:disk_read(%#x, %llu, %llu)\n", (uint32_t)buffer, addr, size); + + if (!_is_initialized) { + return BD_ERROR_DEVICE_ERROR; + } + + /* need to convert from number of bytes to blocks */ + addr = addr / MMC_SECTOR_SIZE; + size = size / MMC_SECTOR_SIZE; + + status = mci_ReadBlocks(buffer, addr, size); + if (status != SDC_RET_OK) { + return status; + } + + return BD_ERROR_OK; } -int MCIFileSystem::disk_sync() +int MCIFileSystem::sync() { - debug_if(MCI_DBG, "mcifs:disk_sync(), _Stat = %#x\n", _Stat); - uint32_t end = us_ticker_read() + 50*1000; // 50ms - while (us_ticker_read() < end) - { - if (mci_GetCardStatus() & R1_READY_FOR_DATA) + debug_if(MCI_DBG, "mcifs:disk_sync()\n"); + + if (!_is_initialized) { + return BD_ERROR_OK; + } + + uint32_t end = us_ticker_read() + 50*1000; // 50ms + while (us_ticker_read() < end) { - // card is ready - return 0; + if (mci_GetCardStatus() & R1_READY_FOR_DATA) + { + // card is ready + return BD_ERROR_OK; + } } - } - // timeout while waiting for card to get ready - return 1; + + // timeout while waiting for card to get ready + return SDC_RET_TIMEOUT; } -uint64_t MCIFileSystem::disk_sectors() +bd_size_t MCIFileSystem::get_read_size() const +{ + return MMC_SECTOR_SIZE; +} + +bd_size_t MCIFileSystem::get_program_size() const { - debug_if(MCI_DBG, "mcifs:disk_sectors(), _Stat = %#x, returning %u\n", _Stat, _sdCardInfo.blocknr); - return _sdCardInfo.blocknr; + return MMC_SECTOR_SIZE; +} + +bd_size_t MCIFileSystem::size() const +{ + return _sdCardInfo.block_len * MMC_SECTOR_SIZE; +} + +const char *MCIFileSystem::get_type() const +{ + return "MCI"; } void MCIFileSystem::mci_MCIIRQHandler() @@ -1386,7 +1426,6 @@ /* compute block length based on CSD response */ _sdCardInfo.block_len = 1 << mci_GetBits(80, 83, _sdCardInfo.csd); - if ((_sdCardInfo.card_type & CARD_TYPE_HC) && (_sdCardInfo.card_type & CARD_TYPE_SD)) { /* See section 5.3.3 CSD Register (CSD Version 2.0) of SD2.0 spec an explanation for the calculation of these values */ CSize = mci_GetBits(48, 63, (uint32_t *) _sdCardInfo.csd) + 1; @@ -1778,6 +1817,7 @@ // If the driver is having problems reading the card, adding a delay here // might help. //wait(0.01); + ThisThread::sleep_for(10); } if (_eventReceived && _eventSuccess) {
diff -r e06e764ff4fd -r bbfe299d4a0c FileSystems/MCIFileSystem.h --- a/FileSystems/MCIFileSystem.h Wed Oct 23 06:59:29 2019 +0000 +++ b/FileSystems/MCIFileSystem.h Mon Nov 04 14:32:50 2019 +0000 @@ -48,24 +48,27 @@ * } * @endcode */ -class MCIFileSystem : public FATFileSystem { +class MCIFileSystem : public BlockDevice { public: /** Create the File System for accessing an SD/MMC Card using MCI * - * @param name The name used to access the virtual filesystem * @param cd The pin connected to the CardDetect line */ - MCIFileSystem(const char* name, PinName cd = P4_16); + MCIFileSystem(PinName cd = P4_16); virtual ~MCIFileSystem(); - virtual int disk_initialize(); - virtual int disk_status(); - virtual int disk_read(uint8_t * buffer, uint64_t block_number, uint8_t count); - virtual int disk_write(const uint8_t * buffer, uint64_t block_number, uint8_t count); - virtual int disk_sync(); - virtual uint64_t disk_sectors(); + virtual int init(); + virtual int deinit(); + + virtual int sync(); + virtual int read(void * buffer, bd_addr_t addr, bd_size_t size); + virtual int program(const void * buffer, bd_addr_t addr, bd_size_t size); + virtual bd_size_t get_read_size() const; + virtual bd_size_t get_program_size() const; + virtual bd_size_t size() const; + virtual const char *get_type() const; void mci_MCIIRQHandler(); void mci_DMAIRQHandler(); @@ -130,20 +133,20 @@ uint16_t rca; /*!< Relative address assigned to card */ uint32_t speed; /*!< Speed */ uint32_t block_len; /*!< Card sector size */ - uint32_t device_size; /*!< Device Size */ + bd_size_t device_size; /*!< Device Size */ uint32_t blocknr; /*!< Block Number */ uint32_t clk_rate; /*!< Clock rate */ } SDMMC_CARD_T; typedef enum { SDC_RET_OK = 0, - SDC_RET_CMD_FAILED = -1, - SDC_RET_BAD_PARAMETERS = -2, - SDC_RET_BUS_NOT_IDLE = -3, - SDC_RET_TIMEOUT = -4, - SDC_RET_ERR_STATE = -5, - SDC_RET_NOT_READY = -6, - SDC_RET_FAILED = -7, + SDC_RET_CMD_FAILED = -5001, + SDC_RET_BAD_PARAMETERS = -5002, + SDC_RET_BUS_NOT_IDLE = -5003, + SDC_RET_TIMEOUT = -5004, + SDC_RET_ERR_STATE = -5005, + SDC_RET_NOT_READY = -5006, + SDC_RET_FAILED = -5007, } ReturnCode; void initMCI(); @@ -192,8 +195,6 @@ ReturnCode _readBlocks(uint32_t card_type, uint32_t startBlock, uint32_t blockNum) const; ReturnCode _writeBlocks(uint32_t card_type, uint32_t startBlock, uint32_t blockNum) const; - - uint32_t _Stat; SDMMC_CARD_T _sdCardInfo; DigitalIn* _cardDetect; @@ -201,6 +202,9 @@ GPDMA::DMAChannels _eventDmaChannel; /*!< DMA Channel used for transfer data */ volatile bool _eventReceived; volatile bool _eventSuccess; + + uint32_t _init_ref_count; + bool _is_initialized; }; #endif
diff -r e06e764ff4fd -r bbfe299d4a0c FileSystems/QSPIFileSystem.cpp --- a/FileSystems/QSPIFileSystem.cpp Wed Oct 23 06:59:29 2019 +0000 +++ b/FileSystems/QSPIFileSystem.cpp Mon Nov 04 14:32:50 2019 +0000 @@ -1238,8 +1238,9 @@ // file is not in any sub folder so it is truly in the wanted dir nextTocIdx = possible + 1; strcpy(cur_entry.d_name, filename); - //return &cur_entry; - *ent = cur_entry; + + memcpy(ent->d_name, cur_entry.d_name, sizeof(cur_entry.d_name)); + return 1; } // this is a file in a subfolder and should not be reported, @@ -1262,13 +1263,14 @@ char* pSlash = strchr(cur_entry.d_name, '/'); // pSlash++; //with ++ the returned dir name is "mydir/" without ++ "mydir" is returned *pSlash = '\0'; - //return &cur_entry; - *ent = cur_entry; + + memcpy(ent->d_name, cur_entry.d_name, sizeof(cur_entry.d_name)); + return 1; } } } } - return NULL; + return 0; } void QSPIDirHandle::rewind() { @@ -1293,7 +1295,7 @@ // ReadWrite - dictates where to read from, writes ignore it but // sets the position to the end afterwards // -FileHandle *QSPIFileSystem::open(const char *filename, int flags) +int QSPIFileSystem::open(FileHandle **file, const char *filename, int flags) { fresult res = qspifs_init(); // if (res == FS_OK) { @@ -1308,6 +1310,7 @@ // res = FS_ERR_INVALID_PARAM; // } // } + if (res == FS_OK) { if (strlen(filename) > HEADER_FNAME_STRLEN) { // Filename is too long @@ -1331,11 +1334,14 @@ res = qspifs_saveTOC(); } if (res == FS_OK) { - return new QSPIFileHandle(&fh, flags); + *file = new QSPIFileHandle(&fh, flags); } } - debug_if(QSPI_DBG, "QSPIFS: Failed to open: %d\n", res); - return NULL; + if (res != FS_OK) { + debug_if(QSPI_DBG, "QSPIFS: Failed to open: %d\n", res); + return -res; + } + return 0; } int QSPIFileSystem::remove(const char *filename) @@ -1399,27 +1405,29 @@ return 0; } -DirHandle *QSPIFileSystem::opendir(const char *name) +int QSPIFileSystem::open(DirHandle **dir, const char *path) { if (isformatted()) { - if (*name == '\0') { - // opendir("/qspi/") will result in a call to this function with name="" - return QSPIDirHandle::openDir(name); + if (*path == '\0') { + // opendir("/qspi/") will result in a call to this function with path="" + *dir = QSPIDirHandle::openDir(path); + return 0; } - FileHandle* fh = open(name, O_RDONLY); - if (fh != NULL) { + FileHandle* fh; + if (open(&fh, path, O_RDONLY) == 0) { // Attempting to open a file as a dir delete fh; - return NULL; + return -1; } -// printf("opendir: name '%s'\n", name); - if (strlen(name) <= HEADER_DNAME_MAXLEN) { - return QSPIDirHandle::openDir(name); +// printf("opendir: path '%s'\n", path); + if (strlen(path) <= HEADER_DNAME_MAXLEN) { + *dir = QSPIDirHandle::openDir(path); + return 0; } } - return NULL; + return -1; } int QSPIFileSystem::mkdir(const char *name, mode_t mode)
diff -r e06e764ff4fd -r bbfe299d4a0c FileSystems/QSPIFileSystem.h --- a/FileSystems/QSPIFileSystem.h Wed Oct 23 06:59:29 2019 +0000 +++ b/FileSystems/QSPIFileSystem.h Mon Nov 04 14:32:50 2019 +0000 @@ -118,10 +118,12 @@ */ QSPIFileSystem(const char* name); - virtual FileHandle *open(const char *filename, int flags); + virtual int open(FileHandle **file, const char *filename, int flags); + virtual int open(DirHandle **dir, const char *path); + virtual int remove(const char *filename); virtual int rename(const char *oldname, const char *newname); - virtual DirHandle *opendir(const char *name); + virtual int mkdir(const char *name, mode_t mode); /** Creates a new file system on the QSPI flash.
diff -r e06e764ff4fd -r bbfe299d4a0c Memory/InternalEEPROM.cpp --- a/Memory/InternalEEPROM.cpp Wed Oct 23 06:59:29 2019 +0000 +++ b/Memory/InternalEEPROM.cpp Mon Nov 04 14:32:50 2019 +0000 @@ -123,11 +123,7 @@ void InternalEEPROM::init() { if (!_initialized) { - - // The EEPROM peripheral is in ROM address space. Must allow - // writes to this address space. - mbed_mpu_manager_lock_rom_write(); - + // Bring EEPROM device out of power down mode powerUp();
diff -r e06e764ff4fd -r bbfe299d4a0c dm_rtc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dm_rtc.cpp Mon Nov 04 14:32:50 2019 +0000 @@ -0,0 +1,103 @@ +/* + * Copyright 2019 Embedded Artists AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/****************************************************************************** + * Includes + *****************************************************************************/ + +#include "mbed.h" +#include "dm_rtc.h" +#include "mbed_mktime.h" + +/****************************************************************************** + * Defines and typedefs + *****************************************************************************/ + +/****************************************************************************** + * External global variables + *****************************************************************************/ + +/****************************************************************************** + * Local variables + *****************************************************************************/ + +/****************************************************************************** + * Local Functions + *****************************************************************************/ + + +/****************************************************************************** + * Public Functions + *****************************************************************************/ + +void dm_init_rtc(void) +{ + LPC_SC->PCONP |= 0x200; // Ensure power is on + LPC_RTC->CCR = 0x00; + + LPC_RTC->CCR |= 1 << 0; // Ensure the RTC is enabled +} + +time_t dm_read_rtc(void) +{ + // Setup a tm structure based on the RTC + struct tm timeinfo; + timeinfo.tm_sec = LPC_RTC->SEC; + timeinfo.tm_min = LPC_RTC->MIN; + timeinfo.tm_hour = LPC_RTC->HOUR; + timeinfo.tm_mday = LPC_RTC->DOM; + timeinfo.tm_mon = LPC_RTC->MONTH - 1; + timeinfo.tm_year = LPC_RTC->YEAR - 1900; + + // Convert to timestamp + time_t t; + if (_rtc_maketime(&timeinfo, &t, RTC_4_YEAR_LEAP_YEAR_SUPPORT) == false) { + return 0; + } + + return t; +} + +void dm_write_rtc(time_t t) +{ + // Convert the time in to a tm + struct tm timeinfo; + if (_rtc_localtime(t, &timeinfo, RTC_4_YEAR_LEAP_YEAR_SUPPORT) == false) { + return; + } + + // Pause clock, and clear counter register (clears us count) + LPC_RTC->CCR |= 2; + + // Set the RTC + LPC_RTC->SEC = timeinfo.tm_sec; + LPC_RTC->MIN = timeinfo.tm_min; + LPC_RTC->HOUR = timeinfo.tm_hour; + LPC_RTC->DOM = timeinfo.tm_mday; + LPC_RTC->MONTH = timeinfo.tm_mon + 1; + LPC_RTC->YEAR = timeinfo.tm_year + 1900; + + // Restart clock + LPC_RTC->CCR &= ~((uint32_t)2); +} + +int dm_isenabled_rtc(void) +{ + return(((LPC_RTC->CCR) & 0x01) != 0); +} + + \ No newline at end of file
diff -r e06e764ff4fd -r bbfe299d4a0c dm_rtc.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dm_rtc.h Mon Nov 04 14:32:50 2019 +0000 @@ -0,0 +1,25 @@ +/* + * Copyright 2014 Embedded Artists AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DM_RTC_H +#define DM_RTC_H + +void dm_init_rtc(void); +time_t dm_read_rtc(void); +void dm_write_rtc(time_t t); +int dm_isenabled_rtc(void); + +#endif \ No newline at end of file