move wm831x code from rk2818
authorluowei <lw@rock-chips.com>
Sat, 26 Feb 2011 06:28:56 +0000 (14:28 +0800)
committerluowei <lw@rock-chips.com>
Sat, 26 Feb 2011 06:30:08 +0000 (14:30 +0800)
19 files changed:
Documentation/hwmon/wm831x [changed mode: 0644->0755]
drivers/gpio/wm831x-gpio.c [changed mode: 0644->0755]
drivers/hwmon/wm831x-hwmon.c [changed mode: 0644->0755]
drivers/input/keyboard/wm831x_gpio_keys.c [new file with mode: 0755]
drivers/input/misc/wm831x-on.c [changed mode: 0644->0755]
drivers/leds/leds-wm831x-status.c [changed mode: 0644->0755]
drivers/mfd/wm831x-core.c [changed mode: 0644->0755]
drivers/mfd/wm831x-i2c.c [new file with mode: 0755]
drivers/mfd/wm831x-irq.c [changed mode: 0644->0755]
drivers/mfd/wm831x-otp.c [changed mode: 0644->0755]
drivers/mfd/wm831x-spi.c [new file with mode: 0755]
drivers/power/wm831x_backup.c [new file with mode: 0755]
drivers/power/wm831x_power.c [changed mode: 0644->0755]
drivers/regulator/wm831x-dcdc.c [changed mode: 0644->0755]
drivers/regulator/wm831x-isink.c [changed mode: 0644->0755]
drivers/regulator/wm831x-ldo.c [changed mode: 0644->0755]
drivers/rtc/rtc-wm831x.c [changed mode: 0644->0755]
drivers/video/backlight/wm831x_bl.c [changed mode: 0644->0755]
drivers/watchdog/wm831x_wdt.c [changed mode: 0644->0755]

old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
index 9a27047..382ae87
@@ -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_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;
-
+       int gpn_pol;
+       
+       ret = wm831x_reg_read(wm831x, WM831X_GPIO1_CONTROL + offset);
+       if (ret < 0)
+               return ret;
+       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;
-
-       if (ret & 1 << offset)
-               return 1;
-       else
-               return 0;
+       
+       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;
old mode 100644 (file)
new mode 100755 (executable)
index c16e9e7..97b1f83
@@ -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>
diff --git a/drivers/input/keyboard/wm831x_gpio_keys.c b/drivers/input/keyboard/wm831x_gpio_keys.c
new file mode 100755 (executable)
index 0000000..db0f448
--- /dev/null
@@ -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");
old mode 100644 (file)
new mode 100755 (executable)
index ba4f5dd..8705da4
@@ -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>
 #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;
+       }
 
+       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);
old mode 100644 (file)
new mode 100755 (executable)
index c586d05..ef5c241
@@ -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>
old mode 100644 (file)
new mode 100755 (executable)
index 7f27576..928d7f1
 
 #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>
 #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);
-
-               ret = wm831x_reg_read(wm831x, WM831X_AUXADC_CONTROL);
-               if (ret < 0)
-                       ret = WM831X_AUX_CVT_ENA;
-       } while ((ret & WM831X_AUX_CVT_ENA) && --tries);
-
-       if (ret & WM831X_AUX_CVT_ENA) {
-               dev_err(wm831x->dev, "Timed out reading AUXADC\n");
-               ret = -EBUSY;
-               goto disable;
+       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_INTERRUPT_STATUS_1);
+                       if (ret < 0) {
+                               dev_err(wm831x->dev,
+                                       "ISR 1 read failed: %d\n", ret);
+                               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);
-
-       ret = i2c_master_send(i2c, (unsigned char *)&r, 2);
-       if (ret < 0)
-               return ret;
-       if (ret != 2)
-               return -EIO;
-
-       ret = i2c_master_recv(i2c, dest, bytes);
-       if (ret < 0)
-               return ret;
-       if (ret != bytes)
-               return -EIO;
-       return 0;
-}
-
-/* Currently we allocate the write buffer on the stack; this is OK for
- * small writes - if we need to do large writes this will need to be
- * revised.
- */
-static int wm831x_i2c_write_device(struct wm831x *wm831x, unsigned short reg,
-                                  int bytes, void *src)
-{
-       struct i2c_client *i2c = wm831x->control_data;
-       unsigned char msg[bytes + 2];
-       int ret;
-
-       reg = cpu_to_be16(reg);
-       memcpy(&msg[0], &reg, 2);
-       memcpy(&msg[2], src, bytes);
+       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]);
+
+               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]);
+               }
+       
+       }
 
