mci: Add MCI over SPI support
This patch adds MMC over SPI support to mci-core.c and mci_spi.c driver. This driver is useful when SOC doesn't have built-in MCI component. Tested with nios, 2Go SD-CARD and FAT file system. Signed-off-by: Franck Jullien <franck.jullien@gmail.com> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
This commit is contained in:
parent
52ef4af579
commit
2aad209b78
|
@ -80,4 +80,21 @@ config MCI_ATMEL
|
|||
Enable this entry to add support to read and write SD cards on a
|
||||
Atmel AT91.
|
||||
|
||||
config MCI_SPI
|
||||
bool "MMC/SD over SPI"
|
||||
help
|
||||
Some systems access MMC/SD/SDIO cards using a SPI controller
|
||||
instead of using a "native" MMC/SD/SDIO controller. This has a
|
||||
disadvantage of being relatively high overhead, but a compensating
|
||||
advantage of working on many systems without dedicated MMC/SD/SDIO
|
||||
controllers.
|
||||
|
||||
config MMC_SPI_CRC_ON
|
||||
bool "Enable CRC protection for transfers"
|
||||
select CRC7
|
||||
select CRC16
|
||||
depends on MCI_SPI
|
||||
help
|
||||
Enable CRC protection for transfers
|
||||
|
||||
endif
|
||||
|
|
|
@ -5,3 +5,4 @@ obj-$(CONFIG_MCI_IMX) += imx.o
|
|||
obj-$(CONFIG_MCI_IMX_ESDHC) += imx-esdhc.o
|
||||
obj-$(CONFIG_MCI_OMAP_HSMMC) += omap_hsmmc.o
|
||||
obj-$(CONFIG_MCI_ATMEL) += atmel_mci.o
|
||||
obj-$(CONFIG_MCI_SPI) += mci_spi.o
|
||||
|
|
|
@ -239,6 +239,7 @@ static int sd_send_op_cond(struct device_d *mci_dev)
|
|||
int timeout = 1000;
|
||||
int err;
|
||||
unsigned voltages;
|
||||
unsigned busy;
|
||||
|
||||
/*
|
||||
* Most cards do not answer if some reserved bits
|
||||
|
@ -258,7 +259,7 @@ static int sd_send_op_cond(struct device_d *mci_dev)
|
|||
}
|
||||
|
||||
mci_setup_cmd(&cmd, SD_CMD_APP_SEND_OP_COND,
|
||||
voltages | (mci->version == SD_VERSION_2 ? OCR_HCS : 0),
|
||||
mmc_host_is_spi(host) ? 0 : (voltages | (mci->version == SD_VERSION_2 ? OCR_HCS : 0)),
|
||||
MMC_RSP_R3);
|
||||
err = mci_send_cmd(mci_dev, &cmd, NULL);
|
||||
if (err) {
|
||||
|
@ -266,7 +267,13 @@ static int sd_send_op_cond(struct device_d *mci_dev)
|
|||
return err;
|
||||
}
|
||||
udelay(1000);
|
||||
} while ((!(cmd.response[0] & OCR_BUSY)) && timeout--);
|
||||
|
||||
if (mmc_host_is_spi(host))
|
||||
busy = cmd.response[0] & R1_SPI_IDLE;
|
||||
else
|
||||
busy = !(cmd.response[0] & OCR_BUSY);
|
||||
|
||||
} while (busy && timeout--);
|
||||
|
||||
if (timeout <= 0) {
|
||||
pr_debug("SD operation condition set timed out\n");
|
||||
|
@ -276,6 +283,13 @@ static int sd_send_op_cond(struct device_d *mci_dev)
|
|||
if (mci->version != SD_VERSION_2)
|
||||
mci->version = SD_VERSION_1_0;
|
||||
|
||||
if (mmc_host_is_spi(host)) { /* read OCR for spi */
|
||||
mci_setup_cmd(&cmd, MMC_CMD_SPI_READ_OCR, 0, MMC_RSP_R3);
|
||||
err = mci_send_cmd(mci_dev, &cmd, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
mci->ocr = cmd.response[0];
|
||||
|
||||
mci->high_capacity = ((mci->ocr & OCR_HCS) == OCR_HCS);
|
||||
|
@ -474,11 +488,17 @@ static int sd_change_freq(struct device_d *mci_dev)
|
|||
struct mci *mci = GET_MCI_DATA(mci_dev);
|
||||
struct mci_cmd cmd;
|
||||
struct mci_data data;
|
||||
#ifdef CONFIG_MCI_SPI
|
||||
struct mci_host *host = GET_MCI_PDATA(mci_dev);
|
||||
#endif
|
||||
uint32_t *switch_status = sector_buf;
|
||||
uint32_t *scr = sector_buf;
|
||||
int timeout;
|
||||
int err;
|
||||
|
||||
if (mmc_host_is_spi(host))
|
||||
return 0;
|
||||
|
||||
pr_debug("Changing transfer frequency\n");
|
||||
mci->card_caps = 0;
|
||||
|
||||
|
@ -769,10 +789,23 @@ static int mci_startup(struct device_d *mci_dev)
|
|||
struct mci_cmd cmd;
|
||||
int err;
|
||||
|
||||
#ifdef CONFIG_MMC_SPI_CRC_ON
|
||||
if (mmc_host_is_spi(host)) { /* enable CRC check for spi */
|
||||
|
||||
mci_setup_cmd(&cmd, MMC_CMD_SPI_CRC_ON_OFF, 1, MMC_RSP_R1);
|
||||
err = mci_send_cmd(mci_dev, &cmd, NULL);
|
||||
|
||||
if (err) {
|
||||
pr_debug("Can't enable CRC check : %d\n", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
pr_debug("Put the Card in Identify Mode\n");
|
||||
|
||||
/* Put the Card in Identify Mode */
|
||||
mci_setup_cmd(&cmd, MMC_CMD_ALL_SEND_CID, 0, MMC_RSP_R2);
|
||||
mci_setup_cmd(&cmd, mmc_host_is_spi(host) ? MMC_CMD_SEND_CID : MMC_CMD_ALL_SEND_CID, 0, MMC_RSP_R2);
|
||||
err = mci_send_cmd(mci_dev, &cmd, NULL);
|
||||
if (err) {
|
||||
pr_debug("Can't bring card into identify mode: %d\n", err);
|
||||
|
@ -789,12 +822,14 @@ static int mci_startup(struct device_d *mci_dev)
|
|||
* For SD cards, get the Relatvie Address.
|
||||
* This also puts the cards into Standby State
|
||||
*/
|
||||
pr_debug("Get/Set relative address\n");
|
||||
mci_setup_cmd(&cmd, SD_CMD_SEND_RELATIVE_ADDR, mci->rca << 16, MMC_RSP_R6);
|
||||
err = mci_send_cmd(mci_dev, &cmd, NULL);
|
||||
if (err) {
|
||||
pr_debug("Get/Set relative address failed: %d\n", err);
|
||||
return err;
|
||||
if (!mmc_host_is_spi(host)) { /* cmd not supported in spi */
|
||||
pr_debug("Get/Set relative address\n");
|
||||
mci_setup_cmd(&cmd, SD_CMD_SEND_RELATIVE_ADDR, mci->rca << 16, MMC_RSP_R6);
|
||||
err = mci_send_cmd(mci_dev, &cmd, NULL);
|
||||
if (err) {
|
||||
pr_debug("Get/Set relative address failed: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_SD(mci))
|
||||
|
@ -835,13 +870,15 @@ static int mci_startup(struct device_d *mci_dev)
|
|||
pr_debug("Read block length: %u, Write block length: %u\n",
|
||||
mci->read_bl_len, mci->write_bl_len);
|
||||
|
||||
pr_debug("Select the card, and put it into Transfer Mode\n");
|
||||
/* Select the card, and put it into Transfer Mode */
|
||||
mci_setup_cmd(&cmd, MMC_CMD_SELECT_CARD, mci->rca << 16, MMC_RSP_R1b);
|
||||
err = mci_send_cmd(mci_dev, &cmd, NULL);
|
||||
if (err) {
|
||||
pr_debug("Putting in transfer mode failed: %d\n", err);
|
||||
return err;
|
||||
if (!mmc_host_is_spi(host)) { /* cmd not supported in spi */
|
||||
pr_debug("Select the card, and put it into Transfer Mode\n");
|
||||
/* Select the card, and put it into Transfer Mode */
|
||||
mci_setup_cmd(&cmd, MMC_CMD_SELECT_CARD, mci->rca << 16, MMC_RSP_R1b);
|
||||
err = mci_send_cmd(mci_dev, &cmd, NULL);
|
||||
if (err) {
|
||||
pr_debug("Putting in transfer mode failed: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_SD(mci))
|
||||
|
|
|
@ -0,0 +1,435 @@
|
|||
/*
|
||||
* (C) Copyright 2011 - Franck JULLIEN <elec4fun@gmail.com>
|
||||
*
|
||||
* This code was inspired from u-boot mmc_spi.c:
|
||||
* Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
|
||||
*
|
||||
* and linux mmc_spi.c:
|
||||
* (C) Copyright 2005, Intec Automation,
|
||||
* Mike Lavender (mike@steroidmicros)
|
||||
* (C) Copyright 2006-2007, David Brownell
|
||||
* (C) Copyright 2007, Axis Communications,
|
||||
* Hans-Peter Nilsson (hp@axis.com)
|
||||
* (C) Copyright 2007, ATRON electronic GmbH,
|
||||
* Jan Nikitenko <jan.nikitenko@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <init.h>
|
||||
#include <errno.h>
|
||||
#include <clock.h>
|
||||
#include <asm/io.h>
|
||||
#include <driver.h>
|
||||
#include <spi/spi.h>
|
||||
#include <mci.h>
|
||||
#include <crc.h>
|
||||
#include <crc7.h>
|
||||
|
||||
#define to_spi_host(mci) container_of(mci, struct mmc_spi_host, mci)
|
||||
#define spi_setup(spi) spi->master->setup(spi)
|
||||
|
||||
/* Response tokens used to ack each block written: */
|
||||
#define SPI_MMC_RESPONSE_CODE(x) ((x) & 0x1f)
|
||||
#define SPI_RESPONSE_ACCEPTED ((2 << 1)|1)
|
||||
|
||||
/* Read and write blocks start with these tokens and end with crc;
|
||||
* on error, read tokens act like a subset of R2_SPI_* values.
|
||||
*/
|
||||
#define SPI_TOKEN_SINGLE 0xFE /* single block r/w, multiblock read */
|
||||
#define SPI_TOKEN_MULTI_WRITE 0xFC /* multiblock write */
|
||||
#define SPI_TOKEN_STOP_TRAN 0xFD /* terminate multiblock write */
|
||||
|
||||
/* MMC SPI commands start with a start bit "0" and a transmit bit "1" */
|
||||
#define MMC_SPI_CMD(x) (0x40 | (x & 0x3F))
|
||||
|
||||
#define MMC_SPI_BLOCKSIZE 512
|
||||
|
||||
/* timeout value */
|
||||
#define CTOUT 8
|
||||
#define RTOUT 3000000 /* 1 sec */
|
||||
#define WTOUT 3000000 /* 1 sec */
|
||||
|
||||
#ifndef CONFIG_MMC_SPI_CRC_ON
|
||||
/* Note that while the CRC, in general, is ignored in SPI mode, the very first
|
||||
* command must be followed by a valid CRC, since the card is not yet in SPI mode.
|
||||
* The CRC byte for a CMD0 command with a zero argument is a constant 0x4A. For
|
||||
* simplicity, this CRC byte is always sent with every command.
|
||||
*/
|
||||
|
||||
static inline u8 crc7(u8 crc, const u8 *buffer, size_t len)
|
||||
{
|
||||
/* This is the crc7 value for a CMD0 command with a zero argument.
|
||||
* It'll be left shifted and ored with '1' in mmc_spi_command_send
|
||||
* to give 0x95 (also known as the CMD0 constant CRC value...)
|
||||
*/
|
||||
return 0x4A;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct mmc_spi_host {
|
||||
struct mci_host mci;
|
||||
struct spi_device *spi;
|
||||
struct device_d *dev;
|
||||
|
||||
/* for bulk data transfers */
|
||||
struct spi_transfer t_tx;
|
||||
struct spi_message m_tx;
|
||||
|
||||
/* for status readback */
|
||||
struct spi_transfer t_rx;
|
||||
struct spi_message m_rx;
|
||||
|
||||
void *ones;
|
||||
};
|
||||
|
||||
static char *maptype(struct mci_cmd *cmd)
|
||||
{
|
||||
switch (cmd->resp_type) {
|
||||
case MMC_RSP_NONE: return "NONE";
|
||||
case MMC_RSP_R1: return "R1";
|
||||
case MMC_RSP_R1b: return "R1B";
|
||||
case MMC_RSP_R2: return "R2/R5";
|
||||
case MMC_RSP_R3: return "R3/R4/R7";
|
||||
default: return "?";
|
||||
}
|
||||
}
|
||||
|
||||
static inline int mmc_cs_off(struct mmc_spi_host *host)
|
||||
{
|
||||
/* chipselect will always be inactive after setup() */
|
||||
return spi_setup(host->spi);
|
||||
}
|
||||
|
||||
static int
|
||||
mmc_spi_readbytes(struct mmc_spi_host *host, unsigned len, void *data)
|
||||
{
|
||||
int status;
|
||||
|
||||
host->t_rx.len = len;
|
||||
host->t_rx.rx_buf = data;
|
||||
|
||||
status = spi_sync(host->spi, &host->m_rx);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int
|
||||
mmc_spi_writebytes(struct mmc_spi_host *host, unsigned len, void *data)
|
||||
{
|
||||
int status;
|
||||
|
||||
host->t_tx.len = len;
|
||||
host->t_tx.tx_buf = data;
|
||||
|
||||
status = spi_sync(host->spi, &host->m_tx);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int mmc_spi_command_send(struct mmc_spi_host *host, struct mci_cmd *cmd)
|
||||
{
|
||||
uint8_t r1;
|
||||
uint8_t command[7];
|
||||
int i;
|
||||
|
||||
command[0] = 0xff;
|
||||
command[1] = MMC_SPI_CMD(cmd->cmdidx);
|
||||
command[2] = cmd->cmdarg >> 24;
|
||||
command[3] = cmd->cmdarg >> 16;
|
||||
command[4] = cmd->cmdarg >> 8;
|
||||
command[5] = cmd->cmdarg;
|
||||
command[6] = (crc7(0, &command[1], 5) << 1) | 0x01;
|
||||
|
||||
mmc_spi_writebytes(host, 7, command);
|
||||
|
||||
for (i = 0; i < CTOUT; i++) {
|
||||
mmc_spi_readbytes(host, 1, &r1);
|
||||
if (i && ((r1 & 0x80) == 0)) { /* r1 response */
|
||||
dev_dbg(host->dev, "%s: CMD%d, TRY %d, RESP %x\n", __func__, cmd->cmdidx, i, r1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return r1;
|
||||
}
|
||||
|
||||
static uint mmc_spi_readdata(struct mmc_spi_host *host, void *xbuf,
|
||||
uint32_t bcnt, uint32_t bsize)
|
||||
{
|
||||
uint8_t *buf = xbuf;
|
||||
uint8_t r1;
|
||||
uint16_t crc;
|
||||
int i;
|
||||
|
||||
while (bcnt--) {
|
||||
for (i = 0; i < RTOUT; i++) {
|
||||
mmc_spi_readbytes(host, 1, &r1);
|
||||
if (r1 != 0xff) /* data token */
|
||||
break;
|
||||
}
|
||||
if (r1 == SPI_TOKEN_SINGLE) {
|
||||
mmc_spi_readbytes(host, bsize, buf);
|
||||
mmc_spi_readbytes(host, 2, &crc);
|
||||
#ifdef CONFIG_MMC_SPI_CRC_ON
|
||||
if (swab16(cyg_crc16(buf, bsize)) != crc) {
|
||||
dev_dbg(host->dev, "%s: CRC error\n", __func__);
|
||||
r1 = R1_SPI_COM_CRC;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
r1 = 0;
|
||||
} else {
|
||||
r1 = R1_SPI_ERROR;
|
||||
break;
|
||||
}
|
||||
buf += bsize;
|
||||
}
|
||||
|
||||
return r1;
|
||||
}
|
||||
|
||||
static uint mmc_spi_writedata(struct mmc_spi_host *host, const void *xbuf,
|
||||
uint32_t bcnt, uint32_t bsize, int multi)
|
||||
{
|
||||
const uint8_t *buf = xbuf;
|
||||
uint8_t r1;
|
||||
uint16_t crc = 0;
|
||||
uint8_t tok[2];
|
||||
int i;
|
||||
|
||||
tok[0] = 0xff;
|
||||
tok[1] = multi ? SPI_TOKEN_MULTI_WRITE : SPI_TOKEN_SINGLE;
|
||||
|
||||
while (bcnt--) {
|
||||
#ifdef CONFIG_MMC_SPI_CRC_ON
|
||||
crc = swab16(cyg_crc16((u8 *)buf, bsize));
|
||||
#endif
|
||||
mmc_spi_writebytes(host, 2, tok);
|
||||
mmc_spi_writebytes(host, bsize, (void *)buf);
|
||||
mmc_spi_writebytes(host, 2, &crc);
|
||||
|
||||
for (i = 0; i < CTOUT; i++) {
|
||||
mmc_spi_readbytes(host, 1, &r1);
|
||||
if ((r1 & 0x11) == 0x01) /* response token */
|
||||
break;
|
||||
}
|
||||
|
||||
dev_dbg(host->dev,"%s : TOKEN%d RESP 0x%X\n", __func__, i, r1);
|
||||
if (SPI_MMC_RESPONSE_CODE(r1) == SPI_RESPONSE_ACCEPTED) {
|
||||
for (i = 0; i < WTOUT; i++) { /* wait busy */
|
||||
mmc_spi_readbytes(host, 1, &r1);
|
||||
if (i && r1 == 0xff) {
|
||||
r1 = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == WTOUT) {
|
||||
dev_dbg(host->dev, "%s: wtout %x\n", __func__, r1);
|
||||
r1 = R1_SPI_ERROR;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
dev_dbg(host->dev, "%s: err %x\n", __func__, r1);
|
||||
r1 = R1_SPI_COM_CRC;
|
||||
break;
|
||||
}
|
||||
buf += bsize;
|
||||
}
|
||||
|
||||
if (multi && bcnt == -1) { /* stop multi write */
|
||||
tok[1] = SPI_TOKEN_STOP_TRAN;
|
||||
mmc_spi_writebytes(host, 2, tok);
|
||||
for (i = 0; i < WTOUT; i++) { /* wait busy */
|
||||
mmc_spi_readbytes(host, 1, &r1);
|
||||
if (i && r1 == 0xff) {
|
||||
r1 = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == WTOUT) {
|
||||
dev_dbg(host->dev, "%s: wstop %x\n", __func__, r1);
|
||||
r1 = R1_SPI_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return r1;
|
||||
}
|
||||
|
||||
static int mmc_spi_request(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data)
|
||||
{
|
||||
struct mmc_spi_host *host = to_spi_host(mci);
|
||||
uint8_t r1;
|
||||
int i;
|
||||
int ret = 0;
|
||||
|
||||
dev_dbg(host->dev, "%s : CMD%02d, RESP %s, ARG 0x%X\n", __func__,
|
||||
cmd->cmdidx, maptype(cmd), cmd->cmdarg);
|
||||
|
||||
r1 = mmc_spi_command_send(host, cmd);
|
||||
|
||||
cmd->response[0] = r1;
|
||||
|
||||
if (r1 == 0xff) { /* no response */
|
||||
ret = -ETIME;
|
||||
goto done;
|
||||
} else if (r1 & R1_SPI_COM_CRC) {
|
||||
ret = -ECOMM;
|
||||
goto done;
|
||||
} else if (r1 & ~R1_SPI_IDLE) { /* other errors */
|
||||
ret = -ETIME;
|
||||
goto done;
|
||||
} else if (cmd->resp_type == MMC_RSP_R2) {
|
||||
r1 = mmc_spi_readdata(host, cmd->response, 1, 16);
|
||||
for (i = 0; i < 4; i++)
|
||||
cmd->response[i] = swab32(cmd->response[i]);
|
||||
dev_dbg(host->dev, "MMC_RSP_R2 -> %x %x %x %x\n", cmd->response[0], cmd->response[1],
|
||||
cmd->response[2], cmd->response[3]);
|
||||
} else if (!data) {
|
||||
switch (cmd->cmdidx) {
|
||||
case SD_CMD_SEND_IF_COND:
|
||||
case MMC_CMD_SPI_READ_OCR:
|
||||
mmc_spi_readbytes(host, 4, cmd->response);
|
||||
cmd->response[0] = swab32(cmd->response[0]);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (data->flags == MMC_DATA_READ) {
|
||||
dev_dbg(host->dev, "%s : DATA READ, %x blocks, bsize = 0x%X\n", __func__,
|
||||
data->blocks, data->blocksize);
|
||||
r1 = mmc_spi_readdata(host, data->dest,
|
||||
data->blocks, data->blocksize);
|
||||
} else if (data->flags == MMC_DATA_WRITE) {
|
||||
dev_dbg(host->dev, "%s : DATA WRITE, %x blocks, bsize = 0x%X\n", __func__,
|
||||
data->blocks, data->blocksize);
|
||||
r1 = mmc_spi_writedata(host, data->src,
|
||||
data->blocks, data->blocksize,
|
||||
(cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK));
|
||||
}
|
||||
if (r1 & R1_SPI_COM_CRC)
|
||||
ret = -ECOMM;
|
||||
else if (r1)
|
||||
ret = -ETIME;
|
||||
}
|
||||
|
||||
done:
|
||||
mmc_cs_off(host);
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static void mmc_spi_set_ios(struct mci_host *mci, struct device_d *mci_dev,
|
||||
unsigned bus_width, unsigned clock)
|
||||
{
|
||||
struct mmc_spi_host *host = to_spi_host(mci);
|
||||
|
||||
spi_setup(host->spi);
|
||||
}
|
||||
|
||||
static int mmc_spi_init(struct mci_host *mci, struct device_d *mci_dev)
|
||||
{
|
||||
struct mmc_spi_host *host = to_spi_host(mci);
|
||||
mmc_spi_readbytes(host, 10, NULL);
|
||||
|
||||
/*
|
||||
* Do a burst with chipselect active-high. We need to do this to
|
||||
* meet the requirement of 74 clock cycles with both chipselect
|
||||
* and CMD (MOSI) high before CMD0 ... after the card has been
|
||||
* powered up to Vdd(min), and so is ready to take commands.
|
||||
*
|
||||
* Some cards are particularly needy of this (e.g. Viking "SD256")
|
||||
* while most others don't seem to care.
|
||||
*
|
||||
* Note that this is one of the places MMC/SD plays games with the
|
||||
* SPI protocol. Another is that when chipselect is released while
|
||||
* the card returns BUSY status, the clock must issue several cycles
|
||||
* with chipselect high before the card will stop driving its output.
|
||||
*/
|
||||
|
||||
host->spi->mode |= SPI_CS_HIGH;
|
||||
if (spi_setup(host->spi) != 0) {
|
||||
/* Just warn; most cards work without it. */
|
||||
dev_warn(&host->spi->dev,
|
||||
"can't change chip-select polarity\n");
|
||||
host->spi->mode &= ~SPI_CS_HIGH;
|
||||
} else {
|
||||
mmc_spi_readbytes(host, 18, NULL);
|
||||
|
||||
host->spi->mode &= ~SPI_CS_HIGH;
|
||||
if (spi_setup(host->spi) != 0) {
|
||||
/* Wot, we can't get the same setup we had before? */
|
||||
dev_err(&host->spi->dev,
|
||||
"can't restore chip-select polarity\n");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spi_mci_probe(struct device_d *dev)
|
||||
{
|
||||
struct spi_device *spi = (struct spi_device *)dev->type_data;
|
||||
struct mmc_spi_host *host;
|
||||
void *ones;
|
||||
|
||||
host = xzalloc(sizeof(*host));
|
||||
host->mci.send_cmd = mmc_spi_request;
|
||||
host->mci.set_ios = mmc_spi_set_ios;
|
||||
host->mci.init = mmc_spi_init;
|
||||
|
||||
host->dev = dev;
|
||||
host->spi = spi;
|
||||
dev->priv = host;
|
||||
|
||||
ones = xmalloc(MMC_SPI_BLOCKSIZE);
|
||||
memset(ones, 0xff, MMC_SPI_BLOCKSIZE);
|
||||
|
||||
host->ones = ones;
|
||||
|
||||
spi_message_init(&host->m_tx);
|
||||
spi_message_init(&host->m_rx);
|
||||
|
||||
spi_message_add_tail(&host->t_tx, &host->m_tx);
|
||||
spi_message_add_tail(&host->t_rx, &host->m_rx);
|
||||
|
||||
host->t_rx.tx_buf = host->ones;
|
||||
host->t_rx.cs_change = 1;
|
||||
|
||||
host->t_tx.cs_change = 1;
|
||||
|
||||
host->mci.voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
|
||||
host->mci.host_caps = MMC_CAP_SPI;
|
||||
|
||||
mci_register(&host->mci);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct driver_d spi_mci_driver = {
|
||||
.name = "spi_mci",
|
||||
.probe = spi_mci_probe,
|
||||
};
|
||||
|
||||
static int spi_mci_init_driver(void)
|
||||
{
|
||||
register_driver(&spi_mci_driver);
|
||||
return 0;
|
||||
}
|
||||
|
||||
device_initcall(spi_mci_init_driver);
|
|
@ -49,6 +49,7 @@
|
|||
|
||||
#define MMC_MODE_HS 0x001
|
||||
#define MMC_MODE_HS_52MHz 0x010
|
||||
#define MMC_CAP_SPI 0x020
|
||||
#define MMC_MODE_4BIT 0x100
|
||||
#define MMC_MODE_8BIT 0x200
|
||||
|
||||
|
@ -56,6 +57,12 @@
|
|||
|
||||
#define IS_SD(x) (x->version & SD_VERSION_SD)
|
||||
|
||||
#ifdef CONFIG_MCI_SPI
|
||||
#define mmc_host_is_spi(host) ((host)->host_caps & MMC_CAP_SPI)
|
||||
#else
|
||||
#define mmc_host_is_spi(host) 0
|
||||
#endif
|
||||
|
||||
#define MMC_DATA_READ 1
|
||||
#define MMC_DATA_WRITE 2
|
||||
|
||||
|
@ -78,6 +85,8 @@
|
|||
#define MMC_CMD_WRITE_SINGLE_BLOCK 24
|
||||
#define MMC_CMD_WRITE_MULTIPLE_BLOCK 25
|
||||
#define MMC_CMD_APP_CMD 55
|
||||
#define MMC_CMD_SPI_READ_OCR 58
|
||||
#define MMC_CMD_SPI_CRC_ON_OFF 59
|
||||
|
||||
#define SD_CMD_SEND_RELATIVE_ADDR 3
|
||||
#define SD_CMD_SWITCH_FUNC 6
|
||||
|
@ -155,6 +164,15 @@
|
|||
#define R1_ILLEGAL_COMMAND (1 << 22)
|
||||
#define R1_APP_CMD (1 << 5)
|
||||
|
||||
#define R1_SPI_IDLE (1 << 0)
|
||||
#define R1_SPI_ERASE_RESET (1 << 1)
|
||||
#define R1_SPI_ILLEGAL_COMMAND (1 << 2)
|
||||
#define R1_SPI_COM_CRC (1 << 3)
|
||||
#define R1_SPI_ERASE_SEQ (1 << 4)
|
||||
#define R1_SPI_ADDRESS (1 << 5)
|
||||
#define R1_SPI_PARAMETER (1 << 6)
|
||||
#define R1_SPI_ERROR (1 << 7)
|
||||
|
||||
/* response types */
|
||||
#define MMC_RSP_PRESENT (1 << 0)
|
||||
#define MMC_RSP_136 (1 << 1) /* 136 bit response */
|
||||
|
|
Loading…
Reference in New Issue