Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b3a1450c authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "soc: qcom: Add in-rush current mitigation driver"

parents 9f7af2ec a84f8fd9
Loading
Loading
Loading
Loading
+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>;
	};
+8 −0
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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
+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);