Loading drivers/mmc/host/sdhci-msm.c +329 −1 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ #include <linux/scatterlist.h> #include <linux/slab.h> #include <linux/mmc/mmc.h> #include <linux/msm-bus.h> #include "sdhci-pltfm.h" Loading Loading @@ -150,6 +151,12 @@ struct sdhci_msm_pin_data { struct sdhci_msm_gpio_data *gpio_data; }; struct sdhci_msm_bus_voting_data { struct msm_bus_scale_pdata *bus_pdata; unsigned int *bw_vecs; unsigned int bw_vecs_size; }; struct sdhci_msm_pltfm_data { /* Supported UHS-I Modes */ u32 caps; Loading @@ -163,9 +170,21 @@ struct sdhci_msm_pltfm_data { bool nonremovable; struct sdhci_msm_pin_data *pin_data; u32 cpu_dma_latency_us; struct sdhci_msm_bus_voting_data *voting_data; }; struct sdhci_msm_bus_vote { uint32_t client_handle; uint32_t curr_vote; int min_bw_vote; int max_bw_vote; bool is_max_bw_needed; struct delayed_work vote_work; struct device_attribute max_bus_bw; }; struct sdhci_msm_host { struct platform_device *pdev; void __iomem *core_mem; /* MSM SDCC mapped address */ struct clk *clk; /* main SD/MMC bus clock */ struct clk *pclk; /* SDHC peripheral bus clock */ Loading @@ -175,6 +194,7 @@ struct sdhci_msm_host { struct mmc_host *mmc; struct sdhci_pltfm_data sdhci_msm_pdata; wait_queue_head_t pwr_irq_wait; struct sdhci_msm_bus_vote msm_bus_vote; }; enum vdd_io_level { Loading Loading @@ -638,6 +658,44 @@ static int sdhci_msm_setup_pins(struct sdhci_msm_pltfm_data *pdata, bool enable) return ret; } static int sdhci_msm_dt_get_array(struct device *dev, const char *prop_name, u32 **out, int *len, u32 size) { int ret = 0; struct device_node *np = dev->of_node; size_t sz; u32 *arr = NULL; if (!of_get_property(np, prop_name, len)) { ret = -EINVAL; goto out; } sz = *len = *len / sizeof(*arr); if (sz <= 0 || (size > 0 && (sz != size))) { dev_err(dev, "%s invalid size\n", prop_name); ret = -EINVAL; goto out; } arr = devm_kzalloc(dev, sz * sizeof(*arr), GFP_KERNEL); if (!arr) { dev_err(dev, "%s failed allocating memory\n", prop_name); ret = -ENOMEM; goto out; } ret = of_property_read_u32_array(np, prop_name, arr, sz); if (ret < 0) { dev_err(dev, "%s failed reading array %d\n", prop_name, ret); goto out; } *out = arr; out: if (ret) *len = 0; return ret; } #define MAX_PROP_SIZE 32 static int sdhci_msm_dt_parse_vreg_info(struct device *dev, struct sdhci_msm_reg_data **vreg_data, const char *vreg_name) Loading Loading @@ -848,6 +906,218 @@ static struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev) return NULL; } /* Returns required bandwidth in Bytes per Sec */ static unsigned int sdhci_get_bw_required(struct sdhci_host *host, struct mmc_ios *ios) { unsigned int bw; bw = host->clock; /* * For DDR mode, SDCC controller clock will be at * the double rate than the actual clock that goes to card. */ if (ios->bus_width == MMC_BUS_WIDTH_4) bw /= 2; else if (ios->bus_width == MMC_BUS_WIDTH_1) bw /= 8; return bw; } static int sdhci_msm_bus_get_vote_for_bw(struct sdhci_msm_host *host, unsigned int bw) { unsigned int *table = host->pdata->voting_data->bw_vecs; unsigned int size = host->pdata->voting_data->bw_vecs_size; int i; if (host->msm_bus_vote.is_max_bw_needed && bw) return host->msm_bus_vote.max_bw_vote; for (i = 0; i < size; i++) { if (bw <= table[i]) break; } if (i && (i == size)) i--; return i; } /* * This function must be called with host lock acquired. * Caller of this function should also ensure that msm bus client * handle is not null. */ static inline int sdhci_msm_bus_set_vote(struct sdhci_msm_host *msm_host, int vote, unsigned long flags) { struct sdhci_host *host = platform_get_drvdata(msm_host->pdev); int rc = 0; if (vote != msm_host->msm_bus_vote.curr_vote) { spin_unlock_irqrestore(&host->lock, flags); rc = msm_bus_scale_client_update_request( msm_host->msm_bus_vote.client_handle, vote); spin_lock_irqsave(&host->lock, flags); if (rc) { pr_err("%s: msm_bus_scale_client_update_request() failed: bus_client_handle=0x%x, vote=%d, err=%d\n", mmc_hostname(host->mmc), msm_host->msm_bus_vote.client_handle, vote, rc); goto out; } msm_host->msm_bus_vote.curr_vote = vote; } out: return rc; } /* * Internal work. Work to set 0 bandwidth for msm bus. */ static void sdhci_msm_bus_work(struct work_struct *work) { struct sdhci_msm_host *msm_host; struct sdhci_host *host; unsigned long flags; msm_host = container_of(work, struct sdhci_msm_host, msm_bus_vote.vote_work.work); host = platform_get_drvdata(msm_host->pdev); if (!msm_host->msm_bus_vote.client_handle) return; spin_lock_irqsave(&host->lock, flags); /* don't vote for 0 bandwidth if any request is in progress */ if (!host->mrq) { sdhci_msm_bus_set_vote(msm_host, msm_host->msm_bus_vote.min_bw_vote, flags); } else pr_warning("%s: %s: Transfer in progress. skipping bus voting to 0 bandwidth\n", mmc_hostname(host->mmc), __func__); spin_unlock_irqrestore(&host->lock, flags); } /* * This function cancels any scheduled delayed work and sets the bus * vote based on bw (bandwidth) argument. */ static void sdhci_msm_bus_cancel_work_and_set_vote(struct sdhci_host *host, unsigned int bw) { int vote; unsigned long flags; struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = pltfm_host->priv; cancel_delayed_work_sync(&msm_host->msm_bus_vote.vote_work); spin_lock_irqsave(&host->lock, flags); vote = sdhci_msm_bus_get_vote_for_bw(msm_host, bw); sdhci_msm_bus_set_vote(msm_host, vote, flags); spin_unlock_irqrestore(&host->lock, flags); } #define MSM_MMC_BUS_VOTING_DELAY 200 /* msecs */ /* This function queues a work which will set the bandwidth requiement to 0 */ static void sdhci_msm_bus_queue_work(struct sdhci_host *host) { unsigned long flags; struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = pltfm_host->priv; spin_lock_irqsave(&host->lock, flags); if (msm_host->msm_bus_vote.min_bw_vote != msm_host->msm_bus_vote.curr_vote) queue_delayed_work(system_wq, &msm_host->msm_bus_vote.vote_work, msecs_to_jiffies(MSM_MMC_BUS_VOTING_DELAY)); spin_unlock_irqrestore(&host->lock, flags); } static int sdhci_msm_bus_register(struct sdhci_msm_host *host, struct platform_device *pdev) { int rc = 0; struct msm_bus_scale_pdata *bus_pdata; struct sdhci_msm_bus_voting_data *data; struct device *dev = &pdev->dev; data = devm_kzalloc(dev, sizeof(struct sdhci_msm_bus_voting_data), GFP_KERNEL); if (!data) { dev_err(&pdev->dev, "%s: failed to allocate memory\n", __func__); rc = -ENOMEM; goto out; } data->bus_pdata = msm_bus_cl_get_pdata(pdev); if (data->bus_pdata) { rc = sdhci_msm_dt_get_array(dev, "qcom,bus-bw-vectors-bps", &data->bw_vecs, &data->bw_vecs_size, 0); if (rc) { dev_err(&pdev->dev, "%s: Failed to get bus-bw-vectors-bps\n", __func__); goto out; } host->pdata->voting_data = data; } if (host->pdata->voting_data && host->pdata->voting_data->bus_pdata && host->pdata->voting_data->bw_vecs && host->pdata->voting_data->bw_vecs_size) { bus_pdata = host->pdata->voting_data->bus_pdata; host->msm_bus_vote.client_handle = msm_bus_scale_register_client(bus_pdata); if (!host->msm_bus_vote.client_handle) { dev_err(&pdev->dev, "msm_bus_scale_register_client()\n"); rc = -EFAULT; goto out; } /* cache the vote index for minimum and maximum bandwidth */ host->msm_bus_vote.min_bw_vote = sdhci_msm_bus_get_vote_for_bw(host, 0); host->msm_bus_vote.max_bw_vote = sdhci_msm_bus_get_vote_for_bw(host, UINT_MAX); } else { devm_kfree(dev, data); } out: return rc; } static void sdhci_msm_bus_unregister(struct sdhci_msm_host *host) { if (host->msm_bus_vote.client_handle) msm_bus_scale_unregister_client( host->msm_bus_vote.client_handle); } static void sdhci_msm_bus_voting(struct sdhci_host *host, u32 enable) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = pltfm_host->priv; struct mmc_ios *ios = &host->mmc->ios; unsigned int bw; if (!msm_host->msm_bus_vote.client_handle) return; bw = sdhci_get_bw_required(host, ios); if (enable) sdhci_msm_bus_cancel_work_and_set_vote(host, bw); else sdhci_msm_bus_queue_work(host); } /* Regulator utility functions */ static int sdhci_msm_vreg_init_reg(struct device *dev, struct sdhci_msm_reg_data *vreg) Loading Loading @@ -1200,6 +1470,36 @@ static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data) return IRQ_HANDLED; } static ssize_t show_sdhci_max_bus_bw(struct device *dev, struct device_attribute *attr, char *buf) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = pltfm_host->priv; return snprintf(buf, PAGE_SIZE, "%u\n", msm_host->msm_bus_vote.is_max_bw_needed); } static ssize_t store_sdhci_max_bus_bw(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = pltfm_host->priv; uint32_t value; unsigned long flags; if (!kstrtou32(buf, 0, &value)) { spin_lock_irqsave(&host->lock, flags); msm_host->msm_bus_vote.is_max_bw_needed = !!value; spin_unlock_irqrestore(&host->lock, flags); } return count; } static void sdhci_msm_check_power_status(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); Loading Loading @@ -1305,6 +1605,7 @@ static struct sdhci_ops sdhci_msm_ops = { .toggle_cdr = sdhci_msm_toggle_cdr, .get_max_segments = sdhci_msm_max_segs, .set_clock = sdhci_msm_set_clock, .platform_bus_voting = sdhci_msm_bus_voting, }; static int sdhci_msm_probe(struct platform_device *pdev) Loading Loading @@ -1335,6 +1636,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) pltfm_host = sdhci_priv(host); pltfm_host->priv = msm_host; msm_host->mmc = host->mmc; msm_host->pdev = pdev; /* Extract platform data */ if (pdev->dev.of_node) { Loading Loading @@ -1469,10 +1771,18 @@ static int sdhci_msm_probe(struct platform_device *pdev) host->cpu_dma_latency_us = msm_host->pdata->cpu_dma_latency_us; ret = sdhci_msm_bus_register(msm_host, pdev); if (ret) goto vreg_deinit; if (msm_host->msm_bus_vote.client_handle) INIT_DELAYED_WORK(&msm_host->msm_bus_vote.vote_work, sdhci_msm_bus_work); ret = sdhci_add_host(host); if (ret) { dev_err(&pdev->dev, "Add host failed (%d)\n", ret); goto vreg_deinit; goto bus_unregister; } /* Set core clk rate, optionally override from dts */ Loading @@ -1484,12 +1794,24 @@ static int sdhci_msm_probe(struct platform_device *pdev) goto remove_host; } msm_host->msm_bus_vote.max_bus_bw.show = show_sdhci_max_bus_bw; msm_host->msm_bus_vote.max_bus_bw.store = store_sdhci_max_bus_bw; sysfs_attr_init(&msm_host->msm_bus_vote.max_bus_bw.attr); msm_host->msm_bus_vote.max_bus_bw.attr.name = "max_bus_bw"; msm_host->msm_bus_vote.max_bus_bw.attr.mode = S_IRUGO | S_IWUSR; ret = device_create_file(&pdev->dev, &msm_host->msm_bus_vote.max_bus_bw); if (ret) goto remove_host; /* Successful initialization */ goto out; remove_host: dead = (readl_relaxed(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); sdhci_remove_host(host, dead); bus_unregister: sdhci_msm_bus_unregister(msm_host); vreg_deinit: sdhci_msm_vreg_init(&pdev->dev, msm_host->pdata, false); clk_disable: Loading Loading @@ -1518,12 +1840,18 @@ static int sdhci_msm_remove(struct platform_device *pdev) 0xffffffff); pr_debug("%s: %s\n", dev_name(&pdev->dev), __func__); device_remove_file(&pdev->dev, &msm_host->msm_bus_vote.max_bus_bw); sdhci_remove_host(host, dead); sdhci_pltfm_free(pdev); sdhci_msm_vreg_init(&pdev->dev, msm_host->pdata, false); if (pdata->pin_data) sdhci_msm_setup_gpio(pdata, false); if (msm_host->msm_bus_vote.client_handle) { sdhci_msm_bus_cancel_work_and_set_vote(host, 0); sdhci_msm_bus_unregister(msm_host); } return 0; } Loading drivers/mmc/host/sdhci.c +6 −0 Original line number Diff line number Diff line Loading @@ -1497,6 +1497,9 @@ static int sdhci_enable(struct mmc_host *mmc) if (host->cpu_dma_latency_us) pm_qos_update_request(&host->pm_qos_req_dma, host->cpu_dma_latency_us); if (host->ops->platform_bus_voting) host->ops->platform_bus_voting(host, 1); return 0; } Loading @@ -1507,6 +1510,9 @@ static int sdhci_disable(struct mmc_host *mmc) if (host->cpu_dma_latency_us) pm_qos_update_request(&host->pm_qos_req_dma, PM_QOS_DEFAULT_VALUE); if (host->ops->platform_bus_voting) host->ops->platform_bus_voting(host, 0); return 0; } Loading drivers/mmc/host/sdhci.h +2 −0 Original line number Diff line number Diff line Loading @@ -486,6 +486,7 @@ struct sdhci_host { bool pending_reset; /* Cmd/data reset is pending */ struct mmc_request *mrqs_done[SDHCI_MAX_MRQS]; /* Requests done */ struct mmc_request *mrq; /* Current request */ struct mmc_command *cmd; /* Current command */ struct mmc_command *data_cmd; /* Current data command */ struct mmc_data *data; /* Current data request */ Loading Loading @@ -575,6 +576,7 @@ struct sdhci_ops { void (*adma_workaround)(struct sdhci_host *host, u32 intmask); unsigned int (*get_max_segments)(void); void (*card_event)(struct sdhci_host *host); void (*platform_bus_voting)(struct sdhci_host *host, u32 enable); void (*toggle_cdr)(struct sdhci_host *host, bool enable); void (*check_power_status)(struct sdhci_host *host); void (*voltage_switch)(struct sdhci_host *host); Loading Loading
drivers/mmc/host/sdhci-msm.c +329 −1 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ #include <linux/scatterlist.h> #include <linux/slab.h> #include <linux/mmc/mmc.h> #include <linux/msm-bus.h> #include "sdhci-pltfm.h" Loading Loading @@ -150,6 +151,12 @@ struct sdhci_msm_pin_data { struct sdhci_msm_gpio_data *gpio_data; }; struct sdhci_msm_bus_voting_data { struct msm_bus_scale_pdata *bus_pdata; unsigned int *bw_vecs; unsigned int bw_vecs_size; }; struct sdhci_msm_pltfm_data { /* Supported UHS-I Modes */ u32 caps; Loading @@ -163,9 +170,21 @@ struct sdhci_msm_pltfm_data { bool nonremovable; struct sdhci_msm_pin_data *pin_data; u32 cpu_dma_latency_us; struct sdhci_msm_bus_voting_data *voting_data; }; struct sdhci_msm_bus_vote { uint32_t client_handle; uint32_t curr_vote; int min_bw_vote; int max_bw_vote; bool is_max_bw_needed; struct delayed_work vote_work; struct device_attribute max_bus_bw; }; struct sdhci_msm_host { struct platform_device *pdev; void __iomem *core_mem; /* MSM SDCC mapped address */ struct clk *clk; /* main SD/MMC bus clock */ struct clk *pclk; /* SDHC peripheral bus clock */ Loading @@ -175,6 +194,7 @@ struct sdhci_msm_host { struct mmc_host *mmc; struct sdhci_pltfm_data sdhci_msm_pdata; wait_queue_head_t pwr_irq_wait; struct sdhci_msm_bus_vote msm_bus_vote; }; enum vdd_io_level { Loading Loading @@ -638,6 +658,44 @@ static int sdhci_msm_setup_pins(struct sdhci_msm_pltfm_data *pdata, bool enable) return ret; } static int sdhci_msm_dt_get_array(struct device *dev, const char *prop_name, u32 **out, int *len, u32 size) { int ret = 0; struct device_node *np = dev->of_node; size_t sz; u32 *arr = NULL; if (!of_get_property(np, prop_name, len)) { ret = -EINVAL; goto out; } sz = *len = *len / sizeof(*arr); if (sz <= 0 || (size > 0 && (sz != size))) { dev_err(dev, "%s invalid size\n", prop_name); ret = -EINVAL; goto out; } arr = devm_kzalloc(dev, sz * sizeof(*arr), GFP_KERNEL); if (!arr) { dev_err(dev, "%s failed allocating memory\n", prop_name); ret = -ENOMEM; goto out; } ret = of_property_read_u32_array(np, prop_name, arr, sz); if (ret < 0) { dev_err(dev, "%s failed reading array %d\n", prop_name, ret); goto out; } *out = arr; out: if (ret) *len = 0; return ret; } #define MAX_PROP_SIZE 32 static int sdhci_msm_dt_parse_vreg_info(struct device *dev, struct sdhci_msm_reg_data **vreg_data, const char *vreg_name) Loading Loading @@ -848,6 +906,218 @@ static struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev) return NULL; } /* Returns required bandwidth in Bytes per Sec */ static unsigned int sdhci_get_bw_required(struct sdhci_host *host, struct mmc_ios *ios) { unsigned int bw; bw = host->clock; /* * For DDR mode, SDCC controller clock will be at * the double rate than the actual clock that goes to card. */ if (ios->bus_width == MMC_BUS_WIDTH_4) bw /= 2; else if (ios->bus_width == MMC_BUS_WIDTH_1) bw /= 8; return bw; } static int sdhci_msm_bus_get_vote_for_bw(struct sdhci_msm_host *host, unsigned int bw) { unsigned int *table = host->pdata->voting_data->bw_vecs; unsigned int size = host->pdata->voting_data->bw_vecs_size; int i; if (host->msm_bus_vote.is_max_bw_needed && bw) return host->msm_bus_vote.max_bw_vote; for (i = 0; i < size; i++) { if (bw <= table[i]) break; } if (i && (i == size)) i--; return i; } /* * This function must be called with host lock acquired. * Caller of this function should also ensure that msm bus client * handle is not null. */ static inline int sdhci_msm_bus_set_vote(struct sdhci_msm_host *msm_host, int vote, unsigned long flags) { struct sdhci_host *host = platform_get_drvdata(msm_host->pdev); int rc = 0; if (vote != msm_host->msm_bus_vote.curr_vote) { spin_unlock_irqrestore(&host->lock, flags); rc = msm_bus_scale_client_update_request( msm_host->msm_bus_vote.client_handle, vote); spin_lock_irqsave(&host->lock, flags); if (rc) { pr_err("%s: msm_bus_scale_client_update_request() failed: bus_client_handle=0x%x, vote=%d, err=%d\n", mmc_hostname(host->mmc), msm_host->msm_bus_vote.client_handle, vote, rc); goto out; } msm_host->msm_bus_vote.curr_vote = vote; } out: return rc; } /* * Internal work. Work to set 0 bandwidth for msm bus. */ static void sdhci_msm_bus_work(struct work_struct *work) { struct sdhci_msm_host *msm_host; struct sdhci_host *host; unsigned long flags; msm_host = container_of(work, struct sdhci_msm_host, msm_bus_vote.vote_work.work); host = platform_get_drvdata(msm_host->pdev); if (!msm_host->msm_bus_vote.client_handle) return; spin_lock_irqsave(&host->lock, flags); /* don't vote for 0 bandwidth if any request is in progress */ if (!host->mrq) { sdhci_msm_bus_set_vote(msm_host, msm_host->msm_bus_vote.min_bw_vote, flags); } else pr_warning("%s: %s: Transfer in progress. skipping bus voting to 0 bandwidth\n", mmc_hostname(host->mmc), __func__); spin_unlock_irqrestore(&host->lock, flags); } /* * This function cancels any scheduled delayed work and sets the bus * vote based on bw (bandwidth) argument. */ static void sdhci_msm_bus_cancel_work_and_set_vote(struct sdhci_host *host, unsigned int bw) { int vote; unsigned long flags; struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = pltfm_host->priv; cancel_delayed_work_sync(&msm_host->msm_bus_vote.vote_work); spin_lock_irqsave(&host->lock, flags); vote = sdhci_msm_bus_get_vote_for_bw(msm_host, bw); sdhci_msm_bus_set_vote(msm_host, vote, flags); spin_unlock_irqrestore(&host->lock, flags); } #define MSM_MMC_BUS_VOTING_DELAY 200 /* msecs */ /* This function queues a work which will set the bandwidth requiement to 0 */ static void sdhci_msm_bus_queue_work(struct sdhci_host *host) { unsigned long flags; struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = pltfm_host->priv; spin_lock_irqsave(&host->lock, flags); if (msm_host->msm_bus_vote.min_bw_vote != msm_host->msm_bus_vote.curr_vote) queue_delayed_work(system_wq, &msm_host->msm_bus_vote.vote_work, msecs_to_jiffies(MSM_MMC_BUS_VOTING_DELAY)); spin_unlock_irqrestore(&host->lock, flags); } static int sdhci_msm_bus_register(struct sdhci_msm_host *host, struct platform_device *pdev) { int rc = 0; struct msm_bus_scale_pdata *bus_pdata; struct sdhci_msm_bus_voting_data *data; struct device *dev = &pdev->dev; data = devm_kzalloc(dev, sizeof(struct sdhci_msm_bus_voting_data), GFP_KERNEL); if (!data) { dev_err(&pdev->dev, "%s: failed to allocate memory\n", __func__); rc = -ENOMEM; goto out; } data->bus_pdata = msm_bus_cl_get_pdata(pdev); if (data->bus_pdata) { rc = sdhci_msm_dt_get_array(dev, "qcom,bus-bw-vectors-bps", &data->bw_vecs, &data->bw_vecs_size, 0); if (rc) { dev_err(&pdev->dev, "%s: Failed to get bus-bw-vectors-bps\n", __func__); goto out; } host->pdata->voting_data = data; } if (host->pdata->voting_data && host->pdata->voting_data->bus_pdata && host->pdata->voting_data->bw_vecs && host->pdata->voting_data->bw_vecs_size) { bus_pdata = host->pdata->voting_data->bus_pdata; host->msm_bus_vote.client_handle = msm_bus_scale_register_client(bus_pdata); if (!host->msm_bus_vote.client_handle) { dev_err(&pdev->dev, "msm_bus_scale_register_client()\n"); rc = -EFAULT; goto out; } /* cache the vote index for minimum and maximum bandwidth */ host->msm_bus_vote.min_bw_vote = sdhci_msm_bus_get_vote_for_bw(host, 0); host->msm_bus_vote.max_bw_vote = sdhci_msm_bus_get_vote_for_bw(host, UINT_MAX); } else { devm_kfree(dev, data); } out: return rc; } static void sdhci_msm_bus_unregister(struct sdhci_msm_host *host) { if (host->msm_bus_vote.client_handle) msm_bus_scale_unregister_client( host->msm_bus_vote.client_handle); } static void sdhci_msm_bus_voting(struct sdhci_host *host, u32 enable) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = pltfm_host->priv; struct mmc_ios *ios = &host->mmc->ios; unsigned int bw; if (!msm_host->msm_bus_vote.client_handle) return; bw = sdhci_get_bw_required(host, ios); if (enable) sdhci_msm_bus_cancel_work_and_set_vote(host, bw); else sdhci_msm_bus_queue_work(host); } /* Regulator utility functions */ static int sdhci_msm_vreg_init_reg(struct device *dev, struct sdhci_msm_reg_data *vreg) Loading Loading @@ -1200,6 +1470,36 @@ static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data) return IRQ_HANDLED; } static ssize_t show_sdhci_max_bus_bw(struct device *dev, struct device_attribute *attr, char *buf) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = pltfm_host->priv; return snprintf(buf, PAGE_SIZE, "%u\n", msm_host->msm_bus_vote.is_max_bw_needed); } static ssize_t store_sdhci_max_bus_bw(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = pltfm_host->priv; uint32_t value; unsigned long flags; if (!kstrtou32(buf, 0, &value)) { spin_lock_irqsave(&host->lock, flags); msm_host->msm_bus_vote.is_max_bw_needed = !!value; spin_unlock_irqrestore(&host->lock, flags); } return count; } static void sdhci_msm_check_power_status(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); Loading Loading @@ -1305,6 +1605,7 @@ static struct sdhci_ops sdhci_msm_ops = { .toggle_cdr = sdhci_msm_toggle_cdr, .get_max_segments = sdhci_msm_max_segs, .set_clock = sdhci_msm_set_clock, .platform_bus_voting = sdhci_msm_bus_voting, }; static int sdhci_msm_probe(struct platform_device *pdev) Loading Loading @@ -1335,6 +1636,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) pltfm_host = sdhci_priv(host); pltfm_host->priv = msm_host; msm_host->mmc = host->mmc; msm_host->pdev = pdev; /* Extract platform data */ if (pdev->dev.of_node) { Loading Loading @@ -1469,10 +1771,18 @@ static int sdhci_msm_probe(struct platform_device *pdev) host->cpu_dma_latency_us = msm_host->pdata->cpu_dma_latency_us; ret = sdhci_msm_bus_register(msm_host, pdev); if (ret) goto vreg_deinit; if (msm_host->msm_bus_vote.client_handle) INIT_DELAYED_WORK(&msm_host->msm_bus_vote.vote_work, sdhci_msm_bus_work); ret = sdhci_add_host(host); if (ret) { dev_err(&pdev->dev, "Add host failed (%d)\n", ret); goto vreg_deinit; goto bus_unregister; } /* Set core clk rate, optionally override from dts */ Loading @@ -1484,12 +1794,24 @@ static int sdhci_msm_probe(struct platform_device *pdev) goto remove_host; } msm_host->msm_bus_vote.max_bus_bw.show = show_sdhci_max_bus_bw; msm_host->msm_bus_vote.max_bus_bw.store = store_sdhci_max_bus_bw; sysfs_attr_init(&msm_host->msm_bus_vote.max_bus_bw.attr); msm_host->msm_bus_vote.max_bus_bw.attr.name = "max_bus_bw"; msm_host->msm_bus_vote.max_bus_bw.attr.mode = S_IRUGO | S_IWUSR; ret = device_create_file(&pdev->dev, &msm_host->msm_bus_vote.max_bus_bw); if (ret) goto remove_host; /* Successful initialization */ goto out; remove_host: dead = (readl_relaxed(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); sdhci_remove_host(host, dead); bus_unregister: sdhci_msm_bus_unregister(msm_host); vreg_deinit: sdhci_msm_vreg_init(&pdev->dev, msm_host->pdata, false); clk_disable: Loading Loading @@ -1518,12 +1840,18 @@ static int sdhci_msm_remove(struct platform_device *pdev) 0xffffffff); pr_debug("%s: %s\n", dev_name(&pdev->dev), __func__); device_remove_file(&pdev->dev, &msm_host->msm_bus_vote.max_bus_bw); sdhci_remove_host(host, dead); sdhci_pltfm_free(pdev); sdhci_msm_vreg_init(&pdev->dev, msm_host->pdata, false); if (pdata->pin_data) sdhci_msm_setup_gpio(pdata, false); if (msm_host->msm_bus_vote.client_handle) { sdhci_msm_bus_cancel_work_and_set_vote(host, 0); sdhci_msm_bus_unregister(msm_host); } return 0; } Loading
drivers/mmc/host/sdhci.c +6 −0 Original line number Diff line number Diff line Loading @@ -1497,6 +1497,9 @@ static int sdhci_enable(struct mmc_host *mmc) if (host->cpu_dma_latency_us) pm_qos_update_request(&host->pm_qos_req_dma, host->cpu_dma_latency_us); if (host->ops->platform_bus_voting) host->ops->platform_bus_voting(host, 1); return 0; } Loading @@ -1507,6 +1510,9 @@ static int sdhci_disable(struct mmc_host *mmc) if (host->cpu_dma_latency_us) pm_qos_update_request(&host->pm_qos_req_dma, PM_QOS_DEFAULT_VALUE); if (host->ops->platform_bus_voting) host->ops->platform_bus_voting(host, 0); return 0; } Loading
drivers/mmc/host/sdhci.h +2 −0 Original line number Diff line number Diff line Loading @@ -486,6 +486,7 @@ struct sdhci_host { bool pending_reset; /* Cmd/data reset is pending */ struct mmc_request *mrqs_done[SDHCI_MAX_MRQS]; /* Requests done */ struct mmc_request *mrq; /* Current request */ struct mmc_command *cmd; /* Current command */ struct mmc_command *data_cmd; /* Current data command */ struct mmc_data *data; /* Current data request */ Loading Loading @@ -575,6 +576,7 @@ struct sdhci_ops { void (*adma_workaround)(struct sdhci_host *host, u32 intmask); unsigned int (*get_max_segments)(void); void (*card_event)(struct sdhci_host *host); void (*platform_bus_voting)(struct sdhci_host *host, u32 enable); void (*toggle_cdr)(struct sdhci_host *host, bool enable); void (*check_power_status)(struct sdhci_host *host); void (*voltage_switch)(struct sdhci_host *host); Loading