-       ret = i2c_master_send(i2c, msg, bytes + 2);
-       if (ret < 0)
-               return ret;
-       if (ret < bytes + 2)
-               return -EIO;
+       /* 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);
+
+               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;
+
+               /* If any of the interrupts are masked read the statuses */
+               if (reg & mask)
+                       reg = wm831x_reg_read(wm831x,
+                                             WM831X_INTERRUPT_STATUS_2);
+
+               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 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;
+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__);
        }
-
-       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;
-
-       return wm831x_device_init(wm831x, id->driver_data, i2c->irq);
+       regulator_put(dcdc);
+#endif 
 }
-
-static int wm831x_i2c_remove(struct i2c_client *i2c)
-{
-       struct wm831x *wm831x = i2c_get_clientdata(i2c);
-
-       wm831x_device_exit(wm831x);
-
-       return 0;
+EXPORT_SYMBOL_GPL(wm831x_enter_sleep);
+
+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 
 }
+EXPORT_SYMBOL_GPL(wm831x_exit_sleep);
 
-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 wm831x_device_shutdown(struct wm831x *wm831x)
 {
-       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;
-}
-subsys_initcall(wm831x_i2c_init);
+       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));
 
-static void __exit wm831x_i2c_exit(void)
-{
-       i2c_del_driver(&wm831x_i2c_driver);
+       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_exit(wm831x_i2c_exit);
 
-MODULE_DESCRIPTION("I2C support for the WM831X AudioPlus PMIC");
+MODULE_DESCRIPTION("Core support for the WM831X AudioPlus PMIC");
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Mark Brown");
diff --git a/drivers/mfd/wm831x-i2c.c b/drivers/mfd/wm831x-i2c.c
new file mode 100755 (executable)
index 0000000..5c6e2e7
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * wm831x-i2c.c  --  I2C access for Wolfson WM831x PMICs
+ *
+ * Copyright 2009,2010 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/mfd/core.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/pdata.h>
+
+static int wm831x_i2c_read_device(struct wm831x *wm831x, unsigned short reg,
+                                 int bytes, void *dest)
+{
+       struct i2c_client *i2c = wm831x->control_data;
+       int ret;
+       u16 r = cpu_to_be16(reg);
+
+       ret = i2c_master_send(i2c, (unsigned char *)&r, 2);
+       if (ret < 0)
+               return ret;
+       if (ret != 2)
+               return -EIO;
+
+       ret = i2c_master_recv(i2c, dest, bytes);
+       if (ret < 0)
+               return ret;
+       if (ret != bytes)
+               return -EIO;
+       return 0;
+}
+
+/* Currently we allocate the write buffer on the stack; this is OK for
+ * small writes - if we need to do large writes this will need to be
+ * revised.
+ */
+static int wm831x_i2c_write_device(struct wm831x *wm831x, unsigned short reg,
+                                  int bytes, void *src)
+{
+       struct i2c_client *i2c = wm831x->control_data;
+       unsigned char msg[bytes + 2];
+       int ret;
+
+       reg = cpu_to_be16(reg);
+       memcpy(&msg[0], &reg, 2);
+       memcpy(&msg[2], src, bytes);
+
+       ret = i2c_master_send(i2c, msg, bytes + 2);
+       if (ret < 0)
+               return ret;
+       if (ret < bytes + 2)
+               return -EIO;
+
+       return 0;
+}
+
+static int wm831x_i2c_probe(struct i2c_client *i2c,
+                           const struct i2c_device_id *id)
+{
+       struct wm831x *wm831x;
+       int ret,gpio,irq;
+       
+       wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL);
+       if (wm831x == NULL)
+               return -ENOMEM;
+
+       i2c_set_clientdata(i2c, wm831x);
+       
+       gpio = i2c->irq;
+       ret = gpio_request(gpio, "wm831x");
+       if (ret) {
+               printk( "failed to request rk gpio irq for wm831x \n");
+               return ret;
+       }
+       gpio_pull_updown(gpio, GPIOPullUp);
+       if (ret) {
+           printk("failed to pull up gpio irq for wm831x \n");
+               return ret;
+       }       
+       irq = gpio_to_irq(gpio);
+       
+       wm831x->dev = &i2c->dev;
+       wm831x->control_data = i2c;
+       wm831x->read_dev = wm831x_i2c_read_device;
+       wm831x->write_dev = wm831x_i2c_write_device;
+
+       return wm831x_device_init(wm831x, id->driver_data, irq);
+}
+
+static int wm831x_i2c_remove(struct i2c_client *i2c)
+{
+       struct wm831x *wm831x = i2c_get_clientdata(i2c);
+
+       wm831x_device_exit(wm831x);
+
+       return 0;
+}
+
+static int wm831x_i2c_suspend(struct i2c_client *i2c, pm_message_t mesg)
+{
+       struct wm831x *wm831x = i2c_get_clientdata(i2c);
+
+       return wm831x_device_suspend(wm831x);
+}
+
+static int wm831x_i2c_resume(struct i2c_client *i2c)
+{
+       struct wm831x *wm831x = i2c_get_clientdata(i2c);
+       int i;
+       //set some intterupt again while resume 
+       for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) {
+               //printk("irq_masks_cur[%d]=0x%x\n",i,wm831x->irq_masks_cur[i]);
+
+               if (wm831x->irq_masks_cur[i] != wm831x->irq_masks_cache[i]) {
+                       wm831x->irq_masks_cache[i] = wm831x->irq_masks_cur[i];
+                       wm831x_reg_write(wm831x,
+                                        WM831X_INTERRUPT_STATUS_1_MASK + i,
+                                        wm831x->irq_masks_cur[i]);
+               }
+       
+       }
+
+       return 0;
+}
+
+void wm831x_i2c_shutdown(struct i2c_client *i2c)
+{
+       struct wm831x *wm831x = i2c_get_clientdata(i2c);
+       printk("%s\n", __FUNCTION__);
+       wm831x_device_shutdown(wm831x);
+}
+
+static const struct i2c_device_id wm831x_i2c_id[] = {
+       { "wm8310", WM8310 },
+       { "wm8311", WM8311 },
+       { "wm8312", WM8312 },
+       { "wm8320", WM8320 },
+       { "wm8321", WM8321 },
+       { "wm8325", WM8325 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, wm831x_i2c_id);
+
+
+static struct i2c_driver wm831x_i2c_driver = {
+       .driver = {
+                  .name = "wm831x",
+                  .owner = THIS_MODULE,
+       },
+       .probe = wm831x_i2c_probe,
+       .remove = wm831x_i2c_remove,
+       .suspend = wm831x_i2c_suspend,
+       .resume = wm831x_i2c_resume,
+       .shutdown = wm831x_i2c_shutdown,
+       .id_table = wm831x_i2c_id,
+};
+
+static int __init wm831x_i2c_init(void)
+{
+       int ret;
+       printk("%s \n", __FUNCTION__);
+       ret = i2c_add_driver(&wm831x_i2c_driver);
+       if (ret != 0)
+               pr_err("Failed to register wm831x I2C driver: %d\n", ret);
+
+       return ret;
+}
+subsys_initcall(wm831x_i2c_init);
+
+static void __exit wm831x_i2c_exit(void)
+{
+       i2c_del_driver(&wm831x_i2c_driver);
+}
+module_exit(wm831x_i2c_exit);
old mode 100644 (file)
new mode 100755 (executable)
index ac056ea..905b59e
 #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
  * 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);
