Loading Documentation/devicetree/bindings/pil/inrush-current-mitigation.txt 0 → 100644 +25 −0 Original line number Diff line number Diff line In Rush Current Mitigation driver: On recent targets, APSS L2 memories are moved to APC domain which were earlier on Mx domain. Analysis suggests that on current targets APSS L2 memories provide reverse capacitance on Mx and this used to provide buffer while powering ON Q6 L2. Now due to L2s moving to APC, the cushion is not available. So there is a chance of droop when Q6 L2 memories are being powered up. During Q6 low power modes L2 is kept in retention only and never collapsed. So the cases where it needs to be taken care is during PIL(modem and adsp bringup during boot). So in cold boot path before bringing up modem or adsp, turn on MM memories. Hence providing a intermediate load. Required properties: - compatible: Must be qcom,msm-inrush-current-mitigation - qcom,dependent-subsystems: List of subsystems which need the intermediate load - vdd-supply: gdsc handle to switch on memory. Example: qcom,inrush-current { compatible = "qcom,msm-inrush-current-mitigation"; qcom,dependent-subsystems = "modem", "adsp"; vdd-supply = <&gdsc_mdss>; }; drivers/soc/qcom/Kconfig +8 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,14 @@ config CP_ACCESS64 If unsure, say N. config MSM_INRUSH_CURRENT_MITIGATION bool "Inrush-current mitigation Driver" help This driver helps in mitigating in-rush current on MSM chipsets which has voltage droop issues due to sudden huge load on a rail. This driver introduces an intermediate load to mitigate the in-rush current. config MSM_QDSP6_APRV2 bool "Audio QDSP6 APRv2 support" depends on MSM_SMD Loading drivers/soc/qcom/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -2,6 +2,7 @@ CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1) ccflags-$(CONFIG_MSM_QBT1000) += -Idrivers/misc/ obj-$(CONFIG_MSM_INRUSH_CURRENT_MITIGATION) += inrush-current-mitigation.o obj-$(CONFIG_MSM_GLADIATOR_ERP) += gladiator_erp.o obj-$(CONFIG_ARM64) += idle-v8.o cpu_ops.o obj-$(CONFIG_MSM_BOOT_STATS) += boot_stats.o Loading drivers/soc/qcom/inrush-current-mitigation.c 0 → 100644 +168 −0 Original line number Diff line number Diff line /* * Copyright (c) 2015, 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. */ #include <linux/slab.h> #include <linux/errno.h> #include <linux/platform_device.h> #include <linux/module.h> #include <linux/notifier.h> #include <linux/of.h> #include <linux/async.h> #include <linux/clk/gdsc.h> #include <linux/regulator/consumer.h> #include <soc/qcom/subsystem_notif.h> struct subsystem { const char *name; void *notif_handle; struct notifier_block nb; bool booted; struct inrush_driver_data *drv_data; }; struct inrush_driver_data { int subsys_count; int subsys_boot_count; struct regulator *vreg; /* Must be the last member */ struct subsystem *subsystems; }; #define notifier_to_subsystem(d) container_of(d, struct subsystem, nb) static void free_resources(void *data, async_cookie_t cookie) { struct inrush_driver_data *drv_data = data; struct subsystem *subsys; int i; gdsc_allow_clear_retention(drv_data->vreg); devm_regulator_put(drv_data->vreg); for (i = 0; i < drv_data->subsys_count; i++) { subsys = &drv_data->subsystems[i]; subsys_notif_unregister_notifier(subsys->notif_handle, &subsys->nb); } kfree(drv_data); pr_info("inrush-current-mitigation driver exited\n"); } static int mitigate_inrush_notifier_cb(struct notifier_block *nb, unsigned long code, void *ss_handle) { struct subsystem *subsys = notifier_to_subsystem(nb); struct inrush_driver_data *drv_data = subsys->drv_data; if (subsys->booted) return NOTIFY_DONE; switch (code) { case SUBSYS_AFTER_POWERUP: pr_info("%s: subsystem %s has completed powerup\n", __func__, subsys->name); subsys->booted = true; drv_data->subsys_boot_count++; break; } /* * If all subsystems are up, job of this driver ends, lets * free resources. */ if (drv_data->subsys_count == drv_data->subsys_boot_count) async_schedule(free_resources, drv_data); return NOTIFY_DONE; } static int mitigate_inrush_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; int i, retval; struct subsystem *subsys; struct inrush_driver_data *drv_data; retval = of_property_count_strings(np, "qcom,dependent-subsystems"); if (IS_ERR_VALUE(retval)) { dev_err(dev, "Failed to get dependent subsystems\n"); return -EINVAL; } drv_data = kzalloc((retval * sizeof(struct subsystem) + sizeof(struct inrush_driver_data)), GFP_KERNEL); if (!drv_data) return -ENOMEM; drv_data->subsystems = (void *)drv_data + sizeof(struct inrush_driver_data); drv_data->subsys_count = retval; for (i = 0; i < drv_data->subsys_count; i++) { subsys = &drv_data->subsystems[i]; subsys->drv_data = drv_data; of_property_read_string_index(np, "qcom,dependent-subsystems", i, &subsys->name); subsys->nb.notifier_call = mitigate_inrush_notifier_cb; subsys->notif_handle = subsys_notif_register_notifier(subsys->name, &subsys->nb); if (IS_ERR(subsys->notif_handle)) { dev_err(dev, "Notifier registration failed for %s\n", subsys->name); retval = PTR_ERR(subsys->notif_handle); goto err_subsys_notif; } } drv_data->vreg = devm_regulator_get(dev, "vdd"); if (IS_ERR(drv_data->vreg)) { dev_err(dev, "Failed to get regulator\n"); return PTR_ERR(drv_data->vreg); } return 0; err_subsys_notif: for (i = 0; i < drv_data->subsys_count; i++) { subsys = &drv_data->subsystems[i]; subsys_notif_unregister_notifier(subsys->notif_handle, &subsys->nb); } kfree(drv_data); return retval; } static const struct of_device_id mitigate_inrush_match_table[] = { { .compatible = "qcom,msm-inrush-current-mitigation" }, {}, }; static struct platform_driver mitigate_inrush_driver = { .probe = mitigate_inrush_probe, .driver = { .name = "msm-inrush-current", .owner = THIS_MODULE, .of_match_table = mitigate_inrush_match_table, }, }; static int init_msm_mitigate_inrush(void) { return platform_driver_register(&mitigate_inrush_driver); } late_initcall(init_msm_mitigate_inrush); Loading
Documentation/devicetree/bindings/pil/inrush-current-mitigation.txt 0 → 100644 +25 −0 Original line number Diff line number Diff line In Rush Current Mitigation driver: On recent targets, APSS L2 memories are moved to APC domain which were earlier on Mx domain. Analysis suggests that on current targets APSS L2 memories provide reverse capacitance on Mx and this used to provide buffer while powering ON Q6 L2. Now due to L2s moving to APC, the cushion is not available. So there is a chance of droop when Q6 L2 memories are being powered up. During Q6 low power modes L2 is kept in retention only and never collapsed. So the cases where it needs to be taken care is during PIL(modem and adsp bringup during boot). So in cold boot path before bringing up modem or adsp, turn on MM memories. Hence providing a intermediate load. Required properties: - compatible: Must be qcom,msm-inrush-current-mitigation - qcom,dependent-subsystems: List of subsystems which need the intermediate load - vdd-supply: gdsc handle to switch on memory. Example: qcom,inrush-current { compatible = "qcom,msm-inrush-current-mitigation"; qcom,dependent-subsystems = "modem", "adsp"; vdd-supply = <&gdsc_mdss>; };
drivers/soc/qcom/Kconfig +8 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,14 @@ config CP_ACCESS64 If unsure, say N. config MSM_INRUSH_CURRENT_MITIGATION bool "Inrush-current mitigation Driver" help This driver helps in mitigating in-rush current on MSM chipsets which has voltage droop issues due to sudden huge load on a rail. This driver introduces an intermediate load to mitigate the in-rush current. config MSM_QDSP6_APRV2 bool "Audio QDSP6 APRv2 support" depends on MSM_SMD Loading
drivers/soc/qcom/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -2,6 +2,7 @@ CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1) ccflags-$(CONFIG_MSM_QBT1000) += -Idrivers/misc/ obj-$(CONFIG_MSM_INRUSH_CURRENT_MITIGATION) += inrush-current-mitigation.o obj-$(CONFIG_MSM_GLADIATOR_ERP) += gladiator_erp.o obj-$(CONFIG_ARM64) += idle-v8.o cpu_ops.o obj-$(CONFIG_MSM_BOOT_STATS) += boot_stats.o Loading
drivers/soc/qcom/inrush-current-mitigation.c 0 → 100644 +168 −0 Original line number Diff line number Diff line /* * Copyright (c) 2015, 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. */ #include <linux/slab.h> #include <linux/errno.h> #include <linux/platform_device.h> #include <linux/module.h> #include <linux/notifier.h> #include <linux/of.h> #include <linux/async.h> #include <linux/clk/gdsc.h> #include <linux/regulator/consumer.h> #include <soc/qcom/subsystem_notif.h> struct subsystem { const char *name; void *notif_handle; struct notifier_block nb; bool booted; struct inrush_driver_data *drv_data; }; struct inrush_driver_data { int subsys_count; int subsys_boot_count; struct regulator *vreg; /* Must be the last member */ struct subsystem *subsystems; }; #define notifier_to_subsystem(d) container_of(d, struct subsystem, nb) static void free_resources(void *data, async_cookie_t cookie) { struct inrush_driver_data *drv_data = data; struct subsystem *subsys; int i; gdsc_allow_clear_retention(drv_data->vreg); devm_regulator_put(drv_data->vreg); for (i = 0; i < drv_data->subsys_count; i++) { subsys = &drv_data->subsystems[i]; subsys_notif_unregister_notifier(subsys->notif_handle, &subsys->nb); } kfree(drv_data); pr_info("inrush-current-mitigation driver exited\n"); } static int mitigate_inrush_notifier_cb(struct notifier_block *nb, unsigned long code, void *ss_handle) { struct subsystem *subsys = notifier_to_subsystem(nb); struct inrush_driver_data *drv_data = subsys->drv_data; if (subsys->booted) return NOTIFY_DONE; switch (code) { case SUBSYS_AFTER_POWERUP: pr_info("%s: subsystem %s has completed powerup\n", __func__, subsys->name); subsys->booted = true; drv_data->subsys_boot_count++; break; } /* * If all subsystems are up, job of this driver ends, lets * free resources. */ if (drv_data->subsys_count == drv_data->subsys_boot_count) async_schedule(free_resources, drv_data); return NOTIFY_DONE; } static int mitigate_inrush_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; int i, retval; struct subsystem *subsys; struct inrush_driver_data *drv_data; retval = of_property_count_strings(np, "qcom,dependent-subsystems"); if (IS_ERR_VALUE(retval)) { dev_err(dev, "Failed to get dependent subsystems\n"); return -EINVAL; } drv_data = kzalloc((retval * sizeof(struct subsystem) + sizeof(struct inrush_driver_data)), GFP_KERNEL); if (!drv_data) return -ENOMEM; drv_data->subsystems = (void *)drv_data + sizeof(struct inrush_driver_data); drv_data->subsys_count = retval; for (i = 0; i < drv_data->subsys_count; i++) { subsys = &drv_data->subsystems[i]; subsys->drv_data = drv_data; of_property_read_string_index(np, "qcom,dependent-subsystems", i, &subsys->name); subsys->nb.notifier_call = mitigate_inrush_notifier_cb; subsys->notif_handle = subsys_notif_register_notifier(subsys->name, &subsys->nb); if (IS_ERR(subsys->notif_handle)) { dev_err(dev, "Notifier registration failed for %s\n", subsys->name); retval = PTR_ERR(subsys->notif_handle); goto err_subsys_notif; } } drv_data->vreg = devm_regulator_get(dev, "vdd"); if (IS_ERR(drv_data->vreg)) { dev_err(dev, "Failed to get regulator\n"); return PTR_ERR(drv_data->vreg); } return 0; err_subsys_notif: for (i = 0; i < drv_data->subsys_count; i++) { subsys = &drv_data->subsystems[i]; subsys_notif_unregister_notifier(subsys->notif_handle, &subsys->nb); } kfree(drv_data); return retval; } static const struct of_device_id mitigate_inrush_match_table[] = { { .compatible = "qcom,msm-inrush-current-mitigation" }, {}, }; static struct platform_driver mitigate_inrush_driver = { .probe = mitigate_inrush_probe, .driver = { .name = "msm-inrush-current", .owner = THIS_MODULE, .of_match_table = mitigate_inrush_match_table, }, }; static int init_msm_mitigate_inrush(void) { return platform_driver_register(&mitigate_inrush_driver); } late_initcall(init_msm_mitigate_inrush);