Loading drivers/mmc/host/sdhci-msm.c +150 −0 Original line number Diff line number Diff line Loading @@ -163,6 +163,8 @@ #define INVALID_TUNING_PHASE -1 #define sdhci_is_valid_gpio_wakeup_int(_h) ((_h)->pdata->sdiowakeup_irq >= 0) #define sdhci_is_valid_gpio_testbus_trigger_int(_h) \ ((_h)->pdata->testbus_trigger_irq >= 0) #define NUM_TUNING_PHASES 16 #define MAX_DRV_TYPES_SUPPORTED_HS200 4 Loading Loading @@ -1210,7 +1212,115 @@ static void sdhci_msm_set_mmc_drv_type(struct sdhci_host *host, u32 opcode, drv_type); } #define MAX_TESTBUS 127 #define IPCAT_MINOR_MASK(val) ((val & 0x0fff0000) >> 0x10) #define TB_CONF_MASK 0x7f #define TB_TRIG_CONF 0xff80ffff #define TB_WRITE_STATUS BIT(8) /* * This function needs to be used when getting mask and * match pattern either from cmdline or sysfs */ void sdhci_msm_mm_dbg_configure(struct sdhci_host *host, u32 mask, u32 match, u32 bit_shift, u32 testbus) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = pltfm_host->priv; struct platform_device *pdev = msm_host->pdev; u32 val; u32 enable_dbg_feature = 0; int ret = 0; if (testbus > MAX_TESTBUS) { dev_err(&pdev->dev, "%s: testbus should be less than 128.\n", __func__); return; } /* Enable debug mode */ writel_relaxed(ENABLE_DBG, host->ioaddr + SDCC_TESTBUS_CONFIG); writel_relaxed(DUMMY, host->ioaddr + SDCC_DEBUG_EN_DIS_REG); writel_relaxed((readl_relaxed(host->ioaddr + SDCC_TESTBUS_CONFIG) | TESTBUS_EN), host->ioaddr + SDCC_TESTBUS_CONFIG); /* Enable particular feature */ enable_dbg_feature |= MM_TRIGGER_DISABLE; writel_relaxed((readl_relaxed(host->ioaddr + SDCC_DEBUG_FEATURE_CFG_REG) | enable_dbg_feature), host->ioaddr + SDCC_DEBUG_FEATURE_CFG_REG); /* Configure Mask & Match pattern*/ writel_relaxed((mask << bit_shift), host->ioaddr + SDCC_DEBUG_MASK_PATTERN_REG); writel_relaxed((match << bit_shift), host->ioaddr + SDCC_DEBUG_MATCH_PATTERN_REG); /* Configure test bus for above mm */ writel_relaxed((testbus & TB_CONF_MASK), host->ioaddr + SDCC_DEBUG_MM_TB_CFG_REG); /* Initiate conf shifting */ writel_relaxed(BIT(8), host->ioaddr + SDCC_DEBUG_MM_TB_CFG_REG); /* Wait for test bus to be configured */ ret = readl_poll_timeout(host->ioaddr + SDCC_DEBUG_MM_TB_CFG_REG, val, !(val & TB_WRITE_STATUS), 50, 1000); if (ret == -ETIMEDOUT) pr_err("%s: Unable to set mask & match\n", mmc_hostname(host->mmc)); /* Direct test bus to GPIO */ writel_relaxed(((readl_relaxed(host->ioaddr + SDCC_TESTBUS_CONFIG) & TB_TRIG_CONF) | (testbus << 16)), host->ioaddr + SDCC_TESTBUS_CONFIG); /* Read back to ensure write went through */ readl_relaxed(host->ioaddr + SDCC_DEBUG_FEATURE_CFG_REG); } static ssize_t store_mask_and_match(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct sdhci_host *host = dev_get_drvdata(dev); unsigned long value; char *token; int i = 0; u32 mask, match, bit_shift, testbus; char *temp = (char *)buf; if (!host) return -EINVAL; while ((token = strsep(&temp, " "))) { kstrtoul(token, 0, &value); if (i == 0) mask = value; else if (i == 1) match = value; else if (i == 2) bit_shift = value; else if (i == 3) { testbus = value; break; } i++; } pr_info("%s: M&M parameter passed are: %d %d %d %d\n", mmc_hostname(host->mmc), mask, match, bit_shift, testbus); pm_runtime_get_sync(dev); sdhci_msm_mm_dbg_configure(host, mask, match, bit_shift, testbus); pm_runtime_put_sync(dev); pr_debug("%s: M&M debug enabled.\n", mmc_hostname(host->mmc)); return count; } /* Enter sdcc debug mode */ void sdhci_msm_enter_dbg_mode(struct sdhci_host *host) Loading Loading @@ -2861,6 +2971,16 @@ static void sdhci_msm_cfg_sdiowakeup_gpio_irq(struct sdhci_host *host, msm_host->is_sdiowakeup_enabled = enable; } static irqreturn_t sdhci_msm_testbus_trigger_irq(int irq, void *data) { struct sdhci_host *host = (struct sdhci_host *)data; pr_info("%s: match happened against mask\n", mmc_hostname(host->mmc)); return IRQ_HANDLED; } static irqreturn_t sdhci_msm_sdiowakeup_irq(int irq, void *data) { struct sdhci_host *host = (struct sdhci_host *)data; Loading Loading @@ -5564,6 +5684,22 @@ static int sdhci_msm_probe(struct platform_device *pdev) } } msm_host->pdata->testbus_trigger_irq = platform_get_irq_byname(pdev, "tb_trig_irq"); if (sdhci_is_valid_gpio_testbus_trigger_int(msm_host)) { dev_info(&pdev->dev, "%s: testbus_trigger_irq = %d\n", __func__, msm_host->pdata->testbus_trigger_irq); ret = request_irq(msm_host->pdata->testbus_trigger_irq, sdhci_msm_testbus_trigger_irq, IRQF_SHARED | IRQF_TRIGGER_RISING, "sdhci-msm tb_trig", host); if (ret) { dev_err(&pdev->dev, "%s: request tb_trig IRQ %d: failed: %d\n", __func__, msm_host->pdata->testbus_trigger_irq, ret); } } if (of_device_is_compatible(node, "qcom,sdhci-msm-cqe")) { dev_dbg(&pdev->dev, "node with qcom,sdhci-msm-cqe\n"); ret = sdhci_msm_cqe_add_host(host, pdev); Loading Loading @@ -5622,6 +5758,20 @@ static int sdhci_msm_probe(struct platform_device *pdev) device_remove_file(&pdev->dev, &msm_host->auto_cmd21_attr); } if (IPCAT_MINOR_MASK(readl_relaxed(host->ioaddr + SDCC_IP_CATALOG)) >= 2) { msm_host->mask_and_match.store = store_mask_and_match; sysfs_attr_init(&msm_host->mask_and_match.attr); msm_host->mask_and_match.attr.name = "mask_and_match"; msm_host->mask_and_match.attr.mode = 0644; ret = device_create_file(&pdev->dev, &msm_host->mask_and_match); if (ret) { pr_err("%s: %s: failed creating M&M attr: %d\n", mmc_hostname(host->mmc), __func__, ret); } } if (sdhci_msm_is_bootdevice(&pdev->dev)) mmc_flush_detect_work(host->mmc); /* Successful initialization */ Loading drivers/mmc/host/sdhci-msm.h +2 −0 Original line number Diff line number Diff line Loading @@ -207,6 +207,7 @@ struct sdhci_msm_pltfm_data { u32 *sup_clk_table; unsigned char sup_clk_cnt; int sdiowakeup_irq; int testbus_trigger_irq; struct sdhci_msm_pm_qos_data pm_qos_data; u32 *bus_clk_table; unsigned char bus_clk_cnt; Loading Loading @@ -288,6 +289,7 @@ struct sdhci_msm_host { struct completion pwr_irq_completion; struct sdhci_msm_bus_vote msm_bus_vote; struct device_attribute polling; struct device_attribute mask_and_match; u32 clk_rate; /* Keeps track of current clock rate that is set */ bool tuning_done; bool calibration_done; Loading Loading
drivers/mmc/host/sdhci-msm.c +150 −0 Original line number Diff line number Diff line Loading @@ -163,6 +163,8 @@ #define INVALID_TUNING_PHASE -1 #define sdhci_is_valid_gpio_wakeup_int(_h) ((_h)->pdata->sdiowakeup_irq >= 0) #define sdhci_is_valid_gpio_testbus_trigger_int(_h) \ ((_h)->pdata->testbus_trigger_irq >= 0) #define NUM_TUNING_PHASES 16 #define MAX_DRV_TYPES_SUPPORTED_HS200 4 Loading Loading @@ -1210,7 +1212,115 @@ static void sdhci_msm_set_mmc_drv_type(struct sdhci_host *host, u32 opcode, drv_type); } #define MAX_TESTBUS 127 #define IPCAT_MINOR_MASK(val) ((val & 0x0fff0000) >> 0x10) #define TB_CONF_MASK 0x7f #define TB_TRIG_CONF 0xff80ffff #define TB_WRITE_STATUS BIT(8) /* * This function needs to be used when getting mask and * match pattern either from cmdline or sysfs */ void sdhci_msm_mm_dbg_configure(struct sdhci_host *host, u32 mask, u32 match, u32 bit_shift, u32 testbus) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = pltfm_host->priv; struct platform_device *pdev = msm_host->pdev; u32 val; u32 enable_dbg_feature = 0; int ret = 0; if (testbus > MAX_TESTBUS) { dev_err(&pdev->dev, "%s: testbus should be less than 128.\n", __func__); return; } /* Enable debug mode */ writel_relaxed(ENABLE_DBG, host->ioaddr + SDCC_TESTBUS_CONFIG); writel_relaxed(DUMMY, host->ioaddr + SDCC_DEBUG_EN_DIS_REG); writel_relaxed((readl_relaxed(host->ioaddr + SDCC_TESTBUS_CONFIG) | TESTBUS_EN), host->ioaddr + SDCC_TESTBUS_CONFIG); /* Enable particular feature */ enable_dbg_feature |= MM_TRIGGER_DISABLE; writel_relaxed((readl_relaxed(host->ioaddr + SDCC_DEBUG_FEATURE_CFG_REG) | enable_dbg_feature), host->ioaddr + SDCC_DEBUG_FEATURE_CFG_REG); /* Configure Mask & Match pattern*/ writel_relaxed((mask << bit_shift), host->ioaddr + SDCC_DEBUG_MASK_PATTERN_REG); writel_relaxed((match << bit_shift), host->ioaddr + SDCC_DEBUG_MATCH_PATTERN_REG); /* Configure test bus for above mm */ writel_relaxed((testbus & TB_CONF_MASK), host->ioaddr + SDCC_DEBUG_MM_TB_CFG_REG); /* Initiate conf shifting */ writel_relaxed(BIT(8), host->ioaddr + SDCC_DEBUG_MM_TB_CFG_REG); /* Wait for test bus to be configured */ ret = readl_poll_timeout(host->ioaddr + SDCC_DEBUG_MM_TB_CFG_REG, val, !(val & TB_WRITE_STATUS), 50, 1000); if (ret == -ETIMEDOUT) pr_err("%s: Unable to set mask & match\n", mmc_hostname(host->mmc)); /* Direct test bus to GPIO */ writel_relaxed(((readl_relaxed(host->ioaddr + SDCC_TESTBUS_CONFIG) & TB_TRIG_CONF) | (testbus << 16)), host->ioaddr + SDCC_TESTBUS_CONFIG); /* Read back to ensure write went through */ readl_relaxed(host->ioaddr + SDCC_DEBUG_FEATURE_CFG_REG); } static ssize_t store_mask_and_match(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct sdhci_host *host = dev_get_drvdata(dev); unsigned long value; char *token; int i = 0; u32 mask, match, bit_shift, testbus; char *temp = (char *)buf; if (!host) return -EINVAL; while ((token = strsep(&temp, " "))) { kstrtoul(token, 0, &value); if (i == 0) mask = value; else if (i == 1) match = value; else if (i == 2) bit_shift = value; else if (i == 3) { testbus = value; break; } i++; } pr_info("%s: M&M parameter passed are: %d %d %d %d\n", mmc_hostname(host->mmc), mask, match, bit_shift, testbus); pm_runtime_get_sync(dev); sdhci_msm_mm_dbg_configure(host, mask, match, bit_shift, testbus); pm_runtime_put_sync(dev); pr_debug("%s: M&M debug enabled.\n", mmc_hostname(host->mmc)); return count; } /* Enter sdcc debug mode */ void sdhci_msm_enter_dbg_mode(struct sdhci_host *host) Loading Loading @@ -2861,6 +2971,16 @@ static void sdhci_msm_cfg_sdiowakeup_gpio_irq(struct sdhci_host *host, msm_host->is_sdiowakeup_enabled = enable; } static irqreturn_t sdhci_msm_testbus_trigger_irq(int irq, void *data) { struct sdhci_host *host = (struct sdhci_host *)data; pr_info("%s: match happened against mask\n", mmc_hostname(host->mmc)); return IRQ_HANDLED; } static irqreturn_t sdhci_msm_sdiowakeup_irq(int irq, void *data) { struct sdhci_host *host = (struct sdhci_host *)data; Loading Loading @@ -5564,6 +5684,22 @@ static int sdhci_msm_probe(struct platform_device *pdev) } } msm_host->pdata->testbus_trigger_irq = platform_get_irq_byname(pdev, "tb_trig_irq"); if (sdhci_is_valid_gpio_testbus_trigger_int(msm_host)) { dev_info(&pdev->dev, "%s: testbus_trigger_irq = %d\n", __func__, msm_host->pdata->testbus_trigger_irq); ret = request_irq(msm_host->pdata->testbus_trigger_irq, sdhci_msm_testbus_trigger_irq, IRQF_SHARED | IRQF_TRIGGER_RISING, "sdhci-msm tb_trig", host); if (ret) { dev_err(&pdev->dev, "%s: request tb_trig IRQ %d: failed: %d\n", __func__, msm_host->pdata->testbus_trigger_irq, ret); } } if (of_device_is_compatible(node, "qcom,sdhci-msm-cqe")) { dev_dbg(&pdev->dev, "node with qcom,sdhci-msm-cqe\n"); ret = sdhci_msm_cqe_add_host(host, pdev); Loading Loading @@ -5622,6 +5758,20 @@ static int sdhci_msm_probe(struct platform_device *pdev) device_remove_file(&pdev->dev, &msm_host->auto_cmd21_attr); } if (IPCAT_MINOR_MASK(readl_relaxed(host->ioaddr + SDCC_IP_CATALOG)) >= 2) { msm_host->mask_and_match.store = store_mask_and_match; sysfs_attr_init(&msm_host->mask_and_match.attr); msm_host->mask_and_match.attr.name = "mask_and_match"; msm_host->mask_and_match.attr.mode = 0644; ret = device_create_file(&pdev->dev, &msm_host->mask_and_match); if (ret) { pr_err("%s: %s: failed creating M&M attr: %d\n", mmc_hostname(host->mmc), __func__, ret); } } if (sdhci_msm_is_bootdevice(&pdev->dev)) mmc_flush_detect_work(host->mmc); /* Successful initialization */ Loading
drivers/mmc/host/sdhci-msm.h +2 −0 Original line number Diff line number Diff line Loading @@ -207,6 +207,7 @@ struct sdhci_msm_pltfm_data { u32 *sup_clk_table; unsigned char sup_clk_cnt; int sdiowakeup_irq; int testbus_trigger_irq; struct sdhci_msm_pm_qos_data pm_qos_data; u32 *bus_clk_table; unsigned char bus_clk_cnt; Loading Loading @@ -288,6 +289,7 @@ struct sdhci_msm_host { struct completion pwr_irq_completion; struct sdhci_msm_bus_vote msm_bus_vote; struct device_attribute polling; struct device_attribute mask_and_match; u32 clk_rate; /* Keeps track of current clock rate that is set */ bool tuning_done; bool calibration_done; Loading