コード例 #1
0
ファイル: ti_mmchs.c プロジェクト: Alkzndr/freebsd
/**
 *	ti_mmchs_reset_controller -
 *	@arg: caller supplied arg
 *	@segs: array of segments (although in our case should only be one)
 *	@nsegs: number of segments (in our case should be 1)
 *	@error:
 *
 *
 *
 */
static void
ti_mmchs_reset_controller(struct ti_mmchs_softc *sc, uint32_t bit)
{
	unsigned long attempts;
	uint32_t sysctl;

	ti_mmchs_dbg(sc, "reseting controller - bit 0x%08x\n", bit);

	sysctl = ti_mmchs_read_4(sc, MMCHS_SYSCTL);
	ti_mmchs_write_4(sc, MMCHS_SYSCTL, sysctl | bit);
	/* 
	 * AM335x and OMAP4 >= ES2 have an updated reset logic.
	 * Monitor a 0->1 transition first.
	 */
	if ((ti_chip() == CHIP_AM335X) || 
	    ((ti_chip() == CHIP_OMAP_4) && (ti_revision() > OMAP4430_REV_ES1_0))) {
		attempts = 10000;
		while (!(ti_mmchs_read_4(sc, MMCHS_SYSCTL) & bit) && (attempts-- > 0))
			continue;
	}

	attempts = 10000;
	while ((ti_mmchs_read_4(sc, MMCHS_SYSCTL) & bit) && (attempts-- > 0))
		continue;

	if (ti_mmchs_read_4(sc, MMCHS_SYSCTL) & bit)
		device_printf(sc->sc_dev, "Error - Timeout waiting on controller reset\n");
}
コード例 #2
0
ファイル: ti_mmchs.c プロジェクト: dmarion/freebsd-armv6-sys
/**
 *	ti_mmchs_intr_error - handles error interrupts
 *	@sc: pointer to the driver context
 *	@cmd: the command that was sent previously
 *	@stat_reg: the value that was in the status register
 *
 *
 *	LOCKING:
 *	Called from interrupt context
 *
 *	RETURNS:
 *	Return value indicates if the transaction is complete, not done = 0, done != 0
 */
static int
ti_mmchs_intr_error(struct ti_mmchs_softc *sc, struct mmc_command *cmd,
					 uint32_t stat_reg)
{
	ti_mmchs_dbg(sc, "error in xfer - stat 0x%08x\n", stat_reg);

	/* Ignore CRC errors on CMD2 and ACMD47, per relevant standards */
	if ((stat_reg & MMCHS_STAT_CCRC) && (cmd->opcode == MMC_SEND_OP_COND ||
	    cmd->opcode == ACMD_SD_SEND_OP_COND))
		cmd->error = MMC_ERR_NONE;
	else if (stat_reg & (MMCHS_STAT_CTO | MMCHS_STAT_DTO))
		cmd->error = MMC_ERR_TIMEOUT;
	else if (stat_reg & (MMCHS_STAT_CCRC | MMCHS_STAT_DCRC))
		cmd->error = MMC_ERR_BADCRC;
	else
		cmd->error = MMC_ERR_FAILED;

	/* If a dma transaction we should also stop the dma transfer */
	if (ti_mmchs_read_4(sc, MMCHS_CMD) & MMCHS_CMD_DE) {

		/* Abort the DMA transfer (DDIR bit tells direction) */
		if (ti_mmchs_read_4(sc, MMCHS_CMD) & MMCHS_CMD_DDIR)
			ti_sdma_stop_xfer(sc->sc_dmach_rd);
		else
			ti_sdma_stop_xfer(sc->sc_dmach_wr);

		/* If an error occure abort the DMA operation and free the dma map */
		if ((sc->sc_dmamapped > 0) && (cmd->error != MMC_ERR_NONE)) {
			bus_dmamap_unload(sc->sc_dmatag, sc->sc_dmamap);
			sc->sc_dmamapped--;
		}
	}

	/* Command error occured? ... if so issue a soft reset for the cmd fsm */
	if (stat_reg & (MMCHS_STAT_CCRC | MMCHS_STAT_CTO)) {
		ti_mmchs_reset_controller(sc, MMCHS_SYSCTL_SRC);
	}

	/* Data error occured? ... if so issue a soft reset for the data line */
	if (stat_reg & (MMCHS_STAT_DEB | MMCHS_STAT_DCRC | MMCHS_STAT_DTO)) {
		ti_mmchs_reset_controller(sc, MMCHS_SYSCTL_SRD);
	}

	/* On any error the command is cancelled ... so we are done */
	return 1;
}
コード例 #3
0
ファイル: ti_mmchs.c プロジェクト: Alkzndr/freebsd
/**
 *	ti_mmchs_intr_error - handles error interrupts
 *	@sc: pointer to the driver context
 *	@cmd: the command that was sent previously
 *	@stat_reg: the value that was in the status register
 *
 *
 *	LOCKING:
 *	Called from interrupt context
 *
 *	RETURNS:
 *	Return value indicates if the transaction is complete, not done = 0, done != 0
 */
