From 379749c46320c82df95c7909ec888da8ed1fb22c Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 14 Jan 2013 13:26:15 +0000 Subject: mfd: ab8500-sysctrl: Provide a platform specific pm_power_off() call-back The kernel allows us to specify a function call-back which will be invoked when a system power-off request has been received. Here we provide one which is to be used when shutting down AB8500 based platforms. Signed-off-by: Lee Jones --- drivers/mfd/ab8500-sysctrl.c | 22 ++++++++++++++++++++++ include/linux/mfd/abx500/ab8500.h | 2 ++ 2 files changed, 24 insertions(+) diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c index 8a33b2c7eead..888e066c715b 100644 --- a/drivers/mfd/ab8500-sysctrl.c +++ b/drivers/mfd/ab8500-sysctrl.c @@ -7,12 +7,29 @@ #include #include #include +#include +#include #include #include #include static struct device *sysctrl_dev; +void ab8500_power_off(void) +{ + sigset_t old; + sigset_t all; + + 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) || @@ -51,7 +68,12 @@ int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value) static int ab8500_sysctrl_probe(struct platform_device *pdev) { + struct ab8500_platform_data *plat; + sysctrl_dev = &pdev->dev; + plat = dev_get_platdata(pdev->dev.parent); + if (plat->pm_power_off) + pm_power_off = ab8500_power_off; return 0; } diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h index 1cb5698b4d76..6119b2fbad97 100644 --- a/include/linux/mfd/abx500/ab8500.h +++ b/include/linux/mfd/abx500/ab8500.h @@ -274,6 +274,7 @@ struct ab8500_codec_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 +283,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; -- cgit v1.2.3 From 7c34d7c2b54818078678a6507ce9a79c3d479243 Mon Sep 17 00:00:00 2001 From: Jonas Aaberg Date: Wed, 17 Aug 2011 13:20:21 +0200 Subject: mfd: ab8500-sysctrl: If a charger is present, reboot instead If a charger is attached on power off, reboot the system into charging mode instead of powering it off. Signed-off-by: Lee Jones Signed-off-by: Jonas Aaberg Reviewed-by: Karl KOMIEROWSKI --- drivers/mfd/ab8500-sysctrl.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c index 888e066c715b..188d22b77a28 100644 --- a/drivers/mfd/ab8500-sysctrl.c +++ b/drivers/mfd/ab8500-sysctrl.c @@ -8,7 +8,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -19,6 +21,31 @@ void ab8500_power_off(void) { sigset_t old; sigset_t all; + static char *pss[] = {"ab8500_ac", "ab8500_usb"}; + int i; + + /* + * If we have a charger connected and we're powering off, + * reboot into charge-only mode. + */ + + for (i = 0; i < ARRAY_SIZE(pss); i++) { + union power_supply_propval val; + struct power_supply *psy; + int ret; + + 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) { + printk(KERN_INFO + "Charger \"%s\" is connected. Rebooting.\n", + pss[i]); + machine_restart(NULL); + } + } sigfillset(&all); -- cgit v1.2.3 From 0903940dcddcaa3f567a1a2308b0c55e2ce0643b Mon Sep 17 00:00:00 2001 From: Jonas Aaberg Date: Wed, 17 Aug 2011 15:58:52 +0200 Subject: mfd: ab8500-sysctrl: Only reboot into charging mode if battery type is known When a charger is connected, we usually want AB8500 based systems to reboot into charging-only mode. However, if the battery type cannot be identified this would be futile, so we'll just shut the system down instead. Signed-off-by: Lee Jones Signed-off-by: Jonas Aaberg Reviewed-by: Karl KOMIEROWSKI --- drivers/mfd/ab8500-sysctrl.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c index 188d22b77a28..405ca686d9f3 100644 --- a/drivers/mfd/ab8500-sysctrl.c +++ b/drivers/mfd/ab8500-sysctrl.c @@ -23,6 +23,10 @@ void ab8500_power_off(void) 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, @@ -30,23 +34,36 @@ void ab8500_power_off(void) */ for (i = 0; i < ARRAY_SIZE(pss); i++) { - union power_supply_propval val; - struct power_supply *psy; - int ret; - 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. Rebooting.\n", + "Charger \"%s\" is connected with known battery." + " Rebooting.\n", pss[i]); machine_restart(NULL); } } +shutdown: sigfillset(&all); if (!sigprocmask(SIG_BLOCK, &all, &old)) { -- cgit v1.2.3 From 5a4bac6e6331feb0edd9522f3c7bbb9a01571566 Mon Sep 17 00:00:00 2001 From: Jonas Aaberg Date: Thu, 18 Aug 2011 10:14:38 +0200 Subject: mfd: ab8500-sysctrl.c: Provide charging as reset reason It's possible to supply a string to provide a reason for triggering a restart. In this case our reason is to enter charging-only mode, as a charger was found to be present. Signed-off-by: Lee Jones Signed-off-by: Jonas Aaberg Reviewed-by: Karl KOMIEROWSKI --- drivers/mfd/ab8500-sysctrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c index 405ca686d9f3..3aaff6043e18 100644 --- a/drivers/mfd/ab8500-sysctrl.c +++ b/drivers/mfd/ab8500-sysctrl.c @@ -59,7 +59,7 @@ void ab8500_power_off(void) "Charger \"%s\" is connected with known battery." " Rebooting.\n", pss[i]); - machine_restart(NULL); + machine_restart("charging"); } } -- cgit v1.2.3 From d0b32fa1e12a6fafbdf4fb142311a36f5314a6ff Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 29 Aug 2011 08:32:36 +0200 Subject: mfd: ab8500-gpadc: Change to usleep_range() for greater resolution The resolution of msleep is related to HZ, so with HZ set to 100 any msleep of less than 10ms will become ~10ms. Signed-off-by: Lee Jones --- drivers/mfd/ab8500-gpadc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c index 3fb1f40d6389..bc0daf3bc93a 100644 --- a/drivers/mfd/ab8500-gpadc.c +++ b/drivers/mfd/ab8500-gpadc.c @@ -344,7 +344,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 */ -- cgit v1.2.3 From 1abf063ffd18e6934bb81a776b3d71278eeff4ab Mon Sep 17 00:00:00 2001 From: Kennet Wallden Date: Tue, 27 Sep 2011 09:23:56 +0200 Subject: mfd: ab8500-sysctrl: Provide configuration for SysClkReqRfClkBuf registers Add the possibility to pass configuration settings for SysCl1kReqRfClkBuf to SysClk8ReqRfClkBuf via platform data. Signed-off-by: Lee Jones Signed-off-by: Kennet Wallden Reviewed-by: Karl-Johan PERNTZ Reviewed-by: Bengt JONSSON --- drivers/mfd/ab8500-sysctrl.c | 24 ++++++++++++++++++++++++ include/linux/mfd/abx500/ab8500-sysctrl.h | 5 +++++ include/linux/mfd/abx500/ab8500.h | 2 ++ 3 files changed, 31 insertions(+) diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c index 3aaff6043e18..236324e1136d 100644 --- a/drivers/mfd/ab8500-sysctrl.c +++ b/drivers/mfd/ab8500-sysctrl.c @@ -113,11 +113,35 @@ int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value) 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; } diff --git a/include/linux/mfd/abx500/ab8500-sysctrl.h b/include/linux/mfd/abx500/ab8500-sysctrl.h index 10eb50973c39..ebf12e793db9 100644 --- a/include/linux/mfd/abx500/ab8500-sysctrl.h +++ b/include/linux/mfd/abx500/ab8500-sysctrl.h @@ -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 diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h index 6119b2fbad97..5251bca56326 100644 --- a/include/linux/mfd/abx500/ab8500.h +++ b/include/linux/mfd/abx500/ab8500.h @@ -270,6 +270,7 @@ 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 @@ -291,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, -- cgit v1.2.3 From c73db9f7a20c3b6a7026cbb70df2603aa2ce2e5a Mon Sep 17 00:00:00 2001 From: Jonas Aaberg Date: Fri, 11 Nov 2011 07:52:10 +0100 Subject: mfd: ab8500-sysctrl: export read/write symbols Export ab8500_sysctrl_read() and ab8500_sysctrl_write() symobols. They will be used by LTP test cases. Signed-off-by: Lee Jones Signed-off-by: Jonas Aaberg Reviewed-by: Bengt JONSSON --- drivers/mfd/ab8500-sysctrl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c index 236324e1136d..108fd86552f0 100644 --- a/drivers/mfd/ab8500-sysctrl.c +++ b/drivers/mfd/ab8500-sysctrl.c @@ -94,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) { @@ -109,6 +110,7 @@ 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) { -- cgit v1.2.3 From e0f4fec030ce412666cc127702adbf0a6cfa0855 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Fri, 27 Jan 2012 11:22:16 +0100 Subject: mfd: abx500-core: Provide an API to dump all ABx500 registers Some drivers can detect subsystem failures e.g. shared memory driver can detect modem sub system failures. It would be helpful in analyzing these failures if AB register dump is available at that point. This patch adds the API for the drivers to dump AB registers in the kernel log. Signed-off-by: Lee Jones Signed-off-by: Mian Yousaf Kaukab Reviewed-by: Linus WALLEIJ Reviewed-by: Jonas ABERG --- drivers/mfd/abx500-core.c | 16 ++++++++++++++++ include/linux/mfd/abx500.h | 2 ++ 2 files changed, 18 insertions(+) diff --git a/drivers/mfd/abx500-core.c b/drivers/mfd/abx500-core.c index 7ce65f49480f..9818afba2515 100644 --- a/drivers/mfd/abx500-core.c +++ b/drivers/mfd/abx500-core.c @@ -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 "); MODULE_DESCRIPTION("ABX500 core driver"); MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h index 2138bd33021a..bd480b248c62 100644 --- a/include/linux/mfd/abx500.h +++ b/include/linux/mfd/abx500.h @@ -308,6 +308,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 *); @@ -318,6 +319,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); -- cgit v1.2.3 From 4b8ac08256781c59b20bfd86fddcf5d620833752 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 14 Jan 2013 16:10:36 +0000 Subject: mfd: ab8500-debugfs: Provide a means for a user subscribe to IRQs Allow users to subscribe to and view IRQ events live from debugfs. Signed-off-by: Lee Jones --- drivers/mfd/ab8500-debugfs.c | 198 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index 5a8e707bc038..2e40e12fc687 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -11,6 +11,9 @@ #include #include #include +#include +#include +#include #include #include @@ -18,6 +21,9 @@ static u32 debug_bank; static u32 debug_address; +static int irq_first; +static int irq_last; + /** * struct ab8500_reg_range * @first: the first address of the range @@ -354,6 +360,21 @@ static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = { }, }; +static irqreturn_t ab8500_debug_handler(int irq, void *data) +{ + char buf[16]; + struct kobject *kobj = (struct kobject *)data; + + /* + * This makes it possible to use poll for events (POLLPRI | POLLERR) + * from userspace on sysfs file named irq- + */ + sprintf(buf, "irq-%d", irq); + sysfs_notify(kobj, NULL, buf); + + return IRQ_HANDLED; +} + static int ab8500_registers_print(struct seq_file *s, void *p) { struct device *dev = s->private; @@ -519,6 +540,131 @@ static ssize_t ab8500_val_write(struct file *file, return count; } +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); +} + +/* + * This function is used for all interrupts and will always print + * the same string. It is however this file sysfs_notify called on. + * Userspace should read this file and then poll. When an event occur + * the blocking poll will be released. + */ +static ssize_t show_irq(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "irq\n"); +} + +static struct device_attribute *dev_attr[AB8500_NR_IRQS]; +static char *event_name[AB8500_NR_IRQS]; + +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; + + /* 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; + } + + /* + * This will create a sysfs file named irq- which userspace can + * use to select or poll and get the AB8500 events + */ + dev_attr[user_val] = kmalloc(sizeof(struct device_attribute), + GFP_KERNEL); + event_name[user_val] = kmalloc(buf_size, GFP_KERNEL); + sprintf(event_name[user_val], "irq-%lu", user_val); + dev_attr[user_val]->show = show_irq; + dev_attr[user_val]->store = NULL; + dev_attr[user_val]->attr.name = event_name[user_val]; + dev_attr[user_val]->attr.mode = S_IRUGO; + err = sysfs_create_file(&dev->kobj, &dev_attr[user_val]->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); + 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; + + /* 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; + } + + free_irq(user_val, &dev->kobj); + kfree(event_name[user_val]); + kfree(dev_attr[user_val]); + + if (dev_attr[user_val]) + sysfs_remove_file(&dev->kobj, &dev_attr[user_val]->attr); + + return buf_size; +} + static const struct file_operations ab8500_bank_fops = { .open = ab8500_bank_open, .write = ab8500_bank_write, @@ -546,17 +692,51 @@ static const struct file_operations ab8500_val_fops = { .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 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_subscribe_file; +static struct dentry *ab8500_unsubscribe_file; static int ab8500_debug_probe(struct platform_device *plf) { debug_bank = AB8500_MISC; debug_address = AB8500_REV_REG & 0x00FF; + 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); + return irq_first; + } + + 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); + return irq_last; + } + ab8500_dir = debugfs_create_dir(AB8500_NAME_STRING, NULL); if (!ab8500_dir) goto exit_no_debugfs; @@ -582,8 +762,26 @@ static int ab8500_debug_probe(struct platform_device *plf) if (!ab8500_val_file) goto exit_destroy_address; + ab8500_subscribe_file = + debugfs_create_file("irq-subscribe", + (S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev, + &ab8500_subscribe_fops); + if (!ab8500_subscribe_file) + goto exit_destroy_val; + + ab8500_unsubscribe_file = + debugfs_create_file("irq-unsubscribe", + (S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev, + &ab8500_unsubscribe_fops); + if (!ab8500_unsubscribe_file) + goto exit_destroy_subscribe; + return 0; +exit_destroy_subscribe: + debugfs_remove(ab8500_subscribe_file); +exit_destroy_val: + debugfs_remove(ab8500_val_file); exit_destroy_address: debugfs_remove(ab8500_address_file); exit_destroy_bank: -- cgit v1.2.3 From 0b337e70d30a8092a3e6c35ccb341c3655d9d543 Mon Sep 17 00:00:00 2001 From: Mattias Wallin Date: Fri, 19 Nov 2010 17:55:11 +0100 Subject: mfd: ab8500-debugfs: Keep count of IRQs in debugfs This patch adds a counter to the sysfs file dynamically created by debugfs. It also fixes an array index error. Signed-off-by: Lee Jones Signed-off-by: Mattias Wallin Reviewed-by: Mattias NILSSON --- drivers/mfd/ab8500-debugfs.c | 77 ++++++++++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 24 deletions(-) diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index 2e40e12fc687..4699fff322a0 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -23,6 +23,10 @@ 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 @@ -364,12 +368,15 @@ static irqreturn_t ab8500_debug_handler(int irq, void *data) { char buf[16]; struct kobject *kobj = (struct kobject *)data; + unsigned int irq_abb = irq - irq_first; + if (irq_abb < AB8500_NR_IRQS) + irq_count[irq_abb]++; /* * This makes it possible to use poll for events (POLLPRI | POLLERR) - * from userspace on sysfs file named irq- + * from userspace on sysfs file named */ - sprintf(buf, "irq-%d", irq); + sprintf(buf, "%d", irq); sysfs_notify(kobj, NULL, buf); return IRQ_HANDLED; @@ -555,19 +562,26 @@ static int ab8500_subscribe_unsubscribe_open(struct inode *inode, } /* - * This function is used for all interrupts and will always print - * the same string. It is however this file sysfs_notify called on. - * Userspace should read this file and then poll. When an event occur + * 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) { - return sprintf(buf, "irq\n"); -} + unsigned long name; + unsigned int irq_index; + int err; -static struct device_attribute *dev_attr[AB8500_NR_IRQS]; -static char *event_name[AB8500_NR_IRQS]; + err = strict_strtoul(attr->attr.name, 0, &name); + if (err) + return err; + + irq_index = name - irq_first; + if (irq_index >= AB8500_NR_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, @@ -578,6 +592,7 @@ static ssize_t ab8500_subscribe_write(struct file *file, 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)); @@ -597,19 +612,23 @@ static ssize_t ab8500_subscribe_write(struct file *file, return -EINVAL; } + irq_index = user_val - irq_first; + if (irq_index >= AB8500_NR_IRQS) + return -EINVAL; + /* - * This will create a sysfs file named irq- which userspace can + * This will create a sysfs file named which userspace can * use to select or poll and get the AB8500 events */ - dev_attr[user_val] = kmalloc(sizeof(struct device_attribute), - GFP_KERNEL); - event_name[user_val] = kmalloc(buf_size, GFP_KERNEL); - sprintf(event_name[user_val], "irq-%lu", user_val); - dev_attr[user_val]->show = show_irq; - dev_attr[user_val]->store = NULL; - dev_attr[user_val]->attr.name = event_name[user_val]; - dev_attr[user_val]->attr.mode = S_IRUGO; - err = sysfs_create_file(&dev->kobj, &dev_attr[user_val]->attr); + 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; @@ -621,6 +640,7 @@ static ssize_t ab8500_subscribe_write(struct file *file, 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; } @@ -636,6 +656,7 @@ static ssize_t ab8500_unsubscribe_write(struct file *file, 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)); @@ -655,12 +676,20 @@ static ssize_t ab8500_unsubscribe_write(struct file *file, return -EINVAL; } - free_irq(user_val, &dev->kobj); - kfree(event_name[user_val]); - kfree(dev_attr[user_val]); + irq_index = user_val - irq_first; + if (irq_index >= AB8500_NR_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); - if (dev_attr[user_val]) - sysfs_remove_file(&dev->kobj, &dev_attr[user_val]->attr); + + free_irq(user_val, &dev->kobj); + kfree(event_name[irq_index]); + kfree(dev_attr[irq_index]); return buf_size; } -- cgit v1.2.3 From fad55a869ba1bf0d1ab7e4c8bff0b171eb5486ee Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 14 Jan 2013 17:17:34 +0000 Subject: mfd: ab8500-debugfs: Use NULL to initialise remaining NULL pointer Partly for coding style reasons, but mostly because sparse warns on it. This patch is a completion of a previous patch by Mark Brown. Reviewed-by: Mark Brown Signed-off-by: Lee Jones --- drivers/mfd/ab8500-debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index 4699fff322a0..1bb74297a3a7 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -60,7 +60,7 @@ struct ab8500_prcmu_ranges { 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, -- cgit v1.2.3 From 0fbce76eff0e7ea92f51b253c504a79d9b3b5769 Mon Sep 17 00:00:00 2001 From: carriere etienne Date: Fri, 8 Apr 2011 16:26:36 +0200 Subject: mfd: ab8500-debugfs: Formated access AB8500 registers from debugfs entry Add debugfs entry ab8500/hwreg to read/write bit-field in AB8500 registers. Check the debugfs entries usage from heading comments in ab8500-debugfs.c Signed-off-by: Lee Jones Signed-off-by: carriere etienne Reviewed-by: Mattias WALLIN --- drivers/mfd/ab8500-debugfs.c | 390 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 338 insertions(+), 52 deletions(-) diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index 1bb74297a3a7..79a954f79732 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -4,6 +4,72 @@ * Author: Mattias Wallin for ST-Ericsson. * License Terms: GNU General Public License v2 */ +/* + * AB8500 register access + * ====================== + * + * read: + * # echo BANK > /ab8500/register-bank + * # echo ADDR > /ab8500/register-address + * # cat /ab8500/register-value + * + * write: + * # echo BANK > /ab8500/register-bank + * # echo ADDR > /ab8500/register-address + * # echo VALUE > /ab8500/register-value + * + * read all registers from a bank: + * # echo BANK > /ab8500/register-bank + * # cat /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 > /ab8500/irq-subscribe + * + * unsubscribe from an AB8500 IRQ: + * # echo IRQ > /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< [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 #include @@ -18,6 +84,11 @@ #include #include +#ifdef CONFIG_DEBUG_FS +#include +#include +#endif + static u32 debug_bank; static u32 debug_address; @@ -52,6 +123,25 @@ 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 @@ -547,6 +637,205 @@ static ssize_t ab8500_val_write(struct file *file, return count; } +/* + * - 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, ®value); + 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); +} + +/* + * 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, ®value); + 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); @@ -694,6 +983,10 @@ static ssize_t ab8500_unsubscribe_write(struct file *file, return buf_size; } +/* + * - several deubgfs nodes fops + */ + static const struct file_operations ab8500_bank_fops = { .open = ab8500_bank_open, .write = ab8500_bank_write, @@ -739,16 +1032,20 @@ static const struct file_operations ab8500_unsubscribe_fops = { .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_subscribe_file; -static struct dentry *ab8500_unsubscribe_file; static int ab8500_debug_probe(struct platform_device *plf) { + struct dentry *file; debug_bank = AB8500_MISC; debug_address = AB8500_REV_REG & 0x00FF; @@ -768,70 +1065,59 @@ static int ab8500_debug_probe(struct platform_device *plf) ab8500_dir = debugfs_create_dir(AB8500_NAME_STRING, NULL); if (!ab8500_dir) - goto exit_no_debugfs; + goto err; - ab8500_reg_file = debugfs_create_file("all-bank-registers", + file = debugfs_create_file("all-bank-registers", S_IRUGO, ab8500_dir, &plf->dev, &ab8500_registers_fops); - if (!ab8500_reg_file) - goto exit_destroy_dir; + if (!file) + goto err; - ab8500_bank_file = debugfs_create_file("register-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 (!file) + goto err; - ab8500_address_file = debugfs_create_file("register-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; + if (!file) + goto err; - ab8500_val_file = debugfs_create_file("register-value", + 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; - - ab8500_subscribe_file = - debugfs_create_file("irq-subscribe", - (S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev, - &ab8500_subscribe_fops); - if (!ab8500_subscribe_file) - goto exit_destroy_val; - - ab8500_unsubscribe_file = - debugfs_create_file("irq-unsubscribe", - (S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev, - &ab8500_unsubscribe_fops); - if (!ab8500_unsubscribe_file) - goto exit_destroy_subscribe; + if (!file) + goto err; + + file = debugfs_create_file("irq-subscribe", + (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, + &ab8500_subscribe_fops); + if (!file) + goto err; + + 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; return 0; -exit_destroy_subscribe: - debugfs_remove(ab8500_subscribe_file); -exit_destroy_val: - debugfs_remove(ab8500_val_file); -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; } 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); return 0; } -- cgit v1.2.3 From 1478a316e3ff3c3b0967091ac36bc6987773570a Mon Sep 17 00:00:00 2001 From: John Beckett Date: Tue, 31 May 2011 13:54:27 +0100 Subject: mfd: ab8500-debugfs: Export all AB8500 ADCs as debugfs nodes Allow a user to take a glimpse into the inner workings of the AB8500 Analogue-to-Digital Converters, via debugfs. Signed-off-by: Lee Jones Signed-off-by: John Beckett Reviewed-by: Mattias WALLIN --- drivers/mfd/ab8500-debugfs.c | 486 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 465 insertions(+), 21 deletions(-) diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index 79a954f79732..f32ac976577f 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -82,7 +82,7 @@ #include #include -#include +#include #ifdef CONFIG_DEBUG_FS #include @@ -143,6 +143,7 @@ static struct hwreg_cfg hwreg_cfg = { }; #define AB8500_NAME_STRING "ab8500" +#define AB8500_ADC_NAME_STRING "gpadc" #define AB8500_NUM_BANKS 22 #define AB8500_REV_REG 0x80 @@ -672,6 +673,382 @@ 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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. @@ -1042,6 +1419,7 @@ static const struct file_operations ab8500_hwreg_fops = { }; static struct dentry *ab8500_dir; +static struct dentry *ab8500_gpadc_dir; static int ab8500_debug_probe(struct platform_device *plf) { @@ -1052,14 +1430,14 @@ static int ab8500_debug_probe(struct platform_device *plf) 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); + irq_first); return irq_first; } 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); + irq_last); return irq_last; } @@ -1067,42 +1445,108 @@ static int ab8500_debug_probe(struct platform_device *plf) if (!ab8500_dir) goto err; - file = debugfs_create_file("all-bank-registers", - S_IRUGO, ab8500_dir, &plf->dev, &ab8500_registers_fops); + 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("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; + + file = debugfs_create_file("irq-subscribe", (S_IRUGO | S_IWUSR), + ab8500_dir, &plf->dev, &ab8500_subscribe_fops); + if (!file) + goto err; + + 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("register-bank", - (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_bank_fops); + 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("register-address", - (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, - &ab8500_address_fops); + 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("register-value", - (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_val_fops); + 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("irq-subscribe", - (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, - &ab8500_subscribe_fops); + 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("irq-unsubscribe", - (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, - &ab8500_unsubscribe_fops); + 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("hwreg", - (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, - &ab8500_hwreg_fops); + file = debugfs_create_file("die_temp", (S_IRUGO | S_IWUSR), + ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_die_temp_fops); if (!file) goto err; -- cgit v1.2.3 From 40c064e43ee4db4159331a1c421d0df577b447dd Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Mon, 17 Oct 2011 09:48:55 +0200 Subject: mfd: ab8500-debugfs: Set the USB charging current to 300mA for ABV3 In case of AB-V3, the eye diagram related issues are resolved. So, set the device charging current to 300mA when connected to standard host. Also, add the USB PHY tuning values to improve the USB eye diagram Signed-off-by: Lee Jones Signed-off-by: --- drivers/mfd/ab8500-debugfs.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index f32ac976577f..226d751d7e51 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -144,7 +144,7 @@ static struct hwreg_cfg hwreg_cfg = { #define AB8500_NAME_STRING "ab8500" #define AB8500_ADC_NAME_STRING "gpadc" -#define AB8500_NUM_BANKS 22 +#define AB8500_NUM_BANKS 24 #define AB8500_REV_REG 0x80 @@ -316,7 +316,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, @@ -350,6 +350,10 @@ static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = { .first = 0xC0, .last = 0xC2, }, + { + .first = 0xf5, + .last = 0xf6, + }, }, }, [AB8500_GAS_GAUGE] = { @@ -369,6 +373,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[]) { -- cgit v1.2.3 From 42002c6de9ee1916a118d6a732c533bfe138ed6f Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Thu, 26 Jan 2012 15:39:20 +0100 Subject: mfd: ab8500-debugfs: add debugfs node to read all registers Update the ab8500_registers_print() to reuse it from multiple places. Signed-off-by: Lee Jones Signed-off-by: Mian Yousaf Kaukab Reviewed-by: Linus WALLEIJ Reviewed-by: Jonas ABERG --- drivers/mfd/ab8500-debugfs.c | 95 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 81 insertions(+), 14 deletions(-) diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index 226d751d7e51..cd3cee814fb7 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -495,15 +495,12 @@ static irqreturn_t ab8500_debug_handler(int irq, void *data) return IRQ_HANDLED; } -static int ab8500_registers_print(struct seq_file *s, void *p) +/* Prints to seq_file or log_buf */ +static int ab8500_registers_print(struct device *dev, u32 bank, + struct seq_file *s) { - struct device *dev = s->private; unsigned int i; - u32 bank = debug_bank; - - seq_printf(s, AB8500_NAME_STRING " register values:\n"); - seq_printf(s, " bank %u:\n", bank); for (i = 0; i < debug_ranges[bank].num_ranges; i++) { u32 reg; @@ -520,22 +517,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 = { @@ -546,6 +563,51 @@ 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; +} + +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); @@ -1477,6 +1539,11 @@ static int ab8500_debug_probe(struct platform_device *plf) 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) -- cgit v1.2.3 From 1d843a6c8c2067615fea0ff8cb62d4a5c4a6f8ae Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Fri, 27 Jan 2012 11:35:41 +0100 Subject: mfd: ab8500-core: Allow the possibility to dump all AB8500 registers Implement an API so that a user may dump all AB8500 registers via debugfs file access. Signed-off-by: Lee Jones Signed-off-by: Mian Yousaf Kaukab Reviewed-by: Linus WALLEIJ Reviewed-by: Jonas ABERG --- drivers/mfd/ab8500-core.c | 1 + drivers/mfd/ab8500-debugfs.c | 13 +++++++++++++ include/linux/mfd/abx500/ab8500.h | 6 ++++++ 3 files changed, 20 insertions(+) diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index e1650badd106..d228697f5d9b 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -319,6 +319,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) diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index cd3cee814fb7..af6f774e658a 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -581,6 +581,19 @@ static int ab8500_print_all_banks(struct seq_file *s, void *p) 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; diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h index 5251bca56326..dbec7b00ea83 100644 --- a/include/linux/mfd/abx500/ab8500.h +++ b/include/linux/mfd/abx500/ab8500.h @@ -345,4 +345,10 @@ 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); +#else +static inline void ab8500_dump_all_banks(struct device *dev) {} +#endif + #endif /* MFD_AB8500_H */ -- cgit v1.2.3 From ddba25f17dc7015f44fbcdf79ce72f69996f2be3 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 3 Feb 2012 11:19:05 +0100 Subject: mfd: ab8500-debugfs: Allow number of IRQs to be provided more dynamically With the introduction of new AB* platforms, it's important to allow as much code reuse as possible. By allowing a system's number of IRQs to be dynamically passed, we can reuse almost all of the -debugfs driver. Signed-off-by: Lee Jones Signed-off-by: Linus Walleij --- drivers/mfd/ab8500-debugfs.c | 51 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index af6f774e658a..8617b132e730 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -94,10 +94,11 @@ static u32 debug_address; static int irq_first; static int irq_last; -static u32 irq_count[AB8500_NR_IRQS]; +static u32 *irq_count; +static int num_irqs; -static struct device_attribute *dev_attr[AB8500_NR_IRQS]; -static char *event_name[AB8500_NR_IRQS]; +static struct device_attribute **dev_attr; +static char **event_name; /** * struct ab8500_reg_range @@ -483,7 +484,7 @@ static irqreturn_t ab8500_debug_handler(int irq, void *data) struct kobject *kobj = (struct kobject *)data; unsigned int irq_abb = irq - irq_first; - if (irq_abb < AB8500_NR_IRQS) + if (irq_abb < num_irqs) irq_count[irq_abb]++; /* * This makes it possible to use poll for events (POLLPRI | POLLERR) @@ -1340,7 +1341,7 @@ static ssize_t show_irq(struct device *dev, return err; irq_index = name - irq_first; - if (irq_index >= AB8500_NR_IRQS) + if (irq_index >= num_irqs) return -EINVAL; else return sprintf(buf, "%u\n", irq_count[irq_index]); @@ -1376,7 +1377,7 @@ static ssize_t ab8500_subscribe_write(struct file *file, } irq_index = user_val - irq_first; - if (irq_index >= AB8500_NR_IRQS) + if (irq_index >= num_irqs) return -EINVAL; /* @@ -1440,7 +1441,7 @@ static ssize_t ab8500_unsubscribe_write(struct file *file, } irq_index = user_val - irq_first; - if (irq_index >= AB8500_NR_IRQS) + if (irq_index >= num_irqs) return -EINVAL; /* Set irq count to 0 when unsubscribe */ @@ -1521,21 +1522,40 @@ 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); - return 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); - return irq_last; + ret = irq_last; + goto out_freeevent_name; } ab8500_dir = debugfs_create_dir(AB8500_NAME_STRING, NULL); @@ -1658,12 +1678,23 @@ 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_recursive(ab8500_dir); + kfree(event_name); + kfree(dev_attr); + kfree(irq_count); + return 0; } -- cgit v1.2.3 From 20bf428329400d3c71be3bdaaa265a485902f90b Mon Sep 17 00:00:00 2001 From: Michel JAOUEN Date: Thu, 9 Feb 2012 12:06:47 +0100 Subject: mfd ab8500-gpadc: Introduce new AB version detection Add support for AB8505 and AB9540 Signed-off-by: Lee Jones Signed-off-by: Maxime Coquelin Signed-off-by: Bengt Jonsson Reviewed-by: Rabin VINCENT --- drivers/mfd/ab8500-gpadc.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c index bc0daf3bc93a..61b176fc457b 100644 --- a/drivers/mfd/ab8500-gpadc.c +++ b/drivers/mfd/ab8500-gpadc.c @@ -102,10 +102,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 +114,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; @@ -332,7 +332,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, @@ -591,6 +591,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 +608,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)) { -- cgit v1.2.3 From 8f0eb43be5f461a28341fe724686f265b0719dd3 Mon Sep 17 00:00:00 2001 From: Bengt Jonsson Date: Tue, 14 Feb 2012 13:01:00 +0100 Subject: mfd: ab8500-debugfs: Add interrupt debug This patch adds an entry in debugfs to check number of interrupts from the AB. Signed-off-by: Lee Jones Signed-off-by: Bengt Jonsson Reviewed-by: Rabin VINCENT --- drivers/mfd/ab8500-core.c | 1 + drivers/mfd/ab8500-debugfs.c | 49 +++++++++++++++++++++++++++++++++++++++ include/linux/mfd/abx500/ab8500.h | 2 ++ 3 files changed, 52 insertions(+) diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index d228697f5d9b..05a7af4b9768 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -521,6 +521,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); diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index 8617b132e730..cbebbd7e7b40 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -736,6 +736,35 @@ 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 */ @@ -1489,6 +1518,14 @@ 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, @@ -1597,6 +1634,18 @@ static int ab8500_debug_probe(struct platform_device *plf) if (!file) goto err; + 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; + + file = debugfs_create_file("interrupts", (S_IRUGO), + ab8500_dir, &plf->dev, &ab8500_interrupts_fops); + if (!file) + goto err; + file = debugfs_create_file("irq-unsubscribe", (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_unsubscribe_fops); if (!file) diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h index dbec7b00ea83..fa7173dd71f2 100644 --- a/include/linux/mfd/abx500/ab8500.h +++ b/include/linux/mfd/abx500/ab8500.h @@ -347,8 +347,10 @@ static inline int is_ab8500_2p0(struct ab8500 *ab) #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 */ -- cgit v1.2.3 From 5f8aaef4fdf97bfe504ff2c78244a53a145917dc Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 4 Feb 2013 08:33:13 +0000 Subject: mfd: ab8500-gpadc: Add runtime pm support Add runtime pm support to speed up multiple ADC reads in a row. Signed-off-by: Lee Jones Signed-off-by: Jonas Aaberg Reviewed-by: Ulf HANSSON --- drivers/mfd/ab8500-gpadc.c | 70 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 64 insertions(+), 6 deletions(-) diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c index 61b176fc457b..580f1008d67a 100644 --- a/drivers/mfd/ab8500-gpadc.c +++ b/drivers/mfd/ab8500-gpadc.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -82,6 +83,9 @@ /* 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 + enum cal_channels { ADC_INPUT_VMAIN = 0, ADC_INPUT_BTEMP, @@ -282,8 +286,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 { @@ -397,8 +402,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 +419,9 @@ out: */ (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 +580,30 @@ 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) +{ + struct ab8500_gpadc *gpadc = dev_get_drvdata(dev); + + pm_runtime_suspend(dev); + return 0; +} + static int ab8500_gpadc_probe(struct platform_device *pdev) { int ret = 0; @@ -615,6 +648,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"); @@ -635,19 +678,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, }, }; -- cgit v1.2.3 From 70bad04f2a30be002d8f4701d98d14259a86391b Mon Sep 17 00:00:00 2001 From: Ashok G Date: Tue, 28 Feb 2012 10:21:00 +0530 Subject: mfd: ab8500-debugfs: sizeof() mismatch bugfix Simple pointer error fix to obtain the expected sizeof() result. Signed-off-by: Lee Jones Signed-off-by: Ashok G Reviewed-by: Mattias WALLIN --- drivers/mfd/ab8500-debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index cbebbd7e7b40..b3fb65354567 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -1567,7 +1567,7 @@ static int ab8500_debug_probe(struct platform_device *plf) ab8500 = dev_get_drvdata(plf->dev.parent); num_irqs = ab8500->mask_size; - irq_count = kzalloc(sizeof(irq_count)*num_irqs, GFP_KERNEL); + irq_count = kzalloc(sizeof(*irq_count)*num_irqs, GFP_KERNEL); if (!irq_count) return -ENOMEM; -- cgit v1.2.3 From f825ebe522e974fec84525f12a43ec70e304e248 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 28 Jan 2013 09:20:45 +0000 Subject: mfd: ab8500-gpadc: Reduce conversion timeout Reduce the conversion timeout from 2s to 0.5s Signed-off-by: Lee Jones Signed-off-by: Jonas Aaberg Reviewed-by: Karl KOMIEROWSKI --- drivers/mfd/ab8500-gpadc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c index 580f1008d67a..44fa224a443e 100644 --- a/drivers/mfd/ab8500-gpadc.c +++ b/drivers/mfd/ab8500-gpadc.c @@ -86,6 +86,8 @@ /* 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, @@ -372,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; -- cgit v1.2.3 From 9d3f653f45b0ad299158d31207e4b747aa6df2c7 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 28 Jan 2013 09:21:58 +0000 Subject: mfd: ab8500-gpadc: Remove unused 'struct ab8500_gpadc' The ab8500_gpadc structure carries lots of important information which most functions make good use of. The initial expectation was that ab8500_gpadc_runtime_idle() would be no exception; however, this hasn't been the case to date. Let's remove it for now and add it back in only when we have a use for it. Signed-off-by: Lee Jones --- drivers/mfd/ab8500-gpadc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c index 44fa224a443e..b1f3561b023f 100644 --- a/drivers/mfd/ab8500-gpadc.c +++ b/drivers/mfd/ab8500-gpadc.c @@ -601,8 +601,6 @@ static int ab8500_gpadc_runtime_resume(struct device *dev) static int ab8500_gpadc_runtime_idle(struct device *dev) { - struct ab8500_gpadc *gpadc = dev_get_drvdata(dev); - pm_runtime_suspend(dev); return 0; } -- cgit v1.2.3 From 8908c04985ed67b9138c6e06be2c81867f24e2d4 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Wed, 18 Apr 2012 15:52:59 +0200 Subject: mfd: ab8500-gpadc: Use new ab8500_gpadc_get() with name parameter The new format of ab8500_gpadc_get() accepts a device name as a parameter to specify which device to retrieve. This patch enforces the use of that new format. Signed-off-by: Lee Jones Signed-off-by: Philippe Langlais --- drivers/mfd/ab8500-debugfs.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index b3fb65354567..ba25f95e1677 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -806,7 +806,7 @@ static int ab8500_gpadc_bat_ctrl_print(struct seq_file *s, void *p) int bat_ctrl_convert; struct ab8500_gpadc *gpadc; - gpadc = ab8500_gpadc_get(); + 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); @@ -834,7 +834,7 @@ static int ab8500_gpadc_btemp_ball_print(struct seq_file *s, void *p) int btemp_ball_convert; struct ab8500_gpadc *gpadc; - gpadc = ab8500_gpadc_get(); + 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); @@ -863,7 +863,7 @@ static int ab8500_gpadc_main_charger_v_print(struct seq_file *s, void *p) int main_charger_v_convert; struct ab8500_gpadc *gpadc; - gpadc = ab8500_gpadc_get(); + 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); @@ -893,7 +893,7 @@ static int ab8500_gpadc_acc_detect1_print(struct seq_file *s, void *p) int acc_detect1_convert; struct ab8500_gpadc *gpadc; - gpadc = ab8500_gpadc_get(); + 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); @@ -923,7 +923,7 @@ static int ab8500_gpadc_acc_detect2_print(struct seq_file *s, void *p) int acc_detect2_convert; struct ab8500_gpadc *gpadc; - gpadc = ab8500_gpadc_get(); + 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); @@ -953,7 +953,7 @@ static int ab8500_gpadc_aux1_print(struct seq_file *s, void *p) int aux1_convert; struct ab8500_gpadc *gpadc; - gpadc = ab8500_gpadc_get(); + 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); @@ -981,7 +981,7 @@ static int ab8500_gpadc_aux2_print(struct seq_file *s, void *p) int aux2_convert; struct ab8500_gpadc *gpadc; - gpadc = ab8500_gpadc_get(); + 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); @@ -1009,7 +1009,7 @@ static int ab8500_gpadc_main_bat_v_print(struct seq_file *s, void *p) int main_bat_v_convert; struct ab8500_gpadc *gpadc; - gpadc = ab8500_gpadc_get(); + 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); @@ -1038,7 +1038,7 @@ static int ab8500_gpadc_vbus_v_print(struct seq_file *s, void *p) int vbus_v_convert; struct ab8500_gpadc *gpadc; - gpadc = ab8500_gpadc_get(); + 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); @@ -1066,7 +1066,7 @@ static int ab8500_gpadc_main_charger_c_print(struct seq_file *s, void *p) int main_charger_c_convert; struct ab8500_gpadc *gpadc; - gpadc = ab8500_gpadc_get(); + 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); @@ -1096,7 +1096,7 @@ static int ab8500_gpadc_usb_charger_c_print(struct seq_file *s, void *p) int usb_charger_c_convert; struct ab8500_gpadc *gpadc; - gpadc = ab8500_gpadc_get(); + 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); @@ -1126,7 +1126,7 @@ static int ab8500_gpadc_bk_bat_v_print(struct seq_file *s, void *p) int bk_bat_v_convert; struct ab8500_gpadc *gpadc; - gpadc = ab8500_gpadc_get(); + 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); @@ -1154,7 +1154,7 @@ static int ab8500_gpadc_die_temp_print(struct seq_file *s, void *p) int die_temp_convert; struct ab8500_gpadc *gpadc; - gpadc = ab8500_gpadc_get(); + 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); -- cgit v1.2.3