You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by gn...@apache.org on 2020/03/03 15:06:42 UTC

[incubator-nuttx] branch master updated: NXP Freedom K28F Board SD-Card support (#423)

This is an automated email from the ASF dual-hosted git repository.

gnutt pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new a7d783d  NXP Freedom K28F Board SD-Card support (#423)
a7d783d is described below

commit a7d783d463e97e91cafc8c55d1ee2fe83e024af8
Author: johannes-nivus <jo...@nivus.com>
AuthorDate: Tue Mar 3 15:52:55 2020 +0100

    NXP Freedom K28F Board SD-Card support (#423)
    
    * Adds SDHC support for NXP Freedom-K28F
---
 arch/arm/src/kinetis/kinetis_sdhc.c                | 158 ++++++-----
 boards/arm/kinetis/freedom-k28f/Kconfig            |  30 +-
 boards/arm/kinetis/freedom-k28f/include/board.h    |  11 +
 boards/arm/kinetis/freedom-k28f/src/Makefile       |   7 +
 boards/arm/kinetis/freedom-k28f/src/freedom-k28f.h | 179 +++++++++++-
 .../arm/kinetis/freedom-k28f/src/k28_automount.c   | 310 +++++++++++++++++++++
 boards/arm/kinetis/freedom-k28f/src/k28_bringup.c  |  37 +++
 boards/arm/kinetis/freedom-k28f/src/k28_sdhc.c     | 240 ++++++++++++++++
 8 files changed, 897 insertions(+), 75 deletions(-)

diff --git a/arch/arm/src/kinetis/kinetis_sdhc.c b/arch/arm/src/kinetis/kinetis_sdhc.c
index 81be303..58d912b 100644
--- a/arch/arm/src/kinetis/kinetis_sdhc.c
+++ b/arch/arm/src/kinetis/kinetis_sdhc.c
@@ -1,35 +1,20 @@
 /****************************************************************************
  * arch/arm/src/kinetis/kinetis_sdhc.c
  *
- *   Copyright (C) 2011-2012, 2014, 2016-2018 Gregory Nutt. All rights reserved.
- *   Author: Gregory Nutt <gn...@nuttx.org>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- * 3. Neither the name NuttX nor the names of its contributors may be
- *    used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
  *
  ****************************************************************************/
 
@@ -174,6 +159,7 @@ struct kinetis_dev_s
   struct sdio_dev_s  dev;        /* Standard, base SDIO interface */
 
   /* Kinetis-specific extensions */
+
   /* Event support */
 
   sem_t              waitsem;    /* Implements event waiting */
@@ -267,16 +253,18 @@ static void kinetis_showregs(struct kinetis_dev_s *priv, const char *msg);
 /* Data Transfer Helpers ****************************************************/
 
 static void kinetis_dataconfig(struct kinetis_dev_s *priv, bool bwrite,
-                               unsigned int blocksize, unsigned int nblocks,
-                               unsigned int timeout);
+              unsigned int blocksize, unsigned int nblocks,
+              unsigned int timeout);
 static void kinetis_datadisable(void);
 #ifndef CONFIG_KINETIS_SDHC_DMA
 static void kinetis_transmit(struct kinetis_dev_s *priv);
 static void kinetis_receive(struct kinetis_dev_s *priv);
 #endif
 static void kinetis_eventtimeout(int argc, uint32_t arg);
-static void kinetis_endwait(struct kinetis_dev_s *priv, sdio_eventset_t wkupevent);
-static void kinetis_endtransfer(struct kinetis_dev_s *priv, sdio_eventset_t wkupevent);
+static void kinetis_endwait(struct kinetis_dev_s *priv,
+              sdio_eventset_t wkupevent);
+static void kinetis_endtransfer(struct kinetis_dev_s *priv,
+              sdio_eventset_t wkupevent);
 
 /* Interrupt Handling *******************************************************/
 
@@ -307,12 +295,19 @@ static int  kinetis_attach(FAR struct sdio_dev_s *dev);
 
 static int  kinetis_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
               uint32_t arg);
+
+#ifdef CONFIG_SDIO_BLOCKSETUP
+static void  kinetis_blocksetup(FAR struct sdio_dev_s *dev,
+              unsigned int blocksize, unsigned int nblocks);
+#endif
+
 #ifndef CONFIG_KINETIS_SDHC_DMA
 static int  kinetis_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
               size_t nbytes);
 static int  kinetis_sendsetup(FAR struct sdio_dev_s *dev,
               FAR const uint8_t *buffer, uint32_t nbytes);
 #endif
+
 static int  kinetis_cancel(FAR struct sdio_dev_s *dev);
 
 static int  kinetis_waitresponse(FAR struct sdio_dev_s *dev, uint32_t cmd);
@@ -367,6 +362,9 @@ struct kinetis_dev_s g_sdhcdev =
     .clock            = kinetis_clock,
     .attach           = kinetis_attach,
     .sendcmd          = kinetis_sendcmd,
+#ifdef CONFIG_SDIO_BLOCKSETUP
+    .blocksetup       = kinetis_blocksetup,
+#endif
 #ifndef CONFIG_KINETIS_SDHC_DMA
     .recvsetup        = kinetis_recvsetup,
     .sendsetup        = kinetis_sendsetup,
