diff options
Diffstat (limited to 'c/src/libchip/i2c/spi-memdrv.c')
-rw-r--r-- | c/src/libchip/i2c/spi-memdrv.c | 445 |
1 files changed, 445 insertions, 0 deletions
diff --git a/c/src/libchip/i2c/spi-memdrv.c b/c/src/libchip/i2c/spi-memdrv.c new file mode 100644 index 0000000000..106a02cacc --- /dev/null +++ b/c/src/libchip/i2c/spi-memdrv.c @@ -0,0 +1,445 @@ +/*===============================================================*\ +| Project: SPI driver for spi memory devices | ++-----------------------------------------------------------------+ +| Copyright (c) 2008 | +| Embedded Brains GmbH | +| Obere Lagerstr. 30 | +| D-82178 Puchheim | +| Germany | +| rtems@embedded-brains.de | ++-----------------------------------------------------------------+ +| The license and distribution terms for this file may be | +| found in the file LICENSE in this distribution or at | +| | +| http://www.rtems.com/license/LICENSE. | +| | ++-----------------------------------------------------------------+ +\*===============================================================*/ +/* + * FIXME: currently, this driver only supports read/write accesses + * erase accesses are to be completed + */ + + +#include <rtems.h> +#include <rtems/libi2c.h> + +#include <libchip/spi-memdrv.h> +#include <rtems/libio.h> + +#define SPI_MEM_CMD_WREN 0x06 +#define SPI_MEM_CMD_WRDIS 0x04 +#define SPI_MEM_CMD_RDID 0x9F +#define SPI_MEM_CMD_RDSR 0x05 +#define SPI_MEM_CMD_WRSR 0x01 +#define SPI_MEM_CMD_READ 0x03 +#define SPI_MEM_CMD_PP 0x02 /* page program */ +#define SPI_MEM_CMD_SE 0xD8 /* sector erase */ +#define SPI_MEM_CMD_BE 0xC7 /* bulk erase */ +#define SPI_MEM_CMD_DP 0xB9 /* deep power down */ +#define SPI_MEM_CMD_RES 0xAB /* release from deep power down */ + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +rtems_status_code spi_memdrv_minor2param_ptr +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| translate given minor device number to param pointer | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + rtems_device_minor_number minor, /* minor number of device */ + spi_memdrv_param_t **param_ptr /* ptr to param ptr */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| o = ok or error code | +\*=========================================================================*/ +{ + rtems_status_code rc = RTEMS_SUCCESSFUL; + spi_memdrv_t *drv_ptr; + + if (rc == RTEMS_SUCCESSFUL) { + rc = -rtems_libi2c_ioctl(minor, + RTEMS_LIBI2C_IOCTL_GET_DRV_T, + &drv_ptr); + } + if ((rc == RTEMS_SUCCESSFUL) && + (drv_ptr->libi2c_drv_entry.size != sizeof(spi_memdrv_t))) { + rc = RTEMS_INVALID_SIZE; + } + if (rc == RTEMS_SUCCESSFUL) { + *param_ptr = &(drv_ptr->spi_memdrv_param); + } + return rc; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +rtems_status_code spi_memdrv_wait_ms +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| wait a certain interval given in ms | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + int ms /* time to wait in milliseconds */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| o = ok or error code | +\*=========================================================================*/ +{ + rtems_status_code rc = RTEMS_SUCCESSFUL; + rtems_interval ticks_per_second; + + rc = rtems_clock_get(RTEMS_CLOCK_GET_TICKS_PER_SECOND,&ticks_per_second); + if (rc == RTEMS_SUCCESSFUL) { + rc = rtems_task_wake_after(ticks_per_second * ms / 1000); + } + return rc; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +rtems_status_code spi_memdrv_write +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| write a block of data to flash | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + rtems_device_major_number major, /* major device number */ + rtems_device_minor_number minor, /* minor device number */ + void *arg /* ptr to write argument struct */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| o = ok or error code | +\*=========================================================================*/ +{ + rtems_status_code rc = RTEMS_SUCCESSFUL; + rtems_libio_rw_args_t *rwargs = arg; + unsigned off = rwargs->offset; + int cnt = rwargs->count; + unsigned char *buf = (unsigned char *)rwargs->buffer; + int bytes_sent = 0; + int curr_cnt; + unsigned char cmdbuf[4]; + int ret_cnt = 0; + int cmd_size; + spi_memdrv_param_t *mem_param_ptr; + rtems_libi2c_tfr_mode_t tfr_mode = { + baudrate: 20000000, /* maximum bits per second */ + bits_per_char: 8, /* how many bits per byte/word/longword? */ + lsb_first: FALSE, /* FALSE: send MSB first */ + clock_inv: FALSE, /* FALSE: non-inverted clock (high active) */ + clock_phs: FALSE /* FALSE: clock starts in middle of data tfr */ + } ; + + /* + * get mem parameters + */ + if (rc == RTEMS_SUCCESSFUL) { + rc = spi_memdrv_minor2param_ptr(minor,&mem_param_ptr); + } + /* + * check arguments + */ + if (rc == RTEMS_SUCCESSFUL) { + if ((cnt <= 0) || + (cnt > mem_param_ptr->mem_size) || + (off > (mem_param_ptr->mem_size-cnt))) { + rc = RTEMS_INVALID_SIZE; + } + else if (buf == NULL) { + rc = RTEMS_INVALID_ADDRESS; + } + } + while ((rc == RTEMS_SUCCESSFUL) && + (cnt > bytes_sent)) { + curr_cnt = cnt; + if ((mem_param_ptr->page_size > 0) && + (off / mem_param_ptr->page_size) != + ((off+curr_cnt+1) / mem_param_ptr->page_size)) { + curr_cnt = mem_param_ptr->page_size - (off % mem_param_ptr->page_size); + } + /* + * select device, set transfer mode, address device + */ + if (rc == RTEMS_SUCCESSFUL) { + rc = rtems_libi2c_send_start(minor); + } + /* + * set transfer mode + */ + if (rc == RTEMS_SUCCESSFUL) { + tfr_mode.baudrate = mem_param_ptr->baudrate; + rc = -rtems_libi2c_ioctl(minor, + RTEMS_LIBI2C_IOCTL_SET_TFRMODE, + &tfr_mode); + } + + /* + * address device + */ + if (rc == RTEMS_SUCCESSFUL) { + rc = rtems_libi2c_send_addr(minor,TRUE); + } + + /* + * send write_enable command + */ + if (rc == RTEMS_SUCCESSFUL) { + cmdbuf[0] = SPI_MEM_CMD_WREN; + ret_cnt = rtems_libi2c_write_bytes(minor,cmdbuf,1); + if (ret_cnt < 0) { + rc = -ret_cnt; + } + } + /* + * terminate transfer + */ + if (rc == RTEMS_SUCCESSFUL) { + rc = rtems_libi2c_send_stop(minor); + } + /* + * select device, set transfer mode + */ + if (rc == RTEMS_SUCCESSFUL) { + rc = rtems_libi2c_send_start(minor); + } + + /* + * address device + */ + if (rc == RTEMS_SUCCESSFUL) { + rc = rtems_libi2c_send_addr(minor,TRUE); + } + + /* + * set transfer mode + */ + if (rc == RTEMS_SUCCESSFUL) { + rc = -rtems_libi2c_ioctl(minor, + RTEMS_LIBI2C_IOCTL_SET_TFRMODE, + &tfr_mode); + } + /* + * send "page program" command and address + */ + if (rc == RTEMS_SUCCESSFUL) { + cmdbuf[0] = SPI_MEM_CMD_PP; + if (mem_param_ptr->mem_size > 256*256) { + cmdbuf[1] = (off >> 16) & 0xff; + cmdbuf[2] = (off >> 8) & 0xff; + cmdbuf[3] = (off >> 0) & 0xff; + cmd_size = 4; + } + else if (mem_param_ptr->mem_size > 256) { + cmdbuf[1] = (off >> 8) & 0xff; + cmdbuf[2] = (off >> 0) & 0xff; + cmd_size = 3; + } + else { + cmdbuf[1] = (off >> 0) & 0xff; + cmd_size = 1; + } + + ret_cnt = rtems_libi2c_write_bytes(minor,cmdbuf,cmd_size); + if (ret_cnt < 0) { + rc = -ret_cnt; + } + } + /* + * send write data + */ + if (rc == RTEMS_SUCCESSFUL) { + ret_cnt = rtems_libi2c_write_bytes(minor,buf,curr_cnt); + if (ret_cnt < 0) { + rc = -ret_cnt; + } + } + /* + * terminate transfer + */ + if (rc == RTEMS_SUCCESSFUL) { + rc = rtems_libi2c_send_stop(minor); + } + /* + * wait proper time for data to store: 5ms + * FIXME: select proper interval or poll, until device is finished + */ + if (rc == RTEMS_SUCCESSFUL) { + rc = spi_memdrv_wait_ms(5); + } + /* + * adjust bytecount to be sent and pointers + */ + bytes_sent += curr_cnt; + off += curr_cnt; + buf += curr_cnt; + } + rwargs->bytes_moved = bytes_sent; + return rc; +} + +/*=========================================================================*\ +| Function: | +\*-------------------------------------------------------------------------*/ +rtems_status_code spi_memdrv_read +( +/*-------------------------------------------------------------------------*\ +| Purpose: | +| read a block of data from flash | ++---------------------------------------------------------------------------+ +| Input Parameters: | +\*-------------------------------------------------------------------------*/ + rtems_device_major_number major, /* major device number */ + rtems_device_minor_number minor, /* minor device number */ + void *arg /* ptr to read argument struct */ +) +/*-------------------------------------------------------------------------*\ +| Return Value: | +| o = ok or error code | +\*=========================================================================*/ +{ + rtems_status_code rc = RTEMS_SUCCESSFUL; + rtems_libio_rw_args_t *rwargs = arg; + unsigned off = rwargs->offset; + int cnt = rwargs->count; + unsigned char *buf = (unsigned char *)rwargs->buffer; + unsigned char cmdbuf[4]; + int ret_cnt = 0; + int cmd_size; + spi_memdrv_param_t *mem_param_ptr; + rtems_libi2c_tfr_mode_t tfr_mode = { + baudrate: 20000000, /* maximum bits per second */ + bits_per_char: 8, /* how many bits per byte/word/longword? */ + lsb_first: FALSE, /* FALSE: send MSB first */ + clock_inv: FALSE, /* FALSE: non-inverted clock (high active) */ + clock_phs: FALSE /* FALSE: clock starts in middle of data tfr */ + } ; + + /* + * get mem parameters + */ + if (rc == RTEMS_SUCCESSFUL) { + rc = spi_memdrv_minor2param_ptr(minor,&mem_param_ptr); + } + /* + * check arguments + */ + if (rc == RTEMS_SUCCESSFUL) { + if ((cnt <= 0) || + (cnt > mem_param_ptr->mem_size) || + (off > (mem_param_ptr->mem_size-cnt))) { + rc = RTEMS_INVALID_SIZE; + } + else if (buf == NULL) { + rc = RTEMS_INVALID_ADDRESS; + } + } + /* + * select device, set transfer mode, address device + */ + if (rc == RTEMS_SUCCESSFUL) { + rc = rtems_libi2c_send_start(minor); + } + /* + * set transfer mode + */ + if (rc == RTEMS_SUCCESSFUL) { + tfr_mode.baudrate = mem_param_ptr->baudrate; + rc = -rtems_libi2c_ioctl(minor, + RTEMS_LIBI2C_IOCTL_SET_TFRMODE, + &tfr_mode); + } + /* + * address device + */ + if (rc == RTEMS_SUCCESSFUL) { + rc = rtems_libi2c_send_addr(minor,TRUE); + } + + if (off >= mem_param_ptr->mem_size) { + /* + * HACK: beyond size of memory array? then read status register instead + */ + /* + * send read status register command + */ + if (rc == RTEMS_SUCCESSFUL) { + cmdbuf[0] = SPI_MEM_CMD_RDSR; + ret_cnt = rtems_libi2c_write_bytes(minor,cmdbuf,1); + if (ret_cnt < 0) { + rc = -ret_cnt; + } + } + } + else { + /* + * send read command and address + */ + if (rc == RTEMS_SUCCESSFUL) { + cmdbuf[0] = SPI_MEM_CMD_READ; + if (mem_param_ptr->mem_size > 256*256) { + cmdbuf[1] = (off >> 16) & 0xff; + cmdbuf[2] = (off >> 8) & 0xff; + cmdbuf[3] = (off >> 0) & 0xff; + cmd_size = 4; + } + else if (mem_param_ptr->mem_size > 256) { + cmdbuf[1] = (off >> 8) & 0xff; + cmdbuf[2] = (off >> 0) & 0xff; + cmd_size = 3; + } + else { + cmdbuf[1] = (off >> 0) & 0xff; + cmd_size = 1; + } + ret_cnt = rtems_libi2c_write_bytes(minor,cmdbuf,cmd_size); + if (ret_cnt < 0) { + rc = -ret_cnt; + } + } + } + /* + * fetch read data + */ + if (rc == RTEMS_SUCCESSFUL) { + ret_cnt = rtems_libi2c_read_bytes (minor,buf,cnt); + if (ret_cnt < 0) { + rc = -ret_cnt; + } + } + + /* + * terminate transfer + */ + if (rc == RTEMS_SUCCESSFUL) { + rc = rtems_libi2c_send_stop(minor); + } + rwargs->bytes_moved = (rc == RTEMS_SUCCESSFUL) ? ret_cnt : 0; + + return rc; +} + +/* + * driver operation tables + */ +rtems_driver_address_table spi_memdrv_rw_ops = { + read_entry: spi_memdrv_read, + write_entry: spi_memdrv_write +}; + +rtems_driver_address_table spi_memdrv_ro_ops = { + read_entry: spi_memdrv_read, +}; + |