1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2016 Google, Inc
4  * Written by Amar <amarendra.xt@samsung.com>
5  */
6 
7 #include <log.h>
8 #include <mmc.h>
9 #include "mmc_private.h"
10 
mmc_resize_boot_micron(struct mmc * mmc,unsigned long bootsize,unsigned long rpmbsize)11 static int mmc_resize_boot_micron(struct mmc *mmc, unsigned long bootsize,
12 				  unsigned long rpmbsize)
13 {
14 	int err;
15 
16 	/* Micron eMMC doesn't support resizing RPMB partition */
17 	(void)rpmbsize;
18 
19 	/* BOOT partition size is multiple of 128KB */
20 	bootsize = (bootsize * 1024) / 128;
21 
22 	if (bootsize > 0xff)
23 		bootsize = 0xff;
24 
25 	/* Set EXT_CSD[175] ERASE_GROUP_DEF to 0x01 */
26 	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
27 			 EXT_CSD_ERASE_GROUP_DEF, 0x01);
28 	if (err)
29 		goto error;
30 
31 	/* Set EXT_CSD[127:125] for BOOT partition size, [125] is low byte */
32 	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
33 			 EXT_CSD_BOOT_SIZE_MULT_MICRON, bootsize);
34 	if (err)
35 		goto error;
36 
37 	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
38 			 EXT_CSD_BOOT_SIZE_MULT_MICRON + 1, 0x00);
39 	if (err)
40 		goto error;
41 
42 	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
43 			 EXT_CSD_BOOT_SIZE_MULT_MICRON + 2, 0x00);
44 	if (err)
45 		goto error;
46 
47 	/* Set EXT_CSD[155] PARTITION_SETTING_COMPLETE to 0x01 */
48 	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
49 			 EXT_CSD_PARTITION_SETTING, 0x01);
50 	if (err)
51 		goto error;
52 
53 	return 0;
54 
55 error:
56 	debug("%s: Error = %d\n", __func__, err);
57 	return err;
58 }
59 
mmc_resize_boot_sandisk(struct mmc * mmc,unsigned long bootsize,unsigned long rpmbsize)60 static int mmc_resize_boot_sandisk(struct mmc *mmc, unsigned long bootsize,
61 				   unsigned long rpmbsize)
62 {
63 	int err;
64 	struct mmc_cmd cmd;
65 
66 	/* BOOT/RPMB partition size is multiple of 128KB */
67 	bootsize = (bootsize * 1024) / 128;
68 	rpmbsize = (rpmbsize * 1024) / 128;
69 
70 	if (bootsize > 0xff)
71 		bootsize = 0xff;
72 
73 	if (rpmbsize > 0xff)
74 		rpmbsize = 0xff;
75 
76 	/* Send BOOT/RPMB resize op code */
77 	cmd.cmdidx = MMC_CMD_RES_MAN;
78 	cmd.resp_type = MMC_RSP_R1b;
79 	cmd.cmdarg = MMC_CMD62_ARG_SANDISK;
80 
81 	err = mmc_send_cmd(mmc, &cmd, NULL);
82 	if (err)
83 		goto error;
84 
85 	/* Arg: BOOT partition size */
86 	cmd.cmdidx = MMC_CMD_RES_MAN;
87 	cmd.resp_type = MMC_RSP_R1b;
88 	cmd.cmdarg = bootsize;
89 
90 	err = mmc_send_cmd(mmc, &cmd, NULL);
91 	if (err)
92 		goto error;
93 
94 	/* Arg: RPMB partition size */
95 	cmd.cmdidx = MMC_CMD_RES_MAN;
96 	cmd.resp_type = MMC_RSP_R1b;
97 	cmd.cmdarg = rpmbsize;
98 
99 	err = mmc_send_cmd(mmc, &cmd, NULL);
100 	if (err)
101 		goto error;
102 
103 	return 0;
104 
105 error:
106 	debug("%s: Error = %d\n", __func__, err);
107 	return err;
108 }
109 
mmc_resize_boot_samsung(struct mmc * mmc,unsigned long bootsize,unsigned long rpmbsize)110 static int mmc_resize_boot_samsung(struct mmc *mmc, unsigned long bootsize,
111 				   unsigned long rpmbsize)
112 {
113 	int err;
114 	struct mmc_cmd cmd;
115 
116 	/* Only use this command for raw EMMC moviNAND. Enter backdoor mode */
117 	cmd.cmdidx = MMC_CMD_RES_MAN;
118 	cmd.resp_type = MMC_RSP_R1b;
119 	cmd.cmdarg = MMC_CMD62_ARG1;
120 
121 	err = mmc_send_cmd(mmc, &cmd, NULL);
122 	if (err)
123 		goto error;
124 
125 	/* Boot partition changing mode */
126 	cmd.cmdidx = MMC_CMD_RES_MAN;
127 	cmd.resp_type = MMC_RSP_R1b;
128 	cmd.cmdarg = MMC_CMD62_ARG2;
129 
130 	err = mmc_send_cmd(mmc, &cmd, NULL);
131 	if (err)
132 		goto error;
133 
134 	/* boot partition size is multiple of 128KB */
135 	bootsize = (bootsize * 1024) / 128;
136 
137 	/* Arg: boot partition size */
138 	cmd.cmdidx = MMC_CMD_RES_MAN;
139 	cmd.resp_type = MMC_RSP_R1b;
140 	cmd.cmdarg = bootsize;
141 
142 	err = mmc_send_cmd(mmc, &cmd, NULL);
143 	if (err)
144 		goto error;
145 
146 	/* RPMB partition size is multiple of 128KB */
147 	rpmbsize = (rpmbsize * 1024) / 128;
148 	/* Arg: RPMB partition size */
149 	cmd.cmdidx = MMC_CMD_RES_MAN;
150 	cmd.resp_type = MMC_RSP_R1b;
151 	cmd.cmdarg = rpmbsize;
152 
153 	err = mmc_send_cmd(mmc, &cmd, NULL);
154 	if (err)
155 		goto error;
156 
157 	return 0;
158 
159 error:
160 	debug("%s: Error = %d\n", __func__, err);
161 	return err;
162 }
163 
164 /*
165  * This function changes the size of BOOT partition and the size of RPMB
166  * partition present on eMMC devices.
167  *
168  * Input Parameters:
169  * struct *mmc: pointer for the mmc device strcuture
170  * bootsize: size of BOOT partition
171  * rpmbsize: size of RPMB partition
172  *
173  * Returns 0 on success.
174  */
175 
mmc_boot_partition_size_change(struct mmc * mmc,unsigned long bootsize,unsigned long rpmbsize)176 int mmc_boot_partition_size_change(struct mmc *mmc, unsigned long bootsize,
177 				   unsigned long rpmbsize)
178 {
179 	switch (mmc->cid[0] >> 24) {
180 	case CID_MANFID_MICRON:
181 		return mmc_resize_boot_micron(mmc, bootsize, rpmbsize);
182 	case CID_MANFID_SAMSUNG:
183 		return mmc_resize_boot_samsung(mmc, bootsize, rpmbsize);
184 	case CID_MANFID_SANDISK:
185 		return mmc_resize_boot_sandisk(mmc, bootsize, rpmbsize);
186 	default:
187 		printf("Unsupported manufacturer id 0x%02x\n",
188 		       mmc->cid[0] >> 24);
189 		return -EPERM;
190 	}
191 }
192 
193 /*
194  * Modify EXT_CSD[177] which is BOOT_BUS_WIDTH
195  * based on the passed in values for BOOT_BUS_WIDTH, RESET_BOOT_BUS_WIDTH
196  * and BOOT_MODE.
197  *
198  * Returns 0 on success.
199  */
mmc_set_boot_bus_width(struct mmc * mmc,u8 width,u8 reset,u8 mode)200 int mmc_set_boot_bus_width(struct mmc *mmc, u8 width, u8 reset, u8 mode)
201 {
202 	return mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BOOT_BUS_WIDTH,
203 			  EXT_CSD_BOOT_BUS_WIDTH_MODE(mode) |
204 			  EXT_CSD_BOOT_BUS_WIDTH_RESET(reset) |
205 			  EXT_CSD_BOOT_BUS_WIDTH_WIDTH(width));
206 }
207 
208 /*
209  * Modify EXT_CSD[179] which is PARTITION_CONFIG (formerly BOOT_CONFIG)
210  * based on the passed in values for BOOT_ACK, BOOT_PARTITION_ENABLE and
211  * PARTITION_ACCESS.
212  *
213  * Returns 0 on success.
214  */
mmc_set_part_conf(struct mmc * mmc,u8 ack,u8 part_num,u8 access)215 int mmc_set_part_conf(struct mmc *mmc, u8 ack, u8 part_num, u8 access)
216 {
217 	int ret;
218 	u8 part_conf;
219 
220 	part_conf = EXT_CSD_BOOT_ACK(ack) |
221 		    EXT_CSD_BOOT_PART_NUM(part_num) |
222 		    EXT_CSD_PARTITION_ACCESS(access);
223 
224 	ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF,
225 			 part_conf);
226 	if (!ret)
227 		mmc->part_config = part_conf;
228 
229 	return ret;
230 }
231 
232 /*
233  * Modify EXT_CSD[162] which is RST_n_FUNCTION based on the given value
234  * for enable.  Note that this is a write-once field for non-zero values.
235  *
236  * Returns 0 on success.
237  */
mmc_set_rst_n_function(struct mmc * mmc,u8 enable)238 int mmc_set_rst_n_function(struct mmc *mmc, u8 enable)
239 {
240 	return mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_RST_N_FUNCTION,
241 			  enable);
242 }
243