imx233/fuze+: add SD detection support

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30196 a1c6a512-1295-4272-9138-f99709370657
diff --git a/bootloader/imx233.c b/bootloader/imx233.c
index bec8cee..f6c5ad9 100644
--- a/bootloader/imx233.c
+++ b/bootloader/imx233.c
@@ -40,8 +40,8 @@
 
 #include "usb.h"
 
-void main(void) NORETURN_ATTR;
-void main(void)
+void main(uint32_t arg) NORETURN_ATTR;
+void main(uint32_t arg)
 {
     unsigned char* loadbuffer;
     int buffer_size;
@@ -62,6 +62,8 @@
     button_init_device();
 
     //button_debug_screen();
+    printf("arg=%c%c%c%c", arg >> 24,
+        (arg >> 16) & 0xff, (arg >> 8) & 0xff, (arg & 0xff));
 
     ret = storage_init();
     if(ret < 0)
@@ -84,6 +86,9 @@
         error(EDISK, ret, true);
     }
 
+    if(button_read_device() & BUTTON_VOL_UP)
+        printf("Booting from SD card required.");
+
     printf("Loading firmware");
 
     loadbuffer = (unsigned char*)DRAM_ORIG; /* DRAM */
diff --git a/firmware/export/config/sansafuzeplus.h b/firmware/export/config/sansafuzeplus.h
index e0dc76a..4c3cd60 100644
--- a/firmware/export/config/sansafuzeplus.h
+++ b/firmware/export/config/sansafuzeplus.h
@@ -118,13 +118,9 @@
 #define HAVE_FLASH_STORAGE
 
 /* define this if the flash memory uses the SecureDigital Memory Card protocol */
-#ifdef BOOTLOADER
-# define CONFIG_STORAGE STORAGE_MMC
-#else
-# define CONFIG_STORAGE (STORAGE_SD | STORAGE_MMC)
-# define NUM_DRIVES 2
-# define HAVE_HOTSWAP
-#endif
+#define CONFIG_STORAGE (STORAGE_SD | STORAGE_MMC)
+#define NUM_DRIVES 2
+#define HAVE_HOTSWAP
 
 /* todo */
 #define BATTERY_CAPACITY_DEFAULT 550    /* default battery capacity */
diff --git a/firmware/target/arm/imx233/mmc-imx233.c b/firmware/target/arm/imx233/mmc-imx233.c
index b0f1f36..889ba0c 100644
--- a/firmware/target/arm/imx233/mmc-imx233.c
+++ b/firmware/target/arm/imx233/mmc-imx233.c
@@ -25,7 +25,6 @@
 #include "storage.h"
 #include "ssp-imx233.h"
 #include "pinctrl-imx233.h"
-#include "button-target.h"
 
 /**
  * This code assumes a single eMMC internal flash
@@ -216,3 +215,9 @@
     (void) buf;
     return -1;
 }
+
+bool mmc_present(IF_MD(int drive))
+{
+    IF_MD((void) drive);
+    return true;
+}
diff --git a/firmware/target/arm/imx233/sd-imx233.c b/firmware/target/arm/imx233/sd-imx233.c
index ccd8bf3..5e9f2cf 100644
--- a/firmware/target/arm/imx233/sd-imx233.c
+++ b/firmware/target/arm/imx233/sd-imx233.c
@@ -22,9 +22,54 @@
 #include "system.h"
 #include "sd.h"
 #include "sdmmc.h"
+#include "ssp-imx233.h"
+#include "pinctrl-imx233.h"
+#include "button-target.h"
+
+/**
+ * This code assumes a single SD card slot
+ */
+
+#ifdef SANSA_FUZEPLUS
+#define SD_SSP      1
+#else
+#error You need to configure the ssp to use
+#endif
+
+static tCardInfo card_info;
+static struct mutex sd_mutex;
+
+static void sd_detect_callback(int ssp)
+{
+    (void)ssp;
+
+    /* This is called only if the state was stable for 300ms - check state
+     * and post appropriate event. */
+    if(imx233_ssp_sdmmc_detect(SD_SSP))
+        queue_broadcast(SYS_HOTSWAP_INSERTED, 0);
+    else
+        queue_broadcast(SYS_HOTSWAP_EXTRACTED, 0);
+    printf("sd_detect_callback(%d)", imx233_ssp_sdmmc_detect(SD_SSP));
+    imx233_ssp_sdmmc_setup_detect(SD_SSP, true, sd_detect_callback);
+}
 
 int sd_init(void)
 {
+    mutex_init(&sd_mutex);
+    
+    imx233_ssp_start(SD_SSP);
+    imx233_ssp_softreset(SD_SSP);
+    imx233_ssp_set_mode(SD_SSP, HW_SSP_CTRL1__SSP_MODE__SD_MMC);
+    #ifdef SANSA_FUZEPLUS
+    imx233_ssp_setup_ssp1_sd_mmc_pins(true, 4, PINCTRL_DRIVE_8mA, false);
+    #endif
+    imx233_ssp_sdmmc_setup_detect(SD_SSP, true, sd_detect_callback);
+    /* SSPCLK @ 96MHz
+     * gives bitrate of 96000 / 240 / 1 = 400kHz */
+    imx233_ssp_set_timings(SD_SSP, 240, 0, 0xffff);
+    imx233_ssp_set_bus_width(SD_SSP, 1);
+    imx233_ssp_set_block_size(SD_SSP, 9);
+    
     return 0;
 }
 
@@ -57,6 +102,12 @@
 int sd_num_drives(int first_drive)
 {
     (void) first_drive;
-    return 0;
+    return 1;
+}
+
+bool sd_present(IF_MD(int drive))
+{
+    IF_MD((void) drive);
+    return imx233_ssp_sdmmc_detect(SD_SSP);
 }
 
diff --git a/firmware/target/arm/imx233/ssp-imx233.c b/firmware/target/arm/imx233/ssp-imx233.c
index 59405bb..c3ce8a8 100644
--- a/firmware/target/arm/imx233/ssp-imx233.c
+++ b/firmware/target/arm/imx233/ssp-imx233.c
@@ -44,6 +44,7 @@
 static struct ssp_dma_command_t ssp_dma_cmd[2];
 static uint32_t ssp_bus_width[2];
 static unsigned ssp_log_block_size[2];
+static ssp_detect_cb_t ssp_detect_cb[2];
 
 void INT_SSP(int ssp)
 {
@@ -146,22 +147,7 @@
 
     if(ssp == 1)
     {
-        /* SSP_SCK: drive 8mA */
-        imx233_set_pin_drive_strength(2, 6, PINCTRL_DRIVE_8mA);
-        /* SSP_{SCK,DATA{3,2,1,0},DETECT,CMD} */
-        imx233_set_pin_function(2, 6, PINCTRL_FUNCTION_MAIN);
-        imx233_set_pin_function(2, 5, PINCTRL_FUNCTION_MAIN);
-        imx233_set_pin_function(2, 4, PINCTRL_FUNCTION_MAIN);
-        imx233_set_pin_function(2, 3, PINCTRL_FUNCTION_MAIN);
-        imx233_set_pin_function(2, 2, PINCTRL_FUNCTION_MAIN);
-        imx233_set_pin_function(2, 1, PINCTRL_FUNCTION_MAIN);
-        imx233_set_pin_function(2, 0, PINCTRL_FUNCTION_MAIN);
-        /* SSP_CMD: pullup */
-        imx233_enable_pin_pullup(2, 0, true);
-        imx233_enable_pin_pullup(2, 2, true);
-        imx233_enable_pin_pullup(2, 3, true);
-        imx233_enable_pin_pullup(2, 4, true);
-        imx233_enable_pin_pullup(2, 5, true);
+        
     }
     else
     {
@@ -170,6 +156,41 @@
 }
 #endif
 
+void imx233_ssp_setup_ssp1_sd_mmc_pins(bool enable_pullups, unsigned bus_width,
+    unsigned drive_strength, bool use_alt)
+{
+    /* SSP_{CMD,SCK} */
+    imx233_set_pin_drive_strength(2, 0, drive_strength);
+    imx233_set_pin_drive_strength(2, 6, drive_strength);
+    imx233_set_pin_function(2, 0, PINCTRL_FUNCTION_MAIN);
+    imx233_set_pin_function(2, 6, PINCTRL_FUNCTION_MAIN);
+    imx233_enable_pin_pullup(2, 0, enable_pullups);
+    /* SSP_DATA{0-3} */
+    for(unsigned i = 0; i < MIN(bus_width, 4); i++)
+    {
+        imx233_set_pin_drive_strength(2, 2 + i, drive_strength);
+        imx233_set_pin_function(2, 2 + i, PINCTRL_FUNCTION_MAIN);
+        imx233_enable_pin_pullup(2, 2 + i, enable_pullups);
+    }
+
+    /* SSP_DATA{4-7} */
+    for(unsigned i = 4; i < bus_width; i++)
+    {
+        if(use_alt)
+        {
+            imx233_set_pin_drive_strength(0, 22 + i, drive_strength);
+            imx233_set_pin_function(0, 22 + i, PINCTRL_FUNCTION_ALT2);
+            imx233_enable_pin_pullup(0, 22 + i, enable_pullups);
+        }
+        else
+        {
+            imx233_set_pin_drive_strength(0, 4 + i, drive_strength);
+            imx233_set_pin_function(0, 4 + i, PINCTRL_FUNCTION_ALT2);
+            imx233_enable_pin_pullup(0, 4 + i, enable_pullups);
+        }
+    }
+}
+
 void imx233_ssp_setup_ssp2_sd_mmc_pins(bool enable_pullups, unsigned bus_width,
     unsigned drive_strength)
 {
@@ -301,3 +322,52 @@
     mdelay(1);
     __REG_CLR(HW_SSP_CMD0(ssp)) = HW_SSP_CMD0__CONT_CLKING_EN;
 }
+
+static int ssp_detect_oneshot_callback(int ssp)
+{
+    printf("ssp_detect_oneshot_callback(%d)", ssp);
+    if(ssp_detect_cb[ssp - 1])
+        ssp_detect_cb[ssp - 1](ssp);
+
+    return 0;
+}
+
+static int ssp1_detect_oneshot_callback(struct timeout *tmo)
+{
+    (void) tmo;
+    return ssp_detect_oneshot_callback(1);
+}
+
+static int ssp2_detect_oneshot_callback(struct timeout *tmo)
+{
+    (void) tmo;
+    return ssp_detect_oneshot_callback(2);
+}
+
+static void detect_irq(int bank, int pin)
+{
+    static struct timeout ssp1_detect_oneshot;
+    static struct timeout ssp2_detect_oneshot;
+    if(bank == 2 && pin == 1)
+        timeout_register(&ssp1_detect_oneshot, ssp1_detect_oneshot_callback, (3*HZ/10), 0);
+    else if(bank == 0 && pin == 19)
+        timeout_register(&ssp2_detect_oneshot, ssp2_detect_oneshot_callback, (3*HZ/10), 0);
+}
+
+void imx233_ssp_sdmmc_setup_detect(int ssp, bool enable, ssp_detect_cb_t fn)
+{
+    int bank = ssp == 1 ? 2 : 0;
+    int pin = ssp == 1 ? 1 : 19;
+    ssp_detect_cb[ssp - 1] = fn;
+    if(enable)
+    {
+        imx233_set_pin_function(bank, pin, PINCTRL_FUNCTION_GPIO);
+        imx233_enable_gpio_output(bank, pin, false);
+    }
+    imx233_setup_pin_irq(bank, pin, enable, true, !imx233_ssp_sdmmc_detect(ssp), detect_irq);
+}
+
+bool imx233_ssp_sdmmc_detect(int ssp)
+{
+    return !!(HW_SSP_STATUS(ssp) & HW_SSP_STATUS__CARD_DETECT);
+}
diff --git a/firmware/target/arm/imx233/ssp-imx233.h b/firmware/target/arm/imx233/ssp-imx233.h
index c7c891e..c9083d8 100644
--- a/firmware/target/arm/imx233/ssp-imx233.h
+++ b/firmware/target/arm/imx233/ssp-imx233.h
@@ -27,6 +27,7 @@
 #include "cpu.h"
 #include "system.h"
 #include "system-target.h"
+#include "pinctrl-imx233.h"
 
 /* ssp can value 1 or 2 */
 #define __SSP_SELECT(ssp, ssp1, ssp2) ((ssp) == 1 ? (ssp1) : (ssp2))
@@ -122,6 +123,7 @@
 #define HW_SSP_STATUS__RESP_TIMEOUT         (1 << 14)
 #define HW_SSP_STATUS__RESP_ERR             (1 << 15)
 #define HW_SSP_STATUS__RESP_CRC_ERR         (1 << 16)
+#define HW_SSP_STATUS__CARD_DETECT          (1 << 28)
 #define HW_SSP_STATUS__ALL_ERRORS           0x1f800
 
 #define HW_SSP_DEBUG(ssp)   (*(volatile uint32_t *)(HW_SSP_BASE(ssp) + 0x100))
@@ -142,6 +144,8 @@
     SSP_LONG_RESP
 };
 
