提交 b779b332 编写于 作者: L 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: (73 commits)
  power: Revert "power_supply: Mark twl4030_charger as broken"
  mfd: Fix a memory leak when unload mc13xxx-core module
  mfd: Fix resource reclaim for max8998
  mfd: Remove unneeded ret value checking for max8998 register updates
  mfd: Add free max8998->ono irq in max8998_irq_exit()
  mfd: Fix resource reclaim in pcf50633_remove()
  omap4: pandaboard: fix up mmc card detect logic
  mfd: Fix ezx_pcap_probe error path
  mfd: Fix off-by-one value range checking for tps6507x
  mfd: Remove __devinitdata from tc6393xb_mmc_resources
  mfd: Add WM831x SPI support
  mfd: Factor out WM831x I2C I/O from the core driver
  mfd: Remove DEBUG defines from mc13xxx-core
  mfd: Fix jz4740_adc_set_enabled
  mfd: Add TPS658621C device ID
  mfd: Fix twl-irq function declaration warnings
  regulator: max8998 BUCK1/2 voltage change with use of GPIOs
  mfd: Voltages and GPIOs platform_data definitions for max8998
  regulator: max8998 BUCK1/2 internal voltages and indexes defined
  mfd: Support for ICs compliant with max8998
  ...
......@@ -269,9 +269,14 @@ static int omap4_twl6030_hsmmc_late_init(struct device *dev)
struct omap_mmc_platform_data *pdata = dev->platform_data;
/* Setting MMC1 Card detect Irq */
if (pdev->id == 0)
if (pdev->id == 0) {
ret = twl6030_mmc_card_detect_config();
if (ret)
pr_err("Failed configuring MMC1 card detect\n");
pdata->slots[0].card_detect_irq = TWL6030_IRQ_BASE +
MMCDETECT_INTR_OFFSET;
pdata->slots[0].card_detect = twl6030_mmc_card_detect;
}
return ret;
}
......
......@@ -160,10 +160,19 @@ static int omap4_twl6030_hsmmc_late_init(struct device *dev)
struct platform_device, dev);
struct omap_mmc_platform_data *pdata = dev->platform_data;
if (!pdata) {
dev_err(dev, "%s: NULL platform data\n", __func__);
return -EINVAL;
}
/* Setting MMC1 Card detect Irq */
if (pdev->id == 0)
pdata->slots[0].card_detect_irq = TWL6030_IRQ_BASE +
MMCDETECT_INTR_OFFSET;
if (pdev->id == 0) {
ret = twl6030_mmc_card_detect_config();
if (ret)
dev_err(dev, "%s: Error card detect config(%d)\n",
__func__, ret);
else
pdata->slots[0].card_detect = twl6030_mmc_card_detect;
}
return ret;
}
......
......@@ -185,6 +185,7 @@ config SMDK6410_WM1192_EV1
select REGULATOR_WM831X
select S3C24XX_GPIO_EXTRA64
select MFD_WM831X
select MFD_WM831X_I2C
help
The Wolfson Microelectronics 1192-EV1 is a WM831x based PMIC
daughtercard for the Samsung SMDK6410 reference platform.
......
......@@ -235,6 +235,18 @@ static struct platform_device smc911x_device = {
},
};
/*
* The card detect pin of the top SD/MMC slot (CN7) is active low and is
* connected to GPIO A22 of SH7372 (GPIO_PORT41).
*/
static int slot_cn7_get_cd(struct platform_device *pdev)
{
if (gpio_is_valid(GPIO_PORT41))
return !gpio_get_value(GPIO_PORT41);
else
return -ENXIO;
}
/* SH_MMCIF */
static struct resource sh_mmcif_resources[] = {
[0] = {
......@@ -261,6 +273,7 @@ static struct sh_mmcif_plat_data sh_mmcif_plat = {
.caps = MMC_CAP_4_BIT_DATA |
MMC_CAP_8_BIT_DATA |
MMC_CAP_NEEDS_POLL,
.get_cd = slot_cn7_get_cd,
};
static struct platform_device sh_mmcif_device = {
......@@ -310,6 +323,8 @@ static struct sh_mobile_sdhi_info sdhi1_info = {
.dma_slave_rx = SHDMA_SLAVE_SDHI1_RX,
.tmio_ocr_mask = MMC_VDD_165_195,
.tmio_flags = TMIO_MMC_WRPROTECT_DISABLE,
.tmio_caps = MMC_CAP_NEEDS_POLL,
.get_cd = slot_cn7_get_cd,
};
static struct resource sdhi1_resources[] = {
......@@ -948,6 +963,10 @@ static void __init ap4evb_init(void)
gpio_no_direction(GPIO_PORT9CR); /* FSIAOBT needs no direction */
gpio_no_direction(GPIO_PORT10CR); /* FSIAOLR needs no direction */
/* card detect pin for MMC slot (CN7) */
gpio_request(GPIO_PORT41, NULL);
gpio_direction_input(GPIO_PORT41);
/* set SPU2 clock to 119.6 MHz */
clk = clk_get(NULL, "spu_clk");
if (!IS_ERR(clk)) {
......
......@@ -116,6 +116,18 @@ config GPIO_SCH
This driver can also be built as a module. If so, the module
will be called sch-gpio.
config GPIO_VX855
tristate "VIA VX855/VX875 GPIO"
depends on GPIOLIB
select MFD_CORE
select MFD_VX855
help
Support access to the VX855/VX875 GPIO lines through the gpio library.
This driver provides common support for accessing the device,
additional drivers must be enabled in order to use the
functionality of the device.
comment "I2C GPIO expanders:"
config GPIO_MAX7300
......
......@@ -40,3 +40,4 @@ obj-$(CONFIG_GPIO_SCH) += sch_gpio.o
obj-$(CONFIG_GPIO_RDC321X) += rdc321x-gpio.o
obj-$(CONFIG_GPIO_JANZ_TTL) += janz-ttl.o
obj-$(CONFIG_GPIO_SX150X) += sx150x.o
obj-$(CONFIG_GPIO_VX855) += vx855_gpio.o
......@@ -30,6 +30,7 @@ struct stmpe_gpio {
struct mutex irq_lock;
int irq_base;
unsigned norequest_mask;
/* Caches of interrupt control registers for bus_lock */
u8 regs[CACHE_NR_REGS][CACHE_NR_BANKS];
......@@ -103,6 +104,9 @@ static int stmpe_gpio_request(struct gpio_chip *chip, unsigned offset)
struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip);
struct stmpe *stmpe = stmpe_gpio->stmpe;
if (stmpe_gpio->norequest_mask & (1 << offset))
return -EINVAL;
return stmpe_set_altfunc(stmpe, 1 << offset, STMPE_BLOCK_GPIO);
}
......@@ -287,8 +291,6 @@ static int __devinit stmpe_gpio_probe(struct platform_device *pdev)
int irq;
pdata = stmpe->pdata->gpio;
if (!pdata)
return -ENODEV;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
......@@ -302,6 +304,7 @@ static int __devinit stmpe_gpio_probe(struct platform_device *pdev)
stmpe_gpio->dev = &pdev->dev;
stmpe_gpio->stmpe = stmpe;
stmpe_gpio->norequest_mask = pdata ? pdata->norequest_mask : 0;
stmpe_gpio->chip = template_chip;
stmpe_gpio->chip.ngpio = stmpe->num_gpios;
......@@ -312,11 +315,11 @@ static int __devinit stmpe_gpio_probe(struct platform_device *pdev)
ret = stmpe_enable(stmpe, STMPE_BLOCK_GPIO);
if (ret)
return ret;
goto out_free;
ret = stmpe_gpio_irq_init(stmpe_gpio);
if (ret)
goto out_free;
goto out_disable;
ret = request_threaded_irq(irq, NULL, stmpe_gpio_irq, IRQF_ONESHOT,
"stmpe-gpio", stmpe_gpio);
......@@ -342,6 +345,8 @@ static int __devinit stmpe_gpio_probe(struct platform_device *pdev)
free_irq(irq, stmpe_gpio);
out_removeirq:
stmpe_gpio_irq_remove(stmpe_gpio);
out_disable:
stmpe_disable(stmpe, STMPE_BLOCK_GPIO);
out_free:
kfree(stmpe_gpio);
return ret;
......
/*
* Linux GPIOlib driver for the VIA VX855 integrated southbridge GPIO
*
* Copyright (C) 2009 VIA Technologies, Inc.
* Copyright (C) 2010 One Laptop per Child
* Author: Harald Welte <HaraldWelte@viatech.com>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/pci.h>
#include <linux/io.h>
#define MODULE_NAME "vx855_gpio"
/* The VX855 south bridge has the following GPIO pins:
* GPI 0...13 General Purpose Input
* GPO 0...12 General Purpose Output
* GPIO 0...14 General Purpose I/O (Open-Drain)
*/
#define NR_VX855_GPI 14
#define NR_VX855_GPO 13
#define NR_VX855_GPIO 15
#define NR_VX855_GPInO (NR_VX855_GPI + NR_VX855_GPO)
#define NR_VX855_GP (NR_VX855_GPI + NR_VX855_GPO + NR_VX855_GPIO)
struct vx855_gpio {
struct gpio_chip gpio;
spinlock_t lock;
u32 io_gpi;
u32 io_gpo;
bool gpi_reserved;
bool gpo_reserved;
};
/* resolve a GPIx into the corresponding bit position */
static inline u_int32_t gpi_i_bit(int i)
{
if (i < 10)
return 1 << i;
else
return 1 << (i + 14);
}
static inline u_int32_t gpo_o_bit(int i)
{
if (i < 11)
return 1 << i;
else
return 1 << (i + 14);
}
static inline u_int32_t gpio_i_bit(int i)
{
if (i < 14)
return 1 << (i + 10);
else
return 1 << (i + 14);
}
static inline u_int32_t gpio_o_bit(int i)
{
if (i < 14)
return 1 << (i + 11);
else
return 1 << (i + 13);
}
/* Mapping betwee numeric GPIO ID and the actual GPIO hardware numbering:
* 0..13 GPI 0..13
* 14..26 GPO 0..12
* 27..41 GPIO 0..14
*/
static int vx855gpio_direction_input(struct gpio_chip *gpio,
unsigned int nr)
{
struct vx855_gpio *vg = container_of(gpio, struct vx855_gpio, gpio);
unsigned long flags;
u_int32_t reg_out;
/* Real GPI bits are always in input direction */
if (nr < NR_VX855_GPI)
return 0;
/* Real GPO bits cannot be put in output direction */
if (nr < NR_VX855_GPInO)
return -EINVAL;
/* Open Drain GPIO have to be set to one */
spin_lock_irqsave(&vg->lock, flags);
reg_out = inl(vg->io_gpo);
reg_out |= gpio_o_bit(nr - NR_VX855_GPInO);
outl(reg_out, vg->io_gpo);
spin_unlock_irqrestore(&vg->lock, flags);
return 0;
}
static int vx855gpio_get(struct gpio_chip *gpio, unsigned int nr)
{
struct vx855_gpio *vg = container_of(gpio, struct vx855_gpio, gpio);
u_int32_t reg_in;
int ret = 0;
if (nr < NR_VX855_GPI) {
reg_in = inl(vg->io_gpi);
if (reg_in & gpi_i_bit(nr))
ret = 1;
} else if (nr < NR_VX855_GPInO) {
/* GPO don't have an input bit, we need to read it
* back from the output register */
reg_in = inl(vg->io_gpo);
if (reg_in & gpo_o_bit(nr - NR_VX855_GPI))
ret = 1;
} else {
reg_in = inl(vg->io_gpi);
if (reg_in & gpio_i_bit(nr - NR_VX855_GPInO))
ret = 1;
}
return ret;
}
static void vx855gpio_set(struct gpio_chip *gpio, unsigned int nr,
int val)
{
struct vx855_gpio *vg = container_of(gpio, struct vx855_gpio, gpio);
unsigned long flags;
u_int32_t reg_out;
/* True GPI cannot be switched to output mode */
if (nr < NR_VX855_GPI)
return;
spin_lock_irqsave(&vg->lock, flags);
reg_out = inl(vg->io_gpo);
if (nr < NR_VX855_GPInO) {
if (val)
reg_out |= gpo_o_bit(nr - NR_VX855_GPI);
else
reg_out &= ~gpo_o_bit(nr - NR_VX855_GPI);
} else {
if (val)
reg_out |= gpio_o_bit(nr - NR_VX855_GPInO);
else
reg_out &= ~gpio_o_bit(nr - NR_VX855_GPInO);
}
outl(reg_out, vg->io_gpo);
spin_unlock_irqrestore(&vg->lock, flags);
}
static int vx855gpio_direction_output(struct gpio_chip *gpio,
unsigned int nr, int val)
{
/* True GPI cannot be switched to output mode */
if (nr < NR_VX855_GPI)
return -EINVAL;
/* True GPO don't need to be switched to output mode,
* and GPIO are open-drain, i.e. also need no switching,
* so all we do is set the level */
vx855gpio_set(gpio, nr, val);
return 0;
}
static const char *vx855gpio_names[NR_VX855_GP] = {
"VX855_GPI0", "VX855_GPI1", "VX855_GPI2", "VX855_GPI3", "VX855_GPI4",
"VX855_GPI5", "VX855_GPI6", "VX855_GPI7", "VX855_GPI8", "VX855_GPI9",
"VX855_GPI10", "VX855_GPI11", "VX855_GPI12", "VX855_GPI13",
"VX855_GPO0", "VX855_GPO1", "VX855_GPO2", "VX855_GPO3", "VX855_GPO4",
"VX855_GPO5", "VX855_GPO6", "VX855_GPO7", "VX855_GPO8", "VX855_GPO9",
"VX855_GPO10", "VX855_GPO11", "VX855_GPO12",
"VX855_GPIO0", "VX855_GPIO1", "VX855_GPIO2", "VX855_GPIO3",
"VX855_GPIO4", "VX855_GPIO5", "VX855_GPIO6", "VX855_GPIO7",
"VX855_GPIO8", "VX855_GPIO9", "VX855_GPIO10", "VX855_GPIO11",
"VX855_GPIO12", "VX855_GPIO13", "VX855_GPIO14"
};
static void vx855gpio_gpio_setup(struct vx855_gpio *vg)
{
struct gpio_chip *c = &vg->gpio;
c->label = "VX855 South Bridge";
c->owner = THIS_MODULE;
c->direction_input = vx855gpio_direction_input;
c->direction_output = vx855gpio_direction_output;
c->get = vx855gpio_get;
c->set = vx855gpio_set;
c->dbg_show = NULL;
c->base = 0;
c->ngpio = NR_VX855_GP;
c->can_sleep = 0;
c->names = vx855gpio_names;
}
/* This platform device is ordinarily registered by the vx855 mfd driver */
static __devinit int vx855gpio_probe(struct platform_device *pdev)
{
struct resource *res_gpi;
struct resource *res_gpo;
struct vx855_gpio *vg;
int ret;
res_gpi = platform_get_resource(pdev, IORESOURCE_IO, 0);
res_gpo = platform_get_resource(pdev, IORESOURCE_IO, 1);
if (!res_gpi || !res_gpo)
return -EBUSY;
vg = kzalloc(sizeof(*vg), GFP_KERNEL);
if (!vg)
return -ENOMEM;
platform_set_drvdata(pdev, vg);
dev_info(&pdev->dev, "found VX855 GPIO controller\n");
vg->io_gpi = res_gpi->start;
vg->io_gpo = res_gpo->start;
spin_lock_init(&vg->lock);
/*
* A single byte is used to control various GPIO ports on the VX855,
* and in the case of the OLPC XO-1.5, some of those ports are used
* for switches that are interpreted and exposed through ACPI. ACPI
* will have reserved the region, so our own reservation will not
* succeed. Ignore and continue.
*/
if (!request_region(res_gpi->start, resource_size(res_gpi),
MODULE_NAME "_gpi"))
dev_warn(&pdev->dev,
"GPI I/O resource busy, probably claimed by ACPI\n");
else
vg->gpi_reserved = true;
if (!request_region(res_gpo->start, resource_size(res_gpo),
MODULE_NAME "_gpo"))
dev_warn(&pdev->dev,
"GPO I/O resource busy, probably claimed by ACPI\n");
else
vg->gpo_reserved = true;
vx855gpio_gpio_setup(vg);
ret = gpiochip_add(&vg->gpio);
if (ret) {
dev_err(&pdev->dev, "failed to register GPIOs\n");
goto out_release;
}
return 0;
out_release:
if (vg->gpi_reserved)
release_region(res_gpi->start, resource_size(res_gpi));
if (vg->gpo_reserved)
release_region(res_gpi->start, resource_size(res_gpo));
platform_set_drvdata(pdev, NULL);
kfree(vg);
return ret;
}
static int __devexit vx855gpio_remove(struct platform_device *pdev)
{
struct vx855_gpio *vg = platform_get_drvdata(pdev);
struct resource *res;
if (gpiochip_remove(&vg->gpio))
dev_err(&pdev->dev, "unable to remove gpio_chip?\n");
if (vg->gpi_reserved) {
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
release_region(res->start, resource_size(res));
}
if (vg->gpo_reserved) {
res = platform_get_resource(pdev, IORESOURCE_IO, 1);
release_region(res->start, resource_size(res));
}
platform_set_drvdata(pdev, NULL);
kfree(vg);
return 0;
}
static struct platform_driver vx855gpio_driver = {
.driver = {
.name = MODULE_NAME,
.owner = THIS_MODULE,
},
.probe = vx855gpio_probe,
.remove = __devexit_p(vx855gpio_remove),
};
static int vx855gpio_init(void)
{
return platform_driver_register(&vx855gpio_driver);
}
module_init(vx855gpio_init);
static void vx855gpio_exit(void)
{
platform_driver_unregister(&vx855gpio_driver);
}
module_exit(vx855gpio_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Harald Welte <HaraldWelte@viatech.com>");
MODULE_DESCRIPTION("GPIO driver for the VIA VX855 chipset");
MODULE_ALIAS("platform:vx855_gpio");
......@@ -140,6 +140,7 @@ static struct gpio_chip template_chip = {
.get = wm8994_gpio_get,
.direction_output = wm8994_gpio_direction_out,
.set = wm8994_gpio_set,
.to_irq = wm8994_gpio_to_irq,
.dbg_show = wm8994_gpio_dbg_show,
.can_sleep = 1,
};
......
......@@ -27,27 +27,37 @@
#include <linux/mfd/max8925.h>
#include <linux/slab.h>
#define SW_INPUT (1 << 7) /* 0/1 -- up/down */
#define HARDRESET_EN (1 << 7)
#define PWREN_EN (1 << 7)
struct max8925_onkey_info {
struct input_dev *idev;
struct i2c_client *i2c;
int irq;
struct device *dev;
int irq[2];
};
/*
* MAX8925 gives us an interrupt when ONKEY is held for 3 seconds.
* MAX8925 gives us an interrupt when ONKEY is pressed or released.
* max8925_set_bits() operates I2C bus and may sleep. So implement
* it in thread IRQ handler.
*/
static irqreturn_t max8925_onkey_handler(int irq, void *data)
{
struct max8925_onkey_info *info = data;
input_report_key(info->idev, KEY_POWER, 1);
int ret, event;
ret = max8925_reg_read(info->i2c, MAX8925_ON_OFF_STATUS);
if (ret & SW_INPUT)
event = 1;
else
event = 0;
input_report_key(info->idev, KEY_POWER, event);
input_sync(info->idev);
dev_dbg(info->dev, "onkey event:%d\n", event);
/* Enable hardreset to halt if system isn't shutdown on time */
max8925_set_bits(info->i2c, MAX8925_SYSENSEL,
HARDRESET_EN, HARDRESET_EN);
......@@ -59,14 +69,42 @@ static int __devinit max8925_onkey_probe(struct platform_device *pdev)
{
struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct max8925_onkey_info *info;
int error;
int irq[2], error;
irq[0] = platform_get_irq(pdev, 0);
if (irq[0] < 0) {
dev_err(&pdev->dev, "No IRQ resource!\n");
return -EINVAL;
}
irq[1] = platform_get_irq(pdev, 1);
if (irq[1] < 0) {
dev_err(&pdev->dev, "No IRQ resource!\n");
return -EINVAL;
}
info = kzalloc(sizeof(struct max8925_onkey_info), GFP_KERNEL);
if (!info)
return -ENOMEM;
info->i2c = chip->i2c;
info->irq = chip->irq_base + MAX8925_IRQ_GPM_SW_3SEC;
info->dev = &pdev->dev;
irq[0] += chip->irq_base;
irq[1] += chip->irq_base;
error = request_threaded_irq(irq[0], NULL, max8925_onkey_handler,
IRQF_ONESHOT, "onkey-down", info);
if (error < 0) {
dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
irq[0], error);
goto out;
}
error = request_threaded_irq(irq[1], NULL, max8925_onkey_handler,
IRQF_ONESHOT, "onkey-up", info);
if (error < 0) {
dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
irq[1], error);
goto out_irq;
}
info->idev = input_allocate_device();
if (!info->idev) {
......@@ -79,32 +117,29 @@ static int __devinit max8925_onkey_probe(struct platform_device *pdev)
info->idev->phys = "max8925_on/input0";
info->idev->id.bustype = BUS_I2C;
info->idev->dev.parent = &pdev->dev;
info->irq[0] = irq[0];
info->irq[1] = irq[1];
info->idev->evbit[0] = BIT_MASK(EV_KEY);
info->idev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER);
error = request_threaded_irq(info->irq, NULL, max8925_onkey_handler,
IRQF_ONESHOT, "onkey", info);
if (error < 0) {
dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
info->irq, error);
goto out_irq;
}
error = input_register_device(info->idev);
if (error) {
dev_err(chip->dev, "Can't register input device: %d\n", error);
goto out;
goto out_reg;
}
platform_set_drvdata(pdev, info);
return 0;
out:
free_irq(info->irq, info);
out_irq:
out_reg:
input_free_device(info->idev);
out_input:
free_irq(info->irq[1], info);
out_irq:
free_irq(info->irq[0], info);
out:
kfree(info);
return error;
}
......@@ -113,7 +148,8 @@ static int __devexit max8925_onkey_remove(struct platform_device *pdev)
{
struct max8925_onkey_info *info = platform_get_drvdata(pdev);
free_irq(info->irq, info);
free_irq(info->irq[0], info);
free_irq(info->irq[1], info);
input_unregister_device(info->idev);
kfree(info);
......
......@@ -24,26 +24,17 @@
#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_ON_CONTINUOUS (0x0F << 3)
#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;
......@@ -54,8 +45,6 @@ struct pm860x_led {
int port;
int iset;
int command;
int offset;
unsigned char brightness;
unsigned char current_brightness;
......@@ -95,10 +84,12 @@ static inline int __blink_off(int port)
case PM8606_LED1_GREEN:
case PM8606_LED1_BLUE:
ret = PM8606_RGB1A;
break;
case PM8606_LED2_RED:
case PM8606_LED2_GREEN:
case PM8606_LED2_BLUE:
ret = PM8606_RGB2A;
break;
}
return ret;
}
......@@ -122,60 +113,35 @@ static inline int __blink_ctl_mask(int port)
return ret;
}
static int __led_set(struct pm860x_led *led, int command)
static void pm860x_led_work(struct work_struct *work)
{
struct pm860x_chip *chip = led->chip;
int mask, ret;
struct pm860x_led *led;
struct pm860x_chip *chip;
int mask;
led = container_of(work, struct pm860x_led, work);
chip = led->chip;
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;
if ((led->current_brightness == 0) && led->brightness) {
if (led->iset) {
pm860x_set_bits(led->i2c, __led_off(led->port),
LED_CURRENT_MASK, led->iset);
}
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;
pm860x_set_bits(led->i2c, PM8606_WLED3B, mask, mask);
} else if (led->brightness == 0) {
pm860x_set_bits(led->i2c, __led_off(led->port),
LED_CURRENT_MASK, 0);
mask = __blink_ctl_mask(led->port);
pm860x_set_bits(led->i2c, PM8606_WLED3B, mask, 0);
}
out:
pm860x_set_bits(led->i2c, __led_off(led->port), LED_PWM_MASK,
led->brightness);
led->current_brightness = led->brightness;
dev_dbg(chip->dev, "Update LED. (reg:%d, brightness:%d)\n",
__led_off(led->port), led->brightness);
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,
......@@ -183,42 +149,10 @@ static void pm860x_led_set(struct led_classdev *cdev,
{
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;
......@@ -257,7 +191,7 @@ static int pm860x_led_probe(struct platform_device *pdev)
pm860x_pdata = pdev->dev.parent->platform_data;
pdata = pm860x_pdata->led;
} else {
dev_err(&pdev->dev, "missing platform data\n");
dev_err(&pdev->dev, "No platform data!\n");
return -EINVAL;
}
......@@ -279,7 +213,6 @@ static int pm860x_led_probe(struct platform_device *pdev)
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);
......
......@@ -158,6 +158,43 @@ static struct mfd_cell onkey_devs[] = {
},
};
static struct resource codec_resources[] = {
{
/* Headset microphone insertion or removal */
.name = "micin",
.start = PM8607_IRQ_MICIN,
.end = PM8607_IRQ_MICIN,
.flags = IORESOURCE_IRQ,
}, {
/* Hook-switch press or release */
.name = "hook",
.start = PM8607_IRQ_HOOK,
.end = PM8607_IRQ_HOOK,
.flags = IORESOURCE_IRQ,
}, {
/* Headset insertion or removal */
.name = "headset",
.start = PM8607_IRQ_HEADSET,
.end = PM8607_IRQ_HEADSET,
.flags = IORESOURCE_IRQ,
}, {
/* Audio short */
.name = "audio-short",
.start = PM8607_IRQ_AUDIO_SHORT,
.end = PM8607_IRQ_AUDIO_SHORT,
.flags = IORESOURCE_IRQ,
},
};
static struct mfd_cell codec_devs[] = {
{
.name = "88pm860x-codec",
.num_resources = ARRAY_SIZE(codec_resources),
.resources = &codec_resources[0],
.id = -1,
},
};
static struct resource regulator_resources[] = {
PM8607_REG_RESOURCE(BUCK1, BUCK1),
PM8607_REG_RESOURCE(BUCK2, BUCK2),
......@@ -608,10 +645,13 @@ static void __devinit device_8607_init(struct pm860x_chip *chip,
dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
goto out;
}
if ((ret & PM8607_VERSION_MASK) == PM8607_VERSION)
switch (ret & PM8607_VERSION_MASK) {
case 0x40:
case 0x50:
dev_info(chip->dev, "Marvell 88PM8607 (ID: %02x) detected\n",
ret);
else {
break;
default:
dev_err(chip->dev, "Failed to detect Marvell 88PM8607. "
"Chip ID: %02x\n", ret);
goto out;
......@@ -687,6 +727,13 @@ static void __devinit device_8607_init(struct pm860x_chip *chip,
goto out_dev;
}
ret = mfd_add_devices(chip->dev, 0, &codec_devs[0],
ARRAY_SIZE(codec_devs),
&codec_resources[0], 0);
if (ret < 0) {
dev_err(chip->dev, "Failed to add codec subdev\n");
goto out_dev;
}
return;
out_dev:
mfd_remove_devices(chip->dev);
......
......@@ -75,7 +75,7 @@ config MFD_DAVINCI_VOICECODEC
config MFD_DM355EVM_MSP
bool "DaVinci DM355 EVM microcontroller"
depends on I2C && MACH_DAVINCI_DM355_EVM
depends on I2C=y && MACH_DAVINCI_DM355_EVM
help
This driver supports the MSP430 microcontroller used on these
boards. MSP430 firmware manages resets and power sequencing,
......@@ -294,14 +294,15 @@ config MFD_MAX8925
to use the functionality of the device.
config MFD_MAX8998
bool "Maxim Semiconductor MAX8998 PMIC Support"
depends on I2C=y
bool "Maxim Semiconductor MAX8998/National LP3974 PMIC Support"
depends on I2C=y && GENERIC_HARDIRQS
select MFD_CORE
help
Say yes here to support for Maxim Semiconductor MAX8998. 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.
Say yes here to support for Maxim Semiconductor MAX8998 and
National Semiconductor LP3974. 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"
......@@ -314,14 +315,30 @@ config MFD_WM8400
the functionality of the device.
config MFD_WM831X
bool "Support Wolfson Microelectronics WM831x/2x PMICs"
bool
depends on GENERIC_HARDIRQS
config MFD_WM831X_I2C
bool "Support Wolfson Microelectronics WM831x/2x PMICs with I2C"
select MFD_CORE
select MFD_WM831X
depends on I2C=y && GENERIC_HARDIRQS
help
Support for the Wolfson Microelecronics WM831x and WM832x PMICs.
This driver provides common support for accessing the device,
additional drivers must be enabled in order to use the
functionality of the device.
Support for the Wolfson Microelecronics WM831x and WM832x PMICs
when controlled using I2C. This driver provides common support
for accessing the device, additional drivers must be enabled in
order to use the functionality of the device.
config MFD_WM831X_SPI
bool "Support Wolfson Microelectronics WM831x/2x PMICs with SPI"
select MFD_CORE
select MFD_WM831X
depends on SPI_MASTER && GENERIC_HARDIRQS
help
Support for the Wolfson Microelecronics WM831x and WM832x PMICs
when controlled using SPI. This driver provides common support
for accessing the device, additional drivers must be enabled in
order to use the functionality of the device.
config MFD_WM8350
bool
......@@ -408,11 +425,16 @@ config MFD_PCF50633
so that function-specific drivers can bind to them.
config MFD_MC13783
tristate "Support Freescale MC13783"
tristate
config MFD_MC13XXX
tristate "Support Freescale MC13783 and MC13892"
depends on SPI_MASTER
select MFD_CORE
select MFD_MC13783
help
Support for the Freescale (Atlas) MC13783 PMIC and audio CODEC.
Support for the Freescale (Atlas) PMIC and audio CODECs
MC13783 and MC13892.
This driver provides common support for accessing the device,
additional drivers must be enabled in order to use the
functionality of the device.
......@@ -433,7 +455,7 @@ config PCF50633_GPIO
config ABX500_CORE
bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions"
default y if ARCH_U300
default y if ARCH_U300 || ARCH_U8500
help
Say yes here if you have the ABX500 Mixed Signal IC family
chips. This core driver expose register access functions.
......@@ -444,6 +466,7 @@ config ABX500_CORE
config AB3100_CORE
bool "ST-Ericsson AB3100 Mixed Signal Circuit core functions"
depends on I2C=y && ABX500_CORE
select MFD_CORE
default y if ARCH_U300
help
Select this to enable the AB3100 Mixed Signal IC core
......@@ -473,14 +496,33 @@ config EZX_PCAP
config AB8500_CORE
bool "ST-Ericsson AB8500 Mixed Signal Power Management chip"
depends on SPI=y && GENERIC_HARDIRQS
depends on GENERIC_HARDIRQS && ABX500_CORE && SPI_MASTER && ARCH_U8500
select MFD_CORE
help
Select this option to enable access to AB8500 power management
chip. This connects to U8500 on the SSP/SPI bus and exports
read/write functions for the devices to get access to this chip.
chip. This connects to U8500 either on the SSP/SPI bus
or the I2C bus via PRCMU. It also adds the irq_chip
parts for handling the Mixed Signal chip events.
This chip embeds various other multimedia funtionalities as well.
config AB8500_I2C_CORE
bool "AB8500 register access via PRCMU I2C"
depends on AB8500_CORE && UX500_SOC_DB8500
default y
help
This enables register access to the AB8500 chip via PRCMU I2C.
The AB8500 chip can be accessed via SPI or I2C. On DB8500 hardware
the I2C bus is connected to the Power Reset
and Mangagement Unit, PRCMU.
config AB8500_DEBUG
bool "Enable debug info via debugfs"
depends on AB8500_CORE && DEBUG_FS
default y if DEBUG_FS
help
Select this option if you want debug information using the debug
filesystem, debugfs.
config AB3550_CORE
bool "ST-Ericsson AB3550 Mixed Signal Circuit core functions"
select MFD_CORE
......@@ -542,8 +584,8 @@ config MFD_JZ4740_ADC
This driver is necessary for jz4740-battery and jz4740-hwmon driver.
config MFD_TPS6586X
tristate "TPS6586x Power Management chips"
depends on I2C && GPIOLIB
bool "TPS6586x Power Management chips"
depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS
select MFD_CORE
help
If you say yes here you get support for the TPS6586X series of
......@@ -555,6 +597,15 @@ config MFD_TPS6586X
This driver can also be built as a module. If so, the module
will be called tps6586x.
config MFD_VX855
tristate "Support for VIA VX855/VX875 integrated south bridge"
depends on PCI
select MFD_CORE
help
Say yes here to enable support for various functions of the
VIA VX855/VX875 south bridge. You will need to enable the vx855_spi
and/or vx855_gpio drivers for this to do anything useful.
endif # MFD_SUPPORT
menu "Multimedia Capabilities Port drivers"
......
......@@ -24,6 +24,8 @@ obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o tmio_core.o
obj-$(CONFIG_MFD_WM8400) += wm8400-core.o
wm831x-objs := wm831x-core.o wm831x-irq.o wm831x-otp.o
obj-$(CONFIG_MFD_WM831X) += wm831x.o
obj-$(CONFIG_MFD_WM831X_I2C) += wm831x-i2c.o
obj-$(CONFIG_MFD_WM831X_SPI) += wm831x-spi.o
wm8350-objs := wm8350-core.o wm8350-regmap.o wm8350-gpio.o
wm8350-objs += wm8350-irq.o
obj-$(CONFIG_MFD_WM8350) += wm8350.o
......@@ -39,7 +41,7 @@ obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o
obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o
obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o
obj-$(CONFIG_MFD_MC13783) += mc13783-core.o
obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o
obj-$(CONFIG_MFD_CORE) += mfd-core.o
......@@ -58,7 +60,7 @@ 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_MAX8998) += max8998.o
obj-$(CONFIG_MFD_MAX8998) += max8998.o max8998-irq.o
pcf50633-objs := pcf50633-core.o pcf50633-irq.o
obj-$(CONFIG_MFD_PCF50633) += pcf50633.o
......@@ -69,6 +71,8 @@ obj-$(CONFIG_AB3100_CORE) += ab3100-core.o
obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o
obj-$(CONFIG_AB3550_CORE) += ab3550-core.o
obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-spi.o
obj-$(CONFIG_AB8500_I2C_CORE) += ab8500-i2c.o
obj-$(CONFIG_AB8500_DEBUG) += ab8500-debugfs.o
obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o
obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
obj-$(CONFIG_LPC_SCH) += lpc_sch.o
......@@ -76,3 +80,4 @@ obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o
obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o
obj-$(CONFIG_MFD_JZ4740_ADC) += jz4740-adc.o
obj-$(CONFIG_MFD_TPS6586X) += tps6586x.o
obj-$(CONFIG_MFD_VX855) += vx855.o
......@@ -19,6 +19,7 @@
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/mfd/core.h>
#include <linux/mfd/abx500.h>
/* These are the only registers inside AB3100 used in this main file */
......@@ -146,7 +147,7 @@ static int ab3100_set_test_register_interruptible(struct ab3100 *ab3100,
}
static int ab3100_get_register_interruptible(struct ab3100 *ab3100,
u8 reg, u8 *regval)
u8 reg, u8 *regval)
{
int err;
......@@ -202,7 +203,7 @@ static int ab3100_get_register_interruptible(struct ab3100 *ab3100,
}
static int get_register_interruptible(struct device *dev, u8 bank, u8 reg,
u8 *value)
u8 *value)
{
struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
......@@ -666,7 +667,7 @@ struct ab3100_init_setting {
u8 setting;
};
static const struct ab3100_init_setting __initconst
static const struct ab3100_init_setting __devinitconst
ab3100_init_settings[] = {
{
.abreg = AB3100_MCA,
......@@ -713,7 +714,7 @@ ab3100_init_settings[] = {
},
};
static int __init ab3100_setup(struct ab3100 *ab3100)
static int __devinit ab3100_setup(struct ab3100 *ab3100)
{
int err = 0;
int i;
......@@ -743,52 +744,64 @@ static int __init ab3100_setup(struct ab3100 *ab3100)
return err;
}
/*
* Here we define all the platform devices that appear
* as children of the AB3100. These are regular platform
* devices with the IORESOURCE_IO .start and .end set
* to correspond to the internal AB3100 register range
* mapping to the corresponding subdevice.
*/
#define AB3100_DEVICE(devname, devid) \
static struct platform_device ab3100_##devname##_device = { \
.name = devid, \
.id = -1, \
}
/* This lists all the subdevices */
AB3100_DEVICE(dac, "ab3100-dac");
AB3100_DEVICE(leds, "ab3100-leds");
AB3100_DEVICE(power, "ab3100-power");
AB3100_DEVICE(regulators, "ab3100-regulators");
AB3100_DEVICE(sim, "ab3100-sim");
AB3100_DEVICE(uart, "ab3100-uart");
AB3100_DEVICE(rtc, "ab3100-rtc");
AB3100_DEVICE(charger, "ab3100-charger");
AB3100_DEVICE(boost, "ab3100-boost");
AB3100_DEVICE(adc, "ab3100-adc");
AB3100_DEVICE(fuelgauge, "ab3100-fuelgauge");
AB3100_DEVICE(vibrator, "ab3100-vibrator");
AB3100_DEVICE(otp, "ab3100-otp");
AB3100_DEVICE(codec, "ab3100-codec");
static struct platform_device *
ab3100_platform_devs[] = {
&ab3100_dac_device,
&ab3100_leds_device,
&ab3100_power_device,
&ab3100_regulators_device,
&ab3100_sim_device,
&ab3100_uart_device,
&ab3100_rtc_device,
&ab3100_charger_device,
&ab3100_boost_device,
&ab3100_adc_device,
&ab3100_fuelgauge_device,
&ab3100_vibrator_device,
&ab3100_otp_device,
&ab3100_codec_device,
/* The subdevices of the AB3100 */
static struct mfd_cell ab3100_devs[] = {
{
.name = "ab3100-dac",
.id = -1,
},
{
.name = "ab3100-leds",
.id = -1,
},
{
.name = "ab3100-power",
.id = -1,
},
{
.name = "ab3100-regulators",
.id = -1,
},
{
.name = "ab3100-sim",
.id = -1,
},
{
.name = "ab3100-uart",
.id = -1,
},
{
.name = "ab3100-rtc",
.id = -1,
},
{
.name = "ab3100-charger",
.id = -1,
},
{
.name = "ab3100-boost",
.id = -1,
},
{
.name = "ab3100-adc",
.id = -1,
},
{
.name = "ab3100-fuelgauge",
.id = -1,
},
{
.name = "ab3100-vibrator",
.id = -1,
},
{
.name = "ab3100-otp",
.id = -1,
},
{
.name = "ab3100-codec",
.id = -1,
},
};
struct ab_family_id {
......@@ -796,7 +809,7 @@ struct ab_family_id {
char *name;
};
static const struct ab_family_id ids[] __initdata = {
static const struct ab_family_id ids[] __devinitdata = {
/* AB3100 */
{
.id = 0xc0,
......@@ -850,8 +863,8 @@ static const struct ab_family_id ids[] __initdata = {
},
};
static int __init ab3100_probe(struct i2c_client *client,
const struct i2c_device_id *id)
static int __devinit ab3100_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct ab3100 *ab3100;
struct ab3100_platform_data *ab3100_plf_data =
......@@ -935,18 +948,14 @@ static int __init ab3100_probe(struct i2c_client *client,
if (err)
goto exit_no_ops;
/* Set parent and a pointer back to the container in device data */
for (i = 0; i < ARRAY_SIZE(ab3100_platform_devs); i++) {
ab3100_platform_devs[i]->dev.parent =
&client->dev;
ab3100_platform_devs[i]->dev.platform_data =
ab3100_plf_data;
platform_set_drvdata(ab3100_platform_devs[i], ab3100);
/* Set up and register the platform devices. */
for (i = 0; i < ARRAY_SIZE(ab3100_devs); i++) {
ab3100_devs[i].platform_data = ab3100_plf_data;
ab3100_devs[i].data_size = sizeof(struct ab3100_platform_data);
}
/* Register the platform devices */
platform_add_devices(ab3100_platform_devs,
ARRAY_SIZE(ab3100_platform_devs));
err = mfd_add_devices(&client->dev, 0, ab3100_devs,
ARRAY_SIZE(ab3100_devs), NULL, 0);
ab3100_setup_debugfs(ab3100);
......@@ -962,14 +971,12 @@ static int __init ab3100_probe(struct i2c_client *client,
return err;
}
static int __exit ab3100_remove(struct i2c_client *client)
static int __devexit ab3100_remove(struct i2c_client *client)
{
struct ab3100 *ab3100 = i2c_get_clientdata(client);
int i;
/* Unregister subdevices */
for (i = 0; i < ARRAY_SIZE(ab3100_platform_devs); i++)
platform_device_unregister(ab3100_platform_devs[i]);
mfd_remove_devices(&client->dev);
ab3100_remove_debugfs();
i2c_unregister_device(ab3100->testreg_client);
......@@ -996,7 +1003,7 @@ static struct i2c_driver ab3100_driver = {
},
.id_table = ab3100_id,
.probe = ab3100_probe,
.remove = __exit_p(ab3100_remove),
.remove = __devexit_p(ab3100_remove),
};
static int __init ab3100_i2c_init(void)
......
......@@ -4,6 +4,7 @@
* License Terms: GNU General Public License v2
* Author: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com>
* Author: Rabin Vincent <rabin.vincent@stericsson.com>
* Changes: Mattias Wallin <mattias.wallin@stericsson.com>
*/
#include <linux/kernel.h>
......@@ -15,6 +16,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mfd/core.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/ab8500.h>
#include <linux/regulator/ab8500.h>
......@@ -22,71 +24,71 @@
* Interrupt register offsets
* Bank : 0x0E
*/
#define AB8500_IT_SOURCE1_REG 0x0E00
#define AB8500_IT_SOURCE2_REG 0x0E01
#define AB8500_IT_SOURCE3_REG 0x0E02
#define AB8500_IT_SOURCE4_REG 0x0E03
#define AB8500_IT_SOURCE5_REG 0x0E04
#define AB8500_IT_SOURCE6_REG 0x0E05
#define AB8500_IT_SOURCE7_REG 0x0E06
#define AB8500_IT_SOURCE8_REG 0x0E07
#define AB8500_IT_SOURCE19_REG 0x0E12
#define AB8500_IT_SOURCE20_REG 0x0E13
#define AB8500_IT_SOURCE21_REG 0x0E14
#define AB8500_IT_SOURCE22_REG 0x0E15
#define AB8500_IT_SOURCE23_REG 0x0E16
#define AB8500_IT_SOURCE24_REG 0x0E17
#define AB8500_IT_SOURCE1_REG 0x00
#define AB8500_IT_SOURCE2_REG 0x01
#define AB8500_IT_SOURCE3_REG 0x02
#define AB8500_IT_SOURCE4_REG 0x03
#define AB8500_IT_SOURCE5_REG 0x04
#define AB8500_IT_SOURCE6_REG 0x05
#define AB8500_IT_SOURCE7_REG 0x06
#define AB8500_IT_SOURCE8_REG 0x07
#define AB8500_IT_SOURCE19_REG 0x12
#define AB8500_IT_SOURCE20_REG 0x13
#define AB8500_IT_SOURCE21_REG 0x14
#define AB8500_IT_SOURCE22_REG 0x15
#define AB8500_IT_SOURCE23_REG 0x16
#define AB8500_IT_SOURCE24_REG 0x17
/*
* latch registers
*/
#define AB8500_IT_LATCH1_REG 0x0E20
#define AB8500_IT_LATCH2_REG 0x0E21
#define AB8500_IT_LATCH3_REG 0x0E22
#define AB8500_IT_LATCH4_REG 0x0E23
#define AB8500_IT_LATCH5_REG 0x0E24
#define AB8500_IT_LATCH6_REG 0x0E25
#define AB8500_IT_LATCH7_REG 0x0E26
#define AB8500_IT_LATCH8_REG 0x0E27
#define AB8500_IT_LATCH9_REG 0x0E28
#define AB8500_IT_LATCH10_REG 0x0E29
#define AB8500_IT_LATCH19_REG 0x0E32
#define AB8500_IT_LATCH20_REG 0x0E33
#define AB8500_IT_LATCH21_REG 0x0E34
#define AB8500_IT_LATCH22_REG 0x0E35
#define AB8500_IT_LATCH23_REG 0x0E36
#define AB8500_IT_LATCH24_REG 0x0E37
#define AB8500_IT_LATCH1_REG 0x20
#define AB8500_IT_LATCH2_REG 0x21
#define AB8500_IT_LATCH3_REG 0x22
#define AB8500_IT_LATCH4_REG 0x23
#define AB8500_IT_LATCH5_REG 0x24
#define AB8500_IT_LATCH6_REG 0x25
#define AB8500_IT_LATCH7_REG 0x26
#define AB8500_IT_LATCH8_REG 0x27
#define AB8500_IT_LATCH9_REG 0x28
#define AB8500_IT_LATCH10_REG 0x29
#define AB8500_IT_LATCH19_REG 0x32
#define AB8500_IT_LATCH20_REG 0x33
#define AB8500_IT_LATCH21_REG 0x34
#define AB8500_IT_LATCH22_REG 0x35
#define AB8500_IT_LATCH23_REG 0x36
#define AB8500_IT_LATCH24_REG 0x37
/*
* mask registers
*/
#define AB8500_IT_MASK1_REG 0x0E40
#define AB8500_IT_MASK2_REG 0x0E41
#define AB8500_IT_MASK3_REG 0x0E42
#define AB8500_IT_MASK4_REG 0x0E43
#define AB8500_IT_MASK5_REG 0x0E44
#define AB8500_IT_MASK6_REG 0x0E45
#define AB8500_IT_MASK7_REG 0x0E46
#define AB8500_IT_MASK8_REG 0x0E47
#define AB8500_IT_MASK9_REG 0x0E48
#define AB8500_IT_MASK10_REG 0x0E49
#define AB8500_IT_MASK11_REG 0x0E4A
#define AB8500_IT_MASK12_REG 0x0E4B
#define AB8500_IT_MASK13_REG 0x0E4C
#define AB8500_IT_MASK14_REG 0x0E4D
#define AB8500_IT_MASK15_REG 0x0E4E
#define AB8500_IT_MASK16_REG 0x0E4F
#define AB8500_IT_MASK17_REG 0x0E50
#define AB8500_IT_MASK18_REG 0x0E51
#define AB8500_IT_MASK19_REG 0x0E52
#define AB8500_IT_MASK20_REG 0x0E53
#define AB8500_IT_MASK21_REG 0x0E54
#define AB8500_IT_MASK22_REG 0x0E55
#define AB8500_IT_MASK23_REG 0x0E56
#define AB8500_IT_MASK24_REG 0x0E57
#define AB8500_REV_REG 0x1080
#define AB8500_IT_MASK1_REG 0x40
#define AB8500_IT_MASK2_REG 0x41
#define AB8500_IT_MASK3_REG 0x42
#define AB8500_IT_MASK4_REG 0x43
#define AB8500_IT_MASK5_REG 0x44
#define AB8500_IT_MASK6_REG 0x45
#define AB8500_IT_MASK7_REG 0x46
#define AB8500_IT_MASK8_REG 0x47
#define AB8500_IT_MASK9_REG 0x48
#define AB8500_IT_MASK10_REG 0x49
#define AB8500_IT_MASK11_REG 0x4A
#define AB8500_IT_MASK12_REG 0x4B
#define AB8500_IT_MASK13_REG 0x4C
#define AB8500_IT_MASK14_REG 0x4D
#define AB8500_IT_MASK15_REG 0x4E
#define AB8500_IT_MASK16_REG 0x4F
#define AB8500_IT_MASK17_REG 0x50
#define AB8500_IT_MASK18_REG 0x51
#define AB8500_IT_MASK19_REG 0x52
#define AB8500_IT_MASK20_REG 0x53
#define AB8500_IT_MASK21_REG 0x54
#define AB8500_IT_MASK22_REG 0x55
#define AB8500_IT_MASK23_REG 0x56
#define AB8500_IT_MASK24_REG 0x57
#define AB8500_REV_REG 0x80
/*
* Map interrupt numbers to the LATCH and MASK register offsets, Interrupt
......@@ -99,96 +101,132 @@ static const int ab8500_irq_regoffset[AB8500_NUM_IRQ_REGS] = {
0, 1, 2, 3, 4, 6, 7, 8, 9, 18, 19, 20, 21,
};
static int __ab8500_write(struct ab8500 *ab8500, u16 addr, u8 data)
static int ab8500_get_chip_id(struct device *dev)
{
struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
return (int)ab8500->chip_id;
}
static int set_register_interruptible(struct ab8500 *ab8500, u8 bank,
u8 reg, u8 data)
{
int ret;
/*
* Put the u8 bank and u8 register together into a an u16.
* The bank on higher 8 bits and register in lower 8 bits.
* */
u16 addr = ((u16)bank) << 8 | reg;
dev_vdbg(ab8500->dev, "wr: addr %#x <= %#x\n", addr, data);
ret = mutex_lock_interruptible(&ab8500->lock);
if (ret)
return ret;
ret = ab8500->write(ab8500, addr, data);
if (ret < 0)
dev_err(ab8500->dev, "failed to write reg %#x: %d\n",
addr, ret);
mutex_unlock(&ab8500->lock);
return ret;
}
/**
* ab8500_write() - write an AB8500 register
* @ab8500: device to write to
* @addr: address of the register
* @data: value to write
*/
int ab8500_write(struct ab8500 *ab8500, u16 addr, u8 data)
static int ab8500_set_register(struct device *dev, u8 bank,
u8 reg, u8 value)
{
int ret;
struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
mutex_lock(&ab8500->lock);
ret = __ab8500_write(ab8500, addr, data);
mutex_unlock(&ab8500->lock);
return ret;
return set_register_interruptible(ab8500, bank, reg, value);
}
EXPORT_SYMBOL_GPL(ab8500_write);
static int __ab8500_read(struct ab8500 *ab8500, u16 addr)
static int get_register_interruptible(struct ab8500 *ab8500, u8 bank,
u8 reg, u8 *value)
{
int ret;
/* put the u8 bank and u8 reg together into a an u16.
* bank on higher 8 bits and reg in lower */
u16 addr = ((u16)bank) << 8 | reg;
ret = mutex_lock_interruptible(&ab8500->lock);
if (ret)
return ret;
ret = ab8500->read(ab8500, addr);
if (ret < 0)
dev_err(ab8500->dev, "failed to read reg %#x: %d\n",
addr, ret);
else
*value = ret;
mutex_unlock(&ab8500->lock);
dev_vdbg(ab8500->dev, "rd: addr %#x => data %#x\n", addr, ret);
return ret;
}
/**
* ab8500_read() - read an AB8500 register
* @ab8500: device to read from
* @addr: address of the register
*/
int ab8500_read(struct ab8500 *ab8500, u16 addr)
static int ab8500_get_register(struct device *dev, u8 bank,
u8 reg, u8 *value)
{
int ret;
mutex_lock(&ab8500->lock);
ret = __ab8500_read(ab8500, addr);
mutex_unlock(&ab8500->lock);
struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
return ret;
return get_register_interruptible(ab8500, bank, reg, value);
}
EXPORT_SYMBOL_GPL(ab8500_read);
/**
* ab8500_set_bits() - set a bitfield in an AB8500 register
* @ab8500: device to read from
* @addr: address of the register
* @mask: mask of the bitfield to modify
* @data: value to set to the bitfield
*/
int ab8500_set_bits(struct ab8500 *ab8500, u16 addr, u8 mask, u8 data)
static int mask_and_set_register_interruptible(struct ab8500 *ab8500, u8 bank,
u8 reg, u8 bitmask, u8 bitvalues)
{
int ret;
u8 data;
/* put the u8 bank and u8 reg together into a an u16.
* bank on higher 8 bits and reg in lower */
u16 addr = ((u16)bank) << 8 | reg;
mutex_lock(&ab8500->lock);
ret = mutex_lock_interruptible(&ab8500->lock);
if (ret)
return ret;
ret = __ab8500_read(ab8500, addr);
if (ret < 0)
ret = ab8500->read(ab8500, addr);
if (ret < 0) {
dev_err(ab8500->dev, "failed to read reg %#x: %d\n",
addr, ret);
goto out;
}
ret &= ~mask;
ret |= data;
data = (u8)ret;
data = (~bitmask & data) | (bitmask & bitvalues);
ret = __ab8500_write(ab8500, addr, ret);
ret = ab8500->write(ab8500, addr, data);
if (ret < 0)
dev_err(ab8500->dev, "failed to write reg %#x: %d\n",
addr, ret);
dev_vdbg(ab8500->dev, "mask: addr %#x => data %#x\n", addr, data);
out:
mutex_unlock(&ab8500->lock);
return ret;
}
EXPORT_SYMBOL_GPL(ab8500_set_bits);
static int ab8500_mask_and_set_register(struct device *dev,
u8 bank, u8 reg, u8 bitmask, u8 bitvalues)
{
struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
return mask_and_set_register_interruptible(ab8500, bank, reg,
bitmask, bitvalues);
}
static struct abx500_ops ab8500_ops = {
.get_chip_id = ab8500_get_chip_id,
.get_register = ab8500_get_register,
.set_register = ab8500_set_register,
.get_register_page = NULL,
.set_register_page = NULL,
.mask_and_set_register = ab8500_mask_and_set_register,
.event_registers_startup_state_get = NULL,
.startup_irq_enabled = NULL,
};
static void ab8500_irq_lock(unsigned int irq)
{
......@@ -213,7 +251,7 @@ static void ab8500_irq_sync_unlock(unsigned int irq)
ab8500->oldmask[i] = new;
reg = AB8500_IT_MASK1_REG + ab8500_irq_regoffset[i];
ab8500_write(ab8500, reg, new);
set_register_interruptible(ab8500, AB8500_INTERRUPT, reg, new);
}
mutex_unlock(&ab8500->irq_lock);
......@@ -257,9 +295,11 @@ static irqreturn_t ab8500_irq(int irq, void *dev)
for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) {
int regoffset = ab8500_irq_regoffset[i];
int status;
u8 value;
status = ab8500_read(ab8500, AB8500_IT_LATCH1_REG + regoffset);
if (status <= 0)
status = get_register_interruptible(ab8500, AB8500_INTERRUPT,
AB8500_IT_LATCH1_REG + regoffset, &value);
if (status < 0 || value == 0)
continue;
do {
......@@ -267,8 +307,8 @@ static irqreturn_t ab8500_irq(int irq, void *dev)
int line = i * 8 + bit;
handle_nested_irq(ab8500->irq_base + line);
status &= ~(1 << bit);
} while (status);
value &= ~(1 << bit);
} while (value);
}
return IRQ_HANDLED;
......@@ -354,6 +394,11 @@ static struct resource ab8500_poweronkey_db_resources[] = {
};
static struct mfd_cell ab8500_devs[] = {
#ifdef CONFIG_DEBUG_FS
{
.name = "ab8500-debug",
},
#endif
{
.name = "ab8500-gpadc",
.num_resources = ARRAY_SIZE(ab8500_gpadc_resources),
......@@ -364,10 +409,21 @@ static struct mfd_cell ab8500_devs[] = {
.num_resources = ARRAY_SIZE(ab8500_rtc_resources),
.resources = ab8500_rtc_resources,
},
{
.name = "ab8500-pwm",
.id = 1,
},
{
.name = "ab8500-pwm",
.id = 2,
},
{
.name = "ab8500-pwm",
.id = 3,
},
{ .name = "ab8500-charger", },
{ .name = "ab8500-audio", },
{ .name = "ab8500-usb", },
{ .name = "ab8500-pwm", },
{ .name = "ab8500-regulator", },
{
.name = "ab8500-poweron-key",
......@@ -381,6 +437,7 @@ int __devinit ab8500_init(struct ab8500 *ab8500)
struct ab8500_platform_data *plat = dev_get_platdata(ab8500->dev);
int ret;
int i;
u8 value;
if (plat)
ab8500->irq_base = plat->irq_base;
......@@ -388,7 +445,8 @@ int __devinit ab8500_init(struct ab8500 *ab8500)
mutex_init(&ab8500->lock);
mutex_init(&ab8500->irq_lock);
ret = ab8500_read(ab8500, AB8500_REV_REG);
ret = get_register_interruptible(ab8500, AB8500_MISC,
AB8500_REV_REG, &value);
if (ret < 0)
return ret;
......@@ -397,28 +455,37 @@ int __devinit ab8500_init(struct ab8500 *ab8500)
* 0x10 - Cut 1.0
* 0x11 - Cut 1.1
*/
if (ret == 0x0 || ret == 0x10 || ret == 0x11) {
ab8500->revision = ret;
dev_info(ab8500->dev, "detected chip, revision: %#x\n", ret);
if (value == 0x0 || value == 0x10 || value == 0x11) {
ab8500->revision = value;
dev_info(ab8500->dev, "detected chip, revision: %#x\n", value);
} else {
dev_err(ab8500->dev, "unknown chip, revision: %#x\n", ret);
dev_err(ab8500->dev, "unknown chip, revision: %#x\n", value);
return -EINVAL;
}
ab8500->chip_id = value;
if (plat && plat->init)
plat->init(ab8500);
/* Clear and mask all interrupts */
for (i = 0; i < 10; i++) {
ab8500_read(ab8500, AB8500_IT_LATCH1_REG + i);
ab8500_write(ab8500, AB8500_IT_MASK1_REG + i, 0xff);
get_register_interruptible(ab8500, AB8500_INTERRUPT,
AB8500_IT_LATCH1_REG + i, &value);
set_register_interruptible(ab8500, AB8500_INTERRUPT,
AB8500_IT_MASK1_REG + i, 0xff);
}
for (i = 18; i < 24; i++) {
ab8500_read(ab8500, AB8500_IT_LATCH1_REG + i);
ab8500_write(ab8500, AB8500_IT_MASK1_REG + i, 0xff);
get_register_interruptible(ab8500, AB8500_INTERRUPT,
AB8500_IT_LATCH1_REG + i, &value);
set_register_interruptible(ab8500, AB8500_INTERRUPT,
AB8500_IT_MASK1_REG + i, 0xff);
}
ret = abx500_register_ops(ab8500->dev, &ab8500_ops);
if (ret)
return ret;
for (i = 0; i < AB8500_NUM_IRQ_REGS; i++)
ab8500->mask[i] = ab8500->oldmask[i] = 0xff;
......
此差异已折叠。
/*
* Copyright (C) ST-Ericsson SA 2010
* Author: Mattias Wallin <mattias.wallin@stericsson.com> for ST-Ericsson.
* License Terms: GNU General Public License v2
* This file was based on drivers/mfd/ab8500-spi.c
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mfd/ab8500.h>
#include <mach/prcmu.h>
static int ab8500_i2c_write(struct ab8500 *ab8500, u16 addr, u8 data)
{
int ret;
ret = prcmu_abb_write((u8)(addr >> 8), (u8)(addr & 0xFF), &data, 1);
if (ret < 0)
dev_err(ab8500->dev, "prcmu i2c error %d\n", ret);
return ret;
}
static int ab8500_i2c_read(struct ab8500 *ab8500, u16 addr)
{
int ret;
u8 data;
ret = prcmu_abb_read((u8)(addr >> 8), (u8)(addr & 0xFF), &data, 1);
if (ret < 0) {
dev_err(ab8500->dev, "prcmu i2c error %d\n", ret);
return ret;
}
return (int)data;
}
static int __devinit ab8500_i2c_probe(struct platform_device *plf)
{
struct ab8500 *ab8500;
struct resource *resource;
int ret;
ab8500 = kzalloc(sizeof *ab8500, GFP_KERNEL);
if (!ab8500)
return -ENOMEM;
ab8500->dev = &plf->dev;
resource = platform_get_resource(plf, IORESOURCE_IRQ, 0);
if (!resource) {
kfree(ab8500);
return -ENODEV;
}
ab8500->irq = resource->start;
ab8500->read = ab8500_i2c_read;
ab8500->write = ab8500_i2c_write;
platform_set_drvdata(plf, ab8500);
ret = ab8500_init(ab8500);
if (ret)
kfree(ab8500);
return ret;
}
static int __devexit ab8500_i2c_remove(struct platform_device *plf)
{
struct ab8500 *ab8500 = platform_get_drvdata(plf);
ab8500_exit(ab8500);
kfree(ab8500);
return 0;
}
static struct platform_driver ab8500_i2c_driver = {
.driver = {
.name = "ab8500-i2c",
.owner = THIS_MODULE,
},
.probe = ab8500_i2c_probe,
.remove = __devexit_p(ab8500_i2c_remove)
};
static int __init ab8500_i2c_init(void)
{
return platform_driver_register(&ab8500_i2c_driver);
}
static void __exit ab8500_i2c_exit(void)
{
platform_driver_unregister(&ab8500_i2c_driver);
}
subsys_initcall(ab8500_i2c_init);
module_exit(ab8500_i2c_exit);
MODULE_AUTHOR("Mattias WALLIN <mattias.wallin@stericsson.com");
MODULE_DESCRIPTION("AB8500 Core access via PRCMU I2C");
MODULE_LICENSE("GPL v2");
......@@ -119,7 +119,7 @@ static int __devexit ab8500_spi_remove(struct spi_device *spi)
static struct spi_driver ab8500_spi_driver = {
.driver = {
.name = "ab8500",
.name = "ab8500-spi",
.owner = THIS_MODULE,
},
.probe = ab8500_spi_probe,
......
......@@ -470,13 +470,19 @@ static int __devinit da903x_add_subdevs(struct da903x_chip *chip,
subdev = &pdata->subdevs[i];
pdev = platform_device_alloc(subdev->name, subdev->id);
if (!pdev) {
ret = -ENOMEM;
goto failed;
}
pdev->dev.parent = chip->dev;
pdev->dev.platform_data = subdev->platform_data;
ret = platform_device_add(pdev);
if (ret)
if (ret) {
platform_device_put(pdev);
goto failed;
}
}
return 0;
......
......@@ -384,12 +384,20 @@ static int __devinit pcap_add_subdev(struct pcap_chip *pcap,
struct pcap_subdev *subdev)
{
struct platform_device *pdev;
int ret;
pdev = platform_device_alloc(subdev->name, subdev->id);
if (!pdev)
return -ENOMEM;
pdev->dev.parent = &pcap->spi->dev;
pdev->dev.platform_data = subdev->platform_data;
return platform_device_add(pdev);
ret = platform_device_add(pdev);
if (ret)
platform_device_put(pdev);
return ret;
}
static int __devexit ezx_pcap_remove(struct spi_device *spi)
......@@ -457,6 +465,7 @@ static int __devinit ezx_pcap_probe(struct spi_device *spi)
pcap->irq_base = pdata->irq_base;
pcap->workqueue = create_singlethread_workqueue("pcapd");
if (!pcap->workqueue) {
ret = -ENOMEM;
dev_err(&spi->dev, "cant create pcap thread\n");
goto free_pcap;
}
......
......@@ -138,13 +138,6 @@ static int __init pasic3_probe(struct platform_device *pdev)
irq = r->start;
}
r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (r) {
ds1wm_resources[1].flags = IORESOURCE_IRQ | (r->flags &
(IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE));
irq = r->start;
}
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r)
return -ENXIO;
......
......@@ -153,7 +153,7 @@ static inline void jz4740_adc_set_enabled(struct jz4740_adc *adc, int engine,
if (enabled)
val |= BIT(engine);
else
val &= BIT(engine);
val &= ~BIT(engine);
writeb(val, adc->base + JZ_REG_ADC_ENABLE);
spin_unlock_irqrestore(&adc->lock, flags);
......
......@@ -93,8 +93,13 @@ static struct mfd_cell rtc_devs[] = {
static struct resource onkey_resources[] = {
{
.name = "max8925-onkey",
.start = MAX8925_IRQ_GPM_SW_3SEC,
.end = MAX8925_IRQ_GPM_SW_3SEC,
.start = MAX8925_IRQ_GPM_SW_R,
.end = MAX8925_IRQ_GPM_SW_R,
.flags = IORESOURCE_IRQ,
}, {
.name = "max8925-onkey",
.start = MAX8925_IRQ_GPM_SW_F,
.end = MAX8925_IRQ_GPM_SW_F,
.flags = IORESOURCE_IRQ,
},
};
......@@ -102,7 +107,7 @@ static struct resource onkey_resources[] = {
static struct mfd_cell onkey_devs[] = {
{
.name = "max8925-onkey",
.num_resources = 1,
.num_resources = 2,
.resources = &onkey_resources[0],
.id = -1,
},
......
/*
* Interrupt controller support for MAX8998
*
* Copyright (C) 2010 Samsung Electronics Co.Ltd
* Author: Joonyoung Shim <jy0922.shim@samsung.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/device.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/mfd/max8998-private.h>
struct max8998_irq_data {
int reg;
int mask;
};
static struct max8998_irq_data max8998_irqs[] = {
[MAX8998_IRQ_DCINF] = {
.reg = 1,
.mask = MAX8998_IRQ_DCINF_MASK,
},
[MAX8998_IRQ_DCINR] = {
.reg = 1,
.mask = MAX8998_IRQ_DCINR_MASK,
},
[MAX8998_IRQ_JIGF] = {
.reg = 1,
.mask = MAX8998_IRQ_JIGF_MASK,
},
[MAX8998_IRQ_JIGR] = {
.reg = 1,
.mask = MAX8998_IRQ_JIGR_MASK,
},
[MAX8998_IRQ_PWRONF] = {
.reg = 1,
.mask = MAX8998_IRQ_PWRONF_MASK,
},
[MAX8998_IRQ_PWRONR] = {
.reg = 1,
.mask = MAX8998_IRQ_PWRONR_MASK,
},
[MAX8998_IRQ_WTSREVNT] = {
.reg = 2,
.mask = MAX8998_IRQ_WTSREVNT_MASK,
},
[MAX8998_IRQ_SMPLEVNT] = {
.reg = 2,
.mask = MAX8998_IRQ_SMPLEVNT_MASK,
},
[MAX8998_IRQ_ALARM1] = {
.reg = 2,
.mask = MAX8998_IRQ_ALARM1_MASK,
},
[MAX8998_IRQ_ALARM0] = {
.reg = 2,
.mask = MAX8998_IRQ_ALARM0_MASK,
},
[MAX8998_IRQ_ONKEY1S] = {
.reg = 3,
.mask = MAX8998_IRQ_ONKEY1S_MASK,
},
[MAX8998_IRQ_TOPOFFR] = {
.reg = 3,
.mask = MAX8998_IRQ_TOPOFFR_MASK,
},
[MAX8998_IRQ_DCINOVPR] = {
.reg = 3,
.mask = MAX8998_IRQ_DCINOVPR_MASK,
},
[MAX8998_IRQ_CHGRSTF] = {
.reg = 3,
.mask = MAX8998_IRQ_CHGRSTF_MASK,
},
[MAX8998_IRQ_DONER] = {
.reg = 3,
.mask = MAX8998_IRQ_DONER_MASK,
},
[MAX8998_IRQ_CHGFAULT] = {
.reg = 3,
.mask = MAX8998_IRQ_CHGFAULT_MASK,
},
[MAX8998_IRQ_LOBAT1] = {
.reg = 4,
.mask = MAX8998_IRQ_LOBAT1_MASK,
},
[MAX8998_IRQ_LOBAT2] = {
.reg = 4,
.mask = MAX8998_IRQ_LOBAT2_MASK,
},
};
static inline struct max8998_irq_data *
irq_to_max8998_irq(struct max8998_dev *max8998, int irq)
{
return &max8998_irqs[irq - max8998->irq_base];
}
static void max8998_irq_lock(unsigned int irq)
{
struct max8998_dev *max8998 = get_irq_chip_data(irq);
mutex_lock(&max8998->irqlock);
}
static void max8998_irq_sync_unlock(unsigned int irq)
{
struct max8998_dev *max8998 = get_irq_chip_data(irq);
int i;
for (i = 0; i < ARRAY_SIZE(max8998->irq_masks_cur); i++) {
/*
* If there's been a change in the mask write it back
* to the hardware.
*/
if (max8998->irq_masks_cur[i] != max8998->irq_masks_cache[i]) {
max8998->irq_masks_cache[i] = max8998->irq_masks_cur[i];
max8998_write_reg(max8998->i2c, MAX8998_REG_IRQM1 + i,
max8998->irq_masks_cur[i]);
}
}
mutex_unlock(&max8998->irqlock);
}
static void max8998_irq_unmask(unsigned int irq)
{
struct max8998_dev *max8998 = get_irq_chip_data(irq);
struct max8998_irq_data *irq_data = irq_to_max8998_irq(max8998, irq);
max8998->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
}
static void max8998_irq_mask(unsigned int irq)
{
struct max8998_dev *max8998 = get_irq_chip_data(irq);
struct max8998_irq_data *irq_data = irq_to_max8998_irq(max8998, irq);
max8998->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
}
static struct irq_chip max8998_irq_chip = {
.name = "max8998",
.bus_lock = max8998_irq_lock,
.bus_sync_unlock = max8998_irq_sync_unlock,
.mask = max8998_irq_mask,
.unmask = max8998_irq_unmask,
};
static irqreturn_t max8998_irq_thread(int irq, void *data)
{
struct max8998_dev *max8998 = data;
u8 irq_reg[MAX8998_NUM_IRQ_REGS];
int ret;
int i;
ret = max8998_bulk_read(max8998->i2c, MAX8998_REG_IRQ1,
MAX8998_NUM_IRQ_REGS, irq_reg);
if (ret < 0) {
dev_err(max8998->dev, "Failed to read interrupt register: %d\n",
ret);
return IRQ_NONE;
}
/* Apply masking */
for (i = 0; i < MAX8998_NUM_IRQ_REGS; i++)
irq_reg[i] &= ~max8998->irq_masks_cur[i];
/* Report */
for (i = 0; i < MAX8998_IRQ_NR; i++) {
if (irq_reg[max8998_irqs[i].reg - 1] & max8998_irqs[i].mask)
handle_nested_irq(max8998->irq_base + i);
}
return IRQ_HANDLED;
}
int max8998_irq_init(struct max8998_dev *max8998)
{
int i;
int cur_irq;
int ret;
if (!max8998->irq) {
dev_warn(max8998->dev,
"No interrupt specified, no interrupts\n");
max8998->irq_base = 0;
return 0;
}
if (!max8998->irq_base) {
dev_err(max8998->dev,
"No interrupt base specified, no interrupts\n");
return 0;
}
mutex_init(&max8998->irqlock);
/* Mask the individual interrupt sources */
for (i = 0; i < MAX8998_NUM_IRQ_REGS; i++) {
max8998->irq_masks_cur[i] = 0xff;
max8998->irq_masks_cache[i] = 0xff;
max8998_write_reg(max8998->i2c, MAX8998_REG_IRQM1 + i, 0xff);
}
max8998_write_reg(max8998->i2c, MAX8998_REG_STATUSM1, 0xff);
max8998_write_reg(max8998->i2c, MAX8998_REG_STATUSM2, 0xff);
/* register with genirq */
for (i = 0; i < MAX8998_IRQ_NR; i++) {
cur_irq = i + max8998->irq_base;
set_irq_chip_data(cur_irq, max8998);
set_irq_chip_and_handler(cur_irq, &max8998_irq_chip,
handle_edge_irq);
set_irq_nested_thread(cur_irq, 1);
#ifdef CONFIG_ARM
set_irq_flags(cur_irq, IRQF_VALID);
#else
set_irq_noprobe(cur_irq);
#endif
}
ret = request_threaded_irq(max8998->irq, NULL, max8998_irq_thread,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"max8998-irq", max8998);
if (ret) {
dev_err(max8998->dev, "Failed to request IRQ %d: %d\n",
max8998->irq, ret);
return ret;
}
if (!max8998->ono)
return 0;
ret = request_threaded_irq(max8998->ono, NULL, max8998_irq_thread,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
IRQF_ONESHOT, "max8998-ono", max8998);
if (ret)
dev_err(max8998->dev, "Failed to request IRQ %d: %d\n",
max8998->ono, ret);
return 0;
}
void max8998_irq_exit(struct max8998_dev *max8998)
{
if (max8998->ono)
free_irq(max8998->ono, max8998);
if (max8998->irq)
free_irq(max8998->irq, max8998);
}
/*
* max8698.c - mfd core driver for the Maxim 8998
* max8998.c - mfd core driver for the Maxim 8998
*
* Copyright (C) 2009-2010 Samsung Electronics
* Kyungmin Park <kyungmin.park@samsung.com>
......@@ -30,19 +30,23 @@
#include <linux/mfd/max8998.h>
#include <linux/mfd/max8998-private.h>
#define RTC_I2C_ADDR (0x0c >> 1)
static struct mfd_cell max8998_devs[] = {
{
.name = "max8998-pmic",
}
}, {
.name = "max8998-rtc",
},
};
static int max8998_i2c_device_read(struct max8998_dev *max8998, u8 reg, u8 *dest)
int max8998_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest)
{
struct i2c_client *client = max8998->i2c_client;
struct max8998_dev *max8998 = i2c_get_clientdata(i2c);
int ret;
mutex_lock(&max8998->iolock);
ret = i2c_smbus_read_byte_data(client, reg);
ret = i2c_smbus_read_byte_data(i2c, reg);
mutex_unlock(&max8998->iolock);
if (ret < 0)
return ret;
......@@ -51,40 +55,71 @@ static int max8998_i2c_device_read(struct max8998_dev *max8998, u8 reg, u8 *dest
*dest = ret;
return 0;
}
EXPORT_SYMBOL(max8998_read_reg);
static int max8998_i2c_device_write(struct max8998_dev *max8998, u8 reg, u8 value)
int max8998_bulk_read(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
{
struct i2c_client *client = max8998->i2c_client;
struct max8998_dev *max8998 = i2c_get_clientdata(i2c);
int ret;
mutex_lock(&max8998->iolock);
ret = i2c_smbus_read_i2c_block_data(i2c, reg, count, buf);
mutex_unlock(&max8998->iolock);
if (ret < 0)
return ret;
return 0;
}
EXPORT_SYMBOL(max8998_bulk_read);
int max8998_write_reg(struct i2c_client *i2c, u8 reg, u8 value)
{
struct max8998_dev *max8998 = i2c_get_clientdata(i2c);
int ret;
mutex_lock(&max8998->iolock);
ret = i2c_smbus_write_byte_data(client, reg, value);
ret = i2c_smbus_write_byte_data(i2c, reg, value);
mutex_unlock(&max8998->iolock);
return ret;
}
EXPORT_SYMBOL(max8998_write_reg);
static int max8998_i2c_device_update(struct max8998_dev *max8998, u8 reg,
u8 val, u8 mask)
int max8998_bulk_write(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
{
struct i2c_client *client = max8998->i2c_client;
struct max8998_dev *max8998 = i2c_get_clientdata(i2c);
int ret;
mutex_lock(&max8998->iolock);
ret = i2c_smbus_write_i2c_block_data(i2c, reg, count, buf);
mutex_unlock(&max8998->iolock);
if (ret < 0)
return ret;
return 0;
}
EXPORT_SYMBOL(max8998_bulk_write);
int max8998_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask)
{
struct max8998_dev *max8998 = i2c_get_clientdata(i2c);
int ret;
mutex_lock(&max8998->iolock);
ret = i2c_smbus_read_byte_data(client, reg);
ret = i2c_smbus_read_byte_data(i2c, reg);
if (ret >= 0) {
u8 old_val = ret & 0xff;
u8 new_val = (val & mask) | (old_val & (~mask));
ret = i2c_smbus_write_byte_data(client, reg, new_val);
if (ret >= 0)
ret = 0;
ret = i2c_smbus_write_byte_data(i2c, reg, new_val);
}
mutex_unlock(&max8998->iolock);
return ret;
}
EXPORT_SYMBOL(max8998_update_reg);
static int max8998_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct max8998_platform_data *pdata = i2c->dev.platform_data;
struct max8998_dev *max8998;
int ret = 0;
......@@ -94,12 +129,20 @@ static int max8998_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, max8998);
max8998->dev = &i2c->dev;
max8998->i2c_client = i2c;
max8998->dev_read = max8998_i2c_device_read;
max8998->dev_write = max8998_i2c_device_write;
max8998->dev_update = max8998_i2c_device_update;
max8998->i2c = i2c;
max8998->irq = i2c->irq;
max8998->type = id->driver_data;
if (pdata) {
max8998->ono = pdata->ono;
max8998->irq_base = pdata->irq_base;
}
mutex_init(&max8998->iolock);
max8998->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR);
i2c_set_clientdata(max8998->rtc, max8998);
max8998_irq_init(max8998);
ret = mfd_add_devices(max8998->dev, -1,
max8998_devs, ARRAY_SIZE(max8998_devs),
NULL, 0);
......@@ -110,6 +153,8 @@ static int max8998_i2c_probe(struct i2c_client *i2c,
err:
mfd_remove_devices(max8998->dev);
max8998_irq_exit(max8998);
i2c_unregister_device(max8998->rtc);
kfree(max8998);
return ret;
}
......@@ -119,14 +164,17 @@ static int max8998_i2c_remove(struct i2c_client *i2c)
struct max8998_dev *max8998 = i2c_get_clientdata(i2c);
mfd_remove_devices(max8998->dev);
max8998_irq_exit(max8998);
i2c_unregister_device(max8998->rtc);
kfree(max8998);
return 0;
}
static const struct i2c_device_id max8998_i2c_id[] = {
{ "max8998", 0 },
{ }
{ "max8998", TYPE_MAX8998 },
{ "lp3974", TYPE_LP3974},
{ }
};
MODULE_DEVICE_TABLE(i2c, max8998_i2c_id);
......
......@@ -38,10 +38,12 @@ static int mfd_add_device(struct device *parent, int id,
pdev->dev.parent = parent;
platform_set_drvdata(pdev, cell->driver_data);
ret = platform_device_add_data(pdev,
cell->platform_data, cell->data_size);
if (ret)
goto fail_res;
if (cell->data_size) {
ret = platform_device_add_data(pdev,
cell->platform_data, cell->data_size);
if (ret)
goto fail_res;
}
for (r = 0; r < cell->num_resources; r++) {
res[r].name = cell->resources[r].name;
......@@ -65,9 +67,11 @@ static int mfd_add_device(struct device *parent, int id,
res[r].end = cell->resources[r].end;
}
ret = acpi_check_resource_conflict(res);
if (ret)
goto fail_res;
if (!cell->ignore_resource_conflicts) {
ret = acpi_check_resource_conflict(res);
if (ret)
goto fail_res;
}
}
ret = platform_device_add_resources(pdev, res, cell->num_resources);
......
......@@ -25,13 +25,6 @@
#include <linux/mfd/pcf50633/core.h>
int pcf50633_irq_init(struct pcf50633 *pcf, int irq);
void pcf50633_irq_free(struct pcf50633 *pcf);
#ifdef CONFIG_PM
int pcf50633_irq_suspend(struct pcf50633 *pcf);
int pcf50633_irq_resume(struct pcf50633 *pcf);
#endif
static int __pcf50633_read(struct pcf50633 *pcf, u8 reg, int num, u8 *data)
{
int ret;
......@@ -346,12 +339,14 @@ static int __devexit pcf50633_remove(struct i2c_client *client)
struct pcf50633 *pcf = i2c_get_clientdata(client);
int i;
sysfs_remove_group(&client->dev.kobj, &pcf_attr_group);
pcf50633_irq_free(pcf);
platform_device_unregister(pcf->input_pdev);
platform_device_unregister(pcf->rtc_pdev);
platform_device_unregister(pcf->mbc_pdev);
platform_device_unregister(pcf->adc_pdev);
platform_device_unregister(pcf->bl_pdev);
for (i = 0; i < PCF50633_NUM_REGULATORS; i++)
platform_device_unregister(pcf->regulator_pdev[i]);
......
......@@ -65,6 +65,17 @@ static void sh_mobile_sdhi_set_pwr(struct platform_device *tmio, int state)
p->set_pwr(pdev, state);
}
static int sh_mobile_sdhi_get_cd(struct platform_device *tmio)
{
struct platform_device *pdev = to_platform_device(tmio->dev.parent);
struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
if (p && p->get_cd)
return p->get_cd(pdev);
else
return -ENOSYS;
}
static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
{
struct sh_mobile_sdhi *priv;
......@@ -106,12 +117,20 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
mmc_data->hclk = clk_get_rate(priv->clk);
mmc_data->set_pwr = sh_mobile_sdhi_set_pwr;
mmc_data->get_cd = sh_mobile_sdhi_get_cd;
mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED;
if (p) {
mmc_data->flags = p->tmio_flags;
mmc_data->ocr_mask = p->tmio_ocr_mask;
mmc_data->capabilities |= p->tmio_caps;
}
/*
* All SDHI blocks support 2-byte and larger block sizes in 4-bit
* bus width mode.
*/
mmc_data->flags |= TMIO_MMC_BLKSZ_2BYTES;
if (p && p->dma_slave_tx >= 0 && p->dma_slave_rx >= 0) {
priv->param_tx.slave_id = p->dma_slave_tx;
priv->param_rx.slave_id = p->dma_slave_rx;
......
......@@ -873,6 +873,28 @@ static int __devinit stmpe_devices_init(struct stmpe *stmpe)
return ret;
}
#ifdef CONFIG_PM
static int stmpe_suspend(struct device *dev)
{
struct i2c_client *i2c = to_i2c_client(dev);
if (device_may_wakeup(&i2c->dev))
enable_irq_wake(i2c->irq);
return 0;
}
static int stmpe_resume(struct device *dev)
{
struct i2c_client *i2c = to_i2c_client(dev);
if (device_may_wakeup(&i2c->dev))
disable_irq_wake(i2c->irq);
return 0;
}
#endif
static int __devinit stmpe_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
......@@ -960,9 +982,19 @@ static const struct i2c_device_id stmpe_id[] = {
};
MODULE_DEVICE_TABLE(i2c, stmpe_id);
#ifdef CONFIG_PM
static const struct dev_pm_ops stmpe_dev_pm_ops = {
.suspend = stmpe_suspend,
.resume = stmpe_resume,
};
#endif
static struct i2c_driver stmpe_driver = {
.driver.name = "stmpe",
.driver.owner = THIS_MODULE,
#ifdef CONFIG_PM
.driver.pm = &stmpe_dev_pm_ops,
#endif
.probe = stmpe_probe,
.remove = __devexit_p(stmpe_remove),
.id_table = stmpe_id,
......
......@@ -155,7 +155,7 @@ static struct resource __devinitdata tc6393xb_nand_resources[] = {
},
};
static struct resource __devinitdata tc6393xb_mmc_resources[] = {
static struct resource tc6393xb_mmc_resources[] = {
{
.start = 0x800,
.end = 0x9ff,
......
......@@ -43,6 +43,8 @@
#include <linux/timb_dma.h>
#include <linux/ks8842.h>
#include "timberdale.h"
#define DRIVER_NAME "timberdale"
......@@ -161,6 +163,12 @@ static const __devinitconst struct resource timberdale_spi_resources[] = {
},
};
static __devinitdata struct ks8842_platform_data
timberdale_ks8842_platform_data = {
.rx_dma_channel = DMA_ETH_RX,
.tx_dma_channel = DMA_ETH_TX
};
static const __devinitconst struct resource timberdale_eth_resources[] = {
{
.start = ETHOFFSET,
......@@ -389,6 +397,8 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg0[] = {
.name = "ks8842",
.num_resources = ARRAY_SIZE(timberdale_eth_resources),
.resources = timberdale_eth_resources,
.platform_data = &timberdale_ks8842_platform_data,
.data_size = sizeof(timberdale_ks8842_platform_data)
},
};
......@@ -447,6 +457,8 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = {
.name = "ks8842",
.num_resources = ARRAY_SIZE(timberdale_eth_resources),
.resources = timberdale_eth_resources,
.platform_data = &timberdale_ks8842_platform_data,
.data_size = sizeof(timberdale_ks8842_platform_data)
},
};
......@@ -538,6 +550,8 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = {
.name = "ks8842",
.num_resources = ARRAY_SIZE(timberdale_eth_resources),
.resources = timberdale_eth_resources,
.platform_data = &timberdale_ks8842_platform_data,
.data_size = sizeof(timberdale_ks8842_platform_data)
},
};
......
......@@ -68,7 +68,7 @@ static int tps6507x_i2c_write_device(struct tps6507x_dev *tps6507x, char reg,
u8 msg[TPS6507X_MAX_REGISTER + 1];
int ret;
if (bytes > (TPS6507X_MAX_REGISTER + 1))
if (bytes > TPS6507X_MAX_REGISTER)
return -EINVAL;
msg[0] = reg;
......
......@@ -15,6 +15,8 @@
* published by the Free Software Foundation.
*/
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
......@@ -29,9 +31,64 @@
#define TPS6586X_GPIOSET1 0x5d
#define TPS6586X_GPIOSET2 0x5e
/* interrupt control registers */
#define TPS6586X_INT_ACK1 0xb5
#define TPS6586X_INT_ACK2 0xb6
#define TPS6586X_INT_ACK3 0xb7
#define TPS6586X_INT_ACK4 0xb8
/* interrupt mask registers */
#define TPS6586X_INT_MASK1 0xb0
#define TPS6586X_INT_MASK2 0xb1
#define TPS6586X_INT_MASK3 0xb2
#define TPS6586X_INT_MASK4 0xb3
#define TPS6586X_INT_MASK5 0xb4
/* device id */
#define TPS6586X_VERSIONCRC 0xcd
#define TPS658621A_VERSIONCRC 0x15
#define TPS658621C_VERSIONCRC 0x2c
struct tps6586x_irq_data {
u8 mask_reg;
u8 mask_mask;
};
#define TPS6586X_IRQ(_reg, _mask) \
{ \
.mask_reg = (_reg) - TPS6586X_INT_MASK1, \
.mask_mask = (_mask), \
}
static const struct tps6586x_irq_data tps6586x_irqs[] = {
[TPS6586X_INT_PLDO_0] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 0),
[TPS6586X_INT_PLDO_1] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 1),
[TPS6586X_INT_PLDO_2] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 2),
[TPS6586X_INT_PLDO_3] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 3),
[TPS6586X_INT_PLDO_4] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 4),
[TPS6586X_INT_PLDO_5] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 5),
[TPS6586X_INT_PLDO_6] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 6),
[TPS6586X_INT_PLDO_7] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 7),
[TPS6586X_INT_COMP_DET] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 1 << 0),
[TPS6586X_INT_ADC] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 1),
[TPS6586X_INT_PLDO_8] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 2),
[TPS6586X_INT_PLDO_9] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 3),
[TPS6586X_INT_PSM_0] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 4),
[TPS6586X_INT_PSM_1] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 5),
[TPS6586X_INT_PSM_2] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 6),
[TPS6586X_INT_PSM_3] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 7),
[TPS6586X_INT_RTC_ALM1] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 4),
[TPS6586X_INT_ACUSB_OVP] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 0x03),
[TPS6586X_INT_USB_DET] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 2),
[TPS6586X_INT_AC_DET] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 3),
[TPS6586X_INT_BAT_DET] = TPS6586X_IRQ(TPS6586X_INT_MASK3, 1 << 0),
[TPS6586X_INT_CHG_STAT] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 0xfc),
[TPS6586X_INT_CHG_TEMP] = TPS6586X_IRQ(TPS6586X_INT_MASK3, 0x06),
[TPS6586X_INT_PP] = TPS6586X_IRQ(TPS6586X_INT_MASK3, 0xf0),
[TPS6586X_INT_RESUME] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 5),
[TPS6586X_INT_LOW_SYS] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 6),
[TPS6586X_INT_RTC_ALM2] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 1 << 1),
};
struct tps6586x {
struct mutex lock;
......@@ -39,6 +96,12 @@ struct tps6586x {
struct i2c_client *client;
struct gpio_chip gpio;
struct irq_chip irq_chip;
struct mutex irq_lock;
int irq_base;
u32 irq_en;
u8 mask_cache[5];
u8 mask_reg[5];
};
static inline int __tps6586x_read(struct i2c_client *client,
......@@ -262,6 +325,129 @@ static int tps6586x_remove_subdevs(struct tps6586x *tps6586x)
return device_for_each_child(tps6586x->dev, NULL, __remove_subdev);
}
static void tps6586x_irq_lock(unsigned int irq)
{
struct tps6586x *tps6586x = get_irq_chip_data(irq);
mutex_lock(&tps6586x->irq_lock);
}
static void tps6586x_irq_enable(unsigned int irq)
{
struct tps6586x *tps6586x = get_irq_chip_data(irq);
unsigned int __irq = irq - tps6586x->irq_base;
const struct tps6586x_irq_data *data = &tps6586x_irqs[__irq];
tps6586x->mask_reg[data->mask_reg] &= ~data->mask_mask;
tps6586x->irq_en |= (1 << __irq);
}
static void tps6586x_irq_disable(unsigned int irq)
{
struct tps6586x *tps6586x = get_irq_chip_data(irq);
unsigned int __irq = irq - tps6586x->irq_base;
const struct tps6586x_irq_data *data = &tps6586x_irqs[__irq];
tps6586x->mask_reg[data->mask_reg] |= data->mask_mask;
tps6586x->irq_en &= ~(1 << __irq);
}
static void tps6586x_irq_sync_unlock(unsigned int irq)
{
struct tps6586x *tps6586x = get_irq_chip_data(irq);
int i;
for (i = 0; i < ARRAY_SIZE(tps6586x->mask_reg); i++) {
if (tps6586x->mask_reg[i] != tps6586x->mask_cache[i]) {
if (!WARN_ON(tps6586x_write(tps6586x->dev,
TPS6586X_INT_MASK1 + i,
tps6586x->mask_reg[i])))
tps6586x->mask_cache[i] = tps6586x->mask_reg[i];
}
}
mutex_unlock(&tps6586x->irq_lock);
}
static irqreturn_t tps6586x_irq(int irq, void *data)
{
struct tps6586x *tps6586x = data;
u32 acks;
int ret = 0;
ret = tps6586x_reads(tps6586x->dev, TPS6586X_INT_ACK1,
sizeof(acks), (uint8_t *)&acks);
if (ret < 0) {
dev_err(tps6586x->dev, "failed to read interrupt status\n");
return IRQ_NONE;
}
acks = le32_to_cpu(acks);
while (acks) {
int i = __ffs(acks);
if (tps6586x->irq_en & (1 << i))
handle_nested_irq(tps6586x->irq_base + i);
acks &= ~(1 << i);
}
return IRQ_HANDLED;
}
static int __devinit tps6586x_irq_init(struct tps6586x *tps6586x, int irq,
int irq_base)
{
int i, ret;
u8 tmp[4];
if (!irq_base) {
dev_warn(tps6586x->dev, "No interrupt support on IRQ base\n");
return -EINVAL;
}
mutex_init(&tps6586x->irq_lock);
for (i = 0; i < 5; i++) {
tps6586x->mask_cache[i] = 0xff;
tps6586x->mask_reg[i] = 0xff;
tps6586x_write(tps6586x->dev, TPS6586X_INT_MASK1 + i, 0xff);
}
tps6586x_reads(tps6586x->dev, TPS6586X_INT_ACK1, sizeof(tmp), tmp);
tps6586x->irq_base = irq_base;
tps6586x->irq_chip.name = "tps6586x";
tps6586x->irq_chip.enable = tps6586x_irq_enable;
tps6586x->irq_chip.disable = tps6586x_irq_disable;
tps6586x->irq_chip.bus_lock = tps6586x_irq_lock;
tps6586x->irq_chip.bus_sync_unlock = tps6586x_irq_sync_unlock;
for (i = 0; i < ARRAY_SIZE(tps6586x_irqs); i++) {
int __irq = i + tps6586x->irq_base;
set_irq_chip_data(__irq, tps6586x);
set_irq_chip_and_handler(__irq, &tps6586x->irq_chip,
handle_simple_irq);
set_irq_nested_thread(__irq, 1);
#ifdef CONFIG_ARM
set_irq_flags(__irq, IRQF_VALID);
#endif
}
ret = request_threaded_irq(irq, NULL, tps6586x_irq, IRQF_ONESHOT,
"tps6586x", tps6586x);
if (!ret) {
device_init_wakeup(tps6586x->dev, 1);
enable_irq_wake(irq);
}
return ret;
}
static int __devinit tps6586x_add_subdevs(struct tps6586x *tps6586x,
struct tps6586x_platform_data *pdata)
{
......@@ -273,13 +459,19 @@ static int __devinit tps6586x_add_subdevs(struct tps6586x *tps6586x,
subdev = &pdata->subdevs[i];
pdev = platform_device_alloc(subdev->name, subdev->id);
if (!pdev) {
ret = -ENOMEM;
goto failed;
}
pdev->dev.parent = tps6586x->dev;
pdev->dev.platform_data = subdev->platform_data;
ret = platform_device_add(pdev);
if (ret)
if (ret) {
platform_device_put(pdev);
goto failed;
}
}
return 0;
......@@ -306,7 +498,8 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client,
return -EIO;
}
if (ret != TPS658621A_VERSIONCRC) {
if ((ret != TPS658621A_VERSIONCRC) &&
(ret != TPS658621C_VERSIONCRC)) {
dev_err(&client->dev, "Unsupported chip ID: %x\n", ret);
return -ENODEV;
}
......@@ -321,6 +514,15 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client,
mutex_init(&tps6586x->lock);
if (client->irq) {
ret = tps6586x_irq_init(tps6586x, client->irq,
pdata->irq_base);
if (ret) {
dev_err(&client->dev, "IRQ init failed: %d\n", ret);
goto err_irq_init;
}
}
ret = tps6586x_add_subdevs(tps6586x, pdata);
if (ret) {
dev_err(&client->dev, "add devices failed: %d\n", ret);
......@@ -332,12 +534,31 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client,
return 0;
err_add_devs:
if (client->irq)
free_irq(client->irq, tps6586x);
err_irq_init:
kfree(tps6586x);
return ret;
}
static int __devexit tps6586x_i2c_remove(struct i2c_client *client)
{
struct tps6586x *tps6586x = i2c_get_clientdata(client);
struct tps6586x_platform_data *pdata = client->dev.platform_data;
int ret;
if (client->irq)
free_irq(client->irq, tps6586x);
if (pdata->gpio_base) {
ret = gpiochip_remove(&tps6586x->gpio);
if (ret)
dev_err(&client->dev, "Can't remove gpio chip: %d\n",
ret);
}
tps6586x_remove_subdevs(tps6586x);
kfree(tps6586x);
return 0;
}
......
......@@ -115,6 +115,12 @@
#define twl_has_codec() false
#endif
#if defined(CONFIG_CHARGER_TWL4030) || defined(CONFIG_CHARGER_TWL4030_MODULE)
#define twl_has_bci() true
#else
#define twl_has_bci() false
#endif
/* Triton Core internal information (BEGIN) */
/* Last - for index max*/
......@@ -202,12 +208,6 @@
/* Few power values */
#define R_CFG_BOOT 0x05
#define R_PROTECT_KEY 0x0E
/* access control values for R_PROTECT_KEY */
#define KEY_UNLOCK1 0xce
#define KEY_UNLOCK2 0xec
#define KEY_LOCK 0x00
/* some fields in R_CFG_BOOT */
#define HFCLK_FREQ_19p2_MHZ (1 << 0)
......@@ -255,7 +255,7 @@ struct twl_mapping {
unsigned char sid; /* Slave ID */
unsigned char base; /* base address */
};
struct twl_mapping *twl_map;
static struct twl_mapping *twl_map;
static struct twl_mapping twl4030_map[TWL4030_MODULE_LAST + 1] = {
/*
......@@ -832,6 +832,17 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
return PTR_ERR(child);
}
if (twl_has_bci() && pdata->bci &&
!(features & (TPS_SUBSET | TWL5031))) {
child = add_child(3, "twl4030_bci",
pdata->bci, sizeof(*pdata->bci), false,
/* irq0 = CHG_PRES, irq1 = BCI */
pdata->irq_base + BCI_PRES_INTR_OFFSET,
pdata->irq_base + BCI_INTR_OFFSET);
if (IS_ERR(child))
return PTR_ERR(child);
}
return 0;
}
......@@ -846,8 +857,8 @@ static inline int __init protect_pm_master(void)
{
int e = 0;
e = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, KEY_LOCK,
R_PROTECT_KEY);
e = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0,
TWL4030_PM_MASTER_PROTECT_KEY);
return e;
}
......@@ -855,10 +866,13 @@ static inline int __init unprotect_pm_master(void)
{
int e = 0;
e |= twl_i2c_write_u8(TWL_MODULE_PM_MASTER, KEY_UNLOCK1,
R_PROTECT_KEY);
e |= twl_i2c_write_u8(TWL_MODULE_PM_MASTER, KEY_UNLOCK2,
R_PROTECT_KEY);
e |= twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
TWL4030_PM_MASTER_KEY_CFG1,
TWL4030_PM_MASTER_PROTECT_KEY);
e |= twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
TWL4030_PM_MASTER_KEY_CFG2,
TWL4030_PM_MASTER_PROTECT_KEY);
return e;
}
......
#ifndef __TWL_CORE_H__
#define __TWL_CORE_H__
extern int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end);
extern int twl6030_exit_irq(void);
extern int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end);
extern int twl4030_exit_irq(void);
extern int twl4030_init_chip_irq(const char *chip);
#endif /* __TWL_CORE_H__ */
......@@ -35,6 +35,7 @@
#include <linux/i2c/twl.h>
#include "twl-core.h"
/*
* TWL4030 IRQ handling has two stages in hardware, and thus in software.
......@@ -144,6 +145,7 @@ static const struct sih sih_modules_twl4030[6] = {
.name = "bci",
.module = TWL4030_MODULE_INTERRUPTS,
.control_offset = TWL4030_INTERRUPTS_BCISIHCTRL,
.set_cor = true,
.bits = 12,
.bytes_ixr = 2,
.edr_offset = TWL4030_INTERRUPTS_BCIEDR1,
......@@ -408,7 +410,7 @@ static int twl4030_init_sih_modules(unsigned line)
* set Clear-On-Read (COR) bit.
*
* NOTE that sometimes COR polarity is documented as being
* inverted: for MADC and BCI, COR=1 means "clear on write".
* inverted: for MADC, COR=1 means "clear on write".
* And for PWR_INT it's not documented...
*/
if (sih->set_cor) {
......
......@@ -63,10 +63,6 @@ static u8 twl4030_start_script_address = 0x2b;
#define R_MEMORY_ADDRESS PHY_TO_OFF_PM_MASTER(0x59)
#define R_MEMORY_DATA PHY_TO_OFF_PM_MASTER(0x5a)
#define R_PROTECT_KEY 0x0E
#define R_KEY_1 0xC0
#define R_KEY_2 0x0C
/* resource configuration registers
<RESOURCE>_DEV_GRP at address 'n+0'
<RESOURCE>_TYPE at address 'n+1'
......@@ -465,15 +461,17 @@ int twl4030_remove_script(u8 flags)
{
int err = 0;
err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, R_KEY_1,
R_PROTECT_KEY);
err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
TWL4030_PM_MASTER_KEY_CFG1,
TWL4030_PM_MASTER_PROTECT_KEY);
if (err) {
pr_err("twl4030: unable to unlock PROTECT_KEY\n");
return err;
}
err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, R_KEY_2,
R_PROTECT_KEY);
err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
TWL4030_PM_MASTER_KEY_CFG2,
TWL4030_PM_MASTER_PROTECT_KEY);
if (err) {
pr_err("twl4030: unable to unlock PROTECT_KEY\n");
return err;
......@@ -504,7 +502,8 @@ int twl4030_remove_script(u8 flags)
return err;
}
err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, R_PROTECT_KEY);
err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0,
TWL4030_PM_MASTER_PROTECT_KEY);
if (err)
pr_err("TWL4030 Unable to relock registers\n");
......@@ -518,13 +517,15 @@ void __init twl4030_power_init(struct twl4030_power_data *twl4030_scripts)
struct twl4030_resconfig *resconfig;
u8 address = twl4030_start_script_address;
err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, R_KEY_1,
R_PROTECT_KEY);
err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
TWL4030_PM_MASTER_KEY_CFG1,
TWL4030_PM_MASTER_PROTECT_KEY);
if (err)
goto unlock;
err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, R_KEY_2,
R_PROTECT_KEY);
err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
TWL4030_PM_MASTER_KEY_CFG2,
TWL4030_PM_MASTER_PROTECT_KEY);
if (err)
goto unlock;
......@@ -546,7 +547,8 @@ void __init twl4030_power_init(struct twl4030_power_data *twl4030_scripts)
}
}
err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, R_PROTECT_KEY);
err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0,
TWL4030_PM_MASTER_PROTECT_KEY);
if (err)
pr_err("TWL4030 Unable to relock registers\n");
return;
......
......@@ -36,6 +36,9 @@
#include <linux/irq.h>
#include <linux/kthread.h>
#include <linux/i2c/twl.h>
#include <linux/platform_device.h>
#include "twl-core.h"
/*
* TWL6030 (unlike its predecessors, which had two level interrupt handling)
......@@ -223,6 +226,78 @@ int twl6030_interrupt_mask(u8 bit_mask, u8 offset)
}
EXPORT_SYMBOL(twl6030_interrupt_mask);
int twl6030_mmc_card_detect_config(void)
{
int ret;
u8 reg_val = 0;
/* Unmasking the Card detect Interrupt line for MMC1 from Phoenix */
twl6030_interrupt_unmask(TWL6030_MMCDETECT_INT_MASK,
REG_INT_MSK_LINE_B);
twl6030_interrupt_unmask(TWL6030_MMCDETECT_INT_MASK,
REG_INT_MSK_STS_B);
/*
* Intially Configuring MMC_CTRL for receving interrupts &
* Card status on TWL6030 for MMC1
*/
ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &reg_val, TWL6030_MMCCTRL);
if (ret < 0) {
pr_err("twl6030: Failed to read MMCCTRL, error %d\n", ret);
return ret;
}
reg_val &= ~VMMC_AUTO_OFF;
reg_val |= SW_FC;
ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, reg_val, TWL6030_MMCCTRL);
if (ret < 0) {
pr_err("twl6030: Failed to write MMCCTRL, error %d\n", ret);
return ret;
}
/* Configuring PullUp-PullDown register */
ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &reg_val,
TWL6030_CFG_INPUT_PUPD3);
if (ret < 0) {
pr_err("twl6030: Failed to read CFG_INPUT_PUPD3, error %d\n",
ret);
return ret;
}
reg_val &= ~(MMC_PU | MMC_PD);
ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, reg_val,
TWL6030_CFG_INPUT_PUPD3);
if (ret < 0) {
pr_err("twl6030: Failed to write CFG_INPUT_PUPD3, error %d\n",
ret);
return ret;
}
return 0;
}
EXPORT_SYMBOL(twl6030_mmc_card_detect_config);
int twl6030_mmc_card_detect(struct device *dev, int slot)
{
int ret = -EIO;
u8 read_reg = 0;
struct platform_device *pdev = to_platform_device(dev);
if (pdev->id) {
/* TWL6030 provide's Card detect support for
* only MMC1 controller.
*/
pr_err("Unkown MMC controller %d in %s\n", pdev->id, __func__);
return ret;
}
/*
* BIT0 of MMC_CTRL on TWL6030 provides card status for MMC1
* 0 - Card not present ,1 - Card present
*/
ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &read_reg,
TWL6030_MMCCTRL);
if (ret >= 0)
ret = read_reg & STS_MMC;
return ret;
}
EXPORT_SYMBOL(twl6030_mmc_card_detect);
int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
{
......
/*
* Linux multi-function-device driver (MFD) for the integrated peripherals
* of the VIA VX855 chipset
*
* Copyright (C) 2009 VIA Technologies, Inc.
* Copyright (C) 2010 One Laptop per Child
* Author: Harald Welte <HaraldWelte@viatech.com>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/pci.h>
#include <linux/mfd/core.h>
/* offset into pci config space indicating the 16bit register containing
* the power management IO space base */
#define VX855_CFG_PMIO_OFFSET 0x88
/* ACPI I/O Space registers */
#define VX855_PMIO_ACPI 0x00
#define VX855_PMIO_ACPI_LEN 0x0b
/* Processor Power Management */
#define VX855_PMIO_PPM 0x10
#define VX855_PMIO_PPM_LEN 0x08
/* General Purpose Power Management */
#define VX855_PMIO_GPPM 0x20
#define VX855_PMIO_R_GPI 0x48
#define VX855_PMIO_R_GPO 0x4c
#define VX855_PMIO_GPPM_LEN 0x33
#define VSPIC_MMIO_SIZE 0x1000
static struct resource vx855_gpio_resources[] = {
{
.flags = IORESOURCE_IO,
},
{
.flags = IORESOURCE_IO,
},
};
static struct mfd_cell vx855_cells[] = {
{
.name = "vx855_gpio",
.num_resources = ARRAY_SIZE(vx855_gpio_resources),
.resources = vx855_gpio_resources,
/* we must ignore resource conflicts, for reasons outlined in
* the vx855_gpio driver */
.ignore_resource_conflicts = true,
},
};
static __devinit int vx855_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
int ret;
u16 gpio_io_offset;
ret = pci_enable_device(pdev);
if (ret)
return -ENODEV;
pci_read_config_word(pdev, VX855_CFG_PMIO_OFFSET, &gpio_io_offset);
if (!gpio_io_offset) {
dev_warn(&pdev->dev,
"BIOS did not assign PMIO base offset?!?\n");
ret = -ENODEV;
goto out;
}
/* mask out the lowest seven bits, as they are always zero, but
* hardware returns them as 0x01 */
gpio_io_offset &= 0xff80;
/* As the region identified here includes many non-GPIO things, we
* only work with the specific registers that concern us. */
vx855_gpio_resources[0].start = gpio_io_offset + VX855_PMIO_R_GPI;
vx855_gpio_resources[0].end = vx855_gpio_resources[0].start + 3;
vx855_gpio_resources[1].start = gpio_io_offset + VX855_PMIO_R_GPO;
vx855_gpio_resources[1].end = vx855_gpio_resources[1].start + 3;
ret = mfd_add_devices(&pdev->dev, -1, vx855_cells, ARRAY_SIZE(vx855_cells),
NULL, 0);
/* we always return -ENODEV here in order to enable other
* drivers like old, not-yet-platform_device ported i2c-viapro */
return -ENODEV;
out:
pci_disable_device(pdev);
return ret;
}
static void vx855_remove(struct pci_dev *pdev)
{
mfd_remove_devices(&pdev->dev);
pci_disable_device(pdev);
}
static struct pci_device_id vx855_pci_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX855) },
{ 0, }
};
static struct pci_driver vx855_pci_driver = {
.name = "vx855",
.id_table = vx855_pci_tbl,
.probe = vx855_probe,
.remove = __devexit_p(vx855_remove),
};
static int vx855_init(void)
{
return pci_register_driver(&vx855_pci_driver);
}
module_init(vx855_init);
static void vx855_exit(void)
{
pci_unregister_driver(&vx855_pci_driver);
}
module_exit(vx855_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Harald Welte <HaraldWelte@viatech.com>");
MODULE_DESCRIPTION("Driver for the VIA VX855 chipset");
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -62,6 +62,15 @@ config ATMEL_PWM
purposes including software controlled power-efficient backlights
on LCD displays, motor control, and waveform generation.
config AB8500_PWM
bool "AB8500 PWM support"
depends on AB8500_CORE
select HAVE_PWM
help
This driver exports functions to enable/disble/config/free Pulse
Width Modulation in the Analog Baseband Chip AB8500.
It is used by led and backlight driver to control the intensity.
config ATMEL_TCLIB
bool "Atmel AT32/AT91 Timer/Counter Library"
depends on (AVR32 || ARCH_AT91)
......
......@@ -41,3 +41,4 @@ obj-$(CONFIG_VMWARE_BALLOON) += vmw_balloon.o
obj-$(CONFIG_ARM_CHARLCD) += arm-charlcd.o
obj-$(CONFIG_PCH_PHUB) += pch_phub.o
obj-y += ti-st/
obj-$(CONFIG_AB8500_PWM) += ab8500-pwm.o
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -182,7 +182,6 @@ config CHARGER_ISP1704
config CHARGER_TWL4030
tristate "OMAP TWL4030 BCI charger driver"
depends on TWL4030_CORE
depends on BROKEN
help
Say Y here to enable support for TWL4030 Battery Charge Interface.
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册