drivers/mmc/host/mvsdio.c

Source file repositories/reference/linux-study-clean/drivers/mmc/host/mvsdio.c

File Facts

System
Linux kernel
Corpus path
drivers/mmc/host/mvsdio.c
Extension
.c
Size
23773 bytes
Lines
832
Domain
Driver Families
Bucket
drivers/mmc
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct mvsd_host {
	void __iomem *base;
	struct mmc_request *mrq;
	spinlock_t lock;
	unsigned int xfer_mode;
	unsigned int intr_en;
	unsigned int ctrl;
	unsigned int pio_size;
	void *pio_ptr;
	unsigned int sg_frags;
	unsigned int ns_per_clk;
	unsigned int clock;
	unsigned int base_clock;
	struct timer_list timer;
	struct mmc_host *mmc;
	struct device *dev;
	struct clk *clk;
};

#define mvsd_write(offs, val)	writel(val, iobase + (offs))
#define mvsd_read(offs)		readl(iobase + (offs))

static int mvsd_setup_data(struct mvsd_host *host, struct mmc_data *data)
{
	void __iomem *iobase = host->base;
	unsigned int tmout;
	int tmout_index;

	/*
	 * Hardware weirdness.  The FIFO_EMPTY bit of the HW_STATE
	 * register is sometimes not set before a while when some
	 * "unusual" data block sizes are used (such as with the SWITCH
	 * command), even despite the fact that the XFER_DONE interrupt
	 * was raised.  And if another data transfer starts before
	 * this bit comes to good sense (which eventually happens by
	 * itself) then the new transfer simply fails with a timeout.
	 */
	if (!(mvsd_read(MVSD_HW_STATE) & (1 << 13))) {
		unsigned long t = jiffies + HZ;
		unsigned int hw_state,  count = 0;
		do {
			hw_state = mvsd_read(MVSD_HW_STATE);
			if (time_after(jiffies, t)) {
				dev_warn(host->dev, "FIFO_EMPTY bit missing\n");
				break;
			}
			count++;
		} while (!(hw_state & (1 << 13)));
		dev_dbg(host->dev, "*** wait for FIFO_EMPTY bit "
				   "(hw=0x%04x, count=%d, jiffies=%ld)\n",
				   hw_state, count, jiffies - (t - HZ));
	}

	/* If timeout=0 then maximum timeout index is used. */
	tmout = DIV_ROUND_UP(data->timeout_ns, host->ns_per_clk);
	tmout += data->timeout_clks;
	tmout_index = fls(tmout - 1) - 12;
	if (tmout_index < 0)
		tmout_index = 0;
	if (tmout_index > MVSD_HOST_CTRL_TMOUT_MAX)
		tmout_index = MVSD_HOST_CTRL_TMOUT_MAX;

	dev_dbg(host->dev, "data %s at 0x%08x: blocks=%d blksz=%d tmout=%u (%d)\n",
		(data->flags & MMC_DATA_READ) ? "read" : "write",
		(u32)sg_virt(data->sg), data->blocks, data->blksz,
		tmout, tmout_index);

	host->ctrl &= ~MVSD_HOST_CTRL_TMOUT_MASK;
	host->ctrl |= MVSD_HOST_CTRL_TMOUT(tmout_index);
	mvsd_write(MVSD_HOST_CTRL, host->ctrl);
	mvsd_write(MVSD_BLK_COUNT, data->blocks);
	mvsd_write(MVSD_BLK_SIZE, data->blksz);

	if (nodma || (data->blksz | data->sg->offset) & 3 ||
	    ((!(data->flags & MMC_DATA_READ) && data->sg->offset & 0x3f))) {
		/*
		 * We cannot do DMA on a buffer which offset or size
		 * is not aligned on a 4-byte boundary.
		 *
		 * It also appears the host to card DMA can corrupt
		 * data when the buffer is not aligned on a 64 byte
		 * boundary.
		 */
		host->pio_size = data->blocks * data->blksz;
		host->pio_ptr = sg_virt(data->sg);
		if (!nodma)
			dev_dbg(host->dev, "fallback to PIO for data at 0x%p size %d\n",
				host->pio_ptr, host->pio_size);
		return 1;
	} else {

Annotation

Implementation Notes