コード例 #1
0
ファイル: at91_mci.c プロジェクト: ChaosJohn/freebsd
static void
at91_mci_start_cmd(struct at91_mci_softc *sc, struct mmc_command *cmd)
{
	uint32_t cmdr, mr;
	struct mmc_data *data;

	sc->curcmd = cmd;
	data = cmd->data;

	/* XXX Upper layers don't always set this */
	cmd->mrq = sc->req;

	/* Begin setting up command register. */

	cmdr = cmd->opcode;

	if (sc->host.ios.bus_mode == opendrain)
		cmdr |= MCI_CMDR_OPDCMD;

	/* Set up response handling.  Allow max timeout for responses. */

	if (MMC_RSP(cmd->flags) == MMC_RSP_NONE)
		cmdr |= MCI_CMDR_RSPTYP_NO;
	else {
		cmdr |= MCI_CMDR_MAXLAT;
		if (cmd->flags & MMC_RSP_136)
			cmdr |= MCI_CMDR_RSPTYP_136;
		else
			cmdr |= MCI_CMDR_RSPTYP_48;
	}

	/*
	 * If there is no data transfer, just set up the right interrupt mask
	 * and start the command.
	 *
	 * The interrupt mask needs to be CMDRDY plus all non-data-transfer
	 * errors. It's important to leave the transfer-related errors out, to
	 * avoid spurious timeout or crc errors on a STOP command following a
	 * multiblock read.  When a multiblock read is in progress, sending a
	 * STOP in the middle of a block occasionally triggers such errors, but
	 * we're totally disinterested in them because we've already gotten all
	 * the data we wanted without error before sending the STOP command.
	 */

	if (data == NULL) {
		uint32_t ier = MCI_SR_CMDRDY |
		    MCI_SR_RTOE | MCI_SR_RENDE |
		    MCI_SR_RCRCE | MCI_SR_RDIRE | MCI_SR_RINDE;

		at91_mci_pdc_disable(sc);

		if (cmd->opcode == MMC_STOP_TRANSMISSION)
			cmdr |= MCI_CMDR_TRCMD_STOP;

		/* Ignore response CRC on CMD2 and ACMD41, per standard. */

		if (cmd->opcode == MMC_SEND_OP_COND ||
		    cmd->opcode == ACMD_SD_SEND_OP_COND)
			ier &= ~MCI_SR_RCRCE;

		if (mci_debug)
			printf("CMDR %x (opcode %d) ARGR %x no data\n",
			    cmdr, cmd->opcode, cmd->arg);

		WR4(sc, MCI_ARGR, cmd->arg);
		WR4(sc, MCI_CMDR, cmdr);
		WR4(sc, MCI_IDR, 0xffffffff);
		WR4(sc, MCI_IER, ier);
		return;
	}

	/* There is data, set up the transfer-related parts of the command. */

	if (data->flags & MMC_DATA_READ)
		cmdr |= MCI_CMDR_TRDIR;

	if (data->flags & (MMC_DATA_READ | MMC_DATA_WRITE))
		cmdr |= MCI_CMDR_TRCMD_START;

	if (data->flags & MMC_DATA_STREAM)
		cmdr |= MCI_CMDR_TRTYP_STREAM;
	else if (data->flags & MMC_DATA_MULTI) {
		cmdr |= MCI_CMDR_TRTYP_MULTIPLE;
		sc->flags |= (data->flags & MMC_DATA_READ) ?
		    CMD_MULTIREAD : CMD_MULTIWRITE;
	}

	/*
	 * Disable PDC until we're ready.
	 *
	 * Set block size and turn on PDC mode for dma xfer.
	 * Note that the block size is the smaller of the amount of data to be
	 * transferred, or 512 bytes.  The 512 size is fixed by the standard;
	 * smaller blocks are possible, but never larger.
	 */

	WR4(sc, PDC_PTCR, PDC_PTCR_RXTDIS | PDC_PTCR_TXTDIS);

	mr = RD4(sc,MCI_MR) & ~MCI_MR_BLKLEN;
	mr |=  min(data->len, 512) << 16;
	WR4(sc, MCI_MR, mr | MCI_MR_PDCMODE|MCI_MR_PDCPADV);

	/*
	 * Set up DMA.
	 *
	 * Use bounce buffers even if we don't need to byteswap, because doing
	 * multi-block IO with large DMA buffers is way fast (compared to
	 * single-block IO), even after incurring the overhead of also copying
	 * from/to the caller's buffers (which may be in non-contiguous physical
	 * pages).
	 *
	 * In an ideal non-byteswap world we could create a dma tag that allows
	 * for discontiguous segments and do the IO directly from/to the
	 * caller's buffer(s), using ENDRX/ENDTX interrupts to chain the
	 * discontiguous buffers through the PDC. Someday.
	 *
	 * If a read is bigger than 2k, split it in half so that we can start
	 * byte-swapping the first half while the second half is on the wire.
	 * It would be best if we could split it into 8k chunks, but we can't
	 * always keep up with the byte-swapping due to other system activity,
	 * and if an RXBUFF interrupt happens while we're still handling the
	 * byte-swap from the prior buffer (IE, we haven't returned from
	 * handling the prior interrupt yet), then data will get dropped on the
	 * floor and we can't easily recover from that.  The right fix for that
	 * would be to have the interrupt handling only keep the DMA flowing and
	 * enqueue filled buffers to be byte-swapped in a non-interrupt context.
	 * Even that won't work on the write side of things though; in that
	 * context we have to have all the data ready to go before starting the
	 * dma.
	 *
	 * XXX what about stream transfers?
	 */
	sc->xfer_offset = 0;
	sc->bbuf_curidx = 0;

	if (data->flags & (MMC_DATA_READ | MMC_DATA_WRITE)) {
		uint32_t len;
		uint32_t remaining = data->len;
		bus_addr_t paddr;
		int err;

		if (remaining > (BBCOUNT*BBSIZE))
			panic("IO read size exceeds MAXDATA\n");

		if (data->flags & MMC_DATA_READ) {
			if (remaining > 2048) // XXX
				len = remaining / 2;
			else
				len = remaining;
			err = bus_dmamap_load(sc->dmatag, sc->bbuf_map[0],
			    sc->bbuf_vaddr[0], len, at91_mci_getaddr,
			    &paddr, BUS_DMA_NOWAIT);
			if (err != 0)
				panic("IO read dmamap_load failed\n");
			bus_dmamap_sync(sc->dmatag, sc->bbuf_map[0],
			    BUS_DMASYNC_PREREAD);
			WR4(sc, PDC_RPR, paddr);
			WR4(sc, PDC_RCR, len / 4);
			sc->bbuf_len[0] = len;
			remaining -= len;
			if (remaining == 0) {
				sc->bbuf_len[1] = 0;
			} else {
				len = remaining;
				err = bus_dmamap_load(sc->dmatag, sc->bbuf_map[1],
				    sc->bbuf_vaddr[1], len, at91_mci_getaddr,
				    &paddr, BUS_DMA_NOWAIT);
				if (err != 0)
					panic("IO read dmamap_load failed\n");
				bus_dmamap_sync(sc->dmatag, sc->bbuf_map[1],
				    BUS_DMASYNC_PREREAD);
				WR4(sc, PDC_RNPR, paddr);
				WR4(sc, PDC_RNCR, len / 4);
				sc->bbuf_len[1] = len;
				remaining -= len;
			}
			WR4(sc, PDC_PTCR, PDC_PTCR_RXTEN);
		} else {
			len = min(BBSIZE, remaining);
			/*
			 * If this is MCI1 revision 2xx controller, apply
			 * a work-around for the "Data Write Operation and
			 * number of bytes" erratum.
			 */
			if ((sc->sc_cap & CAP_MCI1_REV2XX) && len < 12) {
				len = 12;
				memset(sc->bbuf_vaddr[0], 0, 12);
			}
			at91_bswap_buf(sc, sc->bbuf_vaddr[0], data->data, len);
			err = bus_dmamap_load(sc->dmatag, sc->bbuf_map[0],
			    sc->bbuf_vaddr[0], len, at91_mci_getaddr,
			    &paddr, BUS_DMA_NOWAIT);
			if (err != 0)
				panic("IO write dmamap_load failed\n");
			bus_dmamap_sync(sc->dmatag, sc->bbuf_map[0],
			    BUS_DMASYNC_PREWRITE);
			WR4(sc, PDC_TPR,paddr);
			WR4(sc, PDC_TCR, len / 4);
			sc->bbuf_len[0] = len;
			remaining -= len;
			if (remaining == 0) {
				sc->bbuf_len[1] = 0;
			} else {
				len = remaining;
				at91_bswap_buf(sc, sc->bbuf_vaddr[1],
				    ((char *)data->data)+BBSIZE, len);
				err = bus_dmamap_load(sc->dmatag, sc->bbuf_map[1],
				    sc->bbuf_vaddr[1], len, at91_mci_getaddr,
				    &paddr, BUS_DMA_NOWAIT);
				if (err != 0)
					panic("IO write dmamap_load failed\n");
				bus_dmamap_sync(sc->dmatag, sc->bbuf_map[1],
				    BUS_DMASYNC_PREWRITE);
				WR4(sc, PDC_TNPR, paddr);
				WR4(sc, PDC_TNCR, len / 4);
				sc->bbuf_len[1] = len;
				remaining -= len;
			}
			/* do not enable PDC xfer until CMDRDY asserted */
		}
		data->xfer_len = 0; /* XXX what's this? appears to be unused. */
	}

	if (mci_debug)
		printf("CMDR %x (opcode %d) ARGR %x with data len %d\n",
		       cmdr, cmd->opcode, cmd->arg, cmd->data->len);

	WR4(sc, MCI_ARGR, cmd->arg);
	WR4(sc, MCI_CMDR, cmdr);
	WR4(sc, MCI_IER, MCI_SR_ERROR | MCI_SR_CMDRDY);
}
コード例 #2
0
ファイル: ti_mmchs.c プロジェクト: dmarion/freebsd-armv6-sys
/**
 *	ti_mmchs_start_cmd - starts the given command
 *	@sc: pointer to the driver context
 *	@cmd: the command to start
 *
 *	The call tree for this function is
 *		- ti_mmchs_start_cmd
 *			- ti_mmchs_start
 *				- ti_mmchs_request
 *
 *	LOCKING:
 *	Caller should be holding the OMAP_MMC lock.
 *
 *	RETURNS:
 *	nothing
 */
