dect
/
linux-2.6
Archived
13
0
Fork 0

Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6

* 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6: (66 commits)
  mfd: Fix ucb1x00 build failure for collie_defconfig
  mfd: Fix lpc_sch related depends/selects, fix build error
  gpio: Fix sch_gpio warning
  gpio: add Intel SCH GPIO controller driver
  i2c: convert i2c-isch to platform_device
  mfd: Use completion interrupt for WM831x AUXADC
  mfd: Use completion interrupt for WM835x AUXADC
  mfd: Introduce remove_script function for twl4030
  mfd/mmc: SDHI Kconfig update
  mfd: sh_mobile_sdhi MMC_CAP_MMC_HIGHSPEED support
  gpiolib: Force wm831x GPIOs into GPIO mode when requested
  mfd: Add WM831x revision B support
  gpiolib: Correct debugfs display of WM831x GPIO inversion
  gpiolib: Actually set output state in wm831x_gpio_direction_output()
  tmio_mmc: Balance cell enable()/disable() calls
  tmio_mmc: Remove const from platform data V3
  tmio_mmc: Use 100ms mmc_detect_change() delay
  tmio_mmc: Add MMC_CAP_MMC_HIGHSPEED support V2
  tmio_mmc: Keep card-detect interrupts enabled
  mfd: Add twl6030 base addr for ID0, ID1, ID2
  ...
This commit is contained in:
Linus Torvalds 2010-03-07 15:56:04 -08:00
commit 6dc3eb5c1f
78 changed files with 12156 additions and 1029 deletions

View File

@ -94,6 +94,23 @@ config GPIO_VR41XX
help
Say yes here to support the NEC VR4100 series General-purpose I/O Uint
config GPIO_SCH
tristate "Intel SCH GPIO"
depends on GPIOLIB && PCI
select MFD_CORE
select LPC_SCH
help
Say yes here to support GPIO interface on Intel Poulsbo SCH.
The Intel SCH contains a total of 14 GPIO pins. Ten GPIOs are
powered by the core power rail and are turned off during sleep
modes (S3 and higher). The remaining four GPIOs are powered by
the Intel SCH suspend power supply. These GPIOs remain
active during S3. The suspend powered GPIOs can be used to wake the
system from the Suspend-to-RAM state.
This driver can also be built as a module. If so, the module
will be called sch-gpio.
comment "I2C GPIO expanders:"
config GPIO_MAX7300
@ -185,6 +202,20 @@ config GPIO_WM831X
Say yes here to access the GPIO signals of WM831x power management
chips from Wolfson Microelectronics.
config GPIO_WM8350
tristate "WM8350 GPIOs"
depends on MFD_WM8350
help
Say yes here to access the GPIO signals of WM8350 power management
chips from Wolfson Microelectronics.
config GPIO_WM8994
tristate "WM8994 GPIOs"
depends on MFD_WM8994
help
Say yes here to access the GPIO signals of WM8994 audio hub
CODECs from Wolfson Microelectronics.
config GPIO_ADP5520
tristate "GPIO Support for ADP5520 PMIC"
depends on PMIC_ADP5520

View File

@ -25,3 +25,6 @@ obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o
obj-$(CONFIG_GPIO_IT8761E) += it8761e_gpio.o
obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o
obj-$(CONFIG_GPIO_WM831X) += wm831x-gpio.o
obj-$(CONFIG_GPIO_WM8350) += wm8350-gpiolib.o
obj-$(CONFIG_GPIO_WM8994) += wm8994-gpio.o
obj-$(CONFIG_GPIO_SCH) += sch_gpio.o

295
drivers/gpio/sch_gpio.c Normal file
View File

@ -0,0 +1,295 @@
/*
* sch_gpio.c - GPIO interface for Intel Poulsbo SCH
*
* Copyright (c) 2010 CompuLab Ltd
* Author: Denis Turischev <denis@compulab.co.il>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License 2 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/errno.h>
#include <linux/acpi.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
static DEFINE_SPINLOCK(gpio_lock);
#define CGEN (0x00)
#define CGIO (0x04)
#define CGLV (0x08)
#define RGEN (0x20)
#define RGIO (0x24)
#define RGLV (0x28)
static unsigned short gpio_ba;
static int sch_gpio_core_direction_in(struct gpio_chip *gc, unsigned gpio_num)
{
u8 curr_dirs;
unsigned short offset, bit;
spin_lock(&gpio_lock);
offset = CGIO + gpio_num / 8;
bit = gpio_num % 8;
curr_dirs = inb(gpio_ba + offset);
if (!(curr_dirs & (1 << bit)))
outb(curr_dirs | (1 << bit), gpio_ba + offset);
spin_unlock(&gpio_lock);
return 0;
}
static int sch_gpio_core_get(struct gpio_chip *gc, unsigned gpio_num)
{
int res;
unsigned short offset, bit;
offset = CGLV + gpio_num / 8;
bit = gpio_num % 8;
res = !!(inb(gpio_ba + offset) & (1 << bit));
return res;
}
static void sch_gpio_core_set(struct gpio_chip *gc, unsigned gpio_num, int val)
{
u8 curr_vals;
unsigned short offset, bit;
spin_lock(&gpio_lock);
offset = CGLV + gpio_num / 8;
bit = gpio_num % 8;
curr_vals = inb(gpio_ba + offset);
if (val)
outb(curr_vals | (1 << bit), gpio_ba + offset);
else
outb((curr_vals & ~(1 << bit)), gpio_ba + offset);
spin_unlock(&gpio_lock);
}
static int sch_gpio_core_direction_out(struct gpio_chip *gc,
unsigned gpio_num, int val)
{
u8 curr_dirs;
unsigned short offset, bit;
sch_gpio_core_set(gc, gpio_num, val);
spin_lock(&gpio_lock);
offset = CGIO + gpio_num / 8;
bit = gpio_num % 8;
curr_dirs = inb(gpio_ba + offset);
if (curr_dirs & (1 << bit))
outb(curr_dirs & ~(1 << bit), gpio_ba + offset);
spin_unlock(&gpio_lock);
return 0;
}
static struct gpio_chip sch_gpio_core = {
.label = "sch_gpio_core",
.owner = THIS_MODULE,
.direction_input = sch_gpio_core_direction_in,
.get = sch_gpio_core_get,
.direction_output = sch_gpio_core_direction_out,
.set = sch_gpio_core_set,
};
static int sch_gpio_resume_direction_in(struct gpio_chip *gc,
unsigned gpio_num)
{
u8 curr_dirs;
spin_lock(&gpio_lock);
curr_dirs = inb(gpio_ba + RGIO);
if (!(curr_dirs & (1 << gpio_num)))
outb(curr_dirs | (1 << gpio_num) , gpio_ba + RGIO);
spin_unlock(&gpio_lock);
return 0;
}
static int sch_gpio_resume_get(struct gpio_chip *gc, unsigned gpio_num)
{
return !!(inb(gpio_ba + RGLV) & (1 << gpio_num));
}
static void sch_gpio_resume_set(struct gpio_chip *gc,
unsigned gpio_num, int val)
{
u8 curr_vals;
spin_lock(&gpio_lock);
curr_vals = inb(gpio_ba + RGLV);
if (val)
outb(curr_vals | (1 << gpio_num), gpio_ba + RGLV);
else
outb((curr_vals & ~(1 << gpio_num)), gpio_ba + RGLV);
spin_unlock(&gpio_lock);
}
static int sch_gpio_resume_direction_out(struct gpio_chip *gc,
unsigned gpio_num, int val)
{
u8 curr_dirs;
sch_gpio_resume_set(gc, gpio_num, val);
spin_lock(&gpio_lock);
curr_dirs = inb(gpio_ba + RGIO);
if (curr_dirs & (1 << gpio_num))
outb(curr_dirs & ~(1 << gpio_num), gpio_ba + RGIO);
spin_unlock(&gpio_lock);
return 0;
}
static struct gpio_chip sch_gpio_resume = {
.label = "sch_gpio_resume",
.owner = THIS_MODULE,
.direction_input = sch_gpio_resume_direction_in,
.get = sch_gpio_resume_get,
.direction_output = sch_gpio_resume_direction_out,
.set = sch_gpio_resume_set,
};
static int __devinit sch_gpio_probe(struct platform_device *pdev)
{
struct resource *res;
int err;
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (!res)
return -EBUSY;
if (!request_region(res->start, resource_size(res), pdev->name))
return -EBUSY;
gpio_ba = res->start;
sch_gpio_core.base = 0;
sch_gpio_core.ngpio = 10;
sch_gpio_core.dev = &pdev->dev;
sch_gpio_resume.base = 10;
sch_gpio_resume.ngpio = 4;
sch_gpio_resume.dev = &pdev->dev;
err = gpiochip_add(&sch_gpio_core);
if (err < 0)
goto err_sch_gpio_core;
err = gpiochip_add(&sch_gpio_resume);
if (err < 0)
goto err_sch_gpio_resume;
/*
* GPIO[6:0] enabled by default
* GPIO7 is configured by the CMC as SLPIOVR
* Enable GPIO[9:8] core powered gpios explicitly
*/
outb(0x3, gpio_ba + CGEN + 1);
/*
* SUS_GPIO[2:0] enabled by default
* Enable SUS_GPIO3 resume powered gpio explicitly
*/
outb(0x8, gpio_ba + RGEN);
return 0;
err_sch_gpio_resume:
err = gpiochip_remove(&sch_gpio_core);
if (err)
dev_err(&pdev->dev, "%s failed, %d\n",
"gpiochip_remove()", err);
err_sch_gpio_core:
release_region(res->start, resource_size(res));
gpio_ba = 0;
return err;
}
static int __devexit sch_gpio_remove(struct platform_device *pdev)
{
struct resource *res;
if (gpio_ba) {
int err;
err = gpiochip_remove(&sch_gpio_core);
if (err)
dev_err(&pdev->dev, "%s failed, %d\n",
"gpiochip_remove()", err);
err = gpiochip_remove(&sch_gpio_resume);
if (err)
dev_err(&pdev->dev, "%s failed, %d\n",
"gpiochip_remove()", err);
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
release_region(res->start, resource_size(res));
gpio_ba = 0;
return err;
}
return 0;
}
static struct platform_driver sch_gpio_driver = {
.driver = {
.name = "sch_gpio",
.owner = THIS_MODULE,
},
.probe = sch_gpio_probe,
.remove = __devexit_p(sch_gpio_remove),
};
static int __init sch_gpio_init(void)
{
return platform_driver_register(&sch_gpio_driver);
}
static void __exit sch_gpio_exit(void)
{
platform_driver_unregister(&sch_gpio_driver);
}
module_init(sch_gpio_init);
module_exit(sch_gpio_exit);
MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
MODULE_DESCRIPTION("GPIO interface for Intel Poulsbo SCH");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:sch_gpio");

View File

@ -38,10 +38,14 @@ 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;
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)
@ -60,16 +64,6 @@ static int wm831x_gpio_get(struct gpio_chip *chip, unsigned offset)
return 0;
}
static int wm831x_gpio_direction_out(struct gpio_chip *chip,
unsigned offset, int value)
{
struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
struct wm831x *wm831x = wm831x_gpio->wm831x;
return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + offset,
WM831X_GPN_DIR | WM831X_GPN_TRI, 0);
}
static void wm831x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
@ -79,6 +73,29 @@ static void wm831x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
value << offset);
}
static int wm831x_gpio_direction_out(struct gpio_chip *chip,
unsigned offset, int value)
{
struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
struct wm831x *wm831x = wm831x_gpio->wm831x;
int val = 0;
int ret;
if (wm831x->has_gpio_ena)
val |= WM831X_GPN_TRI;
ret = wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + offset,
WM831X_GPN_DIR | WM831X_GPN_TRI |
WM831X_GPN_FN_MASK, val);
if (ret < 0)
return ret;
/* Can only set GPIO state once it's in output mode */
wm831x_gpio_set(chip, offset, value);
return 0;
}
static int wm831x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{
struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
@ -95,7 +112,7 @@ 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;
@ -162,15 +179,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);
}
}

View File

@ -0,0 +1,181 @@
/*
* wm835x-gpiolib.c -- gpiolib support for Wolfson WM835x PMICs
*
* Copyright 2009 Wolfson Microelectronics PLC.
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/mfd/core.h>
#include <linux/platform_device.h>
#include <linux/seq_file.h>
#include <linux/mfd/wm8350/core.h>
#include <linux/mfd/wm8350/gpio.h>
struct wm8350_gpio_data {
struct wm8350 *wm8350;
struct gpio_chip gpio_chip;
};
static inline struct wm8350_gpio_data *to_wm8350_gpio(struct gpio_chip *chip)
{
return container_of(chip, struct wm8350_gpio_data, gpio_chip);
}
static int wm8350_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
{
struct wm8350_gpio_data *wm8350_gpio = to_wm8350_gpio(chip);
struct wm8350 *wm8350 = wm8350_gpio->wm8350;
return wm8350_set_bits(wm8350, WM8350_GPIO_CONFIGURATION_I_O,
1 << offset);
}
static int wm8350_gpio_get(struct gpio_chip *chip, unsigned offset)
{
struct wm8350_gpio_data *wm8350_gpio = to_wm8350_gpio(chip);
struct wm8350 *wm8350 = wm8350_gpio->wm8350;
int ret;
ret = wm8350_reg_read(wm8350, WM8350_GPIO_LEVEL);
if (ret < 0)
return ret;
if (ret & (1 << offset))
return 1;
else
return 0;
}
static void wm8350_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct wm8350_gpio_data *wm8350_gpio = to_wm8350_gpio(chip);
struct wm8350 *wm8350 = wm8350_gpio->wm8350;
if (value)
wm8350_set_bits(wm8350, WM8350_GPIO_LEVEL, 1 << offset);
else
wm8350_clear_bits(wm8350, WM8350_GPIO_LEVEL, 1 << offset);
}
static int wm8350_gpio_direction_out(struct gpio_chip *chip,
unsigned offset, int value)
{
struct wm8350_gpio_data *wm8350_gpio = to_wm8350_gpio(chip);
struct wm8350 *wm8350 = wm8350_gpio->wm8350;
int ret;
ret = wm8350_clear_bits(wm8350, WM8350_GPIO_CONFIGURATION_I_O,
1 << offset);
if (ret < 0)
return ret;
/* Don't have an atomic direction/value setup */
wm8350_gpio_set(chip, offset, value);
return 0;
}
static int wm8350_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{
struct wm8350_gpio_data *wm8350_gpio = to_wm8350_gpio(chip);
struct wm8350 *wm8350 = wm8350_gpio->wm8350;
if (!wm8350->irq_base)
return -EINVAL;
return wm8350->irq_base + WM8350_IRQ_GPIO(offset);
}
static struct gpio_chip template_chip = {
.label = "wm8350",
.owner = THIS_MODULE,
.direction_input = wm8350_gpio_direction_in,
.get = wm8350_gpio_get,
.direction_output = wm8350_gpio_direction_out,
.set = wm8350_gpio_set,
.to_irq = wm8350_gpio_to_irq,
.can_sleep = 1,
};
static int __devinit wm8350_gpio_probe(struct platform_device *pdev)
{
struct wm8350 *wm8350 = dev_get_drvdata(pdev->dev.parent);
struct wm8350_platform_data *pdata = wm8350->dev->platform_data;
struct wm8350_gpio_data *wm8350_gpio;
int ret;
wm8350_gpio = kzalloc(sizeof(*wm8350_gpio), GFP_KERNEL);
if (wm8350_gpio == NULL)
return -ENOMEM;
wm8350_gpio->wm8350 = wm8350;
wm8350_gpio->gpio_chip = template_chip;
wm8350_gpio->gpio_chip.ngpio = 13;
wm8350_gpio->gpio_chip.dev = &pdev->dev;
if (pdata && pdata->gpio_base)
wm8350_gpio->gpio_chip.base = pdata->gpio_base;
else
wm8350_gpio->gpio_chip.base = -1;
ret = gpiochip_add(&wm8350_gpio->gpio_chip);
if (ret < 0) {
dev_err(&pdev->dev, "Could not register gpiochip, %d\n",
ret);
goto err;
}
platform_set_drvdata(pdev, wm8350_gpio);
return ret;
err:
kfree(wm8350_gpio);
return ret;
}
static int __devexit wm8350_gpio_remove(struct platform_device *pdev)
{
struct wm8350_gpio_data *wm8350_gpio = platform_get_drvdata(pdev);
int ret;
ret = gpiochip_remove(&wm8350_gpio->gpio_chip);
if (ret == 0)
kfree(wm8350_gpio);
return ret;
}
static struct platform_driver wm8350_gpio_driver = {
.driver.name = "wm8350-gpio",
.driver.owner = THIS_MODULE,
.probe = wm8350_gpio_probe,
.remove = __devexit_p(wm8350_gpio_remove),
};
static int __init wm8350_gpio_init(void)
{
return platform_driver_register(&wm8350_gpio_driver);
}
subsys_initcall(wm8350_gpio_init);
static void __exit wm8350_gpio_exit(void)
{
platform_driver_unregister(&wm8350_gpio_driver);
}
module_exit(wm8350_gpio_exit);
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_DESCRIPTION("GPIO interface for WM8350 PMICs");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:wm8350-gpio");

204
drivers/gpio/wm8994-gpio.c Normal file
View File

@ -0,0 +1,204 @@
/*
* wm8994-gpio.c -- gpiolib support for Wolfson WM8994
*
* Copyright 2009 Wolfson Microelectronics PLC.
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/mfd/core.h>
#include <linux/platform_device.h>
#include <linux/seq_file.h>
#include <linux/mfd/wm8994/core.h>
#include <linux/mfd/wm8994/pdata.h>
#include <linux/mfd/wm8994/gpio.h>
#include <linux/mfd/wm8994/registers.h>
struct wm8994_gpio {
struct wm8994 *wm8994;
struct gpio_chip gpio_chip;
};
static inline struct wm8994_gpio *to_wm8994_gpio(struct gpio_chip *chip)
{
return container_of(chip, struct wm8994_gpio, gpio_chip);
}
static int wm8994_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
{
struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip);
struct wm8994 *wm8994 = wm8994_gpio->wm8994;
return wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset,
WM8994_GPN_DIR, WM8994_GPN_DIR);
}
static int wm8994_gpio_get(struct gpio_chip *chip, unsigned offset)
{
struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip);
struct wm8994 *wm8994 = wm8994_gpio->wm8994;
int ret;
ret = wm8994_reg_read(wm8994, WM8994_GPIO_1 + offset);
if (ret < 0)
return ret;
if (ret & WM8994_GPN_LVL)
return 1;
else
return 0;
}
static int wm8994_gpio_direction_out(struct gpio_chip *chip,
unsigned offset, int value)
{
struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip);
struct wm8994 *wm8994 = wm8994_gpio->wm8994;
return wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset,
WM8994_GPN_DIR, 0);
}
static void wm8994_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip);
struct wm8994 *wm8994 = wm8994_gpio->wm8994;
if (value)
value = WM8994_GPN_LVL;
wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset, WM8994_GPN_LVL, value);
}
#ifdef CONFIG_DEBUG_FS
static void wm8994_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
{
struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip);
struct wm8994 *wm8994 = wm8994_gpio->wm8994;
int i;
for (i = 0; i < chip->ngpio; i++) {
int gpio = i + chip->base;
int reg;
const char *label;
/* We report the GPIO even if it's not requested since
* we're also reporting things like alternate
* functions which apply even when the GPIO is not in
* use as a GPIO.
*/
label = gpiochip_is_requested(chip, i);
if (!label)
label = "Unrequested";
seq_printf(s, " gpio-%-3d (%-20.20s) ", gpio, label);
reg = wm8994_reg_read(wm8994, WM8994_GPIO_1 + i);
if (reg < 0) {
dev_err(wm8994->dev,
"GPIO control %d read failed: %d\n",
gpio, reg);
seq_printf(s, "\n");
continue;
}
/* No decode yet; note that GPIO2 is special */
seq_printf(s, "(%x)\n", reg);
}
}
#else
#define wm8994_gpio_dbg_show NULL
#endif
static struct gpio_chip template_chip = {
.label = "wm8994",
.owner = THIS_MODULE,
.direction_input = wm8994_gpio_direction_in,
.get = wm8994_gpio_get,
.direction_output = wm8994_gpio_direction_out,
.set = wm8994_gpio_set,
.dbg_show = wm8994_gpio_dbg_show,
.can_sleep = 1,
};
static int __devinit wm8994_gpio_probe(struct platform_device *pdev)
{
struct wm8994 *wm8994 = dev_get_drvdata(pdev->dev.parent);
struct wm8994_pdata *pdata = wm8994->dev->platform_data;
struct wm8994_gpio *wm8994_gpio;
int ret;
wm8994_gpio = kzalloc(sizeof(*wm8994_gpio), GFP_KERNEL);
if (wm8994_gpio == NULL)
return -ENOMEM;
wm8994_gpio->wm8994 = wm8994;
wm8994_gpio->gpio_chip = template_chip;
wm8994_gpio->gpio_chip.ngpio = WM8994_GPIO_MAX;
wm8994_gpio->gpio_chip.dev = &pdev->dev;
if (pdata && pdata->gpio_base)
wm8994_gpio->gpio_chip.base = pdata->gpio_base;
else
wm8994_gpio->gpio_chip.base = -1;
ret = gpiochip_add(&wm8994_gpio->gpio_chip);
if (ret < 0) {
dev_err(&pdev->dev, "Could not register gpiochip, %d\n",
ret);
goto err;
}
platform_set_drvdata(pdev, wm8994_gpio);
return ret;
err:
kfree(wm8994_gpio);
return ret;
}
static int __devexit wm8994_gpio_remove(struct platform_device *pdev)
{
struct wm8994_gpio *wm8994_gpio = platform_get_drvdata(pdev);
int ret;
ret = gpiochip_remove(&wm8994_gpio->gpio_chip);
if (ret == 0)
kfree(wm8994_gpio);
return ret;
}
static struct platform_driver wm8994_gpio_driver = {
.driver.name = "wm8994-gpio",
.driver.owner = THIS_MODULE,
.probe = wm8994_gpio_probe,
.remove = __devexit_p(wm8994_gpio_remove),
};
static int __init wm8994_gpio_init(void)
{
return platform_driver_register(&wm8994_gpio_driver);
}
subsys_initcall(wm8994_gpio_init);
static void __exit wm8994_gpio_exit(void)
{
platform_driver_unregister(&wm8994_gpio_driver);
}
module_exit(wm8994_gpio_exit);
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_DESCRIPTION("GPIO interface for WM8994");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:wm8994-gpio");

View File

@ -106,6 +106,8 @@ config I2C_I801
config I2C_ISCH
tristate "Intel SCH SMBus 1.0"
depends on PCI
select MFD_CORE
select LPC_SCH
help
Say Y here if you want to use SMBus controller on the Intel SCH
based systems.

View File

@ -27,7 +27,7 @@
*/
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/stddef.h>
@ -46,12 +46,6 @@
#define SMBHSTDAT1 (7 + sch_smba)
#define SMBBLKDAT (0x20 + sch_smba)
/* count for request_region */
#define SMBIOSIZE 64
/* PCI Address Constants */
#define SMBBA_SCH 0x40
/* Other settings */
#define MAX_TIMEOUT 500
@ -63,7 +57,6 @@
#define SCH_BLOCK_DATA 0x05
static unsigned short sch_smba;
static struct pci_driver sch_driver;
static struct i2c_adapter sch_adapter;
/*
@ -256,37 +249,23 @@ static struct i2c_adapter sch_adapter = {
.algo = &smbus_algorithm,
};
static const struct pci_device_id sch_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, sch_ids);
static int __devinit sch_probe(struct pci_dev *dev,
const struct pci_device_id *id)
static int __devinit smbus_sch_probe(struct platform_device *dev)
{
struct resource *res;
int retval;
unsigned int smba;
pci_read_config_dword(dev, SMBBA_SCH, &smba);
if (!(smba & (1 << 31))) {
dev_err(&dev->dev, "SMBus I/O space disabled!\n");
return -ENODEV;
}
res = platform_get_resource(dev, IORESOURCE_IO, 0);
if (!res)
return -EBUSY;
sch_smba = (unsigned short)smba;
if (sch_smba == 0) {
dev_err(&dev->dev, "SMBus base address uninitialized!\n");
return -ENODEV;
}
if (acpi_check_region(sch_smba, SMBIOSIZE, sch_driver.name))
return -ENODEV;
if (!request_region(sch_smba, SMBIOSIZE, sch_driver.name)) {
if (!request_region(res->start, resource_size(res), dev->name)) {
dev_err(&dev->dev, "SMBus region 0x%x already in use!\n",
sch_smba);
return -EBUSY;
}
sch_smba = res->start;
dev_dbg(&dev->dev, "SMBA = 0x%X\n", sch_smba);
/* set up the sysfs linkage to our parent device */
@ -298,37 +277,43 @@ static int __devinit sch_probe(struct pci_dev *dev,
retval = i2c_add_adapter(&sch_adapter);
if (retval) {
dev_err(&dev->dev, "Couldn't register adapter!\n");
release_region(sch_smba, SMBIOSIZE);
release_region(res->start, resource_size(res));
sch_smba = 0;
}
return retval;
}
static void __devexit sch_remove(struct pci_dev *dev)
static int __devexit smbus_sch_remove(struct platform_device *pdev)
{
struct resource *res;
if (sch_smba) {
i2c_del_adapter(&sch_adapter);
release_region(sch_smba, SMBIOSIZE);
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
release_region(res->start, resource_size(res));
sch_smba = 0;
}
return 0;
}
static struct pci_driver sch_driver = {
.name = "isch_smbus",
.id_table = sch_ids,
.probe = sch_probe,
.remove = __devexit_p(sch_remove),
static struct platform_driver smbus_sch_driver = {
.driver = {
.name = "isch_smbus",
.owner = THIS_MODULE,
},
.probe = smbus_sch_probe,
.remove = __devexit_p(smbus_sch_remove),
};
static int __init i2c_sch_init(void)
{
return pci_register_driver(&sch_driver);
return platform_driver_register(&smbus_sch_driver);
}
static void __exit i2c_sch_exit(void)
{
pci_unregister_driver(&sch_driver);
platform_driver_unregister(&smbus_sch_driver);
}
MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@intel.com>");
@ -337,3 +322,4 @@ MODULE_LICENSE("GPL");
module_init(i2c_sch_init);
module_exit(i2c_sch_exit);
MODULE_ALIAS("platform:isch_smbus");

View File

@ -0,0 +1,155 @@
/*
* 88pm860x_onkey.c - Marvell 88PM860x ONKEY driver
*
* Copyright (C) 2009-2010 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.com>
*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file "COPYING" in the main directory of this
* archive for more details.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/mfd/88pm860x.h>
#define PM8607_WAKEUP 0x0b
#define LONG_ONKEY_EN (1 << 1)
#define ONKEY_STATUS (1 << 0)
struct pm860x_onkey_info {
struct input_dev *idev;
struct pm860x_chip *chip;
struct i2c_client *i2c;
struct device *dev;
int irq;
};
/* 88PM860x gives us an interrupt when ONKEY is held */
static irqreturn_t pm860x_onkey_handler(int irq, void *data)
{
struct pm860x_onkey_info *info = data;
int ret;
ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
ret &= ONKEY_STATUS;
input_report_key(info->idev, KEY_POWER, ret);
input_sync(info->idev);
/* Enable 8-second long onkey detection */
pm860x_set_bits(info->i2c, PM8607_WAKEUP, 3, LONG_ONKEY_EN);
return IRQ_HANDLED;
}
static int __devinit pm860x_onkey_probe(struct platform_device *pdev)
{
struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct pm860x_onkey_info *info;
int irq, ret;
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "No IRQ resource!\n");
return -EINVAL;
}
info = kzalloc(sizeof(struct pm860x_onkey_info), GFP_KERNEL);
if (!info)
return -ENOMEM;
info->chip = chip;
info->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
info->dev = &pdev->dev;
info->irq = irq + chip->irq_base;
info->idev = input_allocate_device();
if (!info->idev) {
dev_err(chip->dev, "Failed to allocate input dev\n");
ret = -ENOMEM;
goto out;
}
info->idev->name = "88pm860x_on";
info->idev->phys = "88pm860x_on/input0";
info->idev->id.bustype = BUS_I2C;
info->idev->dev.parent = &pdev->dev;
info->irq = irq;
info->idev->evbit[0] = BIT_MASK(EV_KEY);
info->idev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER);
ret = input_register_device(info->idev);
if (ret) {
dev_err(chip->dev, "Can't register input device: %d\n", ret);
goto out_reg;
}
ret = request_threaded_irq(info->irq, NULL, pm860x_onkey_handler,
IRQF_ONESHOT, "onkey", info);
if (ret < 0) {
dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
info->irq, ret);
goto out_irq;
}
platform_set_drvdata(pdev, info);
return 0;
out_irq:
input_unregister_device(info->idev);
kfree(info);
return ret;
out_reg:
input_free_device(info->idev);
out:
kfree(info);
return ret;
}
static int __devexit pm860x_onkey_remove(struct platform_device *pdev)
{
struct pm860x_onkey_info *info = platform_get_drvdata(pdev);
free_irq(info->irq, info);
input_unregister_device(info->idev);
kfree(info);
return 0;
}
static struct platform_driver pm860x_onkey_driver = {
.driver = {
.name = "88pm860x-onkey",
.owner = THIS_MODULE,
},
.probe = pm860x_onkey_probe,
.remove = __devexit_p(pm860x_onkey_remove),
};
static int __init pm860x_onkey_init(void)
{
return platform_driver_register(&pm860x_onkey_driver);
}
module_init(pm860x_onkey_init);
static void __exit pm860x_onkey_exit(void)
{
platform_driver_unregister(&pm860x_onkey_driver);
}
module_exit(pm860x_onkey_exit);
MODULE_DESCRIPTION("Marvell 88PM860x ONKEY driver");
MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
MODULE_LICENSE("GPL");

View File

@ -12,6 +12,16 @@ menuconfig INPUT_MISC
if INPUT_MISC
config INPUT_88PM860X_ONKEY
tristate "88PM860x ONKEY support"
depends on MFD_88PM860X
help
Support the ONKEY of Marvell 88PM860x PMICs as an input device
reporting power button status.
To compile this driver as a module, choose M here: the module
will be called 88pm860x_onkey.
config INPUT_PCSPKR
tristate "PC Speaker support"
depends on PCSPKR_PLATFORM

View File

@ -4,6 +4,7 @@
# Each configuration option enables a list of files.
obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o
obj-$(CONFIG_INPUT_APANEL) += apanel.o
obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o
obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o

View File

@ -0,0 +1,236 @@
/*
* Touchscreen driver for Marvell 88PM860x
*
* Copyright (C) 2009 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/mfd/88pm860x.h>
#define MEAS_LEN (8)
#define ACCURATE_BIT (12)
/* touch register */
#define MEAS_EN3 (0x52)
#define MEAS_TSIX_1 (0x8D)
#define MEAS_TSIX_2 (0x8E)
#define MEAS_TSIY_1 (0x8F)
#define MEAS_TSIY_2 (0x90)
#define MEAS_TSIZ1_1 (0x91)
#define MEAS_TSIZ1_2 (0x92)
#define MEAS_TSIZ2_1 (0x93)
#define MEAS_TSIZ2_2 (0x94)
/* bit definitions of touch */
#define MEAS_PD_EN (1 << 3)
#define MEAS_TSIX_EN (1 << 4)
#define MEAS_TSIY_EN (1 << 5)
#define MEAS_TSIZ1_EN (1 << 6)
#define MEAS_TSIZ2_EN (1 << 7)
struct pm860x_touch {
struct input_dev *idev;
struct i2c_client *i2c;
struct pm860x_chip *chip;
int irq;
int res_x; /* resistor of Xplate */
};
static irqreturn_t pm860x_touch_handler(int irq, void *data)
{
struct pm860x_touch *touch = data;
struct pm860x_chip *chip = touch->chip;
unsigned char buf[MEAS_LEN];
int x, y, pen_down;
int z1, z2, rt = 0;
int ret;
ret = pm860x_bulk_read(touch->i2c, MEAS_TSIX_1, MEAS_LEN, buf);
if (ret < 0)
goto out;
pen_down = buf[1] & (1 << 6);
x = ((buf[0] & 0xFF) << 4) | (buf[1] & 0x0F);
y = ((buf[2] & 0xFF) << 4) | (buf[3] & 0x0F);
z1 = ((buf[4] & 0xFF) << 4) | (buf[5] & 0x0F);
z2 = ((buf[6] & 0xFF) << 4) | (buf[7] & 0x0F);
if (pen_down) {
if ((x != 0) && (z1 != 0) && (touch->res_x != 0)) {
rt = z2 / z1 - 1;
rt = (rt * touch->res_x * x) >> ACCURATE_BIT;
dev_dbg(chip->dev, "z1:%d, z2:%d, rt:%d\n",
z1, z2, rt);
}
input_report_abs(touch->idev, ABS_X, x);
input_report_abs(touch->idev, ABS_Y, y);
input_report_abs(touch->idev, ABS_PRESSURE, rt);
input_report_key(touch->idev, BTN_TOUCH, 1);
dev_dbg(chip->dev, "pen down at [%d, %d].\n", x, y);
} else {
input_report_abs(touch->idev, ABS_PRESSURE, 0);
input_report_key(touch->idev, BTN_TOUCH, 0);
dev_dbg(chip->dev, "pen release\n");
}
input_sync(touch->idev);
out:
return IRQ_HANDLED;
}
static int pm860x_touch_open(struct input_dev *dev)
{
struct pm860x_touch *touch = input_get_drvdata(dev);
int data, ret;
data = MEAS_PD_EN | MEAS_TSIX_EN | MEAS_TSIY_EN
| MEAS_TSIZ1_EN | MEAS_TSIZ2_EN;
ret = pm860x_set_bits(touch->i2c, MEAS_EN3, data, data);
if (ret < 0)
goto out;
return 0;
out:
return ret;
}
static void pm860x_touch_close(struct input_dev *dev)
{
struct pm860x_touch *touch = input_get_drvdata(dev);
int data;
data = MEAS_PD_EN | MEAS_TSIX_EN | MEAS_TSIY_EN
| MEAS_TSIZ1_EN | MEAS_TSIZ2_EN;
pm860x_set_bits(touch->i2c, MEAS_EN3, data, 0);
}
static int __devinit pm860x_touch_probe(struct platform_device *pdev)
{
struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct pm860x_platform_data *pm860x_pdata = \
pdev->dev.parent->platform_data;
struct pm860x_touch_pdata *pdata = NULL;
struct pm860x_touch *touch;
int irq, ret;
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "No IRQ resource!\n");
return -EINVAL;
}
if (!pm860x_pdata) {
dev_err(&pdev->dev, "platform data is missing\n");
return -EINVAL;
}
pdata = pm860x_pdata->touch;
if (!pdata) {
dev_err(&pdev->dev, "touchscreen data is missing\n");
return -EINVAL;
}
touch = kzalloc(sizeof(struct pm860x_touch), GFP_KERNEL);
if (touch == NULL)
return -ENOMEM;
dev_set_drvdata(&pdev->dev, touch);
touch->idev = input_allocate_device();
if (touch->idev == NULL) {
dev_err(&pdev->dev, "Failed to allocate input device!\n");
ret = -ENOMEM;
goto out;
}
touch->idev->name = "88pm860x-touch";
touch->idev->phys = "88pm860x/input0";
touch->idev->id.bustype = BUS_I2C;
touch->idev->dev.parent = &pdev->dev;
touch->idev->open = pm860x_touch_open;
touch->idev->close = pm860x_touch_close;
touch->chip = chip;
touch->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
touch->irq = irq + chip->irq_base;
touch->res_x = pdata->res_x;
input_set_drvdata(touch->idev, touch);
ret = request_threaded_irq(touch->irq, NULL, pm860x_touch_handler,
IRQF_ONESHOT, "touch", touch);
if (ret < 0)
goto out_irq;
__set_bit(EV_ABS, touch->idev->evbit);
__set_bit(ABS_X, touch->idev->absbit);
__set_bit(ABS_Y, touch->idev->absbit);
__set_bit(ABS_PRESSURE, touch->idev->absbit);
__set_bit(EV_SYN, touch->idev->evbit);
__set_bit(EV_KEY, touch->idev->evbit);
__set_bit(BTN_TOUCH, touch->idev->keybit);
input_set_abs_params(touch->idev, ABS_X, 0, 1 << ACCURATE_BIT, 0, 0);
input_set_abs_params(touch->idev, ABS_Y, 0, 1 << ACCURATE_BIT, 0, 0);
input_set_abs_params(touch->idev, ABS_PRESSURE, 0, 1 << ACCURATE_BIT,
0, 0);
ret = input_register_device(touch->idev);
if (ret < 0) {
dev_err(chip->dev, "Failed to register touch!\n");
goto out_rg;
}
platform_set_drvdata(pdev, touch);
return 0;
out_rg:
free_irq(touch->irq, touch);
out_irq:
input_free_device(touch->idev);
out:
kfree(touch);
return ret;
}
static int __devexit pm860x_touch_remove(struct platform_device *pdev)
{
struct pm860x_touch *touch = platform_get_drvdata(pdev);
input_unregister_device(touch->idev);
free_irq(touch->irq, touch);
platform_set_drvdata(pdev, NULL);
kfree(touch);
return 0;
}
static struct platform_driver pm860x_touch_driver = {
.driver = {
.name = "88pm860x-touch",
.owner = THIS_MODULE,
},
.probe = pm860x_touch_probe,
.remove = __devexit_p(pm860x_touch_remove),
};
static int __init pm860x_touch_init(void)
{
return platform_driver_register(&pm860x_touch_driver);
}
module_init(pm860x_touch_init);
static void __exit pm860x_touch_exit(void)
{
platform_driver_unregister(&pm860x_touch_driver);
}
module_exit(pm860x_touch_exit);
MODULE_DESCRIPTION("Touchscreen driver for Marvell Semiconductor 88PM860x");
MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:88pm860x-touch");

View File

@ -11,6 +11,18 @@ menuconfig INPUT_TOUCHSCREEN
if INPUT_TOUCHSCREEN
config TOUCHSCREEN_88PM860X
tristate "Marvell 88PM860x touchscreen"
depends on MFD_88PM860X
help
Say Y here if you have a 88PM860x PMIC and want to enable
support for the built-in touchscreen.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called 88pm860x-ts.
config TOUCHSCREEN_ADS7846
tristate "ADS7846/TSC2046 and ADS7843 based touchscreens"
depends on SPI_MASTER

View File

@ -6,6 +6,7 @@
wm97xx-ts-y := wm97xx-core.o
obj-$(CONFIG_TOUCHSCREEN_88PM860X) += 88pm860x-ts.o
obj-$(CONFIG_TOUCHSCREEN_AD7877) += ad7877.o
obj-$(CONFIG_TOUCHSCREEN_AD7879) += ad7879.o
obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o

