Commit dce7886e authored by Samuel Ortiz's avatar Samuel Ortiz

Merge branch 'for-mfd' of git://git.linaro.org/people/ljones/linux-3.0-ux500 into for-next

Signed-off-by: default avatarSamuel Ortiz <sameo@linux.intel.com>
parents ba3980df 8908c049
......@@ -320,6 +320,7 @@ static struct abx500_ops ab8500_ops = {
.mask_and_set_register = ab8500_mask_and_set_register,
.event_registers_startup_state_get = NULL,
.startup_irq_enabled = NULL,
.dump_all_banks = ab8500_dump_all_banks,
};
static void ab8500_irq_lock(struct irq_data *data)
......@@ -521,6 +522,7 @@ static irqreturn_t ab8500_irq(int irq, void *dev)
int virq = ab8500_irq_get_virq(ab8500, line);
handle_nested_irq(virq);
ab8500_debug_register_interrupt(line);
value &= ~(1 << bit);
} while (value);
......
......@@ -4,6 +4,72 @@
* 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>
......@@ -11,13 +77,29 @@
#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>
#include <linux/mfd/abx500/ab8500-gpadc.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;
static int num_irqs;
static struct device_attribute **dev_attr;
static char **event_name;
/**
* struct ab8500_reg_range
* @first: the first address of the range
......@@ -42,15 +124,35 @@ struct ab8500_prcmu_ranges {
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_ADC_NAME_STRING "gpadc"
#define AB8500_NUM_BANKS 24
#define AB8500_REV_REG 0x80
static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = {
[0x0] = {
.num_ranges = 0,
.range = 0,
.range = NULL,
},
[AB8500_SYS_CTRL1_BLOCK] = {
.num_ranges = 3,
......@@ -215,7 +317,7 @@ static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = {
},
},
[AB8500_CHARGER] = {
.num_ranges = 8,
.num_ranges = 9,
.range = (struct ab8500_reg_range[]) {
{
.first = 0x00,
......@@ -249,6 +351,10 @@ static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = {
.first = 0xC0,
.last = 0xC2,
},
{
.first = 0xf5,
.last = 0xf6,
},
},
},
[AB8500_GAS_GAUGE] = {
......@@ -268,6 +374,24 @@ static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = {
},
},
},
[AB8500_DEVELOPMENT] = {
.num_ranges = 1,
.range = (struct ab8500_reg_range[]) {
{
.first = 0x00,
.last = 0x00,
},
},
},
[AB8500_DEBUG] = {
.num_ranges = 1,
.range = (struct ab8500_reg_range[]) {
{
.first = 0x05,
.last = 0x07,
},
},
},
[AB8500_AUDIO] = {
.num_ranges = 1,
.range = (struct ab8500_reg_range[]) {
......@@ -354,15 +478,30 @@ static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = {
},
};
static int ab8500_registers_print(struct seq_file *s, void *p)
static irqreturn_t ab8500_debug_handler(int irq, void *data)
{
struct device *dev = s->private;
unsigned int i;
u32 bank = debug_bank;
char buf[16];
struct kobject *kobj = (struct kobject *)data;
unsigned int irq_abb = irq - irq_first;
seq_printf(s, AB8500_NAME_STRING " register values:\n");
if (irq_abb < num_irqs)
irq_count[irq_abb]++;
/*
* This makes it possible to use poll for events (POLLPRI | POLLERR)
* from userspace on sysfs file named <irq-nr>
*/
sprintf(buf, "%d", irq);
sysfs_notify(kobj, NULL, buf);
return IRQ_HANDLED;
}
/* Prints to seq_file or log_buf */
static int ab8500_registers_print(struct device *dev, u32 bank,
struct seq_file *s)
{
unsigned int i;
seq_printf(s, " bank %u:\n", bank);
for (i = 0; i < debug_ranges[bank].num_ranges; i++) {
u32 reg;
......@@ -379,22 +518,42 @@ static int ab8500_registers_print(struct seq_file *s, void *p)
return err;
}
err = seq_printf(s, " [%u/0x%02X]: 0x%02X\n", bank,
reg, value);
if (err < 0) {
dev_err(dev, "seq_printf overflow\n");
/* Error is not returned here since
* the output is wanted in any case */
return 0;
if (s) {
err = seq_printf(s, " [%u/0x%02X]: 0x%02X\n",
bank, reg, value);
if (err < 0) {
dev_err(dev,
"seq_printf overflow bank=%d reg=%d\n",
bank, reg);
/* Error is not returned here since
* the output is wanted in any case */
return 0;
}
} else {
printk(KERN_INFO" [%u/0x%02X]: 0x%02X\n", bank,
reg, value);
}
}
}
return 0;
}
static int ab8500_print_bank_registers(struct seq_file *s, void *p)
{
struct device *dev = s->private;
u32 bank = debug_bank;
seq_printf(s, AB8500_NAME_STRING " register values:\n");
seq_printf(s, " bank %u:\n", bank);
ab8500_registers_print(dev, bank, s);
return 0;
}
static int ab8500_registers_open(struct inode *inode, struct file *file)
{
return single_open(file, ab8500_registers_print, inode->i_private);
return single_open(file, ab8500_print_bank_registers, inode->i_private);
}
static const struct file_operations ab8500_registers_fops = {
......@@ -405,6 +564,64 @@ static const struct file_operations ab8500_registers_fops = {
.owner = THIS_MODULE,
};
static int ab8500_print_all_banks(struct seq_file *s, void *p)
{
struct device *dev = s->private;
unsigned int i;
int err;
seq_printf(s, AB8500_NAME_STRING " register values:\n");
for (i = 1; i < AB8500_NUM_BANKS; i++) {
err = seq_printf(s, " bank %u:\n", i);
if (err < 0)
dev_err(dev, "seq_printf overflow, bank=%d\n", i);
ab8500_registers_print(dev, i, s);
}
return 0;
}
/* Dump registers to kernel log */
void ab8500_dump_all_banks(struct device *dev)
{
unsigned int i;
printk(KERN_INFO"ab8500 register values:\n");
for (i = 1; i < AB8500_NUM_BANKS; i++) {
printk(KERN_INFO" bank %u:\n", i);
ab8500_registers_print(dev, i, NULL);
}
}
static int ab8500_all_banks_open(struct inode *inode, struct file *file)
{
struct seq_file *s;
int err;
err = single_open(file, ab8500_print_all_banks, inode->i_private);
if (!err) {
/* Default buf size in seq_read is not enough */
s = (struct seq_file *)file->private_data;
s->size = (PAGE_SIZE * 2);
s->buf = kmalloc(s->size, GFP_KERNEL);
if (!s->buf) {
single_release(inode, file);
err = -ENOMEM;
}
}
return err;
}
static const struct file_operations ab8500_all_banks_fops = {
.open = ab8500_all_banks_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int ab8500_bank_print(struct seq_file *s, void *p)
{
return seq_printf(s, "%d\n", debug_bank);
......@@ -519,6 +736,761 @@ static ssize_t ab8500_val_write(struct file *file,
return count;
}
/*
* Interrupt status
*/
static u32 num_interrupts[AB8500_MAX_NR_IRQS];
static int num_interrupt_lines;
void ab8500_debug_register_interrupt(int line)
{
if (line < num_interrupt_lines)
num_interrupts[line]++;
}
static int ab8500_interrupts_print(struct seq_file *s, void *p)
{
int line;
seq_printf(s, "irq: number of\n");
for (line = 0; line < num_interrupt_lines; line++)
seq_printf(s, "%3i: %6i\n", line, num_interrupts[line]);
return 0;
}
static int ab8500_interrupts_open(struct inode *inode, struct file *file)
{
return single_open(file, ab8500_interrupts_print, inode->i_private);
}
/*
* - HWREG DB8500 formated routines
*/
static int ab8500_hwreg_print(struct seq_file *s, void *d)
{
struct device *dev = s->private;
int ret;
u8 regvalue;
ret = abx500_get_register_interruptible(dev,
(u8)hwreg_cfg.bank, (u8)hwreg_cfg.addr, &regvalue);
if (ret < 0) {
dev_err(dev, "abx500_get_reg fail %d, %d\n",
ret, __LINE__);
return -EINVAL;
}
if (hwreg_cfg.shift >= 0)
regvalue >>= hwreg_cfg.shift;
else
regvalue <<= -hwreg_cfg.shift;
regvalue &= hwreg_cfg.mask;
if (REG_FMT_DEC(&hwreg_cfg))
seq_printf(s, "%d\n", regvalue);
else
seq_printf(s, "0x%02X\n", regvalue);
return 0;
}
static int ab8500_hwreg_open(struct inode *inode, struct file *file)
{
return single_open(file, ab8500_hwreg_print, inode->i_private);
}
static int ab8500_gpadc_bat_ctrl_print(struct seq_file *s, void *p)
{
int bat_ctrl_raw;
int bat_ctrl_convert;
struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
bat_ctrl_raw = ab8500_gpadc_read_raw(gpadc, BAT_CTRL);
bat_ctrl_convert = ab8500_gpadc_ad_to_voltage(gpadc,
BAT_CTRL, bat_ctrl_raw);
return seq_printf(s, "%d,0x%X\n",
bat_ctrl_convert, bat_ctrl_raw);
}
static int ab8500_gpadc_bat_ctrl_open(struct inode *inode, struct file *file)
{
return single_open(file, ab8500_gpadc_bat_ctrl_print, inode->i_private);
}
static const struct file_operations ab8500_gpadc_bat_ctrl_fops = {
.open = ab8500_gpadc_bat_ctrl_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int ab8500_gpadc_btemp_ball_print(struct seq_file *s, void *p)
{
int btemp_ball_raw;
int btemp_ball_convert;
struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
btemp_ball_raw = ab8500_gpadc_read_raw(gpadc, BTEMP_BALL);
btemp_ball_convert = ab8500_gpadc_ad_to_voltage(gpadc, BTEMP_BALL,
btemp_ball_raw);
return seq_printf(s,
"%d,0x%X\n", btemp_ball_convert, btemp_ball_raw);
}
static int ab8500_gpadc_btemp_ball_open(struct inode *inode,
struct file *file)
{
return single_open(file, ab8500_gpadc_btemp_ball_print, inode->i_private);
}
static const struct file_operations ab8500_gpadc_btemp_ball_fops = {
.open = ab8500_gpadc_btemp_ball_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int ab8500_gpadc_main_charger_v_print(struct seq_file *s, void *p)
{
int main_charger_v_raw;
int main_charger_v_convert;
struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
main_charger_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_V);
main_charger_v_convert = ab8500_gpadc_ad_to_voltage(gpadc,
MAIN_CHARGER_V, main_charger_v_raw);
return seq_printf(s, "%d,0x%X\n",
main_charger_v_convert, main_charger_v_raw);
}
static int ab8500_gpadc_main_charger_v_open(struct inode *inode,
struct file *file)
{
return single_open(file, ab8500_gpadc_main_charger_v_print,
inode->i_private);
}
static const struct file_operations ab8500_gpadc_main_charger_v_fops = {
.open = ab8500_gpadc_main_charger_v_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int ab8500_gpadc_acc_detect1_print(struct seq_file *s, void *p)
{
int acc_detect1_raw;
int acc_detect1_convert;
struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
acc_detect1_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT1);
acc_detect1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ACC_DETECT1,
acc_detect1_raw);
return seq_printf(s, "%d,0x%X\n",
acc_detect1_convert, acc_detect1_raw);
}
static int ab8500_gpadc_acc_detect1_open(struct inode *inode,
struct file *file)
{
return single_open(file, ab8500_gpadc_acc_detect1_print,
inode->i_private);
}
static const struct file_operations ab8500_gpadc_acc_detect1_fops = {
.open = ab8500_gpadc_acc_detect1_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int ab8500_gpadc_acc_detect2_print(struct seq_file *s, void *p)
{
int acc_detect2_raw;
int acc_detect2_convert;
struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
acc_detect2_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT2);
acc_detect2_convert = ab8500_gpadc_ad_to_voltage(gpadc,
ACC_DETECT2, acc_detect2_raw);
return seq_printf(s, "%d,0x%X\n",
acc_detect2_convert, acc_detect2_raw);
}
static int ab8500_gpadc_acc_detect2_open(struct inode *inode,
struct file *file)
{
return single_open(file, ab8500_gpadc_acc_detect2_print,
inode->i_private);
}
static const struct file_operations ab8500_gpadc_acc_detect2_fops = {
.open = ab8500_gpadc_acc_detect2_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int ab8500_gpadc_aux1_print(struct seq_file *s, void *p)
{
int aux1_raw;
int aux1_convert;
struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
aux1_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX1);
aux1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX1,
aux1_raw);
return seq_printf(s, "%d,0x%X\n",
aux1_convert, aux1_raw);
}
static int ab8500_gpadc_aux1_open(struct inode *inode, struct file *file)
{
return single_open(file, ab8500_gpadc_aux1_print, inode->i_private);
}
static const struct file_operations ab8500_gpadc_aux1_fops = {
.open = ab8500_gpadc_aux1_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int ab8500_gpadc_aux2_print(struct seq_file *s, void *p)
{
int aux2_raw;
int aux2_convert;
struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
aux2_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX2);
aux2_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX2,
aux2_raw);
return seq_printf(s, "%d,0x%X\n",
aux2_convert, aux2_raw);
}
static int ab8500_gpadc_aux2_open(struct inode *inode, struct file *file)
{
return single_open(file, ab8500_gpadc_aux2_print, inode->i_private);
}
static const struct file_operations ab8500_gpadc_aux2_fops = {
.open = ab8500_gpadc_aux2_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int ab8500_gpadc_main_bat_v_print(struct seq_file *s, void *p)
{
int main_bat_v_raw;
int main_bat_v_convert;
struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
main_bat_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_BAT_V);
main_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, MAIN_BAT_V,
main_bat_v_raw);
return seq_printf(s, "%d,0x%X\n",
main_bat_v_convert, main_bat_v_raw);
}
static int ab8500_gpadc_main_bat_v_open(struct inode *inode,
struct file *file)
{
return single_open(file, ab8500_gpadc_main_bat_v_print, inode->i_private);
}
static const struct file_operations ab8500_gpadc_main_bat_v_fops = {
.open = ab8500_gpadc_main_bat_v_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int ab8500_gpadc_vbus_v_print(struct seq_file *s, void *p)
{
int vbus_v_raw;
int vbus_v_convert;
struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
vbus_v_raw = ab8500_gpadc_read_raw(gpadc, VBUS_V);
vbus_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, VBUS_V,
vbus_v_raw);
return seq_printf(s, "%d,0x%X\n",
vbus_v_convert, vbus_v_raw);
}
static int ab8500_gpadc_vbus_v_open(struct inode *inode, struct file *file)
{
return single_open(file, ab8500_gpadc_vbus_v_print, inode->i_private);
}
static const struct file_operations ab8500_gpadc_vbus_v_fops = {
.open = ab8500_gpadc_vbus_v_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int ab8500_gpadc_main_charger_c_print(struct seq_file *s, void *p)
{
int main_charger_c_raw;
int main_charger_c_convert;
struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
main_charger_c_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_C);
main_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc,
MAIN_CHARGER_C, main_charger_c_raw);
return seq_printf(s, "%d,0x%X\n",
main_charger_c_convert, main_charger_c_raw);
}
static int ab8500_gpadc_main_charger_c_open(struct inode *inode,
struct file *file)
{
return single_open(file, ab8500_gpadc_main_charger_c_print,
inode->i_private);
}
static const struct file_operations ab8500_gpadc_main_charger_c_fops = {
.open = ab8500_gpadc_main_charger_c_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int ab8500_gpadc_usb_charger_c_print(struct seq_file *s, void *p)
{
int usb_charger_c_raw;
int usb_charger_c_convert;
struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
usb_charger_c_raw = ab8500_gpadc_read_raw(gpadc, USB_CHARGER_C);
usb_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc,
USB_CHARGER_C, usb_charger_c_raw);
return seq_printf(s, "%d,0x%X\n",
usb_charger_c_convert, usb_charger_c_raw);
}
static int ab8500_gpadc_usb_charger_c_open(struct inode *inode,
struct file *file)
{
return single_open(file, ab8500_gpadc_usb_charger_c_print,
inode->i_private);
}
static const struct file_operations ab8500_gpadc_usb_charger_c_fops = {
.open = ab8500_gpadc_usb_charger_c_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int ab8500_gpadc_bk_bat_v_print(struct seq_file *s, void *p)
{
int bk_bat_v_raw;
int bk_bat_v_convert;
struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
bk_bat_v_raw = ab8500_gpadc_read_raw(gpadc, BK_BAT_V);
bk_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc,
BK_BAT_V, bk_bat_v_raw);
return seq_printf(s, "%d,0x%X\n",
bk_bat_v_convert, bk_bat_v_raw);
}
static int ab8500_gpadc_bk_bat_v_open(struct inode *inode, struct file *file)
{
return single_open(file, ab8500_gpadc_bk_bat_v_print, inode->i_private);
}
static const struct file_operations ab8500_gpadc_bk_bat_v_fops = {
.open = ab8500_gpadc_bk_bat_v_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int ab8500_gpadc_die_temp_print(struct seq_file *s, void *p)
{
int die_temp_raw;
int die_temp_convert;
struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
die_temp_raw = ab8500_gpadc_read_raw(gpadc, DIE_TEMP);
die_temp_convert = ab8500_gpadc_ad_to_voltage(gpadc, DIE_TEMP,
die_temp_raw);
return seq_printf(s, "%d,0x%X\n",
die_temp_convert, die_temp_raw);
}
static int ab8500_gpadc_die_temp_open(struct inode *inode, struct file *file)
{
return single_open(file, ab8500_gpadc_die_temp_print, inode->i_private);
}
static const struct file_operations ab8500_gpadc_die_temp_fops = {
.open = ab8500_gpadc_die_temp_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
/*
* return length of an ASCII numerical value, 0 is string is not a
* numerical value.
* string shall start at value 1st char.
* string can be tailed with \0 or space or newline chars only.
* value can be decimal or hexadecimal (prefixed 0x or 0X).
*/
static int strval_len(char *b)
{
char *s = b;
if ((*s == '0') && ((*(s+1) == 'x') || (*(s+1) == 'X'))) {
s += 2;
for (; *s && (*s != ' ') && (*s != '\n'); s++) {
if (!isxdigit(*s))
return 0;
}
} else {
if (*s == '-')
s++;
for (; *s && (*s != ' ') && (*s != '\n'); s++) {
if (!isdigit(*s))
return 0;
}
}
return (int) (s-b);
}
/*
* parse hwreg input data.
* update global hwreg_cfg only if input data syntax is ok.
*/
static ssize_t hwreg_common_write(char *b, struct hwreg_cfg *cfg,
struct device *dev)
{
uint write, val = 0;
u8 regvalue;
int ret;
struct hwreg_cfg loc = {
.bank = 0, /* default: invalid phys addr */
.addr = 0, /* default: invalid phys addr */
.fmt = 0, /* default: 32bit access, hex output */
.mask = 0xFFFFFFFF, /* default: no mask */
.shift = 0, /* default: no bit shift */
};
/* read or write ? */
if (!strncmp(b, "read ", 5)) {
write = 0;
b += 5;
} else if (!strncmp(b, "write ", 6)) {
write = 1;
b += 6;
} else
return -EINVAL;
/* OPTIONS -l|-w|-b -s -m -o */
while ((*b == ' ') || (*b == '-')) {
if (*(b-1) != ' ') {
b++;
continue;
}
if ((!strncmp(b, "-d ", 3)) ||
(!strncmp(b, "-dec ", 5))) {
b += (*(b+2) == ' ') ? 3 : 5;
loc.fmt |= (1<<0);
} else if ((!strncmp(b, "-h ", 3)) ||
(!strncmp(b, "-hex ", 5))) {
b += (*(b+2) == ' ') ? 3 : 5;
loc.fmt &= ~(1<<0);
} else if ((!strncmp(b, "-m ", 3)) ||
(!strncmp(b, "-mask ", 6))) {
b += (*(b+2) == ' ') ? 3 : 6;
if (strval_len(b) == 0)
return -EINVAL;
loc.mask = simple_strtoul(b, &b, 0);
} else if ((!strncmp(b, "-s ", 3)) ||
(!strncmp(b, "-shift ", 7))) {
b += (*(b+2) == ' ') ? 3 : 7;
if (strval_len(b) == 0)
return -EINVAL;
loc.shift = simple_strtol(b, &b, 0);
} else {
return -EINVAL;
}
}
/* get arg BANK and ADDRESS */
if (strval_len(b) == 0)
return -EINVAL;
loc.bank = simple_strtoul(b, &b, 0);
while (*b == ' ')
b++;
if (strval_len(b) == 0)
return -EINVAL;
loc.addr = simple_strtoul(b, &b, 0);
if (write) {
while (*b == ' ')
b++;
if (strval_len(b) == 0)
return -EINVAL;
val = simple_strtoul(b, &b, 0);
}
/* args are ok, update target cfg (mainly for read) */
*cfg = loc;
#ifdef ABB_HWREG_DEBUG
pr_warn("HWREG request: %s, %s, addr=0x%08X, mask=0x%X, shift=%d"
"value=0x%X\n", (write) ? "write" : "read",
REG_FMT_DEC(cfg) ? "decimal" : "hexa",
cfg->addr, cfg->mask, cfg->shift, val);
#endif
if (!write)
return 0;
ret = abx500_get_register_interruptible(dev,
(u8)cfg->bank, (u8)cfg->addr, &regvalue);
if (ret < 0) {
dev_err(dev, "abx500_get_reg fail %d, %d\n",
ret, __LINE__);
return -EINVAL;
}
if (cfg->shift >= 0) {
regvalue &= ~(cfg->mask << (cfg->shift));
val = (val & cfg->mask) << (cfg->shift);
} else {
regvalue &= ~(cfg->mask >> (-cfg->shift));
val = (val & cfg->mask) >> (-cfg->shift);
}
val = val | regvalue;
ret = abx500_set_register_interruptible(dev,
(u8)cfg->bank, (u8)cfg->addr, (u8)val);
if (ret < 0) {
pr_err("abx500_set_reg failed %d, %d", ret, __LINE__);
return -EINVAL;
}
return 0;
}
static ssize_t ab8500_hwreg_write(struct file *file,
const char __user *user_buf, size_t count, loff_t *ppos)
{
struct device *dev = ((struct seq_file *)(file->private_data))->private;
char buf[128];
int buf_size, ret;
/* Get userspace string and assure termination */
buf_size = min(count, (sizeof(buf)-1));
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
buf[buf_size] = 0;
/* get args and process */
ret = hwreg_common_write(buf, &hwreg_cfg, dev);
return (ret) ? ret : buf_size;
}
/*
* - irq subscribe/unsubscribe stuff
*/
static int ab8500_subscribe_unsubscribe_print(struct seq_file *s, void *p)
{
seq_printf(s, "%d\n", irq_first);
return 0;
}
static int ab8500_subscribe_unsubscribe_open(struct inode *inode,
struct file *file)
{
return single_open(file, ab8500_subscribe_unsubscribe_print,
inode->i_private);
}
/*
* Userspace should use poll() on this file. When an event occur
* the blocking poll will be released.
*/
static ssize_t show_irq(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned long name;
unsigned int irq_index;
int err;
err = strict_strtoul(attr->attr.name, 0, &name);
if (err)
return err;
irq_index = name - irq_first;
if (irq_index >= num_irqs)
return -EINVAL;
else
return sprintf(buf, "%u\n", irq_count[irq_index]);
}
static ssize_t ab8500_subscribe_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct device *dev = ((struct seq_file *)(file->private_data))->private;
char buf[32];
int buf_size;
unsigned long user_val;
int err;
unsigned int irq_index;
/* Get userspace string and assure termination */
buf_size = min(count, (sizeof(buf)-1));
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
buf[buf_size] = 0;
err = strict_strtoul(buf, 0, &user_val);
if (err)
return -EINVAL;
if (user_val < irq_first) {
dev_err(dev, "debugfs error input < %d\n", irq_first);
return -EINVAL;
}
if (user_val > irq_last) {
dev_err(dev, "debugfs error input > %d\n", irq_last);
return -EINVAL;
}
irq_index = user_val - irq_first;
if (irq_index >= num_irqs)
return -EINVAL;
/*
* This will create a sysfs file named <irq-nr> which userspace can
* use to select or poll and get the AB8500 events
*/
dev_attr[irq_index] = kmalloc(sizeof(struct device_attribute),
GFP_KERNEL);
event_name[irq_index] = kmalloc(buf_size, GFP_KERNEL);
sprintf(event_name[irq_index], "%lu", user_val);
dev_attr[irq_index]->show = show_irq;
dev_attr[irq_index]->store = NULL;
dev_attr[irq_index]->attr.name = event_name[irq_index];
dev_attr[irq_index]->attr.mode = S_IRUGO;
err = sysfs_create_file(&dev->kobj, &dev_attr[irq_index]->attr);
if (err < 0) {
printk(KERN_ERR "sysfs_create_file failed %d\n", err);
return err;
}
err = request_threaded_irq(user_val, NULL, ab8500_debug_handler,
IRQF_SHARED | IRQF_NO_SUSPEND,
"ab8500-debug", &dev->kobj);
if (err < 0) {
printk(KERN_ERR "request_threaded_irq failed %d, %lu\n",
err, user_val);
sysfs_remove_file(&dev->kobj, &dev_attr[irq_index]->attr);
return err;
}
return buf_size;
}
static ssize_t ab8500_unsubscribe_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct device *dev = ((struct seq_file *)(file->private_data))->private;
char buf[32];
int buf_size;
unsigned long user_val;
int err;
unsigned int irq_index;
/* Get userspace string and assure termination */
buf_size = min(count, (sizeof(buf)-1));
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
buf[buf_size] = 0;
err = strict_strtoul(buf, 0, &user_val);
if (err)
return -EINVAL;
if (user_val < irq_first) {
dev_err(dev, "debugfs error input < %d\n", irq_first);
return -EINVAL;
}
if (user_val > irq_last) {
dev_err(dev, "debugfs error input > %d\n", irq_last);
return -EINVAL;
}
irq_index = user_val - irq_first;
if (irq_index >= num_irqs)
return -EINVAL;
/* Set irq count to 0 when unsubscribe */
irq_count[irq_index] = 0;
if (dev_attr[irq_index])
sysfs_remove_file(&dev->kobj, &dev_attr[irq_index]->attr);
free_irq(user_val, &dev->kobj);
kfree(event_name[irq_index]);
kfree(dev_attr[irq_index]);
return buf_size;
}
/*
* - several deubgfs nodes fops
*/
static const struct file_operations ab8500_bank_fops = {
.open = ab8500_bank_open,
.write = ab8500_bank_write,
......@@ -546,64 +1518,231 @@ static const struct file_operations ab8500_val_fops = {
.owner = THIS_MODULE,
};
static const struct file_operations ab8500_interrupts_fops = {
.open = ab8500_interrupts_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static const struct file_operations ab8500_subscribe_fops = {
.open = ab8500_subscribe_unsubscribe_open,
.write = ab8500_subscribe_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static const struct file_operations ab8500_unsubscribe_fops = {
.open = ab8500_subscribe_unsubscribe_open,
.write = ab8500_unsubscribe_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static const struct file_operations ab8500_hwreg_fops = {
.open = ab8500_hwreg_open,
.write = ab8500_hwreg_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static struct dentry *ab8500_dir;
static struct dentry *ab8500_reg_file;
static struct dentry *ab8500_bank_file;
static struct dentry *ab8500_address_file;
static struct dentry *ab8500_val_file;
static struct dentry *ab8500_gpadc_dir;
static int ab8500_debug_probe(struct platform_device *plf)
{
struct dentry *file;
int ret = -ENOMEM;
struct ab8500 *ab8500;
debug_bank = AB8500_MISC;
debug_address = AB8500_REV_REG & 0x00FF;
ab8500 = dev_get_drvdata(plf->dev.parent);
num_irqs = ab8500->mask_size;
irq_count = kzalloc(sizeof(*irq_count)*num_irqs, GFP_KERNEL);
if (!irq_count)
return -ENOMEM;
dev_attr = kzalloc(sizeof(*dev_attr)*num_irqs,GFP_KERNEL);
if (!dev_attr)
goto out_freeirq_count;
event_name = kzalloc(sizeof(*event_name)*num_irqs, GFP_KERNEL);
if (!event_name)
goto out_freedev_attr;
irq_first = platform_get_irq_byname(plf, "IRQ_FIRST");
if (irq_first < 0) {
dev_err(&plf->dev, "First irq not found, err %d\n",
irq_first);
ret = irq_first;
goto out_freeevent_name;
}
irq_last = platform_get_irq_byname(plf, "IRQ_LAST");
if (irq_last < 0) {
dev_err(&plf->dev, "Last irq not found, err %d\n",
irq_last);
ret = irq_last;
goto out_freeevent_name;
}
ab8500_dir = debugfs_create_dir(AB8500_NAME_STRING, NULL);
if (!ab8500_dir)
goto exit_no_debugfs;
goto err;
ab8500_gpadc_dir = debugfs_create_dir(AB8500_ADC_NAME_STRING,
ab8500_dir);
if (!ab8500_gpadc_dir)
goto err;
file = debugfs_create_file("all-bank-registers", S_IRUGO,
ab8500_dir, &plf->dev, &ab8500_registers_fops);
if (!file)
goto err;
file = debugfs_create_file("all-banks", S_IRUGO,
ab8500_dir, &plf->dev, &ab8500_all_banks_fops);
if (!file)
goto err;
file = debugfs_create_file("register-bank", (S_IRUGO | S_IWUSR),
ab8500_dir, &plf->dev, &ab8500_bank_fops);
if (!file)
goto err;
file = debugfs_create_file("register-address", (S_IRUGO | S_IWUSR),
ab8500_dir, &plf->dev, &ab8500_address_fops);
if (!file)
goto err;
file = debugfs_create_file("register-value", (S_IRUGO | S_IWUSR),
ab8500_dir, &plf->dev, &ab8500_val_fops);
if (!file)
goto err;
ab8500_reg_file = debugfs_create_file("all-bank-registers",
S_IRUGO, ab8500_dir, &plf->dev, &ab8500_registers_fops);
if (!ab8500_reg_file)
goto exit_destroy_dir;
file = debugfs_create_file("irq-subscribe", (S_IRUGO | S_IWUSR),
ab8500_dir, &plf->dev, &ab8500_subscribe_fops);
if (!file)
goto err;
ab8500_bank_file = debugfs_create_file("register-bank",
(S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_bank_fops);
if (!ab8500_bank_file)
goto exit_destroy_reg;
if (is_ab8500(ab8500))
num_interrupt_lines = AB8500_NR_IRQS;
else if (is_ab8505(ab8500))
num_interrupt_lines = AB8505_NR_IRQS;
else if (is_ab9540(ab8500))
num_interrupt_lines = AB9540_NR_IRQS;
ab8500_address_file = debugfs_create_file("register-address",
(S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev,
&ab8500_address_fops);
if (!ab8500_address_file)
goto exit_destroy_bank;
file = debugfs_create_file("interrupts", (S_IRUGO),
ab8500_dir, &plf->dev, &ab8500_interrupts_fops);
if (!file)
goto err;
ab8500_val_file = debugfs_create_file("register-value",
(S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_val_fops);
if (!ab8500_val_file)
goto exit_destroy_address;
file = debugfs_create_file("irq-unsubscribe", (S_IRUGO | S_IWUSR),
ab8500_dir, &plf->dev, &ab8500_unsubscribe_fops);
if (!file)
goto err;
file = debugfs_create_file("hwreg", (S_IRUGO | S_IWUSR),
ab8500_dir, &plf->dev, &ab8500_hwreg_fops);
if (!file)
goto err;
file = debugfs_create_file("bat_ctrl", (S_IRUGO | S_IWUSR),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_bat_ctrl_fops);
if (!file)
goto err;
file = debugfs_create_file("btemp_ball", (S_IRUGO | S_IWUSR),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_btemp_ball_fops);
if (!file)
goto err;
file = debugfs_create_file("main_charger_v", (S_IRUGO | S_IWUSR),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_main_charger_v_fops);
if (!file)
goto err;
file = debugfs_create_file("acc_detect1", (S_IRUGO | S_IWUSR),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_acc_detect1_fops);
if (!file)
goto err;
file = debugfs_create_file("acc_detect2", (S_IRUGO | S_IWUSR),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_acc_detect2_fops);
if (!file)
goto err;
file = debugfs_create_file("adc_aux1", (S_IRUGO | S_IWUSR),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_aux1_fops);
if (!file)
goto err;
file = debugfs_create_file("adc_aux2", (S_IRUGO | S_IWUSR),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_aux2_fops);
if (!file)
goto err;
file = debugfs_create_file("main_bat_v", (S_IRUGO | S_IWUSR),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_main_bat_v_fops);
if (!file)
goto err;
file = debugfs_create_file("vbus_v", (S_IRUGO | S_IWUSR),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_vbus_v_fops);
if (!file)
goto err;
file = debugfs_create_file("main_charger_c", (S_IRUGO | S_IWUSR),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_main_charger_c_fops);
if (!file)
goto err;
file = debugfs_create_file("usb_charger_c", (S_IRUGO | S_IWUSR),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_usb_charger_c_fops);
if (!file)
goto err;
file = debugfs_create_file("bk_bat_v", (S_IRUGO | S_IWUSR),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_bk_bat_v_fops);
if (!file)
goto err;
file = debugfs_create_file("die_temp", (S_IRUGO | S_IWUSR),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_die_temp_fops);
if (!file)
goto err;
return 0;
exit_destroy_address:
debugfs_remove(ab8500_address_file);
exit_destroy_bank:
debugfs_remove(ab8500_bank_file);
exit_destroy_reg:
debugfs_remove(ab8500_reg_file);
exit_destroy_dir:
debugfs_remove(ab8500_dir);
exit_no_debugfs:
err:
if (ab8500_dir)
debugfs_remove_recursive(ab8500_dir);
dev_err(&plf->dev, "failed to create debugfs entries.\n");
return -ENOMEM;
out_freeevent_name:
kfree(event_name);
out_freedev_attr:
kfree(dev_attr);
out_freeirq_count:
kfree(irq_count);
return ret;
}
static int ab8500_debug_remove(struct platform_device *plf)
{
debugfs_remove(ab8500_val_file);
debugfs_remove(ab8500_address_file);
debugfs_remove(ab8500_bank_file);
debugfs_remove(ab8500_reg_file);
debugfs_remove(ab8500_dir);
debugfs_remove_recursive(ab8500_dir);
kfree(event_name);
kfree(dev_attr);
kfree(irq_count);
return 0;
}
......
......@@ -12,6 +12,7 @@
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/pm_runtime.h>
#include <linux/platform_device.h>
#include <linux/completion.h>
#include <linux/regulator/consumer.h>
......@@ -82,6 +83,11 @@
/* This is used to not lose precision when dividing to get gain and offset */
#define CALIB_SCALE 1000
/* Time in ms before disabling regulator */
#define GPADC_AUDOSUSPEND_DELAY 1
#define CONVERSION_TIME 500 /* ms */
enum cal_channels {
ADC_INPUT_VMAIN = 0,
ADC_INPUT_BTEMP,
......@@ -102,10 +108,10 @@ struct adc_cal_data {
/**
* struct ab8500_gpadc - AB8500 GPADC device information
* @chip_id ABB chip id
* @dev: pointer to the struct device
* @node: a list of AB8500 GPADCs, hence prepared for
reentrance
* @parent: pointer to the struct ab8500
* @ab8500_gpadc_complete: pointer to the struct completion, to indicate
* the completion of gpadc conversion
* @ab8500_gpadc_lock: structure of type mutex
......@@ -114,9 +120,9 @@ struct adc_cal_data {
* @cal_data array of ADC calibration data structs
*/
struct ab8500_gpadc {
u8 chip_id;
struct device *dev;
struct list_head node;
struct ab8500 *parent;
struct completion ab8500_gpadc_complete;
struct mutex ab8500_gpadc_lock;
struct regulator *regu;
......@@ -282,8 +288,9 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
return -ENODEV;
mutex_lock(&gpadc->ab8500_gpadc_lock);
/* Enable VTVout LDO this is required for GPADC */
regulator_enable(gpadc->regu);
pm_runtime_get_sync(gpadc->dev);
/* Check if ADC is not busy, lock and proceed */
do {
......@@ -332,7 +339,7 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
EN_BUF | EN_ICHAR);
break;
case BTEMP_BALL:
if (gpadc->chip_id >= AB8500_CUT3P0) {
if (!is_ab8500_2p0_or_earlier(gpadc->parent)) {
/* Turn on btemp pull-up on ABB 3.0 */
ret = abx500_mask_and_set_register_interruptible(
gpadc->dev,
......@@ -344,7 +351,7 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
* Delay might be needed for ABB8500 cut 3.0, if not, remove
* when hardware will be available
*/
msleep(1);
usleep_range(1000, 1000);
break;
}
/* Intentional fallthrough */
......@@ -367,7 +374,8 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
goto out;
}
/* wait for completion of conversion */
if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete, 2*HZ)) {
if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete,
msecs_to_jiffies(CONVERSION_TIME))) {
dev_err(gpadc->dev,
"timeout: didn't receive GPADC conversion interrupt\n");
ret = -EINVAL;
......@@ -397,8 +405,10 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
dev_err(gpadc->dev, "gpadc_conversion: disable gpadc failed\n");
goto out;
}
/* Disable VTVout LDO this is required for GPADC */
regulator_disable(gpadc->regu);
pm_runtime_mark_last_busy(gpadc->dev);
pm_runtime_put_autosuspend(gpadc->dev);
mutex_unlock(&gpadc->ab8500_gpadc_lock);
return (high_data << 8) | low_data;
......@@ -412,7 +422,9 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
*/
(void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
AB8500_GPADC_CTRL1_REG, DIS_GPADC);
regulator_disable(gpadc->regu);
pm_runtime_put(gpadc->dev);
mutex_unlock(&gpadc->ab8500_gpadc_lock);
dev_err(gpadc->dev,
"gpadc_conversion: Failed to AD convert channel %d\n", channel);
......@@ -571,6 +583,28 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
gpadc->cal_data[ADC_INPUT_VBAT].offset);
}
static int ab8500_gpadc_runtime_suspend(struct device *dev)
{
struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
regulator_disable(gpadc->regu);
return 0;
}
static int ab8500_gpadc_runtime_resume(struct device *dev)
{
struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
regulator_enable(gpadc->regu);
return 0;
}
static int ab8500_gpadc_runtime_idle(struct device *dev)
{
pm_runtime_suspend(dev);
return 0;
}
static int ab8500_gpadc_probe(struct platform_device *pdev)
{
int ret = 0;
......@@ -591,6 +625,7 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
}
gpadc->dev = &pdev->dev;
gpadc->parent = dev_get_drvdata(pdev->dev.parent);
mutex_init(&gpadc->ab8500_gpadc_lock);
/* Initialize completion used to notify completion of conversion */
......@@ -607,14 +642,6 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
goto fail;
}
/* Get Chip ID of the ABB ASIC */
ret = abx500_get_chip_id(gpadc->dev);
if (ret < 0) {
dev_err(gpadc->dev, "failed to get chip ID\n");
goto fail_irq;
}
gpadc->chip_id = (u8) ret;
/* VTVout LDO used to power up ab8500-GPADC */
gpadc->regu = regulator_get(&pdev->dev, "vddadc");
if (IS_ERR(gpadc->regu)) {
......@@ -622,6 +649,16 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
dev_err(gpadc->dev, "failed to get vtvout LDO\n");
goto fail_irq;
}
platform_set_drvdata(pdev, gpadc);
regulator_enable(gpadc->regu);
pm_runtime_set_autosuspend_delay(gpadc->dev, GPADC_AUDOSUSPEND_DELAY);
pm_runtime_use_autosuspend(gpadc->dev);
pm_runtime_set_active(gpadc->dev);
pm_runtime_enable(gpadc->dev);
ab8500_gpadc_read_calibration_data(gpadc);
list_add_tail(&gpadc->node, &ab8500_gpadc_list);
dev_dbg(gpadc->dev, "probe success\n");
......@@ -642,19 +679,34 @@ static int ab8500_gpadc_remove(struct platform_device *pdev)
list_del(&gpadc->node);
/* remove interrupt - completion of Sw ADC conversion */
free_irq(gpadc->irq, gpadc);
/* disable VTVout LDO that is being used by GPADC */
regulator_put(gpadc->regu);
pm_runtime_get_sync(gpadc->dev);
pm_runtime_disable(gpadc->dev);
regulator_disable(gpadc->regu);
pm_runtime_set_suspended(gpadc->dev);
pm_runtime_put_noidle(gpadc->dev);
kfree(gpadc);
gpadc = NULL;
return 0;
}
static const struct dev_pm_ops ab8500_gpadc_pm_ops = {
SET_RUNTIME_PM_OPS(ab8500_gpadc_runtime_suspend,
ab8500_gpadc_runtime_resume,
ab8500_gpadc_runtime_idle)
};
static struct platform_driver ab8500_gpadc_driver = {
.probe = ab8500_gpadc_probe,
.remove = ab8500_gpadc_remove,
.driver = {
.name = "ab8500-gpadc",
.owner = THIS_MODULE,
.pm = &ab8500_gpadc_pm_ops,
},
};
......
......@@ -7,12 +7,73 @@
#include <linux/err.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/reboot.h>
#include <linux/signal.h>
#include <linux/power_supply.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
#include <linux/mfd/abx500/ab8500-sysctrl.h>
static struct device *sysctrl_dev;
void ab8500_power_off(void)
{
sigset_t old;
sigset_t all;
static char *pss[] = {"ab8500_ac", "ab8500_usb"};
int i;
bool charger_present = false;
union power_supply_propval val;
struct power_supply *psy;
int ret;
/*
* If we have a charger connected and we're powering off,
* reboot into charge-only mode.
*/
for (i = 0; i < ARRAY_SIZE(pss); i++) {
psy = power_supply_get_by_name(pss[i]);
if (!psy)
continue;
ret = psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &val);
if (!ret && val.intval) {
charger_present = true;
break;
}
}
if (!charger_present)
goto shutdown;
/* Check if battery is known */
psy = power_supply_get_by_name("ab8500_btemp");
if (psy) {
ret = psy->get_property(psy, POWER_SUPPLY_PROP_TECHNOLOGY,
&val);
if (!ret && val.intval != POWER_SUPPLY_TECHNOLOGY_UNKNOWN) {
printk(KERN_INFO
"Charger \"%s\" is connected with known battery."
" Rebooting.\n",
pss[i]);
machine_restart("charging");
}
}
shutdown:
sigfillset(&all);
if (!sigprocmask(SIG_BLOCK, &all, &old)) {
(void)ab8500_sysctrl_set(AB8500_STW4500CTRL1,
AB8500_STW4500CTRL1_SWOFF |
AB8500_STW4500CTRL1_SWRESET4500N);
(void)sigprocmask(SIG_SETMASK, &old, NULL);
}
}
static inline bool valid_bank(u8 bank)
{
return ((bank == AB8500_SYS_CTRL1_BLOCK) ||
......@@ -33,6 +94,7 @@ int ab8500_sysctrl_read(u16 reg, u8 *value)
return abx500_get_register_interruptible(sysctrl_dev, bank,
(u8)(reg & 0xFF), value);
}
EXPORT_SYMBOL(ab8500_sysctrl_read);
int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value)
{
......@@ -48,10 +110,40 @@ int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value)
return abx500_mask_and_set_register_interruptible(sysctrl_dev, bank,
(u8)(reg & 0xFF), mask, value);
}
EXPORT_SYMBOL(ab8500_sysctrl_write);
static int ab8500_sysctrl_probe(struct platform_device *pdev)
{
struct ab8500_platform_data *plat;
struct ab8500_sysctrl_platform_data *pdata;
sysctrl_dev = &pdev->dev;
plat = dev_get_platdata(pdev->dev.parent);
if (plat->pm_power_off)
pm_power_off = ab8500_power_off;
pdata = plat->sysctrl;
if (pdata) {
int ret, i, j;
for (i = AB8500_SYSCLKREQ1RFCLKBUF;
i <= AB8500_SYSCLKREQ8RFCLKBUF; i++) {
j = i - AB8500_SYSCLKREQ1RFCLKBUF;
ret = ab8500_sysctrl_write(i, 0xff,
pdata->initial_req_buf_config[j]);
dev_dbg(&pdev->dev,
"Setting SysClkReq%dRfClkBuf 0x%X\n",
j + 1,
pdata->initial_req_buf_config[j]);
if (ret < 0) {
dev_err(&pdev->dev,
"unable to set sysClkReq%dRfClkBuf: "
"%d\n", j + 1, ret);
}
}
}
return 0;
}
......
......@@ -153,6 +153,22 @@ int abx500_startup_irq_enabled(struct device *dev, unsigned int irq)
}
EXPORT_SYMBOL(abx500_startup_irq_enabled);
void abx500_dump_all_banks(void)
{
struct abx500_ops *ops;
struct device dummy_child = {0};
struct abx500_device_entry *dev_entry;
list_for_each_entry(dev_entry, &abx500_list, list) {
dummy_child.parent = dev_entry->dev;
ops = &dev_entry->ops;
if ((ops != NULL) && (ops->dump_all_banks != NULL))
ops->dump_all_banks(&dummy_child);
}
}
EXPORT_SYMBOL(abx500_dump_all_banks);
MODULE_AUTHOR("Mattias Wallin <mattias.wallin@stericsson.com>");
MODULE_DESCRIPTION("ABX500 core driver");
MODULE_LICENSE("GPL");
......@@ -306,6 +306,7 @@ int abx500_mask_and_set_register_interruptible(struct device *dev, u8 bank,
int abx500_get_chip_id(struct device *dev);
int abx500_event_registers_startup_state_get(struct device *dev, u8 *event);
int abx500_startup_irq_enabled(struct device *dev, unsigned int irq);
void abx500_dump_all_banks(void);
struct abx500_ops {
int (*get_chip_id) (struct device *);
......@@ -316,6 +317,7 @@ struct abx500_ops {
int (*mask_and_set_register) (struct device *, u8, u8, u8, u8);
int (*event_registers_startup_state_get) (struct device *, u8 *);
int (*startup_irq_enabled) (struct device *, unsigned int);
void (*dump_all_banks) (struct device *);
};
int abx500_register_ops(struct device *core_dev, struct abx500_ops *ops);
......
......@@ -37,6 +37,11 @@ static inline int ab8500_sysctrl_clear(u16 reg, u8 bits)
return ab8500_sysctrl_write(reg, bits, 0);
}
/* Configuration data for SysClkReq1RfClkBuf - SysClkReq8RfClkBuf */
struct ab8500_sysctrl_platform_data {
u8 initial_req_buf_config[8];
};
/* Registers */
#define AB8500_TURNONSTATUS 0x100
#define AB8500_RESETSTATUS 0x101
......
......@@ -270,10 +270,12 @@ struct regulator_reg_init;
struct regulator_init_data;
struct ab8500_gpio_platform_data;
struct ab8500_codec_platform_data;
struct ab8500_sysctrl_platform_data;
/**
* struct ab8500_platform_data - AB8500 platform data
* @irq_base: start of AB8500 IRQs, AB8500_NR_IRQS will be used
* @pm_power_off: Should machine pm power off hook be registered or not
* @init: board-specific initialization after detection of ab8500
* @num_regulator_reg_init: number of regulator init registers
* @regulator_reg_init: regulator init registers
......@@ -282,6 +284,7 @@ struct ab8500_codec_platform_data;
*/
struct ab8500_platform_data {
int irq_base;
bool pm_power_off;
void (*init) (struct ab8500 *);
int num_regulator_reg_init;
struct ab8500_regulator_reg_init *regulator_reg_init;
......@@ -289,6 +292,7 @@ struct ab8500_platform_data {
struct regulator_init_data *regulator;
struct ab8500_gpio_platform_data *gpio;
struct ab8500_codec_platform_data *codec;
struct ab8500_sysctrl_platform_data *sysctrl;
};
extern int ab8500_init(struct ab8500 *ab8500,
......@@ -341,4 +345,12 @@ static inline int is_ab8500_2p0(struct ab8500 *ab)
return (is_ab8500(ab) && (ab->chip_id == AB8500_CUT2P0));
}
#ifdef CONFIG_AB8500_DEBUG
void ab8500_dump_all_banks(struct device *dev);
void ab8500_debug_register_interrupt(int line);
#else
static inline void ab8500_dump_all_banks(struct device *dev) {}
static inline void ab8500_debug_register_interrupt(int line) {}
#endif
#endif /* MFD_AB8500_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment