]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
c40fdca6 SG |
2 | /* |
3 | * Copyright (C) 2016 Google, Inc | |
39913ace | 4 | * Copyright 2020 NXP |
c40fdca6 | 5 | * Written by Simon Glass <[email protected]> |
c40fdca6 SG |
6 | */ |
7 | ||
f7ae49fc | 8 | #include <log.h> |
5aed4cbb | 9 | #include <malloc.h> |
c40fdca6 | 10 | #include <mmc.h> |
5aed4cbb | 11 | #include "mmc_private.h" |
c40fdca6 SG |
12 | |
13 | static struct list_head mmc_devices; | |
14 | static int cur_dev_num = -1; | |
15 | ||
62d77cea MV |
16 | #if CONFIG_IS_ENABLED(MMC_TINY) |
17 | static struct mmc mmc_static; | |
18 | struct mmc *find_mmc_device(int dev_num) | |
19 | { | |
20 | return &mmc_static; | |
21 | } | |
22 | ||
23 | void mmc_do_preinit(void) | |
24 | { | |
25 | struct mmc *m = &mmc_static; | |
62d77cea MV |
26 | if (m->preinit) |
27 | mmc_start_init(m); | |
28 | } | |
29 | ||
30 | struct blk_desc *mmc_get_blk_desc(struct mmc *mmc) | |
31 | { | |
32 | return &mmc->block_dev; | |
33 | } | |
34 | #else | |
c40fdca6 SG |
35 | struct mmc *find_mmc_device(int dev_num) |
36 | { | |
37 | struct mmc *m; | |
38 | struct list_head *entry; | |
39 | ||
40 | list_for_each(entry, &mmc_devices) { | |
41 | m = list_entry(entry, struct mmc, link); | |
42 | ||
43 | if (m->block_dev.devnum == dev_num) | |
44 | return m; | |
45 | } | |
46 | ||
371dc068 | 47 | #if !defined(CONFIG_XPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) |
c40fdca6 SG |
48 | printf("MMC Device %d not found\n", dev_num); |
49 | #endif | |
50 | ||
51 | return NULL; | |
52 | } | |
53 | ||
54 | int mmc_get_next_devnum(void) | |
55 | { | |
56 | return cur_dev_num++; | |
57 | } | |
58 | ||
59 | struct blk_desc *mmc_get_blk_desc(struct mmc *mmc) | |
60 | { | |
61 | return &mmc->block_dev; | |
62 | } | |
63 | ||
64 | int get_mmc_num(void) | |
65 | { | |
66 | return cur_dev_num; | |
67 | } | |
68 | ||
69 | void mmc_do_preinit(void) | |
70 | { | |
71 | struct mmc *m; | |
72 | struct list_head *entry; | |
73 | ||
74 | list_for_each(entry, &mmc_devices) { | |
75 | m = list_entry(entry, struct mmc, link); | |
76 | ||
c40fdca6 SG |
77 | if (m->preinit) |
78 | mmc_start_init(m); | |
79 | } | |
80 | } | |
b5b838f1 | 81 | #endif |
c40fdca6 SG |
82 | |
83 | void mmc_list_init(void) | |
84 | { | |
85 | INIT_LIST_HEAD(&mmc_devices); | |
86 | cur_dev_num = 0; | |
87 | } | |
88 | ||
89 | void mmc_list_add(struct mmc *mmc) | |
90 | { | |
91 | INIT_LIST_HEAD(&mmc->link); | |
92 | ||
93 | list_add_tail(&mmc->link, &mmc_devices); | |
94 | } | |
95 | ||
371dc068 | 96 | #if !defined(CONFIG_XPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) |
c40fdca6 SG |
97 | void print_mmc_devices(char separator) |
98 | { | |
99 | struct mmc *m; | |
100 | struct list_head *entry; | |
101 | char *mmc_type; | |
102 | ||
103 | list_for_each(entry, &mmc_devices) { | |
104 | m = list_entry(entry, struct mmc, link); | |
105 | ||
106 | if (m->has_init) | |
107 | mmc_type = IS_SD(m) ? "SD" : "eMMC"; | |
108 | else | |
109 | mmc_type = NULL; | |
110 | ||
111 | printf("%s: %d", m->cfg->name, m->block_dev.devnum); | |
112 | if (mmc_type) | |
113 | printf(" (%s)", mmc_type); | |
114 | ||
115 | if (entry->next != &mmc_devices) { | |
116 | printf("%c", separator); | |
117 | if (separator != '\n') | |
118 | puts(" "); | |
119 | } | |
120 | } | |
121 | ||
122 | printf("\n"); | |
123 | } | |
124 | ||
125 | #else | |
126 | void print_mmc_devices(char separator) { } | |
127 | #endif | |
5aed4cbb | 128 | |
b5b838f1 MV |
129 | #if CONFIG_IS_ENABLED(MMC_TINY) |
130 | static struct mmc mmc_static = { | |
131 | .dsr_imp = 0, | |
132 | .dsr = 0xffffffff, | |
133 | .block_dev = { | |
8149b150 | 134 | .uclass_id = UCLASS_MMC, |
b5b838f1 MV |
135 | .removable = 1, |
136 | .devnum = 0, | |
137 | .block_read = mmc_bread, | |
138 | .block_write = mmc_bwrite, | |
139 | .block_erase = mmc_berase, | |
140 | .part_type = 0, | |
141 | }, | |
142 | }; | |
143 | ||
144 | struct mmc *mmc_create(const struct mmc_config *cfg, void *priv) | |
145 | { | |
146 | struct mmc *mmc = &mmc_static; | |
147 | ||
d66fb5b1 EG |
148 | /* First MMC device registered, fail to register a new one. |
149 | * Given users are not expecting this to fail, instead | |
150 | * of failing let's just return the only MMC device | |
151 | */ | |
152 | if (mmc->cfg) { | |
153 | debug("Warning: MMC_TINY doesn't support multiple MMC devices\n"); | |
154 | return mmc; | |
155 | } | |
156 | ||
b5b838f1 MV |
157 | mmc->cfg = cfg; |
158 | mmc->priv = priv; | |
159 | ||
160 | return mmc; | |
161 | } | |
162 | ||
163 | void mmc_destroy(struct mmc *mmc) | |
164 | { | |
165 | } | |
166 | #else | |
5aed4cbb SG |
167 | struct mmc *mmc_create(const struct mmc_config *cfg, void *priv) |
168 | { | |
169 | struct blk_desc *bdesc; | |
170 | struct mmc *mmc; | |
171 | ||
172 | /* quick validation */ | |
177381a9 JC |
173 | if (cfg == NULL || cfg->f_min == 0 || |
174 | cfg->f_max == 0 || cfg->b_max == 0) | |
5aed4cbb SG |
175 | return NULL; |
176 | ||
e7881d85 | 177 | #if !CONFIG_IS_ENABLED(DM_MMC) |
177381a9 JC |
178 | if (cfg->ops == NULL || cfg->ops->send_cmd == NULL) |
179 | return NULL; | |
180 | #endif | |
181 | ||
5aed4cbb SG |
182 | mmc = calloc(1, sizeof(*mmc)); |
183 | if (mmc == NULL) | |
184 | return NULL; | |
185 | ||
186 | mmc->cfg = cfg; | |
187 | mmc->priv = priv; | |
188 | ||
189 | /* the following chunk was mmc_register() */ | |
190 | ||
191 | /* Setup dsr related values */ | |
192 | mmc->dsr_imp = 0; | |
193 | mmc->dsr = 0xffffffff; | |
194 | /* Setup the universal parts of the block interface just once */ | |
195 | bdesc = mmc_get_blk_desc(mmc); | |
8149b150 | 196 | bdesc->uclass_id = UCLASS_MMC; |
5aed4cbb SG |
197 | bdesc->removable = 1; |
198 | bdesc->devnum = mmc_get_next_devnum(); | |
199 | bdesc->block_read = mmc_bread; | |
200 | bdesc->block_write = mmc_bwrite; | |
201 | bdesc->block_erase = mmc_berase; | |
202 | ||
203 | /* setup initial part type */ | |
204 | bdesc->part_type = mmc->cfg->part_type; | |
205 | mmc_list_add(mmc); | |
206 | ||
207 | return mmc; | |
208 | } | |
209 | ||
210 | void mmc_destroy(struct mmc *mmc) | |
211 | { | |
212 | /* only freeing memory for now */ | |
213 | free(mmc); | |
214 | } | |
b5b838f1 | 215 | #endif |
5aed4cbb SG |
216 | |
217 | static int mmc_select_hwpartp(struct blk_desc *desc, int hwpart) | |
218 | { | |
219 | struct mmc *mmc = find_mmc_device(desc->devnum); | |
220 | int ret; | |
221 | ||
222 | if (!mmc) | |
223 | return -ENODEV; | |
224 | ||
225 | if (mmc->block_dev.hwpart == hwpart) | |
226 | return 0; | |
227 | ||
228 | if (mmc->part_config == MMCPART_NOAVAILABLE) | |
229 | return -EMEDIUMTYPE; | |
230 | ||
231 | ret = mmc_switch_part(mmc, hwpart); | |
232 | if (ret) | |
233 | return ret; | |
234 | ||
235 | return 0; | |
236 | } | |
237 | ||
238 | static int mmc_get_dev(int dev, struct blk_desc **descp) | |
239 | { | |
240 | struct mmc *mmc = find_mmc_device(dev); | |
241 | int ret; | |
242 | ||
243 | if (!mmc) | |
244 | return -ENODEV; | |
245 | ret = mmc_init(mmc); | |
246 | if (ret) | |
247 | return ret; | |
248 | ||
249 | *descp = &mmc->block_dev; | |
250 | ||
251 | return 0; | |
252 | } | |
253 | ||
254 | U_BOOT_LEGACY_BLK(mmc) = { | |
8149b150 SG |
255 | .uclass_idname = "mmc", |
256 | .uclass_id = UCLASS_MMC, | |
5aed4cbb SG |
257 | .max_devs = -1, |
258 | .get_dev = mmc_get_dev, | |
259 | .select_hwpart = mmc_select_hwpartp, | |
260 | }; |