Blame view

drivers/mmc/mmc_write.c 4.91 KB
da61fa5f4   Paul Burton   mmc: don't suppor...
1
2
3
4
5
6
7
8
9
10
11
  /*
   * Copyright 2008, Freescale Semiconductor, Inc
   * Andy Fleming
   *
   * Based vaguely on the Linux code
   *
   * SPDX-License-Identifier:	GPL-2.0+
   */
  
  #include <config.h>
  #include <common.h>
33fb211dd   Simon Glass   dm: mmc: Add supp...
12
  #include <dm.h>
da61fa5f4   Paul Burton   mmc: don't suppor...
13
  #include <part.h>
180f87fcc   Tom Rini   mmc_write.c: Make...
14
15
  #include <div64.h>
  #include <linux/math64.h>
da61fa5f4   Paul Burton   mmc: don't suppor...
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
  #include "mmc_private.h"
  
  static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt)
  {
  	struct mmc_cmd cmd;
  	ulong end;
  	int err, start_cmd, end_cmd;
  
  	if (mmc->high_capacity) {
  		end = start + blkcnt - 1;
  	} else {
  		end = (start + blkcnt - 1) * mmc->write_bl_len;
  		start *= mmc->write_bl_len;
  	}
  
  	if (IS_SD(mmc)) {
  		start_cmd = SD_CMD_ERASE_WR_BLK_START;
  		end_cmd = SD_CMD_ERASE_WR_BLK_END;
  	} else {
  		start_cmd = MMC_CMD_ERASE_GROUP_START;
  		end_cmd = MMC_CMD_ERASE_GROUP_END;
  	}
  
  	cmd.cmdidx = start_cmd;
  	cmd.cmdarg = start;
  	cmd.resp_type = MMC_RSP_R1;
  
  	err = mmc_send_cmd(mmc, &cmd, NULL);
  	if (err)
  		goto err_out;
  
  	cmd.cmdidx = end_cmd;
  	cmd.cmdarg = end;
  
  	err = mmc_send_cmd(mmc, &cmd, NULL);
  	if (err)
  		goto err_out;
  
  	cmd.cmdidx = MMC_CMD_ERASE;
1aa2d074a   Eric Nelson   mmc: update MMC_E...
55
  	cmd.cmdarg = MMC_ERASE_ARG;
da61fa5f4   Paul Burton   mmc: don't suppor...
56
57
58
59
60
61
62
63
64
65
66
67
68
  	cmd.resp_type = MMC_RSP_R1b;
  
  	err = mmc_send_cmd(mmc, &cmd, NULL);
  	if (err)
  		goto err_out;
  
  	return 0;
  
  err_out:
  	puts("mmc erase failed
  ");
  	return err;
  }
561e624c3   Simon Glass   dm: mmc: Support ...
69
70
71
72
73
  #ifdef CONFIG_BLK
  ulong mmc_berase(struct udevice *dev, lbaint_t start, lbaint_t blkcnt)
  #else
  ulong mmc_berase(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt)
  #endif
da61fa5f4   Paul Burton   mmc: don't suppor...
74
  {
561e624c3   Simon Glass   dm: mmc: Support ...
75
76
77
  #ifdef CONFIG_BLK
  	struct blk_desc *block_dev = dev_get_uclass_platdata(dev);
  #endif
bcce53d04   Simon Glass   dm: block: Rename...
78
  	int dev_num = block_dev->devnum;
da61fa5f4   Paul Burton   mmc: don't suppor...
79
  	int err = 0;
180f87fcc   Tom Rini   mmc_write.c: Make...
80
  	u32 start_rem, blkcnt_rem;
da61fa5f4   Paul Burton   mmc: don't suppor...
81
82
83
84
85
86
  	struct mmc *mmc = find_mmc_device(dev_num);
  	lbaint_t blk = 0, blk_r = 0;
  	int timeout = 1000;
  
  	if (!mmc)
  		return -1;
69f45cd53   Simon Glass   dm: mmc: Use the ...
87
88
  	err = blk_select_hwpart_devnum(IF_TYPE_MMC, dev_num,
  				       block_dev->hwpart);
873cc1d77   Stephen Warren   mmc: store hwpart...
89
90
  	if (err < 0)
  		return -1;
180f87fcc   Tom Rini   mmc_write.c: Make...
91
92
93
94
95
96
97
98
  	/*
  	 * We want to see if the requested start or total block count are
  	 * unaligned.  We discard the whole numbers and only care about the
  	 * remainder.
  	 */
  	err = div_u64_rem(start, mmc->erase_grp_size, &start_rem);
  	err = div_u64_rem(blkcnt, mmc->erase_grp_size, &blkcnt_rem);
  	if (start_rem || blkcnt_rem)
da61fa5f4   Paul Burton   mmc: don't suppor...
99
100
101
102
103
104
105
106
107
108
109
110
111
  		printf("
  
  Caution! Your devices Erase group is 0x%x
  "
  		       "The erase range would be change to "
  		       "0x" LBAF "~0x" LBAF "
  
  ",
  		       mmc->erase_grp_size, start & ~(mmc->erase_grp_size - 1),
  		       ((start + blkcnt + mmc->erase_grp_size)
  		       & ~(mmc->erase_grp_size - 1)) - 1);
  
  	while (blk < blkcnt) {
e492dbb41   Peng Fan   mmc: sd: optimize...
112
113
114
115
116
117
118
  		if (IS_SD(mmc) && mmc->ssr.au) {
  			blk_r = ((blkcnt - blk) > mmc->ssr.au) ?
  				mmc->ssr.au : (blkcnt - blk);
  		} else {
  			blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ?
  				mmc->erase_grp_size : (blkcnt - blk);
  		}
da61fa5f4   Paul Burton   mmc: don't suppor...
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
  		err = mmc_erase_t(mmc, start + blk, blk_r);
  		if (err)
  			break;
  
  		blk += blk_r;
  
  		/* Waiting for the ready status */
  		if (mmc_send_status(mmc, timeout))
  			return 0;
  	}
  
  	return blk;
  }
  
  static ulong mmc_write_blocks(struct mmc *mmc, lbaint_t start,
  		lbaint_t blkcnt, const void *src)
  {
  	struct mmc_cmd cmd;
  	struct mmc_data data;
  	int timeout = 1000;
c40fdca6b   Simon Glass   dm: mmc: Move the...
139
  	if ((start + blkcnt) > mmc_get_blk_desc(mmc)->lba) {
da61fa5f4   Paul Burton   mmc: don't suppor...
140
141
  		printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")
  ",
c40fdca6b   Simon Glass   dm: mmc: Move the...
142
  		       start + blkcnt, mmc_get_blk_desc(mmc)->lba);
da61fa5f4   Paul Burton   mmc: don't suppor...
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
  		return 0;
  	}
  
  	if (blkcnt == 0)
  		return 0;
  	else if (blkcnt == 1)
  		cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK;
  	else
  		cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
  
  	if (mmc->high_capacity)
  		cmd.cmdarg = start;
  	else
  		cmd.cmdarg = start * mmc->write_bl_len;
  
  	cmd.resp_type = MMC_RSP_R1;
  
  	data.src = src;
  	data.blocks = blkcnt;
  	data.blocksize = mmc->write_bl_len;
  	data.flags = MMC_DATA_WRITE;
  
  	if (mmc_send_cmd(mmc, &cmd, &data)) {
  		printf("mmc write failed
  ");
  		return 0;
  	}
  
  	/* SPI multiblock writes terminate using a special
  	 * token, not a STOP_TRANSMISSION request.
  	 */
  	if (!mmc_host_is_spi(mmc) && blkcnt > 1) {
  		cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
  		cmd.cmdarg = 0;
  		cmd.resp_type = MMC_RSP_R1b;
  		if (mmc_send_cmd(mmc, &cmd, NULL)) {
  			printf("mmc fail to send stop cmd
  ");
  			return 0;
  		}
  	}
  
  	/* Waiting for the ready status */
  	if (mmc_send_status(mmc, timeout))
  		return 0;
  
  	return blkcnt;
  }
33fb211dd   Simon Glass   dm: mmc: Add supp...
191
192
193
194
  #ifdef CONFIG_BLK
  ulong mmc_bwrite(struct udevice *dev, lbaint_t start, lbaint_t blkcnt,
  		 const void *src)
  #else
4101f6879   Simon Glass   dm: Drop the bloc...
195
  ulong mmc_bwrite(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt,
7c4213f6a   Stephen Warren   block: pass block...
196
  		 const void *src)
33fb211dd   Simon Glass   dm: mmc: Add supp...
197
  #endif
da61fa5f4   Paul Burton   mmc: don't suppor...
198
  {
33fb211dd   Simon Glass   dm: mmc: Add supp...
199
200
201
  #ifdef CONFIG_BLK
  	struct blk_desc *block_dev = dev_get_uclass_platdata(dev);
  #endif
bcce53d04   Simon Glass   dm: block: Rename...
202
  	int dev_num = block_dev->devnum;
da61fa5f4   Paul Burton   mmc: don't suppor...
203
  	lbaint_t cur, blocks_todo = blkcnt;
873cc1d77   Stephen Warren   mmc: store hwpart...
204
  	int err;
da61fa5f4   Paul Burton   mmc: don't suppor...
205
206
207
208
  
  	struct mmc *mmc = find_mmc_device(dev_num);
  	if (!mmc)
  		return 0;
69f45cd53   Simon Glass   dm: mmc: Use the ...
209
  	err = blk_select_hwpart_devnum(IF_TYPE_MMC, dev_num, block_dev->hwpart);
873cc1d77   Stephen Warren   mmc: store hwpart...
210
211
  	if (err < 0)
  		return 0;
da61fa5f4   Paul Burton   mmc: don't suppor...
212
213
214
215
  	if (mmc_set_blocklen(mmc, mmc->write_bl_len))
  		return 0;
  
  	do {
93bfd6167   Pantelis Antoniou   mmc: Split mmc st...
216
217
  		cur = (blocks_todo > mmc->cfg->b_max) ?
  			mmc->cfg->b_max : blocks_todo;
da61fa5f4   Paul Burton   mmc: don't suppor...
218
219
220
221
222
223
224
225
226
  		if (mmc_write_blocks(mmc, start, cur, src) != cur)
  			return 0;
  		blocks_todo -= cur;
  		start += cur;
  		src += cur * mmc->write_bl_len;
  	} while (blocks_todo > 0);
  
  	return blkcnt;
  }