static void
ti_mmchs_start_cmd(struct ti_mmchs_softc *sc, struct mmc_command *cmd)
{
	uint32_t cmd_reg, con_reg, ise_reg;
	struct mmc_data *data;
	struct mmc_request *req;
	void *vaddr;
	bus_addr_t paddr;
	uint32_t pktsize;

	sc->curcmd = cmd;
	data = cmd->data;
	req = cmd->mrq;

	/* Ensure the STR and MIT bits are cleared, these are only used for special
	 * command types.
	 */
	con_reg = ti_mmchs_read_4(sc, MMCHS_CON);
	con_reg &= ~(MMCHS_CON_STR | MMCHS_CON_MIT);

	/* Load the command into bits 29:24 of the CMD register */
	cmd_reg = (uint32_t)(cmd->opcode & 0x3F) << 24;

	/* Set the default set of interrupts */
	ise_reg = (MMCHS_STAT_CERR | MMCHS_STAT_CTO | MMCHS_STAT_CC | MMCHS_STAT_CEB);

	/* Enable CRC checking if requested */
	if (cmd->flags & MMC_RSP_CRC)
		ise_reg |= MMCHS_STAT_CCRC;

	/* Enable reply index checking if the response supports it */
	if (cmd->flags & MMC_RSP_OPCODE)
		ise_reg |= MMCHS_STAT_CIE;

	/* Set the expected response length */
	if (MMC_RSP(cmd->flags) == MMC_RSP_NONE) {
		cmd_reg |= MMCHS_CMD_RSP_TYPE_NO;
	} else {
		if (cmd->flags & MMC_RSP_136)
			cmd_reg |= MMCHS_CMD_RSP_TYPE_136;
		else if (cmd->flags & MMC_RSP_BUSY)
			cmd_reg |= MMCHS_CMD_RSP_TYPE_48_BSY;
		else
			cmd_reg |= MMCHS_CMD_RSP_TYPE_48;

		/* Enable command index/crc checks if necessary expected */
		if (cmd->flags & MMC_RSP_CRC)
			cmd_reg |= MMCHS_CMD_CCCE;
		if (cmd->flags & MMC_RSP_OPCODE)
			cmd_reg |= MMCHS_CMD_CICE;
	}

	/* Set the bits for the special commands CMD12 (MMC_STOP_TRANSMISSION) and
	 * CMD52 (SD_IO_RW_DIRECT) */
	if (cmd->opcode == MMC_STOP_TRANSMISSION)
		cmd_reg |= MMCHS_CMD_CMD_TYPE_IO_ABORT;

	/* Check if there is any data to write */
	if (data == NULL) {
		/* Clear the block count */
		ti_mmchs_write_4(sc, MMCHS_BLK, 0);

		/* The no data case is fairly simple */
		ti_mmchs_write_4(sc, MMCHS_CON, con_reg);
		ti_mmchs_write_4(sc, MMCHS_IE, ise_reg);
		ti_mmchs_write_4(sc, MMCHS_ISE, ise_reg);
		ti_mmchs_write_4(sc, MMCHS_ARG, cmd->arg);
		ti_mmchs_write_4(sc, MMCHS_CMD, cmd_reg);
		return;
	}

	/* Indicate that data is present */
	cmd_reg |= MMCHS_CMD_DP | MMCHS_CMD_MSBS | MMCHS_CMD_BCE;

	/* Indicate a read operation */
	if (data->flags & MMC_DATA_READ)
		cmd_reg |= MMCHS_CMD_DDIR;

	/* Streaming mode */
	if (data->flags & MMC_DATA_STREAM) {
		con_reg |= MMCHS_CON_STR;
	}

	/* Multi-block mode */
	if (data->flags & MMC_DATA_MULTI) {
		cmd_reg |= MMCHS_CMD_MSBS;
	}

	/* Enable extra interrupt sources for the transfer */
	ise_reg |= (MMCHS_STAT_TC | MMCHS_STAT_DTO | MMCHS_STAT_DEB | MMCHS_STAT_CEB);
	if (cmd->flags & MMC_RSP_CRC)
		ise_reg |= MMCHS_STAT_DCRC;

	/* Enable the DMA transfer bit */
	cmd_reg |= MMCHS_CMD_DE;

	/* Set the block size and block count */
	ti_mmchs_write_4(sc, MMCHS_BLK, (1 << 16) | data->len);

	/* Setup the DMA stuff */
	if (data->flags & (MMC_DATA_READ | MMC_DATA_WRITE)) {

		vaddr = data->data;
		data->xfer_len = 0;

		/* Map the buffer buf into bus space using the dmamap map. */
		if (bus_dmamap_load(sc->sc_dmatag, sc->sc_dmamap, vaddr, data->len,
		    ti_mmchs_getaddr, &paddr, 0) != 0) {

			if (req->cmd->flags & STOP_STARTED)
				req->stop->error = MMC_ERR_NO_MEMORY;
			else
				req->cmd->error = MMC_ERR_NO_MEMORY;
			sc->req = NULL;
			sc->curcmd = NULL;
			req->done(req);
			return;
		}

		/* Calculate the packet size, the max packet size is 512 bytes
		 * (or 128 32-bit elements).
		 */
		pktsize = min((data->len / 4), (512 / 4));

		/* Sync the DMA buffer and setup the DMA controller */
		if (data->flags & MMC_DATA_READ) {
			bus_dmamap_sync(sc->sc_dmatag, sc->sc_dmamap, BUS_DMASYNC_PREREAD);
			ti_sdma_start_xfer_packet(sc->sc_dmach_rd, sc->sc_data_reg_paddr,
			    paddr, 1, (data->len / 4), pktsize);
		} else {
			bus_dmamap_sync(sc->sc_dmatag, sc->sc_dmamap, BUS_DMASYNC_PREWRITE);
			ti_sdma_start_xfer_packet(sc->sc_dmach_wr, paddr,
			    sc->sc_data_reg_paddr, 1, (data->len / 4), pktsize);
		}

		/* Increase the mapped count */
		sc->sc_dmamapped++;

		sc->sc_cmd_data_vaddr = vaddr;
		sc->sc_cmd_data_len = data->len;
	}

	/* Finally kick off the command */
	ti_mmchs_write_4(sc, MMCHS_CON, con_reg);
	ti_mmchs_write_4(sc, MMCHS_IE, ise_reg);
	ti_mmchs_write_4(sc, MMCHS_ISE, ise_reg);
	ti_mmchs_write_4(sc, MMCHS_ARG, cmd->arg);
	ti_mmchs_write_4(sc, MMCHS_CMD, cmd_reg);

	/* and we're done */
}