-       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_sync_unlock(unsigned int irq)
 {
-       struct wm831x_irq_data *irq_data = &wm831x_irqs[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]);
+               }
+       }
 
-       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]);
+       mutex_unlock(&wm831x->irq_lock);
 }
 
-void wm831x_disable_irq(struct wm831x *wm831x, int irq)
+static void wm831x_irq_unmask(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 void wm831x_irq_mask(unsigned int irq)
 {
-       int ret = 0;
-
-       if (irq < 0 || irq >= WM831X_NUM_IRQS)
-               return -EINVAL;
+       struct wm831x *wm831x = get_irq_chip_data(irq);
+       struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x, irq);
 
-       mutex_lock(&wm831x->irq_lock);
+       wm831x->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
+       //printk("%s:irq=%d\n",__FUNCTION__,irq);
+}
 
-       if (wm831x_irqs[irq].handler) {
-               dev_err(wm831x->dev, "Already have handler for IRQ %d\n", irq);
-               ret = -EINVAL;
-               goto out;
+static int wm831x_irq_set_type(unsigned int irq, unsigned int type)
+{
+       struct wm831x *wm831x = get_irq_chip_data(irq);
+       int val;
+
+       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;
        }
 
-       wm831x_irqs[irq].handler = handler;
-       wm831x_irqs[irq].handler_data = dev;
+       return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + irq - 1,
+                              WM831X_GPN_INT_MODE | WM831X_GPN_POL, val);
+}
 
