]>
Commit | Line | Data |
---|---|---|
c40fdca6 SG |
1 | /* |
2 | * Copyright (C) 2016 Google, Inc | |
3 | * Written by Simon Glass <sjg@chromium.org> | |
4 | * | |
5 | * SPDX-License-Identifier: GPL-2.0+ | |
6 | */ | |
7 | ||
8 | #include <common.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 | ||
16 | struct mmc *find_mmc_device(int dev_num) | |
17 | { | |
18 | struct mmc *m; | |
19 | struct list_head *entry; | |
20 | ||
21 | list_for_each(entry, &mmc_devices) { | |
22 | m = list_entry(entry, struct mmc, link); | |
23 | ||
24 | if (m->block_dev.devnum == dev_num) | |
25 | return m; | |
26 | } | |
27 | ||
28 | #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) | |
29 | printf("MMC Device %d not found\n", dev_num); | |
30 | #endif | |
31 | ||
32 | return NULL; | |
33 | } | |
34 | ||
35 | int mmc_get_next_devnum(void) | |
36 | { | |
37 | return cur_dev_num++; | |
38 | } | |
39 | ||
40 | struct blk_desc *mmc_get_blk_desc(struct mmc *mmc) | |
41 | { | |
42 | return &mmc->block_dev; | |
43 | } | |
44 | ||
45 | int get_mmc_num(void) | |
46 | { | |
47 | return cur_dev_num; | |
48 | } | |
49 | ||
50 | void mmc_do_preinit(void) | |
51 | { | |
52 | struct mmc *m; | |
53 | struct list_head *entry; | |
54 | ||
55 | list_for_each(entry, &mmc_devices) { | |
56 | m = list_entry(entry, struct mmc, link); | |
57 | ||
58 | #ifdef CONFIG_FSL_ESDHC_ADAPTER_IDENT | |
59 | mmc_set_preinit(m, 1); | |
60 | #endif | |
61 | if (m->preinit) | |
62 | mmc_start_init(m); | |
63 | } | |
64 | } | |
65 | ||
66 | void mmc_list_init(void) | |
67 | { | |
68 | INIT_LIST_HEAD(&mmc_devices); | |
69 | cur_dev_num = 0; | |
70 | } | |
71 | ||
72 | void mmc_list_add(struct mmc *mmc) | |
73 | { | |
74 | INIT_LIST_HEAD(&mmc->link); | |
75 | ||
76 | list_add_tail(&mmc->link, &mmc_devices); | |
77 | } | |
78 | ||
79 | #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) | |
80 | void print_mmc_devices(char separator) | |
81 | { | |
82 | struct mmc *m; | |
83 | struct list_head *entry; | |
84 | char *mmc_type; | |
85 | ||
86 | list_for_each(entry, &mmc_devices) { | |
87 | m = list_entry(entry, struct mmc, link); | |
88 | ||
89 | if (m->has_init) | |
90 | mmc_type = IS_SD(m) ? "SD" : "eMMC"; | |
91 | else | |
92 | mmc_type = NULL; | |
93 | ||
94 | printf("%s: %d", m->cfg->name, m->block_dev.devnum); | |
95 | if (mmc_type) | |
96 | printf(" (%s)", mmc_type); | |
97 | ||
98 | if (entry->next != &mmc_devices) { | |
99 | printf("%c", separator); | |
100 | if (separator != '\n') | |
101 | puts(" "); | |
102 | } | |
103 | } | |
104 | ||
105 | printf("\n"); | |
106 | } | |
107 | ||
108 | #else | |
109 | void print_mmc_devices(char separator) { } | |
110 | #endif | |
5aed4cbb SG |
111 | |
112 | struct mmc *mmc_create(const struct mmc_config *cfg, void *priv) | |
113 | { | |
114 | struct blk_desc *bdesc; | |
115 | struct mmc *mmc; | |
116 | ||
117 | /* quick validation */ | |
177381a9 JC |
118 | if (cfg == NULL || cfg->f_min == 0 || |
119 | cfg->f_max == 0 || cfg->b_max == 0) | |
5aed4cbb SG |
120 | return NULL; |
121 | ||
177381a9 JC |
122 | #ifndef CONFIG_DM_MMC_OPS |
123 | if (cfg->ops == NULL || cfg->ops->send_cmd == NULL) | |
124 | return NULL; | |
125 | #endif | |
126 | ||
5aed4cbb SG |
127 | mmc = calloc(1, sizeof(*mmc)); |
128 | if (mmc == NULL) | |
129 | return NULL; | |
130 | ||
131 | mmc->cfg = cfg; | |
132 | mmc->priv = priv; | |
133 | ||
134 | /* the following chunk was mmc_register() */ | |
135 | ||
136 | /* Setup dsr related values */ | |
137 | mmc->dsr_imp = 0; | |
138 | mmc->dsr = 0xffffffff; | |
139 | /* Setup the universal parts of the block interface just once */ | |
140 | bdesc = mmc_get_blk_desc(mmc); | |
141 | bdesc->if_type = IF_TYPE_MMC; | |
142 | bdesc->removable = 1; | |
143 | bdesc->devnum = mmc_get_next_devnum(); | |
144 | bdesc->block_read = mmc_bread; | |
145 | bdesc->block_write = mmc_bwrite; | |
146 | bdesc->block_erase = mmc_berase; | |
147 | ||
148 | /* setup initial part type */ | |
149 | bdesc->part_type = mmc->cfg->part_type; | |
150 | mmc_list_add(mmc); | |
151 | ||
152 | return mmc; | |
153 | } | |
154 | ||
155 | void mmc_destroy(struct mmc *mmc) | |
156 | { | |
157 | /* only freeing memory for now */ | |
158 | free(mmc); | |
159 | } | |
160 | ||
161 | static int mmc_select_hwpartp(struct blk_desc *desc, int hwpart) | |
162 | { | |
163 | struct mmc *mmc = find_mmc_device(desc->devnum); | |
164 | int ret; | |
165 | ||
166 | if (!mmc) | |
167 | return -ENODEV; | |
168 | ||
169 | if (mmc->block_dev.hwpart == hwpart) | |
170 | return 0; | |
171 | ||
172 | if (mmc->part_config == MMCPART_NOAVAILABLE) | |
173 | return -EMEDIUMTYPE; | |
174 | ||
175 | ret = mmc_switch_part(mmc, hwpart); | |
176 | if (ret) | |
177 | return ret; | |
178 | ||
179 | return 0; | |
180 | } | |
181 | ||
182 | static int mmc_get_dev(int dev, struct blk_desc **descp) | |
183 | { | |
184 | struct mmc *mmc = find_mmc_device(dev); | |
185 | int ret; | |
186 | ||
187 | if (!mmc) | |
188 | return -ENODEV; | |
189 | ret = mmc_init(mmc); | |
190 | if (ret) | |
191 | return ret; | |
192 | ||
193 | *descp = &mmc->block_dev; | |
194 | ||
195 | return 0; | |
196 | } | |
197 | ||
198 | U_BOOT_LEGACY_BLK(mmc) = { | |
199 | .if_typename = "mmc", | |
200 | .if_type = IF_TYPE_MMC, | |
201 | .max_devs = -1, | |
202 | .get_dev = mmc_get_dev, | |
203 | .select_hwpart = mmc_select_hwpartp, | |
204 | }; |