@@ -410,9 +408,6 @@ static struct kinetis_sdhcregs_s g_sampleregs[DEBUG_NSAMPLES];
  ****************************************************************************/
 
 /****************************************************************************
- * Low-level Helpers
- ****************************************************************************/
-/****************************************************************************
  * Name: kinetis_takesem
  *
  * Description:
@@ -497,10 +492,6 @@ static void kinetis_configxfrints(struct kinetis_dev_s *priv, uint32_t xfrints)
 }
 
 /****************************************************************************
- * DMA Helpers
- ****************************************************************************/
-
-/****************************************************************************
  * Name: kinetis_sampleinit
  *
  * Description:
@@ -615,9 +606,12 @@ static void kinetis_dumpsample(struct kinetis_dev_s *priv,
 #ifdef CONFIG_SDIO_XFRDEBUG
 static void  kinetis_dumpsamples(struct kinetis_dev_s *priv)
 {
-  kinetis_dumpsample(priv, &g_sampleregs[SAMPLENDX_BEFORE_SETUP], "Before setup");
-  kinetis_dumpsample(priv, &g_sampleregs[SAMPLENDX_AFTER_SETUP], "After setup");
-  kinetis_dumpsample(priv, &g_sampleregs[SAMPLENDX_END_TRANSFER], "End of transfer");
+  kinetis_dumpsample(priv, &g_sampleregs[SAMPLENDX_BEFORE_SETUP],
+                     "Before setup");
+  kinetis_dumpsample(priv, &g_sampleregs[SAMPLENDX_AFTER_SETUP],
+                     "After setup");
+  kinetis_dumpsample(priv, &g_sampleregs[SAMPLENDX_END_TRANSFER],
+                     "End of transfer");
 }
 #endif
 
@@ -640,10 +634,6 @@ static void kinetis_showregs(struct kinetis_dev_s *priv, const char *msg)
 #endif
 
 /****************************************************************************
- * Data Transfer Helpers
- ****************************************************************************/
-
-/****************************************************************************
  * Name: kinetis_dataconfig
  *
  * Description:
@@ -970,7 +960,8 @@ static void kinetis_eventtimeout(int argc, uint32_t arg)
  *
  ****************************************************************************/
 
-static void kinetis_endwait(struct kinetis_dev_s *priv, sdio_eventset_t wkupevent)
+static void kinetis_endwait(struct kinetis_dev_s *priv,
+                            sdio_eventset_t wkupevent)
 {
   /* Cancel the watchdog timeout */
 
@@ -1005,7 +996,8 @@ static void kinetis_endwait(struct kinetis_dev_s *priv, sdio_eventset_t wkupeven
  *
  ****************************************************************************/
 
-static void kinetis_endtransfer(struct kinetis_dev_s *priv, sdio_eventset_t wkupevent)
+static void kinetis_endtransfer(struct kinetis_dev_s *priv,
+                                sdio_eventset_t wkupevent)
 {
 #ifdef CONFIG_KINETIS_SDHC_DMA
   uint32_t regval;
@@ -1054,10 +1046,6 @@ static void kinetis_endtransfer(struct kinetis_dev_s *priv, sdio_eventset_t wkup
 }
 
 /****************************************************************************
- * Interrupt Handling
- ****************************************************************************/
-
-/****************************************************************************
  * Name: kinetis_interrupt
  *
  * Description:
@@ -1143,7 +1131,8 @@ static int kinetis_interrupt(int irq, void *context, FAR void *arg)
         {
           /* Terminate the transfer with an error */
 
-          mcerr("ERROR: Data block CRC failure, remaining: %d\n", priv->remaining);
+          mcerr("ERROR: Data block CRC failure, remaining: %d\n",
+                priv->remaining);
           kinetis_endtransfer(priv, SDIOWAIT_TRANSFERDONE | SDIOWAIT_ERROR);
         }
 
@@ -1192,15 +1181,11 @@ static int kinetis_interrupt(int irq, void *context, FAR void *arg)
 }
 
 /****************************************************************************
- * SDIO Interface Methods
- ****************************************************************************/
-
-/****************************************************************************
  * Name: kinetis_lock
  *
  * Description:
  *   Locks the bus. Function calls low-level multiplexed bus routines to
- *   resolve bus requests and acknowledgement issues.
+ *   resolve bus requests and acknowledgment issues.
  *
  * Input Parameters:
  *   dev    - An instance of the SDIO device interface
@@ -1379,6 +1364,7 @@ static void kinetis_widebus(FAR struct sdio_dev_s *dev, bool wide)
     {
       regval |= SDHC_PROCTL_DTW_1BIT;
     }
+
   putreg32(regval, KINETIS_SDHC_PROCTL);
 }
 
@@ -1690,7 +1676,6 @@ static int kinetis_attach(FAR struct sdio_dev_s *dev)
   ret = irq_attach(KINETIS_IRQ_SDHC, kinetis_interrupt, NULL);
   if (ret == OK)
     {
-
       /* Disable all interrupts at the SDIO controller and clear all pending
        * interrupts.
        */
@@ -1871,6 +1856,40 @@ static int kinetis_sendcmd(FAR struct sdio_dev_s *dev, uint32_t cmd,
 }
 
 /****************************************************************************
+ * Name: kinetis_blocksetup
+ *
+ * Description:
+ *   Configure block size and the number of blocks for next transfer
+ *
+ * Input Parameters:
+ *   dev       - An instance of the SDIO device interface
+ *   blocksize  - The selected block size.
+ *   nblocklen - The number of blocks to transfer
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SDIO_BLOCKSETUP
+static void kinetis_blocksetup(FAR struct sdio_dev_s *dev,
+                             unsigned int blocksize,
+                             unsigned int nblocks)
+{
+  uint32_t regval;
+
+  mcinfo("blocksize=%ld, total transfer=%ld (%ld blocks)\n", blocksize,
+         blocksize * nblocks, nblocks);
+
+  /* Configure block size for next transfer */
+
+  regval = blocksize << SDHC_BLKATTR_SIZE_SHIFT |
+           nblocks   << SDHC_BLKATTR_CNT_SHIFT;
+  putreg32(regval, KINETIS_SDHC_BLKATTR);
+}
+#endif
+
+/****************************************************************************
  * Name: kinetis_recvsetup
  *
  * Description:
@@ -1943,8 +1962,8 @@ static int kinetis_recvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
  ****************************************************************************/
 
 #ifndef CONFIG_KINETIS_SDHC_DMA
