/* * Copyright (C) 2006 by egnite Software GmbH. All rights reserved. * * 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 of the copyright holders nor the names of * 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. * * For additional information see http://www.ethernut.de/ */ /*! * \brief Low Level Multimedia Card Access. * * Low level MMC hardware routines, tested on the AT91SAM7X. * * These routines support SPI mode only and are required by the * basic MMC driver. * * \verbatim * * $Log$ * Revision 1.7 2010/12/15 15:14:13 ve2yag * Add support for all SAM7S and 7SE family. * * Revision 1.6 2008/08/11 06:59:13 haraldkipp * BSD types replaced by stdint types (feature request #1282721). * * Revision 1.5 2008/08/06 12:51:01 haraldkipp * Added support for Ethernut 5 (AT91SAM9XE reference design). * * Revision 1.4 2008/02/15 16:59:02 haraldkipp * Spport for AT91SAM7SE512 added. * * Revision 1.3 2008/01/31 09:16:03 haraldkipp * Failed to compile for SAM7S. Added some default settings. * * Revision 1.2 2006/09/05 12:27:55 haraldkipp * Added support for the AT91SAM9260. * * Revision 1.1 2006/07/26 11:20:08 haraldkipp * Added MMC/SD-Card support for AT91SAM7X Evaluation Kit. * * * \endverbatim */ #include #include #include #include #if 0 /* Use for local debugging. */ #define NUTDEBUG #include #endif /*! * \addtogroup xgAt91SpiMmc */ /*@{*/ #if defined(MCU_AT91SAM9G45) #ifndef MMC_CS_BIT #define MMC_CS_BIT PB3_SPI0_NPCS0_A #endif #define MMC_DATAOUT_BIT PB0_SPI0_MISO_A #define MMC_DATAIN_BIT PB1_SPI0_MOSI_A #define MMC_CLK_BIT PB2_SPI0_SPCK_A #define MMC_PIO_PDR PIOB_PDR #elif defined(MCU_AT91SAM9260) || defined(MCU_AT91SAM9XE512) #ifndef MMC_CS_BIT #define MMC_CS_BIT PA3_SPI0_NPCS0_A #endif #define MMC_DATAOUT_BIT PA0_SPI0_MISO_A #define MMC_DATAIN_BIT PA1_SPI0_MOSI_A #define MMC_CLK_BIT PA2_SPI0_SPCK_A #elif defined(MCU_AT91SAM7X) #ifndef MMC_CS_BIT #define MMC_CS_BIT SPI0_NPCS1_PA13A #endif #define MMC_DATAOUT_BIT SPI0_MISO_PA16A #define MMC_DATAIN_BIT SPI0_MOSI_PA17A #define MMC_CLK_BIT SPI0_SPCK_PA18A //#elif defined(MCU_AT91SAM7S256) || defined(MCU_AT91SAM7SE512) #elif defined(MCU_AT91SAM7S) || defined(MCU_AT91SAM7SE) #ifndef MMC_CS_BIT #define MMC_CS_BIT SPI0_NPCS0_PA11A #endif #define MMC_DATAOUT_BIT SPI0_MISO_PA12A #define MMC_DATAIN_BIT SPI0_MOSI_PA13A #define MMC_CLK_BIT SPI0_SPCK_PA14A #else /* MCU_AT91SAM7X */ #warning "MMC SPI mode not supported on this MCU" #endif #ifndef MMC_PIO_ASR #define MMC_PIO_ASR PIOA_ASR #endif #ifndef MMC_PIO_BSR #define MMC_PIO_BSR PIOA_BSR #endif #ifndef MMC_PIO_PDR #define MMC_PIO_PDR PIOA_PDR #endif #ifndef MMC_PINS_A #define MMC_PINS_A (_BV(MMC_DATAOUT_BIT) | _BV(MMC_DATAIN_BIT) | _BV(MMC_CLK_BIT)) #endif #ifndef MMC_PINS_B #define MMC_PINS_B 0 #endif #ifndef MMC_CS_PER #define MMC_CS_PER PIOA_PER #endif #ifndef MMC_CS_OER #define MMC_CS_OER PIOA_OER #endif #ifndef MMC_CS_SODR #define MMC_CS_SODR PIOA_SODR #endif #ifndef MMC_CS_CODR #define MMC_CS_CODR PIOA_CODR #endif #ifndef MMC_SPI_CR #define MMC_SPI_CR SPI0_CR #endif #ifndef MMC_SPI_MR #define MMC_SPI_MR SPI0_MR #endif #ifndef MMC_SPI_RDR #define MMC_SPI_RDR SPI0_RDR #endif #ifndef MMC_SPI_TDR #define MMC_SPI_TDR SPI0_TDR #endif #ifndef MMC_SPI_SR #define MMC_SPI_SR SPI0_SR #endif #ifndef MMC_SPI_CSR1 #define MMC_SPI_CSR1 SPI0_CSR1 #endif #ifndef MMC_SPI_ID #define MMC_SPI_ID SPI0_ID #endif /*! * \brief Private data of AT91 SPI card interface. * \note that the addressing mode indicates SD-HC or SD-normal cards * SD-HC uses byte addressing, SD-normal uses block addressing */ typedef struct _MMCDCB { int dcb_avail; /*!< Card is available. */ int dcb_changed; /*!< Card has changed. */ int dcb_addr_mode; /*!< Card addressing mode (byte/block. */ } MMCDCB; static MMCDCB mmc0_dcb; /*! * \brief Initialize the card in slot 0. * * Called by the MMC driver during card reset. The card change * detection flag will be cleared. * * \return 0 on success, -1 if no card is detected. */ static int At91SpiMmCard0Init(void) { mmc0_dcb.dcb_changed = 0; if (mmc0_dcb.dcb_avail) { return (0); } return -1; } /*! * \brief Activate or deactivate chip select on card slot 0. * * \param on The card will be selected if 1, deselected if 0. * Any other value can be used to query the status. * * \return Previous select status. 1 if selected, 0 otherwise. */ static int At91SpiMmCard0Select(int on) { int rc = (inr(PIOA_ODSR) & _BV(MMC_CS_BIT)) == 0; /* MMC select is low active. */ if (on == 1) { outr(MMC_CS_CODR, _BV(MMC_CS_BIT)); } else if (on == 0) { outr(MMC_CS_SODR, _BV(MMC_CS_BIT)); } return rc; } /*! * \brief Read the previously received byte and transmit a new one. * * \param val Byte to transmit. * * \return Last byte received. */ static uint8_t At91SpiMmCard0Io(uint8_t val) { #ifdef NUTDEBUG putchar('['); if (val != 0xFF) { printf("s%02X", val); } #endif /* Transmission is started by writing the transmit data. */ outr(MMC_SPI_TDR, val); /* Wait for receiver data register full. */ while((inr(MMC_SPI_SR) & SPI_RDRF) == 0); /* Read data. */ val = (uint8_t)inr(MMC_SPI_RDR); #ifdef NUTDEBUG if (val != 0xFF) { printf("r%02X", val); } putchar(']'); #endif return val; } /*! * \brief Check if card is available in slot 0. * * \todo Card change should verify the card identifier. Right now * any detection of removing and re-inserting a card counts * as a card change. * * \return 0 if no card is detected, 1 if a card is available or 2 if * a card change had been detected after the last mount. */ int At91SpiMmCard0Avail(void) { /* int bPresent; // Mmmm, low-level block device is calling the application???? bPresent=GpioPinGet(MMC_CD_PORT, MMC_CD_PIN); if (mmc0_dcb.dcb_avail != bPresent) { mmc0_dcb.dcb_avail= bPresent; mmc0_dcb.dcb_changed=1; // not used for anything, just in this context.... return (2); } return bPresent; */ mmc0_dcb.dcb_avail = 1; mmc0_dcb.dcb_changed = 0; return 1; } /*! * \brief Check if card in slot 0 is write protected. * * \todo Not implemented. * * \return Always 0. */ int At91SpiMmCard0WrProt(void) { return 0; } /*! * \brief set addressing mode * */ int At91SpiMmCard0SetAdrMode(int mode) { mmc0_dcb.dcb_addr_mode = mode; return(0); } /*! * \brief get addressing mode * */ int At91SpiMmCard0GetAdrMode(void) { return(mmc0_dcb.dcb_addr_mode); } /*! * \brief Initialize MMC hardware interface. * * This function is automatically executed during during device * registration via NutRegisterDevice(). * * \param dev Identifies the device to initialize. */ static int At91SpiMmcIfcInit(NUTDEVICE * dev) { /* Disable PIO lines used for SPI. */ outr(MMC_PIO_PDR, MMC_PINS_A | MMC_PINS_B); /* Enable peripherals. */ outr(MMC_PIO_ASR, MMC_PINS_A); outr(MMC_PIO_BSR, MMC_PINS_B); /* MMC chip select is manually controlled. */ outr(MMC_CS_PER, _BV(MMC_CS_BIT)); outr(MMC_CS_SODR, _BV(MMC_CS_BIT)); outr(MMC_CS_OER, _BV(MMC_CS_BIT)); /* Enable SPI clock. */ outr(PMC_PCER, _BV(MMC_SPI_ID)); /* SPI enable and reset. */ outr(MMC_SPI_CR, SPI_SPIEN | SPI_SWRST); /* Apply a reset twice! See errata at page 652 in the datasheet: 41.4.8.7 SPI: Software Reset must be Written Twice If a software reset (SWRST in the SPI Control Register) is performed, the SPI may not work properly (the clock is enabled before the chip select.) Problem Fix/Workaround The SPI Control Register field, SWRST (Software Reset) needs to be written twice to be correctly set. */ outr(MMC_SPI_CR, SPI_SPIEN | SPI_SWRST); outr(MMC_SPI_CR, SPI_SPIEN); /* Set SPI to master mode, fixed peripheral at CS1, fault detection disabled. */ outr(MMC_SPI_MR, (1 << SPI_PCS_LSB) | SPI_MODFDIS | SPI_MSTR); /* Data changes during clock low and will be sampled on rising edges. */ outr(MMC_SPI_CSR1, (3 << SPI_SCBR_LSB) | SPI_CPOL); return MmCardDevInit(dev); } static MMCIFC mmc0_ifc = { At91SpiMmCard0Init, /*!< mmcifc_in */ At91SpiMmCard0Io, /*!< mmcifc_io */ At91SpiMmCard0Select, /*!< mmcifc_cs */ At91SpiMmCard0Avail, /*!< mmcifc_cd */ At91SpiMmCard0WrProt, /*!< mmcifc_wp */ At91SpiMmCard0SetAdrMode, /*!< mmcifc_sm */ At91SpiMmCard0GetAdrMode /*!< mmcifc_gm */ }; /*! * \brief Multimedia card device information structure. * * A pointer to this structure must be passed to NutRegisterDevice() * to bind this driver to the Nut/OS kernel. An application may then * call * /verbatim * _open("MMC0:", _O_RDWR | _O_BINARY); * /endverbatim * to mount the first active primary partition with any previously * registered file system driver (typically devPhat0). */ NUTDEVICE devAt91SpiMmc0 = { 0, /*!< Pointer to next device, dev_next. */ {'M', 'M', 'C', '0', 0, 0, 0, 0, 0} , /*!< Unique device name, dev_name. */ 0, /*!< Type of device, dev_type. Obsolete. */ 0, /*!< Base address, dev_base. Unused. */ 0, /*!< First interrupt number, dev_irq. Unused. */ &mmc0_ifc, /*!< Interface control block, dev_icb. */ &mmc0_dcb, /*!< Driver control block used by the low level part, dev_dcb. */ At91SpiMmcIfcInit, /*!< Driver initialization routine, dev_init. */ MmCardIOCtl, /*!< Driver specific control function, dev_ioctl. */ MmCardBlockRead, /*!< Read data from a file, dev_read. */ MmCardBlockWrite, /*!< Write data to a file, dev_write. */ #ifdef __HARVARD_ARCH__ MmCardBlockWrite_P, /*!< Write data from program space to a file, dev_write_P. */ #endif MmCardMount, /*!< Mount a file system, dev_open. */ MmCardUnmount, /*!< Unmount a file system, dev_close. */ 0, /*!< Return file size, dev_size. */ 0, /*!< Select function, optional, not yet implemented */ }; /*@}*/