summaryrefslogblamecommitdiffstats
path: root/bsps/shared/dev/i2c/spi-memdrv.c
blob: f3308e8fe185667abd87ca5bd10d8720c5a0e9a3 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13












                                                                   
                                                                   





























                                                                             
                                                   



































                                                                             
                                           













                                                                             
                                    
 


                                                             























                                                                             
                                                 








                                                                  




                                                                              




















                                                          
                                    
                              
                                
                                         
                                                        

















                                                                             
 

















































                                                              
                                                            










































































                                                                             
                                                 






                                                                  





                                                                              









































                                                            
                               
      

                                                                           



                                          




                                                         



                                      
                                   
                                                            




















                                                                
                    






                                                      
 














                                                               

                                 


                                                
                                 

  
/*===============================================================*\
| 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.org/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:                                                                 |
\*-------------------------------------------------------------------------*/
static 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:                                                                 |
\*-------------------------------------------------------------------------*/
static 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_interval   ticks_per_second;

  ticks_per_second = rtems_clock_get_ticks_per_second();
  (void) rtems_task_wake_after(ticks_per_second * ms / 1000);
  return 0;
}

/*=========================================================================*\
| 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;
  off_t                     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 - bytes_sent;
    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 > 0x10000 /* 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;
  off_t                     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 (rc == RTEMS_SUCCESSFUL) {
    /*
     * HACK: beyond size of memory array? then read status register instead
     */
    if (off >= mem_param_ptr->mem_size) {
      /*
       * send read status register command
       */
      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
       */
      cmdbuf[0] = SPI_MEM_CMD_READ;
      if (mem_param_ptr->mem_size > 0x10000 /* 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,
};