Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 266961
b: refs/heads/master
c: be2136e
h: refs/heads/master
i:
  266959: e6c862b
v: v3
  • Loading branch information
Mark Brown committed Aug 9, 2011
1 parent c2db359 commit e74d721
Show file tree
Hide file tree
Showing 6 changed files with 370 additions and 39 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 3566cc9d90e3f774cea47de6986c59a09090ce2b
refs/heads/master: be2136e5c99fd5faa655e571b507362b27df1a28
1 change: 1 addition & 0 deletions trunk/drivers/base/regmap/Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
obj-$(CONFIG_REGMAP) += regmap.o
obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o
obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o
obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
61 changes: 61 additions & 0 deletions trunk/drivers/base/regmap/internal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Register map access API internal header
*
* Copyright 2011 Wolfson Microelectronics plc
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/

#ifndef _REGMAP_INTERNAL_H
#define _REGMAP_INTERNAL_H

#include <linux/regmap.h>
#include <linux/fs.h>

struct regmap;

struct regmap_format {
size_t buf_size;
size_t reg_bytes;
size_t val_bytes;
void (*format_write)(struct regmap *map,
unsigned int reg, unsigned int val);
void (*format_reg)(void *buf, unsigned int reg);
void (*format_val)(void *buf, unsigned int val);
unsigned int (*parse_val)(void *buf);
};

struct regmap {
struct mutex lock;

struct device *dev; /* Device we do I/O on */
void *work_buf; /* Scratch buffer used to format I/O */
struct regmap_format format; /* Buffer format */
const struct regmap_bus *bus;

#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs;
#endif

unsigned int max_register;
bool (*writeable_reg)(struct device *dev, unsigned int reg);
bool (*readable_reg)(struct device *dev, unsigned int reg);
bool (*volatile_reg)(struct device *dev, unsigned int reg);
bool (*precious_reg)(struct device *dev, unsigned int reg);
};

#ifdef CONFIG_DEBUG_FS
extern void regmap_debugfs_initcall(void);
extern void regmap_debugfs_init(struct regmap *map);
extern void regmap_debugfs_exit(struct regmap *map);
#else
void regmap_debugfs_initcall(void) { }
void regmap_debugfs_init(struct regmap *map) { }
void regmap_debugfs_exit(struct regmap *map) { }
#endif

#endif
135 changes: 135 additions & 0 deletions trunk/drivers/base/regmap/regmap-debugfs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*
* Register map access API - debugfs
*
* Copyright 2011 Wolfson Microelectronics plc
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/

#include <linux/slab.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/debugfs.h>
#include <linux/uaccess.h>

#include "internal.h"

static struct dentry *regmap_debugfs_root;

static int regmap_map_open_file(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
return 0;
}

static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
size_t reg_len, val_len, tot_len;
size_t buf_pos = 0;
loff_t p = 0;
ssize_t ret;
int i;
struct regmap *map = file->private_data;
char *buf;
unsigned int val;

if (*ppos < 0 || !count)
return -EINVAL;

buf = kmalloc(count, GFP_KERNEL);
if (!buf)
return -ENOMEM;

/* Calculate the length of a fixed format */
snprintf(buf, count, "%x", map->max_register);
reg_len = strlen(buf);
val_len = 2 * map->format.val_bytes;
tot_len = reg_len + val_len + 3; /* : \n */

for (i = 0; i < map->max_register; i++) {
if (map->readable_reg &&
!map->readable_reg(map->dev, i))
continue;

if (map->precious_reg &&
map->precious_reg(map->dev, i))
continue;

/* If we're in the region the user is trying to read */
if (p >= *ppos) {
/* ...but not beyond it */
if (buf_pos >= count - 1 - tot_len)
break;

/* Format the register */
snprintf(buf + buf_pos, count - buf_pos, "%.*x: ",
reg_len, i);
buf_pos += reg_len + 2;

/* Format the value, write all X if we can't read */
ret = regmap_read(map, i, &val);
if (ret == 0)
snprintf(buf + buf_pos, count - buf_pos,
"%.*x", val_len, val);
else
memset(buf + buf_pos, 'X', val_len);
buf_pos += 2 * map->format.val_bytes;

buf[buf_pos++] = '\n';
}
p += tot_len;
}

ret = buf_pos;

if (copy_to_user(user_buf, buf, buf_pos)) {
ret = -EFAULT;
goto out;
}

*ppos += buf_pos;

out:
kfree(buf);
return ret;
}

static const struct file_operations regmap_map_fops = {
.open = regmap_map_open_file,
.read = regmap_map_read_file,
.llseek = default_llseek,
};


void regmap_debugfs_init(struct regmap *map)
{
map->debugfs = debugfs_create_dir(dev_name(map->dev),
regmap_debugfs_root);
if (!map->debugfs) {
dev_warn(map->dev, "Failed to create debugfs directory\n");
return;
}

if (map->max_register)
debugfs_create_file("registers", 0400, map->debugfs,
map, &regmap_map_fops);
}

void regmap_debugfs_exit(struct regmap *map)
{
debugfs_remove_recursive(map->debugfs);
}

void regmap_debugfs_initcall(void)
{
regmap_debugfs_root = debugfs_create_dir("regmap", NULL);
if (!regmap_debugfs_root) {
pr_warn("regmap: Failed to create debugfs root\n");
return;
}
}
98 changes: 60 additions & 38 deletions trunk/drivers/base/regmap/regmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,34 +15,10 @@
#include <linux/mutex.h>
#include <linux/err.h>

