/** \file \brief This module contains NMC1000 M2M driver APIs implementation. Copyright (c) 2016-2017 Atmel Corporation. All rights reserved. \asf_license_start \page License Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of Atmel may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \asf_license_stop */ #include "common/include/nm_common.h" #include "driver/source/nmbus.h" #include "bsp/include/nm_bsp.h" #include "driver/source/nmdrv.h" #include "driver/source/nmasic.h" #include "driver/include/m2m_types.h" #include "spi_flash/include/spi_flash.h" #ifdef CONF_WINC_USE_SPI #include "driver/source/nmspi.h" #endif #ifdef ARDUINO uint32 nmdrv_firm_ver = 0; #endif /** @fn nm_get_firmware_info(tstrM2mRev* M2mRev) @brief Get Firmware version info @param [out] M2mRev pointer holds address of structure "tstrM2mRev" that contains the firmware version parameters @version 1.0 */ sint8 nm_get_firmware_info(tstrM2mRev* M2mRev) { uint16 curr_drv_ver, min_req_drv_ver, curr_firm_ver; uint32 reg = 0; sint8 ret = M2M_SUCCESS; ret = nm_read_reg_with_ret(NMI_REV_REG, ®); //In case the Firmware running is ATE fw if (M2M_ATE_FW_IS_UP_VALUE == reg) { //Read FW info again from the register specified for ATE ret = nm_read_reg_with_ret(NMI_REV_REG_ATE, ®); } M2mRev->u8DriverMajor = M2M_GET_DRV_MAJOR(reg); M2mRev->u8DriverMinor = M2M_GET_DRV_MINOR(reg); M2mRev->u8DriverPatch = M2M_GET_DRV_PATCH(reg); M2mRev->u8FirmwareMajor = M2M_GET_FW_MAJOR(reg); M2mRev->u8FirmwareMinor = M2M_GET_FW_MINOR(reg); M2mRev->u8FirmwarePatch = M2M_GET_FW_PATCH(reg); M2mRev->u32Chipid = nmi_get_chipid(); M2mRev->u16FirmwareSvnNum = 0; curr_firm_ver = M2M_MAKE_VERSION(M2mRev->u8FirmwareMajor, M2mRev->u8FirmwareMinor, M2mRev->u8FirmwarePatch); #ifdef ARDUINO nmdrv_firm_ver = curr_firm_ver; #endif curr_drv_ver = M2M_MAKE_VERSION(M2M_RELEASE_VERSION_MAJOR_NO, M2M_RELEASE_VERSION_MINOR_NO, M2M_RELEASE_VERSION_PATCH_NO); min_req_drv_ver = M2M_MAKE_VERSION(M2mRev->u8DriverMajor, M2mRev->u8DriverMinor, M2mRev->u8DriverPatch); if (curr_drv_ver < min_req_drv_ver) { /*The current driver version should be larger or equal than the min driver that the current firmware support */ ret = M2M_ERR_FW_VER_MISMATCH; } if (curr_drv_ver > curr_firm_ver) { /*The current driver should be equal or less than the firmware version*/ ret = M2M_ERR_FW_VER_MISMATCH; } return ret; } /** @fn nm_get_firmware_info(tstrM2mRev* M2mRev) @brief Get Firmware version info @param [out] M2mRev pointer holds address of structure "tstrM2mRev" that contains the firmware version parameters @version 1.0 */ sint8 nm_get_firmware_full_info(tstrM2mRev* pstrRev) { uint16 curr_drv_ver, min_req_drv_ver, curr_firm_ver; uint32 reg = 0; sint8 ret = M2M_SUCCESS; tstrGpRegs strgp = {0}; if (pstrRev != NULL) { m2m_memset((uint8*)pstrRev, 0, sizeof(tstrM2mRev)); ret = nm_read_reg_with_ret(rNMI_GP_REG_2, ®); if (ret == M2M_SUCCESS) { if (reg != 0) { ret = nm_read_block(reg | 0x30000, (uint8*)&strgp, sizeof(tstrGpRegs)); if (ret == M2M_SUCCESS) { reg = strgp.u32Firmware_Ota_rev; reg &= 0x0000ffff; if (reg != 0) { ret = nm_read_block(reg | 0x30000, (uint8*)pstrRev, sizeof(tstrM2mRev)); if (ret == M2M_SUCCESS) { curr_firm_ver = M2M_MAKE_VERSION(pstrRev->u8FirmwareMajor, pstrRev->u8FirmwareMinor, pstrRev->u8FirmwarePatch); #ifdef ARDUINO nmdrv_firm_ver = curr_firm_ver; #endif curr_drv_ver = M2M_MAKE_VERSION(M2M_RELEASE_VERSION_MAJOR_NO, M2M_RELEASE_VERSION_MINOR_NO, M2M_RELEASE_VERSION_PATCH_NO); min_req_drv_ver = M2M_MAKE_VERSION(pstrRev->u8DriverMajor, pstrRev->u8DriverMinor, pstrRev->u8DriverPatch); if ((curr_firm_ver == 0) || (min_req_drv_ver == 0) || (min_req_drv_ver == 0)) { ret = M2M_ERR_FAIL; goto EXIT; } if (curr_drv_ver < min_req_drv_ver) { /*The current driver version should be larger or equal than the min driver that the current firmware support */ ret = M2M_ERR_FW_VER_MISMATCH; goto EXIT; } if (curr_drv_ver > curr_firm_ver) { /*The current driver should be equal or less than the firmware version*/ ret = M2M_ERR_FW_VER_MISMATCH; goto EXIT; } } } else { ret = M2M_ERR_FAIL; } } } else { ret = M2M_ERR_FAIL; } } } EXIT: return ret; } /** @fn nm_get_ota_firmware_info(tstrM2mRev* pstrRev) @brief Get Firmware version info @param [out] M2mRev pointer holds address of structure "tstrM2mRev" that contains the firmware version parameters @version 1.0 */ sint8 nm_get_ota_firmware_info(tstrM2mRev* pstrRev) { uint16 curr_drv_ver, min_req_drv_ver, curr_firm_ver; uint32 reg = 0; sint8 ret; tstrGpRegs strgp = {0}; if (pstrRev != NULL) { m2m_memset((uint8*)pstrRev, 0, sizeof(tstrM2mRev)); ret = nm_read_reg_with_ret(rNMI_GP_REG_2, ®); if (ret == M2M_SUCCESS) { if (reg != 0) { ret = nm_read_block(reg | 0x30000, (uint8*)&strgp, sizeof(tstrGpRegs)); if (ret == M2M_SUCCESS) { reg = strgp.u32Firmware_Ota_rev; reg >>= 16; if (reg != 0) { ret = nm_read_block(reg | 0x30000, (uint8*)pstrRev, sizeof(tstrM2mRev)); if (ret == M2M_SUCCESS) { curr_firm_ver = M2M_MAKE_VERSION(pstrRev->u8FirmwareMajor, pstrRev->u8FirmwareMinor, pstrRev->u8FirmwarePatch); curr_drv_ver = M2M_MAKE_VERSION(M2M_RELEASE_VERSION_MAJOR_NO, M2M_RELEASE_VERSION_MINOR_NO, M2M_RELEASE_VERSION_PATCH_NO); min_req_drv_ver = M2M_MAKE_VERSION(pstrRev->u8DriverMajor, pstrRev->u8DriverMinor, pstrRev->u8DriverPatch); if ((curr_firm_ver == 0) || (min_req_drv_ver == 0) || (min_req_drv_ver == 0)) { ret = M2M_ERR_FAIL; goto EXIT; } if (curr_drv_ver < min_req_drv_ver) { /*The current driver version should be larger or equal than the min driver that the current firmware support */ ret = M2M_ERR_FW_VER_MISMATCH; } if (curr_drv_ver > curr_firm_ver) { /*The current driver should be equal or less than the firmware version*/ ret = M2M_ERR_FW_VER_MISMATCH; } } } else { ret = M2M_ERR_INVALID; } } } else { ret = M2M_ERR_FAIL; } } } else { ret = M2M_ERR_INVALID_ARG; } EXIT: return ret; } /* @fn nm_drv_init_download_mode @brief Initialize NMC1000 driver @return M2M_SUCCESS in case of success and Negative error code in case of failure @param [in] arg Generic argument @author Viswanathan Murugesan @date 10 Oct 2014 @version 1.0 */ sint8 nm_drv_init_download_mode() { sint8 ret = M2M_SUCCESS; ret = nm_bus_iface_init(NULL); if (M2M_SUCCESS != ret) { M2M_ERR("[nmi start]: fail init bus\n"); goto ERR1; } /** TODO:reset the chip and halt the cpu in case of no wait efuse is set (add the no wait effuse check) */ if (!ISNMC3000(GET_CHIPID())) { /*Execuate that function only for 1500A/B, no room in 3000, but it may be needed in 3400 no wait*/ chip_reset_and_cpu_halt(); } #ifdef CONF_WINC_USE_SPI /* Must do this after global reset to set SPI data packet size. */ nm_spi_init(); #endif M2M_INFO("Chip ID %lx\n", nmi_get_chipid()); /*disable all interrupt in ROM (to disable uart) in 2b0 chip*/ nm_write_reg(0x20300, 0); ERR1: return ret; } /* @fn nm_drv_init @brief Initialize NMC1000 driver @return M2M_SUCCESS in case of success and Negative error code in case of failure @param [in] arg Generic argument @author M. Abdelmawla @date 15 July 2012 @version 1.0 */ sint8 nm_drv_init(void * arg) { sint8 ret = M2M_SUCCESS; uint8 u8Mode; if (NULL != arg) { u8Mode = *((uint8 *)arg); if ((u8Mode < M2M_WIFI_MODE_NORMAL) || (u8Mode >= M2M_WIFI_MODE_MAX)) { u8Mode = M2M_WIFI_MODE_NORMAL; } } else { u8Mode = M2M_WIFI_MODE_NORMAL; } ret = nm_bus_iface_init(NULL); if (M2M_SUCCESS != ret) { M2M_ERR("[nmi start]: fail init bus\n"); goto ERR1; } #ifdef BUS_ONLY return; #endif #ifdef NO_HW_CHIP_EN ret = chip_wake(); if (M2M_SUCCESS != ret) { M2M_ERR("[nmi start]: fail chip_wakeup\n"); goto ERR2; } /** Go... **/ ret = chip_reset(); if (M2M_SUCCESS != ret) { goto ERR2; } #endif M2M_INFO("Chip ID %lx\n", nmi_get_chipid()); #ifdef ARDUINO if ((REV(GET_CHIPID()) & 0xff0) != REV_3A0 && (REV(GET_CHIPID()) & 0xff0) != REV_B0) { ret = M2M_ERR_INVALID; goto ERR2; } #endif #ifdef CONF_WINC_USE_SPI /* Must do this after global reset to set SPI data packet size. */ nm_spi_init(); #endif ret = wait_for_bootrom(u8Mode); if (M2M_SUCCESS != ret) { goto ERR2; } ret = wait_for_firmware_start(u8Mode); if (M2M_SUCCESS != ret) { goto ERR2; } if ((M2M_WIFI_MODE_ATE_HIGH == u8Mode) || (M2M_WIFI_MODE_ATE_LOW == u8Mode)) { goto ERR1; } else { /*continue running*/ } ret = enable_interrupts(); if (M2M_SUCCESS != ret) { M2M_ERR("failed to enable interrupts..\n"); goto ERR2; } return ret; ERR2: nm_bus_iface_deinit(); ERR1: return ret; } /* @fn nm_drv_deinit @brief Deinitialize NMC1000 driver @author M. Abdelmawla @date 17 July 2012 @version 1.0 */ sint8 nm_drv_deinit(void * arg) { #ifdef ARDUINO (void)arg; // Silence "unused" warning #endif sint8 ret; ret = chip_deinit(); if (M2M_SUCCESS != ret) { M2M_ERR("[nmi stop]: chip_deinit fail\n"); goto ERR1; } /* Disable SPI flash to save power when the chip is off */ ret = spi_flash_enable(0); if (M2M_SUCCESS != ret) { M2M_ERR("[nmi stop]: SPI flash disable fail\n"); goto ERR1; } ret = nm_bus_iface_deinit(); if (M2M_SUCCESS != ret) { M2M_ERR("[nmi stop]: fail init bus\n"); goto ERR1; } #ifdef CONF_WINC_USE_SPI /* Must do this after global reset to set SPI data packet size. */ nm_spi_deinit(); #endif ERR1: return ret; }