-       __wm831x_enable_irq(wm831x, irq);
+static int wm831x_irq_set_wake(unsigned irq, unsigned state)
+{      
+       struct wm831x *wm831x = get_irq_chip_data(irq); 
+
+       //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;
+       }
 
-out:
-       mutex_unlock(&wm831x->irq_lock);
 
-       return ret;
 }
-EXPORT_SYMBOL_GPL(wm831x_request_irq);
 
-void wm831x_free_irq(struct wm831x *wm831x, unsigned int irq, void *data)
+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,
+};
+
+#if WM831X_IRQ_LIST
+static void wm831x_handle_worker(struct work_struct *work)
 {
-       if (irq < 0 || irq >= WM831X_NUM_IRQS)
-               return;
+       struct wm831x *wm831x = container_of(work, struct wm831x, handle_work);
+       int irq;
 
-       mutex_lock(&wm831x->irq_lock);
+       while (1) {
+               unsigned long flags;
+               struct wm831x_handle_irq *hd = NULL;
 
-       wm831x_irqs[irq].handler = NULL;
-       wm831x_irqs[irq].handler_data = 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);
 
-       __wm831x_disable_irq(wm831x, irq);
-
-       mutex_unlock(&wm831x->irq_lock);
-}
-EXPORT_SYMBOL_GPL(wm831x_free_irq);
+               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);
 
-static void wm831x_handle_irq(struct wm831x *wm831x, int irq, int status)
-{
-       struct wm831x_irq_data *irq_data = &wm831x_irqs[irq];
-
-       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;
        }
