You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
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:
@@ -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
|
||||
@@ -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");
|
||||
+36
-15
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
@@ -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");
|
||||
@@ -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 = {
|
||||
.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");
|
||||
|
||||
@@ -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");
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
@@ -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");
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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");
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user