move wm831x code from rk2818

This commit is contained in:
luowei 2011-02-26 14:28:56 +08:00
parent ba3f49dd47
commit 127fdac55d
19 changed files with 2891 additions and 545 deletions

0
Documentation/hwmon/wm831x Normal file → Executable file
View File

116
drivers/gpio/wm831x-gpio.c Normal file → Executable file
View File

@ -13,6 +13,7 @@
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/mfd/core.h>
@ -22,8 +23,7 @@
#include <linux/mfd/wm831x/core.h>
#include <linux/mfd/wm831x/pdata.h>
#include <linux/mfd/wm831x/gpio.h>
#define WM831X_GPIO_MAX 16
#include <linux/mfd/wm831x/irq.h>
struct wm831x_gpio {
struct wm831x *wm831x;
@ -35,14 +35,34 @@ static inline struct wm831x_gpio *to_wm831x_gpio(struct gpio_chip *chip)
return container_of(chip, struct wm831x_gpio, gpio_chip);
}
static int wm831x_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
static int wm831x_gpio_pull_up_down(struct gpio_chip *chip, unsigned offset, unsigned value)
{
struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
struct wm831x *wm831x = wm831x_gpio->wm831x;
if(value == GPIOPullUp)
value = WM831X_GPIO_PULL_UP;
else if(value == GPIOPullDown)
value = WM831X_GPIO_PULL_DOWN;
else if(value == GPIONormal)
value = WM831X_GPIO_PULL_NONE;
//printk("wm831x_gpio_pull_up_down=%x,%x\n",WM831X_GPIO1_CONTROL + offset,value);
return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + offset,
WM831X_GPN_PULL_MASK, value);
}
static int wm831x_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
{
struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
struct wm831x *wm831x = wm831x_gpio->wm831x;
int val = WM831X_GPN_DIR;
if (wm831x->has_gpio_ena)
val |= WM831X_GPN_TRI;
//printk("wm831x_gpio_direction_in=%x,%x\n",WM831X_GPIO1_CONTROL + offset,val);
return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + offset,
WM831X_GPN_DIR | WM831X_GPN_TRI,
WM831X_GPN_DIR);
WM831X_GPN_DIR | WM831X_GPN_TRI |
WM831X_GPN_FN_MASK, val);
}
static int wm831x_gpio_get(struct gpio_chip *chip, unsigned offset)
@ -50,15 +70,19 @@ static int wm831x_gpio_get(struct gpio_chip *chip, unsigned offset)
struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
struct wm831x *wm831x = wm831x_gpio->wm831x;
int ret;
ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL);
int gpn_pol;
ret = wm831x_reg_read(wm831x, WM831X_GPIO1_CONTROL + offset);
if (ret < 0)
return ret;
if (ret & 1 << offset)
return 1;
else
return 0;
gpn_pol = (ret & WM831X_GPN_POL_MASK) >> WM831X_GPN_POL_SHIFT;
ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL);
//printk("wm831x_gpio_get=%x,%d,%d\n",ret,offset,gpn_pol);
if (ret < 0)
return ret;
return !((ret>>offset)^gpn_pol);
}
static void wm831x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
@ -75,10 +99,15 @@ static int wm831x_gpio_direction_out(struct gpio_chip *chip,
{
struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
struct wm831x *wm831x = wm831x_gpio->wm831x;
int val = 0;
int ret;
if (wm831x->has_gpio_ena)
val |= WM831X_GPN_TRI;
//printk("wm831x_gpio_direction_out=%x,%x\n",WM831X_GPIO1_CONTROL + offset,val);
ret = wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + offset,
WM831X_GPN_DIR | WM831X_GPN_TRI, 0);
WM831X_GPN_DIR | WM831X_GPN_TRI |
WM831X_GPN_FN_MASK | WM831X_GPN_POL_MASK, val|WM831X_GPN_POL);
if (ret < 0)
return ret;
@ -88,12 +117,54 @@ static int wm831x_gpio_direction_out(struct gpio_chip *chip,
return 0;
}
static int wm831x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{
struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
struct wm831x *wm831x = wm831x_gpio->wm831x;
if (!wm831x->irq_base)
return -EINVAL;
return wm831x->irq_base + WM831X_IRQ_GPIO_1 + offset;
}
static int wm831x_gpio_set_debounce(struct gpio_chip *chip, unsigned offset,
unsigned debounce)
{
struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
struct wm831x *wm831x = wm831x_gpio->wm831x;
int reg = WM831X_GPIO1_CONTROL + offset;
int ret, fn;
ret = wm831x_reg_read(wm831x, reg);
if (ret < 0)
return ret;
switch (ret & WM831X_GPN_FN_MASK) {
case 0:
case 1:
break;
default:
/* Not in GPIO mode */
return -EBUSY;
}
if (debounce >= 32 && debounce <= 64)
fn = 0;
else if (debounce >= 4000 && debounce <= 8000)
fn = 1;
else
return -EINVAL;
//printk("wm831x_gpio_set_debounce=%x,%x\n",WM831X_GPIO1_CONTROL + offset,fn);
return wm831x_set_bits(wm831x, reg, WM831X_GPN_FN_MASK, fn);
}
#ifdef CONFIG_DEBUG_FS
static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
{
struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
struct wm831x *wm831x = wm831x_gpio->wm831x;
int i;
int i, tristated;
for (i = 0; i < chip->ngpio; i++) {
int gpio = i + chip->base;
@ -160,15 +231,19 @@ static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
break;
}
tristated = reg & WM831X_GPN_TRI;
if (wm831x->has_gpio_ena)
tristated = !tristated;
seq_printf(s, " %s %s %s %s%s\n"
" %s%s (0x%4x)\n",
reg & WM831X_GPN_DIR ? "in" : "out",
wm831x_gpio_get(chip, i) ? "high" : "low",
pull,
powerdomain,
reg & WM831X_GPN_POL ? " inverted" : "",
reg & WM831X_GPN_POL ? "" : " inverted",
reg & WM831X_GPN_OD ? "open-drain" : "CMOS",
reg & WM831X_GPN_TRI ? " tristated" : "",
tristated ? " tristated" : "",
reg);
}
}
@ -183,6 +258,9 @@ static struct gpio_chip template_chip = {
.get = wm831x_gpio_get,
.direction_output = wm831x_gpio_direction_out,
.set = wm831x_gpio_set,
.pull_updown = wm831x_gpio_pull_up_down,
.to_irq = wm831x_gpio_to_irq,
.set_debounce = wm831x_gpio_set_debounce,
.dbg_show = wm831x_gpio_dbg_show,
.can_sleep = 1,
};
@ -193,14 +271,14 @@ static int __devinit wm831x_gpio_probe(struct platform_device *pdev)
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
struct wm831x_gpio *wm831x_gpio;
int ret;
printk("%s\n",__FUNCTION__);
wm831x_gpio = kzalloc(sizeof(*wm831x_gpio), GFP_KERNEL);
if (wm831x_gpio == NULL)
return -ENOMEM;
wm831x_gpio->wm831x = wm831x;
wm831x_gpio->gpio_chip = template_chip;
wm831x_gpio->gpio_chip.ngpio = WM831X_GPIO_MAX;
wm831x_gpio->gpio_chip.ngpio = wm831x->num_gpio;
wm831x_gpio->gpio_chip.dev = &pdev->dev;
if (pdata && pdata->gpio_base)
wm831x_gpio->gpio_chip.base = pdata->gpio_base;

1
drivers/hwmon/wm831x-hwmon.c Normal file → Executable file
View File

@ -24,6 +24,7 @@
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/slab.h>
#include <linux/mfd/wm831x/core.h>
#include <linux/mfd/wm831x/auxadc.h>

View File