static int
ti_mmchs_intr_error(struct ti_mmchs_softc *sc, struct mmc_command *cmd,
					 uint32_t stat_reg)
{
	ti_mmchs_dbg(sc, "error in xfer - stat 0x%08x\n", stat_reg);

	/* Ignore CRC errors on CMD2 and ACMD47, per relevant standards */
	if ((stat_reg & MMCHS_STAT_CCRC) && (cmd->opcode == MMC_SEND_OP_COND ||
	    cmd->opcode == ACMD_SD_SEND_OP_COND))
		cmd->error = MMC_ERR_NONE;
	else if (stat_reg & (MMCHS_STAT_CTO | MMCHS_STAT_DTO))
		cmd->error = MMC_ERR_TIMEOUT;
	else if (stat_reg & (MMCHS_STAT_CCRC | MMCHS_STAT_DCRC))
		cmd->error = MMC_ERR_BADCRC;
	else
		cmd->error = MMC_ERR_FAILED;

	/* If a dma transaction we should also stop the dma transfer */
	if (ti_mmchs_read_4(sc, MMCHS_CMD) & MMCHS_CMD_DE) {

		/* Abort the DMA transfer (DDIR bit tells direction) */
		if (ti_mmchs_read_4(sc, MMCHS_CMD) & MMCHS_CMD_DDIR)
#ifdef SOC_TI_AM335X
			printf("%s: DMA unimplemented\n", __func__);
#else
			ti_sdma_stop_xfer(sc->sc_dmach_rd);
#endif
		else
#ifdef SOC_TI_AM335X
			printf("%s: DMA unimplemented\n", __func__);
#else
			ti_sdma_stop_xfer(sc->sc_dmach_wr);
#endif

		/* If an error occure abort the DMA operation and free the dma map */
		if ((sc->sc_dmamapped > 0) && (cmd->error != MMC_ERR_NONE)) {
			bus_dmamap_unload(sc->sc_dmatag, sc->sc_dmamap);
			sc->sc_dmamapped--;
		}
	}
コード例 #4
0
ファイル: ti_mmchs.c プロジェクト: dmarion/freebsd-armv6-sys
/**
 *	ti_mmchs_send_init_stream - sets bus/controller settings
 *	@brdev: mmc bridge device handle
 *	@reqdev: device doing the request
 *
 *	Send init stream sequence to card before sending IDLE command
 *
 *	LOCKING:
 *
 *
 *	RETURNS:
 *	0 if function succeeded
 */
static void
ti_mmchs_send_init_stream(struct ti_mmchs_softc *sc)
{
	unsigned long timeout;
	uint32_t ie, ise, con;

	ti_mmchs_dbg(sc, "Performing init sequence\n");

	/* Prior to issuing any command, the MMCHS controller has to execute a
	 * special INIT procedure. The MMCHS controller has to generate a clock
	 * during 1ms. During the INIT procedure, the MMCHS controller generates 80
	 * clock periods. In order to keep the 1ms gap, the MMCHS controller should
	 * be configured to generate a clock whose frequency is smaller or equal to
	 * 80 KHz. If the MMCHS controller divider bitfield width doesn't allow to
	 * choose big values, the MMCHS controller driver should perform the INIT
	 * procedure twice or three times. Twice is generally enough.
	 *
	 * The INIt procedure is executed by setting MMCHS1.MMCHS_CON[1] INIT
	 * bitfield to 1 and by sending a dummy command, writing 0x00000000 in
	 * MMCHS1.MMCHS_CMD register.
	 */

	/* Disable interrupt status events but enable interrupt generation.
	 * This doesn't seem right to me, but if the interrupt generation is not
	 * enabled the CC bit doesn't seem to be set in the STAT register.
	 */

	/* Enable interrupt generation */
	ie = ti_mmchs_read_4(sc, MMCHS_IE);
	ti_mmchs_write_4(sc, MMCHS_IE, 0x307F0033);

	/* Disable generation of status events (stops interrupt triggering) */
	ise = ti_mmchs_read_4(sc, MMCHS_ISE);
	ti_mmchs_write_4(sc, MMCHS_ISE, 0);

	/* Set the initialise stream bit */
	con = ti_mmchs_read_4(sc, MMCHS_CON);
	con |= MMCHS_CON_INIT;
	ti_mmchs_write_4(sc, MMCHS_CON, con);

	/* Write a dummy command 0x00 */
	ti_mmchs_write_4(sc, MMCHS_CMD, 0x00000000);

	/* Loop waiting for the command to finish */
	timeout = hz;
	do {
		pause("MMCINIT", 1);
		if (timeout-- == 0) {
			device_printf(sc->sc_dev, "Error: first stream init timed out\n");
			break;
		}
	} while (!(ti_mmchs_read_4(sc, MMCHS_STAT) & MMCHS_STAT_CC));

	/* Clear the command complete status bit */
	ti_mmchs_write_4(sc, MMCHS_STAT, MMCHS_STAT_CC);

	/* Write another dummy command 0x00 */
	ti_mmchs_write_4(sc, MMCHS_CMD, 0x00000000);

	/* Loop waiting for the second command to finish */
	timeout = hz;
	do {
		pause("MMCINIT", 1);
		if (timeout-- == 0) {
			device_printf(sc->sc_dev, "Error: second stream init timed out\n");
			break;
		}
	} while (!(ti_mmchs_read_4(sc, MMCHS_STAT) & MMCHS_STAT_CC));

	/* Clear the stream init bit */
	con &= ~MMCHS_CON_INIT;
	ti_mmchs_write_4(sc, MMCHS_CON, con);

	/* Clear the status register, then restore the IE and ISE registers */
	ti_mmchs_write_4(sc, MMCHS_STAT, 0xffffffff);
	ti_mmchs_read_4(sc, MMCHS_STAT);

	ti_mmchs_write_4(sc, MMCHS_ISE, ise);
	ti_mmchs_write_4(sc, MMCHS_IE, ie);
}