/* Copyright (c) 2014, 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 * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #define pr_fmt(fmt) "%s: " fmt, __func__ #include #include #include #include #include #include #include #include #include #include struct boost_dynamic_controller { /* ADC_TM parameters */ struct qpnp_adc_tm_chip *boost_adc_tm_dev; struct qpnp_adc_tm_btm_param boost_monitor_params; u32 vph_high_thresh_uv; u32 vph_low_thresh_uv; /* Boost mode parameters */ int old_boost_mode; /* RPM control parameters */ const char *boost_resource_type; const char *boost_resource_key; int resource_type; u32 resource_key; u32 resource_id; struct mutex boost_mutex; }; #define BOOST_DYNAMIC_CONTROLLER_DRIVER_NAME "qcom,boost-dynamic-controller" static u32 rpm_vreg_string_to_int(const u8 *str) { int i, len; u32 output = 0; len = strnlen(str, sizeof(u32)); for (i = 0; i < len; i++) output |= str[i] << (i * 8); return output; } static int boost_dynamic_controller_parse_dt( struct boost_dynamic_controller *boost_controller, struct device_node *of_node) { int ret; ret = of_property_read_u32(of_node, "qcom,boost-dynamic-controller-vph-high-threshold-uv", &boost_controller->vph_high_thresh_uv); if (ret < 0) { pr_err("qcom,boost-dynamic-controller-vph-high-threshold-uv is missing, ret = %d", ret); return ret; } ret = of_property_read_u32(of_node, "qcom,boost-dynamic-controller-vph-low-threshold-uv", &boost_controller->vph_low_thresh_uv); if (ret < 0) { pr_err("qcom,boost-dynamic-controller-vph-low-threshold-uv is missing, ret = %d", ret); return ret; } ret = of_property_read_u32(of_node, "qcom,boost-dynamic-controller-boost-resource-id", &boost_controller->resource_id); if (ret < 0) { pr_err("qcom,boost-dynamic-controller-boost-resource-id is missing, ret = %d", ret); return ret; } ret = of_property_read_string(of_node, "qcom,boost-dynamic-controller-boost-resource-type", &boost_controller->boost_resource_type); if (ret < 0) { pr_err("qcom,boost-dynamic-controller-boost-resource-type is missing, ret = %d", ret); return ret; } ret = of_property_read_string(of_node, "qcom,boost-dynamic-controller-boost-resource-key", &boost_controller->boost_resource_key); if (ret < 0) { pr_err("qcom,boost-dynamic-controller-boost-resource-key is missing, ret = %d", ret); return ret; } return ret; } static int boost_rpm_send_message( struct boost_dynamic_controller *boost_controller, u32 data) { int ret; struct msm_rpm_kvp kvp = { .key = boost_controller->resource_key, .data = (void *)&data, .length = sizeof(data), }; ret = msm_rpm_send_message(MSM_RPM_CTX_ACTIVE_SET, boost_controller->resource_type, boost_controller->resource_id, &kvp, 1); if (ret < 0) pr_err("failed to inform RPM! (err = %d)\n", ret); return ret; } static void boost_dynamic_controller_notification(enum qpnp_tm_state state, void *ctx) { struct boost_dynamic_controller *boost_controller = ctx; int ret; mutex_lock(&boost_controller->boost_mutex); if (state == ADC_TM_LOW_STATE && boost_controller->old_boost_mode != ADC_TM_LOW_STATE) { ret = boost_rpm_send_message(boost_controller, 1); if (ret) { pr_err("sending boost mode to RPM failed, ret=%d\n", ret); boost_controller->boost_monitor_params.state_request = ADC_TM_LOW_THR_ENABLE; } else { boost_controller->old_boost_mode = ADC_TM_LOW_STATE; boost_controller->boost_monitor_params.state_request = ADC_TM_HIGH_THR_ENABLE; } } else if (state == ADC_TM_HIGH_STATE && boost_controller->old_boost_mode != ADC_TM_HIGH_STATE) { ret = boost_rpm_send_message(boost_controller, 0); if (ret) { pr_err("sending boost mode to RPM failed, ret=%d\n", ret); boost_controller->boost_monitor_params.state_request = ADC_TM_HIGH_THR_ENABLE; } else { boost_controller->old_boost_mode = ADC_TM_HIGH_STATE; boost_controller->boost_monitor_params.state_request = ADC_TM_LOW_THR_ENABLE; } } mutex_unlock(&boost_controller->boost_mutex); qpnp_adc_tm_channel_measure(boost_controller->boost_adc_tm_dev, &boost_controller->boost_monitor_params); } static int boost_dynamic_controller_setup_vph_monitoring( struct boost_dynamic_controller *boost_controller, struct device *dev) { int ret; char *key = NULL; struct qpnp_vadc_chip *vadc_dev; struct qpnp_vadc_result adc_result; key = "vph-threshold"; boost_controller->boost_adc_tm_dev = qpnp_get_adc_tm(dev, key); if (IS_ERR(boost_controller->boost_adc_tm_dev)) { ret = PTR_ERR(boost_controller->boost_adc_tm_dev); if (ret != -EPROBE_DEFER) pr_err("adc_tm property missing, ret=%d\n", ret); return ret; } boost_controller->boost_monitor_params.high_thr = boost_controller->vph_high_thresh_uv; boost_controller->boost_monitor_params.low_thr = boost_controller->vph_low_thresh_uv; boost_controller->boost_monitor_params.channel = VSYS; boost_controller->boost_monitor_params.btm_ctx = (void *)boost_controller; boost_controller->boost_monitor_params.timer_interval = ADC_MEAS1_INTERVAL_1S; boost_controller->boost_monitor_params.threshold_notification = &boost_dynamic_controller_notification; key = "vph"; vadc_dev = qpnp_get_vadc(dev, key); if (IS_ERR(vadc_dev)) { ret = PTR_ERR(vadc_dev); if (ret != -EPROBE_DEFER) pr_err("vadc property missing, ret=%d\n", ret); return ret; } ret = qpnp_vadc_read(vadc_dev, VSYS, &adc_result); if (ret) { pr_err("Unable to read VPH ret=%d\n", ret); return ret; } if (adc_result.physical > boost_controller->vph_high_thresh_uv) { ret = boost_rpm_send_message(boost_controller, 0); if (ret) { pr_err("sending boost mode to RPM failed, ret=%d\n", ret); return ret; } boost_controller->old_boost_mode = ADC_TM_HIGH_STATE; boost_controller->boost_monitor_params.state_request = ADC_TM_LOW_THR_ENABLE; } else { ret = boost_rpm_send_message(boost_controller, 1); if (ret) { pr_err("sending boost mode to RPM failed, ret=%d\n", ret); return ret; } boost_controller->old_boost_mode = ADC_TM_LOW_STATE; boost_controller->boost_monitor_params.state_request = ADC_TM_HIGH_THR_ENABLE; } ret = qpnp_adc_tm_channel_measure(boost_controller->boost_adc_tm_dev, &boost_controller->boost_monitor_params); if (ret) { pr_err("adc-tm setup failed: ret = %d\n", ret); return ret; } return ret; } static int boost_dynamic_controller_probe(struct platform_device *pdev) { struct boost_dynamic_controller *boost_controller = NULL; struct device *dev = &pdev->dev; int ret; boost_controller = devm_kzalloc(dev, sizeof(struct boost_dynamic_controller), GFP_KERNEL); if (!boost_controller) { dev_err(dev, "Cannot allocate boost_dynamic_controller\n"); return -ENOMEM; } if (!dev->of_node) { dev_err(dev, "Device tree node is missing\n"); return -EINVAL; } ret = boost_dynamic_controller_parse_dt(boost_controller, dev->of_node); if (ret) { pr_err("Wrong DT parameter specified: ret = %d\n", ret); return ret; } boost_controller->resource_type = rpm_vreg_string_to_int(boost_controller->boost_resource_type); boost_controller->resource_key = rpm_vreg_string_to_int(boost_controller->boost_resource_key); mutex_init(&boost_controller->boost_mutex); ret = boost_dynamic_controller_setup_vph_monitoring(boost_controller, dev); if (ret) { mutex_destroy(&boost_controller->boost_mutex); if (ret != -EPROBE_DEFER) pr_err("Vph monitor setup failure, ret = %d\n", ret); return ret; } return ret; } static int boost_dynamic_controller_remove(struct platform_device *pdev) { struct boost_dynamic_controller *boost_controller; boost_controller = platform_get_drvdata(pdev); mutex_destroy(&boost_controller->boost_mutex); return 0; } static struct of_device_id boost_dynamic_controller_match_table[] = { { .compatible = BOOST_DYNAMIC_CONTROLLER_DRIVER_NAME, }, {} }; static struct platform_driver boost_dynamic_controller_driver = { .driver = { .name = BOOST_DYNAMIC_CONTROLLER_DRIVER_NAME, .of_match_table = boost_dynamic_controller_match_table, .owner = THIS_MODULE, }, .probe = boost_dynamic_controller_probe, .remove = boost_dynamic_controller_remove, }; static int __init boost_dynamic_controller_init(void) { return platform_driver_register(&boost_dynamic_controller_driver); } module_init(boost_dynamic_controller_init); static void __exit boost_dynamic_controller_exit(void) { platform_driver_unregister(&boost_dynamic_controller_driver); } module_exit(boost_dynamic_controller_exit); MODULE_DESCRIPTION("Boost dynamic controller driver"); MODULE_LICENSE("GPL v2");