View File

@ -17,6 +17,13 @@ config LEDS_CLASS
comment "LED drivers"
config LEDS_88PM860X
tristate "LED Support for Marvell 88PM860x PMIC"
depends on LEDS_CLASS && MFD_88PM860X
help
This option enables support for on-chip LED drivers found on Marvell
Semiconductor 88PM8606 PMIC.
config LEDS_ATMEL_PWM
tristate "LED Support using Atmel PWM outputs"
depends on LEDS_CLASS && ATMEL_PWM

View File

@ -5,6 +5,7 @@ obj-$(CONFIG_LEDS_CLASS) += led-class.o
obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o
# LED Platform Drivers
obj-$(CONFIG_LEDS_88PM860X) += leds-88pm860x.o
obj-$(CONFIG_LEDS_ATMEL_PWM) += leds-atmel-pwm.o
obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o
obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o

View File

@ -0,0 +1,325 @@
/*
* LED driver for Marvell 88PM860x
*
* Copyright (C) 2009 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/leds.h>
#include <linux/workqueue.h>
#include <linux/mfd/88pm860x.h>
#define LED_PWM_SHIFT (3)
#define LED_PWM_MASK (0x1F)
#define LED_CURRENT_MASK (0x07 << 5)
#define LED_BLINK_ON_MASK (0x07)
#define LED_BLINK_PERIOD_MASK (0x0F << 3)
#define LED_BLINK_MASK (0x7F)
#define LED_BLINK_ON(x) ((x & 0x7) * 66 + 66)
#define LED_BLINK_PERIOD(x) ((x & 0xF) * 530 + 930)
#define LED_BLINK_ON_MIN LED_BLINK_ON(0)
#define LED_BLINK_ON_MAX LED_BLINK_ON(0x7)
#define LED_BLINK_PERIOD_MIN LED_BLINK_PERIOD(0)
#define LED_BLINK_PERIOD_MAX LED_BLINK_PERIOD(0xE)
#define LED_TO_ON(x) ((x - 66) / 66)
#define LED_TO_PERIOD(x) ((x - 930) / 530)
#define LED1_BLINK_EN (1 << 1)
#define LED2_BLINK_EN (1 << 2)
enum {
SET_BRIGHTNESS,
SET_BLINK,
};
struct pm860x_led {
struct led_classdev cdev;
struct i2c_client *i2c;
struct work_struct work;
struct pm860x_chip *chip;
struct mutex lock;
char name[MFD_NAME_SIZE];
int port;
int iset;
int command;
int offset;
unsigned char brightness;
unsigned char current_brightness;
int blink_data;
int blink_time;
int blink_on;
int blink_off;
};
/* return offset of color register */
static inline int __led_off(int port)
{
int ret = -EINVAL;
switch (port) {
case PM8606_LED1_RED:
case PM8606_LED1_GREEN:
case PM8606_LED1_BLUE:
ret = port - PM8606_LED1_RED + PM8606_RGB1B;
break;
case PM8606_LED2_RED:
case PM8606_LED2_GREEN:
case PM8606_LED2_BLUE:
ret = port - PM8606_LED2_RED + PM8606_RGB2B;
break;
}
return ret;
}
/* return offset of blink register */
static inline int __blink_off(int port)
{
int ret = -EINVAL;
switch (port) {
case PM8606_LED1_RED:
case PM8606_LED1_GREEN:
case PM8606_LED1_BLUE:
ret = PM8606_RGB1A;
case PM8606_LED2_RED:
case PM8606_LED2_GREEN:
case PM8606_LED2_BLUE:
ret = PM8606_RGB2A;
}
return ret;
}
static inline int __blink_ctl_mask(int port)
{
int ret = -EINVAL;
switch (port) {
case PM8606_LED1_RED:
case PM8606_LED1_GREEN:
case PM8606_LED1_BLUE:
ret = LED1_BLINK_EN;
break;
case PM8606_LED2_RED:
case PM8606_LED2_GREEN:
case PM8606_LED2_BLUE:
ret = LED2_BLINK_EN;
break;
}
return ret;
}
static int __led_set(struct pm860x_led *led, int command)
{
struct pm860x_chip *chip = led->chip;
int mask, ret;
mutex_lock(&led->lock);
switch (command) {
case SET_BRIGHTNESS:
if ((led->current_brightness == 0) && led->brightness) {
if (led->iset) {
ret = pm860x_set_bits(led->i2c, led->offset,
LED_CURRENT_MASK, led->iset);
if (ret < 0)
goto out;
}
} else if (led->brightness == 0) {
ret = pm860x_set_bits(led->i2c, led->offset,
LED_CURRENT_MASK, 0);
if (ret < 0)
goto out;
}
ret = pm860x_set_bits(led->i2c, led->offset, LED_PWM_MASK,
led->brightness);
if (ret < 0)
goto out;
led->current_brightness = led->brightness;
dev_dbg(chip->dev, "Update LED. (reg:%d, brightness:%d)\n",
led->offset, led->brightness);
break;
case SET_BLINK:
ret = pm860x_set_bits(led->i2c, led->offset,
LED_BLINK_MASK, led->blink_data);
if (ret < 0)
goto out;
mask = __blink_ctl_mask(led->port);
ret = pm860x_set_bits(led->i2c, PM8606_WLED3B, mask, mask);
if (ret < 0)
goto out;
dev_dbg(chip->dev, "LED blink delay on:%dms, delay off:%dms\n",
led->blink_on, led->blink_off);
break;
}
out:
mutex_unlock(&led->lock);
return 0;
}
static void pm860x_led_work(struct work_struct *work)
{
struct pm860x_led *led;
led = container_of(work, struct pm860x_led, work);
__led_set(led, led->command);
}
static void pm860x_led_set(struct led_classdev *cdev,
enum led_brightness value)
{
struct pm860x_led *data = container_of(cdev, struct pm860x_led, cdev);
data->offset = __led_off(data->port);
data->brightness = value >> 3;
data->command = SET_BRIGHTNESS;
schedule_work(&data->work);
}
static int pm860x_led_blink(struct led_classdev *cdev,
unsigned long *delay_on,
unsigned long *delay_off)
{
struct pm860x_led *data = container_of(cdev, struct pm860x_led, cdev);
int period, on;
on = *delay_on;
if ((on < LED_BLINK_ON_MIN) || (on > LED_BLINK_ON_MAX))
return -EINVAL;
on = LED_TO_ON(on);
on = LED_BLINK_ON(on);
period = on + *delay_off;
if ((period < LED_BLINK_PERIOD_MIN) || (period > LED_BLINK_PERIOD_MAX))
return -EINVAL;
period = LED_TO_PERIOD(period);
period = LED_BLINK_PERIOD(period);
data->offset = __blink_off(data->port);
data->blink_on = on;
data->blink_off = period - data->blink_on;
data->blink_data = (period << 3) | data->blink_on;
data->command = SET_BLINK;
schedule_work(&data->work);
return 0;
}
static int __check_device(struct pm860x_led_pdata *pdata, char *name)
{
struct pm860x_led_pdata *p = pdata;
int ret = -EINVAL;
while (p && p->id) {
if ((p->id != PM8606_ID_LED) || (p->flags < 0))
break;
if (!strncmp(name, pm860x_led_name[p->flags],
MFD_NAME_SIZE)) {
ret = (int)p->flags;
break;
}
p++;
}
return ret;
}
static int pm860x_led_probe(struct platform_device *pdev)
{
struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct pm860x_platform_data *pm860x_pdata;
struct pm860x_led_pdata *pdata;
struct pm860x_led *data;
struct resource *res;
int ret;
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (res == NULL) {
dev_err(&pdev->dev, "No I/O resource!\n");
return -EINVAL;
}
if (pdev->dev.parent->platform_data) {
pm860x_pdata = pdev->dev.parent->platform_data;
pdata = pm860x_pdata->led;
} else
pdata = NULL;
data = kzalloc(sizeof(struct pm860x_led), GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
strncpy(data->name, res->name, MFD_NAME_SIZE);
dev_set_drvdata(&pdev->dev, data);
data->chip = chip;
data->i2c = (chip->id == CHIP_PM8606) ? chip->client : chip->companion;
data->iset = pdata->iset;
data->port = __check_device(pdata, data->name);
if (data->port < 0)
return -EINVAL;
data->current_brightness = 0;
data->cdev.name = data->name;
data->cdev.brightness_set = pm860x_led_set;
data->cdev.blink_set = pm860x_led_blink;
mutex_init(&data->lock);
INIT_WORK(&data->work, pm860x_led_work);
ret = led_classdev_register(chip->dev, &data->cdev);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register LED: %d\n", ret);
goto out;
}
return 0;
out:
kfree(data);
return ret;
}
static int pm860x_led_remove(struct platform_device *pdev)
{
struct pm860x_led *data = platform_get_drvdata(pdev);
led_classdev_unregister(&data->cdev);
kfree(data);
return 0;
}
static struct platform_driver pm860x_led_driver = {
.driver = {
.name = "88pm860x-led",
.owner = THIS_MODULE,
},
.probe = pm860x_led_probe,
.remove = pm860x_led_remove,
};
static int __devinit pm860x_led_init(void)
{
return platform_driver_register(&pm860x_led_driver);
}
module_init(pm860x_led_init);
static void __devexit pm860x_led_exit(void)
{
platform_driver_unregister(&pm860x_led_driver);
}
module_exit(pm860x_led_exit);
MODULE_DESCRIPTION("LED driver for Marvell PM860x");
MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:88pm860x-led");

View File

@ -1,302 +0,0 @@
/*
* Base driver for Marvell 88PM8607
*
* Copyright (C) 2009 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/mfd/core.h>
#include <linux/mfd/88pm8607.h>
#define PM8607_REG_RESOURCE(_start, _end) \
{ \
.start = PM8607_##_start, \
.end = PM8607_##_end, \
.flags = IORESOURCE_IO, \
}
static struct resource pm8607_regulator_resources[] = {
PM8607_REG_RESOURCE(BUCK1, BUCK1),
PM8607_REG_RESOURCE(BUCK2, BUCK2),
PM8607_REG_RESOURCE(BUCK3, BUCK3),
PM8607_REG_RESOURCE(LDO1, LDO1),
PM8607_REG_RESOURCE(LDO2, LDO2),
PM8607_REG_RESOURCE(LDO3, LDO3),
PM8607_REG_RESOURCE(LDO4, LDO4),
PM8607_REG_RESOURCE(LDO5, LDO5),
PM8607_REG_RESOURCE(LDO6, LDO6),
PM8607_REG_RESOURCE(LDO7, LDO7),
PM8607_REG_RESOURCE(LDO8, LDO8),
PM8607_REG_RESOURCE(LDO9, LDO9),
PM8607_REG_RESOURCE(LDO10, LDO10),
PM8607_REG_RESOURCE(LDO12, LDO12),
PM8607_REG_RESOURCE(LDO14, LDO14),
};
#define PM8607_REG_DEVS(_name, _id) \
{ \
.name = "88pm8607-" #_name, \
.num_resources = 1, \
.resources = &pm8607_regulator_resources[PM8607_ID_##_id], \
}
static struct mfd_cell pm8607_devs[] = {
PM8607_REG_DEVS(buck1, BUCK1),
PM8607_REG_DEVS(buck2, BUCK2),
PM8607_REG_DEVS(buck3, BUCK3),
PM8607_REG_DEVS(ldo1, LDO1),
PM8607_REG_DEVS(ldo2, LDO2),
PM8607_REG_DEVS(ldo3, LDO3),
PM8607_REG_DEVS(ldo4, LDO4),
PM8607_REG_DEVS(ldo5, LDO5),
PM8607_REG_DEVS(ldo6, LDO6),
PM8607_REG_DEVS(ldo7, LDO7),
PM8607_REG_DEVS(ldo8, LDO8),
PM8607_REG_DEVS(ldo9, LDO9),
PM8607_REG_DEVS(ldo10, LDO10),
PM8607_REG_DEVS(ldo12, LDO12),
PM8607_REG_DEVS(ldo14, LDO14),
};
static inline int pm8607_read_device(struct pm8607_chip *chip,
int reg, int bytes, void *dest)
{
struct i2c_client *i2c = chip->client;
unsigned char data;
int ret;
data = (unsigned char)reg;
ret = i2c_master_send(i2c, &data, 1);
if (ret < 0)
return ret;
ret = i2c_master_recv(i2c, dest, bytes);
if (ret < 0)
return ret;
return 0;
}
static inline int pm8607_write_device(struct pm8607_chip *chip,
int reg, int bytes, void *src)
{
struct i2c_client *i2c = chip->client;
unsigned char buf[bytes + 1];
int ret;
buf[0] = (unsigned char)reg;
memcpy(&buf[1], src, bytes);
ret = i2c_master_send(i2c, buf, bytes + 1);
if (ret < 0)
return ret;
return 0;
}
int pm8607_reg_read(struct pm8607_chip *chip, int reg)
{
unsigned char data;
int ret;
mutex_lock(&chip->io_lock);
ret = chip->read(chip, reg, 1, &data);
mutex_unlock(&chip->io_lock);
if (ret < 0)
return ret;
else
return (int)data;
}
EXPORT_SYMBOL(pm8607_reg_read);
int pm8607_reg_write(struct pm8607_chip *chip, int reg,
unsigned char data)
{
int ret;
mutex_lock(&chip->io_lock);
ret = chip->write(chip, reg, 1, &data);
mutex_unlock(&chip->io_lock);
return ret;
}
EXPORT_SYMBOL(pm8607_reg_write);
int pm8607_bulk_read(struct pm8607_chip *chip, int reg,
int count, unsigned char *buf)
{
int ret;
mutex_lock(&chip->io_lock);
ret = chip->read(chip, reg, count, buf);
mutex_unlock(&chip->io_lock);
return ret;
}
EXPORT_SYMBOL(pm8607_bulk_read);
int pm8607_bulk_write(struct pm8607_chip *chip, int reg,
int count, unsigned char *buf)
{
int ret;
mutex_lock(&chip->io_lock);
ret = chip->write(chip, reg, count, buf);
mutex_unlock(&chip->io_lock);
return ret;
}
EXPORT_SYMBOL(pm8607_bulk_write);
int pm8607_set_bits(struct pm8607_chip *chip, int reg,
unsigned char mask, unsigned char data)
{
unsigned char value;
int ret;
mutex_lock(&chip->io_lock);
ret = chip->read(chip, reg, 1, &value);
if (ret < 0)
goto out;
value &= ~mask;
value |= data;
ret = chip->write(chip, reg, 1, &value);
out:
mutex_unlock(&chip->io_lock);
return ret;
}
EXPORT_SYMBOL(pm8607_set_bits);
static const struct i2c_device_id pm8607_id_table[] = {
{ "88PM8607", 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, pm8607_id_table);
static int __devinit pm8607_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct pm8607_platform_data *pdata = client->dev.platform_data;
struct pm8607_chip *chip;
int i, count;
int ret;
chip = kzalloc(sizeof(struct pm8607_chip), GFP_KERNEL);
if (chip == NULL)
return -ENOMEM;
chip->client = client;
chip->dev = &client->dev;
chip->read = pm8607_read_device;
chip->write = pm8607_write_device;
i2c_set_clientdata(client, chip);
mutex_init(&chip->io_lock);
dev_set_drvdata(chip->dev, chip);
ret = pm8607_reg_read(chip, PM8607_CHIP_ID);
if (ret < 0) {
dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
goto out;
}
if ((ret & CHIP_ID_MASK) == CHIP_ID)
dev_info(chip->dev, "Marvell 88PM8607 (ID: %02x) detected\n",
ret);
else {
dev_err(chip->dev, "Failed to detect Marvell 88PM8607. "
"Chip ID: %02x\n", ret);
goto out;
}
chip->chip_id = ret;
ret = pm8607_reg_read(chip, PM8607_BUCK3);
if (ret < 0) {
dev_err(chip->dev, "Failed to read BUCK3 register: %d\n", ret);
goto out;
}
if (ret & PM8607_BUCK3_DOUBLE)
chip->buck3_double = 1;
ret = pm8607_reg_read(chip, PM8607_MISC1);
if (ret < 0) {
dev_err(chip->dev, "Failed to read MISC1 register: %d\n", ret);
goto out;
}
if (pdata->i2c_port == PI2C_PORT)
ret |= PM8607_MISC1_PI2C;
else
ret &= ~PM8607_MISC1_PI2C;
ret = pm8607_reg_write(chip, PM8607_MISC1, ret);
if (ret < 0) {
dev_err(chip->dev, "Failed to write MISC1 register: %d\n", ret);
goto out;
}
count = ARRAY_SIZE(pm8607_devs);
for (i = 0; i < count; i++) {
ret = mfd_add_devices(chip->dev, i, &pm8607_devs[i],
1, NULL, 0);
if (ret != 0) {
dev_err(chip->dev, "Failed to add subdevs\n");
goto out;
}
}
return 0;
out:
i2c_set_clientdata(client, NULL);
kfree(chip);
return ret;
}
static int __devexit pm8607_remove(struct i2c_client *client)
{
struct pm8607_chip *chip = i2c_get_clientdata(client);
mfd_remove_devices(chip->dev);
kfree(chip);
return 0;
}
static struct i2c_driver pm8607_driver = {
.driver = {
.name = "88PM8607",
.owner = THIS_MODULE,
},
.probe = pm8607_probe,
.remove = __devexit_p(pm8607_remove),
.id_table = pm8607_id_table,
};
static int __init pm8607_init(void)
{
int ret;
ret = i2c_add_driver(&pm8607_driver);
if (ret != 0)
pr_err("Failed to register 88PM8607 I2C driver: %d\n", ret);
return ret;
}
subsys_initcall(pm8607_init);
static void __exit pm8607_exit(void)
{
i2c_del_driver(&pm8607_driver);
}
module_exit(pm8607_exit);
MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM8607");
MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
MODULE_LICENSE("GPL");

740
drivers/mfd/88pm860x-core.c Normal file
View File

@ -0,0 +1,740 @@
/*
* Base driver for Marvell 88PM8607
*
* Copyright (C) 2009 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/mfd/core.h>
#include <linux/mfd/88pm860x.h>
#define INT_STATUS_NUM 3
char pm860x_backlight_name[][MFD_NAME_SIZE] = {
"backlight-0",
"backlight-1",
"backlight-2",
};
EXPORT_SYMBOL(pm860x_backlight_name);
char pm860x_led_name[][MFD_NAME_SIZE] = {
"led0-red",
"led0-green",
"led0-blue",
"led1-red",
"led1-green",
"led1-blue",
};
EXPORT_SYMBOL(pm860x_led_name);
#define PM8606_BACKLIGHT_RESOURCE(_i, _x) \
{ \
.name = pm860x_backlight_name[_i], \
.start = PM8606_##_x, \
.end = PM8606_##_x, \
.flags = IORESOURCE_IO, \
}
static struct resource backlight_resources[] = {
PM8606_BACKLIGHT_RESOURCE(PM8606_BACKLIGHT1, WLED1A),
PM8606_BACKLIGHT_RESOURCE(PM8606_BACKLIGHT2, WLED2A),
PM8606_BACKLIGHT_RESOURCE(PM8606_BACKLIGHT3, WLED3A),
};
#define PM8606_BACKLIGHT_DEVS(_i) \
{ \
.name = "88pm860x-backlight", \
.num_resources = 1, \
.resources = &backlight_resources[_i], \
.id = _i, \
}
static struct mfd_cell backlight_devs[] = {
PM8606_BACKLIGHT_DEVS(PM8606_BACKLIGHT1),
PM8606_BACKLIGHT_DEVS(PM8606_BACKLIGHT2),
PM8606_BACKLIGHT_DEVS(PM8606_BACKLIGHT3),
};
#define PM8606_LED_RESOURCE(_i, _x) \
{ \
.name = pm860x_led_name[_i], \
.start = PM8606_##_x, \
.end = PM8606_##_x, \
.flags = IORESOURCE_IO, \
}
static struct resource led_resources[] = {
PM8606_LED_RESOURCE(PM8606_LED1_RED, RGB2B),
PM8606_LED_RESOURCE(PM8606_LED1_GREEN, RGB2C),
PM8606_LED_RESOURCE(PM8606_LED1_BLUE, RGB2D),
PM8606_LED_RESOURCE(PM8606_LED2_RED, RGB1B),
PM8606_LED_RESOURCE(PM8606_LED2_GREEN, RGB1C),
PM8606_LED_RESOURCE(PM8606_LED2_BLUE, RGB1D),
};
#define PM8606_LED_DEVS(_i) \
{ \
.name = "88pm860x-led", \
.num_resources = 1, \
.resources = &led_resources[_i], \
.id = _i, \
}
static struct mfd_cell led_devs[] = {
PM8606_LED_DEVS(PM8606_LED1_RED),
PM8606_LED_DEVS(PM8606_LED1_GREEN),
PM8606_LED_DEVS(PM8606_LED1_BLUE),
PM8606_LED_DEVS(PM8606_LED2_RED),
PM8606_LED_DEVS(PM8606_LED2_GREEN),
PM8606_LED_DEVS(PM8606_LED2_BLUE),
};
static struct resource touch_resources[] = {
{
.start = PM8607_IRQ_PEN,
.end = PM8607_IRQ_PEN,
.flags = IORESOURCE_IRQ,
},
};
static struct mfd_cell touch_devs[] = {
{
.name = "88pm860x-touch",
.num_resources = 1,
.resources = &touch_resources[0],
},
};
#define PM8607_REG_RESOURCE(_start, _end) \
{ \
.start = PM8607_##_start, \
.end = PM8607_##_end, \
.flags = IORESOURCE_IO, \
}
static struct resource power_supply_resources[] = {
{
.name = "88pm860x-power",
.start = PM8607_IRQ_CHG,
.end = PM8607_IRQ_CHG,
.flags = IORESOURCE_IRQ,
},
};
static struct mfd_cell power_devs[] = {
{
.name = "88pm860x-power",
.num_resources = 1,
.resources = &power_supply_resources[0],
.id = -1,
},
};
static struct resource onkey_resources[] = {
{
.name = "88pm860x-onkey",
.start = PM8607_IRQ_ONKEY,
.end = PM8607_IRQ_ONKEY,
.flags = IORESOURCE_IRQ,
},
};
static struct mfd_cell onkey_devs[] = {
{
.name = "88pm860x-onkey",
.num_resources = 1,
.resources = &onkey_resources[0],
.id = -1,
},
};
static struct resource regulator_resources[] = {
PM8607_REG_RESOURCE(BUCK1, BUCK1),
PM8607_REG_RESOURCE(BUCK2, BUCK2),
PM8607_REG_RESOURCE(BUCK3, BUCK3),
PM8607_REG_RESOURCE(LDO1, LDO1),
PM8607_REG_RESOURCE(LDO2, LDO2),
PM8607_REG_RESOURCE(LDO3, LDO3),
PM8607_REG_RESOURCE(LDO4, LDO4),
PM8607_REG_RESOURCE(LDO5, LDO5),
PM8607_REG_RESOURCE(LDO6, LDO6),
PM8607_REG_RESOURCE(LDO7, LDO7),
PM8607_REG_RESOURCE(LDO8, LDO8),
PM8607_REG_RESOURCE(LDO9, LDO9),
PM8607_REG_RESOURCE(LDO10, LDO10),
PM8607_REG_RESOURCE(LDO12, LDO12),
PM8607_REG_RESOURCE(LDO14, LDO14),
};
#define PM8607_REG_DEVS(_name, _id) \
{ \
.name = "88pm8607-" #_name, \
.num_resources = 1, \
.resources = &regulator_resources[PM8607_ID_##_id], \
.id = PM8607_ID_##_id, \
}
static struct mfd_cell regulator_devs[] = {
PM8607_REG_DEVS(buck1, BUCK1),
PM8607_REG_DEVS(buck2, BUCK2),
PM8607_REG_DEVS(buck3, BUCK3),
PM8607_REG_DEVS(ldo1, LDO1),
PM8607_REG_DEVS(ldo2, LDO2),
PM8607_REG_DEVS(ldo3, LDO3),
PM8607_REG_DEVS(ldo4, LDO4),
PM8607_REG_DEVS(ldo5, LDO5),
PM8607_REG_DEVS(ldo6, LDO6),
PM8607_REG_DEVS(ldo7, LDO7),
PM8607_REG_DEVS(ldo8, LDO8),
PM8607_REG_DEVS(ldo9, LDO9),
PM8607_REG_DEVS(ldo10, LDO10),
PM8607_REG_DEVS(ldo12, LDO12),
PM8607_REG_DEVS(ldo14, LDO14),
};
struct pm860x_irq_data {
int reg;
int mask_reg;
int enable; /* enable or not */
int offs; /* bit offset in mask register */
};
static struct pm860x_irq_data pm860x_irqs[] = {
[PM8607_IRQ_ONKEY] = {
.reg = PM8607_INT_STATUS1,
.mask_reg = PM8607_INT_MASK_1,
.offs = 1 << 0,
},
[PM8607_IRQ_EXTON] = {
.reg = PM8607_INT_STATUS1,
.mask_reg = PM8607_INT_MASK_1,
.offs = 1 << 1,
},
[PM8607_IRQ_CHG] = {
.reg = PM8607_INT_STATUS1,
.mask_reg = PM8607_INT_MASK_1,
.offs = 1 << 2,
},
[PM8607_IRQ_BAT] = {
.reg = PM8607_INT_STATUS1,
.mask_reg = PM8607_INT_MASK_1,
.offs = 1 << 3,
},
[PM8607_IRQ_RTC] = {
.reg = PM8607_INT_STATUS1,
.mask_reg = PM8607_INT_MASK_1,
.offs = 1 << 4,
},
[PM8607_IRQ_CC] = {
.reg = PM8607_INT_STATUS1,
.mask_reg = PM8607_INT_MASK_1,
.offs = 1 << 5,
},
[PM8607_IRQ_VBAT] = {
.reg = PM8607_INT_STATUS2,
.mask_reg = PM8607_INT_MASK_2,
.offs = 1 << 0,
},
[PM8607_IRQ_VCHG] = {
.reg = PM8607_INT_STATUS2,
.mask_reg = PM8607_INT_MASK_2,
.offs = 1 << 1,
},
[PM8607_IRQ_VSYS] = {
.reg = PM8607_INT_STATUS2,
.mask_reg = PM8607_INT_MASK_2,
.offs = 1 << 2,
},
[PM8607_IRQ_TINT] = {
.reg = PM8607_INT_STATUS2,
.mask_reg = PM8607_INT_MASK_2,
.offs = 1 << 3,
},
[PM8607_IRQ_GPADC0] = {
.reg = PM8607_INT_STATUS2,
.mask_reg = PM8607_INT_MASK_2,
.offs = 1 << 4,
},
[PM8607_IRQ_GPADC1] = {
.reg = PM8607_INT_STATUS2,
.mask_reg = PM8607_INT_MASK_2,
.offs = 1 << 5,
},
[PM8607_IRQ_GPADC2] = {
.reg = PM8607_INT_STATUS2,
.mask_reg = PM8607_INT_MASK_2,
.offs = 1 << 6,
},
[PM8607_IRQ_GPADC3] = {
.reg = PM8607_INT_STATUS2,
.mask_reg = PM8607_INT_MASK_2,
.offs = 1 << 7,
},
[PM8607_IRQ_AUDIO_SHORT] = {
.reg = PM8607_INT_STATUS3,
.mask_reg = PM8607_INT_MASK_3,
.offs = 1 << 0,
},
[PM8607_IRQ_PEN] = {
.reg = PM8607_INT_STATUS3,
.mask_reg = PM8607_INT_MASK_3,
.offs = 1 << 1,
},
[PM8607_IRQ_HEADSET] = {
.reg = PM8607_INT_STATUS3,
.mask_reg = PM8607_INT_MASK_3,
.offs = 1 << 2,
},
[PM8607_IRQ_HOOK] = {
.reg = PM8607_INT_STATUS3,
.mask_reg = PM8607_INT_MASK_3,
.offs = 1 << 3,
},
[PM8607_IRQ_MICIN] = {
.reg = PM8607_INT_STATUS3,
.mask_reg = PM8607_INT_MASK_3,
.offs = 1 << 4,
},
[PM8607_IRQ_CHG_FAIL] = {
.reg = PM8607_INT_STATUS3,
.mask_reg = PM8607_INT_MASK_3,
.offs = 1 << 5,
},
[PM8607_IRQ_CHG_DONE] = {
.reg = PM8607_INT_STATUS3,
.mask_reg = PM8607_INT_MASK_3,
.offs = 1 << 6,
},
[PM8607_IRQ_CHG_FAULT] = {
.reg = PM8607_INT_STATUS3,
.mask_reg = PM8607_INT_MASK_3,
.offs = 1 << 7,
},
};
static inline struct pm860x_irq_data *irq_to_pm860x(struct pm860x_chip *chip,
int irq)
{
return &pm860x_irqs[irq - chip->irq_base];
}
static irqreturn_t pm860x_irq(int irq, void *data)
{
struct pm860x_chip *chip = data;
struct pm860x_irq_data *irq_data;
struct i2c_client *i2c;
int read_reg = -1, value = 0;
int i;
i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
irq_data = &pm860x_irqs[i];
if (read_reg != irq_data->reg) {
read_reg = irq_data->reg;
value = pm860x_reg_read(i2c, irq_data->reg);
}
if (value & irq_data->enable)
handle_nested_irq(chip->irq_base + i);
}
return IRQ_HANDLED;
}
static void pm860x_irq_lock(unsigned int irq)
{
struct pm860x_chip *chip = get_irq_chip_data(irq);
mutex_lock(&chip->irq_lock);
}
static void pm860x_irq_sync_unlock(unsigned int irq)
{
struct pm860x_chip *chip = get_irq_chip_data(irq);
struct pm860x_irq_data *irq_data;
struct i2c_client *i2c;
static unsigned char cached[3] = {0x0, 0x0, 0x0};
unsigned char mask[3];
int i;
i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
/* Load cached value. In initial, all IRQs are masked */
for (i = 0; i < 3; i++)
mask[i] = cached[i];
for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
irq_data = &pm860x_irqs[i];
switch (irq_data->mask_reg) {
case PM8607_INT_MASK_1:
mask[0] &= ~irq_data->offs;
mask[0] |= irq_data->enable;
break;
case PM8607_INT_MASK_2:
mask[1] &= ~irq_data->offs;
mask[1] |= irq_data->enable;
break;
case PM8607_INT_MASK_3:
mask[2] &= ~irq_data->offs;
mask[2] |= irq_data->enable;
break;
default:
dev_err(chip->dev, "wrong IRQ\n");
break;
}
}
/* update mask into registers */
for (i = 0; i < 3; i++) {
if (mask[i] != cached[i]) {
cached[i] = mask[i];
pm860x_reg_write(i2c, PM8607_INT_MASK_1 + i, mask[i]);
}
}
mutex_unlock(&chip->irq_lock);
}
static void pm860x_irq_enable(unsigned int irq)
{
struct pm860x_chip *chip = get_irq_chip_data(irq);
pm860x_irqs[irq - chip->irq_base].enable
= pm860x_irqs[irq - chip->irq_base].offs;
}
static void pm860x_irq_disable(unsigned int irq)
{
struct pm860x_chip *chip = get_irq_chip_data(irq);
pm860x_irqs[irq - chip->irq_base].enable = 0;
}
static struct irq_chip pm860x_irq_chip = {
.name = "88pm860x",
.bus_lock = pm860x_irq_lock,
.bus_sync_unlock = pm860x_irq_sync_unlock,
.enable = pm860x_irq_enable,
.disable = pm860x_irq_disable,
};
static int __devinit device_gpadc_init(struct pm860x_chip *chip,
struct pm860x_platform_data *pdata)
{
struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
: chip->companion;
int use_gpadc = 0, data, ret;
/* initialize GPADC without activating it */
if (pdata && pdata->touch) {
/* set GPADC MISC1 register */
data = 0;
data |= (pdata->touch->gpadc_prebias << 1)
& PM8607_GPADC_PREBIAS_MASK;
data |= (pdata->touch->slot_cycle << 3)
& PM8607_GPADC_SLOT_CYCLE_MASK;
data |= (pdata->touch->off_scale << 5)
& PM8607_GPADC_OFF_SCALE_MASK;
data |= (pdata->touch->sw_cal << 7)
& PM8607_GPADC_SW_CAL_MASK;
if (data) {
ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data);
if (ret < 0)
goto out;
}
/* set tsi prebias time */
if (pdata->touch->tsi_prebias) {
data = pdata->touch->tsi_prebias;
ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data);
if (ret < 0)
goto out;
}
/* set prebias & prechg time of pen detect */
data = 0;
data |= pdata->touch->pen_prebias & PM8607_PD_PREBIAS_MASK;
data |= (pdata->touch->pen_prechg << 5)
& PM8607_PD_PRECHG_MASK;
if (data) {
ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data);
if (ret < 0)
goto out;
}
use_gpadc = 1;
}
/* turn on GPADC */
if (use_gpadc) {
ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1,
PM8607_GPADC_EN, PM8607_GPADC_EN);
}
out:
return ret;
}
static int __devinit device_irq_init(struct pm860x_chip *chip,
struct pm860x_platform_data *pdata)
{
struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
: chip->companion;
unsigned char status_buf[INT_STATUS_NUM];
unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
struct irq_desc *desc;
int i, data, mask, ret = -EINVAL;
int __irq;
if (!pdata || !pdata->irq_base) {
dev_warn(chip->dev, "No interrupt support on IRQ base\n");
return -EINVAL;
}
mask = PM8607_B0_MISC1_INV_INT | PM8607_B0_MISC1_INT_CLEAR
| PM8607_B0_MISC1_INT_MASK;
data = 0;
chip->irq_mode = 0;
if (pdata && pdata->irq_mode) {
/*
* irq_mode defines the way of clearing interrupt. If it's 1,
* clear IRQ by write. Otherwise, clear it by read.
* This control bit is valid from 88PM8607 B0 steping.
*/
data |= PM8607_B0_MISC1_INT_CLEAR;
chip->irq_mode = 1;
}
ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, mask, data);
if (ret < 0)
goto out;
/* mask all IRQs */
memset(status_buf, 0, INT_STATUS_NUM);
ret = pm860x_bulk_write(i2c, PM8607_INT_MASK_1,
INT_STATUS_NUM, status_buf);
if (ret < 0)
goto out;
if (chip->irq_mode) {
/* clear interrupt status by write */
memset(status_buf, 0xFF, INT_STATUS_NUM);
ret = pm860x_bulk_write(i2c, PM8607_INT_STATUS1,
INT_STATUS_NUM, status_buf);
} else {
/* clear interrupt status by read */
ret = pm860x_bulk_read(i2c, PM8607_INT_STATUS1,
INT_STATUS_NUM, status_buf);
}
if (ret < 0)
goto out;
mutex_init(&chip->irq_lock);
chip->irq_base = pdata->irq_base;
chip->core_irq = i2c->irq;
if (!chip->core_irq)
goto out;
desc = irq_to_desc(chip->core_irq);
/* register IRQ by genirq */
for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) {
__irq = i + chip->irq_base;
set_irq_chip_data(__irq, chip);
set_irq_chip_and_handler(__irq, &pm860x_irq_chip,
handle_edge_irq);
set_irq_nested_thread(__irq, 1);
#ifdef CONFIG_ARM
set_irq_flags(__irq, IRQF_VALID);
#else
set_irq_noprobe(__irq);
#endif
}
ret = request_threaded_irq(chip->core_irq, NULL, pm860x_irq, flags,
"88pm860x", chip);
if (ret) {
dev_err(chip->dev, "Failed to request IRQ: %d\n", ret);
chip->core_irq = 0;
}
return 0;
out:
chip->core_irq = 0;
return ret;
}
static void __devexit device_irq_exit(struct pm860x_chip *chip)
{
if (chip->core_irq)
free_irq(chip->core_irq, chip);
}
static void __devinit device_8606_init(struct pm860x_chip *chip,
struct i2c_client *i2c,
struct pm860x_platform_data *pdata)
{
int ret;
if (pdata && pdata->backlight) {
ret = mfd_add_devices(chip->dev, 0, &backlight_devs[0],
ARRAY_SIZE(backlight_devs),
&backlight_resources[0], 0);
if (ret < 0) {
dev_err(chip->dev, "Failed to add backlight "
"subdev\n");
goto out_dev;
}
}
if (pdata && pdata->led) {
ret = mfd_add_devices(chip->dev, 0, &led_devs[0],
ARRAY_SIZE(led_devs),
&led_resources[0], 0);
if (ret < 0) {
dev_err(chip->dev, "Failed to add led "
"subdev\n");
goto out_dev;
}
}
return;
out_dev:
mfd_remove_devices(chip->dev);
device_irq_exit(chip);
}
static void __devinit device_8607_init(struct pm860x_chip *chip,
struct i2c_client *i2c,
struct pm860x_platform_data *pdata)
{
int data, ret;
ret = pm860x_reg_read(i2c, PM8607_CHIP_ID);
if (ret < 0) {
dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
goto out;
}
if ((ret & PM8607_VERSION_MASK) == PM8607_VERSION)
dev_info(chip->dev, "Marvell 88PM8607 (ID: %02x) detected\n",
ret);
else {
dev_err(chip->dev, "Failed to detect Marvell 88PM8607. "
"Chip ID: %02x\n", ret);
goto out;
}
ret = pm860x_reg_read(i2c, PM8607_BUCK3);
if (ret < 0) {
dev_err(chip->dev, "Failed to read BUCK3 register: %d\n", ret);
goto out;
}
if (ret & PM8607_BUCK3_DOUBLE)
chip->buck3_double = 1;
ret = pm860x_reg_read(i2c, PM8607_B0_MISC1);
if (ret < 0) {
dev_err(chip->dev, "Failed to read MISC1 register: %d\n", ret);
goto out;
}
if (pdata && (pdata->i2c_port == PI2C_PORT))
data = PM8607_B0_MISC1_PI2C;
else
data = 0;
ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, PM8607_B0_MISC1_PI2C, data);
if (ret < 0) {
dev_err(chip->dev, "Failed to access MISC1:%d\n", ret);
goto out;
}
ret = device_gpadc_init(chip, pdata);
if (ret < 0)
goto out;
ret = device_irq_init(chip, pdata);
if (ret < 0)
goto out;
ret = mfd_add_devices(chip->dev, 0, &regulator_devs[0],
ARRAY_SIZE(regulator_devs),
&regulator_resources[0], 0);
if (ret < 0) {
dev_err(chip->dev, "Failed to add regulator subdev\n");
goto out_dev;
}
if (pdata && pdata->touch) {
ret = mfd_add_devices(chip->dev, 0, &touch_devs[0],
ARRAY_SIZE(touch_devs),
&touch_resources[0], 0);
if (ret < 0) {
dev_err(chip->dev, "Failed to add touch "
"subdev\n");
goto out_dev;
}
}
if (pdata && pdata->power) {
ret = mfd_add_devices(chip->dev, 0, &power_devs[0],
ARRAY_SIZE(power_devs),
&power_supply_resources[0], 0);
if (ret < 0) {
dev_err(chip->dev, "Failed to add power supply "
"subdev\n");
goto out_dev;
}
}
ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
ARRAY_SIZE(onkey_devs),
&onkey_resources[0], 0);
if (ret < 0) {
dev_err(chip->dev, "Failed to add onkey subdev\n");
goto out_dev;
}
return;
out_dev:
mfd_remove_devices(chip->dev);
device_irq_exit(chip);
out:
return;
}
int pm860x_device_init(struct pm860x_chip *chip,
struct pm860x_platform_data *pdata)
{
chip->core_irq = 0;
switch (chip->id) {
case CHIP_PM8606:
device_8606_init(chip, chip->client, pdata);
break;
case CHIP_PM8607:
device_8607_init(chip, chip->client, pdata);
break;
}
if (chip->companion) {
switch (chip->id) {
case CHIP_PM8607:
device_8606_init(chip, chip->companion, pdata);
break;
case CHIP_PM8606:
device_8607_init(chip, chip->companion, pdata);
break;
}
}
return 0;
}
void pm860x_device_exit(struct pm860x_chip *chip)
{
device_irq_exit(chip);
mfd_remove_devices(chip->dev);
}
MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM860x");
MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
MODULE_LICENSE("GPL");

