Commit 875fdd07 authored by Julien Panis's avatar Julien Panis Committed by Greg Kroah-Hartman
Browse files

misc: tps6594-esm: Add driver for TI TPS6594 ESM



This patch adds support for TPS6594 ESM (Error Signal Monitor).
This device monitors the SoC error output signal at its nERR_SOC input pin.
In error condition, ESM toggles its nRSTOUT_SOC pin to reset the SoC.

Signed-off-by: default avatarJulien Panis <jpanis@baylibre.com>
Message-ID: <20230511095126.105104-4-jpanis@baylibre.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 9fb90804
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -538,6 +538,17 @@ config TMR_INJECT

	  Say N here unless you know what you are doing.

config TPS6594_ESM
	tristate "TI TPS6594 Error Signal Monitor support"
	depends on MFD_TPS6594
	default MFD_TPS6594
	help
	  Support ESM (Error Signal Monitor) on TPS6594 PMIC devices.
	  ESM is used typically to reboot the board in error condition.

	  This driver can also be built as a module.  If so, the module
	  will be called tps6594-esm.

source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
+1 −0
Original line number Diff line number Diff line
@@ -65,3 +65,4 @@ obj-$(CONFIG_GP_PCI1XXXX) += mchp_pci1xxxx/
obj-$(CONFIG_VCPU_STALL_DETECTOR)	+= vcpu_stall_detector.o
obj-$(CONFIG_TMR_MANAGER)      += xilinx_tmr_manager.o
obj-$(CONFIG_TMR_INJECT)	+= xilinx_tmr_inject.o
obj-$(CONFIG_TPS6594_ESM)	+= tps6594-esm.o
+132 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*
 * ESM (Error Signal Monitor) driver for TI TPS6594/TPS6593/LP8764 PMICs
 *
 * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/
 */

#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>

#include <linux/mfd/tps6594.h>

static irqreturn_t tps6594_esm_isr(int irq, void *dev_id)
{
	struct platform_device *pdev = dev_id;
	int i;

	for (i = 0 ; i < pdev->num_resources ; i++) {
		if (irq == platform_get_irq_byname(pdev, pdev->resource[i].name)) {
			dev_err(pdev->dev.parent, "%s error detected\n", pdev->resource[i].name);
			return IRQ_HANDLED;
		}
	}

	return IRQ_NONE;
}

static int tps6594_esm_probe(struct platform_device *pdev)
{
	struct tps6594 *tps = dev_get_drvdata(pdev->dev.parent);
	struct device *dev = &pdev->dev;
	int irq;
	int ret;
	int i;

	for (i = 0 ; i < pdev->num_resources ; i++) {
		irq = platform_get_irq_byname(pdev, pdev->resource[i].name);
		if (irq < 0)
			return dev_err_probe(dev, irq, "Failed to get %s irq\n",
					     pdev->resource[i].name);

		ret = devm_request_threaded_irq(dev, irq, NULL,
						tps6594_esm_isr, IRQF_ONESHOT,
						pdev->resource[i].name, pdev);
		if (ret)
			return dev_err_probe(dev, ret, "Failed to request irq\n");
	}

	ret = regmap_set_bits(tps->regmap, TPS6594_REG_ESM_SOC_MODE_CFG,
			      TPS6594_BIT_ESM_SOC_EN | TPS6594_BIT_ESM_SOC_ENDRV);
	if (ret)
		return dev_err_probe(dev, ret, "Failed to configure ESM\n");

	ret = regmap_set_bits(tps->regmap, TPS6594_REG_ESM_SOC_START_REG,
			      TPS6594_BIT_ESM_SOC_START);
	if (ret)
		return dev_err_probe(dev, ret, "Failed to start ESM\n");

	pm_runtime_enable(dev);
	pm_runtime_get_sync(dev);

	return 0;
}

static int tps6594_esm_remove(struct platform_device *pdev)
{
	struct tps6594 *tps = dev_get_drvdata(pdev->dev.parent);
	struct device *dev = &pdev->dev;
	int ret;

	ret = regmap_clear_bits(tps->regmap, TPS6594_REG_ESM_SOC_START_REG,
				TPS6594_BIT_ESM_SOC_START);
	if (ret) {
		dev_err(dev, "Failed to stop ESM\n");
		goto out;
	}

	ret = regmap_clear_bits(tps->regmap, TPS6594_REG_ESM_SOC_MODE_CFG,
				TPS6594_BIT_ESM_SOC_EN | TPS6594_BIT_ESM_SOC_ENDRV);
	if (ret)
		dev_err(dev, "Failed to unconfigure ESM\n");

out:
	pm_runtime_put_sync(dev);
	pm_runtime_disable(dev);

	return ret;
}

static int tps6594_esm_suspend(struct device *dev)
{
	struct tps6594 *tps = dev_get_drvdata(dev->parent);
	int ret;

	ret = regmap_clear_bits(tps->regmap, TPS6594_REG_ESM_SOC_START_REG,
				TPS6594_BIT_ESM_SOC_START);

	pm_runtime_put_sync(dev);

	return ret;
}

static int tps6594_esm_resume(struct device *dev)
{
	struct tps6594 *tps = dev_get_drvdata(dev->parent);

	pm_runtime_get_sync(dev);

	return regmap_set_bits(tps->regmap, TPS6594_REG_ESM_SOC_START_REG,
			       TPS6594_BIT_ESM_SOC_START);
}

static DEFINE_SIMPLE_DEV_PM_OPS(tps6594_esm_pm_ops, tps6594_esm_suspend, tps6594_esm_resume);

static struct platform_driver tps6594_esm_driver = {
	.driver	= {
		.name = "tps6594-esm",
		.pm = pm_sleep_ptr(&tps6594_esm_pm_ops),
	},
	.probe = tps6594_esm_probe,
	.remove = tps6594_esm_remove,
};

module_platform_driver(tps6594_esm_driver);

MODULE_ALIAS("platform:tps6594-esm");
MODULE_AUTHOR("Julien Panis <jpanis@baylibre.com>");
MODULE_DESCRIPTION("TPS6594 Error Signal Monitor Driver");
MODULE_LICENSE("GPL");