Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/*
2
 * mmc / sd memory card
3
 *
4
 * Copyright © 2012 Richard Miller <r.miller@acm.org>
5
 *
6
 * Assumes only one card on the bus
7
 */
8
 
9
#include "u.h"
10
#include "../port/lib.h"
11
#include "../port/error.h"
12
#include "mem.h"
13
#include "dat.h"
14
#include "fns.h"
15
#include "io.h"
16
 
17
#include "../port/sd.h"
18
 
19
#define CSD(end, start)	rbits(csd, start, (end)-(start)+1)
20
 
21
typedef struct Ctlr Ctlr;
22
 
23
enum {
24
	Inittimeout	= 15,
25
	Multiblock	= 1,
26
 
27
	/* Commands */
28
	GO_IDLE_STATE	= 0,
29
	ALL_SEND_CID	= 2,
30
	SEND_RELATIVE_ADDR= 3,
31
	SELECT_CARD	= 7,
32
	SD_SEND_IF_COND	= 8,
33
	SEND_CSD	= 9,
34
	STOP_TRANSMISSION= 12,
35
	SEND_STATUS	= 13,
36
	SET_BLOCKLEN	= 16,
37
	READ_SINGLE_BLOCK= 17,
38
	READ_MULTIPLE_BLOCK= 18,
39
	WRITE_BLOCK	= 24,
40
	WRITE_MULTIPLE_BLOCK= 25,
41
	APP_CMD		= 55,	/* prefix for following app-specific commands */
42
	SET_BUS_WIDTH	= 6,
43
	SD_SEND_OP_COND	= 41,
44
 
45
	/* Command arguments */
46
	/* SD_SEND_IF_COND */
47
	Voltage		= 1<<8,
48
	Checkpattern	= 0x42,
49
 
50
	/* SELECT_CARD */
51
	Rcashift	= 16,
52
 
53
	/* SD_SEND_OP_COND */
54
	Hcs	= 1<<30,	/* host supports SDHC & SDXC */
55
	Ccs	= 1<<30,	/* card is SDHC or SDXC */
56
	V3_3	= 3<<20,	/* 3.2-3.4 volts */
57
 
58
	/* SET_BUS_WIDTH */
59
	Width1	= 0<<0,
60
	Width4	= 2<<0,
61
 
62
	/* OCR (operating conditions register) */
63
	Powerup	= 1<<31,
64
};
65
 
66
struct Ctlr {
67
	SDev	*dev;
68
	SDio	*io;
69
	/* SD card registers */
70
	u16int	rca;
71
	u32int	ocr;
72
	u32int	cid[4];
73
	u32int	csd[4];
74
};
75
 
76
extern SDifc sdmmcifc;
77
extern SDio sdio;
78
 
79
static uint
80
rbits(u32int *p, uint start, uint len)
81
{
82
	uint w, off, v;
83
 
84
	w   = start / 32;
85
	off = start % 32;
86
	if(off == 0)
87
		v = p[w];
88
	else
89
		v = p[w] >> off | p[w+1] << (32-off);
90
	if(len < 32)
91
		return v & ((1<<len) - 1);
92
	else
93
		return v;
94
}
95
 
96
static void
97
identify(SDunit *unit, u32int *csd)
98
{
99
	uint csize, mult;
100
 
101
	unit->secsize = 1 << CSD(83, 80);
102
	switch(CSD(127, 126)){
103
	case 0:				/* CSD version 1 */
104
		csize = CSD(73, 62);
105
		mult = CSD(49, 47);
106
		unit->sectors = (csize+1) * (1<<(mult+2));
107
		break;
108
	case 1:				/* CSD version 2 */
109
		csize = CSD(69, 48);
110
		unit->sectors = (csize+1) * 512LL*KiB / unit->secsize;
111
		break;
112
	}
113
	if(unit->secsize == 1024){
114
		unit->sectors <<= 1;
115
		unit->secsize = 512;
116
	}
117
}
118
 
119
static SDev*
120
mmcpnp(void)
121
{
122
	SDev *sdev;
123
	Ctlr *ctl;
124
 
125
	if(sdio.init() < 0)
126
		return nil;
127
	sdev = malloc(sizeof(SDev));
128
	if(sdev == nil)
129
		return nil;
130
	ctl = malloc(sizeof(Ctlr));
131
	if(ctl == nil){
132
		free(sdev);
133
		return nil;
134
	}
135
	sdev->idno = 'M';
136
	sdev->ifc = &sdmmcifc;
137
	sdev->nunit = 1;
138
	sdev->ctlr = ctl;
139
	ctl->dev = sdev;
140
	ctl->io = &sdio;
141
	return sdev;
142
}
143
 
144
static int
145
mmcverify(SDunit *unit)
146
{
147
	int n;
148
	Ctlr *ctl;
149
 
150
	ctl = unit->dev->ctlr;
151
	n = ctl->io->inquiry((char*)&unit->inquiry[8], sizeof(unit->inquiry)-8);
152
	if(n < 0)
153
		return 0;
154
	unit->inquiry[0] = SDperdisk;
155
	unit->inquiry[1] = SDinq1removable;
156
	unit->inquiry[4] = sizeof(unit->inquiry)-4;
157
	return 1;
158
}
159
 
160
static int
161
mmcenable(SDev* dev)
162
{
163
	Ctlr *ctl;
164
 
165
	ctl = dev->ctlr;
166
	ctl->io->enable();
167
	return 1;
168
}
169
 