-
-       /* Enable top level interrupts, we mask at secondary level */
-       wm831x_reg_write(wm831x, WM831X_SYSTEM_INTERRUPTS_MASK, 0);
-
-       /* 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);
+       INIT_WORK(&wm831x->handle_work, wm831x_handle_worker);
+       INIT_LIST_HEAD(&wm831x->handle_queue);
+
+#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);
+
+               /* 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;
 }
 
old mode 100644 (file)
new mode 100755 (executable)
diff --git a/drivers/mfd/wm831x-spi.c b/drivers/mfd/wm831x-spi.c
new file mode 100755 (executable)
index 0000000..2789b15
--- /dev/null
@@ -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");
diff --git a/drivers/power/wm831x_backup.c b/drivers/power/wm831x_backup.c
new file mode 100755 (executable)
index 0000000..0fd130d
--- /dev/null
@@ -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");
old mode 100644 (file)
new mode 100755 (executable)
index 2a4c8b0..a31225d
 #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,18 +125,68 @@ 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;
+               val->intval = ret / 1000;
+       return ret ;
+#endif
+}
 
-       return ret;
+int wm831x_read_batt_voltage(void)
+{
+       int ret = 0;
+       
+       if(!g_wm831x_power)
+       {
+               printk("err:%s:g_wm831x_power address is 0\n",__FUNCTION__);
+               return -1;
+       }
+       
+       ret = wm831x_auxadc_read_uv(g_wm831x_power->wm831x, WM831X_AUX_BATT);
+       return ret / 1000;
 }
+//EXPORT_SYMBOL_GPL(wm831x_get_batt_voltage);
 
 /*********************************************************************
  *             WALL Power
@@ -190,13 +322,34 @@ static struct chg_map chg_times[] = {
        { 510, 15 << WM831X_CHG_TIME_SHIFT },
 };
 
+static struct chg_map chg_syslos[] = {
+       { 2800, 0 << WM831X_CHG_SYSLO_SHIFT},
+       { 2900, 1 << WM831X_CHG_SYSLO_SHIFT},
+       { 3000, 2 << WM831X_CHG_SYSLO_SHIFT},
+       { 3100, 3 << WM831X_CHG_SYSLO_SHIFT},
+       { 3200, 4 << WM831X_CHG_SYSLO_SHIFT},
+       { 3300, 5 << WM831X_CHG_SYSLO_SHIFT},
+       { 3400, 6 << WM831X_CHG_SYSLO_SHIFT},
+       { 3500, 7 << WM831X_CHG_SYSLO_SHIFT},
+};
+
+static struct chg_map chg_sysoks[] = {
+       { 2800, 0 << WM831X_CHG_SYSOK_SHIFT},
+       { 2900, 1 << WM831X_CHG_SYSOK_SHIFT},
+       { 3000, 2 << WM831X_CHG_SYSOK_SHIFT},
+       { 3100, 3 << WM831X_CHG_SYSOK_SHIFT},
+       { 3200, 4 << WM831X_CHG_SYSOK_SHIFT},
+       { 3300, 5 << WM831X_CHG_SYSOK_SHIFT},
+       { 3400, 6 << WM831X_CHG_SYSOK_SHIFT},
+       { 3500, 7 << WM831X_CHG_SYSOK_SHIFT},
+};
+
 static void wm831x_battey_apply_config(struct wm831x *wm831x,
                                       struct chg_map *map, int count, int val,
                                       int *reg, const char *name,
                                       const char *units)
 {
        int i;
-
        for (i = 0; i < count; i++)
                if (val == map[i].val)
                        break;
@@ -213,7 +366,7 @@ static void wm831x_config_battery(struct wm831x *wm831x)
 {
        struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data;
        struct wm831x_battery_pdata *pdata;
-       int ret, reg1, reg2;
+       int ret, reg1, reg2, reg3;
 
        if (!wm831x_pdata || !wm831x_pdata->battery) {
                dev_warn(wm831x->dev,
@@ -225,6 +378,7 @@ static void wm831x_config_battery(struct wm831x *wm831x)
 
        reg1 = 0;
        reg2 = 0;
+       reg3 = 0;
 
        if (!pdata->enable) {
                dev_info(wm831x->dev, "Battery charger disabled\n");
@@ -258,6 +412,14 @@ static void wm831x_config_battery(struct wm831x *wm831x)
                                   pdata->timeout, &reg2,
                                   "charger timeout", "min");
 
+       wm831x_battey_apply_config(wm831x, chg_syslos, ARRAY_SIZE(chg_syslos),
+                                  pdata->syslo, &reg3,
+                                  "syslo voltage", "mV");
+
+       wm831x_battey_apply_config(wm831x, chg_sysoks, ARRAY_SIZE(chg_sysoks),
+                                  pdata->sysok, &reg3,
+                                  "sysok voltage", "mV");
+
        ret = wm831x_reg_unlock(wm831x);
        if (ret != 0) {
                dev_err(wm831x->dev, "Failed to unlock registers: %d\n", ret);
@@ -267,13 +429,12 @@ static void wm831x_config_battery(struct wm831x *wm831x)
        ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_1,
                              WM831X_CHG_ENA_MASK |
                              WM831X_CHG_FAST_MASK |
-                             WM831X_CHG_ITERM_MASK |
                              WM831X_CHG_ITERM_MASK,
                              reg1);
-       if (ret != 0)
+       if (ret != 0) {
                dev_err(wm831x->dev, "Failed to set charger control 1: %d\n",
                        ret);
-
+       }
        ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_2,
                              WM831X_CHG_OFF_MSK |
                              WM831X_CHG_TIME_MASK |
@@ -281,9 +442,18 @@ static void wm831x_config_battery(struct wm831x *wm831x)
                              WM831X_CHG_TRKL_ILIM_MASK |
                              WM831X_CHG_VSEL_MASK,
                              reg2);
-       if (ret != 0)
+       if (ret != 0) {
                dev_err(wm831x->dev, "Failed to set charger control 2: %d\n",
                        ret);
+       }
+
+       ret = wm831x_set_bits(wm831x, WM831X_SYSVDD_CONTROL,
+                                         WM831X_CHG_SYSLO_MASK |
+                                                 WM831X_CHG_SYSOK_MASK,
+                                                 reg3);
+       if (ret < 0) {
+               dev_err(wm831x->dev, "Failed to set sysvdd control reg: %d\n",ret);
+       }
 
        wm831x_reg_lock(wm831x);
 }
@@ -305,6 +475,7 @@ static int wm831x_bat_check_status(struct wm831x *wm831x, int *status)
        if (ret < 0)
                return ret;
 
+       
        switch (ret & WM831X_CHG_STATE_MASK) {
        case WM831X_CHG_STATE_OFF:
                *status = POWER_SUPPLY_STATUS_NOT_CHARGING;
@@ -321,10 +492,53 @@ static int wm831x_bat_check_status(struct wm831x *wm831x, int *status)
 
        return 0;
 }
+int wm831x_read_bat_charging_status(void)
+{
+       int ret, status;
+       
+       if(!g_wm831x_power)
+       {
+               printk("err:%s:g_wm831x_power address is 0\n",__FUNCTION__);
+               return -1;
+       }
+       
+       ret = wm831x_bat_check_status(g_wm831x_power->wm831x, &status);
+       if (ret < 0)
+               return ret;
+       if (status == POWER_SUPPLY_STATUS_CHARGING) 
+               return 1;
+       return 0;
+}
 
 static int wm831x_bat_check_type(struct wm831x *wm831x, int *type)
 {
        int ret;
+#ifdef WM831X_DEBUG 
+       ret = wm831x_reg_read(wm831x, WM831X_POWER_STATE);
+       if (ret < 0)
+               return ret;
+       WM_BATT_DBG("%s: wm831x power status %#x\n", __FUNCTION__, ret);
+
+       ret = wm831x_reg_read(wm831x, WM831X_SYSTEM_STATUS);
+       if (ret < 0)
+               return ret;
+       WM_BATT_DBG("%s: wm831x system status %#x\n", __FUNCTION__, ret);
+
+       ret = wm831x_reg_read(wm831x, WM831X_CHARGER_CONTROL_1);
+       if (ret < 0)
+               return ret;
+       WM_BATT_DBG("%s: wm831x charger control1 %#x\n", __FUNCTION__, ret);
+
+       ret = wm831x_reg_read(wm831x, WM831X_CHARGER_CONTROL_2);
+       if (ret < 0)
+               return ret;
+       WM_BATT_DBG("%s: wm831x charger control2 %#x\n", __FUNCTION__, ret);
+
+       ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS);
+       if (ret < 0)
+               return ret;
+       WM_BATT_DBG("%s: wm831x charger status %#x\n\n", __FUNCTION__, ret);
+#endif
 
        ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS);
        if (ret < 0)
@@ -392,12 +606,13 @@ static int wm831x_bat_get_prop(struct power_supply *psy,
 {
        struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev->parent);
        struct wm831x *wm831x = wm831x_power->wm831x;
-       int ret = 0;
+       int level, ret = 0;
 
        switch (psp) {
        case POWER_SUPPLY_PROP_STATUS:
                ret = wm831x_bat_check_status(wm831x, &val->intval);
                break;
+       case POWER_SUPPLY_PROP_PRESENT:
        case POWER_SUPPLY_PROP_ONLINE:
                ret = wm831x_power_check_online(wm831x, WM831X_PWR_SRC_BATT,
                                                val);
@@ -411,6 +626,17 @@ static int wm831x_bat_get_prop(struct power_supply *psy,
        case POWER_SUPPLY_PROP_CHARGE_TYPE:
                ret = wm831x_bat_check_type(wm831x, &val->intval);
                break;
+       case POWER_SUPPLY_PROP_CAPACITY:
+               ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_BATT, val);
+               wm831x_batt_vol_level(wm831x_power, val->intval, &level);
+               val->intval = level;
+               break;
+       case POWER_SUPPLY_PROP_TEMP:
+               val->intval = 0;
+               break;
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+               break;
        default:
                ret = -EINVAL;
                break;
@@ -421,9 +647,12 @@ static int wm831x_bat_get_prop(struct power_supply *psy,
 
 static enum power_supply_property wm831x_bat_props[] = {
        POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_ONLINE,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
        POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CAPACITY, /* in percents! */
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_TEMP,
        POWER_SUPPLY_PROP_CHARGE_TYPE,
 };
 
