[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.3, Mon Dec 24 15:49:40 2007 UTC (16 years, 4 months ago) by nbrk
Branch: MAIN
Changes since 1.2: +12 -13 lines

use spi_cs_{low,high}() in first initialization steps.
this allows card to be initializated completely and ready for receive commands in spi mode.
other parts of spisdmmc_init() is broken - more work to do (but atleast it replies ok to GMD1_SEND_OP_COMMAND)

/*
 * $Id: sdmmc_spi.c,v 1.3 2007/12/24 15:49:40 nbrk Exp $
 */
#include <sys/types.h>
#include <sys/device.h>
#include <sys/bus_spi.h>

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

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

//	struct sdmmc_bus_handle;
};

int 	spisdmmc_attach(struct device *, uint32_t, uint8_t);
void 	spisdmmc_send_command(struct spisdmmc_dd *ddp, uint8_t cmd, uint32_t arg, uint32_t crc);
uint8_t spisdmmc_get_response(struct spisdmmc_dd *ddp);
int 	spisdmmc_set_block_size(struct spisdmmc_dd *ddp, uint16_t blksize);
void 	spisdmmc_read_block(struct spisdmmc_dd *ddp, uint16_t nbytes, char *buffp);
uint8_t spisdmmc_get_token(struct spisdmmc_dd *ddp);
void 	spisdmmc_read_cid(struct spisdmmc_dd *ddp, struct sdmmc_cid *cidp);



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

	spisdmmc_init(ddp);

	return(0);
}


int
spisdmmc_init(struct spisdmmc_dd *ddp)
{
	uint8_t i, resp;
	struct sdmmc_cid cid;

	/*
	 * Apply 80 clock pulses.
	 * XXX assert CS. (NPCS0)
	 */
	spi_cs_high(ddp->ss_sbhp);
	for (i = 0; i < 9; i++)
		spi_transmit(ddp->ss_sbhp, 0xff);

	/*
	 * Send CMD0_GO_IDLE_STATE.
	 */
	spi_cs_low(ddp->ss_sbhp);
	spisdmmc_send_command(ddp, CMD0_GO_IDLE_STATE, 0, CMD0_HARDCODED_CRC);

	resp = spisdmmc_get_response(ddp);

	if (resp & R1_IN_IDLE_STATE != 1) {
		printf("spisdmmc_init: CMD0_GO_IDLE_STATE failed with %d\n", resp);

		return(-1);
	}
	printf("spisdmmc_init: Card replied to CMD0_GO_IDLE_STATE with 0x%x\n", resp);

	/*
	 * Okay. Try to initialize the card.
	 * Periodically send CMD1 and poll for response with 0x01 cleared (card exit Idle state).
	 * CRC is off so we can set it 0xff.
	 * XXX send 0xff before.
	 */
	do {
		spi_cs_high(ddp->ss_sbhp);
		spi_transmit(ddp->ss_sbhp, 0xff);

		spi_cs_low(ddp->ss_sbhp);

		spisdmmc_send_command(ddp, CMD1_SEND_OP_COND, 0, 0xff);
		resp = spisdmmc_get_response(ddp);
	} while(resp != 0x00);

	printf("spisdmmc_init: Card initialization completed\n");

	/*
	 * Set block size to 512 bytes.
	 */
	if(spisdmmc_set_block_size(ddp, SDMMC_BLOCK_SIZE) == -1) {
		printf("spisdmmc_init: failed to set block size\n");
		return(-1);
	}
	printf("spisdmmc_init: block size set to %d\n", SDMMC_BLOCK_SIZE);

	spisdmmc_read_cid(ddp, &cid);
	printf("cid.pnm: %s%s%s%s%s\n", cid.cid_pnm[0], cid.cid_pnm[1], cid.cid_pnm[2], cid.cid_pnm[3], cid.cid_pnm[4]);

	return(0);
}


void
spisdmmc_send_command(struct spisdmmc_dd *ddp, uint8_t cmd, uint32_t arg, uint32_t crc)
{
	struct sdmmc_cmdframe sdcf;
	uint8_t i;

	/* 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);
	/* transmit cmd */
	spi_transmit(ddp->ss_sbhp, sdcf.sc_cmd);

	/* transmit 32 bit argument XXX MSB first */
	for(i = 0; i < 4; i++)
		spi_transmit(ddp->ss_sbhp, ((sdcf.sc_arg & (0xff000000 >> i)) >> (8 * (4 - i + 1))) );

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

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


uint8_t
spisdmmc_get_response(struct spisdmmc_dd *ddp)
{
	uint8_t i, resp;
	/*
	 * Get response from card.
	 * After command response comes in 8 to 64 pulses (all this bits will be 1).
	 */

	for(i = 0; i < 64; i++) {
		resp = spi_transmit(ddp->ss_sbhp, 0xff);

//		if (resp == 0 || resp == 1)
		if (resp != 0xff)
			return(resp);
	}
}

uint8_t
spisdmmc_get_token(struct spisdmmc_dd *ddp)
{
	uint16_t i, token;
	/*
	 * Get token.
	 */

	for(i = 0; i < 1000; i++) {
		token = spi_transmit(ddp->ss_sbhp, 0xff);

		if (token == TOK_READ_BLOCK || token == TOK_WRITE_BLOCK_MULTIPLE)
			return(token);
	}
}


int
spisdmmc_set_block_size(struct spisdmmc_dd *ddp, uint16_t blksize)
{
	uint8_t resp;
	/*
	 * Set block size (CMD16).
	 */
	spisdmmc_send_command(ddp,CMD16_SET_BLOCKLEN, blksize, 0xff);
	resp = spisdmmc_get_response(ddp);
	if (resp != 0x00) {
		printf("spisdmmc_set_block_size: set block size to %d failed (0x%x)\n", blksize, resp);
		return(-1);
	}
	return(0);
}

void
spisdmmc_read_cid(struct spisdmmc_dd *ddp, struct sdmmc_cid *cidp)
{
	uint8_t resp;
	struct sdmmc_cid cid;
	/*
	 * Read Card ID register.
	 */
	spisdmmc_send_command(ddp, CMD10_SEND_CID, 0, 0xff);
	resp = spisdmmc_get_response(ddp);
	printf("spisdmmc_read_cid: got r1 response 0x%x\n", resp);

	/*
	 * Wait R1 response.
	 */
	if (resp != 0x00) {
		printf("spisdmmc_read_cid: bad response 0x%x\n", resp);
		return;
	}

	spisdmmc_read_block(ddp, 16, (char *)&cid);
}


void
spisdmmc_read_block(struct spisdmmc_dd *ddp, uint16_t nbytes, char *buffp)
{
	uint8_t token;
	uint16_t crc;
	/*
	 * Read block.
	 */
	token = spisdmmc_get_token(ddp);
	printf("spisdmmc_read_block: got token 0x%x\n", token);
	if (token != TOK_READ_BLOCK) {
		printf("spisdmmc_read_block: error, foreign token\n");
		return;
	}
	/*
	 * Okay, next is the data.
	 */
	while(nbytes != 0) {
		*buffp = spi_transmit(ddp->ss_sbhp, 0xff);
		printf("byte read 0x%x\n", *buffp);

		buffp++;
		nbytes--;
	}

	/*
	 * Read CRC.
	 */
	crc = spi_transmit(ddp->ss_sbhp, 0xff);
	crc <<= 8;
	crc = spi_transmit(ddp->ss_sbhp, 0xff);
};