]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - drivers/mmc/mmc_write.c
mmc_write.c: Make mmc_berase do 32bit safe 64bit math
[karo-tx-uboot.git] / drivers / mmc / mmc_write.c
1 /*
2  * Copyright 2008, Freescale Semiconductor, Inc
3  * Andy Fleming
4  *
5  * Based vaguely on the Linux code
6  *
7  * SPDX-License-Identifier:     GPL-2.0+
8  */
9
10 #include <config.h>
11 #include <common.h>
12 #include <part.h>
13 #include <div64.h>
14 #include <linux/math64.h>
15 #include "mmc_private.h"
16
17 static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt)
18 {
19         struct mmc_cmd cmd;
20         ulong end;
21         int err, start_cmd, end_cmd;
22
23         if (mmc->high_capacity) {
24                 end = start + blkcnt - 1;
25         } else {
26                 end = (start + blkcnt - 1) * mmc->write_bl_len;
27                 start *= mmc->write_bl_len;
28         }
29
30         if (IS_SD(mmc)) {
31                 start_cmd = SD_CMD_ERASE_WR_BLK_START;
32                 end_cmd = SD_CMD_ERASE_WR_BLK_END;
33         } else {
34                 start_cmd = MMC_CMD_ERASE_GROUP_START;
35                 end_cmd = MMC_CMD_ERASE_GROUP_END;
36         }
37
38         cmd.cmdidx = start_cmd;
39         cmd.cmdarg = start;
40         cmd.resp_type = MMC_RSP_R1;
41
42         err = mmc_send_cmd(mmc, &cmd, NULL);
43         if (err)
44                 goto err_out;
45
46         cmd.cmdidx = end_cmd;
47         cmd.cmdarg = end;
48
49         err = mmc_send_cmd(mmc, &cmd, NULL);
50         if (err)
51                 goto err_out;
52
53         cmd.cmdidx = MMC_CMD_ERASE;
54         cmd.cmdarg = SECURE_ERASE;
55         cmd.resp_type = MMC_RSP_R1b;
56
57         err = mmc_send_cmd(mmc, &cmd, NULL);
58         if (err)
59                 goto err_out;
60
61         return 0;
62
63 err_out:
64         puts("mmc erase failed\n");
65         return err;
66 }
67
68 unsigned long mmc_berase(int dev_num, lbaint_t start, lbaint_t blkcnt)
69 {
70         int err = 0;
71         u32 start_rem, blkcnt_rem;
72         struct mmc *mmc = find_mmc_device(dev_num);
73         lbaint_t blk = 0, blk_r = 0;
74         int timeout = 1000;
75
76         if (!mmc)
77                 return -1;
78
79         /*
80          * We want to see if the requested start or total block count are
81          * unaligned.  We discard the whole numbers and only care about the
82          * remainder.
83          */
84         err = div_u64_rem(start, mmc->erase_grp_size, &start_rem);
85         err = div_u64_rem(blkcnt, mmc->erase_grp_size, &blkcnt_rem);
86         if (start_rem || blkcnt_rem)
87                 printf("\n\nCaution! Your devices Erase group is 0x%x\n"
88                        "The erase range would be change to "
89                        "0x" LBAF "~0x" LBAF "\n\n",
90                        mmc->erase_grp_size, start & ~(mmc->erase_grp_size - 1),
91                        ((start + blkcnt + mmc->erase_grp_size)
92                        & ~(mmc->erase_grp_size - 1)) - 1);
93
94         while (blk < blkcnt) {
95                 blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ?
96                         mmc->erase_grp_size : (blkcnt - blk);
97                 err = mmc_erase_t(mmc, start + blk, blk_r);
98                 if (err)
99                         break;
100
101                 blk += blk_r;
102
103                 /* Waiting for the ready status */
104                 if (mmc_send_status(mmc, timeout))
105                         return 0;
106         }
107
108         return blk;
109 }
110
111 static ulong mmc_write_blocks(struct mmc *mmc, lbaint_t start,
112                 lbaint_t blkcnt, const void *src)
113 {
114         struct mmc_cmd cmd;
115         struct mmc_data data;
116         int timeout = 1000;
117
118         if ((start + blkcnt) > mmc->block_dev.lba) {
119                 printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n",
120                        start + blkcnt, mmc->block_dev.lba);
121                 return 0;
122         }
123
124         if (blkcnt == 0)
125                 return 0;
126         else if (blkcnt == 1)
127                 cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK;
128         else
129                 cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
130
131         if (mmc->high_capacity)
132                 cmd.cmdarg = start;
133         else
134                 cmd.cmdarg = start * mmc->write_bl_len;
135
136         cmd.resp_type = MMC_RSP_R1;
137
138         data.src = src;
139         data.blocks = blkcnt;
140         data.blocksize = mmc->write_bl_len;
141         data.flags = MMC_DATA_WRITE;
142
143         if (mmc_send_cmd(mmc, &cmd, &data)) {
144                 printf("mmc write failed\n");
145                 return 0;
146         }
147
148         /* SPI multiblock writes terminate using a special
149          * token, not a STOP_TRANSMISSION request.
150          */
151         if (!mmc_host_is_spi(mmc) && blkcnt > 1) {
152                 cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
153                 cmd.cmdarg = 0;
154                 cmd.resp_type = MMC_RSP_R1b;
155                 if (mmc_send_cmd(mmc, &cmd, NULL)) {
156                         printf("mmc fail to send stop cmd\n");
157                         return 0;
158                 }
159         }
160
161         /* Waiting for the ready status */
162         if (mmc_send_status(mmc, timeout))
163                 return 0;
164
165         return blkcnt;
166 }
167
168 ulong mmc_bwrite(int dev_num, lbaint_t start, lbaint_t blkcnt, const void *src)
169 {
170         lbaint_t cur, blocks_todo = blkcnt;
171
172         struct mmc *mmc = find_mmc_device(dev_num);
173         if (!mmc)
174                 return 0;
175
176         if (mmc_set_blocklen(mmc, mmc->write_bl_len))
177                 return 0;
178
179         do {
180                 cur = (blocks_todo > mmc->cfg->b_max) ?
181                         mmc->cfg->b_max : blocks_todo;
182                 if (mmc_write_blocks(mmc, start, cur, src) != cur)
183                         return 0;
184                 blocks_todo -= cur;
185                 start += cur;
186                 src += cur * mmc->write_bl_len;
187         } while (blocks_todo > 0);
188
189         return blkcnt;
190 }