Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:05

0001 /*
0002  * s3c2400 smc disk block device implementation
0003  *
0004  * Squidge's SMC Low-level access routines.
0005  * Inspired and derived from routines provided by Samsung Electronics
0006  *  M/M R&D Center & FireFly.
0007  */
0008 
0009 #include <rtems.h>
0010 #include <rtems/libio.h>
0011 #include <errno.h>
0012 #include <stdlib.h>
0013 #include <stdio.h>
0014 #include <string.h>
0015 #include <inttypes.h>
0016 
0017 #include <rtems/blkdev.h>
0018 #include "smc.h"
0019 #include <rtems/bspIo.h>
0020 #include <s3c24xx.h>
0021 
0022 #define SMC_DEVICE_NAME "/dev/smc"
0023 #define SMC_SAMSUNG_ID    0xEC
0024 #define SMC_TOSHIBA_ID    0x98
0025 
0026 #define SMC_16MB  0x73
0027 #define SMC_32MB  0x75
0028 #define SMC_64MB  0x76
0029 #define SMC_128MB  0x79
0030 
0031 #define LBA_UNUSED      0x80000000
0032 #define LBA_RESERVED    0x80000001
0033 
0034 #define BLOCK_UNUSED    0x80000000
0035 #define BLOCK_RESERVED  0x80000001
0036 
0037 /* SmartMedia Command */
0038 #define SEQ_DATA_INPUT_CMD   0x80
0039 #define READ1_CMD            0x00
0040 #define READ1_1_CMD          0x01
0041 #define READ2_CMD            0x50
0042 #define READ_ID_CMD          0x90
0043 #define RESET_CMD            0xFF
0044 #define PAGE_PROGRAM_CMD     0x10
0045 #define BLOCK_ERASE_CMD      0x60
0046 #define BLOCK_ERASE_CFM_CMD  0xD0
0047 #define READ_STATUS_CMD      0x70
0048 #define RESET_PTR_CMD        0x00
0049 
0050 
0051 /* Internal SMC disk descriptor */
0052 struct SMC_INFO {
0053   uint8_t  id[3];
0054   uint32_t bytes_per_page;
0055   uint32_t pages_per_block;
0056   uint32_t blocks;
0057   uint32_t mb;
0058 };
0059 
0060 /* Ths S3c2410 uses a different register map */
0061 #ifdef CPU_S3C2410
0062 #define rPBDAT rGPBDAT
0063 #define rPBCON rGPBCON
0064 #define rPDDAT rGPDDAT
0065 #define rPEDAT rGPEDAT
0066 #endif
0067 
0068 
0069 static struct SMC_INFO smc_info;
0070 
0071 uint32_t smc_l2p[0x2000];
0072 uint32_t smc_p2l[0x2000];
0073 
0074 #define sm_busy() while (!(rPDDAT & 0x200))
0075 #define sm_chip_en() rPDDAT &= (~0x80)
0076 #define sm_chip_dis() rPDDAT |= 0x80
0077 #define sm_cle_en() rPEDAT |= 0x20
0078 #define sm_cle_dis() rPEDAT &= (~0x20)
0079 #define sm_ale_en() rPEDAT |= 0x10
0080 #define sm_ale_dis() rPEDAT &= (~0x10)
0081 #define sm_wp_en() rPDDAT &= (~0x40)
0082 #define sm_wp_dis() rPDDAT |= 0x40
0083 #define sm_read_en() rPBCON &= 0xFFFF0000
0084 #define sm_read_dis() rPBCON = (rPBCON & 0xFFFF0000) | 0x5555
0085 #define sm_write_en() sm_read_dis()
0086 #define sm_write_dis() sm_read_en()
0087 
0088 static void sm_write( uint8_t data)
0089 {
0090   rPBDAT = (rPBDAT & 0xFF00) | data;
0091   rPEDAT &= (~0x08);
0092   rPEDAT |= 0x08;
0093 }
0094 
0095 static uint8_t sm_read(void)
0096 {
0097   uint8_t data;
0098 
0099   rPDDAT &= (~0x100);
0100   data = rPBDAT & 0xFF;
0101   rPDDAT |= 0x100;
0102   return data;
0103 }
0104 
0105 static void smc_read_id( uint8_t* buf, uint32_t length)
0106 {
0107   uint32_t i;
0108 
0109   sm_chip_en();
0110 
0111   sm_cle_en();
0112   sm_write_en();
0113   sm_write(READ_ID_CMD);
0114   sm_write_dis();
0115   sm_cle_dis();
0116 
0117   sm_ale_en();
0118   sm_write_en();
0119   sm_write( 0);
0120   sm_write_dis();
0121   sm_ale_dis();
0122 
0123   sm_read_en();
0124   for (i=0;i<length;i++) *(buf+i) = sm_read();
0125   sm_read_dis();
0126 
0127   sm_chip_dis();
0128 }
0129 
0130 /* read an entire logical page of 512 bytes.*/
0131 static uint8_t smc_read_page (uint32_t lpage, uint8_t* buf)
0132 {
0133   uint32_t block, page, i;
0134 
0135   /* convert logical block to physical block
0136      and then convert into page suitable for read1 command...
0137   */
0138   block = lpage >> 5;
0139   if (smc_l2p[block] < LBA_UNUSED) {
0140     page = smc_l2p[block] << 5;
0141     page += (lpage & 0x1F);
0142   }
0143   else
0144     return 0;
0145 
0146   sm_chip_en();
0147 
0148   sm_cle_en();
0149   sm_write_en();
0150   sm_write(READ1_CMD);
0151   sm_write_dis();
0152   sm_cle_dis();
0153 
0154   sm_ale_en();
0155   sm_write_en();
0156   sm_write( 0x00);
0157   sm_write( (uint8_t)(page >> 0));
0158   sm_write( (uint8_t)(page >> 8));
0159   if (smc_info.mb >= 64)
0160     sm_write( (uint8_t)(page >> 16));
0161   sm_write_dis();
0162   sm_ale_dis();
0163 
0164   sm_busy();
0165 
0166   sm_read_en();
0167   for (i = 0; i < 512; i++) {
0168     *buf = sm_read();
0169     buf++;
0170   }
0171   sm_read_dis();
0172   sm_chip_dis();
0173 
0174   sm_busy();
0175   return 1;
0176 }
0177 
0178 static void smc_read_spare( uint32_t page, uint8_t* buf, uint8_t length)
0179 {
0180   uint32_t i;
0181 
0182   sm_chip_en();
0183 
0184   sm_cle_en();
0185   sm_read_dis();
0186   sm_write(READ2_CMD);
0187   sm_read_en();
0188   sm_cle_dis();
0189 
0190   sm_ale_en();
0191   sm_read_dis();
0192   sm_write( 0x00);
0193   sm_write( (uint8_t)(page >> 0));
0194   sm_write( (uint8_t)(page >> 8));
0195   if (smc_info.mb >= 64)
0196     sm_write( (uint8_t)(page >> 16));
0197   sm_read_en();
0198   sm_ale_dis();
0199 
0200   sm_busy();
0201 
0202   sm_read_en();
0203   for (i=0;i<length;i++)
0204     *(buf+i) = sm_read();
0205   sm_read_dis();
0206 
0207   sm_chip_dis();
0208 
0209 }
0210 
0211 static void smc_make_l2p(void)
0212 {
0213   uint32_t pblock, i, j, lblock, zone, count;
0214   uint8_t data[512];
0215 
0216   for (i=0;i<0x2000;i++) {
0217     smc_l2p[i] = LBA_RESERVED;
0218     smc_p2l[i] = BLOCK_RESERVED;
0219   }
0220 
0221   for (pblock=0;pblock<smc_info.blocks;pblock++) {
0222     /* read physical block - first page */
0223     smc_read_spare( pblock*smc_info.pages_per_block, (uint8_t*)&data, 16);
0224 
0225     zone = pblock >> 10; /* divide by 1024 to get zone */
0226     if ((data[5] == 0xFF) && ((data[6]&0xF8) == 0x10)) {
0227       lblock = ((((data[6]<<8)|(data[7]<<0)) >> 1) & 0x03FF) + (zone * 1000);
0228       smc_l2p[lblock] = pblock;
0229       smc_p2l[pblock] = lblock;
0230     } else {
0231       count = 0;
0232       for (j=0;j<16;j++) {
0233         if (data[j] == 0xFF) count++;
0234       }
0235       if (count == 16) {
0236         smc_p2l[pblock] = BLOCK_UNUSED;
0237       } else {
0238         smc_p2l[pblock] = BLOCK_RESERVED;
0239       }
0240     }
0241   }
0242 }
0243 
0244 
0245 static void smc_detect( uint8_t id1, uint8_t id2, uint8_t id3)
0246 {
0247   smc_info.id[0] = id1;
0248   smc_info.id[1] = id2;
0249   smc_info.id[2] = id3;
0250   smc_info.mb    = 0;
0251   smc_info.bytes_per_page  = 0;
0252   smc_info.pages_per_block = 0;
0253   smc_info.blocks          = 0;
0254 
0255   switch (id1) {
0256     case SMC_SAMSUNG_ID:
0257     case SMC_TOSHIBA_ID: {
0258       switch (id2) {
0259         case SMC_16MB  : smc_info.mb = 16; break;
0260         case SMC_32MB  : smc_info.mb = 32; break;
0261         case SMC_64MB  : smc_info.mb = 64; break;
0262         case SMC_128MB : smc_info.mb = 128; break;
0263       }
0264       break;
0265     }
0266   }
0267 
0268   switch (smc_info.mb) {
0269     case 16  : smc_info.bytes_per_page = 512; smc_info.pages_per_block = 32; smc_info.blocks = 0x0400; break;
0270     case 32  : smc_info.bytes_per_page = 512; smc_info.pages_per_block = 32; smc_info.blocks = 0x0800; break;
0271     case 64  : smc_info.bytes_per_page = 512; smc_info.pages_per_block = 32; smc_info.blocks = 0x1000; break;
0272     case 128 : smc_info.bytes_per_page = 512; smc_info.pages_per_block = 32; smc_info.blocks = 0x2000; break;
0273   }
0274 }
0275 
0276 static void smc_init( void)
0277 {
0278   unsigned char buf[32];
0279   int i;
0280 
0281   /* reset smc */
0282   sm_chip_en();
0283   sm_cle_en();
0284   sm_write_en();
0285   sm_write(0xFF);
0286   sm_write_dis();
0287   sm_cle_dis();
0288   for(i=0;i<10;i++);
0289   sm_busy();
0290   sm_chip_dis();
0291 
0292   smc_read_id (buf, 4);
0293   smc_detect (buf[0], buf[1], buf[2]);
0294   printk ("SMC: [%02X-%02X-%02X-%02X]\n", buf[0], buf[1], buf[2], buf[3]);
0295   printk ("SMC size: %" PRIu32 "MB detected\n",smc_info.mb);
0296   smc_make_l2p();
0297 }
0298 
0299 /* smc_write --
0300  * write stub
0301  */
0302 static int smc_write(rtems_blkdev_request *req)
0303 {
0304   rtems_blkdev_request_done(req, RTEMS_SUCCESSFUL);
0305   return 0;
0306 }
0307 
0308 
0309 /* smc_read --
0310  * PARAMETERS:
0311  *     req - pointer to the READ block device request info
0312  *
0313  * RETURNS:
0314  *     ioctl return value
0315  */
0316 static int
0317 smc_read(rtems_blkdev_request *req)
0318 {
0319     uint32_t   i;
0320     rtems_blkdev_sg_buffer *sg;
0321     uint32_t   remains;
0322 
0323     remains = smc_info.bytes_per_page * req->bufnum;
0324     sg = req->bufs;
0325     for (i = 0; (remains > 0) && (i < req->bufnum); i++, sg++)
0326     {
0327         int count = sg->length;
0328         if (count > remains)
0329             count = remains;
0330         smc_read_page(sg->block,sg->buffer);
0331         remains -= count;
0332     }
0333     rtems_blkdev_request_done(req, RTEMS_SUCCESSFUL);
0334     return 0;
0335 }
0336 
0337 /* smc_ioctl --
0338  *     IOCTL handler for SMC device.
0339  *
0340  * PARAMETERS:
0341  *      dev  - device number (major, minor number)
0342  *      req  - IOCTL request code
0343  *      argp - IOCTL argument
0344  *
0345  * RETURNS:
0346  *     IOCTL return value
0347  */
0348 static int
0349 smc_ioctl(rtems_disk_device *dd, uint32_t req, void *argp)
0350 {
0351     switch (req)
0352     {
0353         case RTEMS_BLKIO_REQUEST:
0354         {
0355             rtems_blkdev_request *r = argp;
0356             switch (r->req)
0357             {
0358                 case RTEMS_BLKDEV_REQ_READ:
0359                     return smc_read(r);
0360                 case RTEMS_BLKDEV_REQ_WRITE:
0361                     return smc_write(r);
0362                 default:
0363                     errno = EINVAL;
0364                     return -1;
0365             }
0366             break;
0367         }
0368 
0369         default:
0370             errno = EINVAL;
0371             return -1;
0372     }
0373 }
0374 
0375 /* smc_initialize --
0376  *     RAM disk device driver initialization. Run through RAM disk
0377  *     configuration information and configure appropriate RAM disks.
0378  *
0379  * PARAMETERS:
0380  *     major - RAM disk major device number
0381  *     minor - minor device number, not applicable
0382  *     arg   - initialization argument, not applicable
0383  *
0384  * RETURNS:
0385  *     none
0386  */
0387 rtems_device_driver
0388 smc_initialize(
0389     rtems_device_major_number major,
0390     rtems_device_minor_number minor,
0391     void *arg)
0392 {
0393     rtems_status_code rc;
0394     uint32_t block_num;
0395 
0396     smc_init();
0397     block_num = smc_info.blocks << 5;
0398 
0399     rc = rtems_blkdev_create(SMC_DEVICE_NAME, 512, block_num, smc_ioctl, NULL);
0400 
0401     return rc;
0402 }