source: rtems/bsps/arm/smdk2410/smc/smc.c @ d279e74

Last change on this file since d279e74 was d279e74, checked in by Sebastian Huber <sebastian.huber@…>, on Aug 1, 2018 at 4:14:14 AM

bsp/smdk2410: Use rtems_blkdev_create()

Update #3358.

  • Property mode set to 100644
File size: 8.6 KB
Line 
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
17#include <rtems/blkdev.h>
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 */
52struct 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
69static struct SMC_INFO smc_info;
70
71uint32_t smc_l2p[0x2000];
72uint32_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
88static void sm_write( uint8_t data)
89{
90  rPBDAT = (rPBDAT & 0xFF00) | data;
91  rPEDAT &= (~0x08);
92  rPEDAT |= 0x08;
93}
94
95static 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
105static 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.*/
131static 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
178static 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
211static void smc_make_l2p(void)
212{
213  uint32_t pblock, i, j, lblock, zone, count;
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
245static 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
276static 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]);
295  printk ("SMC size: %" PRIu32 "MB detected\n",smc_info.mb);
296  smc_make_l2p();
297}
298
299/* smc_write --
300 * write stub
301 */
302static 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 */
316static int
317smc_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 */
348static int
349smc_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 */
387rtems_device_driver
388smc_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
399    rc = rtems_blkdev_create(SMC_DEVICE_NAME, 512, block_num, smc_ioctl, NULL);
400
401    return rc;
402}
Note: See TracBrowser for help on using the repository browser.