1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2011 by Amaury Pouly
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
26 #include "ssp-imx233.h"
27 #include "pinctrl-imx233.h"
30 * This code assumes a single eMMC internal flash
36 #error You need to configure the ssp to use
41 /** When set, this values restrict the windows of the read and writes */
42 static unsigned mmc_window_start
= 0;
43 static unsigned mmc_window_end
= INT_MAX
;
44 static bool mmc_window_enable
= true;
45 static long mmc_last_activity
= -1;
46 static bool mmc_is_active
= false;
47 static unsigned mmc_size
= 0;
49 static struct mutex mmc_mutex
;
51 void imx233_mmc_disable_window(void)
53 mmc_window_enable
= false;
58 mutex_init(&mmc_mutex
);
60 imx233_ssp_start(MMC_SSP
);
61 imx233_ssp_softreset(MMC_SSP
);
62 imx233_ssp_set_mode(MMC_SSP
, HW_SSP_CTRL1__SSP_MODE__SD_MMC
);
64 /** Sansa Fuze+ has an internal eMMC 8-bit wide flash, power gate is pin PWM3
65 * and power up time is 20ms */
66 imx233_set_pin_function(1, 29, PINCTRL_FUNCTION_GPIO
);
67 imx233_enable_gpio_output(1, 29, true);
68 imx233_set_gpio_output(1, 29, false);
71 imx233_ssp_setup_ssp2_sd_mmc_pins(true, 8, PINCTRL_DRIVE_8mA
);
74 * gives bitrate of 96000 / 240 / 1 = 400kHz */
75 imx233_ssp_set_timings(MMC_SSP
, 240, 0, 0xffff);
76 imx233_ssp_sd_mmc_power_up_sequence(MMC_SSP
);
77 imx233_ssp_set_bus_width(MMC_SSP
, 1);
78 imx233_ssp_set_block_size(MMC_SSP
, 9);
79 /* go to idle state */
80 int ret
= imx233_ssp_sd_mmc_transfer(MMC_SSP
, 0, 0, SSP_NO_RESP
, NULL
, 0, false, false, NULL
);
83 /* send op cond until the card respond with busy bit set; it must complete within 1sec */
84 unsigned timeout
= current_tick
+ HZ
;
88 ret
= imx233_ssp_sd_mmc_transfer(MMC_SSP
, 1, 0x40ff8000, SSP_SHORT_RESP
, NULL
, 0, false, false, &ocr
);
89 if(ret
== 0 && ocr
& (1 << 31))
91 }while(!TIME_AFTER(current_tick
, timeout
));
97 ret
= imx233_ssp_sd_mmc_transfer(MMC_SSP
, 2, 0, SSP_LONG_RESP
, NULL
, 0, false, false, cid
);
102 ret
= imx233_ssp_sd_mmc_transfer(MMC_SSP
, 3, MMC_RCA
<< 16, SSP_SHORT_RESP
, NULL
, 0, false, false, &status
);
106 ret
= imx233_ssp_sd_mmc_transfer(MMC_SSP
, 7, MMC_RCA
<< 16, SSP_SHORT_RESP
, NULL
, 0, false, false, &status
);
109 /* Check TRAN state */
110 ret
= imx233_ssp_sd_mmc_transfer(MMC_SSP
, 13, MMC_RCA
<< 16, SSP_SHORT_RESP
, NULL
, 0, false, false, &status
);
113 if(((status
>> 9) & 0xf) != 4)
115 /* Switch to 8-bit bus */
116 ret
= imx233_ssp_sd_mmc_transfer(MMC_SSP
, 6, 0x3b70200, SSP_SHORT_RESP
, NULL
, 0, true, false, &status
);
122 imx233_ssp_set_bus_width(MMC_SSP
, 8);
123 /* Switch to high speed mode */
124 ret
= imx233_ssp_sd_mmc_transfer(MMC_SSP
, 6, 0x3b90100, SSP_SHORT_RESP
, NULL
, 0, true, false, &status
);
131 * gives bitrate of 96 / 2 / 1 = 48MHz */
132 imx233_ssp_set_timings(MMC_SSP
, 2, 0, 0xffff);
134 /* read extended CSD */
136 uint8_t ext_csd
[512];
137 ret
= imx233_ssp_sd_mmc_transfer(MMC_SSP
, 8, 0, SSP_SHORT_RESP
, ext_csd
, 1, true, true, &status
);
140 uint32_t *sec_count
= (void *)&ext_csd
[212];
141 mmc_size
= *sec_count
;
144 #ifdef SANSA_FUZEPLUS
145 if(mmc_window_enable
)
148 * The Fuze+ uses a strange layout: is has a first MBR at sector 0 with four entries:
149 * 1) Actual user partition
150 * 2) Sigmatel boot partition
151 * 3)4) Other (certificate related ?) partitions
152 * The partition 1) has type 1 but it's actually a type 5 (logical partition) with
153 * a second partition table with usually one entry which is the FAT32 one.
154 * The first table uses 512-byte sector size and the second one usually uses
155 * 2048-byte logical sector size.
157 * We restrict mmc window to the user partition */
159 mmc_window_start
= 0;
160 mmc_window_end
= INT_MAX
;
161 ret
= mmc_read_sectors(IF_MD2(0,) 0, 1, mbr
);
164 if(mbr
[510] != 0x55 || mbr
[511] != 0xAA)
165 return -101; /* invalid MBR */
166 /* sanity check that the first partition is greater than 2Gib */
167 uint8_t *ent
= &mbr
[446];
168 mmc_window_start
= ent
[8] | ent
[9] << 8 | ent
[10] << 16 | ent
[11] << 24;
169 mmc_window_end
= (ent
[12] | ent
[13] << 8 | ent
[14] << 16 | ent
[15] << 24) +
172 return -102; /* sigmatel partition */
173 if((mmc_window_end
- mmc_window_start
) < 4 * 1024 * 1024)
174 return -103; /* partition too small */
175 mmc_size
= mmc_window_end
- mmc_window_start
;
182 int mmc_num_drives(int first_drive
)
188 #ifdef STORAGE_GET_INFO
189 void mmc_get_info(IF_MD2(int drive
,) struct storage_info
*info
)
191 #ifdef HAVE_MULTIDRIVE
194 info
->sector_size
= 512;
195 info
->num_sectors
= mmc_size
;
196 info
->vendor
= "Rockbox";
197 info
->product
= "Internal Storage";
198 info
->revision
= "0.00";
202 static int transfer_sectors(IF_MD2(int drive
,) unsigned long start
, int count
, void *buf
, bool read
)
206 start
+= mmc_window_start
;
207 if((start
+ count
) > mmc_window_end
)
209 /* get mutex (needed because we do multiple commands for count > 0) */
210 mutex_lock(&mmc_mutex
);
214 mmc_last_activity
= current_tick
;
215 mmc_is_active
= true;
219 int this_count
= MIN(count
, IMX233_MAX_SSP_XFER_SIZE
/ 512);
222 ret
= imx233_ssp_sd_mmc_transfer(MMC_SSP
, read
? 17 : 24, start
,
223 SSP_SHORT_RESP
, buf
, this_count
, false, read
, &resp
);
227 ret
= imx233_ssp_sd_mmc_transfer(MMC_SSP
, 23, this_count
, SSP_SHORT_RESP
, NULL
,
228 0, false, false, &resp
);
230 ret
= imx233_ssp_sd_mmc_transfer(MMC_SSP
, read
? 18 : 25, start
,
231 SSP_SHORT_RESP
, buf
, this_count
, false, read
, &resp
);
235 buf
+= this_count
* 512;
236 }while(count
!= 0 && ret
== SSP_SUCCESS
);
238 mmc_is_active
= false;
240 mutex_unlock(&mmc_mutex
);
245 int mmc_read_sectors(IF_MD2(int drive
,) unsigned long start
, int count
, void *buf
)
247 return transfer_sectors(IF_MD2(drive
,) start
, count
, buf
, true);
250 int mmc_write_sectors(IF_MD2(int drive
,) unsigned long start
, int count
, const void* buf
)
252 return transfer_sectors(IF_MD2(drive
,) start
, count
, (void *)buf
, false);
255 bool mmc_present(IF_MD(int drive
))
261 bool mmc_removable(IF_MD(int drive
))
271 void mmc_sleepnow(void)
275 bool mmc_disk_is_active(void)
277 return mmc_is_active
;
280 bool mmc_usb_active(void)
282 return mmc_disk_is_active();
285 int mmc_soft_reset(void)
299 void mmc_spindown(int seconds
)
304 long mmc_last_disk_activity(void)
306 return mmc_last_activity
;
309 int mmc_spinup_time(void)
314 void mmc_enable(bool enable
)