236
drivers/mfd/88pm860x-i2c.c Normal file
View File

@ -0,0 +1,236 @@
/*
* I2C driver for Marvell 88PM860x
*
* Copyright (C) 2009 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/mfd/88pm860x.h>
static inline int pm860x_read_device(struct i2c_client *i2c,
int reg, int bytes, void *dest)
{
unsigned char data;
int ret;
data = (unsigned char)reg;
ret = i2c_master_send(i2c, &data, 1);
if (ret < 0)
return ret;
ret = i2c_master_recv(i2c, dest, bytes);
if (ret < 0)
return ret;
return 0;
}
static inline int pm860x_write_device(struct i2c_client *i2c,
int reg, int bytes, void *src)
{
unsigned char buf[bytes + 1];
int ret;
buf[0] = (unsigned char)reg;
memcpy(&buf[1], src, bytes);
ret = i2c_master_send(i2c, buf, bytes + 1);
if (ret < 0)
return ret;
return 0;
}
int pm860x_reg_read(struct i2c_client *i2c, int reg)
{
struct pm860x_chip *chip = i2c_get_clientdata(i2c);
unsigned char data;
int ret;
mutex_lock(&chip->io_lock);
ret = pm860x_read_device(i2c, reg, 1, &data);
mutex_unlock(&chip->io_lock);
if (ret < 0)
return ret;
else
return (int)data;
}
EXPORT_SYMBOL(pm860x_reg_read);
int pm860x_reg_write(struct i2c_client *i2c, int reg,
unsigned char data)
{
struct pm860x_chip *chip = i2c_get_clientdata(i2c);
int ret;
mutex_lock(&chip->io_lock);
ret = pm860x_write_device(i2c, reg, 1, &data);
mutex_unlock(&chip->io_lock);
return ret;
}
EXPORT_SYMBOL(pm860x_reg_write);
int pm860x_bulk_read(struct i2c_client *i2c, int reg,
int count, unsigned char *buf)
{
struct pm860x_chip *chip = i2c_get_clientdata(i2c);
int ret;
mutex_lock(&chip->io_lock);
ret = pm860x_read_device(i2c, reg, count, buf);
mutex_unlock(&chip->io_lock);
return ret;
}
EXPORT_SYMBOL(pm860x_bulk_read);
int pm860x_bulk_write(struct i2c_client *i2c, int reg,
int count, unsigned char *buf)
{
struct pm860x_chip *chip = i2c_get_clientdata(i2c);
int ret;
mutex_lock(&chip->io_lock);
ret = pm860x_write_device(i2c, reg, count, buf);
mutex_unlock(&chip->io_lock);
return ret;
}
EXPORT_SYMBOL(pm860x_bulk_write);
int pm860x_set_bits(struct i2c_client *i2c, int reg,
unsigned char mask, unsigned char data)
{
struct pm860x_chip *chip = i2c_get_clientdata(i2c);
unsigned char value;
int ret;
mutex_lock(&chip->io_lock);
ret = pm860x_read_device(i2c, reg, 1, &value);
if (ret < 0)
goto out;
value &= ~mask;
value |= data;
ret = pm860x_write_device(i2c, reg, 1, &value);
out:
mutex_unlock(&chip->io_lock);
return ret;
}
EXPORT_SYMBOL(pm860x_set_bits);
static const struct i2c_device_id pm860x_id_table[] = {
{ "88PM860x", 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, pm860x_id_table);
static int verify_addr(struct i2c_client *i2c)
{
unsigned short addr_8607[] = {0x30, 0x34};
unsigned short addr_8606[] = {0x10, 0x11};
int size, i;
if (i2c == NULL)
return 0;
size = ARRAY_SIZE(addr_8606);
for (i = 0; i < size; i++) {
if (i2c->addr == *(addr_8606 + i))
return CHIP_PM8606;
}
size = ARRAY_SIZE(addr_8607);
for (i = 0; i < size; i++) {
if (i2c->addr == *(addr_8607 + i))
return CHIP_PM8607;
}
return 0;
}
static int __devinit pm860x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct pm860x_platform_data *pdata = client->dev.platform_data;
struct pm860x_chip *chip;
if (!pdata) {
pr_info("No platform data in %s!\n", __func__);
return -EINVAL;
}
chip = kzalloc(sizeof(struct pm860x_chip), GFP_KERNEL);
if (chip == NULL)
return -ENOMEM;
chip->id = verify_addr(client);
chip->client = client;
i2c_set_clientdata(client, chip);
chip->dev = &client->dev;
mutex_init(&chip->io_lock);
dev_set_drvdata(chip->dev, chip);
/*
* Both client and companion client shares same platform driver.
* Driver distinguishes them by pdata->companion_addr.
* pdata->companion_addr is only assigned if companion chip exists.
* At the same time, the companion_addr shouldn't equal to client
* address.
*/
if (pdata->companion_addr && (pdata->companion_addr != client->addr)) {
chip->companion_addr = pdata->companion_addr;
chip->companion = i2c_new_dummy(chip->client->adapter,
chip->companion_addr);
i2c_set_clientdata(chip->companion, chip);
}
pm860x_device_init(chip, pdata);
return 0;
}
static int __devexit pm860x_remove(struct i2c_client *client)
{
struct pm860x_chip *chip = i2c_get_clientdata(client);
pm860x_device_exit(chip);
i2c_unregister_device(chip->companion);
i2c_set_clientdata(chip->companion, NULL);
i2c_set_clientdata(chip->client, NULL);
kfree(chip);
return 0;
}
static struct i2c_driver pm860x_driver = {
.driver = {
.name = "88PM860x",
.owner = THIS_MODULE,
},
.probe = pm860x_probe,
.remove = __devexit_p(pm860x_remove),
.id_table = pm860x_id_table,
};
static int __init pm860x_i2c_init(void)
{
int ret;
ret = i2c_add_driver(&pm860x_driver);
if (ret != 0)
pr_err("Failed to register 88PM860x I2C driver: %d\n", ret);
return ret;
}
subsys_initcall(pm860x_i2c_init);
static void __exit pm860x_i2c_exit(void)
{
i2c_del_driver(&pm860x_driver);
}
module_exit(pm860x_i2c_exit);
MODULE_DESCRIPTION("I2C Driver for Marvell 88PM860x");
MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
MODULE_LICENSE("GPL");

View File

@ -9,6 +9,16 @@ config MFD_CORE
tristate
default n
config MFD_88PM860X
bool "Support Marvell 88PM8606/88PM8607"
depends on I2C=y
select MFD_CORE
help
This supports for Marvell 88PM8606/88PM8607 Power Management IC.
This includes the I2C driver and the core APIs _only_, you have to
select individual components like voltage regulators, RTC and
battery-charger under the corresponding menus.
config MFD_SM501
tristate "Support for Silicon Motion SM501"
---help---
@ -37,7 +47,7 @@ config MFD_ASIC3
config MFD_SH_MOBILE_SDHI
bool "Support for SuperH Mobile SDHI"
depends on SUPERH
depends on SUPERH || ARCH_SHMOBILE
select MFD_CORE
---help---
This driver supports the SDHI hardware block found in many
@ -68,6 +78,15 @@ config HTC_PASIC3
HTC Magician devices, respectively. Actual functionality is
handled by the leds-pasic3 and ds1wm drivers.
config HTC_I2CPLD
bool "HTC I2C PLD chip support"
depends on I2C=y && GPIOLIB
help
If you say yes here you get support for the supposed CPLD
found on omap850 HTC devices like the HTC Wizard and HTC Herald.
This device provides input and output GPIOs through an I2C
interface to one or more sub-chips.
config UCB1400_CORE
tristate "Philips UCB1400 Core driver"
depends on AC97_BUS
@ -184,6 +203,16 @@ config PMIC_ADP5520
individual components like LCD backlight, LEDs, GPIOs and Kepad
under the corresponding menus.
config MFD_MAX8925
bool "Maxim Semiconductor MAX8925 PMIC Support"
depends on I2C=y
select MFD_CORE
help
Say yes here to support for Maxim Semiconductor MAX8925. This is
a Power Management IC. This driver provies common support for
accessing the device, additional drivers must be enabled in order
to use the functionality of the device.
config MFD_WM8400
tristate "Support Wolfson Microelectronics WM8400"
select MFD_CORE
@ -205,7 +234,7 @@ config MFD_WM831X
functionality of the device.
config MFD_WM8350
tristate
bool
config MFD_WM8350_CONFIG_MODE_0
bool
@ -256,9 +285,9 @@ config MFD_WM8352_CONFIG_MODE_3
depends on MFD_WM8350
config MFD_WM8350_I2C
tristate "Support Wolfson Microelectronics WM8350 with I2C"
bool "Support Wolfson Microelectronics WM8350 with I2C"
select MFD_WM8350
depends on I2C
depends on I2C=y
help
The WM8350 is an integrated audio and power management
subsystem with watchdog and RTC functionality for embedded
@ -266,6 +295,18 @@ config MFD_WM8350_I2C
I2C as the control interface. Additional options must be
selected to enable support for the functionality of the chip.
config MFD_WM8994
tristate "Support Wolfson Microelectronics WM8994"
select MFD_CORE
depends on I2C
help
The WM8994 is a highly integrated hi-fi CODEC designed for
smartphone applicatiosn. As well as audio functionality it
has on board GPIO and regulator functionality which is
supported via the relevant subsystems. This driver provides
core support for the WM8994, in order to use the actual
functionaltiy of the device other drivers must be enabled.
config MFD_PCF50633
tristate "Support for NXP PCF50633"
depends on I2C
@ -300,8 +341,8 @@ config PCF50633_GPIO
the PCF50633 chip.
config AB3100_CORE
tristate "ST-Ericsson AB3100 Mixed Signal Circuit core functions"
depends on I2C
bool "ST-Ericsson AB3100 Mixed Signal Circuit core functions"
depends on I2C=y
default y if ARCH_U300
help
Select this to enable the AB3100 Mixed Signal IC core
@ -329,16 +370,6 @@ config EZX_PCAP
This enables the PCAP ASIC present on EZX Phones. This is
needed for MMC, TouchScreen, Sound, USB, etc..
config MFD_88PM8607
bool "Support Marvell 88PM8607"
depends on I2C=y
select MFD_CORE
help
This supports for Marvell 88PM8607 Power Management IC. This includes
the I2C driver and the core APIs _only_, you have to select
individual components like voltage regulators, RTC and
battery-charger under the corresponding menus.
config AB4500_CORE
tristate "ST-Ericsson's AB4500 Mixed Signal Power management chip"
depends on SPI
@ -358,6 +389,15 @@ config MFD_TIMBERDALE
The timberdale FPGA can be found on the Intel Atom development board
for in-vehicle infontainment, called Russellville.
config LPC_SCH
tristate "Intel SCH LPC"
depends on PCI
select MFD_CORE
help
LPC bridge function of the Intel SCH provides support for
System Management Bus and General Purpose I/O.
endmenu
menu "Multimedia Capabilities Port drivers"

View File

@ -2,12 +2,15 @@
# Makefile for multifunction miscellaneous devices
#
88pm860x-objs := 88pm860x-core.o 88pm860x-i2c.o
obj-$(CONFIG_MFD_88PM860X) += 88pm860x.o
obj-$(CONFIG_MFD_SM501) += sm501.o
obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o
obj-$(CONFIG_MFD_SH_MOBILE_SDHI) += sh_mobile_sdhi.o
obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o
obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o
obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o
obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o
@ -22,6 +25,7 @@ wm8350-objs := wm8350-core.o wm8350-regmap.o wm8350-gpio.o
wm8350-objs += wm8350-irq.o
obj-$(CONFIG_MFD_WM8350) += wm8350.o
obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o
obj-$(CONFIG_MFD_WM8994) += wm8994-core.o
obj-$(CONFIG_TPS65010) += tps65010.o
obj-$(CONFIG_MENELAUS) += menelaus.o
@ -47,6 +51,8 @@ endif
obj-$(CONFIG_UCB1400_CORE) += ucb1400_core.o
obj-$(CONFIG_PMIC_DA903X) += da903x.o
max8925-objs := max8925-core.o max8925-i2c.o
obj-$(CONFIG_MFD_MAX8925) += max8925.o
obj-$(CONFIG_MFD_PCF50633) += pcf50633-core.o
obj-$(CONFIG_PCF50633_ADC) += pcf50633-adc.o
@ -55,5 +61,5 @@ obj-$(CONFIG_AB3100_CORE) += ab3100-core.o
obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o
obj-$(CONFIG_AB4500_CORE) += ab4500-core.o
obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o
obj-$(CONFIG_MFD_88PM8607) += 88pm8607.o
obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
obj-$(CONFIG_LPC_SCH) += lpc_sch.o

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2007-2009 ST-Ericsson
* Copyright (C) 2007-2010 ST-Ericsson
* License terms: GNU General Public License (GPL) version 2
* Low-level core for exclusive access to the AB3100 IC on the I2C bus
* and some basic chip-configuration.
@ -14,6 +14,7 @@
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/random.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
@ -365,18 +366,23 @@ int ab3100_event_registers_startup_state_get(struct ab3100 *ab3100,
}
EXPORT_SYMBOL(ab3100_event_registers_startup_state_get);
/* Interrupt handling worker */
static void ab3100_work(struct work_struct *work)
/*
* This is a threaded interrupt handler so we can make some
* I2C calls etc.
*/
static irqreturn_t ab3100_irq_handler(int irq, void *data)
{
struct ab3100 *ab3100 = container_of(work, struct ab3100, work);
struct ab3100 *ab3100 = data;
u8 event_regs[3];
u32 fatevent;
int err;
add_interrupt_randomness(irq);
err = ab3100_get_register_page_interruptible(ab3100, AB3100_EVENTA1,
event_regs, 3);
if (err)
goto err_event_wq;
goto err_event;
fatevent = (event_regs[0] << 16) |
(event_regs[1] << 8) |
@ -398,29 +404,11 @@ static void ab3100_work(struct work_struct *work)
dev_dbg(ab3100->dev,
"IRQ Event: 0x%08x\n", fatevent);
/* By now the IRQ should be acked and deasserted so enable it again */
enable_irq(ab3100->i2c_client->irq);
return;
return IRQ_HANDLED;
err_event_wq:
err_event:
dev_dbg(ab3100->dev,
"error in event workqueue\n");
/* Enable the IRQ anyway, what choice do we have? */
enable_irq(ab3100->i2c_client->irq);
return;
}
static irqreturn_t ab3100_irq_handler(int irq, void *data)
{
struct ab3100 *ab3100 = data;
/*
* Disable the IRQ and dispatch a worker to handle the
* event. Since the chip resides on I2C this is slow
* stuff and we will re-enable the interrupts once th
* worker has finished.
*/
disable_irq_nosync(irq);
schedule_work(&ab3100->work);
"error reading event status\n");
return IRQ_HANDLED;
}
@ -735,10 +723,7 @@ static struct platform_device ab3100_##devname##_device = { \
.id = -1, \
}
/*
* This lists all the subdevices and corresponding register
* ranges.
*/
/* This lists all the subdevices */
AB3100_DEVICE(dac, "ab3100-dac");
AB3100_DEVICE(leds, "ab3100-leds");
AB3100_DEVICE(power, "ab3100-power");
@ -904,12 +889,11 @@ static int __init ab3100_probe(struct i2c_client *client,
if (err)
goto exit_no_setup;
INIT_WORK(&ab3100->work, ab3100_work);
err = request_threaded_irq(client->irq, NULL, ab3100_irq_handler,
IRQF_ONESHOT, "ab3100-core", ab3100);
/* This real unpredictable IRQ is of course sampled for entropy */
err = request_irq(client->irq, ab3100_irq_handler,
IRQF_DISABLED | IRQF_SAMPLE_RANDOM,
"AB3100 IRQ", ab3100);
rand_initialize_irq(client->irq);
if (err)
goto exit_no_irq;

View File

@ -13,6 +13,7 @@
#include <linux/platform_device.h>
#include <linux/mfd/ab3100.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
/* The OTP registers */
#define AB3100_OTP0 0xb0
@ -95,11 +96,10 @@ static int __init ab3100_otp_read(struct ab3100_otp *otp)
* This is a simple debugfs human-readable file that dumps out
* the contents of the OTP.
*/
#ifdef CONFIG_DEBUGFS
static int show_otp(struct seq_file *s, void *v)
#ifdef CONFIG_DEBUG_FS
static int ab3100_show_otp(struct seq_file *s, void *v)
{
struct ab3100_otp *otp = s->private;
int err;
seq_printf(s, "OTP is %s\n", otp->locked ? "LOCKED" : "UNLOCKED");
seq_printf(s, "OTP clock switch startup is %uHz\n", otp->freq);
@ -113,7 +113,7 @@ static int show_otp(struct seq_file *s, void *v)
static int ab3100_otp_open(struct inode *inode, struct file *file)
{
return single_open(file, ab3100_otp_show, inode->i_private);
return single_open(file, ab3100_show_otp, inode->i_private);
}
static const struct file_operations ab3100_otp_operations = {
@ -131,13 +131,14 @@ static int __init ab3100_otp_init_debugfs(struct device *dev,
&ab3100_otp_operations);
if (!otp->debugfs) {
dev_err(dev, "AB3100 debugfs OTP file registration failed!\n");
return err;
return -ENOENT;
}
return 0;
}
static void __exit ab3100_otp_exit_debugfs(struct ab3100_otp *otp)
{
debugfs_remove_file(otp->debugfs);
debugfs_remove(otp->debugfs);
}
#else
/* Compile this out if debugfs not selected */

710
drivers/mfd/htc-i2cpld.c Normal file
View File

@ -0,0 +1,710 @@
/*
* htc-i2cpld.c
* Chip driver for an unknown CPLD chip found on omap850 HTC devices like
* the HTC Wizard and HTC Herald.
* The cpld is located on the i2c bus and acts as an input/output GPIO
* extender.
*
* Copyright (C) 2009 Cory Maccarrone <darkstar6262@gmail.com>
*
* Based on work done in the linwizard project
* Copyright (C) 2008-2009 Angelo Arrifano <miknix@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/irq.h>
#include <linux/spinlock.h>
#include <linux/htcpld.h>
#include <linux/gpio.h>
struct htcpld_chip {
spinlock_t lock;
/* chip info */
u8 reset;
u8 addr;
struct device *dev;
struct i2c_client *client;
/* Output details */
u8 cache_out;
struct gpio_chip chip_out;
/* Input details */
u8 cache_in;
struct gpio_chip chip_in;
u16 irqs_enabled;
uint irq_start;
int nirqs;
/*
* Work structure to allow for setting values outside of any
* possible interrupt context
*/
struct work_struct set_val_work;
};
struct htcpld_data {
/* irq info */
u16 irqs_enabled;
uint irq_start;
int nirqs;
uint chained_irq;
unsigned int int_reset_gpio_hi;
unsigned int int_reset_gpio_lo;
/* htcpld info */
struct htcpld_chip *chip;
unsigned int nchips;
};
/* There does not appear to be a way to proactively mask interrupts
* on the htcpld chip itself. So, we simply ignore interrupts that
* aren't desired. */
static void htcpld_mask(unsigned int irq)
{
struct htcpld_chip *chip = get_irq_chip_data(irq);
chip->irqs_enabled &= ~(1 << (irq - chip->irq_start));
pr_debug("HTCPLD mask %d %04x\n", irq, chip->irqs_enabled);
}
static void htcpld_unmask(unsigned int irq)
{
struct htcpld_chip *chip = get_irq_chip_data(irq);
chip->irqs_enabled |= 1 << (irq - chip->irq_start);
pr_debug("HTCPLD unmask %d %04x\n", irq, chip->irqs_enabled);
}
static int htcpld_set_type(unsigned int irq, unsigned int flags)
{
struct irq_desc *d = irq_to_desc(irq);
if (!d) {
pr_err("HTCPLD invalid IRQ: %d\n", irq);
return -EINVAL;
}
if (flags & ~IRQ_TYPE_SENSE_MASK)
return -EINVAL;
/* We only allow edge triggering */
if (flags & (IRQ_TYPE_LEVEL_LOW|IRQ_TYPE_LEVEL_HIGH))
return -EINVAL;
d->status &= ~IRQ_TYPE_SENSE_MASK;
d->status |= flags;
return 0;
}
static struct irq_chip htcpld_muxed_chip = {
.name = "htcpld",
.mask = htcpld_mask,
.unmask = htcpld_unmask,
.set_type = htcpld_set_type,
};
/* To properly dispatch IRQ events, we need to read from the
* chip. This is an I2C action that could possibly sleep
* (which is bad in interrupt context) -- so we use a threaded
* interrupt handler to get around that.
*/
static irqreturn_t htcpld_handler(int irq, void *dev)
{
struct htcpld_data *htcpld = dev;
unsigned int i;
unsigned long flags;
int irqpin;
struct irq_desc *desc;
if (!htcpld) {
pr_debug("htcpld is null in ISR\n");
return IRQ_HANDLED;
}
/*
* For each chip, do a read of the chip and trigger any interrupts
* desired. The interrupts will be triggered from LSB to MSB (i.e.
* bit 0 first, then bit 1, etc.)
*
* For chips that have no interrupt range specified, just skip 'em.
*/
for (i = 0; i < htcpld->nchips; i++) {
struct htcpld_chip *chip = &htcpld->chip[i];
struct i2c_client *client;
int val;
unsigned long uval, old_val;
if (!chip) {
pr_debug("chip %d is null in ISR\n", i);
continue;
}
if (chip->nirqs == 0)
continue;
client = chip->client;
if (!client) {
pr_debug("client %d is null in ISR\n", i);
continue;
}
/* Scan the chip */
val = i2c_smbus_read_byte_data(client, chip->cache_out);
if (val < 0) {
/* Throw a warning and skip this chip */
dev_warn(chip->dev, "Unable to read from chip: %d\n",
val);
continue;
}
uval = (unsigned long)val;
spin_lock_irqsave(&chip->lock, flags);
/* Save away the old value so we can compare it */
old_val = chip->cache_in;
/* Write the new value */
chip->cache_in = uval;
spin_unlock_irqrestore(&chip->lock, flags);
/*
* For each bit in the data (starting at bit 0), trigger
* associated interrupts.
*/
for (irqpin = 0; irqpin < chip->nirqs; irqpin++) {
unsigned oldb, newb;
int flags;
irq = chip->irq_start + irqpin;
desc = irq_to_desc(irq);
flags = desc->status;
/* Run the IRQ handler, but only if the bit value
* changed, and the proper flags are set */
oldb = (old_val >> irqpin) & 1;
newb = (uval >> irqpin) & 1;
if ((!oldb && newb && (flags & IRQ_TYPE_EDGE_RISING)) ||
(oldb && !newb &&
(flags & IRQ_TYPE_EDGE_FALLING))) {
pr_debug("fire IRQ %d\n", irqpin);
desc->handle_irq(irq, desc);
}
}
}
/*
* In order to continue receiving interrupts, the int_reset_gpio must
* be asserted.
*/
if (htcpld->int_reset_gpio_hi)
gpio_set_value(htcpld->int_reset_gpio_hi, 1);
if (htcpld->int_reset_gpio_lo)
gpio_set_value(htcpld->int_reset_gpio_lo, 0);
return IRQ_HANDLED;
}
/*
* The GPIO set routines can be called from interrupt context, especially if,
* for example they're attached to the led-gpio framework and a trigger is
* enabled. As such, we declared work above in the htcpld_chip structure,
* and that work is scheduled in the set routine. The kernel can then run
* the I2C functions, which will sleep, in process context.
*/
void htcpld_chip_set(struct gpio_chip *chip, unsigned offset, int val)
{
struct i2c_client *client;
struct htcpld_chip *chip_data;
unsigned long flags;
chip_data = container_of(chip, struct htcpld_chip, chip_out);
if (!chip_data)
return;
client = chip_data->client;
if (client == NULL)
return;
spin_lock_irqsave(&chip_data->lock, flags);
if (val)
chip_data->cache_out |= (1 << offset);
else
chip_data->cache_out &= ~(1 << offset);
spin_unlock_irqrestore(&chip_data->lock, flags);
schedule_work(&(chip_data->set_val_work));
}
void htcpld_chip_set_ni(struct work_struct *work)
{
struct htcpld_chip *chip_data;
struct i2c_client *client;
chip_data = container_of(work, struct htcpld_chip, set_val_work);
client = chip_data->client;
i2c_smbus_read_byte_data(client, chip_data->cache_out);
}
int htcpld_chip_get(struct gpio_chip *chip, unsigned offset)
{
struct htcpld_chip *chip_data;
int val = 0;
int is_input = 0;
/* Try out first */
chip_data = container_of(chip, struct htcpld_chip, chip_out);
if (!chip_data) {
/* Try in */
is_input = 1;
chip_data = container_of(chip, struct htcpld_chip, chip_in);
if (!chip_data)
return -EINVAL;
}
/* Determine if this is an input or output GPIO */
if (!is_input)
/* Use the output cache */
val = (chip_data->cache_out >> offset) & 1;
else
/* Use the input cache */
val = (chip_data->cache_in >> offset) & 1;
if (val)
return 1;
else
return 0;
}
static int htcpld_direction_output(struct gpio_chip *chip,
unsigned offset, int value)
{
htcpld_chip_set(chip, offset, value);
return 0;
}
static int htcpld_direction_input(struct gpio_chip *chip,
unsigned offset)
{
/*
* No-op: this function can only be called on the input chip.
* We do however make sure the offset is within range.
*/
return (offset < chip->ngpio) ? 0 : -EINVAL;
}
int htcpld_chip_to_irq(struct gpio_chip *chip, unsigned offset)
{
struct htcpld_chip *chip_data;
chip_data = container_of(chip, struct htcpld_chip, chip_in);
if (offset < chip_data->nirqs)
return chip_data->irq_start + offset;
else
return -EINVAL;
}
void htcpld_chip_reset(struct i2c_client *client)
{
struct htcpld_chip *chip_data = i2c_get_clientdata(client);
if (!chip_data)
return;
i2c_smbus_read_byte_data(
client, (chip_data->cache_out = chip_data->reset));
}
static int __devinit htcpld_setup_chip_irq(
struct platform_device *pdev,
int chip_index)
{
struct htcpld_data *htcpld;
struct device *dev = &pdev->dev;
struct htcpld_core_platform_data *pdata;
struct htcpld_chip *chip;
struct htcpld_chip_platform_data *plat_chip_data;
unsigned int irq, irq_end;
int ret = 0;
/* Get the platform and driver data */
pdata = dev->platform_data;
htcpld = platform_get_drvdata(pdev);
chip = &htcpld->chip[chip_index];
plat_chip_data = &pdata->chip[chip_index];
/* Setup irq handlers */
irq_end = chip->irq_start + chip->nirqs;
for (irq = chip->irq_start; irq < irq_end; irq++) {
set_irq_chip(irq, &htcpld_muxed_chip);
set_irq_chip_data(irq, chip);
set_irq_handler(irq, handle_simple_irq);
#ifdef CONFIG_ARM
set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
#else
set_irq_probe(irq);
#endif
}
return ret;
}
static int __devinit htcpld_register_chip_i2c(
struct platform_device *pdev,
int chip_index)
{
struct htcpld_data *htcpld;
struct device *dev = &pdev->dev;
struct htcpld_core_platform_data *pdata;
struct htcpld_chip *chip;
struct htcpld_chip_platform_data *plat_chip_data;
struct i2c_adapter *adapter;
struct i2c_client *client;
struct i2c_board_info info;
/* Get the platform and driver data */
pdata = dev->platform_data;
htcpld = platform_get_drvdata(pdev);
chip = &htcpld->chip[chip_index];
plat_chip_data = &pdata->chip[chip_index];
adapter = i2c_get_adapter(pdata->i2c_adapter_id);
if (adapter == NULL) {
/* Eek, no such I2C adapter! Bail out. */
dev_warn(dev, "Chip at i2c address 0x%x: Invalid i2c adapter %d\n",
plat_chip_data->addr, pdata->i2c_adapter_id);
return -ENODEV;
}
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
dev_warn(dev, "i2c adapter %d non-functional\n",
pdata->i2c_adapter_id);
return -EINVAL;
}
memset(&info, 0, sizeof(struct i2c_board_info));
info.addr = plat_chip_data->addr;
strlcpy(info.type, "htcpld-chip", I2C_NAME_SIZE);
info.platform_data = chip;
/* Add the I2C device. This calls the probe() function. */
client = i2c_new_device(adapter, &info);
if (!client) {
/* I2C device registration failed, contineu with the next */
dev_warn(dev, "Unable to add I2C device for 0x%x\n",
plat_chip_data->addr);
return -ENODEV;
}
i2c_set_clientdata(client, chip);
snprintf(client->name, I2C_NAME_SIZE, "Chip_0x%d", client->addr);
chip->client = client;
/* Reset the chip */
htcpld_chip_reset(client);
chip->cache_in = i2c_smbus_read_byte_data(client, chip->cache_out);
return 0;
}
static void __devinit htcpld_unregister_chip_i2c(
struct platform_device *pdev,
int chip_index)
{
struct htcpld_data *htcpld;
struct htcpld_chip *chip;
/* Get the platform and driver data */
htcpld = platform_get_drvdata(pdev);
chip = &htcpld->chip[chip_index];
if (chip->client)
i2c_unregister_device(chip->client);
}
static int __devinit htcpld_register_chip_gpio(
struct platform_device *pdev,
int chip_index)
{
struct htcpld_data *htcpld;
struct device *dev = &pdev->dev;
struct htcpld_core_platform_data *pdata;
struct htcpld_chip *chip;
struct htcpld_chip_platform_data *plat_chip_data;
struct gpio_chip *gpio_chip;
int ret = 0;
/* Get the platform and driver data */
pdata = dev->platform_data;
htcpld = platform_get_drvdata(pdev);
chip = &htcpld->chip[chip_index];
plat_chip_data = &pdata->chip[chip_index];
/* Setup the GPIO chips */
gpio_chip = &(chip->chip_out);
gpio_chip->label = "htcpld-out";
gpio_chip->dev = dev;
gpio_chip->owner = THIS_MODULE;
gpio_chip->get = htcpld_chip_get;
gpio_chip->set = htcpld_chip_set;
gpio_chip->direction_input = NULL;
gpio_chip->direction_output = htcpld_direction_output;
gpio_chip->base = plat_chip_data->gpio_out_base;
gpio_chip->ngpio = plat_chip_data->num_gpios;
gpio_chip = &(chip->chip_in);
gpio_chip->label = "htcpld-in";
gpio_chip->dev = dev;
gpio_chip->owner = THIS_MODULE;
gpio_chip->get = htcpld_chip_get;
gpio_chip->set = NULL;
gpio_chip->direction_input = htcpld_direction_input;
gpio_chip->direction_output = NULL;
gpio_chip->to_irq = htcpld_chip_to_irq;
gpio_chip->base = plat_chip_data->gpio_in_base;
gpio_chip->ngpio = plat_chip_data->num_gpios;
/* Add the GPIO chips */
ret = gpiochip_add(&(chip->chip_out));
if (ret) {
dev_warn(dev, "Unable to register output GPIOs for 0x%x: %d\n",
plat_chip_data->addr, ret);
return ret;
}
ret = gpiochip_add(&(chip->chip_in));
if (ret) {
int error;
dev_warn(dev, "Unable to register input GPIOs for 0x%x: %d\n",
plat_chip_data->addr, ret);
error = gpiochip_remove(&(chip->chip_out));
if (error)
dev_warn(dev, "Error while trying to unregister gpio chip: %d\n", error);
return ret;
}
return 0;
}
static int __devinit htcpld_setup_chips(struct platform_device *pdev)
{
struct htcpld_data *htcpld;
struct device *dev = &pdev->dev;
struct htcpld_core_platform_data *pdata;
int i;
/* Get the platform and driver data */
pdata = dev->platform_data;
htcpld = platform_get_drvdata(pdev);
/* Setup each chip's output GPIOs */
htcpld->nchips = pdata->num_chip;
htcpld->chip = kzalloc(sizeof(struct htcpld_chip) * htcpld->nchips,
GFP_KERNEL);
if (!htcpld->chip) {
dev_warn(dev, "Unable to allocate memory for chips\n");
return -ENOMEM;
}
/* Add the chips as best we can */
for (i = 0; i < htcpld->nchips; i++) {
int ret;
/* Setup the HTCPLD chips */
htcpld->chip[i].reset = pdata->chip[i].reset;
htcpld->chip[i].cache_out = pdata->chip[i].reset;
htcpld->chip[i].cache_in = 0;
htcpld->chip[i].dev = dev;
htcpld->chip[i].irq_start = pdata->chip[i].irq_base;
htcpld->chip[i].nirqs = pdata->chip[i].num_irqs;
INIT_WORK(&(htcpld->chip[i].set_val_work), &htcpld_chip_set_ni);
spin_lock_init(&(htcpld->chip[i].lock));
/* Setup the interrupts for the chip */
if (htcpld->chained_irq) {
ret = htcpld_setup_chip_irq(pdev, i);
if (ret)
continue;
}
/* Register the chip with I2C */
ret = htcpld_register_chip_i2c(pdev, i);
if (ret)
continue;
/* Register the chips with the GPIO subsystem */
ret = htcpld_register_chip_gpio(pdev, i);
if (ret) {
/* Unregister the chip from i2c and continue */
htcpld_unregister_chip_i2c(pdev, i);
continue;
}
dev_info(dev, "Registered chip at 0x%x\n", pdata->chip[i].addr);
}
return 0;
}
static int __devinit htcpld_core_probe(struct platform_device *pdev)
{
struct htcpld_data *htcpld;
struct device *dev = &pdev->dev;
struct htcpld_core_platform_data *pdata;
struct resource *res;
int ret = 0;
if (!dev)
return -ENODEV;
pdata = dev->platform_data;
if (!pdata) {
dev_warn(dev, "Platform data not found for htcpld core!\n");
return -ENXIO;
}
htcpld = kzalloc(sizeof(struct htcpld_data), GFP_KERNEL);
if (!htcpld)
return -ENOMEM;
/* Find chained irq */
ret = -EINVAL;
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res) {
int flags;
htcpld->chained_irq = res->start;
/* Setup the chained interrupt handler */
flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;
ret = request_threaded_irq(htcpld->chained_irq,
NULL, htcpld_handler,
flags, pdev->name, htcpld);
if (ret) {
dev_warn(dev, "Unable to setup chained irq handler: %d\n", ret);
goto fail;
} else
device_init_wakeup(dev, 0);
}
/* Set the driver data */
platform_set_drvdata(pdev, htcpld);
/* Setup the htcpld chips */
ret = htcpld_setup_chips(pdev);
if (ret)
goto fail;
/* Request the GPIO(s) for the int reset and set them up */
if (pdata->int_reset_gpio_hi) {
ret = gpio_request(pdata->int_reset_gpio_hi, "htcpld-core");
if (ret) {
/*
* If it failed, that sucks, but we can probably
* continue on without it.
*/
dev_warn(dev, "Unable to request int_reset_gpio_hi -- interrupts may not work\n");
htcpld->int_reset_gpio_hi = 0;
} else {
htcpld->int_reset_gpio_hi = pdata->int_reset_gpio_hi;
gpio_set_value(htcpld->int_reset_gpio_hi, 1);
}
}
if (pdata->int_reset_gpio_lo) {
ret = gpio_request(pdata->int_reset_gpio_lo, "htcpld-core");
if (ret) {
/*
* If it failed, that sucks, but we can probably
* continue on without it.
*/
dev_warn(dev, "Unable to request int_reset_gpio_lo -- interrupts may not work\n");
htcpld->int_reset_gpio_lo = 0;
} else {
htcpld->int_reset_gpio_lo = pdata->int_reset_gpio_lo;
gpio_set_value(htcpld->int_reset_gpio_lo, 0);
}
}
dev_info(dev, "Initialized successfully\n");
return 0;
fail:
kfree(htcpld);
return ret;
}
/* The I2C Driver -- used internally */
static const struct i2c_device_id htcpld_chip_id[] = {
{ "htcpld-chip", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, htcpld_chip_id);
static struct i2c_driver htcpld_chip_driver = {
.driver = {
.name = "htcpld-chip",
},
.id_table = htcpld_chip_id,
};
/* The Core Driver */
static struct platform_driver htcpld_core_driver = {
.driver = {
.name = "i2c-htcpld",
},
};
static int __init htcpld_core_init(void)
{
int ret;
/* Register the I2C Chip driver */
ret = i2c_add_driver(&htcpld_chip_driver);
if (ret)
return ret;
/* Probe for our chips */
return platform_driver_probe(&htcpld_core_driver, htcpld_core_probe);
}
static void __exit htcpld_core_exit(void)
{
i2c_del_driver(&htcpld_chip_driver);
platform_driver_unregister(&htcpld_core_driver);
}
module_init(htcpld_core_init);
module_exit(htcpld_core_exit);
MODULE_AUTHOR("Cory Maccarrone <darkstar6262@gmail.com>");
MODULE_DESCRIPTION("I2C HTC PLD Driver");
MODULE_LICENSE("GPL");

133
drivers/mfd/lpc_sch.c Normal file
View File

@ -0,0 +1,133 @@
/*
* lpc_sch.c - LPC interface for Intel Poulsbo SCH
*
* LPC bridge function of the Intel SCH contains many other
* functional units, such as Interrupt controllers, Timers,
* Power Management, System Management, GPIO, RTC, and LPC
* Configuration Registers.
*
* Copyright (c) 2010 CompuLab Ltd
* Author: Denis Turischev <denis@compulab.co.il>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License 2 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/acpi.h>
#include <linux/pci.h>
#include <linux/mfd/core.h>
#define SMBASE 0x40
#define SMBUS_IO_SIZE 64
#define GPIOBASE 0x44
#define GPIO_IO_SIZE 64
static struct resource smbus_sch_resource = {
.flags = IORESOURCE_IO,
};
static struct resource gpio_sch_resource = {
.flags = IORESOURCE_IO,
};
static struct mfd_cell lpc_sch_cells[] = {
{
.name = "isch_smbus",
.num_resources = 1,
.resources = &smbus_sch_resource,
},
{
.name = "sch_gpio",
.num_resources = 1,
.resources = &gpio_sch_resource,
},
};
static struct pci_device_id lpc_sch_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, lpc_sch_ids);
static int __devinit lpc_sch_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
unsigned int base_addr_cfg;
unsigned short base_addr;
pci_read_config_dword(dev, SMBASE, &base_addr_cfg);
if (!(base_addr_cfg & (1 << 31))) {
dev_err(&dev->dev, "Decode of the SMBus I/O range disabled\n");
return -ENODEV;
}
base_addr = (unsigned short)base_addr_cfg;
if (base_addr == 0) {
dev_err(&dev->dev, "I/O space for SMBus uninitialized\n");
return -ENODEV;
}
smbus_sch_resource.start = base_addr;
smbus_sch_resource.end = base_addr + SMBUS_IO_SIZE - 1;
pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg);
if (!(base_addr_cfg & (1 << 31))) {
dev_err(&dev->dev, "Decode of the GPIO I/O range disabled\n");
return -ENODEV;
}
base_addr = (unsigned short)base_addr_cfg;
if (base_addr == 0) {
dev_err(&dev->dev, "I/O space for GPIO uninitialized\n");
return -ENODEV;
}
gpio_sch_resource.start = base_addr;
gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1;
return mfd_add_devices(&dev->dev, -1,
lpc_sch_cells, ARRAY_SIZE(lpc_sch_cells), NULL, 0);
}
static void __devexit lpc_sch_remove(struct pci_dev *dev)
{
mfd_remove_devices(&dev->dev);
}
static struct pci_driver lpc_sch_driver = {
.name = "lpc_sch",
.id_table = lpc_sch_ids,
.probe = lpc_sch_probe,
.remove = __devexit_p(lpc_sch_remove),
};
static int __init lpc_sch_init(void)
{
return pci_register_driver(&lpc_sch_driver);
}
static void __exit lpc_sch_exit(void)
{
pci_unregister_driver(&lpc_sch_driver);
}
module_init(lpc_sch_init);
module_exit(lpc_sch_exit);
MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
MODULE_DESCRIPTION("LPC interface for Intel Poulsbo SCH");
MODULE_LICENSE("GPL");

656
drivers/mfd/max8925-core.c Normal file
View File

@ -0,0 +1,656 @@
/*
* Base driver for Maxim MAX8925
*
* Copyright (C) 2009-2010 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/mfd/core.h>
#include <linux/mfd/max8925.h>
static struct resource backlight_resources[] = {
{
.name = "max8925-backlight",
.start = MAX8925_WLED_MODE_CNTL,
.end = MAX8925_WLED_CNTL,
.flags = IORESOURCE_IO,
},
};
static struct mfd_cell backlight_devs[] = {
{
.name = "max8925-backlight",
.num_resources = 1,
.resources = &backlight_resources[0],
.id = -1,
},
};
static struct resource touch_resources[] = {
{
.name = "max8925-tsc",
.start = MAX8925_TSC_IRQ,
.end = MAX8925_ADC_RES_END,
.flags = IORESOURCE_IO,
},
};
static struct mfd_cell touch_devs[] = {
{
.name = "max8925-touch",
.num_resources = 1,
.resources = &touch_resources[0],
.id = -1,
},
};
static struct resource power_supply_resources[] = {
{
.name = "max8925-power",
.start = MAX8925_CHG_IRQ1,
.end = MAX8925_CHG_IRQ1_MASK,
.flags = IORESOURCE_IO,
},
};
static struct mfd_cell power_devs[] = {
{
.name = "max8925-power",
.num_resources = 1,
.resources = &power_supply_resources[0],
.id = -1,
},
};
static struct resource rtc_resources[] = {
{
.name = "max8925-rtc",
.start = MAX8925_RTC_IRQ,
.end = MAX8925_RTC_IRQ_MASK,
.flags = IORESOURCE_IO,
},
};
static struct mfd_cell rtc_devs[] = {
{
.name = "max8925-rtc",
.num_resources = 1,
.resources = &rtc_resources[0],
.id = -1,
},
};
#define MAX8925_REG_RESOURCE(_start, _end) \
{ \
.start = MAX8925_##_start, \
.end = MAX8925_##_end, \
.flags = IORESOURCE_IO, \
}
static struct resource regulator_resources[] = {
MAX8925_REG_RESOURCE(SDCTL1, SDCTL1),
MAX8925_REG_RESOURCE(SDCTL2, SDCTL2),
MAX8925_REG_RESOURCE(SDCTL3, SDCTL3),
MAX8925_REG_RESOURCE(LDOCTL1, LDOCTL1),
MAX8925_REG_RESOURCE(LDOCTL2, LDOCTL2),
MAX8925_REG_RESOURCE(LDOCTL3, LDOCTL3),
MAX8925_REG_RESOURCE(LDOCTL4, LDOCTL4),
MAX8925_REG_RESOURCE(LDOCTL5, LDOCTL5),
MAX8925_REG_RESOURCE(LDOCTL6, LDOCTL6),
MAX8925_REG_RESOURCE(LDOCTL7, LDOCTL7),
MAX8925_REG_RESOURCE(LDOCTL8, LDOCTL8),
MAX8925_REG_RESOURCE(LDOCTL9, LDOCTL9),
MAX8925_REG_RESOURCE(LDOCTL10, LDOCTL10),
MAX8925_REG_RESOURCE(LDOCTL11, LDOCTL11),
MAX8925_REG_RESOURCE(LDOCTL12, LDOCTL12),
MAX8925_REG_RESOURCE(LDOCTL13, LDOCTL13),
MAX8925_REG_RESOURCE(LDOCTL14, LDOCTL14),
MAX8925_REG_RESOURCE(LDOCTL15, LDOCTL15),
MAX8925_REG_RESOURCE(LDOCTL16, LDOCTL16),
MAX8925_REG_RESOURCE(LDOCTL17, LDOCTL17),
MAX8925_REG_RESOURCE(LDOCTL18, LDOCTL18),
MAX8925_REG_RESOURCE(LDOCTL19, LDOCTL19),
MAX8925_REG_RESOURCE(LDOCTL20, LDOCTL20),
};
#define MAX8925_REG_DEVS(_id) \
{ \
.name = "max8925-regulator", \
.num_resources = 1, \
.resources = &regulator_resources[MAX8925_ID_##_id], \
.id = MAX8925_ID_##_id, \
}
static struct mfd_cell regulator_devs[] = {
MAX8925_REG_DEVS(SD1),
MAX8925_REG_DEVS(SD2),
MAX8925_REG_DEVS(SD3),
MAX8925_REG_DEVS(LDO1),
MAX8925_REG_DEVS(LDO2),
MAX8925_REG_DEVS(LDO3),
MAX8925_REG_DEVS(LDO4),
MAX8925_REG_DEVS(LDO5),
MAX8925_REG_DEVS(LDO6),
MAX8925_REG_DEVS(LDO7),
MAX8925_REG_DEVS(LDO8),
MAX8925_REG_DEVS(LDO9),
MAX8925_REG_DEVS(LDO10),
MAX8925_REG_DEVS(LDO11),
MAX8925_REG_DEVS(LDO12),
MAX8925_REG_DEVS(LDO13),
MAX8925_REG_DEVS(LDO14),
MAX8925_REG_DEVS(LDO15),
MAX8925_REG_DEVS(LDO16),
MAX8925_REG_DEVS(LDO17),
MAX8925_REG_DEVS(LDO18),
MAX8925_REG_DEVS(LDO19),
MAX8925_REG_DEVS(LDO20),
};
enum {
FLAGS_ADC = 1, /* register in ADC component */
FLAGS_RTC, /* register in RTC component */
};
struct max8925_irq_data {
int reg;
int mask_reg;
int enable; /* enable or not */
int offs; /* bit offset in mask register */
int flags;
int tsc_irq;
};
static struct max8925_irq_data max8925_irqs[] = {
[MAX8925_IRQ_VCHG_DC_OVP] = {
.reg = MAX8925_CHG_IRQ1,
.mask_reg = MAX8925_CHG_IRQ1_MASK,
.offs = 1 << 0,
},
[MAX8925_IRQ_VCHG_DC_F] = {
.reg = MAX8925_CHG_IRQ1,
.mask_reg = MAX8925_CHG_IRQ1_MASK,
.offs = 1 << 1,
},
[MAX8925_IRQ_VCHG_DC_R] = {
.reg = MAX8925_CHG_IRQ1,
.mask_reg = MAX8925_CHG_IRQ1_MASK,
.offs = 1 << 2,
},
[MAX8925_IRQ_VCHG_USB_OVP] = {
.reg = MAX8925_CHG_IRQ1,
.mask_reg = MAX8925_CHG_IRQ1_MASK,
.offs = 1 << 3,
},
[MAX8925_IRQ_VCHG_USB_F] = {
.reg = MAX8925_CHG_IRQ1,
.mask_reg = MAX8925_CHG_IRQ1_MASK,
.offs = 1 << 4,
},
[MAX8925_IRQ_VCHG_USB_R] = {
.reg = MAX8925_CHG_IRQ1,
.mask_reg = MAX8925_CHG_IRQ1_MASK,
.offs = 1 << 5,
},
[MAX8925_IRQ_VCHG_THM_OK_R] = {
.reg = MAX8925_CHG_IRQ2,
.mask_reg = MAX8925_CHG_IRQ2_MASK,
.offs = 1 << 0,
},
[MAX8925_IRQ_VCHG_THM_OK_F] = {
.reg = MAX8925_CHG_IRQ2,
.mask_reg = MAX8925_CHG_IRQ2_MASK,
.offs = 1 << 1,
},
[MAX8925_IRQ_VCHG_SYSLOW_F] = {
.reg = MAX8925_CHG_IRQ2,
.mask_reg = MAX8925_CHG_IRQ2_MASK,
.offs = 1 << 2,
},
[MAX8925_IRQ_VCHG_SYSLOW_R] = {
.reg = MAX8925_CHG_IRQ2,
.mask_reg = MAX8925_CHG_IRQ2_MASK,
.offs = 1 << 3,
},
[MAX8925_IRQ_VCHG_RST] = {
.reg = MAX8925_CHG_IRQ2,
.mask_reg = MAX8925_CHG_IRQ2_MASK,
.offs = 1 << 4,
},
[MAX8925_IRQ_VCHG_DONE] = {
.reg = MAX8925_CHG_IRQ2,
.mask_reg = MAX8925_CHG_IRQ2_MASK,
.offs = 1 << 5,
},
[MAX8925_IRQ_VCHG_TOPOFF] = {
.reg = MAX8925_CHG_IRQ2,
.mask_reg = MAX8925_CHG_IRQ2_MASK,
.offs = 1 << 6,
},
[MAX8925_IRQ_VCHG_TMR_FAULT] = {
.reg = MAX8925_CHG_IRQ2,
.mask_reg = MAX8925_CHG_IRQ2_MASK,
.offs = 1 << 7,
},
[MAX8925_IRQ_GPM_RSTIN] = {
.reg = MAX8925_ON_OFF_IRQ1,
.mask_reg = MAX8925_ON_OFF_IRQ1_MASK,
.offs = 1 << 0,
},
[MAX8925_IRQ_GPM_MPL] = {
.reg = MAX8925_ON_OFF_IRQ1,
.mask_reg = MAX8925_ON_OFF_IRQ1_MASK,
.offs = 1 << 1,
},
[MAX8925_IRQ_GPM_SW_3SEC] = {
.reg = MAX8925_ON_OFF_IRQ1,
.mask_reg = MAX8925_ON_OFF_IRQ1_MASK,
.offs = 1 << 2,
},
[MAX8925_IRQ_GPM_EXTON_F] = {
.reg = MAX8925_ON_OFF_IRQ1,
.mask_reg = MAX8925_ON_OFF_IRQ1_MASK,
.offs = 1 << 3,
},
[MAX8925_IRQ_GPM_EXTON_R] = {
.reg = MAX8925_ON_OFF_IRQ1,
.mask_reg = MAX8925_ON_OFF_IRQ1_MASK,
.offs = 1 << 4,
},
[MAX8925_IRQ_GPM_SW_1SEC] = {
.reg = MAX8925_ON_OFF_IRQ1,
.mask_reg = MAX8925_ON_OFF_IRQ1_MASK,
.offs = 1 << 5,
},
[MAX8925_IRQ_GPM_SW_F] = {
.reg = MAX8925_ON_OFF_IRQ1,
.mask_reg = MAX8925_ON_OFF_IRQ1_MASK,
.offs = 1 << 6,
},
[MAX8925_IRQ_GPM_SW_R] = {
.reg = MAX8925_ON_OFF_IRQ1,
.mask_reg = MAX8925_ON_OFF_IRQ1_MASK,
.offs = 1 << 7,
},
[MAX8925_IRQ_GPM_SYSCKEN_F] = {
.reg = MAX8925_ON_OFF_IRQ2,
.mask_reg = MAX8925_ON_OFF_IRQ2_MASK,
.offs = 1 << 0,
},
[MAX8925_IRQ_GPM_SYSCKEN_R] = {
.reg = MAX8925_ON_OFF_IRQ2,
.mask_reg = MAX8925_ON_OFF_IRQ2_MASK,
.offs = 1 << 1,
},
[MAX8925_IRQ_RTC_ALARM1] = {
.reg = MAX8925_RTC_IRQ,
.mask_reg = MAX8925_RTC_IRQ_MASK,
.offs = 1 << 2,
.flags = FLAGS_RTC,
},
[MAX8925_IRQ_RTC_ALARM0] = {
.reg = MAX8925_RTC_IRQ,
.mask_reg = MAX8925_RTC_IRQ_MASK,
.offs = 1 << 3,
.flags = FLAGS_RTC,
},
[MAX8925_IRQ_TSC_STICK] = {
.reg = MAX8925_TSC_IRQ,
.mask_reg = MAX8925_TSC_IRQ_MASK,
.offs = 1 << 0,
.flags = FLAGS_ADC,
.tsc_irq = 1,
},
[MAX8925_IRQ_TSC_NSTICK] = {
.reg = MAX8925_TSC_IRQ,
.mask_reg = MAX8925_TSC_IRQ_MASK,
.offs = 1 << 1,
.flags = FLAGS_ADC,
.tsc_irq = 1,
},
};
static inline struct max8925_irq_data *irq_to_max8925(struct max8925_chip *chip,
int irq)
{
return &max8925_irqs[irq - chip->irq_base];
}
static irqreturn_t max8925_irq(int irq, void *data)
{
struct max8925_chip *chip = data;
struct max8925_irq_data *irq_data;
struct i2c_client *i2c;
int read_reg = -1, value = 0;
int i;
for (i = 0; i < ARRAY_SIZE(max8925_irqs); i++) {
irq_data = &max8925_irqs[i];
/* TSC IRQ should be serviced in max8925_tsc_irq() */
if (irq_data->tsc_irq)
continue;
if (irq_data->flags == FLAGS_RTC)
i2c = chip->rtc;
else if (irq_data->flags == FLAGS_ADC)
i2c = chip->adc;
else
i2c = chip->i2c;
if (read_reg != irq_data->reg) {
read_reg = irq_data->reg;
value = max8925_reg_read(i2c, irq_data->reg);
}
if (value & irq_data->enable)
handle_nested_irq(chip->irq_base + i);
}
return IRQ_HANDLED;
}
static irqreturn_t max8925_tsc_irq(int irq, void *data)
{
struct max8925_chip *chip = data;
struct max8925_irq_data *irq_data;
struct i2c_client *i2c;
int read_reg = -1, value = 0;
int i;
for (i = 0; i < ARRAY_SIZE(max8925_irqs); i++) {
irq_data = &max8925_irqs[i];
/* non TSC IRQ should be serviced in max8925_irq() */
if (!irq_data->tsc_irq)
continue;
if (irq_data->flags == FLAGS_RTC)
i2c = chip->rtc;
else if (irq_data->flags == FLAGS_ADC)
i2c = chip->adc;
else
i2c = chip->i2c;
if (read_reg != irq_data->reg) {
read_reg = irq_data->reg;
value = max8925_reg_read(i2c, irq_data->reg);
}
if (value & irq_data->enable)
handle_nested_irq(chip->irq_base + i);
}
return IRQ_HANDLED;
}
static void max8925_irq_lock(unsigned int irq)
{
struct max8925_chip *chip = get_irq_chip_data(irq);
mutex_lock(&chip->irq_lock);
}
static void max8925_irq_sync_unlock(unsigned int irq)
{
struct max8925_chip *chip = get_irq_chip_data(irq);
struct max8925_irq_data *irq_data;
static unsigned char cache_chg[2] = {0xff, 0xff};
static unsigned char cache_on[2] = {0xff, 0xff};
static unsigned char cache_rtc = 0xff, cache_tsc = 0xff;
unsigned char irq_chg[2], irq_on[2];
unsigned char irq_rtc, irq_tsc;
int i;
/* Load cached value. In initial, all IRQs are masked */
irq_chg[0] = cache_chg[0];
irq_chg[1] = cache_chg[1];
irq_on[0] = cache_on[0];
irq_on[1] = cache_on[1];
irq_rtc = cache_rtc;
irq_tsc = cache_tsc;
for (i = 0; i < ARRAY_SIZE(max8925_irqs); i++) {
irq_data = &max8925_irqs[i];
switch (irq_data->mask_reg) {
case MAX8925_CHG_IRQ1_MASK:
irq_chg[0] &= irq_data->enable;
break;
case MAX8925_CHG_IRQ2_MASK:
irq_chg[1] &= irq_data->enable;
break;
case MAX8925_ON_OFF_IRQ1_MASK:
irq_on[0] &= irq_data->enable;
break;
case MAX8925_ON_OFF_IRQ2_MASK:
irq_on[1] &= irq_data->enable;
break;
case MAX8925_RTC_IRQ_MASK:
irq_rtc &= irq_data->enable;
break;
case MAX8925_TSC_IRQ_MASK:
irq_tsc &= irq_data->enable;
break;
default:
dev_err(chip->dev, "wrong IRQ\n");
break;
}
}
/* update mask into registers */
if (cache_chg[0] != irq_chg[0]) {
cache_chg[0] = irq_chg[0];
max8925_reg_write(chip->i2c, MAX8925_CHG_IRQ1_MASK,
irq_chg[0]);
}
if (cache_chg[1] != irq_chg[1]) {
cache_chg[1] = irq_chg[1];
max8925_reg_write(chip->i2c, MAX8925_CHG_IRQ2_MASK,
irq_chg[1]);
}
if (cache_on[0] != irq_on[0]) {
cache_on[0] = irq_on[0];
max8925_reg_write(chip->i2c, MAX8925_ON_OFF_IRQ1_MASK,
irq_on[0]);
}
if (cache_on[1] != irq_on[1]) {
cache_on[1] = irq_on[1];
max8925_reg_write(chip->i2c, MAX8925_ON_OFF_IRQ2_MASK,
irq_on[1]);
}
if (cache_rtc != irq_rtc) {
cache_rtc = irq_rtc;
max8925_reg_write(chip->rtc, MAX8925_RTC_IRQ_MASK, irq_rtc);
}
if (cache_tsc != irq_tsc) {
cache_tsc = irq_tsc;
max8925_reg_write(chip->adc, MAX8925_TSC_IRQ_MASK, irq_tsc);
}
mutex_unlock(&chip->irq_lock);
}
static void max8925_irq_enable(unsigned int irq)
{
struct max8925_chip *chip = get_irq_chip_data(irq);
max8925_irqs[irq - chip->irq_base].enable
= max8925_irqs[irq - chip->irq_base].offs;
}
static void max8925_irq_disable(unsigned int irq)
{
struct max8925_chip *chip = get_irq_chip_data(irq);
max8925_irqs[irq - chip->irq_base].enable = 0;
}
static struct irq_chip max8925_irq_chip = {
.name = "max8925",
.bus_lock = max8925_irq_lock,
.bus_sync_unlock = max8925_irq_sync_unlock,
.enable = max8925_irq_enable,
.disable = max8925_irq_disable,
};
static int max8925_irq_init(struct max8925_chip *chip, int irq,
struct max8925_platform_data *pdata)
{
unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
struct irq_desc *desc;
int i, ret;
int __irq;
if (!pdata || !pdata->irq_base) {
dev_warn(chip->dev, "No interrupt support on IRQ base\n");
return -EINVAL;
}
/* clear all interrupts */
max8925_reg_read(chip->i2c, MAX8925_CHG_IRQ1);
max8925_reg_read(chip->i2c, MAX8925_CHG_IRQ2);
max8925_reg_read(chip->i2c, MAX8925_ON_OFF_IRQ1);
max8925_reg_read(chip->i2c, MAX8925_ON_OFF_IRQ2);
max8925_reg_read(chip->rtc, MAX8925_RTC_IRQ);
max8925_reg_read(chip->adc, MAX8925_TSC_IRQ);
/* mask all interrupts */
max8925_reg_write(chip->rtc, MAX8925_ALARM0_CNTL, 0);
max8925_reg_write(chip->rtc, MAX8925_ALARM1_CNTL, 0);
max8925_reg_write(chip->i2c, MAX8925_CHG_IRQ1_MASK, 0xff);
max8925_reg_write(chip->i2c, MAX8925_CHG_IRQ2_MASK, 0xff);
max8925_reg_write(chip->i2c, MAX8925_ON_OFF_IRQ1_MASK, 0xff);
max8925_reg_write(chip->i2c, MAX8925_ON_OFF_IRQ2_MASK, 0xff);
max8925_reg_write(chip->rtc, MAX8925_RTC_IRQ_MASK, 0xff);
max8925_reg_write(chip->adc, MAX8925_TSC_IRQ_MASK, 0xff);
mutex_init(&chip->irq_lock);
chip->core_irq = irq;
chip->irq_base = pdata->irq_base;
desc = irq_to_desc(chip->core_irq);
/* register with genirq */
for (i = 0; i < ARRAY_SIZE(max8925_irqs); i++) {
__irq = i + chip->irq_base;
set_irq_chip_data(__irq, chip);
set_irq_chip_and_handler(__irq, &max8925_irq_chip,
handle_edge_irq);
set_irq_nested_thread(__irq, 1);
#ifdef CONFIG_ARM
set_irq_flags(__irq, IRQF_VALID);
#else
set_irq_noprobe(__irq);
#endif
}
if (!irq) {
dev_warn(chip->dev, "No interrupt support on core IRQ\n");
goto tsc_irq;
}
ret = request_threaded_irq(irq, NULL, max8925_irq, flags,
"max8925", chip);
if (ret) {
dev_err(chip->dev, "Failed to request core IRQ: %d\n", ret);
chip->core_irq = 0;
}
tsc_irq:
if (!pdata->tsc_irq) {
dev_warn(chip->dev, "No interrupt support on TSC IRQ\n");
return 0;
}
chip->tsc_irq = pdata->tsc_irq;
ret = request_threaded_irq(chip->tsc_irq, NULL, max8925_tsc_irq,
flags, "max8925-tsc", chip);
if (ret) {
dev_err(chip->dev, "Failed to request TSC IRQ: %d\n", ret);
chip->tsc_irq = 0;
}
return 0;
}
int __devinit max8925_device_init(struct max8925_chip *chip,
struct max8925_platform_data *pdata)
{
int ret;
max8925_irq_init(chip, chip->i2c->irq, pdata);
if (pdata && (pdata->power || pdata->touch)) {
/* enable ADC to control internal reference */
max8925_set_bits(chip->i2c, MAX8925_RESET_CNFG, 1, 1);
/* enable internal reference for ADC */
max8925_set_bits(chip->adc, MAX8925_TSC_CNFG1, 3, 2);
/* check for internal reference IRQ */
do {
ret = max8925_reg_read(chip->adc, MAX8925_TSC_IRQ);
} while (ret & MAX8925_NREF_OK);
/* enaable ADC scheduler, interval is 1 second */
max8925_set_bits(chip->adc, MAX8925_ADC_SCHED, 3, 2);
}
/* enable Momentary Power Loss */
max8925_set_bits(chip->rtc, MAX8925_MPL_CNTL, 1 << 4, 1 << 4);
ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0],
ARRAY_SIZE(rtc_devs),
&rtc_resources[0], 0);
if (ret < 0) {
dev_err(chip->dev, "Failed to add rtc subdev\n");
goto out;
}
if (pdata && pdata->regulator[0]) {
ret = mfd_add_devices(chip->dev, 0, &regulator_devs[0],
ARRAY_SIZE(regulator_devs),
&regulator_resources[0], 0);
if (ret < 0) {
dev_err(chip->dev, "Failed to add regulator subdev\n");
goto out_dev;
}
}
if (pdata && pdata->backlight) {
ret = mfd_add_devices(chip->dev, 0, &backlight_devs[0],
ARRAY_SIZE(backlight_devs),
&backlight_resources[0], 0);
if (ret < 0) {
dev_err(chip->dev, "Failed to add backlight subdev\n");
goto out_dev;
}
}
if (pdata && pdata->power) {
ret = mfd_add_devices(chip->dev, 0, &power_devs[0],
ARRAY_SIZE(power_devs),
&power_supply_resources[0], 0);
if (ret < 0) {
dev_err(chip->dev, "Failed to add power supply "
"subdev\n");
goto out_dev;
}
}
if (pdata && pdata->touch) {
ret = mfd_add_devices(chip->dev, 0, &touch_devs[0],
ARRAY_SIZE(touch_devs),
&touch_resources[0], 0);
if (ret < 0) {
dev_err(chip->dev, "Failed to add touch subdev\n");
goto out_dev;
}
}
return 0;
out_dev:
mfd_remove_devices(chip->dev);
out:
return ret;
}
void __devexit max8925_device_exit(struct max8925_chip *chip)
{
if (chip->core_irq)
free_irq(chip->core_irq, chip);
if (chip->tsc_irq)
free_irq(chip->tsc_irq, chip);
mfd_remove_devices(chip->dev);
}
MODULE_DESCRIPTION("PMIC Driver for Maxim MAX8925");
MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com");
MODULE_LICENSE("GPL");

211
drivers/mfd/max8925-i2c.c Normal file
View File

@ -0,0 +1,211 @@
/*
* I2C driver for Maxim MAX8925
*
* Copyright (C) 2009 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/mfd/max8925.h>
#define RTC_I2C_ADDR 0x68
#define ADC_I2C_ADDR 0x47
static inline int max8925_read_device(struct i2c_client *i2c,
int reg, int bytes, void *dest)
{
int ret;
if (bytes > 1)
ret = i2c_smbus_read_i2c_block_data(i2c, reg, bytes, dest);
else {
ret = i2c_smbus_read_byte_data(i2c, reg);
if (ret < 0)
return ret;
*(unsigned char *)dest = (unsigned char)ret;
}
return ret;
}
static inline int max8925_write_device(struct i2c_client *i2c,
int reg, int bytes, void *src)
{
unsigned char buf[bytes + 1];
int ret;
buf[0] = (unsigned char)reg;
memcpy(&buf[1], src, bytes);
ret = i2c_master_send(i2c, buf, bytes + 1);
if (ret < 0)
return ret;
return 0;
}
int max8925_reg_read(struct i2c_client *i2c, int reg)
{
struct max8925_chip *chip = i2c_get_clientdata(i2c);
unsigned char data = 0;
int ret;
mutex_lock(&chip->io_lock);
ret = max8925_read_device(i2c, reg, 1, &data);
mutex_unlock(&chip->io_lock);
if (ret < 0)
return ret;
else
return (int)data;
}
EXPORT_SYMBOL(max8925_reg_read);
int max8925_reg_write(struct i2c_client *i2c, int reg,
unsigned char data)
{
struct max8925_chip *chip = i2c_get_clientdata(i2c);
int ret;
mutex_lock(&chip->io_lock);
ret = max8925_write_device(i2c, reg, 1, &data);
mutex_unlock(&chip->io_lock);
return ret;
}
EXPORT_SYMBOL(max8925_reg_write);
int max8925_bulk_read(struct i2c_client *i2c, int reg,
int count, unsigned char *buf)
{
struct max8925_chip *chip = i2c_get_clientdata(i2c);
int ret;
mutex_lock(&chip->io_lock);
ret = max8925_read_device(i2c, reg, count, buf);
mutex_unlock(&chip->io_lock);
return ret;
}
EXPORT_SYMBOL(max8925_bulk_read);
int max8925_bulk_write(struct i2c_client *i2c, int reg,
int count, unsigned char *buf)
{
struct max8925_chip *chip = i2c_get_clientdata(i2c);
int ret;
mutex_lock(&chip->io_lock);
ret = max8925_write_device(i2c, reg, count, buf);
mutex_unlock(&chip->io_lock);
return ret;
}
EXPORT_SYMBOL(max8925_bulk_write);
int max8925_set_bits(struct i2c_client *i2c, int reg,
unsigned char mask, unsigned char data)
{
struct max8925_chip *chip = i2c_get_clientdata(i2c);
unsigned char value;
int ret;
mutex_lock(&chip->io_lock);
ret = max8925_read_device(i2c, reg, 1, &value);
if (ret < 0)
goto out;
value &= ~mask;
value |= data;
ret = max8925_write_device(i2c, reg, 1, &value);
out:
mutex_unlock(&chip->io_lock);
return ret;
}
EXPORT_SYMBOL(max8925_set_bits);
static const struct i2c_device_id max8925_id_table[] = {
{ "max8925", 0 },
{ },
};
MODULE_DEVICE_TABLE(i2c, max8925_id_table);
static int __devinit max8925_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct max8925_platform_data *pdata = client->dev.platform_data;
static struct max8925_chip *chip;
if (!pdata) {
pr_info("%s: platform data is missing\n", __func__);
return -EINVAL;
}
chip = kzalloc(sizeof(struct max8925_chip), GFP_KERNEL);
if (chip == NULL)
return -ENOMEM;
chip->i2c = client;
chip->dev = &client->dev;
i2c_set_clientdata(client, chip);
dev_set_drvdata(chip->dev, chip);
mutex_init(&chip->io_lock);
chip->rtc = i2c_new_dummy(chip->i2c->adapter, RTC_I2C_ADDR);
i2c_set_clientdata(chip->rtc, chip);
chip->adc = i2c_new_dummy(chip->i2c->adapter, ADC_I2C_ADDR);
i2c_set_clientdata(chip->adc, chip);
max8925_device_init(chip, pdata);
return 0;
}
static int __devexit max8925_remove(struct i2c_client *client)
{
struct max8925_chip *chip = i2c_get_clientdata(client);
max8925_device_exit(chip);
i2c_unregister_device(chip->adc);
i2c_unregister_device(chip->rtc);
i2c_set_clientdata(chip->adc, NULL);
i2c_set_clientdata(chip->rtc, NULL);
i2c_set_clientdata(chip->i2c, NULL);
kfree(chip);
return 0;
}
static struct i2c_driver max8925_driver = {
.driver = {
.name = "max8925",
.owner = THIS_MODULE,
},
.probe = max8925_probe,
.remove = __devexit_p(max8925_remove),
.id_table = max8925_id_table,
};
static int __init max8925_i2c_init(void)
{
int ret;
ret = i2c_add_driver(&max8925_driver);
if (ret != 0)
pr_err("Failed to register MAX8925 I2C driver: %d\n", ret);
return ret;
}
subsys_initcall(max8925_i2c_init);
static void __exit max8925_i2c_exit(void)
{
i2c_del_driver(&max8925_driver);
}
module_exit(max8925_i2c_exit);
MODULE_DESCRIPTION("I2C Driver for Maxim 8925");
MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
MODULE_LICENSE("GPL");

View File

@ -13,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/acpi.h>
#include <linux/mfd/core.h>
static int mfd_add_device(struct device *parent, int id,
@ -62,6 +63,10 @@ static int mfd_add_device(struct device *parent, int id,
res[r].start = cell->resources[r].start;
res[r].end = cell->resources[r].end;
}
ret = acpi_check_resource_conflict(res);
if (ret)
goto fail_res;
}
platform_device_add_resources(pdev, res, cell->num_resources);

View File

@ -21,7 +21,7 @@
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/mmc/host.h>
#include <linux/mfd/core.h>
#include <linux/mfd/tmio.h>
#include <linux/mfd/sh_mobile_sdhi.h>
@ -95,9 +95,9 @@ static int __init sh_mobile_sdhi_probe(struct platform_device *pdev)
clk_enable(priv->clk);
/* FIXME: silly const unsigned int hclk */
*(unsigned int *)&priv->mmc_data.hclk = clk_get_rate(priv->clk);
priv->mmc_data.hclk = clk_get_rate(priv->clk);
priv->mmc_data.set_pwr = sh_mobile_sdhi_set_pwr;
priv->mmc_data.capabilities = MMC_CAP_MMC_HIGHSPEED;
memcpy(&priv->cell_mmc, &sh_mobile_sdhi_cell, sizeof(priv->cell_mmc));
priv->cell_mmc.driver_data = &priv->mmc_data;

View File

@ -1430,7 +1430,7 @@ static int __devinit sm501_plat_probe(struct platform_device *dev)
}
sm->regs_claim = request_mem_region(sm->io_res->start,
0x100, "sm501");
resource_size(sm->io_res), "sm501");
if (sm->regs_claim == NULL) {
dev_err(&dev->dev, "cannot claim registers\n");
@ -1440,8 +1440,7 @@ static int __devinit sm501_plat_probe(struct platform_device *dev)
platform_set_drvdata(dev, sm);
sm->regs = ioremap(sm->io_res->start,
(sm->io_res->end - sm->io_res->start) - 1);
sm->regs = ioremap(sm->io_res->start, resource_size(sm->io_res));
if (sm->regs == NULL) {
dev_err(&dev->dev, "cannot remap registers\n");
@ -1645,7 +1644,7 @@ static int __devinit sm501_pci_probe(struct pci_dev *dev,
sm->mem_res = &dev->resource[0];
sm->regs_claim = request_mem_region(sm->io_res->start,
0x100, "sm501");
resource_size(sm->io_res), "sm501");
if (sm->regs_claim == NULL) {
dev_err(&dev->dev, "cannot claim registers\n");
err= -EBUSY;

View File

@ -360,7 +360,7 @@ static int t7l66xb_probe(struct platform_device *dev)
if (ret)
goto err_request_scr;
t7l66xb->scr = ioremap(rscr->start, rscr->end - rscr->start + 1);
t7l66xb->scr = ioremap(rscr->start, resource_size(rscr));
if (!t7l66xb->scr) {
ret = -ENOMEM;
goto err_ioremap;
@ -403,12 +403,12 @@ static int t7l66xb_probe(struct platform_device *dev)
err_ioremap:
release_resource(&t7l66xb->rscr);
err_request_scr:
kfree(t7l66xb);
clk_put(t7l66xb->clk48m);
err_clk48m_get:
clk_put(t7l66xb->clk32k);
err_clk32k_get:
err_noirq:
kfree(t7l66xb);
return ret;
}

View File

@ -647,7 +647,7 @@ static int __devinit tc6393xb_probe(struct platform_device *dev)
if (ret)
goto err_request_scr;
tc6393xb->scr = ioremap(rscr->start, rscr->end - rscr->start + 1);
tc6393xb->scr = ioremap(rscr->start, resource_size(rscr));
if (!tc6393xb->scr) {
ret = -ENOMEM;
goto err_ioremap;

View File

@ -58,13 +58,6 @@
#define DRIVER_NAME "twl"
#if defined(CONFIG_TWL4030_BCI_BATTERY) || \
defined(CONFIG_TWL4030_BCI_BATTERY_MODULE)
#define twl_has_bci() true
#else
#define twl_has_bci() false
#endif
#if defined(CONFIG_KEYBOARD_TWL4030) || defined(CONFIG_KEYBOARD_TWL4030_MODULE)
#define twl_has_keypad() true
#else
@ -130,7 +123,7 @@
#define TWL_NUM_SLAVES 4
#if defined(CONFIG_INPUT_TWL4030_PWRBUTTON) \
|| defined(CONFIG_INPUT_TWL4030_PWBUTTON_MODULE)
|| defined(CONFIG_INPUT_TWL4030_PWRBUTTON_MODULE)
#define twl_has_pwrbutton() true
#else
#define twl_has_pwrbutton() false
@ -205,6 +198,7 @@
/* subchip/slave 3 0x4B - AUDIO */
#define TWL6030_BASEADD_AUDIO 0x0000
#define TWL6030_BASEADD_RSV 0x0000
#define TWL6030_BASEADD_ZERO 0x0000
/* Few power values */
#define R_CFG_BOOT 0x05
@ -320,9 +314,11 @@ static struct twl_mapping twl6030_map[] = {
{ SUB_CHIP_ID1, TWL6030_BASEADD_CHARGER },
{ SUB_CHIP_ID1, TWL6030_BASEADD_GASGAUGE },
{ SUB_CHIP_ID1, TWL6030_BASEADD_PWM },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID0, TWL6030_BASEADD_ZERO },
{ SUB_CHIP_ID1, TWL6030_BASEADD_ZERO },
{ SUB_CHIP_ID2, TWL6030_BASEADD_ZERO },
{ SUB_CHIP_ID2, TWL6030_BASEADD_ZERO },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
@ -588,18 +584,6 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
struct device *child;
unsigned sub_chip_id;
if (twl_has_bci() && pdata->bci &&
!(features & (TPS_SUBSET | TWL5031))) {
child = add_child(3, "twl4030_bci",
pdata->bci, sizeof(*pdata->bci),
false,
/* irq0 = CHG_PRES, irq1 = BCI */
pdata->irq_base + BCI_PRES_INTR_OFFSET,
pdata->irq_base + BCI_INTR_OFFSET);
if (IS_ERR(child))
return PTR_ERR(child);
}
if (twl_has_gpio() && pdata->gpio) {
child = add_child(SUB_CHIP_ID1, "twl4030_gpio",
pdata->gpio, sizeof(*pdata->gpio),
@ -977,6 +961,7 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
int status;
unsigned i;
struct twl4030_platform_data *pdata = client->dev.platform_data;
u8 temp;
if (!pdata) {
dev_dbg(&client->dev, "no platform data?\n");
@ -1044,6 +1029,18 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
goto fail;
}
/* Disable TWL4030/TWL5030 I2C Pull-up on I2C1 and I2C4(SR) interface.
* Program I2C_SCL_CTRL_PU(bit 0)=0, I2C_SDA_CTRL_PU (bit 2)=0,
* SR_I2C_SCL_CTRL_PU(bit 4)=0 and SR_I2C_SDA_CTRL_PU(bit 6)=0.
*/
if (twl_class_is_4030()) {
twl_i2c_read_u8(TWL4030_MODULE_INTBR, &temp, REG_GPPUPDCTR1);
temp &= ~(SR_I2C_SDA_CTRL_PU | SR_I2C_SCL_CTRL_PU | \
I2C_SDA_CTRL_PU | I2C_SCL_CTRL_PU);
twl_i2c_write_u8(TWL4030_MODULE_INTBR, temp, REG_GPPUPDCTR1);
}
status = add_children(pdata, id->driver_data);
fail:
if (status < 0)

View File

@ -405,7 +405,7 @@ static int __init twl4030_configure_resource(struct twl4030_resconfig *rconfig)
if (rconfig->remap_sleep != TWL4030_RESCONFIG_UNDEF) {
remap &= ~SLEEP_STATE_MASK;
remap |= rconfig->remap_off << SLEEP_STATE_SHIFT;
remap |= rconfig->remap_sleep << SLEEP_STATE_SHIFT;
}
err = twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
@ -461,6 +461,56 @@ out:
return err;
}
int twl4030_remove_script(u8 flags)
{
int err = 0;
err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, R_KEY_1,
R_PROTECT_KEY);
if (err) {
pr_err("twl4030: unable to unlock PROTECT_KEY\n");
return err;
}
err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, R_KEY_2,
R_PROTECT_KEY);
if (err) {
pr_err("twl4030: unable to unlock PROTECT_KEY\n");
return err;
}
if (flags & TWL4030_WRST_SCRIPT) {
err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT,
R_SEQ_ADD_WARM);
if (err)
return err;
}
if (flags & TWL4030_WAKEUP12_SCRIPT) {
if (err)
err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT,
R_SEQ_ADD_S2A12);
return err;
}
if (flags & TWL4030_WAKEUP3_SCRIPT) {
err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT,
R_SEQ_ADD_S2A3);
if (err)
return err;
}
if (flags & TWL4030_SLEEP_SCRIPT) {
err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT,
R_SEQ_ADD_A2S);
if (err)
return err;
}
err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, R_PROTECT_KEY);
if (err)
pr_err("TWL4030 Unable to relock registers\n");
return err;
}
void __init twl4030_power_init(struct twl4030_power_data *twl4030_scripts)
{
int err = 0;

View File

@ -27,6 +27,7 @@
#include <linux/mutex.h>
#include <linux/mfd/ucb1x00.h>
#include <linux/gpio.h>
#include <linux/semaphore.h>
#include <mach/dma.h>
#include <mach/hardware.h>

View File

@ -321,7 +321,6 @@ EXPORT_SYMBOL_GPL(wm831x_set_bits);
*/
int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
{
int tries = 10;
int ret, src;
mutex_lock(&wm831x->auxadc_lock);
@ -349,13 +348,14 @@ int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
goto disable;
}
do {
msleep(1);
/* Ignore the result to allow us to soldier on without IRQ hookup */
wait_for_completion_timeout(&wm831x->auxadc_done, msecs_to_jiffies(5));
ret = wm831x_reg_read(wm831x, WM831X_AUXADC_CONTROL);
if (ret < 0)
ret = WM831X_AUX_CVT_ENA;
} while ((ret & WM831X_AUX_CVT_ENA) && --tries);
ret = wm831x_reg_read(wm831x, WM831X_AUXADC_CONTROL);
if (ret < 0) {
dev_err(wm831x->dev, "AUXADC status read failed: %d\n", ret);
goto disable;
}
if (ret & WM831X_AUX_CVT_ENA) {
dev_err(wm831x->dev, "Timed out reading AUXADC\n");
@ -390,6 +390,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
*
@ -1411,6 +1420,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);
@ -1449,18 +1459,33 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
case WM8310:
parent = WM8310;
wm831x->num_gpio = 16;
if (rev > 0) {
wm831x->has_gpio_ena = 1;
wm831x->has_cs_sts = 1;
}
dev_info(wm831x->dev, "WM8310 revision %c\n", 'A' + rev);
break;
case WM8311:
parent = WM8311;
wm831x->num_gpio = 16;
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 WM8312:
parent = WM8312;
wm831x->num_gpio = 16;
if (rev > 0) {
wm831x->has_gpio_ena = 1;
wm831x->has_cs_sts = 1;
}
dev_info(wm831x->dev, "WM8312 revision %c\n", 'A' + rev);
break;
@ -1508,6 +1533,16 @@ 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:
@ -1578,6 +1613,8 @@ static 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);
}

