162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) ST-Ericsson SA 2010
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Authors: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
662306a36Sopenharmony_ci *          Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * UX500 common part of Power domain regulators
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/err.h>
1362306a36Sopenharmony_ci#include <linux/regulator/driver.h>
1462306a36Sopenharmony_ci#include <linux/debugfs.h>
1562306a36Sopenharmony_ci#include <linux/seq_file.h>
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "dbx500-prcmu.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/*
2262306a36Sopenharmony_ci * power state reference count
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_cistatic int power_state_active_cnt; /* will initialize to zero */
2562306a36Sopenharmony_cistatic DEFINE_SPINLOCK(power_state_active_lock);
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_civoid power_state_active_enable(void)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	unsigned long flags;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	spin_lock_irqsave(&power_state_active_lock, flags);
3262306a36Sopenharmony_ci	power_state_active_cnt++;
3362306a36Sopenharmony_ci	spin_unlock_irqrestore(&power_state_active_lock, flags);
3462306a36Sopenharmony_ci}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ciint power_state_active_disable(void)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	int ret = 0;
3962306a36Sopenharmony_ci	unsigned long flags;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	spin_lock_irqsave(&power_state_active_lock, flags);
4262306a36Sopenharmony_ci	if (power_state_active_cnt <= 0) {
4362306a36Sopenharmony_ci		pr_err("power state: unbalanced enable/disable calls\n");
4462306a36Sopenharmony_ci		ret = -EINVAL;
4562306a36Sopenharmony_ci		goto out;
4662306a36Sopenharmony_ci	}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	power_state_active_cnt--;
4962306a36Sopenharmony_ciout:
5062306a36Sopenharmony_ci	spin_unlock_irqrestore(&power_state_active_lock, flags);
5162306a36Sopenharmony_ci	return ret;
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#ifdef CONFIG_REGULATOR_DEBUG
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic int power_state_active_get(void)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	unsigned long flags;
5962306a36Sopenharmony_ci	int cnt;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	spin_lock_irqsave(&power_state_active_lock, flags);
6262306a36Sopenharmony_ci	cnt = power_state_active_cnt;
6362306a36Sopenharmony_ci	spin_unlock_irqrestore(&power_state_active_lock, flags);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	return cnt;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic struct ux500_regulator_debug {
6962306a36Sopenharmony_ci	struct dentry *dir;
7062306a36Sopenharmony_ci	struct dbx500_regulator_info *regulator_array;
7162306a36Sopenharmony_ci	int num_regulators;
7262306a36Sopenharmony_ci	u8 *state_before_suspend;
7362306a36Sopenharmony_ci	u8 *state_after_suspend;
7462306a36Sopenharmony_ci} rdebug;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic int ux500_regulator_power_state_cnt_show(struct seq_file *s, void *p)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	/* print power state count */
7962306a36Sopenharmony_ci	seq_printf(s, "ux500-regulator power state count: %i\n",
8062306a36Sopenharmony_ci		   power_state_active_get());
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	return 0;
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(ux500_regulator_power_state_cnt);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic int ux500_regulator_status_show(struct seq_file *s, void *p)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	int i;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	/* print dump header */
9162306a36Sopenharmony_ci	seq_puts(s, "ux500-regulator status:\n");
9262306a36Sopenharmony_ci	seq_printf(s, "%31s : %8s : %8s\n", "current", "before", "after");
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	for (i = 0; i < rdebug.num_regulators; i++) {
9562306a36Sopenharmony_ci		struct dbx500_regulator_info *info;
9662306a36Sopenharmony_ci		/* Access per-regulator data */
9762306a36Sopenharmony_ci		info = &rdebug.regulator_array[i];
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci		/* print status */
10062306a36Sopenharmony_ci		seq_printf(s, "%20s : %8s : %8s : %8s\n",
10162306a36Sopenharmony_ci			   info->desc.name,
10262306a36Sopenharmony_ci			   info->is_enabled ? "enabled" : "disabled",
10362306a36Sopenharmony_ci			   rdebug.state_before_suspend[i] ? "enabled" : "disabled",
10462306a36Sopenharmony_ci			   rdebug.state_after_suspend[i] ? "enabled" : "disabled");
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	return 0;
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(ux500_regulator_status);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ciint
11262306a36Sopenharmony_ciux500_regulator_debug_init(struct platform_device *pdev,
11362306a36Sopenharmony_ci	struct dbx500_regulator_info *regulator_info,
11462306a36Sopenharmony_ci	int num_regulators)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	/* create directory */
11762306a36Sopenharmony_ci	rdebug.dir = debugfs_create_dir("ux500-regulator", NULL);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	/* create "status" file */
12062306a36Sopenharmony_ci	debugfs_create_file("status", 0444, rdebug.dir, &pdev->dev,
12162306a36Sopenharmony_ci			    &ux500_regulator_status_fops);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	/* create "power-state-count" file */
12462306a36Sopenharmony_ci	debugfs_create_file("power-state-count", 0444, rdebug.dir,
12562306a36Sopenharmony_ci			    &pdev->dev, &ux500_regulator_power_state_cnt_fops);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	rdebug.regulator_array = regulator_info;
12862306a36Sopenharmony_ci	rdebug.num_regulators = num_regulators;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	rdebug.state_before_suspend = kzalloc(num_regulators, GFP_KERNEL);
13162306a36Sopenharmony_ci	if (!rdebug.state_before_suspend)
13262306a36Sopenharmony_ci		goto exit_destroy_power_state;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	rdebug.state_after_suspend = kzalloc(num_regulators, GFP_KERNEL);
13562306a36Sopenharmony_ci	if (!rdebug.state_after_suspend)
13662306a36Sopenharmony_ci		goto exit_free;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	return 0;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ciexit_free:
14162306a36Sopenharmony_ci	kfree(rdebug.state_before_suspend);
14262306a36Sopenharmony_ciexit_destroy_power_state:
14362306a36Sopenharmony_ci	debugfs_remove_recursive(rdebug.dir);
14462306a36Sopenharmony_ci	return -ENOMEM;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ciint ux500_regulator_debug_exit(void)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	debugfs_remove_recursive(rdebug.dir);
15062306a36Sopenharmony_ci	kfree(rdebug.state_after_suspend);
15162306a36Sopenharmony_ci	kfree(rdebug.state_before_suspend);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	return 0;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci#endif
156