[3f5e0961] | 1 | /* |
---|
| 2 | * s3c2400 smc disk block device implementation |
---|
| 3 | * |
---|
| 4 | * Squidge's SMC Low-level access routines. |
---|
| 5 | * Inspired and derived from routines provided by Samsung Electronics |
---|
| 6 | * M/M R&D Center & FireFly. |
---|
| 7 | */ |
---|
| 8 | |
---|
| 9 | #include <rtems.h> |
---|
| 10 | #include <rtems/libio.h> |
---|
| 11 | #include <errno.h> |
---|
| 12 | #include <stdlib.h> |
---|
| 13 | #include <stdio.h> |
---|
| 14 | #include <string.h> |
---|
| 15 | #include <inttypes.h> |
---|
| 16 | |
---|
[d279e74] | 17 | #include <rtems/blkdev.h> |
---|
[3f5e0961] | 18 | #include "smc.h" |
---|
| 19 | #include <rtems/bspIo.h> |
---|
| 20 | #include <s3c24xx.h> |
---|
| 21 | |
---|
| 22 | #define SMC_DEVICE_NAME "/dev/smc" |
---|
| 23 | #define SMC_SAMSUNG_ID 0xEC |
---|
| 24 | #define SMC_TOSHIBA_ID 0x98 |
---|
| 25 | |
---|
| 26 | #define SMC_16MB 0x73 |
---|
| 27 | #define SMC_32MB 0x75 |
---|
| 28 | #define SMC_64MB 0x76 |
---|
| 29 | #define SMC_128MB 0x79 |
---|
| 30 | |
---|
| 31 | #define LBA_UNUSED 0x80000000 |
---|
| 32 | #define LBA_RESERVED 0x80000001 |
---|
| 33 | |
---|
| 34 | #define BLOCK_UNUSED 0x80000000 |
---|
| 35 | #define BLOCK_RESERVED 0x80000001 |
---|
| 36 | |
---|
| 37 | /* SmartMedia Command */ |
---|
| 38 | #define SEQ_DATA_INPUT_CMD 0x80 |
---|
| 39 | #define READ1_CMD 0x00 |
---|
| 40 | #define READ1_1_CMD 0x01 |
---|
| 41 | #define READ2_CMD 0x50 |
---|
| 42 | #define READ_ID_CMD 0x90 |
---|
| 43 | #define RESET_CMD 0xFF |
---|
| 44 | #define PAGE_PROGRAM_CMD 0x10 |
---|
| 45 | #define BLOCK_ERASE_CMD 0x60 |
---|
| 46 | #define BLOCK_ERASE_CFM_CMD 0xD0 |
---|
| 47 | #define READ_STATUS_CMD 0x70 |
---|
| 48 | #define RESET_PTR_CMD 0x00 |
---|
| 49 | |
---|
| 50 | |
---|
| 51 | /* Internal SMC disk descriptor */ |
---|
| 52 | struct SMC_INFO { |
---|
| 53 | uint8_t id[3]; |
---|
| 54 | uint32_t bytes_per_page; |
---|
| 55 | uint32_t pages_per_block; |
---|
| 56 | uint32_t blocks; |
---|
| 57 | uint32_t mb; |
---|
| 58 | }; |
---|
| 59 | |
---|
| 60 | /* Ths S3c2410 uses a different register map */ |
---|
| 61 | #ifdef CPU_S3C2410 |
---|
| 62 | #define rPBDAT rGPBDAT |
---|
| 63 | #define rPBCON rGPBCON |
---|
| 64 | #define rPDDAT rGPDDAT |
---|
| 65 | #define rPEDAT rGPEDAT |
---|
| 66 | #endif |
---|
| 67 | |
---|
| 68 | |
---|
| 69 | static struct SMC_INFO smc_info; |
---|
| 70 | |
---|
| 71 | uint32_t smc_l2p[0x2000]; |
---|
| 72 | uint32_t smc_p2l[0x2000]; |
---|
| 73 | |
---|
| 74 | #define sm_busy() while (!(rPDDAT & 0x200)) |
---|
| 75 | #define sm_chip_en() rPDDAT &= (~0x80) |
---|
| 76 | #define sm_chip_dis() rPDDAT |= 0x80 |
---|
| 77 | #define sm_cle_en() rPEDAT |= 0x20 |
---|
| 78 | #define sm_cle_dis() rPEDAT &= (~0x20) |
---|
| 79 | #define sm_ale_en() rPEDAT |= 0x10 |
---|
| 80 | #define sm_ale_dis() rPEDAT &= (~0x10) |
---|
| 81 | #define sm_wp_en() rPDDAT &= (~0x40) |
---|
| 82 | #define sm_wp_dis() rPDDAT |= 0x40 |
---|
| 83 | #define sm_read_en() rPBCON &= 0xFFFF0000 |
---|
| 84 | #define sm_read_dis() rPBCON = (rPBCON & 0xFFFF0000) | 0x5555 |
---|
| 85 | #define sm_write_en() sm_read_dis() |
---|
| 86 | #define sm_write_dis() sm_read_en() |
---|
| 87 | |
---|
| 88 | static void sm_write( uint8_t data) |
---|
| 89 | { |
---|
| 90 | rPBDAT = (rPBDAT & 0xFF00) | data; |
---|
| 91 | rPEDAT &= (~0x08); |
---|
| 92 | rPEDAT |= 0x08; |
---|
| 93 | } |
---|
| 94 | |
---|
| 95 | static uint8_t sm_read(void) |
---|
| 96 | { |
---|
| 97 | uint8_t data; |
---|
| 98 | |
---|
| 99 | rPDDAT &= (~0x100); |
---|
| 100 | data = rPBDAT & 0xFF; |
---|
| 101 | rPDDAT |= 0x100; |
---|
| 102 | return data; |
---|
| 103 | } |
---|
| 104 | |
---|
| 105 | static void smc_read_id( uint8_t* buf, uint32_t length) |
---|
| 106 | { |
---|
| 107 | uint32_t i; |
---|
| 108 | |
---|
| 109 | sm_chip_en(); |
---|
| 110 | |
---|
| 111 | sm_cle_en(); |
---|
| 112 | sm_write_en(); |
---|
| 113 | sm_write(READ_ID_CMD); |
---|
| 114 | sm_write_dis(); |
---|
| 115 | sm_cle_dis(); |
---|
| 116 | |
---|
| 117 | sm_ale_en(); |
---|
| 118 | sm_write_en(); |
---|
| 119 | sm_write( 0); |
---|
| 120 | sm_write_dis(); |
---|
| 121 | sm_ale_dis(); |
---|
| 122 | |
---|
| 123 | sm_read_en(); |
---|
| 124 | for (i=0;i<length;i++) *(buf+i) = sm_read(); |
---|
| 125 | sm_read_dis(); |
---|
| 126 | |
---|
| 127 | sm_chip_dis(); |
---|
| 128 | } |
---|
| 129 | |
---|
| 130 | /* read an entire logical page of 512 bytes.*/ |
---|
| 131 | static uint8_t smc_read_page (uint32_t lpage, uint8_t* buf) |
---|
| 132 | { |
---|
| 133 | uint32_t block, page, i; |
---|
| 134 | |
---|
| 135 | /* convert logical block to physical block |
---|
| 136 | and then convert into page suitable for read1 command... |
---|
| 137 | */ |
---|
| 138 | block = lpage >> 5; |
---|
| 139 | if (smc_l2p[block] < LBA_UNUSED) { |
---|
| 140 | page = smc_l2p[block] << 5; |
---|
| 141 | page += (lpage & 0x1F); |
---|
| 142 | } |
---|
| 143 | else |
---|
| 144 | return 0; |
---|
| 145 | |
---|
| 146 | sm_chip_en(); |
---|
| 147 | |
---|
| 148 | sm_cle_en(); |
---|
| 149 | sm_write_en(); |
---|
| 150 | sm_write(READ1_CMD); |
---|
| 151 | sm_write_dis(); |
---|
| 152 | sm_cle_dis(); |
---|
| 153 | |
---|
| 154 | sm_ale_en(); |
---|
| 155 | sm_write_en(); |
---|
| 156 | sm_write( 0x00); |
---|
| 157 | sm_write( (uint8_t)(page >> 0)); |
---|
| 158 | sm_write( (uint8_t)(page >> 8)); |
---|
| 159 | if (smc_info.mb >= 64) |
---|
| 160 | sm_write( (uint8_t)(page >> 16)); |
---|
| 161 | sm_write_dis(); |
---|
| 162 | sm_ale_dis(); |
---|
| 163 | |
---|
| 164 | sm_busy(); |
---|
| 165 | |
---|
| 166 | sm_read_en(); |
---|
| 167 | for (i = 0; i < 512; i++) { |
---|
| 168 | *buf = sm_read(); |
---|
| 169 | buf++; |
---|
| 170 | } |
---|
| 171 | sm_read_dis(); |
---|
| 172 | sm_chip_dis(); |
---|
| 173 | |
---|
| 174 | sm_busy(); |
---|
| 175 | return 1; |
---|
| 176 | } |
---|
| 177 | |
---|
| 178 | static void smc_read_spare( uint32_t page, uint8_t* buf, uint8_t length) |
---|
| 179 | { |
---|
| 180 | uint32_t i; |
---|
| 181 | |
---|
| 182 | sm_chip_en(); |
---|
| 183 | |
---|
| 184 | sm_cle_en(); |
---|
| 185 | sm_read_dis(); |
---|
| 186 | sm_write(READ2_CMD); |
---|
| 187 | sm_read_en(); |
---|
| 188 | sm_cle_dis(); |
---|
| 189 | |
---|
| 190 | sm_ale_en(); |
---|
| 191 | sm_read_dis(); |
---|
| 192 | sm_write( 0x00); |
---|
| 193 | sm_write( (uint8_t)(page >> 0)); |
---|
| 194 | sm_write( (uint8_t)(page >> 8)); |
---|
| 195 | if (smc_info.mb >= 64) |
---|
| 196 | sm_write( (uint8_t)(page >> 16)); |
---|
| 197 | sm_read_en(); |
---|
| 198 | sm_ale_dis(); |
---|
| 199 | |
---|
| 200 | sm_busy(); |
---|
| 201 | |
---|
| 202 | sm_read_en(); |
---|
| 203 | for (i=0;i<length;i++) |
---|
| 204 | *(buf+i) = sm_read(); |
---|
| 205 | sm_read_dis(); |
---|
| 206 | |
---|
| 207 | sm_chip_dis(); |
---|
| 208 | |
---|
| 209 | } |
---|
| 210 | |
---|
| 211 | static void smc_make_l2p(void) |
---|
| 212 | { |
---|
[3f09d635] | 213 | uint32_t pblock, i, j, lblock, zone, count; |
---|
[3f5e0961] | 214 | uint8_t data[512]; |
---|
| 215 | |
---|
| 216 | for (i=0;i<0x2000;i++) { |
---|
| 217 | smc_l2p[i] = LBA_RESERVED; |
---|
| 218 | smc_p2l[i] = BLOCK_RESERVED; |
---|
| 219 | } |
---|
| 220 | |
---|
| 221 | for (pblock=0;pblock<smc_info.blocks;pblock++) { |
---|
| 222 | /* read physical block - first page */ |
---|
| 223 | smc_read_spare( pblock*smc_info.pages_per_block, (uint8_t*)&data, 16); |
---|
| 224 | |
---|
| 225 | zone = pblock >> 10; /* divide by 1024 to get zone */ |
---|
| 226 | if ((data[5] == 0xFF) && ((data[6]&0xF8) == 0x10)) { |
---|
| 227 | lblock = ((((data[6]<<8)|(data[7]<<0)) >> 1) & 0x03FF) + (zone * 1000); |
---|
| 228 | smc_l2p[lblock] = pblock; |
---|
| 229 | smc_p2l[pblock] = lblock; |
---|
| 230 | } else { |
---|
| 231 | count = 0; |
---|
| 232 | for (j=0;j<16;j++) { |
---|
| 233 | if (data[j] == 0xFF) count++; |
---|
| 234 | } |
---|
| 235 | if (count == 16) { |
---|
| 236 | smc_p2l[pblock] = BLOCK_UNUSED; |
---|
| 237 | } else { |
---|
| 238 | smc_p2l[pblock] = BLOCK_RESERVED; |
---|
| 239 | } |
---|
| 240 | } |
---|
| 241 | } |
---|
| 242 | } |
---|
| 243 | |
---|
| 244 | |
---|
| 245 | static void smc_detect( uint8_t id1, uint8_t id2, uint8_t id3) |
---|
| 246 | { |
---|
| 247 | smc_info.id[0] = id1; |
---|
| 248 | smc_info.id[1] = id2; |
---|
| 249 | smc_info.id[2] = id3; |
---|
| 250 | smc_info.mb = 0; |
---|
| 251 | smc_info.bytes_per_page = 0; |
---|
| 252 | smc_info.pages_per_block = 0; |
---|
| 253 | smc_info.blocks = 0; |
---|
| 254 | |
---|
| 255 | switch (id1) { |
---|
| 256 | case SMC_SAMSUNG_ID: |
---|
| 257 | case SMC_TOSHIBA_ID: { |
---|
| 258 | switch (id2) { |
---|
| 259 | case SMC_16MB : smc_info.mb = 16; break; |
---|
| 260 | case SMC_32MB : smc_info.mb = 32; break; |
---|
| 261 | case SMC_64MB : smc_info.mb = 64; break; |
---|
| 262 | case SMC_128MB : smc_info.mb = 128; break; |
---|
| 263 | } |
---|
| 264 | break; |
---|
| 265 | } |
---|
| 266 | } |
---|
| 267 | |
---|
| 268 | switch (smc_info.mb) { |
---|
| 269 | case 16 : smc_info.bytes_per_page = 512; smc_info.pages_per_block = 32; smc_info.blocks = 0x0400; break; |
---|
| 270 | case 32 : smc_info.bytes_per_page = 512; smc_info.pages_per_block = 32; smc_info.blocks = 0x0800; break; |
---|
| 271 | case 64 : smc_info.bytes_per_page = 512; smc_info.pages_per_block = 32; smc_info.blocks = 0x1000; break; |
---|
| 272 | case 128 : smc_info.bytes_per_page = 512; smc_info.pages_per_block = 32; smc_info.blocks = 0x2000; break; |
---|
| 273 | } |
---|
| 274 | } |
---|
| 275 | |
---|
| 276 | static void smc_init( void) |
---|
| 277 | { |
---|
| 278 | unsigned char buf[32]; |
---|
| 279 | int i; |
---|
| 280 | |
---|
| 281 | /* reset smc */ |
---|
| 282 | sm_chip_en(); |
---|
| 283 | sm_cle_en(); |
---|
| 284 | sm_write_en(); |
---|
| 285 | sm_write(0xFF); |
---|
| 286 | sm_write_dis(); |
---|
| 287 | sm_cle_dis(); |
---|
| 288 | for(i=0;i<10;i++); |
---|
| 289 | sm_busy(); |
---|
| 290 | sm_chip_dis(); |
---|
| 291 | |
---|
| 292 | smc_read_id (buf, 4); |
---|
| 293 | smc_detect (buf[0], buf[1], buf[2]); |
---|
| 294 | printk ("SMC: [%02X-%02X-%02X-%02X]\n", buf[0], buf[1], buf[2], buf[3]); |
---|
[f25c798] | 295 | printk ("SMC size: %" PRIu32 "MB detected\n",smc_info.mb); |
---|
[3f5e0961] | 296 | smc_make_l2p(); |
---|
| 297 | } |
---|
| 298 | |
---|
| 299 | /* smc_write -- |
---|
| 300 | * write stub |
---|
| 301 | */ |
---|
| 302 | static int smc_write(rtems_blkdev_request *req) |
---|
| 303 | { |
---|
| 304 | rtems_blkdev_request_done(req, RTEMS_SUCCESSFUL); |
---|
| 305 | return 0; |
---|
| 306 | } |
---|
| 307 | |
---|
| 308 | |
---|
| 309 | /* smc_read -- |
---|
| 310 | * PARAMETERS: |
---|
| 311 | * req - pointer to the READ block device request info |
---|
| 312 | * |
---|
| 313 | * RETURNS: |
---|
| 314 | * ioctl return value |
---|
| 315 | */ |
---|
| 316 | static int |
---|
| 317 | smc_read(rtems_blkdev_request *req) |
---|
| 318 | { |
---|
| 319 | uint32_t i; |
---|
| 320 | rtems_blkdev_sg_buffer *sg; |
---|
| 321 | uint32_t remains; |
---|
| 322 | |
---|
| 323 | remains = smc_info.bytes_per_page * req->bufnum; |
---|
| 324 | sg = req->bufs; |
---|
| 325 | for (i = 0; (remains > 0) && (i < req->bufnum); i++, sg++) |
---|
| 326 | { |
---|
| 327 | int count = sg->length; |
---|
| 328 | if (count > remains) |
---|
| 329 | count = remains; |
---|
| 330 | smc_read_page(sg->block,sg->buffer); |
---|
| 331 | remains -= count; |
---|
| 332 | } |
---|
| 333 | rtems_blkdev_request_done(req, RTEMS_SUCCESSFUL); |
---|
| 334 | return 0; |
---|
| 335 | } |
---|
| 336 | |
---|
| 337 | /* smc_ioctl -- |
---|
| 338 | * IOCTL handler for SMC device. |
---|
| 339 | * |
---|
| 340 | * PARAMETERS: |
---|
| 341 | * dev - device number (major, minor number) |
---|
| 342 | * req - IOCTL request code |
---|
| 343 | * argp - IOCTL argument |
---|
| 344 | * |
---|
| 345 | * RETURNS: |
---|
| 346 | * IOCTL return value |
---|
| 347 | */ |
---|
| 348 | static int |
---|
| 349 | smc_ioctl(rtems_disk_device *dd, uint32_t req, void *argp) |
---|
| 350 | { |
---|
| 351 | switch (req) |
---|
| 352 | { |
---|
| 353 | case RTEMS_BLKIO_REQUEST: |
---|
| 354 | { |
---|
| 355 | rtems_blkdev_request *r = argp; |
---|
| 356 | switch (r->req) |
---|
| 357 | { |
---|
| 358 | case RTEMS_BLKDEV_REQ_READ: |
---|
| 359 | return smc_read(r); |
---|
| 360 | case RTEMS_BLKDEV_REQ_WRITE: |
---|
| 361 | return smc_write(r); |
---|
| 362 | default: |
---|
| 363 | errno = EINVAL; |
---|
| 364 | return -1; |
---|
| 365 | } |
---|
| 366 | break; |
---|
| 367 | } |
---|
| 368 | |
---|
| 369 | default: |
---|
| 370 | errno = EINVAL; |
---|
| 371 | return -1; |
---|
| 372 | } |
---|
| 373 | } |
---|
| 374 | |
---|
| 375 | /* smc_initialize -- |
---|
| 376 | * RAM disk device driver initialization. Run through RAM disk |
---|
| 377 | * configuration information and configure appropriate RAM disks. |
---|
| 378 | * |
---|
| 379 | * PARAMETERS: |
---|
| 380 | * major - RAM disk major device number |
---|
| 381 | * minor - minor device number, not applicable |
---|
| 382 | * arg - initialization argument, not applicable |
---|
| 383 | * |
---|
| 384 | * RETURNS: |
---|
| 385 | * none |
---|
| 386 | */ |
---|
| 387 | rtems_device_driver |
---|
| 388 | smc_initialize( |
---|
| 389 | rtems_device_major_number major, |
---|
| 390 | rtems_device_minor_number minor, |
---|
| 391 | void *arg) |
---|
| 392 | { |
---|
| 393 | rtems_status_code rc; |
---|
| 394 | uint32_t block_num; |
---|
| 395 | |
---|
| 396 | smc_init(); |
---|
| 397 | block_num = smc_info.blocks << 5; |
---|
| 398 | |
---|
[d279e74] | 399 | rc = rtems_blkdev_create(SMC_DEVICE_NAME, 512, block_num, smc_ioctl, NULL); |
---|
[3f5e0961] | 400 | |
---|
[d279e74] | 401 | return rc; |
---|
[3f5e0961] | 402 | } |
---|