Add nand driver that works with a nand simulator
[yaffs2.git] / direct / test-framework / nanddrv.c
1 /*
2  * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
3  *
4  * Copyright (C) 2010-2011 Aleph One Ltd.
5  *
6  * Created by Charles Manning <charles@aleph1.co.uk>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11  */
12
13
14 #include "nanddrv.h"
15 #include "nand_chip.h"
16
17 int nanddrv_initialise(void)
18 {
19         return 0;
20 }
21
22 static void nanddrv_send_addr(struct nand_chip *this, int page, int offset)
23 {
24         this->set_ale(this,1);
25         if(offset >= 0){
26             this->write_cycle(this, offset & 0xff);
27             this->write_cycle(this, (offset>>8) & 0x0f);
28         }
29
30         if(page >= 0){
31             this->write_cycle(this, page & 0xff);
32             this->write_cycle(this, (page>>8) & 0xff);
33             this->write_cycle(this, (page>>16) & 0xff);
34         }
35         this->set_ale(this,0);
36 }
37
38 static void nanddrv_send_cmd(struct nand_chip *this, unsigned char cmd)
39 {
40         this->set_cle(this, 1);
41         this->write_cycle(this, cmd);
42         this->set_cle(this, 0);
43 }
44
45
46 static inline int nanddrv_status_pass(unsigned char status)
47 {
48         /* If bit 0 is zero then pass, if 1 then fail */
49         return (status & (1 << 0)) == 0;
50 }
51
52 static inline int nanddrv_status_busy(unsigned char status)
53 {
54         /* If bit 6 is zero then busy, if 1 then ready */
55         return (status & (1 << 6)) == 0;
56 }
57
58 static unsigned char nanddrv_get_status(struct nand_chip *this,
59                                         int wait_not_busy)
60 {
61         unsigned char status;
62
63         nanddrv_send_cmd(this, 0x70);
64         status = this->read_cycle(this) & 0xff;
65         if(!wait_not_busy)
66                 return status;
67         while (nanddrv_status_busy(status)) {
68                 status = this->read_cycle(this) & 0xff;
69         }
70         return status;
71 }
72
73 int nanddrv_read_tr(struct nand_chip *this, int page,
74                 struct nanddrv_transfer *tr, int n_tr)
75 {
76         unsigned char status;
77         int ncycles;
78         unsigned char *buffer;
79
80         if(n_tr < 1)
81                 return 0;
82
83         nanddrv_send_cmd(this, 0x00);
84         nanddrv_send_addr(this, page, tr->offset);
85         nanddrv_send_cmd(this, 0x30);
86         status = nanddrv_get_status(this, 1);
87         if(!nanddrv_status_pass(status))
88                 return -1;
89         nanddrv_send_cmd(this, 0x30);
90         while (1) {
91                 if(this->bus_width_shift == 0) {
92                         unsigned char *buffer = tr->buffer;
93
94                         ncycles = tr->nbytes;
95                         while (ncycles> 0) {
96                                 *buffer = this->read_cycle(this);
97                                 ncycles--;
98                                 buffer++;
99                         }
100                 } else {
101                         unsigned short *buffer = tr->buffer;
102
103                         ncycles = tr->nbytes >> 1;
104                         while (ncycles> 0) {
105                                 *buffer = this->read_cycle(this);
106                                 ncycles--;
107                                 buffer++;
108                         }
109                 }
110                 n_tr--;
111                 tr++;
112                 if(n_tr < 1)
113                         break;
114                 nanddrv_send_cmd(this, 0x05);
115                 nanddrv_send_addr(this, -1, tr->offset);
116                 nanddrv_send_cmd(this, 0xE0);
117         }
118         return 0;
119 }
120
121
122 /*
123  * Program page
124  * Cmd: 0x80, 5-byte address, data bytes,  Cmd: 0x10, wait not busy
125  */
126 int nanddrv_write_tr(struct nand_chip *this, int page,
127                 struct nanddrv_transfer *tr, int n_tr)
128 {
129         unsigned char status;
130         int ncycles;
131
132         if (n_tr < 1)
133                 return 0;
134
135         nanddrv_send_cmd(this, 0x80);
136         nanddrv_send_addr(this, page, tr->offset);
137         while (1) {
138                 if(this->bus_width_shift == 0) {
139                         unsigned char *buffer = tr->buffer;
140
141                         ncycles = tr->nbytes;
142                         while (ncycles> 0) {
143                                 this->write_cycle(this, *buffer);
144                                 ncycles--;
145                                 buffer++;
146                         }
147                 } else {
148                         unsigned short *buffer = tr->buffer;
149
150                         ncycles = tr->nbytes >> 1;
151                         while (ncycles> 0) {
152                                 this->write_cycle(this, *buffer);
153                                 ncycles--;
154                                 buffer++;
155                         }
156                 }
157                 n_tr--;
158                 tr++;
159                 if (n_tr < 1)
160                         break;
161                 nanddrv_send_cmd(this, 0x85);
162                 nanddrv_send_addr(this, -1, tr->offset);
163         }
164
165         if(this->power_check && this->power_check(this) < 0)
166                 return -1;
167
168         nanddrv_send_cmd(this, 0x10);
169         status = nanddrv_get_status(this, 1);
170         if(nanddrv_status_pass(status))
171                 return 0;
172         return -1;
173 }
174
175 /*
176  * Block erase
177  * Cmd: 0x60, 3-byte address, cmd: 0xD0. Wait not busy.
178  */
179 int nanddrv_erase(struct nand_chip *this, int block)
180 {
181         unsigned char status;
182
183         nanddrv_send_cmd(this, 0x60);
184         nanddrv_send_addr(this, block * this->pages_per_block, -1);
185
186         if(this->power_check && this->power_check(this) < 0)
187                 return -1;
188
189         nanddrv_send_cmd(this, 0xD0);
190         status = nanddrv_get_status(this, 1);
191         if(nanddrv_status_pass(status))
192                 return 0;
193         return -1;
194 }
195
196
197