
From: Chee Hong Ang chee.hong.ang@intel.com
MMC driver now access System Manager's SDMMC control register to set SDMMC's clock phase shift via 'altera_sysmgr' driver.
Following entry need to be specified under MMC node in device tree: altr,sysmgr-syscon = <&sysmgr 'x' 'y' 'z'>;
x = offset of the SDMCC control register in System Manager y = start of drvsel's bit field z = start of smplsel's bit field
Example: altr,sysmgr-syscon = <&sysmgr 0x28 0 4>;
Signed-off-by: Chee Hong Ang chee.hong.ang@intel.com --- drivers/mmc/socfpga_dw_mmc.c | 63 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 5 deletions(-)
diff --git a/drivers/mmc/socfpga_dw_mmc.c b/drivers/mmc/socfpga_dw_mmc.c index 786cdc7..4a9627b 100644 --- a/drivers/mmc/socfpga_dw_mmc.c +++ b/drivers/mmc/socfpga_dw_mmc.c @@ -5,16 +5,17 @@
#include <common.h> #include <asm/arch/clock_manager.h> -#include <asm/arch/system_manager.h> #include <clk.h> #include <dm.h> #include <dwmmc.h> #include <errno.h> #include <fdtdec.h> +#include <hang.h> #include <dm/device_compat.h> #include <linux/libfdt.h> #include <linux/err.h> #include <malloc.h> +#include <misc.h> #include <reset.h>
DECLARE_GLOBAL_DATA_PTR; @@ -24,6 +25,13 @@ struct socfpga_dwmci_plat { struct mmc mmc; };
+/* System Manager's SDMMC CCLK phase shift register */ +struct sysmgr_sdmmc_reg { + u32 offset; + u32 drvsel_shift; + u32 smplsel_shift; +}; + /* socfpga implmentation specific driver private data */ struct dwmci_socfpga_priv_data { struct dwmci_host host; @@ -45,11 +53,54 @@ static void socfpga_dwmci_reset(struct udevice *dev) reset_deassert_bulk(&reset_bulk); }
+static int get_sysmgr_sdmmc_reg(struct udevice *dev, + struct sysmgr_sdmmc_reg *reg) +{ + struct ofnode_phandle_args args; + + int ret = dev_read_phandle_with_args(dev, "altr,sysmgr-syscon", NULL, + 3, 0, &args); + if (ret) { + dev_err(dev, "Failed to get syscon: %d\n", ret); + return -EINVAL; + } + + if (args.args_count != 3) { + dev_err(dev, "Invalid number of syscon args\n"); + return -EINVAL; + } + + reg->offset = args.args[0]; + reg->drvsel_shift = args.args[1]; + reg->smplsel_shift = args.args[2]; + + return 0; +} + static void socfpga_dwmci_clksel(struct dwmci_host *host) { struct dwmci_socfpga_priv_data *priv = host->priv; - u32 sdmmc_mask = ((priv->smplsel & 0x7) << SYSMGR_SDMMC_SMPLSEL_SHIFT) | - ((priv->drvsel & 0x7) << SYSMGR_SDMMC_DRVSEL_SHIFT); + struct sysmgr_sdmmc_reg sdmmc_reg; + struct udevice *sysmgr; + u32 sdmmc_mask; + + int ret = uclass_get_device_by_phandle(UCLASS_MISC, host->mmc->dev, + "altr,sysmgr-syscon", &sysmgr); + + if (ret == -ENOENT) { + debug("%s: Could not find 'altr,sysmgr-syscon' phandle\n", + host->mmc->dev->name); + hang(); + } + + if (get_sysmgr_sdmmc_reg(host->mmc->dev, &sdmmc_reg)) { + debug("%s: Error reading sysmgr sdmmc reg info\n", + host->mmc->dev->name); + hang(); + } + + sdmmc_mask = ((priv->smplsel & 0x7) << sdmmc_reg.smplsel_shift) | + ((priv->drvsel & 0x7) << sdmmc_reg.drvsel_shift);
/* Disable SDMMC clock. */ clrbits_le32(socfpga_get_clkmgr_addr() + CLKMGR_PERPLL_EN, @@ -57,10 +108,12 @@ static void socfpga_dwmci_clksel(struct dwmci_host *host)
debug("%s: drvsel %d smplsel %d\n", __func__, priv->drvsel, priv->smplsel); - writel(sdmmc_mask, socfpga_get_sysmgr_addr() + SYSMGR_SDMMC); + + misc_write(sysmgr, sdmmc_reg.offset, &sdmmc_mask, sizeof(sdmmc_mask)); + misc_read(sysmgr, sdmmc_reg.offset, &sdmmc_mask, sizeof(sdmmc_mask));
debug("%s: SYSMGR_SDMMCGRP_CTRL_REG = 0x%x\n", __func__, - readl(socfpga_get_sysmgr_addr() + SYSMGR_SDMMC)); + sdmmc_mask);
/* Enable SDMMC clock */ setbits_le32(socfpga_get_clkmgr_addr() + CLKMGR_PERPLL_EN,