From db2f204cc4a513d56b4cedc7a3c5490a9c440b69 Mon Sep 17 00:00:00 2001 From: wanggang26 Date: Thu, 21 Sep 2023 09:59:09 +0800 Subject: [PATCH] mmcsd: support dump cid and csd with mmc-utils Signed-off-by: wanggang26 --- drivers/mmcsd/CMakeLists.txt | 4 + drivers/mmcsd/Kconfig | 7 + drivers/mmcsd/Make.defs | 4 + drivers/mmcsd/mmcsd.h | 42 +++ drivers/mmcsd/mmcsd_procfs.c | 543 +++++++++++++++++++++++++++++++++++ drivers/mmcsd/mmcsd_sdio.c | 49 +--- 6 files changed, 608 insertions(+), 41 deletions(-) create mode 100644 drivers/mmcsd/mmcsd_procfs.c diff --git a/drivers/mmcsd/CMakeLists.txt b/drivers/mmcsd/CMakeLists.txt index 4321e1fd57d..d8298f44969 100644 --- a/drivers/mmcsd/CMakeLists.txt +++ b/drivers/mmcsd/CMakeLists.txt @@ -31,5 +31,9 @@ if(CONFIG_MMCSD) list(APPEND SRCS mmcsd_spi.c mmcsd_debug.c) endif() + if(CONFIG_MMCSD_PROCFS) + list(APPEND SRCS mmcsd_procfs.c) + endif() + target_sources(drivers PRIVATE ${SRCS}) endif() diff --git a/drivers/mmcsd/Kconfig b/drivers/mmcsd/Kconfig index 790f67caf35..8a663eb1136 100644 --- a/drivers/mmcsd/Kconfig +++ b/drivers/mmcsd/Kconfig @@ -40,6 +40,13 @@ config MMCSD_NSLOTS Number of MMC/SD slots supported by the driver. Default is one. +config MMCSD_PROCFS + bool "MMCSD proc fs support" + default n + depends on FS_PROCFS_REGISTER + ---help--- + Enable procfs for mmcsd. + config MMCSD_READONLY bool "Disable MMC/SD write access" default n diff --git a/drivers/mmcsd/Make.defs b/drivers/mmcsd/Make.defs index 54a5c504925..c80dd7ecafc 100644 --- a/drivers/mmcsd/Make.defs +++ b/drivers/mmcsd/Make.defs @@ -30,6 +30,10 @@ ifeq ($(CONFIG_MMCSD_SPI),y) CSRCS += mmcsd_spi.c mmcsd_debug.c endif +ifeq ($(CONFIG_MMCSD_PROCFS),y) +CSRCS += mmcsd_procfs.c +endif + # Include MMC/SD driver build support DEPPATH += --dep-path mmcsd diff --git a/drivers/mmcsd/mmcsd.h b/drivers/mmcsd/mmcsd.h index bbfb006da2a..f9083711de5 100644 --- a/drivers/mmcsd/mmcsd.h +++ b/drivers/mmcsd/mmcsd.h @@ -26,6 +26,7 @@ ****************************************************************************/ #include +#include #include #include @@ -59,6 +60,43 @@ * Public Types ****************************************************************************/ +/* This structure is contains the unique state of the MMC/SD block driver */ + +struct mmcsd_state_s +{ + FAR struct sdio_dev_s *dev; /* The SDIO device bound to this instance */ + uint8_t crefs; /* Open references on the driver */ + mutex_t lock; /* Assures mutually exclusive access to the slot */ + + /* Status flags */ + + uint8_t probed:1; /* true: mmcsd_probe() discovered a card */ + uint8_t widebus:1; /* true: Wide 4-bit bus selected */ + uint8_t mediachanged:1; /* true: Media changed since last check */ + uint8_t wrbusy:1; /* true: Last transfer was a write, card may be busy */ + uint8_t wrprotect:1; /* true: Card is write protected (from CSD) */ + uint8_t locked:1; /* true: Media is locked (from R1) */ + uint8_t dsrimp:1; /* true: card supports CMD4/DSR setting (from CSD) */ +#ifdef CONFIG_SDIO_DMA + uint8_t dma:1; /* true: hardware supports DMA */ +#endif + + uint8_t mode:2; /* (See MMCSDMODE_* definitions) */ + uint8_t type:4; /* Card type (See MMCSD_CARDTYPE_* definitions) */ + uint8_t buswidth:4; /* Bus widths supported (SD only) */ + sdio_capset_t caps; /* SDIO driver capabilities/limitations */ + uint32_t cid[4]; /* CID register */ + uint32_t csd[4]; /* CSD register */ + uint16_t selblocklen; /* The currently selected block length */ + uint16_t rca; /* Relative Card Address (RCS) register */ + + /* Memory card geometry (extracted from the CSD) */ + + uint8_t blockshift; /* Log2 of blocksize */ + uint16_t blocksize; /* Read block length (== block size) */ + uint32_t nblocks; /* Number of blocks */ +}; + /**************************************************************************** * Public Functions Definitions ****************************************************************************/ @@ -72,6 +110,10 @@ extern "C" #define EXTERN extern #endif +#ifdef CONFIG_MMCSD_PROCFS +void mmcsd_initialize_procfs(void); +#endif + #ifdef CONFIG_MMCSD_DUMPALL # define mmcsd_dumpbuffer(m,b,l) finfodumpbuffer(m,b,l) #else diff --git a/drivers/mmcsd/mmcsd_procfs.c b/drivers/mmcsd/mmcsd_procfs.c new file mode 100644 index 00000000000..5260d49d424 --- /dev/null +++ b/drivers/mmcsd/mmcsd_procfs.c @@ -0,0 +1,543 @@ +/**************************************************************************** + * drivers/mmcsd/mmcsd_procfs.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 +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "mmcsd.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Determines the size of an intermediate buffer that must be large enough + * to handle the longest line generated by this logic (plus a couple of + * bytes). + */ + +#define MMCSD_LINELEN 512 + +typedef CODE ssize_t (*mmcsd_read_t)(FAR struct file *filep, + FAR char *buffer, size_t buflen, + FAR struct mmcsd_state_s *priv); + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* File system methods */ + +static int mmcsd_open(FAR struct file *filep, FAR const char *relpath, + int oflags, mode_t mode); +static int mmcsd_close(FAR struct file *filep); +static ssize_t mmcsd_read_cid(FAR struct file *filep, FAR char *buffer, + size_t buflen, FAR struct mmcsd_state_s *priv); +static ssize_t mmcsd_read_csd(FAR struct file *filep, FAR char *buffer, + size_t buflen, FAR struct mmcsd_state_s *priv); +static ssize_t mmcsd_read_type(FAR struct file *filep, FAR char *buffer, + size_t buflen, FAR struct mmcsd_state_s *priv); +static ssize_t mmcsd_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); +static int mmcsd_dup(FAR const struct file *oldp, + FAR struct file *newp); + +static int mmcsd_opendir(FAR const char *relpath, + FAR struct fs_dirent_s **dir); +static int mmcsd_closedir(FAR struct fs_dirent_s *dir); +static int mmcsd_readdir(FAR struct fs_dirent_s *dir, + FAR struct dirent *entry); +static int mmcsd_rewinddir(FAR struct fs_dirent_s *dir); + +static int mmcsd_stat(FAR const char *relpath, FAR struct stat *buf); + +static int mmcsd_get_file_index(FAR const char *relpath); + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure describes one open "file" */ + +struct mmcsd_file_s +{ + struct procfs_file_s base; /* Base open file structure */ + char line[MMCSD_LINELEN]; /* Pre-allocated buffer for formatted lines */ + int index; /* Device node index */ + mmcsd_read_t read; /* Read function */ +}; + +struct mmcsd_file_ops_s +{ + FAR const char *name; + mmcsd_read_t read; +}; + +static const struct procfs_operations g_mmcsd_operations = +{ + mmcsd_open, /* open */ + mmcsd_close, /* close */ + mmcsd_read, /* read */ + NULL, /* write */ + + mmcsd_dup, /* dup */ + + mmcsd_opendir, /* opendir */ + mmcsd_closedir, /* closedir */ + mmcsd_readdir, /* readdir */ + mmcsd_rewinddir, /* rewinddir */ + + mmcsd_stat /* stat */ +}; + +static const struct procfs_entry_s g_mmcsd_procfs1 = +{ + "mmcsd", &g_mmcsd_operations, PROCFS_DIR_TYPE +}; +static const struct procfs_entry_s g_mmcsd_procfs2 = +{ + "mmcsd/**", &g_mmcsd_operations, PROCFS_UNKOWN_TYPE +}; + +static const struct mmcsd_file_ops_s g_mmcsd_files[] = +{ + {"cid", mmcsd_read_cid}, + {"csd", mmcsd_read_csd}, + {"type", mmcsd_read_type}, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mmcsd_get_file_index + ****************************************************************************/ + +static int mmcsd_get_file_index(FAR const char *relpath) +{ + int i; + + for (i = 0; i < nitems(g_mmcsd_files); i++) + { + if (strncmp(relpath, g_mmcsd_files[i].name, + strlen(g_mmcsd_files[i].name)) == 0) + { + return i; + } + } + + return -1; +} + +/**************************************************************************** + * Name: mmcsd_open + ****************************************************************************/ + +static int mmcsd_open(FAR struct file *filep, FAR const char *relpath, + int oflags, mode_t mode) +{ + FAR struct mmcsd_file_s *mmcsdfile; + int ret; + + /* This PROCFS file is read-only. Any attempt to open with write access + * is not permitted. + */ + + if ((oflags & O_WRONLY) != 0 || (oflags & O_RDONLY) == 0) + { + finfo("ERROR: Only O_RDONLY supported\n"); + return -EACCES; + } + + relpath += strlen("mmcsd/"); + ret = mmcsd_get_file_index(relpath); + if (ret < 0) + { + return -ENOENT; + } + + /* Allocate a container to hold the file attributes */ + + mmcsdfile = kmm_zalloc(sizeof(struct mmcsd_file_s)); + if (mmcsdfile == NULL) + { + finfo("ERROR: Failed to allocate file attributes\n"); + return -ENOMEM; + } + + mmcsdfile->read = g_mmcsd_files[ret].read; + mmcsdfile->index = atoi(relpath + strlen(g_mmcsd_files[ret].name)); + + /* Save the attributes as the open-specific state in filep->f_priv */ + + filep->f_priv = mmcsdfile; + return OK; +} + +/**************************************************************************** + * Name: mmcsd_close + ****************************************************************************/ + +static int mmcsd_close(FAR struct file *filep) +{ + DEBUGASSERT(filep->f_priv); + + /* Release the file attributes structure */ + + kmm_free(filep->f_priv); + filep->f_priv = NULL; + return OK; +} + +/**************************************************************************** + * Name: mmcsd_read_cid + ****************************************************************************/ + +static ssize_t mmcsd_read_cid(FAR struct file *filep, FAR char *buffer, + size_t buflen, FAR struct mmcsd_state_s *priv) +{ + FAR struct mmcsd_file_s *mmcsdfile; + size_t totalsize; + size_t linesize; + off_t offset; + + /* Recover our private data from the struct file instance */ + + mmcsdfile = filep->f_priv; + + /* Save the file offset and the user buffer information */ + + offset = filep->f_pos; + + linesize = snprintf(mmcsdfile->line, MMCSD_LINELEN, "%08lx%08lx%08lx%08lx", + priv->cid[0], priv->cid[1], + priv->cid[2], priv->cid[3]); + totalsize = procfs_memcpy(mmcsdfile->line, linesize, buffer, + buflen, &offset); + filep->f_pos += totalsize; + return totalsize; +} + +/**************************************************************************** + * Name: mmcsd_read_csd + ****************************************************************************/ + +static ssize_t mmcsd_read_csd(FAR struct file *filep, FAR char *buffer, + size_t buflen, FAR struct mmcsd_state_s *priv) +{ + FAR struct mmcsd_file_s *mmcsdfile; + size_t totalsize; + size_t linesize; + off_t offset; + + /* Recover our private data from the struct file instance */ + + mmcsdfile = filep->f_priv; + + /* Save the file offset and the user buffer information */ + + offset = filep->f_pos; + + linesize = snprintf(mmcsdfile->line, MMCSD_LINELEN, "%08lx%08lx%08lx%08lx", + priv->csd[0], priv->csd[1], priv->csd[2], priv->csd[3]); + totalsize = procfs_memcpy(mmcsdfile->line, linesize, buffer, + buflen, &offset); + filep->f_pos += totalsize; + return totalsize; +} + +/**************************************************************************** + * Name: mmcsd_read_type + ****************************************************************************/ + +static ssize_t mmcsd_read_type(FAR struct file *filep, FAR char *buffer, + size_t buflen, FAR struct mmcsd_state_s *priv) +{ + FAR struct mmcsd_file_s *mmcsdfile; + size_t totalsize; + size_t linesize; + off_t offset; + + mmcsdfile = filep->f_priv; + + /* Save the file offset and the user buffer information */ + + offset = filep->f_pos; + + switch (priv->type) + { + case MMCSD_CARDTYPE_SDV1: + case MMCSD_CARDTYPE_SDV2: + case MMCSD_CARDTYPE_SDV2 | MMCSD_CARDTYPE_BLOCK: + linesize = snprintf(mmcsdfile->line, MMCSD_LINELEN, "SD"); + break; + case MMCSD_CARDTYPE_MMC: + case MMCSD_CARDTYPE_MMC | MMCSD_CARDTYPE_BLOCK: + linesize = snprintf(mmcsdfile->line, MMCSD_LINELEN, "MMC"); + break; + + /* Unknown card type */ + + case MMCSD_CARDTYPE_UNKNOWN: + default: + ferr("ERROR: Invalid media type (%d)\n", priv->type); + return -EINVAL; + } + + totalsize = procfs_memcpy(mmcsdfile->line, linesize, buffer, + buflen, &offset); + filep->f_pos += totalsize; + return totalsize; +} + +/**************************************************************************** + * Name: mmcsd_read + ****************************************************************************/ + +static ssize_t mmcsd_read(FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ + FAR struct mmcsd_file_s *mmcsdfile = filep->f_priv; + FAR struct inode *inode; + char path[32]; + ssize_t ret; + + snprintf(path, sizeof(path), "/dev/mmcsd%d", mmcsdfile->index); + ret = open_blockdriver(path, MS_RDONLY, &inode); + if (ret < 0) + { + return ret; + } + + DEBUGASSERT(filep->f_priv); + ret = mmcsdfile->read(filep, buffer, buflen, inode->i_private); + close_blockdriver(inode); + + return ret; +} + +/**************************************************************************** + * Name: mmcsd_dup + * + * Description: + * Duplicate open file data in the new file structure. + * + ****************************************************************************/ + +static int mmcsd_dup(FAR const struct file *oldp, FAR struct file *newp) +{ + FAR struct mmcsd_file_s *oldattr; + FAR struct mmcsd_file_s *newattr; + + /* Recover our private data from the old struct file instance */ + + oldattr = oldp->f_priv; + DEBUGASSERT(oldattr); + + /* Allocate a new container to hold the task and attribute selection */ + + newattr = kmm_malloc(sizeof(struct mmcsd_file_s)); + if (newattr == NULL) + { + ferr("ERROR: Failed to allocate file attributes\n"); + return -ENOMEM; + } + + /* The copy the file attributes from the old attributes to the new */ + + memcpy(newattr, oldattr, sizeof(struct mmcsd_file_s)); + + /* Save the new attributes in the new file structure */ + + newp->f_priv = newattr; + return OK; +} + +/**************************************************************************** + * Name: mmcsd_opendir + * + * Description: + * Open a directory for read access + * + ****************************************************************************/ + +static int mmcsd_opendir(FAR const char *relpath, + FAR struct fs_dirent_s **dir) +{ + FAR struct procfs_dir_priv_s *level1; + + DEBUGASSERT(relpath); + + /* Assume that path refers to the 1st level subdirectory. Allocate the + * level1 the dirent structure before checking. + */ + + level1 = kmm_zalloc(sizeof(struct procfs_dir_priv_s)); + if (level1 == NULL) + { + ferr("ERROR: Failed to allocate the level1 directory structure\n"); + return -ENOMEM; + } + + /* Initialize base structure components */ + + level1->level = 1; + level1->nentries = nitems(g_mmcsd_files); + + *dir = (FAR struct fs_dirent_s *)level1; + return OK; +} + +/**************************************************************************** + * Name: mmcsd_closedir + * + * Description: Close the directory listing + * + ****************************************************************************/ + +static int mmcsd_closedir(FAR struct fs_dirent_s *dir) +{ + DEBUGASSERT(dir); + kmm_free(dir); + return OK; +} + +/**************************************************************************** + * Name: mmcsd_readdir + * + * Description: Read the next directory entry + * + ****************************************************************************/ + +static int mmcsd_readdir(FAR struct fs_dirent_s *dir, + FAR struct dirent *entry) +{ + FAR struct procfs_dir_priv_s *level1; + int index; + int fpos; + + DEBUGASSERT(dir); + level1 = (FAR struct procfs_dir_priv_s *)dir; + + index = level1->index; + if (index >= level1->nentries) + { + /* We signal the end of the directory by returning the special + * error -ENOENT + */ + + finfo("Entry %d: End of directory\n", index); + return -ENOENT; + } + + fpos = index % nitems(g_mmcsd_files); + index = index / nitems(g_mmcsd_files); + + entry->d_type = DTYPE_FILE; + snprintf(entry->d_name, NAME_MAX + 1, "%s%d", + g_mmcsd_files[fpos].name, index); + + level1->index++; + return OK; +} + +/**************************************************************************** + * Name: mmcsd_rewindir + * + * Description: Reset directory read to the first entry + * + ****************************************************************************/ + +static int mmcsd_rewinddir(FAR struct fs_dirent_s *dir) +{ + FAR struct procfs_dir_priv_s *level1; + + DEBUGASSERT(dir); + level1 = (FAR struct procfs_dir_priv_s *)dir; + + level1->index = 0; + return OK; +} + +/**************************************************************************** + * Name: mmcsd_stat + * + * Description: Return information about a file or directory + * + ****************************************************************************/ + +static int mmcsd_stat(FAR const char *relpath, FAR struct stat *buf) +{ + memset(buf, 0, sizeof(struct stat)); + + if (strcmp(relpath, "mmcsd") == 0 || strcmp(relpath, "mmcsd/") == 0) + { + buf->st_mode = S_IFDIR | S_IROTH | S_IRGRP | S_IRUSR; + } + else + { + relpath += strlen("mmcsd/"); + if (mmcsd_get_file_index(relpath) < 0) + { + return -ENOENT; + } + + buf->st_mode = S_IFREG | S_IROTH | S_IRGRP | S_IRUSR; + } + + return OK; +} + +/**************************************************************************** + * Name: mmcsd_initialize_procfs + * + * Description: + * Initialize mmcsd procfs + * + ****************************************************************************/ + +void mmcsd_initialize_procfs(void) +{ + int ret; + ret = procfs_register(&g_mmcsd_procfs1); + if (ret == OK) + { + ret = procfs_register(&g_mmcsd_procfs2); + } + + DEBUGASSERT(ret == OK); +} diff --git a/drivers/mmcsd/mmcsd_sdio.c b/drivers/mmcsd/mmcsd_sdio.c index 24a36b4f4ac..7abad1dfa48 100644 --- a/drivers/mmcsd/mmcsd_sdio.c +++ b/drivers/mmcsd/mmcsd_sdio.c @@ -102,41 +102,6 @@ * Private Types ****************************************************************************/ -/* This structure is contains the unique state of the MMC/SD block driver */ - -struct mmcsd_state_s -{ - FAR struct sdio_dev_s *dev; /* The SDIO device bound to this instance */ - uint8_t crefs; /* Open references on the driver */ - mutex_t lock; /* Assures mutually exclusive access to the slot */ - - /* Status flags */ - - uint8_t probed:1; /* true: mmcsd_probe() discovered a card */ - uint8_t widebus:1; /* true: Wide 4-bit bus selected */ - uint8_t mediachanged:1; /* true: Media changed since last check */ - uint8_t wrbusy:1; /* true: Last transfer was a write, card may be busy */ - uint8_t wrprotect:1; /* true: Card is write protected (from CSD) */ - uint8_t locked:1; /* true: Media is locked (from R1) */ - uint8_t dsrimp:1; /* true: card supports CMD4/DSR setting (from CSD) */ -#ifdef CONFIG_SDIO_DMA - uint8_t dma:1; /* true: hardware supports DMA */ -#endif - - uint8_t mode:2; /* (See MMCSDMODE_* definitions) */ - uint8_t type:4; /* Card type (See MMCSD_CARDTYPE_* definitions) */ - uint8_t buswidth:4; /* Bus widths supported (SD only) */ - sdio_capset_t caps; /* SDIO driver capabilities/limitations */ - uint16_t selblocklen; /* The currently selected block length */ - uint16_t rca; /* Relative Card Address (RCS) register */ - - /* Memory card geometry (extracted from the CSD) */ - - uint8_t blockshift; /* Log2 of blocksize */ - uint16_t blocksize; /* Read block length (== block size) */ - uint32_t nblocks; /* Number of blocks */ -}; - /**************************************************************************** * Private Function Prototypes ****************************************************************************/ @@ -2639,8 +2604,6 @@ static int mmcsd_widebus(FAR struct mmcsd_state_s *priv) #ifdef CONFIG_MMCSD_MMCSUPPORT static int mmcsd_mmcinitialize(FAR struct mmcsd_state_s *priv) { - uint32_t cid[4]; - uint32_t csd[4]; int ret; /* At this point, slow, ID mode clocking has been supplied to the card @@ -2659,7 +2622,7 @@ static int mmcsd_mmcinitialize(FAR struct mmcsd_state_s *priv) finfo("Initialising MMC card.\n"); mmcsd_sendcmdpoll(priv, MMCSD_CMD2, 0); - ret = SDIO_RECVR2(priv->dev, MMCSD_CMD2, cid); + ret = SDIO_RECVR2(priv->dev, MMCSD_CMD2, priv->cid); if (ret != OK) { ferr("ERROR: SDIO_RECVR2 for MMC CID failed: %d\n", ret); @@ -2711,7 +2674,7 @@ static int mmcsd_mmcinitialize(FAR struct mmcsd_state_s *priv) */ mmcsd_sendcmdpoll(priv, MMCSD_CMD9, (uint32_t)priv->rca << 16); - ret = SDIO_RECVR2(priv->dev, MMCSD_CMD9, csd); + ret = SDIO_RECVR2(priv->dev, MMCSD_CMD9, priv->csd); if (ret != OK) { ferr("ERROR: Could not get SD CSD register: %d\n", ret); @@ -2723,7 +2686,7 @@ static int mmcsd_mmcinitialize(FAR struct mmcsd_state_s *priv) * ext_csd commands. */ - mmcsd_decode_csd(priv, csd); + mmcsd_decode_csd(priv, priv->csd); /* Set the Driver Stage Register (DSR) if (1) a CONFIG_MMCSD_DSR has been * provided and (2) the card supports a DSR register. If no DSR value @@ -2781,7 +2744,7 @@ static int mmcsd_mmcinitialize(FAR struct mmcsd_state_s *priv) } } - mmcsd_decode_csd(priv, csd); + mmcsd_decode_csd(priv, priv->csd); /* It's up to the driver to act on the widebus request. mmcsd_widebus() * enables the CLOCK_MMC_TRANSFER, so call it here always. @@ -4201,6 +4164,10 @@ int mmcsd_slotinitialize(int minor, FAR struct sdio_dev_s *dev) goto errout_with_hwinit; } +#ifdef CONFIG_MMCSD_PROCFS + mmcsd_initialize_procfs(); +#endif + return OK; errout_with_hwinit: