static void atmci_set_ios()

in host/atmel-mci.c [1378:1506]


static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
	struct atmel_mci_slot	*slot = mmc_priv(mmc);
	struct atmel_mci	*host = slot->host;
	unsigned int		i;

	slot->sdc_reg &= ~ATMCI_SDCBUS_MASK;
	switch (ios->bus_width) {
	case MMC_BUS_WIDTH_1:
		slot->sdc_reg |= ATMCI_SDCBUS_1BIT;
		break;
	case MMC_BUS_WIDTH_4:
		slot->sdc_reg |= ATMCI_SDCBUS_4BIT;
		break;
	case MMC_BUS_WIDTH_8:
		slot->sdc_reg |= ATMCI_SDCBUS_8BIT;
		break;
	}

	if (ios->clock) {
		unsigned int clock_min = ~0U;
		int clkdiv;

		spin_lock_bh(&host->lock);
		if (!host->mode_reg) {
			atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST);
			atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIEN);
			if (host->caps.has_cfg_reg)
				atmci_writel(host, ATMCI_CFG, host->cfg_reg);
		}

		/*
		 * Use mirror of ios->clock to prevent race with mmc
		 * core ios update when finding the minimum.
		 */
		slot->clock = ios->clock;
		for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
			if (host->slot[i] && host->slot[i]->clock
					&& host->slot[i]->clock < clock_min)
				clock_min = host->slot[i]->clock;
		}

		/* Calculate clock divider */
		if (host->caps.has_odd_clk_div) {
			clkdiv = DIV_ROUND_UP(host->bus_hz, clock_min) - 2;
			if (clkdiv < 0) {
				dev_warn(&mmc->class_dev,
					 "clock %u too fast; using %lu\n",
					 clock_min, host->bus_hz / 2);
				clkdiv = 0;
			} else if (clkdiv > 511) {
				dev_warn(&mmc->class_dev,
				         "clock %u too slow; using %lu\n",
				         clock_min, host->bus_hz / (511 + 2));
				clkdiv = 511;
			}
			host->mode_reg = ATMCI_MR_CLKDIV(clkdiv >> 1)
			                 | ATMCI_MR_CLKODD(clkdiv & 1);
		} else {
			clkdiv = DIV_ROUND_UP(host->bus_hz, 2 * clock_min) - 1;
			if (clkdiv > 255) {
				dev_warn(&mmc->class_dev,
				         "clock %u too slow; using %lu\n",
				         clock_min, host->bus_hz / (2 * 256));
				clkdiv = 255;
			}
			host->mode_reg = ATMCI_MR_CLKDIV(clkdiv);
		}

		/*
		 * WRPROOF and RDPROOF prevent overruns/underruns by
		 * stopping the clock when the FIFO is full/empty.
		 * This state is not expected to last for long.
		 */
		if (host->caps.has_rwproof)
			host->mode_reg |= (ATMCI_MR_WRPROOF | ATMCI_MR_RDPROOF);

		if (host->caps.has_cfg_reg) {
			/* setup High Speed mode in relation with card capacity */
			if (ios->timing == MMC_TIMING_SD_HS)
				host->cfg_reg |= ATMCI_CFG_HSMODE;
			else
				host->cfg_reg &= ~ATMCI_CFG_HSMODE;
		}

		if (list_empty(&host->queue)) {
			atmci_writel(host, ATMCI_MR, host->mode_reg);
			if (host->caps.has_cfg_reg)
				atmci_writel(host, ATMCI_CFG, host->cfg_reg);
		} else {
			host->need_clock_update = true;
		}

		spin_unlock_bh(&host->lock);
	} else {
		bool any_slot_active = false;

		spin_lock_bh(&host->lock);
		slot->clock = 0;
		for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
			if (host->slot[i] && host->slot[i]->clock) {
				any_slot_active = true;
				break;
			}
		}
		if (!any_slot_active) {
			atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIDIS);
			if (host->mode_reg) {
				atmci_readl(host, ATMCI_MR);
			}
			host->mode_reg = 0;
		}
		spin_unlock_bh(&host->lock);
	}

	switch (ios->power_mode) {
	case MMC_POWER_OFF:
		if (!IS_ERR(mmc->supply.vmmc))
			mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
		break;
	case MMC_POWER_UP:
		set_bit(ATMCI_CARD_NEED_INIT, &slot->flags);
		if (!IS_ERR(mmc->supply.vmmc))
			mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd);
		break;
	default:
		break;
	}
}