mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 20:07:46 +09:00
move wm831x code from rk2818
This commit is contained in:
0
Documentation/hwmon/wm831x
Normal file → Executable file
0
Documentation/hwmon/wm831x
Normal file → Executable file
116
drivers/gpio/wm831x-gpio.c
Normal file → Executable file
116
drivers/gpio/wm831x-gpio.c
Normal file → Executable 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
1
drivers/hwmon/wm831x-hwmon.c
Normal file → Executable 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>
|
||||
|
||||
468
drivers/input/keyboard/wm831x_gpio_keys.c
Executable file
468
drivers/input/keyboard/wm831x_gpio_keys.c
Executable 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
155
drivers/input/misc/wm831x-on.c
Normal file → Executable 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
1
drivers/leds/leds-wm831x-status.c
Normal file → Executable 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
569
drivers/mfd/wm831x-core.c
Normal file → Executable 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 @@ out:
|
||||
}
|
||||
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 @@ err:
|
||||
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], ®, 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
188
drivers/mfd/wm831x-i2c.c
Executable 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], ®, 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
363
drivers/mfd/wm831x-irq.c
Normal file → Executable 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
0
drivers/mfd/wm831x-otp.c
Normal file → Executable file
232
drivers/mfd/wm831x-spi.c
Executable file
232
drivers/mfd/wm831x-spi.c
Executable 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
234
drivers/power/wm831x_backup.c
Executable 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
558
drivers/power/wm831x_power.c
Normal file → Executable 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, ®2,
|
||||
"charger timeout", "min");
|
||||
|
||||
wm831x_battey_apply_config(wm831x, chg_syslos, ARRAY_SIZE(chg_syslos),
|
||||
pdata->syslo, ®3,
|
||||
"syslo voltage", "mV");
|
||||
|
||||
wm831x_battey_apply_config(wm831x, chg_sysoks, ARRAY_SIZE(chg_sysoks),
|
||||
pdata->sysok, ®3,
|
||||
"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 //ʹ<>ò<EFBFBD>ѯ<EFBFBD>ķ<EFBFBD>ʽ
|
||||
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 @@ err_kmalloc:
|
||||
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
245
drivers/regulator/wm831x-dcdc.c
Normal file → Executable 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 @@ err_uv:
|
||||
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
21
drivers/regulator/wm831x-isink.c
Normal file → Executable 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
119
drivers/regulator/wm831x-ldo.c
Normal file → Executable 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
57
drivers/rtc/rtc-wm831x.c
Normal file → Executable 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
107
drivers/video/backlight/wm831x_bl.c
Normal file → Executable 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 @@ err:
|
||||
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
2
drivers/watchdog/wm831x_wdt.c
Normal file → Executable 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",
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user