/** * * \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 /* Don't force USE_OLD_SPI_SW, instead we will pass it from Zephyr side #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 ********************************************/ static const uint8 crc7_syndrome_table[256] = { 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) { return crc7_syndrome_table[(crc << 1) ^ data]; } 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) { 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= 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= 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 #if defined USE_OLD_SPI_SW 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; } #endif 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