]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - board/karo/common/mmc.c
arm64: add support for Ka-Ro TXSD-410E
[karo-tx-uboot.git] / board / karo / common / mmc.c
1 /*
2  * (C) Copyright 2014 Lothar Waßmann <LW@KARO-electronics.de>
3  *
4  * See file CREDITS for list of people who contributed to this
5  * project.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * version 2 as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  */
17
18 #include <common.h>
19 #include <blk.h>
20 #include <errno.h>
21 #include <fat.h>
22 #include <fdt_support.h>
23 #include <fs.h>
24 #include <libfdt.h>
25 #include <mmc.h>
26 #include <malloc.h>
27 #include <part.h>
28 #include <linux/err.h>
29 #include <jffs2/load_kernel.h>
30
31 #include "karo.h"
32
33 DECLARE_GLOBAL_DATA_PTR;
34
35 #define MAX_SEARCH_PARTITIONS 16
36
37 static int find_partitions(const char *ifname, int devno, const char *partname,
38                         struct blk_desc **dev_desc, disk_partition_t *info)
39 {
40         struct blk_desc *dd;
41         char dev_part_str[16];
42         int p;
43
44         dd = blk_get_devnum_by_typename(ifname, devno);
45         if (!dd || dd->type == DEV_TYPE_UNKNOWN) {
46                 printf("** Bad device %s %d **\n", ifname, devno);
47                 return -1;
48         }
49         part_init(dd);
50
51         /*
52          * No partition table on device,
53          * or user requested partition 0 (entire device).
54          */
55         if (dd->part_type == PART_TYPE_UNKNOWN) {
56                 printf("** No partition table on device %s %d **\n",
57                         ifname, devno);
58                 return -ENODEV;
59         }
60
61         printf("part type: %08x\n", dd->part_type);
62         for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) {
63                 int ret;
64
65                 if (partname) {
66                         ret = part_get_info(dd, p, info);
67                         if (ret == 0)
68                                 ret = strncmp((char *)info->name, partname,
69                                         sizeof(info->name));
70                 } else {
71                         ret = fat_register_device(dd, p);
72
73                 }
74                 if (ret)
75                         continue;
76
77                 snprintf(dev_part_str, sizeof(dev_part_str), "%d:%d",
78                         devno, p);
79                 ret = fs_set_blk_dev(ifname, dev_part_str, FS_TYPE_ANY);
80                 if (ret == 0) {
81                         dd->log2blksz = LOG2(dd->blksz);
82                         *dev_desc = dd;
83                         return 0;
84                 }
85         }
86         printf("** No valid partition on device %s %d **\n",
87                 ifname, devno);
88         return -ENODEV;
89 }
90
91 static int karo_mmc_find_part(struct mmc *mmc, const char *part,
92                         const char *filename, int devno,
93                         disk_partition_t *part_info)
94 {
95         int ret;
96         struct blk_desc *mmc_dev;
97
98 #if defined(CONFIG_SYS_DTB_OFFSET) && defined(CONFIG_SYS_MMC_ENV_PART)
99         if (strcmp(part, "dtb") == 0) {
100                 const int partnum = CONFIG_SYS_MMC_ENV_PART;
101
102                 part_info->blksz = mmc->read_bl_len;
103                 part_info->start = CONFIG_SYS_DTB_OFFSET / part_info->blksz;
104                 part_info->size = CONFIG_SYS_DTB_PART_SIZE / part_info->blksz;
105                 printf("Using virtual partition %s(%d) ["LBAF".."LBAF"]\n",
106                         part, partnum, part_info->start,
107                         part_info->start + part_info->size - 1);
108                 return partnum;
109         }
110 #endif
111         ret = find_partitions("mmc", devno, part, &mmc_dev, part_info);
112         if (ret < 0) {
113                 printf("No (e)MMC partition found: %d\n", ret);
114                 return ret;
115         }
116         return 0;
117 }
118
119 int karo_mmc_load_part(const char *part_file, void *addr, size_t len)
120 {
121         int ret;
122         struct mmc *mmc;
123         char *partname = strdup(part_file);
124         char *filename;
125         disk_partition_t part_info;
126         int devno = mmc_get_env_dev();
127         lbaint_t blk_cnt;
128
129         if (!partname)
130                 return -ENOMEM;
131
132         mmc = find_mmc_device(devno);
133         if (!mmc) {
134                 printf("Failed to find mmc%u\n", devno);
135                 return -ENODEV;
136         }
137
138         if (mmc_init(mmc)) {
139                 printf("Failed to init MMC device %d\n", devno);
140                 return -EIO;
141         }
142
143         filename = strchr(partname, ':');
144         if (filename) {
145                 *filename = '\0';
146                 filename++;
147         } else {
148                 filename = partname;
149         }
150
151         blk_cnt = DIV_ROUND_UP(len, mmc->read_bl_len);
152
153         ret = karo_mmc_find_part(mmc, partname, filename, devno, &part_info);
154         if (ret < 0)
155                 goto out;
156
157         if (partname == filename) {
158                 int partnum = ret;
159                 struct blk_desc *desc = mmc_get_blk_desc(mmc);
160                 int hwpart = desc->hwpart;
161                 ulong retval;
162
163                 if (part_info.start + blk_cnt < part_info.start) {
164                         printf("%s: given length 0x%08zx exceeds size of partition\n",
165                                 __func__, len);
166                         ret = -EINVAL;
167                         goto out;
168                 }
169                 if (partnum != hwpart) {
170                         ret = blk_select_hwpart_devnum(IF_TYPE_MMC, devno,
171                                                 partnum);
172                         if (ret)
173                                 goto out;
174
175                         desc = mmc_get_blk_desc(mmc);
176                 }
177                 if (part_info.start + blk_cnt > desc->lba)
178                         blk_cnt = desc->lba - part_info.start;
179
180                 debug("Reading 0x"LBAF" blks from MMC partition %d offset 0x"LBAF" to %p\n",
181                         blk_cnt, partnum, part_info.start, addr);
182
183                 retval = blk_dread(desc, part_info.start, blk_cnt, addr);
184                 if (partnum != hwpart)
185                         blk_select_hwpart_devnum(IF_TYPE_MMC, devno, hwpart);
186                 if (IS_ERR_VALUE(retval) || retval == 0) {
187                         printf("Failed to read file %s from MMC partition %s\n",
188                                 filename, partname);
189                         ret = retval ?: -EIO;
190                         goto out;
191                 }
192                 debug("Read %lu (%zu) byte from partition '%s' @ offset 0x"LBAF"\n",
193                         retval * mmc->read_bl_len, len, filename, part_info.start);
194         } else {
195                 loff_t len_read;
196
197                 debug("Trying to read (%zu) byte from file '%s' in mmc partition '%s'\n",
198                         len, filename, partname);
199                 ret = fs_read(filename, (ulong)addr, 0, len, &len_read);
200                 if (ret < 0) {
201                         printf("Failed to read %zu byte from '%s' in mmc partition '%s'; err: %d\n",
202                                 len, filename, partname, ret);
203                         goto out;
204                 }
205                 debug("Read %llu bytes from %s\n", len_read, filename);
206         }
207 out:
208         free(partname);
209         return ret;
210 }