Newer
Older
barebox / drivers / mci / mci_spi.c
/*
 * (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.
 *
 *
 */

#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 */

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;
}

/*
 * 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.
 */
#define MMC_SPI_CMD0_CRC	((0x4a << 1) | 0x1)

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;
#ifdef CONFIG_MMC_SPI_CRC_ON
	command[6] = (crc7(0, &command[1], 5) << 1) | 0x01;
#else
	command[6] = MMC_SPI_CMD0_CRC;
#endif

	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 mci_ios *ios)
{
	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->mci.hw_dev = dev;

	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);