18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) ST-Ericsson SA 2010
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Authors: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
68c2ecf20Sopenharmony_ci *          Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * UX500 common part of Power domain regulators
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/err.h>
138c2ecf20Sopenharmony_ci#include <linux/regulator/driver.h>
148c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
158c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include "dbx500-prcmu.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/*
228c2ecf20Sopenharmony_ci * power state reference count
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_cistatic int power_state_active_cnt; /* will initialize to zero */
258c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(power_state_active_lock);
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_civoid power_state_active_enable(void)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	unsigned long flags;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	spin_lock_irqsave(&power_state_active_lock, flags);
328c2ecf20Sopenharmony_ci	power_state_active_cnt++;
338c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&power_state_active_lock, flags);
348c2ecf20Sopenharmony_ci}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ciint power_state_active_disable(void)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	int ret = 0;
398c2ecf20Sopenharmony_ci	unsigned long flags;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	spin_lock_irqsave(&power_state_active_lock, flags);
428c2ecf20Sopenharmony_ci	if (power_state_active_cnt <= 0) {
438c2ecf20Sopenharmony_ci		pr_err("power state: unbalanced enable/disable calls\n");
448c2ecf20Sopenharmony_ci		ret = -EINVAL;
458c2ecf20Sopenharmony_ci		goto out;
468c2ecf20Sopenharmony_ci	}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	power_state_active_cnt--;
498c2ecf20Sopenharmony_ciout:
508c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&power_state_active_lock, flags);
518c2ecf20Sopenharmony_ci	return ret;
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#ifdef CONFIG_REGULATOR_DEBUG
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic int power_state_active_get(void)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	unsigned long flags;
598c2ecf20Sopenharmony_ci	int cnt;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	spin_lock_irqsave(&power_state_active_lock, flags);
628c2ecf20Sopenharmony_ci	cnt = power_state_active_cnt;
638c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&power_state_active_lock, flags);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	return cnt;
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic struct ux500_regulator_debug {
698c2ecf20Sopenharmony_ci	struct dentry *dir;
708c2ecf20Sopenharmony_ci	struct dbx500_regulator_info *regulator_array;
718c2ecf20Sopenharmony_ci	int num_regulators;
728c2ecf20Sopenharmony_ci	u8 *state_before_suspend;
738c2ecf20Sopenharmony_ci	u8 *state_after_suspend;
748c2ecf20Sopenharmony_ci} rdebug;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic int ux500_regulator_power_state_cnt_show(struct seq_file *s, void *p)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	/* print power state count */
798c2ecf20Sopenharmony_ci	seq_printf(s, "ux500-regulator power state count: %i\n",
808c2ecf20Sopenharmony_ci		   power_state_active_get());
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	return 0;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(ux500_regulator_power_state_cnt);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic int ux500_regulator_status_show(struct seq_file *s, void *p)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	int i;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	/* print dump header */
918c2ecf20Sopenharmony_ci	seq_puts(s, "ux500-regulator status:\n");
928c2ecf20Sopenharmony_ci	seq_printf(s, "%31s : %8s : %8s\n", "current", "before", "after");
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	for (i = 0; i < rdebug.num_regulators; i++) {
958c2ecf20Sopenharmony_ci		struct dbx500_regulator_info *info;
968c2ecf20Sopenharmony_ci		/* Access per-regulator data */
978c2ecf20Sopenharmony_ci		info = &rdebug.regulator_array[i];
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci		/* print status */
1008c2ecf20Sopenharmony_ci		seq_printf(s, "%20s : %8s : %8s : %8s\n",
1018c2ecf20Sopenharmony_ci			   info->desc.name,
1028c2ecf20Sopenharmony_ci			   info->is_enabled ? "enabled" : "disabled",
1038c2ecf20Sopenharmony_ci			   rdebug.state_before_suspend[i] ? "enabled" : "disabled",
1048c2ecf20Sopenharmony_ci			   rdebug.state_after_suspend[i] ? "enabled" : "disabled");
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	return 0;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(ux500_regulator_status);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ciint
1128c2ecf20Sopenharmony_ciux500_regulator_debug_init(struct platform_device *pdev,
1138c2ecf20Sopenharmony_ci	struct dbx500_regulator_info *regulator_info,
1148c2ecf20Sopenharmony_ci	int num_regulators)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	/* create directory */
1178c2ecf20Sopenharmony_ci	rdebug.dir = debugfs_create_dir("ux500-regulator", NULL);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	/* create "status" file */
1208c2ecf20Sopenharmony_ci	debugfs_create_file("status", S_IRUGO, rdebug.dir, &pdev->dev,
1218c2ecf20Sopenharmony_ci			    &ux500_regulator_status_fops);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	/* create "power-state-count" file */
1248c2ecf20Sopenharmony_ci	debugfs_create_file("power-state-count", S_IRUGO, rdebug.dir,
1258c2ecf20Sopenharmony_ci			    &pdev->dev, &ux500_regulator_power_state_cnt_fops);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	rdebug.regulator_array = regulator_info;
1288c2ecf20Sopenharmony_ci	rdebug.num_regulators = num_regulators;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	rdebug.state_before_suspend = kzalloc(num_regulators, GFP_KERNEL);
1318c2ecf20Sopenharmony_ci	if (!rdebug.state_before_suspend)
1328c2ecf20Sopenharmony_ci		goto exit_destroy_power_state;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	rdebug.state_after_suspend = kzalloc(num_regulators, GFP_KERNEL);
1358c2ecf20Sopenharmony_ci	if (!rdebug.state_after_suspend)
1368c2ecf20Sopenharmony_ci		goto exit_free;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	return 0;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ciexit_free:
1418c2ecf20Sopenharmony_ci	kfree(rdebug.state_before_suspend);
1428c2ecf20Sopenharmony_ciexit_destroy_power_state:
1438c2ecf20Sopenharmony_ci	debugfs_remove_recursive(rdebug.dir);
1448c2ecf20Sopenharmony_ci	return -ENOMEM;
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ciint ux500_regulator_debug_exit(void)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	debugfs_remove_recursive(rdebug.dir);
1508c2ecf20Sopenharmony_ci	kfree(rdebug.state_after_suspend);
1518c2ecf20Sopenharmony_ci	kfree(rdebug.state_before_suspend);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	return 0;
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci#endif
156