*/
#include <linux/kernel.h>
+#include <linux/slab.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/mfd/core.h>
#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;
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)
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)
{
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;
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;
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);
}
}
.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,
};
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;
#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>
--- /dev/null
+/*
+ * 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");
#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,
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");
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;
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:
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);
#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>
#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,
};
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)
*/
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);
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) {
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);
}
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
*
},
};
+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,
};
static struct mfd_cell wm8310_devs[] = {
+ {
+ .name = "wm831x-backup",
+ },
{
.name = "wm831x-buckv",
.id = 1,
.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,
};
static struct mfd_cell wm8312_devs[] = {
+ {
+ .name = "wm831x-backup",
+ },
{
.name = "wm831x-buckv",
.id = 1,
},
};
+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",
/*
* 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;
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);
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:
* 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 */
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;
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);
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;
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], ®, 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");
--- /dev/null
+/*
+ * wm831x-i2c.c -- I2C access for Wolfson WM831x PMICs
+ *
+ * Copyright 2009,2010 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/mfd/core.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/pdata.h>
+
+static int wm831x_i2c_read_device(struct wm831x *wm831x, unsigned short reg,
+ int bytes, void *dest)
+{
+ struct i2c_client *i2c = wm831x->control_data;
+ int ret;
+ u16 r = cpu_to_be16(reg);
+
+ ret = i2c_master_send(i2c, (unsigned char *)&r, 2);
+ if (ret < 0)
+ return ret;
+ if (ret != 2)
+ return -EIO;
+
+ ret = i2c_master_recv(i2c, dest, bytes);
+ if (ret < 0)
+ return ret;
+ if (ret != bytes)
+ return -EIO;
+ return 0;
+}
+
+/* Currently we allocate the write buffer on the stack; this is OK for
+ * small writes - if we need to do large writes this will need to be
+ * revised.
+ */
+static int wm831x_i2c_write_device(struct wm831x *wm831x, unsigned short reg,
+ int bytes, void *src)
+{
+ struct i2c_client *i2c = wm831x->control_data;
+ unsigned char msg[bytes + 2];
+ int ret;
+
+ reg = cpu_to_be16(reg);
+ memcpy(&msg[0], ®, 2);
+ memcpy(&msg[2], src, bytes);
+
+ ret = i2c_master_send(i2c, msg, bytes + 2);
+ if (ret < 0)
+ return ret;
+ if (ret < bytes + 2)
+ return -EIO;
+
+ return 0;
+}
+
+static int wm831x_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct wm831x *wm831x;
+ int ret,gpio,irq;
+
+ wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL);
+ if (wm831x == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, wm831x);
+
+ gpio = i2c->irq;
+ ret = gpio_request(gpio, "wm831x");
+ if (ret) {
+ printk( "failed to request rk gpio irq for wm831x \n");
+ return ret;
+ }
+ gpio_pull_updown(gpio, GPIOPullUp);
+ if (ret) {
+ printk("failed to pull up gpio irq for wm831x \n");
+ return ret;
+ }
+ irq = gpio_to_irq(gpio);
+
+ wm831x->dev = &i2c->dev;
+ wm831x->control_data = i2c;
+ wm831x->read_dev = wm831x_i2c_read_device;
+ wm831x->write_dev = wm831x_i2c_write_device;
+
+ return wm831x_device_init(wm831x, id->driver_data, irq);
+}
+
+static int wm831x_i2c_remove(struct i2c_client *i2c)
+{
+ struct wm831x *wm831x = i2c_get_clientdata(i2c);
+
+ wm831x_device_exit(wm831x);
+
+ return 0;
+}
+
+static int wm831x_i2c_suspend(struct i2c_client *i2c, pm_message_t mesg)
+{
+ struct wm831x *wm831x = i2c_get_clientdata(i2c);
+
+ return wm831x_device_suspend(wm831x);
+}
+
+static int wm831x_i2c_resume(struct i2c_client *i2c)
+{
+ struct wm831x *wm831x = i2c_get_clientdata(i2c);
+ int i;
+ //set some intterupt again while resume
+ for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) {
+ //printk("irq_masks_cur[%d]=0x%x\n",i,wm831x->irq_masks_cur[i]);
+
+ if (wm831x->irq_masks_cur[i] != wm831x->irq_masks_cache[i]) {
+ wm831x->irq_masks_cache[i] = wm831x->irq_masks_cur[i];
+ wm831x_reg_write(wm831x,
+ WM831X_INTERRUPT_STATUS_1_MASK + i,
+ wm831x->irq_masks_cur[i]);
+ }
+
+ }
+
+ return 0;
+}
+
+void wm831x_i2c_shutdown(struct i2c_client *i2c)
+{
+ struct wm831x *wm831x = i2c_get_clientdata(i2c);
+ printk("%s\n", __FUNCTION__);
+ wm831x_device_shutdown(wm831x);
+}
+
+static const struct i2c_device_id wm831x_i2c_id[] = {
+ { "wm8310", WM8310 },
+ { "wm8311", WM8311 },
+ { "wm8312", WM8312 },
+ { "wm8320", WM8320 },
+ { "wm8321", WM8321 },
+ { "wm8325", WM8325 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm831x_i2c_id);
+
+
+static struct i2c_driver wm831x_i2c_driver = {
+ .driver = {
+ .name = "wm831x",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm831x_i2c_probe,
+ .remove = wm831x_i2c_remove,
+ .suspend = wm831x_i2c_suspend,
+ .resume = wm831x_i2c_resume,
+ .shutdown = wm831x_i2c_shutdown,
+ .id_table = wm831x_i2c_id,
+};
+
+static int __init wm831x_i2c_init(void)
+{
+ int ret;
+ printk("%s \n", __FUNCTION__);
+ ret = i2c_add_driver(&wm831x_i2c_driver);
+ if (ret != 0)
+ pr_err("Failed to register wm831x I2C driver: %d\n", ret);
+
+ return ret;
+}
+subsys_initcall(wm831x_i2c_init);
+
+static void __exit wm831x_i2c_exit(void)
+{
+ i2c_del_driver(&wm831x_i2c_driver);
+}
+module_exit(wm831x_i2c_exit);
#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[] = {
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)
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
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) {
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;
}
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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");
#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)
{
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
{ 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;
{
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,
reg1 = 0;
reg2 = 0;
+ reg3 = 0;
if (!pdata->enable) {
dev_info(wm831x->dev, "Battery charger disabled\n");
pdata->timeout, ®2,
"charger timeout", "min");
+ wm831x_battey_apply_config(wm831x, chg_syslos, ARRAY_SIZE(chg_syslos),
+ pdata->syslo, ®3,
+ "syslo voltage", "mV");
+
+ wm831x_battey_apply_config(wm831x, chg_sysoks, ARRAY_SIZE(chg_sysoks),
+ pdata->sysok, ®3,
+ "sysok voltage", "mV");
+
ret = wm831x_reg_unlock(wm831x);
if (ret != 0) {
dev_err(wm831x->dev, "Failed to unlock registers: %d\n", ret);
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 |
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);
}
if (ret < 0)
return ret;
+
switch (ret & WM831X_CHG_STATE_MASK) {
case WM831X_CHG_STATE_OFF:
*status = POWER_SUPPLY_STATUS_NOT_CHARGING;
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)
{
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);
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;
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,
};
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);
/*********************************************************************
- * 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)
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);
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;
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",
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:
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",
},
#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)
{
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)
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 */
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,
.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);
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)
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)) {
err_regulator:
regulator_unregister(dcdc->regulator);
err:
+ if (dcdc->dvs_gpio)
+ gpio_free(dcdc->dvs_gpio);
kfree(dcdc);
return ret;
}
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;
.remove = __devexit_p(wm831x_buckv_remove),
.driver = {
.name = "wm831x-buckv",
+ .owner = THIS_MODULE,
},
};
.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)
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);
.remove = __devexit_p(wm831x_buckp_remove),
.driver = {
.name = "wm831x-buckp",
+ .owner = THIS_MODULE,
},
};
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);
.remove = __devexit_p(wm831x_boostp_remove),
.driver = {
.name = "wm831x-boostp",
+ .owner = THIS_MODULE,
},
};
{
struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev);
+ platform_set_drvdata(pdev, NULL);
+
regulator_unregister(dcdc->regulator);
kfree(dcdc);
.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);
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);
#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;
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);
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;
}
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;
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;
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;
}
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);
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);
.remove = __devexit_p(wm831x_isink_remove),
.driver = {
.name = "wm831x-isink",
+ .owner = THIS_MODULE,
},
};
#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
#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;
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);
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);
}
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);
}
{
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);
}
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);
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,
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;
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;
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,
.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)
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;
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);
.remove = __devexit_p(wm831x_gp_ldo_remove),
.driver = {
.name = "wm831x-ldo",
+ .owner = THIS_MODULE,
},
};
{
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);
}
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);
}
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)
.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)
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;
.remove = __devexit_p(wm831x_aldo_remove),
.driver = {
.name = "wm831x-aldo",
+ .owner = THIS_MODULE,
},
};
.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)
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;
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);
if (ret != 0)
pr_err("Failed to register WM831x alive LDO driver: %d\n",
ret);
-
- return 0;
+ return 0;
}
subsys_initcall(wm831x_ldo_init);
#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>
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;
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);
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);
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,
#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,
/* 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;
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;
if (bl->props.state & BL_CORE_SUSPENDED)
brightness = 0;
-
+
return wm831x_backlight_set(bl, brightness);
}
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);
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) {
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",
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;
}
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;
}
return count;
}
-static struct watchdog_info ident = {
+static const struct watchdog_info ident = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.identity = "WM831x Watchdog",
};