diff options
Diffstat (limited to 'firmware/target/arm/imx233/mmc-imx233.c')
-rw-r--r-- | firmware/target/arm/imx233/mmc-imx233.c | 146 |
1 files changed, 132 insertions, 14 deletions
diff --git a/firmware/target/arm/imx233/mmc-imx233.c b/firmware/target/arm/imx233/mmc-imx233.c index f56ff3725c..dafe63f3d3 100644 --- a/firmware/target/arm/imx233/mmc-imx233.c +++ b/firmware/target/arm/imx233/mmc-imx233.c | |||
@@ -22,36 +22,54 @@ | |||
22 | #include "system.h" | 22 | #include "system.h" |
23 | #include "mmc.h" | 23 | #include "mmc.h" |
24 | #include "sdmmc.h" | 24 | #include "sdmmc.h" |
25 | #include "storage.h" | ||
25 | #include "ssp-imx233.h" | 26 | #include "ssp-imx233.h" |
26 | #include "pinctrl-imx233.h" | 27 | #include "pinctrl-imx233.h" |
27 | #include "button-target.h" | 28 | #include "button-target.h" |
28 | 29 | ||
30 | /** | ||
31 | * This code assumes a single eMMC internal flash | ||
32 | */ | ||
33 | |||
29 | #ifdef SANSA_FUZEPLUS | 34 | #ifdef SANSA_FUZEPLUS |
30 | #define MMC_SSP 2 | 35 | #define MMC_SSP 2 |
31 | #else | 36 | #else |
32 | #error You need to configure the ssp to use | 37 | #error You need to configure the ssp to use |
33 | #endif | 38 | #endif |
34 | 39 | ||
40 | #define MMC_RCA 1 | ||
41 | |||
42 | /** When set, this values restrict the windows of the read and writes */ | ||
43 | static unsigned mmc_window_start = 0; | ||
44 | static unsigned mmc_window_end = INT_MAX; | ||
45 | |||
46 | static struct mutex mmc_mutex; | ||
47 | |||
35 | int mmc_init(void) | 48 | int mmc_init(void) |
36 | { | 49 | { |
50 | mutex_init(&mmc_mutex); | ||
51 | |||
37 | imx233_ssp_start(MMC_SSP); | 52 | imx233_ssp_start(MMC_SSP); |
38 | imx233_ssp_softreset(MMC_SSP); | 53 | imx233_ssp_softreset(MMC_SSP); |
39 | imx233_ssp_set_mode(MMC_SSP, HW_SSP_CTRL1__SSP_MODE__SD_MMC); | 54 | imx233_ssp_set_mode(MMC_SSP, HW_SSP_CTRL1__SSP_MODE__SD_MMC); |
40 | #ifdef SANSA_FUZEPLUS | 55 | #ifdef SANSA_FUZEPLUS |
41 | /** Sansa Fuze+ has an internal eMMC 8-bit wide flash, power gate is pin PWM3 */ | 56 | /** Sansa Fuze+ has an internal eMMC 8-bit wide flash, power gate is pin PWM3 |
57 | * and power up time is 20ms */ | ||
42 | imx233_set_pin_function(1, 29, PINCTRL_FUNCTION_GPIO); | 58 | imx233_set_pin_function(1, 29, PINCTRL_FUNCTION_GPIO); |
43 | imx233_enable_gpio_output(1, 29, true); | 59 | imx233_enable_gpio_output(1, 29, true); |
44 | imx233_set_gpio_output(1, 29, false); | 60 | imx233_set_gpio_output(1, 29, false); |
61 | sleep(HZ / 5); | ||
45 | 62 | ||
46 | imx233_ssp_setup_ssp2_sd_mmc_pins(true, 8, PINCTRL_DRIVE_8mA); | 63 | imx233_ssp_setup_ssp2_sd_mmc_pins(true, 8, PINCTRL_DRIVE_8mA); |
47 | #endif | 64 | #endif |
48 | /* SSPCLK @ 120MHz | 65 | /* SSPCLK @ 96MHz |
49 | * gives bitrate of 120 / 100 / 3 = 400kHz */ | 66 | * gives bitrate of 96000 / 240 / 1 = 400kHz */ |
50 | imx233_ssp_set_timings(MMC_SSP, 100, 2); | 67 | imx233_ssp_set_timings(MMC_SSP, 240, 0, 0xffff); |
51 | imx233_ssp_set_timeout(MMC_SSP, 0xffff); | ||
52 | imx233_ssp_sd_mmc_power_up_sequence(MMC_SSP); | 68 | imx233_ssp_sd_mmc_power_up_sequence(MMC_SSP); |
69 | imx233_ssp_set_bus_width(MMC_SSP, 1); | ||
70 | imx233_ssp_set_block_size(MMC_SSP, 9); | ||
53 | /* go to idle state */ | 71 | /* go to idle state */ |
54 | int ret = imx233_ssp_sd_mmc_transfer(MMC_SSP, SD_GO_IDLE_STATE, 0, SSP_NO_RESP, NULL, 0, false, NULL); | 72 | int ret = imx233_ssp_sd_mmc_transfer(MMC_SSP, SD_GO_IDLE_STATE, 0, SSP_NO_RESP, NULL, 0, false, false, NULL); |
55 | if(ret != 0) | 73 | if(ret != 0) |
56 | return -1; | 74 | return -1; |
57 | /* send op cond until the card respond with busy bit set; it must complete within 1sec */ | 75 | /* send op cond until the card respond with busy bit set; it must complete within 1sec */ |
@@ -59,18 +77,82 @@ int mmc_init(void) | |||
59 | do | 77 | do |
60 | { | 78 | { |
61 | uint32_t ocr; | 79 | uint32_t ocr; |
62 | ret = imx233_ssp_sd_mmc_transfer(MMC_SSP, 1, 0x40ff8000, SSP_SHORT_RESP, NULL, 0, false, &ocr); | 80 | ret = imx233_ssp_sd_mmc_transfer(MMC_SSP, 1, 0x40ff8000, SSP_SHORT_RESP, NULL, 0, false, false, &ocr); |
63 | if(ret == 0 && ocr & (1 << 31)) | 81 | if(ret == 0 && ocr & (1 << 31)) |
64 | break; | 82 | break; |
65 | }while(!TIME_AFTER(current_tick, timeout)); | 83 | }while(!TIME_AFTER(current_tick, timeout)); |
66 | 84 | ||
67 | if(ret != 0) | 85 | if(ret != 0) |
68 | return -2; | 86 | return -2; |
69 | 87 | /* get CID */ | |
70 | uint32_t cid[4]; | 88 | uint32_t cid[4]; |
71 | ret = imx233_ssp_sd_mmc_transfer(MMC_SSP, 2, 0, SSP_LONG_RESP, NULL, 0, false, cid); | 89 | ret = imx233_ssp_sd_mmc_transfer(MMC_SSP, 2, 0, SSP_LONG_RESP, NULL, 0, false, false, cid); |
72 | if(ret != 0) | 90 | if(ret != 0) |
73 | return -3; | 91 | return -3; |
92 | /* Set RCA */ | ||
93 | uint32_t status; | ||
94 | ret = imx233_ssp_sd_mmc_transfer(MMC_SSP, 3, MMC_RCA << 16, SSP_SHORT_RESP, NULL, 0, false, false, &status); | ||
95 | if(ret != 0) | ||
96 | return -4; | ||
97 | /* Select card */ | ||
98 | ret = imx233_ssp_sd_mmc_transfer(MMC_SSP, 7, MMC_RCA << 16, SSP_SHORT_RESP, NULL, 0, false, false, &status); | ||
99 | if(ret != 0) | ||
100 | return -5; | ||
101 | /* Check TRAN state */ | ||
102 | ret = imx233_ssp_sd_mmc_transfer(MMC_SSP, 13, MMC_RCA << 16, SSP_SHORT_RESP, NULL, 0, false, false, &status); | ||
103 | if(ret != 0) | ||
104 | return -6; | ||
105 | if(((status >> 9) & 0xf) != 4) | ||
106 | return -7; | ||
107 | /* Switch to 8-bit bus */ | ||
108 | ret = imx233_ssp_sd_mmc_transfer(MMC_SSP, 6, 0x3b70200, SSP_SHORT_RESP, NULL, 0, true, false, &status); | ||
109 | if(ret != 0) | ||
110 | return -8; | ||
111 | /* switch error ? */ | ||
112 | if(status & 0x80) | ||
113 | return -9; | ||
114 | imx233_ssp_set_bus_width(MMC_SSP, 8); | ||
115 | /* Switch to high speed mode */ | ||
116 | ret = imx233_ssp_sd_mmc_transfer(MMC_SSP, 6, 0x3b90100, SSP_SHORT_RESP, NULL, 0, true, false, &status); | ||
117 | if(ret != 0) | ||
118 | return -10; | ||
119 | /* switch error ?*/ | ||
120 | if(status & 0x80) | ||
121 | return -11; | ||
122 | /* SSPCLK @ 96MHz | ||
123 | * gives bitrate of 96 / 2 / 1 = 48MHz */ | ||
124 | imx233_ssp_set_timings(MMC_SSP, 2, 0, 0xffff); | ||
125 | |||
126 | #ifdef SANSA_FUZEPLUS | ||
127 | /** | ||
128 | * The Fuze+ uses a strange layout: is has a first MBR at sector 0 with four entries: | ||
129 | * 1) Actual user partition | ||
130 | * 2) Sigmatel boot partition | ||
131 | * 3)4) Other (certificate related ?) partitions | ||
132 | * The partition 1) has type 1 but it's actually a type 5 (logical partition) with | ||
133 | * a second partition table with usually one entry which is the FAT32 one. | ||
134 | * The first table uses 512-byte sector size and the second one usually uses | ||
135 | * 2048-byte logical sector size. | ||
136 | * | ||
137 | * We restrict mmc window to the user partition */ | ||
138 | uint8_t mbr[512]; | ||
139 | mmc_window_start = 0; | ||
140 | mmc_window_end = INT_MAX; | ||
141 | ret = mmc_read_sectors(IF_MD2(0,) 0, 1, mbr); | ||
142 | if(ret != 0) | ||
143 | return -100; | ||
144 | if(mbr[510] != 0x55 || mbr[511] != 0xAA) | ||
145 | return -101; /* invalid MBR */ | ||
146 | /* sanity check that the first partition is greater than 2Gib */ | ||
147 | uint8_t *ent = &mbr[446]; | ||
148 | mmc_window_start = ent[8] | ent[9] << 8 | ent[10] << 16 | ent[11] << 24; | ||
149 | mmc_window_end = (ent[12] | ent[13] << 8 | ent[14] << 16 | ent[15] << 24) + | ||
150 | mmc_window_start; | ||
151 | if(ent[4] == 0x53) | ||
152 | return -102; /* sigmatel partition */ | ||
153 | if((mmc_window_end - mmc_window_start) < 4 * 1024 * 1024) | ||
154 | return -103; /* partition too small */ | ||
155 | #endif | ||
74 | 156 | ||
75 | return 0; | 157 | return 0; |
76 | } | 158 | } |
@@ -81,13 +163,49 @@ int mmc_num_drives(int first_drive) | |||
81 | return 1; | 163 | return 1; |
82 | } | 164 | } |
83 | 165 | ||
84 | int mmc_read_sectors(IF_MD2(int drive,) unsigned long start, int count, void* buf) | 166 | #ifdef STORAGE_GET_INFO |
167 | void mmc_get_info(IF_MD2(int drive,) struct storage_info *info) | ||
168 | { | ||
169 | #ifdef HAVE_MULTIDRIVE | ||
170 | (void) drive; | ||
171 | #endif | ||
172 | info->sector_size = 512; | ||
173 | info->num_sectors = 0xECC000 - 0x0001ac00; | ||
174 | info->vendor = "Rockbox"; | ||
175 | info->product = "Internal Storage"; | ||
176 | info->revision = "0.00"; | ||
177 | } | ||
178 | #endif | ||
179 | |||
180 | int mmc_read_sectors(IF_MD2(int drive,) unsigned long start, int count, void *buf) | ||
85 | { | 181 | { |
86 | IF_MD((void) drive); | 182 | IF_MD((void) drive); |
87 | (void) start; | 183 | /* check window */ |
88 | (void) count; | 184 | start += mmc_window_start; |
89 | (void) buf; | 185 | if((start + count) > mmc_window_end) |
90 | return -1; | 186 | return -201; |
187 | /* get mutex (needed because we done multiple commands for count > 0 */ | ||
188 | mutex_lock(&mmc_mutex); | ||
189 | int ret = 0; | ||
190 | uint32_t resp; | ||
191 | |||
192 | if(count == 1) | ||
193 | { | ||
194 | ret = imx233_ssp_sd_mmc_transfer(MMC_SSP, 17, start, SSP_SHORT_RESP, buf, | ||
195 | count, false, true, &resp); | ||
196 | } | ||
197 | else | ||
198 | { | ||
199 | ret = imx233_ssp_sd_mmc_transfer(MMC_SSP, 23, count, SSP_SHORT_RESP, NULL, | ||
200 | 0, false, false, &resp); | ||
201 | if(ret == 0) | ||
202 | ret = imx233_ssp_sd_mmc_transfer(MMC_SSP, 18, start, SSP_SHORT_RESP, buf, | ||
203 | count, false, true, &resp); | ||
204 | } | ||
205 | |||
206 | mutex_unlock(&mmc_mutex); | ||
207 | |||
208 | return ret; | ||
91 | } | 209 | } |
92 | 210 | ||
93 | int mmc_write_sectors(IF_MD2(int drive,) unsigned long start, int count, const void* buf) | 211 | int mmc_write_sectors(IF_MD2(int drive,) unsigned long start, int count, const void* buf) |