diff options
Diffstat (limited to 'libiot/hal')
| -rw-r--r-- | libiot/hal/mkfile | 3 | ||||
| -rw-r--r-- | libiot/hal/w25qxx.c | 288 | ||||
| -rw-r--r-- | libiot/hal/w25qxx.h | 88 |
3 files changed, 379 insertions, 0 deletions
diff --git a/libiot/hal/mkfile b/libiot/hal/mkfile new file mode 100644 index 0000000..73b16cb --- /dev/null +++ b/libiot/hal/mkfile @@ -0,0 +1,3 @@ +OFILES=\ + $OFILES\ + hal/w25qxx.$O\ diff --git a/libiot/hal/w25qxx.c b/libiot/hal/w25qxx.c new file mode 100644 index 0000000..a169617 --- /dev/null +++ b/libiot/hal/w25qxx.c @@ -0,0 +1,288 @@ +/* Copyright 2018 Canaan Inc. + * + * 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. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <devices.h> +#include "w25qxx.h" + +uintptr_t spi_adapter; +uintptr_t spi_stand; + +static enum w25qxx_status_t w25qxx_receive_data(uint8_t* cmd_buff, uint8_t cmd_len, uint8_t* rx_buff, uint32_t rx_len) +{ + spi_dev_transfer_sequential(spi_stand, (uint8_t *)cmd_buff, cmd_len, (uint8_t *)rx_buff, rx_len); + return W25QXX_OK; +} + +static enum w25qxx_status_t w25qxx_receive_data_enhanced(uint32_t* cmd_buff, uint8_t cmd_len, uint8_t* rx_buff, uint32_t rx_len) +{ + memcpy(rx_buff, cmd_buff, cmd_len); + io_read(spi_adapter, (uint8_t *)rx_buff, rx_len); + return W25QXX_OK; +} + +static enum w25qxx_status_t w25qxx_send_data(uintptr_t file, uint8_t* cmd_buff, uint8_t cmd_len, uint8_t* tx_buff, uint32_t tx_len) +{ + configASSERT(cmd_len); + uint8_t* tmp_buf = malloc(cmd_len + tx_len); + memcpy(tmp_buf, cmd_buff, cmd_len); + if (tx_len) + memcpy(tmp_buf + cmd_len, tx_buff, tx_len); + io_write(file, (uint8_t *)tmp_buf, cmd_len + tx_len); + free(tmp_buf); + return W25QXX_OK; +} + +static enum w25qxx_status_t w25qxx_write_enable(void) +{ + uint8_t cmd[1] = {WRITE_ENABLE}; + + w25qxx_send_data(spi_stand, cmd, 1, 0, 0); + return W25QXX_OK; +} + +static enum w25qxx_status_t w25qxx_read_status_reg1(uint8_t* reg_data) +{ + uint8_t cmd[1] = {READ_REG1}; + uint8_t data[1]; + + w25qxx_receive_data(cmd, 1, data, 1); + *reg_data = data[0]; + return W25QXX_OK; +} +static enum w25qxx_status_t w25qxx_read_status_reg2(uint8_t* reg_data) +{ + uint8_t cmd[1] = {READ_REG2}; + uint8_t data[1]; + + w25qxx_receive_data(cmd, 1, data, 1); + *reg_data = data[0]; + return W25QXX_OK; +} +static enum w25qxx_status_t w25qxx_write_status_reg(uint8_t reg1_data, uint8_t reg2_data) +{ + uint8_t cmd[3] = {WRITE_REG1, reg1_data, reg2_data}; + + w25qxx_write_enable(); + w25qxx_send_data(spi_stand, cmd, 3, 0, 0); + return W25QXX_OK; +} + +static enum w25qxx_status_t w25qxx_enable_quad_mode(void) +{ + uint8_t reg_data; + + w25qxx_read_status_reg2(®_data); + if (!(reg_data & REG2_QUAL_MASK)) + { + reg_data |= REG2_QUAL_MASK; + w25qxx_write_status_reg(0x00, reg_data); + } + return W25QXX_OK; +} + +static enum w25qxx_status_t w25qxx_is_busy(void) +{ + uint8_t status; + + w25qxx_read_status_reg1(&status); + if (status & REG1_BUSY_MASK) + return W25QXX_BUSY; + return W25QXX_OK; +} + +enum w25qxx_status_t w25qxx_sector_erase(uint32_t addr) +{ + uint8_t cmd[4] = {SECTOR_ERASE}; + + cmd[1] = (uint8_t)(addr >> 16); + cmd[2] = (uint8_t)(addr >> 8); + cmd[3] = (uint8_t)(addr); + w25qxx_write_enable(); + w25qxx_send_data(spi_stand, cmd, 4, 0, 0); + return W25QXX_OK; +} + +enum w25qxx_status_t w25qxx_read_id(uint8_t *manuf_id, uint8_t *device_id) +{ + uint8_t cmd[4] = {READ_ID, 0x00, 0x00, 0x00}; + uint8_t data[2] = {0}; + + w25qxx_receive_data(cmd, 4, data, 2); + *manuf_id = data[0]; + *device_id = data[1]; + return W25QXX_OK; +} + +static enum w25qxx_status_t w25qxx_read_data_less_64kb(uint32_t addr, uint8_t* data_buf, uint32_t length) +{ + uint32_t cmd[2]; + + switch (WORK_TRANS_MODE) + { + case SPI_FF_DUAL: + *(((uint8_t*)cmd) + 0) = FAST_READ_DUAL_OUTPUT; + *(((uint8_t*)cmd) + 1) = (uint8_t)(addr >> 0); + *(((uint8_t*)cmd) + 2) = (uint8_t)(addr >> 8); + *(((uint8_t*)cmd) + 3) = (uint8_t)(addr >> 16); + w25qxx_receive_data_enhanced(cmd, 4, data_buf, length); + break; + case SPI_FF_QUAD: + *(((uint8_t*)cmd) + 0) = FAST_READ_QUAL_OUTPUT; + *(((uint8_t*)cmd) + 1) = (uint8_t)(addr >> 0); + *(((uint8_t*)cmd) + 2) = (uint8_t)(addr >> 8); + *(((uint8_t*)cmd) + 3) = (uint8_t)(addr >> 16); + w25qxx_receive_data_enhanced(cmd, 4, data_buf, length); + break; + case SPI_FF_STANDARD: + default: + *(((uint8_t*)cmd) + 0) = READ_DATA; + *(((uint8_t*)cmd) + 1) = (uint8_t)(addr >> 16); + *(((uint8_t*)cmd) + 2) = (uint8_t)(addr >> 8); + *(((uint8_t*)cmd) + 3) = (uint8_t)(addr >> 0); + w25qxx_receive_data((uint8_t*)cmd, 4, data_buf, length); + break; + } + return W25QXX_OK; +} + +enum w25qxx_status_t w25qxx_read_data(uint32_t addr, uint8_t* data_buf, uint32_t length) +{ + uint32_t len; + + while (length) + { + len = length >= 0x010000 ? 0x010000 : length; + w25qxx_read_data_less_64kb(addr, data_buf, len); + addr += len; + data_buf += len; + length -= len; + } + return W25QXX_OK; +} + +static enum w25qxx_status_t w25qxx_page_program(uint32_t addr, uint8_t* data_buf, uint32_t length) +{ + uint32_t cmd[2]; + w25qxx_write_enable(); + if (WORK_TRANS_MODE == SPI_FF_QUAD) + { + *(((uint8_t*)cmd) + 0) = QUAD_PAGE_PROGRAM; + *(((uint8_t*)cmd) + 1) = (uint8_t)(addr >> 0); + *(((uint8_t*)cmd) + 2) = (uint8_t)(addr >> 8); + *(((uint8_t*)cmd) + 3) = (uint8_t)(addr >> 16); + w25qxx_send_data(spi_adapter, (uint8_t*)cmd, 4, data_buf, length); + } + else + { + *(((uint8_t*)cmd) + 0) = PAGE_PROGRAM; + *(((uint8_t*)cmd) + 1) = (uint8_t)(addr >> 16); + *(((uint8_t*)cmd) + 2) = (uint8_t)(addr >> 8); + *(((uint8_t*)cmd) + 3) = (uint8_t)(addr >> 0); + w25qxx_send_data(spi_stand, (uint8_t*)cmd, 4, data_buf, length); + } + while (w25qxx_is_busy() == W25QXX_BUSY) + ; + return W25QXX_OK; +} + +static enum w25qxx_status_t w25qxx_sector_program(uint32_t addr, uint8_t* data_buf) +{ + uint8_t index; + + for (index = 0; index < w25qxx_FLASH_PAGE_NUM_PER_SECTOR; index++) + { + w25qxx_page_program(addr, data_buf, w25qxx_FLASH_PAGE_SIZE); + addr += w25qxx_FLASH_PAGE_SIZE; + data_buf += w25qxx_FLASH_PAGE_SIZE; + } + return W25QXX_OK; +} + +enum w25qxx_status_t w25qxx_write_data(uint32_t addr, uint8_t* data_buf, uint32_t length) +{ + uint32_t sector_addr, sector_offset, sector_remain, write_len, index; + uint8_t swap_buf[w25qxx_FLASH_SECTOR_SIZE]; + uint8_t *pread, *pwrite; + + while (length) + { + sector_addr = addr & (~(w25qxx_FLASH_SECTOR_SIZE - 1)); + sector_offset = addr & (w25qxx_FLASH_SECTOR_SIZE - 1); + sector_remain = w25qxx_FLASH_SECTOR_SIZE - sector_offset; + write_len = length < sector_remain ? length : sector_remain; + w25qxx_read_data(sector_addr, swap_buf, w25qxx_FLASH_SECTOR_SIZE); + pread = swap_buf + sector_offset; + pwrite = data_buf; + for (index = 0; index < write_len; index++) + { + if ((*pwrite) != ((*pwrite) & (*pread))) + { + w25qxx_sector_erase(sector_addr); + while (w25qxx_is_busy() == W25QXX_BUSY) + ; + break; + } + pwrite++; + pread++; + } + if (write_len == w25qxx_FLASH_SECTOR_SIZE) + w25qxx_sector_program(sector_addr, data_buf); + else + { + pread = swap_buf + sector_offset; + pwrite = data_buf; + for (index = 0; index < write_len; index++) + *pread++ = *pwrite++; + w25qxx_sector_program(sector_addr, swap_buf); + } + length -= write_len; + addr += write_len; + data_buf += write_len; + } + return W25QXX_OK; +} + +enum w25qxx_status_t w25qxx_init(uintptr_t spi_in) +{ + uint8_t manuf_id, device_id; + spi_stand = spi_get_device(spi_in, SPI_MODE_0, SPI_FF_STANDARD, CHIP_SELECT, FRAME_LENGTH); + spi_dev_set_clock_rate(spi_stand, 800000); + w25qxx_read_id(&manuf_id, &device_id); + if ((manuf_id != 0xEF && manuf_id != 0xC8) || (device_id != 0x17 && device_id != 0x16)) + { + printf("manuf_id:0x%02x, device_id:0x%02x\n", manuf_id, device_id); + } + printf("manuf_id:0x%02x, device_id:0x%02x\n", manuf_id, device_id); + switch (WORK_TRANS_MODE) + { + case SPI_FF_DUAL: + spi_adapter = spi_get_device(spi_in, SPI_MODE_0, SPI_FF_DUAL, CHIP_SELECT, FRAME_LENGTH); + spi_dev_config_non_standard(spi_adapter, INSTRUCTION_LENGTH, ADDRESS_LENGTH, WAIT_CYCLE, SPI_AITM_STANDARD); + break; + case SPI_FF_QUAD: + spi_adapter = spi_get_device(spi_in, SPI_MODE_0, SPI_FF_QUAD, CHIP_SELECT, FRAME_LENGTH); + spi_dev_config_non_standard(spi_adapter, INSTRUCTION_LENGTH, ADDRESS_LENGTH, WAIT_CYCLE, SPI_AITM_STANDARD); + w25qxx_enable_quad_mode(); + break; + case SPI_FF_STANDARD: + default: + spi_adapter = spi_stand; + break; + } + return W25QXX_OK; +} + diff --git a/libiot/hal/w25qxx.h b/libiot/hal/w25qxx.h new file mode 100644 index 0000000..5ae32ca --- /dev/null +++ b/libiot/hal/w25qxx.h @@ -0,0 +1,88 @@ +/* Copyright 2018 Canaan Inc. + * + * 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 _W25QXX_H +#define _W25QXX_H +#include <stdint.h> + +/* clang-format off */ +#define WORK_TRANS_MODE SPI_FF_STANDARD +/* #define WORK_TRANS_MODE SPI_FF_DUAL */ +/* #define WORK_TRANS_MODE SPI_FF_QUAD */ + +#define CHIP_SELECT 1 +#define WAIT_CYCLE 8 +#define FRAME_LENGTH 8 +#define INSTRUCTION_LENGTH 8 +#define ADDRESS_LENGTH 24 + +#define SPI_SLAVE_SELECT (0x01) + +#define w25qxx_FLASH_PAGE_SIZE 256 +#define w25qxx_FLASH_SECTOR_SIZE 4096 +#define w25qxx_FLASH_PAGE_NUM_PER_SECTOR 16 +#define w25qxx_FLASH_CHIP_SIZE (16777216 UL) + +#define WRITE_ENABLE 0x06 +#define WRITE_DISABLE 0x04 +#define READ_REG1 0x05 +#define READ_REG2 0x35 +#define READ_REG3 0x15 +#define WRITE_REG1 0x01 +#define WRITE_REG2 0x31 +#define WRITE_REG3 0x11 +#define READ_DATA 0x03 +#define FAST_READ 0x0B +#define FAST_READ_DUAL_OUTPUT 0x3B +#define FAST_READ_QUAL_OUTPUT 0x6B +#define FAST_READ_DUAL_IO 0xBB +#define FAST_READ_QUAL_IO 0xEB +#define DUAL_READ_RESET 0xFFFF +#define QUAL_READ_RESET 0xFF +#define PAGE_PROGRAM 0x02 +#define QUAD_PAGE_PROGRAM 0x32 +#define SECTOR_ERASE 0x20 +#define BLOCK_32K_ERASE 0x52 +#define BLOCK_64K_ERASE 0xD8 +#define CHIP_ERASE 0x60 +#define READ_ID 0x90 +#define ENABLE_QPI 0x38 +#define EXIT_QPI 0xFF +#define ENABLE_RESET 0x66 +#define RESET_DEVICE 0x99 + +#define REG1_BUSY_MASK 0x01 +#define REG2_QUAL_MASK 0x02 + +#define LETOBE(x) ((x >> 24) | ((x & 0x00FF0000) >> 8) | ((x & 0x0000FF00) << 8) | (x << 24)) +/* clang-format on */ + +/** + * @brief w25qxx operating status enumerate + */ +enum w25qxx_status_t +{ + W25QXX_OK = 0, + W25QXX_BUSY, + W25QXX_ERROR, +}; + +enum w25qxx_status_t w25qxx_init(uintptr_t spi_in); +enum w25qxx_status_t w25qxx_write_data(uint32_t addr, uint8_t* data_buf, uint32_t length); +enum w25qxx_status_t w25qxx_read_data(uint32_t addr, uint8_t* data_buf, uint32_t length); + +enum w25qxx_status_t w25qxx_sector_erase(uint32_t addr); + +#endif + |