@@ -444,7 +673,7 @@ static irqreturn_t wm831x_bat_irq(int irq, void *data)
        struct wm831x *wm831x = wm831x_power->wm831x;
 
        dev_dbg(wm831x->dev, "Battery status changed: %d\n", irq);
-
+       WM_BATT_DBG("%s:Battery status changed %d\n", __FUNCTION__, irq);
        /* The battery charger is autonomous so we don't need to do
         * anything except kick user space */
        power_supply_changed(&wm831x_power->battery);
@@ -454,156 +683,119 @@ static irqreturn_t wm831x_bat_irq(int irq, void *data)
 
 
 /*********************************************************************
- *             Backup supply properties
+ *             Initialisation
  *********************************************************************/
 
-static void wm831x_config_backup(struct wm831x *wm831x)
+static irqreturn_t wm831x_syslo_irq(int irq, void *data)
 {
-       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;
+       struct wm831x_power *wm831x_power = data;
+       struct wm831x *wm831x = wm831x_power->wm831x;
 
-       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);
-       }
+       /* 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;
+}
 
-       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);
-       }
+static irqreturn_t wm831x_pwr_src_irq(int irq, void *data)
+{
+       struct wm831x_power *wm831x_power = data;
+       struct wm831x *wm831x = wm831x_power->wm831x;
 
-       ret = wm831x_reg_unlock(wm831x);
-       if (ret != 0) {
-               dev_err(wm831x->dev, "Failed to unlock registers: %d\n", ret);
-               return;
-       }
+       dev_dbg(wm831x->dev, "Power source changed\n");
+       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);
 
-       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);
+       return IRQ_HANDLED;
 }
 
-static int wm831x_backup_get_prop(struct power_supply *psy,
-                                 enum power_supply_property psp,
-                                 union power_supply_propval *val)
+static void wm831x_batt_timer_handler(unsigned long data)
 {
-       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;
+       struct wm831x_power *wm831x_power = (struct wm831x_power*)data;
+       schedule_work(&wm831x_power->batt_work);
+}
 
-       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;
+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;
 
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_BKUP_BATT,
-                                               val);
-               break;
+               if (*level >= 100)
+                       *level = 97;
 
-       case POWER_SUPPLY_PROP_PRESENT:
-               if (ret & WM831X_BKUP_CHG_STS)
-                       val->intval = 1;
-               else
-                       val->intval = 0;
-               break;
+               if (*level < 0)
+                       *level = 0;
 
-       default:
-               ret = -EINVAL;
-               break;
        }
+       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;
 
-       return ret;
-}
+               if ((wm831x_power->priv.old_level - *level) > 5)
+                       *level = wm831x_power->priv.old_level - 5;
 
-static enum power_supply_property wm831x_backup_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_PRESENT,
-};
+               //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;
 
-/*********************************************************************
- *             Initialisation
- *********************************************************************/
+               if (*level > 100)
+                       *level = 100;
+               if (*level < 0)
+                       *level = 0;
+       }
+}
 
