blob: 3564bbeeb2e81fcb9030e60b13b6242a1740d817 [file] [log] [blame]
/*
* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/debugfs.h>
#include <linux/module.h>
struct msm_cpr_debug_device {
struct mutex debug_mutex;
struct dentry *dir;
int addr_offset;
void __iomem *base;
};
static inline
void write_reg(struct msm_cpr_debug_device *cpr, u32 value)
{
writel_relaxed(value, cpr->base + cpr->addr_offset);
}
static inline u32 read_reg(struct msm_cpr_debug_device *cpr)
{
return readl_relaxed(cpr->base + cpr->addr_offset);
}
static bool msm_cpr_debug_addr_is_valid(int addr)
{
if (addr < 0 || addr > 0x15C) {
pr_err("CPR register address is invalid: %d\n", addr);
return false;
}
return true;
}
static int msm_cpr_debug_data_set(void *data, u64 val)
{
struct msm_cpr_debug_device *debugdev = data;
uint32_t reg = val;
mutex_lock(&debugdev->debug_mutex);
if (msm_cpr_debug_addr_is_valid(debugdev->addr_offset))
write_reg(debugdev, reg);
mutex_unlock(&debugdev->debug_mutex);
return 0;
}
static int msm_cpr_debug_data_get(void *data, u64 *val)
{
struct msm_cpr_debug_device *debugdev = data;
uint32_t reg;
mutex_lock(&debugdev->debug_mutex);
if (msm_cpr_debug_addr_is_valid(debugdev->addr_offset)) {
reg = read_reg(debugdev);
*val = reg;
}
mutex_unlock(&debugdev->debug_mutex);
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(debug_data_fops, msm_cpr_debug_data_get,
msm_cpr_debug_data_set, "0x%02llX\n");
static int msm_cpr_debug_addr_set(void *data, u64 val)
{
struct msm_cpr_debug_device *debugdev = data;
if (msm_cpr_debug_addr_is_valid(val)) {
mutex_lock(&debugdev->debug_mutex);
debugdev->addr_offset = val;
mutex_unlock(&debugdev->debug_mutex);
}
return 0;
}
static int msm_cpr_debug_addr_get(void *data, u64 *val)
{
struct msm_cpr_debug_device *debugdev = data;
mutex_lock(&debugdev->debug_mutex);
if (msm_cpr_debug_addr_is_valid(debugdev->addr_offset))
*val = debugdev->addr_offset;
mutex_unlock(&debugdev->debug_mutex);
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(debug_addr_fops, msm_cpr_debug_addr_get,
msm_cpr_debug_addr_set, "0x%03llX\n");
int msm_cpr_debug_init(void *data)
{
char *name = "cpr-debug";
struct msm_cpr_debug_device *debugdev;
struct dentry *dir;
struct dentry *temp;
int rc;
debugdev = kzalloc(sizeof(struct msm_cpr_debug_device), GFP_KERNEL);
if (debugdev == NULL) {
pr_err("kzalloc failed\n");
return -ENOMEM;
}
dir = debugfs_create_dir(name, NULL);
if (dir == NULL || IS_ERR(dir)) {
pr_err("debugfs_create_dir failed: rc=%ld\n", PTR_ERR(dir));
rc = PTR_ERR(dir);
goto dir_error;
}
temp = debugfs_create_file("address", S_IRUGO | S_IWUSR, dir, debugdev,
&debug_addr_fops);
if (temp == NULL || IS_ERR(temp)) {
pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp));
rc = PTR_ERR(temp);
goto file_error;
}
temp = debugfs_create_file("data", S_IRUGO | S_IWUSR, dir, debugdev,
&debug_data_fops);
if (temp == NULL || IS_ERR(temp)) {
pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp));
rc = PTR_ERR(temp);
goto file_error;
}
debugdev->base = data;
debugdev->addr_offset = -1;
debugdev->dir = dir;
mutex_init(&debugdev->debug_mutex);
return 0;
file_error:
debugfs_remove_recursive(dir);
dir_error:
kfree(debugdev);
return rc;
}