Commit 6dc3eb5c authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 8fe900b8 2c08583c
......@@ -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
......
......@@ -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
\ No newline at end of file
/*
* 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");
......@@ -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,23 +64,36 @@ 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)
static void wm831x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
struct wm831x *wm831x = wm831x_gpio->wm831x;
return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + offset,
WM831X_GPN_DIR | WM831X_GPN_TRI, 0);
wm831x_set_bits(wm831x, WM831X_GPIO_LEVEL, 1 << offset,
value << offset);
}
static void wm831x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
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;
wm831x_set_bits(wm831x, WM831X_GPIO_LEVEL, 1 << offset,
value << offset);
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)
......@@ -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);
}
}
......
/*
* 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");
/*
* 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");
......@@ -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.
......
......@@ -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 = {
static struct platform_driver smbus_sch_driver = {
.driver = {
.name = "isch_smbus",
.id_table = sch_ids,
.probe = sch_probe,
.remove = __devexit_p(sch_remove),
.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");
/*
* 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");
......@@ -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
......
......@@ -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
......
/*
* 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");
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
/*
* 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");
This diff is collapsed.
......@@ -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"
......
......@@ -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
\ No newline at end of file
/*
* 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;
......
......@@ -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 */
......
This diff is collapsed.
/*
* 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");
This diff is collapsed.
/*
* 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");
......@@ -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);
......
......@@ -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;
......
......@@ -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;
......
......@@ -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;
}
......
......@@ -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;
......
This diff is collapsed.
This diff is collapsed.
......@@ -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>
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -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
......
This diff is collapsed.
......@@ -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)
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment