Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:04

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /*
0004  * SPI driver for spi memory devices.
0005  */
0006 
0007 /*
0008  * Copyright (c) 2008 embedded brains GmbH & Co. KG
0009  *
0010  * Redistribution and use in source and binary forms, with or without
0011  * modification, are permitted provided that the following conditions
0012  * are met:
0013  * 1. Redistributions of source code must retain the above copyright
0014  *    notice, this list of conditions and the following disclaimer.
0015  * 2. Redistributions in binary form must reproduce the above copyright
0016  *    notice, this list of conditions and the following disclaimer in the
0017  *    documentation and/or other materials provided with the distribution.
0018  *
0019  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0020  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0021  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0022  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0023  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0024  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0025  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0026  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0027  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0028  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0029  * POSSIBILITY OF SUCH DAMAGE.
0030  */
0031 
0032 /*
0033  * FIXME: currently, this driver only supports read/write accesses
0034  * erase accesses are to be completed
0035  */
0036 
0037 
0038 #include <rtems.h>
0039 #include <rtems/libi2c.h>
0040 
0041 #include <libchip/spi-memdrv.h>
0042 #include <rtems/libio.h>
0043 
0044 #define SPI_MEM_CMD_WREN  0x06
0045 #define SPI_MEM_CMD_WRDIS 0x04
0046 #define SPI_MEM_CMD_RDID  0x9F
0047 #define SPI_MEM_CMD_RDSR  0x05
0048 #define SPI_MEM_CMD_WRSR  0x01
0049 #define SPI_MEM_CMD_READ  0x03
0050 #define SPI_MEM_CMD_PP    0x02  /* page program                       */
0051 #define SPI_MEM_CMD_SE    0xD8  /* sector erase                       */
0052 #define SPI_MEM_CMD_BE    0xC7  /* bulk erase                         */
0053 #define SPI_MEM_CMD_DP    0xB9  /* deep power down                    */
0054 #define SPI_MEM_CMD_RES   0xAB  /* release from deep power down       */
0055 
0056 /*=========================================================================*\
0057 | Function:                                                                 |
0058 \*-------------------------------------------------------------------------*/
0059 static rtems_status_code spi_memdrv_minor2param_ptr
0060 (
0061 /*-------------------------------------------------------------------------*\
0062 | Purpose:                                                                  |
0063 |   translate given minor device number to param pointer                    |
0064 +---------------------------------------------------------------------------+
0065 | Input Parameters:                                                         |
0066 \*-------------------------------------------------------------------------*/
0067  rtems_device_minor_number minor,                /* minor number of device */
0068  spi_memdrv_param_t **param_ptr                  /* ptr to param ptr       */
0069 )
0070 /*-------------------------------------------------------------------------*\
0071 | Return Value:                                                             |
0072 |    o = ok or error code                                                   |
0073 \*=========================================================================*/
0074 {
0075   rtems_status_code   rc = RTEMS_SUCCESSFUL;
0076   spi_memdrv_t *drv_ptr;
0077 
0078   if (rc == RTEMS_SUCCESSFUL) {
0079     rc = -rtems_libi2c_ioctl(minor,
0080                  RTEMS_LIBI2C_IOCTL_GET_DRV_T,
0081                  &drv_ptr);
0082   }
0083   if ((rc == RTEMS_SUCCESSFUL) &&
0084       (drv_ptr->libi2c_drv_entry.size != sizeof(spi_memdrv_t))) {
0085     rc = RTEMS_INVALID_SIZE;
0086   }
0087   if (rc == RTEMS_SUCCESSFUL) {
0088     *param_ptr = &(drv_ptr->spi_memdrv_param);
0089   }
0090   return rc;
0091 }
0092 
0093 /*=========================================================================*\
0094 | Function:                                                                 |
0095 \*-------------------------------------------------------------------------*/
0096 static rtems_status_code spi_memdrv_wait_ms
0097 (
0098 /*-------------------------------------------------------------------------*\
0099 | Purpose:                                                                  |
0100 |   wait a certain interval given in ms                                     |
0101 +---------------------------------------------------------------------------+
0102 | Input Parameters:                                                         |
0103 \*-------------------------------------------------------------------------*/
0104  int ms                                  /* time to wait in milliseconds   */
0105 )
0106 /*-------------------------------------------------------------------------*\
0107 | Return Value:                                                             |
0108 |    o = ok or error code                                                   |
0109 \*=========================================================================*/
0110 {
0111   rtems_interval   ticks_per_second;
0112 
0113   ticks_per_second = rtems_clock_get_ticks_per_second();
0114   (void) rtems_task_wake_after(ticks_per_second * ms / 1000);
0115   return 0;
0116 }
0117 
0118 /*=========================================================================*\
0119 | Function:                                                                 |
0120 \*-------------------------------------------------------------------------*/
0121 rtems_status_code spi_memdrv_write
0122 (
0123 /*-------------------------------------------------------------------------*\
0124 | Purpose:                                                                  |
0125 |   write a block of data to flash                                          |
0126 +---------------------------------------------------------------------------+
0127 | Input Parameters:                                                         |
0128 \*-------------------------------------------------------------------------*/
0129  rtems_device_major_number major,        /* major device number            */
0130  rtems_device_minor_number minor,        /* minor device number            */
0131  void                      *arg          /* ptr to write argument struct   */
0132 )
0133 /*-------------------------------------------------------------------------*\
0134 | Return Value:                                                             |
0135 |    o = ok or error code                                                   |
0136 \*=========================================================================*/
0137 {
0138   rtems_status_code          rc = RTEMS_SUCCESSFUL;
0139   rtems_libio_rw_args_t *rwargs = arg;
0140   off_t                     off = rwargs->offset;
0141   int                       cnt = rwargs->count;
0142   unsigned char            *buf = (unsigned char *)rwargs->buffer;
0143   int                bytes_sent = 0;
0144   int                  curr_cnt;
0145   unsigned char       cmdbuf[4];
0146   int                   ret_cnt = 0;
0147   int                  cmd_size;
0148   spi_memdrv_param_t  *mem_param_ptr;
0149   rtems_libi2c_tfr_mode_t tfr_mode = {
0150     .baudrate =      20000000, /* maximum bits per second                   */
0151     .bits_per_char = 8,        /* how many bits per byte/word/longword?     */
0152     .lsb_first =     FALSE,    /* FALSE: send MSB first                     */
0153     .clock_inv =     FALSE,    /* FALSE: non-inverted clock (high active)   */
0154     .clock_phs =     FALSE     /* FALSE: clock starts in middle of data tfr */
0155   } ;
0156 
0157   /*
0158    * get mem parameters
0159    */
0160   if (rc == RTEMS_SUCCESSFUL) {
0161     rc = spi_memdrv_minor2param_ptr(minor,&mem_param_ptr);
0162   }
0163   /*
0164    * check arguments
0165    */
0166   if (rc == RTEMS_SUCCESSFUL) {
0167     if ((cnt <= 0)                      ||
0168     (cnt > mem_param_ptr->mem_size) ||
0169     (off > (mem_param_ptr->mem_size-cnt))) {
0170       rc = RTEMS_INVALID_SIZE;
0171     }
0172     else if (buf == NULL) {
0173       rc = RTEMS_INVALID_ADDRESS;
0174     }
0175   }
0176   while ((rc == RTEMS_SUCCESSFUL) &&
0177      (cnt > bytes_sent)) {
0178     curr_cnt = cnt - bytes_sent;
0179     if ((mem_param_ptr->page_size > 0) &&
0180     (off              / mem_param_ptr->page_size) !=
0181     ((off+curr_cnt+1) / mem_param_ptr->page_size)) {
0182       curr_cnt = mem_param_ptr->page_size - (off % mem_param_ptr->page_size);
0183     }
0184     /*
0185      * select device, set transfer mode, address device
0186      */
0187     if (rc == RTEMS_SUCCESSFUL) {
0188       rc = rtems_libi2c_send_start(minor);
0189     }
0190     /*
0191      * set transfer mode
0192      */
0193     if (rc == RTEMS_SUCCESSFUL) {
0194       tfr_mode.baudrate = mem_param_ptr->baudrate;
0195       rc = -rtems_libi2c_ioctl(minor,
0196                    RTEMS_LIBI2C_IOCTL_SET_TFRMODE,
0197                    &tfr_mode);
0198     }
0199 
0200     /*
0201      * address device
0202      */
0203     if (rc == RTEMS_SUCCESSFUL) {
0204       rc = rtems_libi2c_send_addr(minor,TRUE);
0205     }
0206 
0207     /*
0208      * send write_enable command
0209      */
0210     if (rc == RTEMS_SUCCESSFUL) {
0211       cmdbuf[0] = SPI_MEM_CMD_WREN;
0212       ret_cnt = rtems_libi2c_write_bytes(minor,cmdbuf,1);
0213       if (ret_cnt < 0) {
0214     rc = -ret_cnt;
0215       }
0216     }
0217     /*
0218      * terminate transfer
0219      */
0220     if (rc == RTEMS_SUCCESSFUL) {
0221       rc = rtems_libi2c_send_stop(minor);
0222     }
0223     /*
0224      * select device, set transfer mode
0225      */
0226     if (rc == RTEMS_SUCCESSFUL) {
0227       rc = rtems_libi2c_send_start(minor);
0228     }
0229 
0230     /*
0231      * address device
0232      */
0233     if (rc == RTEMS_SUCCESSFUL) {
0234       rc = rtems_libi2c_send_addr(minor,TRUE);
0235     }
0236 
0237     /*
0238      * set transfer mode
0239      */
0240     if (rc == RTEMS_SUCCESSFUL) {
0241       rc = -rtems_libi2c_ioctl(minor,
0242                    RTEMS_LIBI2C_IOCTL_SET_TFRMODE,
0243                    &tfr_mode);
0244     }
0245     /*
0246      * send "page program" command and address
0247      */
0248     if (rc == RTEMS_SUCCESSFUL) {
0249       cmdbuf[0] = SPI_MEM_CMD_PP;
0250       if (mem_param_ptr->mem_size > 0x10000 /* 256*256 */) {
0251     cmdbuf[1] = (off >> 16) & 0xff;
0252     cmdbuf[2] = (off >>  8) & 0xff;
0253     cmdbuf[3] = (off >>  0) & 0xff;
0254     cmd_size  = 4;
0255       }
0256       else if (mem_param_ptr->mem_size > 256) {
0257     cmdbuf[1] = (off >>  8) & 0xff;
0258     cmdbuf[2] = (off >>  0) & 0xff;
0259     cmd_size  = 3;
0260       }
0261       else {
0262     cmdbuf[1] = (off >>  0) & 0xff;
0263     cmd_size  = 1;
0264       }
0265 
0266       ret_cnt = rtems_libi2c_write_bytes(minor,cmdbuf,cmd_size);
0267       if (ret_cnt < 0) {
0268     rc = -ret_cnt;
0269       }
0270     }
0271     /*
0272      * send write data
0273      */
0274     if (rc == RTEMS_SUCCESSFUL) {
0275       ret_cnt = rtems_libi2c_write_bytes(minor,buf,curr_cnt);
0276       if (ret_cnt < 0) {
0277     rc = -ret_cnt;
0278       }
0279     }
0280     /*
0281      * terminate transfer
0282      */
0283     if (rc == RTEMS_SUCCESSFUL) {
0284       rc = rtems_libi2c_send_stop(minor);
0285     }
0286     /*
0287      * wait proper time for data to store: 5ms
0288      * FIXME: select proper interval or poll, until device is finished
0289      */
0290     if (rc == RTEMS_SUCCESSFUL) {
0291       rc = spi_memdrv_wait_ms(5);
0292     }
0293     /*
0294      * adjust bytecount to be sent and pointers
0295      */
0296     bytes_sent += curr_cnt;
0297     off        += curr_cnt;
0298     buf        += curr_cnt;
0299   }
0300   rwargs->bytes_moved = bytes_sent;
0301   return rc;
0302 }
0303 
0304 /*=========================================================================*\
0305 | Function:                                                                 |
0306 \*-------------------------------------------------------------------------*/
0307 rtems_status_code spi_memdrv_read
0308 (
0309 /*-------------------------------------------------------------------------*\
0310 | Purpose:                                                                  |
0311 |   read a block of data from flash                                         |
0312 +---------------------------------------------------------------------------+
0313 | Input Parameters:                                                         |
0314 \*-------------------------------------------------------------------------*/
0315  rtems_device_major_number major,        /* major device number            */
0316  rtems_device_minor_number minor,        /* minor device number            */
0317  void                      *arg          /* ptr to read argument struct    */
0318 )
0319 /*-------------------------------------------------------------------------*\
0320 | Return Value:                                                             |
0321 |    o = ok or error code                                                   |
0322 \*=========================================================================*/
0323 {
0324   rtems_status_code rc = RTEMS_SUCCESSFUL;
0325   rtems_libio_rw_args_t *rwargs = arg;
0326   off_t                     off = rwargs->offset;
0327   int                       cnt = rwargs->count;
0328   unsigned char            *buf = (unsigned char *)rwargs->buffer;
0329   unsigned char         cmdbuf[4];
0330   int                   ret_cnt = 0;
0331   int                  cmd_size;
0332   spi_memdrv_param_t  *mem_param_ptr;
0333   rtems_libi2c_tfr_mode_t tfr_mode = {
0334     .baudrate =      20000000, /* maximum bits per second                   */
0335     .bits_per_char = 8,        /* how many bits per byte/word/longword?     */
0336     .lsb_first =     FALSE,    /* FALSE: send MSB first                     */
0337     .clock_inv =     FALSE,    /* FALSE: non-inverted clock (high active)   */
0338     .clock_phs =     FALSE     /* FALSE: clock starts in middle of data tfr */
0339   };
0340 
0341   /*
0342    * get mem parameters
0343    */
0344   if (rc == RTEMS_SUCCESSFUL) {
0345     rc = spi_memdrv_minor2param_ptr(minor,&mem_param_ptr);
0346   }
0347   /*
0348    * check arguments
0349    */
0350   if (rc == RTEMS_SUCCESSFUL) {
0351     if ((cnt <= 0)                      ||
0352     (cnt > mem_param_ptr->mem_size) ||
0353     (off > (mem_param_ptr->mem_size-cnt))) {
0354       rc = RTEMS_INVALID_SIZE;
0355     }
0356     else if (buf == NULL) {
0357       rc = RTEMS_INVALID_ADDRESS;
0358     }
0359   }
0360   /*
0361    * select device, set transfer mode, address device
0362    */
0363   if (rc == RTEMS_SUCCESSFUL) {
0364     rc = rtems_libi2c_send_start(minor);
0365   }
0366   /*
0367    * set transfer mode
0368    */
0369   if (rc == RTEMS_SUCCESSFUL) {
0370     tfr_mode.baudrate = mem_param_ptr->baudrate;
0371     rc = -rtems_libi2c_ioctl(minor,
0372                  RTEMS_LIBI2C_IOCTL_SET_TFRMODE,
0373                  &tfr_mode);
0374   }
0375   /*
0376    * address device
0377    */
0378   if (rc == RTEMS_SUCCESSFUL) {
0379     rc = rtems_libi2c_send_addr(minor,TRUE);
0380   }
0381 
0382   if (rc == RTEMS_SUCCESSFUL) {
0383     /*
0384      * HACK: beyond size of memory array? then read status register instead
0385      */
0386     if (off >= mem_param_ptr->mem_size) {
0387       /*
0388        * send read status register command
0389        */
0390       cmdbuf[0] = SPI_MEM_CMD_RDSR;
0391       ret_cnt = rtems_libi2c_write_bytes(minor,cmdbuf,1);
0392       if (ret_cnt < 0) {
0393     rc = -ret_cnt;
0394       }
0395     } else {
0396       /*
0397        * send read command and address
0398        */
0399       cmdbuf[0] = SPI_MEM_CMD_READ;
0400       if (mem_param_ptr->mem_size > 0x10000 /* 256*256 */) {
0401     cmdbuf[1] = (off >> 16) & 0xff;
0402     cmdbuf[2] = (off >>  8) & 0xff;
0403     cmdbuf[3] = (off >>  0) & 0xff;
0404     cmd_size  = 4;
0405       }
0406       else if (mem_param_ptr->mem_size > 256) {
0407     cmdbuf[1] = (off >>  8) & 0xff;
0408     cmdbuf[2] = (off >>  0) & 0xff;
0409     cmd_size  = 3;
0410       }
0411       else {
0412     cmdbuf[1] = (off >>  0) & 0xff;
0413     cmd_size  = 1;
0414       }
0415       ret_cnt = rtems_libi2c_write_bytes(minor,cmdbuf,cmd_size);
0416       if (ret_cnt < 0) {
0417     rc = -ret_cnt;
0418       }
0419     }
0420   }
0421   /*
0422    * fetch read data
0423    */
0424   if (rc == RTEMS_SUCCESSFUL) {
0425     ret_cnt = rtems_libi2c_read_bytes (minor,buf,cnt);
0426     if (ret_cnt < 0) {
0427       rc = -ret_cnt;
0428     }
0429   }
0430 
0431   /*
0432    * terminate transfer
0433    */
0434   if (rc == RTEMS_SUCCESSFUL) {
0435     rc = rtems_libi2c_send_stop(minor);
0436   }
0437   rwargs->bytes_moved = (rc == RTEMS_SUCCESSFUL) ? ret_cnt : 0;
0438 
0439   return rc;
0440 }
0441 
0442 /*
0443  * driver operation tables
0444  */
0445 rtems_driver_address_table spi_memdrv_rw_ops = {
0446   .read_entry =  spi_memdrv_read,
0447   .write_entry = spi_memdrv_write
0448 };
0449 
0450 rtems_driver_address_table spi_memdrv_ro_ops = {
0451   .read_entry =  spi_memdrv_read,
0452 };
0453