View File

@ -339,7 +339,6 @@ EXPORT_SYMBOL_GPL(wm8350_reg_unlock);
int wm8350_read_auxadc(struct wm8350 *wm8350, int channel, int scale, int vref)
{
u16 reg, result = 0;
int tries = 5;
if (channel < WM8350_AUXADC_AUX1 || channel > WM8350_AUXADC_TEMP)
return -EINVAL;
@ -363,12 +362,13 @@ int wm8350_read_auxadc(struct wm8350 *wm8350, int channel, int scale, int vref)
reg |= 1 << channel | WM8350_AUXADC_POLL;
wm8350_reg_write(wm8350, WM8350_DIGITISER_CONTROL_1, reg);
do {
schedule_timeout_interruptible(1);
reg = wm8350_reg_read(wm8350, WM8350_DIGITISER_CONTROL_1);
} while ((reg & WM8350_AUXADC_POLL) && --tries);
/* We ignore the result of the completion and just check for a
* conversion result, allowing us to soldier on if the IRQ
* infrastructure is not set up for the chip. */
wait_for_completion_timeout(&wm8350->auxadc_done, msecs_to_jiffies(5));
if (!tries)
reg = wm8350_reg_read(wm8350, WM8350_DIGITISER_CONTROL_1);
if (reg & WM8350_AUXADC_POLL)
dev_err(wm8350->dev, "adc chn %d read timeout\n", channel);
else
result = wm8350_reg_read(wm8350,
@ -385,6 +385,15 @@ int wm8350_read_auxadc(struct wm8350 *wm8350, int channel, int scale, int vref)
}
EXPORT_SYMBOL_GPL(wm8350_read_auxadc);
static irqreturn_t wm8350_auxadc_irq(int irq, void *irq_data)
{
struct wm8350 *wm8350 = irq_data;
complete(&wm8350->auxadc_done);
return IRQ_HANDLED;
}
/*
* Cache is always host endian.
*/
@ -682,11 +691,22 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
}
mutex_init(&wm8350->auxadc_mutex);
init_completion(&wm8350->auxadc_done);
ret = wm8350_irq_init(wm8350, irq, pdata);
if (ret < 0)
goto err;
if (wm8350->irq_base) {
ret = request_threaded_irq(wm8350->irq_base +
WM8350_IRQ_AUXADC_DATARDY,
NULL, wm8350_auxadc_irq, 0,
"auxadc", wm8350);
if (ret < 0)
dev_warn(wm8350->dev,
"Failed to request AUXADC IRQ: %d\n", ret);
}
if (pdata && pdata->init) {
ret = pdata->init(wm8350);
if (ret != 0) {
@ -736,6 +756,9 @@ void wm8350_device_exit(struct wm8350 *wm8350)
platform_device_unregister(wm8350->gpio.pdev);
platform_device_unregister(wm8350->codec.pdev);
if (wm8350->irq_base)
free_irq(wm8350->irq_base + WM8350_IRQ_AUXADC_DATARDY, wm8350);
wm8350_irq_exit(wm8350);
kfree(wm8350->reg_cache);

View File

@ -18,7 +18,7 @@
#include <linux/bug.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/irq.h>
#include <linux/mfd/wm8350/core.h>
#include <linux/mfd/wm8350/audio.h>
@ -29,8 +29,6 @@
#include <linux/mfd/wm8350/supply.h>
#include <linux/mfd/wm8350/wdt.h>
#define WM8350_NUM_IRQ_REGS 7
#define WM8350_INT_OFFSET_1 0
#define WM8350_INT_OFFSET_2 1
#define WM8350_POWER_UP_INT_OFFSET 2
@ -366,19 +364,10 @@ static struct wm8350_irq_data wm8350_irqs[] = {
},
};
static void wm8350_irq_call_handler(struct wm8350 *wm8350, int irq)
static inline struct wm8350_irq_data *irq_to_wm8350_irq(struct wm8350 *wm8350,
int irq)
{
mutex_lock(&wm8350->irq_mutex);
if (wm8350->irq[irq].handler)
wm8350->irq[irq].handler(irq, wm8350->irq[irq].data);
else {
dev_err(wm8350->dev, "irq %d nobody cared. now masked.\n",
irq);
wm8350_mask_irq(wm8350, irq);
}
mutex_unlock(&wm8350->irq_mutex);
return &wm8350_irqs[irq - wm8350->irq_base];
}
/*
@ -386,7 +375,9 @@ static void wm8350_irq_call_handler(struct wm8350 *wm8350, int irq)
* interrupts are clear on read the IRQ line will be reasserted and
* the physical IRQ will be handled again if another interrupt is
* asserted while we run - in the normal course of events this is a
* rare occurrence so we save I2C/SPI reads.
* rare occurrence so we save I2C/SPI reads. We're also assuming that
* it's rare to get lots of interrupts firing simultaneously so try to
* minimise I/O.
*/
static irqreturn_t wm8350_irq(int irq, void *irq_data)
{
@ -397,7 +388,6 @@ static irqreturn_t wm8350_irq(int irq, void *irq_data)
struct wm8350_irq_data *data;
int i;
/* TODO: Use block reads to improve performance? */
level_one = wm8350_reg_read(wm8350, WM8350_SYSTEM_INTERRUPTS)
& ~wm8350_reg_read(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK);
@ -416,93 +406,101 @@ static irqreturn_t wm8350_irq(int irq, void *irq_data)
sub_reg[data->reg] =
wm8350_reg_read(wm8350, WM8350_INT_STATUS_1 +
data->reg);
sub_reg[data->reg] &=
~wm8350_reg_read(wm8350,
WM8350_INT_STATUS_1_MASK +
data->reg);
sub_reg[data->reg] &= ~wm8350->irq_masks[data->reg];
read_done[data->reg] = 1;
}
if (sub_reg[data->reg] & data->mask)
wm8350_irq_call_handler(wm8350, i);
handle_nested_irq(wm8350->irq_base + i);
}
return IRQ_HANDLED;
}
int wm8350_register_irq(struct wm8350 *wm8350, int irq,
irq_handler_t handler, unsigned long flags,
const char *name, void *data)
static void wm8350_irq_lock(unsigned int irq)
{
if (irq < 0 || irq >= WM8350_NUM_IRQ || !handler)
return -EINVAL;
struct wm8350 *wm8350 = get_irq_chip_data(irq);
if (wm8350->irq[irq].handler)
return -EBUSY;
mutex_lock(&wm8350->irq_mutex);
wm8350->irq[irq].handler = handler;
wm8350->irq[irq].data = data;
mutex_unlock(&wm8350->irq_mutex);
wm8350_unmask_irq(wm8350, irq);
return 0;
mutex_lock(&wm8350->irq_lock);
}
EXPORT_SYMBOL_GPL(wm8350_register_irq);
int wm8350_free_irq(struct wm8350 *wm8350, int irq)
static void wm8350_irq_sync_unlock(unsigned int irq)
{
if (irq < 0 || irq >= WM8350_NUM_IRQ)
return -EINVAL;
struct wm8350 *wm8350 = get_irq_chip_data(irq);
int i;
wm8350_mask_irq(wm8350, irq);
for (i = 0; i < ARRAY_SIZE(wm8350->irq_masks); i++) {
/* If there's been a change in the mask write it back
* to the hardware. */
if (wm8350->irq_masks[i] !=
wm8350->reg_cache[WM8350_INT_STATUS_1_MASK + i])
WARN_ON(wm8350_reg_write(wm8350,
WM8350_INT_STATUS_1_MASK + i,
wm8350->irq_masks[i]));
}
mutex_lock(&wm8350->irq_mutex);
wm8350->irq[irq].handler = NULL;
mutex_unlock(&wm8350->irq_mutex);
return 0;
mutex_unlock(&wm8350->irq_lock);
}
EXPORT_SYMBOL_GPL(wm8350_free_irq);
int wm8350_mask_irq(struct wm8350 *wm8350, int irq)
static void wm8350_irq_enable(unsigned int irq)
{
return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK +
wm8350_irqs[irq].reg,
wm8350_irqs[irq].mask);
}
EXPORT_SYMBOL_GPL(wm8350_mask_irq);
struct wm8350 *wm8350 = get_irq_chip_data(irq);
struct wm8350_irq_data *irq_data = irq_to_wm8350_irq(wm8350, irq);
int wm8350_unmask_irq(struct wm8350 *wm8350, int irq)
{
return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK +
wm8350_irqs[irq].reg,
wm8350_irqs[irq].mask);
wm8350->irq_masks[irq_data->reg] &= ~irq_data->mask;
}
EXPORT_SYMBOL_GPL(wm8350_unmask_irq);
static void wm8350_irq_disable(unsigned int irq)
{
struct wm8350 *wm8350 = get_irq_chip_data(irq);
struct wm8350_irq_data *irq_data = irq_to_wm8350_irq(wm8350, irq);
wm8350->irq_masks[irq_data->reg] |= irq_data->mask;
}
static struct irq_chip wm8350_irq_chip = {
.name = "wm8350",
.bus_lock = wm8350_irq_lock,
.bus_sync_unlock = wm8350_irq_sync_unlock,
.disable = wm8350_irq_disable,
.enable = wm8350_irq_enable,
};
int wm8350_irq_init(struct wm8350 *wm8350, int irq,
struct wm8350_platform_data *pdata)
{
int ret;
int ret, cur_irq, i;
int flags = IRQF_ONESHOT;
if (!irq) {
dev_err(wm8350->dev, "No IRQ configured\n");
return -EINVAL;
dev_warn(wm8350->dev, "No interrupt support, no core IRQ\n");
return 0;
}
if (!pdata || !pdata->irq_base) {
dev_warn(wm8350->dev, "No interrupt support, no IRQ base\n");
return 0;
}
/* Mask top level interrupts */
wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0xFFFF);
wm8350_reg_write(wm8350, WM8350_INT_STATUS_1_MASK, 0xFFFF);
wm8350_reg_write(wm8350, WM8350_INT_STATUS_2_MASK, 0xFFFF);
wm8350_reg_write(wm8350, WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, 0xFFFF);
wm8350_reg_write(wm8350, WM8350_GPIO_INT_STATUS_MASK, 0xFFFF);
wm8350_reg_write(wm8350, WM8350_COMPARATOR_INT_STATUS_MASK, 0xFFFF);
mutex_init(&wm8350->irq_mutex);
/* Mask all individual interrupts by default and cache the
* masks. We read the masks back since there are unwritable
* bits in the mask registers. */
for (i = 0; i < ARRAY_SIZE(wm8350->irq_masks); i++) {
wm8350_reg_write(wm8350, WM8350_INT_STATUS_1_MASK + i,
0xFFFF);
wm8350->irq_masks[i] =
wm8350_reg_read(wm8350,
WM8350_INT_STATUS_1_MASK + i);
}
mutex_init(&wm8350->irq_lock);
wm8350->chip_irq = irq;
wm8350->irq_base = pdata->irq_base;
if (pdata && pdata->irq_high) {
if (pdata->irq_high) {
flags |= IRQF_TRIGGER_HIGH;
wm8350_set_bits(wm8350, WM8350_SYSTEM_CONTROL_1,
@ -514,11 +512,32 @@ int wm8350_irq_init(struct wm8350 *wm8350, int irq,
WM8350_IRQ_POL);
}
/* Register with genirq */
for (cur_irq = wm8350->irq_base;
cur_irq < ARRAY_SIZE(wm8350_irqs) + wm8350->irq_base;
cur_irq++) {
set_irq_chip_data(cur_irq, wm8350);
set_irq_chip_and_handler(cur_irq, &wm8350_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
}
ret = request_threaded_irq(irq, NULL, wm8350_irq, flags,
"wm8350", wm8350);
if (ret != 0)
dev_err(wm8350->dev, "Failed to request IRQ: %d\n", ret);
/* Allow interrupts to fire */
wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0);
return ret;
}

537
drivers/mfd/wm8994-core.c Normal file
View File

@ -0,0 +1,537 @@
/*
* wm8994-core.c -- Device access for Wolfson WM8994
*
* Copyright 2009 Wolfson Microelectronics PLC.
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/mfd/core.h>
#include <linux/regulator/consumer.h>
#include <linux/regulator/machine.h>
#include <linux/mfd/wm8994/core.h>
#include <linux/mfd/wm8994/pdata.h>
#include <linux/mfd/wm8994/registers.h>
static int wm8994_read(struct wm8994 *wm8994, unsigned short reg,
int bytes, void *dest)
{
int ret, i;
u16 *buf = dest;
BUG_ON(bytes % 2);
BUG_ON(bytes <= 0);
ret = wm8994->read_dev(wm8994, reg, bytes, dest);
if (ret < 0)
return ret;
for (i = 0; i < bytes / 2; i++) {
buf[i] = be16_to_cpu(buf[i]);
dev_vdbg(wm8994->dev, "Read %04x from R%d(0x%x)\n",
buf[i], reg + i, reg + i);
}
return 0;
}
/**
* wm8994_reg_read: Read a single WM8994 register.
*
* @wm8994: Device to read from.
* @reg: Register to read.
*/
int wm8994_reg_read(struct wm8994 *wm8994, unsigned short reg)
{
unsigned short val;
int ret;
mutex_lock(&wm8994->io_lock);
ret = wm8994_read(wm8994, reg, 2, &val);
mutex_unlock(&wm8994->io_lock);
if (ret < 0)
return ret;
else
return val;
}
EXPORT_SYMBOL_GPL(wm8994_reg_read);
/**
* wm8994_bulk_read: Read multiple WM8994 registers
*
* @wm8994: Device to read from
* @reg: First register
* @count: Number of registers
* @buf: Buffer to fill.
*/
int wm8994_bulk_read(struct wm8994 *wm8994, unsigned short reg,
int count, u16 *buf)
{
int ret;
mutex_lock(&wm8994->io_lock);
ret = wm8994_read(wm8994, reg, count * 2, buf);
mutex_unlock(&wm8994->io_lock);
return ret;
}
EXPORT_SYMBOL_GPL(wm8994_bulk_read);
static int wm8994_write(struct wm8994 *wm8994, unsigned short reg,
int bytes, void *src)
{
u16 *buf = src;
int i;
BUG_ON(bytes % 2);
BUG_ON(bytes <= 0);
for (i = 0; i < bytes / 2; i++) {
dev_vdbg(wm8994->dev, "Write %04x to R%d(0x%x)\n",
buf[i], reg + i, reg + i);
buf[i] = cpu_to_be16(buf[i]);
}
return wm8994->write_dev(wm8994, reg, bytes, src);
}
/**
* wm8994_reg_write: Write a single WM8994 register.
*
* @wm8994: Device to write to.
* @reg: Register to write to.
* @val: Value to write.
*/
int wm8994_reg_write(struct wm8994 *wm8994, unsigned short reg,
unsigned short val)
{
int ret;
mutex_lock(&wm8994->io_lock);
ret = wm8994_write(wm8994, reg, 2, &val);
mutex_unlock(&wm8994->io_lock);
return ret;
}
EXPORT_SYMBOL_GPL(wm8994_reg_write);
/**
* wm8994_set_bits: Set the value of a bitfield in a WM8994 register
*
* @wm8994: Device to write to.
* @reg: Register to write to.
* @mask: Mask of bits to set.
* @val: Value to set (unshifted)
*/
int wm8994_set_bits(struct wm8994 *wm8994, unsigned short reg,
unsigned short mask, unsigned short val)
{
int ret;
u16 r;
mutex_lock(&wm8994->io_lock);
ret = wm8994_read(wm8994, reg, 2, &r);
if (ret < 0)
goto out;
r &= ~mask;
r |= val;
ret = wm8994_write(wm8994, reg, 2, &r);
out:
mutex_unlock(&wm8994->io_lock);
return ret;
}
EXPORT_SYMBOL_GPL(wm8994_set_bits);
static struct mfd_cell wm8994_regulator_devs[] = {
{ .name = "wm8994-ldo", .id = 1 },
{ .name = "wm8994-ldo", .id = 2 },
};
static struct mfd_cell wm8994_devs[] = {
{ .name = "wm8994-codec" },
{ .name = "wm8994-gpio" },
};
/*
* Supplies for the main bulk of CODEC; the LDO supplies are ignored
* and should be handled via the standard regulator API supply
* management.
*/
static const char *wm8994_main_supplies[] = {
"DBVDD",
"DCVDD",
"AVDD1",
"AVDD2",
"CPVDD",
"SPKVDD1",
"SPKVDD2",
};
#ifdef CONFIG_PM
static int wm8994_device_suspend(struct device *dev)
{
struct wm8994 *wm8994 = dev_get_drvdata(dev);
int ret;
/* GPIO configuration state is saved here since we may be configuring
* the GPIO alternate functions even if we're not using the gpiolib
* driver for them.
*/
ret = wm8994_read(wm8994, WM8994_GPIO_1, WM8994_NUM_GPIO_REGS * 2,
&wm8994->gpio_regs);
if (ret < 0)
dev_err(dev, "Failed to save GPIO registers: %d\n", ret);
/* For similar reasons we also stash the regulator states */
ret = wm8994_read(wm8994, WM8994_LDO_1, WM8994_NUM_LDO_REGS * 2,
&wm8994->ldo_regs);
if (ret < 0)
dev_err(dev, "Failed to save LDO registers: %d\n", ret);
ret = regulator_bulk_disable(ARRAY_SIZE(wm8994_main_supplies),
wm8994->supplies);
if (ret != 0) {
dev_err(dev, "Failed to disable supplies: %d\n", ret);
return ret;
}
return 0;
}
static int wm8994_device_resume(struct device *dev)
{
struct wm8994 *wm8994 = dev_get_drvdata(dev);
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(wm8994_main_supplies),
wm8994->supplies);
if (ret != 0) {
dev_err(dev, "Failed to enable supplies: %d\n", ret);
return ret;
}
ret = wm8994_write(wm8994, WM8994_LDO_1, WM8994_NUM_LDO_REGS * 2,
&wm8994->ldo_regs);
if (ret < 0)
dev_err(dev, "Failed to restore LDO registers: %d\n", ret);
ret = wm8994_write(wm8994, WM8994_GPIO_1, WM8994_NUM_GPIO_REGS * 2,
&wm8994->gpio_regs);
if (ret < 0)
dev_err(dev, "Failed to restore GPIO registers: %d\n", ret);
return 0;
}
#endif
#ifdef CONFIG_REGULATOR
static int wm8994_ldo_in_use(struct wm8994_pdata *pdata, int ldo)
{
struct wm8994_ldo_pdata *ldo_pdata;
if (!pdata)
return 0;
ldo_pdata = &pdata->ldo[ldo];
if (!ldo_pdata->init_data)
return 0;
return ldo_pdata->init_data->num_consumer_supplies != 0;
}
#else
static int wm8994_ldo_in_use(struct wm8994_pdata *pdata, int ldo)
{
return 0;
}
#endif
/*
* Instantiate the generic non-control parts of the device.
*/
static int wm8994_device_init(struct wm8994 *wm8994, unsigned long id, int irq)
{
struct wm8994_pdata *pdata = wm8994->dev->platform_data;
int ret, i;
mutex_init(&wm8994->io_lock);
dev_set_drvdata(wm8994->dev, wm8994);
/* Add the on-chip regulators first for bootstrapping */
ret = mfd_add_devices(wm8994->dev, -1,
wm8994_regulator_devs,
ARRAY_SIZE(wm8994_regulator_devs),
NULL, 0);
if (ret != 0) {
dev_err(wm8994->dev, "Failed to add children: %d\n", ret);
goto err;
}
wm8994->supplies = kzalloc(sizeof(struct regulator_bulk_data) *
ARRAY_SIZE(wm8994_main_supplies),
GFP_KERNEL);
if (!wm8994->supplies)
goto err;
for (i = 0; i < ARRAY_SIZE(wm8994_main_supplies); i++)
wm8994->supplies[i].supply = wm8994_main_supplies[i];
ret = regulator_bulk_get(wm8994->dev, ARRAY_SIZE(wm8994_main_supplies),
wm8994->supplies);
if (ret != 0) {
dev_err(wm8994->dev, "Failed to get supplies: %d\n", ret);
goto err_supplies;
}
ret = regulator_bulk_enable(ARRAY_SIZE(wm8994_main_supplies),
wm8994->supplies);
if (ret != 0) {
dev_err(wm8994->dev, "Failed to enable supplies: %d\n", ret);
goto err_get;
}
ret = wm8994_reg_read(wm8994, WM8994_SOFTWARE_RESET);
if (ret < 0) {
dev_err(wm8994->dev, "Failed to read ID register\n");
goto err_enable;
}
if (ret != 0x8994) {
dev_err(wm8994->dev, "Device is not a WM8994, ID is %x\n",
ret);
ret = -EINVAL;
goto err_enable;
}
ret = wm8994_reg_read(wm8994, WM8994_CHIP_REVISION);
if (ret < 0) {
dev_err(wm8994->dev, "Failed to read revision register: %d\n",
ret);
goto err_enable;
}
switch (ret) {
case 0:
case 1:
dev_warn(wm8994->dev, "revision %c not fully supported\n",
'A' + ret);
break;
default:
dev_info(wm8994->dev, "revision %c\n", 'A' + ret);
break;
}
if (pdata) {
wm8994->gpio_base = pdata->gpio_base;
/* GPIO configuration is only applied if it's non-zero */
for (i = 0; i < ARRAY_SIZE(pdata->gpio_defaults); i++) {
if (pdata->gpio_defaults[i]) {
wm8994_set_bits(wm8994, WM8994_GPIO_1 + i,
0xffff,
pdata->gpio_defaults[i]);
}
}
}
/* In some system designs where the regulators are not in use,
* we can achieve a small reduction in leakage currents by
* floating LDO outputs. This bit makes no difference if the
* LDOs are enabled, it only affects cases where the LDOs were
* in operation and are then disabled.
*/
for (i = 0; i < WM8994_NUM_LDO_REGS; i++) {
if (wm8994_ldo_in_use(pdata, i))
wm8994_set_bits(wm8994, WM8994_LDO_1 + i,
WM8994_LDO1_DISCH, WM8994_LDO1_DISCH);
else
wm8994_set_bits(wm8994, WM8994_LDO_1 + i,
WM8994_LDO1_DISCH, 0);
}
ret = mfd_add_devices(wm8994->dev, -1,
wm8994_devs, ARRAY_SIZE(wm8994_devs),
NULL, 0);
if (ret != 0) {
dev_err(wm8994->dev, "Failed to add children: %d\n", ret);
goto err_enable;
}
return 0;
err_enable:
regulator_bulk_disable(ARRAY_SIZE(wm8994_main_supplies),
wm8994->supplies);
err_get:
regulator_bulk_free(ARRAY_SIZE(wm8994_main_supplies), wm8994->supplies);
err_supplies:
kfree(wm8994->supplies);
err:
mfd_remove_devices(wm8994->dev);
kfree(wm8994);
return ret;
}
static void wm8994_device_exit(struct wm8994 *wm8994)
{
mfd_remove_devices(wm8994->dev);
regulator_bulk_disable(ARRAY_SIZE(wm8994_main_supplies),
wm8994->supplies);
regulator_bulk_free(ARRAY_SIZE(wm8994_main_supplies), wm8994->supplies);
kfree(wm8994->supplies);
kfree(wm8994);
}
static int wm8994_i2c_read_device(struct wm8994 *wm8994, unsigned short reg,
int bytes, void *dest)
{
struct i2c_client *i2c = wm8994->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 wm8994_i2c_write_device(struct wm8994 *wm8994, unsigned short reg,
int bytes, void *src)
{
struct i2c_client *i2c = wm8994->control_data;
unsigned char msg[bytes + 2];
int ret;
reg = cpu_to_be16(reg);
memcpy(&msg[0], &reg, 2);
memcpy(&msg[2], src, bytes);
ret = i2c_master_send(i2c, msg, bytes + 2);
if (ret < 0)
return ret;
if (ret < bytes + 2)
return -EIO;
return 0;
}
static int wm8994_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct wm8994 *wm8994;
wm8994 = kzalloc(sizeof(struct wm8994), GFP_KERNEL);
if (wm8994 == NULL) {
kfree(i2c);
return -ENOMEM;
}
i2c_set_clientdata(i2c, wm8994);
wm8994->dev = &i2c->dev;
wm8994->control_data = i2c;
wm8994->read_dev = wm8994_i2c_read_device;
wm8994->write_dev = wm8994_i2c_write_device;
return wm8994_device_init(wm8994, id->driver_data, i2c->irq);
}
static int wm8994_i2c_remove(struct i2c_client *i2c)
{
struct wm8994 *wm8994 = i2c_get_clientdata(i2c);
wm8994_device_exit(wm8994);
return 0;
}
#ifdef CONFIG_PM
static int wm8994_i2c_suspend(struct i2c_client *i2c, pm_message_t state)
{
return wm8994_device_suspend(&i2c->dev);
}
static int wm8994_i2c_resume(struct i2c_client *i2c)
{
return wm8994_device_resume(&i2c->dev);
}
#else
#define wm8994_i2c_suspend NULL
#define wm8994_i2c_resume NULL
#endif
static const struct i2c_device_id wm8994_i2c_id[] = {
{ "wm8994", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8994_i2c_id);
static struct i2c_driver wm8994_i2c_driver = {
.driver = {
.name = "wm8994",
.owner = THIS_MODULE,
},
.probe = wm8994_i2c_probe,
.remove = wm8994_i2c_remove,
.suspend = wm8994_i2c_suspend,
.resume = wm8994_i2c_resume,
.id_table = wm8994_i2c_id,
};
static int __init wm8994_i2c_init(void)
{
int ret;
ret = i2c_add_driver(&wm8994_i2c_driver);
if (ret != 0)
pr_err("Failed to register wm8994 I2C driver: %d\n", ret);
return ret;
}
module_init(wm8994_i2c_init);
static void __exit wm8994_i2c_exit(void)
{
i2c_del_driver(&wm8994_i2c_driver);
}
module_exit(wm8994_i2c_exit);
MODULE_DESCRIPTION("Core support for the WM8994 audio CODEC");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");

View File

@ -365,7 +365,7 @@ config MMC_SDRICOH_CS
config MMC_TMIO
tristate "Toshiba Mobile IO Controller (TMIO) MMC/SD function support"
depends on MFD_TMIO || MFD_ASIC3 || SUPERH
depends on MFD_TMIO || MFD_ASIC3 || MFD_SH_MOBILE_SDHI
help
This provides support for the SD/MMC cell found in TC6393XB,
T7L66XB and also HTC ASIC3

View File

@ -323,7 +323,7 @@ static irqreturn_t tmio_mmc_irq(int irq, void *devid)
if (ireg & (TMIO_STAT_CARD_INSERT | TMIO_STAT_CARD_REMOVE)) {
ack_mmc_irqs(host, TMIO_STAT_CARD_INSERT |
TMIO_STAT_CARD_REMOVE);
mmc_detect_change(host->mmc, 0);
mmc_detect_change(host->mmc, msecs_to_jiffies(100));
}
/* CRC and other errors */
@ -550,6 +550,7 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev)
mmc->ops = &tmio_mmc_ops;
mmc->caps = MMC_CAP_4_BIT_DATA;
mmc->caps |= pdata->capabilities;
mmc->f_max = pdata->hclk;
mmc->f_min = mmc->f_max / 512;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
@ -568,14 +569,14 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev)
if (ret >= 0)
host->irq = ret;
else
goto unmap_ctl;
goto cell_disable;
disable_mmc_irqs(host, TMIO_MASK_ALL);
ret = request_irq(host->irq, tmio_mmc_irq, IRQF_DISABLED |
IRQF_TRIGGER_FALLING, dev_name(&dev->dev), host);
if (ret)
goto unmap_ctl;
goto cell_disable;
mmc_add_host(mmc);
@ -587,6 +588,9 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev)
return 0;
cell_disable:
if (cell->disable)
cell->disable(dev);
unmap_ctl:
iounmap(host->ctl);
host_free:
@ -597,6 +601,7 @@ out:
static int __devexit tmio_mmc_remove(struct platform_device *dev)
{
struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data;
struct mmc_host *mmc = platform_get_drvdata(dev);
platform_set_drvdata(dev, NULL);
@ -605,6 +610,8 @@ static int __devexit tmio_mmc_remove(struct platform_device *dev)
struct tmio_mmc_host *host = mmc_priv(mmc);
mmc_remove_host(mmc);
free_irq(host->irq, host);
if (cell->disable)
cell->disable(dev);
iounmap(host->ctl);
mmc_free_host(mmc);
}

View File

@ -55,10 +55,8 @@
/* Define some IRQ masks */
/* This is the mask used at reset by the chip */
#define TMIO_MASK_ALL 0x837f031d
#define TMIO_MASK_READOP (TMIO_STAT_RXRDY | TMIO_STAT_DATAEND | \
TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT)
#define TMIO_MASK_WRITEOP (TMIO_STAT_TXRQ | TMIO_STAT_DATAEND | \
TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT)
#define TMIO_MASK_READOP (TMIO_STAT_RXRDY | TMIO_STAT_DATAEND)
#define TMIO_MASK_WRITEOP (TMIO_STAT_TXRQ | TMIO_STAT_DATAEND)
#define TMIO_MASK_CMD (TMIO_STAT_CMDRESPEND | TMIO_STAT_CMDTIMEOUT | \
TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT)
#define TMIO_MASK_IRQ (TMIO_MASK_READOP | TMIO_MASK_WRITEOP | TMIO_MASK_CMD)

View File

@ -29,6 +29,13 @@ config APM_POWER
Say Y here to enable support APM status emulation using
battery class devices.
config MAX8925_POWER
tristate "MAX8925 battery charger support"
depends on MFD_MAX8925
help
Say Y here to enable support for the battery charger in the Maxim
MAX8925 PMIC.
config WM831X_BACKUP
tristate "WM831X backup battery charger support"
depends on MFD_WM831X

View File

@ -16,6 +16,7 @@ obj-$(CONFIG_POWER_SUPPLY) += power_supply.o
obj-$(CONFIG_PDA_POWER) += pda_power.o
obj-$(CONFIG_APM_POWER) += apm_power.o
obj-$(CONFIG_MAX8925_POWER) += max8925_power.o
obj-$(CONFIG_WM831X_BACKUP) += wm831x_backup.o
obj-$(CONFIG_WM831X_POWER) += wm831x_power.o
obj-$(CONFIG_WM8350_POWER) += wm8350_power.o

View File

