mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-10 06:09:23 -04:00
Merge "mmc: Define config flag to add qcom code to upstream module"
This commit is contained in:
commit
ba125366f2
12 changed files with 1174 additions and 12 deletions
|
|
@ -165,3 +165,4 @@ CONFIG_NEURON_APP_BLOCK_SERVER=y
|
|||
CONFIG_ADSPRPC_QGKI=y
|
||||
CONFIG_COMMON_CLK_QCOM_DEBUG=y
|
||||
CONFIG_QMP_DEBUGFS_CLIENT=y
|
||||
CONFIG_SDC_QTI=y
|
||||
|
|
|
|||
|
|
@ -81,3 +81,10 @@ config MMC_TEST
|
|||
This driver is only of interest to those developing or
|
||||
testing a host driver. Most people should say N here.
|
||||
|
||||
config SDC_QTI
|
||||
tristate "Enable QTI specific code in MMC upstream driver"
|
||||
depends on QGKI && MMC_SDHCI_MSM
|
||||
default n
|
||||
help
|
||||
This configuration flag allows adding QTI code in
|
||||
MMC upstream driver.
|
||||
|
|
|
|||
|
|
@ -892,7 +892,9 @@ static inline int mmc_blk_part_switch(struct mmc_card *card,
|
|||
}
|
||||
|
||||
card->ext_csd.part_config = part_config;
|
||||
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
card->part_curr = part_type;
|
||||
#endif
|
||||
ret = mmc_blk_part_switch_post(card, main_md->part_curr);
|
||||
}
|
||||
|
||||
|
|
@ -1949,13 +1951,18 @@ static void mmc_blk_mq_poll_completion(struct mmc_queue *mq,
|
|||
|
||||
static void mmc_blk_mq_dec_in_flight(struct mmc_queue *mq, struct request *req)
|
||||
{
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
struct mmc_host *host = mq->card->host;
|
||||
#endif
|
||||
unsigned long flags;
|
||||
bool put_card;
|
||||
|
||||
spin_lock_irqsave(&mq->lock, flags);
|
||||
|
||||
mq->in_flight[mmc_issue_type(mq, req)] -= 1;
|
||||
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
atomic_dec(&host->active_reqs);
|
||||
#endif
|
||||
put_card = (mmc_tot_in_flight(mq) == 0);
|
||||
|
||||
spin_unlock_irqrestore(&mq->lock, flags);
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@
|
|||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/mmc/sd.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
#include <linux/devfreq.h>
|
||||
#endif
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/mmc.h>
|
||||
|
|
@ -41,7 +44,9 @@
|
|||
#include "host.h"
|
||||
#include "sdio_bus.h"
|
||||
#include "pwrseq.h"
|
||||
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
#include "queue.h"
|
||||
#endif
|
||||
#include "mmc_ops.h"
|
||||
#include "sd_ops.h"
|
||||
#include "sdio_ops.h"
|
||||
|
|
@ -109,6 +114,683 @@ static inline void mmc_should_fail_request(struct mmc_host *host,
|
|||
|
||||
#endif /* CONFIG_FAIL_MMC_REQUEST */
|
||||
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
static bool mmc_is_data_request(struct mmc_request *mmc_request)
|
||||
{
|
||||
switch (mmc_request->cmd->opcode) {
|
||||
case MMC_READ_SINGLE_BLOCK:
|
||||
case MMC_READ_MULTIPLE_BLOCK:
|
||||
case MMC_WRITE_BLOCK:
|
||||
case MMC_WRITE_MULTIPLE_BLOCK:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void mmc_clk_scaling_start_busy(struct mmc_host *host, bool lock_needed)
|
||||
{
|
||||
struct mmc_devfeq_clk_scaling *clk_scaling = &host->clk_scaling;
|
||||
|
||||
if (!clk_scaling->enable)
|
||||
return;
|
||||
|
||||
if (lock_needed)
|
||||
spin_lock_bh(&clk_scaling->lock);
|
||||
|
||||
clk_scaling->start_busy = ktime_get();
|
||||
clk_scaling->is_busy_started = true;
|
||||
|
||||
if (lock_needed)
|
||||
spin_unlock_bh(&clk_scaling->lock);
|
||||
}
|
||||
|
||||
static void mmc_clk_scaling_stop_busy(struct mmc_host *host, bool lock_needed)
|
||||
{
|
||||
struct mmc_devfeq_clk_scaling *clk_scaling = &host->clk_scaling;
|
||||
|
||||
if (!clk_scaling->enable)
|
||||
return;
|
||||
|
||||
if (lock_needed)
|
||||
spin_lock_bh(&clk_scaling->lock);
|
||||
|
||||
if (!clk_scaling->is_busy_started) {
|
||||
WARN_ON(1);
|
||||
goto out;
|
||||
}
|
||||
|
||||
clk_scaling->total_busy_time_us +=
|
||||
ktime_to_us(ktime_sub(ktime_get(),
|
||||
clk_scaling->start_busy));
|
||||
pr_debug("%s: accumulated busy time is %lu usec\n",
|
||||
mmc_hostname(host), clk_scaling->total_busy_time_us);
|
||||
clk_scaling->is_busy_started = false;
|
||||
|
||||
out:
|
||||
if (lock_needed)
|
||||
spin_unlock_bh(&clk_scaling->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* mmc_can_scale_clk() - Check clock scaling capability
|
||||
* @host: pointer to mmc host structure
|
||||
*/
|
||||
bool mmc_can_scale_clk(struct mmc_host *host)
|
||||
{
|
||||
if (!host) {
|
||||
pr_err("bad host parameter\n");
|
||||
WARN_ON(1);
|
||||
return false;
|
||||
}
|
||||
|
||||
return host->caps2 & MMC_CAP2_CLK_SCALE;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_can_scale_clk);
|
||||
|
||||
static int mmc_devfreq_get_dev_status(struct device *dev,
|
||||
struct devfreq_dev_status *status)
|
||||
{
|
||||
struct mmc_host *host = container_of(dev, struct mmc_host, class_dev);
|
||||
struct mmc_devfeq_clk_scaling *clk_scaling;
|
||||
unsigned long flags;
|
||||
|
||||
if (!host) {
|
||||
pr_err("bad host parameter\n");
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
clk_scaling = &host->clk_scaling;
|
||||
|
||||
if (!clk_scaling->enable)
|
||||
return 0;
|
||||
|
||||
spin_lock_irqsave(&host->clk_scaling.lock, flags);
|
||||
|
||||
/* accumulate the busy time of ongoing work */
|
||||
memset(status, 0, sizeof(*status));
|
||||
if (clk_scaling->is_busy_started) {
|
||||
mmc_clk_scaling_stop_busy(host, false);
|
||||
mmc_clk_scaling_start_busy(host, false);
|
||||
}
|
||||
|
||||
status->busy_time = clk_scaling->total_busy_time_us;
|
||||
status->total_time = ktime_to_us(ktime_sub(ktime_get(),
|
||||
clk_scaling->measure_interval_start));
|
||||
clk_scaling->total_busy_time_us = 0;
|
||||
status->current_frequency = clk_scaling->curr_freq;
|
||||
clk_scaling->measure_interval_start = ktime_get();
|
||||
|
||||
pr_debug("%s: status: load = %lu%% - total_time=%lu busy_time = %lu, clk=%lu\n",
|
||||
mmc_hostname(host),
|
||||
(status->busy_time*100)/status->total_time,
|
||||
status->total_time, status->busy_time,
|
||||
status->current_frequency);
|
||||
|
||||
spin_unlock_irqrestore(&host->clk_scaling.lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool mmc_is_valid_state_for_clk_scaling(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_card *card = host->card;
|
||||
u32 status;
|
||||
|
||||
/*
|
||||
* If the current partition type is RPMB, clock switching may not
|
||||
* work properly as sending tuning command (CMD21) is illegal in
|
||||
* this mode.
|
||||
*/
|
||||
if (!card || (mmc_card_mmc(card) &&
|
||||
(card->part_curr == EXT_CSD_PART_CONFIG_ACC_RPMB)))
|
||||
return false;
|
||||
|
||||
if (mmc_send_status(card, &status)) {
|
||||
pr_err("%s: Get card status fail\n", mmc_hostname(card->host));
|
||||
return false;
|
||||
}
|
||||
|
||||
return R1_CURRENT_STATE(status) == R1_STATE_TRAN;
|
||||
}
|
||||
|
||||
int mmc_clk_update_freq(struct mmc_host *host,
|
||||
unsigned long freq, enum mmc_load state)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (!host) {
|
||||
pr_err("bad host parameter\n");
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* make sure the card supports the frequency we want */
|
||||
if (unlikely(freq > host->card->clk_scaling_highest)) {
|
||||
freq = host->card->clk_scaling_highest;
|
||||
pr_warn("%s: %s: High freq was overridden to %lu\n",
|
||||
mmc_hostname(host), __func__,
|
||||
host->card->clk_scaling_highest);
|
||||
}
|
||||
|
||||
if (unlikely(freq < host->card->clk_scaling_lowest)) {
|
||||
freq = host->card->clk_scaling_lowest;
|
||||
pr_warn("%s: %s: Low freq was overridden to %lu\n",
|
||||
mmc_hostname(host), __func__,
|
||||
host->card->clk_scaling_lowest);
|
||||
}
|
||||
|
||||
if (freq == host->clk_scaling.curr_freq)
|
||||
goto out;
|
||||
|
||||
if (host->cqe_on) {
|
||||
err = host->cqe_ops->cqe_wait_for_idle(host);
|
||||
if (err) {
|
||||
pr_err("%s: %s: CQE went in recovery path\n",
|
||||
mmc_hostname(host), __func__);
|
||||
goto out;
|
||||
}
|
||||
host->cqe_ops->cqe_off(host);
|
||||
}
|
||||
|
||||
if (!mmc_is_valid_state_for_clk_scaling(host)) {
|
||||
pr_debug("%s: invalid state for clock scaling - skipping\n",
|
||||
mmc_hostname(host));
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = host->bus_ops->change_bus_speed(host, &freq);
|
||||
if (!err)
|
||||
host->clk_scaling.curr_freq = freq;
|
||||
else
|
||||
pr_err("%s: %s: failed (%d) at freq=%lu\n",
|
||||
mmc_hostname(host), __func__, err, freq);
|
||||
/*
|
||||
* CQE would be enabled as part of CQE issueing path
|
||||
* So no need to unhalt it explicitly
|
||||
*/
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_clk_update_freq);
|
||||
|
||||
static int mmc_devfreq_set_target(struct device *dev,
|
||||
unsigned long *freq, u32 devfreq_flags)
|
||||
{
|
||||
struct mmc_host *host = container_of(dev, struct mmc_host, class_dev);
|
||||
struct mmc_devfeq_clk_scaling *clk_scaling;
|
||||
int err = 0;
|
||||
int abort;
|
||||
unsigned long pflags = current->flags;
|
||||
unsigned long flags;
|
||||
|
||||
/* Ensure scaling would happen even in memory pressure conditions */
|
||||
current->flags |= PF_MEMALLOC;
|
||||
|
||||
if (!(host && freq)) {
|
||||
pr_err("%s: unexpected host/freq parameter\n", __func__);
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
clk_scaling = &host->clk_scaling;
|
||||
|
||||
if (!clk_scaling->enable)
|
||||
goto out;
|
||||
|
||||
pr_debug("%s: target freq = %lu (%s)\n", mmc_hostname(host),
|
||||
*freq, current->comm);
|
||||
|
||||
spin_lock_irqsave(&clk_scaling->lock, flags);
|
||||
if (clk_scaling->target_freq == *freq ||
|
||||
clk_scaling->skip_clk_scale_freq_update) {
|
||||
spin_unlock_irqrestore(&clk_scaling->lock, flags);
|
||||
goto out;
|
||||
}
|
||||
|
||||
clk_scaling->need_freq_change = true;
|
||||
clk_scaling->target_freq = *freq;
|
||||
clk_scaling->state = *freq < clk_scaling->curr_freq ?
|
||||
MMC_LOAD_LOW : MMC_LOAD_HIGH;
|
||||
spin_unlock_irqrestore(&clk_scaling->lock, flags);
|
||||
|
||||
if (!clk_scaling->is_suspended && host->ios.clock)
|
||||
abort = __mmc_claim_host(host, NULL,
|
||||
&clk_scaling->devfreq_abort);
|
||||
else
|
||||
goto out;
|
||||
|
||||
if (abort)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* In case we were able to claim host there is no need to
|
||||
* defer the frequency change. It will be done now
|
||||
*/
|
||||
clk_scaling->need_freq_change = false;
|
||||
|
||||
err = mmc_clk_update_freq(host, *freq, clk_scaling->state);
|
||||
if (err && err != -EAGAIN)
|
||||
pr_err("%s: clock scale to %lu failed with error %d\n",
|
||||
mmc_hostname(host), *freq, err);
|
||||
else
|
||||
pr_debug("%s: clock change to %lu finished successfully (%s)\n",
|
||||
mmc_hostname(host), *freq, current->comm);
|
||||
|
||||
mmc_release_host(host);
|
||||
out:
|
||||
current_restore_flags(pflags, PF_MEMALLOC);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* mmc_deferred_scaling() - scale clocks from data path (mmc thread context)
|
||||
* @host: pointer to mmc host structure
|
||||
*
|
||||
* This function does clock scaling in case "need_freq_change" flag was set
|
||||
* by the clock scaling logic.
|
||||
*/
|
||||
void mmc_deferred_scaling(struct mmc_host *host)
|
||||
{
|
||||
unsigned long target_freq;
|
||||
int err;
|
||||
struct mmc_devfeq_clk_scaling clk_scaling;
|
||||
unsigned long flags;
|
||||
|
||||
if (!host->clk_scaling.enable)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&host->clk_scaling.lock, flags);
|
||||
|
||||
if (!host->clk_scaling.need_freq_change) {
|
||||
spin_unlock_irqrestore(&host->clk_scaling.lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
atomic_inc(&host->clk_scaling.devfreq_abort);
|
||||
target_freq = host->clk_scaling.target_freq;
|
||||
/*
|
||||
* Store the clock scaling state while the lock is acquired so that
|
||||
* if devfreq context modifies clk_scaling, it will get reflected only
|
||||
* in the next deferred scaling check.
|
||||
*/
|
||||
clk_scaling = host->clk_scaling;
|
||||
host->clk_scaling.need_freq_change = false;
|
||||
spin_unlock_irqrestore(&host->clk_scaling.lock, flags);
|
||||
|
||||
pr_debug("%s: doing deferred frequency change (%lu) (%s)\n",
|
||||
mmc_hostname(host),
|
||||
target_freq, current->comm);
|
||||
|
||||
err = mmc_clk_update_freq(host, target_freq,
|
||||
clk_scaling.state);
|
||||
if (err && err != -EAGAIN)
|
||||
pr_err("%s: failed on deferred scale clocks (%d)\n",
|
||||
mmc_hostname(host), err);
|
||||
else
|
||||
pr_debug("%s: clocks were successfully scaled to %lu (%s)\n",
|
||||
mmc_hostname(host),
|
||||
target_freq, current->comm);
|
||||
atomic_dec(&host->clk_scaling.devfreq_abort);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_deferred_scaling);
|
||||
|
||||
static int mmc_devfreq_create_freq_table(struct mmc_host *host)
|
||||
{
|
||||
int i;
|
||||
struct mmc_devfeq_clk_scaling *clk_scaling = &host->clk_scaling;
|
||||
|
||||
pr_debug("%s: supported: lowest=%lu, highest=%lu\n",
|
||||
mmc_hostname(host),
|
||||
host->card->clk_scaling_lowest,
|
||||
host->card->clk_scaling_highest);
|
||||
|
||||
/*
|
||||
* Create the frequency table and initialize it with default values.
|
||||
* Initialize it with platform specific frequencies if the frequency
|
||||
* table supplied by platform driver is present, otherwise initialize
|
||||
* it with min and max frequencies supported by the card.
|
||||
*/
|
||||
if (!clk_scaling->freq_table) {
|
||||
if (clk_scaling->pltfm_freq_table_sz)
|
||||
clk_scaling->freq_table_sz =
|
||||
clk_scaling->pltfm_freq_table_sz;
|
||||
else
|
||||
clk_scaling->freq_table_sz = 2;
|
||||
|
||||
clk_scaling->freq_table = kcalloc(
|
||||
clk_scaling->freq_table_sz,
|
||||
sizeof(*(clk_scaling->freq_table)), GFP_KERNEL);
|
||||
if (!clk_scaling->freq_table)
|
||||
return -ENOMEM;
|
||||
|
||||
if (clk_scaling->pltfm_freq_table) {
|
||||
memcpy(clk_scaling->freq_table,
|
||||
clk_scaling->pltfm_freq_table,
|
||||
(clk_scaling->pltfm_freq_table_sz *
|
||||
sizeof(*(clk_scaling->pltfm_freq_table))));
|
||||
} else {
|
||||
pr_debug("%s: no frequency table defined - setting default\n",
|
||||
mmc_hostname(host));
|
||||
clk_scaling->freq_table[0] =
|
||||
host->card->clk_scaling_lowest;
|
||||
clk_scaling->freq_table[1] =
|
||||
host->card->clk_scaling_highest;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (host->card->clk_scaling_lowest >
|
||||
clk_scaling->freq_table[0])
|
||||
pr_debug("%s: frequency table undershot possible freq\n",
|
||||
mmc_hostname(host));
|
||||
|
||||
for (i = 0; i < clk_scaling->freq_table_sz; i++) {
|
||||
if (clk_scaling->freq_table[i] <=
|
||||
host->card->clk_scaling_highest)
|
||||
continue;
|
||||
clk_scaling->freq_table[i] =
|
||||
host->card->clk_scaling_highest;
|
||||
clk_scaling->freq_table_sz = i + 1;
|
||||
pr_debug("%s: frequency table overshot possible freq (%d)\n",
|
||||
mmc_hostname(host), clk_scaling->freq_table[i]);
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
/**
|
||||
* devfreq requires unsigned long type freq_table while the
|
||||
* freq_table in clk_scaling is un32. Here allocates an individual
|
||||
* memory space for it and release it when exit clock scaling.
|
||||
*/
|
||||
clk_scaling->devfreq_profile.freq_table = kcalloc(
|
||||
clk_scaling->freq_table_sz,
|
||||
sizeof(*(clk_scaling->devfreq_profile.freq_table)),
|
||||
GFP_KERNEL);
|
||||
if (!clk_scaling->devfreq_profile.freq_table) {
|
||||
kfree(clk_scaling->freq_table);
|
||||
return -ENOMEM;
|
||||
}
|
||||
clk_scaling->devfreq_profile.max_state = clk_scaling->freq_table_sz;
|
||||
|
||||
for (i = 0; i < clk_scaling->freq_table_sz; i++) {
|
||||
clk_scaling->devfreq_profile.freq_table[i] =
|
||||
clk_scaling->freq_table[i];
|
||||
pr_debug("%s: freq[%d] = %u\n",
|
||||
mmc_hostname(host), i, clk_scaling->freq_table[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mmc_init_devfreq_clk_scaling() - Initialize clock scaling
|
||||
* @host: pointer to mmc host structure
|
||||
*
|
||||
* Initialize clock scaling for supported hosts. It is assumed that the caller
|
||||
* ensure clock is running at maximum possible frequency before calling this
|
||||
* function. Shall use struct devfreq_simple_ondemand_data to configure
|
||||
* governor.
|
||||
*/
|
||||
int mmc_init_clk_scaling(struct mmc_host *host)
|
||||
{
|
||||
int err;
|
||||
struct devfreq *devfreq;
|
||||
|
||||
if (!host || !host->card) {
|
||||
pr_err("%s: unexpected host/card parameters\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!mmc_can_scale_clk(host) ||
|
||||
!host->bus_ops->change_bus_speed) {
|
||||
pr_debug("%s: clock scaling is not supported\n",
|
||||
mmc_hostname(host));
|
||||
return 0;
|
||||
}
|
||||
|
||||
pr_debug("registering %s dev (%pK) to devfreq\n",
|
||||
mmc_hostname(host),
|
||||
mmc_classdev(host));
|
||||
|
||||
if (host->clk_scaling.devfreq) {
|
||||
pr_err("%s: dev is already registered for dev %pK\n",
|
||||
mmc_hostname(host),
|
||||
mmc_dev(host));
|
||||
return -EPERM;
|
||||
}
|
||||
spin_lock_init(&host->clk_scaling.lock);
|
||||
atomic_set(&host->clk_scaling.devfreq_abort, 0);
|
||||
host->clk_scaling.curr_freq = host->ios.clock;
|
||||
host->clk_scaling.need_freq_change = false;
|
||||
host->clk_scaling.is_busy_started = false;
|
||||
|
||||
host->clk_scaling.devfreq_profile.polling_ms =
|
||||
host->clk_scaling.polling_delay_ms;
|
||||
host->clk_scaling.devfreq_profile.get_dev_status =
|
||||
mmc_devfreq_get_dev_status;
|
||||
host->clk_scaling.devfreq_profile.target = mmc_devfreq_set_target;
|
||||
host->clk_scaling.devfreq_profile.initial_freq = host->ios.clock;
|
||||
|
||||
host->clk_scaling.ondemand_gov_data.upthreshold =
|
||||
host->clk_scaling.upthreshold;
|
||||
host->clk_scaling.ondemand_gov_data.downdifferential =
|
||||
host->clk_scaling.upthreshold - host->clk_scaling.downthreshold;
|
||||
|
||||
err = mmc_devfreq_create_freq_table(host);
|
||||
if (err) {
|
||||
pr_err("%s: fail to create devfreq frequency table\n",
|
||||
mmc_hostname(host));
|
||||
return err;
|
||||
}
|
||||
|
||||
dev_pm_opp_add(mmc_classdev(host),
|
||||
host->clk_scaling.devfreq_profile.freq_table[0], 0);
|
||||
dev_pm_opp_add(mmc_classdev(host),
|
||||
host->clk_scaling.devfreq_profile.freq_table[1], 0);
|
||||
|
||||
pr_debug("%s: adding devfreq with: upthreshold=%u downthreshold=%u polling=%u\n",
|
||||
mmc_hostname(host),
|
||||
host->clk_scaling.ondemand_gov_data.upthreshold,
|
||||
host->clk_scaling.ondemand_gov_data.downdifferential,
|
||||
host->clk_scaling.devfreq_profile.polling_ms);
|
||||
|
||||
devfreq = devfreq_add_device(
|
||||
mmc_classdev(host),
|
||||
&host->clk_scaling.devfreq_profile,
|
||||
"simple_ondemand",
|
||||
&host->clk_scaling.ondemand_gov_data);
|
||||
|
||||
if (IS_ERR(devfreq)) {
|
||||
pr_err("%s: unable to register with devfreq\n",
|
||||
mmc_hostname(host));
|
||||
dev_pm_opp_remove(mmc_classdev(host),
|
||||
host->clk_scaling.devfreq_profile.freq_table[0]);
|
||||
dev_pm_opp_remove(mmc_classdev(host),
|
||||
host->clk_scaling.devfreq_profile.freq_table[1]);
|
||||
return PTR_ERR(devfreq);
|
||||
}
|
||||
|
||||
host->clk_scaling.devfreq = devfreq;
|
||||
pr_debug("%s: clk scaling is enabled for device %s (%pK) with devfreq %pK (clock = %uHz)\n",
|
||||
mmc_hostname(host),
|
||||
dev_name(mmc_classdev(host)),
|
||||
mmc_classdev(host),
|
||||
host->clk_scaling.devfreq,
|
||||
host->ios.clock);
|
||||
|
||||
host->clk_scaling.enable = true;
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_init_clk_scaling);
|
||||
|
||||
/**
|
||||
* mmc_suspend_clk_scaling() - suspend clock scaling
|
||||
* @host: pointer to mmc host structure
|
||||
*
|
||||
* This API will suspend devfreq feature for the specific host.
|
||||
* The statistics collected by mmc will be cleared.
|
||||
* This function is intended to be called by the pm callbacks
|
||||
* (e.g. runtime_suspend, suspend) of the mmc device
|
||||
*/
|
||||
int mmc_suspend_clk_scaling(struct mmc_host *host)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!host) {
|
||||
WARN(1, "bad host parameter\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!mmc_can_scale_clk(host) || !host->clk_scaling.enable ||
|
||||
host->clk_scaling.is_suspended)
|
||||
return 0;
|
||||
|
||||
if (!host->clk_scaling.devfreq) {
|
||||
pr_err("%s: %s: no devfreq is assosiated with this device\n",
|
||||
mmc_hostname(host), __func__);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
atomic_inc(&host->clk_scaling.devfreq_abort);
|
||||
wake_up(&host->wq);
|
||||
err = devfreq_suspend_device(host->clk_scaling.devfreq);
|
||||
if (err) {
|
||||
pr_err("%s: %s: failed to suspend devfreq\n",
|
||||
mmc_hostname(host), __func__);
|
||||
return err;
|
||||
}
|
||||
host->clk_scaling.is_suspended = true;
|
||||
|
||||
host->clk_scaling.total_busy_time_us = 0;
|
||||
|
||||
pr_debug("%s: devfreq was removed\n", mmc_hostname(host));
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_suspend_clk_scaling);
|
||||
|
||||
/**
|
||||
* mmc_resume_clk_scaling() - resume clock scaling
|
||||
* @host: pointer to mmc host structure
|
||||
*
|
||||
* This API will resume devfreq feature for the specific host.
|
||||
* This API is intended to be called by the pm callbacks
|
||||
* (e.g. runtime_suspend, suspend) of the mmc device
|
||||
*/
|
||||
int mmc_resume_clk_scaling(struct mmc_host *host)
|
||||
{
|
||||
int err = 0;
|
||||
u32 max_clk_idx = 0;
|
||||
u32 devfreq_max_clk = 0;
|
||||
u32 devfreq_min_clk = 0;
|
||||
|
||||
if (!host) {
|
||||
WARN(1, "bad host parameter\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!mmc_can_scale_clk(host))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If clock scaling is already exited when resume is called, like
|
||||
* during mmc shutdown, it is not an error and should not fail the
|
||||
* API calling this.
|
||||
*/
|
||||
if (!host->clk_scaling.devfreq) {
|
||||
pr_warn("%s: %s: no devfreq is assosiated with this device\n",
|
||||
mmc_hostname(host), __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
atomic_set(&host->clk_scaling.devfreq_abort, 0);
|
||||
|
||||
max_clk_idx = host->clk_scaling.freq_table_sz - 1;
|
||||
devfreq_max_clk = host->clk_scaling.freq_table[max_clk_idx];
|
||||
devfreq_min_clk = host->clk_scaling.freq_table[0];
|
||||
|
||||
host->clk_scaling.curr_freq = devfreq_max_clk;
|
||||
if (host->ios.clock < host->clk_scaling.freq_table[max_clk_idx])
|
||||
host->clk_scaling.curr_freq = devfreq_min_clk;
|
||||
host->clk_scaling.target_freq = host->clk_scaling.curr_freq;
|
||||
|
||||
err = devfreq_resume_device(host->clk_scaling.devfreq);
|
||||
if (err) {
|
||||
pr_err("%s: %s: failed to resume devfreq (%d)\n",
|
||||
mmc_hostname(host), __func__, err);
|
||||
} else {
|
||||
host->clk_scaling.is_suspended = false;
|
||||
pr_debug("%s: devfreq resumed\n", mmc_hostname(host));
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_resume_clk_scaling);
|
||||
|
||||
/**
|
||||
* mmc_exit_devfreq_clk_scaling() - Disable clock scaling
|
||||
* @host: pointer to mmc host structure
|
||||
*
|
||||
* Disable clock scaling permanently.
|
||||
*/
|
||||
int mmc_exit_clk_scaling(struct mmc_host *host)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!host) {
|
||||
pr_err("%s: bad host parameter\n", __func__);
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!mmc_can_scale_clk(host))
|
||||
return 0;
|
||||
|
||||
if (!host->clk_scaling.devfreq) {
|
||||
pr_err("%s: %s: no devfreq is assosiated with this device\n",
|
||||
mmc_hostname(host), __func__);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
err = mmc_suspend_clk_scaling(host);
|
||||
if (err) {
|
||||
pr_err("%s: %s: fail to suspend clock scaling (%d)\n",
|
||||
mmc_hostname(host), __func__, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = devfreq_remove_device(host->clk_scaling.devfreq);
|
||||
if (err) {
|
||||
pr_err("%s: remove devfreq failed (%d)\n",
|
||||
mmc_hostname(host), err);
|
||||
return err;
|
||||
}
|
||||
|
||||
dev_pm_opp_remove(mmc_classdev(host),
|
||||
host->clk_scaling.devfreq_profile.freq_table[0]);
|
||||
dev_pm_opp_remove(mmc_classdev(host),
|
||||
host->clk_scaling.devfreq_profile.freq_table[1]);
|
||||
|
||||
kfree(host->clk_scaling.devfreq_profile.freq_table);
|
||||
|
||||
host->clk_scaling.devfreq = NULL;
|
||||
atomic_set(&host->clk_scaling.devfreq_abort, 1);
|
||||
|
||||
kfree(host->clk_scaling.freq_table);
|
||||
host->clk_scaling.freq_table = NULL;
|
||||
|
||||
pr_debug("%s: devfreq was removed\n", mmc_hostname(host));
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_exit_clk_scaling);
|
||||
#endif
|
||||
|
||||
static inline void mmc_complete_cmd(struct mmc_request *mrq)
|
||||
{
|
||||
if (mrq->cap_cmd_during_tfr && !completion_done(&mrq->cmd_completion))
|
||||
|
|
@ -139,7 +821,10 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
|
|||
{
|
||||
struct mmc_command *cmd = mrq->cmd;
|
||||
int err = cmd->error;
|
||||
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
if (host->clk_scaling.is_busy_started)
|
||||
mmc_clk_scaling_stop_busy(host, true);
|
||||
#endif
|
||||
/* Flag re-tuning needed on CRC errors */
|
||||
if (cmd->opcode != MMC_SEND_TUNING_BLOCK &&
|
||||
cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200 &&
|
||||
|
|
@ -350,7 +1035,12 @@ int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
|
|||
err = mmc_mrq_prep(host, mrq);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
if (mmc_is_data_request(mrq)) {
|
||||
mmc_deferred_scaling(host);
|
||||
mmc_clk_scaling_start_busy(host, true);
|
||||
}
|
||||
#endif
|
||||
led_trigger_event(host->led, LED_FULL);
|
||||
__mmc_start_request(host, mrq);
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@
|
|||
struct mmc_host;
|
||||
struct mmc_card;
|
||||
struct mmc_request;
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
struct mmc_queue;
|
||||
#endif
|
||||
|
||||
#define MMC_CMD_RETRIES 3
|
||||
|
||||
|
|
@ -29,6 +32,9 @@ struct mmc_bus_ops {
|
|||
int (*shutdown)(struct mmc_host *);
|
||||
int (*hw_reset)(struct mmc_host *);
|
||||
int (*sw_reset)(struct mmc_host *);
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
int (*change_bus_speed)(struct mmc_host *host, unsigned long *freq);
|
||||
#endif
|
||||
};
|
||||
|
||||
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
|
||||
|
|
@ -57,7 +63,10 @@ void mmc_power_off(struct mmc_host *host);
|
|||
void mmc_power_cycle(struct mmc_host *host, u32 ocr);
|
||||
void mmc_set_initial_state(struct mmc_host *host);
|
||||
u32 mmc_vddrange_to_ocrmask(int vdd_min, int vdd_max);
|
||||
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
int mmc_clk_update_freq(struct mmc_host *host,
|
||||
unsigned long freq, enum mmc_load state);
|
||||
#endif
|
||||
static inline void mmc_delay(unsigned int ms)
|
||||
{
|
||||
if (ms <= 20)
|
||||
|
|
@ -89,6 +98,15 @@ void mmc_remove_host_debugfs(struct mmc_host *host);
|
|||
void mmc_add_card_debugfs(struct mmc_card *card);
|
||||
void mmc_remove_card_debugfs(struct mmc_card *card);
|
||||
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
extern bool mmc_can_scale_clk(struct mmc_host *host);
|
||||
extern int mmc_init_clk_scaling(struct mmc_host *host);
|
||||
extern int mmc_suspend_clk_scaling(struct mmc_host *host);
|
||||
extern int mmc_resume_clk_scaling(struct mmc_host *host);
|
||||
extern int mmc_exit_clk_scaling(struct mmc_host *host);
|
||||
extern void mmc_deferred_scaling(struct mmc_host *host);
|
||||
extern unsigned long mmc_get_max_frequency(struct mmc_host *host);
|
||||
#endif
|
||||
int mmc_execute_tuning(struct mmc_card *card);
|
||||
int mmc_hs200_to_hs400(struct mmc_card *card);
|
||||
int mmc_hs400_to_hs200(struct mmc_card *card);
|
||||
|
|
|
|||
|
|
@ -31,6 +31,11 @@
|
|||
|
||||
#define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev)
|
||||
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
#define MMC_DEVFRQ_DEFAULT_UP_THRESHOLD 35
|
||||
#define MMC_DEVFRQ_DEFAULT_DOWN_THRESHOLD 5
|
||||
#define MMC_DEVFRQ_DEFAULT_POLLING_MSEC 100
|
||||
#endif
|
||||
static DEFINE_IDA(mmc_host_ida);
|
||||
|
||||
static void mmc_host_classdev_release(struct device *dev)
|
||||
|
|
@ -161,6 +166,46 @@ static void mmc_retune_timer(struct timer_list *t)
|
|||
mmc_retune_needed(host);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
static int mmc_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_kcalloc(dev, sz, sizeof(*arr), GFP_KERNEL);
|
||||
if (!arr) {
|
||||
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;
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* mmc_of_parse() - parse host's device-tree node
|
||||
* @host: host whose node should be parsed.
|
||||
|
|
@ -176,6 +221,10 @@ int mmc_of_parse(struct mmc_host *host)
|
|||
u32 bus_width, drv_type, cd_debounce_delay_ms;
|
||||
int ret;
|
||||
bool cd_cap_invert, cd_gpio_invert = false;
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
const char *lower_bus_speed = NULL;
|
||||
struct device_node *np = dev->of_node;
|
||||
#endif
|
||||
|
||||
if (!dev || !dev_fwnode(dev))
|
||||
return 0;
|
||||
|
|
@ -337,6 +386,30 @@ int mmc_of_parse(struct mmc_host *host)
|
|||
device_property_read_u32(dev, "post-power-on-delay-ms",
|
||||
&host->ios.power_delay_ms);
|
||||
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
if (mmc_dt_get_array(dev, "qcom,devfreq,freq-table",
|
||||
&host->clk_scaling.pltfm_freq_table,
|
||||
&host->clk_scaling.pltfm_freq_table_sz, 0))
|
||||
pr_debug("%s: no clock scaling frequencies were supplied\n",
|
||||
dev_name(dev));
|
||||
else if (!host->clk_scaling.pltfm_freq_table ||
|
||||
!host->clk_scaling.pltfm_freq_table_sz)
|
||||
dev_info(dev, "bad dts clock scaling frequencies\n");
|
||||
|
||||
/*
|
||||
* Few hosts can support DDR52 mode at the same lower
|
||||
* system voltage corner as high-speed mode. In such
|
||||
* cases, it is always better to put it in DDR
|
||||
* mode which will improve the performance
|
||||
* without any power impact.
|
||||
*/
|
||||
if (!of_property_read_string(np, "qcom,scaling-lower-bus-speed-mode",
|
||||
&lower_bus_speed)) {
|
||||
if (!strcmp(lower_bus_speed, "DDR52"))
|
||||
host->clk_scaling.lower_bus_speed_mode |=
|
||||
MMC_SCALING_LOWER_DDR52_MODE;
|
||||
}
|
||||
#endif
|
||||
return mmc_pwrseq_alloc(host);
|
||||
}
|
||||
|
||||
|
|
@ -427,6 +500,9 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
|
|||
}
|
||||
|
||||
spin_lock_init(&host->lock);
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
atomic_set(&host->active_reqs, 0);
|
||||
#endif
|
||||
init_waitqueue_head(&host->wq);
|
||||
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
|
||||
INIT_DELAYED_WORK(&host->sdio_irq_work, sdio_irq_work);
|
||||
|
|
@ -451,6 +527,155 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
|
|||
|
||||
EXPORT_SYMBOL(mmc_alloc_host);
|
||||
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
static ssize_t enable_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
|
||||
if (!host)
|
||||
return -EINVAL;
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", mmc_can_scale_clk(host));
|
||||
}
|
||||
|
||||
static ssize_t enable_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
unsigned long value;
|
||||
|
||||
if (!host || !host->card || kstrtoul(buf, 0, &value))
|
||||
return -EINVAL;
|
||||
|
||||
mmc_get_card(host->card, NULL);
|
||||
|
||||
if (!value) {
|
||||
/* Suspend the clock scaling and mask host capability */
|
||||
if (host->clk_scaling.enable)
|
||||
mmc_suspend_clk_scaling(host);
|
||||
host->clk_scaling.enable = false;
|
||||
host->caps2 &= ~MMC_CAP2_CLK_SCALE;
|
||||
host->clk_scaling.state = MMC_LOAD_HIGH;
|
||||
/* Set to max. frequency when disabling */
|
||||
mmc_clk_update_freq(host, host->card->clk_scaling_highest,
|
||||
host->clk_scaling.state);
|
||||
} else if (value) {
|
||||
/* Unmask host capability and resume scaling */
|
||||
host->caps2 |= MMC_CAP2_CLK_SCALE;
|
||||
if (!host->clk_scaling.enable) {
|
||||
host->clk_scaling.enable = true;
|
||||
mmc_resume_clk_scaling(host);
|
||||
}
|
||||
}
|
||||
|
||||
mmc_put_card(host->card, NULL);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t up_threshold_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
|
||||
if (!host)
|
||||
return -EINVAL;
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", host->clk_scaling.upthreshold);
|
||||
}
|
||||
|
||||
#define MAX_PERCENTAGE 100
|
||||
static ssize_t up_threshold_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
unsigned long value;
|
||||
|
||||
if (!host || kstrtoul(buf, 0, &value) || (value > MAX_PERCENTAGE))
|
||||
return -EINVAL;
|
||||
|
||||
host->clk_scaling.upthreshold = value;
|
||||
|
||||
pr_debug("%s: clkscale_up_thresh set to %lu\n",
|
||||
mmc_hostname(host), value);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t down_threshold_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
|
||||
if (!host)
|
||||
return -EINVAL;
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n",
|
||||
host->clk_scaling.downthreshold);
|
||||
}
|
||||
|
||||
static ssize_t down_threshold_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
unsigned long value;
|
||||
|
||||
if (!host || kstrtoul(buf, 0, &value) || (value > MAX_PERCENTAGE))
|
||||
return -EINVAL;
|
||||
|
||||
host->clk_scaling.downthreshold = value;
|
||||
|
||||
pr_debug("%s: clkscale_down_thresh set to %lu\n",
|
||||
mmc_hostname(host), value);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t polling_interval_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
|
||||
if (!host)
|
||||
return -EINVAL;
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%lu milliseconds\n",
|
||||
host->clk_scaling.polling_delay_ms);
|
||||
}
|
||||
|
||||
static ssize_t polling_interval_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
unsigned long value;
|
||||
|
||||
if (!host || kstrtoul(buf, 0, &value))
|
||||
return -EINVAL;
|
||||
|
||||
host->clk_scaling.polling_delay_ms = value;
|
||||
|
||||
pr_debug("%s: clkscale_polling_delay_ms set to %lu\n",
|
||||
mmc_hostname(host), value);
|
||||
return count;
|
||||
}
|
||||
|
||||
DEVICE_ATTR_RW(enable);
|
||||
DEVICE_ATTR_RW(polling_interval);
|
||||
DEVICE_ATTR_RW(up_threshold);
|
||||
DEVICE_ATTR_RW(down_threshold);
|
||||
|
||||
static struct attribute *clk_scaling_attrs[] = {
|
||||
&dev_attr_enable.attr,
|
||||
&dev_attr_up_threshold.attr,
|
||||
&dev_attr_down_threshold.attr,
|
||||
&dev_attr_polling_interval.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group clk_scaling_attr_grp = {
|
||||
.name = "clk_scaling",
|
||||
.attrs = clk_scaling_attrs,
|
||||
};
|
||||
#endif
|
||||
/**
|
||||
* mmc_add_host - initialise host hardware
|
||||
* @host: mmc host
|
||||
|
|
@ -471,11 +696,23 @@ int mmc_add_host(struct mmc_host *host)
|
|||
return err;
|
||||
|
||||
led_trigger_register_simple(dev_name(&host->class_dev), &host->led);
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
host->clk_scaling.upthreshold = MMC_DEVFRQ_DEFAULT_UP_THRESHOLD;
|
||||
host->clk_scaling.downthreshold = MMC_DEVFRQ_DEFAULT_DOWN_THRESHOLD;
|
||||
host->clk_scaling.polling_delay_ms = MMC_DEVFRQ_DEFAULT_POLLING_MSEC;
|
||||
host->clk_scaling.skip_clk_scale_freq_update = false;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
mmc_add_host_debugfs(host);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
err = sysfs_create_group(&host->class_dev.kobj, &clk_scaling_attr_grp);
|
||||
if (err)
|
||||
pr_err("%s: failed to create clk scale sysfs group with err %d\n",
|
||||
__func__, err);
|
||||
#endif
|
||||
mmc_start_host(host);
|
||||
if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY))
|
||||
mmc_register_pm_notifier(host);
|
||||
|
|
@ -503,6 +740,9 @@ void mmc_remove_host(struct mmc_host *host)
|
|||
mmc_remove_host_debugfs(host);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
sysfs_remove_group(&host->class_dev.kobj, &clk_scaling_attr_grp);
|
||||
#endif
|
||||
device_del(&host->class_dev);
|
||||
|
||||
led_trigger_unregister_simple(host->led);
|
||||
|
|
|
|||
|
|
@ -296,6 +296,9 @@ static blk_status_t mmc_mq_queue_rq(struct blk_mq_hw_ctx *hctx,
|
|||
mq->busy = true;
|
||||
|
||||
mq->in_flight[issue_type] += 1;
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
atomic_inc(&host->active_reqs);
|
||||
#endif
|
||||
get_card = (mmc_tot_in_flight(mq) == 1);
|
||||
cqe_retune_ok = (mmc_cqe_qcnt(mq) == 1);
|
||||
|
||||
|
|
@ -335,6 +338,9 @@ static blk_status_t mmc_mq_queue_rq(struct blk_mq_hw_ctx *hctx,
|
|||
|
||||
spin_lock_irq(&mq->lock);
|
||||
mq->in_flight[issue_type] -= 1;
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
atomic_dec(&host->active_reqs);
|
||||
#endif
|
||||
if (mmc_tot_in_flight(mq) == 0)
|
||||
put_card = true;
|
||||
mq->busy = false;
|
||||
|
|
|
|||
|
|
@ -592,6 +592,60 @@ static int sd_set_current_limit(struct mmc_card *card, u8 *status)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
/**
|
||||
* mmc_sd_change_bus_speed() - Change SD card bus frequency at runtime
|
||||
* @host: pointer to mmc host structure
|
||||
* @freq: pointer to desired frequency to be set
|
||||
*
|
||||
* Change the SD card bus frequency at runtime after the card is
|
||||
* initialized. Callers are expected to make sure of the card's
|
||||
* state (DATA/RCV/TRANSFER) beforing changing the frequency at runtime.
|
||||
*
|
||||
* If the frequency to change is greater than max. supported by card,
|
||||
* *freq is changed to max. supported by card and if it is less than min.
|
||||
* supported by host, *freq is changed to min. supported by host.
|
||||
*/
|
||||
static int mmc_sd_change_bus_speed(struct mmc_host *host, unsigned long *freq)
|
||||
{
|
||||
int err = 0;
|
||||
struct mmc_card *card;
|
||||
|
||||
/*
|
||||
* Assign card pointer after claiming host to avoid race
|
||||
* conditions that may arise during removal of the card.
|
||||
*/
|
||||
card = host->card;
|
||||
|
||||
/* sanity checks */
|
||||
if (!card || !freq) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mmc_set_clock(host, (unsigned int) (*freq));
|
||||
|
||||
if (!mmc_host_is_spi(card->host) && mmc_card_uhs(card)
|
||||
&& card->host->ops->execute_tuning) {
|
||||
/*
|
||||
* We try to probe host driver for tuning for any
|
||||
* frequency, it is host driver responsibility to
|
||||
* perform actual tuning only when required.
|
||||
*/
|
||||
err = card->host->ops->execute_tuning(card->host,
|
||||
MMC_SEND_TUNING_BLOCK);
|
||||
if (err) {
|
||||
pr_warn("%s: %s: tuning execution failed %d. Restoring to previous clock %lu\n",
|
||||
mmc_hostname(card->host), __func__, err,
|
||||
host->clk_scaling.curr_freq);
|
||||
mmc_set_clock(host, host->clk_scaling.curr_freq);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* UHS-I specific initialization procedure
|
||||
*/
|
||||
|
|
@ -912,13 +966,24 @@ unsigned mmc_sd_get_max_clock(struct mmc_card *card)
|
|||
{
|
||||
unsigned max_dtr = (unsigned int)-1;
|
||||
|
||||
if (mmc_card_hs(card)) {
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
if (mmc_card_uhs(card)) {
|
||||
if (max_dtr > card->sw_caps.uhs_max_dtr)
|
||||
max_dtr = card->sw_caps.uhs_max_dtr;
|
||||
} else if (mmc_card_hs(card)) {
|
||||
if (max_dtr > card->sw_caps.hs_max_dtr)
|
||||
max_dtr = card->sw_caps.hs_max_dtr;
|
||||
} else if (max_dtr > card->csd.max_dtr) {
|
||||
max_dtr = card->csd.max_dtr;
|
||||
}
|
||||
|
||||
#else
|
||||
if (mmc_card_hs(card)) {
|
||||
if (max_dtr > card->sw_caps.uhs_max_dtr)
|
||||
max_dtr = card->sw_caps.uhs_max_dtr;
|
||||
} else if (max_dtr > card->csd.max_dtr) {
|
||||
max_dtr = card->csd.max_dtr;
|
||||
}
|
||||
#endif
|
||||
return max_dtr;
|
||||
}
|
||||
|
||||
|
|
@ -1089,6 +1154,11 @@ retry:
|
|||
err = -EINVAL;
|
||||
goto free_card;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
card->clk_scaling_highest = mmc_sd_get_max_clock(card);
|
||||
card->clk_scaling_lowest = host->f_min;
|
||||
#endif
|
||||
done:
|
||||
host->card = card;
|
||||
return 0;
|
||||
|
|
@ -1105,6 +1175,9 @@ free_card:
|
|||
*/
|
||||
static void mmc_sd_remove(struct mmc_host *host)
|
||||
{
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
mmc_exit_clk_scaling(host);
|
||||
#endif
|
||||
mmc_remove_card(host->card);
|
||||
host->card = NULL;
|
||||
}
|
||||
|
|
@ -1146,7 +1219,14 @@ static void mmc_sd_detect(struct mmc_host *host)
|
|||
static int _mmc_sd_suspend(struct mmc_host *host)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
err = mmc_suspend_clk_scaling(host);
|
||||
if (err) {
|
||||
pr_err("%s: %s: fail to suspend clock scaling (%d)\n",
|
||||
mmc_hostname(host), __func__, err);
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
mmc_claim_host(host);
|
||||
|
||||
if (mmc_card_suspended(host->card))
|
||||
|
|
@ -1198,6 +1278,14 @@ static int _mmc_sd_resume(struct mmc_host *host)
|
|||
err = mmc_sd_init_card(host, host->card->ocr, host->card);
|
||||
mmc_card_clr_suspended(host->card);
|
||||
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
err = mmc_resume_clk_scaling(host);
|
||||
if (err) {
|
||||
pr_err("%s: %s: fail to resume clock scaling (%d)\n",
|
||||
mmc_hostname(host), __func__, err);
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
out:
|
||||
mmc_release_host(host);
|
||||
return err;
|
||||
|
|
@ -1261,6 +1349,9 @@ static const struct mmc_bus_ops mmc_sd_ops = {
|
|||
.alive = mmc_sd_alive,
|
||||
.shutdown = mmc_sd_suspend,
|
||||
.hw_reset = mmc_sd_hw_reset,
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
.change_bus_speed = mmc_sd_change_bus_speed,
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -1321,6 +1412,13 @@ int mmc_attach_sd(struct mmc_host *host)
|
|||
goto remove_card;
|
||||
|
||||
mmc_claim_host(host);
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
err = mmc_init_clk_scaling(host);
|
||||
if (err) {
|
||||
mmc_release_host(host);
|
||||
goto remove_card;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
remove_card:
|
||||
|
|
|
|||
|
|
@ -1019,3 +1019,11 @@ config MMC_SDHCI_AM654
|
|||
If you have a controller with this interface, say Y or M here.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config SDC_QTI
|
||||
tristate "Enable QTI specific code in MMC upstream driver"
|
||||
depends on QGKI && MMC_SDHCI_MSM
|
||||
default n
|
||||
help
|
||||
This configuration flag allows adding QTI code in
|
||||
MMC upstream driver.
|
||||
|
|
|
|||
|
|
@ -3217,6 +3217,9 @@ static int sdhci_msm_probe(struct platform_device *pdev)
|
|||
|
||||
msm_host->pltfm_init_done = true;
|
||||
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
msm_host->mmc->caps2 |= MMC_CAP2_CLK_SCALE;
|
||||
#endif
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
|
|
|||
|
|
@ -244,6 +244,14 @@ struct mmc_card {
|
|||
struct mmc_host *host; /* the host this device belongs to */
|
||||
struct device dev; /* the device */
|
||||
u32 ocr; /* the current OCR setting */
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
unsigned long clk_scaling_lowest; /* lowest scaleable
|
||||
* frequency
|
||||
*/
|
||||
unsigned long clk_scaling_highest; /* highest scaleable
|
||||
* frequency
|
||||
*/
|
||||
#endif
|
||||
unsigned int rca; /* relative card address of device */
|
||||
unsigned int type; /* card type */
|
||||
#define MMC_TYPE_MMC 0 /* MMC card */
|
||||
|
|
@ -307,7 +315,9 @@ struct mmc_card {
|
|||
struct dentry *debugfs_root;
|
||||
struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */
|
||||
unsigned int nr_parts;
|
||||
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
unsigned int part_curr;
|
||||
#endif
|
||||
unsigned int bouncesz; /* Bounce buffer size */
|
||||
struct workqueue_struct *complete_wq; /* Private workqueue */
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@
|
|||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/device.h>
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
#include <linux/devfreq.h>
|
||||
#endif
|
||||
#include <linux/fault-inject.h>
|
||||
|
||||
#include <linux/mmc/core.h>
|
||||
|
|
@ -268,9 +271,76 @@ struct mmc_ctx {
|
|||
struct task_struct *task;
|
||||
};
|
||||
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
enum dev_state {
|
||||
DEV_SUSPENDING = 1,
|
||||
DEV_SUSPENDED,
|
||||
DEV_RESUMED,
|
||||
};
|
||||
|
||||
enum mmc_load {
|
||||
MMC_LOAD_HIGH,
|
||||
MMC_LOAD_LOW,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mmc_devfeq_clk_scaling - main context for MMC clock scaling logic
|
||||
*
|
||||
* @lock: spinlock to protect statistics
|
||||
* @devfreq: struct that represent mmc-host as a client for devfreq
|
||||
* @devfreq_profile: MMC device profile, mostly polling interval and callbacks
|
||||
* @ondemand_gov_data: struct supplied to ondemmand governor (thresholds)
|
||||
* @state: load state, can be HIGH or LOW. used to notify mmc_host_ops callback
|
||||
* @start_busy: timestamped armed once a data request is started
|
||||
* @measure_interval_start: timestamped armed once a measure interval started
|
||||
* @devfreq_abort: flag to sync between different contexts relevant to devfreq
|
||||
* @skip_clk_scale_freq_update: flag that enable/disable frequency change
|
||||
* @freq_table_sz: table size of frequencies supplied to devfreq
|
||||
* @freq_table: frequencies table supplied to devfreq
|
||||
* @curr_freq: current frequency
|
||||
* @polling_delay_ms: polling interval for status collection used by devfreq
|
||||
* @upthreshold: up-threshold supplied to ondemand governor
|
||||
* @downthreshold: down-threshold supplied to ondemand governor
|
||||
* @need_freq_change: flag indicating if a frequency change is required
|
||||
* @is_busy_started: flag indicating if a request is handled by the HW
|
||||
* @enable: flag indicating if the clock scaling logic is enabled for this host
|
||||
* @is_suspended: to make devfreq request queued when mmc is suspened
|
||||
*/
|
||||
struct mmc_devfeq_clk_scaling {
|
||||
spinlock_t lock;
|
||||
struct devfreq *devfreq;
|
||||
struct devfreq_dev_profile devfreq_profile;
|
||||
struct devfreq_simple_ondemand_data ondemand_gov_data;
|
||||
enum mmc_load state;
|
||||
ktime_t start_busy;
|
||||
ktime_t measure_interval_start;
|
||||
atomic_t devfreq_abort;
|
||||
bool skip_clk_scale_freq_update;
|
||||
int freq_table_sz;
|
||||
int pltfm_freq_table_sz;
|
||||
u32 *freq_table;
|
||||
u32 *pltfm_freq_table;
|
||||
unsigned long total_busy_time_us;
|
||||
unsigned long target_freq;
|
||||
unsigned long curr_freq;
|
||||
unsigned long polling_delay_ms;
|
||||
unsigned int upthreshold;
|
||||
unsigned int downthreshold;
|
||||
unsigned int lower_bus_speed_mode;
|
||||
#define MMC_SCALING_LOWER_DDR52_MODE 1
|
||||
bool need_freq_change;
|
||||
bool is_busy_started;
|
||||
bool enable;
|
||||
bool is_suspended;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct mmc_host {
|
||||
struct device *parent;
|
||||
struct device class_dev;
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
struct mmc_devfeq_clk_scaling clk_scaling;
|
||||
#endif
|
||||
int index;
|
||||
const struct mmc_host_ops *ops;
|
||||
struct mmc_pwrseq *pwrseq;
|
||||
|
|
@ -369,7 +439,9 @@ struct mmc_host {
|
|||
#define MMC_CAP2_CQE_DCMD (1 << 24) /* CQE can issue a direct command */
|
||||
#define MMC_CAP2_AVOID_3_3V (1 << 25) /* Host must negotiate down from 3.3V */
|
||||
#define MMC_CAP2_MERGE_CAPABLE (1 << 26) /* Host can merge a segment over the segment size */
|
||||
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
#define MMC_CAP2_CLK_SCALE (1 << 27) /* Allow dynamic clk scaling */
|
||||
#endif
|
||||
int fixed_drv_type; /* fixed driver type for non-removable media */
|
||||
|
||||
mmc_pm_flag_t pm_caps; /* supported pm features */
|
||||
|
|
@ -461,7 +533,9 @@ struct mmc_host {
|
|||
int cqe_qdepth;
|
||||
bool cqe_enabled;
|
||||
bool cqe_on;
|
||||
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
atomic_t active_reqs;
|
||||
#endif
|
||||
unsigned long private[0] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue