1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
// SPDX-License-Identifier: BSD-1-Clause
/*
* Copyright (c) 2015, Atmel Corporation
* Copyright (c) 2019, Ahmad Fatoum, Pengutronix
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*/
#include <common.h>
#include <pbl/bio.h>
#include <mci.h>
#include <debug_ll.h>
#include <mach/at91/xload.h>
#include "atmel-sdhci.h"
#include <mach/at91/early_udelay.h>
#ifdef __PBL__
#define udelay early_udelay
#endif
#define SECTOR_SIZE 512
#define SUPPORT_MAX_BLOCKS 16U
struct at91_sdhci_priv {
struct at91_sdhci host;
bool highcapacity_card;
};
static int sd_cmd_stop_transmission(struct at91_sdhci_priv *priv)
{
struct mci_cmd cmd = {
.cmdidx = MMC_CMD_STOP_TRANSMISSION,
.resp_type = MMC_RSP_R1b,
};
return at91_sdhci_send_command(&priv->host, &cmd, NULL);
}
static int sd_cmd_read_multiple_block(struct at91_sdhci_priv *priv,
void *buf,
unsigned int start,
unsigned int block_count)
{
u16 block_len = SECTOR_SIZE;
struct mci_data data;
struct mci_cmd cmd = {
.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK,
.resp_type = MMC_RSP_R1,
.cmdarg = start,
};
if (!priv->highcapacity_card)
cmd.cmdarg *= block_len;
data.dest = buf;
data.flags = MMC_DATA_READ;
data.blocksize = block_len;
data.blocks = block_count;
return at91_sdhci_send_command(&priv->host, &cmd, &data);
}
static int at91_sdhci_bio_read(struct pbl_bio *bio, off_t start,
void *buf, unsigned int nblocks)
{
struct at91_sdhci_priv *priv = bio->priv;
unsigned int blocks_done = 0;
unsigned int blocks;
unsigned int block_len = SECTOR_SIZE;
unsigned int blocks_read;
int ret;
/*
* Refer to the at91sam9g20 datasheet:
* Figure 35-10. Read Function Flow Diagram
*/
while (blocks_done < nblocks) {
blocks = min(nblocks - blocks_done, SUPPORT_MAX_BLOCKS);
blocks_read = sd_cmd_read_multiple_block(priv, buf,
start + blocks_done,
blocks);
ret = sd_cmd_stop_transmission(priv);
if (ret)
return ret;
blocks_done += blocks_read;
if (blocks_read != blocks)
break;
buf += blocks * block_len;
}
return blocks_done;
}
static struct at91_sdhci_priv atmel_sdcard;
int at91_sdhci_bio_init(struct pbl_bio *bio, void __iomem *base)
{
struct at91_sdhci_priv *priv = &atmel_sdcard;
struct at91_sdhci *host = &priv->host;
struct mci_ios ios = { .bus_width = MMC_BUS_WIDTH_1, .clock = 25000000 };
int ret;
bio->priv = priv;
bio->read = at91_sdhci_bio_read;
at91_sdhci_mmio_init(host, base);
sdhci_reset(&host->sdhci, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
ret = at91_sdhci_init(host, 240000000, true, true);
if (ret)
return ret;
ret = at91_sdhci_set_ios(host, &ios);
// FIXME can we determine this without leaving SD transfer mode?
priv->highcapacity_card = 1;
return 0;
}
|