linux/drivers/regulator/dbx500-prcmu.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) ST-Ericsson SA 2010
   3 *
   4 * License Terms: GNU General Public License v2
   5 * Authors: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
   6 *          Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson
   7 *
   8 * UX500 common part of Power domain regulators
   9 */
  10
  11#include <linux/kernel.h>
  12#include <linux/err.h>
  13#include <linux/regulator/driver.h>
  14#include <linux/debugfs.h>
  15#include <linux/seq_file.h>
  16#include <linux/slab.h>
  17#include <linux/module.h>
  18
  19#include "dbx500-prcmu.h"
  20
  21/*
  22 * power state reference count
  23 */
  24static int power_state_active_cnt; /* will initialize to zero */
  25static DEFINE_SPINLOCK(power_state_active_lock);
  26
  27void power_state_active_enable(void)
  28{
  29        unsigned long flags;
  30
  31        spin_lock_irqsave(&power_state_active_lock, flags);
  32        power_state_active_cnt++;
  33        spin_unlock_irqrestore(&power_state_active_lock, flags);
  34}
  35
  36int power_state_active_disable(void)
  37{
  38        int ret = 0;
  39        unsigned long flags;
  40
  41        spin_lock_irqsave(&power_state_active_lock, flags);
  42        if (power_state_active_cnt <= 0) {
  43                pr_err("power state: unbalanced enable/disable calls\n");
  44                ret = -EINVAL;
  45                goto out;
  46        }
  47
  48        power_state_active_cnt--;
  49out:
  50        spin_unlock_irqrestore(&power_state_active_lock, flags);
  51        return ret;
  52}
  53
  54#ifdef CONFIG_REGULATOR_DEBUG
  55
  56static int power_state_active_get(void)
  57{
  58        unsigned long flags;
  59        int cnt;
  60
  61        spin_lock_irqsave(&power_state_active_lock, flags);
  62        cnt = power_state_active_cnt;
  63        spin_unlock_irqrestore(&power_state_active_lock, flags);
  64
  65        return cnt;
  66}
  67
  68static struct ux500_regulator_debug {
  69        struct dentry *dir;
  70        struct dentry *status_file;
  71        struct dentry *power_state_cnt_file;
  72        struct dbx500_regulator_info *regulator_array;
  73        int num_regulators;
  74        u8 *state_before_suspend;
  75        u8 *state_after_suspend;
  76} rdebug;
  77
  78void ux500_regulator_suspend_debug(void)
  79{
  80        int i;
  81
  82        for (i = 0; i < rdebug.num_regulators; i++)
  83                rdebug.state_before_suspend[i] =
  84                        rdebug.regulator_array[i].is_enabled;
  85}
  86
  87void ux500_regulator_resume_debug(void)
  88{
  89        int i;
  90
  91        for (i = 0; i < rdebug.num_regulators; i++)
  92                rdebug.state_after_suspend[i] =
  93                        rdebug.regulator_array[i].is_enabled;
  94}
  95
  96static int ux500_regulator_power_state_cnt_print(struct seq_file *s, void *p)
  97{
  98        /* print power state count */
  99        seq_printf(s, "ux500-regulator power state count: %i\n",
 100                   power_state_active_get());
 101
 102        return 0;
 103}
 104
 105static int ux500_regulator_power_state_cnt_open(struct inode *inode,
 106        struct file *file)
 107{
 108        return single_open(file, ux500_regulator_power_state_cnt_print,
 109                inode->i_private);
 110}
 111
 112static const struct file_operations ux500_regulator_power_state_cnt_fops = {
 113        .open = ux500_regulator_power_state_cnt_open,
 114        .read = seq_read,
 115        .llseek = seq_lseek,
 116        .release = single_release,
 117        .owner = THIS_MODULE,
 118};
 119
 120static int ux500_regulator_status_print(struct seq_file *s, void *p)
 121{
 122        int i;
 123
 124        /* print dump header */
 125        seq_puts(s, "ux500-regulator status:\n");
 126        seq_printf(s, "%31s : %8s : %8s\n", "current", "before", "after");
 127
 128        for (i = 0; i < rdebug.num_regulators; i++) {
 129                struct dbx500_regulator_info *info;
 130                /* Access per-regulator data */
 131                info = &rdebug.regulator_array[i];
 132
 133                /* print status */
 134                seq_printf(s, "%20s : %8s : %8s : %8s\n",
 135                           info->desc.name,
 136                           info->is_enabled ? "enabled" : "disabled",
 137                           rdebug.state_before_suspend[i] ? "enabled" : "disabled",
 138                           rdebug.state_after_suspend[i] ? "enabled" : "disabled");
 139        }
 140
 141        return 0;
 142}
 143
 144static int ux500_regulator_status_open(struct inode *inode, struct file *file)
 145{
 146        return single_open(file, ux500_regulator_status_print,
 147                inode->i_private);
 148}
 149
 150static const struct file_operations ux500_regulator_status_fops = {
 151        .open = ux500_regulator_status_open,
 152        .read = seq_read,
 153        .llseek = seq_lseek,
 154        .release = single_release,
 155        .owner = THIS_MODULE,
 156};
 157
 158int __attribute__((weak)) dbx500_regulator_testcase(
 159        struct dbx500_regulator_info *regulator_info,
 160        int num_regulators)
 161{
 162        return 0;
 163}
 164
 165int
 166ux500_regulator_debug_init(struct platform_device *pdev,
 167        struct dbx500_regulator_info *regulator_info,
 168        int num_regulators)
 169{
 170        /* create directory */
 171        rdebug.dir = debugfs_create_dir("ux500-regulator", NULL);
 172        if (!rdebug.dir)
 173                goto exit_no_debugfs;
 174
 175        /* create "status" file */
 176        rdebug.status_file = debugfs_create_file("status",
 177                S_IRUGO, rdebug.dir, &pdev->dev,
 178                &ux500_regulator_status_fops);
 179        if (!rdebug.status_file)
 180                goto exit_destroy_dir;
 181
 182        /* create "power-state-count" file */
 183        rdebug.power_state_cnt_file = debugfs_create_file("power-state-count",
 184                S_IRUGO, rdebug.dir, &pdev->dev,
 185                &ux500_regulator_power_state_cnt_fops);
 186        if (!rdebug.power_state_cnt_file)
 187                goto exit_destroy_status;
 188
 189        rdebug.regulator_array = regulator_info;
 190        rdebug.num_regulators = num_regulators;
 191
 192        rdebug.state_before_suspend = kzalloc(num_regulators, GFP_KERNEL);
 193        if (!rdebug.state_before_suspend)
 194                goto exit_destroy_power_state;
 195
 196        rdebug.state_after_suspend = kzalloc(num_regulators, GFP_KERNEL);
 197        if (!rdebug.state_after_suspend)
 198                goto exit_free;
 199
 200        dbx500_regulator_testcase(regulator_info, num_regulators);
 201        return 0;
 202
 203exit_free:
 204        kfree(rdebug.state_before_suspend);
 205exit_destroy_power_state:
 206        debugfs_remove(rdebug.power_state_cnt_file);
 207exit_destroy_status:
 208        debugfs_remove(rdebug.status_file);
 209exit_destroy_dir:
 210        debugfs_remove(rdebug.dir);
 211exit_no_debugfs:
 212        dev_err(&pdev->dev, "failed to create debugfs entries.\n");
 213        return -ENOMEM;
 214}
 215
 216int ux500_regulator_debug_exit(void)
 217{
 218        debugfs_remove_recursive(rdebug.dir);
 219        kfree(rdebug.state_after_suspend);
 220        kfree(rdebug.state_before_suspend);
 221
 222        return 0;
 223}
 224#endif
 225