/*
* Copyright (C) ST-Ericsson SA 2010
*
* Author: Mattias Wallin <mattias.wallin@stericsson.com> for ST-Ericsson.
* License Terms: GNU General Public License v2
*/
/*
* AB8500 register access
* ======================
*
* read:
* # echo BANK > <debugfs>/ab8500/register-bank
* # echo ADDR > <debugfs>/ab8500/register-address
* # cat <debugfs>/ab8500/register-value
*
* write:
* # echo BANK > <debugfs>/ab8500/register-bank
* # echo ADDR > <debugfs>/ab8500/register-address
* # echo VALUE > <debugfs>/ab8500/register-value
*
* read all registers from a bank:
* # echo BANK > <debugfs>/ab8500/register-bank
* # cat <debugfs>/ab8500/all-bank-register
*
* BANK target AB8500 register bank
* ADDR target AB8500 register address
* VALUE decimal or 0x-prefixed hexadecimal
*
*
* User Space notification on AB8500 IRQ
* =====================================
*
* Allows user space entity to be notified when target AB8500 IRQ occurs.
* When subscribed, a sysfs entry is created in ab8500.i2c platform device.
* One can pool this file to get target IRQ occurence information.
*
* subscribe to an AB8500 IRQ:
* # echo IRQ > <debugfs>/ab8500/irq-subscribe
*
* unsubscribe from an AB8500 IRQ:
* # echo IRQ > <debugfs>/ab8500/irq-unsubscribe
*
*
* AB8500 register formated read/write access
* ==========================================
*
* Read: read data, data>>SHIFT, data&=MASK, output data
* [0xABCDEF98] shift=12 mask=0xFFF => 0x00000CDE
* Write: read data, data &= ~(MASK<<SHIFT), data |= (VALUE<<SHIFT), write data
* [0xABCDEF98] shift=12 mask=0xFFF value=0x123 => [0xAB123F98]
*
* Usage:
* # echo "CMD [OPTIONS] BANK ADRESS [VALUE]" > $debugfs/ab8500/hwreg
*
* CMD read read access
* write write access
*
* BANK target reg bank
* ADDRESS target reg address
* VALUE (write) value to be updated
*
* OPTIONS
* -d|-dec (read) output in decimal
* -h|-hexa (read) output in 0x-hexa (default)
* -l|-w|-b 32bit (default), 16bit or 8bit reg access
* -m|-mask MASK 0x-hexa mask (default 0xFFFFFFFF)
* -s|-shift SHIFT bit shift value (read:left, write:right)
* -o|-offset OFFSET address offset to add to ADDRESS value
*
* Warning: bit shift operation is applied to bit-mask.
* Warning: bit shift direction depends on read or right command.
*/
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/kobject.h>
#include <linux/slab.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
#ifdef CONFIG_DEBUG_FS
#include <linux/string.h>
#include <linux/ctype.h>
#endif
static u32 debug_bank;
static u32 debug_address;
static int irq_first;
static int irq_last;
static u32 irq_count[AB8500_NR_IRQS];
static struct device_attribute *dev_attr[AB8500_NR_IRQS];
static char *event_name[AB8500_NR_IRQS];
/**
* struct ab8500_reg_range
* @first: the first address of the range
* @last: the last address of the range
* @perm: access permissions for the range
*/
struct ab8500_reg_range {
u8 first;
u8 last;
u8 perm;
};
/**
* struct ab8500_prcmu_ranges
* @num_ranges: the number of ranges in the list
* @bankid: bank identifier
* @range: the list of register ranges
*/
struct ab8500_prcmu_ranges {
u8 num_ranges;
u8 bankid;
const struct ab8500_reg_range *range;
};
/* hwreg- "mask" and "shift" entries ressources */
struct hwreg_cfg {
u32 bank; /* target bank */
u32 addr; /* target address */
uint fmt; /* format */
uint mask; /* read/write mask, applied before any bit shift */
int shift; /* bit shift (read:right shift, write:left shift */
};
/* fmt bit #0: 0=hexa, 1=dec */
#define REG_FMT_DEC(c) ((c)->fmt & 0x1)
#define REG_FMT_HEX(c) (!REG_FMT_DEC(c))
static struct hwreg_cfg hwreg_cfg = {
.addr = 0, /* default: invalid phys addr */
.fmt = 0, /* default: 32bit access, hex output */
.mask = 0xFFFFFFFF, /* default: no mask */
.shift = 0, /* default: no bit shift */
};
#define AB8500_NAME_STRING "ab8500"
#define AB8500_NUM_BANKS 22
#define AB8500_REV_REG 0x80
static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = {
[0x0] = {
.num_ranges = 0,
.range = NULL,
},
[AB8500_SYS_CTRL1_BLOCK] = {
.num_ranges = 3,
.range = (struct ab8500_reg_range[]) {
{
.first = 0x00,
.last = 0x02,
},
{
.first = 0x42,
.last = 0x42,
},
{
.first = 0x80,
.last = 0x81,
},
},
},
[AB8500_SYS_CTRL2_BLOCK] = {
.num_ranges = 4,
.range = (struct ab8500_reg_range[]) {
{
.first = 0x00,
.last = 0x0D,
},
{
.first = 0x0F,
.last = 0x17,
},
{
.first = 0x30