+ cd_pin = sunxi_mmc_getcd_gpio(sdc_no);
+ if (cd_pin != -1) {
+ ret = gpio_request(cd_pin, "mmc_cd");
+ if (!ret)
+ ret = gpio_direction_input(cd_pin);
+ }
+
+ return ret;
+}
+
+static int mmc_set_mod_clk(struct sunxi_mmc_host *mmchost, unsigned int hz)
+{
+ unsigned int pll, pll_hz, div, n, oclk_dly, sclk_dly;
+
+ if (hz <= 24000000) {
+ pll = CCM_MMC_CTRL_OSCM24;
+ pll_hz = 24000000;
+ } else {
+#ifdef CONFIG_MACH_SUN9I
+ pll = CCM_MMC_CTRL_PLL_PERIPH0;
+ pll_hz = clock_get_pll4_periph0();
+#else
+ pll = CCM_MMC_CTRL_PLL6;
+ pll_hz = clock_get_pll6();
+#endif
+ }
+
+ div = pll_hz / hz;
+ if (pll_hz % hz)
+ div++;
+
+ n = 0;
+ while (div > 16) {
+ n++;
+ div = (div + 1) / 2;
+ }
+
+ if (n > 3) {
+ printf("mmc %u error cannot set clock to %u\n",
+ mmchost->mmc_no, hz);
+ return -1;
+ }
+
+ /* determine delays */
+ if (hz <= 400000) {
+ oclk_dly = 0;
+ sclk_dly = 7;
+ } else if (hz <= 25000000) {
+ oclk_dly = 0;
+ sclk_dly = 5;
+ } else if (hz <= 50000000) {
+ oclk_dly = 3;
+ sclk_dly = 5;
+ } else {
+ /* hz > 50000000 */
+ oclk_dly = 2;
+ sclk_dly = 4;
+ }
+
+ writel(CCM_MMC_CTRL_ENABLE | pll | CCM_MMC_CTRL_SCLK_DLY(sclk_dly) |
+ CCM_MMC_CTRL_N(n) | CCM_MMC_CTRL_OCLK_DLY(oclk_dly) |
+ CCM_MMC_CTRL_M(div), mmchost->mclkreg);
+
+ debug("mmc %u set mod-clk req %u parent %u n %u m %u rate %u\n",
+ mmchost->mmc_no, hz, pll_hz, 1u << n, div,
+ pll_hz / (1u << n) / div);
+