@ -0,0 +1,468 @@
/*
* Driver for keys on GPIO lines capable of generating interrupts.
*
* Copyright 2005 Phil Blundell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/workqueue.h>
#include <asm/gpio.h>
#include <linux/mfd/wm831x/core.h>
#include <linux/mfd/wm831x/pdata.h>
#define CONFIG_WM831X_GPIO_KEY_DEBUG 0
#if (CONFIG_WM831X_GPIO_KEY_DEBUG)
#define WM831X_GPIO_KEY_DG(format, ...) printk(format, ## __VA_ARGS__)
#else
#define WM831X_GPIO_KEY_DG(format, ...)
#endif
bool isHSKeyMIC = false;
int pre_state = 0;
struct wm831x_gpio_keys_button *media_button;
extern bool wm8994_set_status(void);
extern int headset_status(void);
struct wm831x_gpio_button_data {
struct wm831x_gpio_keys_button *button;
struct input_dev *input;
struct timer_list timer;
struct work_struct work;
};
struct wm831x_gpio_keys_drvdata {
struct input_dev *input;
struct wm831x_gpio_button_data data[0];
};
bool isHSKey_MIC(void)
{
return isHSKeyMIC;
}
EXPORT_SYMBOL_GPL(isHSKey_MIC);
void detect_HSMic(void)
{
int state;
struct wm831x_gpio_keys_button *button = media_button;
WM831X_GPIO_KEY_DG("detect_HSMic\n");
if(!headset_status())
{
isHSKeyMIC = false;
return ;
}
else
{
mdelay(500);
}
state = (gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low;
WM831X_GPIO_KEY_DG("detect_HSMic: code=%d,gpio=%d\n",button->gpio,button->code);
if(state){
WM831X_GPIO_KEY_DG("detect_HSMic:---headset without MIC and HSKey---\n");
isHSKeyMIC = false;
}else{
WM831X_GPIO_KEY_DG("detect_HSMic:---headset with MIC---\n");
isHSKeyMIC = true;
}
return;
}
EXPORT_SYMBOL_GPL(detect_HSMic);
static int HSKeyDetect(int state)
{
WM831X_GPIO_KEY_DG("HSKeyDetect\n");
if(headset_status()){
WM831X_GPIO_KEY_DG("headset_status() == true !\n");
if(pre_state != state && !wm8994_set_status()){
WM831X_GPIO_KEY_DG("wm8994_set_status() == true !\n");
pre_state = state;
if(!isHSKeyMIC){
state = -1;
}
}
else{
WM831X_GPIO_KEY_DG("wm8994_set_status() == false !\n");
state = -1;
}
}
else{
WM831X_GPIO_KEY_DG("headset_status() == false !\n");
isHSKeyMIC = false;
state = -1;
}
return state;
}
static void wm831x_gpio_keys_report_event(struct work_struct *work)
{
struct wm831x_gpio_button_data *bdata =
container_of(work, struct wm831x_gpio_button_data, work);
struct wm831x_gpio_keys_button *button = bdata->button;
struct input_dev *input = bdata->input;
unsigned int type = button->type ?: EV_KEY;
int state = (gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low;
if(button->code == KEY_MEDIA)
{
state = HSKeyDetect(state);
if(state == -1)
{
WM831X_GPIO_KEY_DG("wm831x_gpio_keys_report_event:HSKeyDetect=-1\n");
goto out;
}
}
printk("wm831x_gpio_keys_report_event:state=%d,code=%d \n",state,button->code);
input_event(input, type, button->code, state);
input_sync(input);
out:
enable_irq(gpio_to_irq(button->gpio));
return;
}
static void wm831x_gpio_keys_timer(unsigned long _data)
{
struct wm831x_gpio_button_data *data = (struct wm831x_gpio_button_data *)_data;
WM831X_GPIO_KEY_DG("wm831x_gpio_keys_timer\n");
schedule_work(&data->work);
}
static irqreturn_t wm831x_gpio_keys_isr(int irq, void *dev_id)
{
struct wm831x_gpio_button_data *bdata = dev_id;
struct wm831x_gpio_keys_button *button = bdata->button;
//printk("wm831x_gpio_keys_isr:irq=%d,%d \n",irq,button->debounce_interval);
BUG_ON(irq != gpio_to_irq(button->gpio));
disable_irq_nosync(gpio_to_irq(button->gpio));
if (button->debounce_interval)
mod_timer(&bdata->timer,
jiffies + msecs_to_jiffies(button->debounce_interval));
else
schedule_work(&bdata->work);
return IRQ_HANDLED;
}
static int __devinit wm831x_gpio_keys_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
struct wm831x_gpio_keys_pdata *gpio_keys;
struct wm831x_gpio_keys_drvdata *ddata;
struct input_dev *input;
int i, error;
//int wakeup = 0;
printk("wm831x_gpio_keys_probe\n");
if (pdata == NULL || pdata->gpio_keys == NULL)
return -ENODEV;
gpio_keys = pdata->gpio_keys;
ddata = kzalloc(sizeof(struct wm831x_gpio_keys_drvdata) +
gpio_keys->nbuttons * sizeof(struct wm831x_gpio_button_data),
GFP_KERNEL);
input = input_allocate_device();
if (!ddata || !input) {
error = -ENOMEM;
goto fail1;
}
platform_set_drvdata(pdev, ddata);
input->name = pdev->name;
input->phys = "wm831x_gpio-keys/input0";
input->dev.parent = &pdev->dev;
input->id.bustype = BUS_I2C;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
/* Enable auto repeat feature of Linux input subsystem */
if (gpio_keys->rep)
__set_bit(EV_REP, input->evbit);
ddata->input = input;
for (i = 0; i < gpio_keys->nbuttons; i++) {
struct wm831x_gpio_keys_button *button = &gpio_keys->buttons[i];
struct wm831x_gpio_button_data *bdata = &ddata->data[i];
int irq = 0;
unsigned int type = button->type ?: EV_KEY;
bdata->input = input;
bdata->button = button;
if(button->code == KEY_MEDIA)
{
media_button = button;
}
if (button->debounce_interval)
setup_timer(&bdata->timer,
wm831x_gpio_keys_timer, (unsigned long)bdata);
//else
INIT_WORK(&bdata->work, wm831x_gpio_keys_report_event);
error = gpio_request(button->gpio, button->desc ?: "wm831x_gpio_keys");
if (error < 0) {
pr_err("wm831x_gpio-keys: failed to request GPIO %d,"
" error %d\n", button->gpio, error);
goto fail2;
}
if(button->gpio >= WM831X_P01 && button->gpio <= WM831X_P12)
{
error = gpio_pull_updown(button->gpio,GPIOPullUp);
if (error < 0) {
pr_err("wm831x_gpio-keys: failed to pull up"
" for GPIO %d, error %d\n",
button->gpio, error);
gpio_free(button->gpio);
goto fail2;
}
}
error = gpio_direction_input(button->gpio);
if (error < 0) {
pr_err("wm831x_gpio-keys: failed to configure input"
" direction for GPIO %d, error %d\n",
button->gpio, error);
gpio_free(button->gpio);
goto fail2;
}
irq = gpio_to_irq(button->gpio);
if (irq < 0) {
error = irq;
pr_err("wm831x_gpio-keys: Unable to get irq number"
" for GPIO %d, error %d\n",
button->gpio, error);
gpio_free(button->gpio);
goto fail2;
}
printk("wm831x_gpio_keys_probe:i=%d,gpio=%d,irq=%d \n",i,button->gpio,irq);
enable_irq_wake(irq);
if(button->gpio >= WM831X_P01 && button->gpio <= WM831X_P12)
{
error = request_threaded_irq(irq, NULL,wm831x_gpio_keys_isr,
IRQF_SHARED |
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
button->desc ? button->desc : "wm831x_gpio_keys",
bdata);
}
else if(button->gpio >= TCA6424_P00 && button->gpio <= TCA6424_P27)
{
error = request_irq(irq, wm831x_gpio_keys_isr,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
button->desc ? button->desc : "tca6424_gpio_keys",
bdata);
}
if (error) {
pr_err("wm831x_gpio-keys: Unable to claim irq %d; error %d\n",
irq, error);
gpio_free(button->gpio);
goto fail2;
}
//if (button->wakeup)
// wakeup = 1;
input_set_capability(input, type, button->code);
}
error = input_register_device(input);
if (error) {
pr_err("wm831x_gpio-keys: Unable to register input device, "
"error: %d\n", error);
goto fail2;
}
//device_init_wakeup(&pdev->dev, wakeup);
return 0;
fail2:
while (--i >= 0) {
free_irq(gpio_to_irq(gpio_keys->buttons[i].gpio), &ddata->data[i]);
if (gpio_keys->buttons[i].debounce_interval)
del_timer_sync(&ddata->data[i].timer);
//else
cancel_work_sync(&ddata->data[i].work);
gpio_free(gpio_keys->buttons[i].gpio);
}
platform_set_drvdata(pdev, NULL);
fail1:
input_free_device(input);
kfree(ddata);
return error;
}
static int __devexit wm831x_gpio_keys_remove(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
struct wm831x_gpio_keys_pdata *gpio_keys;
struct wm831x_gpio_keys_drvdata *ddata = platform_get_drvdata(pdev);
struct input_dev *input = ddata->input;
int i;
if (pdata == NULL || pdata->gpio_keys == NULL)
return -ENODEV;
gpio_keys = pdata->gpio_keys;
//device_init_wakeup(&pdev->dev, 0);
for (i = 0; i < gpio_keys->nbuttons; i++) {
int irq = gpio_to_irq(gpio_keys->buttons[i].gpio);
free_irq(irq, &ddata->data[i]);
if (gpio_keys->buttons[i].debounce_interval)
del_timer_sync(&ddata->data[i].timer);
//else
cancel_work_sync(&ddata->data[i].work);
gpio_free(gpio_keys->buttons[i].gpio);
}
input_unregister_device(input);
return 0;
}
#ifdef CONFIG_PM
static int wm831x_gpio_keys_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
struct wm831x_gpio_keys_pdata *gpio_keys;
int i,irq;
if (pdata == NULL || pdata->gpio_keys == NULL)
{
printk("wm831x_gpio_keys_suspend fail\n");
return -ENODEV;
}
//printk("wm831x_gpio_keys_suspend\n");
gpio_keys = pdata->gpio_keys;
//if (device_may_wakeup(&pdev->dev)) {
for (i = 0; i < gpio_keys->nbuttons; i++) {
struct wm831x_gpio_keys_button *button = &gpio_keys->buttons[i];
if (button->wakeup) {
irq = gpio_to_irq(button->gpio);
enable_irq_wake(irq);
}
else
{
irq = gpio_to_irq(button->gpio);
disable_irq_wake(irq);
}
}
//}
return 0;
}
static int wm831x_gpio_keys_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
struct wm831x_gpio_keys_pdata *gpio_keys;
int i,irq;
if (pdata == NULL || pdata->gpio_keys == NULL)
{
printk("wm831x_gpio_keys_resume fail\n");
return -ENODEV;
}
//printk("wm831x_gpio_keys_resume\n");
gpio_keys = pdata->gpio_keys;
//if (device_may_wakeup(&pdev->dev)) {
for (i = 0; i < gpio_keys->nbuttons; i++) {
struct wm831x_gpio_keys_button *button = &gpio_keys->buttons[i];
//if (button->wakeup) {
irq = gpio_to_irq(button->gpio);
enable_irq_wake(irq);
//}
}
//}
return 0;
}
static const struct dev_pm_ops wm831x_gpio_keys_pm_ops = {
.suspend = wm831x_gpio_keys_suspend,
.resume = wm831x_gpio_keys_resume,
};
#endif
static struct platform_driver wm831x_gpio_keys_device_driver = {
.probe = wm831x_gpio_keys_probe,
.remove = __devexit_p(wm831x_gpio_keys_remove),
.driver = {
.name = "wm831x_gpio-keys",
.owner = THIS_MODULE,
#ifdef CONFIG_PM
.pm = &wm831x_gpio_keys_pm_ops,
#endif
}
};
static int __init wm831x_gpio_keys_init(void)
{
return platform_driver_register(&wm831x_gpio_keys_device_driver);
}
static void __exit wm831x_gpio_keys_exit(void)
{
platform_driver_unregister(&wm831x_gpio_keys_device_driver);
}
subsys_initcall(wm831x_gpio_keys_init);
module_exit(wm831x_gpio_keys_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("SRT <srt@rock-chip.com>");
MODULE_DESCRIPTION("Keyboard driver for WM831x GPIOs");
MODULE_ALIAS("platform:wm831x_gpio-keys");

155
drivers/input/misc/wm831x-on.c Normal file → Executable file
View File

@ -19,6 +19,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/input.h>
@ -26,17 +27,106 @@
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/mfd/wm831x/core.h>
#include <mach/gpio.h>
#include <mach/iomux.h>
#include <linux/delay.h>
#if 0
#define DBG(x...) printk(x)
#else
#define DBG(x...)
#endif
struct wm831x_on {
struct input_dev *dev;
struct delayed_work work;
struct wm831x *wm831x;
struct wake_lock wm831x_on_wake;
};
struct wm831x_on *g_wm831x_on;
void rk28_send_wakeup_key(void)
{
printk("%s\n", __FUNCTION__);
if(!g_wm831x_on)
{
printk("%s:addr err!\n",__FUNCTION__);
return;
}
input_report_key(g_wm831x_on->dev, KEY_POWER, 1);
input_sync(g_wm831x_on->dev);
input_report_key(g_wm831x_on->dev, KEY_POWER, 0);
input_sync(g_wm831x_on->dev);
printk("%s end\n", __FUNCTION__);
}
#if 1
static int wm831x_on_suspend_noirq(struct device *dev)
{
DBG("%s\n",__FUNCTION__);
return 0;
}
static int wm831x_on_resume_noirq(struct device *dev)
{
int poll, ret;
if(!g_wm831x_on)
{
printk("%s:addr err!\n",__FUNCTION__);
return;
}
ret = wm831x_reg_read(g_wm831x_on->wm831x, WM831X_ON_PIN_CONTROL);
if (ret >= 0) {
poll = !(ret & WM831X_ON_PIN_STS);
//poll = 1;
input_report_key(g_wm831x_on->dev, KEY_POWER, poll);
input_sync(g_wm831x_on->dev);
DBG("%s:poll=%d,ret=0x%x\n",__FUNCTION__,poll,ret);
}
DBG("%s\n",__FUNCTION__);
return 0;
}
static struct dev_pm_ops wm831x_on_dev_pm_ops = {
.suspend_noirq = wm831x_on_suspend_noirq,
.resume_noirq = wm831x_on_resume_noirq,
};
static struct platform_driver wm831x_on_pm_driver = {
.driver = {
.name = "wm831x_on",
.pm = &wm831x_on_dev_pm_ops,
},
};
static struct platform_device wm831x_on_pm_device = {
.name = "wm831x_on",
.id = -1,
.dev = {
.driver = &wm831x_on_pm_driver.driver,
}
};
static inline void wm831x_on_pm_init(void)
{
if (platform_driver_register(&wm831x_on_pm_driver) == 0)
(void) platform_device_register(&wm831x_on_pm_device);
}
#endif
/*
* The chip gives us an interrupt when the ON pin is asserted but we
* then need to poll to see when the pin is deasserted.
*/
static void wm831x_poll_on(struct work_struct *work)
{
struct wm831x_on *wm831x_on = container_of(work, struct wm831x_on,
@ -47,43 +137,82 @@ static void wm831x_poll_on(struct work_struct *work)
ret = wm831x_reg_read(wm831x, WM831X_ON_PIN_CONTROL);
if (ret >= 0) {
poll = !(ret & WM831X_ON_PIN_STS);
input_report_key(wm831x_on->dev, KEY_POWER, poll);
input_sync(wm831x_on->dev);
DBG("%s:poll=%d,ret=0x%x\n",__FUNCTION__,poll,ret);
} else {
dev_err(wm831x->dev, "Failed to read ON status: %d\n", ret);
poll = 1;
}
if (poll)
schedule_delayed_work(&wm831x_on->work, 100);
schedule_delayed_work(&wm831x_on->work, 2);
else
wake_unlock(&wm831x->handle_wake);
//wake_unlock(&wm831x_on->wm831x_on_wake);
}
#if 0
static irqreturn_t wm831x_on_irq(int irq, void *data)
{
struct wm831x_on *wm831x_on = data;
wake_lock(&wm831x_on->wm831x_on_wake);
schedule_delayed_work(&wm831x_on->work, 0);
return IRQ_HANDLED;
}
#else
static irqreturn_t wm831x_on_irq(int irq, void *data)
{
struct wm831x_on *wm831x_on = data;
struct wm831x *wm831x = wm831x_on->wm831x;
int poll, ret;
//wake_lock(&wm831x_on->wm831x_on_wake);
read_again:
ret = wm831x_reg_read(wm831x, WM831X_ON_PIN_CONTROL);
if (ret >= 0) {
poll = !(ret & WM831X_ON_PIN_STS);
input_report_key(wm831x_on->dev, KEY_POWER, poll);
input_sync(wm831x_on->dev);
DBG("%s:poll=%d,ret=0x%x\n",__FUNCTION__,poll,ret);
} else {
dev_err(wm831x->dev, "Failed to read ON status: %d\n", ret);
poll = 1;
}
schedule_delayed_work(&wm831x_on->work, 0);
if (poll)
schedule_delayed_work(&wm831x_on->work, 0);
else
wake_unlock(&wm831x->handle_wake);
//wake_unlock(&wm831x_on->wm831x_on_wake);
return IRQ_HANDLED;
}
#endif
static int __devinit wm831x_on_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);;
struct wm831x_on *wm831x_on;
int irq = platform_get_irq(pdev, 0);
int ret;
printk("%s irq=%d\n", __FUNCTION__,irq);
wm831x_on = kzalloc(sizeof(struct wm831x_on), GFP_KERNEL);
if (!wm831x_on) {
dev_err(&pdev->dev, "Can't allocate data\n");
return -ENOMEM;
}
wm831x_on->wm831x = wm831x;
INIT_DELAYED_WORK(&wm831x_on->work, wm831x_poll_on);
wake_lock_init(&wm831x_on->wm831x_on_wake, WAKE_LOCK_SUSPEND, "wm831x_on_wake");
wm831x_on->dev = input_allocate_device();
if (!wm831x_on->dev) {
dev_err(&pdev->dev, "Can't allocate input dev\n");
@ -96,9 +225,13 @@ static int __devinit wm831x_on_probe(struct platform_device *pdev)
wm831x_on->dev->name = "wm831x_on";
wm831x_on->dev->phys = "wm831x_on/input0";
wm831x_on->dev->dev.parent = &pdev->dev;
g_wm831x_on = wm831x_on;
ret = wm831x_request_irq(wm831x, irq, wm831x_on_irq,
IRQF_TRIGGER_RISING, "wm831x_on", wm831x_on);
wm831x_on_pm_init();
ret = request_threaded_irq(irq, NULL, wm831x_on_irq,
IRQF_TRIGGER_RISING, "wm831x_on",
wm831x_on);
if (ret < 0) {
dev_err(&pdev->dev, "Unable to request IRQ: %d\n", ret);
goto err_input_dev;
@ -114,7 +247,7 @@ static int __devinit wm831x_on_probe(struct platform_device *pdev)
return 0;
err_irq:
wm831x_free_irq(wm831x, irq, NULL);
free_irq(irq, wm831x_on);
err_input_dev:
input_free_device(wm831x_on->dev);
err:
@ -127,7 +260,7 @@ static int __devexit wm831x_on_remove(struct platform_device *pdev)
struct wm831x_on *wm831x_on = platform_get_drvdata(pdev);
int irq = platform_get_irq(pdev, 0);
wm831x_free_irq(wm831x_on->wm831x, irq, wm831x_on);
free_irq(irq, wm831x_on);
cancel_delayed_work_sync(&wm831x_on->work);
input_unregister_device(wm831x_on->dev);
kfree(wm831x_on);

1
drivers/leds/leds-wm831x-status.c Normal file → Executable file
View File

@ -12,6 +12,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/leds.h>
#include <linux/err.h>
#include <linux/mfd/wm831x/core.h>

569
drivers/mfd/wm831x-core.c Normal file → Executable file
View File

@ -14,10 +14,11 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/bcd.h>
#include <linux/delay.h>
#include <linux/mfd/core.h>
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/mfd/wm831x/core.h>
#include <linux/mfd/wm831x/pdata.h>
@ -25,10 +26,13 @@
#include <linux/mfd/wm831x/auxadc.h>
#include <linux/mfd/wm831x/otp.h>
#include <linux/mfd/wm831x/regulator.h>
#include <linux/mfd/wm831x/pmu.h>
/* Current settings - values are 2*2^(reg_val/4) microamps. These are
* exported since they are used by multiple drivers.
*/
extern int reboot_cmd_get(void);
int wm831x_isinkv_values[WM831X_ISINK_MAX_ISEL + 1] = {
2,
2,
@ -89,12 +93,6 @@ int wm831x_isinkv_values[WM831X_ISINK_MAX_ISEL + 1] = {
};
EXPORT_SYMBOL_GPL(wm831x_isinkv_values);
enum wm831x_parent {
WM8310 = 0,
WM8311 = 1,
WM8312 = 2,
};
static int wm831x_reg_locked(struct wm831x *wm831x, unsigned short reg)
{
if (!wm831x->locked)
@ -320,8 +318,11 @@ EXPORT_SYMBOL_GPL(wm831x_set_bits);
*/
int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
{
int tries = 10;
int ret, src;
int ret, src, irq_masked, timeout;
/* Are we using the interrupt? */
irq_masked = wm831x_reg_read(wm831x, WM831X_INTERRUPT_STATUS_1_MASK);
irq_masked &= WM831X_AUXADC_DATA_EINT;
mutex_lock(&wm831x->auxadc_lock);
@ -341,6 +342,9 @@ int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
goto out;
}
/* Clear any notification from a very late arriving interrupt */
try_wait_for_completion(&wm831x->auxadc_done);
ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
WM831X_AUX_CVT_ENA, WM831X_AUX_CVT_ENA);
if (ret < 0) {
@ -348,18 +352,46 @@ int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
goto disable;
}
do {
msleep(1);
if (irq_masked) {
/* If we're not using interrupts then poll the
* interrupt status register */
timeout = 5;
while (timeout) {
msleep(1);
ret = wm831x_reg_read(wm831x, WM831X_AUXADC_CONTROL);
if (ret < 0)
ret = WM831X_AUX_CVT_ENA;
} while ((ret & WM831X_AUX_CVT_ENA) && --tries);
ret = wm831x_reg_read(wm831x,
WM831X_INTERRUPT_STATUS_1);
if (ret < 0) {
dev_err(wm831x->dev,
"ISR 1 read failed: %d\n", ret);
goto disable;
}
if (ret & WM831X_AUX_CVT_ENA) {
dev_err(wm831x->dev, "Timed out reading AUXADC\n");
ret = -EBUSY;
goto disable;
/* Did it complete? */
if (ret & WM831X_AUXADC_DATA_EINT) {
wm831x_reg_write(wm831x,
WM831X_INTERRUPT_STATUS_1,
WM831X_AUXADC_DATA_EINT);
break;
} else {
dev_err(wm831x->dev,
"AUXADC conversion timeout\n");
ret = -EBUSY;
goto disable;
}
}
} else {
/* If we are using interrupts then wait for the
* interrupt to complete. Use an extremely long
* timeout to handle situations with heavy load where
* the notification of the interrupt may be delayed by
* threaded IRQ handling. */
if (!wait_for_completion_timeout(&wm831x->auxadc_done,
msecs_to_jiffies(2000))) {
dev_err(wm831x->dev, "Timed out waiting for AUXADC\n");
ret = -EBUSY;
goto disable;
}
}
ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA);
@ -389,6 +421,15 @@ int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
}
EXPORT_SYMBOL_GPL(wm831x_auxadc_read);
static irqreturn_t wm831x_auxadc_irq(int irq, void *irq_data)
{
struct wm831x *wm831x = irq_data;
complete(&wm831x->auxadc_done);
return IRQ_HANDLED;
}
/**
* wm831x_auxadc_read_uv: Read a voltage from the WM831x AUXADC
*
@ -478,6 +519,20 @@ static struct resource wm831x_dcdc4_resources[] = {
},
};
static struct resource wm8320_dcdc4_buck_resources[] = {
{
.start = WM831X_DC4_CONTROL,
.end = WM832X_DC4_SLEEP_CONTROL,
.flags = IORESOURCE_IO,
},
{
.name = "UV",
.start = WM831X_IRQ_UV_DC4,
.end = WM831X_IRQ_UV_DC4,
.flags = IORESOURCE_IRQ,
},
};
static struct resource wm831x_gpio_resources[] = {
{
.start = WM831X_IRQ_GPIO_1,
@ -793,6 +848,9 @@ static struct resource wm831x_wdt_resources[] = {
};
static struct mfd_cell wm8310_devs[] = {
{
.name = "wm831x-backup",
},
{
.name = "wm831x-buckv",
.id = 1,
@ -943,9 +1001,19 @@ static struct mfd_cell wm8310_devs[] = {
.num_resources = ARRAY_SIZE(wm831x_wdt_resources),
.resources = wm831x_wdt_resources,
},
#if defined(CONFIG_KEYBOARD_WM831X_GPIO)
{
.name = "wm831x_gpio-keys",
.num_resources = 0,
},
#endif
};
static struct mfd_cell wm8311_devs[] = {
{
.name = "wm831x-backup",
},
{
.name = "wm831x-buckv",
.id = 1,
@ -1080,6 +1148,9 @@ static struct mfd_cell wm8311_devs[] = {
};
static struct mfd_cell wm8312_devs[] = {
{
.name = "wm831x-backup",
},
{
.name = "wm831x-buckv",
.id = 1,
@ -1237,6 +1308,137 @@ static struct mfd_cell wm8312_devs[] = {
},
};
static struct mfd_cell wm8320_devs[] = {
{
.name = "wm831x-backup",
},
{
.name = "wm831x-buckv",
.id = 1,
.num_resources = ARRAY_SIZE(wm831x_dcdc1_resources),
.resources = wm831x_dcdc1_resources,
},
{
.name = "wm831x-buckv",
.id = 2,
.num_resources = ARRAY_SIZE(wm831x_dcdc2_resources),
.resources = wm831x_dcdc2_resources,
},
{
.name = "wm831x-buckp",
.id = 3,
.num_resources = ARRAY_SIZE(wm831x_dcdc3_resources),
.resources = wm831x_dcdc3_resources,
},
{
.name = "wm831x-buckp",
.id = 4,
.num_resources = ARRAY_SIZE(wm8320_dcdc4_buck_resources),
.resources = wm8320_dcdc4_buck_resources,
},
{
.name = "wm831x-gpio",
.num_resources = ARRAY_SIZE(wm831x_gpio_resources),
.resources = wm831x_gpio_resources,
},
{
.name = "wm831x-hwmon",
},
{
.name = "wm831x-ldo",
.id = 1,
.num_resources = ARRAY_SIZE(wm831x_ldo1_resources),
.resources = wm831x_ldo1_resources,
},
{
.name = "wm831x-ldo",
.id = 2,
.num_resources = ARRAY_SIZE(wm831x_ldo2_resources),
.resources = wm831x_ldo2_resources,
},
{
.name = "wm831x-ldo",
.id = 3,
.num_resources = ARRAY_SIZE(wm831x_ldo3_resources),
.resources = wm831x_ldo3_resources,
},
{
.name = "wm831x-ldo",
.id = 4,
.num_resources = ARRAY_SIZE(wm831x_ldo4_resources),
.resources = wm831x_ldo4_resources,
},
{
.name = "wm831x-ldo",
.id = 5,
.num_resources = ARRAY_SIZE(wm831x_ldo5_resources),
.resources = wm831x_ldo5_resources,
},
{
.name = "wm831x-ldo",
.id = 6,
.num_resources = ARRAY_SIZE(wm831x_ldo6_resources),
.resources = wm831x_ldo6_resources,
},
{
.name = "wm831x-aldo",
.id = 7,
.num_resources = ARRAY_SIZE(wm831x_ldo7_resources),
.resources = wm831x_ldo7_resources,
},
{
.name = "wm831x-aldo",
.id = 8,
.num_resources = ARRAY_SIZE(wm831x_ldo8_resources),
.resources = wm831x_ldo8_resources,
},
{
.name = "wm831x-aldo",
.id = 9,
.num_resources = ARRAY_SIZE(wm831x_ldo9_resources),
.resources = wm831x_ldo9_resources,
},
{
.name = "wm831x-aldo",
.id = 10,
.num_resources = ARRAY_SIZE(wm831x_ldo10_resources),
.resources = wm831x_ldo10_resources,
},
{
.name = "wm831x-alive-ldo",
.id = 11,
.num_resources = ARRAY_SIZE(wm831x_ldo11_resources),
.resources = wm831x_ldo11_resources,
},
{
.name = "wm831x-on",
.num_resources = ARRAY_SIZE(wm831x_on_resources),
.resources = wm831x_on_resources,
},
{
.name = "wm831x-rtc",
.num_resources = ARRAY_SIZE(wm831x_rtc_resources),
.resources = wm831x_rtc_resources,
},
{
.name = "wm831x-status",
.id = 1,
.num_resources = ARRAY_SIZE(wm831x_status1_resources),
.resources = wm831x_status1_resources,
},
{
.name = "wm831x-status",
.id = 2,
.num_resources = ARRAY_SIZE(wm831x_status2_resources),
.resources = wm831x_status2_resources,
},
{
.name = "wm831x-watchdog",
.num_resources = ARRAY_SIZE(wm831x_wdt_resources),
.resources = wm831x_wdt_resources,
},
};
static struct mfd_cell backlight_devs[] = {
{
.name = "wm831x-backlight",
@ -1246,7 +1448,7 @@ static struct mfd_cell backlight_devs[] = {
/*
* Instantiate the generic non-control parts of the device.
*/
static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
{
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
int rev;
@ -1256,6 +1458,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
mutex_init(&wm831x->io_lock);
mutex_init(&wm831x->key_lock);
mutex_init(&wm831x->auxadc_lock);
init_completion(&wm831x->auxadc_done);
dev_set_drvdata(wm831x->dev, wm831x);
ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID);
@ -1282,50 +1485,70 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
goto err;
}
/* Some engineering samples do not have the ID set, rely on
* the device being registered correctly.
*/
if (ret == 0) {
dev_info(wm831x->dev, "Device is an engineering sample\n");
ret = id;
}
switch (ret) {
case 0x8310:
case WM8310:
parent = WM8310;
switch (rev) {
case 0:
dev_info(wm831x->dev, "WM8310 revision %c\n",
'A' + rev);
break;
wm831x->num_gpio = 12;
wm831x->charger_irq_wake = 1;
if (rev > 0) {
wm831x->has_gpio_ena = 1;
wm831x->has_cs_sts = 1;
}
//ILIM = 900ma
ret = wm831x_reg_read(wm831x, WM831X_POWER_STATE) & 0xffff;
wm831x_reg_write(wm831x, WM831X_POWER_STATE, (ret&0xfff8) | 0x04);
dev_info(wm831x->dev, "WM8310 revision %c\n", 'A' + rev);
break;
case 0x8311:
case WM8311:
parent = WM8311;
switch (rev) {
case 0:
dev_info(wm831x->dev, "WM8311 revision %c\n",
'A' + rev);
break;
wm831x->num_gpio = 16;
wm831x->charger_irq_wake = 1;
if (rev > 0) {
wm831x->has_gpio_ena = 1;
wm831x->has_cs_sts = 1;
}
dev_info(wm831x->dev, "WM8311 revision %c\n", 'A' + rev);
break;
case 0x8312:
case WM8312:
parent = WM8312;
switch (rev) {
case 0:
dev_info(wm831x->dev, "WM8312 revision %c\n",
'A' + rev);
break;
wm831x->num_gpio = 16;
wm831x->charger_irq_wake = 1;
if (rev > 0) {
wm831x->has_gpio_ena = 1;
wm831x->has_cs_sts = 1;
}
dev_info(wm831x->dev, "WM8312 revision %c\n", 'A' + rev);
break;
case 0:
/* Some engineering samples do not have the ID set,
* rely on the device being registered correctly.
* This will need revisiting for future devices with
* multiple dies.
*/
parent = id;
switch (rev) {
case 0:
dev_info(wm831x->dev, "WM831%d ES revision %c\n",
parent, 'A' + rev);
break;
}
case WM8320:
parent = WM8320;
wm831x->num_gpio = 12;
dev_info(wm831x->dev, "WM8320 revision %c\n", 'A' + rev);
break;
case WM8321:
parent = WM8321;
wm831x->num_gpio = 12;
dev_info(wm831x->dev, "WM8321 revision %c\n", 'A' + rev);
break;
case WM8325:
parent = WM8325;
wm831x->num_gpio = 12;
dev_info(wm831x->dev, "WM8325 revision %c\n", 'A' + rev);
break;
default:
@ -1338,7 +1561,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
* current parts.
*/
if (parent != id)
dev_warn(wm831x->dev, "Device was registered as a WM831%lu\n",
dev_warn(wm831x->dev, "Device was registered as a WM%lx\n",
id);
/* Bootstrap the user key */
@ -1366,23 +1589,51 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
if (ret != 0)
goto err;
if (wm831x->irq_base) {
ret = request_threaded_irq(wm831x->irq_base +
WM831X_IRQ_AUXADC_DATA,
NULL, wm831x_auxadc_irq, 0,
"auxadc", wm831x);
if (ret < 0)
dev_err(wm831x->dev, "AUXADC IRQ request failed: %d\n",
ret);
}
/* The core device is up, instantiate the subdevices. */
switch (parent) {
case WM8310:
ret = mfd_add_devices(wm831x->dev, -1,
wm8310_devs, ARRAY_SIZE(wm8310_devs),
NULL, 0);
NULL, wm831x->irq_base);
break;
case WM8311:
ret = mfd_add_devices(wm831x->dev, -1,
wm8311_devs, ARRAY_SIZE(wm8311_devs),
NULL, 0);
NULL, wm831x->irq_base);
break;
case WM8312:
ret = mfd_add_devices(wm831x->dev, -1,
wm8312_devs, ARRAY_SIZE(wm8312_devs),
NULL, wm831x->irq_base);
break;
case WM8320:
ret = mfd_add_devices(wm831x->dev, -1,
wm8320_devs, ARRAY_SIZE(wm8320_devs),
NULL, 0);
break;
case WM8321:
ret = mfd_add_devices(wm831x->dev, -1,
wm8320_devs, ARRAY_SIZE(wm8320_devs),
NULL, 0);
break;
case WM8325:
ret = mfd_add_devices(wm831x->dev, -1,
wm8320_devs, ARRAY_SIZE(wm8320_devs),
NULL, 0);
break;
@ -1399,7 +1650,8 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
if (pdata && pdata->backlight) {
/* Treat errors as non-critical */
ret = mfd_add_devices(wm831x->dev, -1, backlight_devs,
ARRAY_SIZE(backlight_devs), NULL, 0);
ARRAY_SIZE(backlight_devs), NULL,
wm831x->irq_base);
if (ret < 0)
dev_err(wm831x->dev, "Failed to add backlight: %d\n",
ret);
@ -1408,7 +1660,11 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
wm831x_otp_init(wm831x);
if (pdata && pdata->post_init) {
wm831x_reg_unlock(wm831x);
wm831x_set_bits(wm831x, WM831X_RESET_CONTROL,0x0010,0x0000);
wm831x_set_bits(wm831x, WM831X_LDO_ENABLE,0Xf800,0Xf800);
ret = pdata->post_init(wm831x);
wm831x_reg_lock(wm831x);
if (ret != 0) {
dev_err(wm831x->dev, "post_init() failed: %d\n", ret);
goto err_irq;
@ -1425,125 +1681,130 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
return ret;
}
static void wm831x_device_exit(struct wm831x *wm831x)
void wm831x_device_exit(struct wm831x *wm831x)
{
wm831x_otp_exit(wm831x);
mfd_remove_devices(wm831x->dev);
if (wm831x->irq_base)
free_irq(wm831x->irq_base + WM831X_IRQ_AUXADC_DATA, wm831x);
wm831x_irq_exit(wm831x);
kfree(wm831x);
}
static int wm831x_i2c_read_device(struct wm831x *wm831x, unsigned short reg,
int bytes, void *dest)
int wm831x_device_suspend(struct wm831x *wm831x)
{
struct i2c_client *i2c = wm831x->control_data;
int ret;
u16 r = cpu_to_be16(reg);
int reg, mask;
int i;
//mask some intterupt avoid wakeing up system while suspending
for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) {
/* If there's been a change in the mask write it back
* to the hardware. */
//printk("irq_masks_cur[%d]=0x%x\n",i,wm831x->irq_masks_cur[i]);
ret = i2c_master_send(i2c, (unsigned char *)&r, 2);
if (ret < 0)
return ret;
if (ret != 2)
return -EIO;
ret = i2c_master_recv(i2c, dest, bytes);
if (ret < 0)
return ret;
if (ret != bytes)
return -EIO;
return 0;
}
/* Currently we allocate the write buffer on the stack; this is OK for
* small writes - if we need to do large writes this will need to be
* revised.
*/
static int wm831x_i2c_write_device(struct wm831x *wm831x, unsigned short reg,
int bytes, void *src)
{
struct i2c_client *i2c = wm831x->control_data;
unsigned char msg[bytes + 2];
int ret;
reg = cpu_to_be16(reg);
memcpy(&msg[0], &reg, 2);
memcpy(&msg[2], src, bytes);
ret = i2c_master_send(i2c, msg, bytes + 2);
if (ret < 0)
return ret;
if (ret < bytes + 2)
return -EIO;
return 0;
}
static int wm831x_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct wm831x *wm831x;
wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL);
if (wm831x == NULL) {
kfree(i2c);
return -ENOMEM;
if (wm831x->irq_masks_cur[i] != wm831x->irq_masks_cache[i]) {
wm831x->irq_masks_cache[i] = wm831x->irq_masks_cur[i];
wm831x_reg_write(wm831x,
WM831X_INTERRUPT_STATUS_1_MASK + i,
wm831x->irq_masks_cur[i]);
}
}
i2c_set_clientdata(i2c, wm831x);
wm831x->dev = &i2c->dev;
wm831x->control_data = i2c;
wm831x->read_dev = wm831x_i2c_read_device;
wm831x->write_dev = wm831x_i2c_write_device;
/* If the charger IRQs are a wake source then make sure we ack
* them even if they're not actively being used (eg, no power
* driver or no IRQ line wired up) then acknowledge the
* interrupts otherwise suspend won't last very long.
*/
if (wm831x->charger_irq_wake) {
reg = wm831x_reg_read(wm831x, WM831X_INTERRUPT_STATUS_2_MASK);
return wm831x_device_init(wm831x, id->driver_data, i2c->irq);
}
mask = WM831X_CHG_BATT_HOT_EINT |
WM831X_CHG_BATT_COLD_EINT |
WM831X_CHG_BATT_FAIL_EINT |
WM831X_CHG_OV_EINT | WM831X_CHG_END_EINT |
WM831X_CHG_TO_EINT | WM831X_CHG_MODE_EINT |
WM831X_CHG_START_EINT;
static int wm831x_i2c_remove(struct i2c_client *i2c)
{
struct wm831x *wm831x = i2c_get_clientdata(i2c);
/* If any of the interrupts are masked read the statuses */
if (reg & mask)
reg = wm831x_reg_read(wm831x,
WM831X_INTERRUPT_STATUS_2);
wm831x_device_exit(wm831x);
if (reg & mask) {
dev_info(wm831x->dev,
"Acknowledging masked charger IRQs: %x\n",
reg & mask);
wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_2,
reg & mask);
}
}
return 0;
}
static const struct i2c_device_id wm831x_i2c_id[] = {
{ "wm8310", WM8310 },
{ "wm8311", WM8311 },
{ "wm8312", WM8312 },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm831x_i2c_id);
static struct i2c_driver wm831x_i2c_driver = {
.driver = {
.name = "wm831x",
.owner = THIS_MODULE,
},
.probe = wm831x_i2c_probe,
.remove = wm831x_i2c_remove,
.id_table = wm831x_i2c_id,
};
static int __init wm831x_i2c_init(void)
{
int ret;
ret = i2c_add_driver(&wm831x_i2c_driver);
if (ret != 0)
pr_err("Failed to register wm831x I2C driver: %d\n", ret);
return ret;
void wm831x_enter_sleep(void){
#if 1//def CONFIG_RK2818_SOC_PM
struct regulator *dcdc;
int i;
dcdc=regulator_get(NULL, "dcdc1");
struct wm831x_dcdc *dc = regulator_get_drvdata(dcdc);
struct wm831x *wm831x = dc->wm831x;
if(wm831x){
wm831x_set_bits(wm831x, WM831X_POWER_STATE, 0x4000, 0x4000); // SYSTEM SLEEP MODE
for (i=0; i<5; i++)
wm831x_reg_write(wm831x,WM831X_INTERRUPT_STATUS_1+i, 0xffff); // INTRUPT FLAG CLEAR
printk("%s:complete! \n",__func__);
}else{
printk("%s:error!",__func__);
}
regulator_put(dcdc);
#endif
}
subsys_initcall(wm831x_i2c_init);
EXPORT_SYMBOL_GPL(wm831x_enter_sleep);
static void __exit wm831x_i2c_exit(void)
{
i2c_del_driver(&wm831x_i2c_driver);
void wm831x_exit_sleep(void){
#if 1//def CONFIG_RK2818_SOC_PM
struct regulator *dcdc;
dcdc=regulator_get(NULL, "dcdc1");
struct wm831x_dcdc *dc = regulator_get_drvdata(dcdc);
struct wm831x *wm831x = dc->wm831x;
if(wm831x){
wm831x_set_bits(wm831x, WM831X_POWER_STATE, 0x4000, 0); // SYSTEM ON MODE
printk("%s:complete! \n",__func__);
}else{
printk("%s:error!",__func__);
}
regulator_put(dcdc);
#endif
}
module_exit(wm831x_i2c_exit);
EXPORT_SYMBOL_GPL(wm831x_exit_sleep);
MODULE_DESCRIPTION("I2C support for the WM831X AudioPlus PMIC");
int wm831x_device_shutdown(struct wm831x *wm831x)
{
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
int ret = 0;
printk("pre WM831X_POWER_STATE = 0x%x\n", wm831x_reg_read(wm831x, WM831X_POWER_STATE));
if (pdata && pdata->last_deinit) {
ret = pdata->last_deinit(wm831x);
if (ret != 0) {
dev_info(wm831x->dev, "last_deinit() failed: %d\n", ret);
//goto err_irq;
}
}
if(0 == reboot_cmd_get())
{
if(wm831x_set_bits(wm831x, WM831X_POWER_STATE, WM831X_CHIP_ON_MASK, 0) < 0)
printk("%s wm831x_set_bits err\n", __FUNCTION__);
printk("post WM831X_POWER_STATE = 0x%x\n", wm831x_reg_read(wm831x, WM831X_POWER_STATE));
}
return 0;
}
MODULE_DESCRIPTION("Core support for the WM831X AudioPlus PMIC");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mark Brown");

188
drivers/mfd/wm831x-i2c.c Executable file
View File

@ -0,0 +1,188 @@
/*
* wm831x-i2c.c -- I2C access for Wolfson WM831x PMICs
*
* Copyright 2009,2010 Wolfson Microelectronics PLC.
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/mfd/core.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/mfd/wm831x/core.h>
#include <linux/mfd/wm831x/pdata.h>
static int wm831x_i2c_read_device(struct wm831x *wm831x, unsigned short reg,
int bytes, void *dest)
{
struct i2c_client *i2c = wm831x->control_data;
int ret;
u16 r = cpu_to_be16(reg);
ret = i2c_master_send(i2c, (unsigned char *)&r, 2);
if (ret < 0)
return ret;
if (ret != 2)
return -EIO;
ret = i2c_master_recv(i2c, dest, bytes);
if (ret < 0)
return ret;
if (ret != bytes)
return -EIO;
return 0;
}
/* Currently we allocate the write buffer on the stack; this is OK for
* small writes - if we need to do large writes this will need to be
* revised.
*/
static int wm831x_i2c_write_device(struct wm831x *wm831x, unsigned short reg,
int bytes, void *src)
{
struct i2c_client *i2c = wm831x->control_data;
unsigned char msg[bytes + 2];
int ret;
reg = cpu_to_be16(reg);
memcpy(&msg[0], &reg, 2);
memcpy(&msg[2], src, bytes);
ret = i2c_master_send(i2c, msg, bytes + 2);
if (ret < 0)
return ret;
if (ret < bytes + 2)
return -EIO;
return 0;
}
static int wm831x_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct wm831x *wm831x;
int ret,gpio,irq;
wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL);
if (wm831x == NULL)
return -ENOMEM;
i2c_set_clientdata(i2c, wm831x);
gpio = i2c->irq;
ret = gpio_request(gpio, "wm831x");
if (ret) {
printk( "failed to request rk gpio irq for wm831x \n");
return ret;
}
gpio_pull_updown(gpio, GPIOPullUp);
if (ret) {
printk("failed to pull up gpio irq for wm831x \n");
return ret;
}
irq = gpio_to_irq(gpio);
wm831x->dev = &i2c->dev;
wm831x->control_data = i2c;
wm831x->read_dev = wm831x_i2c_read_device;
wm831x->write_dev = wm831x_i2c_write_device;
return wm831x_device_init(wm831x, id->driver_data, irq);
}
static int wm831x_i2c_remove(struct i2c_client *i2c)
{
struct wm831x *wm831x = i2c_get_clientdata(i2c);
wm831x_device_exit(wm831x);
return 0;
}
static int wm831x_i2c_suspend(struct i2c_client *i2c, pm_message_t mesg)
{
struct wm831x *wm831x = i2c_get_clientdata(i2c);
return wm831x_device_suspend(wm831x);
}
static int wm831x_i2c_resume(struct i2c_client *i2c)
{
struct wm831x *wm831x = i2c_get_clientdata(i2c);
int i;
//set some intterupt again while resume
for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) {
//printk("irq_masks_cur[%d]=0x%x\n",i,wm831x->irq_masks_cur[i]);
if (wm831x->irq_masks_cur[i] != wm831x->irq_masks_cache[i]) {
wm831x->irq_masks_cache[i] = wm831x->irq_masks_cur[i];
wm831x_reg_write(wm831x,
WM831X_INTERRUPT_STATUS_1_MASK + i,
wm831x->irq_masks_cur[i]);
}
}
return 0;
}
void wm831x_i2c_shutdown(struct i2c_client *i2c)
{
struct wm831x *wm831x = i2c_get_clientdata(i2c);
printk("%s\n", __FUNCTION__);
wm831x_device_shutdown(wm831x);
}
static const struct i2c_device_id wm831x_i2c_id[] = {
{ "wm8310", WM8310 },
{ "wm8311", WM8311 },
{ "wm8312", WM8312 },
{ "wm8320", WM8320 },
{ "wm8321", WM8321 },
{ "wm8325", WM8325 },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm831x_i2c_id);
static struct i2c_driver wm831x_i2c_driver = {
.driver = {
.name = "wm831x",
.owner = THIS_MODULE,
},
.probe = wm831x_i2c_probe,
.remove = wm831x_i2c_remove,
.suspend = wm831x_i2c_suspend,
.resume = wm831x_i2c_resume,
.shutdown = wm831x_i2c_shutdown,
.id_table = wm831x_i2c_id,
};
static int __init wm831x_i2c_init(void)
{
int ret;
printk("%s \n", __FUNCTION__);
ret = i2c_add_driver(&wm831x_i2c_driver);
if (ret != 0)
pr_err("Failed to register wm831x I2C driver: %d\n", ret);
return ret;
}
subsys_initcall(wm831x_i2c_init);
static void __exit wm831x_i2c_exit(void)
{
i2c_del_driver(&wm831x_i2c_driver);
}
module_exit(wm831x_i2c_exit);

363
drivers/mfd/wm831x-irq.c Normal file → Executable file
View File

@ -15,15 +15,17 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/irq.h>
#include <linux/mfd/core.h>
#include <linux/interrupt.h>
#include <linux/mfd/wm831x/core.h>
#include <linux/mfd/wm831x/pdata.h>
#include <linux/mfd/wm831x/gpio.h>
#include <linux/mfd/wm831x/irq.h>
#include <linux/delay.h>
#include <linux/wakelock.h>
/*
* Since generic IRQs don't currently support interrupt controllers on
* interrupt driven buses we don't use genirq but instead provide an
@ -32,13 +34,18 @@
* the static irq_data table we use to look up the data for individual
* interrupts, but hopefully won't last too long.
*/
#define WM831X_IRQ_TYPE IRQF_TRIGGER_LOW
struct wm831x_irq_data {
int primary;
int reg;
int mask;
irq_handler_t handler;
void *handler_data;
};
struct wm831x_handle_irq
{
int irq;
struct list_head queue;
};
static struct wm831x_irq_data wm831x_irqs[] = {
@ -339,101 +346,151 @@ static inline int irq_data_to_mask_reg(struct wm831x_irq_data *irq_data)
return WM831X_INTERRUPT_STATUS_1_MASK - 1 + irq_data->reg;
}
static void __wm831x_enable_irq(struct wm831x *wm831x, int irq)
static inline struct wm831x_irq_data *irq_to_wm831x_irq(struct wm831x *wm831x,
int irq)
{
struct wm831x_irq_data *irq_data = &wm831x_irqs[irq];
wm831x->irq_masks[irq_data->reg - 1] &= ~irq_data->mask;
wm831x_reg_write(wm831x, irq_data_to_mask_reg(irq_data),
wm831x->irq_masks[irq_data->reg - 1]);
return &wm831x_irqs[irq - wm831x->irq_base];
}
void wm831x_enable_irq(struct wm831x *wm831x, int irq)
static void wm831x_irq_lock(unsigned int irq)
{
struct wm831x *wm831x = get_irq_chip_data(irq);
mutex_lock(&wm831x->irq_lock);
__wm831x_enable_irq(wm831x, irq);
}
static void wm831x_irq_sync_unlock(unsigned int irq)
{
struct wm831x *wm831x = get_irq_chip_data(irq);
int i;
for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) {
/* If there's been a change in the mask write it back
* to the hardware. */
if (wm831x->irq_masks_cur[i] != wm831x->irq_masks_cache[i]) {
wm831x->irq_masks_cache[i] = wm831x->irq_masks_cur[i];
wm831x_reg_write(wm831x,
WM831X_INTERRUPT_STATUS_1_MASK + i,
wm831x->irq_masks_cur[i]);
}
}
mutex_unlock(&wm831x->irq_lock);
}
EXPORT_SYMBOL_GPL(wm831x_enable_irq);
static void __wm831x_disable_irq(struct wm831x *wm831x, int irq)
static void wm831x_irq_unmask(unsigned int irq)
{
struct wm831x_irq_data *irq_data = &wm831x_irqs[irq];
struct wm831x *wm831x = get_irq_chip_data(irq);
struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x, irq);
wm831x->irq_masks[irq_data->reg - 1] |= irq_data->mask;
wm831x_reg_write(wm831x, irq_data_to_mask_reg(irq_data),
wm831x->irq_masks[irq_data->reg - 1]);
wm831x->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
//printk("%s:irq=%d\n",__FUNCTION__,irq);
}
void wm831x_disable_irq(struct wm831x *wm831x, int irq)
static void wm831x_irq_mask(unsigned int irq)
{
mutex_lock(&wm831x->irq_lock);
__wm831x_disable_irq(wm831x, irq);
mutex_unlock(&wm831x->irq_lock);
struct wm831x *wm831x = get_irq_chip_data(irq);
struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x, irq);
wm831x->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
//printk("%s:irq=%d\n",__FUNCTION__,irq);
}
EXPORT_SYMBOL_GPL(wm831x_disable_irq);
int wm831x_request_irq(struct wm831x *wm831x,
unsigned int irq, irq_handler_t handler,
unsigned long flags, const char *name,
void *dev)
static int wm831x_irq_set_type(unsigned int irq, unsigned int type)
{
int ret = 0;
struct wm831x *wm831x = get_irq_chip_data(irq);
int val;
if (irq < 0 || irq >= WM831X_NUM_IRQS)
irq = irq - wm831x->irq_base;
if (irq < WM831X_IRQ_GPIO_1 || irq > WM831X_IRQ_GPIO_12) {
/* Ignore internal-only IRQs */
if (irq >= 0 && irq < WM831X_NUM_IRQS)
return 0;
else
return -EINVAL;
}
//printk("wm831x_irq_set_type:type=%x,irq=%d\n",type,irq);
switch (type) {
case IRQ_TYPE_EDGE_BOTH:
val = WM831X_GPN_INT_MODE;
break;
case IRQ_TYPE_EDGE_RISING:
val = WM831X_GPN_POL;
break;
case IRQ_TYPE_EDGE_FALLING:
val = 0;
break;
default:
return -EINVAL;
mutex_lock(&wm831x->irq_lock);
if (wm831x_irqs[irq].handler) {
dev_err(wm831x->dev, "Already have handler for IRQ %d\n", irq);
ret = -EINVAL;
goto out;
}
wm831x_irqs[irq].handler = handler;
wm831x_irqs[irq].handler_data = dev;
__wm831x_enable_irq(wm831x, irq);
out:
mutex_unlock(&wm831x->irq_lock);
return ret;
return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + irq - 1,
WM831X_GPN_INT_MODE | WM831X_GPN_POL, val);
}
EXPORT_SYMBOL_GPL(wm831x_request_irq);
void wm831x_free_irq(struct wm831x *wm831x, unsigned int irq, void *data)
{
if (irq < 0 || irq >= WM831X_NUM_IRQS)
return;
static int wm831x_irq_set_wake(unsigned irq, unsigned state)
{
struct wm831x *wm831x = get_irq_chip_data(irq);
mutex_lock(&wm831x->irq_lock);
//only wm831x irq
if ((irq > wm831x->irq_base + WM831X_IRQ_TEMP_THW) &&( irq < wm831x->irq_base + WM831X_NUM_IRQS))
{
if(state)
wm831x_irq_unmask(irq);
else
wm831x_irq_mask(irq);
return 0;
}
else
{
printk("%s:irq number err!irq=%d\n",__FUNCTION__,irq);
return -EINVAL;
}
wm831x_irqs[irq].handler = NULL;
wm831x_irqs[irq].handler_data = NULL;
__wm831x_disable_irq(wm831x, irq);
mutex_unlock(&wm831x->irq_lock);
}
EXPORT_SYMBOL_GPL(wm831x_free_irq);
static struct irq_chip wm831x_irq_chip = {
.name = "wm831x",
.bus_lock = wm831x_irq_lock,
.bus_sync_unlock = wm831x_irq_sync_unlock,
.mask = wm831x_irq_mask,
.unmask = wm831x_irq_unmask,
.set_type = wm831x_irq_set_type,
.set_wake = wm831x_irq_set_wake,
};
static void wm831x_handle_irq(struct wm831x *wm831x, int irq, int status)
#if WM831X_IRQ_LIST
static void wm831x_handle_worker(struct work_struct *work)
{
struct wm831x_irq_data *irq_data = &wm831x_irqs[irq];
struct wm831x *wm831x = container_of(work, struct wm831x, handle_work);
int irq;
while (1) {
unsigned long flags;
struct wm831x_handle_irq *hd = NULL;
spin_lock_irqsave(&wm831x->work_lock, flags);
if (!list_empty(&wm831x->handle_queue)) {
hd = list_first_entry(&wm831x->handle_queue, struct wm831x_handle_irq, queue);
list_del(&hd->queue);
}
spin_unlock_irqrestore(&wm831x->work_lock, flags);
if (!hd) // trans_queue empty
break;
irq = hd->irq; //get wm831x intterupt status
//printk("%s:irq=%d\n",__FUNCTION__,irq);
/*start to handle wm831x intterupt*/
handle_nested_irq(wm831x->irq_base + irq);
kfree(hd);
if (irq_data->handler) {
irq_data->handler(irq, irq_data->handler_data);
wm831x_reg_write(wm831x, irq_data_to_status_reg(irq_data),
irq_data->mask);
} else {
dev_err(wm831x->dev, "Unhandled IRQ %d, masking\n", irq);
__wm831x_disable_irq(wm831x, irq);
}
}
#endif
/* Main interrupt handling occurs in a workqueue since we need
* interrupts enabled to interact with the chip. */
static void wm831x_irq_worker(struct work_struct *work)
@ -441,25 +498,37 @@ static void wm831x_irq_worker(struct work_struct *work)
struct wm831x *wm831x = container_of(work, struct wm831x, irq_work);
unsigned int i;
int primary;
int status_regs[5];
int read[5] = { 0 };
int status_regs[WM831X_NUM_IRQ_REGS] = { 0 };
int read[WM831X_NUM_IRQ_REGS] = { 0 };
int *status;
unsigned long flags;
struct wm831x_handle_irq *hd;
int ret;
msleep(2);
#if (WM831X_IRQ_TYPE != IRQF_TRIGGER_LOW)
/*mask wm831x irq at first*/
ret = wm831x_set_bits(wm831x, WM831X_IRQ_CONFIG,
WM831X_IRQ_IM_MASK, WM831X_IRQ_IM_EANBLE);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to mask irq: %d\n", ret);
goto out;
}
#endif
primary = wm831x_reg_read(wm831x, WM831X_SYSTEM_INTERRUPTS);
if (primary < 0) {
dev_err(wm831x->dev, "Failed to read system interrupt: %d\n",
primary);
goto out;
}
mutex_lock(&wm831x->irq_lock);
for (i = 0; i < ARRAY_SIZE(wm831x_irqs); i++) {
int offset = wm831x_irqs[i].reg - 1;
if (!(primary & wm831x_irqs[i].primary))
continue;
status = &status_regs[offset];
/* Hopefully there should only be one register to read
@ -474,47 +543,108 @@ static void wm831x_irq_worker(struct work_struct *work)
goto out_lock;
}
/* Mask out the disabled IRQs */
*status &= ~wm831x->irq_masks[offset];
read[offset] = 1;
}
if (*status & wm831x_irqs[i].mask)
wm831x_handle_irq(wm831x, i, *status);
/* Report it if it isn't masked, or forget the status. */
if ((*status & ~wm831x->irq_masks_cur[offset])
& wm831x_irqs[i].mask)
{
#if WM831X_IRQ_LIST
/*add intterupt handle on list*/
hd = kzalloc(sizeof(struct wm831x_handle_irq), GFP_KERNEL);
if (!hd)
{
printk("err:%s:ENOMEM\n",__FUNCTION__);
return ;
}
if(i == WM831X_IRQ_ON)
wake_lock(&wm831x->handle_wake); //keep wake while handle WM831X_IRQ_ON
hd->irq = i;
spin_lock_irqsave(&wm831x->work_lock, flags);
list_add_tail(&hd->queue, &wm831x->handle_queue);
spin_unlock_irqrestore(&wm831x->work_lock, flags);
queue_work(wm831x->handle_wq, &wm831x->handle_work);
#else
if(i == WM831X_IRQ_ON)
wake_lock(&wm831x->handle_wake); //keep wake while handle WM831X_IRQ_ON
handle_nested_irq(wm831x->irq_base + i);
#endif
}
else
*status &= ~wm831x_irqs[i].mask;
}
out_lock:
out_lock:
mutex_unlock(&wm831x->irq_lock);
out:
enable_irq(wm831x->irq);
for (i = 0; i < ARRAY_SIZE(status_regs); i++) {
if (status_regs[i])
wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1 + i,
status_regs[i]);
}
#if (WM831X_IRQ_TYPE != IRQF_TRIGGER_LOW)
ret = wm831x_set_bits(wm831x, WM831X_IRQ_CONFIG,
WM831X_IRQ_IM_MASK, 0);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to open irq: %d\n", ret);
}
#endif
#if (WM831X_IRQ_TYPE == IRQF_TRIGGER_LOW)
enable_irq(wm831x->irq);
#endif
wake_unlock(&wm831x->irq_wake);
}
static irqreturn_t wm831x_cpu_irq(int irq, void *data)
/* The processing of the primary interrupt occurs in a thread so that
* we can interact with the device over I2C or SPI. */
static irqreturn_t wm831x_irq_thread(int irq, void *data)
{
struct wm831x *wm831x = data;
/* Shut the interrupt to the CPU up and schedule the actual
* handler; we can't check that the IRQ is asserted. */
#if (WM831X_IRQ_TYPE == IRQF_TRIGGER_LOW)
disable_irq_nosync(irq);
#endif
wake_lock(&wm831x->irq_wake);
queue_work(wm831x->irq_wq, &wm831x->irq_work);
//printk("%s\n",__FUNCTION__);
return IRQ_HANDLED;
}
int wm831x_irq_init(struct wm831x *wm831x, int irq)
{
int i, ret;
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
int i, cur_irq, ret;
printk( "wm831x_irq_init:irq=%d,%d\n",irq,pdata->irq_base);
mutex_init(&wm831x->irq_lock);
/* Mask the individual interrupt sources */
for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) {
wm831x->irq_masks_cur[i] = 0xffff;
wm831x->irq_masks_cache[i] = 0xffff;
wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1_MASK + i,
0xffff);
}
if (!irq) {
dev_warn(wm831x->dev,
"No interrupt specified - functionality limited\n");
return 0;
}
if (!pdata || !pdata->irq_base) {
dev_err(wm831x->dev,
"No interrupt base specified, no interrupts\n");
return 0;
}
wm831x->irq_wq = create_singlethread_workqueue("wm831x-irq");
if (!wm831x->irq_wq) {
@ -522,34 +652,59 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
return -ESRCH;
}
wm831x->irq = irq;
wm831x->irq_base = pdata->irq_base;
INIT_WORK(&wm831x->irq_work, wm831x_irq_worker);
/* Mask the individual interrupt sources */
for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks); i++) {
wm831x->irq_masks[i] = 0xffff;
wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1_MASK + i,
0xffff);
wake_lock_init(&wm831x->irq_wake, WAKE_LOCK_SUSPEND, "wm831x_irq_wake");
wake_lock_init(&wm831x->handle_wake, WAKE_LOCK_SUSPEND, "wm831x_handle_wake");
#if WM831X_IRQ_LIST
wm831x->handle_wq = create_rt_workqueue("wm831x_handle_wq");
if (!wm831x->handle_wq) {
printk("cannot create workqueue\n");
return -EBUSY;
}
INIT_WORK(&wm831x->handle_work, wm831x_handle_worker);
INIT_LIST_HEAD(&wm831x->handle_queue);
/* Enable top level interrupts, we mask at secondary level */
wm831x_reg_write(wm831x, WM831X_SYSTEM_INTERRUPTS_MASK, 0);
#endif
/* Register them with genirq */
for (cur_irq = wm831x->irq_base;
cur_irq < ARRAY_SIZE(wm831x_irqs) + wm831x->irq_base;
cur_irq++) {
set_irq_chip_data(cur_irq, wm831x);
set_irq_chip_and_handler(cur_irq, &wm831x_irq_chip,
handle_edge_irq);
set_irq_nested_thread(cur_irq, 1);
/* We're good to go. We set IRQF_SHARED since there's a
* chance the driver will interoperate with another driver but
* the need to disable the IRQ while handing via I2C/SPI means
* that this may break and performance will be impacted. If
* this does happen it's a hardware design issue and the only
* other alternative would be polling.
*/
ret = request_irq(irq, wm831x_cpu_irq, IRQF_TRIGGER_LOW | IRQF_SHARED,
"wm831x", wm831x);
/* ARM needs us to explicitly flag the IRQ as valid
* and will set them noprobe when we do so. */
#ifdef CONFIG_ARM
set_irq_flags(cur_irq, IRQF_VALID);
#else
set_irq_noprobe(cur_irq);
#endif
}
#if (WM831X_IRQ_TYPE == IRQF_TRIGGER_LOW)
ret = request_threaded_irq(wm831x->irq, wm831x_irq_thread, NULL,
IRQF_TRIGGER_LOW| IRQF_ONESHOT,//IRQF_TRIGGER_FALLING, //
"wm831x", wm831x);
#else
ret = request_threaded_irq(wm831x->irq, wm831x_irq_thread, NULL,
IRQF_TRIGGER_FALLING, //IRQF_TRIGGER_LOW| IRQF_ONESHOT,//
"wm831x", wm831x);
#endif
if (ret != 0) {
dev_err(wm831x->dev, "Failed to request IRQ %d: %d\n",
irq, ret);
wm831x->irq, ret);
return ret;
}
enable_irq_wake(wm831x->irq); // so wm831x irq can wake up system
/* Enable top level interrupts, we mask at secondary level */
wm831x_reg_write(wm831x, WM831X_SYSTEM_INTERRUPTS_MASK, 0);
return 0;
}