+typedef void (*ssp_detect_cb_t)(int ssp);
+
 void imx233_ssp_init(void);
 void imx233_ssp_start(int ssp);
 void imx233_ssp_stop(int ssp);
@@ -156,8 +160,14 @@
 enum imx233_ssp_error_t imx233_ssp_sd_mmc_transfer(int ssp, uint8_t cmd,
     uint32_t cmd_arg, enum imx233_ssp_resp_t resp, void *buffer, unsigned block_count,
     bool wait4irq, bool read, uint32_t *resp_ptr);
+void imx233_ssp_setup_ssp1_sd_mmc_pins(bool enable_pullups, unsigned bus_width,
+    unsigned drive_strength, bool use_alt);
 void imx233_ssp_setup_ssp2_sd_mmc_pins(bool enable_pullups, unsigned bus_width,
     unsigned drive_strength);
+/* after callback is fired, imx233_ssp_sdmmc_setup_detect needs to be called
+ * to enable detection again */
+void imx233_ssp_sdmmc_setup_detect(int ssp, bool enable, ssp_detect_cb_t fn);
+bool imx233_ssp_sdmmc_detect(int ssp);
 /* SD/MMC requires that the card be provided the clock during an init sequence of
  * at least 1msec (or 74 clocks). Does NOT touch the clock so it has to be correct. */
 void imx233_ssp_sd_mmc_power_up_sequence(int ssp);