@ -0,0 +1,534 @@
/*
* Battery driver for Maxim MAX8925
*
* Copyright (c) 2009-2010 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/mfd/max8925.h>
/* registers in GPM */
#define MAX8925_OUT5VEN 0x54
#define MAX8925_OUT3VEN 0x58
#define MAX8925_CHG_CNTL1 0x7c
/* bits definition */
#define MAX8925_CHG_STAT_VSYSLOW (1 << 0)
#define MAX8925_CHG_STAT_MODE_MASK (3 << 2)
#define MAX8925_CHG_STAT_EN_MASK (1 << 4)
#define MAX8925_CHG_MBDET (1 << 1)
#define MAX8925_CHG_AC_RANGE_MASK (3 << 6)
/* registers in ADC */
#define MAX8925_ADC_RES_CNFG1 0x06
#define MAX8925_ADC_AVG_CNFG1 0x07
#define MAX8925_ADC_ACQ_CNFG1 0x08
#define MAX8925_ADC_ACQ_CNFG2 0x09
/* 2 bytes registers in below. MSB is 1st, LSB is 2nd. */
#define MAX8925_ADC_AUX2 0x62
#define MAX8925_ADC_VCHG 0x64
#define MAX8925_ADC_VBBATT 0x66
#define MAX8925_ADC_VMBATT 0x68
#define MAX8925_ADC_ISNS 0x6a
#define MAX8925_ADC_THM 0x6c
#define MAX8925_ADC_TDIE 0x6e
#define MAX8925_CMD_AUX2 0xc8
#define MAX8925_CMD_VCHG 0xd0
#define MAX8925_CMD_VBBATT 0xd8
#define MAX8925_CMD_VMBATT 0xe0
#define MAX8925_CMD_ISNS 0xe8
#define MAX8925_CMD_THM 0xf0
#define MAX8925_CMD_TDIE 0xf8
enum {
MEASURE_AUX2,
MEASURE_VCHG,
MEASURE_VBBATT,
MEASURE_VMBATT,
MEASURE_ISNS,
MEASURE_THM,
MEASURE_TDIE,
MEASURE_MAX,
};
struct max8925_power_info {
struct max8925_chip *chip;
struct i2c_client *gpm;
struct i2c_client *adc;
struct power_supply ac;
struct power_supply usb;
struct power_supply battery;
int irq_base;
unsigned ac_online:1;
unsigned usb_online:1;
unsigned bat_online:1;
unsigned chg_mode:2;
unsigned batt_detect:1; /* detecing MB by ID pin */
unsigned topoff_threshold:2;
unsigned fast_charge:3;
int (*set_charger) (int);
};
static int __set_charger(struct max8925_power_info *info, int enable)
{
struct max8925_chip *chip = info->chip;
if (enable) {
/* enable charger in platform */
if (info->set_charger)
info->set_charger(1);
/* enable charger */
max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 1 << 7, 0);
} else {
/* disable charge */
max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 1 << 7, 1 << 7);
if (info->set_charger)
info->set_charger(0);
}
dev_dbg(chip->dev, "%s\n", (enable) ? "Enable charger"
: "Disable charger");
return 0;
}
static irqreturn_t max8925_charger_handler(int irq, void *data)
{
struct max8925_power_info *info = (struct max8925_power_info *)data;
struct max8925_chip *chip = info->chip;
switch (irq - chip->irq_base) {
case MAX8925_IRQ_VCHG_DC_R:
info->ac_online = 1;
__set_charger(info, 1);
dev_dbg(chip->dev, "Adapter inserted\n");
break;
case MAX8925_IRQ_VCHG_DC_F:
info->ac_online = 0;
__set_charger(info, 0);
dev_dbg(chip->dev, "Adapter is removal\n");
break;
case MAX8925_IRQ_VCHG_USB_R:
info->usb_online = 1;
__set_charger(info, 1);
dev_dbg(chip->dev, "USB inserted\n");
break;
case MAX8925_IRQ_VCHG_USB_F:
info->usb_online = 0;
__set_charger(info, 0);
dev_dbg(chip->dev, "USB is removal\n");
break;
case MAX8925_IRQ_VCHG_THM_OK_F:
/* Battery is not ready yet */
dev_dbg(chip->dev, "Battery temperature is out of range\n");
case MAX8925_IRQ_VCHG_DC_OVP:
dev_dbg(chip->dev, "Error detection\n");
__set_charger(info, 0);
break;
case MAX8925_IRQ_VCHG_THM_OK_R:
/* Battery is ready now */
dev_dbg(chip->dev, "Battery temperature is in range\n");
break;
case MAX8925_IRQ_VCHG_SYSLOW_R:
/* VSYS is low */
dev_info(chip->dev, "Sys power is too low\n");
break;
case MAX8925_IRQ_VCHG_SYSLOW_F:
dev_dbg(chip->dev, "Sys power is above low threshold\n");
break;
case MAX8925_IRQ_VCHG_DONE:
__set_charger(info, 0);
dev_dbg(chip->dev, "Charging is done\n");
break;
case MAX8925_IRQ_VCHG_TOPOFF:
dev_dbg(chip->dev, "Charging in top-off mode\n");
break;
case MAX8925_IRQ_VCHG_TMR_FAULT:
__set_charger(info, 0);
dev_dbg(chip->dev, "Safe timer is expired\n");
break;
case MAX8925_IRQ_VCHG_RST:
__set_charger(info, 0);
dev_dbg(chip->dev, "Charger is reset\n");
break;
}
return IRQ_HANDLED;
}
static int start_measure(struct max8925_power_info *info, int type)
{
unsigned char buf[2] = {0, 0};
int meas_reg = 0, ret;
switch (type) {
case MEASURE_VCHG:
meas_reg = MAX8925_ADC_VCHG;
break;
case MEASURE_VBBATT:
meas_reg = MAX8925_ADC_VBBATT;
break;
case MEASURE_VMBATT:
meas_reg = MAX8925_ADC_VMBATT;
break;
case MEASURE_ISNS:
meas_reg = MAX8925_ADC_ISNS;
break;
default:
return -EINVAL;
}
max8925_bulk_read(info->adc, meas_reg, 2, buf);
ret = (buf[0] << 4) | (buf[1] >> 4);
return ret;
}
static int max8925_ac_get_prop(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct max8925_power_info *info = dev_get_drvdata(psy->dev->parent);
int ret = 0;
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
val->intval = info->ac_online;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
if (info->ac_online) {
ret = start_measure(info, MEASURE_VCHG);
if (ret >= 0) {
val->intval = ret << 1; /* unit is mV */
goto out;
}
}
ret = -ENODATA;
break;
default:
ret = -ENODEV;
break;
}
out:
return ret;
}
static enum power_supply_property max8925_ac_props[] = {
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
};
static int max8925_usb_get_prop(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct max8925_power_info *info = dev_get_drvdata(psy->dev->parent);
int ret = 0;
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
val->intval = info->usb_online;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
if (info->usb_online) {
ret = start_measure(info, MEASURE_VCHG);
if (ret >= 0) {
val->intval = ret << 1; /* unit is mV */
goto out;
}
}
ret = -ENODATA;
break;
default:
ret = -ENODEV;
break;
}
out:
return ret;
}
static enum power_supply_property max8925_usb_props[] = {
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
};
static int max8925_bat_get_prop(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct max8925_power_info *info = dev_get_drvdata(psy->dev->parent);
long long int tmp = 0;
int ret = 0;
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
val->intval = info->bat_online;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
if (info->bat_online) {
ret = start_measure(info, MEASURE_VMBATT);
if (ret >= 0) {
val->intval = ret << 1; /* unit is mV */
ret = 0;
break;
}
}
ret = -ENODATA;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
if (info->bat_online) {
ret = start_measure(info, MEASURE_ISNS);
if (ret >= 0) {
tmp = (long long int)ret * 6250 / 4096 - 3125;
ret = (int)tmp;
val->intval = 0;
if (ret > 0)
val->intval = ret; /* unit is mA */
ret = 0;
break;
}
}
ret = -ENODATA;
break;
case POWER_SUPPLY_PROP_CHARGE_TYPE:
if (!info->bat_online) {
ret = -ENODATA;
break;
}
ret = max8925_reg_read(info->gpm, MAX8925_CHG_STATUS);
ret = (ret & MAX8925_CHG_STAT_MODE_MASK) >> 2;
switch (ret) {
case 1:
val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
break;
case 0:
case 2:
val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
break;
case 3:
val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
break;
}
ret = 0;
break;
case POWER_SUPPLY_PROP_STATUS:
if (!info->bat_online) {
ret = -ENODATA;
break;
}
ret = max8925_reg_read(info->gpm, MAX8925_CHG_STATUS);
if (info->usb_online || info->ac_online) {
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
if (ret & MAX8925_CHG_STAT_EN_MASK)
val->intval = POWER_SUPPLY_STATUS_CHARGING;
} else
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
ret = 0;
break;
default:
ret = -ENODEV;
break;
}
return ret;
}
static enum power_supply_property max8925_battery_props[] = {
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_STATUS,
};
#define REQUEST_IRQ(_irq, _name) \
do { \
ret = request_threaded_irq(chip->irq_base + _irq, NULL, \
max8925_charger_handler, \
IRQF_ONESHOT, _name, info); \
if (ret) \
dev_err(chip->dev, "Failed to request IRQ #%d: %d\n", \
_irq, ret); \
} while (0)
static __devinit int max8925_init_charger(struct max8925_chip *chip,
struct max8925_power_info *info)
{
int ret;
REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_OVP, "ac-ovp");
REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_F, "ac-remove");
REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_R, "ac-insert");
REQUEST_IRQ(MAX8925_IRQ_VCHG_USB_OVP, "usb-ovp");
REQUEST_IRQ(MAX8925_IRQ_VCHG_USB_F, "usb-remove");
REQUEST_IRQ(MAX8925_IRQ_VCHG_USB_R, "usb-insert");
REQUEST_IRQ(MAX8925_IRQ_VCHG_THM_OK_R, "batt-temp-in-range");
REQUEST_IRQ(MAX8925_IRQ_VCHG_THM_OK_F, "batt-temp-out-range");
REQUEST_IRQ(MAX8925_IRQ_VCHG_SYSLOW_F, "vsys-high");
REQUEST_IRQ(MAX8925_IRQ_VCHG_SYSLOW_R, "vsys-low");
REQUEST_IRQ(MAX8925_IRQ_VCHG_RST, "charger-reset");
REQUEST_IRQ(MAX8925_IRQ_VCHG_DONE, "charger-done");
REQUEST_IRQ(MAX8925_IRQ_VCHG_TOPOFF, "charger-topoff");
REQUEST_IRQ(MAX8925_IRQ_VCHG_TMR_FAULT, "charger-timer-expire");
info->ac_online = 0;
info->usb_online = 0;
info->bat_online = 0;
ret = max8925_reg_read(info->gpm, MAX8925_CHG_STATUS);
if (ret >= 0) {
/*
* If battery detection is enabled, ID pin of battery is
* connected to MBDET pin of MAX8925. It could be used to
* detect battery presence.
* Otherwise, we have to assume that battery is always on.
*/
if (info->batt_detect)
info->bat_online = (ret & MAX8925_CHG_MBDET) ? 0 : 1;
else
info->bat_online = 1;
if (ret & MAX8925_CHG_AC_RANGE_MASK)
info->ac_online = 1;
else
info->ac_online = 0;
}
/* disable charge */
max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 1 << 7, 1 << 7);
/* set charging current in charge topoff mode */
max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 3 << 5,
info->topoff_threshold << 5);
/* set charing current in fast charge mode */
max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 7, info->fast_charge);
return 0;
}
static __devexit int max8925_deinit_charger(struct max8925_power_info *info)
{
struct max8925_chip *chip = info->chip;
int irq;
irq = chip->irq_base + MAX8925_IRQ_VCHG_DC_OVP;
for (; irq <= chip->irq_base + MAX8925_IRQ_VCHG_TMR_FAULT; irq++)
free_irq(irq, info);
return 0;
}
static __devinit int max8925_power_probe(struct platform_device *pdev)
{
struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct max8925_platform_data *max8925_pdata;
struct max8925_power_pdata *pdata = NULL;
struct max8925_power_info *info;
int ret;
if (pdev->dev.parent->platform_data) {
max8925_pdata = pdev->dev.parent->platform_data;
pdata = max8925_pdata->power;
}
if (!pdata) {
dev_err(&pdev->dev, "platform data isn't assigned to "
"power supply\n");
return -EINVAL;
}
info = kzalloc(sizeof(struct max8925_power_info), GFP_KERNEL);
if (!info)
return -ENOMEM;
info->chip = chip;
info->gpm = chip->i2c;
info->adc = chip->adc;
info->ac.name = "max8925-ac";
info->ac.type = POWER_SUPPLY_TYPE_MAINS;
info->ac.properties = max8925_ac_props;
info->ac.num_properties = ARRAY_SIZE(max8925_ac_props);
info->ac.get_property = max8925_ac_get_prop;
ret = power_supply_register(&pdev->dev, &info->ac);
if (ret)
goto out;
info->ac.dev->parent = &pdev->dev;
info->usb.name = "max8925-usb";
info->usb.type = POWER_SUPPLY_TYPE_USB;
info->usb.properties = max8925_usb_props;
info->usb.num_properties = ARRAY_SIZE(max8925_usb_props);
info->usb.get_property = max8925_usb_get_prop;
ret = power_supply_register(&pdev->dev, &info->usb);
if (ret)
goto out_usb;
info->usb.dev->parent = &pdev->dev;
info->battery.name = "max8925-battery";
info->battery.type = POWER_SUPPLY_TYPE_BATTERY;
info->battery.properties = max8925_battery_props;
info->battery.num_properties = ARRAY_SIZE(max8925_battery_props);
info->battery.get_property = max8925_bat_get_prop;
ret = power_supply_register(&pdev->dev, &info->battery);
if (ret)
goto out_battery;
info->battery.dev->parent = &pdev->dev;
info->batt_detect = pdata->batt_detect;
info->topoff_threshold = pdata->topoff_threshold;
info->fast_charge = pdata->fast_charge;
info->set_charger = pdata->set_charger;
dev_set_drvdata(&pdev->dev, info);
platform_set_drvdata(pdev, info);
max8925_init_charger(chip, info);
return 0;
out_battery:
power_supply_unregister(&info->battery);
out_usb:
power_supply_unregister(&info->ac);
out:
kfree(info);
return ret;
}
static __devexit int max8925_power_remove(struct platform_device *pdev)
{
struct max8925_power_info *info = platform_get_drvdata(pdev);
if (info) {
power_supply_unregister(&info->ac);
power_supply_unregister(&info->usb);
power_supply_unregister(&info->battery);
max8925_deinit_charger(info);
kfree(info);
}
return 0;
}
static struct platform_driver max8925_power_driver = {
.probe = max8925_power_probe,
.remove = __devexit_p(max8925_power_remove),
.driver = {
.name = "max8925-power",
},
};
static int __init max8925_power_init(void)
{
return platform_driver_register(&max8925_power_driver);
}
module_init(max8925_power_init);
static void __exit max8925_power_exit(void)
{
platform_driver_unregister(&max8925_power_driver);
}
module_exit(max8925_power_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Power supply driver for MAX8925");
MODULE_ALIAS("platform:max8925-power");

View File

@ -190,7 +190,7 @@ static irqreturn_t wm8350_charger_handler(int irq, void *data)
struct wm8350_power *power = &wm8350->power;
struct wm8350_charger_policy *policy = power->policy;
switch (irq) {
switch (irq - wm8350->irq_base) {
case WM8350_IRQ_CHG_BAT_FAIL:
dev_err(wm8350->dev, "battery failed\n");
break;
@ -428,18 +428,18 @@ static void wm8350_init_charger(struct wm8350 *wm8350)
static void free_charger_irq(struct wm8350 *wm8350)
{
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_TO);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_END);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_START);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85);
wm8350_free_irq(wm8350, WM8350_IRQ_EXT_USB_FB);
wm8350_free_irq(wm8350, WM8350_IRQ_EXT_WALL_FB);
wm8350_free_irq(wm8350, WM8350_IRQ_EXT_BAT_FB);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT, wm8350);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD, wm8350);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL, wm8350);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_TO, wm8350);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_END, wm8350);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_START, wm8350);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9, wm8350);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1, wm8350);
wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85, wm8350);
wm8350_free_irq(wm8350, WM8350_IRQ_EXT_USB_FB, wm8350);
wm8350_free_irq(wm8350, WM8350_IRQ_EXT_WALL_FB, wm8350);
wm8350_free_irq(wm8350, WM8350_IRQ_EXT_BAT_FB, wm8350);
}
static __devinit int wm8350_power_probe(struct platform_device *pdev)

View File

