From 127fdac55dfa6bceae4fc5cca44912d8e043e738 Mon Sep 17 00:00:00 2001 From: luowei Date: Sat, 26 Feb 2011 14:28:56 +0800 Subject: [PATCH] move wm831x code from rk2818 --- Documentation/hwmon/wm831x | 0 drivers/gpio/wm831x-gpio.c | 112 ++++- drivers/hwmon/wm831x-hwmon.c | 1 + drivers/input/keyboard/wm831x_gpio_keys.c | 468 ++++++++++++++++++ drivers/input/misc/wm831x-on.c | 153 +++++- drivers/leds/leds-wm831x-status.c | 1 + drivers/mfd/wm831x-core.c | 575 ++++++++++++++++------ drivers/mfd/wm831x-i2c.c | 188 +++++++ drivers/mfd/wm831x-irq.c | 367 ++++++++++---- drivers/mfd/wm831x-otp.c | 0 drivers/mfd/wm831x-spi.c | 232 +++++++++ drivers/power/wm831x_backup.c | 234 +++++++++ drivers/power/wm831x_power.c | 556 ++++++++++++++------- drivers/regulator/wm831x-dcdc.c | 245 ++++++++- drivers/regulator/wm831x-isink.c | 21 +- drivers/regulator/wm831x-ldo.c | 119 ++++- drivers/rtc/rtc-wm831x.c | 57 ++- drivers/video/backlight/wm831x_bl.c | 107 +++- drivers/watchdog/wm831x_wdt.c | 2 +- 19 files changed, 2892 insertions(+), 546 deletions(-) mode change 100644 => 100755 Documentation/hwmon/wm831x mode change 100644 => 100755 drivers/gpio/wm831x-gpio.c mode change 100644 => 100755 drivers/hwmon/wm831x-hwmon.c create mode 100755 drivers/input/keyboard/wm831x_gpio_keys.c mode change 100644 => 100755 drivers/input/misc/wm831x-on.c mode change 100644 => 100755 drivers/leds/leds-wm831x-status.c mode change 100644 => 100755 drivers/mfd/wm831x-core.c create mode 100755 drivers/mfd/wm831x-i2c.c mode change 100644 => 100755 drivers/mfd/wm831x-irq.c mode change 100644 => 100755 drivers/mfd/wm831x-otp.c create mode 100755 drivers/mfd/wm831x-spi.c create mode 100755 drivers/power/wm831x_backup.c mode change 100644 => 100755 drivers/power/wm831x_power.c mode change 100644 => 100755 drivers/regulator/wm831x-dcdc.c mode change 100644 => 100755 drivers/regulator/wm831x-isink.c mode change 100644 => 100755 drivers/regulator/wm831x-ldo.c mode change 100644 => 100755 drivers/rtc/rtc-wm831x.c mode change 100644 => 100755 drivers/video/backlight/wm831x_bl.c mode change 100644 => 100755 drivers/watchdog/wm831x_wdt.c diff --git a/Documentation/hwmon/wm831x b/Documentation/hwmon/wm831x old mode 100644 new mode 100755 diff --git a/drivers/gpio/wm831x-gpio.c b/drivers/gpio/wm831x-gpio.c old mode 100644 new mode 100755 index 9a270478acac..382ae877f370 --- a/drivers/gpio/wm831x-gpio.c +++ b/drivers/gpio/wm831x-gpio.c @@ -13,6 +13,7 @@ */ #include +#include #include #include #include @@ -22,8 +23,7 @@ #include #include #include - -#define WM831X_GPIO_MAX 16 +#include struct wm831x_gpio { struct wm831x *wm831x; @@ -35,14 +35,34 @@ static inline struct wm831x_gpio *to_wm831x_gpio(struct gpio_chip *chip) return container_of(chip, struct wm831x_gpio, gpio_chip); } +static int wm831x_gpio_pull_up_down(struct gpio_chip *chip, unsigned offset, unsigned value) +{ + struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); + struct wm831x *wm831x = wm831x_gpio->wm831x; + + if(value == GPIOPullUp) + value = WM831X_GPIO_PULL_UP; + else if(value == GPIOPullDown) + value = WM831X_GPIO_PULL_DOWN; + else if(value == GPIONormal) + value = WM831X_GPIO_PULL_NONE; + //printk("wm831x_gpio_pull_up_down=%x,%x\n",WM831X_GPIO1_CONTROL + offset,value); + return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + offset, + WM831X_GPN_PULL_MASK, value); +} + static int wm831x_gpio_direction_in(struct gpio_chip *chip, unsigned offset) { struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); struct wm831x *wm831x = wm831x_gpio->wm831x; + int val = WM831X_GPN_DIR; + if (wm831x->has_gpio_ena) + val |= WM831X_GPN_TRI; + //printk("wm831x_gpio_direction_in=%x,%x\n",WM831X_GPIO1_CONTROL + offset,val); return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + offset, - WM831X_GPN_DIR | WM831X_GPN_TRI, - WM831X_GPN_DIR); + WM831X_GPN_DIR | WM831X_GPN_TRI | + WM831X_GPN_FN_MASK, val); } static int wm831x_gpio_get(struct gpio_chip *chip, unsigned offset) @@ -50,15 +70,19 @@ static int wm831x_gpio_get(struct gpio_chip *chip, unsigned offset) struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); struct wm831x *wm831x = wm831x_gpio->wm831x; int ret; - + int gpn_pol; + + ret = wm831x_reg_read(wm831x, WM831X_GPIO1_CONTROL + offset); + if (ret < 0) + return ret; + gpn_pol = (ret & WM831X_GPN_POL_MASK) >> WM831X_GPN_POL_SHIFT; + ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL); + //printk("wm831x_gpio_get=%x,%d,%d\n",ret,offset,gpn_pol); if (ret < 0) return ret; - - if (ret & 1 << offset) - return 1; - else - return 0; + + return !((ret>>offset)^gpn_pol); } static void wm831x_gpio_set(struct gpio_chip *chip, unsigned offset, int value) @@ -75,10 +99,15 @@ static int wm831x_gpio_direction_out(struct gpio_chip *chip, { struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); struct wm831x *wm831x = wm831x_gpio->wm831x; + int val = 0; int ret; + if (wm831x->has_gpio_ena) + val |= WM831X_GPN_TRI; + //printk("wm831x_gpio_direction_out=%x,%x\n",WM831X_GPIO1_CONTROL + offset,val); ret = wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + offset, - WM831X_GPN_DIR | WM831X_GPN_TRI, 0); + WM831X_GPN_DIR | WM831X_GPN_TRI | + WM831X_GPN_FN_MASK | WM831X_GPN_POL_MASK, val|WM831X_GPN_POL); if (ret < 0) return ret; @@ -88,12 +117,54 @@ static int wm831x_gpio_direction_out(struct gpio_chip *chip, return 0; } +static int wm831x_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); + struct wm831x *wm831x = wm831x_gpio->wm831x; + + if (!wm831x->irq_base) + return -EINVAL; + + return wm831x->irq_base + WM831X_IRQ_GPIO_1 + offset; +} + +static int wm831x_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, + unsigned debounce) +{ + struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); + struct wm831x *wm831x = wm831x_gpio->wm831x; + int reg = WM831X_GPIO1_CONTROL + offset; + int ret, fn; + + ret = wm831x_reg_read(wm831x, reg); + if (ret < 0) + return ret; + + switch (ret & WM831X_GPN_FN_MASK) { + case 0: + case 1: + break; + default: + /* Not in GPIO mode */ + return -EBUSY; + } + + if (debounce >= 32 && debounce <= 64) + fn = 0; + else if (debounce >= 4000 && debounce <= 8000) + fn = 1; + else + return -EINVAL; + //printk("wm831x_gpio_set_debounce=%x,%x\n",WM831X_GPIO1_CONTROL + offset,fn); + return wm831x_set_bits(wm831x, reg, WM831X_GPN_FN_MASK, fn); +} + #ifdef CONFIG_DEBUG_FS static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) { struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); struct wm831x *wm831x = wm831x_gpio->wm831x; - int i; + int i, tristated; for (i = 0; i < chip->ngpio; i++) { int gpio = i + chip->base; @@ -160,15 +231,19 @@ static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) break; } + tristated = reg & WM831X_GPN_TRI; + if (wm831x->has_gpio_ena) + tristated = !tristated; + seq_printf(s, " %s %s %s %s%s\n" " %s%s (0x%4x)\n", reg & WM831X_GPN_DIR ? "in" : "out", wm831x_gpio_get(chip, i) ? "high" : "low", pull, powerdomain, - reg & WM831X_GPN_POL ? " inverted" : "", + reg & WM831X_GPN_POL ? "" : " inverted", reg & WM831X_GPN_OD ? "open-drain" : "CMOS", - reg & WM831X_GPN_TRI ? " tristated" : "", + tristated ? " tristated" : "", reg); } } @@ -183,6 +258,9 @@ static struct gpio_chip template_chip = { .get = wm831x_gpio_get, .direction_output = wm831x_gpio_direction_out, .set = wm831x_gpio_set, + .pull_updown = wm831x_gpio_pull_up_down, + .to_irq = wm831x_gpio_to_irq, + .set_debounce = wm831x_gpio_set_debounce, .dbg_show = wm831x_gpio_dbg_show, .can_sleep = 1, }; @@ -193,14 +271,14 @@ static int __devinit wm831x_gpio_probe(struct platform_device *pdev) struct wm831x_pdata *pdata = wm831x->dev->platform_data; struct wm831x_gpio *wm831x_gpio; int ret; - + printk("%s\n",__FUNCTION__); wm831x_gpio = kzalloc(sizeof(*wm831x_gpio), GFP_KERNEL); if (wm831x_gpio == NULL) return -ENOMEM; - + wm831x_gpio->wm831x = wm831x; wm831x_gpio->gpio_chip = template_chip; - wm831x_gpio->gpio_chip.ngpio = WM831X_GPIO_MAX; + wm831x_gpio->gpio_chip.ngpio = wm831x->num_gpio; wm831x_gpio->gpio_chip.dev = &pdev->dev; if (pdata && pdata->gpio_base) wm831x_gpio->gpio_chip.base = pdata->gpio_base; diff --git a/drivers/hwmon/wm831x-hwmon.c b/drivers/hwmon/wm831x-hwmon.c old mode 100644 new mode 100755 index c16e9e74c356..97b1f834a471 --- a/drivers/hwmon/wm831x-hwmon.c +++ b/drivers/hwmon/wm831x-hwmon.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include diff --git a/drivers/input/keyboard/wm831x_gpio_keys.c b/drivers/input/keyboard/wm831x_gpio_keys.c new file mode 100755 index 000000000000..db0f44811ce7 --- /dev/null +++ b/drivers/input/keyboard/wm831x_gpio_keys.c @@ -0,0 +1,468 @@ +/* + * Driver for keys on GPIO lines capable of generating interrupts. + * + * Copyright 2005 Phil Blundell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#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 "); +MODULE_DESCRIPTION("Keyboard driver for WM831x GPIOs"); +MODULE_ALIAS("platform:wm831x_gpio-keys"); diff --git a/drivers/input/misc/wm831x-on.c b/drivers/input/misc/wm831x-on.c old mode 100644 new mode 100755 index ba4f5dd7c60e..8705da4d30b9 --- a/drivers/input/misc/wm831x-on.c +++ b/drivers/input/misc/wm831x-on.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -26,17 +27,106 @@ #include #include #include +#include +#include +#include + +#if 0 +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif struct wm831x_on { struct input_dev *dev; struct delayed_work work; struct wm831x *wm831x; + struct wake_lock wm831x_on_wake; }; +struct wm831x_on *g_wm831x_on; + +void rk28_send_wakeup_key(void) +{ + printk("%s\n", __FUNCTION__); + if(!g_wm831x_on) + { + printk("%s:addr err!\n",__FUNCTION__); + return; + } + input_report_key(g_wm831x_on->dev, KEY_POWER, 1); + input_sync(g_wm831x_on->dev); + input_report_key(g_wm831x_on->dev, KEY_POWER, 0); + input_sync(g_wm831x_on->dev); + printk("%s end\n", __FUNCTION__); +} + +#if 1 + +static int wm831x_on_suspend_noirq(struct device *dev) +{ + DBG("%s\n",__FUNCTION__); + return 0; +} + +static int wm831x_on_resume_noirq(struct device *dev) +{ + int poll, ret; + + if(!g_wm831x_on) + { + printk("%s:addr err!\n",__FUNCTION__); + return; + } + + ret = wm831x_reg_read(g_wm831x_on->wm831x, WM831X_ON_PIN_CONTROL); + if (ret >= 0) { + poll = !(ret & WM831X_ON_PIN_STS); + //poll = 1; + input_report_key(g_wm831x_on->dev, KEY_POWER, poll); + input_sync(g_wm831x_on->dev); + DBG("%s:poll=%d,ret=0x%x\n",__FUNCTION__,poll,ret); + } + DBG("%s\n",__FUNCTION__); + return 0; +} + +static struct dev_pm_ops wm831x_on_dev_pm_ops = { + .suspend_noirq = wm831x_on_suspend_noirq, + .resume_noirq = wm831x_on_resume_noirq, +}; + + +static struct platform_driver wm831x_on_pm_driver = { + .driver = { + .name = "wm831x_on", + .pm = &wm831x_on_dev_pm_ops, + }, +}; + +static struct platform_device wm831x_on_pm_device = { + .name = "wm831x_on", + .id = -1, + .dev = { + .driver = &wm831x_on_pm_driver.driver, + } + +}; + +static inline void wm831x_on_pm_init(void) +{ + if (platform_driver_register(&wm831x_on_pm_driver) == 0) + (void) platform_device_register(&wm831x_on_pm_device); +} + + +#endif + /* * The chip gives us an interrupt when the ON pin is asserted but we * then need to poll to see when the pin is deasserted. */ + static void wm831x_poll_on(struct work_struct *work) { struct wm831x_on *wm831x_on = container_of(work, struct wm831x_on, @@ -47,43 +137,82 @@ static void wm831x_poll_on(struct work_struct *work) ret = wm831x_reg_read(wm831x, WM831X_ON_PIN_CONTROL); if (ret >= 0) { poll = !(ret & WM831X_ON_PIN_STS); - input_report_key(wm831x_on->dev, KEY_POWER, poll); input_sync(wm831x_on->dev); + DBG("%s:poll=%d,ret=0x%x\n",__FUNCTION__,poll,ret); } else { dev_err(wm831x->dev, "Failed to read ON status: %d\n", ret); poll = 1; } if (poll) - schedule_delayed_work(&wm831x_on->work, 100); + schedule_delayed_work(&wm831x_on->work, 2); + else + wake_unlock(&wm831x->handle_wake); + //wake_unlock(&wm831x_on->wm831x_on_wake); } +#if 0 static irqreturn_t wm831x_on_irq(int irq, void *data) { struct wm831x_on *wm831x_on = data; - + wake_lock(&wm831x_on->wm831x_on_wake); schedule_delayed_work(&wm831x_on->work, 0); + return IRQ_HANDLED; +} + +#else + +static irqreturn_t wm831x_on_irq(int irq, void *data) +{ + struct wm831x_on *wm831x_on = data; + struct wm831x *wm831x = wm831x_on->wm831x; + int poll, ret; + + //wake_lock(&wm831x_on->wm831x_on_wake); + +read_again: + + ret = wm831x_reg_read(wm831x, WM831X_ON_PIN_CONTROL); + if (ret >= 0) { + poll = !(ret & WM831X_ON_PIN_STS); + input_report_key(wm831x_on->dev, KEY_POWER, poll); + input_sync(wm831x_on->dev); + DBG("%s:poll=%d,ret=0x%x\n",__FUNCTION__,poll,ret); + } else { + dev_err(wm831x->dev, "Failed to read ON status: %d\n", ret); + poll = 1; + } + if (poll) + schedule_delayed_work(&wm831x_on->work, 0); + else + wake_unlock(&wm831x->handle_wake); + //wake_unlock(&wm831x_on->wm831x_on_wake); + return IRQ_HANDLED; } +#endif + static int __devinit wm831x_on_probe(struct platform_device *pdev) { - struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); + struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);; struct wm831x_on *wm831x_on; int irq = platform_get_irq(pdev, 0); int ret; - + printk("%s irq=%d\n", __FUNCTION__,irq); wm831x_on = kzalloc(sizeof(struct wm831x_on), GFP_KERNEL); if (!wm831x_on) { dev_err(&pdev->dev, "Can't allocate data\n"); return -ENOMEM; } + wm831x_on->wm831x = wm831x; INIT_DELAYED_WORK(&wm831x_on->work, wm831x_poll_on); - + wake_lock_init(&wm831x_on->wm831x_on_wake, WAKE_LOCK_SUSPEND, "wm831x_on_wake"); + wm831x_on->dev = input_allocate_device(); if (!wm831x_on->dev) { dev_err(&pdev->dev, "Can't allocate input dev\n"); @@ -96,9 +225,13 @@ static int __devinit wm831x_on_probe(struct platform_device *pdev) wm831x_on->dev->name = "wm831x_on"; wm831x_on->dev->phys = "wm831x_on/input0"; wm831x_on->dev->dev.parent = &pdev->dev; + g_wm831x_on = wm831x_on; - ret = wm831x_request_irq(wm831x, irq, wm831x_on_irq, - IRQF_TRIGGER_RISING, "wm831x_on", wm831x_on); + wm831x_on_pm_init(); + + ret = request_threaded_irq(irq, NULL, wm831x_on_irq, + IRQF_TRIGGER_RISING, "wm831x_on", + wm831x_on); if (ret < 0) { dev_err(&pdev->dev, "Unable to request IRQ: %d\n", ret); goto err_input_dev; @@ -114,7 +247,7 @@ static int __devinit wm831x_on_probe(struct platform_device *pdev) return 0; err_irq: - wm831x_free_irq(wm831x, irq, NULL); + free_irq(irq, wm831x_on); err_input_dev: input_free_device(wm831x_on->dev); err: @@ -127,7 +260,7 @@ static int __devexit wm831x_on_remove(struct platform_device *pdev) struct wm831x_on *wm831x_on = platform_get_drvdata(pdev); int irq = platform_get_irq(pdev, 0); - wm831x_free_irq(wm831x_on->wm831x, irq, wm831x_on); + free_irq(irq, wm831x_on); cancel_delayed_work_sync(&wm831x_on->work); input_unregister_device(wm831x_on->dev); kfree(wm831x_on); diff --git a/drivers/leds/leds-wm831x-status.c b/drivers/leds/leds-wm831x-status.c old mode 100644 new mode 100755 index c586d05e336a..ef5c24140a44 --- a/drivers/leds/leds-wm831x-status.c +++ b/drivers/leds/leds-wm831x-status.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c old mode 100644 new mode 100755 index 7f27576ca046..928d7f1df191 --- a/drivers/mfd/wm831x-core.c +++ b/drivers/mfd/wm831x-core.c @@ -14,10 +14,11 @@ #include #include -#include #include #include #include +#include +#include #include #include @@ -25,10 +26,13 @@ #include #include #include +#include + /* Current settings - values are 2*2^(reg_val/4) microamps. These are * exported since they are used by multiple drivers. */ + extern int reboot_cmd_get(void); int wm831x_isinkv_values[WM831X_ISINK_MAX_ISEL + 1] = { 2, 2, @@ -89,12 +93,6 @@ int wm831x_isinkv_values[WM831X_ISINK_MAX_ISEL + 1] = { }; EXPORT_SYMBOL_GPL(wm831x_isinkv_values); -enum wm831x_parent { - WM8310 = 0, - WM8311 = 1, - WM8312 = 2, -}; - static int wm831x_reg_locked(struct wm831x *wm831x, unsigned short reg) { if (!wm831x->locked) @@ -320,8 +318,11 @@ EXPORT_SYMBOL_GPL(wm831x_set_bits); */ int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input) { - int tries = 10; - int ret, src; + int ret, src, irq_masked, timeout; + + /* Are we using the interrupt? */ + irq_masked = wm831x_reg_read(wm831x, WM831X_INTERRUPT_STATUS_1_MASK); + irq_masked &= WM831X_AUXADC_DATA_EINT; mutex_lock(&wm831x->auxadc_lock); @@ -341,6 +342,9 @@ int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input) goto out; } + /* Clear any notification from a very late arriving interrupt */ + try_wait_for_completion(&wm831x->auxadc_done); + ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, WM831X_AUX_CVT_ENA, WM831X_AUX_CVT_ENA); if (ret < 0) { @@ -348,18 +352,46 @@ int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input) goto disable; } - do { - msleep(1); - - ret = wm831x_reg_read(wm831x, WM831X_AUXADC_CONTROL); - if (ret < 0) - ret = WM831X_AUX_CVT_ENA; - } while ((ret & WM831X_AUX_CVT_ENA) && --tries); - - if (ret & WM831X_AUX_CVT_ENA) { - dev_err(wm831x->dev, "Timed out reading AUXADC\n"); - ret = -EBUSY; - goto disable; + if (irq_masked) { + /* If we're not using interrupts then poll the + * interrupt status register */ + timeout = 5; + while (timeout) { + msleep(1); + + ret = wm831x_reg_read(wm831x, + WM831X_INTERRUPT_STATUS_1); + if (ret < 0) { + dev_err(wm831x->dev, + "ISR 1 read failed: %d\n", ret); + goto disable; + } + + /* Did it complete? */ + if (ret & WM831X_AUXADC_DATA_EINT) { + wm831x_reg_write(wm831x, + WM831X_INTERRUPT_STATUS_1, + WM831X_AUXADC_DATA_EINT); + break; + } else { + dev_err(wm831x->dev, + "AUXADC conversion timeout\n"); + ret = -EBUSY; + goto disable; + } + } + } else { + /* If we are using interrupts then wait for the + * interrupt to complete. Use an extremely long + * timeout to handle situations with heavy load where + * the notification of the interrupt may be delayed by + * threaded IRQ handling. */ + if (!wait_for_completion_timeout(&wm831x->auxadc_done, + msecs_to_jiffies(2000))) { + dev_err(wm831x->dev, "Timed out waiting for AUXADC\n"); + ret = -EBUSY; + goto disable; + } } ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA); @@ -389,6 +421,15 @@ out: } EXPORT_SYMBOL_GPL(wm831x_auxadc_read); +static irqreturn_t wm831x_auxadc_irq(int irq, void *irq_data) +{ + struct wm831x *wm831x = irq_data; + + complete(&wm831x->auxadc_done); + + return IRQ_HANDLED; +} + /** * wm831x_auxadc_read_uv: Read a voltage from the WM831x AUXADC * @@ -478,6 +519,20 @@ static struct resource wm831x_dcdc4_resources[] = { }, }; +static struct resource wm8320_dcdc4_buck_resources[] = { + { + .start = WM831X_DC4_CONTROL, + .end = WM832X_DC4_SLEEP_CONTROL, + .flags = IORESOURCE_IO, + }, + { + .name = "UV", + .start = WM831X_IRQ_UV_DC4, + .end = WM831X_IRQ_UV_DC4, + .flags = IORESOURCE_IRQ, + }, +}; + static struct resource wm831x_gpio_resources[] = { { .start = WM831X_IRQ_GPIO_1, @@ -793,6 +848,9 @@ static struct resource wm831x_wdt_resources[] = { }; static struct mfd_cell wm8310_devs[] = { + { + .name = "wm831x-backup", + }, { .name = "wm831x-buckv", .id = 1, @@ -943,9 +1001,19 @@ static struct mfd_cell wm8310_devs[] = { .num_resources = ARRAY_SIZE(wm831x_wdt_resources), .resources = wm831x_wdt_resources, }, +#if defined(CONFIG_KEYBOARD_WM831X_GPIO) + { + .name = "wm831x_gpio-keys", + .num_resources = 0, + }, +#endif + }; static struct mfd_cell wm8311_devs[] = { + { + .name = "wm831x-backup", + }, { .name = "wm831x-buckv", .id = 1, @@ -1080,6 +1148,9 @@ static struct mfd_cell wm8311_devs[] = { }; static struct mfd_cell wm8312_devs[] = { + { + .name = "wm831x-backup", + }, { .name = "wm831x-buckv", .id = 1, @@ -1237,6 +1308,137 @@ static struct mfd_cell wm8312_devs[] = { }, }; +static struct mfd_cell wm8320_devs[] = { + { + .name = "wm831x-backup", + }, + { + .name = "wm831x-buckv", + .id = 1, + .num_resources = ARRAY_SIZE(wm831x_dcdc1_resources), + .resources = wm831x_dcdc1_resources, + }, + { + .name = "wm831x-buckv", + .id = 2, + .num_resources = ARRAY_SIZE(wm831x_dcdc2_resources), + .resources = wm831x_dcdc2_resources, + }, + { + .name = "wm831x-buckp", + .id = 3, + .num_resources = ARRAY_SIZE(wm831x_dcdc3_resources), + .resources = wm831x_dcdc3_resources, + }, + { + .name = "wm831x-buckp", + .id = 4, + .num_resources = ARRAY_SIZE(wm8320_dcdc4_buck_resources), + .resources = wm8320_dcdc4_buck_resources, + }, + { + .name = "wm831x-gpio", + .num_resources = ARRAY_SIZE(wm831x_gpio_resources), + .resources = wm831x_gpio_resources, + }, + { + .name = "wm831x-hwmon", + }, + { + .name = "wm831x-ldo", + .id = 1, + .num_resources = ARRAY_SIZE(wm831x_ldo1_resources), + .resources = wm831x_ldo1_resources, + }, + { + .name = "wm831x-ldo", + .id = 2, + .num_resources = ARRAY_SIZE(wm831x_ldo2_resources), + .resources = wm831x_ldo2_resources, + }, + { + .name = "wm831x-ldo", + .id = 3, + .num_resources = ARRAY_SIZE(wm831x_ldo3_resources), + .resources = wm831x_ldo3_resources, + }, + { + .name = "wm831x-ldo", + .id = 4, + .num_resources = ARRAY_SIZE(wm831x_ldo4_resources), + .resources = wm831x_ldo4_resources, + }, + { + .name = "wm831x-ldo", + .id = 5, + .num_resources = ARRAY_SIZE(wm831x_ldo5_resources), + .resources = wm831x_ldo5_resources, + }, + { + .name = "wm831x-ldo", + .id = 6, + .num_resources = ARRAY_SIZE(wm831x_ldo6_resources), + .resources = wm831x_ldo6_resources, + }, + { + .name = "wm831x-aldo", + .id = 7, + .num_resources = ARRAY_SIZE(wm831x_ldo7_resources), + .resources = wm831x_ldo7_resources, + }, + { + .name = "wm831x-aldo", + .id = 8, + .num_resources = ARRAY_SIZE(wm831x_ldo8_resources), + .resources = wm831x_ldo8_resources, + }, + { + .name = "wm831x-aldo", + .id = 9, + .num_resources = ARRAY_SIZE(wm831x_ldo9_resources), + .resources = wm831x_ldo9_resources, + }, + { + .name = "wm831x-aldo", + .id = 10, + .num_resources = ARRAY_SIZE(wm831x_ldo10_resources), + .resources = wm831x_ldo10_resources, + }, + { + .name = "wm831x-alive-ldo", + .id = 11, + .num_resources = ARRAY_SIZE(wm831x_ldo11_resources), + .resources = wm831x_ldo11_resources, + }, + { + .name = "wm831x-on", + .num_resources = ARRAY_SIZE(wm831x_on_resources), + .resources = wm831x_on_resources, + }, + { + .name = "wm831x-rtc", + .num_resources = ARRAY_SIZE(wm831x_rtc_resources), + .resources = wm831x_rtc_resources, + }, + { + .name = "wm831x-status", + .id = 1, + .num_resources = ARRAY_SIZE(wm831x_status1_resources), + .resources = wm831x_status1_resources, + }, + { + .name = "wm831x-status", + .id = 2, + .num_resources = ARRAY_SIZE(wm831x_status2_resources), + .resources = wm831x_status2_resources, + }, + { + .name = "wm831x-watchdog", + .num_resources = ARRAY_SIZE(wm831x_wdt_resources), + .resources = wm831x_wdt_resources, + }, +}; + static struct mfd_cell backlight_devs[] = { { .name = "wm831x-backlight", @@ -1246,7 +1448,7 @@ static struct mfd_cell backlight_devs[] = { /* * Instantiate the generic non-control parts of the device. */ -static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) +int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) { struct wm831x_pdata *pdata = wm831x->dev->platform_data; int rev; @@ -1256,6 +1458,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) mutex_init(&wm831x->io_lock); mutex_init(&wm831x->key_lock); mutex_init(&wm831x->auxadc_lock); + init_completion(&wm831x->auxadc_done); dev_set_drvdata(wm831x->dev, wm831x); ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID); @@ -1282,50 +1485,70 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) goto err; } + /* Some engineering samples do not have the ID set, rely on + * the device being registered correctly. + */ + if (ret == 0) { + dev_info(wm831x->dev, "Device is an engineering sample\n"); + ret = id; + } + switch (ret) { - case 0x8310: + case WM8310: parent = WM8310; - switch (rev) { - case 0: - dev_info(wm831x->dev, "WM8310 revision %c\n", - 'A' + rev); - break; + wm831x->num_gpio = 12; + wm831x->charger_irq_wake = 1; + if (rev > 0) { + wm831x->has_gpio_ena = 1; + wm831x->has_cs_sts = 1; } + //ILIM = 900ma + ret = wm831x_reg_read(wm831x, WM831X_POWER_STATE) & 0xffff; + wm831x_reg_write(wm831x, WM831X_POWER_STATE, (ret&0xfff8) | 0x04); + + dev_info(wm831x->dev, "WM8310 revision %c\n", 'A' + rev); break; - case 0x8311: + case WM8311: parent = WM8311; - switch (rev) { - case 0: - dev_info(wm831x->dev, "WM8311 revision %c\n", - 'A' + rev); - break; + wm831x->num_gpio = 16; + wm831x->charger_irq_wake = 1; + if (rev > 0) { + wm831x->has_gpio_ena = 1; + wm831x->has_cs_sts = 1; } + + dev_info(wm831x->dev, "WM8311 revision %c\n", 'A' + rev); break; - case 0x8312: + case WM8312: parent = WM8312; - switch (rev) { - case 0: - dev_info(wm831x->dev, "WM8312 revision %c\n", - 'A' + rev); - break; + wm831x->num_gpio = 16; + wm831x->charger_irq_wake = 1; + if (rev > 0) { + wm831x->has_gpio_ena = 1; + wm831x->has_cs_sts = 1; } + + dev_info(wm831x->dev, "WM8312 revision %c\n", 'A' + rev); break; - case 0: - /* Some engineering samples do not have the ID set, - * rely on the device being registered correctly. - * This will need revisiting for future devices with - * multiple dies. - */ - parent = id; - switch (rev) { - case 0: - dev_info(wm831x->dev, "WM831%d ES revision %c\n", - parent, 'A' + rev); - break; - } + case WM8320: + parent = WM8320; + wm831x->num_gpio = 12; + dev_info(wm831x->dev, "WM8320 revision %c\n", 'A' + rev); + break; + + case WM8321: + parent = WM8321; + wm831x->num_gpio = 12; + dev_info(wm831x->dev, "WM8321 revision %c\n", 'A' + rev); + break; + + case WM8325: + parent = WM8325; + wm831x->num_gpio = 12; + dev_info(wm831x->dev, "WM8325 revision %c\n", 'A' + rev); break; default: @@ -1338,7 +1561,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) * current parts. */ if (parent != id) - dev_warn(wm831x->dev, "Device was registered as a WM831%lu\n", + dev_warn(wm831x->dev, "Device was registered as a WM%lx\n", id); /* Bootstrap the user key */ @@ -1366,23 +1589,51 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) if (ret != 0) goto err; + if (wm831x->irq_base) { + ret = request_threaded_irq(wm831x->irq_base + + WM831X_IRQ_AUXADC_DATA, + NULL, wm831x_auxadc_irq, 0, + "auxadc", wm831x); + if (ret < 0) + dev_err(wm831x->dev, "AUXADC IRQ request failed: %d\n", + ret); + } + /* The core device is up, instantiate the subdevices. */ switch (parent) { case WM8310: ret = mfd_add_devices(wm831x->dev, -1, wm8310_devs, ARRAY_SIZE(wm8310_devs), - NULL, 0); + NULL, wm831x->irq_base); break; case WM8311: ret = mfd_add_devices(wm831x->dev, -1, wm8311_devs, ARRAY_SIZE(wm8311_devs), - NULL, 0); + NULL, wm831x->irq_base); break; case WM8312: ret = mfd_add_devices(wm831x->dev, -1, wm8312_devs, ARRAY_SIZE(wm8312_devs), + NULL, wm831x->irq_base); + break; + + case WM8320: + ret = mfd_add_devices(wm831x->dev, -1, + wm8320_devs, ARRAY_SIZE(wm8320_devs), + NULL, 0); + break; + + case WM8321: + ret = mfd_add_devices(wm831x->dev, -1, + wm8320_devs, ARRAY_SIZE(wm8320_devs), + NULL, 0); + break; + + case WM8325: + ret = mfd_add_devices(wm831x->dev, -1, + wm8320_devs, ARRAY_SIZE(wm8320_devs), NULL, 0); break; @@ -1399,7 +1650,8 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) if (pdata && pdata->backlight) { /* Treat errors as non-critical */ ret = mfd_add_devices(wm831x->dev, -1, backlight_devs, - ARRAY_SIZE(backlight_devs), NULL, 0); + ARRAY_SIZE(backlight_devs), NULL, + wm831x->irq_base); if (ret < 0) dev_err(wm831x->dev, "Failed to add backlight: %d\n", ret); @@ -1408,7 +1660,11 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) wm831x_otp_init(wm831x); if (pdata && pdata->post_init) { + wm831x_reg_unlock(wm831x); + wm831x_set_bits(wm831x, WM831X_RESET_CONTROL,0x0010,0x0000); + wm831x_set_bits(wm831x, WM831X_LDO_ENABLE,0Xf800,0Xf800); ret = pdata->post_init(wm831x); + wm831x_reg_lock(wm831x); if (ret != 0) { dev_err(wm831x->dev, "post_init() failed: %d\n", ret); goto err_irq; @@ -1425,125 +1681,130 @@ err: return ret; } -static void wm831x_device_exit(struct wm831x *wm831x) +void wm831x_device_exit(struct wm831x *wm831x) { wm831x_otp_exit(wm831x); mfd_remove_devices(wm831x->dev); + if (wm831x->irq_base) + free_irq(wm831x->irq_base + WM831X_IRQ_AUXADC_DATA, wm831x); wm831x_irq_exit(wm831x); kfree(wm831x); } -static int wm831x_i2c_read_device(struct wm831x *wm831x, unsigned short reg, - int bytes, void *dest) +int wm831x_device_suspend(struct wm831x *wm831x) { - struct i2c_client *i2c = wm831x->control_data; - int ret; - u16 r = cpu_to_be16(reg); - - ret = i2c_master_send(i2c, (unsigned char *)&r, 2); - if (ret < 0) - return ret; - if (ret != 2) - return -EIO; - - ret = i2c_master_recv(i2c, dest, bytes); - if (ret < 0) - return ret; - if (ret != bytes) - return -EIO; - return 0; -} - -/* Currently we allocate the write buffer on the stack; this is OK for - * small writes - if we need to do large writes this will need to be - * revised. - */ -static int wm831x_i2c_write_device(struct wm831x *wm831x, unsigned short reg, - int bytes, void *src) -{ - struct i2c_client *i2c = wm831x->control_data; - unsigned char msg[bytes + 2]; - int ret; - - reg = cpu_to_be16(reg); - memcpy(&msg[0], ®, 2); - memcpy(&msg[2], src, bytes); + int reg, mask; + int i; + + //mask some intterupt avoid wakeing up system while suspending + for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) { + /* If there's been a change in the mask write it back + * to the hardware. */ + //printk("irq_masks_cur[%d]=0x%x\n",i,wm831x->irq_masks_cur[i]); + + if (wm831x->irq_masks_cur[i] != wm831x->irq_masks_cache[i]) { + wm831x->irq_masks_cache[i] = wm831x->irq_masks_cur[i]; + wm831x_reg_write(wm831x, + WM831X_INTERRUPT_STATUS_1_MASK + i, + wm831x->irq_masks_cur[i]); + } + + } - ret = i2c_master_send(i2c, msg, bytes + 2); - if (ret < 0) - return ret; - if (ret < bytes + 2) - return -EIO; + /* If the charger IRQs are a wake source then make sure we ack + * them even if they're not actively being used (eg, no power + * driver or no IRQ line wired up) then acknowledge the + * interrupts otherwise suspend won't last very long. + */ + if (wm831x->charger_irq_wake) { + reg = wm831x_reg_read(wm831x, WM831X_INTERRUPT_STATUS_2_MASK); + + mask = WM831X_CHG_BATT_HOT_EINT | + WM831X_CHG_BATT_COLD_EINT | + WM831X_CHG_BATT_FAIL_EINT | + WM831X_CHG_OV_EINT | WM831X_CHG_END_EINT | + WM831X_CHG_TO_EINT | WM831X_CHG_MODE_EINT | + WM831X_CHG_START_EINT; + + /* If any of the interrupts are masked read the statuses */ + if (reg & mask) + reg = wm831x_reg_read(wm831x, + WM831X_INTERRUPT_STATUS_2); + + if (reg & mask) { + dev_info(wm831x->dev, + "Acknowledging masked charger IRQs: %x\n", + reg & mask); + wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_2, + reg & mask); + } + } return 0; } - -static int wm831x_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) -{ - struct wm831x *wm831x; - - wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL); - if (wm831x == NULL) { - kfree(i2c); - return -ENOMEM; +void wm831x_enter_sleep(void){ +#if 1//def CONFIG_RK2818_SOC_PM + struct regulator *dcdc; + int i; + dcdc=regulator_get(NULL, "dcdc1"); + struct wm831x_dcdc *dc = regulator_get_drvdata(dcdc); + struct wm831x *wm831x = dc->wm831x; + if(wm831x){ + wm831x_set_bits(wm831x, WM831X_POWER_STATE, 0x4000, 0x4000); // SYSTEM SLEEP MODE + for (i=0; i<5; i++) + wm831x_reg_write(wm831x,WM831X_INTERRUPT_STATUS_1+i, 0xffff); // INTRUPT FLAG CLEAR + + printk("%s:complete! \n",__func__); + + }else{ + printk("%s:error!",__func__); } - - i2c_set_clientdata(i2c, wm831x); - wm831x->dev = &i2c->dev; - wm831x->control_data = i2c; - wm831x->read_dev = wm831x_i2c_read_device; - wm831x->write_dev = wm831x_i2c_write_device; - - return wm831x_device_init(wm831x, id->driver_data, i2c->irq); + regulator_put(dcdc); +#endif } - -static int wm831x_i2c_remove(struct i2c_client *i2c) -{ - struct wm831x *wm831x = i2c_get_clientdata(i2c); - - wm831x_device_exit(wm831x); - - return 0; +EXPORT_SYMBOL_GPL(wm831x_enter_sleep); + +void wm831x_exit_sleep(void){ +#if 1//def CONFIG_RK2818_SOC_PM + struct regulator *dcdc; + dcdc=regulator_get(NULL, "dcdc1"); + struct wm831x_dcdc *dc = regulator_get_drvdata(dcdc); + struct wm831x *wm831x = dc->wm831x; + if(wm831x){ + wm831x_set_bits(wm831x, WM831X_POWER_STATE, 0x4000, 0); // SYSTEM ON MODE + printk("%s:complete! \n",__func__); + + }else{ + printk("%s:error!",__func__); + } + regulator_put(dcdc); +#endif } +EXPORT_SYMBOL_GPL(wm831x_exit_sleep); -static const struct i2c_device_id wm831x_i2c_id[] = { - { "wm8310", WM8310 }, - { "wm8311", WM8311 }, - { "wm8312", WM8312 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, wm831x_i2c_id); - - -static struct i2c_driver wm831x_i2c_driver = { - .driver = { - .name = "wm831x", - .owner = THIS_MODULE, - }, - .probe = wm831x_i2c_probe, - .remove = wm831x_i2c_remove, - .id_table = wm831x_i2c_id, -}; - -static int __init wm831x_i2c_init(void) +int wm831x_device_shutdown(struct wm831x *wm831x) { - int ret; - - ret = i2c_add_driver(&wm831x_i2c_driver); - if (ret != 0) - pr_err("Failed to register wm831x I2C driver: %d\n", ret); - - return ret; -} -subsys_initcall(wm831x_i2c_init); + struct wm831x_pdata *pdata = wm831x->dev->platform_data; + int ret = 0; + + printk("pre WM831X_POWER_STATE = 0x%x\n", wm831x_reg_read(wm831x, WM831X_POWER_STATE)); -static void __exit wm831x_i2c_exit(void) -{ - i2c_del_driver(&wm831x_i2c_driver); + if (pdata && pdata->last_deinit) { + ret = pdata->last_deinit(wm831x); + if (ret != 0) { + dev_info(wm831x->dev, "last_deinit() failed: %d\n", ret); + //goto err_irq; + } + } + if(0 == reboot_cmd_get()) + { + if(wm831x_set_bits(wm831x, WM831X_POWER_STATE, WM831X_CHIP_ON_MASK, 0) < 0) + printk("%s wm831x_set_bits err\n", __FUNCTION__); + printk("post WM831X_POWER_STATE = 0x%x\n", wm831x_reg_read(wm831x, WM831X_POWER_STATE)); + } + return 0; } -module_exit(wm831x_i2c_exit); -MODULE_DESCRIPTION("I2C support for the WM831X AudioPlus PMIC"); +MODULE_DESCRIPTION("Core support for the WM831X AudioPlus PMIC"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Mark Brown"); diff --git a/drivers/mfd/wm831x-i2c.c b/drivers/mfd/wm831x-i2c.c new file mode 100755 index 000000000000..5c6e2e7980bc --- /dev/null +++ b/drivers/mfd/wm831x-i2c.c @@ -0,0 +1,188 @@ +/* + * wm831x-i2c.c -- I2C access for Wolfson WM831x PMICs + * + * Copyright 2009,2010 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +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); diff --git a/drivers/mfd/wm831x-irq.c b/drivers/mfd/wm831x-irq.c old mode 100644 new mode 100755 index ac056ea6b66e..905b59ebaa12 --- a/drivers/mfd/wm831x-irq.c +++ b/drivers/mfd/wm831x-irq.c @@ -15,15 +15,17 @@ #include #include #include +#include #include #include #include #include +#include #include #include - +#include /* * Since generic IRQs don't currently support interrupt controllers on * interrupt driven buses we don't use genirq but instead provide an @@ -32,13 +34,18 @@ * the static irq_data table we use to look up the data for individual * interrupts, but hopefully won't last too long. */ +#define WM831X_IRQ_TYPE IRQF_TRIGGER_LOW struct wm831x_irq_data { int primary; int reg; int mask; - irq_handler_t handler; - void *handler_data; +}; + +struct wm831x_handle_irq +{ + int irq; + struct list_head queue; }; static struct wm831x_irq_data wm831x_irqs[] = { @@ -339,101 +346,151 @@ static inline int irq_data_to_mask_reg(struct wm831x_irq_data *irq_data) return WM831X_INTERRUPT_STATUS_1_MASK - 1 + irq_data->reg; } -static void __wm831x_enable_irq(struct wm831x *wm831x, int irq) +static inline struct wm831x_irq_data *irq_to_wm831x_irq(struct wm831x *wm831x, + int irq) { - struct wm831x_irq_data *irq_data = &wm831x_irqs[irq]; - - wm831x->irq_masks[irq_data->reg - 1] &= ~irq_data->mask; - wm831x_reg_write(wm831x, irq_data_to_mask_reg(irq_data), - wm831x->irq_masks[irq_data->reg - 1]); + return &wm831x_irqs[irq - wm831x->irq_base]; } -void wm831x_enable_irq(struct wm831x *wm831x, int irq) +static void wm831x_irq_lock(unsigned int irq) { + struct wm831x *wm831x = get_irq_chip_data(irq); + mutex_lock(&wm831x->irq_lock); - __wm831x_enable_irq(wm831x, irq); - mutex_unlock(&wm831x->irq_lock); } -EXPORT_SYMBOL_GPL(wm831x_enable_irq); -static void __wm831x_disable_irq(struct wm831x *wm831x, int irq) +static void wm831x_irq_sync_unlock(unsigned int irq) { - struct wm831x_irq_data *irq_data = &wm831x_irqs[irq]; + struct wm831x *wm831x = get_irq_chip_data(irq); + int i; + + for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) { + /* If there's been a change in the mask write it back + * to the hardware. */ + if (wm831x->irq_masks_cur[i] != wm831x->irq_masks_cache[i]) { + wm831x->irq_masks_cache[i] = wm831x->irq_masks_cur[i]; + wm831x_reg_write(wm831x, + WM831X_INTERRUPT_STATUS_1_MASK + i, + wm831x->irq_masks_cur[i]); + } + } - wm831x->irq_masks[irq_data->reg - 1] |= irq_data->mask; - wm831x_reg_write(wm831x, irq_data_to_mask_reg(irq_data), - wm831x->irq_masks[irq_data->reg - 1]); + mutex_unlock(&wm831x->irq_lock); } -void wm831x_disable_irq(struct wm831x *wm831x, int irq) +static void wm831x_irq_unmask(unsigned int irq) { - mutex_lock(&wm831x->irq_lock); - __wm831x_disable_irq(wm831x, irq); - mutex_unlock(&wm831x->irq_lock); + struct wm831x *wm831x = get_irq_chip_data(irq); + struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x, irq); + + wm831x->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask; + //printk("%s:irq=%d\n",__FUNCTION__,irq); } -EXPORT_SYMBOL_GPL(wm831x_disable_irq); -int wm831x_request_irq(struct wm831x *wm831x, - unsigned int irq, irq_handler_t handler, - unsigned long flags, const char *name, - void *dev) +static void wm831x_irq_mask(unsigned int irq) { - int ret = 0; - - if (irq < 0 || irq >= WM831X_NUM_IRQS) - return -EINVAL; + struct wm831x *wm831x = get_irq_chip_data(irq); + struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x, irq); - mutex_lock(&wm831x->irq_lock); + wm831x->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask; + //printk("%s:irq=%d\n",__FUNCTION__,irq); +} - if (wm831x_irqs[irq].handler) { - dev_err(wm831x->dev, "Already have handler for IRQ %d\n", irq); - ret = -EINVAL; - goto out; +static int wm831x_irq_set_type(unsigned int irq, unsigned int type) +{ + struct wm831x *wm831x = get_irq_chip_data(irq); + int val; + + irq = irq - wm831x->irq_base; + if (irq < WM831X_IRQ_GPIO_1 || irq > WM831X_IRQ_GPIO_12) { + /* Ignore internal-only IRQs */ + if (irq >= 0 && irq < WM831X_NUM_IRQS) + return 0; + else + return -EINVAL; + } + //printk("wm831x_irq_set_type:type=%x,irq=%d\n",type,irq); + switch (type) { + case IRQ_TYPE_EDGE_BOTH: + val = WM831X_GPN_INT_MODE; + break; + case IRQ_TYPE_EDGE_RISING: + val = WM831X_GPN_POL; + break; + case IRQ_TYPE_EDGE_FALLING: + val = 0; + break; + default: + return -EINVAL; } - wm831x_irqs[irq].handler = handler; - wm831x_irqs[irq].handler_data = dev; + return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + irq - 1, + WM831X_GPN_INT_MODE | WM831X_GPN_POL, val); +} - __wm831x_enable_irq(wm831x, irq); +static int wm831x_irq_set_wake(unsigned irq, unsigned state) +{ + struct wm831x *wm831x = get_irq_chip_data(irq); + + //only wm831x irq + if ((irq > wm831x->irq_base + WM831X_IRQ_TEMP_THW) &&( irq < wm831x->irq_base + WM831X_NUM_IRQS)) + { + if(state) + wm831x_irq_unmask(irq); + else + wm831x_irq_mask(irq); + return 0; + } + else + { + printk("%s:irq number err!irq=%d\n",__FUNCTION__,irq); + return -EINVAL; + } -out: - mutex_unlock(&wm831x->irq_lock); - return ret; } -EXPORT_SYMBOL_GPL(wm831x_request_irq); -void wm831x_free_irq(struct wm831x *wm831x, unsigned int irq, void *data) +static struct irq_chip wm831x_irq_chip = { + .name = "wm831x", + .bus_lock = wm831x_irq_lock, + .bus_sync_unlock = wm831x_irq_sync_unlock, + .mask = wm831x_irq_mask, + .unmask = wm831x_irq_unmask, + .set_type = wm831x_irq_set_type, + .set_wake = wm831x_irq_set_wake, +}; + +#if WM831X_IRQ_LIST +static void wm831x_handle_worker(struct work_struct *work) { - if (irq < 0 || irq >= WM831X_NUM_IRQS) - return; + struct wm831x *wm831x = container_of(work, struct wm831x, handle_work); + int irq; - mutex_lock(&wm831x->irq_lock); + while (1) { + unsigned long flags; + struct wm831x_handle_irq *hd = NULL; - wm831x_irqs[irq].handler = NULL; - wm831x_irqs[irq].handler_data = NULL; + spin_lock_irqsave(&wm831x->work_lock, flags); + if (!list_empty(&wm831x->handle_queue)) { + hd = list_first_entry(&wm831x->handle_queue, struct wm831x_handle_irq, queue); + list_del(&hd->queue); + } + spin_unlock_irqrestore(&wm831x->work_lock, flags); - __wm831x_disable_irq(wm831x, irq); - - mutex_unlock(&wm831x->irq_lock); -} -EXPORT_SYMBOL_GPL(wm831x_free_irq); + if (!hd) // trans_queue empty + break; + irq = hd->irq; //get wm831x intterupt status + //printk("%s:irq=%d\n",__FUNCTION__,irq); + + /*start to handle wm831x intterupt*/ + handle_nested_irq(wm831x->irq_base + irq); + + kfree(hd); -static void wm831x_handle_irq(struct wm831x *wm831x, int irq, int status) -{ - struct wm831x_irq_data *irq_data = &wm831x_irqs[irq]; - - if (irq_data->handler) { - irq_data->handler(irq, irq_data->handler_data); - wm831x_reg_write(wm831x, irq_data_to_status_reg(irq_data), - irq_data->mask); - } else { - dev_err(wm831x->dev, "Unhandled IRQ %d, masking\n", irq); - __wm831x_disable_irq(wm831x, irq); } } - +#endif /* Main interrupt handling occurs in a workqueue since we need * interrupts enabled to interact with the chip. */ static void wm831x_irq_worker(struct work_struct *work) @@ -441,25 +498,37 @@ static void wm831x_irq_worker(struct work_struct *work) struct wm831x *wm831x = container_of(work, struct wm831x, irq_work); unsigned int i; int primary; - int status_regs[5]; - int read[5] = { 0 }; + int status_regs[WM831X_NUM_IRQ_REGS] = { 0 }; + int read[WM831X_NUM_IRQ_REGS] = { 0 }; int *status; - + unsigned long flags; + struct wm831x_handle_irq *hd; + int ret; + msleep(2); +#if (WM831X_IRQ_TYPE != IRQF_TRIGGER_LOW) + /*mask wm831x irq at first*/ + ret = wm831x_set_bits(wm831x, WM831X_IRQ_CONFIG, + WM831X_IRQ_IM_MASK, WM831X_IRQ_IM_EANBLE); + if (ret < 0) { + dev_err(wm831x->dev, "Failed to mask irq: %d\n", ret); + goto out; + } +#endif primary = wm831x_reg_read(wm831x, WM831X_SYSTEM_INTERRUPTS); if (primary < 0) { dev_err(wm831x->dev, "Failed to read system interrupt: %d\n", primary); goto out; } - + mutex_lock(&wm831x->irq_lock); for (i = 0; i < ARRAY_SIZE(wm831x_irqs); i++) { int offset = wm831x_irqs[i].reg - 1; - + if (!(primary & wm831x_irqs[i].primary)) continue; - + status = &status_regs[offset]; /* Hopefully there should only be one register to read @@ -474,47 +543,108 @@ static void wm831x_irq_worker(struct work_struct *work) goto out_lock; } - /* Mask out the disabled IRQs */ - *status &= ~wm831x->irq_masks[offset]; read[offset] = 1; } - if (*status & wm831x_irqs[i].mask) - wm831x_handle_irq(wm831x, i, *status); + /* Report it if it isn't masked, or forget the status. */ + if ((*status & ~wm831x->irq_masks_cur[offset]) + & wm831x_irqs[i].mask) + { + #if WM831X_IRQ_LIST + /*add intterupt handle on list*/ + hd = kzalloc(sizeof(struct wm831x_handle_irq), GFP_KERNEL); + if (!hd) + { + printk("err:%s:ENOMEM\n",__FUNCTION__); + return ; + } + + if(i == WM831X_IRQ_ON) + wake_lock(&wm831x->handle_wake); //keep wake while handle WM831X_IRQ_ON + hd->irq = i; + spin_lock_irqsave(&wm831x->work_lock, flags); + list_add_tail(&hd->queue, &wm831x->handle_queue); + spin_unlock_irqrestore(&wm831x->work_lock, flags); + queue_work(wm831x->handle_wq, &wm831x->handle_work); + + #else + if(i == WM831X_IRQ_ON) + wake_lock(&wm831x->handle_wake); //keep wake while handle WM831X_IRQ_ON + handle_nested_irq(wm831x->irq_base + i); + + #endif + } + + else + *status &= ~wm831x_irqs[i].mask; } - -out_lock: + +out_lock: mutex_unlock(&wm831x->irq_lock); + out: - enable_irq(wm831x->irq); -} - + for (i = 0; i < ARRAY_SIZE(status_regs); i++) { + if (status_regs[i]) + wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1 + i, + status_regs[i]); + } + +#if (WM831X_IRQ_TYPE != IRQF_TRIGGER_LOW) + ret = wm831x_set_bits(wm831x, WM831X_IRQ_CONFIG, + WM831X_IRQ_IM_MASK, 0); + if (ret < 0) { + dev_err(wm831x->dev, "Failed to open irq: %d\n", ret); + } +#endif +#if (WM831X_IRQ_TYPE == IRQF_TRIGGER_LOW) + enable_irq(wm831x->irq); +#endif + wake_unlock(&wm831x->irq_wake); -static irqreturn_t wm831x_cpu_irq(int irq, void *data) +} +/* The processing of the primary interrupt occurs in a thread so that + * we can interact with the device over I2C or SPI. */ +static irqreturn_t wm831x_irq_thread(int irq, void *data) { struct wm831x *wm831x = data; /* Shut the interrupt to the CPU up and schedule the actual * handler; we can't check that the IRQ is asserted. */ +#if (WM831X_IRQ_TYPE == IRQF_TRIGGER_LOW) disable_irq_nosync(irq); - +#endif + wake_lock(&wm831x->irq_wake); queue_work(wm831x->irq_wq, &wm831x->irq_work); - + //printk("%s\n",__FUNCTION__); return IRQ_HANDLED; } int wm831x_irq_init(struct wm831x *wm831x, int irq) { - int i, ret; - + struct wm831x_pdata *pdata = wm831x->dev->platform_data; + int i, cur_irq, ret; + printk( "wm831x_irq_init:irq=%d,%d\n",irq,pdata->irq_base); mutex_init(&wm831x->irq_lock); + /* Mask the individual interrupt sources */ + for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) { + wm831x->irq_masks_cur[i] = 0xffff; + wm831x->irq_masks_cache[i] = 0xffff; + wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1_MASK + i, + 0xffff); + } + if (!irq) { dev_warn(wm831x->dev, "No interrupt specified - functionality limited\n"); return 0; } + if (!pdata || !pdata->irq_base) { + dev_err(wm831x->dev, + "No interrupt base specified, no interrupts\n"); + return 0; + } wm831x->irq_wq = create_singlethread_workqueue("wm831x-irq"); if (!wm831x->irq_wq) { @@ -522,34 +652,59 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq) return -ESRCH; } + wm831x->irq = irq; + wm831x->irq_base = pdata->irq_base; INIT_WORK(&wm831x->irq_work, wm831x_irq_worker); - - /* Mask the individual interrupt sources */ - for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks); i++) { - wm831x->irq_masks[i] = 0xffff; - wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1_MASK + i, - 0xffff); + wake_lock_init(&wm831x->irq_wake, WAKE_LOCK_SUSPEND, "wm831x_irq_wake"); + wake_lock_init(&wm831x->handle_wake, WAKE_LOCK_SUSPEND, "wm831x_handle_wake"); +#if WM831X_IRQ_LIST + wm831x->handle_wq = create_rt_workqueue("wm831x_handle_wq"); + if (!wm831x->handle_wq) { + printk("cannot create workqueue\n"); + return -EBUSY; } - - /* Enable top level interrupts, we mask at secondary level */ - wm831x_reg_write(wm831x, WM831X_SYSTEM_INTERRUPTS_MASK, 0); - - /* We're good to go. We set IRQF_SHARED since there's a - * chance the driver will interoperate with another driver but - * the need to disable the IRQ while handing via I2C/SPI means - * that this may break and performance will be impacted. If - * this does happen it's a hardware design issue and the only - * other alternative would be polling. - */ - ret = request_irq(irq, wm831x_cpu_irq, IRQF_TRIGGER_LOW | IRQF_SHARED, - "wm831x", wm831x); + INIT_WORK(&wm831x->handle_work, wm831x_handle_worker); + INIT_LIST_HEAD(&wm831x->handle_queue); + +#endif + + /* Register them with genirq */ + for (cur_irq = wm831x->irq_base; + cur_irq < ARRAY_SIZE(wm831x_irqs) + wm831x->irq_base; + cur_irq++) { + set_irq_chip_data(cur_irq, wm831x); + set_irq_chip_and_handler(cur_irq, &wm831x_irq_chip, + handle_edge_irq); + set_irq_nested_thread(cur_irq, 1); + + /* ARM needs us to explicitly flag the IRQ as valid + * and will set them noprobe when we do so. */ +#ifdef CONFIG_ARM + set_irq_flags(cur_irq, IRQF_VALID); +#else + set_irq_noprobe(cur_irq); +#endif + } +#if (WM831X_IRQ_TYPE == IRQF_TRIGGER_LOW) + ret = request_threaded_irq(wm831x->irq, wm831x_irq_thread, NULL, + IRQF_TRIGGER_LOW| IRQF_ONESHOT,//IRQF_TRIGGER_FALLING, // + "wm831x", wm831x); +#else + ret = request_threaded_irq(wm831x->irq, wm831x_irq_thread, NULL, + IRQF_TRIGGER_FALLING, //IRQF_TRIGGER_LOW| IRQF_ONESHOT,// + "wm831x", wm831x); +#endif if (ret != 0) { dev_err(wm831x->dev, "Failed to request IRQ %d: %d\n", - irq, ret); + wm831x->irq, ret); return ret; } + enable_irq_wake(wm831x->irq); // so wm831x irq can wake up system + /* Enable top level interrupts, we mask at secondary level */ + wm831x_reg_write(wm831x, WM831X_SYSTEM_INTERRUPTS_MASK, 0); + return 0; } diff --git a/drivers/mfd/wm831x-otp.c b/drivers/mfd/wm831x-otp.c old mode 100644 new mode 100755 diff --git a/drivers/mfd/wm831x-spi.c b/drivers/mfd/wm831x-spi.c new file mode 100755 index 000000000000..2789b151b0f9 --- /dev/null +++ b/drivers/mfd/wm831x-spi.c @@ -0,0 +1,232 @@ +/* + * wm831x-spi.c -- SPI access for Wolfson WM831x PMICs + * + * Copyright 2009,2010 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * 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 +#include +#include + +#include + +static int wm831x_spi_read_device(struct wm831x *wm831x, unsigned short reg, + int bytes, void *dest) +{ + u16 tx_val; + u16 *d = dest; + int r, ret; + + /* Go register at a time */ + for (r = reg; r < reg + (bytes / 2); r++) { + tx_val = r | 0x8000; + + ret = spi_write_then_read(wm831x->control_data, + (u8 *)&tx_val, 2, (u8 *)d, 2); + if (ret != 0) + return ret; + + *d = be16_to_cpu(*d); + + d++; + } + + return 0; +} + +static int wm831x_spi_write_device(struct wm831x *wm831x, unsigned short reg, + int bytes, void *src) +{ + struct spi_device *spi = wm831x->control_data; + u16 *s = src; + u16 data[2]; + int ret, r; + + /* Go register at a time */ + for (r = reg; r < reg + (bytes / 2); r++) { + data[0] = r; + data[1] = *s++; + + ret = spi_write(spi, (char *)&data, sizeof(data)); + if (ret != 0) + return ret; + } + + return 0; +} + +static int __devinit wm831x_spi_probe(struct spi_device *spi) +{ + struct wm831x *wm831x; + enum wm831x_parent type; + + /* Currently SPI support for ID tables is unmerged, we're faking it */ + if (strcmp(spi->modalias, "wm8310") == 0) + type = WM8310; + else if (strcmp(spi->modalias, "wm8311") == 0) + type = WM8311; + else if (strcmp(spi->modalias, "wm8312") == 0) + type = WM8312; + else if (strcmp(spi->modalias, "wm8320") == 0) + type = WM8320; + else if (strcmp(spi->modalias, "wm8321") == 0) + type = WM8321; + else if (strcmp(spi->modalias, "wm8325") == 0) + type = WM8325; + else { + dev_err(&spi->dev, "Unknown device type\n"); + return -EINVAL; + } + + wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL); + if (wm831x == NULL) + return -ENOMEM; + + spi->bits_per_word = 16; + spi->mode = SPI_MODE_0; + + dev_set_drvdata(&spi->dev, wm831x); + wm831x->dev = &spi->dev; + wm831x->control_data = spi; + wm831x->read_dev = wm831x_spi_read_device; + wm831x->write_dev = wm831x_spi_write_device; + + return wm831x_device_init(wm831x, type, spi->irq); +} + +static int __devexit wm831x_spi_remove(struct spi_device *spi) +{ + struct wm831x *wm831x = dev_get_drvdata(&spi->dev); + + wm831x_device_exit(wm831x); + + return 0; +} + +static int wm831x_spi_suspend(struct spi_device *spi, pm_message_t m) +{ + struct wm831x *wm831x = dev_get_drvdata(&spi->dev); + + return wm831x_device_suspend(wm831x); +} + +static struct spi_driver wm8310_spi_driver = { + .driver = { + .name = "wm8310", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = wm831x_spi_probe, + .remove = __devexit_p(wm831x_spi_remove), + .suspend = wm831x_spi_suspend, +}; + +static struct spi_driver wm8311_spi_driver = { + .driver = { + .name = "wm8311", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = wm831x_spi_probe, + .remove = __devexit_p(wm831x_spi_remove), + .suspend = wm831x_spi_suspend, +}; + +static struct spi_driver wm8312_spi_driver = { + .driver = { + .name = "wm8312", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = wm831x_spi_probe, + .remove = __devexit_p(wm831x_spi_remove), + .suspend = wm831x_spi_suspend, +}; + +static struct spi_driver wm8320_spi_driver = { + .driver = { + .name = "wm8320", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = wm831x_spi_probe, + .remove = __devexit_p(wm831x_spi_remove), + .suspend = wm831x_spi_suspend, +}; + +static struct spi_driver wm8321_spi_driver = { + .driver = { + .name = "wm8321", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = wm831x_spi_probe, + .remove = __devexit_p(wm831x_spi_remove), + .suspend = wm831x_spi_suspend, +}; + +static struct spi_driver wm8325_spi_driver = { + .driver = { + .name = "wm8325", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = wm831x_spi_probe, + .remove = __devexit_p(wm831x_spi_remove), + .suspend = wm831x_spi_suspend, +}; + +static int __init wm831x_spi_init(void) +{ + int ret; + + ret = spi_register_driver(&wm8310_spi_driver); + if (ret != 0) + pr_err("Failed to register WM8310 SPI driver: %d\n", ret); + + ret = spi_register_driver(&wm8311_spi_driver); + if (ret != 0) + pr_err("Failed to register WM8311 SPI driver: %d\n", ret); + + ret = spi_register_driver(&wm8312_spi_driver); + if (ret != 0) + pr_err("Failed to register WM8312 SPI driver: %d\n", ret); + + ret = spi_register_driver(&wm8320_spi_driver); + if (ret != 0) + pr_err("Failed to register WM8320 SPI driver: %d\n", ret); + + ret = spi_register_driver(&wm8321_spi_driver); + if (ret != 0) + pr_err("Failed to register WM8321 SPI driver: %d\n", ret); + + ret = spi_register_driver(&wm8325_spi_driver); + if (ret != 0) + pr_err("Failed to register WM8325 SPI driver: %d\n", ret); + + return 0; +} +subsys_initcall(wm831x_spi_init); + +static void __exit wm831x_spi_exit(void) +{ + spi_unregister_driver(&wm8325_spi_driver); + spi_unregister_driver(&wm8321_spi_driver); + spi_unregister_driver(&wm8320_spi_driver); + spi_unregister_driver(&wm8312_spi_driver); + spi_unregister_driver(&wm8311_spi_driver); + spi_unregister_driver(&wm8310_spi_driver); +} +module_exit(wm831x_spi_exit); + +MODULE_DESCRIPTION("SPI support for WM831x/2x AudioPlus PMICs"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mark Brown"); diff --git a/drivers/power/wm831x_backup.c b/drivers/power/wm831x_backup.c new file mode 100755 index 000000000000..0fd130d80f5d --- /dev/null +++ b/drivers/power/wm831x_backup.c @@ -0,0 +1,234 @@ +/* + * Backup battery driver for Wolfson Microelectronics wm831x PMICs + * + * Copyright 2009 Wolfson Microelectronics PLC. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +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 "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm831x-backup"); diff --git a/drivers/power/wm831x_power.c b/drivers/power/wm831x_power.c old mode 100644 new mode 100755 index 2a4c8b0b829c..a31225d30584 --- a/drivers/power/wm831x_power.c +++ b/drivers/power/wm831x_power.c @@ -12,20 +12,102 @@ #include #include #include +#include #include #include #include #include +#define WM831X_DEBUG +#undef WM831X_DEBUG + +#ifdef WM831X_DEBUG +#define WM_BATT_DBG(x...) printk(KERN_INFO x) +#else +#define WM_BATT_DBG(x...) do {} while (0) +#endif + +#define WM831X_CHG_SYSLO_SHIFT 4 +#define WM831X_CHG_SYSOK_SHIFT 0 +#define WM831X_CHG_SYSLO_MASK ~(0x7 << 4) +#define WM831X_CHG_SYSOK_MASK ~(0x7 << 0) +#define batt_num 57 +static int batt_chg_step_table[batt_num]={ + 3650,3700,3710,3720,3730,3740,3745,3755,3760, //+200 + 3770,3790,3805,3820,3830,3840, //+190 + 3842,3850,3860,3870,3880,3885, //+180 + + 3900,3910,3920,3930,3940,3950,3960,3970,3980,3990, //+170 + 4000,4010,4020,4030,4040,4050,4070,4090,4095,4100,4105,4110,4115,4120,4125,4130, + 4135,4140,4145,4150,4155,4160,4165,4170,4175,4180 +}; + +static int batt_step_table[batt_num]={ + 3450,3500,3510,3520,3530,3540,3545,3555,3560, + 3580,3600,3615,3630,3640,3650, + 3660,3670,3680,3690,3700,3710, + 3720,3730,3740,3750,3760,3770,3780,3790,3800,3810, + 3815,3830,3845,3860,3875,3890,3900,3910,3920,3930,3940,3950,3960,3970,3985,4000, + 4005,4010,4015,4020,4030,4040,4050,4060,4070,4180 +}; + +static int batt_disp_table[batt_num]={ + 0,2,4,6,8,10,12,14,15, + 18,20,23,26,28,30, + 33,37,40,43,47,50, + 52,54,57,60,62,64,66,68,69,70, + 72,74,76,78,79,80,81,82,83,84,85,86,87,88,89,90, + 91,92,93,94,95,96,97,98,99,100 +}; + + +#define TIMER_MS_COUNTS 1000 +struct wm_batt_priv_data { + enum power_supply_property online; + enum power_supply_property status; + enum power_supply_property voltage; + enum power_supply_property health; + enum power_supply_property type; + enum power_supply_property temp; + int old_level; + int charging_level; +}; + struct wm831x_power { struct wm831x *wm831x; struct power_supply wall; - struct power_supply backup; struct power_supply usb; struct power_supply battery; + struct work_struct batt_work; + struct timer_list timer; + struct wm_batt_priv_data priv; + int interval; }; +struct wm831x_power *g_wm831x_power; + +extern void wm831x_batt_vol_level(struct wm831x_power *power, int batt_val, int *level); +static DEFINE_MUTEX(charging_mutex); +static int g_read_level_cnt = 0; + +int wm831x_read_on_pin_status(void) +{ + int ret; + + if(!g_wm831x_power) + { + printk("err:%s:g_wm831x_power address is 0\n",__FUNCTION__); + return -1; + } + + ret = wm831x_reg_read(g_wm831x_power->wm831x, WM831X_ON_PIN_CONTROL); + if (ret < 0) + return ret; + + return !(ret & WM831X_ON_PIN_STS) ? 1 : 0; +} + static int wm831x_power_check_online(struct wm831x *wm831x, int supply, union power_supply_propval *val) { @@ -43,18 +125,68 @@ static int wm831x_power_check_online(struct wm831x *wm831x, int supply, return 0; } +int wm831x_read_chg_status(void) +{ + int ret, usb_chg = 0, wall_chg = 0; + + if(!g_wm831x_power) + { + printk("err:%s:g_wm831x_power address is 0\n",__FUNCTION__); + return -1; + } + + ret = wm831x_reg_read(g_wm831x_power->wm831x, WM831X_SYSTEM_STATUS); + if (ret < 0) + return ret; + + if (ret & WM831X_PWR_USB) + usb_chg = 1; + if (ret & WM831X_PWR_WALL) + wall_chg = 1; + + return ((usb_chg | wall_chg) ? 1 : 0); +} + static int wm831x_power_read_voltage(struct wm831x *wm831x, enum wm831x_auxadc src, union power_supply_propval *val) { int ret; +#if 0 + int loop = 0, vol_sum = 0; + + for (loop = 0; loop < 10; loop++) { + ret = wm831x_auxadc_read_uv(wm831x, src); + if (ret >= 0) { + vol_sum += ret / 1000; + } + } + + val->intval = vol_sum / 10; + return val->intval; +#else ret = wm831x_auxadc_read_uv(wm831x, src); if (ret >= 0) - val->intval = ret; + val->intval = ret / 1000; + return ret ; +#endif +} - return ret; +int wm831x_read_batt_voltage(void) +{ + int ret = 0; + + if(!g_wm831x_power) + { + printk("err:%s:g_wm831x_power address is 0\n",__FUNCTION__); + return -1; + } + + ret = wm831x_auxadc_read_uv(g_wm831x_power->wm831x, WM831X_AUX_BATT); + return ret / 1000; } +//EXPORT_SYMBOL_GPL(wm831x_get_batt_voltage); /********************************************************************* * WALL Power @@ -190,13 +322,34 @@ static struct chg_map chg_times[] = { { 510, 15 << WM831X_CHG_TIME_SHIFT }, }; +static struct chg_map chg_syslos[] = { + { 2800, 0 << WM831X_CHG_SYSLO_SHIFT}, + { 2900, 1 << WM831X_CHG_SYSLO_SHIFT}, + { 3000, 2 << WM831X_CHG_SYSLO_SHIFT}, + { 3100, 3 << WM831X_CHG_SYSLO_SHIFT}, + { 3200, 4 << WM831X_CHG_SYSLO_SHIFT}, + { 3300, 5 << WM831X_CHG_SYSLO_SHIFT}, + { 3400, 6 << WM831X_CHG_SYSLO_SHIFT}, + { 3500, 7 << WM831X_CHG_SYSLO_SHIFT}, +}; + +static struct chg_map chg_sysoks[] = { + { 2800, 0 << WM831X_CHG_SYSOK_SHIFT}, + { 2900, 1 << WM831X_CHG_SYSOK_SHIFT}, + { 3000, 2 << WM831X_CHG_SYSOK_SHIFT}, + { 3100, 3 << WM831X_CHG_SYSOK_SHIFT}, + { 3200, 4 << WM831X_CHG_SYSOK_SHIFT}, + { 3300, 5 << WM831X_CHG_SYSOK_SHIFT}, + { 3400, 6 << WM831X_CHG_SYSOK_SHIFT}, + { 3500, 7 << WM831X_CHG_SYSOK_SHIFT}, +}; + static void wm831x_battey_apply_config(struct wm831x *wm831x, struct chg_map *map, int count, int val, int *reg, const char *name, const char *units) { int i; - for (i = 0; i < count; i++) if (val == map[i].val) break; @@ -213,7 +366,7 @@ static void wm831x_config_battery(struct wm831x *wm831x) { struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data; struct wm831x_battery_pdata *pdata; - int ret, reg1, reg2; + int ret, reg1, reg2, reg3; if (!wm831x_pdata || !wm831x_pdata->battery) { dev_warn(wm831x->dev, @@ -225,6 +378,7 @@ static void wm831x_config_battery(struct wm831x *wm831x) reg1 = 0; reg2 = 0; + reg3 = 0; if (!pdata->enable) { dev_info(wm831x->dev, "Battery charger disabled\n"); @@ -258,6 +412,14 @@ static void wm831x_config_battery(struct wm831x *wm831x) pdata->timeout, ®2, "charger timeout", "min"); + wm831x_battey_apply_config(wm831x, chg_syslos, ARRAY_SIZE(chg_syslos), + pdata->syslo, ®3, + "syslo voltage", "mV"); + + wm831x_battey_apply_config(wm831x, chg_sysoks, ARRAY_SIZE(chg_sysoks), + pdata->sysok, ®3, + "sysok voltage", "mV"); + ret = wm831x_reg_unlock(wm831x); if (ret != 0) { dev_err(wm831x->dev, "Failed to unlock registers: %d\n", ret); @@ -267,13 +429,12 @@ static void wm831x_config_battery(struct wm831x *wm831x) ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_1, WM831X_CHG_ENA_MASK | WM831X_CHG_FAST_MASK | - WM831X_CHG_ITERM_MASK | WM831X_CHG_ITERM_MASK, reg1); - if (ret != 0) + if (ret != 0) { dev_err(wm831x->dev, "Failed to set charger control 1: %d\n", ret); - + } ret = wm831x_set_bits(wm831x, WM831X_CHARGER_CONTROL_2, WM831X_CHG_OFF_MSK | WM831X_CHG_TIME_MASK | @@ -281,9 +442,18 @@ static void wm831x_config_battery(struct wm831x *wm831x) WM831X_CHG_TRKL_ILIM_MASK | WM831X_CHG_VSEL_MASK, reg2); - if (ret != 0) + if (ret != 0) { dev_err(wm831x->dev, "Failed to set charger control 2: %d\n", ret); + } + + ret = wm831x_set_bits(wm831x, WM831X_SYSVDD_CONTROL, + WM831X_CHG_SYSLO_MASK | + WM831X_CHG_SYSOK_MASK, + reg3); + if (ret < 0) { + dev_err(wm831x->dev, "Failed to set sysvdd control reg: %d\n",ret); + } wm831x_reg_lock(wm831x); } @@ -305,6 +475,7 @@ static int wm831x_bat_check_status(struct wm831x *wm831x, int *status) if (ret < 0) return ret; + switch (ret & WM831X_CHG_STATE_MASK) { case WM831X_CHG_STATE_OFF: *status = POWER_SUPPLY_STATUS_NOT_CHARGING; @@ -321,10 +492,53 @@ static int wm831x_bat_check_status(struct wm831x *wm831x, int *status) return 0; } +int wm831x_read_bat_charging_status(void) +{ + int ret, status; + + if(!g_wm831x_power) + { + printk("err:%s:g_wm831x_power address is 0\n",__FUNCTION__); + return -1; + } + + ret = wm831x_bat_check_status(g_wm831x_power->wm831x, &status); + if (ret < 0) + return ret; + if (status == POWER_SUPPLY_STATUS_CHARGING) + return 1; + return 0; +} static int wm831x_bat_check_type(struct wm831x *wm831x, int *type) { int ret; +#ifdef WM831X_DEBUG + ret = wm831x_reg_read(wm831x, WM831X_POWER_STATE); + if (ret < 0) + return ret; + WM_BATT_DBG("%s: wm831x power status %#x\n", __FUNCTION__, ret); + + ret = wm831x_reg_read(wm831x, WM831X_SYSTEM_STATUS); + if (ret < 0) + return ret; + WM_BATT_DBG("%s: wm831x system status %#x\n", __FUNCTION__, ret); + + ret = wm831x_reg_read(wm831x, WM831X_CHARGER_CONTROL_1); + if (ret < 0) + return ret; + WM_BATT_DBG("%s: wm831x charger control1 %#x\n", __FUNCTION__, ret); + + ret = wm831x_reg_read(wm831x, WM831X_CHARGER_CONTROL_2); + if (ret < 0) + return ret; + WM_BATT_DBG("%s: wm831x charger control2 %#x\n", __FUNCTION__, ret); + + ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS); + if (ret < 0) + return ret; + WM_BATT_DBG("%s: wm831x charger status %#x\n\n", __FUNCTION__, ret); +#endif ret = wm831x_reg_read(wm831x, WM831X_CHARGER_STATUS); if (ret < 0) @@ -392,12 +606,13 @@ static int wm831x_bat_get_prop(struct power_supply *psy, { struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev->parent); struct wm831x *wm831x = wm831x_power->wm831x; - int ret = 0; + int level, ret = 0; switch (psp) { case POWER_SUPPLY_PROP_STATUS: ret = wm831x_bat_check_status(wm831x, &val->intval); break; + case POWER_SUPPLY_PROP_PRESENT: case POWER_SUPPLY_PROP_ONLINE: ret = wm831x_power_check_online(wm831x, WM831X_PWR_SRC_BATT, val); @@ -411,6 +626,17 @@ static int wm831x_bat_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_CHARGE_TYPE: ret = wm831x_bat_check_type(wm831x, &val->intval); break; + case POWER_SUPPLY_PROP_CAPACITY: + ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_BATT, val); + wm831x_batt_vol_level(wm831x_power, val->intval, &level); + val->intval = level; + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = 0; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; default: ret = -EINVAL; break; @@ -421,9 +647,12 @@ static int wm831x_bat_get_prop(struct power_supply *psy, static enum power_supply_property wm831x_bat_props[] = { POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_ONLINE, - POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CAPACITY, /* in percents! */ + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_CHARGE_TYPE, }; @@ -444,7 +673,7 @@ static irqreturn_t wm831x_bat_irq(int irq, void *data) struct wm831x *wm831x = wm831x_power->wm831x; dev_dbg(wm831x->dev, "Battery status changed: %d\n", irq); - + WM_BATT_DBG("%s:Battery status changed %d\n", __FUNCTION__, irq); /* The battery charger is autonomous so we don't need to do * anything except kick user space */ power_supply_changed(&wm831x_power->battery); @@ -454,156 +683,119 @@ static irqreturn_t wm831x_bat_irq(int irq, void *data) /********************************************************************* - * Backup supply properties + * Initialisation *********************************************************************/ -static void wm831x_config_backup(struct wm831x *wm831x) +static irqreturn_t wm831x_syslo_irq(int irq, void *data) { - struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data; - struct wm831x_backup_pdata *pdata; - int ret, reg; - - if (!wm831x_pdata || !wm831x_pdata->backup) { - dev_warn(wm831x->dev, - "No backup battery charger configuration\n"); - return; - } - - pdata = wm831x_pdata->backup; - - reg = 0; - - if (pdata->charger_enable) - reg |= WM831X_BKUP_CHG_ENA | WM831X_BKUP_BATT_DET_ENA; - if (pdata->no_constant_voltage) - reg |= WM831X_BKUP_CHG_MODE; + struct wm831x_power *wm831x_power = data; + struct wm831x *wm831x = wm831x_power->wm831x; - switch (pdata->vlim) { - case 2500: - break; - case 3100: - reg |= WM831X_BKUP_CHG_VLIM; - break; - default: - dev_err(wm831x->dev, "Invalid backup voltage limit %dmV\n", - pdata->vlim); - } + /* Not much we can actually *do* but tell people for + * posterity, we're probably about to run out of power. */ + dev_crit(wm831x->dev, "SYSVDD under voltage\n"); + return IRQ_HANDLED; +} - switch (pdata->ilim) { - case 100: - break; - case 200: - reg |= 1; - break; - case 300: - reg |= 2; - break; - case 400: - reg |= 3; - break; - default: - dev_err(wm831x->dev, "Invalid backup current limit %duA\n", - pdata->ilim); - } +static irqreturn_t wm831x_pwr_src_irq(int irq, void *data) +{ + struct wm831x_power *wm831x_power = data; + struct wm831x *wm831x = wm831x_power->wm831x; - ret = wm831x_reg_unlock(wm831x); - if (ret != 0) { - dev_err(wm831x->dev, "Failed to unlock registers: %d\n", ret); - return; - } + dev_dbg(wm831x->dev, "Power source changed\n"); + WM_BATT_DBG("%s:Power source changed\n", __FUNCTION__); + /* Just notify for everything - little harm in overnotifying. */ + power_supply_changed(&wm831x_power->battery); + power_supply_changed(&wm831x_power->usb); + power_supply_changed(&wm831x_power->wall); - ret = wm831x_set_bits(wm831x, WM831X_BACKUP_CHARGER_CONTROL, - WM831X_BKUP_CHG_ENA_MASK | - WM831X_BKUP_CHG_MODE_MASK | - WM831X_BKUP_BATT_DET_ENA_MASK | - WM831X_BKUP_CHG_VLIM_MASK | - WM831X_BKUP_CHG_ILIM_MASK, - reg); - if (ret != 0) - dev_err(wm831x->dev, - "Failed to set backup charger config: %d\n", ret); - wm831x_reg_lock(wm831x); + return IRQ_HANDLED; } -static int wm831x_backup_get_prop(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) +static void wm831x_batt_timer_handler(unsigned long data) { - struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev->parent); - struct wm831x *wm831x = wm831x_power->wm831x; - int ret = 0; - - ret = wm831x_reg_read(wm831x, WM831X_BACKUP_CHARGER_CONTROL); - if (ret < 0) - return ret; + struct wm831x_power *wm831x_power = (struct wm831x_power*)data; + schedule_work(&wm831x_power->batt_work); +} - switch (psp) { - case POWER_SUPPLY_PROP_STATUS: - if (ret & WM831X_BKUP_CHG_STS) - val->intval = POWER_SUPPLY_STATUS_CHARGING; - else - val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; - break; +void wm831x_batt_vol_level(struct wm831x_power *wm831x_power, int batt_val, int *level) +{ + int i, ret, status; + static int count = 0; + union power_supply_propval val; + + ret = wm831x_bat_check_status(wm831x_power->wm831x, &status); + if (status == POWER_SUPPLY_STATUS_CHARGING) { + for(i = 0; i < batt_num; i++){ + if((batt_chg_step_table[i] <= batt_val) && + (batt_chg_step_table[i+1] > batt_val)) + break; + } + *level = batt_disp_table[i]; + if (batt_val < 3650) + *level = 0; + count++; + if (*level < wm831x_power->priv.old_level && count > 20) + *level = wm831x_power->priv.old_level; - case POWER_SUPPLY_PROP_VOLTAGE_NOW: - ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_BKUP_BATT, - val); - break; + if (*level >= 100) + *level = 97; - case POWER_SUPPLY_PROP_PRESENT: - if (ret & WM831X_BKUP_CHG_STS) - val->intval = 1; - else - val->intval = 0; - break; + if (*level < 0) + *level = 0; - default: - ret = -EINVAL; - break; } + else { + for(i = 0; i < batt_num; i++){ + if((batt_step_table[i] <= batt_val) && + (batt_step_table[i+1] > batt_val)) + break; + } + *level = batt_disp_table[i]; + if (batt_val < 3450) + *level = 0; - return ret; -} + if ((wm831x_power->priv.old_level - *level) > 5) + *level = wm831x_power->priv.old_level - 5; -static enum power_supply_property wm831x_backup_props[] = { - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_PRESENT, -}; + //if (g_read_level_cnt >= 10 && *level > wm831x_power->priv.old_level) + if (*level > wm831x_power->priv.old_level) + *level = wm831x_power->priv.old_level; -/********************************************************************* - * Initialisation - *********************************************************************/ + if (*level > 100) + *level = 100; + if (*level < 0) + *level = 0; + } +} -static irqreturn_t wm831x_syslo_irq(int irq, void *data) +static void wm831x_batt_work(struct work_struct *work) { - struct wm831x_power *wm831x_power = data; - struct wm831x *wm831x = wm831x_power->wm831x; - - /* Not much we can actually *do* but tell people for - * posterity, we're probably about to run out of power. */ - dev_crit(wm831x->dev, "SYSVDD under voltage\n"); + int ret, online, status,health,level; + union power_supply_propval val; + struct wm831x_power *power = container_of(work, struct wm831x_power, batt_work); - return IRQ_HANDLED; -} + ret = wm831x_power_check_online(power->wm831x, WM831X_PWR_SRC_BATT, &val); + online = val.intval; -static irqreturn_t wm831x_pwr_src_irq(int irq, void *data) -{ - struct wm831x_power *wm831x_power = data; - struct wm831x *wm831x = wm831x_power->wm831x; + ret = wm831x_bat_check_status(power->wm831x, &status); + ret = wm831x_bat_check_health(power->wm831x, &health); - dev_dbg(wm831x->dev, "Power source changed\n"); + ret = wm831x_power_read_voltage(power->wm831x, WM831X_AUX_BATT, &val); + wm831x_batt_vol_level(power, val.intval, &level); + mod_timer(&power->timer, jiffies + msecs_to_jiffies(power->interval)); - /* Just notify for everything - little harm in overnotifying. - * The backup battery is not a power source while the system - * is running so skip that. - */ - power_supply_changed(&wm831x_power->battery); - power_supply_changed(&wm831x_power->usb); - power_supply_changed(&wm831x_power->wall); + if (online != power->priv.online || status != power->priv.status + || health != power->priv.health || level != power->priv.old_level) + { + power->priv.online = online; + power->priv.status = status; + power->priv.health = health; + power->priv.old_level = level; - return IRQ_HANDLED; + power_supply_changed(&power->battery); + } } static __devinit int wm831x_power_probe(struct platform_device *pdev) @@ -613,7 +805,6 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev) struct power_supply *usb; struct power_supply *battery; struct power_supply *wall; - struct power_supply *backup; int ret, irq, i; power = kzalloc(sizeof(struct wm831x_power), GFP_KERNEL); @@ -626,13 +817,11 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev) usb = &power->usb; battery = &power->battery; wall = &power->wall; - backup = &power->backup; /* We ignore configuration failures since we can still read back - * the status without enabling either of the chargers. + * the status without enabling the charger. */ wm831x_config_battery(wm831x); - wm831x_config_backup(wm831x); wall->name = "wm831x-wall"; wall->type = POWER_SUPPLY_TYPE_MAINS; @@ -661,41 +850,33 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev) if (ret) goto err_battery; - backup->name = "wm831x-backup"; - backup->type = POWER_SUPPLY_TYPE_BATTERY; - backup->properties = wm831x_backup_props; - backup->num_properties = ARRAY_SIZE(wm831x_backup_props); - backup->get_property = wm831x_backup_get_prop; - ret = power_supply_register(&pdev->dev, backup); - if (ret) - goto err_usb; - irq = platform_get_irq_byname(pdev, "SYSLO"); - ret = wm831x_request_irq(wm831x, irq, wm831x_syslo_irq, - IRQF_TRIGGER_RISING, "SYSLO", - power); + ret = request_threaded_irq(irq, NULL, wm831x_syslo_irq, + IRQF_TRIGGER_RISING, "System power low", + power); if (ret != 0) { dev_err(&pdev->dev, "Failed to request SYSLO IRQ %d: %d\n", irq, ret); - goto err_backup; + goto err_usb; } irq = platform_get_irq_byname(pdev, "PWR SRC"); - ret = wm831x_request_irq(wm831x, irq, wm831x_pwr_src_irq, - IRQF_TRIGGER_RISING, "Power source", - power); + ret = request_threaded_irq(irq, NULL, wm831x_pwr_src_irq, + IRQF_TRIGGER_RISING, "Power source", + power); if (ret != 0) { dev_err(&pdev->dev, "Failed to request PWR SRC IRQ %d: %d\n", irq, ret); goto err_syslo; } - +#if 0 //ʹÓòéѯµÄ·½Ê½ for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) { irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]); - ret = wm831x_request_irq(wm831x, irq, wm831x_bat_irq, - IRQF_TRIGGER_RISING, - wm831x_bat_irqs[i], - power); + ret = request_threaded_irq(irq, NULL, wm831x_bat_irq, + IRQF_TRIGGER_RISING, + wm831x_bat_irqs[i], + power); + WM_BATT_DBG("%s: %s irq no %d\n", __FUNCTION__, wm831x_bat_irqs[i], irq); if (ret != 0) { dev_err(&pdev->dev, "Failed to request %s IRQ %d: %d\n", @@ -703,21 +884,28 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev) goto err_bat_irq; } } - +#endif + power->interval = TIMER_MS_COUNTS; + power->priv.old_level = 100; + INIT_WORK(&power->batt_work, wm831x_batt_work); + setup_timer(&power->timer, wm831x_batt_timer_handler, (unsigned long)power); + power->timer.expires = jiffies + msecs_to_jiffies(1000); + add_timer(&power->timer); + + g_wm831x_power = power; + printk("%s:wm831x_power initialized\n",__FUNCTION__); return ret; err_bat_irq: for (; i >= 0; i--) { irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]); - wm831x_free_irq(wm831x, irq, power); + free_irq(irq, power); } irq = platform_get_irq_byname(pdev, "PWR SRC"); - wm831x_free_irq(wm831x, irq, power); + free_irq(irq, power); err_syslo: irq = platform_get_irq_byname(pdev, "SYSLO"); - wm831x_free_irq(wm831x, irq, power); -err_backup: - power_supply_unregister(backup); + free_irq(irq, power); err_usb: power_supply_unregister(usb); err_battery: @@ -732,30 +920,52 @@ err_kmalloc: static __devexit int wm831x_power_remove(struct platform_device *pdev) { struct wm831x_power *wm831x_power = platform_get_drvdata(pdev); - struct wm831x *wm831x = wm831x_power->wm831x; int irq, i; for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) { irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]); - wm831x_free_irq(wm831x, irq, wm831x_power); + free_irq(irq, wm831x_power); } irq = platform_get_irq_byname(pdev, "PWR SRC"); - wm831x_free_irq(wm831x, irq, wm831x_power); + free_irq(irq, wm831x_power); irq = platform_get_irq_byname(pdev, "SYSLO"); - wm831x_free_irq(wm831x, irq, wm831x_power); + free_irq(irq, wm831x_power); - power_supply_unregister(&wm831x_power->backup); power_supply_unregister(&wm831x_power->battery); power_supply_unregister(&wm831x_power->wall); power_supply_unregister(&wm831x_power->usb); + kfree(wm831x_power); + return 0; +} + +#ifdef CONFIG_PM +static int wm831x_battery_suspend(struct platform_device *dev, pm_message_t state) +{ + //struct wm831x_power *power = (struct wm831x_power *)platform_get_drvdata(dev); + //flush_scheduled_work(); + //del_timer(&power->timer); + return 0; +} + +static int wm831x_battery_resume(struct platform_device *dev) +{ + //struct wm831x_power *power = (struct wm831x_power *)platform_get_drvdata(dev); + //power->timer.expires = jiffies + msecs_to_jiffies(power->interval); + //add_timer(&power->timer); return 0; } +#else +#define wm831x_battery_suspend NULL +#define wm831x_battery_resume NULL +#endif static struct platform_driver wm831x_power_driver = { .probe = wm831x_power_probe, .remove = __devexit_p(wm831x_power_remove), + .suspend = wm831x_battery_suspend, + .resume = wm831x_battery_resume, .driver = { .name = "wm831x-power", }, diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c old mode 100644 new mode 100755 index 2eefc1a0cf08..02d1b3c3ecea --- a/drivers/regulator/wm831x-dcdc.c +++ b/drivers/regulator/wm831x-dcdc.c @@ -19,6 +19,9 @@ #include #include #include +#include +#include +#include #include #include @@ -32,25 +35,31 @@ #define WM831X_DCDC_MODE_IDLE 2 #define WM831X_DCDC_MODE_STANDBY 3 -#define WM831X_DCDC_MAX_NAME 6 +//#define WM831X_DCDC_MAX_NAME 6 /* Register offsets in control block */ #define WM831X_DCDC_CONTROL_1 0 #define WM831X_DCDC_CONTROL_2 1 #define WM831X_DCDC_ON_CONFIG 2 #define WM831X_DCDC_SLEEP_CONTROL 3 +#define WM831X_DCDC_DVS_CONTROL 4 /* * Shared */ - +#if 0 struct wm831x_dcdc { char name[WM831X_DCDC_MAX_NAME]; struct regulator_desc desc; int base; struct wm831x *wm831x; struct regulator_dev *regulator; + int dvs_gpio; + int dvs_gpio_state; + int on_vsel; + int dvs_vsel; }; +#endif static int wm831x_dcdc_is_enabled(struct regulator_dev *rdev) { @@ -240,11 +249,9 @@ static int wm831x_buckv_list_voltage(struct regulator_dev *rdev, return -EINVAL; } -static int wm831x_buckv_set_voltage_int(struct regulator_dev *rdev, int reg, - int min_uV, int max_uV) +static int wm831x_buckv_select_min_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) { - struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); - struct wm831x *wm831x = dcdc->wm831x; u16 vsel; if (min_uV < 600000) @@ -257,39 +264,126 @@ static int wm831x_buckv_set_voltage_int(struct regulator_dev *rdev, int reg, if (wm831x_buckv_list_voltage(rdev, vsel) > max_uV) return -EINVAL; - return wm831x_set_bits(wm831x, reg, WM831X_DC1_ON_VSEL_MASK, vsel); + return vsel; +} + +static int wm831x_buckv_select_max_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + u16 vsel; + + if (max_uV < 600000 || max_uV > 1800000) + return -EINVAL; + + vsel = ((max_uV - 600000) / 12500) + 8; + + if (wm831x_buckv_list_voltage(rdev, vsel) < min_uV || + wm831x_buckv_list_voltage(rdev, vsel) < max_uV) + return -EINVAL; + + return vsel; +} + +static int wm831x_buckv_set_dvs(struct regulator_dev *rdev, int state) +{ + struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); + + if (state == dcdc->dvs_gpio_state) + return 0; + + dcdc->dvs_gpio_state = state; + gpio_set_value(dcdc->dvs_gpio, state); + + /* Should wait for DVS state change to be asserted if we have + * a GPIO for it, for now assume the device is configured + * for the fastest possible transition. + */ + + return 0; } static int wm831x_buckv_set_voltage(struct regulator_dev *rdev, - int min_uV, int max_uV) + int min_uV, int max_uV) { struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); - u16 reg = dcdc->base + WM831X_DCDC_ON_CONFIG; + struct wm831x *wm831x = dcdc->wm831x; + int on_reg = dcdc->base + WM831X_DCDC_ON_CONFIG; + int dvs_reg = dcdc->base + WM831X_DCDC_DVS_CONTROL; + int vsel, ret; + + vsel = wm831x_buckv_select_min_voltage(rdev, min_uV, max_uV); + if (vsel < 0) + return vsel; + + /* If this value is already set then do a GPIO update if we can */ + if (dcdc->dvs_gpio && dcdc->on_vsel == vsel) + return wm831x_buckv_set_dvs(rdev, 0); + + if (dcdc->dvs_gpio && dcdc->dvs_vsel == vsel) + return wm831x_buckv_set_dvs(rdev, 1); + + /* Always set the ON status to the minimum voltage */ + ret = wm831x_set_bits(wm831x, on_reg, WM831X_DC1_ON_VSEL_MASK, vsel); + if (ret < 0) + return ret; + dcdc->on_vsel = vsel; + + if (!dcdc->dvs_gpio) + return ret; + + /* Kick the voltage transition now */ + ret = wm831x_buckv_set_dvs(rdev, 0); + if (ret < 0) + return ret; + + /* Set the high voltage as the DVS voltage. This is optimised + * for CPUfreq usage, most processors will keep the maximum + * voltage constant and lower the minimum with the frequency. */ + vsel = wm831x_buckv_select_max_voltage(rdev, min_uV, max_uV); + if (vsel < 0) { + /* This should never happen - at worst the same vsel + * should be chosen */ + WARN_ON(vsel < 0); + return 0; + } + + /* Don't bother if it's the same VSEL we're already using */ + if (vsel == dcdc->on_vsel) + return 0; - return wm831x_buckv_set_voltage_int(rdev, reg, min_uV, max_uV); + ret = wm831x_set_bits(wm831x, dvs_reg, WM831X_DC1_DVS_VSEL_MASK, vsel); + if (ret == 0) + dcdc->dvs_vsel = vsel; + else + dev_warn(wm831x->dev, "Failed to set DCDC DVS VSEL: %d\n", + ret); + + return 0; } static int wm831x_buckv_set_suspend_voltage(struct regulator_dev *rdev, - int uV) + int uV) { struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); + struct wm831x *wm831x = dcdc->wm831x; u16 reg = dcdc->base + WM831X_DCDC_SLEEP_CONTROL; + int vsel; + + vsel = wm831x_buckv_select_min_voltage(rdev, uV, uV); + if (vsel < 0) + return vsel; - return wm831x_buckv_set_voltage_int(rdev, reg, uV, uV); + return wm831x_set_bits(wm831x, reg, WM831X_DC1_SLP_VSEL_MASK, vsel); } static int wm831x_buckv_get_voltage(struct regulator_dev *rdev) { struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); - struct wm831x *wm831x = dcdc->wm831x; - u16 reg = dcdc->base + WM831X_DCDC_ON_CONFIG; - int val; - - val = wm831x_reg_read(wm831x, reg); - if (val < 0) - return val; - return wm831x_buckv_list_voltage(rdev, val & WM831X_DC1_ON_VSEL_MASK); + if (dcdc->dvs_gpio && dcdc->dvs_gpio_state) + return wm831x_buckv_list_voltage(rdev, dcdc->dvs_vsel); + else + return wm831x_buckv_list_voltage(rdev, dcdc->on_vsel); } /* Current limit options */ @@ -329,6 +423,17 @@ static int wm831x_buckv_get_current_limit(struct regulator_dev *rdev) return wm831x_dcdc_ilim[val & WM831X_DC1_HC_THR_MASK]; } +int wm831x_dcdc_set_suspend_enable(struct regulator_dev *rdev) +{ + + return 0; +} +int wm831x_dcdc_set_suspend_disable(struct regulator_dev *rdev) +{ + + return 0; +} + static struct regulator_ops wm831x_buckv_ops = { .set_voltage = wm831x_buckv_set_voltage, .get_voltage = wm831x_buckv_get_voltage, @@ -344,8 +449,68 @@ static struct regulator_ops wm831x_buckv_ops = { .get_mode = wm831x_dcdc_get_mode, .set_mode = wm831x_dcdc_set_mode, .set_suspend_mode = wm831x_dcdc_set_suspend_mode, + .set_suspend_enable = wm831x_dcdc_set_suspend_enable, + .set_suspend_disable = wm831x_dcdc_set_suspend_disable, }; +/* + * Set up DVS control. We just log errors since we can still run + * (with reduced performance) if we fail. + */ +static __devinit void wm831x_buckv_dvs_init(struct wm831x_dcdc *dcdc, + struct wm831x_buckv_pdata *pdata) +{ + struct wm831x *wm831x = dcdc->wm831x; + int ret; + u16 ctrl; + + if (!pdata || !pdata->dvs_gpio) + return; + + switch (pdata->dvs_control_src) { + case 1: + ctrl = 2 << WM831X_DC1_DVS_SRC_SHIFT; + break; + case 2: + ctrl = 3 << WM831X_DC1_DVS_SRC_SHIFT; + break; + default: + dev_err(wm831x->dev, "Invalid DVS control source %d for %s\n", + pdata->dvs_control_src, dcdc->name); + return; + } + + ret = wm831x_set_bits(wm831x, dcdc->base + WM831X_DCDC_DVS_CONTROL, + WM831X_DC1_DVS_SRC_MASK, ctrl); + if (ret < 0) { + dev_err(wm831x->dev, "Failed to set %s DVS source: %d\n", + dcdc->name, ret); + return; + } + + ret = gpio_request(pdata->dvs_gpio, "DCDC DVS"); + if (ret < 0) { + dev_err(wm831x->dev, "Failed to get %s DVS GPIO: %d\n", + dcdc->name, ret); + return; + } + + /* gpiolib won't let us read the GPIO status so pick the higher + * of the two existing voltages so we take it as platform data. + */ + dcdc->dvs_gpio_state = pdata->dvs_init_state; + + ret = gpio_direction_output(pdata->dvs_gpio, dcdc->dvs_gpio_state); + if (ret < 0) { + dev_err(wm831x->dev, "Failed to enable %s DVS GPIO: %d\n", + dcdc->name, ret); + gpio_free(pdata->dvs_gpio); + return; + } + + dcdc->dvs_gpio = pdata->dvs_gpio; +} + static __devinit int wm831x_buckv_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); @@ -354,7 +519,7 @@ static __devinit int wm831x_buckv_probe(struct platform_device *pdev) struct wm831x_dcdc *dcdc; struct resource *res; int ret, irq; - + dev_dbg(&pdev->dev, "Probing DCDC%d\n", id + 1); if (pdata == NULL || pdata->dcdc[id] == NULL) @@ -384,6 +549,23 @@ static __devinit int wm831x_buckv_probe(struct platform_device *pdev) dcdc->desc.ops = &wm831x_buckv_ops; dcdc->desc.owner = THIS_MODULE; + ret = wm831x_reg_read(wm831x, dcdc->base + WM831X_DCDC_ON_CONFIG); + if (ret < 0) { + dev_err(wm831x->dev, "Failed to read ON VSEL: %d\n", ret); + goto err; + } + dcdc->on_vsel = ret & WM831X_DC1_ON_VSEL_MASK; + + ret = wm831x_reg_read(wm831x, dcdc->base + WM831X_DCDC_ON_CONFIG); + if (ret < 0) { + dev_err(wm831x->dev, "Failed to read DVS VSEL: %d\n", ret); + goto err; + } + dcdc->dvs_vsel = ret & WM831X_DC1_DVS_VSEL_MASK; + + if (pdata->dcdc[id]) + wm831x_buckv_dvs_init(dcdc, pdata->dcdc[id]->driver_data); + dcdc->regulator = regulator_register(&dcdc->desc, &pdev->dev, pdata->dcdc[id], dcdc); if (IS_ERR(dcdc->regulator)) { @@ -422,6 +604,8 @@ err_uv: err_regulator: regulator_unregister(dcdc->regulator); err: + if (dcdc->dvs_gpio) + gpio_free(dcdc->dvs_gpio); kfree(dcdc); return ret; } @@ -431,9 +615,13 @@ static __devexit int wm831x_buckv_remove(struct platform_device *pdev) struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); struct wm831x *wm831x = dcdc->wm831x; + platform_set_drvdata(pdev, NULL); + wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "HC"), dcdc); wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), dcdc); regulator_unregister(dcdc->regulator); + if (dcdc->dvs_gpio) + gpio_free(dcdc->dvs_gpio); kfree(dcdc); return 0; @@ -444,6 +632,7 @@ static struct platform_driver wm831x_buckv_driver = { .remove = __devexit_p(wm831x_buckv_remove), .driver = { .name = "wm831x-buckv", + .owner = THIS_MODULE, }, }; @@ -523,6 +712,8 @@ static struct regulator_ops wm831x_buckp_ops = { .get_mode = wm831x_dcdc_get_mode, .set_mode = wm831x_dcdc_set_mode, .set_suspend_mode = wm831x_dcdc_set_suspend_mode, + .set_suspend_enable = wm831x_dcdc_set_suspend_enable, + .set_suspend_disable = wm831x_dcdc_set_suspend_disable, }; static __devinit int wm831x_buckp_probe(struct platform_device *pdev) @@ -598,6 +789,8 @@ static __devexit int wm831x_buckp_remove(struct platform_device *pdev) struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); struct wm831x *wm831x = dcdc->wm831x; + platform_set_drvdata(pdev, NULL); + wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), dcdc); regulator_unregister(dcdc->regulator); kfree(dcdc); @@ -610,6 +803,7 @@ static struct platform_driver wm831x_buckp_driver = { .remove = __devexit_p(wm831x_buckp_remove), .driver = { .name = "wm831x-buckp", + .owner = THIS_MODULE, }, }; @@ -724,6 +918,8 @@ static __devexit int wm831x_boostp_remove(struct platform_device *pdev) struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); struct wm831x *wm831x = dcdc->wm831x; + platform_set_drvdata(pdev, NULL); + wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), dcdc); regulator_unregister(dcdc->regulator); kfree(dcdc); @@ -736,6 +932,7 @@ static struct platform_driver wm831x_boostp_driver = { .remove = __devexit_p(wm831x_boostp_remove), .driver = { .name = "wm831x-boostp", + .owner = THIS_MODULE, }, }; @@ -808,6 +1005,8 @@ static __devexit int wm831x_epe_remove(struct platform_device *pdev) { struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); + platform_set_drvdata(pdev, NULL); + regulator_unregister(dcdc->regulator); kfree(dcdc); @@ -819,12 +1018,14 @@ static struct platform_driver wm831x_epe_driver = { .remove = __devexit_p(wm831x_epe_remove), .driver = { .name = "wm831x-epe", + .owner = THIS_MODULE, }, }; static int __init wm831x_dcdc_init(void) { int ret; + printk("%s \n", __FUNCTION__); ret = platform_driver_register(&wm831x_buckv_driver); if (ret != 0) pr_err("Failed to register WM831x BUCKV driver: %d\n", ret); @@ -840,7 +1041,7 @@ static int __init wm831x_dcdc_init(void) ret = platform_driver_register(&wm831x_epe_driver); if (ret != 0) pr_err("Failed to register WM831x EPE driver: %d\n", ret); - + return 0; } subsys_initcall(wm831x_dcdc_init); diff --git a/drivers/regulator/wm831x-isink.c b/drivers/regulator/wm831x-isink.c old mode 100644 new mode 100755 index 48857008758c..e754528100f8 --- a/drivers/regulator/wm831x-isink.c +++ b/drivers/regulator/wm831x-isink.c @@ -19,13 +19,15 @@ #include #include #include +#include #include #include #include -#define WM831X_ISINK_MAX_NAME 7 +//#define WM831X_ISINK_MAX_NAME 7 +#if 0 struct wm831x_isink { char name[WM831X_ISINK_MAX_NAME]; struct regulator_desc desc; @@ -33,13 +35,14 @@ struct wm831x_isink { struct wm831x *wm831x; struct regulator_dev *regulator; }; +#endif static int wm831x_isink_enable(struct regulator_dev *rdev) { struct wm831x_isink *isink = rdev_get_drvdata(rdev); struct wm831x *wm831x = isink->wm831x; int ret; - + printk("%s:line=%d\n",__FUNCTION__,__LINE__); /* We have a two stage enable: first start the ISINK... */ ret = wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_ENA, WM831X_CS1_ENA); @@ -51,7 +54,7 @@ static int wm831x_isink_enable(struct regulator_dev *rdev) WM831X_CS1_DRIVE); if (ret != 0) wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_ENA, 0); - + printk("%s:line=%d,ret=0x%x\n",__FUNCTION__,__LINE__,ret); return ret; } @@ -61,7 +64,7 @@ static int wm831x_isink_disable(struct regulator_dev *rdev) struct wm831x_isink *isink = rdev_get_drvdata(rdev); struct wm831x *wm831x = isink->wm831x; int ret; - + printk("%s:line=%d\n",__FUNCTION__,__LINE__); ret = wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_DRIVE, 0); if (ret < 0) return ret; @@ -79,11 +82,11 @@ static int wm831x_isink_is_enabled(struct regulator_dev *rdev) struct wm831x_isink *isink = rdev_get_drvdata(rdev); struct wm831x *wm831x = isink->wm831x; int ret; - + printk("%s:line=%d\n",__FUNCTION__,__LINE__); ret = wm831x_reg_read(wm831x, isink->reg); if (ret < 0) return ret; - + if ((ret & (WM831X_CS1_ENA | WM831X_CS1_DRIVE)) == (WM831X_CS1_ENA | WM831X_CS1_DRIVE)) return 1; @@ -157,7 +160,7 @@ static __devinit int wm831x_isink_probe(struct platform_device *pdev) int ret, irq; dev_dbg(&pdev->dev, "Probing ISINK%d\n", id + 1); - + printk("%s:line=%d\n",__FUNCTION__,__LINE__); if (pdata == NULL || pdata->isink[id] == NULL) return -ENODEV; @@ -197,6 +200,7 @@ static __devinit int wm831x_isink_probe(struct platform_device *pdev) } irq = platform_get_irq(pdev, 0); + printk("%s:line=%d,irq=%d\n",__FUNCTION__,__LINE__,irq); ret = wm831x_request_irq(wm831x, irq, wm831x_isink_irq, IRQF_TRIGGER_RISING, isink->name, isink); @@ -222,6 +226,8 @@ static __devexit int wm831x_isink_remove(struct platform_device *pdev) struct wm831x_isink *isink = platform_get_drvdata(pdev); struct wm831x *wm831x = isink->wm831x; + platform_set_drvdata(pdev, NULL); + wm831x_free_irq(wm831x, platform_get_irq(pdev, 0), isink); regulator_unregister(isink->regulator); @@ -235,6 +241,7 @@ static struct platform_driver wm831x_isink_driver = { .remove = __devexit_p(wm831x_isink_remove), .driver = { .name = "wm831x-isink", + .owner = THIS_MODULE, }, }; diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c old mode 100644 new mode 100755 index 902db56ce099..e44d9b4f9153 --- a/drivers/regulator/wm831x-ldo.c +++ b/drivers/regulator/wm831x-ldo.c @@ -19,12 +19,13 @@ #include #include #include +#include #include #include #include -#define WM831X_LDO_MAX_NAME 6 +//#define WM831X_LDO_MAX_NAME 6 #define WM831X_LDO_CONTROL 0 #define WM831X_LDO_ON_CONTROL 1 @@ -33,6 +34,7 @@ #define WM831X_ALIVE_LDO_ON_CONTROL 0 #define WM831X_ALIVE_LDO_SLEEP_CONTROL 1 +#if 0 struct wm831x_ldo { char name[WM831X_LDO_MAX_NAME]; struct regulator_desc desc; @@ -40,11 +42,12 @@ struct wm831x_ldo { struct wm831x *wm831x; struct regulator_dev *regulator; }; +#endif /* * Shared */ - +extern int reboot_cmd_get(void); static int wm831x_ldo_is_enabled(struct regulator_dev *rdev) { struct wm831x_ldo *ldo = rdev_get_drvdata(rdev); @@ -67,7 +70,7 @@ static int wm831x_ldo_enable(struct regulator_dev *rdev) struct wm831x_ldo *ldo = rdev_get_drvdata(rdev); struct wm831x *wm831x = ldo->wm831x; int mask = 1 << rdev_get_id(rdev); - + //printk("%s,%x\n", __FUNCTION__,mask); return wm831x_set_bits(wm831x, WM831X_LDO_ENABLE, mask, mask); } @@ -76,7 +79,7 @@ static int wm831x_ldo_disable(struct regulator_dev *rdev) struct wm831x_ldo *ldo = rdev_get_drvdata(rdev); struct wm831x *wm831x = ldo->wm831x; int mask = 1 << rdev_get_id(rdev); - + //printk("%s\n", __FUNCTION__); return wm831x_set_bits(wm831x, WM831X_LDO_ENABLE, mask, 0); } @@ -140,7 +143,7 @@ static int wm831x_gp_ldo_set_voltage(struct regulator_dev *rdev, { struct wm831x_ldo *ldo = rdev_get_drvdata(rdev); int reg = ldo->base + WM831X_LDO_ON_CONTROL; - + printk("%s base=%x,%d,%d\n", __FUNCTION__,ldo->base,min_uV,max_uV); return wm831x_gp_ldo_set_voltage_int(rdev, reg, min_uV, max_uV); } @@ -163,7 +166,7 @@ static int wm831x_gp_ldo_get_voltage(struct regulator_dev *rdev) ret = wm831x_reg_read(wm831x, reg); if (ret < 0) return ret; - + printk("%s base=%x,ret=%x\n", __FUNCTION__,ldo->base,ret); ret &= WM831X_LDO1_ON_VSEL_MASK; return wm831x_gp_ldo_list_voltage(rdev, ret); @@ -203,7 +206,7 @@ static int wm831x_gp_ldo_set_mode(struct regulator_dev *rdev, int on_reg = ldo->base + WM831X_LDO_ON_CONTROL; int ret; - + printk("%s base=%x,mode=%x\n", __FUNCTION__,ldo->base,mode); switch (mode) { case REGULATOR_MODE_NORMAL: ret = wm831x_set_bits(wm831x, on_reg, @@ -214,8 +217,7 @@ static int wm831x_gp_ldo_set_mode(struct regulator_dev *rdev, case REGULATOR_MODE_IDLE: ret = wm831x_set_bits(wm831x, ctrl_reg, - WM831X_LDO1_LP_MODE, - WM831X_LDO1_LP_MODE); + WM831X_LDO1_LP_MODE, 0); if (ret < 0) return ret; @@ -224,10 +226,12 @@ static int wm831x_gp_ldo_set_mode(struct regulator_dev *rdev, WM831X_LDO1_ON_MODE); if (ret < 0) return ret; + break; case REGULATOR_MODE_STANDBY: ret = wm831x_set_bits(wm831x, ctrl_reg, - WM831X_LDO1_LP_MODE, 0); + WM831X_LDO1_LP_MODE, + WM831X_LDO1_LP_MODE); if (ret < 0) return ret; @@ -282,6 +286,16 @@ static unsigned int wm831x_gp_ldo_get_optimum_mode(struct regulator_dev *rdev, return REGULATOR_MODE_NORMAL; } +int wm831x_ldo_set_suspend_enable(struct regulator_dev *rdev) +{ + + return 0; +} +int wm831x_ldo_set_suspend_disable(struct regulator_dev *rdev) +{ + + return 0; +} static struct regulator_ops wm831x_gp_ldo_ops = { .list_voltage = wm831x_gp_ldo_list_voltage, @@ -296,6 +310,8 @@ static struct regulator_ops wm831x_gp_ldo_ops = { .is_enabled = wm831x_ldo_is_enabled, .enable = wm831x_ldo_enable, .disable = wm831x_ldo_disable, + .set_suspend_enable = wm831x_ldo_set_suspend_enable, + .set_suspend_disable = wm831x_ldo_set_suspend_disable, }; static __devinit int wm831x_gp_ldo_probe(struct platform_device *pdev) @@ -308,7 +324,7 @@ static __devinit int wm831x_gp_ldo_probe(struct platform_device *pdev) int ret, irq; dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1); - + printk("Probing LDO%d\n", id + 1); if (pdata == NULL || pdata->ldo[id] == NULL) return -ENODEV; @@ -371,6 +387,8 @@ static __devexit int wm831x_gp_ldo_remove(struct platform_device *pdev) struct wm831x_ldo *ldo = platform_get_drvdata(pdev); struct wm831x *wm831x = ldo->wm831x; + platform_set_drvdata(pdev, NULL); + wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), ldo); regulator_unregister(ldo->regulator); kfree(ldo); @@ -383,6 +401,7 @@ static struct platform_driver wm831x_gp_ldo_driver = { .remove = __devexit_p(wm831x_gp_ldo_remove), .driver = { .name = "wm831x-ldo", + .owner = THIS_MODULE, }, }; @@ -436,7 +455,7 @@ static int wm831x_aldo_set_voltage(struct regulator_dev *rdev, { struct wm831x_ldo *ldo = rdev_get_drvdata(rdev); int reg = ldo->base + WM831X_LDO_ON_CONTROL; - + printk("%s base=%x,min_uV=%d,%d\n", __FUNCTION__,ldo->base,min_uV,max_uV); return wm831x_aldo_set_voltage_int(rdev, reg, min_uV, max_uV); } @@ -455,13 +474,13 @@ static int wm831x_aldo_get_voltage(struct regulator_dev *rdev) struct wm831x *wm831x = ldo->wm831x; int reg = ldo->base + WM831X_LDO_ON_CONTROL; int ret; - + ret = wm831x_reg_read(wm831x, reg); if (ret < 0) return ret; - + printk("%s base=%x,ret=%x\n", __FUNCTION__,ldo->base,ret); ret &= WM831X_LDO7_ON_VSEL_MASK; - + return wm831x_aldo_list_voltage(rdev, ret); } @@ -470,7 +489,7 @@ static unsigned int wm831x_aldo_get_mode(struct regulator_dev *rdev) struct wm831x_ldo *ldo = rdev_get_drvdata(rdev); struct wm831x *wm831x = ldo->wm831x; int on_reg = ldo->base + WM831X_LDO_ON_CONTROL; - unsigned int ret; + int ret; ret = wm831x_reg_read(wm831x, on_reg); if (ret < 0) @@ -553,6 +572,8 @@ static struct regulator_ops wm831x_aldo_ops = { .is_enabled = wm831x_ldo_is_enabled, .enable = wm831x_ldo_enable, .disable = wm831x_ldo_disable, + .set_suspend_enable = wm831x_ldo_set_suspend_enable, + .set_suspend_disable = wm831x_ldo_set_suspend_disable, }; static __devinit int wm831x_aldo_probe(struct platform_device *pdev) @@ -565,7 +586,7 @@ static __devinit int wm831x_aldo_probe(struct platform_device *pdev) int ret, irq; dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1); - + printk("Probing LDO%d--\n", id + 1); if (pdata == NULL || pdata->ldo[id] == NULL) return -ENODEV; @@ -640,6 +661,7 @@ static struct platform_driver wm831x_aldo_driver = { .remove = __devexit_p(wm831x_aldo_remove), .driver = { .name = "wm831x-aldo", + .owner = THIS_MODULE, }, }; @@ -738,6 +760,8 @@ static struct regulator_ops wm831x_alive_ldo_ops = { .is_enabled = wm831x_ldo_is_enabled, .enable = wm831x_ldo_enable, .disable = wm831x_ldo_disable, + .set_suspend_enable = wm831x_ldo_set_suspend_enable, + .set_suspend_disable = wm831x_ldo_set_suspend_disable, }; static __devinit int wm831x_alive_ldo_probe(struct platform_device *pdev) @@ -750,7 +774,7 @@ static __devinit int wm831x_alive_ldo_probe(struct platform_device *pdev) int ret; dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1); - + printk("wm831x_alive_ldo_probe Probing LDO%d\n", id + 1); if (pdata == NULL || pdata->ldo[id] == NULL) return -ENODEV; @@ -806,18 +830,72 @@ static __devexit int wm831x_alive_ldo_remove(struct platform_device *pdev) return 0; } +static __devexit int wm831x_alive_ldo_shutdown(struct platform_device *pdev) /*ZMF*/ +{ + //struct wm831x_ldo *ldo = platform_get_drvdata(pdev); + struct regulator* ldo; + + if (reboot_cmd_get()) + return 0; + printk("%s\n", __FUNCTION__); + + ldo = regulator_get(NULL, "ldo1"); + regulator_disable(ldo); + regulator_put(ldo); + + ldo = regulator_get(NULL, "ldo2"); + regulator_disable(ldo); + regulator_put(ldo); + + ldo = regulator_get(NULL, "ldo3"); + regulator_disable(ldo); + regulator_put(ldo); + + ldo = regulator_get(NULL, "ldo4"); + //regulator_disable(ldo); + regulator_put(ldo); + + ldo = regulator_get(NULL, "ldo5"); + regulator_disable(ldo); + regulator_put(ldo); + + ldo = regulator_get(NULL, "ldo6"); + regulator_disable(ldo); + regulator_put(ldo); + + ldo = regulator_get(NULL, "ldo7"); + regulator_disable(ldo); + regulator_put(ldo); + + ldo = regulator_get(NULL, "ldo8"); + //regulator_disable(ldo); + regulator_put(ldo); + + ldo = regulator_get(NULL, "ldo9"); + regulator_disable(ldo); + regulator_put(ldo); + + ldo = regulator_get(NULL, "ldo10"); + regulator_disable(ldo); + regulator_put(ldo); + + return 0; +} + static struct platform_driver wm831x_alive_ldo_driver = { .probe = wm831x_alive_ldo_probe, .remove = __devexit_p(wm831x_alive_ldo_remove), + .shutdown = __devexit_p(wm831x_alive_ldo_shutdown), .driver = { .name = "wm831x-alive-ldo", + .owner = THIS_MODULE, }, }; static int __init wm831x_ldo_init(void) { int ret; - + printk("%s \n", __FUNCTION__); ret = platform_driver_register(&wm831x_gp_ldo_driver); if (ret != 0) pr_err("Failed to register WM831x GP LDO driver: %d\n", ret); @@ -830,8 +908,7 @@ static int __init wm831x_ldo_init(void) if (ret != 0) pr_err("Failed to register WM831x alive LDO driver: %d\n", ret); - - return 0; + return 0; } subsys_initcall(wm831x_ldo_init); diff --git a/drivers/rtc/rtc-wm831x.c b/drivers/rtc/rtc-wm831x.c old mode 100644 new mode 100755 index 79795cdf6ed8..b6c830b7a2f9 --- a/drivers/rtc/rtc-wm831x.c +++ b/drivers/rtc/rtc-wm831x.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -423,7 +424,7 @@ static int wm831x_rtc_probe(struct platform_device *pdev) int per_irq = platform_get_irq_byname(pdev, "PER"); int alm_irq = platform_get_irq_byname(pdev, "ALM"); int ret = 0; - + //printk("wm831x_rtc_probe\n"); wm831x_rtc = kzalloc(sizeof(*wm831x_rtc), GFP_KERNEL); if (wm831x_rtc == NULL) return -ENOMEM; @@ -431,6 +432,38 @@ static int wm831x_rtc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, wm831x_rtc); wm831x_rtc->wm831x = wm831x; + #if 0 + /*set time when power on for debug*/ + ret = wm831x_reg_write(wm831x, WM831X_RTC_TIME_1, + (0x1000000 >> 16) & 0xffff); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to write TIME_1: %d\n", ret); + return ret; + } + + ret = wm831x_reg_write(wm831x, WM831X_RTC_TIME_2, 0x100000 & 0xffff); + + if (ret < 0) { + dev_err(&pdev->dev, "Failed to write TIME_2: %d\n", ret); + return ret; + } + + ret = wm831x_reg_read(wm831x, WM831X_RTC_TIME_1); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to read TIME_2: %d\n", ret); + goto err; + } + printk("%s:WM831X_RTC_TIME_1=0x%x\n",__FUNCTION__,ret); + ret = wm831x_reg_read(wm831x, WM831X_RTC_TIME_2); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to read TIME_2: %d\n", ret); + goto err; + } + printk("%s:WM831X_RTC_TIME_2=0x%x\n",__FUNCTION__,ret); + +#endif + + ret = wm831x_reg_read(wm831x, WM831X_RTC_CONTROL); if (ret < 0) { dev_err(&pdev->dev, "Failed to read RTC control: %d\n", ret); @@ -447,18 +480,18 @@ static int wm831x_rtc_probe(struct platform_device *pdev) ret = PTR_ERR(wm831x_rtc->rtc); goto err; } - - ret = wm831x_request_irq(wm831x, per_irq, wm831x_per_irq, - IRQF_TRIGGER_RISING, "wm831x_rtc_per", - wm831x_rtc); + //printk("1wm831x_rtc_probe=%d\n",per_irq); + ret = request_threaded_irq(per_irq, NULL, wm831x_per_irq, + IRQF_TRIGGER_RISING, "RTC period", + wm831x_rtc); if (ret != 0) { dev_err(&pdev->dev, "Failed to request periodic IRQ %d: %d\n", per_irq, ret); } - - ret = wm831x_request_irq(wm831x, alm_irq, wm831x_alm_irq, - IRQF_TRIGGER_RISING, "wm831x_rtc_alm", - wm831x_rtc); + //printk("2wm831x_rtc_probe=%d\n",alm_irq); + ret = request_threaded_irq(alm_irq, NULL, wm831x_alm_irq, + IRQF_TRIGGER_RISING, "RTC alarm", + wm831x_rtc); if (ret != 0) { dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n", alm_irq, ret); @@ -477,15 +510,15 @@ static int __devexit wm831x_rtc_remove(struct platform_device *pdev) int per_irq = platform_get_irq_byname(pdev, "PER"); int alm_irq = platform_get_irq_byname(pdev, "ALM"); - wm831x_free_irq(wm831x_rtc->wm831x, alm_irq, wm831x_rtc); - wm831x_free_irq(wm831x_rtc->wm831x, per_irq, wm831x_rtc); + free_irq(alm_irq, wm831x_rtc); + free_irq(per_irq, wm831x_rtc); rtc_device_unregister(wm831x_rtc->rtc); kfree(wm831x_rtc); return 0; } -static struct dev_pm_ops wm831x_rtc_pm_ops = { +static const struct dev_pm_ops wm831x_rtc_pm_ops = { .suspend = wm831x_rtc_suspend, .resume = wm831x_rtc_resume, diff --git a/drivers/video/backlight/wm831x_bl.c b/drivers/video/backlight/wm831x_bl.c old mode 100644 new mode 100755 index 467bdb7efb23..e985c24d8e25 --- a/drivers/video/backlight/wm831x_bl.c +++ b/drivers/video/backlight/wm831x_bl.c @@ -13,25 +13,47 @@ #include #include #include +#include #include #include #include - +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif +#include +#include +#define BL_SET 255 struct wm831x_backlight_data { struct wm831x *wm831x; int isink_reg; int current_brightness; }; - +#define TS_POLL_DELAY (10000*1000*1000) +int suspend_flag = 0; +int wm831x_bright = 0; +int max_tp = 0; +#ifdef CONFIG_HAS_EARLYSUSPEND +static struct backlight_device *wm831x_bl; +static struct timer_list wm831x_timer; +static struct wm831x_backlight_data *wm831x_data; +static struct wm831x *gpwm831x; +#endif +struct hrtimer wm831x_bl_timer; static int wm831x_backlight_set(struct backlight_device *bl, int brightness) { struct wm831x_backlight_data *data = bl_get_data(bl); struct wm831x *wm831x = data->wm831x; - int power_up = !data->current_brightness && brightness; - int power_down = data->current_brightness && !brightness; +// int power_up = !data->current_brightness && brightness; +// int power_down = data->current_brightness && !brightness; + int power_up; + int power_down; int ret; + int bright_tp; + bright_tp =( max_tp*brightness)/BL_SET; + power_up =!data->current_brightness && bright_tp; + power_down = data->current_brightness && !bright_tp; if (power_up) { /* Enable the ISINK */ ret = wm831x_set_bits(wm831x, data->isink_reg, @@ -62,7 +84,7 @@ static int wm831x_backlight_set(struct backlight_device *bl, int brightness) /* Set the new brightness */ ret = wm831x_set_bits(wm831x, data->isink_reg, - WM831X_CS1_ISEL_MASK, brightness); + WM831X_CS1_ISEL_MASK, bright_tp); if (ret < 0) goto err; @@ -93,7 +115,8 @@ err: static int wm831x_backlight_update_status(struct backlight_device *bl) { int brightness = bl->props.brightness; - + if (suspend_flag == 1) + brightness = 0; if (bl->props.power != FB_BLANK_UNBLANK) brightness = 0; @@ -102,7 +125,7 @@ static int wm831x_backlight_update_status(struct backlight_device *bl) if (bl->props.state & BL_CORE_SUSPENDED) brightness = 0; - + return wm831x_backlight_set(bl, brightness); } @@ -112,12 +135,39 @@ static int wm831x_backlight_get_brightness(struct backlight_device *bl) return data->current_brightness; } -static struct backlight_ops wm831x_backlight_ops = { +static const struct backlight_ops wm831x_backlight_ops = { .options = BL_CORE_SUSPENDRESUME, .update_status = wm831x_backlight_update_status, .get_brightness = wm831x_backlight_get_brightness, }; +static void wm831x_delaybacklight_timer(unsigned long data) +{ + wm831x_backlight_update_status(wm831x_bl); +} +#ifdef CONFIG_HAS_EARLYSUSPEND +static void wm831x_bl_suspend(struct early_suspend *h) +{ + suspend_flag = 1; + wm831x_delaybacklight_timer(NULL); +} + + +static void wm831x_bl_resume(struct early_suspend *h) +{ +// wm831x_timer.expires = jiffies + 30; +// add_timer(&wm831x_timer); + suspend_flag = 0; + wm831x_delaybacklight_timer(NULL); +} + +static struct early_suspend bl_early_suspend; +#endif +static enum hrtimer_restart wm831x_bl_timer_fuction(struct hrtimer *handle) +{ + backlight_update_status(wm831x_bl); + return HRTIMER_NORESTART; +} static int wm831x_backlight_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); @@ -125,7 +175,9 @@ static int wm831x_backlight_probe(struct platform_device *pdev) struct wm831x_backlight_pdata *pdata; struct wm831x_backlight_data *data; struct backlight_device *bl; + struct backlight_properties props; int ret, i, max_isel, isink_reg, dcdc_cfg; + /* We need platform data */ if (pdev->dev.parent->platform_data) { @@ -151,7 +203,7 @@ static int wm831x_backlight_probe(struct platform_device *pdev) return -EINVAL; } max_isel = i - 1; - + max_tp = max_isel; if (pdata->max_uA != wm831x_isinkv_values[max_isel]) dev_warn(&pdev->dev, "Maximum current is %duA not %duA as requested\n", @@ -187,28 +239,40 @@ static int wm831x_backlight_probe(struct platform_device *pdev) if (data == NULL) return -ENOMEM; - data->wm831x = wm831x; + data->wm831x = gpwm831x = wm831x; data->current_brightness = 0; data->isink_reg = isink_reg; - - bl = backlight_device_register("wm831x", &pdev->dev, - data, &wm831x_backlight_ops); + + props.max_brightness = max_isel; + //bl = backlight_device_register("wm831x", &pdev->dev, data, + // &wm831x_backlight_ops, &props); + wm831x_bl = bl = backlight_device_register("wm831x", &pdev->dev, data, + &wm831x_backlight_ops); if (IS_ERR(bl)) { dev_err(&pdev->dev, "failed to register backlight\n"); kfree(data); return PTR_ERR(bl); } + setup_timer(&wm831x_timer, wm831x_delaybacklight_timer, NULL); - bl->props.max_brightness = max_isel; - bl->props.brightness = max_isel; - + bl->props.brightness = BL_SET; + bl->props.max_brightness= BL_SET; + wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE, WM831X_DC4_ENA, 0); platform_set_drvdata(pdev, bl); +#ifdef CONFIG_HAS_EARLYSUSPEND + bl_early_suspend.suspend = wm831x_bl_suspend; + bl_early_suspend.resume = wm831x_bl_resume; + bl_early_suspend.level = ~0x0; + register_early_suspend(&bl_early_suspend); +#endif + hrtimer_init(&wm831x_bl_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + wm831x_bl_timer.function = wm831x_bl_timer_fuction; + hrtimer_start(&wm831x_bl_timer, ktime_set(0, TS_POLL_DELAY), + HRTIMER_MODE_REL); /* Disable the DCDC if it was started so we can bootstrap */ - wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE, WM831X_DC4_ENA, 0); - - - backlight_update_status(bl); + + //backlight_update_status(bl); return 0; } @@ -219,6 +283,9 @@ static int wm831x_backlight_remove(struct platform_device *pdev) struct wm831x_backlight_data *data = bl_get_data(bl); backlight_device_unregister(bl); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&bl_early_suspend); +#endif kfree(data); return 0; } diff --git a/drivers/watchdog/wm831x_wdt.c b/drivers/watchdog/wm831x_wdt.c old mode 100644 new mode 100755 index 775bcd807f31..8c4b2d5bb7da --- a/drivers/watchdog/wm831x_wdt.c +++ b/drivers/watchdog/wm831x_wdt.c @@ -213,7 +213,7 @@ static ssize_t wm831x_wdt_write(struct file *file, return count; } -static struct watchdog_info ident = { +static const struct watchdog_info ident = { .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, .identity = "WM831x Watchdog", }; -- 2.34.1