-static irqreturn_t wm831x_syslo_irq(int irq, void *data)
+static void wm831x_batt_work(struct work_struct *work)
 {
-       struct wm831x_power *wm831x_power = data;
-       struct wm831x *wm831x = wm831x_power->wm831x;
-
-       /* 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");
+       int ret, online, status,health,level;
+    union power_supply_propval val;
+       struct wm831x_power *power = container_of(work, struct wm831x_power, batt_work);
 
-       return IRQ_HANDLED;
-}
+       ret = wm831x_power_check_online(power->wm831x, WM831X_PWR_SRC_BATT, &val);
+       online = val.intval;
 
-static irqreturn_t wm831x_pwr_src_irq(int irq, void *data)
-{
-       struct wm831x_power *wm831x_power = data;
-       struct wm831x *wm831x = wm831x_power->wm831x;
+       ret = wm831x_bat_check_status(power->wm831x, &status);
+       ret = wm831x_bat_check_health(power->wm831x, &health);
 
-       dev_dbg(wm831x->dev, "Power source changed\n");
+       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));
 
-       /* Just notify for everything - little harm in overnotifying.
-        * The backup battery is not a power source while the system
-        * is running so skip that.
-        */
-       power_supply_changed(&wm831x_power->battery);
-       power_supply_changed(&wm831x_power->usb);
-       power_supply_changed(&wm831x_power->wall);
+       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;
 
-       return IRQ_HANDLED;
+               power_supply_changed(&power->battery);
+       }
 }
 
 static __devinit int wm831x_power_probe(struct platform_device *pdev)
@@ -613,7 +805,6 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
        struct power_supply *usb;
        struct power_supply *battery;
        struct power_supply *wall;
-       struct power_supply *backup;
        int ret, irq, i;
 
        power = kzalloc(sizeof(struct wm831x_power), GFP_KERNEL);
@@ -626,13 +817,11 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
        usb = &power->usb;
        battery = &power->battery;
        wall = &power->wall;
-       backup = &power->backup;
 
        /* We ignore configuration failures since we can still read back
-        * the status without enabling either of the chargers.
+        * the status without enabling the charger.
         */
        wm831x_config_battery(wm831x);
-       wm831x_config_backup(wm831x);
 
        wall->name = "wm831x-wall";
        wall->type = POWER_SUPPLY_TYPE_MAINS;
@@ -661,41 +850,33 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
        if (ret)
                goto err_battery;
 
-       backup->name = "wm831x-backup";
-       backup->type = POWER_SUPPLY_TYPE_BATTERY;
-       backup->properties = wm831x_backup_props;
-       backup->num_properties = ARRAY_SIZE(wm831x_backup_props);
-       backup->get_property = wm831x_backup_get_prop;
-       ret = power_supply_register(&pdev->dev, backup);
-       if (ret)
-               goto err_usb;
-
        irq = platform_get_irq_byname(pdev, "SYSLO");