170
static int
171
mmconline(SDunit *unit)
172
{
173
	int hcs, i;
174
	u32int r[4];
175
	Ctlr *ctl;
176
	SDio *io;
177
 
178
	ctl = unit->dev->ctlr;
179
	io = ctl->io;
180
	assert(unit->subno == 0);
181
 
182
	if(waserror()){
183
		unit->sectors = 0;
184
		return 0;
185
	}
186
	if(unit->sectors != 0){
187
		io->cmd(SEND_STATUS, ctl->rca<<Rcashift, r);
188
		poperror();
189
		return 1;
190
	}
191
	io->cmd(GO_IDLE_STATE, 0, r);
192
	hcs = 0;
193
	if(!waserror()){
194
		io->cmd(SD_SEND_IF_COND, Voltage|Checkpattern, r);
195
		if(r[0] == (Voltage|Checkpattern))	/* SD 2.0 or above */
196
			hcs = Hcs;
197
		poperror();
198
	}
199
	for(i = 0; i < Inittimeout; i++){
200
		delay(100);
201
		io->cmd(APP_CMD, 0, r);
202
		io->cmd(SD_SEND_OP_COND, hcs|V3_3, r);
203
		if(r[0] & Powerup)
204
			break;
205
	}
206
	if(i == Inittimeout){
207
		print("sdmmc: card won't power up\n");
208
		poperror();
209
		return 2;
210
	}
211
	ctl->ocr = r[0];
212
	io->cmd(ALL_SEND_CID, 0, r);
213
	memmove(ctl->cid, r, sizeof ctl->cid);
214
	io->cmd(SEND_RELATIVE_ADDR, 0, r);
215
	ctl->rca = r[0]>>16;
216
	io->cmd(SEND_CSD, ctl->rca<<Rcashift, r);
217
	memmove(ctl->csd, r, sizeof ctl->csd);
218
	identify(unit, ctl->csd);
219
	io->cmd(SELECT_CARD, ctl->rca<<Rcashift, r);
220
	io->cmd(SET_BLOCKLEN, unit->secsize, r);
221
	io->cmd(APP_CMD, ctl->rca<<Rcashift, r);
222
	io->cmd(SET_BUS_WIDTH, Width4, r);
223
	poperror();
224
	return 1;
225
}
226
 
227
static int
228
mmcrctl(SDunit *unit, char *p, int l)
229
{
230
	Ctlr *ctl;
231
	int i, n;
232
 
233
	ctl = unit->dev->ctlr;
234
	assert(unit->subno == 0);
235
	if(unit->sectors == 0){
236
		mmconline(unit);
237
		if(unit->sectors == 0)
238
			return 0;
239
	}
240
	n = snprint(p, l, "rca %4.4ux ocr %8.8ux\ncid ", ctl->rca, ctl->ocr);
241
	for(i = nelem(ctl->cid)-1; i >= 0; i--)
242
		n += snprint(p+n, l-n, "%8.8ux", ctl->cid[i]);
243
	n += snprint(p+n, l-n, " csd ");
244
	for(i = nelem(ctl->csd)-1; i >= 0; i--)
245
		n += snprint(p+n, l-n, "%8.8ux", ctl->csd[i]);
246
	n += snprint(p+n, l-n, "\ngeometry %llud %ld\n",
247
		unit->sectors, unit->secsize);
248
	return n;
249
}
250
 
251
static long
252
mmcbio(SDunit *unit, int lun, int write, void *data, long nb, uvlong bno)
253
{
254
	int len, tries;
255
	ulong b;
256
	u32int r[4];
257
	uchar *buf;
258
	Ctlr *ctl;
259
	SDio *io;
260
 
261
	USED(lun);
262
	ctl = unit->dev->ctlr;
263
	io = ctl->io;
264
	assert(unit->subno == 0);
265
	if(unit->sectors == 0)
266
		error("media change");
267
	buf = data;
268
	len = unit->secsize;
269
	if(Multiblock){
270
		b = bno;
271
		tries = 0;
272
		while(waserror())
273
			if(++tries == 3)
274
				nexterror();
275
		io->iosetup(write, buf, len, nb);
276
		if(waserror()){
277
			io->cmd(STOP_TRANSMISSION, 0, r);
278
			nexterror();
279
		}
280
		io->cmd(write? WRITE_MULTIPLE_BLOCK: READ_MULTIPLE_BLOCK,
281
			ctl->ocr & Ccs? b: b * len, r);
282
		io->io(write, buf, nb * len);
283
		poperror();
284
		io->cmd(STOP_TRANSMISSION, 0, r);
285
		poperror();
286
		b += nb;
287
	}else{
288
		for(b = bno; b < bno + nb; b++){
289
			io->iosetup(write, buf, len, 1);
290
			io->cmd(write? WRITE_BLOCK : READ_SINGLE_BLOCK,
291
				ctl->ocr & Ccs? b: b * len, r);
292
			io->io(write, buf, len);
293
			buf += len;
294
		}
295
	}
296
	return (b - bno) * len;
297
}
298
 
299
static int
300
mmcrio(SDreq*)
301
{
302
	return -1;
303
}
304
 
305
SDifc sdmmcifc = {
306
	.name	= "mmc",
307
	.pnp	= mmcpnp,
308
	.enable	= mmcenable,
309
	.verify	= mmcverify,
310
	.online	= mmconline,
311
	.rctl	= mmcrctl,
312
	.bio	= mmcbio,
313
	.rio	= mmcrio,
314
};