From a9207f6cfc67dafc06a0aa957c052a485b07b947 Mon Sep 17 00:00:00 2001
From: Greg Beresford <greg.beresford@displaydata.com>
Date: Thu, 27 Feb 2025 14:47:27 +0000
Subject: [PATCH] Add tps65219 power on status

---
 drivers/mfd/tps65219.c       | 180 +++++++++++++++++++++++++++++++++++
 include/linux/mfd/tps65219.h |   3 +
 2 files changed, 183 insertions(+)

diff --git a/drivers/mfd/tps65219.c b/drivers/mfd/tps65219.c
index 0e0c42e4fdfc..b9c2494ef0a7 100644
--- a/drivers/mfd/tps65219.c
+++ b/drivers/mfd/tps65219.c
@@ -211,6 +211,13 @@ static struct regmap_irq tps65219_irqs[] = {
 	TPS65219_REGMAP_IRQ_REG(TPS65219_INT_PB_RISING_EDGE_DETECT, TPS65219_REG_INT_PB_POS),
 };

+static const char* const tps65219_state_names[] = {
+	[0] = "transition",
+	[1] = "initialize",
+	[2] = "standby",
+	[3] = "active"
+};
+
 static struct regmap_irq_chip tps65219_irq_chip = {
 	.name = "tps65219_irq",
 	.main_status = TPS65219_REG_INT_SOURCE,
@@ -225,6 +232,176 @@ static struct regmap_irq_chip tps65219_irq_chip = {
 	.sub_reg_offsets = tps65219_sub_irq_offsets,
 };

+static int tps65219_get_power_up_status(struct tps65219 *tps, const char** fsd, const char** en, const char** cold_reset, const char** off, const char** state, u8* retries, u8* raw)
+{
+	int power_up_status;
+
+	if (tps->power_up_status_read) {
+		power_up_status = tps->power_up_status;
+	} else {
+		int ret = regmap_read(tps->regmap, TPS65219_REG_POWER_UP_STATUS, &power_up_status);
+		if (ret) {
+			dev_err(tps->dev, "Failed to read power up status: %d\n", ret);
+			return ret;
+		}
+		tps->power_up_status = power_up_status;
+		tps->power_up_status_read = true;
+	}
+
+	*fsd = power_up_status        & (1 << 7) ? "fsd" : "";
+	*en = power_up_status         & (1 << 6) ? "en" : "";
+	*cold_reset = power_up_status & (1 << 5) ? "cold_reset" : "";
+	*off = power_up_status        & (1 << 0) ? "off" : "";
+
+	u8 state_num = (power_up_status & (0x18)) >> 3;
+	*state = tps65219_state_names[state_num];
+
+	*retries = (power_up_status & 0x06) >> 1;
+	*raw = power_up_status;
+
+	return 0;
+}
+
+static ssize_t tps65219_power_up_status_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int ret;
+	struct i2c_client *client = to_i2c_client(dev);
+	struct tps65219 *tps = i2c_get_clientdata(client);
+
+	const char *fsd, *en, *cold_reset, *off, *state;
+	u8 retries, raw;
+
+	ret = tps65219_get_power_up_status(tps, &fsd, &en, &cold_reset, &off, &state, &retries, &raw);
+	if (ret) {
+		return ret;
+	}
+
+	int len = 0;
+	bool is_first = true;
+	if (*fsd) {
+		len += sysfs_emit_at(buf, len, "%s%s", is_first ? "" : " ", fsd);
+		is_first = false;
+	}
+	if (*en) {
+		len += sysfs_emit_at(buf, len, "%s%s", is_first ? "" : " ", en);
+		is_first = false;
+	}
+	if (*cold_reset) {
+		len += sysfs_emit_at(buf, len, "%s%s", is_first ? "" : " ", cold_reset);
+		is_first = false;
+	}
+	if (*off) {
+		len += sysfs_emit_at(buf, len, "%s%s", is_first ? "" : " ", off);
+		is_first = false;
+	}
+
+	if (len == 0) {
+		return sysfs_emit(buf, "none\n");
+	} else {
+		len += sysfs_emit_at(buf, len, "\n");
+	}
+
+	return len;
+}
+
+static ssize_t tps65219_power_up_status_store(struct device *dev,
+	struct device_attribute *attr,
+	const char *buf, size_t len)
+{
+	int ret;
+	struct i2c_client *client = to_i2c_client(dev);
+	struct tps65219 *tps = i2c_get_clientdata(client);
+
+	u8 val;
+
+	if (kstrtou8(buf, 0, &val) || val != 0)
+		return -EINVAL;
+
+	tps->power_up_status_read = false;
+
+	ret = regmap_write(tps->regmap, TPS65219_REG_POWER_UP_STATUS, 0xFF);
+	if (ret) {
+		dev_err(tps->dev, "Failed to clear power up status: %d\n", ret);
+		return ret;
+	}
+
+	return len;
+}
+
+static ssize_t tps65219_power_up_retries_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int ret;
+	struct i2c_client *client = to_i2c_client(dev);
+	struct tps65219 *tps = i2c_get_clientdata(client);
+
+	const char *fsd, *en, *cold_reset, *off, *state;
+	u8 retries, raw;
+
+	ret = tps65219_get_power_up_status(tps, &fsd, &en, &cold_reset, &off, &state, &retries, &raw);
+	if (ret) {
+		return ret;
+	}
+
+	return sysfs_emit(buf, "%d\n", retries);
+}
+
+static ssize_t tps65219_power_up_state_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int ret;
+	struct i2c_client *client = to_i2c_client(dev);
+	struct tps65219 *tps = i2c_get_clientdata(client);
+
+	const char *fsd, *en, *cold_reset, *off, *state;
+	u8 retries, raw;
+
+	ret = tps65219_get_power_up_status(tps, &fsd, &en, &cold_reset, &off, &state, &retries, &raw);
+	if (ret) {
+		return ret;
+	}
+
+	return sysfs_emit(buf, "%s\n", state);
+}
+
+static struct device_attribute tps65219_dev_attr_power_up_status = {
+	.attr	= { .name = "power_up_status", .mode = 0600 },
+	.show	= tps65219_power_up_status_show,
+	.store	= tps65219_power_up_status_store,
+};
+
+static struct device_attribute tps65219_dev_attr_power_up_retries = {
+	.attr	= { .name = "power_up_retries", .mode = 0444 },
+	.show	= tps65219_power_up_retries_show,
+};
+
+static struct device_attribute tps65219_dev_attr_power_up_state = {
+	.attr	= { .name = "power_up_state", .mode = 0444 },
+	.show	= tps65219_power_up_state_show,
+};
+
+static struct attribute *tps65219_dev_attrs[] = {
+	&tps65219_dev_attr_power_up_status.attr,
+	&tps65219_dev_attr_power_up_retries.attr,
+	&tps65219_dev_attr_power_up_state.attr,
+	NULL,
+};
+
+ATTRIBUTE_GROUPS(tps65219_dev);
+
+static void tps65219_log_power_up_status(struct tps65219 *tps, const char* prefix)
+{
+	const char *fsd, *en, *cold_reset, *off, *state;
+	u8 retries, raw;
+
+	tps65219_get_power_up_status(tps, &fsd, &en, &cold_reset, &off, &state, &retries, &raw);
+
+	dev_info(tps->dev, "%s%s retries:%d%s%s%s%s%s%s%s%s (0x%02x)\n", prefix, state, retries,
+		*fsd ? " " : "", fsd,
+		*en ? " " : "", en,
+		*cold_reset ? " " : "", cold_reset,
+		*off ? " " : "", off,
+		raw);
+}
+
 static int tps65219_probe(struct i2c_client *client)
 {
 	struct tps65219 *tps;
@@ -278,6 +455,8 @@ static int tps65219_probe(struct i2c_client *client)
 		}
 	}

+	tps65219_log_power_up_status(tps, "Power up status: ");
+
 	ret = devm_register_restart_handler(tps->dev,
 					    tps65219_restart_handler,
 					    tps);
@@ -307,6 +486,7 @@ static struct i2c_driver tps65219_driver = {
 	.driver		= {
 		.name	= "tps65219",
 		.of_match_table = of_tps65219_match_table,
+		.dev_groups = tps65219_dev_groups,
 	},
 	.probe		= tps65219_probe,
 };
diff --git a/include/linux/mfd/tps65219.h b/include/linux/mfd/tps65219.h
index e6826e34e2a6..18d78bd9cdcc 100644
--- a/include/linux/mfd/tps65219.h
+++ b/include/linux/mfd/tps65219.h
@@ -340,6 +340,9 @@ struct tps65219 {

 	struct regmap_irq_chip_data *irq_data;
 	struct notifier_block nb;
+
+	bool power_up_status_read;
+	u8 power_up_status;
 };

 #endif /* MFD_TPS65219_H */