@ -11,15 +11,17 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/mfd/88pm8607.h>
#include <linux/mfd/88pm860x.h>
struct pm8607_regulator_info {
struct regulator_desc desc;
struct pm8607_chip *chip;
struct pm860x_chip *chip;
struct regulator_dev *regulator;
struct i2c_client *i2c;
int min_uV;
int max_uV;
@ -46,7 +48,6 @@ static inline int check_range(struct pm8607_regulator_info *info,
static int pm8607_list_voltage(struct regulator_dev *rdev, unsigned index)
{
struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
uint8_t chip_id = info->chip->chip_id;
int ret = -EINVAL;
switch (info->desc.id) {
@ -88,79 +89,29 @@ static int pm8607_list_voltage(struct regulator_dev *rdev, unsigned index)
case PM8607_ID_LDO2:
case PM8607_ID_LDO3:
case PM8607_ID_LDO9:
switch (chip_id) {
case PM8607_CHIP_A0:
case PM8607_CHIP_A1:
ret = (index < 3) ? (index * 50000 + 1800000) :
((index < 8) ? (index * 50000 + 2550000) :
-EINVAL);
break;
case PM8607_CHIP_B0:
ret = (index < 3) ? (index * 50000 + 1800000) :
((index < 7) ? (index * 50000 + 2550000) :
3300000);
break;
}
ret = (index < 3) ? (index * 50000 + 1800000) :
((index < 7) ? (index * 50000 + 2550000) :
3300000);
break;
case PM8607_ID_LDO4:
switch (chip_id) {
case PM8607_CHIP_A0:
case PM8607_CHIP_A1:
ret = (index < 3) ? (index * 50000 + 1800000) :
((index < 8) ? (index * 50000 + 2550000) :
-EINVAL);
break;
case PM8607_CHIP_B0:
ret = (index < 3) ? (index * 50000 + 1800000) :
((index < 6) ? (index * 50000 + 2550000) :
((index == 6) ? 2900000 : 3300000));
break;
}
ret = (index < 3) ? (index * 50000 + 1800000) :
((index < 6) ? (index * 50000 + 2550000) :
((index == 6) ? 2900000 : 3300000));
break;
case PM8607_ID_LDO6:
switch (chip_id) {
case PM8607_CHIP_A0:
case PM8607_CHIP_A1:
ret = (index < 3) ? (index * 50000 + 1800000) :
((index < 8) ? (index * 50000 + 2450000) :
-EINVAL);
break;
case PM8607_CHIP_B0:
ret = (index < 2) ? (index * 50000 + 1800000) :
((index < 7) ? (index * 50000 + 2500000) :
3300000);
break;
}
ret = (index < 2) ? (index * 50000 + 1800000) :
((index < 7) ? (index * 50000 + 2500000) :
3300000);
break;
case PM8607_ID_LDO10:
switch (chip_id) {
case PM8607_CHIP_A0:
case PM8607_CHIP_A1:
ret = (index < 3) ? (index * 50000 + 1800000) :
((index < 8) ? (index * 50000 + 2550000) :
1200000);
break;
case PM8607_CHIP_B0:
ret = (index < 3) ? (index * 50000 + 1800000) :
((index < 7) ? (index * 50000 + 2550000) :
((index == 7) ? 3300000 : 1200000));
break;
}
ret = (index < 3) ? (index * 50000 + 1800000) :
((index < 7) ? (index * 50000 + 2550000) :
((index == 7) ? 3300000 : 1200000));
break;
case PM8607_ID_LDO14:
switch (chip_id) {
case PM8607_CHIP_A0:
case PM8607_CHIP_A1:
ret = (index < 3) ? (index * 50000 + 1800000) :
((index < 8) ? (index * 50000 + 2550000) :
-EINVAL);
break;
case PM8607_CHIP_B0:
ret = (index < 2) ? (index * 50000 + 1800000) :
((index < 7) ? (index * 50000 + 2600000) :
3300000);
break;
}
ret = (index < 2) ? (index * 50000 + 1800000) :
((index < 7) ? (index * 50000 + 2600000) :
3300000);
break;
}
return ret;
@ -169,7 +120,6 @@ static int pm8607_list_voltage(struct regulator_dev *rdev, unsigned index)
static int choose_voltage(struct regulator_dev *rdev, int min_uV, int max_uV)
{
struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
uint8_t chip_id = info->chip->chip_id;
int val = -ENOENT;
int ret;
@ -254,161 +204,77 @@ static int choose_voltage(struct regulator_dev *rdev, int min_uV, int max_uV)
case PM8607_ID_LDO2:
case PM8607_ID_LDO3:
case PM8607_ID_LDO9:
switch (chip_id) {
case PM8607_CHIP_A0:
case PM8607_CHIP_A1:
if (min_uV < 2700000) /* 1800mV ~ 1900mV / 50mV */
if (min_uV <= 1800000)
val = 0;
else if (min_uV <= 1900000)
val = (min_uV - 1750001) / 50000;
else
val = 3; /* 2700mV */
else { /* 2700mV ~ 2900mV / 50mV */
if (min_uV <= 2900000) {
val = (min_uV - 2650001) / 50000;
val += 3;
} else
val = -EINVAL;
}
break;
case PM8607_CHIP_B0:
if (min_uV < 2700000) { /* 1800mV ~ 1900mV / 50mV */
if (min_uV <= 1800000)
val = 0;
else if (min_uV <= 1900000)
val = (min_uV - 1750001) / 50000;
else
val = 3; /* 2700mV */
} else { /* 2700mV ~ 2850mV / 50mV */
if (min_uV <= 2850000) {
val = (min_uV - 2650001) / 50000;
val += 3;
} else if (min_uV <= 3300000)
val = 7;
else
val = -EINVAL;
}
break;
if (min_uV < 2700000) { /* 1800mV ~ 1900mV / 50mV */
if (min_uV <= 1800000)
val = 0;
else if (min_uV <= 1900000)
val = (min_uV - 1750001) / 50000;
else
val = 3; /* 2700mV */
} else { /* 2700mV ~ 2850mV / 50mV */
if (min_uV <= 2850000) {
val = (min_uV - 2650001) / 50000;
val += 3;
} else if (min_uV <= 3300000)
val = 7;
else
val = -EINVAL;
}
break;
case PM8607_ID_LDO4:
switch (chip_id) {
case PM8607_CHIP_A0:
case PM8607_CHIP_A1:
if (min_uV < 2700000) /* 1800mV ~ 1900mV / 50mV */
if (min_uV <= 1800000)
val = 0;
else if (min_uV <= 1900000)
val = (min_uV - 1750001) / 50000;
else
val = 3; /* 2700mV */
else { /* 2700mV ~ 2900mV / 50mV */
if (min_uV <= 2900000) {
val = (min_uV - 2650001) / 50000;
val += 3;
} else
val = -EINVAL;
}
break;
case PM8607_CHIP_B0:
if (min_uV < 2700000) { /* 1800mV ~ 1900mV / 50mV */
if (min_uV <= 1800000)
val = 0;
else if (min_uV <= 1900000)
val = (min_uV - 1750001) / 50000;
else
val = 3; /* 2700mV */
} else { /* 2700mV ~ 2800mV / 50mV */
if (min_uV <= 2850000) {
val = (min_uV - 2650001) / 50000;
val += 3;
} else if (min_uV <= 2900000)
val = 6;
else if (min_uV <= 3300000)
val = 7;
else
val = -EINVAL;
}
break;
if (min_uV < 2700000) { /* 1800mV ~ 1900mV / 50mV */
if (min_uV <= 1800000)
val = 0;
else if (min_uV <= 1900000)
val = (min_uV - 1750001) / 50000;
else
val = 3; /* 2700mV */
} else { /* 2700mV ~ 2800mV / 50mV */
if (min_uV <= 2850000) {
val = (min_uV - 2650001) / 50000;
val += 3;
} else if (min_uV <= 2900000)
val = 6;
else if (min_uV <= 3300000)
val = 7;
else
val = -EINVAL;
}
break;
case PM8607_ID_LDO6:
switch (chip_id) {
case PM8607_CHIP_A0:
case PM8607_CHIP_A1:
if (min_uV < 2600000) { /* 1800mV ~ 1900mV / 50mV */
if (min_uV <= 1800000)
val = 0;
else if (min_uV <= 1900000)
val = (min_uV - 1750001) / 50000;
else
val = 3; /* 2600mV */
} else { /* 2600mV ~ 2800mV / 50mV */
if (min_uV <= 2800000) {
val = (min_uV - 2550001) / 50000;
val += 3;
} else
val = -EINVAL;
}
break;
case PM8607_CHIP_B0:
if (min_uV < 2600000) { /* 1800mV ~ 1850mV / 50mV */
if (min_uV <= 1800000)
val = 0;
else if (min_uV <= 1850000)
val = (min_uV - 1750001) / 50000;
else
val = 2; /* 2600mV */
} else { /* 2600mV ~ 2800mV / 50mV */
if (min_uV <= 2800000) {
val = (min_uV - 2550001) / 50000;
val += 2;
} else if (min_uV <= 3300000)
val = 7;
else
val = -EINVAL;
}
break;
if (min_uV < 2600000) { /* 1800mV ~ 1850mV / 50mV */
if (min_uV <= 1800000)
val = 0;
else if (min_uV <= 1850000)
val = (min_uV - 1750001) / 50000;
else
val = 2; /* 2600mV */
} else { /* 2600mV ~ 2800mV / 50mV */
if (min_uV <= 2800000) {
val = (min_uV - 2550001) / 50000;
val += 2;
} else if (min_uV <= 3300000)
val = 7;
else
val = -EINVAL;
}
break;
case PM8607_ID_LDO14:
switch (chip_id) {
case PM8607_CHIP_A0:
case PM8607_CHIP_A1:
if (min_uV < 2700000) { /* 1800mV ~ 1900mV / 50mV */
if (min_uV <= 1800000)
val = 0;
else if (min_uV <= 1900000)
val = (min_uV - 1750001) / 50000;
else
val = 3; /* 2700mV */
} else { /* 2700mV ~ 2900mV / 50mV */
if (min_uV <= 2900000) {
val = (min_uV - 2650001) / 50000;
val += 3;
} else
val = -EINVAL;
}
break;
case PM8607_CHIP_B0:
if (min_uV < 2700000) { /* 1800mV ~ 1850mV / 50mV */
if (min_uV <= 1800000)
val = 0;
else if (min_uV <= 1850000)
val = (min_uV - 1750001) / 50000;
else
val = 2; /* 2700mV */
} else { /* 2700mV ~ 2900mV / 50mV */
if (min_uV <= 2900000) {
val = (min_uV - 2650001) / 50000;
val += 2;
} else if (min_uV <= 3300000)
val = 7;
else
val = -EINVAL;
}
break;
if (min_uV < 2700000) { /* 1800mV ~ 1850mV / 50mV */
if (min_uV <= 1800000)
val = 0;
else if (min_uV <= 1850000)
val = (min_uV - 1750001) / 50000;
else
val = 2; /* 2700mV */
} else { /* 2700mV ~ 2900mV / 50mV */
if (min_uV <= 2900000) {
val = (min_uV - 2650001) / 50000;
val += 2;
} else if (min_uV <= 3300000)
val = 7;
else
val = -EINVAL;
}
break;
}
@ -428,7 +294,6 @@ static int pm8607_set_voltage(struct regulator_dev *rdev,
int min_uV, int max_uV)
{
struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
struct pm8607_chip *chip = info->chip;
uint8_t val, mask;
int ret;
@ -443,13 +308,13 @@ static int pm8607_set_voltage(struct regulator_dev *rdev,
val = (uint8_t)(ret << info->vol_shift);
mask = ((1 << info->vol_nbits) - 1) << info->vol_shift;
ret = pm8607_set_bits(chip, info->vol_reg, mask, val);
ret = pm860x_set_bits(info->i2c, info->vol_reg, mask, val);
if (ret)
return ret;
switch (info->desc.id) {
case PM8607_ID_BUCK1:
case PM8607_ID_BUCK3:
ret = pm8607_set_bits(chip, info->update_reg,
ret = pm860x_set_bits(info->i2c, info->update_reg,
1 << info->update_bit,
1 << info->update_bit);
break;
@ -460,11 +325,10 @@ static int pm8607_set_voltage(struct regulator_dev *rdev,
static int pm8607_get_voltage(struct regulator_dev *rdev)
{
struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
struct pm8607_chip *chip = info->chip;
uint8_t val, mask;
int ret;
ret = pm8607_reg_read(chip, info->vol_reg);
ret = pm860x_reg_read(info->i2c, info->vol_reg);
if (ret < 0)
return ret;
@ -477,9 +341,8 @@ static int pm8607_get_voltage(struct regulator_dev *rdev)
static int pm8607_enable(struct regulator_dev *rdev)
{
struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
struct pm8607_chip *chip = info->chip;
return pm8607_set_bits(chip, info->enable_reg,
return pm860x_set_bits(info->i2c, info->enable_reg,
1 << info->enable_bit,
1 << info->enable_bit);
}
@ -487,19 +350,17 @@ static int pm8607_enable(struct regulator_dev *rdev)
static int pm8607_disable(struct regulator_dev *rdev)
{
struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
struct pm8607_chip *chip = info->chip;
return pm8607_set_bits(chip, info->enable_reg,
return pm860x_set_bits(info->i2c, info->enable_reg,
1 << info->enable_bit, 0);
}
static int pm8607_is_enabled(struct regulator_dev *rdev)
{
struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
struct pm8607_chip *chip = info->chip;
int ret;
ret = pm8607_reg_read(chip, info->enable_reg);
ret = pm860x_reg_read(info->i2c, info->enable_reg);
if (ret < 0)
return ret;
@ -589,8 +450,8 @@ static inline struct pm8607_regulator_info *find_regulator_info(int id)
static int __devinit pm8607_regulator_probe(struct platform_device *pdev)
{
struct pm8607_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct pm8607_platform_data *pdata = chip->dev->platform_data;
struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct pm860x_platform_data *pdata = chip->dev->platform_data;
struct pm8607_regulator_info *info = NULL;
info = find_regulator_info(pdev->id);
@ -599,6 +460,7 @@ static int __devinit pm8607_regulator_probe(struct platform_device *pdev)
return -EINVAL;
}
info->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
info->chip = chip;
info->regulator = regulator_register(&info->desc, &pdev->dev,

View File

@ -94,6 +94,12 @@ config REGULATOR_MAX8660
This driver controls a Maxim 8660/8661 voltage output
regulator via I2C bus.
config REGULATOR_MAX8925
tristate "Maxim MAX8925 Power Management IC"
depends on MFD_MAX8925
help
Say y here to support the voltage regulaltor of Maxim MAX8925 PMIC.
config REGULATOR_TWL4030
bool "TI TWL4030/TWL5030/TWL6030/TPS695x0 PMIC"
depends on TWL4030_CORE
@ -191,7 +197,7 @@ config REGULATOR_TPS6507X
config REGULATOR_88PM8607
bool "Marvell 88PM8607 Power regulators"
depends on MFD_88PM8607=y
depends on MFD_88PM860X=y
help
This driver supports 88PM8607 voltage regulator chips.

View File

@ -15,6 +15,7 @@ obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o
obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o
obj-$(CONFIG_REGULATOR_MAX8649) += max8649.o
obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o
obj-$(CONFIG_REGULATOR_MAX8925) += max8925-regulator.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o

View File

@ -0,0 +1,306 @@
/*
* Regulators driver for Maxim max8925
*
* Copyright (C) 2009 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/mfd/max8925.h>
#define SD1_DVM_VMIN 850000
#define SD1_DVM_VMAX 1000000
#define SD1_DVM_STEP 50000
#define SD1_DVM_SHIFT 5 /* SDCTL1 bit5 */
#define SD1_DVM_EN 6 /* SDV1 bit 6 */
struct max8925_regulator_info {
struct regulator_desc desc;
struct regulator_dev *regulator;
struct i2c_client *i2c;
struct max8925_chip *chip;
int min_uV;
int max_uV;
int step_uV;
int vol_reg;
int vol_shift;
int vol_nbits;
int enable_bit;
int enable_reg;
};
static inline int check_range(struct max8925_regulator_info *info,
int min_uV, int max_uV)
{
if (min_uV < info->min_uV || min_uV > info->max_uV)
return -EINVAL;
return 0;
}
static int max8925_list_voltage(struct regulator_dev *rdev, unsigned index)
{
struct max8925_regulator_info *info = rdev_get_drvdata(rdev);
return info->min_uV + index * info->step_uV;
}
static int max8925_set_voltage(struct regulator_dev *rdev,
int min_uV, int max_uV)
{
struct max8925_regulator_info *info = rdev_get_drvdata(rdev);
unsigned char data, mask;
if (check_range(info, min_uV, max_uV)) {
dev_err(info->chip->dev, "invalid voltage range (%d, %d) uV\n",
min_uV, max_uV);
return -EINVAL;
}
data = (min_uV - info->min_uV + info->step_uV - 1) / info->step_uV;
data <<= info->vol_shift;
mask = ((1 << info->vol_nbits) - 1) << info->vol_shift;
return max8925_set_bits(info->i2c, info->vol_reg, mask, data);
}
static int max8925_get_voltage(struct regulator_dev *rdev)
{
struct max8925_regulator_info *info = rdev_get_drvdata(rdev);
unsigned char data, mask;
int ret;
ret = max8925_reg_read(info->i2c, info->vol_reg);
if (ret < 0)
return ret;
mask = ((1 << info->vol_nbits) - 1) << info->vol_shift;
data = (ret & mask) >> info->vol_shift;
return max8925_list_voltage(rdev, data);
}
static int max8925_enable(struct regulator_dev *rdev)
{
struct max8925_regulator_info *info = rdev_get_drvdata(rdev);
return max8925_set_bits(info->i2c, info->enable_reg,
1 << info->enable_bit,
1 << info->enable_bit);
}
static int max8925_disable(struct regulator_dev *rdev)
{
struct max8925_regulator_info *info = rdev_get_drvdata(rdev);
return max8925_set_bits(info->i2c, info->enable_reg,
1 << info->enable_bit, 0);
}
static int max8925_is_enabled(struct regulator_dev *rdev)
{
struct max8925_regulator_info *info = rdev_get_drvdata(rdev);
int ret;
ret = max8925_reg_read(info->i2c, info->vol_reg);
if (ret < 0)
return ret;
return ret & (1 << info->enable_bit);
}
static int max8925_set_dvm_voltage(struct regulator_dev *rdev, int uV)
{
struct max8925_regulator_info *info = rdev_get_drvdata(rdev);
unsigned char data, mask;
if (uV < SD1_DVM_VMIN || uV > SD1_DVM_VMAX)
return -EINVAL;
data = (uV - SD1_DVM_VMIN + SD1_DVM_STEP - 1) / SD1_DVM_STEP;
data <<= SD1_DVM_SHIFT;
mask = 3 << SD1_DVM_SHIFT;
return max8925_set_bits(info->i2c, info->enable_reg, mask, data);
}
static int max8925_set_dvm_enable(struct regulator_dev *rdev)
{
struct max8925_regulator_info *info = rdev_get_drvdata(rdev);
return max8925_set_bits(info->i2c, info->vol_reg, 1 << SD1_DVM_EN,
1 << SD1_DVM_EN);
}
static int max8925_set_dvm_disable(struct regulator_dev *rdev)
{
struct max8925_regulator_info *info = rdev_get_drvdata(rdev);
return max8925_set_bits(info->i2c, info->vol_reg, 1 << SD1_DVM_EN, 0);
}
static struct regulator_ops max8925_regulator_sdv_ops = {
.set_voltage = max8925_set_voltage,
.get_voltage = max8925_get_voltage,
.enable = max8925_enable,
.disable = max8925_disable,
.is_enabled = max8925_is_enabled,
.set_suspend_voltage = max8925_set_dvm_voltage,
.set_suspend_enable = max8925_set_dvm_enable,
.set_suspend_disable = max8925_set_dvm_disable,
};
static struct regulator_ops max8925_regulator_ldo_ops = {
.set_voltage = max8925_set_voltage,
.get_voltage = max8925_get_voltage,
.enable = max8925_enable,
.disable = max8925_disable,
.is_enabled = max8925_is_enabled,
};
#define MAX8925_SDV(_id, min, max, step) \
{ \
.desc = { \
.name = "SDV" #_id, \
.ops = &max8925_regulator_sdv_ops, \
.type = REGULATOR_VOLTAGE, \
.id = MAX8925_ID_SD##_id, \
.owner = THIS_MODULE, \
}, \
.min_uV = min * 1000, \
.max_uV = max * 1000, \
.step_uV = step * 1000, \
.vol_reg = MAX8925_SDV##_id, \
.vol_shift = 0, \
.vol_nbits = 6, \
.enable_reg = MAX8925_SDCTL##_id, \
.enable_bit = 0, \
}
#define MAX8925_LDO(_id, min, max, step) \
{ \
.desc = { \
.name = "LDO" #_id, \
.ops = &max8925_regulator_ldo_ops, \
.type = REGULATOR_VOLTAGE, \
.id = MAX8925_ID_LDO##_id, \
.owner = THIS_MODULE, \
}, \
.min_uV = min * 1000, \
.max_uV = max * 1000, \
.step_uV = step * 1000, \
.vol_reg = MAX8925_LDOVOUT##_id, \
.vol_shift = 0, \
.vol_nbits = 6, \
.enable_reg = MAX8925_LDOCTL##_id, \
.enable_bit = 0, \
}
static struct max8925_regulator_info max8925_regulator_info[] = {
MAX8925_SDV(1, 637.5, 1425, 12.5),
MAX8925_SDV(2, 650, 2225, 25),
MAX8925_SDV(3, 750, 3900, 50),
MAX8925_LDO(1, 750, 3900, 50),
MAX8925_LDO(2, 650, 2250, 25),
MAX8925_LDO(3, 650, 2250, 25),
MAX8925_LDO(4, 750, 3900, 50),
MAX8925_LDO(5, 750, 3900, 50),
MAX8925_LDO(6, 750, 3900, 50),
MAX8925_LDO(7, 750, 3900, 50),
MAX8925_LDO(8, 750, 3900, 50),
MAX8925_LDO(9, 750, 3900, 50),
MAX8925_LDO(10, 750, 3900, 50),
MAX8925_LDO(11, 750, 3900, 50),
MAX8925_LDO(12, 750, 3900, 50),
MAX8925_LDO(13, 750, 3900, 50),
MAX8925_LDO(14, 750, 3900, 50),
MAX8925_LDO(15, 750, 3900, 50),
MAX8925_LDO(16, 750, 3900, 50),
MAX8925_LDO(17, 650, 2250, 25),
MAX8925_LDO(18, 650, 2250, 25),
MAX8925_LDO(19, 750, 3900, 50),
MAX8925_LDO(20, 750, 3900, 50),
};
static inline struct max8925_regulator_info *find_regulator_info(int id)
{
struct max8925_regulator_info *ri;
int i;
for (i = 0; i < ARRAY_SIZE(max8925_regulator_info); i++) {
ri = &max8925_regulator_info[i];
if (ri->desc.id == id)
return ri;
}
return NULL;
}
static int __devinit max8925_regulator_probe(struct platform_device *pdev)
{
struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct max8925_platform_data *pdata = chip->dev->platform_data;
struct max8925_regulator_info *ri = NULL;
struct regulator_dev *rdev;
ri = find_regulator_info(pdev->id);
if (ri == NULL) {
dev_err(&pdev->dev, "invalid regulator ID specified\n");
return -EINVAL;
}
ri->i2c = chip->i2c;
ri->chip = chip;
rdev = regulator_register(&ri->desc, &pdev->dev,
pdata->regulator[pdev->id], ri);
if (IS_ERR(rdev)) {
dev_err(&pdev->dev, "failed to register regulator %s\n",
ri->desc.name);
return PTR_ERR(rdev);
}
platform_set_drvdata(pdev, rdev);
return 0;
}
static int __devexit max8925_regulator_remove(struct platform_device *pdev)
{
struct regulator_dev *rdev = platform_get_drvdata(pdev);
regulator_unregister(rdev);
return 0;
}
static struct platform_driver max8925_regulator_driver = {
.driver = {
.name = "max8925-regulator",
.owner = THIS_MODULE,
},
.probe = max8925_regulator_probe,
.remove = __devexit_p(max8925_regulator_remove),
};
static int __init max8925_regulator_init(void)
{
return platform_driver_register(&max8925_regulator_driver);
}
subsys_initcall(max8925_regulator_init);
static void __exit max8925_regulator_exit(void)
{
platform_driver_unregister(&max8925_regulator_driver);
}
module_exit(max8925_regulator_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
MODULE_DESCRIPTION("Regulator Driver for Maxim 8925 PMIC");
MODULE_ALIAS("platform:max8925-regulator");

View File

@ -1453,7 +1453,7 @@ static int wm8350_regulator_remove(struct platform_device *pdev)
struct regulator_dev *rdev = platform_get_drvdata(pdev);
struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
wm8350_free_irq(wm8350, wm8350_reg[pdev->id].irq);
wm8350_free_irq(wm8350, wm8350_reg[pdev->id].irq, rdev);
regulator_unregister(rdev);

View File

@ -175,6 +175,16 @@ config RTC_DRV_MAX6900
This driver can also be built as a module. If so, the module
will be called rtc-max6900.
config RTC_DRV_MAX8925
tristate "Maxim MAX8925"
depends on MFD_MAX8925
help
If you say yes here you will get support for the
RTC of Maxim MAX8925 PMIC.
This driver can also be built as a module. If so, the module
will be called rtc-max8925.
config RTC_DRV_RS5C372
tristate "Ricoh R2025S/D, RS5C372A/B, RV5C386, RV5C387A"
help

View File

@ -52,6 +52,7 @@ obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o
obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o
obj-$(CONFIG_RTC_MXC) += rtc-mxc.o
obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o
obj-$(CONFIG_RTC_DRV_MAX8925) += rtc-max8925.o
obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
obj-$(CONFIG_RTC_DRV_MC13783) += rtc-mc13783.o
obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o

314
drivers/rtc/rtc-max8925.c Normal file
View File

@ -0,0 +1,314 @@
/*
* RTC driver for Maxim MAX8925
*
* Copyright (C) 2009-2010 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/rtc.h>
#include <linux/platform_device.h>
#include <linux/mfd/max8925.h>
enum {
RTC_SEC = 0,
RTC_MIN,
RTC_HOUR,
RTC_WEEKDAY,
RTC_DATE,
RTC_MONTH,
RTC_YEAR1,
RTC_YEAR2,
};
#define MAX8925_RTC_SEC 0x00
#define MAX8925_RTC_MIN 0x01
#define MAX8925_RTC_HOUR 0x02
#define MAX8925_RTC_WEEKDAY 0x03
#define MAX8925_RTC_DATE 0x04
#define MAX8925_RTC_MONTH 0x05
#define MAX8925_RTC_YEAR1 0x06
#define MAX8925_RTC_YEAR2 0x07
#define MAX8925_ALARM0_SEC 0x08
#define MAX8925_ALARM0_MIN 0x09
#define MAX8925_ALARM0_HOUR 0x0a
#define MAX8925_ALARM0_WEEKDAY 0x0b
#define MAX8925_ALARM0_DATE 0x0c
#define MAX8925_ALARM0_MON 0x0d
#define MAX8925_ALARM0_YEAR1 0x0e
#define MAX8925_ALARM0_YEAR2 0x0f
#define MAX8925_ALARM1_SEC 0x10
#define MAX8925_ALARM1_MIN 0x11
#define MAX8925_ALARM1_HOUR 0x12
#define MAX8925_ALARM1_WEEKDAY 0x13
#define MAX8925_ALARM1_DATE 0x14
#define MAX8925_ALARM1_MON 0x15
#define MAX8925_ALARM1_YEAR1 0x16
#define MAX8925_ALARM1_YEAR2 0x17
#define MAX8925_RTC_CNTL 0x1b
#define MAX8925_RTC_STATUS 0x20
#define TIME_NUM 8
#define ALARM_1SEC (1 << 7)
#define HOUR_12 (1 << 7)
#define HOUR_AM_PM (1 << 5)
#define ALARM0_IRQ (1 << 3)
#define ALARM1_IRQ (1 << 2)
#define ALARM0_STATUS (1 << 2)
#define ALARM1_STATUS (1 << 1)
struct max8925_rtc_info {
struct rtc_device *rtc_dev;
struct max8925_chip *chip;
struct i2c_client *rtc;
struct device *dev;
};
static irqreturn_t rtc_update_handler(int irq, void *data)
{
struct max8925_rtc_info *info = (struct max8925_rtc_info *)data;
/* disable ALARM0 except for 1SEC alarm */
max8925_set_bits(info->rtc, MAX8925_ALARM0_CNTL, 0x7f, 0);
rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
return IRQ_HANDLED;
}
static int tm_calc(struct rtc_time *tm, unsigned char *buf, int len)
{
if (len < TIME_NUM)
return -EINVAL;
tm->tm_year = (buf[RTC_YEAR2] >> 4) * 1000
+ (buf[RTC_YEAR2] & 0xf) * 100
+ (buf[RTC_YEAR1] >> 4) * 10
+ (buf[RTC_YEAR1] & 0xf);
tm->tm_year -= 1900;
tm->tm_mon = ((buf[RTC_MONTH] >> 4) & 0x01) * 10
+ (buf[RTC_MONTH] & 0x0f);
tm->tm_mday = ((buf[RTC_DATE] >> 4) & 0x03) * 10
+ (buf[RTC_DATE] & 0x0f);
tm->tm_wday = buf[RTC_WEEKDAY] & 0x07;
if (buf[RTC_HOUR] & HOUR_12) {
tm->tm_hour = ((buf[RTC_HOUR] >> 4) & 0x1) * 10
+ (buf[RTC_HOUR] & 0x0f);
if (buf[RTC_HOUR] & HOUR_AM_PM)
tm->tm_hour += 12;
} else
tm->tm_hour = ((buf[RTC_HOUR] >> 4) & 0x03) * 10
+ (buf[RTC_HOUR] & 0x0f);
tm->tm_min = ((buf[RTC_MIN] >> 4) & 0x7) * 10
+ (buf[RTC_MIN] & 0x0f);
tm->tm_sec = ((buf[RTC_SEC] >> 4) & 0x7) * 10
+ (buf[RTC_SEC] & 0x0f);
return 0;
}
static int data_calc(unsigned char *buf, struct rtc_time *tm, int len)
{
unsigned char high, low;
if (len < TIME_NUM)
return -EINVAL;
high = (tm->tm_year + 1900) / 1000;
low = (tm->tm_year + 1900) / 100;
low = low - high * 10;
buf[RTC_YEAR2] = (high << 4) + low;
high = (tm->tm_year + 1900) / 10;
low = tm->tm_year + 1900;
low = low - high * 10;
high = high - (high / 10) * 10;
buf[RTC_YEAR1] = (high << 4) + low;
high = tm->tm_mon / 10;
low = tm->tm_mon;
low = low - high * 10;
buf[RTC_MONTH] = (high << 4) + low;
high = tm->tm_mday / 10;
low = tm->tm_mday;
low = low - high * 10;
buf[RTC_DATE] = (high << 4) + low;
buf[RTC_WEEKDAY] = tm->tm_wday;
high = tm->tm_hour / 10;
low = tm->tm_hour;
low = low - high * 10;
buf[RTC_HOUR] = (high << 4) + low;
high = tm->tm_min / 10;
low = tm->tm_min;
low = low - high * 10;
buf[RTC_MIN] = (high << 4) + low;
high = tm->tm_sec / 10;
low = tm->tm_sec;
low = low - high * 10;
buf[RTC_SEC] = (high << 4) + low;
return 0;
}
static int max8925_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct max8925_rtc_info *info = dev_get_drvdata(dev);
unsigned char buf[TIME_NUM];
int ret;
ret = max8925_bulk_read(info->rtc, MAX8925_RTC_SEC, TIME_NUM, buf);
if (ret < 0)
goto out;
ret = tm_calc(tm, buf, TIME_NUM);
out:
return ret;
}
static int max8925_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct max8925_rtc_info *info = dev_get_drvdata(dev);
unsigned char buf[TIME_NUM];
int ret;
ret = data_calc(buf, tm, TIME_NUM);
if (ret < 0)
goto out;
ret = max8925_bulk_write(info->rtc, MAX8925_RTC_SEC, TIME_NUM, buf);
out:
return ret;
}
static int max8925_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct max8925_rtc_info *info = dev_get_drvdata(dev);
unsigned char buf[TIME_NUM];
int ret;
ret = max8925_bulk_read(info->rtc, MAX8925_ALARM0_SEC, TIME_NUM, buf);
if (ret < 0)
goto out;
ret = tm_calc(&alrm->time, buf, TIME_NUM);
if (ret < 0)
goto out;
ret = max8925_reg_read(info->rtc, MAX8925_RTC_IRQ_MASK);
if (ret < 0)
goto out;
if ((ret & ALARM0_IRQ) == 0)
alrm->enabled = 1;
else
alrm->enabled = 0;
ret = max8925_reg_read(info->rtc, MAX8925_RTC_STATUS);
if (ret < 0)
goto out;
if (ret & ALARM0_STATUS)
alrm->pending = 1;
else
alrm->pending = 0;
out:
return ret;
}
static int max8925_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct max8925_rtc_info *info = dev_get_drvdata(dev);
unsigned char buf[TIME_NUM];
int ret;
ret = data_calc(buf, &alrm->time, TIME_NUM);
if (ret < 0)
goto out;
ret = max8925_bulk_write(info->rtc, MAX8925_ALARM0_SEC, TIME_NUM, buf);
if (ret < 0)
goto out;
/* only enable alarm on year/month/day/hour/min/sec */
ret = max8925_reg_write(info->rtc, MAX8925_ALARM0_CNTL, 0x77);
if (ret < 0)
goto out;
out:
return ret;
}
static const struct rtc_class_ops max8925_rtc_ops = {
.read_time = max8925_rtc_read_time,
.set_time = max8925_rtc_set_time,
.read_alarm = max8925_rtc_read_alarm,
.set_alarm = max8925_rtc_set_alarm,
};
static int __devinit max8925_rtc_probe(struct platform_device *pdev)
{
struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct max8925_rtc_info *info;
int irq, ret;
info = kzalloc(sizeof(struct max8925_rtc_info), GFP_KERNEL);
if (!info)
return -ENOMEM;
info->chip = chip;
info->rtc = chip->rtc;
info->dev = &pdev->dev;
irq = chip->irq_base + MAX8925_IRQ_RTC_ALARM0;
ret = request_threaded_irq(irq, NULL, rtc_update_handler,
IRQF_ONESHOT, "rtc-alarm0", info);
if (ret < 0) {
dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
irq, ret);
goto out_irq;
}
info->rtc_dev = rtc_device_register("max8925-rtc", &pdev->dev,
&max8925_rtc_ops, THIS_MODULE);
ret = PTR_ERR(info->rtc_dev);
if (IS_ERR(info->rtc_dev)) {
dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
goto out_rtc;
}
dev_set_drvdata(&pdev->dev, info);
platform_set_drvdata(pdev, info);
return 0;
out_rtc:
free_irq(chip->irq_base + MAX8925_IRQ_RTC_ALARM0, info);
out_irq:
kfree(info);
return ret;
}
static int __devexit max8925_rtc_remove(struct platform_device *pdev)
{
struct max8925_rtc_info *info = platform_get_drvdata(pdev);
if (info) {
free_irq(info->chip->irq_base + MAX8925_IRQ_RTC_ALARM0, info);
rtc_device_unregister(info->rtc_dev);
kfree(info);
}
return 0;
}
static struct platform_driver max8925_rtc_driver = {
.driver = {
.name = "max8925-rtc",
.owner = THIS_MODULE,
},
.probe = max8925_rtc_probe,
.remove = __devexit_p(max8925_rtc_remove),
};
static int __init max8925_rtc_init(void)
{
return platform_driver_register(&max8925_rtc_driver);
}
module_init(max8925_rtc_init);
static void __exit max8925_rtc_exit(void)
{
platform_driver_unregister(&max8925_rtc_driver);
}
module_exit(max8925_rtc_exit);
MODULE_DESCRIPTION("Maxim MAX8925 RTC driver");
MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
MODULE_LICENSE("GPL");

View File

@ -307,11 +307,18 @@ static int wm8350_rtc_update_irq_enable(struct device *dev,
{
struct wm8350 *wm8350 = dev_get_drvdata(dev);
/* Suppress duplicate changes since genirq nests enable and
* disable calls. */
if (enabled == wm8350->rtc.update_enabled)
return 0;
if (enabled)
wm8350_unmask_irq(wm8350, WM8350_IRQ_RTC_SEC);
else
wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_SEC);
wm8350->rtc.update_enabled = enabled;
return 0;
}
@ -478,8 +485,8 @@ static int __devexit wm8350_rtc_remove(struct platform_device *pdev)
struct wm8350 *wm8350 = platform_get_drvdata(pdev);
struct wm8350_rtc *wm_rtc = &wm8350->rtc;
wm8350_free_irq(wm8350, WM8350_IRQ_RTC_SEC);
wm8350_free_irq(wm8350, WM8350_IRQ_RTC_ALM);
wm8350_free_irq(wm8350, WM8350_IRQ_RTC_SEC, wm8350);
wm8350_free_irq(wm8350, WM8350_IRQ_RTC_ALM, wm8350);
rtc_device_unregister(wm_rtc->rtc);

View File

@ -0,0 +1,304 @@
/*
* Backlight driver for Marvell Semiconductor 88PM8606
*
* Copyright (C) 2009 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/i2c.h>
#include <linux/backlight.h>
#include <linux/mfd/88pm860x.h>
#define MAX_BRIGHTNESS (0xFF)
#define MIN_BRIGHTNESS (0)
#define CURRENT_MASK (0x1F << 1)
struct pm860x_backlight_data {
struct pm860x_chip *chip;
struct i2c_client *i2c;
int current_brightness;
int port;
int pwm;
int iset;
};
static inline int wled_a(int port)
{
int ret;
ret = ((port - PM8606_BACKLIGHT1) << 1) + 2;
return ret;
}
static inline int wled_b(int port)
{
int ret;
ret = ((port - PM8606_BACKLIGHT1) << 1) + 3;
return ret;
}
/* WLED2 & WLED3 share the same IDC */
static inline int wled_idc(int port)
{
int ret;
switch (port) {
case PM8606_BACKLIGHT1:
case PM8606_BACKLIGHT2:
ret = ((port - PM8606_BACKLIGHT1) << 1) + 3;
break;
case PM8606_BACKLIGHT3:
default:
ret = ((port - PM8606_BACKLIGHT2) << 1) + 3;
break;
}
return ret;
}
static int pm860x_backlight_set(struct backlight_device *bl, int brightness)
{
struct pm860x_backlight_data *data = bl_get_data(bl);
struct pm860x_chip *chip = data->chip;
unsigned char value;
int ret;
if (brightness > MAX_BRIGHTNESS)
value = MAX_BRIGHTNESS;
else
value = brightness;
ret = pm860x_reg_write(data->i2c, wled_a(data->port), value);
if (ret < 0)
goto out;
if ((data->current_brightness == 0) && brightness) {
if (data->iset) {
ret = pm860x_set_bits(data->i2c, wled_idc(data->port),
CURRENT_MASK, data->iset);
if (ret < 0)
goto out;
}
if (data->pwm) {
ret = pm860x_set_bits(data->i2c, PM8606_PWM,
PM8606_PWM_FREQ_MASK, data->pwm);
if (ret < 0)
goto out;
}
if (brightness == MAX_BRIGHTNESS) {
/* set WLED_ON bit as 100% */
ret = pm860x_set_bits(data->i2c, wled_b(data->port),
PM8606_WLED_ON, PM8606_WLED_ON);
}
} else {
if (brightness == MAX_BRIGHTNESS) {
/* set WLED_ON bit as 100% */
ret = pm860x_set_bits(data->i2c, wled_b(data->port),
PM8606_WLED_ON, PM8606_WLED_ON);
} else {
/* clear WLED_ON bit since it's not 100% */
ret = pm860x_set_bits(data->i2c, wled_b(data->port),
PM8606_WLED_ON, 0);
}
}
if (ret < 0)
goto out;
dev_dbg(chip->dev, "set brightness %d\n", value);
data->current_brightness = value;
return 0;
out:
dev_dbg(chip->dev, "set brightness %d failure with return "
"value:%d\n", value, ret);
return ret;
}
static int pm860x_backlight_update_status(struct backlight_device *bl)
{
int brightness = bl->props.brightness;
if (bl->props.power != FB_BLANK_UNBLANK)
brightness = 0;
if (bl->props.fb_blank != FB_BLANK_UNBLANK)
brightness = 0;
if (bl->props.state & BL_CORE_SUSPENDED)
brightness = 0;
return pm860x_backlight_set(bl, brightness);
}
static int pm860x_backlight_get_brightness(struct backlight_device *bl)
{
struct pm860x_backlight_data *data = bl_get_data(bl);
struct pm860x_chip *chip = data->chip;
int ret;
ret = pm860x_reg_read(data->i2c, wled_a(data->port));
if (ret < 0)
goto out;
data->current_brightness = ret;
dev_dbg(chip->dev, "get brightness %d\n", data->current_brightness);
return data->current_brightness;
out:
return -EINVAL;
}
static struct backlight_ops pm860x_backlight_ops = {
.options = BL_CORE_SUSPENDRESUME,
.update_status = pm860x_backlight_update_status,
.get_brightness = pm860x_backlight_get_brightness,
};
static int __check_device(struct pm860x_backlight_pdata *pdata, char *name)
{
struct pm860x_backlight_pdata *p = pdata;
int ret = -EINVAL;
while (p && p->id) {
if ((p->id != PM8606_ID_BACKLIGHT) || (p->flags < 0))
break;
if (!strncmp(name, pm860x_backlight_name[p->flags],
MFD_NAME_SIZE)) {
ret = (int)p->flags;
break;
}
p++;
}
return ret;
}
static int pm860x_backlight_probe(struct platform_device *pdev)
{
struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct pm860x_platform_data *pm860x_pdata;
struct pm860x_backlight_pdata *pdata = NULL;
struct pm860x_backlight_data *data;
struct backlight_device *bl;
struct resource *res;
unsigned char value;
char name[MFD_NAME_SIZE];
int ret;
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (res == NULL) {
dev_err(&pdev->dev, "No I/O resource!\n");
return -EINVAL;
}
if (pdev->dev.parent->platform_data) {
pm860x_pdata = pdev->dev.parent->platform_data;
pdata = pm860x_pdata->backlight;
}
if (pdata == NULL) {
dev_err(&pdev->dev, "platform data isn't assigned to "
"backlight\n");
return -EINVAL;
}
data = kzalloc(sizeof(struct pm860x_backlight_data), GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
strncpy(name, res->name, MFD_NAME_SIZE);
data->chip = chip;
data->i2c = (chip->id == CHIP_PM8606) ? chip->client \
: chip->companion;
data->current_brightness = MAX_BRIGHTNESS;
data->pwm = pdata->pwm;
data->iset = pdata->iset;
data->port = __check_device(pdata, name);
if (data->port < 0) {
dev_err(&pdev->dev, "wrong platform data is assigned");
return -EINVAL;
}
bl = backlight_device_register(name, &pdev->dev, data,
&pm860x_backlight_ops);
if (IS_ERR(bl)) {
dev_err(&pdev->dev, "failed to register backlight\n");
kfree(data);
return PTR_ERR(bl);
}
bl->props.max_brightness = MAX_BRIGHTNESS;
bl->props.brightness = MAX_BRIGHTNESS;
platform_set_drvdata(pdev, bl);
/* Enable reference VSYS */
ret = pm860x_reg_read(data->i2c, PM8606_VSYS);
if (ret < 0)
goto out;
if ((ret & PM8606_VSYS_EN) == 0) {
value = ret | PM8606_VSYS_EN;
ret = pm860x_reg_write(data->i2c, PM8606_VSYS, value);
if (ret < 0)
goto out;
}
/* Enable reference OSC */
ret = pm860x_reg_read(data->i2c, PM8606_MISC);
if (ret < 0)
goto out;
if ((ret & PM8606_MISC_OSC_EN) == 0) {
value = ret | PM8606_MISC_OSC_EN;
ret = pm860x_reg_write(data->i2c, PM8606_MISC, value);
if (ret < 0)
goto out;
}
/* read current backlight */
ret = pm860x_backlight_get_brightness(bl);
if (ret < 0)
goto out;
backlight_update_status(bl);
return 0;
out:
kfree(data);
return ret;
}
static int pm860x_backlight_remove(struct platform_device *pdev)
{
struct backlight_device *bl = platform_get_drvdata(pdev);
struct pm860x_backlight_data *data = bl_get_data(bl);
backlight_device_unregister(bl);
kfree(data);
return 0;
}
static struct platform_driver pm860x_backlight_driver = {
.driver = {
.name = "88pm860x-backlight",
.owner = THIS_MODULE,
},
.probe = pm860x_backlight_probe,
.remove = pm860x_backlight_remove,
};
static int __init pm860x_backlight_init(void)
{
return platform_driver_register(&pm860x_backlight_driver);
}
module_init(pm860x_backlight_init);
static void __exit pm860x_backlight_exit(void)
{
platform_driver_unregister(&pm860x_backlight_driver);
}
module_exit(pm860x_backlight_exit);
MODULE_DESCRIPTION("Backlight Driver for Marvell Semiconductor 88PM8606");
MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:88pm860x-backlight");

View File

@ -212,6 +212,13 @@ config BACKLIGHT_DA903X
If you have a LCD backlight connected to the WLED output of DA9030
or DA9034 WLED output, say Y here to enable this driver.
config BACKLIGHT_MAX8925
tristate "Backlight driver for MAX8925"
depends on BACKLIGHT_CLASS_DEVICE && MFD_MAX8925
help
If you have a LCD backlight connected to the WLED output of MAX8925
WLED output, say Y here to enable this driver.
config BACKLIGHT_MBP_NVIDIA
tristate "MacBook Pro Nvidia Backlight Driver"
depends on BACKLIGHT_CLASS_DEVICE && X86
@ -262,3 +269,9 @@ config BACKLIGHT_ADP5520
To compile this driver as a module, choose M here: the module will
be called adp5520_bl.
config BACKLIGHT_88PM860X
tristate "Backlight Driver for 88PM8606 using WLED"
depends on BACKLIGHT_CLASS_DEVICE && MFD_88PM860X
help
Say Y to enable the backlight driver for Marvell 88PM8606.

View File

@ -22,10 +22,12 @@ obj-$(CONFIG_BACKLIGHT_PROGEAR) += progear_bl.o
obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o
obj-$(CONFIG_BACKLIGHT_PWM) += pwm_bl.o
obj-$(CONFIG_BACKLIGHT_DA903X) += da903x_bl.o
obj-$(CONFIG_BACKLIGHT_MAX8925) += max8925_bl.o
obj-$(CONFIG_BACKLIGHT_MBP_NVIDIA) += mbp_nvidia_bl.o
obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o
obj-$(CONFIG_BACKLIGHT_SAHARA) += kb3886_bl.o
obj-$(CONFIG_BACKLIGHT_WM831X) += wm831x_bl.o
obj-$(CONFIG_BACKLIGHT_ADX) += adx_bl.o
obj-$(CONFIG_BACKLIGHT_ADP5520) += adp5520_bl.o
obj-$(CONFIG_BACKLIGHT_88PM860X) += 88pm860x_bl.o

View File

@ -0,0 +1,200 @@
/*
* Backlight driver for Maxim MAX8925
*
* Copyright (C) 2009 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/i2c.h>
#include <linux/backlight.h>
#include <linux/mfd/max8925.h>
#define MAX_BRIGHTNESS (0xff)
#define MIN_BRIGHTNESS (0)
#define LWX_FREQ(x) (((x - 601) / 100) & 0x7)
struct max8925_backlight_data {
struct max8925_chip *chip;
int current_brightness;
};
static int max8925_backlight_set(struct backlight_device *bl, int brightness)
{
struct max8925_backlight_data *data = bl_get_data(bl);
struct max8925_chip *chip = data->chip;
unsigned char value;
int ret;
if (brightness > MAX_BRIGHTNESS)
value = MAX_BRIGHTNESS;
else
value = brightness;
ret = max8925_reg_write(chip->i2c, MAX8925_WLED_CNTL, value);
if (ret < 0)
goto out;
if (!data->current_brightness && brightness)
/* enable WLED output */
ret = max8925_set_bits(chip->i2c, MAX8925_WLED_MODE_CNTL, 1, 1);
else if (!brightness)
/* disable WLED output */
ret = max8925_set_bits(chip->i2c, MAX8925_WLED_MODE_CNTL, 1, 0);
if (ret < 0)
goto out;
dev_dbg(chip->dev, "set brightness %d\n", value);
data->current_brightness = value;
return 0;
out:
dev_dbg(chip->dev, "set brightness %d failure with return value:%d\n",
value, ret);
return ret;
}
static int max8925_backlight_update_status(struct backlight_device *bl)
{
int brightness = bl->props.brightness;
if (bl->props.power != FB_BLANK_UNBLANK)
brightness = 0;
if (bl->props.fb_blank != FB_BLANK_UNBLANK)
brightness = 0;
if (bl->props.state & BL_CORE_SUSPENDED)
brightness = 0;
return max8925_backlight_set(bl, brightness);
}
static int max8925_backlight_get_brightness(struct backlight_device *bl)
{
struct max8925_backlight_data *data = bl_get_data(bl);
struct max8925_chip *chip = data->chip;
int ret;
ret = max8925_reg_read(chip->i2c, MAX8925_WLED_CNTL);
if (ret < 0)
return -EINVAL;
data->current_brightness = ret;
dev_dbg(chip->dev, "get brightness %d\n", data->current_brightness);
return ret;
}
static struct backlight_ops max8925_backlight_ops = {
.options = BL_CORE_SUSPENDRESUME,
.update_status = max8925_backlight_update_status,
.get_brightness = max8925_backlight_get_brightness,
};
static int __devinit max8925_backlight_probe(struct platform_device *pdev)
{
struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct max8925_platform_data *max8925_pdata;
struct max8925_backlight_pdata *pdata = NULL;
struct max8925_backlight_data *data;
struct backlight_device *bl;
struct resource *res;
char name[MAX8925_NAME_SIZE];
unsigned char value;
int ret;
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (res == NULL) {
dev_err(&pdev->dev, "No I/O resource!\n");
return -EINVAL;
}
if (pdev->dev.parent->platform_data) {
max8925_pdata = pdev->dev.parent->platform_data;
pdata = max8925_pdata->backlight;
}
if (!pdata) {
dev_err(&pdev->dev, "platform data isn't assigned to "
"backlight\n");
return -EINVAL;
}
data = kzalloc(sizeof(struct max8925_backlight_data), GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
strncpy(name, res->name, MAX8925_NAME_SIZE);
data->chip = chip;
data->current_brightness = 0;
bl = backlight_device_register(name, &pdev->dev, data,
&max8925_backlight_ops);
if (IS_ERR(bl)) {
dev_err(&pdev->dev, "failed to register backlight\n");
kfree(data);
return PTR_ERR(bl);
}
bl->props.max_brightness = MAX_BRIGHTNESS;
bl->props.brightness = MAX_BRIGHTNESS;
platform_set_drvdata(pdev, bl);
value = 0;
if (pdata->lxw_scl)
value |= (1 << 7);
if (pdata->lxw_freq)
value |= (LWX_FREQ(pdata->lxw_freq) << 4);
if (pdata->dual_string)
value |= (1 << 1);
ret = max8925_set_bits(chip->i2c, MAX8925_WLED_MODE_CNTL, 0xfe, value);
if (ret < 0)
goto out;
backlight_update_status(bl);
return 0;
out:
kfree(data);
return ret;
}
static int __devexit max8925_backlight_remove(struct platform_device *pdev)
{
struct backlight_device *bl = platform_get_drvdata(pdev);
struct max8925_backlight_data *data = bl_get_data(bl);
backlight_device_unregister(bl);
kfree(data);
return 0;
}
static struct platform_driver max8925_backlight_driver = {
.driver = {
.name = "max8925-backlight",
.owner = THIS_MODULE,
},
.probe = max8925_backlight_probe,
.remove = __devexit_p(max8925_backlight_remove),
};
static int __init max8925_backlight_init(void)
{
return platform_driver_register(&max8925_backlight_driver);
}
module_init(max8925_backlight_init);
static void __exit max8925_backlight_exit(void)
{
platform_driver_unregister(&max8925_backlight_driver);
};
module_exit(max8925_backlight_exit);
MODULE_DESCRIPTION("Backlight Driver for Maxim MAX8925");
MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:max8925-backlight");

24
include/linux/htcpld.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef __LINUX_HTCPLD_H
#define __LINUX_HTCPLD_H
struct htcpld_chip_platform_data {
unsigned int addr;
unsigned int reset;
unsigned int num_gpios;
unsigned int gpio_out_base;
unsigned int gpio_in_base;
unsigned int irq_base;
unsigned int num_irqs;
};
struct htcpld_core_platform_data {
unsigned int int_reset_gpio_hi;
unsigned int int_reset_gpio_lo;
unsigned int i2c_adapter_id;
struct htcpld_chip_platform_data *chip;
unsigned int num_chip;
};
#endif /* __LINUX_HTCPLD_H */

View File

@ -80,6 +80,11 @@
#define TWL_MODULE_PM_MASTER TWL4030_MODULE_PM_MASTER
#define TWL_MODULE_PM_RECEIVER TWL4030_MODULE_PM_RECEIVER
#define TWL_MODULE_RTC TWL4030_MODULE_RTC
#define TWL_MODULE_PWM TWL4030_MODULE_PWM0
#define TWL6030_MODULE_ID0 0x0D
#define TWL6030_MODULE_ID1 0x0E
#define TWL6030_MODULE_ID2 0x0F
#define GPIO_INTR_OFFSET 0
#define KEYPAD_INTR_OFFSET 1
@ -239,6 +244,21 @@ int twl6030_interrupt_mask(u8 bit_mask, u8 offset);
/*----------------------------------------------------------------------*/
/*Interface Bit Register (INTBR) offsets
*(Use TWL_4030_MODULE_INTBR)
*/
#define REG_GPPUPDCTR1 0x0F
/*I2C1 and I2C4(SR) SDA/SCL pull-up control bits */
#define I2C_SCL_CTRL_PU BIT(0)
#define I2C_SDA_CTRL_PU BIT(2)
#define SR_I2C_SCL_CTRL_PU BIT(4)
#define SR_I2C_SDA_CTRL_PU BIT(6)
/*----------------------------------------------------------------------*/
/*
* Keypad register offsets (use TWL4030_MODULE_KEYPAD)
* ... SIH/interrupt only
@ -530,6 +550,7 @@ struct twl4030_power_data {
};
extern void twl4030_power_init(struct twl4030_power_data *triton2_scripts);
extern int twl4030_remove_script(u8 flags);
struct twl4030_codec_audio_data {
unsigned int audio_mclk;
@ -605,12 +626,7 @@ int twl4030_sih_setup(int module);
#define TWL4030_VAUX3_DEV_GRP 0x1F
#define TWL4030_VAUX3_DEDICATED 0x22
#if defined(CONFIG_TWL4030_BCI_BATTERY) || \
defined(CONFIG_TWL4030_BCI_BATTERY_MODULE)
extern int twl4030charger_usb_en(int enable);
#else
static inline int twl4030charger_usb_en(int enable) { return 0; }
#endif
static inline int twl4030charger_usb_en(int enable) { return 0; }
/*----------------------------------------------------------------------*/

View File

@ -1,217 +0,0 @@
/*
* Marvell 88PM8607 Interface
*
* Copyright (C) 2009 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.com>
*
* 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.
*/
#ifndef __LINUX_MFD_88PM8607_H
#define __LINUX_MFD_88PM8607_H
enum {
PM8607_ID_BUCK1 = 0,
PM8607_ID_BUCK2,
PM8607_ID_BUCK3,
PM8607_ID_LDO1,
PM8607_ID_LDO2,
PM8607_ID_LDO3,
PM8607_ID_LDO4,
PM8607_ID_LDO5,
PM8607_ID_LDO6,
PM8607_ID_LDO7,
PM8607_ID_LDO8,
PM8607_ID_LDO9,
PM8607_ID_LDO10,
PM8607_ID_LDO12,
PM8607_ID_LDO14,
PM8607_ID_RG_MAX,
};
#define CHIP_ID (0x40)
#define CHIP_ID_MASK (0xF8)
/* Interrupt Registers */
#define PM8607_STATUS_1 (0x01)
#define PM8607_STATUS_2 (0x02)
#define PM8607_INT_STATUS1 (0x03)
#define PM8607_INT_STATUS2 (0x04)
#define PM8607_INT_STATUS3 (0x05)
#define PM8607_INT_MASK_1 (0x06)
#define PM8607_INT_MASK_2 (0x07)
#define PM8607_INT_MASK_3 (0x08)
/* Regulator Control Registers */
#define PM8607_LDO1 (0x10)
#define PM8607_LDO2 (0x11)
#define PM8607_LDO3 (0x12)
#define PM8607_LDO4 (0x13)
#define PM8607_LDO5 (0x14)
#define PM8607_LDO6 (0x15)
#define PM8607_LDO7 (0x16)
#define PM8607_LDO8 (0x17)
#define PM8607_LDO9 (0x18)
#define PM8607_LDO10 (0x19)
#define PM8607_LDO12 (0x1A)
#define PM8607_LDO14 (0x1B)
#define PM8607_SLEEP_MODE1 (0x1C)
#define PM8607_SLEEP_MODE2 (0x1D)
#define PM8607_SLEEP_MODE3 (0x1E)
#define PM8607_SLEEP_MODE4 (0x1F)
#define PM8607_GO (0x20)
#define PM8607_SLEEP_BUCK1 (0x21)
#define PM8607_SLEEP_BUCK2 (0x22)
#define PM8607_SLEEP_BUCK3 (0x23)
#define PM8607_BUCK1 (0x24)
#define PM8607_BUCK2 (0x25)
#define PM8607_BUCK3 (0x26)
#define PM8607_BUCK_CONTROLS (0x27)
#define PM8607_SUPPLIES_EN11 (0x2B)
#define PM8607_SUPPLIES_EN12 (0x2C)
#define PM8607_GROUP1 (0x2D)
#define PM8607_GROUP2 (0x2E)
#define PM8607_GROUP3 (0x2F)
#define PM8607_GROUP4 (0x30)
#define PM8607_GROUP5 (0x31)
#define PM8607_GROUP6 (0x32)
#define PM8607_SUPPLIES_EN21 (0x33)
#define PM8607_SUPPLIES_EN22 (0x34)
/* RTC Control Registers */
#define PM8607_RTC1 (0xA0)
#define PM8607_RTC_COUNTER1 (0xA1)
#define PM8607_RTC_COUNTER2 (0xA2)
#define PM8607_RTC_COUNTER3 (0xA3)
#define PM8607_RTC_COUNTER4 (0xA4)
#define PM8607_RTC_EXPIRE1 (0xA5)
#define PM8607_RTC_EXPIRE2 (0xA6)
#define PM8607_RTC_EXPIRE3 (0xA7)
#define PM8607_RTC_EXPIRE4 (0xA8)
#define PM8607_RTC_TRIM1 (0xA9)
#define PM8607_RTC_TRIM2 (0xAA)
#define PM8607_RTC_TRIM3 (0xAB)
#define PM8607_RTC_TRIM4 (0xAC)
#define PM8607_RTC_MISC1 (0xAD)
#define PM8607_RTC_MISC2 (0xAE)
#define PM8607_RTC_MISC3 (0xAF)
/* Misc Registers */
#define PM8607_CHIP_ID (0x00)
#define PM8607_LDO1 (0x10)
#define PM8607_DVC3 (0x26)
#define PM8607_MISC1 (0x40)
/* bit definitions for PM8607 events */
#define PM8607_EVENT_ONKEY (1 << 0)
#define PM8607_EVENT_EXTON (1 << 1)
#define PM8607_EVENT_CHG (1 << 2)
#define PM8607_EVENT_BAT (1 << 3)
#define PM8607_EVENT_RTC (1 << 4)
#define PM8607_EVENT_CC (1 << 5)
#define PM8607_EVENT_VBAT (1 << 8)
#define PM8607_EVENT_VCHG (1 << 9)
#define PM8607_EVENT_VSYS (1 << 10)
#define PM8607_EVENT_TINT (1 << 11)
#define PM8607_EVENT_GPADC0 (1 << 12)
#define PM8607_EVENT_GPADC1 (1 << 13)
#define PM8607_EVENT_GPADC2 (1 << 14)
#define PM8607_EVENT_GPADC3 (1 << 15)
#define PM8607_EVENT_AUDIO_SHORT (1 << 16)
#define PM8607_EVENT_PEN (1 << 17)
#define PM8607_EVENT_HEADSET (1 << 18)
#define PM8607_EVENT_HOOK (1 << 19)
#define PM8607_EVENT_MICIN (1 << 20)
#define PM8607_EVENT_CHG_TIMEOUT (1 << 21)
#define PM8607_EVENT_CHG_DONE (1 << 22)
#define PM8607_EVENT_CHG_FAULT (1 << 23)
/* bit definitions of Status Query Interface */
#define PM8607_STATUS_CC (1 << 3)
#define PM8607_STATUS_PEN (1 << 4)
#define PM8607_STATUS_HEADSET (1 << 5)
#define PM8607_STATUS_HOOK (1 << 6)
#define PM8607_STATUS_MICIN (1 << 7)
#define PM8607_STATUS_ONKEY (1 << 8)
#define PM8607_STATUS_EXTON (1 << 9)
#define PM8607_STATUS_CHG (1 << 10)
#define PM8607_STATUS_BAT (1 << 11)
#define PM8607_STATUS_VBUS (1 << 12)
#define PM8607_STATUS_OV (1 << 13)
/* bit definitions of BUCK3 */
#define PM8607_BUCK3_DOUBLE (1 << 6)
/* bit definitions of Misc1 */
#define PM8607_MISC1_PI2C (1 << 0)
/* Interrupt Number in 88PM8607 */
enum {
PM8607_IRQ_ONKEY = 0,
PM8607_IRQ_EXTON,
PM8607_IRQ_CHG,
PM8607_IRQ_BAT,
PM8607_IRQ_RTC,
PM8607_IRQ_VBAT = 8,
PM8607_IRQ_VCHG,
PM8607_IRQ_VSYS,
PM8607_IRQ_TINT,
PM8607_IRQ_GPADC0,
PM8607_IRQ_GPADC1,
PM8607_IRQ_GPADC2,
PM8607_IRQ_GPADC3,
PM8607_IRQ_AUDIO_SHORT = 16,
PM8607_IRQ_PEN,
PM8607_IRQ_HEADSET,
PM8607_IRQ_HOOK,
PM8607_IRQ_MICIN,
PM8607_IRQ_CHG_FAIL,
PM8607_IRQ_CHG_DONE,
PM8607_IRQ_CHG_FAULT,
};
enum {
PM8607_CHIP_A0 = 0x40,
PM8607_CHIP_A1 = 0x41,
PM8607_CHIP_B0 = 0x48,
};
struct pm8607_chip {
struct device *dev;
struct mutex io_lock;
struct i2c_client *client;
int (*read)(struct pm8607_chip *chip, int reg, int bytes, void *dest);
int (*write)(struct pm8607_chip *chip, int reg, int bytes, void *src);
int buck3_double; /* DVC ramp slope double */
unsigned char chip_id;
};
#define PM8607_MAX_REGULATOR 15 /* 3 Bucks, 12 LDOs */
enum {
GI2C_PORT = 0,
PI2C_PORT,
};
struct pm8607_platform_data {
int i2c_port; /* Controlled by GI2C or PI2C */
struct regulator_init_data *regulator[PM8607_MAX_REGULATOR];
};
extern int pm8607_reg_read(struct pm8607_chip *, int);
extern int pm8607_reg_write(struct pm8607_chip *, int, unsigned char);
extern int pm8607_bulk_read(struct pm8607_chip *, int, int,
unsigned char *);
extern int pm8607_bulk_write(struct pm8607_chip *, int, int,
unsigned char *);
extern int pm8607_set_bits(struct pm8607_chip *, int, unsigned char,
unsigned char);
#endif /* __LINUX_MFD_88PM8607_H */

View File

@ -0,0 +1,375 @@
/*
* Marvell 88PM860x Interface
*
* Copyright (C) 2009 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.com>
*
* 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.
*/
#ifndef __LINUX_MFD_88PM860X_H
#define __LINUX_MFD_88PM860X_H
#include <linux/interrupt.h>
#define MFD_NAME_SIZE (40)
enum {
CHIP_INVALID = 0,
CHIP_PM8606,
CHIP_PM8607,
CHIP_MAX,
};
enum {
PM8606_ID_INVALID,
PM8606_ID_BACKLIGHT,
PM8606_ID_LED,
PM8606_ID_VIBRATOR,
PM8606_ID_TOUCH,
PM8606_ID_SOUND,
PM8606_ID_CHARGER,
PM8606_ID_MAX,
};
enum {
PM8606_BACKLIGHT1 = 0,
PM8606_BACKLIGHT2,
PM8606_BACKLIGHT3,
};
enum {
PM8606_LED1_RED = 0,
PM8606_LED1_GREEN,
PM8606_LED1_BLUE,
PM8606_LED2_RED,
PM8606_LED2_GREEN,
PM8606_LED2_BLUE,
PM8607_LED_VIBRATOR,
};
/* 8606 Registers */
#define PM8606_DCM_BOOST (0x00)
#define PM8606_PWM (0x01)
/* Backlight Registers */
#define PM8606_WLED1A (0x02)
#define PM8606_WLED1B (0x03)
#define PM8606_WLED2A (0x04)
#define PM8606_WLED2B (0x05)
#define PM8606_WLED3A (0x06)
#define PM8606_WLED3B (0x07)
/* LED Registers */
#define PM8606_RGB2A (0x08)
#define PM8606_RGB2B (0x09)
#define PM8606_RGB2C (0x0A)
#define PM8606_RGB2D (0x0B)
#define PM8606_RGB1A (0x0C)
#define PM8606_RGB1B (0x0D)
#define PM8606_RGB1C (0x0E)
#define PM8606_RGB1D (0x0F)
#define PM8606_PREREGULATORA (0x10)
#define PM8606_PREREGULATORB (0x11)
#define PM8606_VIBRATORA (0x12)
#define PM8606_VIBRATORB (0x13)
#define PM8606_VCHG (0x14)
#define PM8606_VSYS (0x15)
#define PM8606_MISC (0x16)
#define PM8606_CHIP_ID (0x17)
#define PM8606_STATUS (0x18)
#define PM8606_FLAGS (0x19)
#define PM8606_PROTECTA (0x1A)
#define PM8606_PROTECTB (0x1B)
#define PM8606_PROTECTC (0x1C)
/* Bit definitions of PM8606 registers */
#define PM8606_DCM_500MA (0x0) /* current limit */
#define PM8606_DCM_750MA (0x1)
#define PM8606_DCM_1000MA (0x2)
#define PM8606_DCM_1250MA (0x3)
#define PM8606_DCM_250MV (0x0 << 2)
#define PM8606_DCM_300MV (0x1 << 2)
#define PM8606_DCM_350MV (0x2 << 2)
#define PM8606_DCM_400MV (0x3 << 2)
#define PM8606_PWM_31200HZ (0x0)
#define PM8606_PWM_15600HZ (0x1)
#define PM8606_PWM_7800HZ (0x2)
#define PM8606_PWM_3900HZ (0x3)
#define PM8606_PWM_1950HZ (0x4)
#define PM8606_PWM_976HZ (0x5)
#define PM8606_PWM_488HZ (0x6)
#define PM8606_PWM_244HZ (0x7)
#define PM8606_PWM_FREQ_MASK (0x7)
#define PM8606_WLED_ON (1 << 0)
#define PM8606_WLED_CURRENT(x) ((x & 0x1F) << 1)
#define PM8606_LED_CURRENT(x) (((x >> 2) & 0x07) << 5)
#define PM8606_VSYS_EN (1 << 1)
#define PM8606_MISC_OSC_EN (1 << 4)
enum {
PM8607_ID_BUCK1 = 0,
PM8607_ID_BUCK2,
PM8607_ID_BUCK3,
PM8607_ID_LDO1,
PM8607_ID_LDO2,
PM8607_ID_LDO3,
PM8607_ID_LDO4,
PM8607_ID_LDO5,
PM8607_ID_LDO6,
PM8607_ID_LDO7,
PM8607_ID_LDO8,
PM8607_ID_LDO9,
PM8607_ID_LDO10,
PM8607_ID_LDO12,
PM8607_ID_LDO14,
PM8607_ID_RG_MAX,
};
#define PM8607_VERSION (0x40) /* 8607 chip ID */
#define PM8607_VERSION_MASK (0xF0) /* 8607 chip ID mask */
/* Interrupt Registers */
#define PM8607_STATUS_1 (0x01)
#define PM8607_STATUS_2 (0x02)
#define PM8607_INT_STATUS1 (0x03)
#define PM8607_INT_STATUS2 (0x04)
#define PM8607_INT_STATUS3 (0x05)
#define PM8607_INT_MASK_1 (0x06)
#define PM8607_INT_MASK_2 (0x07)
#define PM8607_INT_MASK_3 (0x08)
/* Regulator Control Registers */
#define PM8607_LDO1 (0x10)
#define PM8607_LDO2 (0x11)
#define PM8607_LDO3 (0x12)
#define PM8607_LDO4 (0x13)
#define PM8607_LDO5 (0x14)
#define PM8607_LDO6 (0x15)
#define PM8607_LDO7 (0x16)
#define PM8607_LDO8 (0x17)
#define PM8607_LDO9 (0x18)
#define PM8607_LDO10 (0x19)
#define PM8607_LDO12 (0x1A)
#define PM8607_LDO14 (0x1B)
#define PM8607_SLEEP_MODE1 (0x1C)
#define PM8607_SLEEP_MODE2 (0x1D)
#define PM8607_SLEEP_MODE3 (0x1E)
#define PM8607_SLEEP_MODE4 (0x1F)
#define PM8607_GO (0x20)
#define PM8607_SLEEP_BUCK1 (0x21)
#define PM8607_SLEEP_BUCK2 (0x22)
#define PM8607_SLEEP_BUCK3 (0x23)
#define PM8607_BUCK1 (0x24)
#define PM8607_BUCK2 (0x25)
#define PM8607_BUCK3 (0x26)
#define PM8607_BUCK_CONTROLS (0x27)
#define PM8607_SUPPLIES_EN11 (0x2B)
#define PM8607_SUPPLIES_EN12 (0x2C)
#define PM8607_GROUP1 (0x2D)
#define PM8607_GROUP2 (0x2E)
#define PM8607_GROUP3 (0x2F)
#define PM8607_GROUP4 (0x30)
#define PM8607_GROUP5 (0x31)
#define PM8607_GROUP6 (0x32)
#define PM8607_SUPPLIES_EN21 (0x33)
#define PM8607_SUPPLIES_EN22 (0x34)
/* Vibrator Control Registers */
#define PM8607_VIBRATOR_SET (0x28)
#define PM8607_VIBRATOR_PWM (0x29)
/* GPADC Registers */
#define PM8607_GP_BIAS1 (0x4F)
#define PM8607_MEAS_EN1 (0x50)
#define PM8607_MEAS_EN2 (0x51)
#define PM8607_MEAS_EN3 (0x52)
#define PM8607_MEAS_OFF_TIME1 (0x53)
#define PM8607_MEAS_OFF_TIME2 (0x54)
#define PM8607_TSI_PREBIAS (0x55) /* prebias time */
#define PM8607_PD_PREBIAS (0x56) /* prebias time */
#define PM8607_GPADC_MISC1 (0x57)
/* RTC Control Registers */
#define PM8607_RTC1 (0xA0)
#define PM8607_RTC_COUNTER1 (0xA1)
#define PM8607_RTC_COUNTER2 (0xA2)
#define PM8607_RTC_COUNTER3 (0xA3)
#define PM8607_RTC_COUNTER4 (0xA4)
#define PM8607_RTC_EXPIRE1 (0xA5)
#define PM8607_RTC_EXPIRE2 (0xA6)
#define PM8607_RTC_EXPIRE3 (0xA7)
#define PM8607_RTC_EXPIRE4 (0xA8)
#define PM8607_RTC_TRIM1 (0xA9)
#define PM8607_RTC_TRIM2 (0xAA)
#define PM8607_RTC_TRIM3 (0xAB)
#define PM8607_RTC_TRIM4 (0xAC)
#define PM8607_RTC_MISC1 (0xAD)
#define PM8607_RTC_MISC2 (0xAE)
#define PM8607_RTC_MISC3 (0xAF)
/* Misc Registers */
#define PM8607_CHIP_ID (0x00)
#define PM8607_B0_MISC1 (0x0C)
#define PM8607_LDO1 (0x10)
#define PM8607_DVC3 (0x26)
#define PM8607_A1_MISC1 (0x40)
/* bit definitions of Status Query Interface */
#define PM8607_STATUS_CC (1 << 3)
#define PM8607_STATUS_PEN (1 << 4)
#define PM8607_STATUS_HEADSET (1 << 5)
#define PM8607_STATUS_HOOK (1 << 6)
#define PM8607_STATUS_MICIN (1 << 7)
#define PM8607_STATUS_ONKEY (1 << 8)
#define PM8607_STATUS_EXTON (1 << 9)
#define PM8607_STATUS_CHG (1 << 10)
#define PM8607_STATUS_BAT (1 << 11)
#define PM8607_STATUS_VBUS (1 << 12)
#define PM8607_STATUS_OV (1 << 13)
/* bit definitions of BUCK3 */
#define PM8607_BUCK3_DOUBLE (1 << 6)
/* bit definitions of Misc1 */
#define PM8607_A1_MISC1_PI2C (1 << 0)
#define PM8607_B0_MISC1_INV_INT (1 << 0)
#define PM8607_B0_MISC1_INT_CLEAR (1 << 1)
#define PM8607_B0_MISC1_INT_MASK (1 << 2)
#define PM8607_B0_MISC1_PI2C (1 << 3)
#define PM8607_B0_MISC1_RESET (1 << 6)
/* bits definitions of GPADC */
#define PM8607_GPADC_EN (1 << 0)
#define PM8607_GPADC_PREBIAS_MASK (3 << 1)
#define PM8607_GPADC_SLOT_CYCLE_MASK (3 << 3) /* slow mode */
#define PM8607_GPADC_OFF_SCALE_MASK (3 << 5) /* GP sleep mode */
#define PM8607_GPADC_SW_CAL_MASK (1 << 7)
#define PM8607_PD_PREBIAS_MASK (0x1F << 0)
#define PM8607_PD_PRECHG_MASK (7 << 5)
/* Interrupt Number in 88PM8607 */
enum {
PM8607_IRQ_ONKEY,
PM8607_IRQ_EXTON,
PM8607_IRQ_CHG,
PM8607_IRQ_BAT,
PM8607_IRQ_RTC,
PM8607_IRQ_CC,
PM8607_IRQ_VBAT,
PM8607_IRQ_VCHG,
PM8607_IRQ_VSYS,
PM8607_IRQ_TINT,
PM8607_IRQ_GPADC0,
PM8607_IRQ_GPADC1,
PM8607_IRQ_GPADC2,
PM8607_IRQ_GPADC3,
PM8607_IRQ_AUDIO_SHORT,
PM8607_IRQ_PEN,
PM8607_IRQ_HEADSET,
PM8607_IRQ_HOOK,
PM8607_IRQ_MICIN,
PM8607_IRQ_CHG_FAIL,
PM8607_IRQ_CHG_DONE,
PM8607_IRQ_CHG_FAULT,
};
enum {
PM8607_CHIP_A0 = 0x40,
PM8607_CHIP_A1 = 0x41,
PM8607_CHIP_B0 = 0x48,
};
struct pm860x_chip {
struct device *dev;
struct mutex io_lock;
struct mutex irq_lock;
struct i2c_client *client;
struct i2c_client *companion; /* companion chip client */
int buck3_double; /* DVC ramp slope double */
unsigned short companion_addr;
int id;
int irq_mode;
int irq_base;
int core_irq;
unsigned char chip_version;
};
#define PM8607_MAX_REGULATOR 15 /* 3 Bucks, 12 LDOs */
enum {
GI2C_PORT = 0,
PI2C_PORT,
};
struct pm860x_backlight_pdata {
int id;
int pwm;
int iset;
unsigned long flags;
};
struct pm860x_led_pdata {
int id;
int iset;
unsigned long flags;
};
struct pm860x_touch_pdata {
int gpadc_prebias;
int slot_cycle;
int off_scale;
int sw_cal;
int tsi_prebias; /* time, slot */
int pen_prebias; /* time, slot */
int pen_prechg; /* time, slot */
int res_x; /* resistor of Xplate */
unsigned long flags;
};
struct pm860x_power_pdata {
unsigned fast_charge; /* charge current */
};
struct pm860x_platform_data {
struct pm860x_backlight_pdata *backlight;
struct pm860x_led_pdata *led;
struct pm860x_touch_pdata *touch;
struct pm860x_power_pdata *power;
unsigned short companion_addr; /* I2C address of companion chip */
int i2c_port; /* Controlled by GI2C or PI2C */
int irq_mode; /* Clear interrupt by read/write(0/1) */
int irq_base; /* IRQ base number of 88pm860x */
struct regulator_init_data *regulator[PM8607_MAX_REGULATOR];
};
extern char pm860x_backlight_name[][MFD_NAME_SIZE];
extern char pm860x_led_name[][MFD_NAME_SIZE];
extern int pm860x_reg_read(struct i2c_client *, int);
extern int pm860x_reg_write(struct i2c_client *, int, unsigned char);
extern int pm860x_bulk_read(struct i2c_client *, int, int, unsigned char *);
extern int pm860x_bulk_write(struct i2c_client *, int, int, unsigned char *);
extern int pm860x_set_bits(struct i2c_client *, int, unsigned char,
unsigned char);
extern int pm860x_device_init(struct pm860x_chip *chip,
struct pm860x_platform_data *pdata);
extern void pm860x_device_exit(struct pm860x_chip *chip);
#endif /* __LINUX_MFD_88PM860X_H */

View File

@ -6,7 +6,6 @@
*/
#include <linux/device.h>
#include <linux/workqueue.h>
#include <linux/regulator/machine.h>
#ifndef MFD_AB3100_H
@ -74,7 +73,6 @@
* @testreg_client: secondary client for test registers
* @chip_name: name of this chip variant
* @chip_id: 8 bit chip ID for this chip variant
* @work: an event handling worker
* @event_subscribers: event subscribers are listed here
* @startup_events: a copy of the first reading of the event registers
* @startup_events_read: whether the first events have been read
@ -90,7 +88,6 @@ struct ab3100 {
struct i2c_client *testreg_client;
char chip_name[32];
u8 chip_id;
struct work_struct work;
struct blocking_notifier_head event_subscribers;
u32 startup_events;
bool startup_events_read;

253
include/linux/mfd/max8925.h Normal file
View File

@ -0,0 +1,253 @@
/*
* Maxim8925 Interface
*
* Copyright (C) 2009 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.com>
*
* 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.
*/
#ifndef __LINUX_MFD_MAX8925_H
#define __LINUX_MFD_MAX8925_H
#include <linux/mutex.h>
#include <linux/interrupt.h>
/* Unified sub device IDs for MAX8925 */
enum {
MAX8925_ID_SD1,
MAX8925_ID_SD2,
MAX8925_ID_SD3,
MAX8925_ID_LDO1,
MAX8925_ID_LDO2,
MAX8925_ID_LDO3,
MAX8925_ID_LDO4,
MAX8925_ID_LDO5,
MAX8925_ID_LDO6,
MAX8925_ID_LDO7,
MAX8925_ID_LDO8,
MAX8925_ID_LDO9,
MAX8925_ID_LDO10,
MAX8925_ID_LDO11,
MAX8925_ID_LDO12,
MAX8925_ID_LDO13,
MAX8925_ID_LDO14,
MAX8925_ID_LDO15,
MAX8925_ID_LDO16,
MAX8925_ID_LDO17,
MAX8925_ID_LDO18,
MAX8925_ID_LDO19,
MAX8925_ID_LDO20,
MAX8925_ID_MAX,
};
enum {
/*
* Charging current threshold trigger going from fast charge
* to TOPOFF charge. From 5% to 20% of fasting charging current.
*/
MAX8925_TOPOFF_THR_5PER,
MAX8925_TOPOFF_THR_10PER,
MAX8925_TOPOFF_THR_15PER,
MAX8925_TOPOFF_THR_20PER,
};
enum {
/* Fast charging current */
MAX8925_FCHG_85MA,
MAX8925_FCHG_300MA,
MAX8925_FCHG_460MA,
MAX8925_FCHG_600MA,
MAX8925_FCHG_700MA,
MAX8925_FCHG_800MA,
MAX8925_FCHG_900MA,
MAX8925_FCHG_1000MA,
};
/* Charger registers */
#define MAX8925_CHG_IRQ1 (0x7e)
#define MAX8925_CHG_IRQ2 (0x7f)
#define MAX8925_CHG_IRQ1_MASK (0x80)
#define MAX8925_CHG_IRQ2_MASK (0x81)
#define MAX8925_CHG_STATUS (0x82)
/* GPM registers */
#define MAX8925_SYSENSEL (0x00)
#define MAX8925_ON_OFF_IRQ1 (0x01)
#define MAX8925_ON_OFF_IRQ1_MASK (0x02)
#define MAX8925_ON_OFF_STATUS (0x03)
#define MAX8925_ON_OFF_IRQ2 (0x0d)
#define MAX8925_ON_OFF_IRQ2_MASK (0x0e)
#define MAX8925_RESET_CNFG (0x0f)
/* Touch registers */
#define MAX8925_TSC_IRQ (0x00)
#define MAX8925_TSC_IRQ_MASK (0x01)
#define MAX8925_TSC_CNFG1 (0x02)
#define MAX8925_ADC_SCHED (0x10)
#define MAX8925_ADC_RES_END (0x6f)
#define MAX8925_NREF_OK (1 << 4)
/* RTC registers */
#define MAX8925_ALARM0_CNTL (0x18)
#define MAX8925_ALARM1_CNTL (0x19)
#define MAX8925_RTC_IRQ (0x1c)
#define MAX8925_RTC_IRQ_MASK (0x1d)
#define MAX8925_MPL_CNTL (0x1e)
/* WLED registers */
#define MAX8925_WLED_MODE_CNTL (0x84)
#define MAX8925_WLED_CNTL (0x85)
/* MAX8925 Registers */
#define MAX8925_SDCTL1 (0x04)
#define MAX8925_SDCTL2 (0x07)
#define MAX8925_SDCTL3 (0x0A)
#define MAX8925_SDV1 (0x06)
#define MAX8925_SDV2 (0x09)
#define MAX8925_SDV3 (0x0C)
#define MAX8925_LDOCTL1 (0x18)
#define MAX8925_LDOCTL2 (0x1C)
#define MAX8925_LDOCTL3 (0x20)
#define MAX8925_LDOCTL4 (0x24)
#define MAX8925_LDOCTL5 (0x28)
#define MAX8925_LDOCTL6 (0x2C)
#define MAX8925_LDOCTL7 (0x30)
#define MAX8925_LDOCTL8 (0x34)
#define MAX8925_LDOCTL9 (0x38)
#define MAX8925_LDOCTL10 (0x3C)
#define MAX8925_LDOCTL11 (0x40)
#define MAX8925_LDOCTL12 (0x44)
#define MAX8925_LDOCTL13 (0x48)
#define MAX8925_LDOCTL14 (0x4C)
#define MAX8925_LDOCTL15 (0x50)
#define MAX8925_LDOCTL16 (0x10)
#define MAX8925_LDOCTL17 (0x14)
#define MAX8925_LDOCTL18 (0x72)
#define MAX8925_LDOCTL19 (0x5C)
#define MAX8925_LDOCTL20 (0x9C)
#define MAX8925_LDOVOUT1 (0x1A)
#define MAX8925_LDOVOUT2 (0x1E)
#define MAX8925_LDOVOUT3 (0x22)
#define MAX8925_LDOVOUT4 (0x26)
#define MAX8925_LDOVOUT5 (0x2A)
#define MAX8925_LDOVOUT6 (0x2E)
#define MAX8925_LDOVOUT7 (0x32)
#define MAX8925_LDOVOUT8 (0x36)
#define MAX8925_LDOVOUT9 (0x3A)
#define MAX8925_LDOVOUT10 (0x3E)
#define MAX8925_LDOVOUT11 (0x42)
#define MAX8925_LDOVOUT12 (0x46)
#define MAX8925_LDOVOUT13 (0x4A)
#define MAX8925_LDOVOUT14 (0x4E)
#define MAX8925_LDOVOUT15 (0x52)
#define MAX8925_LDOVOUT16 (0x12)
#define MAX8925_LDOVOUT17 (0x16)
#define MAX8925_LDOVOUT18 (0x74)
#define MAX8925_LDOVOUT19 (0x5E)
#define MAX8925_LDOVOUT20 (0x9E)
/* bit definitions */
#define CHG_IRQ1_MASK (0x07)
#define CHG_IRQ2_MASK (0xff)
#define ON_OFF_IRQ1_MASK (0xff)
#define ON_OFF_IRQ2_MASK (0x03)
#define TSC_IRQ_MASK (0x03)
#define RTC_IRQ_MASK (0x0c)
#define MAX8925_MAX_REGULATOR (23)
#define MAX8925_NAME_SIZE (32)
/* IRQ definitions */
enum {
MAX8925_IRQ_VCHG_DC_OVP,
MAX8925_IRQ_VCHG_DC_F,
MAX8925_IRQ_VCHG_DC_R,
MAX8925_IRQ_VCHG_USB_OVP,
MAX8925_IRQ_VCHG_USB_F,
MAX8925_IRQ_VCHG_USB_R,
MAX8925_IRQ_VCHG_THM_OK_R,
MAX8925_IRQ_VCHG_THM_OK_F,
MAX8925_IRQ_VCHG_SYSLOW_F,
MAX8925_IRQ_VCHG_SYSLOW_R,
MAX8925_IRQ_VCHG_RST,
MAX8925_IRQ_VCHG_DONE,
MAX8925_IRQ_VCHG_TOPOFF,
MAX8925_IRQ_VCHG_TMR_FAULT,
MAX8925_IRQ_GPM_RSTIN,
MAX8925_IRQ_GPM_MPL,
MAX8925_IRQ_GPM_SW_3SEC,
MAX8925_IRQ_GPM_EXTON_F,
MAX8925_IRQ_GPM_EXTON_R,
MAX8925_IRQ_GPM_SW_1SEC,
MAX8925_IRQ_GPM_SW_F,
MAX8925_IRQ_GPM_SW_R,
MAX8925_IRQ_GPM_SYSCKEN_F,
MAX8925_IRQ_GPM_SYSCKEN_R,
MAX8925_IRQ_RTC_ALARM1,
MAX8925_IRQ_RTC_ALARM0,
MAX8925_IRQ_TSC_STICK,
MAX8925_IRQ_TSC_NSTICK,
MAX8925_NR_IRQS,
};
struct max8925_chip {
struct device *dev;
struct i2c_client *i2c;
struct i2c_client *adc;
struct i2c_client *rtc;
struct mutex io_lock;
struct mutex irq_lock;
int irq_base;
int core_irq;
int tsc_irq;
};
struct max8925_backlight_pdata {
int lxw_scl; /* 0/1 -- 0.8Ohm/0.4Ohm */
int lxw_freq; /* 700KHz ~ 1400KHz */
int dual_string; /* 0/1 -- single/dual string */
};
struct max8925_touch_pdata {
unsigned int flags;
};
struct max8925_power_pdata {
int (*set_charger)(int);
unsigned batt_detect:1;
unsigned topoff_threshold:2;
unsigned fast_charge:3; /* charge current */
};
/*
* irq_base: stores IRQ base number of MAX8925 in platform
* tsc_irq: stores IRQ number of MAX8925 TSC
*/
struct max8925_platform_data {
struct max8925_backlight_pdata *backlight;
struct max8925_touch_pdata *touch;
struct max8925_power_pdata *power;
struct regulator_init_data *regulator[MAX8925_MAX_REGULATOR];
int irq_base;
int tsc_irq;
};
extern int max8925_reg_read(struct i2c_client *, int);
extern int max8925_reg_write(struct i2c_client *, int, unsigned char);
extern int max8925_bulk_read(struct i2c_client *, int, int, unsigned char *);
extern int max8925_bulk_write(struct i2c_client *, int, int, unsigned char *);
extern int max8925_set_bits(struct i2c_client *, int, unsigned char,
unsigned char);
extern int max8925_device_init(struct max8925_chip *,
struct max8925_platform_data *);
extern void max8925_device_exit(struct max8925_chip *);
#endif /* __LINUX_MFD_MAX8925_H */

View File

@ -59,7 +59,8 @@ void tmio_core_mmc_clk_div(void __iomem *cnf, int shift, int state);
* data for the MMC controller
*/
struct tmio_mmc_data {
const unsigned int hclk;
unsigned int hclk;
unsigned long capabilities;
void (*set_pwr)(struct platform_device *host, int state);
void (*set_clk_div)(struct platform_device *host, int state);
};

View File

@ -12,6 +12,7 @@
#include <linux/mfd/mcp.h>
#include <linux/gpio.h>
#include <linux/semaphore.h>
#define UCB_IO_DATA 0x00
#define UCB_IO_DIR 0x01

View File

@ -15,6 +15,7 @@
#ifndef __MFD_WM831X_CORE_H__
#define __MFD_WM831X_CORE_H__
#include <linux/completion.h>
#include <linux/interrupt.h>
/*
@ -254,9 +255,14 @@ struct wm831x {
int irq_masks_cur[WM831X_NUM_IRQ_REGS]; /* Currently active value */
int irq_masks_cache[WM831X_NUM_IRQ_REGS]; /* Cached hardware value */
/* Chip revision based flags */
unsigned has_gpio_ena:1; /* Has GPIO enable bit */
unsigned has_cs_sts:1; /* Has current sink status bit */
int num_gpio;
struct mutex auxadc_lock;
struct completion auxadc_done;
/* The WM831x has a security key blocking access to certain
* registers. The mutex is taken by the accessors for locking

View File

@ -41,6 +41,10 @@
#define WM831X_GPN_OD_MASK 0x0200 /* GPN_OD */
#define WM831X_GPN_OD_SHIFT 9 /* GPN_OD */
#define WM831X_GPN_OD_WIDTH 1 /* GPN_OD */
#define WM831X_GPN_ENA 0x0080 /* GPN_ENA */
#define WM831X_GPN_ENA_MASK 0x0080 /* GPN_ENA */
#define WM831X_GPN_ENA_SHIFT 7 /* GPN_ENA */
#define WM831X_GPN_ENA_WIDTH 1 /* GPN_ENA */
#define WM831X_GPN_TRI 0x0080 /* GPN_TRI */
#define WM831X_GPN_TRI_MASK 0x0080 /* GPN_TRI */
#define WM831X_GPN_TRI_SHIFT 7 /* GPN_TRI */

View File

@ -16,6 +16,7 @@
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/interrupt.h>
#include <linux/completion.h>
#include <linux/mfd/wm8350/audio.h>
#include <linux/mfd/wm8350/gpio.h>
@ -579,6 +580,8 @@
#define WM8350_NUM_IRQ 63
#define WM8350_NUM_IRQ_REGS 7
struct wm8350_reg_access {
u16 readable; /* Mask of readable bits */
u16 writable; /* Mask of writable bits */
@ -600,11 +603,6 @@ extern const u16 wm8352_mode3_defaults[];
struct wm8350;
struct wm8350_irq {
irq_handler_t handler;
void *data;
};
struct wm8350_hwmon {
struct platform_device *pdev;
struct device *classdev;
@ -624,11 +622,13 @@ struct wm8350 {
u16 *reg_cache;
struct mutex auxadc_mutex;
struct completion auxadc_done;
/* Interrupt handling */
struct mutex irq_mutex; /* IRQ table mutex */
struct wm8350_irq irq[WM8350_NUM_IRQ];
struct mutex irq_lock;
int chip_irq;
int irq_base;
u16 irq_masks[WM8350_NUM_IRQ_REGS];
/* Client devices */
struct wm8350_codec codec;
@ -647,11 +647,13 @@ struct wm8350 {
* used by the platform to configure GPIO functions and similar.
* @irq_high: Set if WM8350 IRQ is active high.
* @irq_base: Base IRQ for genirq (not currently used).
* @gpio_base: Base for gpiolib.
*/
struct wm8350_platform_data {
int (*init)(struct wm8350 *wm8350);
int irq_high;
int irq_base;
int gpio_base;
};
@ -677,12 +679,33 @@ int wm8350_block_write(struct wm8350 *wm8350, int reg, int size, u16 *src);
/*
* WM8350 internal interrupts
*/
int wm8350_register_irq(struct wm8350 *wm8350, int irq,
irq_handler_t handler, unsigned long flags,
const char *name, void *data);
int wm8350_free_irq(struct wm8350 *wm8350, int irq);
int wm8350_mask_irq(struct wm8350 *wm8350, int irq);
int wm8350_unmask_irq(struct wm8350 *wm8350, int irq);
static inline int wm8350_register_irq(struct wm8350 *wm8350, int irq,
irq_handler_t handler,
unsigned long flags,
const char *name, void *data)
{
if (!wm8350->irq_base)
return -ENODEV;
return request_threaded_irq(irq + wm8350->irq_base, NULL,
handler, flags, name, data);
}
static inline void wm8350_free_irq(struct wm8350 *wm8350, int irq, void *data)
{
free_irq(irq + wm8350->irq_base, data);
}
static inline void wm8350_mask_irq(struct wm8350 *wm8350, int irq)
{
disable_irq(irq + wm8350->irq_base);
}
static inline void wm8350_unmask_irq(struct wm8350 *wm8350, int irq)
{
enable_irq(irq + wm8350->irq_base);
}
int wm8350_irq_init(struct wm8350 *wm8350, int irq,
struct wm8350_platform_data *pdata);
int wm8350_irq_exit(struct wm8350 *wm8350);

View File

@ -29,6 +29,7 @@
#define WM8350_GPIO_FUNCTION_SELECT_2 0x8D
#define WM8350_GPIO_FUNCTION_SELECT_3 0x8E
#define WM8350_GPIO_FUNCTION_SELECT_4 0x8F
#define WM8350_GPIO_LEVEL 0xE6
/*
* GPIO Functions

View File

@ -263,6 +263,7 @@ struct wm8350_rtc {
struct platform_device *pdev;
struct rtc_device *rtc;
int alarm_enabled; /* used over suspend/resume */
int update_enabled;
};
#endif

View File

@ -0,0 +1,54 @@
/*
* include/linux/mfd/wm8994/core.h -- Core interface for WM8994
*
* Copyright 2009 Wolfson Microelectronics PLC.
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#ifndef __MFD_WM8994_CORE_H__
#define __MFD_WM8994_CORE_H__
struct regulator_dev;
struct regulator_bulk_data;
#define WM8994_NUM_GPIO_REGS 11
#define WM8994_NUM_LDO_REGS 2
struct wm8994 {
struct mutex io_lock;
struct device *dev;
int (*read_dev)(struct wm8994 *wm8994, unsigned short reg,
int bytes, void *dest);
int (*write_dev)(struct wm8994 *wm8994, unsigned short reg,
int bytes, void *src);
void *control_data;
int gpio_base;
/* Used over suspend/resume */
u16 ldo_regs[WM8994_NUM_LDO_REGS];
u16 gpio_regs[WM8994_NUM_GPIO_REGS];
struct regulator_dev *dbvdd;
struct regulator_bulk_data *supplies;
};
/* Device I/O API */
int wm8994_reg_read(struct wm8994 *wm8994, unsigned short reg);
int wm8994_reg_write(struct wm8994 *wm8994, unsigned short reg,
unsigned short val);
int wm8994_set_bits(struct wm8994 *wm8994, unsigned short reg,
unsigned short mask, unsigned short val);
int wm8994_bulk_read(struct wm8994 *wm8994, unsigned short reg,
int count, u16 *buf);
#endif

View File

@ -0,0 +1,72 @@
/*
* include/linux/mfd/wm8994/gpio.h - GPIO configuration for WM8994
*
* Copyright 2009 Wolfson Microelectronics PLC.
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#ifndef __MFD_WM8994_GPIO_H__
#define __MFD_WM8994_GPIO_H__
#define WM8994_GPIO_MAX 11
#define WM8994_GP_FN_PIN_SPECIFIC 0
#define WM8994_GP_FN_GPIO 1
#define WM8994_GP_FN_SDOUT 2
#define WM8994_GP_FN_IRQ 3
#define WM8994_GP_FN_TEMPERATURE 4
#define WM8994_GP_FN_MICBIAS1_DET 5
#define WM8994_GP_FN_MICBIAS1_SHORT 6
#define WM8994_GP_FN_MICBIAS2_DET 7
#define WM8994_GP_FN_MICBIAS2_SHORT 8
#define WM8994_GP_FN_FLL1_LOCK 9
#define WM8994_GP_FN_FLL2_LOCK 10
#define WM8994_GP_FN_SRC1_LOCK 11
#define WM8994_GP_FN_SRC2_LOCK 12
#define WM8994_GP_FN_DRC1_ACT 13
#define WM8994_GP_FN_DRC2_ACT 14
#define WM8994_GP_FN_DRC3_ACT 15
#define WM8994_GP_FN_WSEQ_STATUS 16
#define WM8994_GP_FN_FIFO_ERROR 17
#define WM8994_GP_FN_OPCLK 18
#define WM8994_GPN_DIR 0x8000 /* GPN_DIR */
#define WM8994_GPN_DIR_MASK 0x8000 /* GPN_DIR */
#define WM8994_GPN_DIR_SHIFT 15 /* GPN_DIR */
#define WM8994_GPN_DIR_WIDTH 1 /* GPN_DIR */
#define WM8994_GPN_PU 0x4000 /* GPN_PU */
#define WM8994_GPN_PU_MASK 0x4000 /* GPN_PU */
#define WM8994_GPN_PU_SHIFT 14 /* GPN_PU */
#define WM8994_GPN_PU_WIDTH 1 /* GPN_PU */
#define WM8994_GPN_PD 0x2000 /* GPN_PD */
#define WM8994_GPN_PD_MASK 0x2000 /* GPN_PD */
#define WM8994_GPN_PD_SHIFT 13 /* GPN_PD */
#define WM8994_GPN_PD_WIDTH 1 /* GPN_PD */
#define WM8994_GPN_POL 0x0400 /* GPN_POL */
#define WM8994_GPN_POL_MASK 0x0400 /* GPN_POL */
#define WM8994_GPN_POL_SHIFT 10 /* GPN_POL */
#define WM8994_GPN_POL_WIDTH 1 /* GPN_POL */
#define WM8994_GPN_OP_CFG 0x0200 /* GPN_OP_CFG */
#define WM8994_GPN_OP_CFG_MASK 0x0200 /* GPN_OP_CFG */
#define WM8994_GPN_OP_CFG_SHIFT 9 /* GPN_OP_CFG */
#define WM8994_GPN_OP_CFG_WIDTH 1 /* GPN_OP_CFG */
#define WM8994_GPN_DB 0x0100 /* GPN_DB */
#define WM8994_GPN_DB_MASK 0x0100 /* GPN_DB */
#define WM8994_GPN_DB_SHIFT 8 /* GPN_DB */
#define WM8994_GPN_DB_WIDTH 1 /* GPN_DB */
#define WM8994_GPN_LVL 0x0040 /* GPN_LVL */
#define WM8994_GPN_LVL_MASK 0x0040 /* GPN_LVL */
#define WM8994_GPN_LVL_SHIFT 6 /* GPN_LVL */
#define WM8994_GPN_LVL_WIDTH 1 /* GPN_LVL */
#define WM8994_GPN_FN_MASK 0x001F /* GPN_FN - [4:0] */
#define WM8994_GPN_FN_SHIFT 0 /* GPN_FN - [4:0] */
#define WM8994_GPN_FN_WIDTH 5 /* GPN_FN - [4:0] */
#endif

View File

@ -0,0 +1,97 @@
/*
* include/linux/mfd/wm8994/pdata.h -- Platform data for WM8994
*
* Copyright 2009 Wolfson Microelectronics PLC.
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#ifndef __MFD_WM8994_PDATA_H__
#define __MFD_WM8994_PDATA_H__
#define WM8994_NUM_LDO 2
#define WM8994_NUM_GPIO 11
struct wm8994_ldo_pdata {
/** GPIOs to enable regulator, 0 or less if not available */
int enable;
const char *supply;
struct regulator_init_data *init_data;
};
#define WM8994_CONFIGURE_GPIO 0x8000
#define WM8994_DRC_REGS 5
#define WM8994_EQ_REGS 19
/**
* DRC configurations are specified with a label and a set of register
* values to write (the enable bits will be ignored). At runtime an
* enumerated control will be presented for each DRC block allowing
* the user to choose the configration to use.
*
* Configurations may be generated by hand or by using the DRC control
* panel provided by the WISCE - see http://www.wolfsonmicro.com/wisce/
* for details.
*/
struct wm8994_drc_cfg {
const char *name;
u16 regs[WM8994_DRC_REGS];
};
/**
* ReTune Mobile configurations are specified with a label, sample
* rate and set of values to write (the enable bits will be ignored).
*
* Configurations are expected to be generated using the ReTune Mobile
* control panel in WISCE - see http://www.wolfsonmicro.com/wisce/
*/
struct wm8994_retune_mobile_cfg {
const char *name;
unsigned int rate;
u16 regs[WM8994_EQ_REGS];
};
struct wm8994_pdata {
int gpio_base;
/**
* Default values for GPIOs if non-zero, WM8994_CONFIGURE_GPIO
* can be used for all zero values.
*/
int gpio_defaults[WM8994_NUM_GPIO];
struct wm8994_ldo_pdata ldo[WM8994_NUM_LDO];
int num_drc_cfgs;
struct wm8994_drc_cfg *drc_cfgs;
int num_retune_mobile_cfgs;
struct wm8994_retune_mobile_cfg *retune_mobile_cfgs;
/* LINEOUT can be differential or single ended */
unsigned int lineout1_diff:1;
unsigned int lineout2_diff:1;
/* Common mode feedback */
unsigned int lineout1fb:1;
unsigned int lineout2fb:1;
/* Microphone biases: 0=0.9*AVDD1 1=0.65*AVVD1 */
unsigned int micbias1_lvl:1;
unsigned int micbias2_lvl:1;
/* Jack detect threashold levels, see datasheet for values */
unsigned int jd_scthr:2;
unsigned int jd_thr:2;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1349,7 +1349,7 @@ static irqreturn_t wm8350_hp_jack_handler(int irq, void *data)
int mask;
struct wm8350_jack_data *jack = NULL;
switch (irq) {
switch (irq - wm8350->irq_base) {
case WM8350_IRQ_CODEC_JCK_DET_L:
jack = &priv->hpl;
mask = WM8350_JACK_L_LVL;
@ -1424,7 +1424,7 @@ int wm8350_hp_jack_detect(struct snd_soc_codec *codec, enum wm8350_jack which,
wm8350_set_bits(wm8350, WM8350_JACK_DETECT, ena);
/* Sync status */
wm8350_hp_jack_handler(irq, priv);
wm8350_hp_jack_handler(irq + wm8350->irq_base, priv);
return 0;
}
@ -1521,8 +1521,8 @@ static int wm8350_remove(struct platform_device *pdev)
WM8350_JDL_ENA | WM8350_JDR_ENA);
wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_TOCLK_ENA);
wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L);
wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R);
wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L, priv);
wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R, priv);
priv->hpl.jack = NULL;
priv->hpr.jack = NULL;