10376148fSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
238e96838SBengt Jonsson /*
338e96838SBengt Jonsson  * Copyright (C) ST-Ericsson SA 2010
438e96838SBengt Jonsson  *
538e96838SBengt Jonsson  * Authors: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
638e96838SBengt Jonsson  *          Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson
738e96838SBengt Jonsson  *
838e96838SBengt Jonsson  * UX500 common part of Power domain regulators
938e96838SBengt Jonsson  */
1038e96838SBengt Jonsson 
1138e96838SBengt Jonsson #include <linux/kernel.h>
1238e96838SBengt Jonsson #include <linux/err.h>
1338e96838SBengt Jonsson #include <linux/regulator/driver.h>
1438e96838SBengt Jonsson #include <linux/debugfs.h>
1538e96838SBengt Jonsson #include <linux/seq_file.h>
1638e96838SBengt Jonsson #include <linux/slab.h>
177f46d0f8SSteven Rostedt #include <linux/module.h>
1838e96838SBengt Jonsson 
1938e96838SBengt Jonsson #include "dbx500-prcmu.h"
2038e96838SBengt Jonsson 
2138e96838SBengt Jonsson /*
2238e96838SBengt Jonsson  * power state reference count
2338e96838SBengt Jonsson  */
2438e96838SBengt Jonsson static int power_state_active_cnt; /* will initialize to zero */
2538e96838SBengt Jonsson static DEFINE_SPINLOCK(power_state_active_lock);
2638e96838SBengt Jonsson 
power_state_active_enable(void)2738e96838SBengt Jonsson void power_state_active_enable(void)
2838e96838SBengt Jonsson {
2938e96838SBengt Jonsson 	unsigned long flags;
3038e96838SBengt Jonsson 
3138e96838SBengt Jonsson 	spin_lock_irqsave(&power_state_active_lock, flags);
3238e96838SBengt Jonsson 	power_state_active_cnt++;
3338e96838SBengt Jonsson 	spin_unlock_irqrestore(&power_state_active_lock, flags);
3438e96838SBengt Jonsson }
3538e96838SBengt Jonsson 
power_state_active_disable(void)3638e96838SBengt Jonsson int power_state_active_disable(void)
3738e96838SBengt Jonsson {
3838e96838SBengt Jonsson 	int ret = 0;
3938e96838SBengt Jonsson 	unsigned long flags;
4038e96838SBengt Jonsson 
4138e96838SBengt Jonsson 	spin_lock_irqsave(&power_state_active_lock, flags);
4238e96838SBengt Jonsson 	if (power_state_active_cnt <= 0) {
4338e96838SBengt Jonsson 		pr_err("power state: unbalanced enable/disable calls\n");
4438e96838SBengt Jonsson 		ret = -EINVAL;
4538e96838SBengt Jonsson 		goto out;
4638e96838SBengt Jonsson 	}
4738e96838SBengt Jonsson 
4838e96838SBengt Jonsson 	power_state_active_cnt--;
4938e96838SBengt Jonsson out:
5038e96838SBengt Jonsson 	spin_unlock_irqrestore(&power_state_active_lock, flags);
5138e96838SBengt Jonsson 	return ret;
5238e96838SBengt Jonsson }
5338e96838SBengt Jonsson 
5438e96838SBengt Jonsson #ifdef CONFIG_REGULATOR_DEBUG
5538e96838SBengt Jonsson 
power_state_active_get(void)563d75095aSSachin Kamat static int power_state_active_get(void)
573d75095aSSachin Kamat {
583d75095aSSachin Kamat 	unsigned long flags;
593d75095aSSachin Kamat 	int cnt;
603d75095aSSachin Kamat 
613d75095aSSachin Kamat 	spin_lock_irqsave(&power_state_active_lock, flags);
623d75095aSSachin Kamat 	cnt = power_state_active_cnt;
633d75095aSSachin Kamat 	spin_unlock_irqrestore(&power_state_active_lock, flags);
643d75095aSSachin Kamat 
653d75095aSSachin Kamat 	return cnt;
663d75095aSSachin Kamat }
673d75095aSSachin Kamat 
6838e96838SBengt Jonsson static struct ux500_regulator_debug {
6938e96838SBengt Jonsson 	struct dentry *dir;
7038e96838SBengt Jonsson 	struct dbx500_regulator_info *regulator_array;
7138e96838SBengt Jonsson 	int num_regulators;
7238e96838SBengt Jonsson 	u8 *state_before_suspend;
7338e96838SBengt Jonsson 	u8 *state_after_suspend;
7438e96838SBengt Jonsson } rdebug;
7538e96838SBengt Jonsson 
ux500_regulator_power_state_cnt_show(struct seq_file * s,void * p)763e60b4fcSYangtao Li static int ux500_regulator_power_state_cnt_show(struct seq_file *s, void *p)
7738e96838SBengt Jonsson {
7838e96838SBengt Jonsson 	/* print power state count */
79af78114eSJoe Perches 	seq_printf(s, "ux500-regulator power state count: %i\n",
8038e96838SBengt Jonsson 		   power_state_active_get());
8138e96838SBengt Jonsson 
8238e96838SBengt Jonsson 	return 0;
8338e96838SBengt Jonsson }
843e60b4fcSYangtao Li DEFINE_SHOW_ATTRIBUTE(ux500_regulator_power_state_cnt);
8538e96838SBengt Jonsson 
ux500_regulator_status_show(struct seq_file * s,void * p)863e60b4fcSYangtao Li static int ux500_regulator_status_show(struct seq_file *s, void *p)
8738e96838SBengt Jonsson {
8838e96838SBengt Jonsson 	int i;
8938e96838SBengt Jonsson 
9038e96838SBengt Jonsson 	/* print dump header */
91af78114eSJoe Perches 	seq_puts(s, "ux500-regulator status:\n");
92af78114eSJoe Perches 	seq_printf(s, "%31s : %8s : %8s\n", "current", "before", "after");
9338e96838SBengt Jonsson 
9438e96838SBengt Jonsson 	for (i = 0; i < rdebug.num_regulators; i++) {
9538e96838SBengt Jonsson 		struct dbx500_regulator_info *info;
9638e96838SBengt Jonsson 		/* Access per-regulator data */
9738e96838SBengt Jonsson 		info = &rdebug.regulator_array[i];
9838e96838SBengt Jonsson 
9938e96838SBengt Jonsson 		/* print status */
100af78114eSJoe Perches 		seq_printf(s, "%20s : %8s : %8s : %8s\n",
101af78114eSJoe Perches 			   info->desc.name,
10238e96838SBengt Jonsson 			   info->is_enabled ? "enabled" : "disabled",
10338e96838SBengt Jonsson 			   rdebug.state_before_suspend[i] ? "enabled" : "disabled",
10438e96838SBengt Jonsson 			   rdebug.state_after_suspend[i] ? "enabled" : "disabled");
10538e96838SBengt Jonsson 	}
10638e96838SBengt Jonsson 
10738e96838SBengt Jonsson 	return 0;
10838e96838SBengt Jonsson }
1093e60b4fcSYangtao Li DEFINE_SHOW_ATTRIBUTE(ux500_regulator_status);
11038e96838SBengt Jonsson 
111a5023574SBill Pemberton int
ux500_regulator_debug_init(struct platform_device * pdev,struct dbx500_regulator_info * regulator_info,int num_regulators)11238e96838SBengt Jonsson ux500_regulator_debug_init(struct platform_device *pdev,
11338e96838SBengt Jonsson 	struct dbx500_regulator_info *regulator_info,
11438e96838SBengt Jonsson 	int num_regulators)
11538e96838SBengt Jonsson {
11638e96838SBengt Jonsson 	/* create directory */
11738e96838SBengt Jonsson 	rdebug.dir = debugfs_create_dir("ux500-regulator", NULL);
11838e96838SBengt Jonsson 
11938e96838SBengt Jonsson 	/* create "status" file */
120*894cda54SJinchao Wang 	debugfs_create_file("status", 0444, rdebug.dir, &pdev->dev,
12138e96838SBengt Jonsson 			    &ux500_regulator_status_fops);
12238e96838SBengt Jonsson 
12338e96838SBengt Jonsson 	/* create "power-state-count" file */
124*894cda54SJinchao Wang 	debugfs_create_file("power-state-count", 0444, rdebug.dir,
1258bdaa438SGreg Kroah-Hartman 			    &pdev->dev, &ux500_regulator_power_state_cnt_fops);
12638e96838SBengt Jonsson 
12738e96838SBengt Jonsson 	rdebug.regulator_array = regulator_info;
12838e96838SBengt Jonsson 	rdebug.num_regulators = num_regulators;
12938e96838SBengt Jonsson 
13038e96838SBengt Jonsson 	rdebug.state_before_suspend = kzalloc(num_regulators, GFP_KERNEL);
131cb487c5cSSachin Kamat 	if (!rdebug.state_before_suspend)
13238e96838SBengt Jonsson 		goto exit_destroy_power_state;
13338e96838SBengt Jonsson 
13438e96838SBengt Jonsson 	rdebug.state_after_suspend = kzalloc(num_regulators, GFP_KERNEL);
135cb487c5cSSachin Kamat 	if (!rdebug.state_after_suspend)
13638e96838SBengt Jonsson 		goto exit_free;
13738e96838SBengt Jonsson 
13838e96838SBengt Jonsson 	return 0;
13938e96838SBengt Jonsson 
14038e96838SBengt Jonsson exit_free:
14138e96838SBengt Jonsson 	kfree(rdebug.state_before_suspend);
14238e96838SBengt Jonsson exit_destroy_power_state:
1438bdaa438SGreg Kroah-Hartman 	debugfs_remove_recursive(rdebug.dir);
14438e96838SBengt Jonsson 	return -ENOMEM;
14538e96838SBengt Jonsson }
14638e96838SBengt Jonsson 
ux500_regulator_debug_exit(void)1478dc995f5SBill Pemberton int ux500_regulator_debug_exit(void)
14838e96838SBengt Jonsson {
14938e96838SBengt Jonsson 	debugfs_remove_recursive(rdebug.dir);
15038e96838SBengt Jonsson 	kfree(rdebug.state_after_suspend);
15138e96838SBengt Jonsson 	kfree(rdebug.state_before_suspend);
15238e96838SBengt Jonsson 
15338e96838SBengt Jonsson 	return 0;
15438e96838SBengt Jonsson }
15538e96838SBengt Jonsson #endif
156