zephyr/drivers/flash/spi_flash_w25qxxdv.c
Leandro Pereira 732424f065 drivers, net: Clean up semaphore initialization
Change the common "init with 0" + "give" idiom to "init with 1".  This
won't change the behavior or performance, but should decrease the size
ever so slightly.

This change has been performed mechanically with the following
Coccinelle script:

    @@
    expression SEM;
    expression LIMIT;
    expression TIMEOUT;
    @@

    - k_sem_init(SEM, 0, LIMIT);
    - k_sem_give(SEM);
    + k_sem_init(SEM, 1, LIMIT);

Signed-off-by: Leandro Pereira <leandro.pereira@intel.com>
2017-07-27 15:23:07 -04:00

377 lines
8.2 KiB
C

/*
* Copyright (c) 2016 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <errno.h>
#include <flash.h>
#include <spi.h>
#include <init.h>
#include <string.h>
#include "spi_flash_w25qxxdv_defs.h"
#include "spi_flash_w25qxxdv.h"
static inline int spi_flash_wb_id(struct device *dev)
{
struct spi_flash_data *const driver_data = dev->driver_data;
u8_t buf[W25QXXDV_LEN_CMD_AND_ID];
u32_t temp_data;
buf[0] = W25QXXDV_CMD_RDID;
if (spi_transceive(driver_data->spi, buf, W25QXXDV_LEN_CMD_AND_ID,
buf, W25QXXDV_LEN_CMD_AND_ID) != 0) {
return -EIO;
}
temp_data = ((u32_t) buf[1]) << 16;
temp_data |= ((u32_t) buf[2]) << 8;
temp_data |= (u32_t) buf[3];
if (temp_data != W25QXXDV_RDID_VALUE) {
return -ENODEV;
}
return 0;
}
static int spi_flash_wb_config(struct device *dev)
{
struct spi_flash_data *const driver_data = dev->driver_data;
struct spi_config config;
config.max_sys_freq = CONFIG_SPI_FLASH_W25QXXDV_SPI_FREQ_0;
config.config = SPI_WORD(8);
if (spi_slave_select(driver_data->spi,
CONFIG_SPI_FLASH_W25QXXDV_SPI_SLAVE) !=
0) {
return -EIO;
}
if (spi_configure(driver_data->spi, &config) != 0) {
return -EIO;
}
return spi_flash_wb_id(dev);
}
static int spi_flash_wb_reg_read(struct device *dev, u8_t *data)
{
struct spi_flash_data *const driver_data = dev->driver_data;
u8_t buf[2];
if (spi_transceive(driver_data->spi, data, 2, buf, 2) != 0) {
return -EIO;
}
memcpy(data, buf, 2);
return 0;
}
static inline void wait_for_flash_idle(struct device *dev)
{
u8_t buf[2];
buf[0] = W25QXXDV_CMD_RDSR;
spi_flash_wb_reg_read(dev, buf);
while (buf[1] & W25QXXDV_WIP_BIT) {
buf[0] = W25QXXDV_CMD_RDSR;
spi_flash_wb_reg_read(dev, buf);
}
}
static int spi_flash_wb_reg_write(struct device *dev, u8_t *data)
{
struct spi_flash_data *const driver_data = dev->driver_data;
u8_t buf;
wait_for_flash_idle(dev);
if (spi_transceive(driver_data->spi, data, 1,
&buf /*dummy */, 1) != 0) {
return -EIO;
}
return 0;
}
static int spi_flash_wb_read(struct device *dev, off_t offset, void *data,
size_t len)
{
struct spi_flash_data *const driver_data = dev->driver_data;
u8_t *buf = driver_data->buf;
if (len > CONFIG_SPI_FLASH_W25QXXDV_MAX_DATA_LEN || offset < 0) {
return -ENODEV;
}
k_sem_take(&driver_data->sem, K_FOREVER);
if (spi_flash_wb_config(dev) != 0) {
k_sem_give(&driver_data->sem);
return -EIO;
}
wait_for_flash_idle(dev);
buf[0] = W25QXXDV_CMD_READ;
buf[1] = (u8_t) (offset >> 16);
buf[2] = (u8_t) (offset >> 8);
buf[3] = (u8_t) offset;
memset(buf + W25QXXDV_LEN_CMD_ADDRESS, 0, len);
if (spi_transceive(driver_data->spi, buf, len + W25QXXDV_LEN_CMD_ADDRESS,
buf, len + W25QXXDV_LEN_CMD_ADDRESS) != 0) {
k_sem_give(&driver_data->sem);
return -EIO;
}
memcpy(data, buf + W25QXXDV_LEN_CMD_ADDRESS, len);
k_sem_give(&driver_data->sem);
return 0;
}
static int spi_flash_wb_write(struct device *dev, off_t offset,
const void *data, size_t len)
{
struct spi_flash_data *const driver_data = dev->driver_data;
u8_t *buf = driver_data->buf;
if (len > CONFIG_SPI_FLASH_W25QXXDV_MAX_DATA_LEN || offset < 0) {
return -ENOTSUP;
}
k_sem_take(&driver_data->sem, K_FOREVER);
if (spi_flash_wb_config(dev) != 0) {
k_sem_give(&driver_data->sem);
return -EIO;
}
wait_for_flash_idle(dev);
buf[0] = W25QXXDV_CMD_RDSR;
spi_flash_wb_reg_read(dev, buf);
if (!(buf[1] & W25QXXDV_WEL_BIT)) {
k_sem_give(&driver_data->sem);
return -EIO;
}
wait_for_flash_idle(dev);
buf[0] = W25QXXDV_CMD_PP;
buf[1] = (u8_t) (offset >> 16);
buf[2] = (u8_t) (offset >> 8);
buf[3] = (u8_t) offset;
memcpy(buf + W25QXXDV_LEN_CMD_ADDRESS, data, len);
/* Assume write protection has been disabled. Note that w25qxxdv
* flash automatically turns on write protection at the completion
* of each write or erase transaction.
*/
if (spi_write(driver_data->spi, buf, len + W25QXXDV_LEN_CMD_ADDRESS) != 0) {
k_sem_give(&driver_data->sem);
return -EIO;
}
k_sem_give(&driver_data->sem);
return 0;
}
static int spi_flash_wb_write_protection_set(struct device *dev, bool enable)
{
struct spi_flash_data *const driver_data = dev->driver_data;
u8_t buf = 0;
k_sem_take(&driver_data->sem, K_FOREVER);
if (spi_flash_wb_config(dev) != 0) {
k_sem_give(&driver_data->sem);
return -EIO;
}
wait_for_flash_idle(dev);
if (enable) {
buf = W25QXXDV_CMD_WRDI;
} else {
buf = W25QXXDV_CMD_WREN;
}
if (spi_flash_wb_reg_write(dev, &buf) != 0) {
k_sem_give(&driver_data->sem);
return -EIO;
}
k_sem_give(&driver_data->sem);
return 0;
}
static inline int spi_flash_wb_erase_internal(struct device *dev,
off_t offset, size_t size)
{
struct spi_flash_data *const driver_data = dev->driver_data;
u8_t buf[W25QXXDV_LEN_CMD_ADDRESS];
u8_t erase_opcode;
u32_t len;
if (offset < 0) {
return -ENOTSUP;
}
wait_for_flash_idle(dev);
/* write enable */
buf[0] = W25QXXDV_CMD_WREN;
spi_flash_wb_reg_write(dev, buf);
wait_for_flash_idle(dev);
switch (size) {
case W25QXXDV_SECTOR_SIZE:
erase_opcode = W25QXXDV_CMD_SE;
len = W25QXXDV_LEN_CMD_ADDRESS;
break;
case W25QXXDV_BLOCK32K_SIZE:
erase_opcode = W25QXXDV_CMD_BE32K;
len = W25QXXDV_LEN_CMD_ADDRESS;
break;
case W25QXXDV_BLOCK_SIZE:
erase_opcode = W25QXXDV_CMD_BE;
len = W25QXXDV_LEN_CMD_ADDRESS;
break;
case CONFIG_SPI_FLASH_W25QXXDV_FLASH_SIZE:
erase_opcode = W25QXXDV_CMD_CE;
len = 1;
break;
default:
return -EIO;
}
buf[0] = erase_opcode;
buf[1] = (u8_t) (offset >> 16);
buf[2] = (u8_t) (offset >> 8);
buf[3] = (u8_t) offset;
/* Assume write protection has been disabled. Note that w25qxxdv
* flash automatically turns on write protection at the completion
* of each write or erase transaction.
*/
return spi_write(driver_data->spi, buf, len);
}
static int spi_flash_wb_erase(struct device *dev, off_t offset, size_t size)
{
struct spi_flash_data *const driver_data = dev->driver_data;
u8_t *buf = driver_data->buf;
int ret = 0;
u32_t new_offset = offset;
u32_t size_remaining = size;
if ((offset < 0) || ((offset & W25QXXDV_SECTOR_MASK) != 0) ||
((size + offset) > CONFIG_SPI_FLASH_W25QXXDV_FLASH_SIZE) ||
((size & W25QXXDV_SECTOR_MASK) != 0)) {
return -ENODEV;
}
k_sem_take(&driver_data->sem, K_FOREVER);
if (spi_flash_wb_config(dev) != 0) {
k_sem_give(&driver_data->sem);
return -EIO;
}
buf[0] = W25QXXDV_CMD_RDSR;
spi_flash_wb_reg_read(dev, buf);
if (!(buf[1] & W25QXXDV_WEL_BIT)) {
k_sem_give(&driver_data->sem);
return -EIO;
}
while ((size_remaining >= W25QXXDV_SECTOR_SIZE) && (ret == 0)) {
if (size_remaining == CONFIG_SPI_FLASH_W25QXXDV_FLASH_SIZE) {
ret = spi_flash_wb_erase_internal(dev, offset, size);
break;
}
if (size_remaining >= W25QXXDV_BLOCK_SIZE) {
ret = spi_flash_wb_erase_internal(dev, new_offset,
W25QXXDV_BLOCK_SIZE);
new_offset += W25QXXDV_BLOCK_SIZE;
size_remaining -= W25QXXDV_BLOCK_SIZE;
continue;
}
if (size_remaining >= W25QXXDV_BLOCK32K_SIZE) {
ret = spi_flash_wb_erase_internal(dev, new_offset,
W25QXXDV_BLOCK32K_SIZE);
new_offset += W25QXXDV_BLOCK32K_SIZE;
size_remaining -= W25QXXDV_BLOCK32K_SIZE;
continue;
}
if (size_remaining >= W25QXXDV_SECTOR_SIZE) {
ret = spi_flash_wb_erase_internal(dev, new_offset,
W25QXXDV_SECTOR_SIZE);
new_offset += W25QXXDV_SECTOR_SIZE;
size_remaining -= W25QXXDV_SECTOR_SIZE;
continue;
}
}
k_sem_give(&driver_data->sem);
return ret;
}
static const struct flash_driver_api spi_flash_api = {
.read = spi_flash_wb_read,
.write = spi_flash_wb_write,
.erase = spi_flash_wb_erase,
.write_protection = spi_flash_wb_write_protection_set,
};
static int spi_flash_init(struct device *dev)
{
struct device *spi_dev;
struct spi_flash_data *data = dev->driver_data;
int ret;
spi_dev = device_get_binding(CONFIG_SPI_FLASH_W25QXXDV_SPI_NAME);
if (!spi_dev) {
return -EIO;
}
data->spi = spi_dev;
k_sem_init(&data->sem, 1, UINT_MAX);
ret = spi_flash_wb_config(dev);
if (!ret) {
dev->driver_api = &spi_flash_api;
}
return ret;
}
static struct spi_flash_data spi_flash_memory_data;
DEVICE_INIT(spi_flash_memory, CONFIG_SPI_FLASH_W25QXXDV_DRV_NAME, spi_flash_init,
&spi_flash_memory_data, NULL, POST_KERNEL,
CONFIG_SPI_FLASH_W25QXXDV_INIT_PRIORITY);