0
drivers/mfd/wm831x-otp.c Normal file → Executable file
View File

232
drivers/mfd/wm831x-spi.c Executable file
View File

@ -0,0 +1,232 @@
/*
* wm831x-spi.c -- SPI access for Wolfson WM831x PMICs
*
* Copyright 2009,2010 Wolfson Microelectronics PLC.
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/mfd/wm831x/core.h>
static int wm831x_spi_read_device(struct wm831x *wm831x, unsigned short reg,
int bytes, void *dest)
{
u16 tx_val;
u16 *d = dest;
int r, ret;
/* Go register at a time */
for (r = reg; r < reg + (bytes / 2); r++) {
tx_val = r | 0x8000;
ret = spi_write_then_read(wm831x->control_data,
(u8 *)&tx_val, 2, (u8 *)d, 2);
if (ret != 0)
return ret;
*d = be16_to_cpu(*d);
d++;
}
return 0;
}
static int wm831x_spi_write_device(struct wm831x *wm831x, unsigned short reg,
int bytes, void *src)
{
struct spi_device *spi = wm831x->control_data;
u16 *s = src;
u16 data[2];
int ret, r;
/* Go register at a time */
for (r = reg; r < reg + (bytes / 2); r++) {
data[0] = r;
data[1] = *s++;
ret = spi_write(spi, (char *)&data, sizeof(data));
if (ret != 0)
return ret;
}
return 0;
}
static int __devinit wm831x_spi_probe(struct spi_device *spi)
{
struct wm831x *wm831x;
enum wm831x_parent type;
/* Currently SPI support for ID tables is unmerged, we're faking it */
if (strcmp(spi->modalias, "wm8310") == 0)
type = WM8310;
else if (strcmp(spi->modalias, "wm8311") == 0)
type = WM8311;
else if (strcmp(spi->modalias, "wm8312") == 0)
type = WM8312;
else if (strcmp(spi->modalias, "wm8320") == 0)
type = WM8320;
else if (strcmp(spi->modalias, "wm8321") == 0)
type = WM8321;
else if (strcmp(spi->modalias, "wm8325") == 0)
type = WM8325;
else {
dev_err(&spi->dev, "Unknown device type\n");
return -EINVAL;
}
wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL);
if (wm831x == NULL)
return -ENOMEM;
spi->bits_per_word = 16;
spi->mode = SPI_MODE_0;
dev_set_drvdata(&spi->dev, wm831x);
wm831x->dev = &spi->dev;
wm831x->control_data = spi;
wm831x->read_dev = wm831x_spi_read_device;
wm831x->write_dev = wm831x_spi_write_device;
return wm831x_device_init(wm831x, type, spi->irq);
}
static int __devexit wm831x_spi_remove(struct spi_device *spi)
{
struct wm831x *wm831x = dev_get_drvdata(&spi->dev);
wm831x_device_exit(wm831x);
return 0;
}
static int wm831x_spi_suspend(struct spi_device *spi, pm_message_t m)
{
struct wm831x *wm831x = dev_get_drvdata(&spi->dev);
return wm831x_device_suspend(wm831x);
}
static struct spi_driver wm8310_spi_driver = {
.driver = {
.name = "wm8310",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = wm831x_spi_probe,
.remove = __devexit_p(wm831x_spi_remove),
.suspend = wm831x_spi_suspend,
};
static struct spi_driver wm8311_spi_driver = {
.driver = {
.name = "wm8311",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = wm831x_spi_probe,
.remove = __devexit_p(wm831x_spi_remove),
.suspend = wm831x_spi_suspend,
};
static struct spi_driver wm8312_spi_driver = {
.driver = {
.name = "wm8312",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = wm831x_spi_probe,
.remove = __devexit_p(wm831x_spi_remove),
.suspend = wm831x_spi_suspend,
};
static struct spi_driver wm8320_spi_driver = {
.driver = {
.name = "wm8320",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = wm831x_spi_probe,
.remove = __devexit_p(wm831x_spi_remove),
.suspend = wm831x_spi_suspend,
};
static struct spi_driver wm8321_spi_driver = {
.driver = {
.name = "wm8321",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = wm831x_spi_probe,
.remove = __devexit_p(wm831x_spi_remove),
.suspend = wm831x_spi_suspend,
};
static struct spi_driver wm8325_spi_driver = {
.driver = {
.name = "wm8325",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = wm831x_spi_probe,
.remove = __devexit_p(wm831x_spi_remove),
.suspend = wm831x_spi_suspend,
};
static int __init wm831x_spi_init(void)
{
int ret;
ret = spi_register_driver(&wm8310_spi_driver);
if (ret != 0)
pr_err("Failed to register WM8310 SPI driver: %d\n", ret);
ret = spi_register_driver(&wm8311_spi_driver);
if (ret != 0)
pr_err("Failed to register WM8311 SPI driver: %d\n", ret);
ret = spi_register_driver(&wm8312_spi_driver);
if (ret != 0)
pr_err("Failed to register WM8312 SPI driver: %d\n", ret);
ret = spi_register_driver(&wm8320_spi_driver);
if (ret != 0)
pr_err("Failed to register WM8320 SPI driver: %d\n", ret);
ret = spi_register_driver(&wm8321_spi_driver);
if (ret != 0)
pr_err("Failed to register WM8321 SPI driver: %d\n", ret);
ret = spi_register_driver(&wm8325_spi_driver);
if (ret != 0)
pr_err("Failed to register WM8325 SPI driver: %d\n", ret);
return 0;
}
subsys_initcall(wm831x_spi_init);
static void __exit wm831x_spi_exit(void)
{
spi_unregister_driver(&wm8325_spi_driver);
spi_unregister_driver(&wm8321_spi_driver);
spi_unregister_driver(&wm8320_spi_driver);
spi_unregister_driver(&wm8312_spi_driver);
spi_unregister_driver(&wm8311_spi_driver);
spi_unregister_driver(&wm8310_spi_driver);
}
module_exit(wm831x_spi_exit);
MODULE_DESCRIPTION("SPI support for WM831x/2x AudioPlus PMICs");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mark Brown");

234
drivers/power/wm831x_backup.c Executable file
View File

@ -0,0 +1,234 @@
/*
* Backup battery driver for Wolfson Microelectronics wm831x PMICs
*
* Copyright 2009 Wolfson Microelectronics PLC.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/slab.h>
#include <linux/mfd/wm831x/core.h>
#include <linux/mfd/wm831x/auxadc.h>
#include <linux/mfd/wm831x/pmu.h>
#include <linux/mfd/wm831x/pdata.h>
struct wm831x_backup {
struct wm831x *wm831x;
struct power_supply backup;
};
static int wm831x_backup_read_voltage(struct wm831x *wm831x,
enum wm831x_auxadc src,
union power_supply_propval *val)
{
int ret;
ret = wm831x_auxadc_read_uv(wm831x, src);
if (ret >= 0)
val->intval = ret;
return ret;
}
/*********************************************************************
* Backup supply properties
*********************************************************************/
static void wm831x_config_backup(struct wm831x *wm831x)
{
struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data;
struct wm831x_backup_pdata *pdata;
int ret, reg;
if (!wm831x_pdata || !wm831x_pdata->backup) {
dev_warn(wm831x->dev,
"No backup battery charger configuration\n");
return;
}
pdata = wm831x_pdata->backup;
reg = 0;
if (pdata->charger_enable)
reg |= WM831X_BKUP_CHG_ENA | WM831X_BKUP_BATT_DET_ENA;
if (pdata->no_constant_voltage)
reg |= WM831X_BKUP_CHG_MODE;
switch (pdata->vlim) {
case 2500:
break;
case 3100:
reg |= WM831X_BKUP_CHG_VLIM;
break;
default:
dev_err(wm831x->dev, "Invalid backup voltage limit %dmV\n",
pdata->vlim);
}
switch (pdata->ilim) {
case 100:
break;
case 200:
reg |= 1;
break;
case 300:
reg |= 2;
break;
case 400:
reg |= 3;
break;
default:
dev_err(wm831x->dev, "Invalid backup current limit %duA\n",
pdata->ilim);
}
ret = wm831x_reg_unlock(wm831x);
if (ret != 0) {
dev_err(wm831x->dev, "Failed to unlock registers: %d\n", ret);
return;
}
ret = wm831x_set_bits(wm831x, WM831X_BACKUP_CHARGER_CONTROL,
WM831X_BKUP_CHG_ENA_MASK |
WM831X_BKUP_CHG_MODE_MASK |
WM831X_BKUP_BATT_DET_ENA_MASK |
WM831X_BKUP_CHG_VLIM_MASK |
WM831X_BKUP_CHG_ILIM_MASK,
reg);
if (ret != 0)
dev_err(wm831x->dev,
"Failed to set backup charger config: %d\n", ret);
wm831x_reg_lock(wm831x);
}
static int wm831x_backup_get_prop(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct wm831x_backup *devdata = dev_get_drvdata(psy->dev->parent);
struct wm831x *wm831x = devdata->wm831x;
int ret = 0;
ret = wm831x_reg_read(wm831x, WM831X_BACKUP_CHARGER_CONTROL);
if (ret < 0)
return ret;
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
if (ret & WM831X_BKUP_CHG_STS)
val->intval = POWER_SUPPLY_STATUS_CHARGING;
else
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
ret = wm831x_backup_read_voltage(wm831x, WM831X_AUX_BKUP_BATT,
val);
break;
case POWER_SUPPLY_PROP_PRESENT:
if (ret & WM831X_BKUP_CHG_STS)
val->intval = 1;
else
val->intval = 0;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static enum power_supply_property wm831x_backup_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_PRESENT,
};
/*********************************************************************
* Initialisation
*********************************************************************/
static __devinit int wm831x_backup_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
struct wm831x_backup *devdata;
struct power_supply *backup;
int ret;
devdata = kzalloc(sizeof(struct wm831x_backup), GFP_KERNEL);
if (devdata == NULL)
return -ENOMEM;
devdata->wm831x = wm831x;
platform_set_drvdata(pdev, devdata);
backup = &devdata->backup;
/* We ignore configuration failures since we can still read
* back the status without enabling the charger (which may
* already be enabled anyway).
*/
wm831x_config_backup(wm831x);
backup->name = "wm831x-backup";
backup->type = POWER_SUPPLY_TYPE_BATTERY;
backup->properties = wm831x_backup_props;
backup->num_properties = ARRAY_SIZE(wm831x_backup_props);
backup->get_property = wm831x_backup_get_prop;
ret = power_supply_register(&pdev->dev, backup);
if (ret)
goto err_kmalloc;
return ret;
err_kmalloc:
kfree(devdata);
return ret;
}
static __devexit int wm831x_backup_remove(struct platform_device *pdev)
{
struct wm831x_backup *devdata = platform_get_drvdata(pdev);
power_supply_unregister(&devdata->backup);
kfree(devdata);
return 0;
}
static struct platform_driver wm831x_backup_driver = {
.probe = wm831x_backup_probe,
.remove = __devexit_p(wm831x_backup_remove),
.driver = {
.name = "wm831x-backup",
},
};
static int __init wm831x_backup_init(void)
{
return platform_driver_register(&wm831x_backup_driver);
}
module_init(wm831x_backup_init);
static void __exit wm831x_backup_exit(void)
{
platform_driver_unregister(&wm831x_backup_driver);
}
module_exit(wm831x_backup_exit);
MODULE_DESCRIPTION("Backup battery charger driver for WM831x PMICs");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:wm831x-backup");

558
drivers/power/wm831x_power.c Normal file → Executable file
View File

@ -12,20 +12,102 @@
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/slab.h>
#include <linux/mfd/wm831x/core.h>
#include <linux/mfd/wm831x/auxadc.h>
#include <linux/mfd/wm831x/pmu.h>
#include <linux/mfd/wm831x/pdata.h>
#define WM831X_DEBUG
#undef WM831X_DEBUG
#ifdef WM831X_DEBUG
#define WM_BATT_DBG(x...) printk(KERN_INFO x)
#else
#define WM_BATT_DBG(x...) do {} while (0)
#endif
#define WM831X_CHG_SYSLO_SHIFT 4
#define WM831X_CHG_SYSOK_SHIFT 0
#define WM831X_CHG_SYSLO_MASK ~(0x7 << 4)
#define WM831X_CHG_SYSOK_MASK ~(0x7 << 0)
#define batt_num 57
static int batt_chg_step_table[batt_num]={
3650,3700,3710,3720,3730,3740,3745,3755,3760, //+200
3770,3790,3805,3820,3830,3840, //+190
3842,3850,3860,3870,3880,3885, //+180
3900,3910,3920,3930,3940,3950,3960,3970,3980,3990, //+170
4000,4010,4020,4030,4040,4050,4070,4090,4095,4100,4105,4110,4115,4120,4125,4130,
4135,4140,4145,4150,4155,4160,4165,4170,4175,4180
};
static int batt_step_table[batt_num]={
3450,3500,3510,3520,3530,3540,3545,3555,3560,
3580,3600,3615,3630,3640,3650,
3660,3670,3680,3690,3700,3710,
3720,3730,3740,3750,3760,3770,3780,3790,3800,3810,
3815,3830,3845,3860,3875,3890,3900,3910,3920,3930,3940,3950,3960,3970,3985,4000,
4005,4010,4015,4020,4030,4040,4050,4060,4070,4180
};
static int batt_disp_table[batt_num]={
0,2,4,6,8,10,12,14,15,
18,20,23,26,28,30,
33,37,40,43,47,50,
52,54,57,60,62,64,66,68,69,70,
72,74,76,78,79,80,81,82,83,84,85,86,87,88,89,90,
91,92,93,94,95,96,97,98,99,100
};
#define TIMER_MS_COUNTS 1000
struct wm_batt_priv_data {
enum power_supply_property online;
enum power_supply_property status;
enum power_supply_property voltage;
enum power_supply_property health;
enum power_supply_property type;
enum power_supply_property temp;
int old_level;
int charging_level;
};
struct wm831x_power {
struct wm831x *wm831x;
struct power_supply wall;
struct power_supply backup;
struct power_supply usb;
struct power_supply battery;
struct work_struct batt_work;
struct timer_list timer;
struct wm_batt_priv_data priv;
int interval;
};
struct wm831x_power *g_wm831x_power;
extern void wm831x_batt_vol_level(struct wm831x_power *power, int batt_val, int *level);
static DEFINE_MUTEX(charging_mutex);
static int g_read_level_cnt = 0;
int wm831x_read_on_pin_status(void)
{
int ret;
if(!g_wm831x_power)
{
printk("err:%s:g_wm831x_power address is 0\n",__FUNCTION__);
return -1;
}
ret = wm831x_reg_read(g_wm831x_power->wm831x, WM831X_ON_PIN_CONTROL);
if (ret < 0)
return ret;
return !(ret & WM831X_ON_PIN_STS) ? 1 : 0;
}
static int wm831x_power_check_online(struct wm831x *wm831x, int supply,
union power_supply_propval *val)
{
@ -43,19 +125,69 @@ static int wm831x_power_check_online(struct wm831x *wm831x, int supply,
return 0;
}
int wm831x_read_chg_status(void)
{
int ret, usb_chg = 0, wall_chg = 0;
if(!g_wm831x_power)
{
printk("err:%s:g_wm831x_power address is 0\n",__FUNCTION__);
return -1;
}
ret = wm831x_reg_read(g_wm831x_power->wm831x, WM831X_SYSTEM_STATUS);
if (ret < 0)
return ret;
if (ret & WM831X_PWR_USB)
usb_chg = 1;
if (ret & WM831X_PWR_WALL)
wall_chg = 1;
return ((usb_chg | wall_chg) ? 1 : 0);
}
static int wm831x_power_read_voltage(struct wm831x *wm831x,
enum wm831x_auxadc src,
union power_supply_propval *val)
{
int ret;
#if 0
int loop = 0, vol_sum = 0;
for (loop = 0; loop < 10; loop++) {
ret = wm831x_auxadc_read_uv(wm831x, src);
if (ret >= 0) {
vol_sum += ret / 1000;
}
}
val->intval = vol_sum / 10;
return val->intval;
#else
ret = wm831x_auxadc_read_uv(wm831x, src);
if (ret >= 0)
val->intval = ret;
return ret;
val->intval = ret / 1000;
return ret ;
#endif
}
int wm831x_read_batt_voltage(void)
{
int ret = 0;
if(!g_wm831x_power)
{
printk("err:%s:g_wm831x_power address is 0\n",__FUNCTION__);
return -1;
}
ret = wm831x_auxadc_read_uv(g_wm831x_power->wm831x, WM831X_AUX_BATT);
return ret / 1000;
}
//EXPORT_SYMBOL_GPL(wm831x_get_batt_voltage);
/*********************************************************************
* WALL Power
*********************************************************************/
@ -190,13 +322,34 @@ static struct chg_map chg_times[] = {
{ 510, 15 << WM831X_CHG_TIME_SHIFT },
};
static struct chg_map chg_syslos[] = {
{ 2800, 0 << WM831X_CHG_SYSLO_SHIFT},
{ 2900, 1 << WM831X_CHG_SYSLO_SHIFT},
{ 3000, 2 << WM831X_CHG_SYSLO_SHIFT},
{ 3100, 3 << WM831X_CHG_SYSLO_SHIFT},
{ 3200, 4 << WM831X_CHG_SYSLO_SHIFT},
{ 3300, 5 << WM831X_CHG_SYSLO_SHIFT},
{ 3400, 6 << WM831X_CHG_SYSLO_SHIFT},
{ 3500, 7 << WM831X_CHG_SYSLO_SHIFT},
};
static struct chg_map chg_sysoks[] = {
{ 2800, 0 << WM831X_CHG_SYSOK_SHIFT},
{ 2900, 1 << WM831X_CHG_SYSOK_SHIFT},
{ 3000, 2 << WM831X_CHG_SYSOK_SHIFT},
{ 3100, 3 << WM831X_CHG_SYSOK_SHIFT},
{ 3200, 4 << WM831X_CHG_SYSOK_SHIFT},
{ 3300, 5 << WM831X_CHG_SYSOK_SHIFT},
{ 3400, 6 << WM831X_CHG_SYSOK_SHIFT},
{ 3500, 7 << WM831X_CHG_SYSOK_SHIFT},
};
static void wm831x_battey_apply_config(struct wm831x *wm831x,
struct chg_map *map, int count, int val,
int *reg, const char *name,
const char *units)
{
int i;
for (i = 0; i < count; i++)
if (val == map[i].val)
break;
@ -213,7 +366,7 @@ static void wm831x_config_battery(struct wm831x *wm831x)
{
struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data;
struct wm831x_battery_pdata *pdata;
int ret, reg1, reg2;
int ret, reg1, reg2, reg3;
if (!wm831x_pdata || !wm831x_pdata->battery) {
dev_warn(wm831x->dev,
@ -225,6 +378,7 @@ static void wm831x_config_battery(struct wm831x *wm831x)
reg1 = 0;
reg2 = 0;
reg3 = 0;
if (!pdata->enable) {
dev_info(wm831x->dev, "Battery charger disabled\n");
@ -258,6 +412,14 @@ static void wm831x_config_battery(struct wm831x *wm831x)
pdata->timeout, &reg2,
"charger timeout", "min");
wm831x_battey_apply_config(wm831x, chg_syslos, ARRAY_SIZE(chg_syslos),
pdata->syslo, &reg3,
"syslo voltage", "mV");
wm831x_battey_apply_config(wm831x, chg_sysoks, ARRAY_SIZE(chg_sysoks),
pdata->sysok, &reg3,
"sysok voltage", "mV");
ret = wm831x_reg_unlock(wm831x);
if (ret != 0) {
dev_err(wm831x->dev, "Failed to unlock registers: %d\n", ret);
@ -267,13 +429,12 @@ static void wm831x_config_battery(struct wm831x *wm831x)
ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_1,
WM831X_CHG_ENA_MASK |
WM831X_CHG_FAST_MASK |
WM831X_CHG_ITERM_MASK |
WM831X_CHG_ITERM_MASK,
reg1);
if (ret != 0)
if (ret != 0) {
dev_err(wm831x->dev, "Failed to set charger control 1: %d\n",
ret);
}
ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_2,
WM831X_CHG_OFF_MSK |
WM831X_CHG_TIME_MASK |
@ -281,9 +442,18 @@ static void wm831x_config_battery(struct wm831x *wm831x)
WM831X_CHG_TRKL_ILIM_MASK |
WM831X_CHG_VSEL_MASK,
reg2);
if (ret != 0)
if (ret != 0) {
dev_err(wm831x->dev, "Failed to set charger control 2: %d\n",
ret);
}
ret = wm831x_set_bits(wm831x, WM831X_SYSVDD_CONTROL,
WM831X_CHG_SYSLO_MASK |
WM831X_CHG_SYSOK_MASK,
reg3);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to set sysvdd control reg: %d\n",ret);
}
wm831x_reg_lock(wm831x);
}
@ -305,6 +475,7 @@ static int wm831x_bat_check_status(struct wm831x *wm831x, int *status)
if (ret < 0)
return ret;
switch (ret & WM831X_CHG_STATE_MASK) {
case WM831X_CHG_STATE_OFF:
*status = POWER_SUPPLY_STATUS_NOT_CHARGING;
@ -321,10 +492,53 @@ static int wm831x_bat_check_status(struct wm831x *wm831x, int *status)
return 0;
}
int wm831x_read_bat_charging_status(void)
{
int ret, status;
if(!g_wm831x_power)
{
printk("err:%s:g_wm831x_power address is 0\n",__FUNCTION__);
return -1;
}
ret = wm831x_bat_check_status(g_wm831x_power->wm831x, &status);
if (ret < 0)
return ret;
if (status == POWER_SUPPLY_STATUS_CHARGING)
return 1;
return 0;
}
static int wm831x_bat_check_type(struct wm831x *wm831x, int *type)
{
int ret;
#ifdef WM831X_DEBUG
ret = wm831x_reg_read(wm831x, WM831X_POWER_STATE);
if (ret < 0)
return ret;
WM_BATT_DBG("%s: wm831x power status %#x\n", __FUNCTION__, ret);
ret = wm831x_reg_read(wm831x, WM831X_SYSTEM_STATUS);
if (ret < 0)
return ret;
WM_BATT_DBG("%s: wm831x system status %#x\n", __FUNCTION__, ret);
ret = wm831x_reg_read(wm831x, WM831X_CHARGER_CONTROL_1);
if (ret < 0)
return ret;
WM_BATT_DBG("%s: wm831x charger control1 %#x\n", __FUNCTION__, ret);
ret = wm831x_reg_read(wm831x, WM831X_CHARGER_CONTROL_2);
if (ret < 0)
return ret;
WM_BATT_DBG("%s: wm831x charger control2 %#x\n", __FUNCTION__, ret);
ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS);
if (ret < 0)
return ret;
WM_BATT_DBG("%s: wm831x charger status %#x\n\n", __FUNCTION__, ret);
#endif
ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS);
if (ret < 0)
@ -392,12 +606,13 @@ static int wm831x_bat_get_prop(struct power_supply *psy,
{
struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev->parent);
struct wm831x *wm831x = wm831x_power->wm831x;
int ret = 0;
int level, ret = 0;
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
ret = wm831x_bat_check_status(wm831x, &val->intval);
break;
case POWER_SUPPLY_PROP_PRESENT:
case POWER_SUPPLY_PROP_ONLINE:
ret = wm831x_power_check_online(wm831x, WM831X_PWR_SRC_BATT,
val);
@ -411,6 +626,17 @@ static int wm831x_bat_get_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_CHARGE_TYPE:
ret = wm831x_bat_check_type(wm831x, &val->intval);
break;
case POWER_SUPPLY_PROP_CAPACITY:
ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_BATT, val);
wm831x_batt_vol_level(wm831x_power, val->intval, &level);
val->intval = level;
break;
case POWER_SUPPLY_PROP_TEMP:
val->intval = 0;
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
break;
default:
ret = -EINVAL;
break;
@ -421,9 +647,12 @@ static int wm831x_bat_get_prop(struct power_supply *psy,
static enum power_supply_property wm831x_bat_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CAPACITY, /* in percents! */
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_CHARGE_TYPE,
};
@ -444,7 +673,7 @@ static irqreturn_t wm831x_bat_irq(int irq, void *data)
struct wm831x *wm831x = wm831x_power->wm831x;
dev_dbg(wm831x->dev, "Battery status changed: %d\n", irq);
WM_BATT_DBG("%s:Battery status changed %d\n", __FUNCTION__, irq);
/* The battery charger is autonomous so we don't need to do
* anything except kick user space */
power_supply_changed(&wm831x_power->battery);
@ -453,125 +682,6 @@ static irqreturn_t wm831x_bat_irq(int irq, void *data)
}
/*********************************************************************
* Backup supply properties
*********************************************************************/
static void wm831x_config_backup(struct wm831x *wm831x)
{
struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data;
struct wm831x_backup_pdata *pdata;
int ret, reg;
if (!wm831x_pdata || !wm831x_pdata->backup) {
dev_warn(wm831x->dev,
"No backup battery charger configuration\n");
return;
}
pdata = wm831x_pdata->backup;
reg = 0;
if (pdata->charger_enable)
reg |= WM831X_BKUP_CHG_ENA | WM831X_BKUP_BATT_DET_ENA;
if (pdata->no_constant_voltage)
reg |= WM831X_BKUP_CHG_MODE;
switch (pdata->vlim) {
case 2500:
break;
case 3100:
reg |= WM831X_BKUP_CHG_VLIM;
break;
default:
dev_err(wm831x->dev, "Invalid backup voltage limit %dmV\n",
pdata->vlim);
}
switch (pdata->ilim) {
case 100:
break;
case 200:
reg |= 1;
break;
case 300:
reg |= 2;
break;
case 400:
reg |= 3;
break;
default:
dev_err(wm831x->dev, "Invalid backup current limit %duA\n",
pdata->ilim);
}
ret = wm831x_reg_unlock(wm831x);
if (ret != 0) {
dev_err(wm831x->dev, "Failed to unlock registers: %d\n", ret);
return;
}
ret = wm831x_set_bits(wm831x, WM831X_BACKUP_CHARGER_CONTROL,
WM831X_BKUP_CHG_ENA_MASK |
WM831X_BKUP_CHG_MODE_MASK |
WM831X_BKUP_BATT_DET_ENA_MASK |
WM831X_BKUP_CHG_VLIM_MASK |
WM831X_BKUP_CHG_ILIM_MASK,
reg);
if (ret != 0)
dev_err(wm831x->dev,
"Failed to set backup charger config: %d\n", ret);
wm831x_reg_lock(wm831x);
}
static int wm831x_backup_get_prop(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev->parent);
struct wm831x *wm831x = wm831x_power->wm831x;
int ret = 0;
ret = wm831x_reg_read(wm831x, WM831X_BACKUP_CHARGER_CONTROL);
if (ret < 0)
return ret;
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
if (ret & WM831X_BKUP_CHG_STS)
val->intval = POWER_SUPPLY_STATUS_CHARGING;
else
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_BKUP_BATT,
val);
break;
case POWER_SUPPLY_PROP_PRESENT:
if (ret & WM831X_BKUP_CHG_STS)
val->intval = 1;
else
val->intval = 0;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static enum power_supply_property wm831x_backup_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_PRESENT,
};
/*********************************************************************
* Initialisation
*********************************************************************/
@ -584,7 +694,6 @@ static irqreturn_t wm831x_syslo_irq(int irq, void *data)
/* Not much we can actually *do* but tell people for
* posterity, we're probably about to run out of power. */
dev_crit(wm831x->dev, "SYSVDD under voltage\n");
return IRQ_HANDLED;
}
@ -594,18 +703,101 @@ static irqreturn_t wm831x_pwr_src_irq(int irq, void *data)
struct wm831x *wm831x = wm831x_power->wm831x;
dev_dbg(wm831x->dev, "Power source changed\n");
/* Just notify for everything - little harm in overnotifying.
* The backup battery is not a power source while the system
* is running so skip that.
*/
WM_BATT_DBG("%s:Power source changed\n", __FUNCTION__);
/* Just notify for everything - little harm in overnotifying. */
power_supply_changed(&wm831x_power->battery);
power_supply_changed(&wm831x_power->usb);
power_supply_changed(&wm831x_power->wall);
return IRQ_HANDLED;
}
static void wm831x_batt_timer_handler(unsigned long data)
{
struct wm831x_power *wm831x_power = (struct wm831x_power*)data;
schedule_work(&wm831x_power->batt_work);
}
void wm831x_batt_vol_level(struct wm831x_power *wm831x_power, int batt_val, int *level)
{
int i, ret, status;
static int count = 0;
union power_supply_propval val;
ret = wm831x_bat_check_status(wm831x_power->wm831x, &status);
if (status == POWER_SUPPLY_STATUS_CHARGING) {
for(i = 0; i < batt_num; i++){
if((batt_chg_step_table[i] <= batt_val) &&
(batt_chg_step_table[i+1] > batt_val))
break;
}
*level = batt_disp_table[i];
if (batt_val < 3650)
*level = 0;
count++;
if (*level < wm831x_power->priv.old_level && count > 20)
*level = wm831x_power->priv.old_level;
if (*level >= 100)
*level = 97;
if (*level < 0)
*level = 0;
}
else {
for(i = 0; i < batt_num; i++){
if((batt_step_table[i] <= batt_val) &&
(batt_step_table[i+1] > batt_val))
break;
}
*level = batt_disp_table[i];
if (batt_val < 3450)
*level = 0;
if ((wm831x_power->priv.old_level - *level) > 5)
*level = wm831x_power->priv.old_level - 5;
//if (g_read_level_cnt >= 10 && *level > wm831x_power->priv.old_level)
if (*level > wm831x_power->priv.old_level)
*level = wm831x_power->priv.old_level;
if (*level > 100)
*level = 100;
if (*level < 0)
*level = 0;
}
}
static void wm831x_batt_work(struct work_struct *work)
{
int ret, online, status,health,level;
union power_supply_propval val;
struct wm831x_power *power = container_of(work, struct wm831x_power, batt_work);
ret = wm831x_power_check_online(power->wm831x, WM831X_PWR_SRC_BATT, &val);
online = val.intval;
ret = wm831x_bat_check_status(power->wm831x, &status);
ret = wm831x_bat_check_health(power->wm831x, &health);
ret = wm831x_power_read_voltage(power->wm831x, WM831X_AUX_BATT, &val);
wm831x_batt_vol_level(power, val.intval, &level);
mod_timer(&power->timer, jiffies + msecs_to_jiffies(power->interval));
if (online != power->priv.online || status != power->priv.status
|| health != power->priv.health || level != power->priv.old_level)
{
power->priv.online = online;
power->priv.status = status;
power->priv.health = health;
power->priv.old_level = level;
power_supply_changed(&power->battery);
}
}
static __devinit int wm831x_power_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
@ -613,7 +805,6 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
struct power_supply *usb;
struct power_supply *battery;
struct power_supply *wall;
struct power_supply *backup;
int ret, irq, i;
power = kzalloc(sizeof(struct wm831x_power), GFP_KERNEL);
@ -626,13 +817,11 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
usb = &power->usb;
battery = &power->battery;
wall = &power->wall;
backup = &power->backup;
/* We ignore configuration failures since we can still read back
* the status without enabling either of the chargers.
* the status without enabling the charger.
*/
wm831x_config_battery(wm831x);
wm831x_config_backup(wm831x);
wall->name = "wm831x-wall";
wall->type = POWER_SUPPLY_TYPE_MAINS;
@ -661,41 +850,33 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
if (ret)
goto err_battery;
backup->name = "wm831x-backup";
backup->type = POWER_SUPPLY_TYPE_BATTERY;
backup->properties = wm831x_backup_props;
backup->num_properties = ARRAY_SIZE(wm831x_backup_props);
backup->get_property = wm831x_backup_get_prop;
ret = power_supply_register(&pdev->dev, backup);
if (ret)
goto err_usb;
irq = platform_get_irq_byname(pdev, "SYSLO");
ret = wm831x_request_irq(wm831x, irq, wm831x_syslo_irq,
IRQF_TRIGGER_RISING, "SYSLO",
power);
ret = request_threaded_irq(irq, NULL, wm831x_syslo_irq,
IRQF_TRIGGER_RISING, "System power low",
power);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to request SYSLO IRQ %d: %d\n",
irq, ret);
goto err_backup;
goto err_usb;
}
irq = platform_get_irq_byname(pdev, "PWR SRC");
ret = wm831x_request_irq(wm831x, irq, wm831x_pwr_src_irq,
IRQF_TRIGGER_RISING, "Power source",
power);
ret = request_threaded_irq(irq, NULL, wm831x_pwr_src_irq,
IRQF_TRIGGER_RISING, "Power source",
power);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to request PWR SRC IRQ %d: %d\n",
irq, ret);
goto err_syslo;
}
#if 0 //ʹÓòéѯµÄ·½Ê½
for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
ret = wm831x_request_irq(wm831x, irq, wm831x_bat_irq,
IRQF_TRIGGER_RISING,
wm831x_bat_irqs[i],
power);
ret = request_threaded_irq(irq, NULL, wm831x_bat_irq,
IRQF_TRIGGER_RISING,
wm831x_bat_irqs[i],
power);
WM_BATT_DBG("%s: %s irq no %d\n", __FUNCTION__, wm831x_bat_irqs[i], irq);
if (ret != 0) {
dev_err(&pdev->dev,
"Failed to request %s IRQ %d: %d\n",
@ -703,21 +884,28 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
goto err_bat_irq;
}
}
#endif
power->interval = TIMER_MS_COUNTS;
power->priv.old_level = 100;
INIT_WORK(&power->batt_work, wm831x_batt_work);
setup_timer(&power->timer, wm831x_batt_timer_handler, (unsigned long)power);
power->timer.expires = jiffies + msecs_to_jiffies(1000);
add_timer(&power->timer);
g_wm831x_power = power;
printk("%s:wm831x_power initialized\n",__FUNCTION__);
return ret;
err_bat_irq:
for (; i >= 0; i--) {
irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
wm831x_free_irq(wm831x, irq, power);
free_irq(irq, power);
}
irq = platform_get_irq_byname(pdev, "PWR SRC");
wm831x_free_irq(wm831x, irq, power);
free_irq(irq, power);
err_syslo:
irq = platform_get_irq_byname(pdev, "SYSLO");
wm831x_free_irq(wm831x, irq, power);
err_backup:
power_supply_unregister(backup);
free_irq(irq, power);
err_usb:
power_supply_unregister(usb);
err_battery:
@ -732,30 +920,52 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
static __devexit int wm831x_power_remove(struct platform_device *pdev)
{
struct wm831x_power *wm831x_power = platform_get_drvdata(pdev);
struct wm831x *wm831x = wm831x_power->wm831x;
int irq, i;
for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
wm831x_free_irq(wm831x, irq, wm831x_power);
free_irq(irq, wm831x_power);
}
irq = platform_get_irq_byname(pdev, "PWR SRC");
wm831x_free_irq(wm831x, irq, wm831x_power);
free_irq(irq, wm831x_power);
irq = platform_get_irq_byname(pdev, "SYSLO");
wm831x_free_irq(wm831x, irq, wm831x_power);
free_irq(irq, wm831x_power);
power_supply_unregister(&wm831x_power->backup);
power_supply_unregister(&wm831x_power->battery);
power_supply_unregister(&wm831x_power->wall);
power_supply_unregister(&wm831x_power->usb);
kfree(wm831x_power);
return 0;
}
#ifdef CONFIG_PM
static int wm831x_battery_suspend(struct platform_device *dev, pm_message_t state)
{
//struct wm831x_power *power = (struct wm831x_power *)platform_get_drvdata(dev);
//flush_scheduled_work();
//del_timer(&power->timer);
return 0;
}
static int wm831x_battery_resume(struct platform_device *dev)
{
//struct wm831x_power *power = (struct wm831x_power *)platform_get_drvdata(dev);
//power->timer.expires = jiffies + msecs_to_jiffies(power->interval);
//add_timer(&power->timer);
return 0;
}
#else
#define wm831x_battery_suspend NULL
#define wm831x_battery_resume NULL
#endif
static struct platform_driver wm831x_power_driver = {
.probe = wm831x_power_probe,
.remove = __devexit_p(wm831x_power_remove),
.suspend = wm831x_battery_suspend,
.resume = wm831x_battery_resume,
.driver = {
.name = "wm831x-power",
},

245
drivers/regulator/wm831x-dcdc.c Normal file → Executable file
View File

@ -19,6 +19,9 @@
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/mfd/wm831x/core.h>
#include <linux/mfd/wm831x/regulator.h>
@ -32,25 +35,31 @@
#define WM831X_DCDC_MODE_IDLE 2
#define WM831X_DCDC_MODE_STANDBY 3
#define WM831X_DCDC_MAX_NAME 6
//#define WM831X_DCDC_MAX_NAME 6
/* Register offsets in control block */
#define WM831X_DCDC_CONTROL_1 0
#define WM831X_DCDC_CONTROL_2 1
#define WM831X_DCDC_ON_CONFIG 2
#define WM831X_DCDC_SLEEP_CONTROL 3
#define WM831X_DCDC_DVS_CONTROL 4
/*
* Shared
*/
#if 0
struct wm831x_dcdc {
char name[WM831X_DCDC_MAX_NAME];
struct regulator_desc desc;
int base;
struct wm831x *wm831x;
struct regulator_dev *regulator;
int dvs_gpio;
int dvs_gpio_state;
int on_vsel;
int dvs_vsel;
};
#endif
static int wm831x_dcdc_is_enabled(struct regulator_dev *rdev)
{
@ -240,11 +249,9 @@ static int wm831x_buckv_list_voltage(struct regulator_dev *rdev,
return -EINVAL;
}
static int wm831x_buckv_set_voltage_int(struct regulator_dev *rdev, int reg,
int min_uV, int max_uV)
static int wm831x_buckv_select_min_voltage(struct regulator_dev *rdev,
int min_uV, int max_uV)
{
struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
struct wm831x *wm831x = dcdc->wm831x;
u16 vsel;
if (min_uV < 600000)
@ -257,39 +264,126 @@ static int wm831x_buckv_set_voltage_int(struct regulator_dev *rdev, int reg,
if (wm831x_buckv_list_voltage(rdev, vsel) > max_uV)
return -EINVAL;
return wm831x_set_bits(wm831x, reg, WM831X_DC1_ON_VSEL_MASK, vsel);
return vsel;
}
static int wm831x_buckv_select_max_voltage(struct regulator_dev *rdev,
int min_uV, int max_uV)
{
u16 vsel;
if (max_uV < 600000 || max_uV > 1800000)
return -EINVAL;
vsel = ((max_uV - 600000) / 12500) + 8;
if (wm831x_buckv_list_voltage(rdev, vsel) < min_uV ||
wm831x_buckv_list_voltage(rdev, vsel) < max_uV)
return -EINVAL;
return vsel;
}
static int wm831x_buckv_set_dvs(struct regulator_dev *rdev, int state)
{
struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
if (state == dcdc->dvs_gpio_state)
return 0;
dcdc->dvs_gpio_state = state;
gpio_set_value(dcdc->dvs_gpio, state);
/* Should wait for DVS state change to be asserted if we have
* a GPIO for it, for now assume the device is configured
* for the fastest possible transition.
*/
return 0;
}
static int wm831x_buckv_set_voltage(struct regulator_dev *rdev,
int min_uV, int max_uV)
int min_uV, int max_uV)
{
struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
u16 reg = dcdc->base + WM831X_DCDC_ON_CONFIG;
struct wm831x *wm831x = dcdc->wm831x;
int on_reg = dcdc->base + WM831X_DCDC_ON_CONFIG;
int dvs_reg = dcdc->base + WM831X_DCDC_DVS_CONTROL;
int vsel, ret;
return wm831x_buckv_set_voltage_int(rdev, reg, min_uV, max_uV);
vsel = wm831x_buckv_select_min_voltage(rdev, min_uV, max_uV);
if (vsel < 0)
return vsel;
/* If this value is already set then do a GPIO update if we can */
if (dcdc->dvs_gpio && dcdc->on_vsel == vsel)
return wm831x_buckv_set_dvs(rdev, 0);
if (dcdc->dvs_gpio && dcdc->dvs_vsel == vsel)
return wm831x_buckv_set_dvs(rdev, 1);
/* Always set the ON status to the minimum voltage */
ret = wm831x_set_bits(wm831x, on_reg, WM831X_DC1_ON_VSEL_MASK, vsel);
if (ret < 0)
return ret;
dcdc->on_vsel = vsel;
if (!dcdc->dvs_gpio)
return ret;
/* Kick the voltage transition now */
ret = wm831x_buckv_set_dvs(rdev, 0);
if (ret < 0)
return ret;
/* Set the high voltage as the DVS voltage. This is optimised
* for CPUfreq usage, most processors will keep the maximum
* voltage constant and lower the minimum with the frequency. */
vsel = wm831x_buckv_select_max_voltage(rdev, min_uV, max_uV);
if (vsel < 0) {
/* This should never happen - at worst the same vsel
* should be chosen */
WARN_ON(vsel < 0);
return 0;
}
/* Don't bother if it's the same VSEL we're already using */
if (vsel == dcdc->on_vsel)
return 0;
ret = wm831x_set_bits(wm831x, dvs_reg, WM831X_DC1_DVS_VSEL_MASK, vsel);
if (ret == 0)
dcdc->dvs_vsel = vsel;
else
dev_warn(wm831x->dev, "Failed to set DCDC DVS VSEL: %d\n",
ret);
return 0;
}
static int wm831x_buckv_set_suspend_voltage(struct regulator_dev *rdev,
int uV)
int uV)
{
struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
struct wm831x *wm831x = dcdc->wm831x;
u16 reg = dcdc->base + WM831X_DCDC_SLEEP_CONTROL;
int vsel;
return wm831x_buckv_set_voltage_int(rdev, reg, uV, uV);
vsel = wm831x_buckv_select_min_voltage(rdev, uV, uV);
if (vsel < 0)
return vsel;
return wm831x_set_bits(wm831x, reg, WM831X_DC1_SLP_VSEL_MASK, vsel);
}
static int wm831x_buckv_get_voltage(struct regulator_dev *rdev)
{
struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
struct wm831x *wm831x = dcdc->wm831x;
u16 reg = dcdc->base + WM831X_DCDC_ON_CONFIG;
int val;
val = wm831x_reg_read(wm831x, reg);
if (val < 0)
return val;
return wm831x_buckv_list_voltage(rdev, val & WM831X_DC1_ON_VSEL_MASK);
if (dcdc->dvs_gpio && dcdc->dvs_gpio_state)
return wm831x_buckv_list_voltage(rdev, dcdc->dvs_vsel);
else
return wm831x_buckv_list_voltage(rdev, dcdc->on_vsel);
}
/* Current limit options */
@ -329,6 +423,17 @@ static int wm831x_buckv_get_current_limit(struct regulator_dev *rdev)
return wm831x_dcdc_ilim[val & WM831X_DC1_HC_THR_MASK];
}
int wm831x_dcdc_set_suspend_enable(struct regulator_dev *rdev)
{
return 0;
}
int wm831x_dcdc_set_suspend_disable(struct regulator_dev *rdev)
{
return 0;
}
static struct regulator_ops wm831x_buckv_ops = {
.set_voltage = wm831x_buckv_set_voltage,
.get_voltage = wm831x_buckv_get_voltage,
@ -344,8 +449,68 @@ static struct regulator_ops wm831x_buckv_ops = {
.get_mode = wm831x_dcdc_get_mode,
.set_mode = wm831x_dcdc_set_mode,
.set_suspend_mode = wm831x_dcdc_set_suspend_mode,
.set_suspend_enable = wm831x_dcdc_set_suspend_enable,
.set_suspend_disable = wm831x_dcdc_set_suspend_disable,
};
/*
* Set up DVS control. We just log errors since we can still run
* (with reduced performance) if we fail.
*/
static __devinit void wm831x_buckv_dvs_init(struct wm831x_dcdc *dcdc,
struct wm831x_buckv_pdata *pdata)
{
struct wm831x *wm831x = dcdc->wm831x;
int ret;
u16 ctrl;
if (!pdata || !pdata->dvs_gpio)
return;
switch (pdata->dvs_control_src) {
case 1:
ctrl = 2 << WM831X_DC1_DVS_SRC_SHIFT;
break;
case 2:
ctrl = 3 << WM831X_DC1_DVS_SRC_SHIFT;
break;
default:
dev_err(wm831x->dev, "Invalid DVS control source %d for %s\n",
pdata->dvs_control_src, dcdc->name);
return;
}
ret = wm831x_set_bits(wm831x, dcdc->base + WM831X_DCDC_DVS_CONTROL,
WM831X_DC1_DVS_SRC_MASK, ctrl);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to set %s DVS source: %d\n",
dcdc->name, ret);
return;
}
ret = gpio_request(pdata->dvs_gpio, "DCDC DVS");
if (ret < 0) {
dev_err(wm831x->dev, "Failed to get %s DVS GPIO: %d\n",
dcdc->name, ret);
return;
}
/* gpiolib won't let us read the GPIO status so pick the higher
* of the two existing voltages so we take it as platform data.
*/
dcdc->dvs_gpio_state = pdata->dvs_init_state;
ret = gpio_direction_output(pdata->dvs_gpio, dcdc->dvs_gpio_state);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to enable %s DVS GPIO: %d\n",
dcdc->name, ret);
gpio_free(pdata->dvs_gpio);
return;
}
dcdc->dvs_gpio = pdata->dvs_gpio;
}
static __devinit int wm831x_buckv_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
@ -354,7 +519,7 @@ static __devinit int wm831x_buckv_probe(struct platform_device *pdev)
struct wm831x_dcdc *dcdc;
struct resource *res;
int ret, irq;
dev_dbg(&pdev->dev, "Probing DCDC%d\n", id + 1);
if (pdata == NULL || pdata->dcdc[id] == NULL)
@ -384,6 +549,23 @@ static __devinit int wm831x_buckv_probe(struct platform_device *pdev)
dcdc->desc.ops = &wm831x_buckv_ops;
dcdc->desc.owner = THIS_MODULE;
ret = wm831x_reg_read(wm831x, dcdc->base + WM831X_DCDC_ON_CONFIG);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to read ON VSEL: %d\n", ret);
goto err;
}
dcdc->on_vsel = ret & WM831X_DC1_ON_VSEL_MASK;
ret = wm831x_reg_read(wm831x, dcdc->base + WM831X_DCDC_ON_CONFIG);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to read DVS VSEL: %d\n", ret);
goto err;
}
dcdc->dvs_vsel = ret & WM831X_DC1_DVS_VSEL_MASK;
if (pdata->dcdc[id])
wm831x_buckv_dvs_init(dcdc, pdata->dcdc[id]->driver_data);
dcdc->regulator = regulator_register(&dcdc->desc, &pdev->dev,
pdata->dcdc[id], dcdc);
if (IS_ERR(dcdc->regulator)) {
@ -422,6 +604,8 @@ static __devinit int wm831x_buckv_probe(struct platform_device *pdev)
err_regulator:
regulator_unregister(dcdc->regulator);
err:
if (dcdc->dvs_gpio)
gpio_free(dcdc->dvs_gpio);
kfree(dcdc);
return ret;
}
@ -431,9 +615,13 @@ static __devexit int wm831x_buckv_remove(struct platform_device *pdev)
struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev);
struct wm831x *wm831x = dcdc->wm831x;
platform_set_drvdata(pdev, NULL);
wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "HC"), dcdc);
wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), dcdc);
regulator_unregister(dcdc->regulator);
if (dcdc->dvs_gpio)
gpio_free(dcdc->dvs_gpio);
kfree(dcdc);
return 0;
@ -444,6 +632,7 @@ static struct platform_driver wm831x_buckv_driver = {
.remove = __devexit_p(wm831x_buckv_remove),
.driver = {
.name = "wm831x-buckv",
.owner = THIS_MODULE,
},
};
@ -523,6 +712,8 @@ static struct regulator_ops wm831x_buckp_ops = {
.get_mode = wm831x_dcdc_get_mode,
.set_mode = wm831x_dcdc_set_mode,
.set_suspend_mode = wm831x_dcdc_set_suspend_mode,
.set_suspend_enable = wm831x_dcdc_set_suspend_enable,
.set_suspend_disable = wm831x_dcdc_set_suspend_disable,
};
static __devinit int wm831x_buckp_probe(struct platform_device *pdev)
@ -598,6 +789,8 @@ static __devexit int wm831x_buckp_remove(struct platform_device *pdev)
struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev);
struct wm831x *wm831x = dcdc->wm831x;
platform_set_drvdata(pdev, NULL);
wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), dcdc);
regulator_unregister(dcdc->regulator);
kfree(dcdc);
@ -610,6 +803,7 @@ static struct platform_driver wm831x_buckp_driver = {
.remove = __devexit_p(wm831x_buckp_remove),
.driver = {
.name = "wm831x-buckp",
.owner = THIS_MODULE,
},
};
@ -724,6 +918,8 @@ static __devexit int wm831x_boostp_remove(struct platform_device *pdev)
struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev);
struct wm831x *wm831x = dcdc->wm831x;
platform_set_drvdata(pdev, NULL);
wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), dcdc);
regulator_unregister(dcdc->regulator);
kfree(dcdc);
@ -736,6 +932,7 @@ static struct platform_driver wm831x_boostp_driver = {
.remove = __devexit_p(wm831x_boostp_remove),
.driver = {
.name = "wm831x-boostp",
.owner = THIS_MODULE,
},
};
@ -808,6 +1005,8 @@ static __devexit int wm831x_epe_remove(struct platform_device *pdev)
{
struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
regulator_unregister(dcdc->regulator);
kfree(dcdc);
@ -819,12 +1018,14 @@ static struct platform_driver wm831x_epe_driver = {
.remove = __devexit_p(wm831x_epe_remove),
.driver = {
.name = "wm831x-epe",
.owner = THIS_MODULE,
},
};
static int __init wm831x_dcdc_init(void)
{
int ret;
printk("%s \n", __FUNCTION__);
ret = platform_driver_register(&wm831x_buckv_driver);
if (ret != 0)
pr_err("Failed to register WM831x BUCKV driver: %d\n", ret);
@ -840,7 +1041,7 @@ static int __init wm831x_dcdc_init(void)
ret = platform_driver_register(&wm831x_epe_driver);
if (ret != 0)
pr_err("Failed to register WM831x EPE driver: %d\n", ret);
return 0;
}
subsys_initcall(wm831x_dcdc_init);

21
drivers/regulator/wm831x-isink.c Normal file → Executable file
View File

@ -19,13 +19,15 @@
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/slab.h>
#include <linux/mfd/wm831x/core.h>
#include <linux/mfd/wm831x/regulator.h>
#include <linux/mfd/wm831x/pdata.h>
#define WM831X_ISINK_MAX_NAME 7
//#define WM831X_ISINK_MAX_NAME 7
#if 0
struct wm831x_isink {
char name[WM831X_ISINK_MAX_NAME];
struct regulator_desc desc;
@ -33,13 +35,14 @@ struct wm831x_isink {
struct wm831x *wm831x;
struct regulator_dev *regulator;
};
#endif
static int wm831x_isink_enable(struct regulator_dev *rdev)
{
struct wm831x_isink *isink = rdev_get_drvdata(rdev);
struct wm831x *wm831x = isink->wm831x;
int ret;
printk("%s:line=%d\n",__FUNCTION__,__LINE__);
/* We have a two stage enable: first start the ISINK... */
ret = wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_ENA,
WM831X_CS1_ENA);
@ -51,7 +54,7 @@ static int wm831x_isink_enable(struct regulator_dev *rdev)
WM831X_CS1_DRIVE);
if (ret != 0)
wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_ENA, 0);
printk("%s:line=%d,ret=0x%x\n",__FUNCTION__,__LINE__,ret);
return ret;
}
@ -61,7 +64,7 @@ static int wm831x_isink_disable(struct regulator_dev *rdev)
struct wm831x_isink *isink = rdev_get_drvdata(rdev);
struct wm831x *wm831x = isink->wm831x;
int ret;
printk("%s:line=%d\n",__FUNCTION__,__LINE__);
ret = wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_DRIVE, 0);
if (ret < 0)
return ret;
@ -79,11 +82,11 @@ static int wm831x_isink_is_enabled(struct regulator_dev *rdev)
struct wm831x_isink *isink = rdev_get_drvdata(rdev);
struct wm831x *wm831x = isink->wm831x;
int ret;
printk("%s:line=%d\n",__FUNCTION__,__LINE__);
ret = wm831x_reg_read(wm831x, isink->reg);
if (ret < 0)
return ret;
if ((ret & (WM831X_CS1_ENA | WM831X_CS1_DRIVE)) ==
(WM831X_CS1_ENA | WM831X_CS1_DRIVE))
return 1;
@ -157,7 +160,7 @@ static __devinit int wm831x_isink_probe(struct platform_device *pdev)
int ret, irq;
dev_dbg(&pdev->dev, "Probing ISINK%d\n", id + 1);
printk("%s:line=%d\n",__FUNCTION__,__LINE__);
if (pdata == NULL || pdata->isink[id] == NULL)
return -ENODEV;
@ -197,6 +200,7 @@ static __devinit int wm831x_isink_probe(struct platform_device *pdev)
}
irq = platform_get_irq(pdev, 0);
printk("%s:line=%d,irq=%d\n",__FUNCTION__,__LINE__,irq);
ret = wm831x_request_irq(wm831x, irq, wm831x_isink_irq,
IRQF_TRIGGER_RISING, isink->name,
isink);
@ -222,6 +226,8 @@ static __devexit int wm831x_isink_remove(struct platform_device *pdev)
struct wm831x_isink *isink = platform_get_drvdata(pdev);
struct wm831x *wm831x = isink->wm831x;
platform_set_drvdata(pdev, NULL);
wm831x_free_irq(wm831x, platform_get_irq(pdev, 0), isink);
regulator_unregister(isink->regulator);
@ -235,6 +241,7 @@ static struct platform_driver wm831x_isink_driver = {
.remove = __devexit_p(wm831x_isink_remove),
.driver = {
.name = "wm831x-isink",
.owner = THIS_MODULE,
},
};

