diff options
Diffstat (limited to 'drivers/mmc/host/sdhci-s3c.c')
| -rw-r--r-- | drivers/mmc/host/sdhci-s3c.c | 308 | 
1 files changed, 104 insertions, 204 deletions
diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index 6debda95215..fa5954a0544 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -33,9 +33,6 @@  #define MAX_BUS_CLK	(4) -/* Number of gpio's used is max data bus width + command and clock lines */ -#define NUM_GPIOS(x)	(x + 2) -  /**   * struct sdhci_s3c - S3C SDHCI instance   * @host: The SDHCI host created @@ -51,12 +48,15 @@ struct sdhci_s3c {  	struct platform_device	*pdev;  	struct resource		*ioarea;  	struct s3c_sdhci_platdata *pdata; -	unsigned int		cur_clk; +	int			cur_clk;  	int			ext_cd_irq;  	int			ext_cd_gpio;  	struct clk		*clk_io;  	struct clk		*clk_bus[MAX_BUS_CLK]; +	unsigned long		clk_rates[MAX_BUS_CLK]; + +	bool			no_divider;  };  /** @@ -69,6 +69,7 @@ struct sdhci_s3c {   */  struct sdhci_s3c_drv_data {  	unsigned int	sdhci_quirks; +	bool		no_divider;  };  static inline struct sdhci_s3c *to_s3c(struct sdhci_host *host) @@ -77,32 +78,6 @@ static inline struct sdhci_s3c *to_s3c(struct sdhci_host *host)  }  /** - * get_curclk - convert ctrl2 register to clock source number - * @ctrl2: Control2 register value. - */ -static u32 get_curclk(u32 ctrl2) -{ -	ctrl2 &= S3C_SDHCI_CTRL2_SELBASECLK_MASK; -	ctrl2 >>= S3C_SDHCI_CTRL2_SELBASECLK_SHIFT; - -	return ctrl2; -} - -static void sdhci_s3c_check_sclk(struct sdhci_host *host) -{ -	struct sdhci_s3c *ourhost = to_s3c(host); -	u32 tmp = readl(host->ioaddr + S3C_SDHCI_CONTROL2); - -	if (get_curclk(tmp) != ourhost->cur_clk) { -		dev_dbg(&ourhost->pdev->dev, "restored ctrl2 clock setting\n"); - -		tmp &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK; -		tmp |= ourhost->cur_clk << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT; -		writel(tmp, host->ioaddr + S3C_SDHCI_CONTROL2); -	} -} - -/**   * sdhci_s3c_get_max_clk - callback to get maximum clock frequency.   * @host: The SDHCI host instance.   * @@ -111,20 +86,11 @@ static void sdhci_s3c_check_sclk(struct sdhci_host *host)  static unsigned int sdhci_s3c_get_max_clk(struct sdhci_host *host)  {  	struct sdhci_s3c *ourhost = to_s3c(host); -	struct clk *busclk; -	unsigned int rate, max; -	int clk; - -	/* note, a reset will reset the clock source */ - -	sdhci_s3c_check_sclk(host); - -	for (max = 0, clk = 0; clk < MAX_BUS_CLK; clk++) { -		busclk = ourhost->clk_bus[clk]; -		if (!busclk) -			continue; +	unsigned long rate, max = 0; +	int src; -		rate = clk_get_rate(busclk); +	for (src = 0; src < MAX_BUS_CLK; src++) { +		rate = ourhost->clk_rates[src];  		if (rate > max)  			max = rate;  	} @@ -144,31 +110,38 @@ static unsigned int sdhci_s3c_consider_clock(struct sdhci_s3c *ourhost,  {  	unsigned long rate;  	struct clk *clksrc = ourhost->clk_bus[src]; -	int div; +	int shift; -	if (!clksrc) +	if (IS_ERR(clksrc))  		return UINT_MAX;  	/*  	 * If controller uses a non-standard clock division, find the best clock  	 * speed possible with selected clock source and skip the division.  	 */ -	if (ourhost->host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) { +	if (ourhost->no_divider) {  		rate = clk_round_rate(clksrc, wanted);  		return wanted - rate;  	} -	rate = clk_get_rate(clksrc); +	rate = ourhost->clk_rates[src]; -	for (div = 1; div < 256; div *= 2) { -		if ((rate / div) <= wanted) +	for (shift = 0; shift <= 8; ++shift) { +		if ((rate >> shift) <= wanted)  			break;  	} +	if (shift > 8) { +		dev_dbg(&ourhost->pdev->dev, +			"clk %d: rate %ld, min rate %lu > wanted %u\n", +			src, rate, rate / 256, wanted); +		return UINT_MAX; +	} +  	dev_dbg(&ourhost->pdev->dev, "clk %d: rate %ld, want %d, got %ld\n", -		src, rate, wanted, rate / div); +		src, rate, wanted, rate >> shift); -	return wanted - (rate / div); +	return wanted - (rate >> shift);  }  /** @@ -188,9 +161,13 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock)  	int src;  	u32 ctrl; +	host->mmc->actual_clock = 0; +  	/* don't bother if the clock is going off. */ -	if (clock == 0) +	if (clock == 0) { +		sdhci_set_clock(host, clock);  		return; +	}  	for (src = 0; src < MAX_BUS_CLK; src++) {  		delta = sdhci_s3c_consider_clock(ourhost, src, clock); @@ -209,20 +186,22 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock)  		struct clk *clk = ourhost->clk_bus[best_src];  		clk_prepare_enable(clk); -		clk_disable_unprepare(ourhost->clk_bus[ourhost->cur_clk]); - -		/* turn clock off to card before changing clock source */ -		writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL); +		if (ourhost->cur_clk >= 0) +			clk_disable_unprepare( +					ourhost->clk_bus[ourhost->cur_clk]);  		ourhost->cur_clk = best_src; -		host->max_clk = clk_get_rate(clk); - -		ctrl = readl(host->ioaddr + S3C_SDHCI_CONTROL2); -		ctrl &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK; -		ctrl |= best_src << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT; -		writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL2); +		host->max_clk = ourhost->clk_rates[best_src];  	} +	/* turn clock off to card before changing clock source */ +	writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL); + +	ctrl = readl(host->ioaddr + S3C_SDHCI_CONTROL2); +	ctrl &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK; +	ctrl |= best_src << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT; +	writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL2); +  	/* reprogram default hardware configuration */  	writel(S3C64XX_SDHCI_CONTROL4_DRIVE_9mA,  		host->ioaddr + S3C64XX_SDHCI_CONTROL4); @@ -240,6 +219,8 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock)  	if (clock < 25 * 1000000)  		ctrl |= (S3C_SDHCI_CTRL3_FCSEL3 | S3C_SDHCI_CTRL3_FCSEL2);  	writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL3); + +	sdhci_set_clock(host, clock);  }  /** @@ -254,17 +235,17 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock)  static unsigned int sdhci_s3c_get_min_clock(struct sdhci_host *host)  {  	struct sdhci_s3c *ourhost = to_s3c(host); -	unsigned int delta, min = UINT_MAX; +	unsigned long rate, min = ULONG_MAX;  	int src;  	for (src = 0; src < MAX_BUS_CLK; src++) { -		delta = sdhci_s3c_consider_clock(ourhost, src, 0); -		if (delta == UINT_MAX) +		rate = ourhost->clk_rates[src] / 256; +		if (!rate)  			continue; -		/* delta is a negative value in this case */ -		if (-delta < min) -			min = -delta; +		if (rate < min) +			min = rate;  	} +  	return min;  } @@ -272,20 +253,44 @@ static unsigned int sdhci_s3c_get_min_clock(struct sdhci_host *host)  static unsigned int sdhci_cmu_get_max_clock(struct sdhci_host *host)  {  	struct sdhci_s3c *ourhost = to_s3c(host); +	unsigned long rate, max = 0; +	int src; -	return clk_round_rate(ourhost->clk_bus[ourhost->cur_clk], UINT_MAX); +	for (src = 0; src < MAX_BUS_CLK; src++) { +		struct clk *clk; + +		clk = ourhost->clk_bus[src]; +		if (IS_ERR(clk)) +			continue; + +		rate = clk_round_rate(clk, ULONG_MAX); +		if (rate > max) +			max = rate; +	} + +	return max;  }  /* sdhci_cmu_get_min_clock - callback to get minimal supported clock value. */  static unsigned int sdhci_cmu_get_min_clock(struct sdhci_host *host)  {  	struct sdhci_s3c *ourhost = to_s3c(host); +	unsigned long rate, min = ULONG_MAX; +	int src; -	/* -	 * initial clock can be in the frequency range of -	 * 100KHz-400KHz, so we set it as max value. -	 */ -	return clk_round_rate(ourhost->clk_bus[ourhost->cur_clk], 400000); +	for (src = 0; src < MAX_BUS_CLK; src++) { +		struct clk *clk; + +		clk = ourhost->clk_bus[src]; +		if (IS_ERR(clk)) +			continue; + +		rate = clk_round_rate(clk, 0); +		if (rate < min) +			min = rate; +	} + +	return min;  }  /* sdhci_cmu_set_clock - callback on clock change.*/ @@ -296,10 +301,11 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock)  	unsigned long timeout;  	u16 clk = 0; +	host->mmc->actual_clock = 0; +  	/* If the clock is going off, set to 0 at clock control register */  	if (clock == 0) {  		sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); -		host->clock = clock;  		return;  	} @@ -307,8 +313,6 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock)  	clk_set_rate(ourhost->clk_bus[ourhost->cur_clk], clock); -	host->clock = clock; -  	clk = SDHCI_CLOCK_INT_EN;  	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); @@ -330,14 +334,14 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock)  }  /** - * sdhci_s3c_platform_bus_width - support 8bit buswidth + * sdhci_s3c_set_bus_width - support 8bit buswidth   * @host: The SDHCI host being queried   * @width: MMC_BUS_WIDTH_ macro for the bus width being requested   *   * We have 8-bit width support but is not a v3 controller.   * So we add platform_bus_width() and support 8bit width.   */ -static int sdhci_s3c_platform_bus_width(struct sdhci_host *host, int width) +static void sdhci_s3c_set_bus_width(struct sdhci_host *host, int width)  {  	u8 ctrl; @@ -359,93 +363,23 @@ static int sdhci_s3c_platform_bus_width(struct sdhci_host *host, int width)  	}  	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); - -	return 0;  }  static struct sdhci_ops sdhci_s3c_ops = {  	.get_max_clock		= sdhci_s3c_get_max_clk,  	.set_clock		= sdhci_s3c_set_clock,  	.get_min_clock		= sdhci_s3c_get_min_clock, -	.platform_bus_width	= sdhci_s3c_platform_bus_width, +	.set_bus_width		= sdhci_s3c_set_bus_width, +	.reset			= sdhci_reset, +	.set_uhs_signaling	= sdhci_set_uhs_signaling,  }; -static void sdhci_s3c_notify_change(struct platform_device *dev, int state) -{ -	struct sdhci_host *host = platform_get_drvdata(dev); -#ifdef CONFIG_PM_RUNTIME -	struct sdhci_s3c *sc = sdhci_priv(host); -#endif -	unsigned long flags; - -	if (host) { -		spin_lock_irqsave(&host->lock, flags); -		if (state) { -			dev_dbg(&dev->dev, "card inserted.\n"); -#ifdef CONFIG_PM_RUNTIME -			clk_prepare_enable(sc->clk_io); -#endif -			host->flags &= ~SDHCI_DEVICE_DEAD; -			host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; -		} else { -			dev_dbg(&dev->dev, "card removed.\n"); -			host->flags |= SDHCI_DEVICE_DEAD; -			host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; -#ifdef CONFIG_PM_RUNTIME -			clk_disable_unprepare(sc->clk_io); -#endif -		} -		tasklet_schedule(&host->card_tasklet); -		spin_unlock_irqrestore(&host->lock, flags); -	} -} - -static irqreturn_t sdhci_s3c_gpio_card_detect_thread(int irq, void *dev_id) -{ -	struct sdhci_s3c *sc = dev_id; -	int status = gpio_get_value(sc->ext_cd_gpio); -	if (sc->pdata->ext_cd_gpio_invert) -		status = !status; -	sdhci_s3c_notify_change(sc->pdev, status); -	return IRQ_HANDLED; -} - -static void sdhci_s3c_setup_card_detect_gpio(struct sdhci_s3c *sc) -{ -	struct s3c_sdhci_platdata *pdata = sc->pdata; -	struct device *dev = &sc->pdev->dev; - -	if (devm_gpio_request(dev, pdata->ext_cd_gpio, "SDHCI EXT CD") == 0) { -		sc->ext_cd_gpio = pdata->ext_cd_gpio; -		sc->ext_cd_irq = gpio_to_irq(pdata->ext_cd_gpio); -		if (sc->ext_cd_irq && -		    request_threaded_irq(sc->ext_cd_irq, NULL, -					 sdhci_s3c_gpio_card_detect_thread, -					 IRQF_TRIGGER_RISING | -					 IRQF_TRIGGER_FALLING | -					 IRQF_ONESHOT, -					 dev_name(dev), sc) == 0) { -			int status = gpio_get_value(sc->ext_cd_gpio); -			if (pdata->ext_cd_gpio_invert) -				status = !status; -			sdhci_s3c_notify_change(sc->pdev, status); -		} else { -			dev_warn(dev, "cannot request irq for card detect\n"); -			sc->ext_cd_irq = 0; -		} -	} else { -		dev_err(dev, "cannot request gpio for card detect\n"); -	} -} -  #ifdef CONFIG_OF  static int sdhci_s3c_parse_dt(struct device *dev,  		struct sdhci_host *host, struct s3c_sdhci_platdata *pdata)  {  	struct device_node *node = dev->of_node; -	struct sdhci_s3c *ourhost = to_s3c(host);  	u32 max_width; -	int gpio;  	/* if the bus-width property is not specified, assume width as 1 */  	if (of_property_read_u32(node, "bus-width", &max_width)) @@ -463,18 +397,8 @@ static int sdhci_s3c_parse_dt(struct device *dev,  		return 0;  	} -	gpio = of_get_named_gpio(node, "cd-gpios", 0); -	if (gpio_is_valid(gpio)) { -		pdata->cd_type = S3C_SDHCI_CD_GPIO; -		pdata->ext_cd_gpio = gpio; -		ourhost->ext_cd_gpio = -1; -		if (of_get_property(node, "cd-inverted", NULL)) -			pdata->ext_cd_gpio_invert = 1; +	if (of_get_named_gpio(node, "cd-gpios", 0))  		return 0; -	} else if (gpio != -ENOENT) { -		dev_err(dev, "invalid card detect gpio specified\n"); -		return -EINVAL; -	}  	/* assuming internal card detect that will be configured by pinctrl */  	pdata->cd_type = S3C_SDHCI_CD_INTERNAL; @@ -552,6 +476,7 @@ static int sdhci_s3c_probe(struct platform_device *pdev)  	sc->host = host;  	sc->pdev = pdev;  	sc->pdata = pdata; +	sc->cur_clk = -1;  	platform_set_drvdata(pdev, host); @@ -566,25 +491,18 @@ static int sdhci_s3c_probe(struct platform_device *pdev)  	clk_prepare_enable(sc->clk_io);  	for (clks = 0, ptr = 0; ptr < MAX_BUS_CLK; ptr++) { -		struct clk *clk;  		char name[14];  		snprintf(name, 14, "mmc_busclk.%d", ptr); -		clk = devm_clk_get(dev, name); -		if (IS_ERR(clk)) +		sc->clk_bus[ptr] = devm_clk_get(dev, name); +		if (IS_ERR(sc->clk_bus[ptr]))  			continue;  		clks++; -		sc->clk_bus[ptr] = clk; - -		/* -		 * save current clock index to know which clock bus -		 * is used later in overriding functions. -		 */ -		sc->cur_clk = ptr; +		sc->clk_rates[ptr] = clk_get_rate(sc->clk_bus[ptr]);  		dev_info(dev, "clock source %d: %s (%ld Hz)\n", -			 ptr, name, clk_get_rate(clk)); +				ptr, name, sc->clk_rates[ptr]);  	}  	if (clks == 0) { @@ -593,10 +511,6 @@ static int sdhci_s3c_probe(struct platform_device *pdev)  		goto err_no_busclks;  	} -#ifndef CONFIG_PM_RUNTIME -	clk_prepare_enable(sc->clk_bus[sc->cur_clk]); -#endif -  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  	host->ioaddr = devm_ioremap_resource(&pdev->dev, res);  	if (IS_ERR(host->ioaddr)) { @@ -617,8 +531,10 @@ static int sdhci_s3c_probe(struct platform_device *pdev)  	/* Setup quirks for the controller */  	host->quirks |= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC;  	host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT; -	if (drv_data) +	if (drv_data) {  		host->quirks |= drv_data->sdhci_quirks; +		sc->no_divider = drv_data->no_divider; +	}  #ifndef CONFIG_MMC_SDHCI_S3C_DMA @@ -667,7 +583,7 @@ static int sdhci_s3c_probe(struct platform_device *pdev)  	 * If controller does not have internal clock divider,  	 * we can use overriding functions instead of default.  	 */ -	if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) { +	if (sc->no_divider) {  		sdhci_s3c_ops.set_clock = sdhci_cmu_set_clock;  		sdhci_s3c_ops.get_min_clock = sdhci_cmu_get_min_clock;  		sdhci_s3c_ops.get_max_clock = sdhci_cmu_get_max_clock; @@ -685,6 +601,8 @@ static int sdhci_s3c_probe(struct platform_device *pdev)  	pm_runtime_use_autosuspend(&pdev->dev);  	pm_suspend_ignore_children(&pdev->dev, 1); +	mmc_of_parse(host->mmc); +  	ret = sdhci_add_host(host);  	if (ret) {  		dev_err(dev, "sdhci_add_host() failed\n"); @@ -693,15 +611,6 @@ static int sdhci_s3c_probe(struct platform_device *pdev)  		goto err_req_regs;  	} -	/* The following two methods of card detection might call -	   sdhci_s3c_notify_change() immediately, so they can be called -	   only after sdhci_add_host(). Setup errors are ignored. */ -	if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_init) -		pdata->ext_cd_init(&sdhci_s3c_notify_change); -	if (pdata->cd_type == S3C_SDHCI_CD_GPIO && -	    gpio_is_valid(pdata->ext_cd_gpio)) -		sdhci_s3c_setup_card_detect_gpio(sc); -  #ifdef CONFIG_PM_RUNTIME  	if (pdata->cd_type != S3C_SDHCI_CD_INTERNAL)  		clk_disable_unprepare(sc->clk_io); @@ -709,10 +618,6 @@ static int sdhci_s3c_probe(struct platform_device *pdev)  	return 0;   err_req_regs: -#ifndef CONFIG_PM_RUNTIME -	clk_disable_unprepare(sc->clk_bus[sc->cur_clk]); -#endif -   err_no_busclks:  	clk_disable_unprepare(sc->clk_io); @@ -726,16 +631,12 @@ static int sdhci_s3c_remove(struct platform_device *pdev)  {  	struct sdhci_host *host =  platform_get_drvdata(pdev);  	struct sdhci_s3c *sc = sdhci_priv(host); -	struct s3c_sdhci_platdata *pdata = sc->pdata; - -	if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_cleanup) -		pdata->ext_cd_cleanup(&sdhci_s3c_notify_change);  	if (sc->ext_cd_irq)  		free_irq(sc->ext_cd_irq, sc);  #ifdef CONFIG_PM_RUNTIME -	if (pdata->cd_type != S3C_SDHCI_CD_INTERNAL) +	if (sc->pdata->cd_type != S3C_SDHCI_CD_INTERNAL)  		clk_prepare_enable(sc->clk_io);  #endif  	sdhci_remove_host(host, 1); @@ -743,9 +644,6 @@ static int sdhci_s3c_remove(struct platform_device *pdev)  	pm_runtime_dont_use_autosuspend(&pdev->dev);  	pm_runtime_disable(&pdev->dev); -#ifndef CONFIG_PM_RUNTIME -	clk_disable_unprepare(sc->clk_bus[sc->cur_clk]); -#endif  	clk_disable_unprepare(sc->clk_io);  	sdhci_free_host(host); @@ -779,7 +677,8 @@ static int sdhci_s3c_runtime_suspend(struct device *dev)  	ret = sdhci_runtime_suspend_host(host); -	clk_disable_unprepare(ourhost->clk_bus[ourhost->cur_clk]); +	if (ourhost->cur_clk >= 0) +		clk_disable_unprepare(ourhost->clk_bus[ourhost->cur_clk]);  	clk_disable_unprepare(busclk);  	return ret;  } @@ -792,7 +691,8 @@ static int sdhci_s3c_runtime_resume(struct device *dev)  	int ret;  	clk_prepare_enable(busclk); -	clk_prepare_enable(ourhost->clk_bus[ourhost->cur_clk]); +	if (ourhost->cur_clk >= 0) +		clk_prepare_enable(ourhost->clk_bus[ourhost->cur_clk]);  	ret = sdhci_runtime_resume_host(host);  	return ret;  } @@ -813,7 +713,7 @@ static const struct dev_pm_ops sdhci_s3c_pmops = {  #if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS4212)  static struct sdhci_s3c_drv_data exynos4_sdhci_drv_data = { -	.sdhci_quirks = SDHCI_QUIRK_NONSTANDARD_CLOCK, +	.no_divider = true,  };  #define EXYNOS4_SDHCI_DRV_DATA ((kernel_ulong_t)&exynos4_sdhci_drv_data)  #else  | 
