/** \file \brief This module contains NMC1000 SPI protocol bus 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" #ifdef CONF_WINC_USE_SPI #define USE_OLD_SPI_SW #include "bus_wrapper/include/nm_bus_wrapper.h" #include "nmspi.h" #define NMI_PERIPH_REG_BASE 0x1000 #define NMI_INTR_REG_BASE (NMI_PERIPH_REG_BASE+0xa00) #define NMI_CHIPID (NMI_PERIPH_REG_BASE) #define NMI_PIN_MUX_0 (NMI_PERIPH_REG_BASE + 0x408) #define NMI_INTR_ENABLE (NMI_INTR_REG_BASE) #define NMI_SPI_REG_BASE 0xe800 #define NMI_SPI_CTL (NMI_SPI_REG_BASE) #define NMI_SPI_MASTER_DMA_ADDR (NMI_SPI_REG_BASE+0x4) #define NMI_SPI_MASTER_DMA_COUNT (NMI_SPI_REG_BASE+0x8) #define NMI_SPI_SLAVE_DMA_ADDR (NMI_SPI_REG_BASE+0xc) #define NMI_SPI_SLAVE_DMA_COUNT (NMI_SPI_REG_BASE+0x10) #define NMI_SPI_TX_MODE (NMI_SPI_REG_BASE+0x20) #define NMI_SPI_PROTOCOL_CONFIG (NMI_SPI_REG_BASE+0x24) #define NMI_SPI_INTR_CTL (NMI_SPI_REG_BASE+0x2c) #define NMI_SPI_MISC_CTRL (NMI_SPI_REG_BASE+0x48) #define NMI_SPI_PROTOCOL_OFFSET (NMI_SPI_PROTOCOL_CONFIG-NMI_SPI_REG_BASE) #define SPI_BASE NMI_SPI_REG_BASE #define CMD_DMA_WRITE 0xc1 #define CMD_DMA_READ 0xc2 #define CMD_INTERNAL_WRITE 0xc3 #define CMD_INTERNAL_READ 0xc4 #define CMD_TERMINATE 0xc5 #define CMD_REPEAT 0xc6 #define CMD_DMA_EXT_WRITE 0xc7 #define CMD_DMA_EXT_READ 0xc8 #define CMD_SINGLE_WRITE 0xc9 #define CMD_SINGLE_READ 0xca #define CMD_RESET 0xcf #define N_OK 1 #define N_FAIL 0 #define N_RESET -1 #define N_RETRY -2 #define SPI_RESP_RETRY_COUNT (10) #define SPI_RETRY_COUNT (10) #define DATA_PKT_SZ_256 256 #define DATA_PKT_SZ_512 512 #define DATA_PKT_SZ_1K 1024 #define DATA_PKT_SZ_4K (4 * 1024) #define DATA_PKT_SZ_8K (8 * 1024) #define DATA_PKT_SZ DATA_PKT_SZ_8K static uint8 gu8Crc_off = 0; static sint8 nmi_spi_read(uint8* b, uint16 sz) { tstrNmSpiRw spi; spi.pu8InBuf = NULL; spi.pu8OutBuf = b; spi.u16Sz = sz; return nm_bus_ioctl(NM_BUS_IOCTL_RW, &spi); } static sint8 nmi_spi_write(uint8* b, uint16 sz) { tstrNmSpiRw spi; spi.pu8InBuf = b; spi.pu8OutBuf = NULL; spi.u16Sz = sz; return nm_bus_ioctl(NM_BUS_IOCTL_RW, &spi); } #ifndef USE_OLD_SPI_SW static sint8 nmi_spi_rw(uint8 *bin, uint8* bout, uint16 sz) { tstrNmSpiRw spi; spi.pu8InBuf = bin; spi.pu8OutBuf = bout; spi.u16Sz = sz; return nm_bus_ioctl(NM_BUS_IOCTL_RW, &spi); } #endif /******************************************** Crc7 ********************************************/ #if (defined ARDUINO_ARCH_AVR) #include static PROGMEM const uint8 crc7_syndrome_table[256] = { #else static const uint8 crc7_syndrome_table[256] = { #endif 0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77, 0x19, 0x10, 0x0b, 0x02, 0x3d, 0x34, 0x2f, 0x26, 0x51, 0x58, 0x43, 0x4a, 0x75, 0x7c, 0x67, 0x6e, 0x32, 0x3b, 0x20, 0x29, 0x16, 0x1f, 0x04, 0x0d, 0x7a, 0x73, 0x68, 0x61, 0x5e, 0x57, 0x4c, 0x45, 0x2b, 0x22, 0x39, 0x30, 0x0f, 0x06, 0x1d, 0x14, 0x63, 0x6a, 0x71, 0x78, 0x47, 0x4e, 0x55, 0x5c, 0x64, 0x6d, 0x76, 0x7f, 0x40, 0x49, 0x52, 0x5b, 0x2c, 0x25, 0x3e, 0x37, 0x08, 0x01, 0x1a, 0x13, 0x7d, 0x74, 0x6f, 0x66, 0x59, 0x50, 0x4b, 0x42, 0x35, 0x3c, 0x27, 0x2e, 0x11, 0x18, 0x03, 0x0a, 0x56, 0x5f, 0x44, 0x4d, 0x72, 0x7b, 0x60, 0x69, 0x1e, 0x17, 0x0c, 0x05, 0x3a, 0x33, 0x28, 0x21, 0x4f, 0x46, 0x5d, 0x54, 0x6b, 0x62, 0x79, 0x70, 0x07, 0x0e, 0x15, 0x1c, 0x23, 0x2a, 0x31, 0x38, 0x41, 0x48, 0x53, 0x5a, 0x65, 0x6c, 0x77, 0x7e, 0x09, 0x00, 0x1b, 0x12, 0x2d, 0x24, 0x3f, 0x36, 0x58, 0x51, 0x4a, 0x43, 0x7c, 0x75, 0x6e, 0x67, 0x10, 0x19, 0x02, 0x0b, 0x34, 0x3d, 0x26, 0x2f, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c, 0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, 0x6a, 0x63, 0x78, 0x71, 0x4e, 0x47, 0x5c, 0x55, 0x22, 0x2b, 0x30, 0x39, 0x06, 0x0f, 0x14, 0x1d, 0x25, 0x2c, 0x37, 0x3e, 0x01, 0x08, 0x13, 0x1a, 0x6d, 0x64, 0x7f, 0x76, 0x49, 0x40, 0x5b, 0x52, 0x3c, 0x35, 0x2e, 0x27, 0x18, 0x11, 0x0a, 0x03, 0x74, 0x7d, 0x66, 0x6f, 0x50, 0x59, 0x42, 0x4b, 0x17, 0x1e, 0x05, 0x0c, 0x33, 0x3a, 0x21, 0x28, 0x5f, 0x56, 0x4d, 0x44, 0x7b, 0x72, 0x69, 0x60, 0x0e, 0x07, 0x1c, 0x15, 0x2a, 0x23, 0x38, 0x31, 0x46, 0x4f, 0x54, 0x5d, 0x62, 0x6b, 0x70, 0x79 }; static uint8 crc7_byte(uint8 crc, uint8 data) { #if (defined ARDUINO_ARCH_AVR) return pgm_read_byte_near(crc7_syndrome_table + ((crc << 1) ^ data)); #else return crc7_syndrome_table[(crc << 1) ^ data]; #endif } static uint8 crc7(uint8 crc, const uint8 *buffer, uint32 len) { while (len--) crc = crc7_byte(crc, *buffer++); return crc; } /******************************************** Spi protocol Function ********************************************/ #define CMD_DMA_WRITE 0xc1 #define CMD_DMA_READ 0xc2 #define CMD_INTERNAL_WRITE 0xc3 #define CMD_INTERNAL_READ 0xc4 #define CMD_TERMINATE 0xc5 #define CMD_REPEAT 0xc6 #define CMD_DMA_EXT_WRITE 0xc7 #define CMD_DMA_EXT_READ 0xc8 #define CMD_SINGLE_WRITE 0xc9 #define CMD_SINGLE_READ 0xca #define CMD_RESET 0xcf #define DATA_PKT_SZ_256 256 #define DATA_PKT_SZ_512 512 #define DATA_PKT_SZ_1K 1024 #define DATA_PKT_SZ_4K (4 * 1024) #define DATA_PKT_SZ_8K (8 * 1024) #define DATA_PKT_SZ DATA_PKT_SZ_8K static sint8 spi_cmd(uint8 cmd, uint32 adr, uint32 u32data, uint32 sz, uint8 clockless) { uint8 bc[9]; uint8 len = 5; sint8 result = N_OK; bc[0] = cmd; switch (cmd) { case CMD_SINGLE_READ: /* single word (4 bytes) read */ bc[1] = (uint8)(adr >> 16); bc[2] = (uint8)(adr >> 8); bc[3] = (uint8)adr; len = 5; break; case CMD_INTERNAL_READ: /* internal register read */ bc[1] = (uint8)(adr >> 8); if (clockless) bc[1] |= (1 << 7); bc[2] = (uint8)adr; bc[3] = 0x00; len = 5; break; case CMD_TERMINATE: /* termination */ bc[1] = 0x00; bc[2] = 0x00; bc[3] = 0x00; len = 5; break; case CMD_REPEAT: /* repeat */ bc[1] = 0x00; bc[2] = 0x00; bc[3] = 0x00; len = 5; break; case CMD_RESET: /* reset */ bc[1] = 0xff; bc[2] = 0xff; bc[3] = 0xff; len = 5; break; case CMD_DMA_WRITE: /* dma write */ case CMD_DMA_READ: /* dma read */ bc[1] = (uint8)(adr >> 16); bc[2] = (uint8)(adr >> 8); bc[3] = (uint8)adr; bc[4] = (uint8)(sz >> 8); bc[5] = (uint8)(sz); len = 7; break; case CMD_DMA_EXT_WRITE: /* dma extended write */ case CMD_DMA_EXT_READ: /* dma extended read */ bc[1] = (uint8)(adr >> 16); bc[2] = (uint8)(adr >> 8); bc[3] = (uint8)adr; bc[4] = (uint8)(sz >> 16); bc[5] = (uint8)(sz >> 8); bc[6] = (uint8)(sz); len = 8; break; case CMD_INTERNAL_WRITE: /* internal register write */ bc[1] = (uint8)(adr >> 8); if (clockless) bc[1] |= (1 << 7); bc[2] = (uint8)(adr); bc[3] = (uint8)(u32data >> 24); bc[4] = (uint8)(u32data >> 16); bc[5] = (uint8)(u32data >> 8); bc[6] = (uint8)(u32data); len = 8; break; case CMD_SINGLE_WRITE: /* single word write */ bc[1] = (uint8)(adr >> 16); bc[2] = (uint8)(adr >> 8); bc[3] = (uint8)(adr); bc[4] = (uint8)(u32data >> 24); bc[5] = (uint8)(u32data >> 16); bc[6] = (uint8)(u32data >> 8); bc[7] = (uint8)(u32data); len = 9; break; default: result = N_FAIL; break; } if (result) { if (!gu8Crc_off) bc[len - 1] = (crc7(0x7f, (const uint8 *)&bc[0], len - 1)) << 1; else len -= 1; if (M2M_SUCCESS != nmi_spi_write(bc, len)) { M2M_ERR("[nmi spi]: Failed cmd write, bus error...\n"); result = N_FAIL; } } return result; } static sint8 spi_data_rsp(uint8 cmd) { #ifdef ARDUINO (void)cmd; // Silence "unused" warning #endif uint8 len; uint8 rsp[3]; sint8 result = N_OK; if (!gu8Crc_off) len = 2; else len = 3; if (M2M_SUCCESS != nmi_spi_read(&rsp[0], len)) { M2M_ERR("[nmi spi]: Failed bus error...\n"); result = N_FAIL; goto _fail_; } if ((rsp[len - 1] != 0) || (rsp[len - 2] != 0xC3)) { M2M_ERR("[nmi spi]: Failed data response read, %x %x %x\n", rsp[0], rsp[1], rsp[2]); result = N_FAIL; goto _fail_; } _fail_: return result; } static sint8 spi_cmd_rsp(uint8 cmd) { uint8 rsp; sint8 result = N_OK; sint8 s8RetryCnt; /** Command/Control response **/ if ((cmd == CMD_RESET) || (cmd == CMD_TERMINATE) || (cmd == CMD_REPEAT)) { if (M2M_SUCCESS != nmi_spi_read(&rsp, 1)) { result = N_FAIL; goto _fail_; } } /* wait for response */ s8RetryCnt = SPI_RESP_RETRY_COUNT; do { if (M2M_SUCCESS != nmi_spi_read(&rsp, 1)) { M2M_ERR("[nmi spi]: Failed cmd response read, bus error...\n"); result = N_FAIL; goto _fail_; } } while ((rsp != cmd) && (s8RetryCnt-- > 0)); /** State response **/ /* wait for response */ s8RetryCnt = SPI_RESP_RETRY_COUNT; do { if (M2M_SUCCESS != nmi_spi_read(&rsp, 1)) { M2M_ERR("[nmi spi]: Failed cmd response read, bus error...\n"); result = N_FAIL; goto _fail_; } } while ((rsp != 0x00) && (s8RetryCnt-- > 0)); _fail_: return result; } #ifndef USE_OLD_SPI_SW static int spi_cmd_complete(uint8_t cmd, uint32_t adr, uint8_t *b, uint32_t sz, uint8_t clockless) { uint8_t wb[32], rb[32]; uint8_t wix, rix; uint32_t len2; uint8_t rsp; int len = 0; int result = N_OK; wb[0] = cmd; switch (cmd) { case CMD_SINGLE_READ: /* single word (4 bytes) read */ wb[1] = (uint8_t)(adr >> 16); wb[2] = (uint8_t)(adr >> 8); wb[3] = (uint8_t)adr; len = 5; break; case CMD_INTERNAL_READ: /* internal register read */ wb[1] = (uint8_t)(adr >> 8); if (clockless == 1) wb[1] |= (1 << 7); wb[2] = (uint8_t)adr; wb[3] = 0x00; len = 5; break; case CMD_TERMINATE: /* termination */ wb[1] = 0x00; wb[2] = 0x00; wb[3] = 0x00; len = 5; break; case CMD_REPEAT: /* repeat */ wb[1] = 0x00; wb[2] = 0x00; wb[3] = 0x00; len = 5; break; case CMD_RESET: /* reset */ wb[1] = 0xff; wb[2] = 0xff; wb[3] = 0xff; len = 5; break; case CMD_DMA_WRITE: /* dma write */ case CMD_DMA_READ: /* dma read */ wb[1] = (uint8_t)(adr >> 16); wb[2] = (uint8_t)(adr >> 8); wb[3] = (uint8_t)adr; wb[4] = (uint8_t)(sz >> 8); wb[5] = (uint8_t)(sz); len = 7; break; case CMD_DMA_EXT_WRITE: /* dma extended write */ case CMD_DMA_EXT_READ: /* dma extended read */ wb[1] = (uint8_t)(adr >> 16); wb[2] = (uint8_t)(adr >> 8); wb[3] = (uint8_t)adr; wb[4] = (uint8_t)(sz >> 16); wb[5] = (uint8_t)(sz >> 8); wb[6] = (uint8_t)(sz); len = 8; break; case CMD_INTERNAL_WRITE: /* internal register write */ wb[1] = (uint8_t)(adr >> 8); if (clockless == 1) wb[1] |= (1 << 7); wb[2] = (uint8_t)(adr); wb[3] = b[3]; wb[4] = b[2]; wb[5] = b[1]; wb[6] = b[0]; len = 8; break; case CMD_SINGLE_WRITE: /* single word write */ wb[1] = (uint8_t)(adr >> 16); wb[2] = (uint8_t)(adr >> 8); wb[3] = (uint8_t)(adr); wb[4] = b[3]; wb[5] = b[2]; wb[6] = b[1]; wb[7] = b[0]; len = 9; break; default: result = N_FAIL; break; } if (result != N_OK) { return result; } if (!gu8Crc_off) { wb[len - 1] = (crc7(0x7f, (const uint8_t *)&wb[0], len - 1)) << 1; } else { len -= 1; } #define NUM_SKIP_BYTES (1) #define NUM_RSP_BYTES (2) #define NUM_DATA_HDR_BYTES (1) #define NUM_DATA_BYTES (4) #define NUM_CRC_BYTES (2) #define NUM_DUMMY_BYTES (3) if ((cmd == CMD_RESET) || (cmd == CMD_TERMINATE) || (cmd == CMD_REPEAT)) { len2 = len + (NUM_SKIP_BYTES + NUM_RSP_BYTES + NUM_DUMMY_BYTES); } else if ((cmd == CMD_INTERNAL_READ) || (cmd == CMD_SINGLE_READ)) { if (!gu8Crc_off) { len2 = len + (NUM_RSP_BYTES + NUM_DATA_HDR_BYTES + NUM_DATA_BYTES + NUM_CRC_BYTES + NUM_DUMMY_BYTES); } else { len2 = len + (NUM_RSP_BYTES + NUM_DATA_HDR_BYTES + NUM_DATA_BYTES + NUM_DUMMY_BYTES); } } else { len2 = len + (NUM_RSP_BYTES + NUM_DUMMY_BYTES); } #undef NUM_DUMMY_BYTES if (len2 > (sizeof(wb) / sizeof(wb[0]))) { M2M_ERR("[nmi spi]: spi buffer size too small (%d) (%d)\n", len2, (sizeof(wb) / sizeof(wb[0]))); result = N_FAIL; return result; } /* zero spi write buffers. */ for (wix = len; wix < len2; wix++) { wb[wix] = 0; } rix = len; if (nmi_spi_rw(wb, rb, len2) != M2M_SUCCESS) { M2M_ERR("[nmi spi]: Failed cmd write, bus error...\n"); result = N_FAIL; return result; } #if 0 { int jj; printk("--- cnd = %x, len=%d, len2=%d\n", cmd, len, len2); for (jj = 0; jj < sizeof(wb) / sizeof(wb[0]); jj++) { if (jj >= len2) break; if (((jj + 1) % 16) != 0) { if ((jj % 16) == 0) { printk("wb[%02x]: %02x ", jj, wb[jj]); } else { printk("%02x ", wb[jj]); } } else { printk("%02x\n", wb[jj]); } } printk("\n"); for (jj = 0; jj < sizeof(rb) / sizeof(rb[0]); jj++) { if (jj >= len2) break; if (((jj + 1) % 16) != 0) { if ((jj % 16) == 0) { printk("rb[%02x]: %02x ", jj, rb[jj]); } else { printk("%02x ", rb[jj]); } } else { printk("%02x\n", rb[jj]); } } printk("\n"); } #endif /** Command/Control response **/ if ((cmd == CMD_RESET) || (cmd == CMD_TERMINATE) || (cmd == CMD_REPEAT)) { rix++; /* skip 1 byte */ } rsp = rb[rix++]; if (rsp != cmd) { M2M_ERR("[nmi spi]: Failed cmd response, cmd (%02x), resp (%02x)\n", cmd, rsp); result = N_FAIL; return result; } /** State response **/ rsp = rb[rix++]; if (rsp != 0x00) { M2M_ERR("[nmi spi]: Failed cmd state response state (%02x)\n", rsp); result = N_FAIL; return result; } if ((cmd == CMD_INTERNAL_READ) || (cmd == CMD_SINGLE_READ) || (cmd == CMD_DMA_READ) || (cmd == CMD_DMA_EXT_READ)) { int retry; //uint16_t crc1, crc2; uint8_t crc[2]; /** Data Respnose header **/ retry = SPI_RESP_RETRY_COUNT; do { /* ensure there is room in buffer later to read data and crc */ if (rix < len2) { rsp = rb[rix++]; } else { retry = 0; break; } if (((rsp >> 4) & 0xf) == 0xf) break; } while (retry--); if (retry <= 0) { M2M_ERR("[nmi spi]: Error, data read response (%02x)\n", rsp); result = N_RESET; return result; } if ((cmd == CMD_INTERNAL_READ) || (cmd == CMD_SINGLE_READ)) { /** Read bytes **/ if ((rix + 3) < len2) { b[0] = rb[rix++]; b[1] = rb[rix++]; b[2] = rb[rix++]; b[3] = rb[rix++]; } else { M2M_ERR("[nmi spi]: buffer overrun when reading data.\n"); result = N_FAIL; return result; } if (!gu8Crc_off) { /** Read Crc **/ if ((rix + 1) < len2) { crc[0] = rb[rix++]; crc[1] = rb[rix++]; } else { M2M_ERR("[nmi spi]: buffer overrun when reading crc.\n"); result = N_FAIL; return result; } } } else if ((cmd == CMD_DMA_READ) || (cmd == CMD_DMA_EXT_READ)) { int ix; /* some data may be read in response to dummy bytes. */ for (ix = 0; (rix < len2) && (ix < sz);) { b[ix++] = rb[rix++]; } #if 0 if (ix) M2M_INFO("ttt %d %d\n", sz, ix); #endif sz -= ix; if (sz > 0) { int nbytes; if (sz <= (DATA_PKT_SZ - ix)) { nbytes = sz; } else { nbytes = DATA_PKT_SZ - ix; } /** Read bytes **/ if (nmi_spi_read(&b[ix], nbytes) != M2M_SUCCESS) { M2M_ERR("[nmi spi]: Failed data block read, bus error...\n"); result = N_FAIL; goto _error_; } /** Read Crc **/ if (!gu8Crc_off) { if (nmi_spi_read(crc, 2) != M2M_SUCCESS) { M2M_ERR("[nmi spi]: Failed data block crc read, bus error...\n"); result = N_FAIL; goto _error_; } } ix += nbytes; sz -= nbytes; } /* if any data in left unread, then read the rest using normal DMA code.*/ while (sz > 0) { int nbytes; if (sz <= DATA_PKT_SZ) { nbytes = sz; } else { nbytes = DATA_PKT_SZ; } /** read data response only on the next DMA cycles not the first DMA since data response header is already handled above for the first DMA. **/ /** Data Respnose header **/ retry = SPI_RESP_RETRY_COUNT; do { if (nmi_spi_read(&rsp, 1) != M2M_SUCCESS) { M2M_ERR("[nmi spi]: Failed data response read, bus error...\n"); result = N_FAIL; break; } if (((rsp >> 4) & 0xf) == 0xf) break; } while (retry--); if (result == N_FAIL) break; /** Read bytes **/ if (nmi_spi_read(&b[ix], nbytes) != M2M_SUCCESS) { M2M_ERR("[nmi spi]: Failed data block read, bus error...\n"); result = N_FAIL; break; } /** Read Crc **/ if (!gu8Crc_off) { if (nmi_spi_read(crc, 2) != M2M_SUCCESS) { M2M_ERR("[nmi spi]: Failed data block crc read, bus error...\n"); result = N_FAIL; break; } } ix += nbytes; sz -= nbytes; } } } _error_: return result; } #endif static sint8 spi_data_read(uint8 *b, uint16 sz, uint8 clockless) { sint16 retry, ix, nbytes; sint8 result = N_OK; uint8 crc[2]; uint8 rsp; /** Data **/ ix = 0; do { if (sz <= DATA_PKT_SZ) nbytes = sz; else nbytes = DATA_PKT_SZ; /** Data Respnose header **/ retry = SPI_RESP_RETRY_COUNT; do { if (M2M_SUCCESS != nmi_spi_read(&rsp, 1)) { M2M_ERR("[nmi spi]: Failed data response read, bus error...\n"); result = N_FAIL; break; } if (((rsp >> 4) & 0xf) == 0xf) break; } while (retry--); if (result == N_FAIL) break; if (retry <= 0) { M2M_ERR("[nmi spi]: Failed data response read...(%02x)\n", rsp); result = N_FAIL; break; } /** Read bytes **/ if (M2M_SUCCESS != nmi_spi_read(&b[ix], nbytes)) { M2M_ERR("[nmi spi]: Failed data block read, bus error...\n"); result = N_FAIL; break; } if (!clockless) { /** Read Crc **/ if (!gu8Crc_off) { if (M2M_SUCCESS != nmi_spi_read(crc, 2)) { M2M_ERR("[nmi spi]: Failed data block crc read, bus error...\n"); result = N_FAIL; break; } } } ix += nbytes; sz -= nbytes; } while (sz); return result; } static sint8 spi_data_write(uint8 *b, uint16 sz) { sint16 ix; uint16 nbytes; sint8 result = 1; uint8 cmd, order, crc[2] = {0}; //uint8 rsp; /** Data **/ ix = 0; do { if (sz <= DATA_PKT_SZ) nbytes = sz; else nbytes = DATA_PKT_SZ; /** Write command **/ cmd = 0xf0; if (ix == 0) { if (sz <= DATA_PKT_SZ) order = 0x3; else order = 0x1; } else { if (sz <= DATA_PKT_SZ) order = 0x3; else order = 0x2; } cmd |= order; if (M2M_SUCCESS != nmi_spi_write(&cmd, 1)) { M2M_ERR("[nmi spi]: Failed data block cmd write, bus error...\n"); result = N_FAIL; break; } /** Write data **/ if (M2M_SUCCESS != nmi_spi_write(&b[ix], nbytes)) { M2M_ERR("[nmi spi]: Failed data block write, bus error...\n"); result = N_FAIL; break; } /** Write Crc **/ if (!gu8Crc_off) { if (M2M_SUCCESS != nmi_spi_write(crc, 2)) { M2M_ERR("[nmi spi]: Failed data block crc write, bus error...\n"); result = N_FAIL; break; } } ix += nbytes; sz -= nbytes; } while (sz); return result; } /******************************************** Spi Internal Read/Write Function ********************************************/ /******************************************** Spi interfaces ********************************************/ static sint8 spi_write_reg(uint32 addr, uint32 u32data) { uint8 retry = SPI_RETRY_COUNT; sint8 result = N_OK; uint8 cmd = CMD_SINGLE_WRITE; uint8 clockless = 0; _RETRY_: if (addr <= 0x30) { /** NMC1000 clockless registers. **/ cmd = CMD_INTERNAL_WRITE; clockless = 1; } else { cmd = CMD_SINGLE_WRITE; clockless = 0; } #if defined USE_OLD_SPI_SW result = spi_cmd(cmd, addr, u32data, 4, clockless); if (result != N_OK) { M2M_ERR("[nmi spi]: Failed cmd, write reg (%08x)...\n", (unsigned int)addr); goto _FAIL_; } result = spi_cmd_rsp(cmd); if (result != N_OK) { M2M_ERR("[nmi spi]: Failed cmd response, write reg (%08x)...\n", (unsigned int)addr); goto _FAIL_; } #else result = spi_cmd_complete(cmd, addr, (uint8*)&u32data, 4, clockless); if (result != N_OK) { M2M_ERR( "[nmi spi]: Failed cmd, write reg (%08x)...\n", addr); goto _FAIL_; } #endif _FAIL_: if (result != N_OK) { nm_bsp_sleep(1); spi_cmd(CMD_RESET, 0, 0, 0, 0); spi_cmd_rsp(CMD_RESET); M2M_ERR("Reset and retry %d %lx %lx\n", retry, addr, u32data); nm_bsp_sleep(1); retry--; if (retry) goto _RETRY_; } return result; } static sint8 nm_spi_write(uint32 addr, uint8 *buf, uint16 size) { sint8 result; uint8 retry = SPI_RETRY_COUNT; uint8 cmd = CMD_DMA_EXT_WRITE; _RETRY_: /** Command **/ #if defined USE_OLD_SPI_SW //Workaround hardware problem with single byte transfers over SPI bus if (size == 1) size = 2; result = spi_cmd(cmd, addr, 0, size, 0); if (result != N_OK) { M2M_ERR("[nmi spi]: Failed cmd, write block (%08x)...\n", (unsigned int)addr); goto _FAIL_; } result = spi_cmd_rsp(cmd); if (result != N_OK) { M2M_ERR("[nmi spi ]: Failed cmd response, write block (%08x)...\n", (unsigned int)addr); goto _FAIL_; } #else result = spi_cmd_complete(cmd, addr, NULL, size, 0); if (result != N_OK) { M2M_ERR( "[nmi spi]: Failed cmd, write block (%08x)...\n", addr); goto _FAIL_; } #endif /** Data **/ result = spi_data_write(buf, size); if (result != N_OK) { M2M_ERR("[nmi spi]: Failed block data write...\n"); goto _FAIL_; } /** Data RESP **/ result = spi_data_rsp(cmd); if (result != N_OK) { M2M_ERR("[nmi spi]: Failed block data write...\n"); goto _FAIL_; } _FAIL_: if (result != N_OK) { nm_bsp_sleep(1); spi_cmd(CMD_RESET, 0, 0, 0, 0); spi_cmd_rsp(CMD_RESET); M2M_ERR("Reset and retry %d %lx %d\n", retry, addr, size); nm_bsp_sleep(1); retry--; if (retry) goto _RETRY_; } return result; } static sint8 spi_read_reg(uint32 addr, uint32 *u32data) { uint8 retry = SPI_RETRY_COUNT; sint8 result = N_OK; uint8 cmd = CMD_SINGLE_READ; uint8 tmp[4]; uint8 clockless = 0; _RETRY_: if (addr <= 0xff) { /** NMC1000 clockless registers. **/ cmd = CMD_INTERNAL_READ; clockless = 1; } else { cmd = CMD_SINGLE_READ; clockless = 0; } #if defined USE_OLD_SPI_SW result = spi_cmd(cmd, addr, 0, 4, clockless); if (result != N_OK) { M2M_ERR("[nmi spi]: Failed cmd, read reg (%08x)...\n", (unsigned int)addr); goto _FAIL_; } result = spi_cmd_rsp(cmd); if (result != N_OK) { M2M_ERR("[nmi spi]: Failed cmd response, read reg (%08x)...\n", (unsigned int)addr); goto _FAIL_; } /* to avoid endianess issues */ result = spi_data_read(&tmp[0], 4, clockless); if (result != N_OK) { M2M_ERR("[nmi spi]: Failed data read...\n"); goto _FAIL_; } #else result = spi_cmd_complete(cmd, addr, (uint8*)&tmp[0], 4, clockless); if (result != N_OK) { M2M_ERR( "[nmi spi]: Failed cmd, read reg (%08x)...\n", addr); goto _FAIL_; } #endif *u32data = tmp[0] | ((uint32)tmp[1] << 8) | ((uint32)tmp[2] << 16) | ((uint32)tmp[3] << 24); _FAIL_: if (result != N_OK) { nm_bsp_sleep(1); spi_cmd(CMD_RESET, 0, 0, 0, 0); spi_cmd_rsp(CMD_RESET); M2M_ERR("Reset and retry %d %lx\n", retry, addr); nm_bsp_sleep(1); retry--; if (retry) goto _RETRY_; } return result; } static sint8 nm_spi_read(uint32 addr, uint8 *buf, uint16 size) { uint8 cmd = CMD_DMA_EXT_READ; sint8 result; uint8 retry = SPI_RETRY_COUNT; #if defined USE_OLD_SPI_SW uint8 tmp[2]; uint8 single_byte_workaround = 0; #endif _RETRY_: /** Command **/ #if defined USE_OLD_SPI_SW if (size == 1) { //Workaround hardware problem with single byte transfers over SPI bus size = 2; single_byte_workaround = 1; } result = spi_cmd(cmd, addr, 0, size, 0); if (result != N_OK) { M2M_ERR("[nmi spi]: Failed cmd, read block (%08x)...\n", (unsigned int)addr); goto _FAIL_; } result = spi_cmd_rsp(cmd); if (result != N_OK) { M2M_ERR("[nmi spi]: Failed cmd response, read block (%08x)...\n", (unsigned int)addr); goto _FAIL_; } /** Data **/ if (single_byte_workaround) { result = spi_data_read(tmp, size, 0); buf[0] = tmp[0]; } else result = spi_data_read(buf, size, 0); if (result != N_OK) { M2M_ERR("[nmi spi]: Failed block data read...\n"); goto _FAIL_; } #else result = spi_cmd_complete(cmd, addr, buf, size, 0); if (result != N_OK) { M2M_ERR("[nmi spi]: Failed cmd, read block (%08x)...\n", addr); goto _FAIL_; } #endif _FAIL_: if (result != N_OK) { nm_bsp_sleep(1); spi_cmd(CMD_RESET, 0, 0, 0, 0); spi_cmd_rsp(CMD_RESET); M2M_ERR("Reset and retry %d %lx %d\n", retry, addr, size); nm_bsp_sleep(1); retry--; if (retry) goto _RETRY_; } return result; } /******************************************** Bus interfaces ********************************************/ static void spi_init_pkt_sz(void) { uint32 val32; /* Make sure SPI max. packet size fits the defined DATA_PKT_SZ. */ val32 = nm_spi_read_reg(SPI_BASE + 0x24); val32 &= ~(0x7 << 4); switch (DATA_PKT_SZ) { case 256: val32 |= (0 << 4); break; case 512: val32 |= (1 << 4); break; case 1024: val32 |= (2 << 4); break; case 2048: val32 |= (3 << 4); break; case 4096: val32 |= (4 << 4); break; case 8192: val32 |= (5 << 4); break; } nm_spi_write_reg(SPI_BASE + 0x24, val32); } sint8 nm_spi_reset(void) { spi_cmd(CMD_RESET, 0, 0, 0, 0); spi_cmd_rsp(CMD_RESET); return M2M_SUCCESS; } /* @fn nm_spi_init @brief Initialize the SPI @return M2M_SUCCESS in case of success and M2M_ERR_BUS_FAIL in case of failure @author M. Abdelmawla @date 11 July 2012 @version 1.0 */ sint8 nm_spi_init(void) { uint32 chipid; uint32 reg = 0; /** configure protocol **/ gu8Crc_off = 0; // TODO: We can remove the CRC trials if there is a definite way to reset // the SPI to it's initial value. if (!spi_read_reg(NMI_SPI_PROTOCOL_CONFIG, ®)) { /* Read failed. Try with CRC off. This might happen when module is removed but chip isn't reset*/ gu8Crc_off = 1; M2M_ERR("[nmi spi]: Failed internal read protocol with CRC on, retyring with CRC off...\n"); if (!spi_read_reg(NMI_SPI_PROTOCOL_CONFIG, ®)) { // Reaad failed with both CRC on and off, something went bad M2M_ERR( "[nmi spi]: Failed internal read protocol...\n"); return 0; } } if (gu8Crc_off == 0) { reg &= ~0xc; /* disable crc checking */ reg &= ~0x70; reg |= (0x5 << 4); if (!spi_write_reg(NMI_SPI_PROTOCOL_CONFIG, reg)) { M2M_ERR( "[nmi spi]: Failed internal write protocol reg...\n"); return 0; } gu8Crc_off = 1; } /** make sure can read back chip id correctly **/ if (!spi_read_reg(0x1000, &chipid)) { M2M_ERR("[nmi spi]: Fail cmd read chip id...\n"); return M2M_ERR_BUS_FAIL; } M2M_DBG("[nmi spi]: chipid (%08x)\n", (unsigned int)chipid); spi_init_pkt_sz(); return M2M_SUCCESS; } /* @fn nm_spi_init @brief DeInitialize the SPI @return M2M_SUCCESS in case of success and M2M_ERR_BUS_FAIL in case of failure @author Samer Sarhan @date 27 Feb 2015 @version 1.0 */ sint8 nm_spi_deinit(void) { gu8Crc_off = 0; return M2M_SUCCESS; } /* @fn nm_spi_read_reg @brief Read register @param [in] u32Addr Register address @return Register value @author M. Abdelmawla @date 11 July 2012 @version 1.0 */ uint32 nm_spi_read_reg(uint32 u32Addr) { uint32 u32Val; spi_read_reg(u32Addr, &u32Val); return u32Val; } /* @fn nm_spi_read_reg_with_ret @brief Read register with error code return @param [in] u32Addr Register address @param [out] pu32RetVal Pointer to u32 variable used to return the read value @return M2M_SUCCESS in case of success and M2M_ERR_BUS_FAIL in case of failure @author M. Abdelmawla @date 11 July 2012 @version 1.0 */ sint8 nm_spi_read_reg_with_ret(uint32 u32Addr, uint32* pu32RetVal) { sint8 s8Ret; s8Ret = spi_read_reg(u32Addr, pu32RetVal); if (N_OK == s8Ret) s8Ret = M2M_SUCCESS; else s8Ret = M2M_ERR_BUS_FAIL; return s8Ret; } /* @fn nm_spi_write_reg @brief write register @param [in] u32Addr Register address @param [in] u32Val Value to be written to the register @return M2M_SUCCESS in case of success and M2M_ERR_BUS_FAIL in case of failure @author M. Abdelmawla @date 11 July 2012 @version 1.0 */ sint8 nm_spi_write_reg(uint32 u32Addr, uint32 u32Val) { sint8 s8Ret; s8Ret = spi_write_reg(u32Addr, u32Val); if (N_OK == s8Ret) s8Ret = M2M_SUCCESS; else s8Ret = M2M_ERR_BUS_FAIL; return s8Ret; } /* @fn nm_spi_read_block @brief Read block of data @param [in] u32Addr Start address @param [out] puBuf Pointer to a buffer used to return the read data @param [in] u16Sz Number of bytes to read. The buffer size must be >= u16Sz @return M2M_SUCCESS in case of success and M2M_ERR_BUS_FAIL in case of failure @author M. Abdelmawla @date 11 July 2012 @version 1.0 */ sint8 nm_spi_read_block(uint32 u32Addr, uint8 *puBuf, uint16 u16Sz) { sint8 s8Ret; s8Ret = nm_spi_read(u32Addr, puBuf, u16Sz); if (N_OK == s8Ret) s8Ret = M2M_SUCCESS; else s8Ret = M2M_ERR_BUS_FAIL; return s8Ret; } /* @fn nm_spi_write_block @brief Write block of data @param [in] u32Addr Start address @param [in] puBuf Pointer to the buffer holding the data to be written @param [in] u16Sz Number of bytes to write. The buffer size must be >= u16Sz @return M2M_SUCCESS in case of success and M2M_ERR_BUS_FAIL in case of failure @author M. Abdelmawla @date 11 July 2012 @version 1.0 */ sint8 nm_spi_write_block(uint32 u32Addr, uint8 *puBuf, uint16 u16Sz) { sint8 s8Ret; s8Ret = nm_spi_write(u32Addr, puBuf, u16Sz); if (N_OK == s8Ret) s8Ret = M2M_SUCCESS; else s8Ret = M2M_ERR_BUS_FAIL; return s8Ret; } #endif