#include <linux/regmap.h>

struct regmap;

struct regmap_format {
size_t buf_size;
size_t reg_bytes;
size_t val_bytes;
void (*format_write)(struct regmap *map,
unsigned int reg, unsigned int val);
void (*format_reg)(void *buf, unsigned int reg);
void (*format_val)(void *buf, unsigned int val);
unsigned int (*parse_val)(void *buf);
};

struct regmap {
struct mutex lock;

struct device *dev; /* Device we do I/O on */
void *work_buf; /* Scratch buffer used to format I/O */
struct regmap_format format; /* Buffer format */
const struct regmap_bus *bus;

unsigned int max_register;
bool (*writeable_reg)(struct device *dev, unsigned int reg);
bool (*readable_reg)(struct device *dev, unsigned int reg);
bool (*volatile_reg)(struct device *dev, unsigned int reg);
};
#define CREATE_TRACE_POINTS
#include <trace/events/regmap.h>

#include "internal.h"

static void regmap_format_4_12_write(struct regmap *map,
unsigned int reg, unsigned int val)
Expand Down Expand Up @@ -125,6 +101,7 @@ struct regmap *regmap_init(struct device *dev,
map->writeable_reg = config->writeable_reg;
map->readable_reg = config->readable_reg;
map->volatile_reg = config->volatile_reg;
map->precious_reg = config->precious_reg;

switch (config->reg_bits) {
case 4:
Expand Down Expand Up @@ -180,6 +157,8 @@ struct regmap *regmap_init(struct device *dev,
goto err_bus;
}

regmap_debugfs_init(map);

return map;

err_bus:
Expand All @@ -196,6 +175,7 @@ EXPORT_SYMBOL_GPL(regmap_init);
*/
void regmap_exit(struct regmap *map)
{
regmap_debugfs_exit(map);
kfree(map->work_buf);
module_put(map->bus->owner);
kfree(map);
Expand All @@ -208,16 +188,32 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
void *buf;
int ret = -ENOTSUPP;
size_t len;
int i;

/* Check for unwritable registers before we start */
if (map->writeable_reg)
for (i = 0; i < val_len / map->format.val_bytes; i++)
if (!map->writeable_reg(map->dev, reg + i))
return -EINVAL;

map->format.format_reg(map->work_buf, reg);

/* Try to do a gather write if we can */
if (map->bus->gather_write)
trace_regmap_hw_write_start(map->dev, reg,
val_len / map->format.val_bytes);

/* If we're doing a single register write we can probably just
* send the work_buf directly, otherwise try to do a gather
* write.
*/
if (val == map->work_buf + map->format.reg_bytes)
ret = map->bus->write(map->dev, map->work_buf,
map->format.reg_bytes + val_len);
else if (map->bus->gather_write)
ret = map->bus->gather_write(map->dev, map->work_buf,
map->format.reg_bytes,
val, val_len);

/* Otherwise fall back on linearising by hand. */
/* If that didn't work fall back on linearising by hand. */
if (ret == -ENOTSUPP) {
len = map->format.reg_bytes + val_len;
buf = kmalloc(len, GFP_KERNEL);
Expand All @@ -231,19 +227,31 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
kfree(buf);
}

trace_regmap_hw_write_done(map->dev, reg,
val_len / map->format.val_bytes);

return ret;
}

static int _regmap_write(struct regmap *map, unsigned int reg,
unsigned int val)
{
int ret;
BUG_ON(!map->format.format_write && !map->format.format_val);

trace_regmap_reg_write(map->dev, reg, val);

if (map->format.format_write) {
map->format.format_write(map, reg, val);

return map->bus->write(map->dev, map->work_buf,
map->format.buf_size);
trace_regmap_hw_write_start(map->dev, reg, 1);

ret = map->bus->write(map->dev, map->work_buf,
map->format.buf_size);

trace_regmap_hw_write_done(map->dev, reg, 1);

return ret;
} else {
map->format.format_val(map->work_buf + map->format.reg_bytes,
val);
Expand Down Expand Up @@ -325,12 +333,16 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
if (map->bus->read_flag_mask)
u8[0] |= map->bus->read_flag_mask;

trace_regmap_hw_read_start(map->dev, reg,
val_len / map->format.val_bytes);

ret = map->bus->read(map->dev, map->work_buf, map->format.reg_bytes,
val, map->format.val_bytes);
if (ret != 0)
return ret;
val, val_len);

return 0;
trace_regmap_hw_read_done(map->dev, reg,
val_len / map->format.val_bytes);

return ret;
}

static int _regmap_read(struct regmap *map, unsigned int reg,
Expand All @@ -342,8 +354,10 @@ static int _regmap_read(struct regmap *map, unsigned int reg,
return -EINVAL;

ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes);
if (ret == 0)
if (ret == 0) {
*val = map->format.parse_val(map->work_buf);
trace_regmap_reg_read(map->dev, reg, *val);
}

return ret;
}
Expand Down Expand Up @@ -462,3 +476,11 @@ int regmap_update_bits(struct regmap *map, unsigned int reg,
return ret;
}
EXPORT_SYMBOL_GPL(regmap_update_bits);

static int __init regmap_initcall(void)
{
regmap_debugfs_initcall();

return 0;
}
postcore_initcall(regmap_initcall);
Loading

0 comments on commit e74d721

Please sign in to comment.