-static int kinetis_sendsetup(FAR struct sdio_dev_s *dev, FAR const uint8_t *buffer,
-                           size_t nbytes)
+static int kinetis_sendsetup(FAR struct sdio_dev_s *dev,
+                             FAR const uint8_t *buffer, size_t nbytes)
 {
   struct kinetis_dev_s *priv = (struct kinetis_dev_s *)dev;
 
@@ -2170,7 +2189,6 @@ static int kinetis_recvshortcrc(FAR struct sdio_dev_s *dev, uint32_t cmd,
    *     0         1               End bit
    */
 
-
 #ifdef CONFIG_DEBUG_FEATURES
   if (!rshort)
     {
@@ -2331,7 +2349,8 @@ static int kinetis_recvshort(FAR struct sdio_dev_s *dev, uint32_t cmd,
 
 /* MMC responses not supported */
 
-static int kinetis_recvnotimpl(FAR struct sdio_dev_s *dev, uint32_t cmd, uint32_t *rnotimpl)
+static int kinetis_recvnotimpl(FAR struct sdio_dev_s *dev, uint32_t cmd,
+                               uint32_t *rnotimpl)
 {
   /* Just return an error */
 
@@ -2685,9 +2704,6 @@ static int kinetis_dmasendsetup(FAR struct sdio_dev_s *dev,
 #endif
 
 /****************************************************************************
- * Initialization/uninitialization/reset
- ****************************************************************************/
-/****************************************************************************
  * Name: kinetis_callback
  *
  * Description:
@@ -2752,8 +2768,10 @@ static void kinetis_callback(void *arg)
         {
           /* Yes.. queue it */
 
-           mcinfo("Queuing callback to %p(%p)\n", priv->callback, priv->cbarg);
-          work_queue(HPWORK, &priv->cbwork, (worker_t)priv->callback, priv->cbarg, 0);
+          mcinfo("Queuing callback to %p(%p)\n",
+                 priv->callback, priv->cbarg);
+          work_queue(HPWORK, &priv->cbwork, (worker_t)priv->callback,
+                     priv->cbarg, 0);
         }
       else
         {
@@ -2793,6 +2811,7 @@ FAR struct sdio_dev_s *sdhc_initialize(int slotno)
   DEBUGASSERT(slotno == 0);
 
   /* Initialize the SDHC slot structure data structure */
+
   /* Initialize semaphores */
 
   nxsem_init(&priv->waitsem, 0, 0);
@@ -2833,7 +2852,6 @@ FAR struct sdio_dev_s *sdhc_initialize(int slotno)
   putreg32(regval, KINETIS_SIM_SCGC3);
   mcinfo("SIM_SCGC3: %08x\n", regval);
 
-
   /* Configure pins for 1 or 4-bit, wide-bus operation (the chip is capable
    * of 8-bit wide bus operation but D4-D7 are not configured).
    *
diff --git a/boards/arm/kinetis/freedom-k28f/Kconfig b/boards/arm/kinetis/freedom-k28f/Kconfig
index 8a8ba84..928307b 100644
--- a/boards/arm/kinetis/freedom-k28f/Kconfig
+++ b/boards/arm/kinetis/freedom-k28f/Kconfig
@@ -5,4 +5,32 @@
 
 if ARCH_BOARD_FREEDOM_K28F
 
-endif
+config FRDMK28F_SDHC_AUTOMOUNT
+	bool "SDHC automounter"
+	default n
+	depends on FS_AUTOMOUNTER && KINETIS_SDHC
+
+if FRDMK28F_SDHC_AUTOMOUNT
+
+config FRDMK28F_SDHC_AUTOMOUNT_FSTYPE
+	string "SDHC file system type"
+	default "vfat"
+
+config FRDMK28F_SDHC_AUTOMOUNT_BLKDEV
+	string "SDHC block device"
+	default "/dev/mmcsd0"
+
+config FRDMK28F_SDHC_AUTOMOUNT_MOUNTPOINT
+	string "SDHC mount point"
+	default "/mnt/sdcard"
+
+config FRDMK28F_SDHC_AUTOMOUNT_DDELAY
+	int "SDHC debounce delay (milliseconds)"
+	default 1000
+
+config FRDMK28F_SDHC_AUTOMOUNT_UDELAY
+	int "SDHC unmount retry delay (milliseconds)"
+	default 2000
+
+endif # FRDMK28F_SDHC_AUTOMOUNT
+endif # ARCH_BOARD_FREEDOM_K28F
diff --git a/boards/arm/kinetis/freedom-k28f/include/board.h b/boards/arm/kinetis/freedom-k28f/include/board.h
index a17c39d..8dcee67 100644
--- a/boards/arm/kinetis/freedom-k28f/include/board.h
+++ b/boards/arm/kinetis/freedom-k28f/include/board.h
@@ -415,6 +415,17 @@
 #endif
 #endif
 
+/* SDHC */
+
+#ifdef CONFIG_KINETIS_SDHC
+#  define PIN_SDHC0_CMD     PIN_SDHC0_CMD_1
+#  define PIN_SDHC0_D0      PIN_SDHC0_D0_1
+#  define PIN_SDHC0_D1      PIN_SDHC0_D1_1
+#  define PIN_SDHC0_D2      PIN_SDHC0_D2_1
+#  define PIN_SDHC0_D3      PIN_SDHC0_D3_1
+#  define PIN_SDHC0_DCLK    PIN_SDHC0_DCLK_1
+#endif
+
 /* LED definitions **********************************************************/
 
 /* The Freedom K28F has a single RGB LED driven by the K28F as follows:
diff --git a/boards/arm/kinetis/freedom-k28f/src/Makefile b/boards/arm/kinetis/freedom-k28f/src/Makefile
index c2c6c0f..8b7b793 100644
--- a/boards/arm/kinetis/freedom-k28f/src/Makefile
+++ b/boards/arm/kinetis/freedom-k28f/src/Makefile
@@ -52,6 +52,13 @@ CSRCS += k28_userleds.c
 endif
 endif
 
+ifeq ($(CONFIG_KINETIS_SDHC),y)
+CSRCS += k28_sdhc.c
+ifeq ($(CONFIG_FS_AUTOMOUNTER),y)
+CSRCS += k28_automount.c
+endif
+endif
+
 ifeq ($(CONFIG_PWM),y)
 CSRCS += k28_pwm.c
 endif
diff --git a/boards/arm/kinetis/freedom-k28f/src/freedom-k28f.h b/boards/arm/kinetis/freedom-k28f/src/freedom-k28f.h
index 0554a43..e1ef15c 100644
--- a/boards/arm/kinetis/freedom-k28f/src/freedom-k28f.h
+++ b/boards/arm/kinetis/freedom-k28f/src/freedom-k28f.h
@@ -47,8 +47,95 @@
  * Pre-processor Definitions
  ****************************************************************************/
 
+/* Application Configuration ************************************************/
+
+/* Assume we have everything */
+
+#define HAVE_MMCSD       1
+#define HAVE_AUTOMOUNTER 1
+
+/* SD card support */
+
+#define MMCSD_SLOTNO 0
+
+/* Can't support MMC/SD features if mountpoints are disabled or if SDHC
+ * support is not enabled.
+ */
+
+#if defined(CONFIG_DISABLE_MOUNTPOINT) || !defined(CONFIG_KINETIS_SDHC)
+#  undef HAVE_MMCSD
+#endif
+
+#ifdef HAVE_MMCSD
+#  if defined(CONFIG_NSH_MMCSDSLOTNO) && CONFIG_NSH_MMCSDSLOTNO != 0
+#    error Only one MMC/SD slot, slot 0
+#  endif
+
+#  ifdef CONFIG_NSH_MMCSDMINOR
+#    define MMSCD_MINOR CONFIG_NSH_MMCSDMINOR
+#  else
+#    define MMSCD_MINOR 0
+#  endif
+
+/* We expect to receive GPIO interrupts for card insertion events */
+
+#  ifndef CONFIG_KINETIS_GPIOIRQ
+#    error "CONFIG_KINETIS_GPIOIRQ required for card detect interrupt"
+#  endif
+
+#  ifndef CONFIG_KINETIS_PORTBINTS
+#    error "CONFIG_KINETIS_PORTBINTS required for card detect interrupt"
+#  endif
+
+#endif
+
+/* Automounter */
+
+#if !defined(CONFIG_FS_AUTOMOUNTER) || !defined(HAVE_MMCSD)
+#  undef HAVE_AUTOMOUNTER
+#  undef CONFIG_FRDMK28F_SDHC_AUTOMOUNT
+#endif
+
+#ifndef CONFIG_FRDMK28F_SDHC_AUTOMOUNT
+#  undef HAVE_AUTOMOUNTER
+#endif
+
+/* Automounter defaults */
+
+#ifdef HAVE_AUTOMOUNTER
+
+#  ifndef CONFIG_FRDMK28F_SDHC_AUTOMOUNT_FSTYPE
+#    define CONFIG_FRDMK28F_SDHC_AUTOMOUNT_FSTYPE "vfat"
+#  endif
+
+#  ifndef CONFIG_FRDMK28F_SDHC_AUTOMOUNT_BLKDEV
+#    define CONFIG_FRDMK28F_SDHC_AUTOMOUNT_BLKDEV "/dev/mmcds0"
+#  endif
+
+#  ifndef CONFIG_FRDMK28F_SDHC_AUTOMOUNT_MOUNTPOINT
+#    define CONFIG_FRDMK28F_SDHC_AUTOMOUNT_MOUNTPOINT "/mnt/sdcard"
+#  endif
+
+#  ifndef CONFIG_FRDMK28F_SDHC_AUTOMOUNT_DDELAY
+#    define CONFIG_FRDMK28F_SDHC_AUTOMOUNT_DDELAY 1000
+#  endif
+
+#  ifndef CONFIG_FRDMK28F_SDHC_AUTOMOUNT_UDELAY
+#    define CONFIG_FRDMK28F_SDHC_AUTOMOUNT_UDELAY 2000
+#  endif
+#endif /* HAVE_AUTOMOUNTER */
+
 /* Freedom-K28F GPIOs *******************************************************/
 
+/* A micro Secure Digital (SD) card slot is available on the FRDM-K28F
+ * connected to the SD Host Controller (SDHC) signals of the MCU.
+ * This slot will accept micro format SD memory cards.
+ * The SD card detect pin (PTB5) is an open switch that shorts with VDD when
+ * card is inserted.
+ */
+
+#define GPIO_SD_CARDDETECT (GPIO_INPUT | PIN_INT_BOTH | PIN_PORTB | PIN5)
+
 /* An RGB LED is connected through GPIO as shown below:
  *
  *   LED    K28
@@ -90,14 +177,14 @@
 struct i2c_master_s;  /* Forward reference */
 
 #ifdef CONFIG_KINETIS_I2C0
-extern FAR struct i2c_master_s* g_i2c0_dev;
+extern FAR struct i2c_master_s *g_i2c0_dev;
 #endif
 #ifdef CONFIG_KINETIS_I2C1
-extern FAR struct i2c_master_s* g_i2c1_dev;
+extern FAR struct i2c_master_s *g_i2c1_dev;
 #endif
 
 /****************************************************************************
- * Public Functions
+ * Public Function Prototypes
  ****************************************************************************/
 
 /****************************************************************************
@@ -133,7 +220,7 @@ void weak_function k28_spidev_initialize(void);
  * Description:
  *   Called to configure I2C
  *
- *****************************************************************************/
+ ****************************************************************************/
 
 void k28_i2cdev_initialize(void);
 
@@ -148,6 +235,90 @@ void k28_i2cdev_initialize(void);
 extern void weak_function k28_usbdev_initialize(void);
 
 /****************************************************************************
+ * Name: k28_sdhc_initialize
+ *
+ * Description:
+ *   Inititialize the SDHC SD card slot
+ *
+ ****************************************************************************/
+
+#ifdef HAVE_MMCSD
+int k28_sdhc_initialize(void);
+#else
+#  define k28_sdhc_initialize() (OK)
+#endif
+
+/****************************************************************************
+ * Name: k28_cardinserted
+ *
+ * Description:
+ *   Check if a card is inserted into the SDHC slot
+ *
+ ****************************************************************************/
+
+#ifdef HAVE_AUTOMOUNTER
+bool k28_cardinserted(void);
+#else
+#  define k28_cardinserted() (false)
+#endif
+
+/****************************************************************************
+ * Name: k28_writeprotected
+ *
+ * Description:
+ *   Check if the card in the MMC/SD slot is write protected
+ *
+ ****************************************************************************/
+
+#ifdef HAVE_AUTOMOUNTER
+bool k28_writeprotected(void);
+#else
+#  define k28_writeprotected() (false)
+#endif
+
+/****************************************************************************
+ * Name:  k28_automount_initialize
+ *
+ * Description:
+ *   Configure auto-mounter for the configured SDHC slot
+ *
+ * Input Parameters:
+ *   None
+ *
+ *  Returned Value:
+ *    None
+ *
+ ****************************************************************************/
+
+#ifdef HAVE_AUTOMOUNTER
+void k28_automount_initialize(void);
+#endif
+
+/****************************************************************************
+ * Name:  k28_automount_event
+ *
+ * Description:
+ *   The SDHC card detection logic has detected an insertion or removal event.
+ *   It has already scheduled the MMC/SD block driver operations.
+ *   Now we need to schedule the auto-mount event which will occur with a
+ *   substantial delay to make sure that everything has settle down.
+ *
+ * Input Parameters:
+ *   inserted - True if the card is inserted in the slot.  False otherwise.
+ *
+ *  Returned Value:
+ *    None
+ *
+ *  Assumptions:
+ *    Interrupts are disabled.
+ *
+ ****************************************************************************/
+
+#ifdef HAVE_AUTOMOUNTER
+void k28_automount_event(bool inserted);
+#endif
+
+/****************************************************************************
  * Name: k28_pwm_setup
  *
  * Description:
diff --git a/boards/arm/kinetis/freedom-k28f/src/k28_automount.c b/boards/arm/kinetis/freedom-k28f/src/k28_automount.c
new file mode 100644
index 0000000..cf996a5
--- /dev/null
+++ b/boards/arm/kinetis/freedom-k28f/src/k28_automount.c
@@ -0,0 +1,310 @@
+/****************************************************************************
+ * boards/arm/kinetis/freedom-k28f/src/k28_automount.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#if defined(CONFIG_FS_AUTOMOUNTER_DEBUG) && !defined(CONFIG_DEBUG_FS)
+#  define CONFIG_DEBUG_FS 1
+#endif
+
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/clock.h>
+#include <nuttx/fs/automount.h>
+
+#include "freedom-k28f.h"
+
+#ifdef HAVE_AUTOMOUNTER
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#ifndef NULL
+#  define NULL (FAR void *)0
+#endif
+
+#ifndef OK
+#  define OK 0
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* This structure represents the changeable state of the automounter */
+
+struct k28_automount_state_s
+{
+  volatile automount_handler_t handler;    /* Upper half handler */
+  FAR void *arg;                           /* Handler argument */
+  bool enable;                             /* Fake interrupt enable */
+  bool pending;                            /* Set if there an event while disabled */
+};
+
+/* This structure represents the static configuration of an automounter */
+
+struct k28_automount_config_s
+{
+  /* This must be first thing in structure so that we can simply cast from
+   * struct automount_lower_s to struct k28_automount_config_s
+   */
+
+  struct automount_lower_s lower;          /* Publicly visible part */
+  FAR struct k28_automount_state_s *state; /* Changeable state */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int  k28_attach(FAR const struct automount_lower_s *lower,
+                       automount_handler_t isr, FAR void *arg);
+static void k28_enable(FAR const struct automount_lower_s *lower,
+                       bool enable);
+static bool k28_inserted(FAR const struct automount_lower_s *lower);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct k28_automount_state_s g_sdhc_state;
+static const struct k28_automount_config_s g_sdhc_config =
+{
+  .lower        =
+  {
+    .fstype     = CONFIG_FRDMK28F_SDHC_AUTOMOUNT_FSTYPE,
+    .blockdev   = CONFIG_FRDMK28F_SDHC_AUTOMOUNT_BLKDEV,
+    .mountpoint = CONFIG_FRDMK28F_SDHC_AUTOMOUNT_MOUNTPOINT,
+    .ddelay     = MSEC2TICK(CONFIG_FRDMK28F_SDHC_AUTOMOUNT_DDELAY),
+    .udelay     = MSEC2TICK(CONFIG_FRDMK28F_SDHC_AUTOMOUNT_UDELAY),
+    .attach     = k28_attach,
+    .enable     = k28_enable,
+    .inserted   = k28_inserted
+  },
+  .state        = &g_sdhc_state
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name:  k28_attach
+ *
+ * Description:
+ *   Attach a new SDHC event handler
+ *
+ * Input Parameters:
+ *   lower - An instance of the auto-mounter lower half state structure
+ *   isr   - The new event handler to be attach
+ *   arg   - Client data to be provided when the event handler is invoked.
+ *
+ *  Returned Value:
+ *    Always returns OK
+ *
+ ****************************************************************************/
+
+static int k28_attach(FAR const struct automount_lower_s *lower,
+                      automount_handler_t isr, FAR void *arg)
+{
+  FAR const struct k28_automount_config_s *config;
+  FAR struct k28_automount_state_s *state;
+
+  /* Recover references to our structure */
+
+  config = (FAR struct k28_automount_config_s *)lower;
+  DEBUGASSERT(config != NULL && config->state != NULL);
+
+  state = config->state;
+
+  /* Save the new handler info (clearing the handler first to eliminate race
+   * conditions).
+   */
+
+  state->handler = NULL;
+  state->pending = false;
+  state->arg     = arg;
+  state->handler = isr;
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  k28_enable
+ *
+ * Description:
+ *   Enable card insertion/removal event detection
+ *
+ * Input Parameters:
+ *   lower - An instance of the auto-mounter lower half state structure
+ *   enable - True: enable event detection; False: disable
+ *
+ *  Returned Value:
+ *    None
+ *
+ ****************************************************************************/
+
+static void k28_enable(FAR const struct automount_lower_s *lower,
+                       bool enable)
+{
+  FAR const struct k28_automount_config_s *config;
+  FAR struct k28_automount_state_s *state;
+  irqstate_t flags;
+
+  /* Recover references to our structure */
+
+  config = (FAR struct k28_automount_config_s *)lower;
+  DEBUGASSERT(config != NULL && config->state != NULL);
+
+  state = config->state;
+
+  /* Save the fake enable setting */
+
+  flags = enter_critical_section();
+  state->enable = enable;
+
+  /* Did an interrupt occur while interrupts were disabled? */
+
+  if (enable && state->pending)
+    {
+      /* Yes.. perform the fake interrupt if the interrutp is attached */
+
+      if (state->handler)
+        {
+          bool inserted = k28_cardinserted();
+          state->handler(&config->lower, state->arg, inserted);
+        }
+
+      state->pending = false;
+    }
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: k28_inserted
+ *
+ * Description:
+ *   Check if a card is inserted into the slot.
+ *
+ * Input Parameters:
+ *   lower - An instance of the auto-mounter lower half state structure
+ *
+ *  Returned Value:
+ *    True if the card is inserted; False otherwise
+ *
+ ****************************************************************************/
+
+static bool k28_inserted(FAR const struct automount_lower_s *lower)
+{
+  return k28_cardinserted();
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name:  k28_automount_initialize
+ *
+ * Description:
+ *   Configure auto-mounters for each enable and so configured SDHC
+ *
+ * Input Parameters:
+ *   None
+ *
+ *  Returned Value:
+ *    None
+ *
+ ****************************************************************************/
+
+void k28_automount_initialize(void)
+{
+  FAR void *handle;
+
+  finfo("Initializing automounter(s)\n");
+
+  /* Initialize the SDHC0 auto-mounter */
+
+  handle = automount_initialize(&g_sdhc_config.lower);
+  if (!handle)
+    {
+      ferr("ERROR: Failed to initialize auto-mounter for SDHC0\n");
+    }
+}
+
+/****************************************************************************
+ * Name:  k28_automount_event
+ *
+ * Description:
+ *   The SDHC card detection logic has detected an insertion or removal event.
+ *   It has already scheduled the MMC/SD block driver operations.
+ *   Now we need to schedule the auto-mount event which will occur with a
+ *   substantial delay to make sure that everything has settle down.
+ *
+ * Input Parameters:
+ *   slotno - Identifies the SDHC0 slot: SDHC0_SLOTNO or SDHC1_SLOTNO.
+ *      There is a terminology problem here:  Each SDHC supports two slots,
+ *      slot A and slot B. Only slot A is used.
+ *      So this is not a really a slot, but an HSCMI peripheral number.
+ *   inserted - True if the card is inserted in the slot.  False otherwise.
+ *
+ *  Returned Value:
+ *    None
+ *
+ *  Assumptions:
+ *    Interrupts are disabled.
+ *
+ ****************************************************************************/
+
+void k28_automount_event(bool inserted)
+{
+  FAR const struct k28_automount_config_s *config = &g_sdhc_config;
+  FAR struct k28_automount_state_s *state = &g_sdhc_state;
+
+  /* Is the auto-mounter interrupt attached? */
+
+  if (state->handler)
+    {
+      /* Yes.. Have we been asked to hold off interrupts? */
+
+      if (!state->enable)
+        {
+          /* Yes.. just remember that there is a pending interrupt. We will
+           * deliver the interrupt when interrupts are "re-enabled."
+           */
+
+          state->pending = true;
+        }
+      else
+        {
+          /* No.. forward the event to the handler */
+
+          state->handler(&config->lower, state->arg, inserted);
+        }
+    }
+}
+
+#endif /* HAVE_AUTOMOUNTER */
diff --git a/boards/arm/kinetis/freedom-k28f/src/k28_bringup.c b/boards/arm/kinetis/freedom-k28f/src/k28_bringup.c
index 108e6b0..4bb3671 100644
--- a/boards/arm/kinetis/freedom-k28f/src/k28_bringup.c
+++ b/boards/arm/kinetis/freedom-k28f/src/k28_bringup.c
@@ -45,6 +45,8 @@
 #include <debug.h>
 #include <errno.h>
 
+#include "freedom-k28f.h"
+
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
@@ -93,6 +95,41 @@ int k28_bringup(void)
   k28_i2cdev_initialize();
 #endif
 
+#ifdef HAVE_MMCSD
+  /* Initialize the SDHC driver */
+
+  ret = k28_sdhc_initialize();
+  if (ret < 0)
+    {
+      syslog(LOG_ERR, "ERROR: k28_sdhc_initialize() failed: %d\n", ret);
+    }
+
+#ifdef CONFIG_FRDMK28F_SDHC_MOUNT
+  else
+    {
+      /* Mount the volume on HSMCI0 */
+
+      ret = mount(CONFIG_FRDMK28F_SDHC_MOUNT_BLKDEV,
+                  CONFIG_FRDMK28F_SDHC_MOUNT_MOUNTPOINT,
+                  CONFIG_FRDMK28F_SDHC_MOUNT_FSTYPE,
+                  0, NULL);
+
+      if (ret < 0)
+        {
+          syslog(LOG_ERR, "ERROR: Failed to mount %s: %d\n",
+                 CONFIG_FRDMK28F_SDHC_MOUNT_MOUNTPOINT, errno);
+        }
+    }
+
+#endif /* CONFIG_FRDMK28F_SDHC_MOUNT */
+#endif /* HAVE_MMCSD */
+
+#ifdef HAVE_AUTOMOUNTER
+  /* Initialize the auto-mounter */
+
+  k28_automount_initialize();
+#endif
+
   UNUSED(ret);
   return OK;
 }
diff --git a/boards/arm/kinetis/freedom-k28f/src/k28_sdhc.c b/boards/arm/kinetis/freedom-k28f/src/k28_sdhc.c
new file mode 100644
index 0000000..356c912
--- /dev/null
+++ b/boards/arm/kinetis/freedom-k28f/src/k28_sdhc.c
@@ -0,0 +1,240 @@
+/****************************************************************************
+ * boards/arm/kinetis/freedom-k28f/src/k28_sdhc.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/* A micro Secure Digital (SD) card slot is available on the FRDM-K64F
+ * connected to the SD Host Controller (SDHC) signals of the MCU.
+ * This slot will accept micro format SD memory cards.
+ * The SD card detect pin (PTB5) is an open switch that shorts with VDD when
+ * card is inserted.
+ *
+ *   ------------ ------------- --------
+ *    SD Card Slot Board Signal  K64F Pin
+ *    ------------ ------------- --------
+ *    DAT0         SDHC0_D0      PTA25
+ *    DAT1         SDHC0_D1      PTA24
+ *    DAT2         SDHC0_D2      PTA29
+ *    CD/DAT3      SDHC0_D3      PTA28
+ *    CMD          SDHC0_CMD     PTA27
+ *    CLK          SDHC0_DCLK    PTA26
+ *    SWITCH       D_CARD_DETECT PTB5
+ *    ------------ ------------- --------
+ *
+ * There is no Write Protect pin available to the K28F.
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <debug.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/sdio.h>
+#include <nuttx/mmcsd.h>
+
+#include "kinetis.h"
+
+#include "freedom-k28f.h"
+
+#ifdef HAVE_MMCSD
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* This structure holds static information unique to one SDHC peripheral */
+
+struct k28_sdhc_state_s
+{
+  struct sdio_dev_s *sdhc;    /* R/W device handle */
+  bool inserted;              /* TRUE: card is inserted */
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* HSCMI device state */
+
+static struct k28_sdhc_state_s g_sdhc;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: k28_mediachange
+ ****************************************************************************/
+
+static void k28_mediachange(void)
+{
+  bool inserted;
+
+  /* Get the current value of the card detect pin.  This pin is pulled up on
+   * board.  So low means that a card is present.
+   */
+
+  inserted = kinetis_gpioread(GPIO_SD_CARDDETECT);
+  mcinfo("inserted: %s\n", inserted ? "Yes" : "No");
+
+  /* Has the pin changed state? */
+
+  if (inserted != g_sdhc.inserted)
+    {
+      mcinfo("Media change: %d->%d\n",  g_sdhc.inserted, inserted);
+
+      /* Yes.. perform the appropriate action (this might need some debounce). */
+
+      g_sdhc.inserted = inserted;
+      sdhc_mediachange(g_sdhc.sdhc, inserted);
+
+#ifdef CONFIG_FRDMK28F_SDHC_AUTOMOUNT
+      /* Let the automounter know about the insertion event */
+
+      k28_automount_event(k28_cardinserted());
+#endif
+    }
+}
+
+/****************************************************************************
+ * Name: k28_cdinterrupt
+ ****************************************************************************/
+
+static int k28_cdinterrupt(int irq, FAR void *context, FAR void *arg)
+{
+  /* All of the work is done by k28_mediachange() */
+
+  k28_mediachange();
+  return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: k28_sdhc_initialize
+ *
+ * Description:
+ *   Inititialize the SDHC SD card slot
+ *
+ ****************************************************************************/
+
+int k28_sdhc_initialize(void)
+{
+  int ret;
+
+  /* Configure GPIO pins */
+
+  kinetis_pinconfig(GPIO_SD_CARDDETECT);
+
+  /* Attached the card detect interrupt (but don't enable it yet) */
+
+  kinetis_pinirqattach(GPIO_SD_CARDDETECT, k28_cdinterrupt, NULL);
+
+  /* Configure the write protect GPIO -- None */
+
+  /* Mount the SDHC-based MMC/SD block driver */
+
+  /* First, get an instance of the SDHC interface */
+
+  mcinfo("Initializing SDHC slot %d\n", MMCSD_SLOTNO);
+
+  g_sdhc.sdhc = sdhc_initialize(MMCSD_SLOTNO);
+  if (!g_sdhc.sdhc)
+    {
+      mcerr("ERROR: Failed to initialize SDHC slot %d\n", MMCSD_SLOTNO);
+      return -ENODEV;
+    }
+
+  /* Now bind the SDHC interface to the MMC/SD driver */
+
+  mcinfo("Bind SDHC to the MMC/SD driver, minor=%d\n", MMSCD_MINOR);
+
+  ret = mmcsd_slotinitialize(MMSCD_MINOR, g_sdhc.sdhc);
+  if (ret != OK)
+    {
+      syslog(LOG_ERR, "ERROR: Failed to bind SDHC to the MMC/SD driver: %d\n",
+             ret);
+      return ret;
+    }
+
+  syslog(LOG_INFO, "Successfully bound SDHC to the MMC/SD driver\n");
+
+  /* Handle the initial card state */
+
+  k28_mediachange();
+
+  /* Enable CD interrupts to handle subsequent media changes */
+
+  kinetis_pinirqenable(GPIO_SD_CARDDETECT);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: k28_cardinserted
+ *
+ * Description:
+ *   Check if a card is inserted into the SDHC slot
+ *
+ ****************************************************************************/
+
+#ifdef HAVE_AUTOMOUNTER
+bool k28_cardinserted(void)
+{
+  bool inserted;
+
+  /* Get the current value of the card detect pin.  This pin is pulled up on
+   * board.  So low means that a card is present.
+   */
+
+  inserted = kinetis_gpioread(GPIO_SD_CARDDETECT);
+  mcinfo("inserted: %s\n", inserted ? "Yes" : "No");
+  return inserted;
+}
+#endif
+
+/****************************************************************************
+ * Name: k28_writeprotected
+ *
+ * Description:
+ *   Check if a card is inserted into the SDHC slot
+ *
+ ****************************************************************************/
+
+#ifdef HAVE_AUTOMOUNTER
+bool k28_writeprotected(void)
+{
+  /* There are no write protect pins */
+
+  return false;
+}
+#endif
+
+#endif /* HAVE_MMCSD */