119
drivers/regulator/wm831x-ldo.c Normal file → Executable file
View File

@ -19,12 +19,13 @@
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/slab.h>
#include <linux/mfd/wm831x/core.h>
#include <linux/mfd/wm831x/regulator.h>
#include <linux/mfd/wm831x/pdata.h>
#define WM831X_LDO_MAX_NAME 6
//#define WM831X_LDO_MAX_NAME 6
#define WM831X_LDO_CONTROL 0
#define WM831X_LDO_ON_CONTROL 1
@ -33,6 +34,7 @@
#define WM831X_ALIVE_LDO_ON_CONTROL 0
#define WM831X_ALIVE_LDO_SLEEP_CONTROL 1
#if 0
struct wm831x_ldo {
char name[WM831X_LDO_MAX_NAME];
struct regulator_desc desc;
@ -40,11 +42,12 @@ struct wm831x_ldo {
struct wm831x *wm831x;
struct regulator_dev *regulator;
};
#endif
/*
* Shared
*/
extern int reboot_cmd_get(void);
static int wm831x_ldo_is_enabled(struct regulator_dev *rdev)
{
struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
@ -67,7 +70,7 @@ static int wm831x_ldo_enable(struct regulator_dev *rdev)
struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
struct wm831x *wm831x = ldo->wm831x;
int mask = 1 << rdev_get_id(rdev);
//printk("%s,%x\n", __FUNCTION__,mask);
return wm831x_set_bits(wm831x, WM831X_LDO_ENABLE, mask, mask);
}
@ -76,7 +79,7 @@ static int wm831x_ldo_disable(struct regulator_dev *rdev)
struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
struct wm831x *wm831x = ldo->wm831x;
int mask = 1 << rdev_get_id(rdev);
//printk("%s\n", __FUNCTION__);
return wm831x_set_bits(wm831x, WM831X_LDO_ENABLE, mask, 0);
}
@ -140,7 +143,7 @@ static int wm831x_gp_ldo_set_voltage(struct regulator_dev *rdev,
{
struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
int reg = ldo->base + WM831X_LDO_ON_CONTROL;
printk("%s base=%x,%d,%d\n", __FUNCTION__,ldo->base,min_uV,max_uV);
return wm831x_gp_ldo_set_voltage_int(rdev, reg, min_uV, max_uV);
}
@ -163,7 +166,7 @@ static int wm831x_gp_ldo_get_voltage(struct regulator_dev *rdev)
ret = wm831x_reg_read(wm831x, reg);
if (ret < 0)
return ret;
printk("%s base=%x,ret=%x\n", __FUNCTION__,ldo->base,ret);
ret &= WM831X_LDO1_ON_VSEL_MASK;
return wm831x_gp_ldo_list_voltage(rdev, ret);
@ -203,7 +206,7 @@ static int wm831x_gp_ldo_set_mode(struct regulator_dev *rdev,
int on_reg = ldo->base + WM831X_LDO_ON_CONTROL;
int ret;
printk("%s base=%x,mode=%x\n", __FUNCTION__,ldo->base,mode);
switch (mode) {
case REGULATOR_MODE_NORMAL:
ret = wm831x_set_bits(wm831x, on_reg,
@ -214,8 +217,7 @@ static int wm831x_gp_ldo_set_mode(struct regulator_dev *rdev,
case REGULATOR_MODE_IDLE:
ret = wm831x_set_bits(wm831x, ctrl_reg,
WM831X_LDO1_LP_MODE,
WM831X_LDO1_LP_MODE);
WM831X_LDO1_LP_MODE, 0);
if (ret < 0)
return ret;
@ -224,10 +226,12 @@ static int wm831x_gp_ldo_set_mode(struct regulator_dev *rdev,
WM831X_LDO1_ON_MODE);
if (ret < 0)
return ret;
break;
case REGULATOR_MODE_STANDBY:
ret = wm831x_set_bits(wm831x, ctrl_reg,
WM831X_LDO1_LP_MODE, 0);
WM831X_LDO1_LP_MODE,
WM831X_LDO1_LP_MODE);
if (ret < 0)
return ret;
@ -282,6 +286,16 @@ static unsigned int wm831x_gp_ldo_get_optimum_mode(struct regulator_dev *rdev,
return REGULATOR_MODE_NORMAL;
}
int wm831x_ldo_set_suspend_enable(struct regulator_dev *rdev)
{
return 0;
}
int wm831x_ldo_set_suspend_disable(struct regulator_dev *rdev)
{
return 0;
}
static struct regulator_ops wm831x_gp_ldo_ops = {
.list_voltage = wm831x_gp_ldo_list_voltage,
@ -296,6 +310,8 @@ static struct regulator_ops wm831x_gp_ldo_ops = {
.is_enabled = wm831x_ldo_is_enabled,
.enable = wm831x_ldo_enable,
.disable = wm831x_ldo_disable,
.set_suspend_enable = wm831x_ldo_set_suspend_enable,
.set_suspend_disable = wm831x_ldo_set_suspend_disable,
};
static __devinit int wm831x_gp_ldo_probe(struct platform_device *pdev)
@ -308,7 +324,7 @@ static __devinit int wm831x_gp_ldo_probe(struct platform_device *pdev)
int ret, irq;
dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1);
printk("Probing LDO%d\n", id + 1);
if (pdata == NULL || pdata->ldo[id] == NULL)
return -ENODEV;
@ -371,6 +387,8 @@ static __devexit int wm831x_gp_ldo_remove(struct platform_device *pdev)
struct wm831x_ldo *ldo = platform_get_drvdata(pdev);
struct wm831x *wm831x = ldo->wm831x;
platform_set_drvdata(pdev, NULL);
wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), ldo);
regulator_unregister(ldo->regulator);
kfree(ldo);
@ -383,6 +401,7 @@ static struct platform_driver wm831x_gp_ldo_driver = {
.remove = __devexit_p(wm831x_gp_ldo_remove),
.driver = {
.name = "wm831x-ldo",
.owner = THIS_MODULE,
},
};
@ -436,7 +455,7 @@ static int wm831x_aldo_set_voltage(struct regulator_dev *rdev,
{
struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
int reg = ldo->base + WM831X_LDO_ON_CONTROL;
printk("%s base=%x,min_uV=%d,%d\n", __FUNCTION__,ldo->base,min_uV,max_uV);
return wm831x_aldo_set_voltage_int(rdev, reg, min_uV, max_uV);
}
@ -455,13 +474,13 @@ static int wm831x_aldo_get_voltage(struct regulator_dev *rdev)
struct wm831x *wm831x = ldo->wm831x;
int reg = ldo->base + WM831X_LDO_ON_CONTROL;
int ret;
ret = wm831x_reg_read(wm831x, reg);
if (ret < 0)
return ret;
printk("%s base=%x,ret=%x\n", __FUNCTION__,ldo->base,ret);
ret &= WM831X_LDO7_ON_VSEL_MASK;
return wm831x_aldo_list_voltage(rdev, ret);
}
@ -470,7 +489,7 @@ static unsigned int wm831x_aldo_get_mode(struct regulator_dev *rdev)
struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
struct wm831x *wm831x = ldo->wm831x;
int on_reg = ldo->base + WM831X_LDO_ON_CONTROL;
unsigned int ret;
int ret;
ret = wm831x_reg_read(wm831x, on_reg);
if (ret < 0)
@ -553,6 +572,8 @@ static struct regulator_ops wm831x_aldo_ops = {
.is_enabled = wm831x_ldo_is_enabled,
.enable = wm831x_ldo_enable,
.disable = wm831x_ldo_disable,
.set_suspend_enable = wm831x_ldo_set_suspend_enable,
.set_suspend_disable = wm831x_ldo_set_suspend_disable,
};
static __devinit int wm831x_aldo_probe(struct platform_device *pdev)
@ -565,7 +586,7 @@ static __devinit int wm831x_aldo_probe(struct platform_device *pdev)
int ret, irq;
dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1);
printk("Probing LDO%d--\n", id + 1);
if (pdata == NULL || pdata->ldo[id] == NULL)
return -ENODEV;
@ -640,6 +661,7 @@ static struct platform_driver wm831x_aldo_driver = {
.remove = __devexit_p(wm831x_aldo_remove),
.driver = {
.name = "wm831x-aldo",
.owner = THIS_MODULE,
},
};
@ -738,6 +760,8 @@ static struct regulator_ops wm831x_alive_ldo_ops = {
.is_enabled = wm831x_ldo_is_enabled,
.enable = wm831x_ldo_enable,
.disable = wm831x_ldo_disable,
.set_suspend_enable = wm831x_ldo_set_suspend_enable,
.set_suspend_disable = wm831x_ldo_set_suspend_disable,
};
static __devinit int wm831x_alive_ldo_probe(struct platform_device *pdev)
@ -750,7 +774,7 @@ static __devinit int wm831x_alive_ldo_probe(struct platform_device *pdev)
int ret;
dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1);
printk("wm831x_alive_ldo_probe Probing LDO%d\n", id + 1);
if (pdata == NULL || pdata->ldo[id] == NULL)
return -ENODEV;
@ -806,18 +830,72 @@ static __devexit int wm831x_alive_ldo_remove(struct platform_device *pdev)
return 0;
}
static __devexit int wm831x_alive_ldo_shutdown(struct platform_device *pdev) /*ZMF*/
{
//struct wm831x_ldo *ldo = platform_get_drvdata(pdev);
struct regulator* ldo;
if (reboot_cmd_get())
return 0;
printk("%s\n", __FUNCTION__);
ldo = regulator_get(NULL, "ldo1");
regulator_disable(ldo);
regulator_put(ldo);
ldo = regulator_get(NULL, "ldo2");
regulator_disable(ldo);
regulator_put(ldo);
ldo = regulator_get(NULL, "ldo3");
regulator_disable(ldo);
regulator_put(ldo);
ldo = regulator_get(NULL, "ldo4");
//regulator_disable(ldo);
regulator_put(ldo);
ldo = regulator_get(NULL, "ldo5");
regulator_disable(ldo);
regulator_put(ldo);
ldo = regulator_get(NULL, "ldo6");
regulator_disable(ldo);
regulator_put(ldo);
ldo = regulator_get(NULL, "ldo7");
regulator_disable(ldo);
regulator_put(ldo);
ldo = regulator_get(NULL, "ldo8");
//regulator_disable(ldo);
regulator_put(ldo);
ldo = regulator_get(NULL, "ldo9");
regulator_disable(ldo);
regulator_put(ldo);
ldo = regulator_get(NULL, "ldo10");
regulator_disable(ldo);
regulator_put(ldo);
return 0;
}
static struct platform_driver wm831x_alive_ldo_driver = {
.probe = wm831x_alive_ldo_probe,
.remove = __devexit_p(wm831x_alive_ldo_remove),
.shutdown = __devexit_p(wm831x_alive_ldo_shutdown),
.driver = {
.name = "wm831x-alive-ldo",
.owner = THIS_MODULE,
},
};
static int __init wm831x_ldo_init(void)
{
int ret;
printk("%s \n", __FUNCTION__);
ret = platform_driver_register(&wm831x_gp_ldo_driver);
if (ret != 0)
pr_err("Failed to register WM831x GP LDO driver: %d\n", ret);
@ -830,8 +908,7 @@ static int __init wm831x_ldo_init(void)
if (ret != 0)
pr_err("Failed to register WM831x alive LDO driver: %d\n",
ret);
return 0;
return 0;
}
subsys_initcall(wm831x_ldo_init);

