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