]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - drivers/block/mg_disk.c
f9e175dcb830854cf573db9a9a0313b194e5b470
[karo-tx-uboot.git] / drivers / block / mg_disk.c
1 /*
2  * (C) Copyright 2009 mGine co.
3  * unsik Kim <donari75@gmail.com>
4  *
5  * See file CREDITS for list of people who contributed to this
6  * project.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  * MA 02111-1307 USA
22  */
23
24 #include <common.h>
25 #include <malloc.h>
26 #include <part.h>
27 #include <ata.h>
28 #include <asm/io.h>
29 #include "mg_disk_prv.h"
30
31 #ifndef CONFIG_MG_DISK_RES
32 #define CONFIG_MG_DISK_RES      0
33 #endif
34
35 #define MG_RES_SEC ((CONFIG_MG_DISK_RES) << 1)
36
37 static struct mg_host host;
38
39 static inline u32 mg_base(void)
40 {
41         return host.drv_data->base;
42 }
43
44 static block_dev_desc_t mg_disk_dev = {
45         .if_type = IF_TYPE_ATAPI,
46         .part_type = PART_TYPE_UNKNOWN,
47         .type = DEV_TYPE_HARDDISK,
48         .blksz = MG_SECTOR_SIZE,
49         .priv = &host };
50
51 static void mg_dump_status (const char *msg, unsigned int stat, unsigned err)
52 {
53         char *name = MG_DEV_NAME;
54
55         printf("%s: %s: status=0x%02x { ", name, msg, stat & 0xff);
56         if (stat & MG_REG_STATUS_BIT_BUSY)
57                 printf("Busy ");
58         if (stat & MG_REG_STATUS_BIT_READY)
59                 printf("DriveReady ");
60         if (stat & MG_REG_STATUS_BIT_WRITE_FAULT)
61                 printf("WriteFault ");
62         if (stat & MG_REG_STATUS_BIT_SEEK_DONE)
63                 printf("SeekComplete ");
64         if (stat & MG_REG_STATUS_BIT_DATA_REQ)
65                 printf("DataRequest ");
66         if (stat & MG_REG_STATUS_BIT_CORRECTED_ERROR)
67                 printf("CorrectedError ");
68         if (stat & MG_REG_STATUS_BIT_ERROR)
69                 printf("Error ");
70         printf("}\n");
71
72         if ((stat & MG_REG_STATUS_BIT_ERROR)) {
73                 printf("%s: %s: error=0x%02x { ", name, msg, err & 0xff);
74                 if (err & MG_REG_ERR_BBK)
75                         printf("BadSector ");
76                 if (err & MG_REG_ERR_UNC)
77                         printf("UncorrectableError ");
78                 if (err & MG_REG_ERR_IDNF)
79                         printf("SectorIdNotFound ");
80                 if (err & MG_REG_ERR_ABRT)
81                         printf("DriveStatusError ");
82                 if (err & MG_REG_ERR_AMNF)
83                         printf("AddrMarkNotFound ");
84                 printf("}\n");
85         }
86 }
87
88 static unsigned int mg_wait (u32 expect, u32 msec)
89 {
90         u8 status;
91         u32 from, cur, err;
92
93         err = MG_ERR_NONE;
94         reset_timer();
95         from = get_timer(0);
96
97         status = readb(mg_base() + MG_REG_STATUS);
98         do {
99                 cur = get_timer(from);
100                 if (status & MG_REG_STATUS_BIT_BUSY) {
101                         if (expect == MG_REG_STATUS_BIT_BUSY)
102                                 break;
103                 } else {
104                         /* Check the error condition! */
105                         if (status & MG_REG_STATUS_BIT_ERROR) {
106                                 err = readb(mg_base() + MG_REG_ERROR);
107                                 mg_dump_status("mg_wait", status, err);
108                                 break;
109                         }
110
111                         if (expect == MG_STAT_READY)
112                                 if (MG_READY_OK(status))
113                                         break;
114
115                         if (expect == MG_REG_STATUS_BIT_DATA_REQ)
116                                 if (status & MG_REG_STATUS_BIT_DATA_REQ)
117                                         break;
118                 }
119                 status = readb(mg_base() + MG_REG_STATUS);
120         } while (cur < msec);
121
122         if (cur >= msec)
123                 err = MG_ERR_TIMEOUT;
124
125         return err;
126 }
127
128 static int mg_get_disk_id (void)
129 {
130         u16 id[(MG_SECTOR_SIZE / sizeof(u16))];
131         hd_driveid_t *iop = (hd_driveid_t *)id;
132         u32 i, err, res;
133
134         writeb(MG_CMD_ID, mg_base() + MG_REG_COMMAND);
135         err = mg_wait(MG_REG_STATUS_BIT_DATA_REQ, 3000);
136         if (err)
137                 return err;
138
139         for(i = 0; i < (MG_SECTOR_SIZE / sizeof(u16)); i++)
140                 id[i] = readw(mg_base() + MG_BUFF_OFFSET + i * 2);
141
142         writeb(MG_CMD_RD_CONF, mg_base() + MG_REG_COMMAND);
143         err = mg_wait(MG_STAT_READY, 3000);
144         if (err)
145                 return err;
146
147         ata_swap_buf_le16(id, MG_SECTOR_SIZE / sizeof(u16));
148
149         if((iop->field_valid & 1) == 0)
150                 return MG_ERR_TRANSLATION;
151
152         ata_id_c_string(id, (unsigned char *)mg_disk_dev.revision,
153                         ATA_ID_FW_REV, sizeof(mg_disk_dev.revision));
154         ata_id_c_string(id, (unsigned char *)mg_disk_dev.vendor,
155                         ATA_ID_PROD, sizeof(mg_disk_dev.vendor));
156         ata_id_c_string(id, (unsigned char *)mg_disk_dev.product,
157                         ATA_ID_SERNO, sizeof(mg_disk_dev.product));
158
159 #ifdef __BIG_ENDIAN
160         iop->lba_capacity = (iop->lba_capacity << 16) |
161                 (iop->lba_capacity >> 16);
162 #endif /* __BIG_ENDIAN */
163
164         if (MG_RES_SEC) {
165                 MG_DBG("MG_RES_SEC=%d\n", MG_RES_SEC);
166                 iop->cyls = (iop->lba_capacity - MG_RES_SEC) /
167                         iop->sectors / iop->heads;
168                 res = iop->lba_capacity -
169                         iop->cyls * iop->heads * iop->sectors;
170                 iop->lba_capacity -= res;
171                 printf("mg_disk: %d sectors reserved\n", res);
172         }
173
174         mg_disk_dev.lba = iop->lba_capacity;
175         return MG_ERR_NONE;
176 }
177
178 static int mg_disk_reset (void)
179 {
180         struct mg_drv_data *prv_data = host.drv_data;
181         s32 err;
182         u8 init_status;
183
184         /* hdd rst low */
185         prv_data->mg_hdrst_pin(0);
186         err = mg_wait(MG_REG_STATUS_BIT_BUSY, 300);
187         if(err)
188                 return err;
189
190         /* hdd rst high */
191         prv_data->mg_hdrst_pin(1);
192         err = mg_wait(MG_STAT_READY, 3000);
193         if(err)
194                 return err;
195
196         /* soft reset on */
197         writeb(MG_REG_CTRL_RESET | MG_REG_CTRL_INTR_DISABLE,
198                 mg_base() + MG_REG_DRV_CTRL);
199         err = mg_wait(MG_REG_STATUS_BIT_BUSY, 3000);
200         if(err)
201                 return err;
202
203         /* soft reset off */
204         writeb(MG_REG_CTRL_INTR_DISABLE, mg_base() + MG_REG_DRV_CTRL);
205         err = mg_wait(MG_STAT_READY, 3000);
206         if(err)
207                 return err;
208
209         init_status = readb(mg_base() + MG_REG_STATUS) & 0xf;
210
211         if (init_status == 0xf)
212                 return MG_ERR_INIT_STAT;
213
214         return err;
215 }
216
217
218 static unsigned int mg_out(unsigned int sect_num,
219                         unsigned int sect_cnt,
220                         unsigned int cmd)
221 {
222         u32 err = MG_ERR_NONE;
223
224         err = mg_wait(MG_STAT_READY, 3000);
225         if (err)
226                 return err;
227
228         writeb((u8)sect_cnt, mg_base() + MG_REG_SECT_CNT);
229         writeb((u8)sect_num, mg_base() + MG_REG_SECT_NUM);
230         writeb((u8)(sect_num >> 8), mg_base() + MG_REG_CYL_LOW);
231         writeb((u8)(sect_num >> 16), mg_base() + MG_REG_CYL_HIGH);
232         writeb((u8)((sect_num >> 24) | MG_REG_HEAD_LBA_MODE),
233                 mg_base() + MG_REG_DRV_HEAD);
234         writeb(cmd, mg_base() + MG_REG_COMMAND);
235
236         return err;
237 }
238
239 static unsigned int mg_do_read_sects(void *buff, u32 sect_num, u32 sect_cnt)
240 {
241         u32 i, j, err;
242         u8 *buff_ptr = buff;
243         union mg_uniwb uniwb;
244
245         err = mg_out(sect_num, sect_cnt, MG_CMD_RD);
246         if (err)
247                 return err;
248
249         for (i = 0; i < sect_cnt; i++) {
250                 err = mg_wait(MG_REG_STATUS_BIT_DATA_REQ, 3000);
251                 if (err)
252                         return err;
253
254                 if ((u32)buff_ptr & 1) {
255                         for (j = 0; j < MG_SECTOR_SIZE >> 1; j++) {
256                                 uniwb.w = readw(mg_base() + MG_BUFF_OFFSET
257                                                 + (j << 1));
258                                 *buff_ptr++ = uniwb.b[0];
259                                 *buff_ptr++ = uniwb.b[1];
260                         }
261                 } else {
262                         for(j = 0; j < MG_SECTOR_SIZE >> 1; j++) {
263                                 *(u16 *)buff_ptr = readw(mg_base() +
264                                                 MG_BUFF_OFFSET + (j << 1));
265                                 buff_ptr += 2;
266                         }
267                 }
268                 writeb(MG_CMD_RD_CONF, mg_base() + MG_REG_COMMAND);
269
270                 MG_DBG("%u (0x%8.8x) sector read", sect_num + i,
271                         (sect_num + i) * MG_SECTOR_SIZE);
272         }
273
274         return err;
275 }
276
277 unsigned int mg_disk_read_sects(void *buff, u32 sect_num, u32 sect_cnt)
278 {
279         u32 quotient, residue, i, err;
280         u8 *buff_ptr = buff;
281
282         quotient = sect_cnt >> 8;
283         residue = sect_cnt % 256;
284
285         for (i = 0; i < quotient; i++) {
286                 MG_DBG("sect num : %u buff : 0x%8.8x", sect_num, (u32)buff_ptr);
287                 err = mg_do_read_sects(buff_ptr, sect_num, 256);
288                 if (err)
289                         return err;
290                 sect_num += 256;
291                 buff_ptr += 256 * MG_SECTOR_SIZE;
292         }
293
294         if (residue) {
295                 MG_DBG("sect num : %u buff : %8.8x", sect_num, (u32)buff_ptr);
296                 err = mg_do_read_sects(buff_ptr, sect_num, residue);
297         }
298
299         return err;
300 }
301
302 unsigned long mg_block_read (int dev, unsigned long start,
303                 lbaint_t blkcnt, void *buffer)
304 {
305         start += MG_RES_SEC;
306         if (! mg_disk_read_sects(buffer, start, blkcnt))
307                 return blkcnt;
308         else
309                 return 0;
310 }
311
312 unsigned int mg_disk_read (u32 addr, u8 *buff, u32 len)
313 {
314         u8 *sect_buff, *buff_ptr = buff;
315         u32 cur_addr, next_sec_addr, end_addr, cnt, sect_num;
316         u32 err = MG_ERR_NONE;
317
318         /* TODO : sanity chk */
319         cnt = 0;
320         cur_addr = addr;
321         end_addr = addr + len;
322
323         sect_buff = malloc(MG_SECTOR_SIZE);
324
325         if (cur_addr & MG_SECTOR_SIZE_MASK) {
326                 next_sec_addr = (cur_addr + MG_SECTOR_SIZE) &
327                                 ~MG_SECTOR_SIZE_MASK;
328                 sect_num = cur_addr >> MG_SECTOR_SIZE_SHIFT;
329                 err = mg_disk_read_sects(sect_buff, sect_num, 1);
330                 if (err)
331                         goto mg_read_exit;
332
333                 if (end_addr < next_sec_addr) {
334                         memcpy(buff_ptr,
335                                 sect_buff + (cur_addr & MG_SECTOR_SIZE_MASK),
336                                 end_addr - cur_addr);
337                         MG_DBG("copies %u byte from sector offset 0x%8.8x",
338                                 end_addr - cur_addr, cur_addr);
339                         cur_addr = end_addr;
340                 } else {
341                         memcpy(buff_ptr,
342                                 sect_buff + (cur_addr & MG_SECTOR_SIZE_MASK),
343                                 next_sec_addr - cur_addr);
344                         MG_DBG("copies %u byte from sector offset 0x%8.8x",
345                                 next_sec_addr - cur_addr, cur_addr);
346                         buff_ptr += (next_sec_addr - cur_addr);
347                         cur_addr = next_sec_addr;
348                 }
349         }
350
351         if (cur_addr < end_addr) {
352                 sect_num = cur_addr >> MG_SECTOR_SIZE_SHIFT;
353                 cnt = ((end_addr & ~MG_SECTOR_SIZE_MASK) - cur_addr) >>
354                         MG_SECTOR_SIZE_SHIFT;
355
356                 if (cnt)
357                         err = mg_disk_read_sects(buff_ptr, sect_num, cnt);
358                 if (err)
359                         goto mg_read_exit;
360
361                 buff_ptr += cnt * MG_SECTOR_SIZE;
362                 cur_addr += cnt * MG_SECTOR_SIZE;
363
364                 if (cur_addr < end_addr) {
365                         sect_num = cur_addr >> MG_SECTOR_SIZE_SHIFT;
366                         err = mg_disk_read_sects(sect_buff, sect_num, 1);
367                         if (err)
368                                 goto mg_read_exit;
369                         memcpy(buff_ptr, sect_buff, end_addr - cur_addr);
370                         MG_DBG("copies %u byte", end_addr - cur_addr);
371                 }
372         }
373
374 mg_read_exit:
375         free(sect_buff);
376
377         return err;
378 }
379 static int mg_do_write_sects(void *buff, u32 sect_num, u32 sect_cnt)
380 {
381         u32 i, j, err;
382         u8 *buff_ptr = buff;
383         union mg_uniwb uniwb;
384
385         err = mg_out(sect_num, sect_cnt, MG_CMD_WR);
386         if (err)
387                 return err;
388
389         for (i = 0; i < sect_cnt; i++) {
390                 err = mg_wait(MG_REG_STATUS_BIT_DATA_REQ, 3000);
391                 if (err)
392                         return err;
393
394                 if ((u32)buff_ptr & 1) {
395                         uniwb.b[0] = *buff_ptr++;
396                         uniwb.b[1] = *buff_ptr++;
397                         writew(uniwb.w, mg_base() + MG_BUFF_OFFSET + (j << 1));
398                 } else {
399                         for(j = 0; j < MG_SECTOR_SIZE >> 1; j++) {
400                                 writew(*(u16 *)buff_ptr,
401                                                 mg_base() + MG_BUFF_OFFSET +
402                                                 (j << 1));
403                                 buff_ptr += 2;
404                         }
405                 }
406                 writeb(MG_CMD_WR_CONF, mg_base() + MG_REG_COMMAND);
407
408                 MG_DBG("%u (0x%8.8x) sector write",
409                         sect_num + i, (sect_num + i) * MG_SECTOR_SIZE);
410         }
411
412         return err;
413 }
414
415 unsigned int mg_disk_write_sects(void *buff, u32 sect_num, u32 sect_cnt)
416 {
417         u32 quotient, residue, i;
418         u32 err = MG_ERR_NONE;
419         u8 *buff_ptr = buff;
420
421         quotient = sect_cnt >> 8;
422         residue = sect_cnt % 256;
423
424         for (i = 0; i < quotient; i++) {
425                 MG_DBG("sect num : %u buff : %8.8x", sect_num, (u32)buff_ptr);
426                 err = mg_do_write_sects(buff_ptr, sect_num, 256);
427                 if (err)
428                         return err;
429                 sect_num += 256;
430                 buff_ptr += 256 * MG_SECTOR_SIZE;
431         }
432
433         if (residue) {
434                 MG_DBG("sect num : %u buff : %8.8x", sect_num, (u32)buff_ptr);
435                 err = mg_do_write_sects(buff_ptr, sect_num, residue);
436         }
437
438         return err;
439 }
440
441 unsigned long mg_block_write (int dev, unsigned long start,
442                 lbaint_t blkcnt, const void *buffer)
443 {
444         start += MG_RES_SEC;
445         if (!mg_disk_write_sects((void *)buffer, start, blkcnt))
446                 return blkcnt;
447         else
448                 return 0;
449 }
450
451 unsigned int mg_disk_write(u32 addr, u8 *buff, u32 len)
452 {
453         u8 *sect_buff, *buff_ptr = buff;
454         u32 cur_addr, next_sec_addr, end_addr, cnt, sect_num;
455         u32 err = MG_ERR_NONE;
456
457         /* TODO : sanity chk */
458         cnt = 0;
459         cur_addr = addr;
460         end_addr = addr + len;
461
462         sect_buff = malloc(MG_SECTOR_SIZE);
463
464         if (cur_addr & MG_SECTOR_SIZE_MASK) {
465
466                 next_sec_addr = (cur_addr + MG_SECTOR_SIZE) &
467                                 ~MG_SECTOR_SIZE_MASK;
468                 sect_num = cur_addr >> MG_SECTOR_SIZE_SHIFT;
469                 err = mg_disk_read_sects(sect_buff, sect_num, 1);
470                 if (err)
471                         goto mg_write_exit;
472
473                 if (end_addr < next_sec_addr) {
474                         memcpy(sect_buff + (cur_addr & MG_SECTOR_SIZE_MASK),
475                                 buff_ptr, end_addr - cur_addr);
476                         MG_DBG("copies %u byte to sector offset 0x%8.8x",
477                                 end_addr - cur_addr, cur_addr);
478                         cur_addr = end_addr;
479                 } else {
480                         memcpy(sect_buff + (cur_addr & MG_SECTOR_SIZE_MASK),
481                                 buff_ptr, next_sec_addr - cur_addr);
482                         MG_DBG("copies %u byte to sector offset 0x%8.8x",
483                                 next_sec_addr - cur_addr, cur_addr);
484                         buff_ptr += (next_sec_addr - cur_addr);
485                         cur_addr = next_sec_addr;
486                 }
487
488                 err = mg_disk_write_sects(sect_buff, sect_num, 1);
489                 if (err)
490                         goto mg_write_exit;
491         }
492
493         if (cur_addr < end_addr) {
494
495                 sect_num = cur_addr >> MG_SECTOR_SIZE_SHIFT;
496                 cnt = ((end_addr & ~MG_SECTOR_SIZE_MASK) - cur_addr) >>
497                         MG_SECTOR_SIZE_SHIFT;
498
499                 if (cnt)
500                         err = mg_disk_write_sects(buff_ptr, sect_num, cnt);
501                 if (err)
502                         goto mg_write_exit;
503
504                 buff_ptr += cnt * MG_SECTOR_SIZE;
505                 cur_addr += cnt * MG_SECTOR_SIZE;
506
507                 if (cur_addr < end_addr) {
508                         sect_num = cur_addr >> MG_SECTOR_SIZE_SHIFT;
509                         err = mg_disk_read_sects(sect_buff, sect_num, 1);
510                         if (err)
511                                 goto mg_write_exit;
512                         memcpy(sect_buff, buff_ptr, end_addr - cur_addr);
513                         MG_DBG("copies %u byte", end_addr - cur_addr);
514                         err = mg_disk_write_sects(sect_buff, sect_num, 1);
515                 }
516
517         }
518
519 mg_write_exit:
520         free(sect_buff);
521
522         return err;
523 }
524
525 #ifdef CONFIG_PARTITIONS
526 block_dev_desc_t *mg_disk_get_dev(int dev)
527 {
528         return ((block_dev_desc_t *) & mg_disk_dev);
529 }
530 #endif
531
532 /* must override this function */
533 struct mg_drv_data * __attribute__((weak)) mg_get_drv_data (void)
534 {
535         puts ("### WARNING ### port mg_get_drv_data function\n");
536         return NULL;
537 }
538
539 unsigned int mg_disk_init (void)
540 {
541         struct mg_drv_data *prv_data;
542         u32 err = MG_ERR_NONE;
543
544         prv_data = mg_get_drv_data();
545         if (! prv_data) {
546                 printf("%s:%d fail (no driver_data)\n", __func__, __LINE__);
547                 err = MG_ERR_NO_DRV_DATA;
548                 return err;
549         }
550
551         ((struct mg_host *)mg_disk_dev.priv)->drv_data = prv_data;
552
553         /* init ctrl pin */
554         if (prv_data->mg_ctrl_pin_init)
555                 prv_data->mg_ctrl_pin_init();
556
557         if (! prv_data->mg_hdrst_pin) {
558                 err = MG_ERR_CTRL_RST;
559                 return err;
560         }
561
562         /* disk reset */
563         err = mg_disk_reset();
564         if (err) {
565                 printf("%s:%d fail (err code : %d)\n", __func__, __LINE__, err);
566                 return err;
567         }
568
569         /* get disk id */
570         err = mg_get_disk_id();
571         if (err) {
572                 printf("%s:%d fail (err code : %d)\n", __func__, __LINE__, err);
573                 return err;
574         }
575
576         mg_disk_dev.block_read = mg_block_read;
577         mg_disk_dev.block_write = mg_block_write;
578
579         init_part(&mg_disk_dev);
580
581         dev_print(&mg_disk_dev);
582
583         return err;
584 }