57
drivers/rtc/rtc-wm831x.c Normal file → Executable file
View File

@ -16,6 +16,7 @@
#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/rtc.h>
#include <linux/slab.h>
#include <linux/bcd.h>
#include <linux/interrupt.h>
#include <linux/ioctl.h>
@ -423,7 +424,7 @@ static int wm831x_rtc_probe(struct platform_device *pdev)
int per_irq = platform_get_irq_byname(pdev, "PER");
int alm_irq = platform_get_irq_byname(pdev, "ALM");
int ret = 0;
//printk("wm831x_rtc_probe\n");
wm831x_rtc = kzalloc(sizeof(*wm831x_rtc), GFP_KERNEL);
if (wm831x_rtc == NULL)
return -ENOMEM;
@ -431,6 +432,38 @@ static int wm831x_rtc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, wm831x_rtc);
wm831x_rtc->wm831x = wm831x;
#if 0
/*set time when power on for debug*/
ret = wm831x_reg_write(wm831x, WM831X_RTC_TIME_1,
(0x1000000 >> 16) & 0xffff);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to write TIME_1: %d\n", ret);
return ret;
}
ret = wm831x_reg_write(wm831x, WM831X_RTC_TIME_2, 0x100000 & 0xffff);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to write TIME_2: %d\n", ret);
return ret;
}
ret = wm831x_reg_read(wm831x, WM831X_RTC_TIME_1);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to read TIME_2: %d\n", ret);
goto err;
}
printk("%s:WM831X_RTC_TIME_1=0x%x\n",__FUNCTION__,ret);
ret = wm831x_reg_read(wm831x, WM831X_RTC_TIME_2);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to read TIME_2: %d\n", ret);
goto err;
}
printk("%s:WM831X_RTC_TIME_2=0x%x\n",__FUNCTION__,ret);
#endif
ret = wm831x_reg_read(wm831x, WM831X_RTC_CONTROL);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to read RTC control: %d\n", ret);
@ -447,18 +480,18 @@ static int wm831x_rtc_probe(struct platform_device *pdev)
ret = PTR_ERR(wm831x_rtc->rtc);
goto err;
}
ret = wm831x_request_irq(wm831x, per_irq, wm831x_per_irq,
IRQF_TRIGGER_RISING, "wm831x_rtc_per",
wm831x_rtc);
//printk("1wm831x_rtc_probe=%d\n",per_irq);
ret = request_threaded_irq(per_irq, NULL, wm831x_per_irq,
IRQF_TRIGGER_RISING, "RTC period",
wm831x_rtc);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to request periodic IRQ %d: %d\n",
per_irq, ret);
}
ret = wm831x_request_irq(wm831x, alm_irq, wm831x_alm_irq,
IRQF_TRIGGER_RISING, "wm831x_rtc_alm",
wm831x_rtc);
//printk("2wm831x_rtc_probe=%d\n",alm_irq);
ret = request_threaded_irq(alm_irq, NULL, wm831x_alm_irq,
IRQF_TRIGGER_RISING, "RTC alarm",
wm831x_rtc);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n",
alm_irq, ret);
@ -477,15 +510,15 @@ static int __devexit wm831x_rtc_remove(struct platform_device *pdev)
int per_irq = platform_get_irq_byname(pdev, "PER");
int alm_irq = platform_get_irq_byname(pdev, "ALM");
wm831x_free_irq(wm831x_rtc->wm831x, alm_irq, wm831x_rtc);
wm831x_free_irq(wm831x_rtc->wm831x, per_irq, wm831x_rtc);
free_irq(alm_irq, wm831x_rtc);
free_irq(per_irq, wm831x_rtc);
rtc_device_unregister(wm831x_rtc->rtc);
kfree(wm831x_rtc);
return 0;
}
static struct dev_pm_ops wm831x_rtc_pm_ops = {
static const struct dev_pm_ops wm831x_rtc_pm_ops = {
.suspend = wm831x_rtc_suspend,
.resume = wm831x_rtc_resume,

107
drivers/video/backlight/wm831x_bl.c Normal file → Executable file
View File

@ -13,25 +13,47 @@
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/backlight.h>
#include <linux/slab.h>
#include <linux/mfd/wm831x/core.h>
#include <linux/mfd/wm831x/pdata.h>
#include <linux/mfd/wm831x/regulator.h>
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif
#include <linux/delay.h>
#include <linux/ktime.h>
#define BL_SET 255
struct wm831x_backlight_data {
struct wm831x *wm831x;
int isink_reg;
int current_brightness;
};
#define TS_POLL_DELAY (10000*1000*1000)
int suspend_flag = 0;
int wm831x_bright = 0;
int max_tp = 0;
#ifdef CONFIG_HAS_EARLYSUSPEND
static struct backlight_device *wm831x_bl;
static struct timer_list wm831x_timer;
static struct wm831x_backlight_data *wm831x_data;
static struct wm831x *gpwm831x;
#endif
struct hrtimer wm831x_bl_timer;
static int wm831x_backlight_set(struct backlight_device *bl, int brightness)
{
struct wm831x_backlight_data *data = bl_get_data(bl);
struct wm831x *wm831x = data->wm831x;
int power_up = !data->current_brightness && brightness;
int power_down = data->current_brightness && !brightness;
// int power_up = !data->current_brightness && brightness;
// int power_down = data->current_brightness && !brightness;
int power_up;
int power_down;
int ret;
int bright_tp;
bright_tp =( max_tp*brightness)/BL_SET;
power_up =!data->current_brightness && bright_tp;
power_down = data->current_brightness && !bright_tp;
if (power_up) {
/* Enable the ISINK */
ret = wm831x_set_bits(wm831x, data->isink_reg,
@ -62,7 +84,7 @@ static int wm831x_backlight_set(struct backlight_device *bl, int brightness)
/* Set the new brightness */
ret = wm831x_set_bits(wm831x, data->isink_reg,
WM831X_CS1_ISEL_MASK, brightness);
WM831X_CS1_ISEL_MASK, bright_tp);
if (ret < 0)
goto err;
@ -93,7 +115,8 @@ static int wm831x_backlight_set(struct backlight_device *bl, int brightness)
static int wm831x_backlight_update_status(struct backlight_device *bl)
{
int brightness = bl->props.brightness;
if (suspend_flag == 1)
brightness = 0;
if (bl->props.power != FB_BLANK_UNBLANK)
brightness = 0;
@ -102,7 +125,7 @@ static int wm831x_backlight_update_status(struct backlight_device *bl)
if (bl->props.state & BL_CORE_SUSPENDED)
brightness = 0;
return wm831x_backlight_set(bl, brightness);
}
@ -112,12 +135,39 @@ static int wm831x_backlight_get_brightness(struct backlight_device *bl)
return data->current_brightness;
}
static struct backlight_ops wm831x_backlight_ops = {
static const struct backlight_ops wm831x_backlight_ops = {
.options = BL_CORE_SUSPENDRESUME,
.update_status = wm831x_backlight_update_status,
.get_brightness = wm831x_backlight_get_brightness,
};
static void wm831x_delaybacklight_timer(unsigned long data)
{
wm831x_backlight_update_status(wm831x_bl);
}
#ifdef CONFIG_HAS_EARLYSUSPEND
static void wm831x_bl_suspend(struct early_suspend *h)
{
suspend_flag = 1;
wm831x_delaybacklight_timer(NULL);
}
static void wm831x_bl_resume(struct early_suspend *h)
{
// wm831x_timer.expires = jiffies + 30;
// add_timer(&wm831x_timer);
suspend_flag = 0;
wm831x_delaybacklight_timer(NULL);
}
static struct early_suspend bl_early_suspend;
#endif
static enum hrtimer_restart wm831x_bl_timer_fuction(struct hrtimer *handle)
{
backlight_update_status(wm831x_bl);
return HRTIMER_NORESTART;
}
static int wm831x_backlight_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
@ -125,7 +175,9 @@ static int wm831x_backlight_probe(struct platform_device *pdev)
struct wm831x_backlight_pdata *pdata;
struct wm831x_backlight_data *data;
struct backlight_device *bl;
struct backlight_properties props;
int ret, i, max_isel, isink_reg, dcdc_cfg;
/* We need platform data */
if (pdev->dev.parent->platform_data) {
@ -151,7 +203,7 @@ static int wm831x_backlight_probe(struct platform_device *pdev)
return -EINVAL;
}
max_isel = i - 1;
max_tp = max_isel;
if (pdata->max_uA != wm831x_isinkv_values[max_isel])
dev_warn(&pdev->dev,
"Maximum current is %duA not %duA as requested\n",
@ -187,28 +239,40 @@ static int wm831x_backlight_probe(struct platform_device *pdev)
if (data == NULL)
return -ENOMEM;
data->wm831x = wm831x;
data->wm831x = gpwm831x = wm831x;
data->current_brightness = 0;
data->isink_reg = isink_reg;
bl = backlight_device_register("wm831x", &pdev->dev,
data, &wm831x_backlight_ops);
props.max_brightness = max_isel;
//bl = backlight_device_register("wm831x", &pdev->dev, data,
// &wm831x_backlight_ops, &props);
wm831x_bl = bl = backlight_device_register("wm831x", &pdev->dev, data,
&wm831x_backlight_ops);
if (IS_ERR(bl)) {
dev_err(&pdev->dev, "failed to register backlight\n");
kfree(data);
return PTR_ERR(bl);
}
setup_timer(&wm831x_timer, wm831x_delaybacklight_timer, NULL);
bl->props.max_brightness = max_isel;
bl->props.brightness = max_isel;
bl->props.brightness = BL_SET;
bl->props.max_brightness= BL_SET;
wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE, WM831X_DC4_ENA, 0);
platform_set_drvdata(pdev, bl);
#ifdef CONFIG_HAS_EARLYSUSPEND
bl_early_suspend.suspend = wm831x_bl_suspend;
bl_early_suspend.resume = wm831x_bl_resume;
bl_early_suspend.level = ~0x0;
register_early_suspend(&bl_early_suspend);
#endif
hrtimer_init(&wm831x_bl_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
wm831x_bl_timer.function = wm831x_bl_timer_fuction;
hrtimer_start(&wm831x_bl_timer, ktime_set(0, TS_POLL_DELAY),
HRTIMER_MODE_REL);
/* Disable the DCDC if it was started so we can bootstrap */
wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE, WM831X_DC4_ENA, 0);
backlight_update_status(bl);
//backlight_update_status(bl);
return 0;
}
@ -219,6 +283,9 @@ static int wm831x_backlight_remove(struct platform_device *pdev)
struct wm831x_backlight_data *data = bl_get_data(bl);
backlight_device_unregister(bl);
#ifdef CONFIG_HAS_EARLYSUSPEND
unregister_early_suspend(&bl_early_suspend);
#endif
kfree(data);
return 0;
}

2
drivers/watchdog/wm831x_wdt.c Normal file → Executable file
View File

@ -213,7 +213,7 @@ static ssize_t wm831x_wdt_write(struct file *file,
return count;
}
static struct watchdog_info ident = {
static const struct watchdog_info ident = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.identity = "WM831x Watchdog",
};