-       ret = wm831x_request_irq(wm831x, irq, wm831x_syslo_irq,
-                                IRQF_TRIGGER_RISING, "SYSLO",
-                                power);
+       ret = request_threaded_irq(irq, NULL, wm831x_syslo_irq, 
+                                  IRQF_TRIGGER_RISING, "System power low",
+                                  power);
        if (ret != 0) {
                dev_err(&pdev->dev, "Failed to request SYSLO IRQ %d: %d\n",
                        irq, ret);
-               goto err_backup;
+               goto err_usb;
        }
 
        irq = platform_get_irq_byname(pdev, "PWR SRC");
-       ret = wm831x_request_irq(wm831x, irq, wm831x_pwr_src_irq,
-                                IRQF_TRIGGER_RISING, "Power source",
-                                power);
+       ret = request_threaded_irq(irq, NULL, wm831x_pwr_src_irq,
+                                  IRQF_TRIGGER_RISING, "Power source",
+                                  power);
        if (ret != 0) {
                dev_err(&pdev->dev, "Failed to request PWR SRC IRQ %d: %d\n",
                        irq, ret);
                goto err_syslo;
        }
-
+#if 0       //ʹÓòéѯµÄ·½Ê½
        for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
                irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
-               ret = wm831x_request_irq(wm831x, irq, wm831x_bat_irq,
-                                        IRQF_TRIGGER_RISING,
-                                        wm831x_bat_irqs[i],
-                                        power);
+               ret = request_threaded_irq(irq, NULL, wm831x_bat_irq,
+                                          IRQF_TRIGGER_RISING,
+                                          wm831x_bat_irqs[i],
+                                          power);
+               WM_BATT_DBG("%s: %s irq no %d\n", __FUNCTION__, wm831x_bat_irqs[i], irq);
                if (ret != 0) {
                        dev_err(&pdev->dev,
                                "Failed to request %s IRQ %d: %d\n",
@@ -703,21 +884,28 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
                        goto err_bat_irq;
                }
        }
-
+#endif
+       power->interval = TIMER_MS_COUNTS;
+       power->priv.old_level = 100;
+       INIT_WORK(&power->batt_work, wm831x_batt_work);
+       setup_timer(&power->timer, wm831x_batt_timer_handler, (unsigned long)power);
+       power->timer.expires = jiffies + msecs_to_jiffies(1000);
+       add_timer(&power->timer);
+
+       g_wm831x_power = power;
+       printk("%s:wm831x_power initialized\n",__FUNCTION__);
        return ret;
 
 err_bat_irq:
        for (; i >= 0; i--) {
                irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
-               wm831x_free_irq(wm831x, irq, power);
+               free_irq(irq, power);
        }
        irq = platform_get_irq_byname(pdev, "PWR SRC");
-       wm831x_free_irq(wm831x, irq, power);
+       free_irq(irq, power);
 err_syslo:
        irq = platform_get_irq_byname(pdev, "SYSLO");
-       wm831x_free_irq(wm831x, irq, power);
-err_backup:
-       power_supply_unregister(backup);
+       free_irq(irq, power);
 err_usb:
        power_supply_unregister(usb);
 err_battery:
@@ -732,30 +920,52 @@ 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",
        },
old mode 100644 (file)
new mode 100755 (executable)
index 2eefc1a..02d1b3c
@@ -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>
 #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;
+
+       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;
 
-       return wm831x_buckv_set_voltage_int(rdev, reg, min_uV, max_uV);
+       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;
+
+       vsel = wm831x_buckv_select_min_voltage(rdev, uV, uV);
+       if (vsel < 0)
+               return vsel;
 
-       return wm831x_buckv_set_voltage_int(rdev, reg, uV, uV);
+       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);
old mode 100644 (file)
new mode 100755 (executable)
index 4885700..e754528
 #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,
        },
 };
 
old mode 100644 (file)
new mode 100755 (executable)
index 902db56..e44d9b4
 #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);
 
old mode 100644 (file)
new mode 100755 (executable)
index 79795cd..b6c830b
@@ -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,
 
old mode 100644 (file)
new mode 100755 (executable)
index 467bdb7..e985c24
 #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;
 }
old mode 100644 (file)
new mode 100755 (executable)
index 775bcd8..8c4b2d5
@@ -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",
 };