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 ...
This commit is contained in:
commit
b779b332d0
@ -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;
|
||||
|
332
drivers/gpio/vx855_gpio.c
Normal file
332
drivers/gpio/vx855_gpio.c
Normal file
@ -0,0 +1,332 @@
|
||||
/*
|
||||
* 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;
|
||||
int ret, event;
|
||||
|
||||
input_report_key(info->idev, KEY_POWER, 1);
|
||||
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)
|
||||
{
|
||||
struct pm860x_chip *chip = led->chip;
|
||||
int mask, ret;
|
||||
|
||||
mutex_lock(&led->lock);
|
||||
switch (command) {
|
||||
case SET_BRIGHTNESS:
|
||||
if ((led->current_brightness == 0) && led->brightness) {
|
||||
if (led->iset) {
|
||||
ret = pm860x_set_bits(led->i2c, led->offset,
|
||||
LED_CURRENT_MASK, led->iset);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
} else if (led->brightness == 0) {
|
||||
ret = pm860x_set_bits(led->i2c, led->offset,
|
||||
LED_CURRENT_MASK, 0);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
ret = pm860x_set_bits(led->i2c, led->offset, LED_PWM_MASK,
|
||||
led->brightness);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
led->current_brightness = led->brightness;
|
||||
dev_dbg(chip->dev, "Update LED. (reg:%d, brightness:%d)\n",
|
||||
led->offset, led->brightness);
|
||||
break;
|
||||
case SET_BLINK:
|
||||
ret = pm860x_set_bits(led->i2c, led->offset,
|
||||
LED_BLINK_MASK, led->blink_data);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
mask = __blink_ctl_mask(led->port);
|
||||
ret = pm860x_set_bits(led->i2c, PM8606_WLED3B, mask, mask);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
dev_dbg(chip->dev, "LED blink delay on:%dms, delay off:%dms\n",
|
||||
led->blink_on, led->blink_off);
|
||||
break;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&led->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pm860x_led_work(struct work_struct *work)
|
||||
{
|
||||
|
||||
struct pm860x_led *led;
|
||||
struct pm860x_chip *chip;
|
||||
int mask;
|
||||
|
||||
led = container_of(work, struct pm860x_led, work);
|
||||
__led_set(led, led->command);
|
||||
chip = led->chip;
|
||||
mutex_lock(&led->lock);
|
||||
if ((led->current_brightness == 0) && led->brightness) {
|
||||
if (led->iset) {
|
||||
pm860x_set_bits(led->i2c, __led_off(led->port),
|
||||
LED_CURRENT_MASK, led->iset);
|
||||
}
|
||||
mask = __blink_ctl_mask(led->port);
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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 */
|
||||
@ -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,7 +863,7 @@ static const struct ab_family_id ids[] __initdata = {
|
||||
},
|
||||
};
|
||||
|
||||
static int __init ab3100_probe(struct i2c_client *client,
|
||||
static int __devinit ab3100_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct ab3100 *ab3100;
|
||||
@ -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_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 0x1080
|
||||
#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;
|
||||
}
|
||||
|
||||
static int ab8500_set_register(struct device *dev, u8 bank,
|
||||
u8 reg, u8 value)
|
||||
{
|
||||
struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
|
||||
|
||||
return set_register_interruptible(ab8500, bank, reg, value);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static int ab8500_get_register(struct device *dev, u8 bank,
|
||||
u8 reg, u8 *value)
|
||||
{
|
||||
struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
|
||||
|
||||
return get_register_interruptible(ab8500, bank, reg, value);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
goto out;
|
||||
}
|
||||
|
||||
data = (u8)ret;
|
||||
data = (~bitmask & data) | (bitmask & bitvalues);
|
||||
|
||||
ret = ab8500->write(ab8500, addr, data);
|
||||
if (ret < 0)
|
||||
dev_err(ab8500->dev, "failed to write reg %#x: %d\n",
|
||||
addr, ret);
|
||||
|
||||
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)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ab8500->lock);
|
||||
ret = __ab8500_write(ab8500, addr, data);
|
||||
mutex_unlock(&ab8500->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ab8500_write);
|
||||
|
||||
static int __ab8500_read(struct ab8500 *ab8500, u16 addr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ab8500->read(ab8500, addr);
|
||||
if (ret < 0)
|
||||
dev_err(ab8500->dev, "failed to read reg %#x: %d\n",
|
||||
addr, ret);
|
||||
|
||||
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)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ab8500->lock);
|
||||
ret = __ab8500_read(ab8500, addr);
|
||||
mutex_unlock(&ab8500->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
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)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ab8500->lock);
|
||||
|
||||
ret = __ab8500_read(ab8500, addr);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret &= ~mask;
|
||||
ret |= data;
|
||||
|
||||
ret = __ab8500_write(ab8500, 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;
|
||||
|
||||
|
652
drivers/mfd/ab8500-debugfs.c
Normal file
652
drivers/mfd/ab8500-debugfs.c
Normal file
@ -0,0 +1,652 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* Author: Mattias Wallin <mattias.wallin@stericsson.com> for ST-Ericsson.
|
||||
* License Terms: GNU General Public License v2
|
||||
*/
|
||||
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <linux/mfd/abx500.h>
|
||||
#include <linux/mfd/ab8500.h>
|
||||
|
||||
static u32 debug_bank;
|
||||
static u32 debug_address;
|
||||
|
||||
/**
|
||||
* struct ab8500_reg_range
|
||||
* @first: the first address of the range
|
||||
* @last: the last address of the range
|
||||
* @perm: access permissions for the range
|
||||
*/
|
||||
struct ab8500_reg_range {
|
||||
u8 first;
|
||||
u8 last;
|
||||
u8 perm;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ab8500_i2c_ranges
|
||||
* @num_ranges: the number of ranges in the list
|
||||
* @bankid: bank identifier
|
||||
* @range: the list of register ranges
|
||||
*/
|
||||
struct ab8500_i2c_ranges {
|
||||
u8 num_ranges;
|
||||
u8 bankid;
|
||||
const struct ab8500_reg_range *range;
|
||||
};
|
||||
|
||||
#define AB8500_NAME_STRING "ab8500"
|
||||
#define AB8500_NUM_BANKS 22
|
||||
|
||||
#define AB8500_REV_REG 0x80
|
||||
|
||||
static struct ab8500_i2c_ranges debug_ranges[AB8500_NUM_BANKS] = {
|
||||
[0x0] = {
|
||||
.num_ranges = 0,
|
||||
.range = 0,
|
||||
},
|
||||
[AB8500_SYS_CTRL1_BLOCK] = {
|
||||
.num_ranges = 3,
|
||||
.range = (struct ab8500_reg_range[]) {
|
||||
{
|
||||
.first = 0x00,
|
||||
.last = 0x02,
|
||||
},
|
||||
{
|
||||
.first = 0x42,
|
||||
.last = 0x42,
|
||||
},
|
||||
{
|
||||
.first = 0x80,
|
||||
.last = 0x81,
|
||||
},
|
||||
},
|
||||
},
|
||||
[AB8500_SYS_CTRL2_BLOCK] = {
|
||||
.num_ranges = 4,
|
||||
.range = (struct ab8500_reg_range[]) {
|
||||
{
|
||||
.first = 0x00,
|
||||
.last = 0x0D,
|
||||
},
|
||||
{
|
||||
.first = 0x0F,
|
||||
.last = 0x17,
|
||||
},
|
||||
{
|
||||
.first = 0x30,
|
||||
.last = 0x30,
|
||||
},
|
||||
{
|
||||
.first = 0x32,
|
||||
.last = 0x33,
|
||||
},
|
||||
},
|
||||
},
|
||||
[AB8500_REGU_CTRL1] = {
|
||||
.num_ranges = 3,
|
||||
.range = (struct ab8500_reg_range[]) {
|
||||
{
|
||||
.first = 0x00,
|
||||
.last = 0x00,
|
||||
},
|
||||
{
|
||||
.first = 0x03,
|
||||
.last = 0x10,
|
||||
},
|
||||
{
|
||||
.first = 0x80,
|
||||
.last = 0x84,
|
||||
},
|
||||
},
|
||||
},
|
||||
[AB8500_REGU_CTRL2] = {
|
||||
.num_ranges = 5,
|
||||
.range = (struct ab8500_reg_range[]) {
|
||||
{
|
||||
.first = 0x00,
|
||||
.last = 0x15,
|
||||
},
|
||||
{
|
||||
.first = 0x17,
|
||||
.last = 0x19,
|
||||
},
|
||||
{
|
||||
.first = 0x1B,
|
||||
.last = 0x1D,
|
||||
},
|
||||
{
|
||||
.first = 0x1F,
|
||||
.last = 0x22,
|
||||
},
|
||||
{
|
||||
.first = 0x40,
|
||||
.last = 0x44,
|
||||
},
|
||||
/* 0x80-0x8B is SIM registers and should
|
||||
* not be accessed from here */
|
||||
},
|
||||
},
|
||||
[AB8500_USB] = {
|
||||
.num_ranges = 2,
|
||||
.range = (struct ab8500_reg_range[]) {
|
||||
{
|
||||
.first = 0x80,
|
||||
.last = 0x83,
|
||||
},
|
||||
{
|
||||
.first = 0x87,
|
||||
.last = 0x8A,
|
||||
},
|
||||
},
|
||||
},
|
||||
[AB8500_TVOUT] = {
|
||||
.num_ranges = 9,
|
||||
.range = (struct ab8500_reg_range[]) {
|
||||
{
|
||||
.first = 0x00,
|
||||
.last = 0x12,
|
||||
},
|
||||
{
|
||||
.first = 0x15,
|
||||
.last = 0x17,
|
||||
},
|
||||
{
|
||||
.first = 0x19,
|
||||
.last = 0x21,
|
||||
},
|
||||
{
|
||||
.first = 0x27,
|
||||
.last = 0x2C,
|
||||
},
|
||||
{
|
||||
.first = 0x41,
|
||||
.last = 0x41,
|
||||
},
|
||||
{
|
||||
.first = 0x45,
|
||||
.last = 0x5B,
|
||||
},
|
||||
{
|
||||
.first = 0x5D,
|
||||
.last = 0x5D,
|
||||
},
|
||||
{
|
||||
.first = 0x69,
|
||||
.last = 0x69,
|
||||
},
|
||||
{
|
||||
.first = 0x80,
|
||||
.last = 0x81,
|
||||
},
|
||||
},
|
||||
},
|
||||
[AB8500_DBI] = {
|
||||
.num_ranges = 0,
|
||||
.range = 0,
|
||||
},
|
||||
[AB8500_ECI_AV_ACC] = {
|
||||
.num_ranges = 1,
|
||||
.range = (struct ab8500_reg_range[]) {
|
||||
{
|
||||
.first = 0x80,
|
||||
.last = 0x82,
|
||||
},
|
||||
},
|
||||
},
|
||||
[0x9] = {
|
||||
.num_ranges = 0,
|
||||
.range = 0,
|
||||
},
|
||||
[AB8500_GPADC] = {
|
||||
.num_ranges = 1,
|
||||
.range = (struct ab8500_reg_range[]) {
|
||||
{
|
||||
.first = 0x00,
|
||||
.last = 0x08,
|
||||
},
|
||||
},
|
||||
},
|
||||
[AB8500_CHARGER] = {
|
||||
.num_ranges = 8,
|
||||
.range = (struct ab8500_reg_range[]) {
|
||||
{
|
||||
.first = 0x00,
|
||||
.last = 0x03,
|
||||
},
|
||||
{
|
||||
.first = 0x05,
|
||||
.last = 0x05,
|
||||
},
|
||||
{
|
||||
.first = 0x40,
|
||||
.last = 0x40,
|
||||
},
|
||||
{
|
||||
.first = 0x42,
|
||||
.last = 0x42,
|
||||
},
|
||||
{
|
||||
.first = 0x44,
|
||||
.last = 0x44,
|
||||
},
|
||||
{
|
||||
.first = 0x50,
|
||||
.last = 0x55,
|
||||
},
|
||||
{
|
||||
.first = 0x80,
|
||||
.last = 0x82,
|
||||
},
|
||||
{
|
||||
.first = 0xC0,
|
||||
.last = 0xC2,
|
||||
},
|
||||
},
|
||||
},
|
||||
[AB8500_GAS_GAUGE] = {
|
||||
.num_ranges = 3,
|
||||
.range = (struct ab8500_reg_range[]) {
|
||||
{
|
||||
.first = 0x00,
|
||||
.last = 0x00,
|
||||
},
|
||||
{
|
||||
.first = 0x07,
|
||||
.last = 0x0A,
|
||||
},
|
||||
{
|
||||
.first = 0x10,
|
||||
.last = 0x14,
|
||||
},
|
||||
},
|
||||
},
|
||||
[AB8500_AUDIO] = {
|
||||
.num_ranges = 1,
|
||||
.range = (struct ab8500_reg_range[]) {
|
||||
{
|
||||
.first = 0x00,
|
||||
.last = 0x6F,
|
||||
},
|
||||
},
|
||||
},
|
||||
[AB8500_INTERRUPT] = {
|
||||
.num_ranges = 0,
|
||||
.range = 0,
|
||||
},
|
||||
[AB8500_RTC] = {
|
||||
.num_ranges = 1,
|
||||
.range = (struct ab8500_reg_range[]) {
|
||||
{
|
||||
.first = 0x00,
|
||||
.last = 0x0F,
|
||||
},
|
||||
},
|
||||
},
|
||||
[AB8500_MISC] = {
|
||||
.num_ranges = 8,
|
||||
.range = (struct ab8500_reg_range[]) {
|
||||
{
|
||||
.first = 0x00,
|
||||
.last = 0x05,
|
||||
},
|
||||
{
|
||||
.first = 0x10,
|
||||
.last = 0x15,
|
||||
},
|
||||
{
|
||||
.first = 0x20,
|
||||
.last = 0x25,
|
||||
},
|
||||
{
|
||||
.first = 0x30,
|
||||
.last = 0x35,
|
||||
},
|
||||
{
|
||||
.first = 0x40,
|
||||
.last = 0x45,
|
||||
},
|
||||
{
|
||||
.first = 0x50,
|
||||
.last = 0x50,
|
||||
},
|
||||
{
|
||||
.first = 0x60,
|
||||
.last = 0x67,
|
||||
},
|
||||
{
|
||||
.first = 0x80,
|
||||
.last = 0x80,
|
||||
},
|
||||
},
|
||||
},
|
||||
[0x11] = {
|
||||
.num_ranges = 0,
|
||||
.range = 0,
|
||||
},
|
||||
[0x12] = {
|
||||
.num_ranges = 0,
|
||||
.range = 0,
|
||||
},
|
||||
[0x13] = {
|
||||
.num_ranges = 0,
|
||||
.range = 0,
|
||||
},
|
||||
[0x14] = {
|
||||
.num_ranges = 0,
|
||||
.range = 0,
|
||||
},
|
||||
[AB8500_OTP_EMUL] = {
|
||||
.num_ranges = 1,
|
||||
.range = (struct ab8500_reg_range[]) {
|
||||
{
|
||||
.first = 0x01,
|
||||
.last = 0x0F,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static int ab8500_registers_print(struct seq_file *s, void *p)
|
||||
{
|
||||
struct device *dev = s->private;
|
||||
unsigned int i;
|
||||
u32 bank = debug_bank;
|
||||
|
||||
seq_printf(s, AB8500_NAME_STRING " register values:\n");
|
||||
|
||||
seq_printf(s, " bank %u:\n", bank);
|
||||
for (i = 0; i < debug_ranges[bank].num_ranges; i++) {
|
||||
u32 reg;
|
||||
|
||||
for (reg = debug_ranges[bank].range[i].first;
|
||||
reg <= debug_ranges[bank].range[i].last;
|
||||
reg++) {
|
||||
u8 value;
|
||||
int err;
|
||||
|
||||
err = abx500_get_register_interruptible(dev,
|
||||
(u8)bank, (u8)reg, &value);
|
||||
if (err < 0) {
|
||||
dev_err(dev, "ab->read fail %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = seq_printf(s, " [%u/0x%02X]: 0x%02X\n", bank,
|
||||
reg, value);
|
||||
if (err < 0) {
|
||||
dev_err(dev, "seq_printf overflow\n");
|
||||
/* Error is not returned here since
|
||||
* the output is wanted in any case */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ab8500_registers_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, ab8500_registers_print, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations ab8500_registers_fops = {
|
||||
.open = ab8500_registers_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ab8500_bank_print(struct seq_file *s, void *p)
|
||||
{
|
||||
return seq_printf(s, "%d\n", debug_bank);
|
||||
}
|
||||
|
||||
static int ab8500_bank_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, ab8500_bank_print, inode->i_private);
|
||||
}
|
||||
|
||||
static ssize_t ab8500_bank_write(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct device *dev = ((struct seq_file *)(file->private_data))->private;
|
||||
char buf[32];
|
||||
int buf_size;
|
||||
unsigned long user_bank;
|
||||
int err;
|
||||
|
||||
/* Get userspace string and assure termination */
|
||||
buf_size = min(count, (sizeof(buf) - 1));
|
||||
if (copy_from_user(buf, user_buf, buf_size))
|
||||
return -EFAULT;
|
||||
buf[buf_size] = 0;
|
||||
|
||||
err = strict_strtoul(buf, 0, &user_bank);
|
||||
if (err)
|
||||
return -EINVAL;
|
||||
|
||||
if (user_bank >= AB8500_NUM_BANKS) {
|
||||
dev_err(dev, "debugfs error input > number of banks\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
debug_bank = user_bank;
|
||||
|
||||
return buf_size;
|
||||
}
|
||||
|
||||
static int ab8500_address_print(struct seq_file *s, void *p)
|
||||
{
|
||||
return seq_printf(s, "0x%02X\n", debug_address);
|
||||
}
|
||||
|
||||
static int ab8500_address_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, ab8500_address_print, inode->i_private);
|
||||
}
|
||||
|
||||
static ssize_t ab8500_address_write(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct device *dev = ((struct seq_file *)(file->private_data))->private;
|
||||
char buf[32];
|
||||
int buf_size;
|
||||
unsigned long user_address;
|
||||
int err;
|
||||
|
||||
/* Get userspace string and assure termination */
|
||||
buf_size = min(count, (sizeof(buf) - 1));
|
||||
if (copy_from_user(buf, user_buf, buf_size))
|
||||
return -EFAULT;
|
||||
buf[buf_size] = 0;
|
||||
|
||||
err = strict_strtoul(buf, 0, &user_address);
|
||||
if (err)
|
||||
return -EINVAL;
|
||||
if (user_address > 0xff) {
|
||||
dev_err(dev, "debugfs error input > 0xff\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
debug_address = user_address;
|
||||
return buf_size;
|
||||
}
|
||||
|
||||
static int ab8500_val_print(struct seq_file *s, void *p)
|
||||
{
|
||||
struct device *dev = s->private;
|
||||
int ret;
|
||||
u8 regvalue;
|
||||
|
||||
ret = abx500_get_register_interruptible(dev,
|
||||
(u8)debug_bank, (u8)debug_address, ®value);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "abx500_get_reg fail %d, %d\n",
|
||||
ret, __LINE__);
|
||||
return -EINVAL;
|
||||
}
|
||||
seq_printf(s, "0x%02X\n", regvalue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ab8500_val_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, ab8500_val_print, inode->i_private);
|
||||
}
|
||||
|
||||
static ssize_t ab8500_val_write(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct device *dev = ((struct seq_file *)(file->private_data))->private;
|
||||
char buf[32];
|
||||
int buf_size;
|
||||
unsigned long user_val;
|
||||
int err;
|
||||
|
||||
/* Get userspace string and assure termination */
|
||||
buf_size = min(count, (sizeof(buf)-1));
|
||||
if (copy_from_user(buf, user_buf, buf_size))
|
||||
return -EFAULT;
|
||||
buf[buf_size] = 0;
|
||||
|
||||
err = strict_strtoul(buf, 0, &user_val);
|
||||
if (err)
|
||||
return -EINVAL;
|
||||
if (user_val > 0xff) {
|
||||
dev_err(dev, "debugfs error input > 0xff\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
err = abx500_set_register_interruptible(dev,
|
||||
(u8)debug_bank, debug_address, (u8)user_val);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "abx500_set_reg failed %d, %d", err, __LINE__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return buf_size;
|
||||
}
|
||||
|
||||
static const struct file_operations ab8500_bank_fops = {
|
||||
.open = ab8500_bank_open,
|
||||
.write = ab8500_bank_write,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct file_operations ab8500_address_fops = {
|
||||
.open = ab8500_address_open,
|
||||
.write = ab8500_address_write,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct file_operations ab8500_val_fops = {
|
||||
.open = ab8500_val_open,
|
||||
.write = ab8500_val_write,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct dentry *ab8500_dir;
|
||||
static struct dentry *ab8500_reg_file;
|
||||
static struct dentry *ab8500_bank_file;
|
||||
static struct dentry *ab8500_address_file;
|
||||
static struct dentry *ab8500_val_file;
|
||||
|
||||
static int __devinit ab8500_debug_probe(struct platform_device *plf)
|
||||
{
|
||||
debug_bank = AB8500_MISC;
|
||||
debug_address = AB8500_REV_REG & 0x00FF;
|
||||
|
||||
ab8500_dir = debugfs_create_dir(AB8500_NAME_STRING, NULL);
|
||||
if (!ab8500_dir)
|
||||
goto exit_no_debugfs;
|
||||
|
||||
ab8500_reg_file = debugfs_create_file("all-bank-registers",
|
||||
S_IRUGO, ab8500_dir, &plf->dev, &ab8500_registers_fops);
|
||||
if (!ab8500_reg_file)
|
||||
goto exit_destroy_dir;
|
||||
|
||||
ab8500_bank_file = debugfs_create_file("register-bank",
|
||||
(S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev, &ab8500_bank_fops);
|
||||
if (!ab8500_bank_file)
|
||||
goto exit_destroy_reg;
|
||||
|
||||
ab8500_address_file = debugfs_create_file("register-address",
|
||||
(S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev,
|
||||
&ab8500_address_fops);
|
||||
if (!ab8500_address_file)
|
||||
goto exit_destroy_bank;
|
||||
|
||||
ab8500_val_file = debugfs_create_file("register-value",
|
||||
(S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev, &ab8500_val_fops);
|
||||
if (!ab8500_val_file)
|
||||
goto exit_destroy_address;
|
||||
|
||||
return 0;
|
||||
|
||||
exit_destroy_address:
|
||||
debugfs_remove(ab8500_address_file);
|
||||
exit_destroy_bank:
|
||||
debugfs_remove(ab8500_bank_file);
|
||||
exit_destroy_reg:
|
||||
debugfs_remove(ab8500_reg_file);
|
||||
exit_destroy_dir:
|
||||
debugfs_remove(ab8500_dir);
|
||||
exit_no_debugfs:
|
||||
dev_err(&plf->dev, "failed to create debugfs entries.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int __devexit ab8500_debug_remove(struct platform_device *plf)
|
||||
{
|
||||
debugfs_remove(ab8500_val_file);
|
||||
debugfs_remove(ab8500_address_file);
|
||||
debugfs_remove(ab8500_bank_file);
|
||||
debugfs_remove(ab8500_reg_file);
|
||||
debugfs_remove(ab8500_dir);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ab8500_debug_driver = {
|
||||
.driver = {
|
||||
.name = "ab8500-debug",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ab8500_debug_probe,
|
||||
.remove = __devexit_p(ab8500_debug_remove)
|
||||
};
|
||||
|
||||
static int __init ab8500_debug_init(void)
|
||||
{
|
||||
return platform_driver_register(&ab8500_debug_driver);
|
||||
}
|
||||
|
||||
static void __exit ab8500_debug_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ab8500_debug_driver);
|
||||
}
|
||||
subsys_initcall(ab8500_debug_init);
|
||||
module_exit(ab8500_debug_exit);
|
||||
|
||||
MODULE_AUTHOR("Mattias WALLIN <mattias.wallin@stericsson.com");
|
||||
MODULE_DESCRIPTION("AB8500 DEBUG");
|
||||
MODULE_LICENSE("GPL v2");
|
105
drivers/mfd/ab8500-i2c.c
Normal file
105
drivers/mfd/ab8500-i2c.c
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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,14 +470,20 @@ 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;
|
||||
|
||||
failed:
|
||||
|
@ -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,
|
||||
},
|
||||
|
258
drivers/mfd/max8998-irq.c
Normal file
258
drivers/mfd/max8998-irq.c
Normal file
@ -0,0 +1,258 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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_write_byte_data(client, reg, value);
|
||||
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(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_read_byte_data(client, reg);
|
||||
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(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,13 +164,16 @@ 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);
|
||||
|
@ -1,752 +0,0 @@
|
||||
/*
|
||||
* Copyright 2009 Pengutronix
|
||||
* Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
|
||||
*
|
||||
* loosely based on an earlier driver that has
|
||||
* Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License version 2 as published by the
|
||||
* Free Software Foundation.
|
||||
*/
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/mc13783.h>
|
||||
|
||||
struct mc13783 {
|
||||
struct spi_device *spidev;
|
||||
struct mutex lock;
|
||||
int irq;
|
||||
int flags;
|
||||
|
||||
irq_handler_t irqhandler[MC13783_NUM_IRQ];
|
||||
void *irqdata[MC13783_NUM_IRQ];
|
||||
|
||||
/* XXX these should go as platformdata to the regulator subdevice */
|
||||
struct mc13783_regulator_init_data *regulators;
|
||||
int num_regulators;
|
||||
};
|
||||
|
||||
#define MC13783_REG_REVISION 7
|
||||
#define MC13783_REG_ADC_0 43
|
||||
#define MC13783_REG_ADC_1 44
|
||||
#define MC13783_REG_ADC_2 45
|
||||
|
||||
#define MC13783_IRQSTAT0 0
|
||||
#define MC13783_IRQSTAT0_ADCDONEI (1 << 0)
|
||||
#define MC13783_IRQSTAT0_ADCBISDONEI (1 << 1)
|
||||
#define MC13783_IRQSTAT0_TSI (1 << 2)
|
||||
#define MC13783_IRQSTAT0_WHIGHI (1 << 3)
|
||||
#define MC13783_IRQSTAT0_WLOWI (1 << 4)
|
||||
#define MC13783_IRQSTAT0_CHGDETI (1 << 6)
|
||||
#define MC13783_IRQSTAT0_CHGOVI (1 << 7)
|
||||
#define MC13783_IRQSTAT0_CHGREVI (1 << 8)
|
||||
#define MC13783_IRQSTAT0_CHGSHORTI (1 << 9)
|
||||
#define MC13783_IRQSTAT0_CCCVI (1 << 10)
|
||||
#define MC13783_IRQSTAT0_CHGCURRI (1 << 11)
|
||||
#define MC13783_IRQSTAT0_BPONI (1 << 12)
|
||||
#define MC13783_IRQSTAT0_LOBATLI (1 << 13)
|
||||
#define MC13783_IRQSTAT0_LOBATHI (1 << 14)
|
||||
#define MC13783_IRQSTAT0_UDPI (1 << 15)
|
||||
#define MC13783_IRQSTAT0_USBI (1 << 16)
|
||||
#define MC13783_IRQSTAT0_IDI (1 << 19)
|
||||
#define MC13783_IRQSTAT0_SE1I (1 << 21)
|
||||
#define MC13783_IRQSTAT0_CKDETI (1 << 22)
|
||||
#define MC13783_IRQSTAT0_UDMI (1 << 23)
|
||||
|
||||
#define MC13783_IRQMASK0 1
|
||||
#define MC13783_IRQMASK0_ADCDONEM MC13783_IRQSTAT0_ADCDONEI
|
||||
#define MC13783_IRQMASK0_ADCBISDONEM MC13783_IRQSTAT0_ADCBISDONEI
|
||||
#define MC13783_IRQMASK0_TSM MC13783_IRQSTAT0_TSI
|
||||
#define MC13783_IRQMASK0_WHIGHM MC13783_IRQSTAT0_WHIGHI
|
||||
#define MC13783_IRQMASK0_WLOWM MC13783_IRQSTAT0_WLOWI
|
||||
#define MC13783_IRQMASK0_CHGDETM MC13783_IRQSTAT0_CHGDETI
|
||||
#define MC13783_IRQMASK0_CHGOVM MC13783_IRQSTAT0_CHGOVI
|
||||
#define MC13783_IRQMASK0_CHGREVM MC13783_IRQSTAT0_CHGREVI
|
||||
#define MC13783_IRQMASK0_CHGSHORTM MC13783_IRQSTAT0_CHGSHORTI
|
||||
#define MC13783_IRQMASK0_CCCVM MC13783_IRQSTAT0_CCCVI
|
||||
#define MC13783_IRQMASK0_CHGCURRM MC13783_IRQSTAT0_CHGCURRI
|
||||
#define MC13783_IRQMASK0_BPONM MC13783_IRQSTAT0_BPONI
|
||||
#define MC13783_IRQMASK0_LOBATLM MC13783_IRQSTAT0_LOBATLI
|
||||
#define MC13783_IRQMASK0_LOBATHM MC13783_IRQSTAT0_LOBATHI
|
||||
#define MC13783_IRQMASK0_UDPM MC13783_IRQSTAT0_UDPI
|
||||
#define MC13783_IRQMASK0_USBM MC13783_IRQSTAT0_USBI
|
||||
#define MC13783_IRQMASK0_IDM MC13783_IRQSTAT0_IDI
|
||||
#define MC13783_IRQMASK0_SE1M MC13783_IRQSTAT0_SE1I
|
||||
#define MC13783_IRQMASK0_CKDETM MC13783_IRQSTAT0_CKDETI
|
||||
#define MC13783_IRQMASK0_UDMM MC13783_IRQSTAT0_UDMI
|
||||
|
||||
#define MC13783_IRQSTAT1 3
|
||||
#define MC13783_IRQSTAT1_1HZI (1 << 0)
|
||||
#define MC13783_IRQSTAT1_TODAI (1 << 1)
|
||||
#define MC13783_IRQSTAT1_ONOFD1I (1 << 3)
|
||||
#define MC13783_IRQSTAT1_ONOFD2I (1 << 4)
|
||||
#define MC13783_IRQSTAT1_ONOFD3I (1 << 5)
|
||||
#define MC13783_IRQSTAT1_SYSRSTI (1 << 6)
|
||||
#define MC13783_IRQSTAT1_RTCRSTI (1 << 7)
|
||||
#define MC13783_IRQSTAT1_PCI (1 << 8)
|
||||
#define MC13783_IRQSTAT1_WARMI (1 << 9)
|
||||
#define MC13783_IRQSTAT1_MEMHLDI (1 << 10)
|
||||
#define MC13783_IRQSTAT1_PWRRDYI (1 << 11)
|
||||
#define MC13783_IRQSTAT1_THWARNLI (1 << 12)
|
||||
#define MC13783_IRQSTAT1_THWARNHI (1 << 13)
|
||||
#define MC13783_IRQSTAT1_CLKI (1 << 14)
|
||||
#define MC13783_IRQSTAT1_SEMAFI (1 << 15)
|
||||
#define MC13783_IRQSTAT1_MC2BI (1 << 17)
|
||||
#define MC13783_IRQSTAT1_HSDETI (1 << 18)
|
||||
#define MC13783_IRQSTAT1_HSLI (1 << 19)
|
||||
#define MC13783_IRQSTAT1_ALSPTHI (1 << 20)
|
||||
#define MC13783_IRQSTAT1_AHSSHORTI (1 << 21)
|
||||
|
||||
#define MC13783_IRQMASK1 4
|
||||
#define MC13783_IRQMASK1_1HZM MC13783_IRQSTAT1_1HZI
|
||||
#define MC13783_IRQMASK1_TODAM MC13783_IRQSTAT1_TODAI
|
||||
#define MC13783_IRQMASK1_ONOFD1M MC13783_IRQSTAT1_ONOFD1I
|
||||
#define MC13783_IRQMASK1_ONOFD2M MC13783_IRQSTAT1_ONOFD2I
|
||||
#define MC13783_IRQMASK1_ONOFD3M MC13783_IRQSTAT1_ONOFD3I
|
||||
#define MC13783_IRQMASK1_SYSRSTM MC13783_IRQSTAT1_SYSRSTI
|
||||
#define MC13783_IRQMASK1_RTCRSTM MC13783_IRQSTAT1_RTCRSTI
|
||||
#define MC13783_IRQMASK1_PCM MC13783_IRQSTAT1_PCI
|
||||
#define MC13783_IRQMASK1_WARMM MC13783_IRQSTAT1_WARMI
|
||||
#define MC13783_IRQMASK1_MEMHLDM MC13783_IRQSTAT1_MEMHLDI
|
||||
#define MC13783_IRQMASK1_PWRRDYM MC13783_IRQSTAT1_PWRRDYI
|
||||
#define MC13783_IRQMASK1_THWARNLM MC13783_IRQSTAT1_THWARNLI
|
||||
#define MC13783_IRQMASK1_THWARNHM MC13783_IRQSTAT1_THWARNHI
|
||||
#define MC13783_IRQMASK1_CLKM MC13783_IRQSTAT1_CLKI
|
||||
#define MC13783_IRQMASK1_SEMAFM MC13783_IRQSTAT1_SEMAFI
|
||||
#define MC13783_IRQMASK1_MC2BM MC13783_IRQSTAT1_MC2BI
|
||||
#define MC13783_IRQMASK1_HSDETM MC13783_IRQSTAT1_HSDETI
|
||||
#define MC13783_IRQMASK1_HSLM MC13783_IRQSTAT1_HSLI
|
||||
#define MC13783_IRQMASK1_ALSPTHM MC13783_IRQSTAT1_ALSPTHI
|
||||
#define MC13783_IRQMASK1_AHSSHORTM MC13783_IRQSTAT1_AHSSHORTI
|
||||
|
||||
#define MC13783_ADC1 44
|
||||
#define MC13783_ADC1_ADEN (1 << 0)
|
||||
#define MC13783_ADC1_RAND (1 << 1)
|
||||
#define MC13783_ADC1_ADSEL (1 << 3)
|
||||
#define MC13783_ADC1_ASC (1 << 20)
|
||||
#define MC13783_ADC1_ADTRIGIGN (1 << 21)
|
||||
|
||||
#define MC13783_NUMREGS 0x3f
|
||||
|
||||
void mc13783_lock(struct mc13783 *mc13783)
|
||||
{
|
||||
if (!mutex_trylock(&mc13783->lock)) {
|
||||
dev_dbg(&mc13783->spidev->dev, "wait for %s from %pf\n",
|
||||
__func__, __builtin_return_address(0));
|
||||
|
||||
mutex_lock(&mc13783->lock);
|
||||
}
|
||||
dev_dbg(&mc13783->spidev->dev, "%s from %pf\n",
|
||||
__func__, __builtin_return_address(0));
|
||||
}
|
||||
EXPORT_SYMBOL(mc13783_lock);
|
||||
|
||||
void mc13783_unlock(struct mc13783 *mc13783)
|
||||
{
|
||||
dev_dbg(&mc13783->spidev->dev, "%s from %pf\n",
|
||||
__func__, __builtin_return_address(0));
|
||||
mutex_unlock(&mc13783->lock);
|
||||
}
|
||||
EXPORT_SYMBOL(mc13783_unlock);
|
||||
|
||||
#define MC13783_REGOFFSET_SHIFT 25
|
||||
int mc13783_reg_read(struct mc13783 *mc13783, unsigned int offset, u32 *val)
|
||||
{
|
||||
struct spi_transfer t;
|
||||
struct spi_message m;
|
||||
int ret;
|
||||
|
||||
BUG_ON(!mutex_is_locked(&mc13783->lock));
|
||||
|
||||
if (offset > MC13783_NUMREGS)
|
||||
return -EINVAL;
|
||||
|
||||
*val = offset << MC13783_REGOFFSET_SHIFT;
|
||||
|
||||
memset(&t, 0, sizeof(t));
|
||||
|
||||
t.tx_buf = val;
|
||||
t.rx_buf = val;
|
||||
t.len = sizeof(u32);
|
||||
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&t, &m);
|
||||
|
||||
ret = spi_sync(mc13783->spidev, &m);
|
||||
|
||||
/* error in message.status implies error return from spi_sync */
|
||||
BUG_ON(!ret && m.status);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val &= 0xffffff;
|
||||
|
||||
dev_vdbg(&mc13783->spidev->dev, "[0x%02x] -> 0x%06x\n", offset, *val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mc13783_reg_read);
|
||||
|
||||
int mc13783_reg_write(struct mc13783 *mc13783, unsigned int offset, u32 val)
|
||||
{
|
||||
u32 buf;
|
||||
struct spi_transfer t;
|
||||
struct spi_message m;
|
||||
int ret;
|
||||
|
||||
BUG_ON(!mutex_is_locked(&mc13783->lock));
|
||||
|
||||
dev_vdbg(&mc13783->spidev->dev, "[0x%02x] <- 0x%06x\n", offset, val);
|
||||
|
||||
if (offset > MC13783_NUMREGS || val > 0xffffff)
|
||||
return -EINVAL;
|
||||
|
||||
buf = 1 << 31 | offset << MC13783_REGOFFSET_SHIFT | val;
|
||||
|
||||
memset(&t, 0, sizeof(t));
|
||||
|
||||
t.tx_buf = &buf;
|
||||
t.rx_buf = &buf;
|
||||
t.len = sizeof(u32);
|
||||
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&t, &m);
|
||||
|
||||
ret = spi_sync(mc13783->spidev, &m);
|
||||
|
||||
BUG_ON(!ret && m.status);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mc13783_reg_write);
|
||||
|
||||
int mc13783_reg_rmw(struct mc13783 *mc13783, unsigned int offset,
|
||||
u32 mask, u32 val)
|
||||
{
|
||||
int ret;
|
||||
u32 valread;
|
||||
|
||||
BUG_ON(val & ~mask);
|
||||
|
||||
ret = mc13783_reg_read(mc13783, offset, &valread);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
valread = (valread & ~mask) | val;
|
||||
|
||||
return mc13783_reg_write(mc13783, offset, valread);
|
||||
}
|
||||
EXPORT_SYMBOL(mc13783_reg_rmw);
|
||||
|
||||
int mc13783_get_flags(struct mc13783 *mc13783)
|
||||
{
|
||||
return mc13783->flags;
|
||||
}
|
||||
EXPORT_SYMBOL(mc13783_get_flags);
|
||||
|
||||
int mc13783_irq_mask(struct mc13783 *mc13783, int irq)
|
||||
{
|
||||
int ret;
|
||||
unsigned int offmask = irq < 24 ? MC13783_IRQMASK0 : MC13783_IRQMASK1;
|
||||
u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
|
||||
u32 mask;
|
||||
|
||||
if (irq < 0 || irq >= MC13783_NUM_IRQ)
|
||||
return -EINVAL;
|
||||
|
||||
ret = mc13783_reg_read(mc13783, offmask, &mask);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (mask & irqbit)
|
||||
/* already masked */
|
||||
return 0;
|
||||
|
||||
return mc13783_reg_write(mc13783, offmask, mask | irqbit);
|
||||
}
|
||||
EXPORT_SYMBOL(mc13783_irq_mask);
|
||||
|
||||
int mc13783_irq_unmask(struct mc13783 *mc13783, int irq)
|
||||
{
|
||||
int ret;
|
||||
unsigned int offmask = irq < 24 ? MC13783_IRQMASK0 : MC13783_IRQMASK1;
|
||||
u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
|
||||
u32 mask;
|
||||
|
||||
if (irq < 0 || irq >= MC13783_NUM_IRQ)
|
||||
return -EINVAL;
|
||||
|
||||
ret = mc13783_reg_read(mc13783, offmask, &mask);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!(mask & irqbit))
|
||||
/* already unmasked */
|
||||
return 0;
|
||||
|
||||
return mc13783_reg_write(mc13783, offmask, mask & ~irqbit);
|
||||
}
|
||||
EXPORT_SYMBOL(mc13783_irq_unmask);
|
||||
|
||||
int mc13783_irq_status(struct mc13783 *mc13783, int irq,
|
||||
int *enabled, int *pending)
|
||||
{
|
||||
int ret;
|
||||
unsigned int offmask = irq < 24 ? MC13783_IRQMASK0 : MC13783_IRQMASK1;
|
||||
unsigned int offstat = irq < 24 ? MC13783_IRQSTAT0 : MC13783_IRQSTAT1;
|
||||
u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
|
||||
|
||||
if (irq < 0 || irq >= MC13783_NUM_IRQ)
|
||||
return -EINVAL;
|
||||
|
||||
if (enabled) {
|
||||
u32 mask;
|
||||
|
||||
ret = mc13783_reg_read(mc13783, offmask, &mask);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*enabled = mask & irqbit;
|
||||
}
|
||||
|
||||
if (pending) {
|
||||
u32 stat;
|
||||
|
||||
ret = mc13783_reg_read(mc13783, offstat, &stat);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*pending = stat & irqbit;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mc13783_irq_status);
|
||||
|
||||
int mc13783_irq_ack(struct mc13783 *mc13783, int irq)
|
||||
{
|
||||
unsigned int offstat = irq < 24 ? MC13783_IRQSTAT0 : MC13783_IRQSTAT1;
|
||||
unsigned int val = 1 << (irq < 24 ? irq : irq - 24);
|
||||
|
||||
BUG_ON(irq < 0 || irq >= MC13783_NUM_IRQ);
|
||||
|
||||
return mc13783_reg_write(mc13783, offstat, val);
|
||||
}
|
||||
EXPORT_SYMBOL(mc13783_irq_ack);
|
||||
|
||||
int mc13783_irq_request_nounmask(struct mc13783 *mc13783, int irq,
|
||||
irq_handler_t handler, const char *name, void *dev)
|
||||
{
|
||||
BUG_ON(!mutex_is_locked(&mc13783->lock));
|
||||
BUG_ON(!handler);
|
||||
|
||||
if (irq < 0 || irq >= MC13783_NUM_IRQ)
|
||||
return -EINVAL;
|
||||
|
||||
if (mc13783->irqhandler[irq])
|
||||
return -EBUSY;
|
||||
|
||||
mc13783->irqhandler[irq] = handler;
|
||||
mc13783->irqdata[irq] = dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mc13783_irq_request_nounmask);
|
||||
|
||||
int mc13783_irq_request(struct mc13783 *mc13783, int irq,
|
||||
irq_handler_t handler, const char *name, void *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = mc13783_irq_request_nounmask(mc13783, irq, handler, name, dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mc13783_irq_unmask(mc13783, irq);
|
||||
if (ret) {
|
||||
mc13783->irqhandler[irq] = NULL;
|
||||
mc13783->irqdata[irq] = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mc13783_irq_request);
|
||||
|
||||
int mc13783_irq_free(struct mc13783 *mc13783, int irq, void *dev)
|
||||
{
|
||||
int ret;
|
||||
BUG_ON(!mutex_is_locked(&mc13783->lock));
|
||||
|
||||
if (irq < 0 || irq >= MC13783_NUM_IRQ || !mc13783->irqhandler[irq] ||
|
||||
mc13783->irqdata[irq] != dev)
|
||||
return -EINVAL;
|
||||
|
||||
ret = mc13783_irq_mask(mc13783, irq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mc13783->irqhandler[irq] = NULL;
|
||||
mc13783->irqdata[irq] = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mc13783_irq_free);
|
||||
|
||||
static inline irqreturn_t mc13783_irqhandler(struct mc13783 *mc13783, int irq)
|
||||
{
|
||||
return mc13783->irqhandler[irq](irq, mc13783->irqdata[irq]);
|
||||
}
|
||||
|
||||
/*
|
||||
* returns: number of handled irqs or negative error
|
||||
* locking: holds mc13783->lock
|
||||
*/
|
||||
static int mc13783_irq_handle(struct mc13783 *mc13783,
|
||||
unsigned int offstat, unsigned int offmask, int baseirq)
|
||||
{
|
||||
u32 stat, mask;
|
||||
int ret = mc13783_reg_read(mc13783, offstat, &stat);
|
||||
int num_handled = 0;
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mc13783_reg_read(mc13783, offmask, &mask);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
while (stat & ~mask) {
|
||||
int irq = __ffs(stat & ~mask);
|
||||
|
||||
stat &= ~(1 << irq);
|
||||
|
||||
if (likely(mc13783->irqhandler[baseirq + irq])) {
|
||||
irqreturn_t handled;
|
||||
|
||||
handled = mc13783_irqhandler(mc13783, baseirq + irq);
|
||||
if (handled == IRQ_HANDLED)
|
||||
num_handled++;
|
||||
} else {
|
||||
dev_err(&mc13783->spidev->dev,
|
||||
"BUG: irq %u but no handler\n",
|
||||
baseirq + irq);
|
||||
|
||||
mask |= 1 << irq;
|
||||
|
||||
ret = mc13783_reg_write(mc13783, offmask, mask);
|
||||
}
|
||||
}
|
||||
|
||||
return num_handled;
|
||||
}
|
||||
|
||||
static irqreturn_t mc13783_irq_thread(int irq, void *data)
|
||||
{
|
||||
struct mc13783 *mc13783 = data;
|
||||
irqreturn_t ret;
|
||||
int handled = 0;
|
||||
|
||||
mc13783_lock(mc13783);
|
||||
|
||||
ret = mc13783_irq_handle(mc13783, MC13783_IRQSTAT0,
|
||||
MC13783_IRQMASK0, MC13783_IRQ_ADCDONE);
|
||||
if (ret > 0)
|
||||
handled = 1;
|
||||
|
||||
ret = mc13783_irq_handle(mc13783, MC13783_IRQSTAT1,
|
||||
MC13783_IRQMASK1, MC13783_IRQ_1HZ);
|
||||
if (ret > 0)
|
||||
handled = 1;
|
||||
|
||||
mc13783_unlock(mc13783);
|
||||
|
||||
return IRQ_RETVAL(handled);
|
||||
}
|
||||
|
||||
#define MC13783_ADC1_CHAN0_SHIFT 5
|
||||
#define MC13783_ADC1_CHAN1_SHIFT 8
|
||||
|
||||
struct mc13783_adcdone_data {
|
||||
struct mc13783 *mc13783;
|
||||
struct completion done;
|
||||
};
|
||||
|
||||
static irqreturn_t mc13783_handler_adcdone(int irq, void *data)
|
||||
{
|
||||
struct mc13783_adcdone_data *adcdone_data = data;
|
||||
|
||||
mc13783_irq_ack(adcdone_data->mc13783, irq);
|
||||
|
||||
complete_all(&adcdone_data->done);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
#define MC13783_ADC_WORKING (1 << 16)
|
||||
|
||||
int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
|
||||
unsigned int channel, unsigned int *sample)
|
||||
{
|
||||
u32 adc0, adc1, old_adc0;
|
||||
int i, ret;
|
||||
struct mc13783_adcdone_data adcdone_data = {
|
||||
.mc13783 = mc13783,
|
||||
};
|
||||
init_completion(&adcdone_data.done);
|
||||
|
||||
dev_dbg(&mc13783->spidev->dev, "%s\n", __func__);
|
||||
|
||||
mc13783_lock(mc13783);
|
||||
|
||||
if (mc13783->flags & MC13783_ADC_WORKING) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mc13783->flags |= MC13783_ADC_WORKING;
|
||||
|
||||
mc13783_reg_read(mc13783, MC13783_ADC0, &old_adc0);
|
||||
|
||||
adc0 = MC13783_ADC0_ADINC1 | MC13783_ADC0_ADINC2;
|
||||
adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN | MC13783_ADC1_ASC;
|
||||
|
||||
if (channel > 7)
|
||||
adc1 |= MC13783_ADC1_ADSEL;
|
||||
|
||||
switch (mode) {
|
||||
case MC13783_ADC_MODE_TS:
|
||||
adc0 |= MC13783_ADC0_ADREFEN | MC13783_ADC0_TSMOD0 |
|
||||
MC13783_ADC0_TSMOD1;
|
||||
adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
|
||||
break;
|
||||
|
||||
case MC13783_ADC_MODE_SINGLE_CHAN:
|
||||
adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK;
|
||||
adc1 |= (channel & 0x7) << MC13783_ADC1_CHAN0_SHIFT;
|
||||
adc1 |= MC13783_ADC1_RAND;
|
||||
break;
|
||||
|
||||
case MC13783_ADC_MODE_MULT_CHAN:
|
||||
adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK;
|
||||
adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
|
||||
break;
|
||||
|
||||
default:
|
||||
mc13783_unlock(mc13783);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(&mc13783->spidev->dev, "%s: request irq\n", __func__);
|
||||
mc13783_irq_request(mc13783, MC13783_IRQ_ADCDONE,
|
||||
mc13783_handler_adcdone, __func__, &adcdone_data);
|
||||
mc13783_irq_ack(mc13783, MC13783_IRQ_ADCDONE);
|
||||
|
||||
mc13783_reg_write(mc13783, MC13783_REG_ADC_0, adc0);
|
||||
mc13783_reg_write(mc13783, MC13783_REG_ADC_1, adc1);
|
||||
|
||||
mc13783_unlock(mc13783);
|
||||
|
||||
ret = wait_for_completion_interruptible_timeout(&adcdone_data.done, HZ);
|
||||
|
||||
if (!ret)
|
||||
ret = -ETIMEDOUT;
|
||||
|
||||
mc13783_lock(mc13783);
|
||||
|
||||
mc13783_irq_free(mc13783, MC13783_IRQ_ADCDONE, &adcdone_data);
|
||||
|
||||
if (ret > 0)
|
||||
for (i = 0; i < 4; ++i) {
|
||||
ret = mc13783_reg_read(mc13783,
|
||||
MC13783_REG_ADC_2, &sample[i]);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
if (mode == MC13783_ADC_MODE_TS)
|
||||
/* restore TSMOD */
|
||||
mc13783_reg_write(mc13783, MC13783_REG_ADC_0, old_adc0);
|
||||
|
||||
mc13783->flags &= ~MC13783_ADC_WORKING;
|
||||
out:
|
||||
mc13783_unlock(mc13783);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mc13783_adc_do_conversion);
|
||||
|
||||
static int mc13783_add_subdevice_pdata(struct mc13783 *mc13783,
|
||||
const char *name, void *pdata, size_t pdata_size)
|
||||
{
|
||||
struct mfd_cell cell = {
|
||||
.name = name,
|
||||
.platform_data = pdata,
|
||||
.data_size = pdata_size,
|
||||
};
|
||||
|
||||
return mfd_add_devices(&mc13783->spidev->dev, -1, &cell, 1, NULL, 0);
|
||||
}
|
||||
|
||||
static int mc13783_add_subdevice(struct mc13783 *mc13783, const char *name)
|
||||
{
|
||||
return mc13783_add_subdevice_pdata(mc13783, name, NULL, 0);
|
||||
}
|
||||
|
||||
static int mc13783_check_revision(struct mc13783 *mc13783)
|
||||
{
|
||||
u32 rev_id, rev1, rev2, finid, icid;
|
||||
|
||||
mc13783_reg_read(mc13783, MC13783_REG_REVISION, &rev_id);
|
||||
|
||||
rev1 = (rev_id & 0x018) >> 3;
|
||||
rev2 = (rev_id & 0x007);
|
||||
icid = (rev_id & 0x01C0) >> 6;
|
||||
finid = (rev_id & 0x01E00) >> 9;
|
||||
|
||||
/* Ver 0.2 is actually 3.2a. Report as 3.2 */
|
||||
if ((rev1 == 0) && (rev2 == 2))
|
||||
rev1 = 3;
|
||||
|
||||
if (rev1 == 0 || icid != 2) {
|
||||
dev_err(&mc13783->spidev->dev, "No MC13783 detected.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dev_info(&mc13783->spidev->dev,
|
||||
"MC13783 Rev %d.%d FinVer %x detected\n",
|
||||
rev1, rev2, finid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mc13783_probe(struct spi_device *spi)
|
||||
{
|
||||
struct mc13783 *mc13783;
|
||||
struct mc13783_platform_data *pdata = dev_get_platdata(&spi->dev);
|
||||
int ret;
|
||||
|
||||
mc13783 = kzalloc(sizeof(*mc13783), GFP_KERNEL);
|
||||
if (!mc13783)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&spi->dev, mc13783);
|
||||
spi->mode = SPI_MODE_0 | SPI_CS_HIGH;
|
||||
spi->bits_per_word = 32;
|
||||
spi_setup(spi);
|
||||
|
||||
mc13783->spidev = spi;
|
||||
|
||||
mutex_init(&mc13783->lock);
|
||||
mc13783_lock(mc13783);
|
||||
|
||||
ret = mc13783_check_revision(mc13783);
|
||||
if (ret)
|
||||
goto err_revision;
|
||||
|
||||
/* mask all irqs */
|
||||
ret = mc13783_reg_write(mc13783, MC13783_IRQMASK0, 0x00ffffff);
|
||||
if (ret)
|
||||
goto err_mask;
|
||||
|
||||
ret = mc13783_reg_write(mc13783, MC13783_IRQMASK1, 0x00ffffff);
|
||||
if (ret)
|
||||
goto err_mask;
|
||||
|
||||
ret = request_threaded_irq(spi->irq, NULL, mc13783_irq_thread,
|
||||
IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mc13783", mc13783);
|
||||
|
||||
if (ret) {
|
||||
err_mask:
|
||||
err_revision:
|
||||
mutex_unlock(&mc13783->lock);
|
||||
dev_set_drvdata(&spi->dev, NULL);
|
||||
kfree(mc13783);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* This should go away (BEGIN) */
|
||||
if (pdata) {
|
||||
mc13783->flags = pdata->flags;
|
||||
mc13783->regulators = pdata->regulators;
|
||||
mc13783->num_regulators = pdata->num_regulators;
|
||||
}
|
||||
/* This should go away (END) */
|
||||
|
||||
mc13783_unlock(mc13783);
|
||||
|
||||
if (pdata->flags & MC13783_USE_ADC)
|
||||
mc13783_add_subdevice(mc13783, "mc13783-adc");
|
||||
|
||||
if (pdata->flags & MC13783_USE_CODEC)
|
||||
mc13783_add_subdevice(mc13783, "mc13783-codec");
|
||||
|
||||
if (pdata->flags & MC13783_USE_REGULATOR) {
|
||||
struct mc13783_regulator_platform_data regulator_pdata = {
|
||||
.num_regulators = pdata->num_regulators,
|
||||
.regulators = pdata->regulators,
|
||||
};
|
||||
|
||||
mc13783_add_subdevice_pdata(mc13783, "mc13783-regulator",
|
||||
®ulator_pdata, sizeof(regulator_pdata));
|
||||
}
|
||||
|
||||
if (pdata->flags & MC13783_USE_RTC)
|
||||
mc13783_add_subdevice(mc13783, "mc13783-rtc");
|
||||
|
||||
if (pdata->flags & MC13783_USE_TOUCHSCREEN)
|
||||
mc13783_add_subdevice(mc13783, "mc13783-ts");
|
||||
|
||||
if (pdata->flags & MC13783_USE_LED)
|
||||
mc13783_add_subdevice_pdata(mc13783, "mc13783-led",
|
||||
pdata->leds, sizeof(*pdata->leds));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devexit mc13783_remove(struct spi_device *spi)
|
||||
{
|
||||
struct mc13783 *mc13783 = dev_get_drvdata(&spi->dev);
|
||||
|
||||
free_irq(mc13783->spidev->irq, mc13783);
|
||||
|
||||
mfd_remove_devices(&spi->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct spi_driver mc13783_driver = {
|
||||
.driver = {
|
||||
.name = "mc13783",
|
||||
.bus = &spi_bus_type,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = mc13783_probe,
|
||||
.remove = __devexit_p(mc13783_remove),
|
||||
};
|
||||
|
||||
static int __init mc13783_init(void)
|
||||
{
|
||||
return spi_register_driver(&mc13783_driver);
|
||||
}
|
||||
subsys_initcall(mc13783_init);
|
||||
|
||||
static void __exit mc13783_exit(void)
|
||||
{
|
||||
spi_unregister_driver(&mc13783_driver);
|
||||
}
|
||||
module_exit(mc13783_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Core driver for Freescale MC13783 PMIC");
|
||||
MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
|
||||
MODULE_LICENSE("GPL v2");
|
840
drivers/mfd/mc13xxx-core.c
Normal file
840
drivers/mfd/mc13xxx-core.c
Normal file
@ -0,0 +1,840 @@
|
||||
/*
|
||||
* Copyright 2009-2010 Pengutronix
|
||||
* Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
|
||||
*
|
||||
* loosely based on an earlier driver that has
|
||||
* Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License version 2 as published by the
|
||||
* Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/mc13xxx.h>
|
||||
|
||||
struct mc13xxx {
|
||||
struct spi_device *spidev;
|
||||
struct mutex lock;
|
||||
int irq;
|
||||
|
||||
irq_handler_t irqhandler[MC13XXX_NUM_IRQ];
|
||||
void *irqdata[MC13XXX_NUM_IRQ];
|
||||
};
|
||||
|
||||
struct mc13783 {
|
||||
struct mc13xxx mc13xxx;
|
||||
|
||||
int adcflags;
|
||||
};
|
||||
|
||||
struct mc13xxx *mc13783_to_mc13xxx(struct mc13783 *mc13783)
|
||||
{
|
||||
return &mc13783->mc13xxx;
|
||||
}
|
||||
EXPORT_SYMBOL(mc13783_to_mc13xxx);
|
||||
|
||||
#define MC13XXX_IRQSTAT0 0
|
||||
#define MC13XXX_IRQSTAT0_ADCDONEI (1 << 0)
|
||||
#define MC13XXX_IRQSTAT0_ADCBISDONEI (1 << 1)
|
||||
#define MC13XXX_IRQSTAT0_TSI (1 << 2)
|
||||
#define MC13783_IRQSTAT0_WHIGHI (1 << 3)
|
||||
#define MC13783_IRQSTAT0_WLOWI (1 << 4)
|
||||
#define MC13XXX_IRQSTAT0_CHGDETI (1 << 6)
|
||||
#define MC13783_IRQSTAT0_CHGOVI (1 << 7)
|
||||
#define MC13XXX_IRQSTAT0_CHGREVI (1 << 8)
|
||||
#define MC13XXX_IRQSTAT0_CHGSHORTI (1 << 9)
|
||||
#define MC13XXX_IRQSTAT0_CCCVI (1 << 10)
|
||||
#define MC13XXX_IRQSTAT0_CHGCURRI (1 << 11)
|
||||
#define MC13XXX_IRQSTAT0_BPONI (1 << 12)
|
||||
#define MC13XXX_IRQSTAT0_LOBATLI (1 << 13)
|
||||
#define MC13XXX_IRQSTAT0_LOBATHI (1 << 14)
|
||||
#define MC13783_IRQSTAT0_UDPI (1 << 15)
|
||||
#define MC13783_IRQSTAT0_USBI (1 << 16)
|
||||
#define MC13783_IRQSTAT0_IDI (1 << 19)
|
||||
#define MC13783_IRQSTAT0_SE1I (1 << 21)
|
||||
#define MC13783_IRQSTAT0_CKDETI (1 << 22)
|
||||
#define MC13783_IRQSTAT0_UDMI (1 << 23)
|
||||
|
||||
#define MC13XXX_IRQMASK0 1
|
||||
#define MC13XXX_IRQMASK0_ADCDONEM MC13XXX_IRQSTAT0_ADCDONEI
|
||||
#define MC13XXX_IRQMASK0_ADCBISDONEM MC13XXX_IRQSTAT0_ADCBISDONEI
|
||||
#define MC13XXX_IRQMASK0_TSM MC13XXX_IRQSTAT0_TSI
|
||||
#define MC13783_IRQMASK0_WHIGHM MC13783_IRQSTAT0_WHIGHI
|
||||
#define MC13783_IRQMASK0_WLOWM MC13783_IRQSTAT0_WLOWI
|
||||
#define MC13XXX_IRQMASK0_CHGDETM MC13XXX_IRQSTAT0_CHGDETI
|
||||
#define MC13783_IRQMASK0_CHGOVM MC13783_IRQSTAT0_CHGOVI
|
||||
#define MC13XXX_IRQMASK0_CHGREVM MC13XXX_IRQSTAT0_CHGREVI
|
||||
#define MC13XXX_IRQMASK0_CHGSHORTM MC13XXX_IRQSTAT0_CHGSHORTI
|
||||
#define MC13XXX_IRQMASK0_CCCVM MC13XXX_IRQSTAT0_CCCVI
|
||||
#define MC13XXX_IRQMASK0_CHGCURRM MC13XXX_IRQSTAT0_CHGCURRI
|
||||
#define MC13XXX_IRQMASK0_BPONM MC13XXX_IRQSTAT0_BPONI
|
||||
#define MC13XXX_IRQMASK0_LOBATLM MC13XXX_IRQSTAT0_LOBATLI
|
||||
#define MC13XXX_IRQMASK0_LOBATHM MC13XXX_IRQSTAT0_LOBATHI
|
||||
#define MC13783_IRQMASK0_UDPM MC13783_IRQSTAT0_UDPI
|
||||
#define MC13783_IRQMASK0_USBM MC13783_IRQSTAT0_USBI
|
||||
#define MC13783_IRQMASK0_IDM MC13783_IRQSTAT0_IDI
|
||||
#define MC13783_IRQMASK0_SE1M MC13783_IRQSTAT0_SE1I
|
||||
#define MC13783_IRQMASK0_CKDETM MC13783_IRQSTAT0_CKDETI
|
||||
#define MC13783_IRQMASK0_UDMM MC13783_IRQSTAT0_UDMI
|
||||
|
||||
#define MC13XXX_IRQSTAT1 3
|
||||
#define MC13XXX_IRQSTAT1_1HZI (1 << 0)
|
||||
#define MC13XXX_IRQSTAT1_TODAI (1 << 1)
|
||||
#define MC13783_IRQSTAT1_ONOFD1I (1 << 3)
|
||||
#define MC13783_IRQSTAT1_ONOFD2I (1 << 4)
|
||||
#define MC13783_IRQSTAT1_ONOFD3I (1 << 5)
|
||||
#define MC13XXX_IRQSTAT1_SYSRSTI (1 << 6)
|
||||
#define MC13XXX_IRQSTAT1_RTCRSTI (1 << 7)
|
||||
#define MC13XXX_IRQSTAT1_PCI (1 << 8)
|
||||
#define MC13XXX_IRQSTAT1_WARMI (1 << 9)
|
||||
#define MC13XXX_IRQSTAT1_MEMHLDI (1 << 10)
|
||||
#define MC13783_IRQSTAT1_PWRRDYI (1 << 11)
|
||||
#define MC13XXX_IRQSTAT1_THWARNLI (1 << 12)
|
||||
#define MC13XXX_IRQSTAT1_THWARNHI (1 << 13)
|
||||
#define MC13XXX_IRQSTAT1_CLKI (1 << 14)
|
||||
#define MC13783_IRQSTAT1_SEMAFI (1 << 15)
|
||||
#define MC13783_IRQSTAT1_MC2BI (1 << 17)
|
||||
#define MC13783_IRQSTAT1_HSDETI (1 << 18)
|
||||
#define MC13783_IRQSTAT1_HSLI (1 << 19)
|
||||
#define MC13783_IRQSTAT1_ALSPTHI (1 << 20)
|
||||
#define MC13783_IRQSTAT1_AHSSHORTI (1 << 21)
|
||||
|
||||
#define MC13XXX_IRQMASK1 4
|
||||
#define MC13XXX_IRQMASK1_1HZM MC13XXX_IRQSTAT1_1HZI
|
||||
#define MC13XXX_IRQMASK1_TODAM MC13XXX_IRQSTAT1_TODAI
|
||||
#define MC13783_IRQMASK1_ONOFD1M MC13783_IRQSTAT1_ONOFD1I
|
||||
#define MC13783_IRQMASK1_ONOFD2M MC13783_IRQSTAT1_ONOFD2I
|
||||
#define MC13783_IRQMASK1_ONOFD3M MC13783_IRQSTAT1_ONOFD3I
|
||||
#define MC13XXX_IRQMASK1_SYSRSTM MC13XXX_IRQSTAT1_SYSRSTI
|
||||
#define MC13XXX_IRQMASK1_RTCRSTM MC13XXX_IRQSTAT1_RTCRSTI
|
||||
#define MC13XXX_IRQMASK1_PCM MC13XXX_IRQSTAT1_PCI
|
||||
#define MC13XXX_IRQMASK1_WARMM MC13XXX_IRQSTAT1_WARMI
|
||||
#define MC13XXX_IRQMASK1_MEMHLDM MC13XXX_IRQSTAT1_MEMHLDI
|
||||
#define MC13783_IRQMASK1_PWRRDYM MC13783_IRQSTAT1_PWRRDYI
|
||||
#define MC13XXX_IRQMASK1_THWARNLM MC13XXX_IRQSTAT1_THWARNLI
|
||||
#define MC13XXX_IRQMASK1_THWARNHM MC13XXX_IRQSTAT1_THWARNHI
|
||||
#define MC13XXX_IRQMASK1_CLKM MC13XXX_IRQSTAT1_CLKI
|
||||
#define MC13783_IRQMASK1_SEMAFM MC13783_IRQSTAT1_SEMAFI
|
||||
#define MC13783_IRQMASK1_MC2BM MC13783_IRQSTAT1_MC2BI
|
||||
#define MC13783_IRQMASK1_HSDETM MC13783_IRQSTAT1_HSDETI
|
||||
#define MC13783_IRQMASK1_HSLM MC13783_IRQSTAT1_HSLI
|
||||
#define MC13783_IRQMASK1_ALSPTHM MC13783_IRQSTAT1_ALSPTHI
|
||||
#define MC13783_IRQMASK1_AHSSHORTM MC13783_IRQSTAT1_AHSSHORTI
|
||||
|
||||
#define MC13XXX_REVISION 7
|
||||
#define MC13XXX_REVISION_REVMETAL (0x07 << 0)
|
||||
#define MC13XXX_REVISION_REVFULL (0x03 << 3)
|
||||
#define MC13XXX_REVISION_ICID (0x07 << 6)
|
||||
#define MC13XXX_REVISION_FIN (0x03 << 9)
|
||||
#define MC13XXX_REVISION_FAB (0x03 << 11)
|
||||
#define MC13XXX_REVISION_ICIDCODE (0x3f << 13)
|
||||
|
||||
#define MC13783_ADC1 44
|
||||
#define MC13783_ADC1_ADEN (1 << 0)
|
||||
#define MC13783_ADC1_RAND (1 << 1)
|
||||
#define MC13783_ADC1_ADSEL (1 << 3)
|
||||
#define MC13783_ADC1_ASC (1 << 20)
|
||||
#define MC13783_ADC1_ADTRIGIGN (1 << 21)
|
||||
|
||||
#define MC13783_ADC2 45
|
||||
|
||||
#define MC13XXX_NUMREGS 0x3f
|
||||
|
||||
void mc13xxx_lock(struct mc13xxx *mc13xxx)
|
||||
{
|
||||
if (!mutex_trylock(&mc13xxx->lock)) {
|
||||
dev_dbg(&mc13xxx->spidev->dev, "wait for %s from %pf\n",
|
||||
__func__, __builtin_return_address(0));
|
||||
|
||||
mutex_lock(&mc13xxx->lock);
|
||||
}
|
||||
dev_dbg(&mc13xxx->spidev->dev, "%s from %pf\n",
|
||||
__func__, __builtin_return_address(0));
|
||||
}
|
||||
EXPORT_SYMBOL(mc13xxx_lock);
|
||||
|
||||
void mc13xxx_unlock(struct mc13xxx *mc13xxx)
|
||||
{
|
||||
dev_dbg(&mc13xxx->spidev->dev, "%s from %pf\n",
|
||||
__func__, __builtin_return_address(0));
|
||||
mutex_unlock(&mc13xxx->lock);
|
||||
}
|
||||
EXPORT_SYMBOL(mc13xxx_unlock);
|
||||
|
||||
#define MC13XXX_REGOFFSET_SHIFT 25
|
||||
int mc13xxx_reg_read(struct mc13xxx *mc13xxx, unsigned int offset, u32 *val)
|
||||
{
|
||||
struct spi_transfer t;
|
||||
struct spi_message m;
|
||||
int ret;
|
||||
|
||||
BUG_ON(!mutex_is_locked(&mc13xxx->lock));
|
||||
|
||||
if (offset > MC13XXX_NUMREGS)
|
||||
return -EINVAL;
|
||||
|
||||
*val = offset << MC13XXX_REGOFFSET_SHIFT;
|
||||
|
||||
memset(&t, 0, sizeof(t));
|
||||
|
||||
t.tx_buf = val;
|
||||
t.rx_buf = val;
|
||||
t.len = sizeof(u32);
|
||||
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&t, &m);
|
||||
|
||||
ret = spi_sync(mc13xxx->spidev, &m);
|
||||
|
||||
/* error in message.status implies error return from spi_sync */
|
||||
BUG_ON(!ret && m.status);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val &= 0xffffff;
|
||||
|
||||
dev_vdbg(&mc13xxx->spidev->dev, "[0x%02x] -> 0x%06x\n", offset, *val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mc13xxx_reg_read);
|
||||
|
||||
int mc13xxx_reg_write(struct mc13xxx *mc13xxx, unsigned int offset, u32 val)
|
||||
{
|
||||
u32 buf;
|
||||
struct spi_transfer t;
|
||||
struct spi_message m;
|
||||
int ret;
|
||||
|
||||
BUG_ON(!mutex_is_locked(&mc13xxx->lock));
|
||||
|
||||
dev_vdbg(&mc13xxx->spidev->dev, "[0x%02x] <- 0x%06x\n", offset, val);
|
||||
|
||||
if (offset > MC13XXX_NUMREGS || val > 0xffffff)
|
||||
return -EINVAL;
|
||||
|
||||
buf = 1 << 31 | offset << MC13XXX_REGOFFSET_SHIFT | val;
|
||||
|
||||
memset(&t, 0, sizeof(t));
|
||||
|
||||
t.tx_buf = &buf;
|
||||
t.rx_buf = &buf;
|
||||
t.len = sizeof(u32);
|
||||
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&t, &m);
|
||||
|
||||
ret = spi_sync(mc13xxx->spidev, &m);
|
||||
|
||||
BUG_ON(!ret && m.status);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mc13xxx_reg_write);
|
||||
|
||||
int mc13xxx_reg_rmw(struct mc13xxx *mc13xxx, unsigned int offset,
|
||||
u32 mask, u32 val)
|
||||
{
|
||||
int ret;
|
||||
u32 valread;
|
||||
|
||||
BUG_ON(val & ~mask);
|
||||
|
||||
ret = mc13xxx_reg_read(mc13xxx, offset, &valread);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
valread = (valread & ~mask) | val;
|
||||
|
||||
return mc13xxx_reg_write(mc13xxx, offset, valread);
|
||||
}
|
||||
EXPORT_SYMBOL(mc13xxx_reg_rmw);
|
||||
|
||||
int mc13xxx_irq_mask(struct mc13xxx *mc13xxx, int irq)
|
||||
{
|
||||
int ret;
|
||||
unsigned int offmask = irq < 24 ? MC13XXX_IRQMASK0 : MC13XXX_IRQMASK1;
|
||||
u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
|
||||
u32 mask;
|
||||
|
||||
if (irq < 0 || irq >= MC13XXX_NUM_IRQ)
|
||||
return -EINVAL;
|
||||
|
||||
ret = mc13xxx_reg_read(mc13xxx, offmask, &mask);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (mask & irqbit)
|
||||
/* already masked */
|
||||
return 0;
|
||||
|
||||
return mc13xxx_reg_write(mc13xxx, offmask, mask | irqbit);
|
||||
}
|
||||
EXPORT_SYMBOL(mc13xxx_irq_mask);
|
||||
|
||||
int mc13xxx_irq_unmask(struct mc13xxx *mc13xxx, int irq)
|
||||
{
|
||||
int ret;
|
||||
unsigned int offmask = irq < 24 ? MC13XXX_IRQMASK0 : MC13XXX_IRQMASK1;
|
||||
u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
|
||||
u32 mask;
|
||||
|
||||
if (irq < 0 || irq >= MC13XXX_NUM_IRQ)
|
||||
return -EINVAL;
|
||||
|
||||
ret = mc13xxx_reg_read(mc13xxx, offmask, &mask);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!(mask & irqbit))
|
||||
/* already unmasked */
|
||||
return 0;
|
||||
|
||||
return mc13xxx_reg_write(mc13xxx, offmask, mask & ~irqbit);
|
||||
}
|
||||
EXPORT_SYMBOL(mc13xxx_irq_unmask);
|
||||
|
||||
int mc13xxx_irq_status(struct mc13xxx *mc13xxx, int irq,
|
||||
int *enabled, int *pending)
|
||||
{
|
||||
int ret;
|
||||
unsigned int offmask = irq < 24 ? MC13XXX_IRQMASK0 : MC13XXX_IRQMASK1;
|
||||
unsigned int offstat = irq < 24 ? MC13XXX_IRQSTAT0 : MC13XXX_IRQSTAT1;
|
||||
u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
|
||||
|
||||
if (irq < 0 || irq >= MC13XXX_NUM_IRQ)
|
||||
return -EINVAL;
|
||||
|
||||
if (enabled) {
|
||||
u32 mask;
|
||||
|
||||
ret = mc13xxx_reg_read(mc13xxx, offmask, &mask);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*enabled = mask & irqbit;
|
||||
}
|
||||
|
||||
if (pending) {
|
||||
u32 stat;
|
||||
|
||||
ret = mc13xxx_reg_read(mc13xxx, offstat, &stat);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*pending = stat & irqbit;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mc13xxx_irq_status);
|
||||
|
||||
int mc13xxx_irq_ack(struct mc13xxx *mc13xxx, int irq)
|
||||
{
|
||||
unsigned int offstat = irq < 24 ? MC13XXX_IRQSTAT0 : MC13XXX_IRQSTAT1;
|
||||
unsigned int val = 1 << (irq < 24 ? irq : irq - 24);
|
||||
|
||||
BUG_ON(irq < 0 || irq >= MC13XXX_NUM_IRQ);
|
||||
|
||||
return mc13xxx_reg_write(mc13xxx, offstat, val);
|
||||
}
|
||||
EXPORT_SYMBOL(mc13xxx_irq_ack);
|
||||
|
||||
int mc13xxx_irq_request_nounmask(struct mc13xxx *mc13xxx, int irq,
|
||||
irq_handler_t handler, const char *name, void *dev)
|
||||
{
|
||||
BUG_ON(!mutex_is_locked(&mc13xxx->lock));
|
||||
BUG_ON(!handler);
|
||||
|
||||
if (irq < 0 || irq >= MC13XXX_NUM_IRQ)
|
||||
return -EINVAL;
|
||||
|
||||
if (mc13xxx->irqhandler[irq])
|
||||
return -EBUSY;
|
||||
|
||||
mc13xxx->irqhandler[irq] = handler;
|
||||
mc13xxx->irqdata[irq] = dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mc13xxx_irq_request_nounmask);
|
||||
|
||||
int mc13xxx_irq_request(struct mc13xxx *mc13xxx, int irq,
|
||||
irq_handler_t handler, const char *name, void *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = mc13xxx_irq_request_nounmask(mc13xxx, irq, handler, name, dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mc13xxx_irq_unmask(mc13xxx, irq);
|
||||
if (ret) {
|
||||
mc13xxx->irqhandler[irq] = NULL;
|
||||
mc13xxx->irqdata[irq] = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mc13xxx_irq_request);
|
||||
|
||||
int mc13xxx_irq_free(struct mc13xxx *mc13xxx, int irq, void *dev)
|
||||
{
|
||||
int ret;
|
||||
BUG_ON(!mutex_is_locked(&mc13xxx->lock));
|
||||
|
||||
if (irq < 0 || irq >= MC13XXX_NUM_IRQ || !mc13xxx->irqhandler[irq] ||
|
||||
mc13xxx->irqdata[irq] != dev)
|
||||
return -EINVAL;
|
||||
|
||||
ret = mc13xxx_irq_mask(mc13xxx, irq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mc13xxx->irqhandler[irq] = NULL;
|
||||
mc13xxx->irqdata[irq] = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mc13xxx_irq_free);
|
||||
|
||||
static inline irqreturn_t mc13xxx_irqhandler(struct mc13xxx *mc13xxx, int irq)
|
||||
{
|
||||
return mc13xxx->irqhandler[irq](irq, mc13xxx->irqdata[irq]);
|
||||
}
|
||||
|
||||
/*
|
||||
* returns: number of handled irqs or negative error
|
||||
* locking: holds mc13xxx->lock
|
||||
*/
|
||||
static int mc13xxx_irq_handle(struct mc13xxx *mc13xxx,
|
||||
unsigned int offstat, unsigned int offmask, int baseirq)
|
||||
{
|
||||
u32 stat, mask;
|
||||
int ret = mc13xxx_reg_read(mc13xxx, offstat, &stat);
|
||||
int num_handled = 0;
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mc13xxx_reg_read(mc13xxx, offmask, &mask);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
while (stat & ~mask) {
|
||||
int irq = __ffs(stat & ~mask);
|
||||
|
||||
stat &= ~(1 << irq);
|
||||
|
||||
if (likely(mc13xxx->irqhandler[baseirq + irq])) {
|
||||
irqreturn_t handled;
|
||||
|
||||
handled = mc13xxx_irqhandler(mc13xxx, baseirq + irq);
|
||||
if (handled == IRQ_HANDLED)
|
||||
num_handled++;
|
||||
} else {
|
||||
dev_err(&mc13xxx->spidev->dev,
|
||||
"BUG: irq %u but no handler\n",
|
||||
baseirq + irq);
|
||||
|
||||
mask |= 1 << irq;
|
||||
|
||||
ret = mc13xxx_reg_write(mc13xxx, offmask, mask);
|
||||
}
|
||||
}
|
||||
|
||||
return num_handled;
|
||||
}
|
||||
|
||||
static irqreturn_t mc13xxx_irq_thread(int irq, void *data)
|
||||
{
|
||||
struct mc13xxx *mc13xxx = data;
|
||||
irqreturn_t ret;
|
||||
int handled = 0;
|
||||
|
||||
mc13xxx_lock(mc13xxx);
|
||||
|
||||
ret = mc13xxx_irq_handle(mc13xxx, MC13XXX_IRQSTAT0,
|
||||
MC13XXX_IRQMASK0, 0);
|
||||
if (ret > 0)
|
||||
handled = 1;
|
||||
|
||||
ret = mc13xxx_irq_handle(mc13xxx, MC13XXX_IRQSTAT1,
|
||||
MC13XXX_IRQMASK1, 24);
|
||||
if (ret > 0)
|
||||
handled = 1;
|
||||
|
||||
mc13xxx_unlock(mc13xxx);
|
||||
|
||||
return IRQ_RETVAL(handled);
|
||||
}
|
||||
|
||||
enum mc13xxx_id {
|
||||
MC13XXX_ID_MC13783,
|
||||
MC13XXX_ID_MC13892,
|
||||
MC13XXX_ID_INVALID,
|
||||
};
|
||||
|
||||
const char *mc13xxx_chipname[] = {
|
||||
[MC13XXX_ID_MC13783] = "mc13783",
|
||||
[MC13XXX_ID_MC13892] = "mc13892",
|
||||
};
|
||||
|
||||
#define maskval(reg, mask) (((reg) & (mask)) >> __ffs(mask))
|
||||
static int mc13xxx_identify(struct mc13xxx *mc13xxx, enum mc13xxx_id *id)
|
||||
{
|
||||
u32 icid;
|
||||
u32 revision;
|
||||
const char *name;
|
||||
int ret;
|
||||
|
||||
ret = mc13xxx_reg_read(mc13xxx, 46, &icid);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
icid = (icid >> 6) & 0x7;
|
||||
|
||||
switch (icid) {
|
||||
case 2:
|
||||
*id = MC13XXX_ID_MC13783;
|
||||
name = "mc13783";
|
||||
break;
|
||||
case 7:
|
||||
*id = MC13XXX_ID_MC13892;
|
||||
name = "mc13892";
|
||||
break;
|
||||
default:
|
||||
*id = MC13XXX_ID_INVALID;
|
||||
break;
|
||||
}
|
||||
|
||||
if (*id == MC13XXX_ID_MC13783 || *id == MC13XXX_ID_MC13892) {
|
||||
ret = mc13xxx_reg_read(mc13xxx, MC13XXX_REVISION, &revision);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_info(&mc13xxx->spidev->dev, "%s: rev: %d.%d, "
|
||||
"fin: %d, fab: %d, icid: %d/%d\n",
|
||||
mc13xxx_chipname[*id],
|
||||
maskval(revision, MC13XXX_REVISION_REVFULL),
|
||||
maskval(revision, MC13XXX_REVISION_REVMETAL),
|
||||
maskval(revision, MC13XXX_REVISION_FIN),
|
||||
maskval(revision, MC13XXX_REVISION_FAB),
|
||||
maskval(revision, MC13XXX_REVISION_ICID),
|
||||
maskval(revision, MC13XXX_REVISION_ICIDCODE));
|
||||
}
|
||||
|
||||
if (*id != MC13XXX_ID_INVALID) {
|
||||
const struct spi_device_id *devid =
|
||||
spi_get_device_id(mc13xxx->spidev);
|
||||
if (!devid || devid->driver_data != *id)
|
||||
dev_warn(&mc13xxx->spidev->dev, "device id doesn't "
|
||||
"match auto detection!\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *mc13xxx_get_chipname(struct mc13xxx *mc13xxx)
|
||||
{
|
||||
const struct spi_device_id *devid =
|
||||
spi_get_device_id(mc13xxx->spidev);
|
||||
|
||||
if (!devid)
|
||||
return NULL;
|
||||
|
||||
return mc13xxx_chipname[devid->driver_data];
|
||||
}
|
||||
|
||||
#include <linux/mfd/mc13783.h>
|
||||
|
||||
int mc13xxx_get_flags(struct mc13xxx *mc13xxx)
|
||||
{
|
||||
struct mc13xxx_platform_data *pdata =
|
||||
dev_get_platdata(&mc13xxx->spidev->dev);
|
||||
|
||||
return pdata->flags;
|
||||
}
|
||||
EXPORT_SYMBOL(mc13xxx_get_flags);
|
||||
|
||||
#define MC13783_ADC1_CHAN0_SHIFT 5
|
||||
#define MC13783_ADC1_CHAN1_SHIFT 8
|
||||
|
||||
struct mc13xxx_adcdone_data {
|
||||
struct mc13xxx *mc13xxx;
|
||||
struct completion done;
|
||||
};
|
||||
|
||||
static irqreturn_t mc13783_handler_adcdone(int irq, void *data)
|
||||
{
|
||||
struct mc13xxx_adcdone_data *adcdone_data = data;
|
||||
|
||||
mc13xxx_irq_ack(adcdone_data->mc13xxx, irq);
|
||||
|
||||
complete_all(&adcdone_data->done);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
#define MC13783_ADC_WORKING (1 << 0)
|
||||
|
||||
int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
|
||||
unsigned int channel, unsigned int *sample)
|
||||
{
|
||||
struct mc13xxx *mc13xxx = &mc13783->mc13xxx;
|
||||
u32 adc0, adc1, old_adc0;
|
||||
int i, ret;
|
||||
struct mc13xxx_adcdone_data adcdone_data = {
|
||||
.mc13xxx = mc13xxx,
|
||||
};
|
||||
init_completion(&adcdone_data.done);
|
||||
|
||||
dev_dbg(&mc13xxx->spidev->dev, "%s\n", __func__);
|
||||
|
||||
mc13xxx_lock(mc13xxx);
|
||||
|
||||
if (mc13783->adcflags & MC13783_ADC_WORKING) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mc13783->adcflags |= MC13783_ADC_WORKING;
|
||||
|
||||
mc13xxx_reg_read(mc13xxx, MC13783_ADC0, &old_adc0);
|
||||
|
||||
adc0 = MC13783_ADC0_ADINC1 | MC13783_ADC0_ADINC2;
|
||||
adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN | MC13783_ADC1_ASC;
|
||||
|
||||
if (channel > 7)
|
||||
adc1 |= MC13783_ADC1_ADSEL;
|
||||
|
||||
switch (mode) {
|
||||
case MC13783_ADC_MODE_TS:
|
||||
adc0 |= MC13783_ADC0_ADREFEN | MC13783_ADC0_TSMOD0 |
|
||||
MC13783_ADC0_TSMOD1;
|
||||
adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
|
||||
break;
|
||||
|
||||
case MC13783_ADC_MODE_SINGLE_CHAN:
|
||||
adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK;
|
||||
adc1 |= (channel & 0x7) << MC13783_ADC1_CHAN0_SHIFT;
|
||||
adc1 |= MC13783_ADC1_RAND;
|
||||
break;
|
||||
|
||||
case MC13783_ADC_MODE_MULT_CHAN:
|
||||
adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK;
|
||||
adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
|
||||
break;
|
||||
|
||||
default:
|
||||
mc13783_unlock(mc13783);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(&mc13783->mc13xxx.spidev->dev, "%s: request irq\n", __func__);
|
||||
mc13xxx_irq_request(mc13xxx, MC13783_IRQ_ADCDONE,
|
||||
mc13783_handler_adcdone, __func__, &adcdone_data);
|
||||
mc13xxx_irq_ack(mc13xxx, MC13783_IRQ_ADCDONE);
|
||||
|
||||
mc13xxx_reg_write(mc13xxx, MC13783_ADC0, adc0);
|
||||
mc13xxx_reg_write(mc13xxx, MC13783_ADC1, adc1);
|
||||
|
||||
mc13xxx_unlock(mc13xxx);
|
||||
|
||||
ret = wait_for_completion_interruptible_timeout(&adcdone_data.done, HZ);
|
||||
|
||||
if (!ret)
|
||||
ret = -ETIMEDOUT;
|
||||
|
||||
mc13xxx_lock(mc13xxx);
|
||||
|
||||
mc13xxx_irq_free(mc13xxx, MC13783_IRQ_ADCDONE, &adcdone_data);
|
||||
|
||||
if (ret > 0)
|
||||
for (i = 0; i < 4; ++i) {
|
||||
ret = mc13xxx_reg_read(mc13xxx,
|
||||
MC13783_ADC2, &sample[i]);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
if (mode == MC13783_ADC_MODE_TS)
|
||||
/* restore TSMOD */
|
||||
mc13xxx_reg_write(mc13xxx, MC13783_ADC0, old_adc0);
|
||||
|
||||
mc13783->adcflags &= ~MC13783_ADC_WORKING;
|
||||
out:
|
||||
mc13xxx_unlock(mc13xxx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mc13783_adc_do_conversion);
|
||||
|
||||
static int mc13xxx_add_subdevice_pdata(struct mc13xxx *mc13xxx,
|
||||
const char *format, void *pdata, size_t pdata_size)
|
||||
{
|
||||
char buf[30];
|
||||
const char *name = mc13xxx_get_chipname(mc13xxx);
|
||||
|
||||
struct mfd_cell cell = {
|
||||
.platform_data = pdata,
|
||||
.data_size = pdata_size,
|
||||
};
|
||||
|
||||
/* there is no asnprintf in the kernel :-( */
|
||||
if (snprintf(buf, sizeof(buf), format, name) > sizeof(buf))
|
||||
return -E2BIG;
|
||||
|
||||
cell.name = kmemdup(buf, strlen(buf) + 1, GFP_KERNEL);
|
||||
if (!cell.name)
|
||||
return -ENOMEM;
|
||||
|
||||
return mfd_add_devices(&mc13xxx->spidev->dev, -1, &cell, 1, NULL, 0);
|
||||
}
|
||||
|
||||
static int mc13xxx_add_subdevice(struct mc13xxx *mc13xxx, const char *format)
|
||||
{
|
||||
return mc13xxx_add_subdevice_pdata(mc13xxx, format, NULL, 0);
|
||||
}
|
||||
|
||||
static int mc13xxx_probe(struct spi_device *spi)
|
||||
{
|
||||
struct mc13xxx *mc13xxx;
|
||||
struct mc13xxx_platform_data *pdata = dev_get_platdata(&spi->dev);
|
||||
enum mc13xxx_id id;
|
||||
int ret;
|
||||
|
||||
mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL);
|
||||
if (!mc13xxx)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&spi->dev, mc13xxx);
|
||||
spi->mode = SPI_MODE_0 | SPI_CS_HIGH;
|
||||
spi->bits_per_word = 32;
|
||||
spi_setup(spi);
|
||||
|
||||
mc13xxx->spidev = spi;
|
||||
|
||||
mutex_init(&mc13xxx->lock);
|
||||
mc13xxx_lock(mc13xxx);
|
||||
|
||||
ret = mc13xxx_identify(mc13xxx, &id);
|
||||
if (ret || id == MC13XXX_ID_INVALID)
|
||||
goto err_revision;
|
||||
|
||||
/* mask all irqs */
|
||||
ret = mc13xxx_reg_write(mc13xxx, MC13XXX_IRQMASK0, 0x00ffffff);
|
||||
if (ret)
|
||||
goto err_mask;
|
||||
|
||||
ret = mc13xxx_reg_write(mc13xxx, MC13XXX_IRQMASK1, 0x00ffffff);
|
||||
if (ret)
|
||||
goto err_mask;
|
||||
|
||||
ret = request_threaded_irq(spi->irq, NULL, mc13xxx_irq_thread,
|
||||
IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mc13xxx", mc13xxx);
|
||||
|
||||
if (ret) {
|
||||
err_mask:
|
||||
err_revision:
|
||||
mutex_unlock(&mc13xxx->lock);
|
||||
dev_set_drvdata(&spi->dev, NULL);
|
||||
kfree(mc13xxx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
mc13xxx_unlock(mc13xxx);
|
||||
|
||||
if (pdata->flags & MC13XXX_USE_ADC)
|
||||
mc13xxx_add_subdevice(mc13xxx, "%s-adc");
|
||||
|
||||
if (pdata->flags & MC13XXX_USE_CODEC)
|
||||
mc13xxx_add_subdevice(mc13xxx, "%s-codec");
|
||||
|
||||
if (pdata->flags & MC13XXX_USE_REGULATOR) {
|
||||
struct mc13xxx_regulator_platform_data regulator_pdata = {
|
||||
.num_regulators = pdata->num_regulators,
|
||||
.regulators = pdata->regulators,
|
||||
};
|
||||
|
||||
mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator",
|
||||
®ulator_pdata, sizeof(regulator_pdata));
|
||||
}
|
||||
|
||||
if (pdata->flags & MC13XXX_USE_RTC)
|
||||
mc13xxx_add_subdevice(mc13xxx, "%s-rtc");
|
||||
|
||||
if (pdata->flags & MC13XXX_USE_TOUCHSCREEN)
|
||||
mc13xxx_add_subdevice(mc13xxx, "%s-ts");
|
||||
|
||||
if (pdata->flags & MC13XXX_USE_LED) {
|
||||
mc13xxx_add_subdevice_pdata(mc13xxx, "%s-led",
|
||||
pdata->leds, sizeof(*pdata->leds));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devexit mc13xxx_remove(struct spi_device *spi)
|
||||
{
|
||||
struct mc13xxx *mc13xxx = dev_get_drvdata(&spi->dev);
|
||||
|
||||
free_irq(mc13xxx->spidev->irq, mc13xxx);
|
||||
|
||||
mfd_remove_devices(&spi->dev);
|
||||
|
||||
kfree(mc13xxx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id mc13xxx_device_id[] = {
|
||||
{
|
||||
.name = "mc13783",
|
||||
.driver_data = MC13XXX_ID_MC13783,
|
||||
}, {
|
||||
.name = "mc13892",
|
||||
.driver_data = MC13XXX_ID_MC13892,
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
};
|
||||
|
||||
static struct spi_driver mc13xxx_driver = {
|
||||
.id_table = mc13xxx_device_id,
|
||||
.driver = {
|
||||
.name = "mc13xxx",
|
||||
.bus = &spi_bus_type,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = mc13xxx_probe,
|
||||
.remove = __devexit_p(mc13xxx_remove),
|
||||
};
|
||||
|
||||
static int __init mc13xxx_init(void)
|
||||
{
|
||||
return spi_register_driver(&mc13xxx_driver);
|
||||
}
|
||||
subsys_initcall(mc13xxx_init);
|
||||
|
||||
static void __exit mc13xxx_exit(void)
|
||||
{
|
||||
spi_unregister_driver(&mc13xxx_driver);
|
||||
}
|
||||
module_exit(mc13xxx_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Core driver for Freescale MC13XXX PMIC");
|
||||
MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -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);
|
||||
|
||||
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,10 +67,12 @@ static int mfd_add_device(struct device *parent, int id,
|
||||
res[r].end = cell->resources[r].end;
|
||||
}
|
||||
|
||||
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);
|
||||
if (ret)
|
||||
|
@ -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,14 +459,20 @@ 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;
|
||||
|
||||
failed:
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
10
drivers/mfd/twl-core.h
Normal file
10
drivers/mfd/twl-core.h
Normal file
@ -0,0 +1,10 @@
|
||||
#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, ®_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, ®_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)
|
||||
{
|
||||
|
||||
|
147
drivers/mfd/vx855.c
Normal file
147
drivers/mfd/vx855.c
Normal file
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* 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");
|
@ -14,7 +14,6 @@
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mfd/core.h>
|
||||
@ -90,14 +89,6 @@ int wm831x_isinkv_values[WM831X_ISINK_MAX_ISEL + 1] = {
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(wm831x_isinkv_values);
|
||||
|
||||
enum wm831x_parent {
|
||||
WM8310 = 0x8310,
|
||||
WM8311 = 0x8311,
|
||||
WM8312 = 0x8312,
|
||||
WM8320 = 0x8320,
|
||||
WM8321 = 0x8321,
|
||||
};
|
||||
|
||||
static int wm831x_reg_locked(struct wm831x *wm831x, unsigned short reg)
|
||||
{
|
||||
if (!wm831x->locked)
|
||||
@ -1446,7 +1437,7 @@ static struct mfd_cell backlight_devs[] = {
|
||||
/*
|
||||
* Instantiate the generic non-control parts of the device.
|
||||
*/
|
||||
static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
|
||||
int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
|
||||
{
|
||||
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
|
||||
int rev;
|
||||
@ -1540,6 +1531,12 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
|
||||
dev_info(wm831x->dev, "WM8321 revision %c\n", 'A' + rev);
|
||||
break;
|
||||
|
||||
case WM8325:
|
||||
parent = WM8325;
|
||||
wm831x->num_gpio = 12;
|
||||
dev_info(wm831x->dev, "WM8325 revision %c\n", 'A' + rev);
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(wm831x->dev, "Unknown WM831x device %04x\n", ret);
|
||||
ret = -EINVAL;
|
||||
@ -1620,6 +1617,12 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
|
||||
NULL, 0);
|
||||
break;
|
||||
|
||||
case WM8325:
|
||||
ret = mfd_add_devices(wm831x->dev, -1,
|
||||
wm8320_devs, ARRAY_SIZE(wm8320_devs),
|
||||
NULL, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* If this happens the bus probe function is buggy */
|
||||
BUG();
|
||||
@ -1660,7 +1663,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void wm831x_device_exit(struct wm831x *wm831x)
|
||||
void wm831x_device_exit(struct wm831x *wm831x)
|
||||
{
|
||||
wm831x_otp_exit(wm831x);
|
||||
mfd_remove_devices(wm831x->dev);
|
||||
@ -1670,7 +1673,7 @@ static void wm831x_device_exit(struct wm831x *wm831x)
|
||||
kfree(wm831x);
|
||||
}
|
||||
|
||||
static int wm831x_device_suspend(struct wm831x *wm831x)
|
||||
int wm831x_device_suspend(struct wm831x *wm831x)
|
||||
{
|
||||
int reg, mask;
|
||||
|
||||
@ -1706,125 +1709,6 @@ static int wm831x_device_suspend(struct wm831x *wm831x)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm831x_i2c_read_device(struct wm831x *wm831x, unsigned short reg,
|
||||
int bytes, void *dest)
|
||||
{
|
||||
struct i2c_client *i2c = wm831x->control_data;
|
||||
int ret;
|
||||
u16 r = cpu_to_be16(reg);
|
||||
|
||||
ret = i2c_master_send(i2c, (unsigned char *)&r, 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != 2)
|
||||
return -EIO;
|
||||
|
||||
ret = i2c_master_recv(i2c, dest, bytes);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != bytes)
|
||||
return -EIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Currently we allocate the write buffer on the stack; this is OK for
|
||||
* small writes - if we need to do large writes this will need to be
|
||||
* revised.
|
||||
*/
|
||||
static int wm831x_i2c_write_device(struct wm831x *wm831x, unsigned short reg,
|
||||
int bytes, void *src)
|
||||
{
|
||||
struct i2c_client *i2c = wm831x->control_data;
|
||||
unsigned char msg[bytes + 2];
|
||||
int ret;
|
||||
|
||||
reg = cpu_to_be16(reg);
|
||||
memcpy(&msg[0], ®, 2);
|
||||
memcpy(&msg[2], src, bytes);
|
||||
|
||||
ret = i2c_master_send(i2c, msg, bytes + 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret < bytes + 2)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm831x_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct wm831x *wm831x;
|
||||
|
||||
wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL);
|
||||
if (wm831x == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(i2c, wm831x);
|
||||
wm831x->dev = &i2c->dev;
|
||||
wm831x->control_data = i2c;
|
||||
wm831x->read_dev = wm831x_i2c_read_device;
|
||||
wm831x->write_dev = wm831x_i2c_write_device;
|
||||
|
||||
return wm831x_device_init(wm831x, id->driver_data, i2c->irq);
|
||||
}
|
||||
|
||||
static int wm831x_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
struct wm831x *wm831x = i2c_get_clientdata(i2c);
|
||||
|
||||
wm831x_device_exit(wm831x);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm831x_i2c_suspend(struct i2c_client *i2c, pm_message_t mesg)
|
||||
{
|
||||
struct wm831x *wm831x = i2c_get_clientdata(i2c);
|
||||
|
||||
return wm831x_device_suspend(wm831x);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id wm831x_i2c_id[] = {
|
||||
{ "wm8310", WM8310 },
|
||||
{ "wm8311", WM8311 },
|
||||
{ "wm8312", WM8312 },
|
||||
{ "wm8320", WM8320 },
|
||||
{ "wm8321", WM8321 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, wm831x_i2c_id);
|
||||
|
||||
|
||||
static struct i2c_driver wm831x_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "wm831x",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = wm831x_i2c_probe,
|
||||
.remove = wm831x_i2c_remove,
|
||||
.suspend = wm831x_i2c_suspend,
|
||||
.id_table = wm831x_i2c_id,
|
||||
};
|
||||
|
||||
static int __init wm831x_i2c_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_add_driver(&wm831x_i2c_driver);
|
||||
if (ret != 0)
|
||||
pr_err("Failed to register wm831x I2C driver: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
subsys_initcall(wm831x_i2c_init);
|
||||
|
||||
static void __exit wm831x_i2c_exit(void)
|
||||
{
|
||||
i2c_del_driver(&wm831x_i2c_driver);
|
||||
}
|
||||
module_exit(wm831x_i2c_exit);
|
||||
|
||||
MODULE_DESCRIPTION("I2C support for the WM831X AudioPlus PMIC");
|
||||
MODULE_DESCRIPTION("Core support for the WM831X AudioPlus PMIC");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Mark Brown");
|
||||
|
143
drivers/mfd/wm831x-i2c.c
Normal file
143
drivers/mfd/wm831x-i2c.c
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* wm831x-i2c.c -- I2C access for Wolfson WM831x PMICs
|
||||
*
|
||||
* Copyright 2009,2010 Wolfson Microelectronics PLC.
|
||||
*
|
||||
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/mfd/wm831x/core.h>
|
||||
#include <linux/mfd/wm831x/pdata.h>
|
||||
|
||||
static int wm831x_i2c_read_device(struct wm831x *wm831x, unsigned short reg,
|
||||
int bytes, void *dest)
|
||||
{
|
||||
struct i2c_client *i2c = wm831x->control_data;
|
||||
int ret;
|
||||
u16 r = cpu_to_be16(reg);
|
||||
|
||||
ret = i2c_master_send(i2c, (unsigned char *)&r, 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != 2)
|
||||
return -EIO;
|
||||
|
||||
ret = i2c_master_recv(i2c, dest, bytes);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != bytes)
|
||||
return -EIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Currently we allocate the write buffer on the stack; this is OK for
|
||||
* small writes - if we need to do large writes this will need to be
|
||||
* revised.
|
||||
*/
|
||||
static int wm831x_i2c_write_device(struct wm831x *wm831x, unsigned short reg,
|
||||
int bytes, void *src)
|
||||
{
|
||||
struct i2c_client *i2c = wm831x->control_data;
|
||||
unsigned char msg[bytes + 2];
|
||||
int ret;
|
||||
|
||||
reg = cpu_to_be16(reg);
|
||||
memcpy(&msg[0], ®, 2);
|
||||
memcpy(&msg[2], src, bytes);
|
||||
|
||||
ret = i2c_master_send(i2c, msg, bytes + 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret < bytes + 2)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm831x_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct wm831x *wm831x;
|
||||
|
||||
wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL);
|
||||
if (wm831x == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(i2c, wm831x);
|
||||
wm831x->dev = &i2c->dev;
|
||||
wm831x->control_data = i2c;
|
||||
wm831x->read_dev = wm831x_i2c_read_device;
|
||||
wm831x->write_dev = wm831x_i2c_write_device;
|
||||
|
||||
return wm831x_device_init(wm831x, id->driver_data, i2c->irq);
|
||||
}
|
||||
|
||||
static int wm831x_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
struct wm831x *wm831x = i2c_get_clientdata(i2c);
|
||||
|
||||
wm831x_device_exit(wm831x);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm831x_i2c_suspend(struct i2c_client *i2c, pm_message_t mesg)
|
||||
{
|
||||
struct wm831x *wm831x = i2c_get_clientdata(i2c);
|
||||
|
||||
return wm831x_device_suspend(wm831x);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id wm831x_i2c_id[] = {
|
||||
{ "wm8310", WM8310 },
|
||||
{ "wm8311", WM8311 },
|
||||
{ "wm8312", WM8312 },
|
||||
{ "wm8320", WM8320 },
|
||||
{ "wm8321", WM8321 },
|
||||
{ "wm8325", WM8325 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, wm831x_i2c_id);
|
||||
|
||||
|
||||
static struct i2c_driver wm831x_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "wm831x",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = wm831x_i2c_probe,
|
||||
.remove = wm831x_i2c_remove,
|
||||
.suspend = wm831x_i2c_suspend,
|
||||
.id_table = wm831x_i2c_id,
|
||||
};
|
||||
|
||||
static int __init wm831x_i2c_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_add_driver(&wm831x_i2c_driver);
|
||||
if (ret != 0)
|
||||
pr_err("Failed to register wm831x I2C driver: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
subsys_initcall(wm831x_i2c_init);
|
||||
|
||||
static void __exit wm831x_i2c_exit(void)
|
||||
{
|
||||
i2c_del_driver(&wm831x_i2c_driver);
|
||||
}
|
||||
module_exit(wm831x_i2c_exit);
|
232
drivers/mfd/wm831x-spi.c
Normal file
232
drivers/mfd/wm831x-spi.c
Normal file
@ -0,0 +1,232 @@
|
||||
/*
|
||||
* wm831x-spi.c -- SPI access for Wolfson WM831x PMICs
|
||||
*
|
||||
* Copyright 2009,2010 Wolfson Microelectronics PLC.
|
||||
*
|
||||
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <linux/mfd/wm831x/core.h>
|
||||
|
||||
static int wm831x_spi_read_device(struct wm831x *wm831x, unsigned short reg,
|
||||
int bytes, void *dest)
|
||||
{
|
||||
u16 tx_val;
|
||||
u16 *d = dest;
|
||||
int r, ret;
|
||||
|
||||
/* Go register at a time */
|
||||
for (r = reg; r < reg + (bytes / 2); r++) {
|
||||
tx_val = r | 0x8000;
|
||||
|
||||
ret = spi_write_then_read(wm831x->control_data,
|
||||
(u8 *)&tx_val, 2, (u8 *)d, 2);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
*d = be16_to_cpu(*d);
|
||||
|
||||
d++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm831x_spi_write_device(struct wm831x *wm831x, unsigned short reg,
|
||||
int bytes, void *src)
|
||||
{
|
||||
struct spi_device *spi = wm831x->control_data;
|
||||
u16 *s = src;
|
||||
u16 data[2];
|
||||
int ret, r;
|
||||
|
||||
/* Go register at a time */
|
||||
for (r = reg; r < reg + (bytes / 2); r++) {
|
||||
data[0] = r;
|
||||
data[1] = *s++;
|
||||
|
||||
ret = spi_write(spi, (char *)&data, sizeof(data));
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit wm831x_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct wm831x *wm831x;
|
||||
enum wm831x_parent type;
|
||||
|
||||
/* Currently SPI support for ID tables is unmerged, we're faking it */
|
||||
if (strcmp(spi->modalias, "wm8310") == 0)
|
||||
type = WM8310;
|
||||
else if (strcmp(spi->modalias, "wm8311") == 0)
|
||||
type = WM8311;
|
||||
else if (strcmp(spi->modalias, "wm8312") == 0)
|
||||
type = WM8312;
|
||||
else if (strcmp(spi->modalias, "wm8320") == 0)
|
||||
type = WM8320;
|
||||
else if (strcmp(spi->modalias, "wm8321") == 0)
|
||||
type = WM8321;
|
||||
else if (strcmp(spi->modalias, "wm8325") == 0)
|
||||
type = WM8325;
|
||||
else {
|
||||
dev_err(&spi->dev, "Unknown device type\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL);
|
||||
if (wm831x == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
spi->bits_per_word = 16;
|
||||
spi->mode = SPI_MODE_0;
|
||||
|
||||
dev_set_drvdata(&spi->dev, wm831x);
|
||||
wm831x->dev = &spi->dev;
|
||||
wm831x->control_data = spi;
|
||||
wm831x->read_dev = wm831x_spi_read_device;
|
||||
wm831x->write_dev = wm831x_spi_write_device;
|
||||
|
||||
return wm831x_device_init(wm831x, type, spi->irq);
|
||||
}
|
||||
|
||||
static int __devexit wm831x_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
struct wm831x *wm831x = dev_get_drvdata(&spi->dev);
|
||||
|
||||
wm831x_device_exit(wm831x);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm831x_spi_suspend(struct spi_device *spi, pm_message_t m)
|
||||
{
|
||||
struct wm831x *wm831x = dev_get_drvdata(&spi->dev);
|
||||
|
||||
return wm831x_device_suspend(wm831x);
|
||||
}
|
||||
|
||||
static struct spi_driver wm8310_spi_driver = {
|
||||
.driver = {
|
||||
.name = "wm8310",
|
||||
.bus = &spi_bus_type,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = wm831x_spi_probe,
|
||||
.remove = __devexit_p(wm831x_spi_remove),
|
||||
.suspend = wm831x_spi_suspend,
|
||||
};
|
||||
|
||||
static struct spi_driver wm8311_spi_driver = {
|
||||
.driver = {
|
||||
.name = "wm8311",
|
||||
.bus = &spi_bus_type,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = wm831x_spi_probe,
|
||||
.remove = __devexit_p(wm831x_spi_remove),
|
||||
.suspend = wm831x_spi_suspend,
|
||||
};
|
||||
|
||||
static struct spi_driver wm8312_spi_driver = {
|
||||
.driver = {
|
||||
.name = "wm8312",
|
||||
.bus = &spi_bus_type,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = wm831x_spi_probe,
|
||||
.remove = __devexit_p(wm831x_spi_remove),
|
||||
.suspend = wm831x_spi_suspend,
|
||||
};
|
||||
|
||||
static struct spi_driver wm8320_spi_driver = {
|
||||
.driver = {
|
||||
.name = "wm8320",
|
||||
.bus = &spi_bus_type,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = wm831x_spi_probe,
|
||||
.remove = __devexit_p(wm831x_spi_remove),
|
||||
.suspend = wm831x_spi_suspend,
|
||||
};
|
||||
|
||||
static struct spi_driver wm8321_spi_driver = {
|
||||
.driver = {
|
||||
.name = "wm8321",
|
||||
.bus = &spi_bus_type,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = wm831x_spi_probe,
|
||||
.remove = __devexit_p(wm831x_spi_remove),
|
||||
.suspend = wm831x_spi_suspend,
|
||||
};
|
||||
|
||||
static struct spi_driver wm8325_spi_driver = {
|
||||
.driver = {
|
||||
.name = "wm8325",
|
||||
.bus = &spi_bus_type,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = wm831x_spi_probe,
|
||||
.remove = __devexit_p(wm831x_spi_remove),
|
||||
.suspend = wm831x_spi_suspend,
|
||||
};
|
||||
|
||||
static int __init wm831x_spi_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = spi_register_driver(&wm8310_spi_driver);
|
||||
if (ret != 0)
|
||||
pr_err("Failed to register WM8310 SPI driver: %d\n", ret);
|
||||
|
||||
ret = spi_register_driver(&wm8311_spi_driver);
|
||||
if (ret != 0)
|
||||
pr_err("Failed to register WM8311 SPI driver: %d\n", ret);
|
||||
|
||||
ret = spi_register_driver(&wm8312_spi_driver);
|
||||
if (ret != 0)
|
||||
pr_err("Failed to register WM8312 SPI driver: %d\n", ret);
|
||||
|
||||
ret = spi_register_driver(&wm8320_spi_driver);
|
||||
if (ret != 0)
|
||||
pr_err("Failed to register WM8320 SPI driver: %d\n", ret);
|
||||
|
||||
ret = spi_register_driver(&wm8321_spi_driver);
|
||||
if (ret != 0)
|
||||
pr_err("Failed to register WM8321 SPI driver: %d\n", ret);
|
||||
|
||||
ret = spi_register_driver(&wm8325_spi_driver);
|
||||
if (ret != 0)
|
||||
pr_err("Failed to register WM8325 SPI driver: %d\n", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
subsys_initcall(wm831x_spi_init);
|
||||
|
||||
static void __exit wm831x_spi_exit(void)
|
||||
{
|
||||
spi_unregister_driver(&wm8325_spi_driver);
|
||||
spi_unregister_driver(&wm8321_spi_driver);
|
||||
spi_unregister_driver(&wm8320_spi_driver);
|
||||
spi_unregister_driver(&wm8312_spi_driver);
|
||||
spi_unregister_driver(&wm8311_spi_driver);
|
||||
spi_unregister_driver(&wm8310_spi_driver);
|
||||
}
|
||||
module_exit(wm831x_spi_exit);
|
||||
|
||||
MODULE_DESCRIPTION("SPI support for WM831x/2x AudioPlus PMICs");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Mark Brown");
|
@ -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
|
||||
|
168
drivers/misc/ab8500-pwm.c
Normal file
168
drivers/misc/ab8500-pwm.c
Normal file
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* Author: Arun R Murthy <arun.murthy@stericsson.com>
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
*/
|
||||
#include <linux/err.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pwm.h>
|
||||
#include <linux/mfd/ab8500.h>
|
||||
#include <linux/mfd/abx500.h>
|
||||
|
||||
/*
|
||||
* PWM Out generators
|
||||
* Bank: 0x10
|
||||
*/
|
||||
#define AB8500_PWM_OUT_CTRL1_REG 0x60
|
||||
#define AB8500_PWM_OUT_CTRL2_REG 0x61
|
||||
#define AB8500_PWM_OUT_CTRL7_REG 0x66
|
||||
|
||||
/* backlight driver constants */
|
||||
#define ENABLE_PWM 1
|
||||
#define DISABLE_PWM 0
|
||||
|
||||
struct pwm_device {
|
||||
struct device *dev;
|
||||
struct list_head node;
|
||||
const char *label;
|
||||
unsigned int pwm_id;
|
||||
};
|
||||
|
||||
static LIST_HEAD(pwm_list);
|
||||
|
||||
int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int higher_val, lower_val;
|
||||
u8 reg;
|
||||
|
||||
/*
|
||||
* get the first 8 bits that are be written to
|
||||
* AB8500_PWM_OUT_CTRL1_REG[0:7]
|
||||
*/
|
||||
lower_val = duty_ns & 0x00FF;
|
||||
/*
|
||||
* get bits [9:10] that are to be written to
|
||||
* AB8500_PWM_OUT_CTRL2_REG[0:1]
|
||||
*/
|
||||
higher_val = ((duty_ns & 0x0300) >> 8);
|
||||
|
||||
reg = AB8500_PWM_OUT_CTRL1_REG + ((pwm->pwm_id - 1) * 2);
|
||||
|
||||
ret = abx500_set_register_interruptible(pwm->dev, AB8500_MISC,
|
||||
reg, (u8)lower_val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = abx500_set_register_interruptible(pwm->dev, AB8500_MISC,
|
||||
(reg + 1), (u8)higher_val);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(pwm_config);
|
||||
|
||||
int pwm_enable(struct pwm_device *pwm)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = abx500_mask_and_set_register_interruptible(pwm->dev,
|
||||
AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG,
|
||||
1 << (pwm->pwm_id-1), ENABLE_PWM);
|
||||
if (ret < 0)
|
||||
dev_err(pwm->dev, "%s: Failed to disable PWM, Error %d\n",
|
||||
pwm->label, ret);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(pwm_enable);
|
||||
|
||||
void pwm_disable(struct pwm_device *pwm)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = abx500_mask_and_set_register_interruptible(pwm->dev,
|
||||
AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG,
|
||||
1 << (pwm->pwm_id-1), DISABLE_PWM);
|
||||
if (ret < 0)
|
||||
dev_err(pwm->dev, "%s: Failed to disable PWM, Error %d\n",
|
||||
pwm->label, ret);
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL(pwm_disable);
|
||||
|
||||
struct pwm_device *pwm_request(int pwm_id, const char *label)
|
||||
{
|
||||
struct pwm_device *pwm;
|
||||
|
||||
list_for_each_entry(pwm, &pwm_list, node) {
|
||||
if (pwm->pwm_id == pwm_id) {
|
||||
pwm->label = label;
|
||||
pwm->pwm_id = pwm_id;
|
||||
return pwm;
|
||||
}
|
||||
}
|
||||
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
EXPORT_SYMBOL(pwm_request);
|
||||
|
||||
void pwm_free(struct pwm_device *pwm)
|
||||
{
|
||||
pwm_disable(pwm);
|
||||
}
|
||||
EXPORT_SYMBOL(pwm_free);
|
||||
|
||||
static int __devinit ab8500_pwm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct pwm_device *pwm;
|
||||
/*
|
||||
* Nothing to be done in probe, this is required to get the
|
||||
* device which is required for ab8500 read and write
|
||||
*/
|
||||
pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
|
||||
if (pwm == NULL) {
|
||||
dev_err(&pdev->dev, "failed to allocate memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
pwm->dev = &pdev->dev;
|
||||
pwm->pwm_id = pdev->id;
|
||||
list_add_tail(&pwm->node, &pwm_list);
|
||||
platform_set_drvdata(pdev, pwm);
|
||||
dev_dbg(pwm->dev, "pwm probe successful\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devexit ab8500_pwm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct pwm_device *pwm = platform_get_drvdata(pdev);
|
||||
list_del(&pwm->node);
|
||||
dev_dbg(&pdev->dev, "pwm driver removed\n");
|
||||
kfree(pwm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ab8500_pwm_driver = {
|
||||
.driver = {
|
||||
.name = "ab8500-pwm",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ab8500_pwm_probe,
|
||||
.remove = __devexit_p(ab8500_pwm_remove),
|
||||
};
|
||||
|
||||
static int __init ab8500_pwm_init(void)
|
||||
{
|
||||
return platform_driver_register(&ab8500_pwm_driver);
|
||||
}
|
||||
|
||||
static void __exit ab8500_pwm_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ab8500_pwm_driver);
|
||||
}
|
||||
|
||||
subsys_initcall(ab8500_pwm_init);
|
||||
module_exit(ab8500_pwm_exit);
|
||||
MODULE_AUTHOR("Arun MURTHY <arun.murthy@stericsson.com>");
|
||||
MODULE_DESCRIPTION("AB8500 Pulse Width Modulation Driver");
|
||||
MODULE_ALIAS("AB8500 PWM driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -483,8 +483,6 @@ static int omap_hsmmc_gpio_init(struct omap_mmc_platform_data *pdata)
|
||||
int ret;
|
||||
|
||||
if (gpio_is_valid(pdata->slots[0].switch_pin)) {
|
||||
pdata->suspend = omap_hsmmc_suspend_cdirq;
|
||||
pdata->resume = omap_hsmmc_resume_cdirq;
|
||||
if (pdata->slots[0].cover)
|
||||
pdata->slots[0].get_cover_state =
|
||||
omap_hsmmc_get_cover_state;
|
||||
@ -2218,6 +2216,8 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
|
||||
"Unable to grab MMC CD IRQ\n");
|
||||
goto err_irq_cd;
|
||||
}
|
||||
pdata->suspend = omap_hsmmc_suspend_cdirq;
|
||||
pdata->resume = omap_hsmmc_resume_cdirq;
|
||||
}
|
||||
|
||||
omap_hsmmc_disable_irq(host);
|
||||
|
@ -710,9 +710,21 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
host->bus_width = ios->bus_width;
|
||||
}
|
||||
|
||||
static int sh_mmcif_get_cd(struct mmc_host *mmc)
|
||||
{
|
||||
struct sh_mmcif_host *host = mmc_priv(mmc);
|
||||
struct sh_mmcif_plat_data *p = host->pd->dev.platform_data;
|
||||
|
||||
if (!p->get_cd)
|
||||
return -ENOSYS;
|
||||
else
|
||||
return p->get_cd(host->pd);
|
||||
}
|
||||
|
||||
static struct mmc_host_ops sh_mmcif_ops = {
|
||||
.request = sh_mmcif_request,
|
||||
.set_ios = sh_mmcif_set_ios,
|
||||
.get_cd = sh_mmcif_get_cd,
|
||||
};
|
||||
|
||||
static void sh_mmcif_detect(struct mmc_host *mmc)
|
||||
|
@ -658,15 +658,22 @@ static void tmio_mmc_release_dma(struct tmio_mmc_host *host)
|
||||
static int tmio_mmc_start_data(struct tmio_mmc_host *host,
|
||||
struct mmc_data *data)
|
||||
{
|
||||
struct mfd_cell *cell = host->pdev->dev.platform_data;
|
||||
struct tmio_mmc_data *pdata = cell->driver_data;
|
||||
|
||||
pr_debug("setup data transfer: blocksize %08x nr_blocks %d\n",
|
||||
data->blksz, data->blocks);
|
||||
|
||||
/* Hardware cannot perform 1 and 2 byte requests in 4 bit mode */
|
||||
if (data->blksz < 4 && host->mmc->ios.bus_width == MMC_BUS_WIDTH_4) {
|
||||
/* Some hardware cannot perform 2 byte requests in 4 bit mode */
|
||||
if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4) {
|
||||
int blksz_2bytes = pdata->flags & TMIO_MMC_BLKSZ_2BYTES;
|
||||
|
||||
if (data->blksz < 2 || (data->blksz < 4 && !blksz_2bytes)) {
|
||||
pr_err("%s: %d byte block unsupported in 4 bit mode\n",
|
||||
mmc_hostname(host->mmc), data->blksz);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
tmio_mmc_init_sg(host, data);
|
||||
host->data = data;
|
||||
@ -756,10 +763,23 @@ static int tmio_mmc_get_ro(struct mmc_host *mmc)
|
||||
(sd_ctrl_read32(host, CTL_STATUS) & TMIO_STAT_WRPROTECT)) ? 0 : 1;
|
||||
}
|
||||
|
||||
static int tmio_mmc_get_cd(struct mmc_host *mmc)
|
||||
{
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
struct mfd_cell *cell = host->pdev->dev.platform_data;
|
||||
struct tmio_mmc_data *pdata = cell->driver_data;
|
||||
|
||||
if (!pdata->get_cd)
|
||||
return -ENOSYS;
|
||||
else
|
||||
return pdata->get_cd(host->pdev);
|
||||
}
|
||||
|
||||
static const struct mmc_host_ops tmio_mmc_ops = {
|
||||
.request = tmio_mmc_request,
|
||||
.set_ios = tmio_mmc_set_ios,
|
||||
.get_ro = tmio_mmc_get_ro,
|
||||
.get_cd = tmio_mmc_get_cd,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
@ -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.
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/ab8500.h>
|
||||
#include <linux/mfd/abx500.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/regulator/ab8500.h>
|
||||
@ -33,9 +34,11 @@
|
||||
* @max_uV: maximum voltage (for variable voltage supplies)
|
||||
* @min_uV: minimum voltage (for variable voltage supplies)
|
||||
* @fixed_uV: typical voltage (for fixed voltage supplies)
|
||||
* @update_bank: bank to control on/off
|
||||
* @update_reg: register to control on/off
|
||||
* @mask: mask to enable/disable regulator
|
||||
* @enable: bits to enable the regulator in normal(high power) mode
|
||||
* @voltage_bank: bank to control regulator voltage
|
||||
* @voltage_reg: register to control regulator voltage
|
||||
* @voltage_mask: mask to control regulator voltage
|
||||
* @supported_voltages: supported voltage table
|
||||
@ -49,11 +52,13 @@ struct ab8500_regulator_info {
|
||||
int max_uV;
|
||||
int min_uV;
|
||||
int fixed_uV;
|
||||
int update_reg;
|
||||
int mask;
|
||||
int enable;
|
||||
int voltage_reg;
|
||||
int voltage_mask;
|
||||
u8 update_bank;
|
||||
u8 update_reg;
|
||||
u8 mask;
|
||||
u8 enable;
|
||||
u8 voltage_bank;
|
||||
u8 voltage_reg;
|
||||
u8 voltage_mask;
|
||||
int const *supported_voltages;
|
||||
int voltages_len;
|
||||
};
|
||||
@ -97,8 +102,8 @@ static int ab8500_regulator_enable(struct regulator_dev *rdev)
|
||||
if (regulator_id >= AB8500_NUM_REGULATORS)
|
||||
return -EINVAL;
|
||||
|
||||
ret = ab8500_set_bits(info->ab8500, info->update_reg,
|
||||
info->mask, info->enable);
|
||||
ret = abx500_mask_and_set_register_interruptible(info->dev,
|
||||
info->update_bank, info->update_reg, info->mask, info->enable);
|
||||
if (ret < 0)
|
||||
dev_err(rdev_get_dev(rdev),
|
||||
"couldn't set enable bits for regulator\n");
|
||||
@ -114,8 +119,8 @@ static int ab8500_regulator_disable(struct regulator_dev *rdev)
|
||||
if (regulator_id >= AB8500_NUM_REGULATORS)
|
||||
return -EINVAL;
|
||||
|
||||
ret = ab8500_set_bits(info->ab8500, info->update_reg,
|
||||
info->mask, 0x0);
|
||||
ret = abx500_mask_and_set_register_interruptible(info->dev,
|
||||
info->update_bank, info->update_reg, info->mask, 0x0);
|
||||
if (ret < 0)
|
||||
dev_err(rdev_get_dev(rdev),
|
||||
"couldn't set disable bits for regulator\n");
|
||||
@ -126,19 +131,21 @@ static int ab8500_regulator_is_enabled(struct regulator_dev *rdev)
|
||||
{
|
||||
int regulator_id, ret;
|
||||
struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
|
||||
u8 value;
|
||||
|
||||
regulator_id = rdev_get_id(rdev);
|
||||
if (regulator_id >= AB8500_NUM_REGULATORS)
|
||||
return -EINVAL;
|
||||
|
||||
ret = ab8500_read(info->ab8500, info->update_reg);
|
||||
ret = abx500_get_register_interruptible(info->dev,
|
||||
info->update_bank, info->update_reg, &value);
|
||||
if (ret < 0) {
|
||||
dev_err(rdev_get_dev(rdev),
|
||||
"couldn't read 0x%x register\n", info->update_reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ret & info->mask)
|
||||
if (value & info->mask)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
@ -165,14 +172,16 @@ static int ab8500_list_voltage(struct regulator_dev *rdev, unsigned selector)
|
||||
|
||||
static int ab8500_regulator_get_voltage(struct regulator_dev *rdev)
|
||||
{
|
||||
int regulator_id, ret, val;
|
||||
int regulator_id, ret;
|
||||
struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
|
||||
u8 value;
|
||||
|
||||
regulator_id = rdev_get_id(rdev);
|
||||
if (regulator_id >= AB8500_NUM_REGULATORS)
|
||||
return -EINVAL;
|
||||
|
||||
ret = ab8500_read(info->ab8500, info->voltage_reg);
|
||||
ret = abx500_get_register_interruptible(info->dev, info->voltage_bank,
|
||||
info->voltage_reg, &value);
|
||||
if (ret < 0) {
|
||||
dev_err(rdev_get_dev(rdev),
|
||||
"couldn't read voltage reg for regulator\n");
|
||||
@ -180,11 +189,11 @@ static int ab8500_regulator_get_voltage(struct regulator_dev *rdev)
|
||||
}
|
||||
|
||||
/* vintcore has a different layout */
|
||||
val = ret & info->voltage_mask;
|
||||
value &= info->voltage_mask;
|
||||
if (regulator_id == AB8500_LDO_INTCORE)
|
||||
ret = info->supported_voltages[val >> 0x3];
|
||||
ret = info->supported_voltages[value >> 0x3];
|
||||
else
|
||||
ret = info->supported_voltages[val];
|
||||
ret = info->supported_voltages[value];
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -224,8 +233,9 @@ static int ab8500_regulator_set_voltage(struct regulator_dev *rdev,
|
||||
}
|
||||
|
||||
/* set the registers for the request */
|
||||
ret = ab8500_set_bits(info->ab8500, info->voltage_reg,
|
||||
info->voltage_mask, ret);
|
||||
ret = abx500_mask_and_set_register_interruptible(info->dev,
|
||||
info->voltage_bank, info->voltage_reg,
|
||||
info->voltage_mask, (u8)ret);
|
||||
if (ret < 0)
|
||||
dev_err(rdev_get_dev(rdev),
|
||||
"couldn't set voltage reg for regulator\n");
|
||||
@ -262,9 +272,9 @@ static struct regulator_ops ab8500_ldo_fixed_ops = {
|
||||
.list_voltage = ab8500_list_voltage,
|
||||
};
|
||||
|
||||
#define AB8500_LDO(_id, min, max, reg, reg_mask, reg_enable, \
|
||||
volt_reg, volt_mask, voltages, \
|
||||
len_volts) \
|
||||
#define AB8500_LDO(_id, min, max, bank, reg, reg_mask, \
|
||||
reg_enable, volt_bank, volt_reg, volt_mask, \
|
||||
voltages, len_volts) \
|
||||
{ \
|
||||
.desc = { \
|
||||
.name = "LDO-" #_id, \
|
||||
@ -275,9 +285,11 @@ static struct regulator_ops ab8500_ldo_fixed_ops = {
|
||||
}, \
|
||||
.min_uV = (min) * 1000, \
|
||||
.max_uV = (max) * 1000, \
|
||||
.update_bank = bank, \
|
||||
.update_reg = reg, \
|
||||
.mask = reg_mask, \
|
||||
.enable = reg_enable, \
|
||||
.voltage_bank = volt_bank, \
|
||||
.voltage_reg = volt_reg, \
|
||||
.voltage_mask = volt_mask, \
|
||||
.supported_voltages = voltages, \
|
||||
@ -285,8 +297,8 @@ static struct regulator_ops ab8500_ldo_fixed_ops = {
|
||||
.fixed_uV = 0, \
|
||||
}
|
||||
|
||||
#define AB8500_FIXED_LDO(_id, fixed, reg, reg_mask, \
|
||||
reg_enable) \
|
||||
#define AB8500_FIXED_LDO(_id, fixed, bank, reg, \
|
||||
reg_mask, reg_enable) \
|
||||
{ \
|
||||
.desc = { \
|
||||
.name = "LDO-" #_id, \
|
||||
@ -296,6 +308,7 @@ static struct regulator_ops ab8500_ldo_fixed_ops = {
|
||||
.owner = THIS_MODULE, \
|
||||
}, \
|
||||
.fixed_uV = fixed * 1000, \
|
||||
.update_bank = bank, \
|
||||
.update_reg = reg, \
|
||||
.mask = reg_mask, \
|
||||
.enable = reg_enable, \
|
||||
@ -304,28 +317,29 @@ static struct regulator_ops ab8500_ldo_fixed_ops = {
|
||||
static struct ab8500_regulator_info ab8500_regulator_info[] = {
|
||||
/*
|
||||
* Variable Voltage LDOs
|
||||
* name, min uV, max uV, ctrl reg, reg mask, enable mask,
|
||||
* volt ctrl reg, volt ctrl mask, volt table, num supported volts
|
||||
* name, min uV, max uV, ctrl bank, ctrl reg, reg mask, enable mask,
|
||||
* volt ctrl bank, volt ctrl reg, volt ctrl mask, volt table,
|
||||
* num supported volts
|
||||
*/
|
||||
AB8500_LDO(AUX1, 1100, 3300, 0x0409, 0x3, 0x1, 0x041f, 0xf,
|
||||
AB8500_LDO(AUX1, 1100, 3300, 0x04, 0x09, 0x3, 0x1, 0x04, 0x1f, 0xf,
|
||||
ldo_vauxn_voltages, ARRAY_SIZE(ldo_vauxn_voltages)),
|
||||
AB8500_LDO(AUX2, 1100, 3300, 0x0409, 0xc, 0x4, 0x0420, 0xf,
|
||||
AB8500_LDO(AUX2, 1100, 3300, 0x04, 0x09, 0xc, 0x4, 0x04, 0x20, 0xf,
|
||||
ldo_vauxn_voltages, ARRAY_SIZE(ldo_vauxn_voltages)),
|
||||
AB8500_LDO(AUX3, 1100, 3300, 0x040a, 0x3, 0x1, 0x0421, 0xf,
|
||||
AB8500_LDO(AUX3, 1100, 3300, 0x04, 0x0a, 0x3, 0x1, 0x04, 0x21, 0xf,
|
||||
ldo_vauxn_voltages, ARRAY_SIZE(ldo_vauxn_voltages)),
|
||||
AB8500_LDO(INTCORE, 1100, 3300, 0x0380, 0x4, 0x4, 0x0380, 0x38,
|
||||
AB8500_LDO(INTCORE, 1100, 3300, 0x03, 0x80, 0x4, 0x4, 0x03, 0x80, 0x38,
|
||||
ldo_vintcore_voltages, ARRAY_SIZE(ldo_vintcore_voltages)),
|
||||
|
||||
/*
|
||||
* Fixed Voltage LDOs
|
||||
* name, o/p uV, ctrl reg, enable, disable
|
||||
* name, o/p uV, ctrl bank, ctrl reg, enable, disable
|
||||
*/
|
||||
AB8500_FIXED_LDO(TVOUT, 2000, 0x0380, 0x2, 0x2),
|
||||
AB8500_FIXED_LDO(AUDIO, 2000, 0x0383, 0x2, 0x2),
|
||||
AB8500_FIXED_LDO(ANAMIC1, 2050, 0x0383, 0x4, 0x4),
|
||||
AB8500_FIXED_LDO(ANAMIC2, 2050, 0x0383, 0x8, 0x8),
|
||||
AB8500_FIXED_LDO(DMIC, 1800, 0x0383, 0x10, 0x10),
|
||||
AB8500_FIXED_LDO(ANA, 1200, 0x0383, 0xc, 0x4),
|
||||
AB8500_FIXED_LDO(TVOUT, 2000, 0x03, 0x80, 0x2, 0x2),
|
||||
AB8500_FIXED_LDO(AUDIO, 2000, 0x03, 0x83, 0x2, 0x2),
|
||||
AB8500_FIXED_LDO(ANAMIC1, 2050, 0x03, 0x83, 0x4, 0x4),
|
||||
AB8500_FIXED_LDO(ANAMIC2, 2050, 0x03, 0x83, 0x8, 0x8),
|
||||
AB8500_FIXED_LDO(DMIC, 1800, 0x03, 0x83, 0x10, 0x10),
|
||||
AB8500_FIXED_LDO(ANA, 1200, 0x03, 0x83, 0xc, 0x4),
|
||||
};
|
||||
|
||||
static inline struct ab8500_regulator_info *find_regulator_info(int id)
|
||||
|
@ -39,6 +39,11 @@ struct max8998_data {
|
||||
struct max8998_dev *iodev;
|
||||
int num_regulators;
|
||||
struct regulator_dev **rdev;
|
||||
u8 buck1_vol[4]; /* voltages for selection */
|
||||
u8 buck2_vol[2];
|
||||
unsigned int buck1_idx; /* index to last changed voltage */
|
||||
/* value in a set */
|
||||
unsigned int buck2_idx;
|
||||
};
|
||||
|
||||
struct voltage_map_desc {
|
||||
@ -173,6 +178,7 @@ static int max8998_get_enable_register(struct regulator_dev *rdev,
|
||||
static int max8998_ldo_is_enabled(struct regulator_dev *rdev)
|
||||
{
|
||||
struct max8998_data *max8998 = rdev_get_drvdata(rdev);
|
||||
struct i2c_client *i2c = max8998->iodev->i2c;
|
||||
int ret, reg, shift = 8;
|
||||
u8 val;
|
||||
|
||||
@ -180,7 +186,7 @@ static int max8998_ldo_is_enabled(struct regulator_dev *rdev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = max8998_read_reg(max8998->iodev, reg, &val);
|
||||
ret = max8998_read_reg(i2c, reg, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -190,31 +196,34 @@ static int max8998_ldo_is_enabled(struct regulator_dev *rdev)
|
||||
static int max8998_ldo_enable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct max8998_data *max8998 = rdev_get_drvdata(rdev);
|
||||
struct i2c_client *i2c = max8998->iodev->i2c;
|
||||
int reg, shift = 8, ret;
|
||||
|
||||
ret = max8998_get_enable_register(rdev, ®, &shift);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return max8998_update_reg(max8998->iodev, reg, 1<<shift, 1<<shift);
|
||||
return max8998_update_reg(i2c, reg, 1<<shift, 1<<shift);
|
||||
}
|
||||
|
||||
static int max8998_ldo_disable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct max8998_data *max8998 = rdev_get_drvdata(rdev);
|
||||
struct i2c_client *i2c = max8998->iodev->i2c;
|
||||
int reg, shift = 8, ret;
|
||||
|
||||
ret = max8998_get_enable_register(rdev, ®, &shift);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return max8998_update_reg(max8998->iodev, reg, 0, 1<<shift);
|
||||
return max8998_update_reg(i2c, reg, 0, 1<<shift);
|
||||
}
|
||||
|
||||
static int max8998_get_voltage_register(struct regulator_dev *rdev,
|
||||
int *_reg, int *_shift, int *_mask)
|
||||
{
|
||||
int ldo = max8998_get_ldo(rdev);
|
||||
struct max8998_data *max8998 = rdev_get_drvdata(rdev);
|
||||
int reg, shift = 0, mask = 0xff;
|
||||
|
||||
switch (ldo) {
|
||||
@ -251,10 +260,10 @@ static int max8998_get_voltage_register(struct regulator_dev *rdev,
|
||||
reg = MAX8998_REG_LDO12 + (ldo - MAX8998_LDO12);
|
||||
break;
|
||||
case MAX8998_BUCK1:
|
||||
reg = MAX8998_REG_BUCK1_DVSARM1;
|
||||
reg = MAX8998_REG_BUCK1_VOLTAGE1 + max8998->buck1_idx;
|
||||
break;
|
||||
case MAX8998_BUCK2:
|
||||
reg = MAX8998_REG_BUCK2_DVSINT1;
|
||||
reg = MAX8998_REG_BUCK2_VOLTAGE1 + max8998->buck2_idx;
|
||||
break;
|
||||
case MAX8998_BUCK3:
|
||||
reg = MAX8998_REG_BUCK3;
|
||||
@ -276,6 +285,7 @@ static int max8998_get_voltage_register(struct regulator_dev *rdev,
|
||||
static int max8998_get_voltage(struct regulator_dev *rdev)
|
||||
{
|
||||
struct max8998_data *max8998 = rdev_get_drvdata(rdev);
|
||||
struct i2c_client *i2c = max8998->iodev->i2c;
|
||||
int reg, shift = 0, mask, ret;
|
||||
u8 val;
|
||||
|
||||
@ -283,7 +293,7 @@ static int max8998_get_voltage(struct regulator_dev *rdev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = max8998_read_reg(max8998->iodev, reg, &val);
|
||||
ret = max8998_read_reg(i2c, reg, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -293,18 +303,16 @@ static int max8998_get_voltage(struct regulator_dev *rdev)
|
||||
return max8998_list_voltage(rdev, val);
|
||||
}
|
||||
|
||||
static int max8998_set_voltage(struct regulator_dev *rdev,
|
||||
static int max8998_set_voltage_ldo(struct regulator_dev *rdev,
|
||||
int min_uV, int max_uV)
|
||||
{
|
||||
struct max8998_data *max8998 = rdev_get_drvdata(rdev);
|
||||
struct i2c_client *i2c = max8998->iodev->i2c;
|
||||
int min_vol = min_uV / 1000, max_vol = max_uV / 1000;
|
||||
int previous_vol = 0;
|
||||
const struct voltage_map_desc *desc;
|
||||
int ldo = max8998_get_ldo(rdev);
|
||||
int reg, shift = 0, mask, ret;
|
||||
int i = 0;
|
||||
u8 val;
|
||||
bool en_ramp = false;
|
||||
|
||||
if (ldo >= ARRAY_SIZE(ldo_voltage_map))
|
||||
return -EINVAL;
|
||||
@ -327,23 +335,154 @@ static int max8998_set_voltage(struct regulator_dev *rdev,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* wait for RAMP_UP_DELAY if rdev is BUCK1/2 and
|
||||
* ENRAMP is ON */
|
||||
if (ldo == MAX8998_BUCK1 || ldo == MAX8998_BUCK2) {
|
||||
max8998_read_reg(max8998->iodev, MAX8998_REG_ONOFF4, &val);
|
||||
if (val & (1 << 4)) {
|
||||
en_ramp = true;
|
||||
ret = max8998_update_reg(i2c, reg, i<<shift, mask<<shift);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void buck1_gpio_set(int gpio1, int gpio2, int v)
|
||||
{
|
||||
gpio_set_value(gpio1, v & 0x1);
|
||||
gpio_set_value(gpio2, (v >> 1) & 0x1);
|
||||
}
|
||||
|
||||
static inline void buck2_gpio_set(int gpio, int v)
|
||||
{
|
||||
gpio_set_value(gpio, v & 0x1);
|
||||
}
|
||||
|
||||
static int max8998_set_voltage_buck(struct regulator_dev *rdev,
|
||||
int min_uV, int max_uV)
|
||||
{
|
||||
struct max8998_data *max8998 = rdev_get_drvdata(rdev);
|
||||
struct max8998_platform_data *pdata =
|
||||
dev_get_platdata(max8998->iodev->dev);
|
||||
struct i2c_client *i2c = max8998->iodev->i2c;
|
||||
int min_vol = min_uV / 1000, max_vol = max_uV / 1000;
|
||||
const struct voltage_map_desc *desc;
|
||||
int buck = max8998_get_ldo(rdev);
|
||||
int reg, shift = 0, mask, ret;
|
||||
int difference = 0, i = 0, j = 0, previous_vol = 0;
|
||||
u8 val = 0;
|
||||
static u8 buck1_last_val;
|
||||
|
||||
if (buck >= ARRAY_SIZE(ldo_voltage_map))
|
||||
return -EINVAL;
|
||||
|
||||
desc = ldo_voltage_map[buck];
|
||||
|
||||
if (desc == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (max_vol < desc->min || min_vol > desc->max)
|
||||
return -EINVAL;
|
||||
|
||||
while (desc->min + desc->step*i < min_vol &&
|
||||
desc->min + desc->step*i < desc->max)
|
||||
i++;
|
||||
|
||||
if (desc->min + desc->step*i > max_vol)
|
||||
return -EINVAL;
|
||||
|
||||
ret = max8998_get_voltage_register(rdev, ®, &shift, &mask);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
previous_vol = max8998_get_voltage(rdev);
|
||||
|
||||
/* Check if voltage needs to be changed */
|
||||
/* if previous_voltage equal new voltage, return */
|
||||
if (previous_vol == max8998_list_voltage(rdev, i)) {
|
||||
dev_dbg(max8998->dev, "No voltage change, old:%d, new:%d\n",
|
||||
previous_vol, max8998_list_voltage(rdev, i));
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (buck) {
|
||||
case MAX8998_BUCK1:
|
||||
dev_dbg(max8998->dev,
|
||||
"BUCK1, i:%d, buck1_vol1:%d, buck1_vol2:%d\n\
|
||||
buck1_vol3:%d, buck1_vol4:%d\n",
|
||||
i, max8998->buck1_vol[0], max8998->buck1_vol[1],
|
||||
max8998->buck1_vol[2], max8998->buck1_vol[3]);
|
||||
|
||||
if (gpio_is_valid(pdata->buck1_set1) &&
|
||||
gpio_is_valid(pdata->buck1_set2)) {
|
||||
|
||||
/* check if requested voltage */
|
||||
/* value is already defined */
|
||||
for (j = 0; j < ARRAY_SIZE(max8998->buck1_vol); j++) {
|
||||
if (max8998->buck1_vol[j] == i) {
|
||||
max8998->buck1_idx = j;
|
||||
buck1_gpio_set(pdata->buck1_set1,
|
||||
pdata->buck1_set2, j);
|
||||
goto buck1_exit;
|
||||
}
|
||||
}
|
||||
|
||||
ret = max8998_update_reg(max8998->iodev, reg, i<<shift, mask<<shift);
|
||||
/* no predefine regulator found */
|
||||
max8998->buck1_idx = (buck1_last_val % 2) + 2;
|
||||
dev_dbg(max8998->dev, "max8998->buck1_idx:%d\n",
|
||||
max8998->buck1_idx);
|
||||
max8998->buck1_vol[max8998->buck1_idx] = i;
|
||||
ret = max8998_get_voltage_register(rdev, ®,
|
||||
&shift,
|
||||
&mask);
|
||||
ret = max8998_write_reg(i2c, reg, i);
|
||||
buck1_gpio_set(pdata->buck1_set1,
|
||||
pdata->buck1_set2, max8998->buck1_idx);
|
||||
buck1_last_val++;
|
||||
buck1_exit:
|
||||
dev_dbg(max8998->dev, "%s: SET1:%d, SET2:%d\n",
|
||||
i2c->name, gpio_get_value(pdata->buck1_set1),
|
||||
gpio_get_value(pdata->buck1_set2));
|
||||
break;
|
||||
} else {
|
||||
ret = max8998_write_reg(i2c, reg, i);
|
||||
}
|
||||
break;
|
||||
|
||||
if (en_ramp == true) {
|
||||
int difference = desc->min + desc->step*i - previous_vol/1000;
|
||||
case MAX8998_BUCK2:
|
||||
dev_dbg(max8998->dev,
|
||||
"BUCK2, i:%d buck2_vol1:%d, buck2_vol2:%d\n"
|
||||
, i, max8998->buck2_vol[0], max8998->buck2_vol[1]);
|
||||
if (gpio_is_valid(pdata->buck2_set3)) {
|
||||
if (max8998->buck2_vol[0] == i) {
|
||||
max8998->buck1_idx = 0;
|
||||
buck2_gpio_set(pdata->buck2_set3, 0);
|
||||
} else {
|
||||
max8998->buck1_idx = 1;
|
||||
ret = max8998_get_voltage_register(rdev, ®,
|
||||
&shift,
|
||||
&mask);
|
||||
ret = max8998_write_reg(i2c, reg, i);
|
||||
max8998->buck2_vol[1] = i;
|
||||
buck2_gpio_set(pdata->buck2_set3, 1);
|
||||
}
|
||||
dev_dbg(max8998->dev, "%s: SET3:%d\n", i2c->name,
|
||||
gpio_get_value(pdata->buck2_set3));
|
||||
} else {
|
||||
ret = max8998_write_reg(i2c, reg, i);
|
||||
}
|
||||
break;
|
||||
|
||||
case MAX8998_BUCK3:
|
||||
case MAX8998_BUCK4:
|
||||
ret = max8998_update_reg(i2c, reg, i<<shift, mask<<shift);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Voltage stabilization */
|
||||
max8998_read_reg(i2c, MAX8998_REG_ONOFF4, &val);
|
||||
|
||||
/* lp3974 hasn't got ENRAMP bit - ramp is assumed as true */
|
||||
/* MAX8998 has ENRAMP bit implemented, so test it*/
|
||||
if (max8998->iodev->type == TYPE_MAX8998 && !(val & MAX8998_ENRAMP))
|
||||
return ret;
|
||||
|
||||
difference = desc->min + desc->step*i - previous_vol/1000;
|
||||
if (difference > 0)
|
||||
udelay(difference / ((val & 0x0f) + 1));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -354,7 +493,7 @@ static struct regulator_ops max8998_ldo_ops = {
|
||||
.enable = max8998_ldo_enable,
|
||||
.disable = max8998_ldo_disable,
|
||||
.get_voltage = max8998_get_voltage,
|
||||
.set_voltage = max8998_set_voltage,
|
||||
.set_voltage = max8998_set_voltage_ldo,
|
||||
.set_suspend_enable = max8998_ldo_enable,
|
||||
.set_suspend_disable = max8998_ldo_disable,
|
||||
};
|
||||
@ -365,7 +504,7 @@ static struct regulator_ops max8998_buck_ops = {
|
||||
.enable = max8998_ldo_enable,
|
||||
.disable = max8998_ldo_disable,
|
||||
.get_voltage = max8998_get_voltage,
|
||||
.set_voltage = max8998_set_voltage,
|
||||
.set_voltage = max8998_set_voltage_buck,
|
||||
.set_suspend_enable = max8998_ldo_enable,
|
||||
.set_suspend_disable = max8998_ldo_disable,
|
||||
};
|
||||
@ -538,6 +677,7 @@ static __devinit int max8998_pmic_probe(struct platform_device *pdev)
|
||||
struct max8998_platform_data *pdata = dev_get_platdata(iodev->dev);
|
||||
struct regulator_dev **rdev;
|
||||
struct max8998_data *max8998;
|
||||
struct i2c_client *i2c;
|
||||
int i, ret, size;
|
||||
|
||||
if (!pdata) {
|
||||
@ -561,6 +701,86 @@ static __devinit int max8998_pmic_probe(struct platform_device *pdev)
|
||||
max8998->iodev = iodev;
|
||||
max8998->num_regulators = pdata->num_regulators;
|
||||
platform_set_drvdata(pdev, max8998);
|
||||
i2c = max8998->iodev->i2c;
|
||||
|
||||
/* NOTE: */
|
||||
/* For unused GPIO NOT marked as -1 (thereof equal to 0) WARN_ON */
|
||||
/* will be displayed */
|
||||
|
||||
/* Check if MAX8998 voltage selection GPIOs are defined */
|
||||
if (gpio_is_valid(pdata->buck1_set1) &&
|
||||
gpio_is_valid(pdata->buck1_set2)) {
|
||||
/* Check if SET1 is not equal to 0 */
|
||||
if (!pdata->buck1_set1) {
|
||||
printk(KERN_ERR "MAX8998 SET1 GPIO defined as 0 !\n");
|
||||
WARN_ON(!pdata->buck1_set1);
|
||||
return -EIO;
|
||||
}
|
||||
/* Check if SET2 is not equal to 0 */
|
||||
if (!pdata->buck1_set2) {
|
||||
printk(KERN_ERR "MAX8998 SET2 GPIO defined as 0 !\n");
|
||||
WARN_ON(!pdata->buck1_set2);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
gpio_request(pdata->buck1_set1, "MAX8998 BUCK1_SET1");
|
||||
gpio_direction_output(pdata->buck1_set1,
|
||||
max8998->buck1_idx & 0x1);
|
||||
|
||||
|
||||
gpio_request(pdata->buck1_set2, "MAX8998 BUCK1_SET2");
|
||||
gpio_direction_output(pdata->buck1_set2,
|
||||
(max8998->buck1_idx >> 1) & 0x1);
|
||||
/* Set predefined value for BUCK1 register 1 */
|
||||
i = 0;
|
||||
while (buck12_voltage_map_desc.min +
|
||||
buck12_voltage_map_desc.step*i
|
||||
!= (pdata->buck1_max_voltage1 / 1000))
|
||||
i++;
|
||||
printk(KERN_ERR "i:%d, buck1_idx:%d\n", i, max8998->buck1_idx);
|
||||
max8998->buck1_vol[0] = i;
|
||||
ret = max8998_write_reg(i2c, MAX8998_REG_BUCK1_VOLTAGE1, i);
|
||||
|
||||
/* Set predefined value for BUCK1 register 2 */
|
||||
i = 0;
|
||||
while (buck12_voltage_map_desc.min +
|
||||
buck12_voltage_map_desc.step*i
|
||||
!= (pdata->buck1_max_voltage2 / 1000))
|
||||
i++;
|
||||
|
||||
max8998->buck1_vol[1] = i;
|
||||
printk(KERN_ERR "i:%d, buck1_idx:%d\n", i, max8998->buck1_idx);
|
||||
ret = max8998_write_reg(i2c, MAX8998_REG_BUCK1_VOLTAGE2, i)
|
||||
+ ret;
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
if (gpio_is_valid(pdata->buck2_set3)) {
|
||||
/* Check if SET3 is not equal to 0 */
|
||||
if (!pdata->buck2_set3) {
|
||||
printk(KERN_ERR "MAX8998 SET3 GPIO defined as 0 !\n");
|
||||
WARN_ON(!pdata->buck2_set3);
|
||||
return -EIO;
|
||||
}
|
||||
gpio_request(pdata->buck2_set3, "MAX8998 BUCK2_SET3");
|
||||
gpio_direction_output(pdata->buck2_set3,
|
||||
max8998->buck2_idx & 0x1);
|
||||
|
||||
/* BUCK2 - set preset default voltage value to buck2_vol[0] */
|
||||
i = 0;
|
||||
while (buck12_voltage_map_desc.min +
|
||||
buck12_voltage_map_desc.step*i
|
||||
!= (pdata->buck2_max_voltage / 1000))
|
||||
i++;
|
||||
printk(KERN_ERR "i:%d, buck2_idx:%d\n", i, max8998->buck2_idx);
|
||||
max8998->buck2_vol[0] = i;
|
||||
ret = max8998_write_reg(i2c, MAX8998_REG_BUCK2_VOLTAGE1, i);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
for (i = 0; i < pdata->num_regulators; i++) {
|
||||
const struct voltage_map_desc *desc;
|
||||
|
@ -196,6 +196,16 @@ config RTC_DRV_MAX8925
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-max8925.
|
||||
|
||||
config RTC_DRV_MAX8998
|
||||
tristate "Maxim MAX8998"
|
||||
depends on MFD_MAX8998
|
||||
help
|
||||
If you say yes here you will get support for the
|
||||
RTC of Maxim MAX8998 PMIC.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-max8998.
|
||||
|
||||
config RTC_DRV_RS5C372
|
||||
tristate "Ricoh R2025S/D, RS5C372A/B, RV5C386, RV5C387A"
|
||||
help
|
||||
@ -926,11 +936,12 @@ config RTC_DRV_PCAP
|
||||
If you say Y here you will get support for the RTC found on
|
||||
the PCAP2 ASIC used on some Motorola phones.
|
||||
|
||||
config RTC_DRV_MC13783
|
||||
depends on MFD_MC13783
|
||||
tristate "Freescale MC13783 RTC"
|
||||
config RTC_DRV_MC13XXX
|
||||
depends on MFD_MC13XXX
|
||||
tristate "Freescale MC13xxx RTC"
|
||||
help
|
||||
This enables support for the Freescale MC13783 PMIC RTC
|
||||
This enables support for the RTCs found on Freescale's PMICs
|
||||
MC13783 and MC13892.
|
||||
|
||||
config RTC_DRV_MPC5121
|
||||
tristate "Freescale MPC5121 built-in RTC"
|
||||
|
@ -60,8 +60,9 @@ obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o
|
||||
obj-$(CONFIG_RTC_MXC) += rtc-mxc.o
|
||||
obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o
|
||||
obj-$(CONFIG_RTC_DRV_MAX8925) += rtc-max8925.o
|
||||
obj-$(CONFIG_RTC_DRV_MAX8998) += rtc-max8998.o
|
||||
obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
|
||||
obj-$(CONFIG_RTC_DRV_MC13783) += rtc-mc13783.o
|
||||
obj-$(CONFIG_RTC_DRV_MC13XXX) += rtc-mc13xxx.o
|
||||
obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o
|
||||
obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o
|
||||
obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o
|
||||
|
@ -14,26 +14,26 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/mfd/abx500.h>
|
||||
#include <linux/mfd/ab8500.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#define AB8500_RTC_SOFF_STAT_REG 0x0F00
|
||||
#define AB8500_RTC_CC_CONF_REG 0x0F01
|
||||
#define AB8500_RTC_READ_REQ_REG 0x0F02
|
||||
#define AB8500_RTC_WATCH_TSECMID_REG 0x0F03
|
||||
#define AB8500_RTC_WATCH_TSECHI_REG 0x0F04
|
||||
#define AB8500_RTC_WATCH_TMIN_LOW_REG 0x0F05
|
||||
#define AB8500_RTC_WATCH_TMIN_MID_REG 0x0F06
|
||||
#define AB8500_RTC_WATCH_TMIN_HI_REG 0x0F07
|
||||
#define AB8500_RTC_ALRM_MIN_LOW_REG 0x0F08
|
||||
#define AB8500_RTC_ALRM_MIN_MID_REG 0x0F09
|
||||
#define AB8500_RTC_ALRM_MIN_HI_REG 0x0F0A
|
||||
#define AB8500_RTC_STAT_REG 0x0F0B
|
||||
#define AB8500_RTC_BKUP_CHG_REG 0x0F0C
|
||||
#define AB8500_RTC_FORCE_BKUP_REG 0x0F0D
|
||||
#define AB8500_RTC_CALIB_REG 0x0F0E
|
||||
#define AB8500_RTC_SWITCH_STAT_REG 0x0F0F
|
||||
#define AB8500_REV_REG 0x1080
|
||||
#define AB8500_RTC_SOFF_STAT_REG 0x00
|
||||
#define AB8500_RTC_CC_CONF_REG 0x01
|
||||
#define AB8500_RTC_READ_REQ_REG 0x02
|
||||
#define AB8500_RTC_WATCH_TSECMID_REG 0x03
|
||||
#define AB8500_RTC_WATCH_TSECHI_REG 0x04
|
||||
#define AB8500_RTC_WATCH_TMIN_LOW_REG 0x05
|
||||
#define AB8500_RTC_WATCH_TMIN_MID_REG 0x06
|
||||
#define AB8500_RTC_WATCH_TMIN_HI_REG 0x07
|
||||
#define AB8500_RTC_ALRM_MIN_LOW_REG 0x08
|
||||
#define AB8500_RTC_ALRM_MIN_MID_REG 0x09
|
||||
#define AB8500_RTC_ALRM_MIN_HI_REG 0x0A
|
||||
#define AB8500_RTC_STAT_REG 0x0B
|
||||
#define AB8500_RTC_BKUP_CHG_REG 0x0C
|
||||
#define AB8500_RTC_FORCE_BKUP_REG 0x0D
|
||||
#define AB8500_RTC_CALIB_REG 0x0E
|
||||
#define AB8500_RTC_SWITCH_STAT_REG 0x0F
|
||||
|
||||
/* RtcReadRequest bits */
|
||||
#define RTC_READ_REQUEST 0x01
|
||||
@ -46,13 +46,13 @@
|
||||
#define COUNTS_PER_SEC (0xF000 / 60)
|
||||
#define AB8500_RTC_EPOCH 2000
|
||||
|
||||
static const unsigned long ab8500_rtc_time_regs[] = {
|
||||
static const u8 ab8500_rtc_time_regs[] = {
|
||||
AB8500_RTC_WATCH_TMIN_HI_REG, AB8500_RTC_WATCH_TMIN_MID_REG,
|
||||
AB8500_RTC_WATCH_TMIN_LOW_REG, AB8500_RTC_WATCH_TSECHI_REG,
|
||||
AB8500_RTC_WATCH_TSECMID_REG
|
||||
};
|
||||
|
||||
static const unsigned long ab8500_rtc_alarm_regs[] = {
|
||||
static const u8 ab8500_rtc_alarm_regs[] = {
|
||||
AB8500_RTC_ALRM_MIN_HI_REG, AB8500_RTC_ALRM_MIN_MID_REG,
|
||||
AB8500_RTC_ALRM_MIN_LOW_REG
|
||||
};
|
||||
@ -76,29 +76,30 @@ static unsigned long get_elapsed_seconds(int year)
|
||||
|
||||
static int ab8500_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
|
||||
unsigned long timeout = jiffies + HZ;
|
||||
int retval, i;
|
||||
unsigned long mins, secs;
|
||||
unsigned char buf[ARRAY_SIZE(ab8500_rtc_time_regs)];
|
||||
u8 value;
|
||||
|
||||
/* Request a data read */
|
||||
retval = ab8500_write(ab8500, AB8500_RTC_READ_REQ_REG,
|
||||
RTC_READ_REQUEST);
|
||||
retval = abx500_set_register_interruptible(dev,
|
||||
AB8500_RTC, AB8500_RTC_READ_REQ_REG, RTC_READ_REQUEST);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
/* Early AB8500 chips will not clear the rtc read request bit */
|
||||
if (ab8500->revision == 0) {
|
||||
if (abx500_get_chip_id(dev) == 0) {
|
||||
msleep(1);
|
||||
} else {
|
||||
/* Wait for some cycles after enabling the rtc read in ab8500 */
|
||||
while (time_before(jiffies, timeout)) {
|
||||
retval = ab8500_read(ab8500, AB8500_RTC_READ_REQ_REG);
|
||||
retval = abx500_get_register_interruptible(dev,
|
||||
AB8500_RTC, AB8500_RTC_READ_REQ_REG, &value);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
if (!(retval & RTC_READ_REQUEST))
|
||||
if (!(value & RTC_READ_REQUEST))
|
||||
break;
|
||||
|
||||
msleep(1);
|
||||
@ -107,10 +108,11 @@ static int ab8500_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
|
||||
/* Read the Watchtime registers */
|
||||
for (i = 0; i < ARRAY_SIZE(ab8500_rtc_time_regs); i++) {
|
||||
retval = ab8500_read(ab8500, ab8500_rtc_time_regs[i]);
|
||||
retval = abx500_get_register_interruptible(dev,
|
||||
AB8500_RTC, ab8500_rtc_time_regs[i], &value);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
buf[i] = retval;
|
||||
buf[i] = value;
|
||||
}
|
||||
|
||||
mins = (buf[0] << 16) | (buf[1] << 8) | buf[2];
|
||||
@ -128,7 +130,6 @@ static int ab8500_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
|
||||
static int ab8500_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
|
||||
int retval, i;
|
||||
unsigned char buf[ARRAY_SIZE(ab8500_rtc_time_regs)];
|
||||
unsigned long no_secs, no_mins, secs = 0;
|
||||
@ -162,27 +163,29 @@ static int ab8500_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
buf[0] = (no_mins >> 16) & 0xFF;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ab8500_rtc_time_regs); i++) {
|
||||
retval = ab8500_write(ab8500, ab8500_rtc_time_regs[i], buf[i]);
|
||||
retval = abx500_set_register_interruptible(dev, AB8500_RTC,
|
||||
ab8500_rtc_time_regs[i], buf[i]);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Request a data write */
|
||||
return ab8500_write(ab8500, AB8500_RTC_READ_REQ_REG, RTC_WRITE_REQUEST);
|
||||
return abx500_set_register_interruptible(dev, AB8500_RTC,
|
||||
AB8500_RTC_READ_REQ_REG, RTC_WRITE_REQUEST);
|
||||
}
|
||||
|
||||
static int ab8500_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
{
|
||||
struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
|
||||
int retval, i;
|
||||
int rtc_ctrl;
|
||||
u8 rtc_ctrl, value;
|
||||
unsigned char buf[ARRAY_SIZE(ab8500_rtc_alarm_regs)];
|
||||
unsigned long secs, mins;
|
||||
|
||||
/* Check if the alarm is enabled or not */
|
||||
rtc_ctrl = ab8500_read(ab8500, AB8500_RTC_STAT_REG);
|
||||
if (rtc_ctrl < 0)
|
||||
return rtc_ctrl;
|
||||
retval = abx500_get_register_interruptible(dev, AB8500_RTC,
|
||||
AB8500_RTC_STAT_REG, &rtc_ctrl);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
if (rtc_ctrl & RTC_ALARM_ENA)
|
||||
alarm->enabled = 1;
|
||||
@ -192,10 +195,11 @@ static int ab8500_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
alarm->pending = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ab8500_rtc_alarm_regs); i++) {
|
||||
retval = ab8500_read(ab8500, ab8500_rtc_alarm_regs[i]);
|
||||
retval = abx500_get_register_interruptible(dev, AB8500_RTC,
|
||||
ab8500_rtc_alarm_regs[i], &value);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
buf[i] = retval;
|
||||
buf[i] = value;
|
||||
}
|
||||
|
||||
mins = (buf[0] << 16) | (buf[1] << 8) | (buf[2]);
|
||||
@ -211,15 +215,13 @@ static int ab8500_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
|
||||
static int ab8500_rtc_irq_enable(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
|
||||
|
||||
return ab8500_set_bits(ab8500, AB8500_RTC_STAT_REG, RTC_ALARM_ENA,
|
||||
return abx500_mask_and_set_register_interruptible(dev, AB8500_RTC,
|
||||
AB8500_RTC_STAT_REG, RTC_ALARM_ENA,
|
||||
enabled ? RTC_ALARM_ENA : 0);
|
||||
}
|
||||
|
||||
static int ab8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
{
|
||||
struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
|
||||
int retval, i;
|
||||
unsigned char buf[ARRAY_SIZE(ab8500_rtc_alarm_regs)];
|
||||
unsigned long mins, secs = 0;
|
||||
@ -247,7 +249,8 @@ static int ab8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
|
||||
/* Set the alarm time */
|
||||
for (i = 0; i < ARRAY_SIZE(ab8500_rtc_alarm_regs); i++) {
|
||||
retval = ab8500_write(ab8500, ab8500_rtc_alarm_regs[i], buf[i]);
|
||||
retval = abx500_set_register_interruptible(dev, AB8500_RTC,
|
||||
ab8500_rtc_alarm_regs[i], buf[i]);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
}
|
||||
@ -276,10 +279,9 @@ static const struct rtc_class_ops ab8500_rtc_ops = {
|
||||
|
||||
static int __devinit ab8500_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
|
||||
int err;
|
||||
struct rtc_device *rtc;
|
||||
int rtc_ctrl;
|
||||
u8 rtc_ctrl;
|
||||
int irq;
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "ALARM");
|
||||
@ -287,17 +289,18 @@ static int __devinit ab8500_rtc_probe(struct platform_device *pdev)
|
||||
return irq;
|
||||
|
||||
/* For RTC supply test */
|
||||
err = ab8500_set_bits(ab8500, AB8500_RTC_STAT_REG, RTC_STATUS_DATA,
|
||||
RTC_STATUS_DATA);
|
||||
err = abx500_mask_and_set_register_interruptible(&pdev->dev, AB8500_RTC,
|
||||
AB8500_RTC_STAT_REG, RTC_STATUS_DATA, RTC_STATUS_DATA);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Wait for reset by the PorRtc */
|
||||
msleep(1);
|
||||
|
||||
rtc_ctrl = ab8500_read(ab8500, AB8500_RTC_STAT_REG);
|
||||
if (rtc_ctrl < 0)
|
||||
return rtc_ctrl;
|
||||
err = abx500_get_register_interruptible(&pdev->dev, AB8500_RTC,
|
||||
AB8500_RTC_STAT_REG, &rtc_ctrl);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Check if the RTC Supply fails */
|
||||
if (!(rtc_ctrl & RTC_STATUS_DATA)) {
|
||||
|
300
drivers/rtc/rtc-max8998.c
Normal file
300
drivers/rtc/rtc-max8998.c
Normal file
@ -0,0 +1,300 @@
|
||||
/*
|
||||
* RTC driver for Maxim MAX8998
|
||||
*
|
||||
* Copyright (C) 2010 Samsung Electronics Co.Ltd
|
||||
* Author: Minkyu Kang <mk7.kang@samsung.com>
|
||||
* 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/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/max8998.h>
|
||||
#include <linux/mfd/max8998-private.h>
|
||||
|
||||
#define MAX8998_RTC_SEC 0x00
|
||||
#define MAX8998_RTC_MIN 0x01
|
||||
#define MAX8998_RTC_HOUR 0x02
|
||||
#define MAX8998_RTC_WEEKDAY 0x03
|
||||
#define MAX8998_RTC_DATE 0x04
|
||||
#define MAX8998_RTC_MONTH 0x05
|
||||
#define MAX8998_RTC_YEAR1 0x06
|
||||
#define MAX8998_RTC_YEAR2 0x07
|
||||
#define MAX8998_ALARM0_SEC 0x08
|
||||
#define MAX8998_ALARM0_MIN 0x09
|
||||
#define MAX8998_ALARM0_HOUR 0x0a
|
||||
#define MAX8998_ALARM0_WEEKDAY 0x0b
|
||||
#define MAX8998_ALARM0_DATE 0x0c
|
||||
#define MAX8998_ALARM0_MONTH 0x0d
|
||||
#define MAX8998_ALARM0_YEAR1 0x0e
|
||||
#define MAX8998_ALARM0_YEAR2 0x0f
|
||||
#define MAX8998_ALARM1_SEC 0x10
|
||||
#define MAX8998_ALARM1_MIN 0x11
|
||||
#define MAX8998_ALARM1_HOUR 0x12
|
||||
#define MAX8998_ALARM1_WEEKDAY 0x13
|
||||
#define MAX8998_ALARM1_DATE 0x14
|
||||
#define MAX8998_ALARM1_MONTH 0x15
|
||||
#define MAX8998_ALARM1_YEAR1 0x16
|
||||
#define MAX8998_ALARM1_YEAR2 0x17
|
||||
#define MAX8998_ALARM0_CONF 0x18
|
||||
#define MAX8998_ALARM1_CONF 0x19
|
||||
#define MAX8998_RTC_STATUS 0x1a
|
||||
#define MAX8998_WTSR_SMPL_CNTL 0x1b
|
||||
#define MAX8998_TEST 0x1f
|
||||
|
||||
#define HOUR_12 (1 << 7)
|
||||
#define HOUR_PM (1 << 5)
|
||||
#define ALARM0_STATUS (1 << 1)
|
||||
#define ALARM1_STATUS (1 << 2)
|
||||
|
||||
enum {
|
||||
RTC_SEC = 0,
|
||||
RTC_MIN,
|
||||
RTC_HOUR,
|
||||
RTC_WEEKDAY,
|
||||
RTC_DATE,
|
||||
RTC_MONTH,
|
||||
RTC_YEAR1,
|
||||
RTC_YEAR2,
|
||||
};
|
||||
|
||||
struct max8998_rtc_info {
|
||||
struct device *dev;
|
||||
struct max8998_dev *max8998;
|
||||
struct i2c_client *rtc;
|
||||
struct rtc_device *rtc_dev;
|
||||
int irq;
|
||||
};
|
||||
|
||||
static void max8998_data_to_tm(u8 *data, struct rtc_time *tm)
|
||||
{
|
||||
tm->tm_sec = bcd2bin(data[RTC_SEC]);
|
||||
tm->tm_min = bcd2bin(data[RTC_MIN]);
|
||||
if (data[RTC_HOUR] & HOUR_12) {
|
||||
tm->tm_hour = bcd2bin(data[RTC_HOUR] & 0x1f);
|
||||
if (data[RTC_HOUR] & HOUR_PM)
|
||||
tm->tm_hour += 12;
|
||||
} else
|
||||
tm->tm_hour = bcd2bin(data[RTC_HOUR] & 0x3f);
|
||||
|
||||
tm->tm_wday = data[RTC_WEEKDAY] & 0x07;
|
||||
tm->tm_mday = bcd2bin(data[RTC_DATE]);
|
||||
tm->tm_mon = bcd2bin(data[RTC_MONTH]);
|
||||
tm->tm_year = bcd2bin(data[RTC_YEAR1]) + bcd2bin(data[RTC_YEAR2]) * 100;
|
||||
tm->tm_year -= 1900;
|
||||
}
|
||||
|
||||
static void max8998_tm_to_data(struct rtc_time *tm, u8 *data)
|
||||
{
|
||||
data[RTC_SEC] = bin2bcd(tm->tm_sec);
|
||||
data[RTC_MIN] = bin2bcd(tm->tm_min);
|
||||
data[RTC_HOUR] = bin2bcd(tm->tm_hour);
|
||||
data[RTC_WEEKDAY] = tm->tm_wday;
|
||||
data[RTC_DATE] = bin2bcd(tm->tm_mday);
|
||||
data[RTC_MONTH] = bin2bcd(tm->tm_mon);
|
||||
data[RTC_YEAR1] = bin2bcd(tm->tm_year % 100);
|
||||
data[RTC_YEAR2] = bin2bcd((tm->tm_year + 1900) / 100);
|
||||
}
|
||||
|
||||
static int max8998_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct max8998_rtc_info *info = dev_get_drvdata(dev);
|
||||
u8 data[8];
|
||||
int ret;
|
||||
|
||||
ret = max8998_bulk_read(info->rtc, MAX8998_RTC_SEC, 8, data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
max8998_data_to_tm(data, tm);
|
||||
|
||||
return rtc_valid_tm(tm);
|
||||
}
|
||||
|
||||
static int max8998_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct max8998_rtc_info *info = dev_get_drvdata(dev);
|
||||
u8 data[8];
|
||||
|
||||
max8998_tm_to_data(tm, data);
|
||||
|
||||
return max8998_bulk_write(info->rtc, MAX8998_RTC_SEC, 8, data);
|
||||
}
|
||||
|
||||
static int max8998_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct max8998_rtc_info *info = dev_get_drvdata(dev);
|
||||
u8 data[8];
|
||||
u8 val;
|
||||
int ret;
|
||||
|
||||
ret = max8998_bulk_read(info->rtc, MAX8998_ALARM0_SEC, 8, data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
max8998_data_to_tm(data, &alrm->time);
|
||||
|
||||
ret = max8998_read_reg(info->rtc, MAX8998_ALARM0_CONF, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
alrm->enabled = !!val;
|
||||
|
||||
ret = max8998_read_reg(info->rtc, MAX8998_RTC_STATUS, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (val & ALARM0_STATUS)
|
||||
alrm->pending = 1;
|
||||
else
|
||||
alrm->pending = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max8998_rtc_stop_alarm(struct max8998_rtc_info *info)
|
||||
{
|
||||
return max8998_write_reg(info->rtc, MAX8998_ALARM0_CONF, 0);
|
||||
}
|
||||
|
||||
static int max8998_rtc_start_alarm(struct max8998_rtc_info *info)
|
||||
{
|
||||
return max8998_write_reg(info->rtc, MAX8998_ALARM0_CONF, 0x77);
|
||||
}
|
||||
|
||||
static int max8998_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct max8998_rtc_info *info = dev_get_drvdata(dev);
|
||||
u8 data[8];
|
||||
int ret;
|
||||
|
||||
max8998_tm_to_data(&alrm->time, data);
|
||||
|
||||
ret = max8998_rtc_stop_alarm(info);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = max8998_bulk_write(info->rtc, MAX8998_ALARM0_SEC, 8, data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (alrm->enabled)
|
||||
return max8998_rtc_start_alarm(info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max8998_rtc_alarm_irq_enable(struct device *dev,
|
||||
unsigned int enabled)
|
||||
{
|
||||
struct max8998_rtc_info *info = dev_get_drvdata(dev);
|
||||
|
||||
if (enabled)
|
||||
return max8998_rtc_start_alarm(info);
|
||||
else
|
||||
return max8998_rtc_stop_alarm(info);
|
||||
}
|
||||
|
||||
static irqreturn_t max8998_rtc_alarm_irq(int irq, void *data)
|
||||
{
|
||||
struct max8998_rtc_info *info = data;
|
||||
|
||||
rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops max8998_rtc_ops = {
|
||||
.read_time = max8998_rtc_read_time,
|
||||
.set_time = max8998_rtc_set_time,
|
||||
.read_alarm = max8998_rtc_read_alarm,
|
||||
.set_alarm = max8998_rtc_set_alarm,
|
||||
.alarm_irq_enable = max8998_rtc_alarm_irq_enable,
|
||||
};
|
||||
|
||||
static int __devinit max8998_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct max8998_dev *max8998 = dev_get_drvdata(pdev->dev.parent);
|
||||
struct max8998_rtc_info *info;
|
||||
int ret;
|
||||
|
||||
info = kzalloc(sizeof(struct max8998_rtc_info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
info->dev = &pdev->dev;
|
||||
info->max8998 = max8998;
|
||||
info->rtc = max8998->rtc;
|
||||
info->irq = max8998->irq_base + MAX8998_IRQ_ALARM0;
|
||||
|
||||
info->rtc_dev = rtc_device_register("max8998-rtc", &pdev->dev,
|
||||
&max8998_rtc_ops, THIS_MODULE);
|
||||
|
||||
if (IS_ERR(info->rtc_dev)) {
|
||||
ret = PTR_ERR(info->rtc_dev);
|
||||
dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
|
||||
goto out_rtc;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, info);
|
||||
|
||||
ret = request_threaded_irq(info->irq, NULL, max8998_rtc_alarm_irq, 0,
|
||||
"rtc-alarm0", info);
|
||||
if (ret < 0)
|
||||
dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
|
||||
info->irq, ret);
|
||||
|
||||
return 0;
|
||||
|
||||
out_rtc:
|
||||
kfree(info);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit max8998_rtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct max8998_rtc_info *info = platform_get_drvdata(pdev);
|
||||
|
||||
if (info) {
|
||||
free_irq(info->irq, info);
|
||||
rtc_device_unregister(info->rtc_dev);
|
||||
kfree(info);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver max8998_rtc_driver = {
|
||||
.driver = {
|
||||
.name = "max8998-rtc",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = max8998_rtc_probe,
|
||||
.remove = __devexit_p(max8998_rtc_remove),
|
||||
};
|
||||
|
||||
static int __init max8998_rtc_init(void)
|
||||
{
|
||||
return platform_driver_register(&max8998_rtc_driver);
|
||||
}
|
||||
module_init(max8998_rtc_init);
|
||||
|
||||
static void __exit max8998_rtc_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&max8998_rtc_driver);
|
||||
}
|
||||
module_exit(max8998_rtc_exit);
|
||||
|
||||
MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>");
|
||||
MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
|
||||
MODULE_DESCRIPTION("Maxim MAX8998 RTC driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,428 +0,0 @@
|
||||
/*
|
||||
* Real Time Clock driver for Freescale MC13783 PMIC
|
||||
*
|
||||
* (C) 2009 Sascha Hauer, Pengutronix
|
||||
* (C) 2009 Uwe Kleine-Koenig, Pengutronix
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/mfd/mc13783.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/rtc.h>
|
||||
|
||||
#define DRIVER_NAME "mc13783-rtc"
|
||||
|
||||
#define MC13783_RTCTOD 20
|
||||
#define MC13783_RTCTODA 21
|
||||
#define MC13783_RTCDAY 22
|
||||
#define MC13783_RTCDAYA 23
|
||||
|
||||
struct mc13783_rtc {
|
||||
struct rtc_device *rtc;
|
||||
struct mc13783 *mc13783;
|
||||
int valid;
|
||||
};
|
||||
|
||||
static int mc13783_rtc_irq_enable_unlocked(struct device *dev,
|
||||
unsigned int enabled, int irq)
|
||||
{
|
||||
struct mc13783_rtc *priv = dev_get_drvdata(dev);
|
||||
int (*func)(struct mc13783 *mc13783, int irq);
|
||||
|
||||
if (!priv->valid)
|
||||
return -ENODATA;
|
||||
|
||||
func = enabled ? mc13783_irq_unmask : mc13783_irq_mask;
|
||||
return func(priv->mc13783, irq);
|
||||
}
|
||||
|
||||
static int mc13783_rtc_irq_enable(struct device *dev,
|
||||
unsigned int enabled, int irq)
|
||||
{
|
||||
struct mc13783_rtc *priv = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
mc13783_lock(priv->mc13783);
|
||||
|
||||
ret = mc13783_rtc_irq_enable_unlocked(dev, enabled, irq);
|
||||
|
||||
mc13783_unlock(priv->mc13783);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mc13783_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct mc13783_rtc *priv = dev_get_drvdata(dev);
|
||||
unsigned int seconds, days1, days2;
|
||||
unsigned long s1970;
|
||||
int ret;
|
||||
|
||||
mc13783_lock(priv->mc13783);
|
||||
|
||||
if (!priv->valid) {
|
||||
ret = -ENODATA;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = mc13783_reg_read(priv->mc13783, MC13783_RTCDAY, &days1);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
ret = mc13783_reg_read(priv->mc13783, MC13783_RTCTOD, &seconds);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
ret = mc13783_reg_read(priv->mc13783, MC13783_RTCDAY, &days2);
|
||||
out:
|
||||
mc13783_unlock(priv->mc13783);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (days2 == days1 + 1) {
|
||||
if (seconds >= 86400 / 2)
|
||||
days2 = days1;
|
||||
else
|
||||
days1 = days2;
|
||||
}
|
||||
|
||||
if (days1 != days2)
|
||||
return -EIO;
|
||||
|
||||
s1970 = days1 * 86400 + seconds;
|
||||
|
||||
rtc_time_to_tm(s1970, tm);
|
||||
|
||||
return rtc_valid_tm(tm);
|
||||
}
|
||||
|
||||
static int mc13783_rtc_set_mmss(struct device *dev, unsigned long secs)
|
||||
{
|
||||
struct mc13783_rtc *priv = dev_get_drvdata(dev);
|
||||
unsigned int seconds, days;
|
||||
unsigned int alarmseconds;
|
||||
int ret;
|
||||
|
||||
seconds = secs % 86400;
|
||||
days = secs / 86400;
|
||||
|
||||
mc13783_lock(priv->mc13783);
|
||||
|
||||
/*
|
||||
* temporarily invalidate alarm to prevent triggering it when the day is
|
||||
* already updated while the time isn't yet.
|
||||
*/
|
||||
ret = mc13783_reg_read(priv->mc13783, MC13783_RTCTODA, &alarmseconds);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
if (alarmseconds < 86400) {
|
||||
ret = mc13783_reg_write(priv->mc13783,
|
||||
MC13783_RTCTODA, 0x1ffff);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* write seconds=0 to prevent a day switch between writing days
|
||||
* and seconds below
|
||||
*/
|
||||
ret = mc13783_reg_write(priv->mc13783, MC13783_RTCTOD, 0);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
ret = mc13783_reg_write(priv->mc13783, MC13783_RTCDAY, days);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
ret = mc13783_reg_write(priv->mc13783, MC13783_RTCTOD, seconds);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
/* restore alarm */
|
||||
if (alarmseconds < 86400) {
|
||||
ret = mc13783_reg_write(priv->mc13783,
|
||||
MC13783_RTCTODA, alarmseconds);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = mc13783_irq_ack(priv->mc13783, MC13783_IRQ_RTCRST);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
ret = mc13783_irq_unmask(priv->mc13783, MC13783_IRQ_RTCRST);
|
||||
out:
|
||||
priv->valid = !ret;
|
||||
|
||||
mc13783_unlock(priv->mc13783);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mc13783_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
{
|
||||
struct mc13783_rtc *priv = dev_get_drvdata(dev);
|
||||
unsigned seconds, days;
|
||||
unsigned long s1970;
|
||||
int enabled, pending;
|
||||
int ret;
|
||||
|
||||
mc13783_lock(priv->mc13783);
|
||||
|
||||
ret = mc13783_reg_read(priv->mc13783, MC13783_RTCTODA, &seconds);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
if (seconds >= 86400) {
|
||||
ret = -ENODATA;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = mc13783_reg_read(priv->mc13783, MC13783_RTCDAY, &days);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
ret = mc13783_irq_status(priv->mc13783, MC13783_IRQ_TODA,
|
||||
&enabled, &pending);
|
||||
|
||||
out:
|
||||
mc13783_unlock(priv->mc13783);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
alarm->enabled = enabled;
|
||||
alarm->pending = pending;
|
||||
|
||||
s1970 = days * 86400 + seconds;
|
||||
|
||||
rtc_time_to_tm(s1970, &alarm->time);
|
||||
dev_dbg(dev, "%s: %lu\n", __func__, s1970);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mc13783_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
{
|
||||
struct mc13783_rtc *priv = dev_get_drvdata(dev);
|
||||
unsigned long s1970;
|
||||
unsigned seconds, days;
|
||||
int ret;
|
||||
|
||||
mc13783_lock(priv->mc13783);
|
||||
|
||||
/* disable alarm to prevent false triggering */
|
||||
ret = mc13783_reg_write(priv->mc13783, MC13783_RTCTODA, 0x1ffff);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
ret = mc13783_irq_ack(priv->mc13783, MC13783_IRQ_TODA);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
ret = rtc_tm_to_time(&alarm->time, &s1970);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
dev_dbg(dev, "%s: o%2.s %lu\n", __func__, alarm->enabled ? "n" : "ff",
|
||||
s1970);
|
||||
|
||||
ret = mc13783_rtc_irq_enable_unlocked(dev, alarm->enabled,
|
||||
MC13783_IRQ_TODA);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
seconds = s1970 % 86400;
|
||||
days = s1970 / 86400;
|
||||
|
||||
ret = mc13783_reg_write(priv->mc13783, MC13783_RTCDAYA, days);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
ret = mc13783_reg_write(priv->mc13783, MC13783_RTCTODA, seconds);
|
||||
|
||||
out:
|
||||
mc13783_unlock(priv->mc13783);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t mc13783_rtc_alarm_handler(int irq, void *dev)
|
||||
{
|
||||
struct mc13783_rtc *priv = dev;
|
||||
struct mc13783 *mc13783 = priv->mc13783;
|
||||
|
||||
dev_dbg(&priv->rtc->dev, "Alarm\n");
|
||||
|
||||
rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_AF);
|
||||
|
||||
mc13783_irq_ack(mc13783, irq);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t mc13783_rtc_update_handler(int irq, void *dev)
|
||||
{
|
||||
struct mc13783_rtc *priv = dev;
|
||||
struct mc13783 *mc13783 = priv->mc13783;
|
||||
|
||||
dev_dbg(&priv->rtc->dev, "1HZ\n");
|
||||
|
||||
rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_UF);
|
||||
|
||||
mc13783_irq_ack(mc13783, irq);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int mc13783_rtc_update_irq_enable(struct device *dev,
|
||||
unsigned int enabled)
|
||||
{
|
||||
return mc13783_rtc_irq_enable(dev, enabled, MC13783_IRQ_1HZ);
|
||||
}
|
||||
|
||||
static int mc13783_rtc_alarm_irq_enable(struct device *dev,
|
||||
unsigned int enabled)
|
||||
{
|
||||
return mc13783_rtc_irq_enable(dev, enabled, MC13783_IRQ_TODA);
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops mc13783_rtc_ops = {
|
||||
.read_time = mc13783_rtc_read_time,
|
||||
.set_mmss = mc13783_rtc_set_mmss,
|
||||
.read_alarm = mc13783_rtc_read_alarm,
|
||||
.set_alarm = mc13783_rtc_set_alarm,
|
||||
.alarm_irq_enable = mc13783_rtc_alarm_irq_enable,
|
||||
.update_irq_enable = mc13783_rtc_update_irq_enable,
|
||||
};
|
||||
|
||||
static irqreturn_t mc13783_rtc_reset_handler(int irq, void *dev)
|
||||
{
|
||||
struct mc13783_rtc *priv = dev;
|
||||
struct mc13783 *mc13783 = priv->mc13783;
|
||||
|
||||
dev_dbg(&priv->rtc->dev, "RTCRST\n");
|
||||
priv->valid = 0;
|
||||
|
||||
mc13783_irq_mask(mc13783, irq);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __devinit mc13783_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct mc13783_rtc *priv;
|
||||
struct mc13783 *mc13783;
|
||||
int rtcrst_pending;
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
mc13783 = dev_get_drvdata(pdev->dev.parent);
|
||||
priv->mc13783 = mc13783;
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
mc13783_lock(mc13783);
|
||||
|
||||
ret = mc13783_irq_request(mc13783, MC13783_IRQ_RTCRST,
|
||||
mc13783_rtc_reset_handler, DRIVER_NAME, priv);
|
||||
if (ret)
|
||||
goto err_reset_irq_request;
|
||||
|
||||
ret = mc13783_irq_status(mc13783, MC13783_IRQ_RTCRST,
|
||||
NULL, &rtcrst_pending);
|
||||
if (ret)
|
||||
goto err_reset_irq_status;
|
||||
|
||||
priv->valid = !rtcrst_pending;
|
||||
|
||||
ret = mc13783_irq_request_nounmask(mc13783, MC13783_IRQ_1HZ,
|
||||
mc13783_rtc_update_handler, DRIVER_NAME, priv);
|
||||
if (ret)
|
||||
goto err_update_irq_request;
|
||||
|
||||
ret = mc13783_irq_request_nounmask(mc13783, MC13783_IRQ_TODA,
|
||||
mc13783_rtc_alarm_handler, DRIVER_NAME, priv);
|
||||
if (ret)
|
||||
goto err_alarm_irq_request;
|
||||
|
||||
priv->rtc = rtc_device_register(pdev->name,
|
||||
&pdev->dev, &mc13783_rtc_ops, THIS_MODULE);
|
||||
if (IS_ERR(priv->rtc)) {
|
||||
ret = PTR_ERR(priv->rtc);
|
||||
|
||||
mc13783_irq_free(mc13783, MC13783_IRQ_TODA, priv);
|
||||
err_alarm_irq_request:
|
||||
|
||||
mc13783_irq_free(mc13783, MC13783_IRQ_1HZ, priv);
|
||||
err_update_irq_request:
|
||||
|
||||
err_reset_irq_status:
|
||||
|
||||
mc13783_irq_free(mc13783, MC13783_IRQ_RTCRST, priv);
|
||||
err_reset_irq_request:
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
mc13783_unlock(mc13783);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit mc13783_rtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mc13783_rtc *priv = platform_get_drvdata(pdev);
|
||||
|
||||
mc13783_lock(priv->mc13783);
|
||||
|
||||
rtc_device_unregister(priv->rtc);
|
||||
|
||||
mc13783_irq_free(priv->mc13783, MC13783_IRQ_TODA, priv);
|
||||
mc13783_irq_free(priv->mc13783, MC13783_IRQ_1HZ, priv);
|
||||
mc13783_irq_free(priv->mc13783, MC13783_IRQ_RTCRST, priv);
|
||||
|
||||
mc13783_unlock(priv->mc13783);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
kfree(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver mc13783_rtc_driver = {
|
||||
.remove = __devexit_p(mc13783_rtc_remove),
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init mc13783_rtc_init(void)
|
||||
{
|
||||
return platform_driver_probe(&mc13783_rtc_driver, &mc13783_rtc_probe);
|
||||
}
|
||||
module_init(mc13783_rtc_init);
|
||||
|
||||
static void __exit mc13783_rtc_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&mc13783_rtc_driver);
|
||||
}
|
||||
module_exit(mc13783_rtc_exit);
|
||||
|
||||
MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
|
||||
MODULE_DESCRIPTION("RTC driver for Freescale MC13783 PMIC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRIVER_NAME);
|
437
drivers/rtc/rtc-mc13xxx.c
Normal file
437
drivers/rtc/rtc-mc13xxx.c
Normal file
@ -0,0 +1,437 @@
|
||||
/*
|
||||
* Real Time Clock driver for Freescale MC13XXX PMIC
|
||||
*
|
||||
* (C) 2009 Sascha Hauer, Pengutronix
|
||||
* (C) 2009 Uwe Kleine-Koenig, Pengutronix
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/mfd/mc13xxx.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/rtc.h>
|
||||
|
||||
#define DRIVER_NAME "mc13xxx-rtc"
|
||||
|
||||
#define MC13XXX_RTCTOD 20
|
||||
#define MC13XXX_RTCTODA 21
|
||||
#define MC13XXX_RTCDAY 22
|
||||
#define MC13XXX_RTCDAYA 23
|
||||
|
||||
struct mc13xxx_rtc {
|
||||
struct rtc_device *rtc;
|
||||
struct mc13xxx *mc13xxx;
|
||||
int valid;
|
||||
};
|
||||
|
||||
static int mc13xxx_rtc_irq_enable_unlocked(struct device *dev,
|
||||
unsigned int enabled, int irq)
|
||||
{
|
||||
struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
|
||||
int (*func)(struct mc13xxx *mc13xxx, int irq);
|
||||
|
||||
if (!priv->valid)
|
||||
return -ENODATA;
|
||||
|
||||
func = enabled ? mc13xxx_irq_unmask : mc13xxx_irq_mask;
|
||||
return func(priv->mc13xxx, irq);
|
||||
}
|
||||
|
||||
static int mc13xxx_rtc_irq_enable(struct device *dev,
|
||||
unsigned int enabled, int irq)
|
||||
{
|
||||
struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
mc13xxx_lock(priv->mc13xxx);
|
||||
|
||||
ret = mc13xxx_rtc_irq_enable_unlocked(dev, enabled, irq);
|
||||
|
||||
mc13xxx_unlock(priv->mc13xxx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mc13xxx_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
|
||||
unsigned int seconds, days1, days2;
|
||||
unsigned long s1970;
|
||||
int ret;
|
||||
|
||||
mc13xxx_lock(priv->mc13xxx);
|
||||
|
||||
if (!priv->valid) {
|
||||
ret = -ENODATA;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCDAY, &days1);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCTOD, &seconds);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCDAY, &days2);
|
||||
out:
|
||||
mc13xxx_unlock(priv->mc13xxx);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (days2 == days1 + 1) {
|
||||
if (seconds >= 86400 / 2)
|
||||
days2 = days1;
|
||||
else
|
||||
days1 = days2;
|
||||
}
|
||||
|
||||
if (days1 != days2)
|
||||
return -EIO;
|
||||
|
||||
s1970 = days1 * 86400 + seconds;
|
||||
|
||||
rtc_time_to_tm(s1970, tm);
|
||||
|
||||
return rtc_valid_tm(tm);
|
||||
}
|
||||
|
||||
static int mc13xxx_rtc_set_mmss(struct device *dev, unsigned long secs)
|
||||
{
|
||||
struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
|
||||
unsigned int seconds, days;
|
||||
unsigned int alarmseconds;
|
||||
int ret;
|
||||
|
||||
seconds = secs % 86400;
|
||||
days = secs / 86400;
|
||||
|
||||
mc13xxx_lock(priv->mc13xxx);
|
||||
|
||||
/*
|
||||
* temporarily invalidate alarm to prevent triggering it when the day is
|
||||
* already updated while the time isn't yet.
|
||||
*/
|
||||
ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCTODA, &alarmseconds);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
if (alarmseconds < 86400) {
|
||||
ret = mc13xxx_reg_write(priv->mc13xxx,
|
||||
MC13XXX_RTCTODA, 0x1ffff);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* write seconds=0 to prevent a day switch between writing days
|
||||
* and seconds below
|
||||
*/
|
||||
ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCTOD, 0);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCDAY, days);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCTOD, seconds);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
/* restore alarm */
|
||||
if (alarmseconds < 86400) {
|
||||
ret = mc13xxx_reg_write(priv->mc13xxx,
|
||||
MC13XXX_RTCTODA, alarmseconds);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = mc13xxx_irq_ack(priv->mc13xxx, MC13XXX_IRQ_RTCRST);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
ret = mc13xxx_irq_unmask(priv->mc13xxx, MC13XXX_IRQ_RTCRST);
|
||||
out:
|
||||
priv->valid = !ret;
|
||||
|
||||
mc13xxx_unlock(priv->mc13xxx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mc13xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
{
|
||||
struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
|
||||
unsigned seconds, days;
|
||||
unsigned long s1970;
|
||||
int enabled, pending;
|
||||
int ret;
|
||||
|
||||
mc13xxx_lock(priv->mc13xxx);
|
||||
|
||||
ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCTODA, &seconds);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
if (seconds >= 86400) {
|
||||
ret = -ENODATA;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCDAY, &days);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
ret = mc13xxx_irq_status(priv->mc13xxx, MC13XXX_IRQ_TODA,
|
||||
&enabled, &pending);
|
||||
|
||||
out:
|
||||
mc13xxx_unlock(priv->mc13xxx);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
alarm->enabled = enabled;
|
||||
alarm->pending = pending;
|
||||
|
||||
s1970 = days * 86400 + seconds;
|
||||
|
||||
rtc_time_to_tm(s1970, &alarm->time);
|
||||
dev_dbg(dev, "%s: %lu\n", __func__, s1970);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mc13xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
{
|
||||
struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
|
||||
unsigned long s1970;
|
||||
unsigned seconds, days;
|
||||
int ret;
|
||||
|
||||
mc13xxx_lock(priv->mc13xxx);
|
||||
|
||||
/* disable alarm to prevent false triggering */
|
||||
ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCTODA, 0x1ffff);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
ret = mc13xxx_irq_ack(priv->mc13xxx, MC13XXX_IRQ_TODA);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
ret = rtc_tm_to_time(&alarm->time, &s1970);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
dev_dbg(dev, "%s: o%2.s %lu\n", __func__, alarm->enabled ? "n" : "ff",
|
||||
s1970);
|
||||
|
||||
ret = mc13xxx_rtc_irq_enable_unlocked(dev, alarm->enabled,
|
||||
MC13XXX_IRQ_TODA);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
seconds = s1970 % 86400;
|
||||
days = s1970 / 86400;
|
||||
|
||||
ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCDAYA, days);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCTODA, seconds);
|
||||
|
||||
out:
|
||||
mc13xxx_unlock(priv->mc13xxx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t mc13xxx_rtc_alarm_handler(int irq, void *dev)
|
||||
{
|
||||
struct mc13xxx_rtc *priv = dev;
|
||||
struct mc13xxx *mc13xxx = priv->mc13xxx;
|
||||
|
||||
dev_dbg(&priv->rtc->dev, "Alarm\n");
|
||||
|
||||
rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_AF);
|
||||
|
||||
mc13xxx_irq_ack(mc13xxx, irq);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t mc13xxx_rtc_update_handler(int irq, void *dev)
|
||||
{
|
||||
struct mc13xxx_rtc *priv = dev;
|
||||
struct mc13xxx *mc13xxx = priv->mc13xxx;
|
||||
|
||||
dev_dbg(&priv->rtc->dev, "1HZ\n");
|
||||
|
||||
rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_UF);
|
||||
|
||||
mc13xxx_irq_ack(mc13xxx, irq);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int mc13xxx_rtc_update_irq_enable(struct device *dev,
|
||||
unsigned int enabled)
|
||||
{
|
||||
return mc13xxx_rtc_irq_enable(dev, enabled, MC13XXX_IRQ_1HZ);
|
||||
}
|
||||
|
||||
static int mc13xxx_rtc_alarm_irq_enable(struct device *dev,
|
||||
unsigned int enabled)
|
||||
{
|
||||
return mc13xxx_rtc_irq_enable(dev, enabled, MC13XXX_IRQ_TODA);
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops mc13xxx_rtc_ops = {
|
||||
.read_time = mc13xxx_rtc_read_time,
|
||||
.set_mmss = mc13xxx_rtc_set_mmss,
|
||||
.read_alarm = mc13xxx_rtc_read_alarm,
|
||||
.set_alarm = mc13xxx_rtc_set_alarm,
|
||||
.alarm_irq_enable = mc13xxx_rtc_alarm_irq_enable,
|
||||
.update_irq_enable = mc13xxx_rtc_update_irq_enable,
|
||||
};
|
||||
|
||||
static irqreturn_t mc13xxx_rtc_reset_handler(int irq, void *dev)
|
||||
{
|
||||
struct mc13xxx_rtc *priv = dev;
|
||||
struct mc13xxx *mc13xxx = priv->mc13xxx;
|
||||
|
||||
dev_dbg(&priv->rtc->dev, "RTCRST\n");
|
||||
priv->valid = 0;
|
||||
|
||||
mc13xxx_irq_mask(mc13xxx, irq);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __devinit mc13xxx_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct mc13xxx_rtc *priv;
|
||||
struct mc13xxx *mc13xxx;
|
||||
int rtcrst_pending;
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
mc13xxx = dev_get_drvdata(pdev->dev.parent);
|
||||
priv->mc13xxx = mc13xxx;
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
mc13xxx_lock(mc13xxx);
|
||||
|
||||
ret = mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_RTCRST,
|
||||
mc13xxx_rtc_reset_handler, DRIVER_NAME, priv);
|
||||
if (ret)
|
||||
goto err_reset_irq_request;
|
||||
|
||||
ret = mc13xxx_irq_status(mc13xxx, MC13XXX_IRQ_RTCRST,
|
||||
NULL, &rtcrst_pending);
|
||||
if (ret)
|
||||
goto err_reset_irq_status;
|
||||
|
||||
priv->valid = !rtcrst_pending;
|
||||
|
||||
ret = mc13xxx_irq_request_nounmask(mc13xxx, MC13XXX_IRQ_1HZ,
|
||||
mc13xxx_rtc_update_handler, DRIVER_NAME, priv);
|
||||
if (ret)
|
||||
goto err_update_irq_request;
|
||||
|
||||
ret = mc13xxx_irq_request_nounmask(mc13xxx, MC13XXX_IRQ_TODA,
|
||||
mc13xxx_rtc_alarm_handler, DRIVER_NAME, priv);
|
||||
if (ret)
|
||||
goto err_alarm_irq_request;
|
||||
|
||||
priv->rtc = rtc_device_register(pdev->name,
|
||||
&pdev->dev, &mc13xxx_rtc_ops, THIS_MODULE);
|
||||
if (IS_ERR(priv->rtc)) {
|
||||
ret = PTR_ERR(priv->rtc);
|
||||
|
||||
mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_TODA, priv);
|
||||
err_alarm_irq_request:
|
||||
|
||||
mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_1HZ, priv);
|
||||
err_update_irq_request:
|
||||
|
||||
err_reset_irq_status:
|
||||
|
||||
mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_RTCRST, priv);
|
||||
err_reset_irq_request:
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
mc13xxx_unlock(mc13xxx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit mc13xxx_rtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mc13xxx_rtc *priv = platform_get_drvdata(pdev);
|
||||
|
||||
mc13xxx_lock(priv->mc13xxx);
|
||||
|
||||
rtc_device_unregister(priv->rtc);
|
||||
|
||||
mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_TODA, priv);
|
||||
mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_1HZ, priv);
|
||||
mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_RTCRST, priv);
|
||||
|
||||
mc13xxx_unlock(priv->mc13xxx);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
kfree(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct platform_device_id mc13xxx_rtc_idtable[] = {
|
||||
{
|
||||
.name = "mc13783-rtc",
|
||||
}, {
|
||||
.name = "mc13892-rtc",
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_driver mc13xxx_rtc_driver = {
|
||||
.id_table = mc13xxx_rtc_idtable,
|
||||
.remove = __devexit_p(mc13xxx_rtc_remove),
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init mc13xxx_rtc_init(void)
|
||||
{
|
||||
return platform_driver_probe(&mc13xxx_rtc_driver, &mc13xxx_rtc_probe);
|
||||
}
|
||||
module_init(mc13xxx_rtc_init);
|
||||
|
||||
static void __exit mc13xxx_rtc_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&mc13xxx_rtc_driver);
|
||||
}
|
||||
module_exit(mc13xxx_rtc_exit);
|
||||
|
||||
MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
|
||||
MODULE_DESCRIPTION("RTC driver for Freescale MC13XXX PMIC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRIVER_NAME);
|
@ -124,7 +124,6 @@
|
||||
#define PHY_DPLL_CLK (1 << 0)
|
||||
|
||||
/* In module TWL4030_MODULE_PM_MASTER */
|
||||
#define PROTECT_KEY 0x0E
|
||||
#define STS_HW_CONDITIONS 0x0F
|
||||
|
||||
/* In module TWL4030_MODULE_PM_RECEIVER */
|
||||
@ -418,8 +417,13 @@ static void twl4030_phy_resume(struct twl4030_usb *twl)
|
||||
static int twl4030_usb_ldo_init(struct twl4030_usb *twl)
|
||||
{
|
||||
/* Enable writing to power configuration registers */
|
||||
twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0xC0, PROTECT_KEY);
|
||||
twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0x0C, PROTECT_KEY);
|
||||
twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
|
||||
TWL4030_PM_MASTER_KEY_CFG1,
|
||||
TWL4030_PM_MASTER_PROTECT_KEY);
|
||||
|
||||
twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
|
||||
TWL4030_PM_MASTER_KEY_CFG2,
|
||||
TWL4030_PM_MASTER_PROTECT_KEY);
|
||||
|
||||
/* Keep VUSB3V1 LDO in sleep state until VBUS/ID change detected*/
|
||||
/*twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2);*/
|
||||
@ -455,7 +459,8 @@ static int twl4030_usb_ldo_init(struct twl4030_usb *twl)
|
||||
twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE);
|
||||
|
||||
/* disable access to power configuration registers */
|
||||
twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, PROTECT_KEY);
|
||||
twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0,
|
||||
TWL4030_PM_MASTER_PROTECT_KEY);
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -141,6 +141,16 @@
|
||||
#define TWL6030_CHARGER_CTRL_INT_MASK 0x10
|
||||
#define TWL6030_CHARGER_FAULT_INT_MASK 0x60
|
||||
|
||||
#define TWL6030_MMCCTRL 0xEE
|
||||
#define VMMC_AUTO_OFF (0x1 << 3)
|
||||
#define SW_FC (0x1 << 2)
|
||||
#define STS_MMC 0x1
|
||||
|
||||
#define TWL6030_CFG_INPUT_PUPD3 0xF2
|
||||
#define MMC_PU (0x1 << 3)
|
||||
#define MMC_PD (0x1 << 2)
|
||||
|
||||
|
||||
|
||||
#define TWL4030_CLASS_ID 0x4030
|
||||
#define TWL6030_CLASS_ID 0x6030
|
||||
@ -173,6 +183,27 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes);
|
||||
int twl6030_interrupt_unmask(u8 bit_mask, u8 offset);
|
||||
int twl6030_interrupt_mask(u8 bit_mask, u8 offset);
|
||||
|
||||
/* Card detect Configuration for MMC1 Controller on OMAP4 */
|
||||
#ifdef CONFIG_TWL4030_CORE
|
||||
int twl6030_mmc_card_detect_config(void);
|
||||
#else
|
||||
static inline int twl6030_mmc_card_detect_config(void)
|
||||
{
|
||||
pr_debug("twl6030_mmc_card_detect_config not supported\n");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* MMC1 Controller on OMAP4 uses Phoenix irq for Card detect */
|
||||
#ifdef CONFIG_TWL4030_CORE
|
||||
int twl6030_mmc_card_detect(struct device *dev, int slot);
|
||||
#else
|
||||
static inline int twl6030_mmc_card_detect(struct device *dev, int slot)
|
||||
{
|
||||
pr_debug("Call back twl6030_mmc_card_detect not supported\n");
|
||||
return -EIO;
|
||||
}
|
||||
#endif
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
@ -357,6 +388,52 @@ int twl6030_interrupt_mask(u8 bit_mask, u8 offset);
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* PM Master module register offsets (use TWL4030_MODULE_PM_MASTER)
|
||||
*/
|
||||
|
||||
#define TWL4030_PM_MASTER_CFG_P1_TRANSITION 0x00
|
||||
#define TWL4030_PM_MASTER_CFG_P2_TRANSITION 0x01
|
||||
#define TWL4030_PM_MASTER_CFG_P3_TRANSITION 0x02
|
||||
#define TWL4030_PM_MASTER_CFG_P123_TRANSITION 0x03
|
||||
#define TWL4030_PM_MASTER_STS_BOOT 0x04
|
||||
#define TWL4030_PM_MASTER_CFG_BOOT 0x05
|
||||
#define TWL4030_PM_MASTER_SHUNDAN 0x06
|
||||
#define TWL4030_PM_MASTER_BOOT_BCI 0x07
|
||||
#define TWL4030_PM_MASTER_CFG_PWRANA1 0x08
|
||||
#define TWL4030_PM_MASTER_CFG_PWRANA2 0x09
|
||||
#define TWL4030_PM_MASTER_BACKUP_MISC_STS 0x0b
|
||||
#define TWL4030_PM_MASTER_BACKUP_MISC_CFG 0x0c
|
||||
#define TWL4030_PM_MASTER_BACKUP_MISC_TST 0x0d
|
||||
#define TWL4030_PM_MASTER_PROTECT_KEY 0x0e
|
||||
#define TWL4030_PM_MASTER_STS_HW_CONDITIONS 0x0f
|
||||
#define TWL4030_PM_MASTER_P1_SW_EVENTS 0x10
|
||||
#define TWL4030_PM_MASTER_P2_SW_EVENTS 0x11
|
||||
#define TWL4030_PM_MASTER_P3_SW_EVENTS 0x12
|
||||
#define TWL4030_PM_MASTER_STS_P123_STATE 0x13
|
||||
#define TWL4030_PM_MASTER_PB_CFG 0x14
|
||||
#define TWL4030_PM_MASTER_PB_WORD_MSB 0x15
|
||||
#define TWL4030_PM_MASTER_PB_WORD_LSB 0x16
|
||||
#define TWL4030_PM_MASTER_SEQ_ADD_W2P 0x1c
|
||||
#define TWL4030_PM_MASTER_SEQ_ADD_P2A 0x1d
|
||||
#define TWL4030_PM_MASTER_SEQ_ADD_A2W 0x1e
|
||||
#define TWL4030_PM_MASTER_SEQ_ADD_A2S 0x1f
|
||||
#define TWL4030_PM_MASTER_SEQ_ADD_S2A12 0x20
|
||||
#define TWL4030_PM_MASTER_SEQ_ADD_S2A3 0x21
|
||||
#define TWL4030_PM_MASTER_SEQ_ADD_WARM 0x22
|
||||
#define TWL4030_PM_MASTER_MEMORY_ADDRESS 0x23
|
||||
#define TWL4030_PM_MASTER_MEMORY_DATA 0x24
|
||||
|
||||
#define TWL4030_PM_MASTER_KEY_CFG1 0xc0
|
||||
#define TWL4030_PM_MASTER_KEY_CFG2 0x0c
|
||||
|
||||
#define TWL4030_PM_MASTER_KEY_TST1 0xe0
|
||||
#define TWL4030_PM_MASTER_KEY_TST2 0x0e
|
||||
|
||||
#define TWL4030_PM_MASTER_GLOBAL_TST 0xb6
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
/* Power bus message definitions */
|
||||
|
||||
/* The TWL4030/5030 splits its power-management resources (the various
|
||||
|
@ -138,7 +138,7 @@ enum {
|
||||
PM8607_ID_RG_MAX,
|
||||
};
|
||||
|
||||
#define PM8607_VERSION (0x40) /* 8607 chip ID */
|
||||
/* 8607 chip ID is 0x40 or 0x50 */
|
||||
#define PM8607_VERSION_MASK (0xF0) /* 8607 chip ID mask */
|
||||
|
||||
/* Interrupt Registers */
|
||||
|
@ -9,6 +9,29 @@
|
||||
|
||||
#include <linux/device.h>
|
||||
|
||||
/*
|
||||
* AB8500 bank addresses
|
||||
*/
|
||||
#define AB8500_SYS_CTRL1_BLOCK 0x1
|
||||
#define AB8500_SYS_CTRL2_BLOCK 0x2
|
||||
#define AB8500_REGU_CTRL1 0x3
|
||||
#define AB8500_REGU_CTRL2 0x4
|
||||
#define AB8500_USB 0x5
|
||||
#define AB8500_TVOUT 0x6
|
||||
#define AB8500_DBI 0x7
|
||||
#define AB8500_ECI_AV_ACC 0x8
|
||||
#define AB8500_RESERVED 0x9
|
||||
#define AB8500_GPADC 0xA
|
||||
#define AB8500_CHARGER 0xB
|
||||
#define AB8500_GAS_GAUGE 0xC
|
||||
#define AB8500_AUDIO 0xD
|
||||
#define AB8500_INTERRUPT 0xE
|
||||
#define AB8500_RTC 0xF
|
||||
#define AB8500_MISC 0x10
|
||||
#define AB8500_DEBUG 0x12
|
||||
#define AB8500_PROD_TEST 0x13
|
||||
#define AB8500_OTP_EMUL 0x15
|
||||
|
||||
/*
|
||||
* Interrupts
|
||||
*/
|
||||
@ -99,6 +122,7 @@ struct ab8500 {
|
||||
int revision;
|
||||
int irq_base;
|
||||
int irq;
|
||||
u8 chip_id;
|
||||
|
||||
int (*write) (struct ab8500 *a8500, u16 addr, u8 data);
|
||||
int (*read) (struct ab8500 *a8500, u16 addr);
|
||||
@ -124,10 +148,6 @@ struct ab8500_platform_data {
|
||||
struct regulator_init_data *regulator[AB8500_NUM_REGULATORS];
|
||||
};
|
||||
|
||||
extern int ab8500_write(struct ab8500 *a8500, u16 addr, u8 data);
|
||||
extern int ab8500_read(struct ab8500 *a8500, u16 addr);
|
||||
extern int ab8500_set_bits(struct ab8500 *a8500, u16 addr, u8 mask, u8 data);
|
||||
|
||||
extern int __devinit ab8500_init(struct ab8500 *ab8500);
|
||||
extern int __devexit ab8500_exit(struct ab8500 *ab8500);
|
||||
|
||||
|
@ -6,8 +6,7 @@
|
||||
*
|
||||
* ABX500 core access functions.
|
||||
* The abx500 interface is used for the Analog Baseband chip
|
||||
* ab3100, ab3550, ab5500 and possibly comming. It is not used for
|
||||
* ab4500 and ab8500 since they are another family of chip.
|
||||
* ab3100, ab3550, ab5500, and ab8500.
|
||||
*
|
||||
* Author: Mattias Wallin <mattias.wallin@stericsson.com>
|
||||
* Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com>
|
||||
@ -230,4 +229,5 @@ struct abx500_ops {
|
||||
};
|
||||
|
||||
int abx500_register_ops(struct device *core_dev, struct abx500_ops *ops);
|
||||
void abx500_remove_ops(struct device *dev);
|
||||
#endif
|
||||
|
@ -44,6 +44,9 @@ struct mfd_cell {
|
||||
*/
|
||||
int num_resources;
|
||||
const struct resource *resources;
|
||||
|
||||
/* don't check for resource conflicts */
|
||||
bool ignore_resource_conflicts;
|
||||
};
|
||||
|
||||
extern int mfd_add_devices(struct device *parent, int id,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* max8698.h - Voltage regulator driver for the Maxim 8998
|
||||
* max8998.h - Voltage regulator driver for the Maxim 8998
|
||||
*
|
||||
* Copyright (C) 2009-2010 Samsung Electrnoics
|
||||
* Kyungmin Park <kyungmin.park@samsung.com>
|
||||
@ -23,6 +23,8 @@
|
||||
#ifndef __LINUX_MFD_MAX8998_PRIV_H
|
||||
#define __LINUX_MFD_MAX8998_PRIV_H
|
||||
|
||||
#define MAX8998_NUM_IRQ_REGS 4
|
||||
|
||||
/* MAX 8998 registers */
|
||||
enum {
|
||||
MAX8998_REG_IRQ1,
|
||||
@ -46,12 +48,12 @@ enum {
|
||||
MAX8998_REG_ONOFF2,
|
||||
MAX8998_REG_ONOFF3,
|
||||
MAX8998_REG_ONOFF4,
|
||||
MAX8998_REG_BUCK1_DVSARM1,
|
||||
MAX8998_REG_BUCK1_DVSARM2,
|
||||
MAX8998_REG_BUCK1_DVSARM3,
|
||||
MAX8998_REG_BUCK1_DVSARM4,
|
||||
MAX8998_REG_BUCK2_DVSINT1,
|
||||
MAX8998_REG_BUCK2_DVSINT2,
|
||||
MAX8998_REG_BUCK1_VOLTAGE1,
|
||||
MAX8998_REG_BUCK1_VOLTAGE2,
|
||||
MAX8998_REG_BUCK1_VOLTAGE3,
|
||||
MAX8998_REG_BUCK1_VOLTAGE4,
|
||||
MAX8998_REG_BUCK2_VOLTAGE1,
|
||||
MAX8998_REG_BUCK2_VOLTAGE2,
|
||||
MAX8998_REG_BUCK3,
|
||||
MAX8998_REG_BUCK4,
|
||||
MAX8998_REG_LDO2_LDO3,
|
||||
@ -72,41 +74,102 @@ enum {
|
||||
MAX8998_REG_LBCNFG2,
|
||||
};
|
||||
|
||||
/* IRQ definitions */
|
||||
enum {
|
||||
MAX8998_IRQ_DCINF,
|
||||
MAX8998_IRQ_DCINR,
|
||||
MAX8998_IRQ_JIGF,
|
||||
MAX8998_IRQ_JIGR,
|
||||
MAX8998_IRQ_PWRONF,
|
||||
MAX8998_IRQ_PWRONR,
|
||||
|
||||
MAX8998_IRQ_WTSREVNT,
|
||||
MAX8998_IRQ_SMPLEVNT,
|
||||
MAX8998_IRQ_ALARM1,
|
||||
MAX8998_IRQ_ALARM0,
|
||||
|
||||
MAX8998_IRQ_ONKEY1S,
|
||||
MAX8998_IRQ_TOPOFFR,
|
||||
MAX8998_IRQ_DCINOVPR,
|
||||
MAX8998_IRQ_CHGRSTF,
|
||||
MAX8998_IRQ_DONER,
|
||||
MAX8998_IRQ_CHGFAULT,
|
||||
|
||||
MAX8998_IRQ_LOBAT1,
|
||||
MAX8998_IRQ_LOBAT2,
|
||||
|
||||
MAX8998_IRQ_NR,
|
||||
};
|
||||
|
||||
/* MAX8998 various variants */
|
||||
enum {
|
||||
TYPE_MAX8998 = 0, /* Default */
|
||||
TYPE_LP3974, /* National version of MAX8998 */
|
||||
TYPE_LP3979, /* Added AVS */
|
||||
};
|
||||
|
||||
#define MAX8998_IRQ_DCINF_MASK (1 << 2)
|
||||
#define MAX8998_IRQ_DCINR_MASK (1 << 3)
|
||||
#define MAX8998_IRQ_JIGF_MASK (1 << 4)
|
||||
#define MAX8998_IRQ_JIGR_MASK (1 << 5)
|
||||
#define MAX8998_IRQ_PWRONF_MASK (1 << 6)
|
||||
#define MAX8998_IRQ_PWRONR_MASK (1 << 7)
|
||||
|
||||
#define MAX8998_IRQ_WTSREVNT_MASK (1 << 0)
|
||||
#define MAX8998_IRQ_SMPLEVNT_MASK (1 << 1)
|
||||
#define MAX8998_IRQ_ALARM1_MASK (1 << 2)
|
||||
#define MAX8998_IRQ_ALARM0_MASK (1 << 3)
|
||||
|
||||
#define MAX8998_IRQ_ONKEY1S_MASK (1 << 0)
|
||||
#define MAX8998_IRQ_TOPOFFR_MASK (1 << 2)
|
||||
#define MAX8998_IRQ_DCINOVPR_MASK (1 << 3)
|
||||
#define MAX8998_IRQ_CHGRSTF_MASK (1 << 4)
|
||||
#define MAX8998_IRQ_DONER_MASK (1 << 5)
|
||||
#define MAX8998_IRQ_CHGFAULT_MASK (1 << 7)
|
||||
|
||||
#define MAX8998_IRQ_LOBAT1_MASK (1 << 0)
|
||||
#define MAX8998_IRQ_LOBAT2_MASK (1 << 1)
|
||||
|
||||
#define MAX8998_ENRAMP (1 << 4)
|
||||
|
||||
/**
|
||||
* struct max8998_dev - max8998 master device for sub-drivers
|
||||
* @dev: master device of the chip (can be used to access platform data)
|
||||
* @i2c_client: i2c client private data
|
||||
* @dev_read(): chip register read function
|
||||
* @dev_write(): chip register write function
|
||||
* @dev_update(): chip register update function
|
||||
* @i2c: i2c client private data for regulator
|
||||
* @rtc: i2c client private data for rtc
|
||||
* @iolock: mutex for serializing io access
|
||||
* @irqlock: mutex for buslock
|
||||
* @irq_base: base IRQ number for max8998, required for IRQs
|
||||
* @irq: generic IRQ number for max8998
|
||||
* @ono: power onoff IRQ number for max8998
|
||||
* @irq_masks_cur: currently active value
|
||||
* @irq_masks_cache: cached hardware value
|
||||
* @type: indicate which max8998 "variant" is used
|
||||
*/
|
||||
|
||||
struct max8998_dev {
|
||||
struct device *dev;
|
||||
struct i2c_client *i2c_client;
|
||||
int (*dev_read)(struct max8998_dev *max8998, u8 reg, u8 *dest);
|
||||
int (*dev_write)(struct max8998_dev *max8998, u8 reg, u8 val);
|
||||
int (*dev_update)(struct max8998_dev *max8998, u8 reg, u8 val, u8 mask);
|
||||
struct i2c_client *i2c;
|
||||
struct i2c_client *rtc;
|
||||
struct mutex iolock;
|
||||
struct mutex irqlock;
|
||||
|
||||
int irq_base;
|
||||
int irq;
|
||||
int ono;
|
||||
u8 irq_masks_cur[MAX8998_NUM_IRQ_REGS];
|
||||
u8 irq_masks_cache[MAX8998_NUM_IRQ_REGS];
|
||||
int type;
|
||||
};
|
||||
|
||||
static inline int max8998_read_reg(struct max8998_dev *max8998, u8 reg,
|
||||
u8 *value)
|
||||
{
|
||||
return max8998->dev_read(max8998, reg, value);
|
||||
}
|
||||
int max8998_irq_init(struct max8998_dev *max8998);
|
||||
void max8998_irq_exit(struct max8998_dev *max8998);
|
||||
|
||||
static inline int max8998_write_reg(struct max8998_dev *max8998, u8 reg,
|
||||
u8 value)
|
||||
{
|
||||
return max8998->dev_write(max8998, reg, value);
|
||||
}
|
||||
|
||||
static inline int max8998_update_reg(struct max8998_dev *max8998, u8 reg,
|
||||
u8 value, u8 mask)
|
||||
{
|
||||
return max8998->dev_update(max8998, reg, value, mask);
|
||||
}
|
||||
extern int max8998_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest);
|
||||
extern int max8998_bulk_read(struct i2c_client *i2c, u8 reg, int count,
|
||||
u8 *buf);
|
||||
extern int max8998_write_reg(struct i2c_client *i2c, u8 reg, u8 value);
|
||||
extern int max8998_bulk_write(struct i2c_client *i2c, u8 reg, int count,
|
||||
u8 *buf);
|
||||
extern int max8998_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask);
|
||||
|
||||
#endif /* __LINUX_MFD_MAX8998_PRIV_H */
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* max8698.h - Voltage regulator driver for the Maxim 8998
|
||||
* max8998.h - Voltage regulator driver for the Maxim 8998
|
||||
*
|
||||
* Copyright (C) 2009-2010 Samsung Electrnoics
|
||||
* Kyungmin Park <kyungmin.park@samsung.com>
|
||||
@ -66,13 +66,28 @@ struct max8998_regulator_data {
|
||||
|
||||
/**
|
||||
* struct max8998_board - packages regulator init data
|
||||
* @num_regulators: number of regultors used
|
||||
* @regulators: array of defined regulators
|
||||
* @num_regulators: number of regultors used
|
||||
* @irq_base: base IRQ number for max8998, required for IRQs
|
||||
* @ono: power onoff IRQ number for max8998
|
||||
* @buck1_max_voltage1: BUCK1 maximum alowed voltage register 1
|
||||
* @buck1_max_voltage2: BUCK1 maximum alowed voltage register 2
|
||||
* @buck2_max_voltage: BUCK2 maximum alowed voltage
|
||||
* @buck1_set1: BUCK1 gpio pin 1 to set output voltage
|
||||
* @buck1_set2: BUCK1 gpio pin 2 to set output voltage
|
||||
* @buck2_set3: BUCK2 gpio pin to set output voltage
|
||||
*/
|
||||
|
||||
struct max8998_platform_data {
|
||||
int num_regulators;
|
||||
struct max8998_regulator_data *regulators;
|
||||
int num_regulators;
|
||||
int irq_base;
|
||||
int ono;
|
||||
int buck1_max_voltage1;
|
||||
int buck1_max_voltage2;
|
||||
int buck2_max_voltage;
|
||||
int buck1_set1;
|
||||
int buck1_set2;
|
||||
int buck2_set3;
|
||||
};
|
||||
|
||||
#endif /* __LINUX_MFD_MAX8998_H */
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2009 Pengutronix
|
||||
* Copyright 2009-2010 Pengutronix
|
||||
* Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
@ -9,48 +9,83 @@
|
||||
#ifndef __LINUX_MFD_MC13783_H
|
||||
#define __LINUX_MFD_MC13783_H
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/mc13xxx.h>
|
||||
|
||||
struct mc13783;
|
||||
|
||||
void mc13783_lock(struct mc13783 *mc13783);
|
||||
void mc13783_unlock(struct mc13783 *mc13783);
|
||||
struct mc13xxx *mc13783_to_mc13xxx(struct mc13783 *mc13783);
|
||||
|
||||
int mc13783_reg_read(struct mc13783 *mc13783, unsigned int offset, u32 *val);
|
||||
int mc13783_reg_write(struct mc13783 *mc13783, unsigned int offset, u32 val);
|
||||
int mc13783_reg_rmw(struct mc13783 *mc13783, unsigned int offset,
|
||||
u32 mask, u32 val);
|
||||
|
||||
int mc13783_get_flags(struct mc13783 *mc13783);
|
||||
|
||||
int mc13783_irq_request(struct mc13783 *mc13783, int irq,
|
||||
irq_handler_t handler, const char *name, void *dev);
|
||||
int mc13783_irq_request_nounmask(struct mc13783 *mc13783, int irq,
|
||||
irq_handler_t handler, const char *name, void *dev);
|
||||
int mc13783_irq_free(struct mc13783 *mc13783, int irq, void *dev);
|
||||
|
||||
int mc13783_irq_mask(struct mc13783 *mc13783, int irq);
|
||||
int mc13783_irq_unmask(struct mc13783 *mc13783, int irq);
|
||||
int mc13783_irq_status(struct mc13783 *mc13783, int irq,
|
||||
int *enabled, int *pending);
|
||||
int mc13783_irq_ack(struct mc13783 *mc13783, int irq);
|
||||
|
||||
static inline int mc13783_mask(struct mc13783 *mc13783, int irq) __deprecated;
|
||||
static inline int mc13783_mask(struct mc13783 *mc13783, int irq)
|
||||
static inline void mc13783_lock(struct mc13783 *mc13783)
|
||||
{
|
||||
return mc13783_irq_mask(mc13783, irq);
|
||||
mc13xxx_lock(mc13783_to_mc13xxx(mc13783));
|
||||
}
|
||||
|
||||
static inline int mc13783_unmask(struct mc13783 *mc13783, int irq) __deprecated;
|
||||
static inline int mc13783_unmask(struct mc13783 *mc13783, int irq)
|
||||
static inline void mc13783_unlock(struct mc13783 *mc13783)
|
||||
{
|
||||
return mc13783_irq_unmask(mc13783, irq);
|
||||
mc13xxx_unlock(mc13783_to_mc13xxx(mc13783));
|
||||
}
|
||||
|
||||
static inline int mc13783_ackirq(struct mc13783 *mc13783, int irq) __deprecated;
|
||||
static inline int mc13783_ackirq(struct mc13783 *mc13783, int irq)
|
||||
static inline int mc13783_reg_read(struct mc13783 *mc13783,
|
||||
unsigned int offset, u32 *val)
|
||||
{
|
||||
return mc13783_irq_ack(mc13783, irq);
|
||||
return mc13xxx_reg_read(mc13783_to_mc13xxx(mc13783), offset, val);
|
||||
}
|
||||
|
||||
static inline int mc13783_reg_write(struct mc13783 *mc13783,
|
||||
unsigned int offset, u32 val)
|
||||
{
|
||||
return mc13xxx_reg_write(mc13783_to_mc13xxx(mc13783), offset, val);
|
||||
}
|
||||
|
||||
static inline int mc13783_reg_rmw(struct mc13783 *mc13783,
|
||||
unsigned int offset, u32 mask, u32 val)
|
||||
{
|
||||
return mc13xxx_reg_rmw(mc13783_to_mc13xxx(mc13783), offset, mask, val);
|
||||
}
|
||||
|
||||
static inline int mc13783_get_flags(struct mc13783 *mc13783)
|
||||
{
|
||||
return mc13xxx_get_flags(mc13783_to_mc13xxx(mc13783));
|
||||
}
|
||||
|
||||
static inline int mc13783_irq_request(struct mc13783 *mc13783, int irq,
|
||||
irq_handler_t handler, const char *name, void *dev)
|
||||
{
|
||||
return mc13xxx_irq_request(mc13783_to_mc13xxx(mc13783), irq,
|
||||
handler, name, dev);
|
||||
}
|
||||
|
||||
static inline int mc13783_irq_request_nounmask(struct mc13783 *mc13783, int irq,
|
||||
irq_handler_t handler, const char *name, void *dev)
|
||||
{
|
||||
return mc13xxx_irq_request_nounmask(mc13783_to_mc13xxx(mc13783), irq,
|
||||
handler, name, dev);
|
||||
}
|
||||
|
||||
static inline int mc13783_irq_free(struct mc13783 *mc13783, int irq, void *dev)
|
||||
{
|
||||
return mc13xxx_irq_free(mc13783_to_mc13xxx(mc13783), irq, dev);
|
||||
}
|
||||
|
||||
static inline int mc13783_irq_mask(struct mc13783 *mc13783, int irq)
|
||||
{
|
||||
return mc13xxx_irq_mask(mc13783_to_mc13xxx(mc13783), irq);
|
||||
}
|
||||
|
||||
static inline int mc13783_irq_unmask(struct mc13783 *mc13783, int irq)
|
||||
{
|
||||
return mc13xxx_irq_unmask(mc13783_to_mc13xxx(mc13783), irq);
|
||||
}
|
||||
static inline int mc13783_irq_status(struct mc13783 *mc13783, int irq,
|
||||
int *enabled, int *pending)
|
||||
{
|
||||
return mc13xxx_irq_status(mc13783_to_mc13xxx(mc13783),
|
||||
irq, enabled, pending);
|
||||
}
|
||||
|
||||
static inline int mc13783_irq_ack(struct mc13783 *mc13783, int irq)
|
||||
{
|
||||
return mc13xxx_irq_ack(mc13783_to_mc13xxx(mc13783), irq);
|
||||
}
|
||||
|
||||
#define MC13783_ADC0 43
|
||||
@ -66,96 +101,18 @@ static inline int mc13783_ackirq(struct mc13783 *mc13783, int irq)
|
||||
MC13783_ADC0_TSMOD1 | \
|
||||
MC13783_ADC0_TSMOD2)
|
||||
|
||||
struct mc13783_led_platform_data {
|
||||
#define MC13783_LED_MD 0
|
||||
#define MC13783_LED_AD 1
|
||||
#define MC13783_LED_KP 2
|
||||
#define MC13783_LED_R1 3
|
||||
#define MC13783_LED_G1 4
|
||||
#define MC13783_LED_B1 5
|
||||
#define MC13783_LED_R2 6
|
||||
#define MC13783_LED_G2 7
|
||||
#define MC13783_LED_B2 8
|
||||
#define MC13783_LED_R3 9
|
||||
#define MC13783_LED_G3 10
|
||||
#define MC13783_LED_B3 11
|
||||
#define MC13783_LED_MAX MC13783_LED_B3
|
||||
int id;
|
||||
const char *name;
|
||||
const char *default_trigger;
|
||||
#define mc13783_regulator_init_data mc13xxx_regulator_init_data
|
||||
#define mc13783_regulator_platform_data mc13xxx_regulator_platform_data
|
||||
#define mc13783_led_platform_data mc13xxx_led_platform_data
|
||||
#define mc13783_leds_platform_data mc13xxx_leds_platform_data
|
||||
|
||||
/* Three or two bits current selection depending on the led */
|
||||
char max_current;
|
||||
};
|
||||
|
||||
struct mc13783_leds_platform_data {
|
||||
int num_leds;
|
||||
struct mc13783_led_platform_data *led;
|
||||
|
||||
#define MC13783_LED_TRIODE_MD (1 << 0)
|
||||
#define MC13783_LED_TRIODE_AD (1 << 1)
|
||||
#define MC13783_LED_TRIODE_KP (1 << 2)
|
||||
#define MC13783_LED_BOOST_EN (1 << 3)
|
||||
#define MC13783_LED_TC1HALF (1 << 4)
|
||||
#define MC13783_LED_SLEWLIMTC (1 << 5)
|
||||
#define MC13783_LED_SLEWLIMBL (1 << 6)
|
||||
#define MC13783_LED_TRIODE_TC1 (1 << 7)
|
||||
#define MC13783_LED_TRIODE_TC2 (1 << 8)
|
||||
#define MC13783_LED_TRIODE_TC3 (1 << 9)
|
||||
int flags;
|
||||
|
||||
#define MC13783_LED_AB_DISABLED 0
|
||||
#define MC13783_LED_AB_MD1 1
|
||||
#define MC13783_LED_AB_MD12 2
|
||||
#define MC13783_LED_AB_MD123 3
|
||||
#define MC13783_LED_AB_MD1234 4
|
||||
#define MC13783_LED_AB_MD1234_AD1 5
|
||||
#define MC13783_LED_AB_MD1234_AD12 6
|
||||
#define MC13783_LED_AB_MD1_AD 7
|
||||
char abmode;
|
||||
|
||||
#define MC13783_LED_ABREF_200MV 0
|
||||
#define MC13783_LED_ABREF_400MV 1
|
||||
#define MC13783_LED_ABREF_600MV 2
|
||||
#define MC13783_LED_ABREF_800MV 3
|
||||
char abref;
|
||||
|
||||
#define MC13783_LED_PERIOD_10MS 0
|
||||
#define MC13783_LED_PERIOD_100MS 1
|
||||
#define MC13783_LED_PERIOD_500MS 2
|
||||
#define MC13783_LED_PERIOD_2S 3
|
||||
char bl_period;
|
||||
char tc1_period;
|
||||
char tc2_period;
|
||||
char tc3_period;
|
||||
};
|
||||
|
||||
/* to be cleaned up */
|
||||
struct regulator_init_data;
|
||||
|
||||
struct mc13783_regulator_init_data {
|
||||
int id;
|
||||
struct regulator_init_data *init_data;
|
||||
};
|
||||
|
||||
struct mc13783_regulator_platform_data {
|
||||
int num_regulators;
|
||||
struct mc13783_regulator_init_data *regulators;
|
||||
};
|
||||
|
||||
struct mc13783_platform_data {
|
||||
int num_regulators;
|
||||
struct mc13783_regulator_init_data *regulators;
|
||||
struct mc13783_leds_platform_data *leds;
|
||||
|
||||
#define MC13783_USE_TOUCHSCREEN (1 << 0)
|
||||
#define MC13783_USE_CODEC (1 << 1)
|
||||
#define MC13783_USE_ADC (1 << 2)
|
||||
#define MC13783_USE_RTC (1 << 3)
|
||||
#define MC13783_USE_REGULATOR (1 << 4)
|
||||
#define MC13783_USE_LED (1 << 5)
|
||||
unsigned int flags;
|
||||
};
|
||||
#define mc13783_platform_data mc13xxx_platform_data
|
||||
#define MC13783_USE_TOUCHSCREEN MC13XXX_USE_TOUCHSCREEN
|
||||
#define MC13783_USE_CODEC MC13XXX_USE_CODEC
|
||||
#define MC13783_USE_ADC MC13XXX_USE_ADC
|
||||
#define MC13783_USE_RTC MC13XXX_USE_RTC
|
||||
#define MC13783_USE_REGULATOR MC13XXX_USE_REGULATOR
|
||||
#define MC13783_USE_LED MC13XXX_USE_LED
|
||||
|
||||
#define MC13783_ADC_MODE_TS 1
|
||||
#define MC13783_ADC_MODE_SINGLE_CHAN 2
|
||||
@ -199,46 +156,46 @@ int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
|
||||
#define MC13783_REGU_PWGT1SPI 31
|
||||
#define MC13783_REGU_PWGT2SPI 32
|
||||
|
||||
#define MC13783_IRQ_ADCDONE 0
|
||||
#define MC13783_IRQ_ADCBISDONE 1
|
||||
#define MC13783_IRQ_TS 2
|
||||
#define MC13783_IRQ_ADCDONE MC13XXX_IRQ_ADCDONE
|
||||
#define MC13783_IRQ_ADCBISDONE MC13XXX_IRQ_ADCBISDONE
|
||||
#define MC13783_IRQ_TS MC13XXX_IRQ_TS
|
||||
#define MC13783_IRQ_WHIGH 3
|
||||
#define MC13783_IRQ_WLOW 4
|
||||
#define MC13783_IRQ_CHGDET 6
|
||||
#define MC13783_IRQ_CHGDET MC13XXX_IRQ_CHGDET
|
||||
#define MC13783_IRQ_CHGOV 7
|
||||
#define MC13783_IRQ_CHGREV 8
|
||||
#define MC13783_IRQ_CHGSHORT 9
|
||||
#define MC13783_IRQ_CCCV 10
|
||||
#define MC13783_IRQ_CHGCURR 11
|
||||
#define MC13783_IRQ_BPON 12
|
||||
#define MC13783_IRQ_LOBATL 13
|
||||
#define MC13783_IRQ_LOBATH 14
|
||||
#define MC13783_IRQ_CHGREV MC13XXX_IRQ_CHGREV
|
||||
#define MC13783_IRQ_CHGSHORT MC13XXX_IRQ_CHGSHORT
|
||||
#define MC13783_IRQ_CCCV MC13XXX_IRQ_CCCV
|
||||
#define MC13783_IRQ_CHGCURR MC13XXX_IRQ_CHGCURR
|
||||
#define MC13783_IRQ_BPON MC13XXX_IRQ_BPON
|
||||
#define MC13783_IRQ_LOBATL MC13XXX_IRQ_LOBATL
|
||||
#define MC13783_IRQ_LOBATH MC13XXX_IRQ_LOBATH
|
||||
#define MC13783_IRQ_UDP 15
|
||||
#define MC13783_IRQ_USB 16
|
||||
#define MC13783_IRQ_ID 19
|
||||
#define MC13783_IRQ_SE1 21
|
||||
#define MC13783_IRQ_CKDET 22
|
||||
#define MC13783_IRQ_UDM 23
|
||||
#define MC13783_IRQ_1HZ 24
|
||||
#define MC13783_IRQ_TODA 25
|
||||
#define MC13783_IRQ_1HZ MC13XXX_IRQ_1HZ
|
||||
#define MC13783_IRQ_TODA MC13XXX_IRQ_TODA
|
||||
#define MC13783_IRQ_ONOFD1 27
|
||||
#define MC13783_IRQ_ONOFD2 28
|
||||
#define MC13783_IRQ_ONOFD3 29
|
||||
#define MC13783_IRQ_SYSRST 30
|
||||
#define MC13783_IRQ_RTCRST 31
|
||||
#define MC13783_IRQ_PC 32
|
||||
#define MC13783_IRQ_WARM 33
|
||||
#define MC13783_IRQ_MEMHLD 34
|
||||
#define MC13783_IRQ_SYSRST MC13XXX_IRQ_SYSRST
|
||||
#define MC13783_IRQ_RTCRST MC13XXX_IRQ_RTCRST
|
||||
#define MC13783_IRQ_PC MC13XXX_IRQ_PC
|
||||
#define MC13783_IRQ_WARM MC13XXX_IRQ_WARM
|
||||
#define MC13783_IRQ_MEMHLD MC13XXX_IRQ_MEMHLD
|
||||
#define MC13783_IRQ_PWRRDY 35
|
||||
#define MC13783_IRQ_THWARNL 36
|
||||
#define MC13783_IRQ_THWARNH 37
|
||||
#define MC13783_IRQ_CLK 38
|
||||
#define MC13783_IRQ_THWARNL MC13XXX_IRQ_THWARNL
|
||||
#define MC13783_IRQ_THWARNH MC13XXX_IRQ_THWARNH
|
||||
#define MC13783_IRQ_CLK MC13XXX_IRQ_CLK
|
||||
#define MC13783_IRQ_SEMAF 39
|
||||
#define MC13783_IRQ_MC2B 41
|
||||
#define MC13783_IRQ_HSDET 42
|
||||
#define MC13783_IRQ_HSL 43
|
||||
#define MC13783_IRQ_ALSPTH 44
|
||||
#define MC13783_IRQ_AHSSHORT 45
|
||||
#define MC13783_NUM_IRQ 46
|
||||
#define MC13783_NUM_IRQ MC13XXX_NUM_IRQ
|
||||
|
||||
#endif /* __LINUX_MFD_MC13783_H */
|
||||
#endif /* ifndef __LINUX_MFD_MC13783_H */
|
||||
|
154
include/linux/mfd/mc13xxx.h
Normal file
154
include/linux/mfd/mc13xxx.h
Normal file
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright 2009-2010 Pengutronix
|
||||
* Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License version 2 as published by the
|
||||
* Free Software Foundation.
|
||||
*/
|
||||
#ifndef __LINUX_MFD_MC13XXX_H
|
||||
#define __LINUX_MFD_MC13XXX_H
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
struct mc13xxx;
|
||||
|
||||
void mc13xxx_lock(struct mc13xxx *mc13xxx);
|
||||
void mc13xxx_unlock(struct mc13xxx *mc13xxx);
|
||||
|
||||
int mc13xxx_reg_read(struct mc13xxx *mc13xxx, unsigned int offset, u32 *val);
|
||||
int mc13xxx_reg_write(struct mc13xxx *mc13xxx, unsigned int offset, u32 val);
|
||||
int mc13xxx_reg_rmw(struct mc13xxx *mc13xxx, unsigned int offset,
|
||||
u32 mask, u32 val);
|
||||
|
||||
int mc13xxx_get_flags(struct mc13xxx *mc13xxx);
|
||||
|
||||
int mc13xxx_irq_request(struct mc13xxx *mc13xxx, int irq,
|
||||
irq_handler_t handler, const char *name, void *dev);
|
||||
int mc13xxx_irq_request_nounmask(struct mc13xxx *mc13xxx, int irq,
|
||||
irq_handler_t handler, const char *name, void *dev);
|
||||
int mc13xxx_irq_free(struct mc13xxx *mc13xxx, int irq, void *dev);
|
||||
|
||||
int mc13xxx_irq_mask(struct mc13xxx *mc13xxx, int irq);
|
||||
int mc13xxx_irq_unmask(struct mc13xxx *mc13xxx, int irq);
|
||||
int mc13xxx_irq_status(struct mc13xxx *mc13xxx, int irq,
|
||||
int *enabled, int *pending);
|
||||
int mc13xxx_irq_ack(struct mc13xxx *mc13xxx, int irq);
|
||||
|
||||
int mc13xxx_get_flags(struct mc13xxx *mc13xxx);
|
||||
|
||||
#define MC13XXX_IRQ_ADCDONE 0
|
||||
#define MC13XXX_IRQ_ADCBISDONE 1
|
||||
#define MC13XXX_IRQ_TS 2
|
||||
#define MC13XXX_IRQ_CHGDET 6
|
||||
#define MC13XXX_IRQ_CHGREV 8
|
||||
#define MC13XXX_IRQ_CHGSHORT 9
|
||||
#define MC13XXX_IRQ_CCCV 10
|
||||
#define MC13XXX_IRQ_CHGCURR 11
|
||||
#define MC13XXX_IRQ_BPON 12
|
||||
#define MC13XXX_IRQ_LOBATL 13
|
||||
#define MC13XXX_IRQ_LOBATH 14
|
||||
#define MC13XXX_IRQ_1HZ 24
|
||||
#define MC13XXX_IRQ_TODA 25
|
||||
#define MC13XXX_IRQ_SYSRST 30
|
||||
#define MC13XXX_IRQ_RTCRST 31
|
||||
#define MC13XXX_IRQ_PC 32
|
||||
#define MC13XXX_IRQ_WARM 33
|
||||
#define MC13XXX_IRQ_MEMHLD 34
|
||||
#define MC13XXX_IRQ_THWARNL 36
|
||||
#define MC13XXX_IRQ_THWARNH 37
|
||||
#define MC13XXX_IRQ_CLK 38
|
||||
|
||||
#define MC13XXX_NUM_IRQ 46
|
||||
|
||||
struct regulator_init_data;
|
||||
|
||||
struct mc13xxx_regulator_init_data {
|
||||
int id;
|
||||
struct regulator_init_data *init_data;
|
||||
};
|
||||
|
||||
struct mc13xxx_regulator_platform_data {
|
||||
int num_regulators;
|
||||
struct mc13xxx_regulator_init_data *regulators;
|
||||
};
|
||||
|
||||
struct mc13xxx_led_platform_data {
|
||||
#define MC13783_LED_MD 0
|
||||
#define MC13783_LED_AD 1
|
||||
#define MC13783_LED_KP 2
|
||||
#define MC13783_LED_R1 3
|
||||
#define MC13783_LED_G1 4
|
||||
#define MC13783_LED_B1 5
|
||||
#define MC13783_LED_R2 6
|
||||
#define MC13783_LED_G2 7
|
||||
#define MC13783_LED_B2 8
|
||||
#define MC13783_LED_R3 9
|
||||
#define MC13783_LED_G3 10
|
||||
#define MC13783_LED_B3 11
|
||||
#define MC13783_LED_MAX MC13783_LED_B3
|
||||
int id;
|
||||
const char *name;
|
||||
const char *default_trigger;
|
||||
|
||||
/* Three or two bits current selection depending on the led */
|
||||
char max_current;
|
||||
};
|
||||
|
||||
struct mc13xxx_leds_platform_data {
|
||||
int num_leds;
|
||||
struct mc13xxx_led_platform_data *led;
|
||||
|
||||
#define MC13783_LED_TRIODE_MD (1 << 0)
|
||||
#define MC13783_LED_TRIODE_AD (1 << 1)
|
||||
#define MC13783_LED_TRIODE_KP (1 << 2)
|
||||
#define MC13783_LED_BOOST_EN (1 << 3)
|
||||
#define MC13783_LED_TC1HALF (1 << 4)
|
||||
#define MC13783_LED_SLEWLIMTC (1 << 5)
|
||||
#define MC13783_LED_SLEWLIMBL (1 << 6)
|
||||
#define MC13783_LED_TRIODE_TC1 (1 << 7)
|
||||
#define MC13783_LED_TRIODE_TC2 (1 << 8)
|
||||
#define MC13783_LED_TRIODE_TC3 (1 << 9)
|
||||
int flags;
|
||||
|
||||
#define MC13783_LED_AB_DISABLED 0
|
||||
#define MC13783_LED_AB_MD1 1
|
||||
#define MC13783_LED_AB_MD12 2
|
||||
#define MC13783_LED_AB_MD123 3
|
||||
#define MC13783_LED_AB_MD1234 4
|
||||
#define MC13783_LED_AB_MD1234_AD1 5
|
||||
#define MC13783_LED_AB_MD1234_AD12 6
|
||||
#define MC13783_LED_AB_MD1_AD 7
|
||||
char abmode;
|
||||
|
||||
#define MC13783_LED_ABREF_200MV 0
|
||||
#define MC13783_LED_ABREF_400MV 1
|
||||
#define MC13783_LED_ABREF_600MV 2
|
||||
#define MC13783_LED_ABREF_800MV 3
|
||||
char abref;
|
||||
|
||||
#define MC13783_LED_PERIOD_10MS 0
|
||||
#define MC13783_LED_PERIOD_100MS 1
|
||||
#define MC13783_LED_PERIOD_500MS 2
|
||||
#define MC13783_LED_PERIOD_2S 3
|
||||
char bl_period;
|
||||
char tc1_period;
|
||||
char tc2_period;
|
||||
char tc3_period;
|
||||
};
|
||||
|
||||
struct mc13xxx_platform_data {
|
||||
#define MC13XXX_USE_TOUCHSCREEN (1 << 0)
|
||||
#define MC13XXX_USE_CODEC (1 << 1)
|
||||
#define MC13XXX_USE_ADC (1 << 2)
|
||||
#define MC13XXX_USE_RTC (1 << 3)
|
||||
#define MC13XXX_USE_REGULATOR (1 << 4)
|
||||
#define MC13XXX_USE_LED (1 << 5)
|
||||
unsigned int flags;
|
||||
|
||||
int num_regulators;
|
||||
struct mc13xxx_regulator_init_data *regulators;
|
||||
struct mc13xxx_leds_platform_data *leds;
|
||||
};
|
||||
|
||||
#endif /* ifndef __LINUX_MFD_MC13XXX_H */
|
@ -227,4 +227,11 @@ static inline struct pcf50633 *dev_to_pcf50633(struct device *dev)
|
||||
return dev_get_drvdata(dev);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
#endif
|
||||
|
@ -7,8 +7,10 @@ struct sh_mobile_sdhi_info {
|
||||
int dma_slave_tx;
|
||||
int dma_slave_rx;
|
||||
unsigned long tmio_flags;
|
||||
unsigned long tmio_caps;
|
||||
u32 tmio_ocr_mask; /* available MMC voltages */
|
||||
void (*set_pwr)(struct platform_device *pdev, int state);
|
||||
int (*get_cd)(struct platform_device *pdev);
|
||||
};
|
||||
|
||||
#endif /* __SH_MOBILE_SDHI_H__ */
|
||||
|
@ -112,13 +112,19 @@ struct stmpe_keypad_platform_data {
|
||||
bool no_autorepeat;
|
||||
};
|
||||
|
||||
#define STMPE_GPIO_NOREQ_811_TOUCH (0xf0)
|
||||
|
||||
/**
|
||||
* struct stmpe_gpio_platform_data - STMPE GPIO platform data
|
||||
* @gpio_base: first gpio number assigned. A maximum of
|
||||
* %STMPE_NR_GPIOS GPIOs will be allocated.
|
||||
* @norequest_mask: bitmask specifying which GPIOs should _not_ be
|
||||
* requestable due to different usage (e.g. touch, keypad)
|
||||
* STMPE_GPIO_NOREQ_* macros can be used here.
|
||||
*/
|
||||
struct stmpe_gpio_platform_data {
|
||||
int gpio_base;
|
||||
unsigned norequest_mask;
|
||||
void (*setup)(struct stmpe *stmpe, unsigned gpio_base);
|
||||
void (*remove)(struct stmpe *stmpe, unsigned gpio_base);
|
||||
};
|
||||
|
@ -52,6 +52,11 @@
|
||||
|
||||
/* tmio MMC platform flags */
|
||||
#define TMIO_MMC_WRPROTECT_DISABLE (1 << 0)
|
||||
/*
|
||||
* Some controllers can support a 2-byte block size when the bus width
|
||||
* is configured in 4-bit mode.
|
||||
*/
|
||||
#define TMIO_MMC_BLKSZ_2BYTES (1 << 1)
|
||||
|
||||
int tmio_core_mmc_enable(void __iomem *cnf, int shift, unsigned long base);
|
||||
int tmio_core_mmc_resume(void __iomem *cnf, int shift, unsigned long base);
|
||||
@ -74,6 +79,7 @@ struct tmio_mmc_data {
|
||||
struct tmio_mmc_dma *dma;
|
||||
void (*set_pwr)(struct platform_device *host, int state);
|
||||
void (*set_clk_div)(struct platform_device *host, int state);
|
||||
int (*get_cd)(struct platform_device *host);
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -18,6 +18,36 @@ enum {
|
||||
TPS6586X_ID_LDO_RTC,
|
||||
};
|
||||
|
||||
enum {
|
||||
TPS6586X_INT_PLDO_0,
|
||||
TPS6586X_INT_PLDO_1,
|
||||
TPS6586X_INT_PLDO_2,
|
||||
TPS6586X_INT_PLDO_3,
|
||||
TPS6586X_INT_PLDO_4,
|
||||
TPS6586X_INT_PLDO_5,
|
||||
TPS6586X_INT_PLDO_6,
|
||||
TPS6586X_INT_PLDO_7,
|
||||
TPS6586X_INT_COMP_DET,
|
||||
TPS6586X_INT_ADC,
|
||||
TPS6586X_INT_PLDO_8,
|
||||
TPS6586X_INT_PLDO_9,
|
||||
TPS6586X_INT_PSM_0,
|
||||
TPS6586X_INT_PSM_1,
|
||||
TPS6586X_INT_PSM_2,
|
||||
TPS6586X_INT_PSM_3,
|
||||
TPS6586X_INT_RTC_ALM1,
|
||||
TPS6586X_INT_ACUSB_OVP,
|
||||
TPS6586X_INT_USB_DET,
|
||||
TPS6586X_INT_AC_DET,
|
||||
TPS6586X_INT_BAT_DET,
|
||||
TPS6586X_INT_CHG_STAT,
|
||||
TPS6586X_INT_CHG_TEMP,
|
||||
TPS6586X_INT_PP,
|
||||
TPS6586X_INT_RESUME,
|
||||
TPS6586X_INT_LOW_SYS,
|
||||
TPS6586X_INT_RTC_ALM2,
|
||||
};
|
||||
|
||||
struct tps6586x_subdev_info {
|
||||
int id;
|
||||
const char *name;
|
||||
@ -29,6 +59,7 @@ struct tps6586x_platform_data {
|
||||
struct tps6586x_subdev_info *subdevs;
|
||||
|
||||
int gpio_base;
|
||||
int irq_base;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -238,6 +238,15 @@ struct regulator_dev;
|
||||
|
||||
#define WM831X_NUM_IRQ_REGS 5
|
||||
|
||||
enum wm831x_parent {
|
||||
WM8310 = 0x8310,
|
||||
WM8311 = 0x8311,
|
||||
WM8312 = 0x8312,
|
||||
WM8320 = 0x8320,
|
||||
WM8321 = 0x8321,
|
||||
WM8325 = 0x8325,
|
||||
};
|
||||
|
||||
struct wm831x {
|
||||
struct mutex io_lock;
|
||||
|
||||
@ -285,6 +294,9 @@ int wm831x_set_bits(struct wm831x *wm831x, unsigned short reg,
|
||||
int wm831x_bulk_read(struct wm831x *wm831x, unsigned short reg,
|
||||
int count, u16 *buf);
|
||||
|
||||
int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq);
|
||||
void wm831x_device_exit(struct wm831x *wm831x);
|
||||
int wm831x_device_suspend(struct wm831x *wm831x);
|
||||
int wm831x_irq_init(struct wm831x *wm831x, int irq);
|
||||
void wm831x_irq_exit(struct wm831x *wm831x);
|
||||
|
||||
|
@ -34,6 +34,7 @@
|
||||
struct sh_mmcif_plat_data {
|
||||
void (*set_pwr)(struct platform_device *pdev, int state);
|
||||
void (*down_pwr)(struct platform_device *pdev);
|
||||
int (*get_cd)(struct platform_device *pdef);
|
||||
u8 sup_pclk; /* 1 :SH7757, 0: SH7724/SH7372 */
|
||||
unsigned long caps;
|
||||
u32 ocr;
|
||||
|
Loading…
Reference in New Issue
Block a user