diff -u -N -r stblinux-3.14-1.8/drivers/clk/clk.c stblinux-3.14-1.10-git/drivers/clk/clk.c --- stblinux-3.14-1.8/drivers/clk/clk.c 2015-07-28 23:57:38.000000000 +0000 +++ stblinux-3.14-1.10-git/drivers/clk/clk.c 2019-02-10 22:05:52.950336124 +0000 @@ -929,6 +929,9 @@ if (clk->prepare_count == 0) { if (clk->flags & CLK_IS_SW) for (i = 0; i < clk->num_parents; i++) { + if (!clk->parents[i]) + clk->parents[i] = + __clk_lookup(clk->parent_names[i]); ret = __clk_prepare(clk->parents[i]); if (ret) { for (j = i - 1; j >= 0; j--) @@ -1042,6 +1045,9 @@ if (clk->enable_count == 0) { if (clk->flags & CLK_IS_SW) for (i = 0; i < clk->num_parents && !ret; i++) { + if (!clk->parents[i]) + clk->parents[i] = + __clk_lookup(clk->parent_names[i]); ret = __clk_enable(clk->parents[i]); if (ret) { for (j = i - 1; j >= 0; j--) diff -u -N -r stblinux-3.14-1.8/drivers/mmc/core/mmc.c stblinux-3.14-1.10-git/drivers/mmc/core/mmc.c --- stblinux-3.14-1.8/drivers/mmc/core/mmc.c 2015-07-28 23:57:39.000000000 +0000 +++ stblinux-3.14-1.10-git/drivers/mmc/core/mmc.c 2019-02-10 22:05:52.950336124 +0000 @@ -293,13 +293,12 @@ } } + /* + * The EXT_CSD format is meant to be forward compatible. As long + * as CSD_STRUCTURE does not change, all values for EXT_CSD_REV + * are authorized, see JEDEC JESD84-B50 section B.8. + */ card->ext_csd.rev = ext_csd[EXT_CSD_REV]; - if (card->ext_csd.rev > 7) { - pr_err("%s: unrecognised EXT_CSD revision %d\n", - mmc_hostname(card->host), card->ext_csd.rev); - err = -EINVAL; - goto out; - } card->ext_csd.raw_sectors[0] = ext_csd[EXT_CSD_SEC_CNT + 0]; card->ext_csd.raw_sectors[1] = ext_csd[EXT_CSD_SEC_CNT + 1]; diff -u -N -r stblinux-3.14-1.8/drivers/mmc/core/sd.c stblinux-3.14-1.10-git/drivers/mmc/core/sd.c --- stblinux-3.14-1.8/drivers/mmc/core/sd.c 2015-07-28 23:57:39.000000000 +0000 +++ stblinux-3.14-1.10-git/drivers/mmc/core/sd.c 2019-02-10 22:05:52.950336124 +0000 @@ -389,18 +389,9 @@ { int host_drv_type = SD_DRIVER_TYPE_B; int card_drv_type = SD_DRIVER_TYPE_B; - int drive_strength; + int drive_strength, drv_type; int err; - /* - * If the host doesn't support any of the Driver Types A,C or D, - * or there is no board specific handler then default Driver - * Type B is used. - */ - if (!(card->host->caps & (MMC_CAP_DRIVER_TYPE_A | MMC_CAP_DRIVER_TYPE_C - | MMC_CAP_DRIVER_TYPE_D))) - return 0; - if (!card->host->ops->select_drive_strength) return 0; @@ -429,22 +420,24 @@ * return what is possible given the options */ mmc_host_clk_hold(card->host); - drive_strength = card->host->ops->select_drive_strength( + drive_strength = card->host->ops->select_drive_strength(card, card->sw_caps.uhs_max_dtr, - host_drv_type, card_drv_type); + host_drv_type, card_drv_type, &drv_type); mmc_host_clk_release(card->host); - err = mmc_sd_switch(card, 1, 2, drive_strength, status); - if (err) - return err; - - if ((status[15] & 0xF) != drive_strength) { - pr_warning("%s: Problem setting drive strength!\n", - mmc_hostname(card->host)); - return 0; + if (drive_strength) { + err = mmc_sd_switch(card, 1, 2, drive_strength, status); + if (err) + return err; + if ((status[15] & 0xF) != drive_strength) { + pr_warn("%s: Problem setting drive strength!\n", + mmc_hostname(card->host)); + return 0; + } } - mmc_set_driver_type(card->host, drive_strength); + if (drv_type) + mmc_set_driver_type(card->host, drv_type); return 0; } diff -u -N -r stblinux-3.14-1.8/drivers/mmc/core/sdio.c stblinux-3.14-1.10-git/drivers/mmc/core/sdio.c --- stblinux-3.14-1.8/drivers/mmc/core/sdio.c 2015-07-28 23:57:39.000000000 +0000 +++ stblinux-3.14-1.10-git/drivers/mmc/core/sdio.c 2019-02-10 22:05:52.950336124 +0000 @@ -401,21 +401,10 @@ { int host_drv_type = SD_DRIVER_TYPE_B; int card_drv_type = SD_DRIVER_TYPE_B; - int drive_strength; + int drive_strength, drv_type; unsigned char card_strength; int err; - /* - * If the host doesn't support any of the Driver Types A,C or D, - * or there is no board specific handler then default Driver - * Type B is used. - */ - if (!(card->host->caps & - (MMC_CAP_DRIVER_TYPE_A | - MMC_CAP_DRIVER_TYPE_C | - MMC_CAP_DRIVER_TYPE_D))) - return; - if (!card->host->ops->select_drive_strength) return; @@ -443,25 +432,29 @@ * information and let the hardware specific code * return what is possible given the options */ - drive_strength = card->host->ops->select_drive_strength( + drive_strength = card->host->ops->select_drive_strength(card, card->sw_caps.uhs_max_dtr, - host_drv_type, card_drv_type); - - /* if error just use default for drive strength B */ - err = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_DRIVE_STRENGTH, 0, - &card_strength); - if (err) - return; - - card_strength &= ~(SDIO_DRIVE_DTSx_MASK<host, drive_strength); + if (drv_type) + mmc_set_driver_type(card->host, drv_type); } diff -u -N -r stblinux-3.14-1.8/drivers/mmc/host/sdhci-brcmstb.c stblinux-3.14-1.10-git/drivers/mmc/host/sdhci-brcmstb.c --- stblinux-3.14-1.8/drivers/mmc/host/sdhci-brcmstb.c 2015-07-28 23:57:39.000000000 +0000 +++ stblinux-3.14-1.10-git/drivers/mmc/host/sdhci-brcmstb.c 2019-02-10 22:06:28.132824631 +0000 @@ -31,6 +31,18 @@ #define SDIO_CFG_REG(x, y) (x + BCHP_SDIO_0_CFG_##y - \ BCHP_SDIO_0_CFG_REG_START) +struct sdhci_brcmstb_priv { + void __iomem *cfg_regs; + int host_driver_type; + int host_hs_driver_type; + int card_driver_type; +}; + +#define MASK_OFF_DRV (SDHCI_PRESET_SDCLK_FREQ_MASK | \ + SDHCI_PRESET_CLKGEN_SEL_MASK) + +static char *strength_type_to_string[] = {"B", "A", "C", "D"}; + #if defined(CONFIG_BCM74371A0) /* * HW7445-1183 @@ -47,27 +59,128 @@ writeb(val, host->ioaddr + reg); } +/* We don't support drive strength override on chips that use the + * old version of the SDIO core. + */ +static void set_host_driver_strength_overrides( + struct sdhci_host *host, + struct sdhci_brcmstb_priv *priv) +{ +} + +#else /* CONFIG_BCM74371A0 */ + +static void set_host_driver_strength_overrides( + struct sdhci_host *host, + struct sdhci_brcmstb_priv *priv) +{ + u16 strength; + u16 sdr25; + u16 sdr50; + u16 ddr50; + u16 sdr104; + u32 val; + u32 cfg_base = (u32)priv->cfg_regs; + + if (priv->host_driver_type) { + dev_info(mmc_dev(host->mmc), + "Changing UHS Host Driver TYPE Presets to TYPE %s\n", + strength_type_to_string[priv->host_driver_type]); + strength = (u16)priv->host_driver_type << 11; + sdr25 = sdhci_readw(host, + SDHCI_PRESET_FOR_SDR25) & MASK_OFF_DRV; + sdr50 = sdhci_readw(host, + SDHCI_PRESET_FOR_SDR50) & MASK_OFF_DRV; + ddr50 = sdhci_readw(host, + SDHCI_PRESET_FOR_DDR50) & MASK_OFF_DRV; + sdr104 = sdhci_readw(host, + SDHCI_PRESET_FOR_SDR104) & MASK_OFF_DRV; + val = (sdr25 | strength); + val |= ((u32)(sdr50 | strength)) << 16; + val |= 0x80000000; + DEV_WR(SDIO_CFG_REG(cfg_base, PRESET3), val); + val = (sdr104 | strength); + val |= ((u32)(ddr50 | strength)) << 16; + val |= 0x80000000; + DEV_WR(SDIO_CFG_REG(cfg_base, PRESET4), val); + } + + /* + * The Host Controller Specification states that the driver + * strength setting is only valid for UHS modes, but our + * host controller allows this setting to be used for HS modes + * as well. + */ + if (priv->host_hs_driver_type) { + u16 sdr12; + u16 hs; + + dev_info(mmc_dev(host->mmc), + "Changing HS Host Driver TYPE Presets to TYPE %s\n", + strength_type_to_string[priv->host_hs_driver_type]); + strength = (u16)priv->host_hs_driver_type << 11; + sdr12 = sdhci_readw(host, SDHCI_PRESET_FOR_SDR12) & + MASK_OFF_DRV; + hs = sdhci_readw(host, SDHCI_PRESET_FOR_HS) & MASK_OFF_DRV; + val = (hs | strength); + val |= ((u32)(sdr12 | strength)) << 16; + val |= 0x80000000; + DEV_WR(SDIO_CFG_REG(cfg_base, PRESET2), val); + } +} +#endif /* CONFIG_BCM74371A0 */ + +static int select_one_drive_strength(struct sdhci_host *host, int supported, + int requested, char *type) +{ + char strength_ok_msg[] = "Changing %s Driver to TYPE %s\n"; + char strength_err_msg[] = + "Request to change %s Driver to TYPE %s not supported by %s\n"; + if (supported & (1 << requested)) { + if (requested) + dev_info(mmc_dev(host->mmc), strength_ok_msg, type, + strength_type_to_string[requested], type); + return requested; + } else { + dev_warn(mmc_dev(host->mmc), strength_err_msg, type, + strength_type_to_string[requested], type); + return 0; + } +} + +static int sdhci_brcmstb_select_drive_strength(struct sdhci_host *host, + struct mmc_card *card, + unsigned int max_dtr, int host_drv, + int card_drv, int *drv_type) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host); + + *drv_type = select_one_drive_strength(host, host_drv, + priv->host_driver_type, "Host"); + return select_one_drive_strength(host, card_drv, + priv->card_driver_type, "Card"); +} + static struct sdhci_ops sdhci_brcmstb_ops = { - .write_b = sdhci_brcmstb_writeb, + .select_drive_strength = sdhci_brcmstb_select_drive_strength, }; -#endif static struct sdhci_pltfm_data sdhci_brcmstb_pdata = { }; #if defined(CONFIG_BCM3390A0) || defined(CONFIG_BCM7145B0) || \ defined(CONFIG_BCM7250B0) || defined(CONFIG_BCM7364A0) || \ - defined(CONFIG_BCM7439B0) || defined(CONFIG_BCM7445D0) -static int sdhci_override_caps(struct platform_device *pdev, - uint32_t cap0_setbits, - uint32_t cap0_clearbits, - uint32_t cap1_setbits, - uint32_t cap1_clearbits) + defined(CONFIG_BCM7445D0) +static void sdhci_override_caps(struct sdhci_host *host, + struct sdhci_brcmstb_priv *priv, + uint32_t cap0_setbits, + uint32_t cap0_clearbits, + uint32_t cap1_setbits, + uint32_t cap1_clearbits) { uint32_t val; - struct resource *iomem; - uintptr_t cfg_base; - struct sdhci_host *host = platform_get_drvdata(pdev); + void *cfg_base = priv->cfg_regs; /* * The CAP's override bits in the CFG registers default to all @@ -75,32 +188,33 @@ * CAPS registers and then modify the requested bits and write * them to the override CFG registers. */ - iomem = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!iomem) - return -EINVAL; - cfg_base = iomem->start; val = sdhci_readl(host, SDHCI_CAPABILITIES); val &= ~cap0_clearbits; val |= cap0_setbits; - BDEV_WR(SDIO_CFG_REG(cfg_base, CAP_REG0), val); + DEV_WR(SDIO_CFG_REG(cfg_base, CAP_REG0), val); val = sdhci_readl(host, SDHCI_CAPABILITIES_1); val &= ~cap1_clearbits; val |= cap1_setbits; - BDEV_WR(SDIO_CFG_REG(cfg_base, CAP_REG1), val); - BDEV_WR(SDIO_CFG_REG(cfg_base, CAP_REG_OVERRIDE), + DEV_WR(SDIO_CFG_REG(cfg_base, CAP_REG1), val); + DEV_WR(SDIO_CFG_REG(cfg_base, CAP_REG_OVERRIDE), BCHP_SDIO_0_CFG_CAP_REG_OVERRIDE_CAP_REG_OVERRIDE_MASK); - return 0; } -static int sdhci_fix_caps(struct platform_device *pdev) +static void sdhci_fix_caps(struct sdhci_host *host, + struct sdhci_brcmstb_priv *priv) { +#if defined(CONFIG_BCM7445D0) + /* Fixed for E0 and above */ + if (BRCM_CHIP_REV() >= 0x40) + return 0; +#endif /* Disable SDR50 support because tuning is broken. */ - return sdhci_override_caps(pdev, 0, 0, 0, SDHCI_SUPPORT_SDR50); + sdhci_override_caps(host, priv, 0, 0, 0, SDHCI_SUPPORT_SDR50); } #else -static int sdhci_fix_caps(struct platform_device *pdev) +static void sdhci_fix_caps(struct sdhci_host *host, + struct sdhci_brcmstb_priv *priv) { - return 0; } #endif @@ -123,11 +237,14 @@ { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host); int err; err = clk_enable(pltfm_host->clk); if (err) return err; + sdhci_fix_caps(host, priv); + set_host_driver_strength_overrides(host, priv); return sdhci_resume_host(host); } @@ -136,12 +253,34 @@ static SIMPLE_DEV_PM_OPS(sdhci_brcmstb_pmops, sdhci_brcmstb_suspend, sdhci_brcmstb_resume); +static void sdhci_brcmstb_of_get_driver_type(struct device_node *dn, + char *name, int *dtype) +{ + const char *driver_type; + int res; + + res = of_property_read_string(dn, name, &driver_type); + if (res == 0) { + if (strcmp(driver_type, "A") == 0) + *dtype = MMC_SET_DRIVER_TYPE_A; + else if (strcmp(driver_type, "B") == 0) + *dtype = MMC_SET_DRIVER_TYPE_B; + else if (strcmp(driver_type, "C") == 0) + *dtype = MMC_SET_DRIVER_TYPE_C; + else if (strcmp(driver_type, "D") == 0) + *dtype = MMC_SET_DRIVER_TYPE_D; + } +} + + static int sdhci_brcmstb_probe(struct platform_device *pdev) { struct device_node *dn = pdev->dev.of_node; struct sdhci_host *host; struct sdhci_pltfm_host *pltfm_host; + struct sdhci_brcmstb_priv *priv; struct clk *clk; + struct resource *resource; int res; clk = of_clk_get_by_name(dn, "sw_sdio"); @@ -153,27 +292,46 @@ if (res) goto undo_clk_get; -/* Only enable reset workaround for 7439a0 and 74371a0 senior */ #if defined(CONFIG_BCM74371A0) + /* Only enable reset workaround for 74371a0 senior */ if (BRCM_CHIP_ID() == 0x7439) - sdhci_brcmstb_pdata.ops = &sdhci_brcmstb_ops; -#endif - host = sdhci_pltfm_init(pdev, &sdhci_brcmstb_pdata, 0); + sdhci_brcmstb_ops.write_b = sdhci_brcmstb_writeb; +#endif /* CONFIG_BCM74371A0 */ + sdhci_brcmstb_pdata.ops = &sdhci_brcmstb_ops; + host = sdhci_pltfm_init(pdev, &sdhci_brcmstb_pdata, + sizeof(struct sdhci_brcmstb_priv)); if (IS_ERR(host)) { res = PTR_ERR(host); goto undo_clk_prep; } sdhci_get_of_property(pdev); mmc_of_parse(host->mmc); - res = sdhci_fix_caps(pdev); - if (res) - goto undo_pltfm_init; + pltfm_host = sdhci_priv(host); + priv = sdhci_pltfm_priv(pltfm_host); + resource = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (resource == NULL) { + dev_err(&pdev->dev, "can't get SDHCI CFG base address\n"); + return -EINVAL; + } + priv->cfg_regs = devm_request_and_ioremap(&pdev->dev, resource); + if (!priv->cfg_regs) { + dev_err(&pdev->dev, "can't map register space\n"); + return -EINVAL; + } + sdhci_fix_caps(host, priv); + + sdhci_brcmstb_of_get_driver_type(dn, "host-driver-strength", + &priv->host_driver_type); + sdhci_brcmstb_of_get_driver_type(dn, "host-hs-driver-strength", + &priv->host_hs_driver_type); + sdhci_brcmstb_of_get_driver_type(dn, "card-driver-strength", + &priv->card_driver_type); + set_host_driver_strength_overrides(host, priv); res = sdhci_add_host(host); if (res) goto undo_pltfm_init; - pltfm_host = sdhci_priv(host); pltfm_host->clk = clk; return res; diff -u -N -r stblinux-3.14-1.8/drivers/mmc/host/sdhci.c stblinux-3.14-1.10-git/drivers/mmc/host/sdhci.c --- stblinux-3.14-1.8/drivers/mmc/host/sdhci.c 2015-07-28 23:57:39.000000000 +0000 +++ stblinux-3.14-1.10-git/drivers/mmc/host/sdhci.c 2019-02-10 22:05:52.954335952 +0000 @@ -1527,8 +1527,18 @@ ctrl_2 &= ~SDHCI_CTRL_DRV_TYPE_MASK; if (ios->drv_type == MMC_SET_DRIVER_TYPE_A) ctrl_2 |= SDHCI_CTRL_DRV_TYPE_A; + else if (ios->drv_type == MMC_SET_DRIVER_TYPE_B) + ctrl_2 |= SDHCI_CTRL_DRV_TYPE_B; else if (ios->drv_type == MMC_SET_DRIVER_TYPE_C) ctrl_2 |= SDHCI_CTRL_DRV_TYPE_C; + else if (ios->drv_type == MMC_SET_DRIVER_TYPE_D) + ctrl_2 |= SDHCI_CTRL_DRV_TYPE_D; + else { + pr_warn("%s: invalid driver type, default to " + "driver type B\n", + mmc_hostname(host->mmc)); + ctrl_2 |= SDHCI_CTRL_DRV_TYPE_B; + } sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); } else { @@ -2047,6 +2057,18 @@ return err; } +static int sdhci_select_drive_strength(struct mmc_card *card, + unsigned int max_dtr, int host_drv, + int card_drv, int *drv_type) +{ + struct sdhci_host *host = mmc_priv(card->host); + + if (!host->ops->select_drive_strength) + return 0; + + return host->ops->select_drive_strength(host, card, max_dtr, host_drv, + card_drv, drv_type); +} static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable) { @@ -2110,6 +2132,7 @@ .enable_sdio_irq = sdhci_enable_sdio_irq, .start_signal_voltage_switch = sdhci_start_signal_voltage_switch, .execute_tuning = sdhci_execute_tuning, + .select_drive_strength = sdhci_select_drive_strength, .card_event = sdhci_card_event, .card_busy = sdhci_card_busy, }; diff -u -N -r stblinux-3.14-1.8/drivers/mmc/host/sdhci.h stblinux-3.14-1.10-git/drivers/mmc/host/sdhci.h --- stblinux-3.14-1.8/drivers/mmc/host/sdhci.h 2015-07-28 23:57:39.000000000 +0000 +++ stblinux-3.14-1.10-git/drivers/mmc/host/sdhci.h 2019-02-10 22:05:52.954335952 +0000 @@ -229,6 +229,7 @@ /* 60-FB reserved */ +#define SDHCI_PRESET_FOR_HS 0x64 #define SDHCI_PRESET_FOR_SDR12 0x66 #define SDHCI_PRESET_FOR_SDR25 0x68 #define SDHCI_PRESET_FOR_SDR50 0x6A @@ -296,6 +297,10 @@ void (*adma_workaround)(struct sdhci_host *host, u32 intmask); void (*platform_init)(struct sdhci_host *host); void (*card_event)(struct sdhci_host *host); + int (*select_drive_strength)(struct sdhci_host *host, + struct mmc_card *card, + unsigned int max_dtr, int host_drv, + int card_drv, int *drv_type); }; #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS diff -u -N -r stblinux-3.14-1.8/include/linux/mmc/host.h stblinux-3.14-1.10-git/include/linux/mmc/host.h --- stblinux-3.14-1.8/include/linux/mmc/host.h 2015-07-28 23:57:41.000000000 +0000 +++ stblinux-3.14-1.10-git/include/linux/mmc/host.h 2019-02-10 22:05:53.146327704 +0000 @@ -136,7 +136,9 @@ /* The tuning command opcode value is different for SD and eMMC cards */ int (*execute_tuning)(struct mmc_host *host, u32 opcode); - int (*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv); + int (*select_drive_strength)(struct mmc_card *card, + unsigned int max_dtr, int host_drv, + int card_drv, int *drv_type); void (*hw_reset)(struct mmc_host *host); void (*card_event)(struct mmc_host *host); };