From c8296f389b3b20042fd3c3e3fc2f3dfde012b7c4 Mon Sep 17 00:00:00 2001 From: Ram Prakash Gupta Date: Mon, 23 Jul 2018 16:47:59 +0530 Subject: [PATCH] mmc: host: sdhci-msm: Temperature controlled clock scaling Clock scaling based on measured msm temperature is implemented. Used tsens API to check msm temperature and register call back when temperature changes beyond set threshold value of enable and disable clock scaling. Late init is introduced to initialize sdcard after tsens driver. Change-Id: Ie7e04c0ee086e3a54683d3b605e85a6405bf6c78 Signed-off-by: Ram Prakash Gupta --- drivers/mmc/host/sdhci-msm.c | 178 +++++++++++++++++++++++++++++++++++ drivers/mmc/host/sdhci-msm.h | 12 ++- drivers/mmc/host/sdhci.c | 24 +++++ drivers/mmc/host/sdhci.h | 3 + 4 files changed, 216 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 81a781c1f9d6..0468ea464055 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -40,6 +40,8 @@ #include #include #include +#include +#include #include "sdhci-msm.h" #include "sdhci-msm-ice.h" @@ -169,6 +171,8 @@ #define MAX_DRV_TYPES_SUPPORTED_HS200 4 #define MSM_AUTOSUSPEND_DELAY_MS 100 +#define CENTI_DEGREE_TO_DEGREE 10 + struct sdhci_msm_offset { u32 CORE_MCI_DATA_CNT; u32 CORE_MCI_STATUS; @@ -3534,6 +3538,151 @@ int sdhci_msm_notify_load(struct sdhci_host *host, enum mmc_load state) return 0; } +static void sdhci_msm_tsens_threshold_notify( + struct therm_threshold *tsens_cb_data) +{ + struct threshold_info *info = tsens_cb_data->parent; + struct sdhci_msm_host *msm_host = container_of(info, + struct sdhci_msm_host, tsens_threshold_config); + int ret = 0; + + pr_debug("%s: Triggered tsens-notification type=%d zone_id =%d\n", + mmc_hostname(msm_host->mmc), tsens_cb_data->trip_triggered, + tsens_cb_data->sensor_id); + + switch (tsens_cb_data->trip_triggered) { + case THERMAL_TRIP_CONFIGURABLE_HI: + atomic_set(&msm_host->clk_scaling_disable, 0); + break; + case THERMAL_TRIP_CONFIGURABLE_LOW: + atomic_set(&msm_host->clk_scaling_disable, 1); + break; + default: + pr_err("%s: trip type %d not supported\n", + mmc_hostname(msm_host->mmc), + tsens_cb_data->trip_triggered); + break; + } + + ret = sensor_mgr_set_threshold(tsens_cb_data->sensor_id, + tsens_cb_data->threshold); + if (ret < 0) + pr_err("%s: failed to set threshold temp, ret==%d\n", + __func__, ret); +} + +static int sdhci_msm_check_tsens(struct sdhci_msm_host *msm_host) +{ + int ret = 0; + int temp = 0; + bool disable; + struct tsens_device tsens_dev; + + if (tsens_is_ready() > 0) { + tsens_dev.sensor_num = msm_host->tsens_id; + ret = tsens_get_temp(&tsens_dev, &temp); + if (ret < 0) { + pr_err("%s: failed to read tsens, ret = %d\n", + mmc_hostname(msm_host->mmc), ret); + return ret; + } + /* convert centidegree to degree*/ + temp /= CENTI_DEGREE_TO_DEGREE; + disable = temp <= msm_host->disable_scaling_threshold_temp; + if (disable) + atomic_set(&msm_host->clk_scaling_disable, 1); + } + return ret; +} + +static int sdhci_msm_register_cb(struct sdhci_msm_host *msm_host) +{ + int ret; + + ret = sdhci_msm_check_tsens(msm_host); + if (ret) { + pr_err("%s: unable to check tsens\n", + mmc_hostname(msm_host->mmc)); + return ret; + } + + ret = sensor_mgr_init_threshold(&msm_host->tsens_threshold_config, + msm_host->tsens_id, + msm_host->enable_scaling_threshold_temp,/*high*/ + msm_host->disable_scaling_threshold_temp,/*low*/ + sdhci_msm_tsens_threshold_notify); + if (ret) { + pr_err("%s: failed to register cb for tsens, ret = %d\n", + mmc_hostname(msm_host->mmc), ret); + return ret; + } + + ret = sensor_mgr_convert_id_and_set_threshold( + &msm_host->tsens_threshold_config); + if (ret) { + pr_err("%s: failed to set tsens threshold, ret = %d\n", + mmc_hostname(msm_host->mmc), ret); + return ret; + } + return ret; +} + +static int sdhci_msm_tsens_pltfm_init(struct sdhci_msm_host *msm_host) +{ + int ret = 0; + struct device *dev = &msm_host->pdev->dev; + struct device_node *np = dev->of_node; + + of_property_read_u32(np, "qcom,tsens-id", &msm_host->tsens_id); + of_property_read_s32(np, "qcom,disable_scaling_threshold_temp", + &msm_host->disable_scaling_threshold_temp); + of_property_read_s32(np, "qcom,enable_scaling_threshold_temp", + &msm_host->enable_scaling_threshold_temp); + + if (msm_host->tsens_id) + msm_host->temp_control_scaling = true; + else + msm_host->temp_control_scaling = false; + + atomic_set(&msm_host->clk_scaling_disable, 0); + return ret; +} + +static int sdhci_msm_dereg_temp_callback(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = pltfm_host->priv; + + if (msm_host->temp_control_scaling) + sensor_mgr_remove_threshold( + &msm_host->tsens_threshold_config); + return 0; +} + +static int sdhci_msm_reg_temp_callback(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = pltfm_host->priv; + int ret = 0; + + if (msm_host->temp_control_scaling) { + ret = sdhci_msm_register_cb(msm_host); + if (ret) + pr_err("%s: failed register temp monitoring call back, ret = %d\n", + mmc_hostname(msm_host->mmc), ret); + } + return ret; +} + +static int sdhci_msm_check_temp(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = pltfm_host->priv; + + return atomic_read(&msm_host->clk_scaling_disable); +} + + void sdhci_msm_reset_workaround(struct sdhci_host *host, u32 enable) { u32 vendor_func2; @@ -4080,6 +4229,9 @@ static struct sdhci_ops sdhci_msm_ops = { .clear_set_dumpregs = sdhci_msm_clear_set_dumpregs, .enhanced_strobe_mask = sdhci_msm_enhanced_strobe_mask, .notify_load = sdhci_msm_notify_load, + .check_temp = sdhci_msm_check_temp, + .reg_temp_callback = sdhci_msm_reg_temp_callback, + .dereg_temp_callback = sdhci_msm_dereg_temp_callback, .reset_workaround = sdhci_msm_reset_workaround, .init = sdhci_msm_init, .pre_req = sdhci_msm_pre_req, @@ -4650,6 +4802,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) MMC_CAP2_PACKED_WR_CONTROL); } + sdhci_msm_tsens_pltfm_init(msm_host); init_completion(&msm_host->pwr_irq_completion); if (gpio_is_valid(msm_host->pdata->status_gpio)) { @@ -5073,5 +5226,30 @@ static struct platform_driver sdhci_msm_driver = { module_platform_driver(sdhci_msm_driver); +static const struct of_device_id late_sdhci_msm_dt_match[] = { + {.compatible = "qcom,late-sdhci-msm"}, + {.compatible = "qcom,sdhci-msm-v5"}, + {}, +}; +MODULE_DEVICE_TABLE(of, late_sdhci_msm_dt_match); + +static struct platform_driver late_sdhci_msm_driver = { + .probe = sdhci_msm_probe, + .remove = sdhci_msm_remove, + .driver = { + .name = "late_sdhci_msm", + .owner = THIS_MODULE, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .of_match_table = late_sdhci_msm_dt_match, + .pm = SDHCI_MSM_PMOPS, + }, +}; + +static int __init late_sdhci_msm_init_driver(void) +{ + return platform_driver_register(&late_sdhci_msm_driver); +} +late_initcall(late_sdhci_msm_init_driver); + MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Secure Digital Host Controller Interface driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/sdhci-msm.h b/drivers/mmc/host/sdhci-msm.h index 79949c2c537f..bb15cd58c664 100644 --- a/drivers/mmc/host/sdhci-msm.h +++ b/drivers/mmc/host/sdhci-msm.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -17,6 +17,8 @@ #include #include +#include +#include #include "sdhci-pltfm.h" /* This structure keeps information per regulator */ @@ -227,6 +229,14 @@ struct sdhci_msm_host { const struct sdhci_msm_offset *offset; bool core_3_0v_support; bool pltfm_init_done; + + /* temperature controlled scaling */ + int tsens_id; + int disable_scaling_threshold_temp; + int enable_scaling_threshold_temp; + bool temp_control_scaling; + atomic_t clk_scaling_disable; + struct threshold_info tsens_threshold_config; }; extern char *saved_command_line; diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 40a08a520861..b96f27f861ba 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1583,6 +1583,27 @@ static void sdhci_notify_halt(struct mmc_host *mmc, bool halt) } } +static int sdhci_reg_temp_callback(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + + return host->ops->reg_temp_callback(host); +} + +static int sdhci_check_temp(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + + return host->ops->check_temp(host); +} + +static int sdhci_dereg_temp_callback(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + + return host->ops->dereg_temp_callback(host); +} + static inline void sdhci_update_power_policy(struct sdhci_host *host, enum sdhci_power_policy policy) { @@ -2769,6 +2790,9 @@ static const struct mmc_host_ops sdhci_ops = { .notify_load = sdhci_notify_load, .notify_halt = sdhci_notify_halt, .force_err_irq = sdhci_force_err_irq, + .check_temp = sdhci_check_temp, + .dereg_temp_callback = sdhci_dereg_temp_callback, + .reg_temp_callback = sdhci_reg_temp_callback, }; /*****************************************************************************\ diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 81aecb90ac8d..86b7066a81c2 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -691,6 +691,9 @@ struct sdhci_ops { unsigned int max_dtr, int host_drv, int card_drv, int *drv_type); int (*notify_load)(struct sdhci_host *host, enum mmc_load state); + int (*check_temp)(struct sdhci_host *host); + int (*reg_temp_callback)(struct sdhci_host *host); + int (*dereg_temp_callback)(struct sdhci_host *host); void (*reset_workaround)(struct sdhci_host *host, u32 enable); void (*init)(struct sdhci_host *host); void (*pre_req)(struct sdhci_host *host, struct mmc_request *req);