[BACK]Return to sdmmc_spi.c CVS log [TXT][DIR] Up to [local] / funnyos / dev / sdmmc

File: [local] / funnyos / dev / sdmmc / sdmmc_spi.c (download)

Revision 1.4, Tue Dec 25 14:10:51 2007 UTC (16 years, 4 months ago) by nbrk
Branch: MAIN
CVS Tags: HEAD
Changes since 1.3: +86 -149 lines

shuffle code around; sdmmc.c now MI device.
in sdmmc_spi.c shrink ugly code and start implementing new backends for sdmmc_bus_handle.
to be done

/*
 * $Id: sdmmc_spi.c,v 1.4 2007/12/25 14:10:51 nbrk Exp $
 */
#include <sys/types.h>
#include <sys/device.h>
#include <sys/bus_spi.h>

#include <dev/sdmmc/sdmmcvar.h>
#include <libkern/printf.h>

#define SPISDMMC_DEBUG 

#ifdef SPISDMMC_DEBUG
#define DPRINTF(x...) 	do { printf(x); } while (0)
#else
#define DPRINTF(x...) 	{ }
#endif

#define SPISDMMC_RESP_TIMEOUT 64 

/*
 * SD/MMC conversation over SPI bus.
 */
struct spisdmmc_dd {
	struct spi_bus_handle *ss_sbhp;

	struct sdmmc_bus_handle ss_smbh;
};

int 		spisdmmc_attach(struct device *, uint32_t, uint8_t);
uint8_t		spisdmmc_init(void *dd);
uint8_t 	spisdmmc_send_command(void *dd, uint8_t cmd, uint32_t arg, uint8_t crc);
uint8_t 	spisdmmc_set_block_size(void *dd, uint16_t size);
uint8_t 	spisdmmc_read_block(void *dd, uint32_t addr, char *buffp);



struct driver spisdmmc_dr = {
	sizeof(struct spisdmmc_dd),
	spisdmmc_attach,
	NULL,
	NULL
};


int
spisdmmc_attach(struct device *self, uint32_t loc, uint8_t flags)
{
	struct spisdmmc_dd *ddp = self->dv_devdata;
	ddp->ss_sbhp = self->dv_parent->dv_aux;

	printf("SPI SD/MMC\n");

	/*
	 * Interface to children.
	 */
	ddp->ss_smbh.sb_init = spisdmmc_init;
	ddp->ss_smbh.sb_send_command = spisdmmc_send_command;
	ddp->ss_smbh.sb_set_block_size = spisdmmc_set_block_size;
//	ddp->ss_smbh.sb_read_block = spisdmmc_read_block;
//	ddp->ss_smbh.sb_write_block = spisdmmc_write_block;
	ddp->ss_smbh.sb_dd = ddp;

	self->dv_aux = &ddp->ss_smbh;

	return(0);
}


uint8_t
spisdmmc_init(void *dd)
{
	struct spisdmmc_dd *ddp = dd;
	uint8_t i, resp;
	/*
	 * Initialize SD/MMC card so it can accept generic read/write commands.
	 * Remember that we should hold CS signal low during all transactions (cmd, resp, data).
	 */

	/* CS high */
	spi_cs_high(ddp->ss_sbhp);
	/* wait 80 pulses */
	for (i =0; i < 9; i++)
		spi_transmit(ddp->ss_sbhp, 0xff);

	/*
	 * Send CMD0_GO_IDLE_STATE
	 * CS line will go low in send_command and up before return here.
	 */
	resp = spisdmmc_send_command(ddp, CMD0_GO_IDLE_STATE, 0, 0x95);
	printf("spisdmmc_init: CMD0 reply 0x%x\n", resp);

	return(0);
}


uint8_t
spisdmmc_send_command(void *dd, uint8_t cmd, uint32_t arg, uint8_t crc)
{
	struct spisdmmc_dd *ddp = dd;
	uint8_t resp;
	/*
	 * Construct command frame and send command to the card.
	 * Try to read reply SASPI_RESP_TIMEOUT times and return it if it's R1 response.
	 * Return 255 if timeout.
	 */
	struct sdmmc_cmdframe sdcf;
	uint8_t to = 0;

	/* construct frame */
	sdcf = sdmmc_command(cmd, arg, crc);

//	printf("spisdmmc_send_command: sending cmd %d arg 0x%x crc 0x%x\n", sdcf.sc_cmd & ~0xc0, sdcf.sc_arg, sdcf.sc_crc);
	/* CS low */
	spi_cs_low(ddp->ss_sbhp);

	/* transmit cmd */
	spi_transmit(ddp->ss_sbhp, sdcf.sc_cmd);

	/* transmit 32 bit argument MSB first */
	spi_transmit(ddp->ss_sbhp, sdcf.sc_arg >> 24 );
	spi_transmit(ddp->ss_sbhp, sdcf.sc_arg >> 16 );
	spi_transmit(ddp->ss_sbhp, sdcf.sc_arg >> 8 );
	spi_transmit(ddp->ss_sbhp, sdcf.sc_arg );

	/* transmit CRC field */
	spi_transmit(ddp->ss_sbhp, sdcf.sc_crc);

	/* CS high */
	spi_cs_high(ddp->ss_sbhp);

	/* XXX Apply NCR clock pulses */
	spi_transmit(ddp->ss_sbhp, 0xff);

	/* CS low */
	spi_cs_low(ddp->ss_sbhp);

	/* read response */
	/* XXX what about non-R1 responses? */
	while(to < SPISDMMC_RESP_TIMEOUT) {
		resp = spi_transmit(ddp->ss_sbhp, 0xff);

//		if (resp & 0x80 == 0) {
		if (resp != 0xff) {
			/* seems like R1 response */
			spi_cs_high(ddp->ss_sbhp);

			return(resp);
		}

		to++;
	}

	/* timeout */
	spi_cs_high(ddp->ss_sbhp);

	DPRINTF("spisdmmc_send_command: response timeout for cmd %d arg 0x%x crc 0x%x\n", cmd, arg, crc);

	return(0xff);	/* error */
}


uint8_t
spisdmmc_set_block_size(void *dd, uint16_t blksize)
{
	struct spisdmmc_dd *ddp = dd;
	uint8_t resp;
	/*
	 * Set block size (CMD16).
	 */

	return(0);
}


uint8_t
spisdmmc_read_block(void *dd, uint32_t addr, char *buff)
{
	struct spisdmmc_dd *ddp = dd;
	/*
	 * Read block.
	 */
};