mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 05:02:12 +00:00
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6
* 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6: (47 commits) mfd: Rename twl5031 sih modules mfd: Storage class for timberdale should be before const qualifier mfd: Remove unneeded and dangerous clearing of clientdata mfd: New AB8500 driver gpio: Fix inverted rdc321x gpio data out registers mfd: Change rdc321x resources flags to IORESOURCE_IO mfd: Move pcf50633 irq related functions to its own file. mfd: Use threaded irq for pcf50633 mfd: pcf50633-adc: Fix potential race in pcf50633_adc_sync_read mfd: Fix pcf50633 bitfield logic in interrupt handler gpio: rdc321x needs to select MFD_CORE mfd: Use menuconfig for quicker config editing ARM: AB3550 board configuration and irq for U300 mfd: AB3550 core driver mfd: AB3100 register access change to abx500 API mfd: Renamed ab3100.h to abx500.h gpio: Add TC35892 GPIO driver mfd: Add Toshiba's TC35892 MFD core mfd: Delay to mask tsc irq in max8925 mfd: Remove incorrect wm8350 kfree ...
This commit is contained in:
commit
17d30ac077
@ -17,6 +17,7 @@
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c/at24.h>
|
||||
#include <linux/i2c/pca953x.h>
|
||||
#include <linux/mfd/tps6507x.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
@ -24,6 +25,8 @@
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/physmap.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/mfd/tps6507x.h>
|
||||
#include <linux/input/tps6507x-ts.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/mach/arch.h>
|
||||
@ -533,10 +536,24 @@ struct regulator_init_data tps65070_regulator_data[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct touchscreen_init_data tps6507x_touchscreen_data = {
|
||||
.poll_period = 30, /* ms between touch samples */
|
||||
.min_pressure = 0x30, /* minimum pressure to trigger touch */
|
||||
.vref = 0, /* turn off vref when not using A/D */
|
||||
.vendor = 0, /* /sys/class/input/input?/id/vendor */
|
||||
.product = 65070, /* /sys/class/input/input?/id/product */
|
||||
.version = 0x100, /* /sys/class/input/input?/id/version */
|
||||
};
|
||||
|
||||
static struct tps6507x_board tps_board = {
|
||||
.tps6507x_pmic_init_data = &tps65070_regulator_data[0],
|
||||
.tps6507x_ts_init_data = &tps6507x_touchscreen_data,
|
||||
};
|
||||
|
||||
static struct i2c_board_info __initdata da850evm_tps65070_info[] = {
|
||||
{
|
||||
I2C_BOARD_INFO("tps6507x", 0x48),
|
||||
.platform_data = &tps65070_regulator_data[0],
|
||||
.platform_data = &tps_board,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mfd/ab3100.h>
|
||||
#include <linux/mfd/abx500.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/amba/bus.h>
|
||||
#include <mach/irqs.h>
|
||||
@ -46,6 +46,7 @@
|
||||
/* BUCK SLEEP 0xAC: 1.05V, Not used, SLEEP_A and B, Not used */
|
||||
#define BUCK_SLEEP_SETTING 0xAC
|
||||
|
||||
#ifdef CONFIG_AB3100_CORE
|
||||
static struct regulator_consumer_supply supply_ldo_c[] = {
|
||||
{
|
||||
.dev_name = "ab3100-codec",
|
||||
@ -253,14 +254,68 @@ static struct ab3100_platform_data ab3100_plf_data = {
|
||||
LDO_D_SETTING,
|
||||
},
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_AB3550_CORE
|
||||
static struct abx500_init_settings ab3550_init_settings[] = {
|
||||
{
|
||||
.bank = 0,
|
||||
.reg = AB3550_IMR1,
|
||||
.setting = 0xff
|
||||
},
|
||||
{
|
||||
.bank = 0,
|
||||
.reg = AB3550_IMR2,
|
||||
.setting = 0xff
|
||||
},
|
||||
{
|
||||
.bank = 0,
|
||||
.reg = AB3550_IMR3,
|
||||
.setting = 0xff
|
||||
},
|
||||
{
|
||||
.bank = 0,
|
||||
.reg = AB3550_IMR4,
|
||||
.setting = 0xff
|
||||
},
|
||||
{
|
||||
.bank = 0,
|
||||
.reg = AB3550_IMR5,
|
||||
/* The two most significant bits are not used */
|
||||
.setting = 0x3f
|
||||
},
|
||||
};
|
||||
|
||||
static struct ab3550_platform_data ab3550_plf_data = {
|
||||
.irq = {
|
||||
.base = IRQ_AB3550_BASE,
|
||||
.count = (IRQ_AB3550_END - IRQ_AB3550_BASE + 1),
|
||||
},
|
||||
.dev_data = {
|
||||
},
|
||||
.init_settings = ab3550_init_settings,
|
||||
.init_settings_sz = ARRAY_SIZE(ab3550_init_settings),
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct i2c_board_info __initdata bus0_i2c_board_info[] = {
|
||||
#if defined(CONFIG_AB3550_CORE)
|
||||
{
|
||||
.type = "ab3550",
|
||||
.addr = 0x4A,
|
||||
.irq = IRQ_U300_IRQ0_EXT,
|
||||
.platform_data = &ab3550_plf_data,
|
||||
},
|
||||
#elif defined(CONFIG_AB3100_CORE)
|
||||
{
|
||||
.type = "ab3100",
|
||||
.addr = 0x48,
|
||||
.irq = IRQ_U300_IRQ0_EXT,
|
||||
.platform_data = &ab3100_plf_data,
|
||||
},
|
||||
#else
|
||||
{ },
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct i2c_board_info __initdata bus1_i2c_board_info[] = {
|
||||
|
@ -109,6 +109,13 @@
|
||||
#define U300_NR_IRQS 48
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_AB3550_CORE
|
||||
#define IRQ_AB3550_BASE (U300_NR_IRQS)
|
||||
#define IRQ_AB3550_END (IRQ_AB3550_BASE + 37)
|
||||
|
||||
#define NR_IRQS (IRQ_AB3550_END + 1)
|
||||
#else
|
||||
#define NR_IRQS U300_NR_IRQS
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -50,7 +50,7 @@ struct pl022_config_chip ab4500_chip_info = {
|
||||
|
||||
static struct spi_board_info u8500_spi_devices[] = {
|
||||
{
|
||||
.modalias = "ab4500",
|
||||
.modalias = "ab8500",
|
||||
.controller_data = &ab4500_chip_info,
|
||||
.max_speed_hz = 12000000,
|
||||
.bus_num = 0,
|
||||
|
@ -1,12 +0,0 @@
|
||||
#define PFX "rdc321x: "
|
||||
|
||||
/* General purpose configuration and data registers */
|
||||
#define RDC3210_CFGREG_ADDR 0x0CF8
|
||||
#define RDC3210_CFGREG_DATA 0x0CFC
|
||||
|
||||
#define RDC321X_GPIO_CTRL_REG1 0x48
|
||||
#define RDC321X_GPIO_CTRL_REG2 0x84
|
||||
#define RDC321X_GPIO_DATA_REG1 0x4c
|
||||
#define RDC321X_GPIO_DATA_REG2 0x88
|
||||
|
||||
#define RDC321X_MAX_GPIO 58
|
@ -195,6 +195,13 @@ config GPIO_PCF857X
|
||||
This driver provides an in-kernel interface to those GPIOs using
|
||||
platform-neutral GPIO calls.
|
||||
|
||||
config GPIO_TC35892
|
||||
bool "TC35892 GPIOs"
|
||||
depends on MFD_TC35892
|
||||
help
|
||||
This enables support for the GPIOs found on the TC35892
|
||||
I/O Expander.
|
||||
|
||||
config GPIO_TWL4030
|
||||
tristate "TWL4030, TWL5030, and TPS659x0 GPIOs"
|
||||
depends on TWL4030_CORE
|
||||
@ -282,6 +289,15 @@ config GPIO_TIMBERDALE
|
||||
---help---
|
||||
Add support for the GPIO IP in the timberdale FPGA.
|
||||
|
||||
config GPIO_RDC321X
|
||||
tristate "RDC R-321x GPIO support"
|
||||
depends on PCI && GPIOLIB
|
||||
select MFD_CORE
|
||||
select MFD_RDC321X
|
||||
help
|
||||
Support for the RDC R321x SoC GPIOs over southbridge
|
||||
PCI configuration space.
|
||||
|
||||
comment "SPI GPIO expanders:"
|
||||
|
||||
config GPIO_MAX7301
|
||||
@ -317,4 +333,14 @@ config GPIO_UCB1400
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ucb1400_gpio.
|
||||
|
||||
comment "MODULbus GPIO expanders:"
|
||||
|
||||
config GPIO_JANZ_TTL
|
||||
tristate "Janz VMOD-TTL Digital IO Module"
|
||||
depends on MFD_JANZ_CMODIO
|
||||
help
|
||||
This enables support for the Janz VMOD-TTL Digital IO module.
|
||||
This driver provides support for driving the pins in output
|
||||
mode only. Input mode is not supported.
|
||||
|
||||
endif
|
||||
|
@ -16,6 +16,7 @@ obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o
|
||||
obj-$(CONFIG_GPIO_PCA953X) += pca953x.o
|
||||
obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o
|
||||
obj-$(CONFIG_GPIO_PL061) += pl061.o
|
||||
obj-$(CONFIG_GPIO_TC35892) += tc35892-gpio.o
|
||||
obj-$(CONFIG_GPIO_TIMBERDALE) += timbgpio.o
|
||||
obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o
|
||||
obj-$(CONFIG_GPIO_UCB1400) += ucb1400_gpio.o
|
||||
@ -27,4 +28,6 @@ obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o
|
||||
obj-$(CONFIG_GPIO_WM831X) += wm831x-gpio.o
|
||||
obj-$(CONFIG_GPIO_WM8350) += wm8350-gpiolib.o
|
||||
obj-$(CONFIG_GPIO_WM8994) += wm8994-gpio.o
|
||||
obj-$(CONFIG_GPIO_SCH) += sch_gpio.o
|
||||
obj-$(CONFIG_GPIO_SCH) += sch_gpio.o
|
||||
obj-$(CONFIG_GPIO_RDC321X) += rdc321x-gpio.o
|
||||
obj-$(CONFIG_GPIO_JANZ_TTL) += janz-ttl.o
|
||||
|
258
drivers/gpio/janz-ttl.c
Normal file
258
drivers/gpio/janz-ttl.c
Normal file
@ -0,0 +1,258 @@
|
||||
/*
|
||||
* Janz MODULbus VMOD-TTL GPIO Driver
|
||||
*
|
||||
* Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu>
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/mfd/janz.h>
|
||||
|
||||
#define DRV_NAME "janz-ttl"
|
||||
|
||||
#define PORTA_DIRECTION 0x23
|
||||
#define PORTB_DIRECTION 0x2B
|
||||
#define PORTC_DIRECTION 0x06
|
||||
#define PORTA_IOCTL 0x24
|
||||
#define PORTB_IOCTL 0x2C
|
||||
#define PORTC_IOCTL 0x07
|
||||
|
||||
#define MASTER_INT_CTL 0x00
|
||||
#define MASTER_CONF_CTL 0x01
|
||||
|
||||
#define CONF_PAE (1 << 2)
|
||||
#define CONF_PBE (1 << 7)
|
||||
#define CONF_PCE (1 << 4)
|
||||
|
||||
struct ttl_control_regs {
|
||||
__be16 portc;
|
||||
__be16 portb;
|
||||
__be16 porta;
|
||||
__be16 control;
|
||||
};
|
||||
|
||||
struct ttl_module {
|
||||
struct gpio_chip gpio;
|
||||
|
||||
/* base address of registers */
|
||||
struct ttl_control_regs __iomem *regs;
|
||||
|
||||
u8 portc_shadow;
|
||||
u8 portb_shadow;
|
||||
u8 porta_shadow;
|
||||
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static int ttl_get_value(struct gpio_chip *gpio, unsigned offset)
|
||||
{
|
||||
struct ttl_module *mod = dev_get_drvdata(gpio->dev);
|
||||
u8 *shadow;
|
||||
int ret;
|
||||
|
||||
if (offset < 8) {
|
||||
shadow = &mod->porta_shadow;
|
||||
} else if (offset < 16) {
|
||||
shadow = &mod->portb_shadow;
|
||||
offset -= 8;
|
||||
} else {
|
||||
shadow = &mod->portc_shadow;
|
||||
offset -= 16;
|
||||
}
|
||||
|
||||
spin_lock(&mod->lock);
|
||||
ret = *shadow & (1 << offset);
|
||||
spin_unlock(&mod->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ttl_set_value(struct gpio_chip *gpio, unsigned offset, int value)
|
||||
{
|
||||
struct ttl_module *mod = dev_get_drvdata(gpio->dev);
|
||||
void __iomem *port;
|
||||
u8 *shadow;
|
||||
|
||||
if (offset < 8) {
|
||||
port = &mod->regs->porta;
|
||||
shadow = &mod->porta_shadow;
|
||||
} else if (offset < 16) {
|
||||
port = &mod->regs->portb;
|
||||
shadow = &mod->portb_shadow;
|
||||
offset -= 8;
|
||||
} else {
|
||||
port = &mod->regs->portc;
|
||||
shadow = &mod->portc_shadow;
|
||||
offset -= 16;
|
||||
}
|
||||
|
||||
spin_lock(&mod->lock);
|
||||
if (value)
|
||||
*shadow |= (1 << offset);
|
||||
else
|
||||
*shadow &= ~(1 << offset);
|
||||
|
||||
iowrite16be(*shadow, port);
|
||||
spin_unlock(&mod->lock);
|
||||
}
|
||||
|
||||
static void __devinit ttl_write_reg(struct ttl_module *mod, u8 reg, u16 val)
|
||||
{
|
||||
iowrite16be(reg, &mod->regs->control);
|
||||
iowrite16be(val, &mod->regs->control);
|
||||
}
|
||||
|
||||
static void __devinit ttl_setup_device(struct ttl_module *mod)
|
||||
{
|
||||
/* reset the device to a known state */
|
||||
iowrite16be(0x0000, &mod->regs->control);
|
||||
iowrite16be(0x0001, &mod->regs->control);
|
||||
iowrite16be(0x0000, &mod->regs->control);
|
||||
|
||||
/* put all ports in open-drain mode */
|
||||
ttl_write_reg(mod, PORTA_IOCTL, 0x00ff);
|
||||
ttl_write_reg(mod, PORTB_IOCTL, 0x00ff);
|
||||
ttl_write_reg(mod, PORTC_IOCTL, 0x000f);
|
||||
|
||||
/* set all ports as outputs */
|
||||
ttl_write_reg(mod, PORTA_DIRECTION, 0x0000);
|
||||
ttl_write_reg(mod, PORTB_DIRECTION, 0x0000);
|
||||
ttl_write_reg(mod, PORTC_DIRECTION, 0x0000);
|
||||
|
||||
/* set all ports to drive zeroes */
|
||||
iowrite16be(0x0000, &mod->regs->porta);
|
||||
iowrite16be(0x0000, &mod->regs->portb);
|
||||
iowrite16be(0x0000, &mod->regs->portc);
|
||||
|
||||
/* enable all ports */
|
||||
ttl_write_reg(mod, MASTER_CONF_CTL, CONF_PAE | CONF_PBE | CONF_PCE);
|
||||
}
|
||||
|
||||
static int __devinit ttl_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct janz_platform_data *pdata;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct ttl_module *mod;
|
||||
struct gpio_chip *gpio;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
pdata = pdev->dev.platform_data;
|
||||
if (!pdata) {
|
||||
dev_err(dev, "no platform data\n");
|
||||
ret = -ENXIO;
|
||||
goto out_return;
|
||||
}
|
||||
|
||||
mod = kzalloc(sizeof(*mod), GFP_KERNEL);
|
||||
if (!mod) {
|
||||
dev_err(dev, "unable to allocate private data\n");
|
||||
ret = -ENOMEM;
|
||||
goto out_return;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, mod);
|
||||
spin_lock_init(&mod->lock);
|
||||
|
||||
/* get access to the MODULbus registers for this module */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "MODULbus registers not found\n");
|
||||
ret = -ENODEV;
|
||||
goto out_free_mod;
|
||||
}
|
||||
|
||||
mod->regs = ioremap(res->start, resource_size(res));
|
||||
if (!mod->regs) {
|
||||
dev_err(dev, "MODULbus registers not ioremap\n");
|
||||
ret = -ENOMEM;
|
||||
goto out_free_mod;
|
||||
}
|
||||
|
||||
ttl_setup_device(mod);
|
||||
|
||||
/* Initialize the GPIO data structures */
|
||||
gpio = &mod->gpio;
|
||||
gpio->dev = &pdev->dev;
|
||||
gpio->label = pdev->name;
|
||||
gpio->get = ttl_get_value;
|
||||
gpio->set = ttl_set_value;
|
||||
gpio->owner = THIS_MODULE;
|
||||
|
||||
/* request dynamic allocation */
|
||||
gpio->base = -1;
|
||||
gpio->ngpio = 20;
|
||||
|
||||
ret = gpiochip_add(gpio);
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to add GPIO chip\n");
|
||||
goto out_iounmap_regs;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "module %d: registered GPIO device\n",
|
||||
pdata->modno);
|
||||
return 0;
|
||||
|
||||
out_iounmap_regs:
|
||||
iounmap(mod->regs);
|
||||
out_free_mod:
|
||||
kfree(mod);
|
||||
out_return:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit ttl_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ttl_module *mod = platform_get_drvdata(pdev);
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
|
||||
ret = gpiochip_remove(&mod->gpio);
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to remove GPIO chip\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
iounmap(mod->regs);
|
||||
kfree(mod);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ttl_driver = {
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ttl_probe,
|
||||
.remove = __devexit_p(ttl_remove),
|
||||
};
|
||||
|
||||
static int __init ttl_init(void)
|
||||
{
|
||||
return platform_driver_register(&ttl_driver);
|
||||
}
|
||||
|
||||
static void __exit ttl_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ttl_driver);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
|
||||
MODULE_DESCRIPTION("Janz MODULbus VMOD-TTL Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:janz-ttl");
|
||||
|
||||
module_init(ttl_init);
|
||||
module_exit(ttl_exit);
|
246
drivers/gpio/rdc321x-gpio.c
Normal file
246
drivers/gpio/rdc321x-gpio.c
Normal file
@ -0,0 +1,246 @@
|
||||
/*
|
||||
* RDC321x GPIO driver
|
||||
*
|
||||
* Copyright (C) 2008, Volker Weiss <dev@tintuc.de>
|
||||
* Copyright (C) 2007-2010 Florian Fainelli <florian@openwrt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/mfd/rdc321x.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct rdc321x_gpio {
|
||||
spinlock_t lock;
|
||||
struct pci_dev *sb_pdev;
|
||||
u32 data_reg[2];
|
||||
int reg1_ctrl_base;
|
||||
int reg1_data_base;
|
||||
int reg2_ctrl_base;
|
||||
int reg2_data_base;
|
||||
struct gpio_chip chip;
|
||||
};
|
||||
|
||||
/* read GPIO pin */
|
||||
static int rdc_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
|
||||
{
|
||||
struct rdc321x_gpio *gpch;
|
||||
u32 value = 0;
|
||||
int reg;
|
||||
|
||||
gpch = container_of(chip, struct rdc321x_gpio, chip);
|
||||
reg = gpio < 32 ? gpch->reg1_data_base : gpch->reg2_data_base;
|
||||
|
||||
spin_lock(&gpch->lock);
|
||||
pci_write_config_dword(gpch->sb_pdev, reg,
|
||||
gpch->data_reg[gpio < 32 ? 0 : 1]);
|
||||
pci_read_config_dword(gpch->sb_pdev, reg, &value);
|
||||
spin_unlock(&gpch->lock);
|
||||
|
||||
return (1 << (gpio & 0x1f)) & value ? 1 : 0;
|
||||
}
|
||||
|
||||
static void rdc_gpio_set_value_impl(struct gpio_chip *chip,
|
||||
unsigned gpio, int value)
|
||||
{
|
||||
struct rdc321x_gpio *gpch;
|
||||
int reg = (gpio < 32) ? 0 : 1;
|
||||
|
||||
gpch = container_of(chip, struct rdc321x_gpio, chip);
|
||||
|
||||
if (value)
|
||||
gpch->data_reg[reg] |= 1 << (gpio & 0x1f);
|
||||
else
|
||||
gpch->data_reg[reg] &= ~(1 << (gpio & 0x1f));
|
||||
|
||||
pci_write_config_dword(gpch->sb_pdev,
|
||||
reg ? gpch->reg2_data_base : gpch->reg1_data_base,
|
||||
gpch->data_reg[reg]);
|
||||
}
|
||||
|
||||
/* set GPIO pin to value */
|
||||
static void rdc_gpio_set_value(struct gpio_chip *chip,
|
||||
unsigned gpio, int value)
|
||||
{
|
||||
struct rdc321x_gpio *gpch;
|
||||
|
||||
gpch = container_of(chip, struct rdc321x_gpio, chip);
|
||||
spin_lock(&gpch->lock);
|
||||
rdc_gpio_set_value_impl(chip, gpio, value);
|
||||
spin_unlock(&gpch->lock);
|
||||
}
|
||||
|
||||
static int rdc_gpio_config(struct gpio_chip *chip,
|
||||
unsigned gpio, int value)
|
||||
{
|
||||
struct rdc321x_gpio *gpch;
|
||||
int err;
|
||||
u32 reg;
|
||||
|
||||
gpch = container_of(chip, struct rdc321x_gpio, chip);
|
||||
|
||||
spin_lock(&gpch->lock);
|
||||
err = pci_read_config_dword(gpch->sb_pdev, gpio < 32 ?
|
||||
gpch->reg1_ctrl_base : gpch->reg2_ctrl_base, ®);
|
||||
if (err)
|
||||
goto unlock;
|
||||
|
||||
reg |= 1 << (gpio & 0x1f);
|
||||
|
||||
err = pci_write_config_dword(gpch->sb_pdev, gpio < 32 ?
|
||||
gpch->reg1_ctrl_base : gpch->reg2_ctrl_base, reg);
|
||||
if (err)
|
||||
goto unlock;
|
||||
|
||||
rdc_gpio_set_value_impl(chip, gpio, value);
|
||||
|
||||
unlock:
|
||||
spin_unlock(&gpch->lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* configure GPIO pin as input */
|
||||
static int rdc_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
|
||||
{
|
||||
return rdc_gpio_config(chip, gpio, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Cache the initial value of both GPIO data registers
|
||||
*/
|
||||
static int __devinit rdc321x_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
int err;
|
||||
struct resource *r;
|
||||
struct rdc321x_gpio *rdc321x_gpio_dev;
|
||||
struct rdc321x_gpio_pdata *pdata;
|
||||
|
||||
pdata = pdev->dev.platform_data;
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "no platform data supplied\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
rdc321x_gpio_dev = kzalloc(sizeof(struct rdc321x_gpio), GFP_KERNEL);
|
||||
if (!rdc321x_gpio_dev) {
|
||||
dev_err(&pdev->dev, "failed to allocate private data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
r = platform_get_resource_byname(pdev, IORESOURCE_IO, "gpio-reg1");
|
||||
if (!r) {
|
||||
dev_err(&pdev->dev, "failed to get gpio-reg1 resource\n");
|
||||
err = -ENODEV;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
spin_lock_init(&rdc321x_gpio_dev->lock);
|
||||
rdc321x_gpio_dev->sb_pdev = pdata->sb_pdev;
|
||||
rdc321x_gpio_dev->reg1_ctrl_base = r->start;
|
||||
rdc321x_gpio_dev->reg1_data_base = r->start + 0x4;
|
||||
|
||||
r = platform_get_resource_byname(pdev, IORESOURCE_IO, "gpio-reg2");
|
||||
if (!r) {
|
||||
dev_err(&pdev->dev, "failed to get gpio-reg2 resource\n");
|
||||
err = -ENODEV;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
rdc321x_gpio_dev->reg2_ctrl_base = r->start;
|
||||
rdc321x_gpio_dev->reg2_data_base = r->start + 0x4;
|
||||
|
||||
rdc321x_gpio_dev->chip.label = "rdc321x-gpio";
|
||||
rdc321x_gpio_dev->chip.direction_input = rdc_gpio_direction_input;
|
||||
rdc321x_gpio_dev->chip.direction_output = rdc_gpio_config;
|
||||
rdc321x_gpio_dev->chip.get = rdc_gpio_get_value;
|
||||
rdc321x_gpio_dev->chip.set = rdc_gpio_set_value;
|
||||
rdc321x_gpio_dev->chip.base = 0;
|
||||
rdc321x_gpio_dev->chip.ngpio = pdata->max_gpios;
|
||||
|
||||
platform_set_drvdata(pdev, rdc321x_gpio_dev);
|
||||
|
||||
/* This might not be, what others (BIOS, bootloader, etc.)
|
||||
wrote to these registers before, but it's a good guess. Still
|
||||
better than just using 0xffffffff. */
|
||||
err = pci_read_config_dword(rdc321x_gpio_dev->sb_pdev,
|
||||
rdc321x_gpio_dev->reg1_data_base,
|
||||
&rdc321x_gpio_dev->data_reg[0]);
|
||||
if (err)
|
||||
goto out_drvdata;
|
||||
|
||||
err = pci_read_config_dword(rdc321x_gpio_dev->sb_pdev,
|
||||
rdc321x_gpio_dev->reg2_data_base,
|
||||
&rdc321x_gpio_dev->data_reg[1]);
|
||||
if (err)
|
||||
goto out_drvdata;
|
||||
|
||||
dev_info(&pdev->dev, "registering %d GPIOs\n",
|
||||
rdc321x_gpio_dev->chip.ngpio);
|
||||
return gpiochip_add(&rdc321x_gpio_dev->chip);
|
||||
|
||||
out_drvdata:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
out_free:
|
||||
kfree(rdc321x_gpio_dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devexit rdc321x_gpio_remove(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct rdc321x_gpio *rdc321x_gpio_dev = platform_get_drvdata(pdev);
|
||||
|
||||
ret = gpiochip_remove(&rdc321x_gpio_dev->chip);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "failed to unregister chip\n");
|
||||
|
||||
kfree(rdc321x_gpio_dev);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver rdc321x_gpio_driver = {
|
||||
.driver.name = "rdc321x-gpio",
|
||||
.driver.owner = THIS_MODULE,
|
||||
.probe = rdc321x_gpio_probe,
|
||||
.remove = __devexit_p(rdc321x_gpio_remove),
|
||||
};
|
||||
|
||||
static int __init rdc321x_gpio_init(void)
|
||||
{
|
||||
return platform_driver_register(&rdc321x_gpio_driver);
|
||||
}
|
||||
|
||||
static void __exit rdc321x_gpio_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&rdc321x_gpio_driver);
|
||||
}
|
||||
|
||||
module_init(rdc321x_gpio_init);
|
||||
module_exit(rdc321x_gpio_exit);
|
||||
|
||||
MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
|
||||
MODULE_DESCRIPTION("RDC321x GPIO driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:rdc321x-gpio");
|
381
drivers/gpio/tc35892-gpio.c
Normal file
381
drivers/gpio/tc35892-gpio.c
Normal file
@ -0,0 +1,381 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* License Terms: GNU General Public License, version 2
|
||||
* Author: Hanumath Prasad <hanumath.prasad@stericsson.com> for ST-Ericsson
|
||||
* Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/tc35892.h>
|
||||
|
||||
/*
|
||||
* These registers are modified under the irq bus lock and cached to avoid
|
||||
* unnecessary writes in bus_sync_unlock.
|
||||
*/
|
||||
enum { REG_IBE, REG_IEV, REG_IS, REG_IE };
|
||||
|
||||
#define CACHE_NR_REGS 4
|
||||
#define CACHE_NR_BANKS 3
|
||||
|
||||
struct tc35892_gpio {
|
||||
struct gpio_chip chip;
|
||||
struct tc35892 *tc35892;
|
||||
struct device *dev;
|
||||
struct mutex irq_lock;
|
||||
|
||||
int irq_base;
|
||||
|
||||
/* Caches of interrupt control registers for bus_lock */
|
||||
u8 regs[CACHE_NR_REGS][CACHE_NR_BANKS];
|
||||
u8 oldregs[CACHE_NR_REGS][CACHE_NR_BANKS];
|
||||
};
|
||||
|
||||
static inline struct tc35892_gpio *to_tc35892_gpio(struct gpio_chip *chip)
|
||||
{
|
||||
return container_of(chip, struct tc35892_gpio, chip);
|
||||
}
|
||||
|
||||
static int tc35892_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip);
|
||||
struct tc35892 *tc35892 = tc35892_gpio->tc35892;
|
||||
u8 reg = TC35892_GPIODATA0 + (offset / 8) * 2;
|
||||
u8 mask = 1 << (offset % 8);
|
||||
int ret;
|
||||
|
||||
ret = tc35892_reg_read(tc35892, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ret & mask;
|
||||
}
|
||||
|
||||
static void tc35892_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
|
||||
{
|
||||
struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip);
|
||||
struct tc35892 *tc35892 = tc35892_gpio->tc35892;
|
||||
u8 reg = TC35892_GPIODATA0 + (offset / 8) * 2;
|
||||
unsigned pos = offset % 8;
|
||||
u8 data[] = {!!val << pos, 1 << pos};
|
||||
|
||||
tc35892_block_write(tc35892, reg, ARRAY_SIZE(data), data);
|
||||
}
|
||||
|
||||
static int tc35892_gpio_direction_output(struct gpio_chip *chip,
|
||||
unsigned offset, int val)
|
||||
{
|
||||
struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip);
|
||||
struct tc35892 *tc35892 = tc35892_gpio->tc35892;
|
||||
u8 reg = TC35892_GPIODIR0 + offset / 8;
|
||||
unsigned pos = offset % 8;
|
||||
|
||||
tc35892_gpio_set(chip, offset, val);
|
||||
|
||||
return tc35892_set_bits(tc35892, reg, 1 << pos, 1 << pos);
|
||||
}
|
||||
|
||||
static int tc35892_gpio_direction_input(struct gpio_chip *chip,
|
||||
unsigned offset)
|
||||
{
|
||||
struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip);
|
||||
struct tc35892 *tc35892 = tc35892_gpio->tc35892;
|
||||
u8 reg = TC35892_GPIODIR0 + offset / 8;
|
||||
unsigned pos = offset % 8;
|
||||
|
||||
return tc35892_set_bits(tc35892, reg, 1 << pos, 0);
|
||||
}
|
||||
|
||||
static int tc35892_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip);
|
||||
|
||||
return tc35892_gpio->irq_base + offset;
|
||||
}
|
||||
|
||||
static struct gpio_chip template_chip = {
|
||||
.label = "tc35892",
|
||||
.owner = THIS_MODULE,
|
||||
.direction_input = tc35892_gpio_direction_input,
|
||||
.get = tc35892_gpio_get,
|
||||
.direction_output = tc35892_gpio_direction_output,
|
||||
.set = tc35892_gpio_set,
|
||||
.to_irq = tc35892_gpio_to_irq,
|
||||
.can_sleep = 1,
|
||||
};
|
||||
|
||||
static int tc35892_gpio_irq_set_type(unsigned int irq, unsigned int type)
|
||||
{
|
||||
struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq);
|
||||
int offset = irq - tc35892_gpio->irq_base;
|
||||
int regoffset = offset / 8;
|
||||
int mask = 1 << (offset % 8);
|
||||
|
||||
if (type == IRQ_TYPE_EDGE_BOTH) {
|
||||
tc35892_gpio->regs[REG_IBE][regoffset] |= mask;
|
||||
return 0;
|
||||
}
|
||||
|
||||
tc35892_gpio->regs[REG_IBE][regoffset] &= ~mask;
|
||||
|
||||
if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH)
|
||||
tc35892_gpio->regs[REG_IS][regoffset] |= mask;
|
||||
else
|
||||
tc35892_gpio->regs[REG_IS][regoffset] &= ~mask;
|
||||
|
||||
if (type == IRQ_TYPE_EDGE_RISING || type == IRQ_TYPE_LEVEL_HIGH)
|
||||
tc35892_gpio->regs[REG_IEV][regoffset] |= mask;
|
||||
else
|
||||
tc35892_gpio->regs[REG_IEV][regoffset] &= ~mask;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tc35892_gpio_irq_lock(unsigned int irq)
|
||||
{
|
||||
struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq);
|
||||
|
||||
mutex_lock(&tc35892_gpio->irq_lock);
|
||||
}
|
||||
|
||||
static void tc35892_gpio_irq_sync_unlock(unsigned int irq)
|
||||
{
|
||||
struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq);
|
||||
struct tc35892 *tc35892 = tc35892_gpio->tc35892;
|
||||
static const u8 regmap[] = {
|
||||
[REG_IBE] = TC35892_GPIOIBE0,
|
||||
[REG_IEV] = TC35892_GPIOIEV0,
|
||||
[REG_IS] = TC35892_GPIOIS0,
|
||||
[REG_IE] = TC35892_GPIOIE0,
|
||||
};
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < CACHE_NR_REGS; i++) {
|
||||
for (j = 0; j < CACHE_NR_BANKS; j++) {
|
||||
u8 old = tc35892_gpio->oldregs[i][j];
|
||||
u8 new = tc35892_gpio->regs[i][j];
|
||||
|
||||
if (new == old)
|
||||
continue;
|
||||
|
||||
tc35892_gpio->oldregs[i][j] = new;
|
||||
tc35892_reg_write(tc35892, regmap[i] + j * 8, new);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&tc35892_gpio->irq_lock);
|
||||
}
|
||||
|
||||
static void tc35892_gpio_irq_mask(unsigned int irq)
|
||||
{
|
||||
struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq);
|
||||
int offset = irq - tc35892_gpio->irq_base;
|
||||
int regoffset = offset / 8;
|
||||
int mask = 1 << (offset % 8);
|
||||
|
||||
tc35892_gpio->regs[REG_IE][regoffset] &= ~mask;
|
||||
}
|
||||
|
||||
static void tc35892_gpio_irq_unmask(unsigned int irq)
|
||||
{
|
||||
struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq);
|
||||
int offset = irq - tc35892_gpio->irq_base;
|
||||
int regoffset = offset / 8;
|
||||
int mask = 1 << (offset % 8);
|
||||
|
||||
tc35892_gpio->regs[REG_IE][regoffset] |= mask;
|
||||
}
|
||||
|
||||
static struct irq_chip tc35892_gpio_irq_chip = {
|
||||
.name = "tc35892-gpio",
|
||||
.bus_lock = tc35892_gpio_irq_lock,
|
||||
.bus_sync_unlock = tc35892_gpio_irq_sync_unlock,
|
||||
.mask = tc35892_gpio_irq_mask,
|
||||
.unmask = tc35892_gpio_irq_unmask,
|
||||
.set_type = tc35892_gpio_irq_set_type,
|
||||
};
|
||||
|
||||
static irqreturn_t tc35892_gpio_irq(int irq, void *dev)
|
||||
{
|
||||
struct tc35892_gpio *tc35892_gpio = dev;
|
||||
struct tc35892 *tc35892 = tc35892_gpio->tc35892;
|
||||
u8 status[CACHE_NR_BANKS];
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
ret = tc35892_block_read(tc35892, TC35892_GPIOMIS0,
|
||||
ARRAY_SIZE(status), status);
|
||||
if (ret < 0)
|
||||
return IRQ_NONE;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(status); i++) {
|
||||
unsigned int stat = status[i];
|
||||
if (!stat)
|
||||
continue;
|
||||
|
||||
while (stat) {
|
||||
int bit = __ffs(stat);
|
||||
int line = i * 8 + bit;
|
||||
|
||||
handle_nested_irq(tc35892_gpio->irq_base + line);
|
||||
stat &= ~(1 << bit);
|
||||
}
|
||||
|
||||
tc35892_reg_write(tc35892, TC35892_GPIOIC0 + i, status[i]);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int tc35892_gpio_irq_init(struct tc35892_gpio *tc35892_gpio)
|
||||
{
|
||||
int base = tc35892_gpio->irq_base;
|
||||
int irq;
|
||||
|
||||
for (irq = base; irq < base + tc35892_gpio->chip.ngpio; irq++) {
|
||||
set_irq_chip_data(irq, tc35892_gpio);
|
||||
set_irq_chip_and_handler(irq, &tc35892_gpio_irq_chip,
|
||||
handle_simple_irq);
|
||||
set_irq_nested_thread(irq, 1);
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(irq, IRQF_VALID);
|
||||
#else
|
||||
set_irq_noprobe(irq);
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tc35892_gpio_irq_remove(struct tc35892_gpio *tc35892_gpio)
|
||||
{
|
||||
int base = tc35892_gpio->irq_base;
|
||||
int irq;
|
||||
|
||||
for (irq = base; irq < base + tc35892_gpio->chip.ngpio; irq++) {
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(irq, 0);
|
||||
#endif
|
||||
set_irq_chip_and_handler(irq, NULL, NULL);
|
||||
set_irq_chip_data(irq, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static int __devinit tc35892_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct tc35892 *tc35892 = dev_get_drvdata(pdev->dev.parent);
|
||||
struct tc35892_gpio_platform_data *pdata;
|
||||
struct tc35892_gpio *tc35892_gpio;
|
||||
int ret;
|
||||
int irq;
|
||||
|
||||
pdata = tc35892->pdata->gpio;
|
||||
if (!pdata)
|
||||
return -ENODEV;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
tc35892_gpio = kzalloc(sizeof(struct tc35892_gpio), GFP_KERNEL);
|
||||
if (!tc35892_gpio)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&tc35892_gpio->irq_lock);
|
||||
|
||||
tc35892_gpio->dev = &pdev->dev;
|
||||
tc35892_gpio->tc35892 = tc35892;
|
||||
|
||||
tc35892_gpio->chip = template_chip;
|
||||
tc35892_gpio->chip.ngpio = tc35892->num_gpio;
|
||||
tc35892_gpio->chip.dev = &pdev->dev;
|
||||
tc35892_gpio->chip.base = pdata->gpio_base;
|
||||
|
||||
tc35892_gpio->irq_base = tc35892->irq_base + TC35892_INT_GPIO(0);
|
||||
|
||||
/* Bring the GPIO module out of reset */
|
||||
ret = tc35892_set_bits(tc35892, TC35892_RSTCTRL,
|
||||
TC35892_RSTCTRL_GPIRST, 0);
|
||||
if (ret < 0)
|
||||
goto out_free;
|
||||
|
||||
ret = tc35892_gpio_irq_init(tc35892_gpio);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
ret = request_threaded_irq(irq, NULL, tc35892_gpio_irq, IRQF_ONESHOT,
|
||||
"tc35892-gpio", tc35892_gpio);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to get irq: %d\n", ret);
|
||||
goto out_removeirq;
|
||||
}
|
||||
|
||||
ret = gpiochip_add(&tc35892_gpio->chip);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret);
|
||||
goto out_freeirq;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, tc35892_gpio);
|
||||
|
||||
return 0;
|
||||
|
||||
out_freeirq:
|
||||
free_irq(irq, tc35892_gpio);
|
||||
out_removeirq:
|
||||
tc35892_gpio_irq_remove(tc35892_gpio);
|
||||
out_free:
|
||||
kfree(tc35892_gpio);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit tc35892_gpio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tc35892_gpio *tc35892_gpio = platform_get_drvdata(pdev);
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
int ret;
|
||||
|
||||
ret = gpiochip_remove(&tc35892_gpio->chip);
|
||||
if (ret < 0) {
|
||||
dev_err(tc35892_gpio->dev,
|
||||
"unable to remove gpiochip: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
free_irq(irq, tc35892_gpio);
|
||||
tc35892_gpio_irq_remove(tc35892_gpio);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(tc35892_gpio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver tc35892_gpio_driver = {
|
||||
.driver.name = "tc35892-gpio",
|
||||
.driver.owner = THIS_MODULE,
|
||||
.probe = tc35892_gpio_probe,
|
||||
.remove = __devexit_p(tc35892_gpio_remove),
|
||||
};
|
||||
|
||||
static int __init tc35892_gpio_init(void)
|
||||
{
|
||||
return platform_driver_register(&tc35892_gpio_driver);
|
||||
}
|
||||
subsys_initcall(tc35892_gpio_init);
|
||||
|
||||
static void __exit tc35892_gpio_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&tc35892_gpio_driver);
|
||||
}
|
||||
module_exit(tc35892_gpio_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("TC35892 GPIO driver");
|
||||
MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent");
|
@ -590,4 +590,17 @@ config TOUCHSCREEN_PCAP
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called pcap_ts.
|
||||
|
||||
config TOUCHSCREEN_TPS6507X
|
||||
tristate "TPS6507x based touchscreens"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you have a TPS6507x based touchscreen
|
||||
controller.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called tps6507x_ts.
|
||||
|
||||
endif
|
||||
|
@ -46,3 +46,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL) += atmel-wm97xx.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o
|
||||
|
400
drivers/input/touchscreen/tps6507x-ts.c
Normal file
400
drivers/input/touchscreen/tps6507x-ts.c
Normal file
@ -0,0 +1,400 @@
|
||||
/*
|
||||
* drivers/input/touchscreen/tps6507x_ts.c
|
||||
*
|
||||
* Touchscreen driver for the tps6507x chip.
|
||||
*
|
||||
* Copyright (c) 2009 RidgeRun (todd.fischer@ridgerun.com)
|
||||
*
|
||||
* Credits:
|
||||
*
|
||||
* Using code from tsc2007, MtekVision Co., Ltd.
|
||||
*
|
||||
* For licencing details see kernel-base/COPYING
|
||||
*
|
||||
* TPS65070, TPS65073, TPS650731, and TPS650732 support
|
||||
* 10 bit touch screen interface.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/tps6507x.h>
|
||||
#include <linux/input/tps6507x-ts.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#define TSC_DEFAULT_POLL_PERIOD 30 /* ms */
|
||||
#define TPS_DEFAULT_MIN_PRESSURE 0x30
|
||||
#define MAX_10BIT ((1 << 10) - 1)
|
||||
|
||||
#define TPS6507X_ADCONFIG_CONVERT_TS (TPS6507X_ADCONFIG_AD_ENABLE | \
|
||||
TPS6507X_ADCONFIG_START_CONVERSION | \
|
||||
TPS6507X_ADCONFIG_INPUT_REAL_TSC)
|
||||
#define TPS6507X_ADCONFIG_POWER_DOWN_TS (TPS6507X_ADCONFIG_INPUT_REAL_TSC)
|
||||
|
||||
struct ts_event {
|
||||
u16 x;
|
||||
u16 y;
|
||||
u16 pressure;
|
||||
};
|
||||
|
||||
struct tps6507x_ts {
|
||||
struct input_dev *input_dev;
|
||||
struct device *dev;
|
||||
char phys[32];
|
||||
struct workqueue_struct *wq;
|
||||
struct delayed_work work;
|
||||
unsigned polling; /* polling is active */
|
||||
struct ts_event tc;
|
||||
struct tps6507x_dev *mfd;
|
||||
u16 model;
|
||||
unsigned pendown;
|
||||
int irq;
|
||||
void (*clear_penirq)(void);
|
||||
unsigned long poll_period; /* ms */
|
||||
u16 min_pressure;
|
||||
int vref; /* non-zero to leave vref on */
|
||||
};
|
||||
|
||||
static int tps6507x_read_u8(struct tps6507x_ts *tsc, u8 reg, u8 *data)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = tsc->mfd->read_dev(tsc->mfd, reg, 1, data);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tps6507x_write_u8(struct tps6507x_ts *tsc, u8 reg, u8 data)
|
||||
{
|
||||
return tsc->mfd->write_dev(tsc->mfd, reg, 1, &data);
|
||||
}
|
||||
|
||||
static s32 tps6507x_adc_conversion(struct tps6507x_ts *tsc,
|
||||
u8 tsc_mode, u16 *value)
|
||||
{
|
||||
s32 ret;
|
||||
u8 adc_status;
|
||||
u8 result;
|
||||
|
||||
/* Route input signal to A/D converter */
|
||||
|
||||
ret = tps6507x_write_u8(tsc, TPS6507X_REG_TSCMODE, tsc_mode);
|
||||
if (ret) {
|
||||
dev_err(tsc->dev, "TSC mode read failed\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Start A/D conversion */
|
||||
|
||||
ret = tps6507x_write_u8(tsc, TPS6507X_REG_ADCONFIG,
|
||||
TPS6507X_ADCONFIG_CONVERT_TS);
|
||||
if (ret) {
|
||||
dev_err(tsc->dev, "ADC config write failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
do {
|
||||
ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADCONFIG,
|
||||
&adc_status);
|
||||
if (ret) {
|
||||
dev_err(tsc->dev, "ADC config read failed\n");
|
||||
goto err;
|
||||
}
|
||||
} while (adc_status & TPS6507X_ADCONFIG_START_CONVERSION);
|
||||
|
||||
ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADRESULT_2, &result);
|
||||
if (ret) {
|
||||
dev_err(tsc->dev, "ADC result 2 read failed\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
*value = (result & TPS6507X_REG_ADRESULT_2_MASK) << 8;
|
||||
|
||||
ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADRESULT_1, &result);
|
||||
if (ret) {
|
||||
dev_err(tsc->dev, "ADC result 1 read failed\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
*value |= result;
|
||||
|
||||
dev_dbg(tsc->dev, "TSC channel %d = 0x%X\n", tsc_mode, *value);
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Need to call tps6507x_adc_standby() after using A/D converter for the
|
||||
* touch screen interrupt to work properly.
|
||||
*/
|
||||
|
||||
static s32 tps6507x_adc_standby(struct tps6507x_ts *tsc)
|
||||
{
|
||||
s32 ret;
|
||||
s32 loops = 0;
|
||||
u8 val;
|
||||
|
||||
ret = tps6507x_write_u8(tsc, TPS6507X_REG_ADCONFIG,
|
||||
TPS6507X_ADCONFIG_INPUT_TSC);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = tps6507x_write_u8(tsc, TPS6507X_REG_TSCMODE,
|
||||
TPS6507X_TSCMODE_STANDBY);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = tps6507x_read_u8(tsc, TPS6507X_REG_INT, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
while (val & TPS6507X_REG_TSC_INT) {
|
||||
mdelay(10);
|
||||
ret = tps6507x_read_u8(tsc, TPS6507X_REG_INT, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
loops++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void tps6507x_ts_handler(struct work_struct *work)
|
||||
{
|
||||
struct tps6507x_ts *tsc = container_of(work,
|
||||
struct tps6507x_ts, work.work);
|
||||
struct input_dev *input_dev = tsc->input_dev;
|
||||
int pendown;
|
||||
int schd;
|
||||
int poll = 0;
|
||||
s32 ret;
|
||||
|
||||
ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_PRESSURE,
|
||||
&tsc->tc.pressure);
|
||||
if (ret)
|
||||
goto done;
|
||||
|
||||
pendown = tsc->tc.pressure > tsc->min_pressure;
|
||||
|
||||
if (unlikely(!pendown && tsc->pendown)) {
|
||||
dev_dbg(tsc->dev, "UP\n");
|
||||
input_report_key(input_dev, BTN_TOUCH, 0);
|
||||
input_report_abs(input_dev, ABS_PRESSURE, 0);
|
||||
input_sync(input_dev);
|
||||
tsc->pendown = 0;
|
||||
}
|
||||
|
||||
if (pendown) {
|
||||
|
||||
if (!tsc->pendown) {
|
||||
dev_dbg(tsc->dev, "DOWN\n");
|
||||
input_report_key(input_dev, BTN_TOUCH, 1);
|
||||
} else
|
||||
dev_dbg(tsc->dev, "still down\n");
|
||||
|
||||
ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_X_POSITION,
|
||||
&tsc->tc.x);
|
||||
if (ret)
|
||||
goto done;
|
||||
|
||||
ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_Y_POSITION,
|
||||
&tsc->tc.y);
|
||||
if (ret)
|
||||
goto done;
|
||||
|
||||
input_report_abs(input_dev, ABS_X, tsc->tc.x);
|
||||
input_report_abs(input_dev, ABS_Y, tsc->tc.y);
|
||||
input_report_abs(input_dev, ABS_PRESSURE, tsc->tc.pressure);
|
||||
input_sync(input_dev);
|
||||
tsc->pendown = 1;
|
||||
poll = 1;
|
||||
}
|
||||
|
||||
done:
|
||||
/* always poll if not using interrupts */
|
||||
poll = 1;
|
||||
|
||||
if (poll) {
|
||||
schd = queue_delayed_work(tsc->wq, &tsc->work,
|
||||
tsc->poll_period * HZ / 1000);
|
||||
if (schd)
|
||||
tsc->polling = 1;
|
||||
else {
|
||||
tsc->polling = 0;
|
||||
dev_err(tsc->dev, "re-schedule failed");
|
||||
}
|
||||
} else
|
||||
tsc->polling = 0;
|
||||
|
||||
ret = tps6507x_adc_standby(tsc);
|
||||
}
|
||||
|
||||
static int tps6507x_ts_probe(struct platform_device *pdev)
|
||||
{
|
||||
int error;
|
||||
struct tps6507x_ts *tsc;
|
||||
struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent);
|
||||
struct touchscreen_init_data *init_data;
|
||||
struct input_dev *input_dev;
|
||||
struct tps6507x_board *tps_board;
|
||||
int schd;
|
||||
|
||||
/**
|
||||
* tps_board points to pmic related constants
|
||||
* coming from the board-evm file.
|
||||
*/
|
||||
|
||||
tps_board = (struct tps6507x_board *)tps6507x_dev->dev->platform_data;
|
||||
|
||||
if (!tps_board) {
|
||||
dev_err(tps6507x_dev->dev,
|
||||
"Could not find tps6507x platform data\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/**
|
||||
* init_data points to array of regulator_init structures
|
||||
* coming from the board-evm file.
|
||||
*/
|
||||
|
||||
init_data = tps_board->tps6507x_ts_init_data;
|
||||
|
||||
tsc = kzalloc(sizeof(struct tps6507x_ts), GFP_KERNEL);
|
||||
if (!tsc) {
|
||||
dev_err(tps6507x_dev->dev, "failed to allocate driver data\n");
|
||||
error = -ENOMEM;
|
||||
goto err0;
|
||||
}
|
||||
|
||||
tps6507x_dev->ts = tsc;
|
||||
tsc->mfd = tps6507x_dev;
|
||||
tsc->dev = tps6507x_dev->dev;
|
||||
input_dev = input_allocate_device();
|
||||
if (!input_dev) {
|
||||
dev_err(tsc->dev, "Failed to allocate input device.\n");
|
||||
error = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
||||
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_X, 0, MAX_10BIT, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0, MAX_10BIT, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_10BIT, 0, 0);
|
||||
|
||||
input_dev->name = "TPS6507x Touchscreen";
|
||||
input_dev->id.bustype = BUS_I2C;
|
||||
input_dev->dev.parent = tsc->dev;
|
||||
|
||||
snprintf(tsc->phys, sizeof(tsc->phys),
|
||||
"%s/input0", dev_name(tsc->dev));
|
||||
input_dev->phys = tsc->phys;
|
||||
|
||||
dev_dbg(tsc->dev, "device: %s\n", input_dev->phys);
|
||||
|
||||
input_set_drvdata(input_dev, tsc);
|
||||
|
||||
tsc->input_dev = input_dev;
|
||||
|
||||
INIT_DELAYED_WORK(&tsc->work, tps6507x_ts_handler);
|
||||
tsc->wq = create_workqueue("TPS6507x Touchscreen");
|
||||
|
||||
if (init_data) {
|
||||
tsc->poll_period = init_data->poll_period;
|
||||
tsc->vref = init_data->vref;
|
||||
tsc->min_pressure = init_data->min_pressure;
|
||||
input_dev->id.vendor = init_data->vendor;
|
||||
input_dev->id.product = init_data->product;
|
||||
input_dev->id.version = init_data->version;
|
||||
} else {
|
||||
tsc->poll_period = TSC_DEFAULT_POLL_PERIOD;
|
||||
tsc->min_pressure = TPS_DEFAULT_MIN_PRESSURE;
|
||||
}
|
||||
|
||||
error = tps6507x_adc_standby(tsc);
|
||||
if (error)
|
||||
goto err2;
|
||||
|
||||
error = input_register_device(input_dev);
|
||||
if (error)
|
||||
goto err2;
|
||||
|
||||
schd = queue_delayed_work(tsc->wq, &tsc->work,
|
||||
tsc->poll_period * HZ / 1000);
|
||||
|
||||
if (schd)
|
||||
tsc->polling = 1;
|
||||
else {
|
||||
tsc->polling = 0;
|
||||
dev_err(tsc->dev, "schedule failed");
|
||||
goto err2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err2:
|
||||
cancel_delayed_work(&tsc->work);
|
||||
flush_workqueue(tsc->wq);
|
||||
destroy_workqueue(tsc->wq);
|
||||
tsc->wq = 0;
|
||||
input_free_device(input_dev);
|
||||
err1:
|
||||
kfree(tsc);
|
||||
tps6507x_dev->ts = NULL;
|
||||
err0:
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __devexit tps6507x_ts_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tps6507x_dev *tps6507x_dev = platform_get_drvdata(pdev);
|
||||
struct tps6507x_ts *tsc = tps6507x_dev->ts;
|
||||
struct input_dev *input_dev = tsc->input_dev;
|
||||
|
||||
if (!tsc)
|
||||
return 0;
|
||||
|
||||
cancel_delayed_work(&tsc->work);
|
||||
flush_workqueue(tsc->wq);
|
||||
destroy_workqueue(tsc->wq);
|
||||
tsc->wq = 0;
|
||||
|
||||
input_free_device(input_dev);
|
||||
|
||||
tps6507x_dev->ts = NULL;
|
||||
kfree(tsc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver tps6507x_ts_driver = {
|
||||
.driver = {
|
||||
.name = "tps6507x-ts",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = tps6507x_ts_probe,
|
||||
.remove = __devexit_p(tps6507x_ts_remove),
|
||||
};
|
||||
|
||||
static int __init tps6507x_ts_init(void)
|
||||
{
|
||||
return platform_driver_register(&tps6507x_ts_driver);
|
||||
}
|
||||
module_init(tps6507x_ts_init);
|
||||
|
||||
static void __exit tps6507x_ts_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&tps6507x_ts_driver);
|
||||
}
|
||||
module_exit(tps6507x_ts_exit);
|
||||
|
||||
MODULE_AUTHOR("Todd Fischer <todd.fischer@ridgerun.com>");
|
||||
MODULE_DESCRIPTION("TPS6507x - TouchScreen driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:tps6507x-tsc");
|
@ -566,7 +566,7 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __devexit device_irq_exit(struct pm860x_chip *chip)
|
||||
static void device_irq_exit(struct pm860x_chip *chip)
|
||||
{
|
||||
if (chip->core_irq)
|
||||
free_irq(chip->core_irq, chip);
|
||||
@ -703,7 +703,7 @@ out:
|
||||
return;
|
||||
}
|
||||
|
||||
int pm860x_device_init(struct pm860x_chip *chip,
|
||||
int __devinit pm860x_device_init(struct pm860x_chip *chip,
|
||||
struct pm860x_platform_data *pdata)
|
||||
{
|
||||
chip->core_irq = 0;
|
||||
@ -731,7 +731,7 @@ int pm860x_device_init(struct pm860x_chip *chip,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pm860x_device_exit(struct pm860x_chip *chip)
|
||||
void __devexit pm860x_device_exit(struct pm860x_chip *chip)
|
||||
{
|
||||
device_irq_exit(chip);
|
||||
mfd_remove_devices(chip->dev);
|
||||
|
@ -200,8 +200,8 @@ static int __devexit pm860x_remove(struct i2c_client *client)
|
||||
|
||||
pm860x_device_exit(chip);
|
||||
i2c_unregister_device(chip->companion);
|
||||
i2c_set_clientdata(chip->companion, NULL);
|
||||
i2c_set_clientdata(chip->client, NULL);
|
||||
i2c_set_clientdata(client, NULL);
|
||||
kfree(chip);
|
||||
return 0;
|
||||
}
|
||||
|
@ -2,8 +2,14 @@
|
||||
# Multifunction miscellaneous devices
|
||||
#
|
||||
|
||||
menu "Multifunction device drivers"
|
||||
menuconfig MFD_SUPPORT
|
||||
bool "Multifunction device drivers"
|
||||
depends on HAS_IOMEM
|
||||
default y
|
||||
help
|
||||
Configure MFD device drivers.
|
||||
|
||||
if MFD_SUPPORT
|
||||
|
||||
config MFD_CORE
|
||||
tristate
|
||||
@ -116,6 +122,18 @@ config TPS65010
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called tps65010.
|
||||
|
||||
config TPS6507X
|
||||
tristate "TPS6507x Power Management / Touch Screen chips"
|
||||
select MFD_CORE
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for the TPS6507x series of
|
||||
Power Management / Touch Screen chips. These include voltage
|
||||
regulators, lithium ion/polymer battery charging, touch screen
|
||||
and other features that are often used in portable devices.
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called tps6507x.
|
||||
|
||||
config MENELAUS
|
||||
bool "Texas Instruments TWL92330/Menelaus PM chip"
|
||||
depends on I2C=y && ARCH_OMAP2
|
||||
@ -159,6 +177,17 @@ config TWL4030_CODEC
|
||||
select MFD_CORE
|
||||
default n
|
||||
|
||||
config MFD_TC35892
|
||||
bool "Support Toshiba TC35892"
|
||||
depends on I2C=y && GENERIC_HARDIRQS
|
||||
select MFD_CORE
|
||||
help
|
||||
Support for the Toshiba TC35892 I/O Expander.
|
||||
|
||||
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_TMIO
|
||||
bool
|
||||
default n
|
||||
@ -351,9 +380,19 @@ config PCF50633_GPIO
|
||||
Say yes here if you want to include support GPIO for pins on
|
||||
the PCF50633 chip.
|
||||
|
||||
config ABX500_CORE
|
||||
bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions"
|
||||
default y if ARCH_U300
|
||||
help
|
||||
Say yes here if you have the ABX500 Mixed Signal IC family
|
||||
chips. This core driver expose register access functions.
|
||||
Functionality specific drivers using these functions can
|
||||
remain unchanged when IC changes. Binding of the functions to
|
||||
actual register access is done by the IC core driver.
|
||||
|
||||
config AB3100_CORE
|
||||
bool "ST-Ericsson AB3100 Mixed Signal Circuit core functions"
|
||||
depends on I2C=y
|
||||
depends on I2C=y && ABX500_CORE
|
||||
default y if ARCH_U300
|
||||
help
|
||||
Select this to enable the AB3100 Mixed Signal IC core
|
||||
@ -381,15 +420,30 @@ config EZX_PCAP
|
||||
This enables the PCAP ASIC present on EZX Phones. This is
|
||||
needed for MMC, TouchScreen, Sound, USB, etc..
|
||||
|
||||
config AB4500_CORE
|
||||
tristate "ST-Ericsson's AB4500 Mixed Signal Power management chip"
|
||||
depends on SPI
|
||||
config AB8500_CORE
|
||||
bool "ST-Ericsson AB8500 Mixed Signal Power Management chip"
|
||||
depends on SPI=y && GENERIC_HARDIRQS
|
||||
select MFD_CORE
|
||||
help
|
||||
Select this option to enable access to AB4500 power management
|
||||
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.
|
||||
This chip embeds various other multimedia funtionalities as well.
|
||||
|
||||
config AB3550_CORE
|
||||
bool "ST-Ericsson AB3550 Mixed Signal Circuit core functions"
|
||||
select MFD_CORE
|
||||
depends on I2C=y && GENERIC_HARDIRQS && ABX500_CORE
|
||||
help
|
||||
Select this to enable the AB3550 Mixed Signal IC core
|
||||
functionality. This connects to a AB3550 on the I2C bus
|
||||
and expose a number of symbols needed for dependent devices
|
||||
to read and write registers and subscribe to events from
|
||||
this multi-functional IC. This is needed to use other features
|
||||
of the AB3550 such as battery-backed RTC, charging control,
|
||||
LEDs, vibrator, system power and temperature, power management
|
||||
and ALSA sound.
|
||||
|
||||
config MFD_TIMBERDALE
|
||||
tristate "Support for the Timberdale FPGA"
|
||||
select MFD_CORE
|
||||
@ -409,7 +463,26 @@ config LPC_SCH
|
||||
LPC bridge function of the Intel SCH provides support for
|
||||
System Management Bus and General Purpose I/O.
|
||||
|
||||
endmenu
|
||||
config MFD_RDC321X
|
||||
tristate "Support for RDC-R321x southbridge"
|
||||
select MFD_CORE
|
||||
depends on PCI
|
||||
help
|
||||
Say yes here if you want to have support for the RDC R-321x SoC
|
||||
southbridge which provides access to GPIOs and Watchdog using the
|
||||
southbridge PCI device configuration space.
|
||||
|
||||
config MFD_JANZ_CMODIO
|
||||
tristate "Support for Janz CMOD-IO PCI MODULbus Carrier Board"
|
||||
select MFD_CORE
|
||||
depends on PCI
|
||||
help
|
||||
This is the core driver for the Janz CMOD-IO PCI MODULbus
|
||||
carrier board. This device is a PCI to MODULbus bridge which may
|
||||
host many different types of MODULbus daughterboards, including
|
||||
CAN and GPIO controllers.
|
||||
|
||||
endif # MFD_SUPPORT
|
||||
|
||||
menu "Multimedia Capabilities Port drivers"
|
||||
depends on ARCH_SA1100
|
||||
|
@ -15,6 +15,7 @@ obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o
|
||||
obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o
|
||||
obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o
|
||||
|
||||
obj-$(CONFIG_MFD_TC35892) += tc35892.o
|
||||
obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o
|
||||
obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o tmio_core.o
|
||||
obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o tmio_core.o
|
||||
@ -29,6 +30,7 @@ obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o
|
||||
obj-$(CONFIG_MFD_WM8994) += wm8994-core.o wm8994-irq.o
|
||||
|
||||
obj-$(CONFIG_TPS65010) += tps65010.o
|
||||
obj-$(CONFIG_TPS6507X) += tps6507x.o
|
||||
obj-$(CONFIG_MENELAUS) += menelaus.o
|
||||
|
||||
obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o
|
||||
@ -55,12 +57,17 @@ obj-$(CONFIG_PMIC_DA903X) += da903x.o
|
||||
max8925-objs := max8925-core.o max8925-i2c.o
|
||||
obj-$(CONFIG_MFD_MAX8925) += max8925.o
|
||||
|
||||
obj-$(CONFIG_MFD_PCF50633) += pcf50633-core.o
|
||||
pcf50633-objs := pcf50633-core.o pcf50633-irq.o
|
||||
obj-$(CONFIG_MFD_PCF50633) += pcf50633.o
|
||||
obj-$(CONFIG_PCF50633_ADC) += pcf50633-adc.o
|
||||
obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o
|
||||
obj-$(CONFIG_ABX500_CORE) += abx500-core.o
|
||||
obj-$(CONFIG_AB3100_CORE) += ab3100-core.o
|
||||
obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o
|
||||
obj-$(CONFIG_AB4500_CORE) += ab4500-core.o
|
||||
obj-$(CONFIG_AB3550_CORE) += ab3550-core.o
|
||||
obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-spi.o
|
||||
obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o
|
||||
obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
|
||||
obj-$(CONFIG_LPC_SCH) += lpc_sch.o
|
||||
obj-$(CONFIG_LPC_SCH) += lpc_sch.o
|
||||
obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o
|
||||
obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/mfd/ab3100.h>
|
||||
#include <linux/mfd/abx500.h>
|
||||
|
||||
/* These are the only registers inside AB3100 used in this main file */
|
||||
|
||||
@ -59,24 +59,15 @@
|
||||
* The AB3100 is usually assigned address 0x48 (7-bit)
|
||||
* The chip is defined in the platform i2c_board_data section.
|
||||
*/
|
||||
|
||||
u8 ab3100_get_chip_type(struct ab3100 *ab3100)
|
||||
static int ab3100_get_chip_id(struct device *dev)
|
||||
{
|
||||
u8 chip = ABUNKNOWN;
|
||||
struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
|
||||
|
||||
switch (ab3100->chip_id & 0xf0) {
|
||||
case 0xa0:
|
||||
chip = AB3000;
|
||||
break;
|
||||
case 0xc0:
|
||||
chip = AB3100;
|
||||
break;
|
||||
}
|
||||
return chip;
|
||||
return (int)ab3100->chip_id;
|
||||
}
|
||||
EXPORT_SYMBOL(ab3100_get_chip_type);
|
||||
|
||||
int ab3100_set_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 regval)
|
||||
static int ab3100_set_register_interruptible(struct ab3100 *ab3100,
|
||||
u8 reg, u8 regval)
|
||||
{
|
||||
u8 regandval[2] = {reg, regval};
|
||||
int err;
|
||||
@ -108,8 +99,14 @@ int ab3100_set_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 regval)
|
||||
mutex_unlock(&ab3100->access_mutex);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(ab3100_set_register_interruptible);
|
||||
|
||||
static int set_register_interruptible(struct device *dev,
|
||||
u8 bank, u8 reg, u8 value)
|
||||
{
|
||||
struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
|
||||
|
||||
return ab3100_set_register_interruptible(ab3100, reg, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* The test registers exist at an I2C bus address up one
|
||||
@ -148,8 +145,8 @@ static int ab3100_set_test_register_interruptible(struct ab3100 *ab3100,
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int ab3100_get_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 *regval)
|
||||
static int ab3100_get_register_interruptible(struct ab3100 *ab3100,
|
||||
u8 reg, u8 *regval)
|
||||
{
|
||||
int err;
|
||||
|
||||
@ -203,10 +200,16 @@ int ab3100_get_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 *regval)
|
||||
mutex_unlock(&ab3100->access_mutex);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(ab3100_get_register_interruptible);
|
||||
|
||||
static int get_register_interruptible(struct device *dev, u8 bank, u8 reg,
|
||||
u8 *value)
|
||||
{
|
||||
struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
|
||||
|
||||
int ab3100_get_register_page_interruptible(struct ab3100 *ab3100,
|
||||
return ab3100_get_register_interruptible(ab3100, reg, value);
|
||||
}
|
||||
|
||||
static int ab3100_get_register_page_interruptible(struct ab3100 *ab3100,
|
||||
u8 first_reg, u8 *regvals, u8 numregs)
|
||||
{
|
||||
int err;
|
||||
@ -260,10 +263,17 @@ int ab3100_get_register_page_interruptible(struct ab3100 *ab3100,
|
||||
mutex_unlock(&ab3100->access_mutex);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(ab3100_get_register_page_interruptible);
|
||||
|
||||
static int get_register_page_interruptible(struct device *dev, u8 bank,
|
||||
u8 first_reg, u8 *regvals, u8 numregs)
|
||||
{
|
||||
struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
|
||||
|
||||
int ab3100_mask_and_set_register_interruptible(struct ab3100 *ab3100,
|
||||
return ab3100_get_register_page_interruptible(ab3100,
|
||||
first_reg, regvals, numregs);
|
||||
}
|
||||
|
||||
static int ab3100_mask_and_set_register_interruptible(struct ab3100 *ab3100,
|
||||
u8 reg, u8 andmask, u8 ormask)
|
||||
{
|
||||
u8 regandval[2] = {reg, 0};
|
||||
@ -331,8 +341,15 @@ int ab3100_mask_and_set_register_interruptible(struct ab3100 *ab3100,
|
||||
mutex_unlock(&ab3100->access_mutex);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(ab3100_mask_and_set_register_interruptible);
|
||||
|
||||
static int mask_and_set_register_interruptible(struct device *dev, u8 bank,
|
||||
u8 reg, u8 bitmask, u8 bitvalues)
|
||||
{
|
||||
struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
|
||||
|
||||
return ab3100_mask_and_set_register_interruptible(ab3100,
|
||||
reg, bitmask, (bitmask & bitvalues));
|
||||
}
|
||||
|
||||
/*
|
||||
* Register a simple callback for handling any AB3100 events.
|
||||
@ -357,15 +374,27 @@ int ab3100_event_unregister(struct ab3100 *ab3100,
|
||||
EXPORT_SYMBOL(ab3100_event_unregister);
|
||||
|
||||
|
||||
int ab3100_event_registers_startup_state_get(struct ab3100 *ab3100,
|
||||
u32 *fatevent)
|
||||
static int ab3100_event_registers_startup_state_get(struct device *dev,
|
||||
u8 *event)
|
||||
{
|
||||
struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
|
||||
if (!ab3100->startup_events_read)
|
||||
return -EAGAIN; /* Try again later */
|
||||
*fatevent = ab3100->startup_events;
|
||||
memcpy(event, ab3100->startup_events, 3);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ab3100_event_registers_startup_state_get);
|
||||
|
||||
static struct abx500_ops ab3100_ops = {
|
||||
.get_chip_id = ab3100_get_chip_id,
|
||||
.set_register = set_register_interruptible,
|
||||
.get_register = get_register_interruptible,
|
||||
.get_register_page = get_register_page_interruptible,
|
||||
.set_register_page = NULL,
|
||||
.mask_and_set_register = mask_and_set_register_interruptible,
|
||||
.event_registers_startup_state_get =
|
||||
ab3100_event_registers_startup_state_get,
|
||||
.startup_irq_enabled = NULL,
|
||||
};
|
||||
|
||||
/*
|
||||
* This is a threaded interrupt handler so we can make some
|
||||
@ -390,7 +419,9 @@ static irqreturn_t ab3100_irq_handler(int irq, void *data)
|
||||
event_regs[2];
|
||||
|
||||
if (!ab3100->startup_events_read) {
|
||||
ab3100->startup_events = fatevent;
|
||||
ab3100->startup_events[0] = event_regs[0];
|
||||
ab3100->startup_events[1] = event_regs[1];
|
||||
ab3100->startup_events[2] = event_regs[2];
|
||||
ab3100->startup_events_read = true;
|
||||
}
|
||||
/*
|
||||
@ -703,7 +734,8 @@ static int __init ab3100_setup(struct ab3100 *ab3100)
|
||||
dev_warn(ab3100->dev,
|
||||
"AB3100 P1E variant detected, "
|
||||
"forcing chip to 32KHz\n");
|
||||
err = ab3100_set_test_register_interruptible(ab3100, 0x02, 0x08);
|
||||
err = ab3100_set_test_register_interruptible(ab3100,
|
||||
0x02, 0x08);
|
||||
}
|
||||
|
||||
exit_no_setup:
|
||||
@ -898,6 +930,10 @@ static int __init ab3100_probe(struct i2c_client *client,
|
||||
if (err)
|
||||
goto exit_no_irq;
|
||||
|
||||
err = abx500_register_ops(&client->dev, &ab3100_ops);
|
||||
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 =
|
||||
@ -915,11 +951,13 @@ static int __init ab3100_probe(struct i2c_client *client,
|
||||
|
||||
return 0;
|
||||
|
||||
exit_no_ops:
|
||||
exit_no_irq:
|
||||
exit_no_setup:
|
||||
i2c_unregister_device(ab3100->testreg_client);
|
||||
exit_no_testreg_client:
|
||||
exit_no_detect:
|
||||
i2c_set_clientdata(client, NULL);
|
||||
kfree(ab3100);
|
||||
return err;
|
||||
}
|
||||
@ -941,6 +979,7 @@ static int __exit ab3100_remove(struct i2c_client *client)
|
||||
* their notifiers so deactivate IRQ
|
||||
*/
|
||||
free_irq(client->irq, ab3100);
|
||||
i2c_set_clientdata(client, NULL);
|
||||
kfree(ab3100);
|
||||
return 0;
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/ab3100.h>
|
||||
#include <linux/mfd/abx500.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
@ -30,7 +30,6 @@
|
||||
/**
|
||||
* struct ab3100_otp
|
||||
* @dev containing device
|
||||
* @ab3100 a pointer to the parent ab3100 device struct
|
||||
* @locked whether the OTP is locked, after locking, no more bits
|
||||
* can be changed but before locking it is still possible
|
||||
* to change bits from 1->0.
|
||||
@ -49,7 +48,6 @@
|
||||
*/
|
||||
struct ab3100_otp {
|
||||
struct device *dev;
|
||||
struct ab3100 *ab3100;
|
||||
bool locked;
|
||||
u32 freq;
|
||||
bool paf;
|
||||
@ -63,19 +61,19 @@ struct ab3100_otp {
|
||||
|
||||
static int __init ab3100_otp_read(struct ab3100_otp *otp)
|
||||
{
|
||||
struct ab3100 *ab = otp->ab3100;
|
||||
u8 otpval[8];
|
||||
u8 otpp;
|
||||
int err;
|
||||
|
||||
err = ab3100_get_register_interruptible(ab, AB3100_OTPP, &otpp);
|
||||
err = abx500_get_register_interruptible(otp->dev, 0,
|
||||
AB3100_OTPP, &otpp);
|
||||
if (err) {
|
||||
dev_err(otp->dev, "unable to read OTPP register\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = ab3100_get_register_page_interruptible(ab, AB3100_OTP0,
|
||||
otpval, 8);
|
||||
err = abx500_get_register_page_interruptible(otp->dev, 0,
|
||||
AB3100_OTP0, otpval, 8);
|
||||
if (err) {
|
||||
dev_err(otp->dev, "unable to read OTP register page\n");
|
||||
return err;
|
||||
@ -197,7 +195,6 @@ static int __init ab3100_otp_probe(struct platform_device *pdev)
|
||||
otp->dev = &pdev->dev;
|
||||
|
||||
/* Replace platform data coming in with a local struct */
|
||||
otp->ab3100 = platform_get_drvdata(pdev);
|
||||
platform_set_drvdata(pdev, otp);
|
||||
|
||||
err = ab3100_otp_read(otp);
|
||||
|
1401
drivers/mfd/ab3550-core.c
Normal file
1401
drivers/mfd/ab3550-core.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,209 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2009 ST-Ericsson
|
||||
*
|
||||
* Author: Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it
|
||||
* and/or modify it under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* AB4500 is a companion power management chip used with U8500.
|
||||
* On this platform, this is interfaced with SSP0 controller
|
||||
* which is a ARM primecell pl022.
|
||||
*
|
||||
* At the moment the module just exports read/write features.
|
||||
* Interrupt management to be added - TODO.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/mfd/ab4500.h>
|
||||
|
||||
/* just required if probe fails, we need to
|
||||
* unregister the device
|
||||
*/
|
||||
static struct spi_driver ab4500_driver;
|
||||
|
||||
/*
|
||||
* This funtion writes to any AB4500 registers using
|
||||
* SPI protocol & before it writes it packs the data
|
||||
* in the below 24 bit frame format
|
||||
*
|
||||
* *|------------------------------------|
|
||||
* *| 23|22...18|17.......10|9|8|7......0|
|
||||
* *| r/w bank adr data |
|
||||
* * ------------------------------------
|
||||
*
|
||||
* This function shouldn't be called from interrupt
|
||||
* context
|
||||
*/
|
||||
int ab4500_write(struct ab4500 *ab4500, unsigned char block,
|
||||
unsigned long addr, unsigned char data)
|
||||
{
|
||||
struct spi_transfer xfer;
|
||||
struct spi_message msg;
|
||||
int err;
|
||||
unsigned long spi_data =
|
||||
block << 18 | addr << 10 | data;
|
||||
|
||||
mutex_lock(&ab4500->lock);
|
||||
ab4500->tx_buf[0] = spi_data;
|
||||
ab4500->rx_buf[0] = 0;
|
||||
|
||||
xfer.tx_buf = ab4500->tx_buf;
|
||||
xfer.rx_buf = NULL;
|
||||
xfer.len = sizeof(unsigned long);
|
||||
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&xfer, &msg);
|
||||
|
||||
err = spi_sync(ab4500->spi, &msg);
|
||||
mutex_unlock(&ab4500->lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(ab4500_write);
|
||||
|
||||
int ab4500_read(struct ab4500 *ab4500, unsigned char block,
|
||||
unsigned long addr)
|
||||
{
|
||||
struct spi_transfer xfer;
|
||||
struct spi_message msg;
|
||||
unsigned long spi_data =
|
||||
1 << 23 | block << 18 | addr << 10;
|
||||
|
||||
mutex_lock(&ab4500->lock);
|
||||
ab4500->tx_buf[0] = spi_data;
|
||||
ab4500->rx_buf[0] = 0;
|
||||
|
||||
xfer.tx_buf = ab4500->tx_buf;
|
||||
xfer.rx_buf = ab4500->rx_buf;
|
||||
xfer.len = sizeof(unsigned long);
|
||||
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&xfer, &msg);
|
||||
|
||||
spi_sync(ab4500->spi, &msg);
|
||||
mutex_unlock(&ab4500->lock);
|
||||
|
||||
return ab4500->rx_buf[0];
|
||||
}
|
||||
EXPORT_SYMBOL(ab4500_read);
|
||||
|
||||
/* ref: ab3100 core */
|
||||
#define AB4500_DEVICE(devname, devid) \
|
||||
static struct platform_device ab4500_##devname##_device = { \
|
||||
.name = devid, \
|
||||
.id = -1, \
|
||||
}
|
||||
|
||||
/* list of childern devices of ab4500 - all are
|
||||
* not populated here - TODO
|
||||
*/
|
||||
AB4500_DEVICE(charger, "ab4500-charger");
|
||||
AB4500_DEVICE(audio, "ab4500-audio");
|
||||
AB4500_DEVICE(usb, "ab4500-usb");
|
||||
AB4500_DEVICE(tvout, "ab4500-tvout");
|
||||
AB4500_DEVICE(sim, "ab4500-sim");
|
||||
AB4500_DEVICE(gpadc, "ab4500-gpadc");
|
||||
AB4500_DEVICE(clkmgt, "ab4500-clkmgt");
|
||||
AB4500_DEVICE(misc, "ab4500-misc");
|
||||
|
||||
static struct platform_device *ab4500_platform_devs[] = {
|
||||
&ab4500_charger_device,
|
||||
&ab4500_audio_device,
|
||||
&ab4500_usb_device,
|
||||
&ab4500_tvout_device,
|
||||
&ab4500_sim_device,
|
||||
&ab4500_gpadc_device,
|
||||
&ab4500_clkmgt_device,
|
||||
&ab4500_misc_device,
|
||||
};
|
||||
|
||||
static int __init ab4500_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ab4500 *ab4500;
|
||||
unsigned char revision;
|
||||
int err = 0;
|
||||
int i;
|
||||
|
||||
ab4500 = kzalloc(sizeof *ab4500, GFP_KERNEL);
|
||||
if (!ab4500) {
|
||||
dev_err(&spi->dev, "could not allocate AB4500\n");
|
||||
err = -ENOMEM;
|
||||
goto not_detect;
|
||||
}
|
||||
|
||||
ab4500->spi = spi;
|
||||
spi_set_drvdata(spi, ab4500);
|
||||
|
||||
mutex_init(&ab4500->lock);
|
||||
|
||||
/* read the revision register */
|
||||
revision = ab4500_read(ab4500, AB4500_MISC, AB4500_REV_REG);
|
||||
|
||||
/* revision id 0x0 is for early drop, 0x10 is for cut1.0 */
|
||||
if (revision == 0x0 || revision == 0x10)
|
||||
dev_info(&spi->dev, "Detected chip: %s, revision = %x\n",
|
||||
ab4500_driver.driver.name, revision);
|
||||
else {
|
||||
dev_err(&spi->dev, "unknown chip: 0x%x\n", revision);
|
||||
goto not_detect;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ab4500_platform_devs); i++) {
|
||||
ab4500_platform_devs[i]->dev.parent =
|
||||
&spi->dev;
|
||||
platform_set_drvdata(ab4500_platform_devs[i], ab4500);
|
||||
}
|
||||
|
||||
/* register the ab4500 platform devices */
|
||||
platform_add_devices(ab4500_platform_devs,
|
||||
ARRAY_SIZE(ab4500_platform_devs));
|
||||
|
||||
return err;
|
||||
|
||||
not_detect:
|
||||
spi_unregister_driver(&ab4500_driver);
|
||||
kfree(ab4500);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devexit ab4500_remove(struct spi_device *spi)
|
||||
{
|
||||
struct ab4500 *ab4500 =
|
||||
spi_get_drvdata(spi);
|
||||
|
||||
kfree(ab4500);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct spi_driver ab4500_driver = {
|
||||
.driver = {
|
||||
.name = "ab4500",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ab4500_probe,
|
||||
.remove = __devexit_p(ab4500_remove)
|
||||
};
|
||||
|
||||
static int __devinit ab4500_init(void)
|
||||
{
|
||||
return spi_register_driver(&ab4500_driver);
|
||||
}
|
||||
|
||||
static void __exit ab4500_exit(void)
|
||||
{
|
||||
spi_unregister_driver(&ab4500_driver);
|
||||
}
|
||||
|
||||
subsys_initcall(ab4500_init);
|
||||
module_exit(ab4500_exit);
|
||||
|
||||
MODULE_AUTHOR("Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com");
|
||||
MODULE_DESCRIPTION("AB4500 core driver");
|
||||
MODULE_LICENSE("GPL");
|
444
drivers/mfd/ab8500-core.c
Normal file
444
drivers/mfd/ab8500-core.c
Normal file
@ -0,0 +1,444 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* License Terms: GNU General Public License v2
|
||||
* Author: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com>
|
||||
* Author: Rabin Vincent <rabin.vincent@stericsson.com>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/ab8500.h>
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
||||
/*
|
||||
* mask registers
|
||||
*/
|
||||
|
||||
#define AB8500_IT_MASK1_REG 0x0E40
|
||||
#define AB8500_IT_MASK2_REG 0x0E41
|
||||
#define AB8500_IT_MASK3_REG 0x0E42
|
||||
#define AB8500_IT_MASK4_REG 0x0E43
|
||||
#define AB8500_IT_MASK5_REG 0x0E44
|
||||
#define AB8500_IT_MASK6_REG 0x0E45
|
||||
#define AB8500_IT_MASK7_REG 0x0E46
|
||||
#define AB8500_IT_MASK8_REG 0x0E47
|
||||
#define AB8500_IT_MASK9_REG 0x0E48
|
||||
#define AB8500_IT_MASK10_REG 0x0E49
|
||||
#define AB8500_IT_MASK11_REG 0x0E4A
|
||||
#define AB8500_IT_MASK12_REG 0x0E4B
|
||||
#define AB8500_IT_MASK13_REG 0x0E4C
|
||||
#define AB8500_IT_MASK14_REG 0x0E4D
|
||||
#define AB8500_IT_MASK15_REG 0x0E4E
|
||||
#define AB8500_IT_MASK16_REG 0x0E4F
|
||||
#define AB8500_IT_MASK17_REG 0x0E50
|
||||
#define AB8500_IT_MASK18_REG 0x0E51
|
||||
#define AB8500_IT_MASK19_REG 0x0E52
|
||||
#define AB8500_IT_MASK20_REG 0x0E53
|
||||
#define AB8500_IT_MASK21_REG 0x0E54
|
||||
#define AB8500_IT_MASK22_REG 0x0E55
|
||||
#define AB8500_IT_MASK23_REG 0x0E56
|
||||
#define AB8500_IT_MASK24_REG 0x0E57
|
||||
|
||||
#define AB8500_REV_REG 0x1080
|
||||
|
||||
/*
|
||||
* Map interrupt numbers to the LATCH and MASK register offsets, Interrupt
|
||||
* numbers are indexed into this array with (num / 8).
|
||||
*
|
||||
* This is one off from the register names, i.e. AB8500_IT_MASK1_REG is at
|
||||
* offset 0.
|
||||
*/
|
||||
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)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dev_vdbg(ab8500->dev, "wr: addr %#x <= %#x\n", addr, data);
|
||||
|
||||
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);
|
||||
|
||||
out:
|
||||
mutex_unlock(&ab8500->lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ab8500_set_bits);
|
||||
|
||||
static void ab8500_irq_lock(unsigned int irq)
|
||||
{
|
||||
struct ab8500 *ab8500 = get_irq_chip_data(irq);
|
||||
|
||||
mutex_lock(&ab8500->irq_lock);
|
||||
}
|
||||
|
||||
static void ab8500_irq_sync_unlock(unsigned int irq)
|
||||
{
|
||||
struct ab8500 *ab8500 = get_irq_chip_data(irq);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) {
|
||||
u8 old = ab8500->oldmask[i];
|
||||
u8 new = ab8500->mask[i];
|
||||
int reg;
|
||||
|
||||
if (new == old)
|
||||
continue;
|
||||
|
||||
ab8500->oldmask[i] = new;
|
||||
|
||||
reg = AB8500_IT_MASK1_REG + ab8500_irq_regoffset[i];
|
||||
ab8500_write(ab8500, reg, new);
|
||||
}
|
||||
|
||||
mutex_unlock(&ab8500->irq_lock);
|
||||
}
|
||||
|
||||
static void ab8500_irq_mask(unsigned int irq)
|
||||
{
|
||||
struct ab8500 *ab8500 = get_irq_chip_data(irq);
|
||||
int offset = irq - ab8500->irq_base;
|
||||
int index = offset / 8;
|
||||
int mask = 1 << (offset % 8);
|
||||
|
||||
ab8500->mask[index] |= mask;
|
||||
}
|
||||
|
||||
static void ab8500_irq_unmask(unsigned int irq)
|
||||
{
|
||||
struct ab8500 *ab8500 = get_irq_chip_data(irq);
|
||||
int offset = irq - ab8500->irq_base;
|
||||
int index = offset / 8;
|
||||
int mask = 1 << (offset % 8);
|
||||
|
||||
ab8500->mask[index] &= ~mask;
|
||||
}
|
||||
|
||||
static struct irq_chip ab8500_irq_chip = {
|
||||
.name = "ab8500",
|
||||
.bus_lock = ab8500_irq_lock,
|
||||
.bus_sync_unlock = ab8500_irq_sync_unlock,
|
||||
.mask = ab8500_irq_mask,
|
||||
.unmask = ab8500_irq_unmask,
|
||||
};
|
||||
|
||||
static irqreturn_t ab8500_irq(int irq, void *dev)
|
||||
{
|
||||
struct ab8500 *ab8500 = dev;
|
||||
int i;
|
||||
|
||||
dev_vdbg(ab8500->dev, "interrupt\n");
|
||||
|
||||
for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) {
|
||||
int regoffset = ab8500_irq_regoffset[i];
|
||||
int status;
|
||||
|
||||
status = ab8500_read(ab8500, AB8500_IT_LATCH1_REG + regoffset);
|
||||
if (status <= 0)
|
||||
continue;
|
||||
|
||||
do {
|
||||
int bit = __ffs(status);
|
||||
int line = i * 8 + bit;
|
||||
|
||||
handle_nested_irq(ab8500->irq_base + line);
|
||||
status &= ~(1 << bit);
|
||||
} while (status);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ab8500_irq_init(struct ab8500 *ab8500)
|
||||
{
|
||||
int base = ab8500->irq_base;
|
||||
int irq;
|
||||
|
||||
for (irq = base; irq < base + AB8500_NR_IRQS; irq++) {
|
||||
set_irq_chip_data(irq, ab8500);
|
||||
set_irq_chip_and_handler(irq, &ab8500_irq_chip,
|
||||
handle_simple_irq);
|
||||
set_irq_nested_thread(irq, 1);
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(irq, IRQF_VALID);
|
||||
#else
|
||||
set_irq_noprobe(irq);
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ab8500_irq_remove(struct ab8500 *ab8500)
|
||||
{
|
||||
int base = ab8500->irq_base;
|
||||
int irq;
|
||||
|
||||
for (irq = base; irq < base + AB8500_NR_IRQS; irq++) {
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(irq, 0);
|
||||
#endif
|
||||
set_irq_chip_and_handler(irq, NULL, NULL);
|
||||
set_irq_chip_data(irq, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static struct resource ab8500_gpadc_resources[] = {
|
||||
{
|
||||
.name = "HW_CONV_END",
|
||||
.start = AB8500_INT_GP_HW_ADC_CONV_END,
|
||||
.end = AB8500_INT_GP_HW_ADC_CONV_END,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
{
|
||||
.name = "SW_CONV_END",
|
||||
.start = AB8500_INT_GP_SW_ADC_CONV_END,
|
||||
.end = AB8500_INT_GP_SW_ADC_CONV_END,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource ab8500_rtc_resources[] = {
|
||||
{
|
||||
.name = "60S",
|
||||
.start = AB8500_INT_RTC_60S,
|
||||
.end = AB8500_INT_RTC_60S,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
{
|
||||
.name = "ALARM",
|
||||
.start = AB8500_INT_RTC_ALARM,
|
||||
.end = AB8500_INT_RTC_ALARM,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell ab8500_devs[] = {
|
||||
{
|
||||
.name = "ab8500-gpadc",
|
||||
.num_resources = ARRAY_SIZE(ab8500_gpadc_resources),
|
||||
.resources = ab8500_gpadc_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-rtc",
|
||||
.num_resources = ARRAY_SIZE(ab8500_rtc_resources),
|
||||
.resources = ab8500_rtc_resources,
|
||||
},
|
||||
{ .name = "ab8500-charger", },
|
||||
{ .name = "ab8500-audio", },
|
||||
{ .name = "ab8500-usb", },
|
||||
{ .name = "ab8500-pwm", },
|
||||
};
|
||||
|
||||
int __devinit ab8500_init(struct ab8500 *ab8500)
|
||||
{
|
||||
struct ab8500_platform_data *plat = dev_get_platdata(ab8500->dev);
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
if (plat)
|
||||
ab8500->irq_base = plat->irq_base;
|
||||
|
||||
mutex_init(&ab8500->lock);
|
||||
mutex_init(&ab8500->irq_lock);
|
||||
|
||||
ret = ab8500_read(ab8500, AB8500_REV_REG);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* 0x0 - Early Drop
|
||||
* 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);
|
||||
} else {
|
||||
dev_err(ab8500->dev, "unknown chip, revision: %#x\n", ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
for (i = 18; i < 24; i++) {
|
||||
ab8500_read(ab8500, AB8500_IT_LATCH1_REG + i);
|
||||
ab8500_write(ab8500, AB8500_IT_MASK1_REG + i, 0xff);
|
||||
}
|
||||
|
||||
for (i = 0; i < AB8500_NUM_IRQ_REGS; i++)
|
||||
ab8500->mask[i] = ab8500->oldmask[i] = 0xff;
|
||||
|
||||
if (ab8500->irq_base) {
|
||||
ret = ab8500_irq_init(ab8500);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = request_threaded_irq(ab8500->irq, NULL, ab8500_irq,
|
||||
IRQF_ONESHOT, "ab8500", ab8500);
|
||||
if (ret)
|
||||
goto out_removeirq;
|
||||
}
|
||||
|
||||
ret = mfd_add_devices(ab8500->dev, -1, ab8500_devs,
|
||||
ARRAY_SIZE(ab8500_devs), NULL,
|
||||
ab8500->irq_base);
|
||||
if (ret)
|
||||
goto out_freeirq;
|
||||
|
||||
return ret;
|
||||
|
||||
out_freeirq:
|
||||
if (ab8500->irq_base) {
|
||||
free_irq(ab8500->irq, ab8500);
|
||||
out_removeirq:
|
||||
ab8500_irq_remove(ab8500);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int __devexit ab8500_exit(struct ab8500 *ab8500)
|
||||
{
|
||||
mfd_remove_devices(ab8500->dev);
|
||||
if (ab8500->irq_base) {
|
||||
free_irq(ab8500->irq, ab8500);
|
||||
ab8500_irq_remove(ab8500);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Srinidhi Kasagar, Rabin Vincent");
|
||||
MODULE_DESCRIPTION("AB8500 MFD core");
|
||||
MODULE_LICENSE("GPL v2");
|
133
drivers/mfd/ab8500-spi.c
Normal file
133
drivers/mfd/ab8500-spi.c
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* License Terms: GNU General Public License v2
|
||||
* Author: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/mfd/ab8500.h>
|
||||
|
||||
/*
|
||||
* This funtion writes to any AB8500 registers using
|
||||
* SPI protocol & before it writes it packs the data
|
||||
* in the below 24 bit frame format
|
||||
*
|
||||
* *|------------------------------------|
|
||||
* *| 23|22...18|17.......10|9|8|7......0|
|
||||
* *| r/w bank adr data |
|
||||
* * ------------------------------------
|
||||
*
|
||||
* This function shouldn't be called from interrupt
|
||||
* context
|
||||
*/
|
||||
static int ab8500_spi_write(struct ab8500 *ab8500, u16 addr, u8 data)
|
||||
{
|
||||
struct spi_device *spi = container_of(ab8500->dev, struct spi_device,
|
||||
dev);
|
||||
unsigned long spi_data = addr << 10 | data;
|
||||
struct spi_transfer xfer;
|
||||
struct spi_message msg;
|
||||
|
||||
ab8500->tx_buf[0] = spi_data;
|
||||
ab8500->rx_buf[0] = 0;
|
||||
|
||||
xfer.tx_buf = ab8500->tx_buf;
|
||||
xfer.rx_buf = NULL;
|
||||
xfer.len = sizeof(unsigned long);
|
||||
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&xfer, &msg);
|
||||
|
||||
return spi_sync(spi, &msg);
|
||||
}
|
||||
|
||||
static int ab8500_spi_read(struct ab8500 *ab8500, u16 addr)
|
||||
{
|
||||
struct spi_device *spi = container_of(ab8500->dev, struct spi_device,
|
||||
dev);
|
||||
unsigned long spi_data = 1 << 23 | addr << 10;
|
||||
struct spi_transfer xfer;
|
||||
struct spi_message msg;
|
||||
int ret;
|
||||
|
||||
ab8500->tx_buf[0] = spi_data;
|
||||
ab8500->rx_buf[0] = 0;
|
||||
|
||||
xfer.tx_buf = ab8500->tx_buf;
|
||||
xfer.rx_buf = ab8500->rx_buf;
|
||||
xfer.len = sizeof(unsigned long);
|
||||
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&xfer, &msg);
|
||||
|
||||
ret = spi_sync(spi, &msg);
|
||||
if (!ret)
|
||||
ret = ab8500->rx_buf[0];
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devinit ab8500_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ab8500 *ab8500;
|
||||
int ret;
|
||||
|
||||
ab8500 = kzalloc(sizeof *ab8500, GFP_KERNEL);
|
||||
if (!ab8500)
|
||||
return -ENOMEM;
|
||||
|
||||
ab8500->dev = &spi->dev;
|
||||
ab8500->irq = spi->irq;
|
||||
|
||||
ab8500->read = ab8500_spi_read;
|
||||
ab8500->write = ab8500_spi_write;
|
||||
|
||||
spi_set_drvdata(spi, ab8500);
|
||||
|
||||
ret = ab8500_init(ab8500);
|
||||
if (ret)
|
||||
kfree(ab8500);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit ab8500_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
struct ab8500 *ab8500 = spi_get_drvdata(spi);
|
||||
|
||||
ab8500_exit(ab8500);
|
||||
kfree(ab8500);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct spi_driver ab8500_spi_driver = {
|
||||
.driver = {
|
||||
.name = "ab8500",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ab8500_spi_probe,
|
||||
.remove = __devexit_p(ab8500_spi_remove)
|
||||
};
|
||||
|
||||
static int __init ab8500_spi_init(void)
|
||||
{
|
||||
return spi_register_driver(&ab8500_spi_driver);
|
||||
}
|
||||
subsys_initcall(ab8500_spi_init);
|
||||
|
||||
static void __exit ab8500_spi_exit(void)
|
||||
{
|
||||
spi_unregister_driver(&ab8500_spi_driver);
|
||||
}
|
||||
module_exit(ab8500_spi_exit);
|
||||
|
||||
MODULE_AUTHOR("Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com");
|
||||
MODULE_DESCRIPTION("AB8500 SPI");
|
||||
MODULE_LICENSE("GPL v2");
|
157
drivers/mfd/abx500-core.c
Normal file
157
drivers/mfd/abx500-core.c
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2010 ST-Ericsson
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
* Register access functions for the ABX500 Mixed Signal IC family.
|
||||
* Author: Mattias Wallin <mattias.wallin@stericsson.com>
|
||||
*/
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mfd/abx500.h>
|
||||
|
||||
static LIST_HEAD(abx500_list);
|
||||
|
||||
struct abx500_device_entry {
|
||||
struct list_head list;
|
||||
struct abx500_ops ops;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
static void lookup_ops(struct device *dev, struct abx500_ops **ops)
|
||||
{
|
||||
struct abx500_device_entry *dev_entry;
|
||||
|
||||
*ops = NULL;
|
||||
list_for_each_entry(dev_entry, &abx500_list, list) {
|
||||
if (dev_entry->dev == dev) {
|
||||
*ops = &dev_entry->ops;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int abx500_register_ops(struct device *dev, struct abx500_ops *ops)
|
||||
{
|
||||
struct abx500_device_entry *dev_entry;
|
||||
|
||||
dev_entry = kzalloc(sizeof(struct abx500_device_entry), GFP_KERNEL);
|
||||
if (IS_ERR(dev_entry)) {
|
||||
dev_err(dev, "register_ops kzalloc failed");
|
||||
return -ENOMEM;
|
||||
}
|
||||
dev_entry->dev = dev;
|
||||
memcpy(&dev_entry->ops, ops, sizeof(struct abx500_ops));
|
||||
|
||||
list_add_tail(&dev_entry->list, &abx500_list);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(abx500_register_ops);
|
||||
|
||||
void abx500_remove_ops(struct device *dev)
|
||||
{
|
||||
struct abx500_device_entry *dev_entry, *tmp;
|
||||
|
||||
list_for_each_entry_safe(dev_entry, tmp, &abx500_list, list)
|
||||
{
|
||||
if (dev_entry->dev == dev) {
|
||||
list_del(&dev_entry->list);
|
||||
kfree(dev_entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(abx500_remove_ops);
|
||||
|
||||
int abx500_set_register_interruptible(struct device *dev, u8 bank, u8 reg,
|
||||
u8 value)
|
||||
{
|
||||
struct abx500_ops *ops;
|
||||
|
||||
lookup_ops(dev->parent, &ops);
|
||||
if ((ops != NULL) && (ops->set_register != NULL))
|
||||
return ops->set_register(dev, bank, reg, value);
|
||||
else
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
EXPORT_SYMBOL(abx500_set_register_interruptible);
|
||||
|
||||
int abx500_get_register_interruptible(struct device *dev, u8 bank, u8 reg,
|
||||
u8 *value)
|
||||
{
|
||||
struct abx500_ops *ops;
|
||||
|
||||
lookup_ops(dev->parent, &ops);
|
||||
if ((ops != NULL) && (ops->get_register != NULL))
|
||||
return ops->get_register(dev, bank, reg, value);
|
||||
else
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
EXPORT_SYMBOL(abx500_get_register_interruptible);
|
||||
|
||||
int abx500_get_register_page_interruptible(struct device *dev, u8 bank,
|
||||
u8 first_reg, u8 *regvals, u8 numregs)
|
||||
{
|
||||
struct abx500_ops *ops;
|
||||
|
||||
lookup_ops(dev->parent, &ops);
|
||||
if ((ops != NULL) && (ops->get_register_page != NULL))
|
||||
return ops->get_register_page(dev, bank,
|
||||
first_reg, regvals, numregs);
|
||||
else
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
EXPORT_SYMBOL(abx500_get_register_page_interruptible);
|
||||
|
||||
int abx500_mask_and_set_register_interruptible(struct device *dev, u8 bank,
|
||||
u8 reg, u8 bitmask, u8 bitvalues)
|
||||
{
|
||||
struct abx500_ops *ops;
|
||||
|
||||
lookup_ops(dev->parent, &ops);
|
||||
if ((ops != NULL) && (ops->mask_and_set_register != NULL))
|
||||
return ops->mask_and_set_register(dev, bank,
|
||||
reg, bitmask, bitvalues);
|
||||
else
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
EXPORT_SYMBOL(abx500_mask_and_set_register_interruptible);
|
||||
|
||||
int abx500_get_chip_id(struct device *dev)
|
||||
{
|
||||
struct abx500_ops *ops;
|
||||
|
||||
lookup_ops(dev->parent, &ops);
|
||||
if ((ops != NULL) && (ops->get_chip_id != NULL))
|
||||
return ops->get_chip_id(dev);
|
||||
else
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
EXPORT_SYMBOL(abx500_get_chip_id);
|
||||
|
||||
int abx500_event_registers_startup_state_get(struct device *dev, u8 *event)
|
||||
{
|
||||
struct abx500_ops *ops;
|
||||
|
||||
lookup_ops(dev->parent, &ops);
|
||||
if ((ops != NULL) && (ops->event_registers_startup_state_get != NULL))
|
||||
return ops->event_registers_startup_state_get(dev, event);
|
||||
else
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
EXPORT_SYMBOL(abx500_event_registers_startup_state_get);
|
||||
|
||||
int abx500_startup_irq_enabled(struct device *dev, unsigned int irq)
|
||||
{
|
||||
struct abx500_ops *ops;
|
||||
|
||||
lookup_ops(dev->parent, &ops);
|
||||
if ((ops != NULL) && (ops->startup_irq_enabled != NULL))
|
||||
return ops->startup_irq_enabled(dev, irq);
|
||||
else
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
EXPORT_SYMBOL(abx500_startup_irq_enabled);
|
||||
|
||||
MODULE_AUTHOR("Mattias Wallin <mattias.wallin@stericsson.com>");
|
||||
MODULE_DESCRIPTION("ABX500 core driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -544,6 +544,7 @@ static int __devexit da903x_remove(struct i2c_client *client)
|
||||
struct da903x_chip *chip = i2c_get_clientdata(client);
|
||||
|
||||
da903x_remove_subdevs(chip);
|
||||
i2c_set_clientdata(client, NULL);
|
||||
kfree(chip);
|
||||
return 0;
|
||||
}
|
||||
|
304
drivers/mfd/janz-cmodio.c
Normal file
304
drivers/mfd/janz-cmodio.c
Normal file
@ -0,0 +1,304 @@
|
||||
/*
|
||||
* Janz CMOD-IO MODULbus Carrier Board PCI Driver
|
||||
*
|
||||
* Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu>
|
||||
*
|
||||
* Lots of inspiration and code was copied from drivers/mfd/sm501.c
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/core.h>
|
||||
|
||||
#include <linux/mfd/janz.h>
|
||||
|
||||
#define DRV_NAME "janz-cmodio"
|
||||
|
||||
/* Size of each MODULbus module in PCI BAR4 */
|
||||
#define CMODIO_MODULBUS_SIZE 0x200
|
||||
|
||||
/* Maximum number of MODULbus modules on a CMOD-IO carrier board */
|
||||
#define CMODIO_MAX_MODULES 4
|
||||
|
||||
/* Module Parameters */
|
||||
static unsigned int num_modules = CMODIO_MAX_MODULES;
|
||||
static unsigned char *modules[CMODIO_MAX_MODULES] = {
|
||||
"empty", "empty", "empty", "empty",
|
||||
};
|
||||
|
||||
module_param_array(modules, charp, &num_modules, S_IRUGO);
|
||||
MODULE_PARM_DESC(modules, "MODULbus modules attached to the carrier board");
|
||||
|
||||
/* Unique Device Id */
|
||||
static unsigned int cmodio_id;
|
||||
|
||||
struct cmodio_device {
|
||||
/* Parent PCI device */
|
||||
struct pci_dev *pdev;
|
||||
|
||||
/* PLX control registers */
|
||||
struct janz_cmodio_onboard_regs __iomem *ctrl;
|
||||
|
||||
/* hex switch position */
|
||||
u8 hex;
|
||||
|
||||
/* mfd-core API */
|
||||
struct mfd_cell cells[CMODIO_MAX_MODULES];
|
||||
struct resource resources[3 * CMODIO_MAX_MODULES];
|
||||
struct janz_platform_data pdata[CMODIO_MAX_MODULES];
|
||||
};
|
||||
|
||||
/*
|
||||
* Subdevices using the mfd-core API
|
||||
*/
|
||||
|
||||
static int __devinit cmodio_setup_subdevice(struct cmodio_device *priv,
|
||||
char *name, unsigned int devno,
|
||||
unsigned int modno)
|
||||
{
|
||||
struct janz_platform_data *pdata;
|
||||
struct mfd_cell *cell;
|
||||
struct resource *res;
|
||||
struct pci_dev *pci;
|
||||
|
||||
pci = priv->pdev;
|
||||
cell = &priv->cells[devno];
|
||||
res = &priv->resources[devno * 3];
|
||||
pdata = &priv->pdata[devno];
|
||||
|
||||
cell->name = name;
|
||||
cell->resources = res;
|
||||
cell->num_resources = 3;
|
||||
|
||||
/* Setup the subdevice ID -- must be unique */
|
||||
cell->id = cmodio_id++;
|
||||
|
||||
/* Add platform data */
|
||||
pdata->modno = modno;
|
||||
cell->platform_data = pdata;
|
||||
cell->data_size = sizeof(*pdata);
|
||||
|
||||
/* MODULbus registers -- PCI BAR3 is big-endian MODULbus access */
|
||||
res->flags = IORESOURCE_MEM;
|
||||
res->parent = &pci->resource[3];
|
||||
res->start = pci->resource[3].start + (CMODIO_MODULBUS_SIZE * modno);
|
||||
res->end = res->start + CMODIO_MODULBUS_SIZE - 1;
|
||||
res++;
|
||||
|
||||
/* PLX Control Registers -- PCI BAR4 is interrupt and other registers */
|
||||
res->flags = IORESOURCE_MEM;
|
||||
res->parent = &pci->resource[4];
|
||||
res->start = pci->resource[4].start;
|
||||
res->end = pci->resource[4].end;
|
||||
res++;
|
||||
|
||||
/*
|
||||
* IRQ
|
||||
*
|
||||
* The start and end fields are used as an offset to the irq_base
|
||||
* parameter passed into the mfd_add_devices() function call. All
|
||||
* devices share the same IRQ.
|
||||
*/
|
||||
res->flags = IORESOURCE_IRQ;
|
||||
res->parent = NULL;
|
||||
res->start = 0;
|
||||
res->end = 0;
|
||||
res++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Probe each submodule using kernel parameters */
|
||||
static int __devinit cmodio_probe_submodules(struct cmodio_device *priv)
|
||||
{
|
||||
struct pci_dev *pdev = priv->pdev;
|
||||
unsigned int num_probed = 0;
|
||||
char *name;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_modules; i++) {
|
||||
name = modules[i];
|
||||
if (!strcmp(name, "") || !strcmp(name, "empty"))
|
||||
continue;
|
||||
|
||||
dev_dbg(&priv->pdev->dev, "MODULbus %d: name %s\n", i, name);
|
||||
cmodio_setup_subdevice(priv, name, num_probed, i);
|
||||
num_probed++;
|
||||
}
|
||||
|
||||
/* print an error message if no modules were probed */
|
||||
if (num_probed == 0) {
|
||||
dev_err(&priv->pdev->dev, "no MODULbus modules specified, "
|
||||
"please set the ``modules'' kernel "
|
||||
"parameter according to your "
|
||||
"hardware configuration\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return mfd_add_devices(&pdev->dev, 0, priv->cells,
|
||||
num_probed, NULL, pdev->irq);
|
||||
}
|
||||
|
||||
/*
|
||||
* SYSFS Attributes
|
||||
*/
|
||||
|
||||
static ssize_t mbus_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct cmodio_device *priv = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%x\n", priv->hex);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(modulbus_number, S_IRUGO, mbus_show, NULL);
|
||||
|
||||
static struct attribute *cmodio_sysfs_attrs[] = {
|
||||
&dev_attr_modulbus_number.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group cmodio_sysfs_attr_group = {
|
||||
.attrs = cmodio_sysfs_attrs,
|
||||
};
|
||||
|
||||
/*
|
||||
* PCI Driver
|
||||
*/
|
||||
|
||||
static int __devinit cmodio_pci_probe(struct pci_dev *dev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
struct cmodio_device *priv;
|
||||
int ret;
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
dev_err(&dev->dev, "unable to allocate private data\n");
|
||||
ret = -ENOMEM;
|
||||
goto out_return;
|
||||
}
|
||||
|
||||
pci_set_drvdata(dev, priv);
|
||||
priv->pdev = dev;
|
||||
|
||||
/* Hardware Initialization */
|
||||
ret = pci_enable_device(dev);
|
||||
if (ret) {
|
||||
dev_err(&dev->dev, "unable to enable device\n");
|
||||
goto out_free_priv;
|
||||
}
|
||||
|
||||
pci_set_master(dev);
|
||||
ret = pci_request_regions(dev, DRV_NAME);
|
||||
if (ret) {
|
||||
dev_err(&dev->dev, "unable to request regions\n");
|
||||
goto out_pci_disable_device;
|
||||
}
|
||||
|
||||
/* Onboard configuration registers */
|
||||
priv->ctrl = pci_ioremap_bar(dev, 4);
|
||||
if (!priv->ctrl) {
|
||||
dev_err(&dev->dev, "unable to remap onboard regs\n");
|
||||
ret = -ENOMEM;
|
||||
goto out_pci_release_regions;
|
||||
}
|
||||
|
||||
/* Read the hex switch on the carrier board */
|
||||
priv->hex = ioread8(&priv->ctrl->int_enable);
|
||||
|
||||
/* Add the MODULbus number (hex switch value) to the device's sysfs */
|
||||
ret = sysfs_create_group(&dev->dev.kobj, &cmodio_sysfs_attr_group);
|
||||
if (ret) {
|
||||
dev_err(&dev->dev, "unable to create sysfs attributes\n");
|
||||
goto out_unmap_ctrl;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable all interrupt lines, each submodule will enable its
|
||||
* own interrupt line if needed
|
||||
*/
|
||||
iowrite8(0xf, &priv->ctrl->int_disable);
|
||||
|
||||
/* Register drivers for all submodules */
|
||||
ret = cmodio_probe_submodules(priv);
|
||||
if (ret) {
|
||||
dev_err(&dev->dev, "unable to probe submodules\n");
|
||||
goto out_sysfs_remove_group;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_sysfs_remove_group:
|
||||
sysfs_remove_group(&dev->dev.kobj, &cmodio_sysfs_attr_group);
|
||||
out_unmap_ctrl:
|
||||
iounmap(priv->ctrl);
|
||||
out_pci_release_regions:
|
||||
pci_release_regions(dev);
|
||||
out_pci_disable_device:
|
||||
pci_disable_device(dev);
|
||||
out_free_priv:
|
||||
kfree(priv);
|
||||
out_return:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __devexit cmodio_pci_remove(struct pci_dev *dev)
|
||||
{
|
||||
struct cmodio_device *priv = pci_get_drvdata(dev);
|
||||
|
||||
mfd_remove_devices(&dev->dev);
|
||||
sysfs_remove_group(&dev->dev.kobj, &cmodio_sysfs_attr_group);
|
||||
iounmap(priv->ctrl);
|
||||
pci_release_regions(dev);
|
||||
pci_disable_device(dev);
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
#define PCI_VENDOR_ID_JANZ 0x13c3
|
||||
|
||||
/* The list of devices that this module will support */
|
||||
static DEFINE_PCI_DEVICE_TABLE(cmodio_pci_ids) = {
|
||||
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_JANZ, 0x0101 },
|
||||
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_JANZ, 0x0100 },
|
||||
{ 0, }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, cmodio_pci_ids);
|
||||
|
||||
static struct pci_driver cmodio_pci_driver = {
|
||||
.name = DRV_NAME,
|
||||
.id_table = cmodio_pci_ids,
|
||||
.probe = cmodio_pci_probe,
|
||||
.remove = __devexit_p(cmodio_pci_remove),
|
||||
};
|
||||
|
||||
/*
|
||||
* Module Init / Exit
|
||||
*/
|
||||
|
||||
static int __init cmodio_init(void)
|
||||
{
|
||||
return pci_register_driver(&cmodio_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit cmodio_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&cmodio_pci_driver);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
|
||||
MODULE_DESCRIPTION("Janz CMOD-IO PCI MODULbus Carrier Board Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(cmodio_init);
|
||||
module_exit(cmodio_exit);
|
@ -508,7 +508,7 @@ static int max8925_irq_init(struct max8925_chip *chip, int irq,
|
||||
max8925_reg_read(chip->i2c, MAX8925_ON_OFF_IRQ2);
|
||||
max8925_reg_read(chip->rtc, MAX8925_RTC_IRQ);
|
||||
max8925_reg_read(chip->adc, MAX8925_TSC_IRQ);
|
||||
/* mask all interrupts */
|
||||
/* mask all interrupts except for TSC */
|
||||
max8925_reg_write(chip->rtc, MAX8925_ALARM0_CNTL, 0);
|
||||
max8925_reg_write(chip->rtc, MAX8925_ALARM1_CNTL, 0);
|
||||
max8925_reg_write(chip->i2c, MAX8925_CHG_IRQ1_MASK, 0xff);
|
||||
@ -516,7 +516,6 @@ static int max8925_irq_init(struct max8925_chip *chip, int irq,
|
||||
max8925_reg_write(chip->i2c, MAX8925_ON_OFF_IRQ1_MASK, 0xff);
|
||||
max8925_reg_write(chip->i2c, MAX8925_ON_OFF_IRQ2_MASK, 0xff);
|
||||
max8925_reg_write(chip->rtc, MAX8925_RTC_IRQ_MASK, 0xff);
|
||||
max8925_reg_write(chip->adc, MAX8925_TSC_IRQ_MASK, 0xff);
|
||||
|
||||
mutex_init(&chip->irq_lock);
|
||||
chip->core_irq = irq;
|
||||
@ -547,7 +546,11 @@ static int max8925_irq_init(struct max8925_chip *chip, int irq,
|
||||
dev_err(chip->dev, "Failed to request core IRQ: %d\n", ret);
|
||||
chip->core_irq = 0;
|
||||
}
|
||||
|
||||
tsc_irq:
|
||||
/* mask TSC interrupt */
|
||||
max8925_reg_write(chip->adc, MAX8925_TSC_IRQ_MASK, 0x0f);
|
||||
|
||||
if (!pdata->tsc_irq) {
|
||||
dev_warn(chip->dev, "No interrupt support on TSC IRQ\n");
|
||||
return 0;
|
||||
|
@ -173,8 +173,6 @@ static int __devexit max8925_remove(struct i2c_client *client)
|
||||
max8925_device_exit(chip);
|
||||
i2c_unregister_device(chip->adc);
|
||||
i2c_unregister_device(chip->rtc);
|
||||
i2c_set_clientdata(chip->adc, NULL);
|
||||
i2c_set_clientdata(chip->rtc, NULL);
|
||||
i2c_set_clientdata(chip->i2c, NULL);
|
||||
kfree(chip);
|
||||
return 0;
|
||||
|
@ -1228,6 +1228,7 @@ fail2:
|
||||
free_irq(client->irq, menelaus);
|
||||
flush_scheduled_work();
|
||||
fail1:
|
||||
i2c_set_clientdata(client, NULL);
|
||||
kfree(menelaus);
|
||||
return err;
|
||||
}
|
||||
@ -1237,8 +1238,8 @@ static int __exit menelaus_remove(struct i2c_client *client)
|
||||
struct menelaus_chip *menelaus = i2c_get_clientdata(client);
|
||||
|
||||
free_irq(client->irq, menelaus);
|
||||
kfree(menelaus);
|
||||
i2c_set_clientdata(client, NULL);
|
||||
kfree(menelaus);
|
||||
the_menelaus = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ static int mfd_add_device(struct device *parent, int id,
|
||||
res[r].flags = cell->resources[r].flags;
|
||||
|
||||
/* Find out base to use */
|
||||
if (cell->resources[r].flags & IORESOURCE_MEM) {
|
||||
if ((cell->resources[r].flags & IORESOURCE_MEM) && mem_base) {
|
||||
res[r].parent = mem_base;
|
||||
res[r].start = mem_base->start +
|
||||
cell->resources[r].start;
|
||||
|
@ -30,13 +30,13 @@
|
||||
struct pcf50633_adc_request {
|
||||
int mux;
|
||||
int avg;
|
||||
int result;
|
||||
void (*callback)(struct pcf50633 *, void *, int);
|
||||
void *callback_param;
|
||||
};
|
||||
|
||||
/* Used in case of sync requests */
|
||||
struct pcf50633_adc_sync_request {
|
||||
int result;
|
||||
struct completion completion;
|
||||
|
||||
};
|
||||
|
||||
#define PCF50633_MAX_ADC_FIFO_DEPTH 8
|
||||
@ -109,10 +109,10 @@ adc_enqueue_request(struct pcf50633 *pcf, struct pcf50633_adc_request *req)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
pcf50633_adc_sync_read_callback(struct pcf50633 *pcf, void *param, int result)
|
||||
static void pcf50633_adc_sync_read_callback(struct pcf50633 *pcf, void *param,
|
||||
int result)
|
||||
{
|
||||
struct pcf50633_adc_request *req = param;
|
||||
struct pcf50633_adc_sync_request *req = param;
|
||||
|
||||
req->result = result;
|
||||
complete(&req->completion);
|
||||
@ -120,28 +120,19 @@ pcf50633_adc_sync_read_callback(struct pcf50633 *pcf, void *param, int result)
|
||||
|
||||
int pcf50633_adc_sync_read(struct pcf50633 *pcf, int mux, int avg)
|
||||
{
|
||||
struct pcf50633_adc_request *req;
|
||||
int err;
|
||||
struct pcf50633_adc_sync_request req;
|
||||
int ret;
|
||||
|
||||
/* req is freed when the result is ready, in interrupt handler */
|
||||
req = kzalloc(sizeof(*req), GFP_KERNEL);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
init_completion(&req.completion);
|
||||
|
||||
req->mux = mux;
|
||||
req->avg = avg;
|
||||
req->callback = pcf50633_adc_sync_read_callback;
|
||||
req->callback_param = req;
|
||||
ret = pcf50633_adc_async_read(pcf, mux, avg,
|
||||
pcf50633_adc_sync_read_callback, &req);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
init_completion(&req->completion);
|
||||
err = adc_enqueue_request(pcf, req);
|
||||
if (err)
|
||||
return err;
|
||||
wait_for_completion(&req.completion);
|
||||
|
||||
wait_for_completion(&req->completion);
|
||||
|
||||
/* FIXME by this time req might be already freed */
|
||||
return req->result;
|
||||
return req.result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pcf50633_adc_sync_read);
|
||||
|
||||
|
@ -21,16 +21,16 @@
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/mfd/pcf50633/core.h>
|
||||
|
||||
/* Two MBCS registers used during cold start */
|
||||
#define PCF50633_REG_MBCS1 0x4b
|
||||
#define PCF50633_REG_MBCS2 0x4c
|
||||
#define PCF50633_MBCS1_USBPRES 0x01
|
||||
#define PCF50633_MBCS1_ADAPTPRES 0x01
|
||||
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)
|
||||
{
|
||||
@ -215,244 +215,6 @@ static struct attribute_group pcf_attr_group = {
|
||||
.attrs = pcf_sysfs_entries,
|
||||
};
|
||||
|
||||
int pcf50633_register_irq(struct pcf50633 *pcf, int irq,
|
||||
void (*handler) (int, void *), void *data)
|
||||
{
|
||||
if (irq < 0 || irq > PCF50633_NUM_IRQ || !handler)
|
||||
return -EINVAL;
|
||||
|
||||
if (WARN_ON(pcf->irq_handler[irq].handler))
|
||||
return -EBUSY;
|
||||
|
||||
mutex_lock(&pcf->lock);
|
||||
pcf->irq_handler[irq].handler = handler;
|
||||
pcf->irq_handler[irq].data = data;
|
||||
mutex_unlock(&pcf->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pcf50633_register_irq);
|
||||
|
||||
int pcf50633_free_irq(struct pcf50633 *pcf, int irq)
|
||||
{
|
||||
if (irq < 0 || irq > PCF50633_NUM_IRQ)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&pcf->lock);
|
||||
pcf->irq_handler[irq].handler = NULL;
|
||||
mutex_unlock(&pcf->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pcf50633_free_irq);
|
||||
|
||||
static int __pcf50633_irq_mask_set(struct pcf50633 *pcf, int irq, u8 mask)
|
||||
{
|
||||
u8 reg, bits, tmp;
|
||||
int ret = 0, idx;
|
||||
|
||||
idx = irq >> 3;
|
||||
reg = PCF50633_REG_INT1M + idx;
|
||||
bits = 1 << (irq & 0x07);
|
||||
|
||||
mutex_lock(&pcf->lock);
|
||||
|
||||
if (mask) {
|
||||
ret = __pcf50633_read(pcf, reg, 1, &tmp);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
tmp |= bits;
|
||||
|
||||
ret = __pcf50633_write(pcf, reg, 1, &tmp);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
pcf->mask_regs[idx] &= ~bits;
|
||||
pcf->mask_regs[idx] |= bits;
|
||||
} else {
|
||||
ret = __pcf50633_read(pcf, reg, 1, &tmp);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
tmp &= ~bits;
|
||||
|
||||
ret = __pcf50633_write(pcf, reg, 1, &tmp);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
pcf->mask_regs[idx] &= ~bits;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&pcf->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int pcf50633_irq_mask(struct pcf50633 *pcf, int irq)
|
||||
{
|
||||
dev_dbg(pcf->dev, "Masking IRQ %d\n", irq);
|
||||
|
||||
return __pcf50633_irq_mask_set(pcf, irq, 1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pcf50633_irq_mask);
|
||||
|
||||
int pcf50633_irq_unmask(struct pcf50633 *pcf, int irq)
|
||||
{
|
||||
dev_dbg(pcf->dev, "Unmasking IRQ %d\n", irq);
|
||||
|
||||
return __pcf50633_irq_mask_set(pcf, irq, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pcf50633_irq_unmask);
|
||||
|
||||
int pcf50633_irq_mask_get(struct pcf50633 *pcf, int irq)
|
||||
{
|
||||
u8 reg, bits;
|
||||
|
||||
reg = irq >> 3;
|
||||
bits = 1 << (irq & 0x07);
|
||||
|
||||
return pcf->mask_regs[reg] & bits;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pcf50633_irq_mask_get);
|
||||
|
||||
static void pcf50633_irq_call_handler(struct pcf50633 *pcf, int irq)
|
||||
{
|
||||
if (pcf->irq_handler[irq].handler)
|
||||
pcf->irq_handler[irq].handler(irq, pcf->irq_handler[irq].data);
|
||||
}
|
||||
|
||||
/* Maximum amount of time ONKEY is held before emergency action is taken */
|
||||
#define PCF50633_ONKEY1S_TIMEOUT 8
|
||||
|
||||
static void pcf50633_irq_worker(struct work_struct *work)
|
||||
{
|
||||
struct pcf50633 *pcf;
|
||||
int ret, i, j;
|
||||
u8 pcf_int[5], chgstat;
|
||||
|
||||
pcf = container_of(work, struct pcf50633, irq_work);
|
||||
|
||||
/* Read the 5 INT regs in one transaction */
|
||||
ret = pcf50633_read_block(pcf, PCF50633_REG_INT1,
|
||||
ARRAY_SIZE(pcf_int), pcf_int);
|
||||
if (ret != ARRAY_SIZE(pcf_int)) {
|
||||
dev_err(pcf->dev, "Error reading INT registers\n");
|
||||
|
||||
/*
|
||||
* If this doesn't ACK the interrupt to the chip, we'll be
|
||||
* called once again as we're level triggered.
|
||||
*/
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* defeat 8s death from lowsys on A5 */
|
||||
pcf50633_reg_write(pcf, PCF50633_REG_OOCSHDWN, 0x04);
|
||||
|
||||
/* We immediately read the usb and adapter status. We thus make sure
|
||||
* only of USBINS/USBREM IRQ handlers are called */
|
||||
if (pcf_int[0] & (PCF50633_INT1_USBINS | PCF50633_INT1_USBREM)) {
|
||||
chgstat = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2);
|
||||
if (chgstat & (0x3 << 4))
|
||||
pcf_int[0] &= ~(1 << PCF50633_INT1_USBREM);
|
||||
else
|
||||
pcf_int[0] &= ~(1 << PCF50633_INT1_USBINS);
|
||||
}
|
||||
|
||||
/* Make sure only one of ADPINS or ADPREM is set */
|
||||
if (pcf_int[0] & (PCF50633_INT1_ADPINS | PCF50633_INT1_ADPREM)) {
|
||||
chgstat = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2);
|
||||
if (chgstat & (0x3 << 4))
|
||||
pcf_int[0] &= ~(1 << PCF50633_INT1_ADPREM);
|
||||
else
|
||||
pcf_int[0] &= ~(1 << PCF50633_INT1_ADPINS);
|
||||
}
|
||||
|
||||
dev_dbg(pcf->dev, "INT1=0x%02x INT2=0x%02x INT3=0x%02x "
|
||||
"INT4=0x%02x INT5=0x%02x\n", pcf_int[0],
|
||||
pcf_int[1], pcf_int[2], pcf_int[3], pcf_int[4]);
|
||||
|
||||
/* Some revisions of the chip don't have a 8s standby mode on
|
||||
* ONKEY1S press. We try to manually do it in such cases. */
|
||||
if ((pcf_int[0] & PCF50633_INT1_SECOND) && pcf->onkey1s_held) {
|
||||
dev_info(pcf->dev, "ONKEY1S held for %d secs\n",
|
||||
pcf->onkey1s_held);
|
||||
if (pcf->onkey1s_held++ == PCF50633_ONKEY1S_TIMEOUT)
|
||||
if (pcf->pdata->force_shutdown)
|
||||
pcf->pdata->force_shutdown(pcf);
|
||||
}
|
||||
|
||||
if (pcf_int[2] & PCF50633_INT3_ONKEY1S) {
|
||||
dev_info(pcf->dev, "ONKEY1S held\n");
|
||||
pcf->onkey1s_held = 1 ;
|
||||
|
||||
/* Unmask IRQ_SECOND */
|
||||
pcf50633_reg_clear_bits(pcf, PCF50633_REG_INT1M,
|
||||
PCF50633_INT1_SECOND);
|
||||
|
||||
/* Unmask IRQ_ONKEYR */
|
||||
pcf50633_reg_clear_bits(pcf, PCF50633_REG_INT2M,
|
||||
PCF50633_INT2_ONKEYR);
|
||||
}
|
||||
|
||||
if ((pcf_int[1] & PCF50633_INT2_ONKEYR) && pcf->onkey1s_held) {
|
||||
pcf->onkey1s_held = 0;
|
||||
|
||||
/* Mask SECOND and ONKEYR interrupts */
|
||||
if (pcf->mask_regs[0] & PCF50633_INT1_SECOND)
|
||||
pcf50633_reg_set_bit_mask(pcf,
|
||||
PCF50633_REG_INT1M,
|
||||
PCF50633_INT1_SECOND,
|
||||
PCF50633_INT1_SECOND);
|
||||
|
||||
if (pcf->mask_regs[1] & PCF50633_INT2_ONKEYR)
|
||||
pcf50633_reg_set_bit_mask(pcf,
|
||||
PCF50633_REG_INT2M,
|
||||
PCF50633_INT2_ONKEYR,
|
||||
PCF50633_INT2_ONKEYR);
|
||||
}
|
||||
|
||||
/* Have we just resumed ? */
|
||||
if (pcf->is_suspended) {
|
||||
pcf->is_suspended = 0;
|
||||
|
||||
/* Set the resume reason filtering out non resumers */
|
||||
for (i = 0; i < ARRAY_SIZE(pcf_int); i++)
|
||||
pcf->resume_reason[i] = pcf_int[i] &
|
||||
pcf->pdata->resumers[i];
|
||||
|
||||
/* Make sure we don't pass on any ONKEY events to
|
||||
* userspace now */
|
||||
pcf_int[1] &= ~(PCF50633_INT2_ONKEYR | PCF50633_INT2_ONKEYF);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pcf_int); i++) {
|
||||
/* Unset masked interrupts */
|
||||
pcf_int[i] &= ~pcf->mask_regs[i];
|
||||
|
||||
for (j = 0; j < 8 ; j++)
|
||||
if (pcf_int[i] & (1 << j))
|
||||
pcf50633_irq_call_handler(pcf, (i * 8) + j);
|
||||
}
|
||||
|
||||
out:
|
||||
put_device(pcf->dev);
|
||||
enable_irq(pcf->irq);
|
||||
}
|
||||
|
||||
static irqreturn_t pcf50633_irq(int irq, void *data)
|
||||
{
|
||||
struct pcf50633 *pcf = data;
|
||||
|
||||
dev_dbg(pcf->dev, "pcf50633_irq\n");
|
||||
|
||||
get_device(pcf->dev);
|
||||
disable_irq_nosync(pcf->irq);
|
||||
queue_work(pcf->work_queue, &pcf->irq_work);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void
|
||||
pcf50633_client_dev_register(struct pcf50633 *pcf, const char *name,
|
||||
struct platform_device **pdev)
|
||||
@ -479,70 +241,17 @@ pcf50633_client_dev_register(struct pcf50633 *pcf, const char *name,
|
||||
static int pcf50633_suspend(struct i2c_client *client, pm_message_t state)
|
||||
{
|
||||
struct pcf50633 *pcf;
|
||||
int ret = 0, i;
|
||||
u8 res[5];
|
||||
|
||||
pcf = i2c_get_clientdata(client);
|
||||
|
||||
/* Make sure our interrupt handlers are not called
|
||||
* henceforth */
|
||||
disable_irq(pcf->irq);
|
||||
|
||||
/* Make sure that any running IRQ worker has quit */
|
||||
cancel_work_sync(&pcf->irq_work);
|
||||
|
||||
/* Save the masks */
|
||||
ret = pcf50633_read_block(pcf, PCF50633_REG_INT1M,
|
||||
ARRAY_SIZE(pcf->suspend_irq_masks),
|
||||
pcf->suspend_irq_masks);
|
||||
if (ret < 0) {
|
||||
dev_err(pcf->dev, "error saving irq masks\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Write wakeup irq masks */
|
||||
for (i = 0; i < ARRAY_SIZE(res); i++)
|
||||
res[i] = ~pcf->pdata->resumers[i];
|
||||
|
||||
ret = pcf50633_write_block(pcf, PCF50633_REG_INT1M,
|
||||
ARRAY_SIZE(res), &res[0]);
|
||||
if (ret < 0) {
|
||||
dev_err(pcf->dev, "error writing wakeup irq masks\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
pcf->is_suspended = 1;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
return pcf50633_irq_suspend(pcf);
|
||||
}
|
||||
|
||||
static int pcf50633_resume(struct i2c_client *client)
|
||||
{
|
||||
struct pcf50633 *pcf;
|
||||
int ret;
|
||||
|
||||
pcf = i2c_get_clientdata(client);
|
||||
|
||||
/* Write the saved mask registers */
|
||||
ret = pcf50633_write_block(pcf, PCF50633_REG_INT1M,
|
||||
ARRAY_SIZE(pcf->suspend_irq_masks),
|
||||
pcf->suspend_irq_masks);
|
||||
if (ret < 0)
|
||||
dev_err(pcf->dev, "Error restoring saved suspend masks\n");
|
||||
|
||||
/* Restore regulators' state */
|
||||
|
||||
|
||||
get_device(pcf->dev);
|
||||
|
||||
/*
|
||||
* Clear any pending interrupts and set resume reason if any.
|
||||
* This will leave with enable_irq()
|
||||
*/
|
||||
pcf50633_irq_worker(&pcf->irq_work);
|
||||
|
||||
return 0;
|
||||
return pcf50633_irq_resume(pcf);
|
||||
}
|
||||
#else
|
||||
#define pcf50633_suspend NULL
|
||||
@ -573,43 +282,19 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
|
||||
i2c_set_clientdata(client, pcf);
|
||||
pcf->dev = &client->dev;
|
||||
pcf->i2c_client = client;
|
||||
pcf->irq = client->irq;
|
||||
pcf->work_queue = create_singlethread_workqueue("pcf50633");
|
||||
|
||||
if (!pcf->work_queue) {
|
||||
dev_err(&client->dev, "Failed to alloc workqueue\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
INIT_WORK(&pcf->irq_work, pcf50633_irq_worker);
|
||||
|
||||
version = pcf50633_reg_read(pcf, 0);
|
||||
variant = pcf50633_reg_read(pcf, 1);
|
||||
if (version < 0 || variant < 0) {
|
||||
dev_err(pcf->dev, "Unable to probe pcf50633\n");
|
||||
ret = -ENODEV;
|
||||
goto err_destroy_workqueue;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
dev_info(pcf->dev, "Probed device version %d variant %d\n",
|
||||
version, variant);
|
||||
|
||||
/* Enable all interrupts except RTC SECOND */
|
||||
pcf->mask_regs[0] = 0x80;
|
||||
pcf50633_reg_write(pcf, PCF50633_REG_INT1M, pcf->mask_regs[0]);
|
||||
pcf50633_reg_write(pcf, PCF50633_REG_INT2M, 0x00);
|
||||
pcf50633_reg_write(pcf, PCF50633_REG_INT3M, 0x00);
|
||||
pcf50633_reg_write(pcf, PCF50633_REG_INT4M, 0x00);
|
||||
pcf50633_reg_write(pcf, PCF50633_REG_INT5M, 0x00);
|
||||
|
||||
ret = request_irq(client->irq, pcf50633_irq,
|
||||
IRQF_TRIGGER_LOW, "pcf50633", pcf);
|
||||
|
||||
if (ret) {
|
||||
dev_err(pcf->dev, "Failed to request IRQ %d\n", ret);
|
||||
goto err_destroy_workqueue;
|
||||
}
|
||||
pcf50633_irq_init(pcf, client->irq);
|
||||
|
||||
/* Create sub devices */
|
||||
pcf50633_client_dev_register(pcf, "pcf50633-input",
|
||||
@ -641,10 +326,6 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
|
||||
platform_device_add(pdev);
|
||||
}
|
||||
|
||||
if (enable_irq_wake(client->irq) < 0)
|
||||
dev_err(pcf->dev, "IRQ %u cannot be enabled as wake-up source"
|
||||
"in this hardware revision", client->irq);
|
||||
|
||||
ret = sysfs_create_group(&client->dev.kobj, &pcf_attr_group);
|
||||
if (ret)
|
||||
dev_err(pcf->dev, "error creating sysfs entries\n");
|
||||
@ -654,8 +335,6 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
|
||||
|
||||
return 0;
|
||||
|
||||
err_destroy_workqueue:
|
||||
destroy_workqueue(pcf->work_queue);
|
||||
err_free:
|
||||
i2c_set_clientdata(client, NULL);
|
||||
kfree(pcf);
|
||||
@ -668,8 +347,7 @@ static int __devexit pcf50633_remove(struct i2c_client *client)
|
||||
struct pcf50633 *pcf = i2c_get_clientdata(client);
|
||||
int i;
|
||||
|
||||
free_irq(pcf->irq, pcf);
|
||||
destroy_workqueue(pcf->work_queue);
|
||||
pcf50633_irq_free(pcf);
|
||||
|
||||
platform_device_unregister(pcf->input_pdev);
|
||||
platform_device_unregister(pcf->rtc_pdev);
|
||||
@ -679,6 +357,7 @@ static int __devexit pcf50633_remove(struct i2c_client *client)
|
||||
for (i = 0; i < PCF50633_NUM_REGULATORS; i++)
|
||||
platform_device_unregister(pcf->regulator_pdev[i]);
|
||||
|
||||
i2c_set_clientdata(client, NULL);
|
||||
kfree(pcf);
|
||||
|
||||
return 0;
|
||||
|
318
drivers/mfd/pcf50633-irq.c
Normal file
318
drivers/mfd/pcf50633-irq.c
Normal file
@ -0,0 +1,318 @@
|
||||
/* NXP PCF50633 Power Management Unit (PMU) driver
|
||||
*
|
||||
* (C) 2006-2008 by Openmoko, Inc.
|
||||
* Author: Harald Welte <laforge@openmoko.org>
|
||||
* Balaji Rao <balajirrao@openmoko.org>
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/mfd/pcf50633/core.h>
|
||||
|
||||
/* Two MBCS registers used during cold start */
|
||||
#define PCF50633_REG_MBCS1 0x4b
|
||||
#define PCF50633_REG_MBCS2 0x4c
|
||||
#define PCF50633_MBCS1_USBPRES 0x01
|
||||
#define PCF50633_MBCS1_ADAPTPRES 0x01
|
||||
|
||||
int pcf50633_register_irq(struct pcf50633 *pcf, int irq,
|
||||
void (*handler) (int, void *), void *data)
|
||||
{
|
||||
if (irq < 0 || irq >= PCF50633_NUM_IRQ || !handler)
|
||||
return -EINVAL;
|
||||
|
||||
if (WARN_ON(pcf->irq_handler[irq].handler))
|
||||
return -EBUSY;
|
||||
|
||||
mutex_lock(&pcf->lock);
|
||||
pcf->irq_handler[irq].handler = handler;
|
||||
pcf->irq_handler[irq].data = data;
|
||||
mutex_unlock(&pcf->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pcf50633_register_irq);
|
||||
|
||||
int pcf50633_free_irq(struct pcf50633 *pcf, int irq)
|
||||
{
|
||||
if (irq < 0 || irq >= PCF50633_NUM_IRQ)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&pcf->lock);
|
||||
pcf->irq_handler[irq].handler = NULL;
|
||||
mutex_unlock(&pcf->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pcf50633_free_irq);
|
||||
|
||||
static int __pcf50633_irq_mask_set(struct pcf50633 *pcf, int irq, u8 mask)
|
||||
{
|
||||
u8 reg, bit;
|
||||
int ret = 0, idx;
|
||||
|
||||
idx = irq >> 3;
|
||||
reg = PCF50633_REG_INT1M + idx;
|
||||
bit = 1 << (irq & 0x07);
|
||||
|
||||
pcf50633_reg_set_bit_mask(pcf, reg, bit, mask ? bit : 0);
|
||||
|
||||
mutex_lock(&pcf->lock);
|
||||
|
||||
if (mask)
|
||||
pcf->mask_regs[idx] |= bit;
|
||||
else
|
||||
pcf->mask_regs[idx] &= ~bit;
|
||||
|
||||
mutex_unlock(&pcf->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int pcf50633_irq_mask(struct pcf50633 *pcf, int irq)
|
||||
{
|
||||
dev_dbg(pcf->dev, "Masking IRQ %d\n", irq);
|
||||
|
||||
return __pcf50633_irq_mask_set(pcf, irq, 1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pcf50633_irq_mask);
|
||||
|
||||
int pcf50633_irq_unmask(struct pcf50633 *pcf, int irq)
|
||||
{
|
||||
dev_dbg(pcf->dev, "Unmasking IRQ %d\n", irq);
|
||||
|
||||
return __pcf50633_irq_mask_set(pcf, irq, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pcf50633_irq_unmask);
|
||||
|
||||
int pcf50633_irq_mask_get(struct pcf50633 *pcf, int irq)
|
||||
{
|
||||
u8 reg, bits;
|
||||
|
||||
reg = irq >> 3;
|
||||
bits = 1 << (irq & 0x07);
|
||||
|
||||
return pcf->mask_regs[reg] & bits;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pcf50633_irq_mask_get);
|
||||
|
||||
static void pcf50633_irq_call_handler(struct pcf50633 *pcf, int irq)
|
||||
{
|
||||
if (pcf->irq_handler[irq].handler)
|
||||
pcf->irq_handler[irq].handler(irq, pcf->irq_handler[irq].data);
|
||||
}
|
||||
|
||||
/* Maximum amount of time ONKEY is held before emergency action is taken */
|
||||
#define PCF50633_ONKEY1S_TIMEOUT 8
|
||||
|
||||
static irqreturn_t pcf50633_irq(int irq, void *data)
|
||||
{
|
||||
struct pcf50633 *pcf = data;
|
||||
int ret, i, j;
|
||||
u8 pcf_int[5], chgstat;
|
||||
|
||||
/* Read the 5 INT regs in one transaction */
|
||||
ret = pcf50633_read_block(pcf, PCF50633_REG_INT1,
|
||||
ARRAY_SIZE(pcf_int), pcf_int);
|
||||
if (ret != ARRAY_SIZE(pcf_int)) {
|
||||
dev_err(pcf->dev, "Error reading INT registers\n");
|
||||
|
||||
/*
|
||||
* If this doesn't ACK the interrupt to the chip, we'll be
|
||||
* called once again as we're level triggered.
|
||||
*/
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* defeat 8s death from lowsys on A5 */
|
||||
pcf50633_reg_write(pcf, PCF50633_REG_OOCSHDWN, 0x04);
|
||||
|
||||
/* We immediately read the usb and adapter status. We thus make sure
|
||||
* only of USBINS/USBREM IRQ handlers are called */
|
||||
if (pcf_int[0] & (PCF50633_INT1_USBINS | PCF50633_INT1_USBREM)) {
|
||||
chgstat = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2);
|
||||
if (chgstat & (0x3 << 4))
|
||||
pcf_int[0] &= ~PCF50633_INT1_USBREM;
|
||||
else
|
||||
pcf_int[0] &= ~PCF50633_INT1_USBINS;
|
||||
}
|
||||
|
||||
/* Make sure only one of ADPINS or ADPREM is set */
|
||||
if (pcf_int[0] & (PCF50633_INT1_ADPINS | PCF50633_INT1_ADPREM)) {
|
||||
chgstat = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2);
|
||||
if (chgstat & (0x3 << 4))
|
||||
pcf_int[0] &= ~PCF50633_INT1_ADPREM;
|
||||
else
|
||||
pcf_int[0] &= ~PCF50633_INT1_ADPINS;
|
||||
}
|
||||
|
||||
dev_dbg(pcf->dev, "INT1=0x%02x INT2=0x%02x INT3=0x%02x "
|
||||
"INT4=0x%02x INT5=0x%02x\n", pcf_int[0],
|
||||
pcf_int[1], pcf_int[2], pcf_int[3], pcf_int[4]);
|
||||
|
||||
/* Some revisions of the chip don't have a 8s standby mode on
|
||||
* ONKEY1S press. We try to manually do it in such cases. */
|
||||
if ((pcf_int[0] & PCF50633_INT1_SECOND) && pcf->onkey1s_held) {
|
||||
dev_info(pcf->dev, "ONKEY1S held for %d secs\n",
|
||||
pcf->onkey1s_held);
|
||||
if (pcf->onkey1s_held++ == PCF50633_ONKEY1S_TIMEOUT)
|
||||
if (pcf->pdata->force_shutdown)
|
||||
pcf->pdata->force_shutdown(pcf);
|
||||
}
|
||||
|
||||
if (pcf_int[2] & PCF50633_INT3_ONKEY1S) {
|
||||
dev_info(pcf->dev, "ONKEY1S held\n");
|
||||
pcf->onkey1s_held = 1 ;
|
||||
|
||||
/* Unmask IRQ_SECOND */
|
||||
pcf50633_reg_clear_bits(pcf, PCF50633_REG_INT1M,
|
||||
PCF50633_INT1_SECOND);
|
||||
|
||||
/* Unmask IRQ_ONKEYR */
|
||||
pcf50633_reg_clear_bits(pcf, PCF50633_REG_INT2M,
|
||||
PCF50633_INT2_ONKEYR);
|
||||
}
|
||||
|
||||
if ((pcf_int[1] & PCF50633_INT2_ONKEYR) && pcf->onkey1s_held) {
|
||||
pcf->onkey1s_held = 0;
|
||||
|
||||
/* Mask SECOND and ONKEYR interrupts */
|
||||
if (pcf->mask_regs[0] & PCF50633_INT1_SECOND)
|
||||
pcf50633_reg_set_bit_mask(pcf,
|
||||
PCF50633_REG_INT1M,
|
||||
PCF50633_INT1_SECOND,
|
||||
PCF50633_INT1_SECOND);
|
||||
|
||||
if (pcf->mask_regs[1] & PCF50633_INT2_ONKEYR)
|
||||
pcf50633_reg_set_bit_mask(pcf,
|
||||
PCF50633_REG_INT2M,
|
||||
PCF50633_INT2_ONKEYR,
|
||||
PCF50633_INT2_ONKEYR);
|
||||
}
|
||||
|
||||
/* Have we just resumed ? */
|
||||
if (pcf->is_suspended) {
|
||||
pcf->is_suspended = 0;
|
||||
|
||||
/* Set the resume reason filtering out non resumers */
|
||||
for (i = 0; i < ARRAY_SIZE(pcf_int); i++)
|
||||
pcf->resume_reason[i] = pcf_int[i] &
|
||||
pcf->pdata->resumers[i];
|
||||
|
||||
/* Make sure we don't pass on any ONKEY events to
|
||||
* userspace now */
|
||||
pcf_int[1] &= ~(PCF50633_INT2_ONKEYR | PCF50633_INT2_ONKEYF);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pcf_int); i++) {
|
||||
/* Unset masked interrupts */
|
||||
pcf_int[i] &= ~pcf->mask_regs[i];
|
||||
|
||||
for (j = 0; j < 8 ; j++)
|
||||
if (pcf_int[i] & (1 << j))
|
||||
pcf50633_irq_call_handler(pcf, (i * 8) + j);
|
||||
}
|
||||
|
||||
out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
int pcf50633_irq_suspend(struct pcf50633 *pcf)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
u8 res[5];
|
||||
|
||||
|
||||
/* Make sure our interrupt handlers are not called
|
||||
* henceforth */
|
||||
disable_irq(pcf->irq);
|
||||
|
||||
/* Save the masks */
|
||||
ret = pcf50633_read_block(pcf, PCF50633_REG_INT1M,
|
||||
ARRAY_SIZE(pcf->suspend_irq_masks),
|
||||
pcf->suspend_irq_masks);
|
||||
if (ret < 0) {
|
||||
dev_err(pcf->dev, "error saving irq masks\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Write wakeup irq masks */
|
||||
for (i = 0; i < ARRAY_SIZE(res); i++)
|
||||
res[i] = ~pcf->pdata->resumers[i];
|
||||
|
||||
ret = pcf50633_write_block(pcf, PCF50633_REG_INT1M,
|
||||
ARRAY_SIZE(res), &res[0]);
|
||||
if (ret < 0) {
|
||||
dev_err(pcf->dev, "error writing wakeup irq masks\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
pcf->is_suspended = 1;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int pcf50633_irq_resume(struct pcf50633 *pcf)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Write the saved mask registers */
|
||||
ret = pcf50633_write_block(pcf, PCF50633_REG_INT1M,
|
||||
ARRAY_SIZE(pcf->suspend_irq_masks),
|
||||
pcf->suspend_irq_masks);
|
||||
if (ret < 0)
|
||||
dev_err(pcf->dev, "Error restoring saved suspend masks\n");
|
||||
|
||||
enable_irq(pcf->irq);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int pcf50633_irq_init(struct pcf50633 *pcf, int irq)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pcf->irq = irq;
|
||||
|
||||
/* Enable all interrupts except RTC SECOND */
|
||||
pcf->mask_regs[0] = 0x80;
|
||||
pcf50633_reg_write(pcf, PCF50633_REG_INT1M, pcf->mask_regs[0]);
|
||||
pcf50633_reg_write(pcf, PCF50633_REG_INT2M, 0x00);
|
||||
pcf50633_reg_write(pcf, PCF50633_REG_INT3M, 0x00);
|
||||
pcf50633_reg_write(pcf, PCF50633_REG_INT4M, 0x00);
|
||||
pcf50633_reg_write(pcf, PCF50633_REG_INT5M, 0x00);
|
||||
|
||||
ret = request_threaded_irq(irq, NULL, pcf50633_irq,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
"pcf50633", pcf);
|
||||
|
||||
if (ret)
|
||||
dev_err(pcf->dev, "Failed to request IRQ %d\n", ret);
|
||||
|
||||
if (enable_irq_wake(irq) < 0)
|
||||
dev_err(pcf->dev, "IRQ %u cannot be enabled as wake-up source"
|
||||
"in this hardware revision", irq);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void pcf50633_irq_free(struct pcf50633 *pcf)
|
||||
{
|
||||
free_irq(pcf->irq, pcf);
|
||||
}
|
123
drivers/mfd/rdc321x-southbridge.c
Normal file
123
drivers/mfd/rdc321x-southbridge.c
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* RDC321x MFD southbrige driver
|
||||
*
|
||||
* Copyright (C) 2007-2010 Florian Fainelli <florian@openwrt.org>
|
||||
* Copyright (C) 2010 Bernhard Loos <bernhardloos@googlemail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/rdc321x.h>
|
||||
|
||||
static struct rdc321x_wdt_pdata rdc321x_wdt_pdata;
|
||||
|
||||
static struct resource rdc321x_wdt_resource[] = {
|
||||
{
|
||||
.name = "wdt-reg",
|
||||
.start = RDC321X_WDT_CTRL,
|
||||
.end = RDC321X_WDT_CTRL + 0x3,
|
||||
.flags = IORESOURCE_IO,
|
||||
}
|
||||
};
|
||||
|
||||
static struct rdc321x_gpio_pdata rdc321x_gpio_pdata = {
|
||||
.max_gpios = RDC321X_MAX_GPIO,
|
||||
};
|
||||
|
||||
static struct resource rdc321x_gpio_resources[] = {
|
||||
{
|
||||
.name = "gpio-reg1",
|
||||
.start = RDC321X_GPIO_CTRL_REG1,
|
||||
.end = RDC321X_GPIO_CTRL_REG1 + 0x7,
|
||||
.flags = IORESOURCE_IO,
|
||||
}, {
|
||||
.name = "gpio-reg2",
|
||||
.start = RDC321X_GPIO_CTRL_REG2,
|
||||
.end = RDC321X_GPIO_CTRL_REG2 + 0x7,
|
||||
.flags = IORESOURCE_IO,
|
||||
}
|
||||
};
|
||||
|
||||
static struct mfd_cell rdc321x_sb_cells[] = {
|
||||
{
|
||||
.name = "rdc321x-wdt",
|
||||
.resources = rdc321x_wdt_resource,
|
||||
.num_resources = ARRAY_SIZE(rdc321x_wdt_resource),
|
||||
.driver_data = &rdc321x_wdt_pdata,
|
||||
}, {
|
||||
.name = "rdc321x-gpio",
|
||||
.resources = rdc321x_gpio_resources,
|
||||
.num_resources = ARRAY_SIZE(rdc321x_gpio_resources),
|
||||
.driver_data = &rdc321x_gpio_pdata,
|
||||
},
|
||||
};
|
||||
|
||||
static int __devinit rdc321x_sb_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *ent)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = pci_enable_device(pdev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to enable device\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
rdc321x_gpio_pdata.sb_pdev = pdev;
|
||||
rdc321x_wdt_pdata.sb_pdev = pdev;
|
||||
|
||||
return mfd_add_devices(&pdev->dev, -1,
|
||||
rdc321x_sb_cells, ARRAY_SIZE(rdc321x_sb_cells), NULL, 0);
|
||||
}
|
||||
|
||||
static void __devexit rdc321x_sb_remove(struct pci_dev *pdev)
|
||||
{
|
||||
mfd_remove_devices(&pdev->dev);
|
||||
}
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(rdc321x_sb_table) = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_RDC, PCI_DEVICE_ID_RDC_R6030) },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct pci_driver rdc321x_sb_driver = {
|
||||
.name = "RDC321x Southbridge",
|
||||
.id_table = rdc321x_sb_table,
|
||||
.probe = rdc321x_sb_probe,
|
||||
.remove = __devexit_p(rdc321x_sb_remove),
|
||||
};
|
||||
|
||||
static int __init rdc321x_sb_init(void)
|
||||
{
|
||||
return pci_register_driver(&rdc321x_sb_driver);
|
||||
}
|
||||
|
||||
static void __exit rdc321x_sb_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&rdc321x_sb_driver);
|
||||
}
|
||||
|
||||
module_init(rdc321x_sb_init);
|
||||
module_exit(rdc321x_sb_exit);
|
||||
|
||||
MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("RDC R-321x MFD southbridge driver");
|
@ -318,6 +318,9 @@ static int t7l66xb_probe(struct platform_device *dev)
|
||||
struct resource *iomem, *rscr;
|
||||
int ret;
|
||||
|
||||
if (pdata == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
iomem = platform_get_resource(dev, IORESOURCE_MEM, 0);
|
||||
if (!iomem)
|
||||
return -EINVAL;
|
||||
|
347
drivers/mfd/tc35892.c
Normal file
347
drivers/mfd/tc35892.c
Normal file
@ -0,0 +1,347 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* License Terms: GNU General Public License, version 2
|
||||
* Author: Hanumath Prasad <hanumath.prasad@stericsson.com> for ST-Ericsson
|
||||
* Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/tc35892.h>
|
||||
|
||||
/**
|
||||
* tc35892_reg_read() - read a single TC35892 register
|
||||
* @tc35892: Device to read from
|
||||
* @reg: Register to read
|
||||
*/
|
||||
int tc35892_reg_read(struct tc35892 *tc35892, u8 reg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(tc35892->i2c, reg);
|
||||
if (ret < 0)
|
||||
dev_err(tc35892->dev, "failed to read reg %#x: %d\n",
|
||||
reg, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tc35892_reg_read);
|
||||
|
||||
/**
|
||||
* tc35892_reg_read() - write a single TC35892 register
|
||||
* @tc35892: Device to write to
|
||||
* @reg: Register to read
|
||||
* @data: Value to write
|
||||
*/
|
||||
int tc35892_reg_write(struct tc35892 *tc35892, u8 reg, u8 data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(tc35892->i2c, reg, data);
|
||||
if (ret < 0)
|
||||
dev_err(tc35892->dev, "failed to write reg %#x: %d\n",
|
||||
reg, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tc35892_reg_write);
|
||||
|
||||
/**
|
||||
* tc35892_block_read() - read multiple TC35892 registers
|
||||
* @tc35892: Device to read from
|
||||
* @reg: First register
|
||||
* @length: Number of registers
|
||||
* @values: Buffer to write to
|
||||
*/
|
||||
int tc35892_block_read(struct tc35892 *tc35892, u8 reg, u8 length, u8 *values)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(tc35892->i2c, reg, length, values);
|
||||
if (ret < 0)
|
||||
dev_err(tc35892->dev, "failed to read regs %#x: %d\n",
|
||||
reg, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tc35892_block_read);
|
||||
|
||||
/**
|
||||
* tc35892_block_write() - write multiple TC35892 registers
|
||||
* @tc35892: Device to write to
|
||||
* @reg: First register
|
||||
* @length: Number of registers
|
||||
* @values: Values to write
|
||||
*/
|
||||
int tc35892_block_write(struct tc35892 *tc35892, u8 reg, u8 length,
|
||||
const u8 *values)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_i2c_block_data(tc35892->i2c, reg, length,
|
||||
values);
|
||||
if (ret < 0)
|
||||
dev_err(tc35892->dev, "failed to write regs %#x: %d\n",
|
||||
reg, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tc35892_block_write);
|
||||
|
||||
/**
|
||||
* tc35892_set_bits() - set the value of a bitfield in a TC35892 register
|
||||
* @tc35892: Device to write to
|
||||
* @reg: Register to write
|
||||
* @mask: Mask of bits to set
|
||||
* @values: Value to set
|
||||
*/
|
||||
int tc35892_set_bits(struct tc35892 *tc35892, u8 reg, u8 mask, u8 val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&tc35892->lock);
|
||||
|
||||
ret = tc35892_reg_read(tc35892, reg);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret &= ~mask;
|
||||
ret |= val;
|
||||
|
||||
ret = tc35892_reg_write(tc35892, reg, ret);
|
||||
|
||||
out:
|
||||
mutex_unlock(&tc35892->lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tc35892_set_bits);
|
||||
|
||||
static struct resource gpio_resources[] = {
|
||||
{
|
||||
.start = TC35892_INT_GPIIRQ,
|
||||
.end = TC35892_INT_GPIIRQ,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell tc35892_devs[] = {
|
||||
{
|
||||
.name = "tc35892-gpio",
|
||||
.num_resources = ARRAY_SIZE(gpio_resources),
|
||||
.resources = &gpio_resources[0],
|
||||
},
|
||||
};
|
||||
|
||||
static irqreturn_t tc35892_irq(int irq, void *data)
|
||||
{
|
||||
struct tc35892 *tc35892 = data;
|
||||
int status;
|
||||
|
||||
status = tc35892_reg_read(tc35892, TC35892_IRQST);
|
||||
if (status < 0)
|
||||
return IRQ_NONE;
|
||||
|
||||
while (status) {
|
||||
int bit = __ffs(status);
|
||||
|
||||
handle_nested_irq(tc35892->irq_base + bit);
|
||||
status &= ~(1 << bit);
|
||||
}
|
||||
|
||||
/*
|
||||
* A dummy read or write (to any register) appears to be necessary to
|
||||
* have the last interrupt clear (for example, GPIO IC write) take
|
||||
* effect.
|
||||
*/
|
||||
tc35892_reg_read(tc35892, TC35892_IRQST);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void tc35892_irq_dummy(unsigned int irq)
|
||||
{
|
||||
/* No mask/unmask at this level */
|
||||
}
|
||||
|
||||
static struct irq_chip tc35892_irq_chip = {
|
||||
.name = "tc35892",
|
||||
.mask = tc35892_irq_dummy,
|
||||
.unmask = tc35892_irq_dummy,
|
||||
};
|
||||
|
||||
static int tc35892_irq_init(struct tc35892 *tc35892)
|
||||
{
|
||||
int base = tc35892->irq_base;
|
||||
int irq;
|
||||
|
||||
for (irq = base; irq < base + TC35892_NR_INTERNAL_IRQS; irq++) {
|
||||
set_irq_chip_data(irq, tc35892);
|
||||
set_irq_chip_and_handler(irq, &tc35892_irq_chip,
|
||||
handle_edge_irq);
|
||||
set_irq_nested_thread(irq, 1);
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(irq, IRQF_VALID);
|
||||
#else
|
||||
set_irq_noprobe(irq);
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tc35892_irq_remove(struct tc35892 *tc35892)
|
||||
{
|
||||
int base = tc35892->irq_base;
|
||||
int irq;
|
||||
|
||||
for (irq = base; irq < base + TC35892_NR_INTERNAL_IRQS; irq++) {
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(irq, 0);
|
||||
#endif
|
||||
set_irq_chip_and_handler(irq, NULL, NULL);
|
||||
set_irq_chip_data(irq, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static int tc35892_chip_init(struct tc35892 *tc35892)
|
||||
{
|
||||
int manf, ver, ret;
|
||||
|
||||
manf = tc35892_reg_read(tc35892, TC35892_MANFCODE);
|
||||
if (manf < 0)
|
||||
return manf;
|
||||
|
||||
ver = tc35892_reg_read(tc35892, TC35892_VERSION);
|
||||
if (ver < 0)
|
||||
return ver;
|
||||
|
||||
if (manf != TC35892_MANFCODE_MAGIC) {
|
||||
dev_err(tc35892->dev, "unknown manufacturer: %#x\n", manf);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_info(tc35892->dev, "manufacturer: %#x, version: %#x\n", manf, ver);
|
||||
|
||||
/* Put everything except the IRQ module into reset */
|
||||
ret = tc35892_reg_write(tc35892, TC35892_RSTCTRL,
|
||||
TC35892_RSTCTRL_TIMRST
|
||||
| TC35892_RSTCTRL_ROTRST
|
||||
| TC35892_RSTCTRL_KBDRST
|
||||
| TC35892_RSTCTRL_GPIRST);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Clear the reset interrupt. */
|
||||
return tc35892_reg_write(tc35892, TC35892_RSTINTCLR, 0x1);
|
||||
}
|
||||
|
||||
static int __devinit tc35892_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct tc35892_platform_data *pdata = i2c->dev.platform_data;
|
||||
struct tc35892 *tc35892;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA
|
||||
| I2C_FUNC_SMBUS_I2C_BLOCK))
|
||||
return -EIO;
|
||||
|
||||
tc35892 = kzalloc(sizeof(struct tc35892), GFP_KERNEL);
|
||||
if (!tc35892)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&tc35892->lock);
|
||||
|
||||
tc35892->dev = &i2c->dev;
|
||||
tc35892->i2c = i2c;
|
||||
tc35892->pdata = pdata;
|
||||
tc35892->irq_base = pdata->irq_base;
|
||||
tc35892->num_gpio = id->driver_data;
|
||||
|
||||
i2c_set_clientdata(i2c, tc35892);
|
||||
|
||||
ret = tc35892_chip_init(tc35892);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
ret = tc35892_irq_init(tc35892);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
ret = request_threaded_irq(tc35892->i2c->irq, NULL, tc35892_irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
"tc35892", tc35892);
|
||||
if (ret) {
|
||||
dev_err(tc35892->dev, "failed to request IRQ: %d\n", ret);
|
||||
goto out_removeirq;
|
||||
}
|
||||
|
||||
ret = mfd_add_devices(tc35892->dev, -1, tc35892_devs,
|
||||
ARRAY_SIZE(tc35892_devs), NULL,
|
||||
tc35892->irq_base);
|
||||
if (ret) {
|
||||
dev_err(tc35892->dev, "failed to add children\n");
|
||||
goto out_freeirq;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_freeirq:
|
||||
free_irq(tc35892->i2c->irq, tc35892);
|
||||
out_removeirq:
|
||||
tc35892_irq_remove(tc35892);
|
||||
out_free:
|
||||
i2c_set_clientdata(i2c, NULL);
|
||||
kfree(tc35892);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit tc35892_remove(struct i2c_client *client)
|
||||
{
|
||||
struct tc35892 *tc35892 = i2c_get_clientdata(client);
|
||||
|
||||
mfd_remove_devices(tc35892->dev);
|
||||
|
||||
free_irq(tc35892->i2c->irq, tc35892);
|
||||
tc35892_irq_remove(tc35892);
|
||||
|
||||
i2c_set_clientdata(client, NULL);
|
||||
kfree(tc35892);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id tc35892_id[] = {
|
||||
{ "tc35892", 24 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tc35892_id);
|
||||
|
||||
static struct i2c_driver tc35892_driver = {
|
||||
.driver.name = "tc35892",
|
||||
.driver.owner = THIS_MODULE,
|
||||
.probe = tc35892_probe,
|
||||
.remove = __devexit_p(tc35892_remove),
|
||||
.id_table = tc35892_id,
|
||||
};
|
||||
|
||||
static int __init tc35892_init(void)
|
||||
{
|
||||
return i2c_add_driver(&tc35892_driver);
|
||||
}
|
||||
subsys_initcall(tc35892_init);
|
||||
|
||||
static void __exit tc35892_exit(void)
|
||||
{
|
||||
i2c_del_driver(&tc35892_driver);
|
||||
}
|
||||
module_exit(tc35892_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("TC35892 MFD core driver");
|
||||
MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent");
|
@ -31,6 +31,7 @@
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-ocores.h>
|
||||
#include <linux/i2c-xiic.h>
|
||||
#include <linux/i2c/tsc2007.h>
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
@ -40,6 +41,8 @@
|
||||
|
||||
#include <media/timb_radio.h>
|
||||
|
||||
#include <linux/timb_dma.h>
|
||||
|
||||
#include "timberdale.h"
|
||||
|
||||
#define DRIVER_NAME "timberdale"
|
||||
@ -69,6 +72,12 @@ static struct i2c_board_info timberdale_i2c_board_info[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static __devinitdata struct xiic_i2c_platform_data
|
||||
timberdale_xiic_platform_data = {
|
||||
.devices = timberdale_i2c_board_info,
|
||||
.num_devices = ARRAY_SIZE(timberdale_i2c_board_info)
|
||||
};
|
||||
|
||||
static __devinitdata struct ocores_i2c_platform_data
|
||||
timberdale_ocores_platform_data = {
|
||||
.regstep = 4,
|
||||
@ -77,7 +86,20 @@ timberdale_ocores_platform_data = {
|
||||
.num_devices = ARRAY_SIZE(timberdale_i2c_board_info)
|
||||
};
|
||||
|
||||
const static __devinitconst struct resource timberdale_ocores_resources[] = {
|
||||
static const __devinitconst struct resource timberdale_xiic_resources[] = {
|
||||
{
|
||||
.start = XIICOFFSET,
|
||||
.end = XIICEND,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
{
|
||||
.start = IRQ_TIMBERDALE_I2C,
|
||||
.end = IRQ_TIMBERDALE_I2C,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static const __devinitconst struct resource timberdale_ocores_resources[] = {
|
||||
{
|
||||
.start = OCORESOFFSET,
|
||||
.end = OCORESEND,
|
||||
@ -126,7 +148,7 @@ static __devinitdata struct xspi_platform_data timberdale_xspi_platform_data = {
|
||||
*/
|
||||
};
|
||||
|
||||
const static __devinitconst struct resource timberdale_spi_resources[] = {
|
||||
static const __devinitconst struct resource timberdale_spi_resources[] = {
|
||||
{
|
||||
.start = SPIOFFSET,
|
||||
.end = SPIEND,
|
||||
@ -139,7 +161,7 @@ const static __devinitconst struct resource timberdale_spi_resources[] = {
|
||||
},
|
||||
};
|
||||
|
||||
const static __devinitconst struct resource timberdale_eth_resources[] = {
|
||||
static const __devinitconst struct resource timberdale_eth_resources[] = {
|
||||
{
|
||||
.start = ETHOFFSET,
|
||||
.end = ETHEND,
|
||||
@ -159,7 +181,7 @@ static __devinitdata struct timbgpio_platform_data
|
||||
.irq_base = 200,
|
||||
};
|
||||
|
||||
const static __devinitconst struct resource timberdale_gpio_resources[] = {
|
||||
static const __devinitconst struct resource timberdale_gpio_resources[] = {
|
||||
{
|
||||
.start = GPIOOFFSET,
|
||||
.end = GPIOEND,
|
||||
@ -172,7 +194,7 @@ const static __devinitconst struct resource timberdale_gpio_resources[] = {
|
||||
},
|
||||
};
|
||||
|
||||
const static __devinitconst struct resource timberdale_mlogicore_resources[] = {
|
||||
static const __devinitconst struct resource timberdale_mlogicore_resources[] = {
|
||||
{
|
||||
.start = MLCOREOFFSET,
|
||||
.end = MLCOREEND,
|
||||
@ -190,7 +212,7 @@ const static __devinitconst struct resource timberdale_mlogicore_resources[] = {
|
||||
},
|
||||
};
|
||||
|
||||
const static __devinitconst struct resource timberdale_uart_resources[] = {
|
||||
static const __devinitconst struct resource timberdale_uart_resources[] = {
|
||||
{
|
||||
.start = UARTOFFSET,
|
||||
.end = UARTEND,
|
||||
@ -203,7 +225,7 @@ const static __devinitconst struct resource timberdale_uart_resources[] = {
|
||||
},
|
||||
};
|
||||
|
||||
const static __devinitconst struct resource timberdale_uartlite_resources[] = {
|
||||
static const __devinitconst struct resource timberdale_uartlite_resources[] = {
|
||||
{
|
||||
.start = UARTLITEOFFSET,
|
||||
.end = UARTLITEEND,
|
||||
@ -216,7 +238,7 @@ const static __devinitconst struct resource timberdale_uartlite_resources[] = {
|
||||
},
|
||||
};
|
||||
|
||||
const static __devinitconst struct resource timberdale_radio_resources[] = {
|
||||
static const __devinitconst struct resource timberdale_radio_resources[] = {
|
||||
{
|
||||
.start = RDSOFFSET,
|
||||
.end = RDSEND,
|
||||
@ -250,7 +272,66 @@ static __devinitdata struct timb_radio_platform_data
|
||||
}
|
||||
};
|
||||
|
||||
const static __devinitconst struct resource timberdale_dma_resources[] = {
|
||||
static __devinitdata struct timb_dma_platform_data timb_dma_platform_data = {
|
||||
.nr_channels = 10,
|
||||
.channels = {
|
||||
{
|
||||
/* UART RX */
|
||||
.rx = true,
|
||||
.descriptors = 2,
|
||||
.descriptor_elements = 1
|
||||
},
|
||||
{
|
||||
/* UART TX */
|
||||
.rx = false,
|
||||
.descriptors = 2,
|
||||
.descriptor_elements = 1
|
||||
},
|
||||
{
|
||||
/* MLB RX */
|
||||
.rx = true,
|
||||
.descriptors = 2,
|
||||
.descriptor_elements = 1
|
||||
},
|
||||
{
|
||||
/* MLB TX */
|
||||
.rx = false,
|
||||
.descriptors = 2,
|
||||
.descriptor_elements = 1
|
||||
},
|
||||
{
|
||||
/* Video RX */
|
||||
.rx = true,
|
||||
.bytes_per_line = 1440,
|
||||
.descriptors = 2,
|
||||
.descriptor_elements = 16
|
||||
},
|
||||
{
|
||||
/* Video framedrop */
|
||||
},
|
||||
{
|
||||
/* SDHCI RX */
|
||||
.rx = true,
|
||||
},
|
||||
{
|
||||
/* SDHCI TX */
|
||||
},
|
||||
{
|
||||
/* ETH RX */
|
||||
.rx = true,
|
||||
.descriptors = 2,
|
||||
.descriptor_elements = 1
|
||||
},
|
||||
{
|
||||
/* ETH TX */
|
||||
.rx = false,
|
||||
.descriptors = 2,
|
||||
.descriptor_elements = 1
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
static const __devinitconst struct resource timberdale_dma_resources[] = {
|
||||
{
|
||||
.start = DMAOFFSET,
|
||||
.end = DMAEND,
|
||||
@ -264,11 +345,25 @@ const static __devinitconst struct resource timberdale_dma_resources[] = {
|
||||
};
|
||||
|
||||
static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg0[] = {
|
||||
{
|
||||
.name = "timb-dma",
|
||||
.num_resources = ARRAY_SIZE(timberdale_dma_resources),
|
||||
.resources = timberdale_dma_resources,
|
||||
.platform_data = &timb_dma_platform_data,
|
||||
.data_size = sizeof(timb_dma_platform_data),
|
||||
},
|
||||
{
|
||||
.name = "timb-uart",
|
||||
.num_resources = ARRAY_SIZE(timberdale_uart_resources),
|
||||
.resources = timberdale_uart_resources,
|
||||
},
|
||||
{
|
||||
.name = "xiic-i2c",
|
||||
.num_resources = ARRAY_SIZE(timberdale_xiic_resources),
|
||||
.resources = timberdale_xiic_resources,
|
||||
.platform_data = &timberdale_xiic_platform_data,
|
||||
.data_size = sizeof(timberdale_xiic_platform_data),
|
||||
},
|
||||
{
|
||||
.name = "timb-gpio",
|
||||
.num_resources = ARRAY_SIZE(timberdale_gpio_resources),
|
||||
@ -295,14 +390,16 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg0[] = {
|
||||
.num_resources = ARRAY_SIZE(timberdale_eth_resources),
|
||||
.resources = timberdale_eth_resources,
|
||||
},
|
||||
};
|
||||
|
||||
static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = {
|
||||
{
|
||||
.name = "timb-dma",
|
||||
.num_resources = ARRAY_SIZE(timberdale_dma_resources),
|
||||
.resources = timberdale_dma_resources,
|
||||
.platform_data = &timb_dma_platform_data,
|
||||
.data_size = sizeof(timb_dma_platform_data),
|
||||
},
|
||||
};
|
||||
|
||||
static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = {
|
||||
{
|
||||
.name = "timb-uart",
|
||||
.num_resources = ARRAY_SIZE(timberdale_uart_resources),
|
||||
@ -313,6 +410,13 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = {
|
||||
.num_resources = ARRAY_SIZE(timberdale_uartlite_resources),
|
||||
.resources = timberdale_uartlite_resources,
|
||||
},
|
||||
{
|
||||
.name = "xiic-i2c",
|
||||
.num_resources = ARRAY_SIZE(timberdale_xiic_resources),
|
||||
.resources = timberdale_xiic_resources,
|
||||
.platform_data = &timberdale_xiic_platform_data,
|
||||
.data_size = sizeof(timberdale_xiic_platform_data),
|
||||
},
|
||||
{
|
||||
.name = "timb-gpio",
|
||||
.num_resources = ARRAY_SIZE(timberdale_gpio_resources),
|
||||
@ -344,19 +448,28 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = {
|
||||
.num_resources = ARRAY_SIZE(timberdale_eth_resources),
|
||||
.resources = timberdale_eth_resources,
|
||||
},
|
||||
{
|
||||
.name = "timb-dma",
|
||||
.num_resources = ARRAY_SIZE(timberdale_dma_resources),
|
||||
.resources = timberdale_dma_resources,
|
||||
},
|
||||
};
|
||||
|
||||
static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg2[] = {
|
||||
{
|
||||
.name = "timb-dma",
|
||||
.num_resources = ARRAY_SIZE(timberdale_dma_resources),
|
||||
.resources = timberdale_dma_resources,
|
||||
.platform_data = &timb_dma_platform_data,
|
||||
.data_size = sizeof(timb_dma_platform_data),
|
||||
},
|
||||
{
|
||||
.name = "timb-uart",
|
||||
.num_resources = ARRAY_SIZE(timberdale_uart_resources),
|
||||
.resources = timberdale_uart_resources,
|
||||
},
|
||||
{
|
||||
.name = "xiic-i2c",
|
||||
.num_resources = ARRAY_SIZE(timberdale_xiic_resources),
|
||||
.resources = timberdale_xiic_resources,
|
||||
.platform_data = &timberdale_xiic_platform_data,
|
||||
.data_size = sizeof(timberdale_xiic_platform_data),
|
||||
},
|
||||
{
|
||||
.name = "timb-gpio",
|
||||
.num_resources = ARRAY_SIZE(timberdale_gpio_resources),
|
||||
@ -378,14 +491,16 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg2[] = {
|
||||
.platform_data = &timberdale_xspi_platform_data,
|
||||
.data_size = sizeof(timberdale_xspi_platform_data),
|
||||
},
|
||||
};
|
||||
|
||||
static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = {
|
||||
{
|
||||
.name = "timb-dma",
|
||||
.num_resources = ARRAY_SIZE(timberdale_dma_resources),
|
||||
.resources = timberdale_dma_resources,
|
||||
.platform_data = &timb_dma_platform_data,
|
||||
.data_size = sizeof(timb_dma_platform_data),
|
||||
},
|
||||
};
|
||||
|
||||
static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = {
|
||||
{
|
||||
.name = "timb-uart",
|
||||
.num_resources = ARRAY_SIZE(timberdale_uart_resources),
|
||||
@ -424,11 +539,6 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = {
|
||||
.num_resources = ARRAY_SIZE(timberdale_eth_resources),
|
||||
.resources = timberdale_eth_resources,
|
||||
},
|
||||
{
|
||||
.name = "timb-dma",
|
||||
.num_resources = ARRAY_SIZE(timberdale_dma_resources),
|
||||
.resources = timberdale_dma_resources,
|
||||
},
|
||||
};
|
||||
|
||||
static const __devinitconst struct resource timberdale_sdhc_resources[] = {
|
||||
|
@ -23,7 +23,7 @@
|
||||
#ifndef MFD_TIMBERDALE_H
|
||||
#define MFD_TIMBERDALE_H
|
||||
|
||||
#define DRV_VERSION "0.1"
|
||||
#define DRV_VERSION "0.2"
|
||||
|
||||
/* This driver only support versions >= 3.8 and < 4.0 */
|
||||
#define TIMB_SUPPORTED_MAJOR 3
|
||||
@ -66,7 +66,7 @@
|
||||
|
||||
#define CHIPCTLOFFSET 0x800
|
||||
#define CHIPCTLEND 0x8ff
|
||||
#define CHIPCTLSIZE (CHIPCTLEND - CHIPCTLOFFSET)
|
||||
#define CHIPCTLSIZE (CHIPCTLEND - CHIPCTLOFFSET + 1)
|
||||
|
||||
#define INTCOFFSET 0xc00
|
||||
#define INTCEND 0xfff
|
||||
@ -127,4 +127,16 @@
|
||||
#define GPIO_PIN_BT_RST 15
|
||||
#define GPIO_NR_PINS 16
|
||||
|
||||
/* DMA Channels */
|
||||
#define DMA_UART_RX 0
|
||||
#define DMA_UART_TX 1
|
||||
#define DMA_MLB_RX 2
|
||||
#define DMA_MLB_TX 3
|
||||
#define DMA_VIDEO_RX 4
|
||||
#define DMA_VIDEO_DROP 5
|
||||
#define DMA_SDHCI_RX 6
|
||||
#define DMA_SDHCI_TX 7
|
||||
#define DMA_ETH_RX 8
|
||||
#define DMA_ETH_TX 9
|
||||
|
||||
#endif
|
||||
|
@ -530,8 +530,8 @@ static int __exit tps65010_remove(struct i2c_client *client)
|
||||
cancel_delayed_work(&tps->work);
|
||||
flush_scheduled_work();
|
||||
debugfs_remove(tps->file);
|
||||
kfree(tps);
|
||||
i2c_set_clientdata(client, NULL);
|
||||
kfree(tps);
|
||||
the_tps = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
159
drivers/mfd/tps6507x.c
Normal file
159
drivers/mfd/tps6507x.c
Normal file
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* tps6507x.c -- TPS6507x chip family multi-function driver
|
||||
*
|
||||
* Copyright (c) 2010 RidgeRun (todd.fischer@ridgerun.com)
|
||||
*
|
||||
* Author: Todd Fischer
|
||||
* todd.fischer@ridgerun.com
|
||||
*
|
||||
* Credits:
|
||||
*
|
||||
* Using code from wm831x-*.c, wm8400-core, Wolfson Microelectronics PLC.
|
||||
*
|
||||
* For licencing details see kernel-base/COPYING
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/tps6507x.h>
|
||||
|
||||
static struct mfd_cell tps6507x_devs[] = {
|
||||
{
|
||||
.name = "tps6507x-pmic",
|
||||
},
|
||||
{
|
||||
.name = "tps6507x-ts",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
static int tps6507x_i2c_read_device(struct tps6507x_dev *tps6507x, char reg,
|
||||
int bytes, void *dest)
|
||||
{
|
||||
struct i2c_client *i2c = tps6507x->i2c_client;
|
||||
struct i2c_msg xfer[2];
|
||||
int ret;
|
||||
|
||||
/* Write register */
|
||||
xfer[0].addr = i2c->addr;
|
||||
xfer[0].flags = 0;
|
||||
xfer[0].len = 1;
|
||||
xfer[0].buf = ®
|
||||
|
||||
/* Read data */
|
||||
xfer[1].addr = i2c->addr;
|
||||
xfer[1].flags = I2C_M_RD;
|
||||
xfer[1].len = bytes;
|
||||
xfer[1].buf = dest;
|
||||
|
||||
ret = i2c_transfer(i2c->adapter, xfer, 2);
|
||||
if (ret == 2)
|
||||
ret = 0;
|
||||
else if (ret >= 0)
|
||||
ret = -EIO;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tps6507x_i2c_write_device(struct tps6507x_dev *tps6507x, char reg,
|
||||
int bytes, void *src)
|
||||
{
|
||||
struct i2c_client *i2c = tps6507x->i2c_client;
|
||||
/* we add 1 byte for device register */
|
||||
u8 msg[TPS6507X_MAX_REGISTER + 1];
|
||||
int ret;
|
||||
|
||||
if (bytes > (TPS6507X_MAX_REGISTER + 1))
|
||||
return -EINVAL;
|
||||
|
||||
msg[0] = reg;
|
||||
memcpy(&msg[1], src, bytes);
|
||||
|
||||
ret = i2c_master_send(i2c, msg, bytes + 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != bytes + 1)
|
||||
return -EIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tps6507x_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct tps6507x_dev *tps6507x;
|
||||
int ret = 0;
|
||||
|
||||
tps6507x = kzalloc(sizeof(struct tps6507x_dev), GFP_KERNEL);
|
||||
if (tps6507x == NULL) {
|
||||
kfree(i2c);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(i2c, tps6507x);
|
||||
tps6507x->dev = &i2c->dev;
|
||||
tps6507x->i2c_client = i2c;
|
||||
tps6507x->read_dev = tps6507x_i2c_read_device;
|
||||
tps6507x->write_dev = tps6507x_i2c_write_device;
|
||||
|
||||
ret = mfd_add_devices(tps6507x->dev, -1,
|
||||
tps6507x_devs, ARRAY_SIZE(tps6507x_devs),
|
||||
NULL, 0);
|
||||
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
return ret;
|
||||
|
||||
err:
|
||||
mfd_remove_devices(tps6507x->dev);
|
||||
kfree(tps6507x);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tps6507x_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
struct tps6507x_dev *tps6507x = i2c_get_clientdata(i2c);
|
||||
|
||||
mfd_remove_devices(tps6507x->dev);
|
||||
kfree(tps6507x);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id tps6507x_i2c_id[] = {
|
||||
{ "tps6507x", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tps6507x_i2c_id);
|
||||
|
||||
|
||||
static struct i2c_driver tps6507x_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "tps6507x",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = tps6507x_i2c_probe,
|
||||
.remove = tps6507x_i2c_remove,
|
||||
.id_table = tps6507x_i2c_id,
|
||||
};
|
||||
|
||||
static int __init tps6507x_i2c_init(void)
|
||||
{
|
||||
return i2c_add_driver(&tps6507x_i2c_driver);
|
||||
}
|
||||
/* init early so consumer devices can complete system boot */
|
||||
subsys_initcall(tps6507x_i2c_init);
|
||||
|
||||
static void __exit tps6507x_i2c_exit(void)
|
||||
{
|
||||
i2c_del_driver(&tps6507x_i2c_driver);
|
||||
}
|
||||
module_exit(tps6507x_i2c_exit);
|
||||
|
||||
MODULE_DESCRIPTION("TPS6507x chip family multi-function driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -232,10 +232,11 @@ static const struct sih sih_modules_twl5031[8] = {
|
||||
},
|
||||
[6] = {
|
||||
/*
|
||||
* ACI doesn't use the same SIH organization.
|
||||
* For example, it supports only one interrupt line
|
||||
* ECI/DBI doesn't use the same SIH organization.
|
||||
* For example, it supports only one interrupt output line.
|
||||
* That is, the interrupts are seen on both INT1 and INT2 lines.
|
||||
*/
|
||||
.name = "aci",
|
||||
.name = "eci_dbi",
|
||||
.module = TWL5031_MODULE_ACCESSORY,
|
||||
.bits = 9,
|
||||
.bytes_ixr = 2,
|
||||
@ -247,8 +248,8 @@ static const struct sih sih_modules_twl5031[8] = {
|
||||
|
||||
},
|
||||
[7] = {
|
||||
/* Accessory */
|
||||
.name = "acc",
|
||||
/* Audio accessory */
|
||||
.name = "audio",
|
||||
.module = TWL5031_MODULE_ACCESSORY,
|
||||
.control_offset = TWL5031_ACCSIHCTRL,
|
||||
.bits = 2,
|
||||
|
@ -322,7 +322,11 @@ EXPORT_SYMBOL_GPL(wm831x_set_bits);
|
||||
*/
|
||||
int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
|
||||
{
|
||||
int ret, src;
|
||||
int ret, src, irq_masked, timeout;
|
||||
|
||||
/* Are we using the interrupt? */
|
||||
irq_masked = wm831x_reg_read(wm831x, WM831X_INTERRUPT_STATUS_1_MASK);
|
||||
irq_masked &= WM831X_AUXADC_DATA_EINT;
|
||||
|
||||
mutex_lock(&wm831x->auxadc_lock);
|
||||
|
||||
@ -342,6 +346,9 @@ int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Clear any notification from a very late arriving interrupt */
|
||||
try_wait_for_completion(&wm831x->auxadc_done);
|
||||
|
||||
ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
|
||||
WM831X_AUX_CVT_ENA, WM831X_AUX_CVT_ENA);
|
||||
if (ret < 0) {
|
||||
@ -349,22 +356,46 @@ int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
|
||||
goto disable;
|
||||
}
|
||||
|
||||
/* If an interrupt arrived late clean up after it */
|
||||
try_wait_for_completion(&wm831x->auxadc_done);
|
||||
if (irq_masked) {
|
||||
/* If we're not using interrupts then poll the
|
||||
* interrupt status register */
|
||||
timeout = 5;
|
||||
while (timeout) {
|
||||
msleep(1);
|
||||
|
||||
/* Ignore the result to allow us to soldier on without IRQ hookup */
|
||||
wait_for_completion_timeout(&wm831x->auxadc_done, msecs_to_jiffies(5));
|
||||
ret = wm831x_reg_read(wm831x,
|
||||
WM831X_INTERRUPT_STATUS_1);
|
||||
if (ret < 0) {
|
||||
dev_err(wm831x->dev,
|
||||
"ISR 1 read failed: %d\n", ret);
|
||||
goto disable;
|
||||
}
|
||||
|
||||
ret = wm831x_reg_read(wm831x, WM831X_AUXADC_CONTROL);
|
||||
if (ret < 0) {
|
||||
dev_err(wm831x->dev, "AUXADC status read failed: %d\n", ret);
|
||||
goto disable;
|
||||
}
|
||||
|
||||
if (ret & WM831X_AUX_CVT_ENA) {
|
||||
dev_err(wm831x->dev, "Timed out reading AUXADC\n");
|
||||
ret = -EBUSY;
|
||||
goto disable;
|
||||
/* Did it complete? */
|
||||
if (ret & WM831X_AUXADC_DATA_EINT) {
|
||||
wm831x_reg_write(wm831x,
|
||||
WM831X_INTERRUPT_STATUS_1,
|
||||
WM831X_AUXADC_DATA_EINT);
|
||||
break;
|
||||
} else {
|
||||
dev_err(wm831x->dev,
|
||||
"AUXADC conversion timeout\n");
|
||||
ret = -EBUSY;
|
||||
goto disable;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* If we are using interrupts then wait for the
|
||||
* interrupt to complete. Use an extremely long
|
||||
* timeout to handle situations with heavy load where
|
||||
* the notification of the interrupt may be delayed by
|
||||
* threaded IRQ handling. */
|
||||
if (!wait_for_completion_timeout(&wm831x->auxadc_done,
|
||||
msecs_to_jiffies(500))) {
|
||||
dev_err(wm831x->dev, "Timed out waiting for AUXADC\n");
|
||||
ret = -EBUSY;
|
||||
goto disable;
|
||||
}
|
||||
}
|
||||
|
||||
ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA);
|
||||
@ -1463,6 +1494,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
|
||||
case WM8310:
|
||||
parent = WM8310;
|
||||
wm831x->num_gpio = 16;
|
||||
wm831x->charger_irq_wake = 1;
|
||||
if (rev > 0) {
|
||||
wm831x->has_gpio_ena = 1;
|
||||
wm831x->has_cs_sts = 1;
|
||||
@ -1474,6 +1506,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
|
||||
case WM8311:
|
||||
parent = WM8311;
|
||||
wm831x->num_gpio = 16;
|
||||
wm831x->charger_irq_wake = 1;
|
||||
if (rev > 0) {
|
||||
wm831x->has_gpio_ena = 1;
|
||||
wm831x->has_cs_sts = 1;
|
||||
@ -1485,6 +1518,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
|
||||
case WM8312:
|
||||
parent = WM8312;
|
||||
wm831x->num_gpio = 16;
|
||||
wm831x->charger_irq_wake = 1;
|
||||
if (rev > 0) {
|
||||
wm831x->has_gpio_ena = 1;
|
||||
wm831x->has_cs_sts = 1;
|
||||
@ -1623,6 +1657,42 @@ static void wm831x_device_exit(struct wm831x *wm831x)
|
||||
kfree(wm831x);
|
||||
}
|
||||
|
||||
static int wm831x_device_suspend(struct wm831x *wm831x)
|
||||
{
|
||||
int reg, mask;
|
||||
|
||||
/* If the charger IRQs are a wake source then make sure we ack
|
||||
* them even if they're not actively being used (eg, no power
|
||||
* driver or no IRQ line wired up) then acknowledge the
|
||||
* interrupts otherwise suspend won't last very long.
|
||||
*/
|
||||
if (wm831x->charger_irq_wake) {
|
||||
reg = wm831x_reg_read(wm831x, WM831X_INTERRUPT_STATUS_2_MASK);
|
||||
|
||||
mask = WM831X_CHG_BATT_HOT_EINT |
|
||||
WM831X_CHG_BATT_COLD_EINT |
|
||||
WM831X_CHG_BATT_FAIL_EINT |
|
||||
WM831X_CHG_OV_EINT | WM831X_CHG_END_EINT |
|
||||
WM831X_CHG_TO_EINT | WM831X_CHG_MODE_EINT |
|
||||
WM831X_CHG_START_EINT;
|
||||
|
||||
/* If any of the interrupts are masked read the statuses */
|
||||
if (reg & mask)
|
||||
reg = wm831x_reg_read(wm831x,
|
||||
WM831X_INTERRUPT_STATUS_2);
|
||||
|
||||
if (reg & mask) {
|
||||
dev_info(wm831x->dev,
|
||||
"Acknowledging masked charger IRQs: %x\n",
|
||||
reg & mask);
|
||||
wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_2,
|
||||
reg & mask);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm831x_i2c_read_device(struct wm831x *wm831x, unsigned short reg,
|
||||
int bytes, void *dest)
|
||||
{
|
||||
@ -1697,6 +1767,13 @@ static int wm831x_i2c_remove(struct i2c_client *i2c)
|
||||
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 },
|
||||
@ -1714,6 +1791,7 @@ static struct i2c_driver wm831x_i2c_driver = {
|
||||
},
|
||||
.probe = wm831x_i2c_probe,
|
||||
.remove = wm831x_i2c_remove,
|
||||
.suspend = wm831x_i2c_suspend,
|
||||
.id_table = wm831x_i2c_id,
|
||||
};
|
||||
|
||||
|
@ -39,8 +39,6 @@ struct wm831x_irq_data {
|
||||
int primary;
|
||||
int reg;
|
||||
int mask;
|
||||
irq_handler_t handler;
|
||||
void *handler_data;
|
||||
};
|
||||
|
||||
static struct wm831x_irq_data wm831x_irqs[] = {
|
||||
@ -492,6 +490,14 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
|
||||
|
||||
mutex_init(&wm831x->irq_lock);
|
||||
|
||||
/* Mask the individual interrupt sources */
|
||||
for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) {
|
||||
wm831x->irq_masks_cur[i] = 0xffff;
|
||||
wm831x->irq_masks_cache[i] = 0xffff;
|
||||
wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1_MASK + i,
|
||||
0xffff);
|
||||
}
|
||||
|
||||
if (!irq) {
|
||||
dev_warn(wm831x->dev,
|
||||
"No interrupt specified - functionality limited\n");
|
||||
@ -507,14 +513,6 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
|
||||
wm831x->irq = irq;
|
||||
wm831x->irq_base = pdata->irq_base;
|
||||
|
||||
/* Mask the individual interrupt sources */
|
||||
for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) {
|
||||
wm831x->irq_masks_cur[i] = 0xffff;
|
||||
wm831x->irq_masks_cache[i] = 0xffff;
|
||||
wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1_MASK + i,
|
||||
0xffff);
|
||||
}
|
||||
|
||||
/* Register them with genirq */
|
||||
for (cur_irq = wm831x->irq_base;
|
||||
cur_irq < ARRAY_SIZE(wm831x_irqs) + wm831x->irq_base;
|
||||
|
@ -64,10 +64,8 @@ static int wm8350_i2c_probe(struct i2c_client *i2c,
|
||||
int ret = 0;
|
||||
|
||||
wm8350 = kzalloc(sizeof(struct wm8350), GFP_KERNEL);
|
||||
if (wm8350 == NULL) {
|
||||
kfree(i2c);
|
||||
if (wm8350 == NULL)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(i2c, wm8350);
|
||||
wm8350->dev = &i2c->dev;
|
||||
@ -82,6 +80,7 @@ static int wm8350_i2c_probe(struct i2c_client *i2c,
|
||||
return ret;
|
||||
|
||||
err:
|
||||
i2c_set_clientdata(i2c, NULL);
|
||||
kfree(wm8350);
|
||||
return ret;
|
||||
}
|
||||
@ -91,6 +90,7 @@ static int wm8350_i2c_remove(struct i2c_client *i2c)
|
||||
struct wm8350 *wm8350 = i2c_get_clientdata(i2c);
|
||||
|
||||
wm8350_device_exit(wm8350);
|
||||
i2c_set_clientdata(i2c, NULL);
|
||||
kfree(wm8350);
|
||||
|
||||
return 0;
|
||||
|
@ -118,7 +118,7 @@ static int wm8400_read(struct wm8400 *wm8400, u8 reg, int num_regs, u16 *dest)
|
||||
{
|
||||
int i, ret = 0;
|
||||
|
||||
BUG_ON(reg + num_regs - 1 > ARRAY_SIZE(wm8400->reg_cache));
|
||||
BUG_ON(reg + num_regs > ARRAY_SIZE(wm8400->reg_cache));
|
||||
|
||||
/* If there are any volatile reads then read back the entire block */
|
||||
for (i = reg; i < reg + num_regs; i++)
|
||||
@ -144,7 +144,7 @@ static int wm8400_write(struct wm8400 *wm8400, u8 reg, int num_regs,
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
BUG_ON(reg + num_regs - 1 > ARRAY_SIZE(wm8400->reg_cache));
|
||||
BUG_ON(reg + num_regs > ARRAY_SIZE(wm8400->reg_cache));
|
||||
|
||||
for (i = 0; i < num_regs; i++) {
|
||||
BUG_ON(!reg_data[reg + i].writable);
|
||||
|
@ -63,6 +63,16 @@ config CAN_BFIN
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called bfin_can.
|
||||
|
||||
config CAN_JANZ_ICAN3
|
||||
tristate "Janz VMOD-ICAN3 Intelligent CAN controller"
|
||||
depends on CAN_DEV && MFD_JANZ_CMODIO
|
||||
---help---
|
||||
Driver for Janz VMOD-ICAN3 Intelligent CAN controller module, which
|
||||
connects to a MODULbus carrier board.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called janz-ican3.ko.
|
||||
|
||||
source "drivers/net/can/mscan/Kconfig"
|
||||
|
||||
source "drivers/net/can/sja1000/Kconfig"
|
||||
|
@ -15,5 +15,6 @@ obj-$(CONFIG_CAN_AT91) += at91_can.o
|
||||
obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o
|
||||
obj-$(CONFIG_CAN_MCP251X) += mcp251x.o
|
||||
obj-$(CONFIG_CAN_BFIN) += bfin_can.o
|
||||
obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o
|
||||
|
||||
ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
|
||||
|
1830
drivers/net/can/janz-ican3.c
Normal file
1830
drivers/net/can/janz-ican3.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -16,7 +16,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/mfd/ab3100.h>
|
||||
#include <linux/mfd/abx500.h>
|
||||
|
||||
/* LDO registers and some handy masking definitions for AB3100 */
|
||||
#define AB3100_LDO_A 0x40
|
||||
@ -41,7 +41,7 @@
|
||||
* struct ab3100_regulator
|
||||
* A struct passed around the individual regulator functions
|
||||
* @platform_device: platform device holding this regulator
|
||||
* @ab3100: handle to the AB3100 parent chip
|
||||
* @dev: handle to the device
|
||||
* @plfdata: AB3100 platform data passed in at probe time
|
||||
* @regreg: regulator register number in the AB3100
|
||||
* @fixed_voltage: a fixed voltage for this regulator, if this
|
||||
@ -52,7 +52,7 @@
|
||||
*/
|
||||
struct ab3100_regulator {
|
||||
struct regulator_dev *rdev;
|
||||
struct ab3100 *ab3100;
|
||||
struct device *dev;
|
||||
struct ab3100_platform_data *plfdata;
|
||||
u8 regreg;
|
||||
int fixed_voltage;
|
||||
@ -183,7 +183,7 @@ static int ab3100_enable_regulator(struct regulator_dev *reg)
|
||||
int err;
|
||||
u8 regval;
|
||||
|
||||
err = ab3100_get_register_interruptible(abreg->ab3100, abreg->regreg,
|
||||
err = abx500_get_register_interruptible(abreg->dev, 0, abreg->regreg,
|
||||
®val);
|
||||
if (err) {
|
||||
dev_warn(®->dev, "failed to get regid %d value\n",
|
||||
@ -197,7 +197,7 @@ static int ab3100_enable_regulator(struct regulator_dev *reg)
|
||||
|
||||
regval |= AB3100_REG_ON_MASK;
|
||||
|
||||
err = ab3100_set_register_interruptible(abreg->ab3100, abreg->regreg,
|
||||
err = abx500_set_register_interruptible(abreg->dev, 0, abreg->regreg,
|
||||
regval);
|
||||
if (err) {
|
||||
dev_warn(®->dev, "failed to set regid %d value\n",
|
||||
@ -245,14 +245,14 @@ static int ab3100_disable_regulator(struct regulator_dev *reg)
|
||||
if (abreg->regreg == AB3100_LDO_D) {
|
||||
dev_info(®->dev, "disabling LDO D - shut down system\n");
|
||||
/* Setting LDO D to 0x00 cuts the power to the SoC */
|
||||
return ab3100_set_register_interruptible(abreg->ab3100,
|
||||
return abx500_set_register_interruptible(abreg->dev, 0,
|
||||
AB3100_LDO_D, 0x00U);
|
||||
}
|
||||
|
||||
/*
|
||||
* All other regulators are handled here
|
||||
*/
|
||||
err = ab3100_get_register_interruptible(abreg->ab3100, abreg->regreg,
|
||||
err = abx500_get_register_interruptible(abreg->dev, 0, abreg->regreg,
|
||||
®val);
|
||||
if (err) {
|
||||
dev_err(®->dev, "unable to get register 0x%x\n",
|
||||
@ -260,7 +260,7 @@ static int ab3100_disable_regulator(struct regulator_dev *reg)
|
||||
return err;
|
||||
}
|
||||
regval &= ~AB3100_REG_ON_MASK;
|
||||
return ab3100_set_register_interruptible(abreg->ab3100, abreg->regreg,
|
||||
return abx500_set_register_interruptible(abreg->dev, 0, abreg->regreg,
|
||||
regval);
|
||||
}
|
||||
|
||||
@ -270,7 +270,7 @@ static int ab3100_is_enabled_regulator(struct regulator_dev *reg)
|
||||
u8 regval;
|
||||
int err;
|
||||
|
||||
err = ab3100_get_register_interruptible(abreg->ab3100, abreg->regreg,
|
||||
err = abx500_get_register_interruptible(abreg->dev, 0, abreg->regreg,
|
||||
®val);
|
||||
if (err) {
|
||||
dev_err(®->dev, "unable to get register 0x%x\n",
|
||||
@ -305,7 +305,7 @@ static int ab3100_get_voltage_regulator(struct regulator_dev *reg)
|
||||
* For variable types, read out setting and index into
|
||||
* supplied voltage list.
|
||||
*/
|
||||
err = ab3100_get_register_interruptible(abreg->ab3100,
|
||||
err = abx500_get_register_interruptible(abreg->dev, 0,
|
||||
abreg->regreg, ®val);
|
||||
if (err) {
|
||||
dev_warn(®->dev,
|
||||
@ -373,7 +373,7 @@ static int ab3100_set_voltage_regulator(struct regulator_dev *reg,
|
||||
if (bestindex < 0)
|
||||
return bestindex;
|
||||
|
||||
err = ab3100_get_register_interruptible(abreg->ab3100,
|
||||
err = abx500_get_register_interruptible(abreg->dev, 0,
|
||||
abreg->regreg, ®val);
|
||||
if (err) {
|
||||
dev_warn(®->dev,
|
||||
@ -386,7 +386,7 @@ static int ab3100_set_voltage_regulator(struct regulator_dev *reg,
|
||||
regval &= ~0xE0;
|
||||
regval |= (bestindex << 5);
|
||||
|
||||
err = ab3100_set_register_interruptible(abreg->ab3100,
|
||||
err = abx500_set_register_interruptible(abreg->dev, 0,
|
||||
abreg->regreg, regval);
|
||||
if (err)
|
||||
dev_warn(®->dev, "failed to set regulator register %02x\n",
|
||||
@ -414,7 +414,7 @@ static int ab3100_set_suspend_voltage_regulator(struct regulator_dev *reg,
|
||||
/* LDO E and BUCK have special suspend voltages you can set */
|
||||
bestindex = ab3100_get_best_voltage_index(reg, uV, uV);
|
||||
|
||||
err = ab3100_get_register_interruptible(abreg->ab3100,
|
||||
err = abx500_get_register_interruptible(abreg->dev, 0,
|
||||
targetreg, ®val);
|
||||
if (err) {
|
||||
dev_warn(®->dev,
|
||||
@ -427,7 +427,7 @@ static int ab3100_set_suspend_voltage_regulator(struct regulator_dev *reg,
|
||||
regval &= ~0xE0;
|
||||
regval |= (bestindex << 5);
|
||||
|
||||
err = ab3100_set_register_interruptible(abreg->ab3100,
|
||||
err = abx500_set_register_interruptible(abreg->dev, 0,
|
||||
targetreg, regval);
|
||||
if (err)
|
||||
dev_warn(®->dev, "failed to set regulator register %02x\n",
|
||||
@ -574,13 +574,12 @@ ab3100_regulator_desc[AB3100_NUM_REGULATORS] = {
|
||||
static int __devinit ab3100_regulators_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ab3100_platform_data *plfdata = pdev->dev.platform_data;
|
||||
struct ab3100 *ab3100 = platform_get_drvdata(pdev);
|
||||
int err = 0;
|
||||
u8 data;
|
||||
int i;
|
||||
|
||||
/* Check chip state */
|
||||
err = ab3100_get_register_interruptible(ab3100,
|
||||
err = abx500_get_register_interruptible(&pdev->dev, 0,
|
||||
AB3100_LDO_D, &data);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "could not read initial status of LDO_D\n");
|
||||
@ -595,7 +594,7 @@ static int __devinit ab3100_regulators_probe(struct platform_device *pdev)
|
||||
|
||||
/* Set up regulators */
|
||||
for (i = 0; i < ARRAY_SIZE(ab3100_reg_init_order); i++) {
|
||||
err = ab3100_set_register_interruptible(ab3100,
|
||||
err = abx500_set_register_interruptible(&pdev->dev, 0,
|
||||
ab3100_reg_init_order[i],
|
||||
plfdata->reg_initvals[i]);
|
||||
if (err) {
|
||||
@ -617,7 +616,7 @@ static int __devinit ab3100_regulators_probe(struct platform_device *pdev)
|
||||
* see what it looks like for a certain machine, go
|
||||
* into the machine I2C setup.
|
||||
*/
|
||||
reg->ab3100 = ab3100;
|
||||
reg->dev = &pdev->dev;
|
||||
reg->plfdata = plfdata;
|
||||
|
||||
/*
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -9,7 +9,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/mfd/ab3100.h>
|
||||
#include <linux/mfd/abx500.h>
|
||||
|
||||
/* Clock rate in Hz */
|
||||
#define AB3100_RTC_CLOCK_RATE 32768
|
||||
@ -45,7 +45,6 @@
|
||||
*/
|
||||
static int ab3100_rtc_set_mmss(struct device *dev, unsigned long secs)
|
||||
{
|
||||
struct ab3100 *ab3100_data = dev_get_drvdata(dev);
|
||||
u8 regs[] = {AB3100_TI0, AB3100_TI1, AB3100_TI2,
|
||||
AB3100_TI3, AB3100_TI4, AB3100_TI5};
|
||||
unsigned char buf[6];
|
||||
@ -61,27 +60,26 @@ static int ab3100_rtc_set_mmss(struct device *dev, unsigned long secs)
|
||||
buf[5] = (fat_time >> 40) & 0xFF;
|
||||
|
||||
for (i = 0; i < 6; i++) {
|
||||
err = ab3100_set_register_interruptible(ab3100_data,
|
||||
err = abx500_set_register_interruptible(dev, 0,
|
||||
regs[i], buf[i]);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Set the flag to mark that the clock is now set */
|
||||
return ab3100_mask_and_set_register_interruptible(ab3100_data,
|
||||
return abx500_mask_and_set_register_interruptible(dev, 0,
|
||||
AB3100_RTC,
|
||||
0xFE, 0x01);
|
||||
0x01, 0x01);
|
||||
|
||||
}
|
||||
|
||||
static int ab3100_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct ab3100 *ab3100_data = dev_get_drvdata(dev);
|
||||
unsigned long time;
|
||||
u8 rtcval;
|
||||
int err;
|
||||
|
||||
err = ab3100_get_register_interruptible(ab3100_data,
|
||||
err = abx500_get_register_interruptible(dev, 0,
|
||||
AB3100_RTC, &rtcval);
|
||||
if (err)
|
||||
return err;
|
||||
@ -94,7 +92,7 @@ static int ab3100_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
u8 buf[6];
|
||||
|
||||
/* Read out time registers */
|
||||
err = ab3100_get_register_page_interruptible(ab3100_data,
|
||||
err = abx500_get_register_page_interruptible(dev, 0,
|
||||
AB3100_TI0,
|
||||
buf, 6);
|
||||
if (err != 0)
|
||||
@ -114,7 +112,6 @@ static int ab3100_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
|
||||
static int ab3100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
{
|
||||
struct ab3100 *ab3100_data = dev_get_drvdata(dev);
|
||||
unsigned long time;
|
||||
u64 fat_time;
|
||||
u8 buf[6];
|
||||
@ -122,7 +119,7 @@ static int ab3100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
int err;
|
||||
|
||||
/* Figure out if alarm is enabled or not */
|
||||
err = ab3100_get_register_interruptible(ab3100_data,
|
||||
err = abx500_get_register_interruptible(dev, 0,
|
||||
AB3100_RTC, &rtcval);
|
||||
if (err)
|
||||
return err;
|
||||
@ -133,7 +130,7 @@ static int ab3100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
/* No idea how this could be represented */
|
||||
alarm->pending = 0;
|
||||
/* Read out alarm registers, only 4 bytes */
|
||||
err = ab3100_get_register_page_interruptible(ab3100_data,
|
||||
err = abx500_get_register_page_interruptible(dev, 0,
|
||||
AB3100_AL0, buf, 4);
|
||||
if (err)
|
||||
return err;
|
||||
@ -148,7 +145,6 @@ static int ab3100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
|
||||
static int ab3100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
{
|
||||
struct ab3100 *ab3100_data = dev_get_drvdata(dev);
|
||||
u8 regs[] = {AB3100_AL0, AB3100_AL1, AB3100_AL2, AB3100_AL3};
|
||||
unsigned char buf[4];
|
||||
unsigned long secs;
|
||||
@ -165,21 +161,19 @@ static int ab3100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
|
||||
/* Set the alarm */
|
||||
for (i = 0; i < 4; i++) {
|
||||
err = ab3100_set_register_interruptible(ab3100_data,
|
||||
err = abx500_set_register_interruptible(dev, 0,
|
||||
regs[i], buf[i]);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
/* Then enable the alarm */
|
||||
return ab3100_mask_and_set_register_interruptible(ab3100_data,
|
||||
AB3100_RTC, ~(1 << 2),
|
||||
return abx500_mask_and_set_register_interruptible(dev, 0,
|
||||
AB3100_RTC, (1 << 2),
|
||||
alarm->enabled << 2);
|
||||
}
|
||||
|
||||
static int ab3100_rtc_irq_enable(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
struct ab3100 *ab3100_data = dev_get_drvdata(dev);
|
||||
|
||||
/*
|
||||
* It's not possible to enable/disable the alarm IRQ for this RTC.
|
||||
* It does not actually trigger any IRQ: instead its only function is
|
||||
@ -188,12 +182,12 @@ static int ab3100_rtc_irq_enable(struct device *dev, unsigned int enabled)
|
||||
* and need to be handled there instead.
|
||||
*/
|
||||
if (enabled)
|
||||
return ab3100_mask_and_set_register_interruptible(ab3100_data,
|
||||
AB3100_RTC, ~(1 << 2),
|
||||
return abx500_mask_and_set_register_interruptible(dev, 0,
|
||||
AB3100_RTC, (1 << 2),
|
||||
1 << 2);
|
||||
else
|
||||
return ab3100_mask_and_set_register_interruptible(ab3100_data,
|
||||
AB3100_RTC, ~(1 << 2),
|
||||
return abx500_mask_and_set_register_interruptible(dev, 0,
|
||||
AB3100_RTC, (1 << 2),
|
||||
0);
|
||||
}
|
||||
|
||||
@ -210,10 +204,9 @@ static int __init ab3100_rtc_probe(struct platform_device *pdev)
|
||||
int err;
|
||||
u8 regval;
|
||||
struct rtc_device *rtc;
|
||||
struct ab3100 *ab3100_data = platform_get_drvdata(pdev);
|
||||
|
||||
/* The first RTC register needs special treatment */
|
||||
err = ab3100_get_register_interruptible(ab3100_data,
|
||||
err = abx500_get_register_interruptible(&pdev->dev, 0,
|
||||
AB3100_RTC, ®val);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "unable to read RTC register\n");
|
||||
@ -231,7 +224,7 @@ static int __init ab3100_rtc_probe(struct platform_device *pdev)
|
||||
* This bit remains until RTC power is lost.
|
||||
*/
|
||||
regval = 1 | RTC_SETTING;
|
||||
err = ab3100_set_register_interruptible(ab3100_data,
|
||||
err = abx500_set_register_interruptible(&pdev->dev, 0,
|
||||
AB3100_RTC, regval);
|
||||
/* Ignore any error on this write */
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* RDC321x watchdog driver
|
||||
*
|
||||
* Copyright (C) 2007 Florian Fainelli <florian@openwrt.org>
|
||||
* Copyright (C) 2007-2010 Florian Fainelli <florian@openwrt.org>
|
||||
*
|
||||
* This driver is highly inspired from the cpu5_wdt driver
|
||||
*
|
||||
@ -36,8 +36,7 @@
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <asm/rdc321x_defs.h>
|
||||
#include <linux/mfd/rdc321x.h>
|
||||
|
||||
#define RDC_WDT_MASK 0x80000000 /* Mask */
|
||||
#define RDC_WDT_EN 0x00800000 /* Enable bit */
|
||||
@ -63,6 +62,8 @@ static struct {
|
||||
int default_ticks;
|
||||
unsigned long inuse;
|
||||
spinlock_t lock;
|
||||
struct pci_dev *sb_pdev;
|
||||
int base_reg;
|
||||
} rdc321x_wdt_device;
|
||||
|
||||
/* generic helper functions */
|
||||
@ -70,14 +71,18 @@ static struct {
|
||||
static void rdc321x_wdt_trigger(unsigned long unused)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
if (rdc321x_wdt_device.running)
|
||||
ticks--;
|
||||
|
||||
/* keep watchdog alive */
|
||||
spin_lock_irqsave(&rdc321x_wdt_device.lock, flags);
|
||||
outl(RDC_WDT_EN | inl(RDC3210_CFGREG_DATA),
|
||||
RDC3210_CFGREG_DATA);
|
||||
pci_read_config_dword(rdc321x_wdt_device.sb_pdev,
|
||||
rdc321x_wdt_device.base_reg, &val);
|
||||
val |= RDC_WDT_EN;
|
||||
pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
|
||||
rdc321x_wdt_device.base_reg, val);
|
||||
spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags);
|
||||
|
||||
/* requeue?? */
|
||||
@ -105,10 +110,13 @@ static void rdc321x_wdt_start(void)
|
||||
|
||||
/* Clear the timer */
|
||||
spin_lock_irqsave(&rdc321x_wdt_device.lock, flags);
|
||||
outl(RDC_CLS_TMR, RDC3210_CFGREG_ADDR);
|
||||
pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
|
||||
rdc321x_wdt_device.base_reg, RDC_CLS_TMR);
|
||||
|
||||
/* Enable watchdog and set the timeout to 81.92 us */
|
||||
outl(RDC_WDT_EN | RDC_WDT_CNT, RDC3210_CFGREG_DATA);
|
||||
pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
|
||||
rdc321x_wdt_device.base_reg,
|
||||
RDC_WDT_EN | RDC_WDT_CNT);
|
||||
spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags);
|
||||
|
||||
mod_timer(&rdc321x_wdt_device.timer,
|
||||
@ -148,7 +156,7 @@ static long rdc321x_wdt_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
void __user *argp = (void __user *)arg;
|
||||
unsigned int value;
|
||||
u32 value;
|
||||
static const struct watchdog_info ident = {
|
||||
.options = WDIOF_CARDRESET,
|
||||
.identity = "RDC321x WDT",
|
||||
@ -162,9 +170,10 @@ static long rdc321x_wdt_ioctl(struct file *file, unsigned int cmd,
|
||||
case WDIOC_GETSTATUS:
|
||||
/* Read the value from the DATA register */
|
||||
spin_lock_irqsave(&rdc321x_wdt_device.lock, flags);
|
||||
value = inl(RDC3210_CFGREG_DATA);
|
||||
pci_read_config_dword(rdc321x_wdt_device.sb_pdev,
|
||||
rdc321x_wdt_device.base_reg, &value);
|
||||
spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags);
|
||||
if (copy_to_user(argp, &value, sizeof(int)))
|
||||
if (copy_to_user(argp, &value, sizeof(u32)))
|
||||
return -EFAULT;
|
||||
break;
|
||||
case WDIOC_GETSUPPORT:
|
||||
@ -219,17 +228,35 @@ static struct miscdevice rdc321x_wdt_misc = {
|
||||
static int __devinit rdc321x_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
int err;
|
||||
struct resource *r;
|
||||
struct rdc321x_wdt_pdata *pdata;
|
||||
|
||||
pdata = pdev->dev.platform_data;
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "no platform data supplied\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
r = platform_get_resource_byname(pdev, IORESOURCE_IO, "wdt-reg");
|
||||
if (!r) {
|
||||
dev_err(&pdev->dev, "failed to get wdt-reg resource\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
rdc321x_wdt_device.sb_pdev = pdata->sb_pdev;
|
||||
rdc321x_wdt_device.base_reg = r->start;
|
||||
|
||||
err = misc_register(&rdc321x_wdt_misc);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR PFX "watchdog misc_register failed\n");
|
||||
dev_err(&pdev->dev, "misc_register failed\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
spin_lock_init(&rdc321x_wdt_device.lock);
|
||||
|
||||
/* Reset the watchdog */
|
||||
outl(RDC_WDT_RST, RDC3210_CFGREG_DATA);
|
||||
pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
|
||||
rdc321x_wdt_device.base_reg, RDC_WDT_RST);
|
||||
|
||||
init_completion(&rdc321x_wdt_device.stop);
|
||||
rdc321x_wdt_device.queue = 0;
|
||||
@ -240,7 +267,7 @@ static int __devinit rdc321x_wdt_probe(struct platform_device *pdev)
|
||||
|
||||
rdc321x_wdt_device.default_ticks = ticks;
|
||||
|
||||
printk(KERN_INFO PFX "watchdog init success\n");
|
||||
dev_info(&pdev->dev, "watchdog init success\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
24
include/linux/input/tps6507x-ts.h
Normal file
24
include/linux/input/tps6507x-ts.h
Normal file
@ -0,0 +1,24 @@
|
||||
/* linux/i2c/tps6507x-ts.h
|
||||
*
|
||||
* Functions to access TPS65070 touch screen chip.
|
||||
*
|
||||
* Copyright (c) 2009 RidgeRun (todd.fischer@ridgerun.com)
|
||||
*
|
||||
*
|
||||
* For licencing details see kernel-base/COPYING
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_I2C_TPS6507X_TS_H
|
||||
#define __LINUX_I2C_TPS6507X_TS_H
|
||||
|
||||
/* Board specific touch screen initial values */
|
||||
struct touchscreen_init_data {
|
||||
int poll_period; /* ms */
|
||||
int vref; /* non-zero to leave vref on */
|
||||
__u16 min_pressure; /* min reading to be treated as a touch */
|
||||
__u16 vendor;
|
||||
__u16 product;
|
||||
__u16 version;
|
||||
};
|
||||
|
||||
#endif /* __LINUX_I2C_TPS6507X_TS_H */
|
@ -370,7 +370,7 @@ extern int pm860x_set_bits(struct i2c_client *, int, unsigned char,
|
||||
unsigned char);
|
||||
|
||||
extern int pm860x_device_init(struct pm860x_chip *chip,
|
||||
struct pm860x_platform_data *pdata);
|
||||
extern void pm860x_device_exit(struct pm860x_chip *chip);
|
||||
struct pm860x_platform_data *pdata) __devinit ;
|
||||
extern void pm860x_device_exit(struct pm860x_chip *chip) __devexit ;
|
||||
|
||||
#endif /* __LINUX_MFD_88PM860X_H */
|
||||
|
@ -1,262 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2009 ST-Ericsson
|
||||
*
|
||||
* Author: Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* AB4500 device core funtions, for client access
|
||||
*/
|
||||
#ifndef MFD_AB4500_H
|
||||
#define MFD_AB4500_H
|
||||
|
||||
#include <linux/device.h>
|
||||
|
||||
/*
|
||||
* AB4500 bank addresses
|
||||
*/
|
||||
#define AB4500_SYS_CTRL1_BLOCK 0x1
|
||||
#define AB4500_SYS_CTRL2_BLOCK 0x2
|
||||
#define AB4500_REGU_CTRL1 0x3
|
||||
#define AB4500_REGU_CTRL2 0x4
|
||||
#define AB4500_USB 0x5
|
||||
#define AB4500_TVOUT 0x6
|
||||
#define AB4500_DBI 0x7
|
||||
#define AB4500_ECI_AV_ACC 0x8
|
||||
#define AB4500_RESERVED 0x9
|
||||
#define AB4500_GPADC 0xA
|
||||
#define AB4500_CHARGER 0xB
|
||||
#define AB4500_GAS_GAUGE 0xC
|
||||
#define AB4500_AUDIO 0xD
|
||||
#define AB4500_INTERRUPT 0xE
|
||||
#define AB4500_RTC 0xF
|
||||
#define AB4500_MISC 0x10
|
||||
#define AB4500_DEBUG 0x12
|
||||
#define AB4500_PROD_TEST 0x13
|
||||
#define AB4500_OTP_EMUL 0x15
|
||||
|
||||
/*
|
||||
* System control 1 register offsets.
|
||||
* Bank = 0x01
|
||||
*/
|
||||
#define AB4500_TURNON_STAT_REG 0x0100
|
||||
#define AB4500_RESET_STAT_REG 0x0101
|
||||
#define AB4500_PONKEY1_PRESS_STAT_REG 0x0102
|
||||
|
||||
#define AB4500_FSM_STAT1_REG 0x0140
|
||||
#define AB4500_FSM_STAT2_REG 0x0141
|
||||
#define AB4500_SYSCLK_REQ_STAT_REG 0x0142
|
||||
#define AB4500_USB_STAT1_REG 0x0143
|
||||
#define AB4500_USB_STAT2_REG 0x0144
|
||||
#define AB4500_STATUS_SPARE1_REG 0x0145
|
||||
#define AB4500_STATUS_SPARE2_REG 0x0146
|
||||
|
||||
#define AB4500_CTRL1_REG 0x0180
|
||||
#define AB4500_CTRL2_REG 0x0181
|
||||
|
||||
/*
|
||||
* System control 2 register offsets.
|
||||
* bank = 0x02
|
||||
*/
|
||||
#define AB4500_CTRL3_REG 0x0200
|
||||
#define AB4500_MAIN_WDOG_CTRL_REG 0x0201
|
||||
#define AB4500_MAIN_WDOG_TIMER_REG 0x0202
|
||||
#define AB4500_LOW_BAT_REG 0x0203
|
||||
#define AB4500_BATT_OK_REG 0x0204
|
||||
#define AB4500_SYSCLK_TIMER_REG 0x0205
|
||||
#define AB4500_SMPSCLK_CTRL_REG 0x0206
|
||||
#define AB4500_SMPSCLK_SEL1_REG 0x0207
|
||||
#define AB4500_SMPSCLK_SEL2_REG 0x0208
|
||||
#define AB4500_SMPSCLK_SEL3_REG 0x0209
|
||||
#define AB4500_SYSULPCLK_CONF_REG 0x020A
|
||||
#define AB4500_SYSULPCLK_CTRL1_REG 0x020B
|
||||
#define AB4500_SYSCLK_CTRL_REG 0x020C
|
||||
#define AB4500_SYSCLK_REQ1_VALID_REG 0x020D
|
||||
#define AB4500_SYSCLK_REQ_VALID_REG 0x020E
|
||||
#define AB4500_SYSCTRL_SPARE_REG 0x020F
|
||||
#define AB4500_PAD_CONF_REG 0x0210
|
||||
|
||||
/*
|
||||
* Regu control1 register offsets
|
||||
* Bank = 0x03
|
||||
*/
|
||||
#define AB4500_REGU_SERIAL_CTRL1_REG 0x0300
|
||||
#define AB4500_REGU_SERIAL_CTRL2_REG 0x0301
|
||||
#define AB4500_REGU_SERIAL_CTRL3_REG 0x0302
|
||||
#define AB4500_REGU_REQ_CTRL1_REG 0x0303
|
||||
#define AB4500_REGU_REQ_CTRL2_REG 0x0304
|
||||
#define AB4500_REGU_REQ_CTRL3_REG 0x0305
|
||||
#define AB4500_REGU_REQ_CTRL4_REG 0x0306
|
||||
#define AB4500_REGU_MISC1_REG 0x0380
|
||||
#define AB4500_REGU_OTGSUPPLY_CTRL_REG 0x0381
|
||||
#define AB4500_REGU_VUSB_CTRL_REG 0x0382
|
||||
#define AB4500_REGU_VAUDIO_SUPPLY_REG 0x0383
|
||||
#define AB4500_REGU_CTRL1_SPARE_REG 0x0384
|
||||
|
||||
/*
|
||||
* Regu control2 Vmod register offsets
|
||||
*/
|
||||
#define AB4500_REGU_VMOD_REGU_REG 0x0440
|
||||
#define AB4500_REGU_VMOD_SEL1_REG 0x0441
|
||||
#define AB4500_REGU_VMOD_SEL2_REG 0x0442
|
||||
#define AB4500_REGU_CTRL_DISCH_REG 0x0443
|
||||
#define AB4500_REGU_CTRL_DISCH2_REG 0x0444
|
||||
|
||||
/*
|
||||
* USB/ULPI register offsets
|
||||
* Bank : 0x5
|
||||
*/
|
||||
#define AB4500_USB_LINE_STAT_REG 0x0580
|
||||
#define AB4500_USB_LINE_CTRL1_REG 0x0581
|
||||
#define AB4500_USB_LINE_CTRL2_REG 0x0582
|
||||
#define AB4500_USB_LINE_CTRL3_REG 0x0583
|
||||
#define AB4500_USB_LINE_CTRL4_REG 0x0584
|
||||
#define AB4500_USB_LINE_CTRL5_REG 0x0585
|
||||
#define AB4500_USB_OTG_CTRL_REG 0x0587
|
||||
#define AB4500_USB_OTG_STAT_REG 0x0588
|
||||
#define AB4500_USB_OTG_STAT_REG 0x0588
|
||||
#define AB4500_USB_CTRL_SPARE_REG 0x0589
|
||||
#define AB4500_USB_PHY_CTRL_REG 0x058A
|
||||
|
||||
/*
|
||||
* TVOUT / CTRL register offsets
|
||||
* Bank : 0x06
|
||||
*/
|
||||
#define AB4500_TVOUT_CTRL_REG 0x0680
|
||||
|
||||
/*
|
||||
* DBI register offsets
|
||||
* Bank : 0x07
|
||||
*/
|
||||
#define AB4500_DBI_REG1_REG 0x0700
|
||||
#define AB4500_DBI_REG2_REG 0x0701
|
||||
|
||||
/*
|
||||
* ECI regsiter offsets
|
||||
* Bank : 0x08
|
||||
*/
|
||||
#define AB4500_ECI_CTRL_REG 0x0800
|
||||
#define AB4500_ECI_HOOKLEVEL_REG 0x0801
|
||||
#define AB4500_ECI_DATAOUT_REG 0x0802
|
||||
#define AB4500_ECI_DATAIN_REG 0x0803
|
||||
|
||||
/*
|
||||
* AV Connector register offsets
|
||||
* Bank : 0x08
|
||||
*/
|
||||
#define AB4500_AV_CONN_REG 0x0840
|
||||
|
||||
/*
|
||||
* Accessory detection register offsets
|
||||
* Bank : 0x08
|
||||
*/
|
||||
#define AB4500_ACC_DET_DB1_REG 0x0880
|
||||
#define AB4500_ACC_DET_DB2_REG 0x0881
|
||||
|
||||
/*
|
||||
* GPADC register offsets
|
||||
* Bank : 0x0A
|
||||
*/
|
||||
#define AB4500_GPADC_CTRL1_REG 0x0A00
|
||||
#define AB4500_GPADC_CTRL2_REG 0x0A01
|
||||
#define AB4500_GPADC_CTRL3_REG 0x0A02
|
||||
#define AB4500_GPADC_AUTO_TIMER_REG 0x0A03
|
||||
#define AB4500_GPADC_STAT_REG 0x0A04
|
||||
#define AB4500_GPADC_MANDATAL_REG 0x0A05
|
||||
#define AB4500_GPADC_MANDATAH_REG 0x0A06
|
||||
#define AB4500_GPADC_AUTODATAL_REG 0x0A07
|
||||
#define AB4500_GPADC_AUTODATAH_REG 0x0A08
|
||||
#define AB4500_GPADC_MUX_CTRL_REG 0x0A09
|
||||
|
||||
/*
|
||||
* Charger / status register offfsets
|
||||
* Bank : 0x0B
|
||||
*/
|
||||
#define AB4500_CH_STATUS1_REG 0x0B00
|
||||
#define AB4500_CH_STATUS2_REG 0x0B01
|
||||
#define AB4500_CH_USBCH_STAT1_REG 0x0B02
|
||||
#define AB4500_CH_USBCH_STAT2_REG 0x0B03
|
||||
#define AB4500_CH_FSM_STAT_REG 0x0B04
|
||||
#define AB4500_CH_STAT_REG 0x0B05
|
||||
|
||||
/*
|
||||
* Charger / control register offfsets
|
||||
* Bank : 0x0B
|
||||
*/
|
||||
#define AB4500_CH_VOLT_LVL_REG 0x0B40
|
||||
|
||||
/*
|
||||
* Charger / main control register offfsets
|
||||
* Bank : 0x0B
|
||||
*/
|
||||
#define AB4500_MCH_CTRL1 0x0B80
|
||||
#define AB4500_MCH_CTRL2 0x0B81
|
||||
#define AB4500_MCH_IPT_CURLVL_REG 0x0B82
|
||||
#define AB4500_CH_WD_REG 0x0B83
|
||||
|
||||
/*
|
||||
* Charger / USB control register offsets
|
||||
* Bank : 0x0B
|
||||
*/
|
||||
#define AB4500_USBCH_CTRL1_REG 0x0BC0
|
||||
#define AB4500_USBCH_CTRL2_REG 0x0BC1
|
||||
#define AB4500_USBCH_IPT_CRNTLVL_REG 0x0BC2
|
||||
|
||||
/*
|
||||
* RTC bank register offsets
|
||||
* Bank : 0xF
|
||||
*/
|
||||
#define AB4500_RTC_SOFF_STAT_REG 0x0F00
|
||||
#define AB4500_RTC_CC_CONF_REG 0x0F01
|
||||
#define AB4500_RTC_READ_REQ_REG 0x0F02
|
||||
#define AB4500_RTC_WATCH_TSECMID_REG 0x0F03
|
||||
#define AB4500_RTC_WATCH_TSECHI_REG 0x0F04
|
||||
#define AB4500_RTC_WATCH_TMIN_LOW_REG 0x0F05
|
||||
#define AB4500_RTC_WATCH_TMIN_MID_REG 0x0F06
|
||||
#define AB4500_RTC_WATCH_TMIN_HI_REG 0x0F07
|
||||
#define AB4500_RTC_ALRM_MIN_LOW_REG 0x0F08
|
||||
#define AB4500_RTC_ALRM_MIN_MID_REG 0x0F09
|
||||
#define AB4500_RTC_ALRM_MIN_HI_REG 0x0F0A
|
||||
#define AB4500_RTC_STAT_REG 0x0F0B
|
||||
#define AB4500_RTC_BKUP_CHG_REG 0x0F0C
|
||||
#define AB4500_RTC_FORCE_BKUP_REG 0x0F0D
|
||||
#define AB4500_RTC_CALIB_REG 0x0F0E
|
||||
#define AB4500_RTC_SWITCH_STAT_REG 0x0F0F
|
||||
|
||||
/*
|
||||
* PWM Out generators
|
||||
* Bank: 0x10
|
||||
*/
|
||||
#define AB4500_PWM_OUT_CTRL1_REG 0x1060
|
||||
#define AB4500_PWM_OUT_CTRL2_REG 0x1061
|
||||
#define AB4500_PWM_OUT_CTRL3_REG 0x1062
|
||||
#define AB4500_PWM_OUT_CTRL4_REG 0x1063
|
||||
#define AB4500_PWM_OUT_CTRL5_REG 0x1064
|
||||
#define AB4500_PWM_OUT_CTRL6_REG 0x1065
|
||||
#define AB4500_PWM_OUT_CTRL7_REG 0x1066
|
||||
|
||||
#define AB4500_I2C_PAD_CTRL_REG 0x1067
|
||||
#define AB4500_REV_REG 0x1080
|
||||
|
||||
/**
|
||||
* struct ab4500
|
||||
* @spi: spi device structure
|
||||
* @tx_buf: transmit buffer
|
||||
* @rx_buf: receive buffer
|
||||
* @lock: sync primitive
|
||||
*/
|
||||
struct ab4500 {
|
||||
struct spi_device *spi;
|
||||
unsigned long tx_buf[4];
|
||||
unsigned long rx_buf[4];
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
int ab4500_write(struct ab4500 *ab4500, unsigned char block,
|
||||
unsigned long addr, unsigned char data);
|
||||
int ab4500_read(struct ab4500 *ab4500, unsigned char block,
|
||||
unsigned long addr);
|
||||
|
||||
#endif /* MFD_AB4500_H */
|
128
include/linux/mfd/ab8500.h
Normal file
128
include/linux/mfd/ab8500.h
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* License Terms: GNU General Public License v2
|
||||
* Author: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com>
|
||||
*/
|
||||
#ifndef MFD_AB8500_H
|
||||
#define MFD_AB8500_H
|
||||
|
||||
#include <linux/device.h>
|
||||
|
||||
/*
|
||||
* Interrupts
|
||||
*/
|
||||
|
||||
#define AB8500_INT_MAIN_EXT_CH_NOT_OK 0
|
||||
#define AB8500_INT_UN_PLUG_TV_DET 1
|
||||
#define AB8500_INT_PLUG_TV_DET 2
|
||||
#define AB8500_INT_TEMP_WARM 3
|
||||
#define AB8500_INT_PON_KEY2DB_F 4
|
||||
#define AB8500_INT_PON_KEY2DB_R 5
|
||||
#define AB8500_INT_PON_KEY1DB_F 6
|
||||
#define AB8500_INT_PON_KEY1DB_R 7
|
||||
#define AB8500_INT_BATT_OVV 8
|
||||
#define AB8500_INT_MAIN_CH_UNPLUG_DET 10
|
||||
#define AB8500_INT_MAIN_CH_PLUG_DET 11
|
||||
#define AB8500_INT_USB_ID_DET_F 12
|
||||
#define AB8500_INT_USB_ID_DET_R 13
|
||||
#define AB8500_INT_VBUS_DET_F 14
|
||||
#define AB8500_INT_VBUS_DET_R 15
|
||||
#define AB8500_INT_VBUS_CH_DROP_END 16
|
||||
#define AB8500_INT_RTC_60S 17
|
||||
#define AB8500_INT_RTC_ALARM 18
|
||||
#define AB8500_INT_BAT_CTRL_INDB 20
|
||||
#define AB8500_INT_CH_WD_EXP 21
|
||||
#define AB8500_INT_VBUS_OVV 22
|
||||
#define AB8500_INT_MAIN_CH_DROP_END 23
|
||||
#define AB8500_INT_CCN_CONV_ACC 24
|
||||
#define AB8500_INT_INT_AUD 25
|
||||
#define AB8500_INT_CCEOC 26
|
||||
#define AB8500_INT_CC_INT_CALIB 27
|
||||
#define AB8500_INT_LOW_BAT_F 28
|
||||
#define AB8500_INT_LOW_BAT_R 29
|
||||
#define AB8500_INT_BUP_CHG_NOT_OK 30
|
||||
#define AB8500_INT_BUP_CHG_OK 31
|
||||
#define AB8500_INT_GP_HW_ADC_CONV_END 32
|
||||
#define AB8500_INT_ACC_DETECT_1DB_F 33
|
||||
#define AB8500_INT_ACC_DETECT_1DB_R 34
|
||||
#define AB8500_INT_ACC_DETECT_22DB_F 35
|
||||
#define AB8500_INT_ACC_DETECT_22DB_R 36
|
||||
#define AB8500_INT_ACC_DETECT_21DB_F 37
|
||||
#define AB8500_INT_ACC_DETECT_21DB_R 38
|
||||
#define AB8500_INT_GP_SW_ADC_CONV_END 39
|
||||
#define AB8500_INT_BTEMP_LOW 72
|
||||
#define AB8500_INT_BTEMP_LOW_MEDIUM 73
|
||||
#define AB8500_INT_BTEMP_MEDIUM_HIGH 74
|
||||
#define AB8500_INT_BTEMP_HIGH 75
|
||||
#define AB8500_INT_USB_CHARGER_NOT_OK 81
|
||||
#define AB8500_INT_ID_WAKEUP_R 82
|
||||
#define AB8500_INT_ID_DET_R1R 84
|
||||
#define AB8500_INT_ID_DET_R2R 85
|
||||
#define AB8500_INT_ID_DET_R3R 86
|
||||
#define AB8500_INT_ID_DET_R4R 87
|
||||
#define AB8500_INT_ID_WAKEUP_F 88
|
||||
#define AB8500_INT_ID_DET_R1F 90
|
||||
#define AB8500_INT_ID_DET_R2F 91
|
||||
#define AB8500_INT_ID_DET_R3F 92
|
||||
#define AB8500_INT_ID_DET_R4F 93
|
||||
#define AB8500_INT_USB_CHG_DET_DONE 94
|
||||
#define AB8500_INT_USB_CH_TH_PROT_F 96
|
||||
#define AB8500_INT_USB_CH_TH_PROP_R 97
|
||||
#define AB8500_INT_MAIN_CH_TH_PROP_F 98
|
||||
#define AB8500_INT_MAIN_CH_TH_PROT_R 99
|
||||
#define AB8500_INT_USB_CHARGER_NOT_OKF 103
|
||||
|
||||
#define AB8500_NR_IRQS 104
|
||||
#define AB8500_NUM_IRQ_REGS 13
|
||||
|
||||
/**
|
||||
* struct ab8500 - ab8500 internal structure
|
||||
* @dev: parent device
|
||||
* @lock: read/write operations lock
|
||||
* @irq_lock: genirq bus lock
|
||||
* @revision: chip revision
|
||||
* @irq: irq line
|
||||
* @write: register write
|
||||
* @read: register read
|
||||
* @rx_buf: rx buf for SPI
|
||||
* @tx_buf: tx buf for SPI
|
||||
* @mask: cache of IRQ regs for bus lock
|
||||
* @oldmask: cache of previous IRQ regs for bus lock
|
||||
*/
|
||||
struct ab8500 {
|
||||
struct device *dev;
|
||||
struct mutex lock;
|
||||
struct mutex irq_lock;
|
||||
int revision;
|
||||
int irq_base;
|
||||
int irq;
|
||||
|
||||
int (*write) (struct ab8500 *a8500, u16 addr, u8 data);
|
||||
int (*read) (struct ab8500 *a8500, u16 addr);
|
||||
|
||||
unsigned long tx_buf[4];
|
||||
unsigned long rx_buf[4];
|
||||
|
||||
u8 mask[AB8500_NUM_IRQ_REGS];
|
||||
u8 oldmask[AB8500_NUM_IRQ_REGS];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ab8500_platform_data - AB8500 platform data
|
||||
* @irq_base: start of AB8500 IRQs, AB8500_NR_IRQS will be used
|
||||
* @init: board-specific initialization after detection of ab8500
|
||||
*/
|
||||
struct ab8500_platform_data {
|
||||
int irq_base;
|
||||
void (*init) (struct ab8500 *);
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
#endif /* MFD_AB8500_H */
|
@ -3,17 +3,37 @@
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
* AB3100 core access functions
|
||||
* Author: Linus Walleij <linus.walleij@stericsson.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Author: Mattias Wallin <mattias.wallin@stericsson.com>
|
||||
* Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com>
|
||||
* Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com>
|
||||
* Author: Rickard Andersson <rickard.andersson@stericsson.com>
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
|
||||
#ifndef MFD_AB3100_H
|
||||
#define MFD_AB3100_H
|
||||
#ifndef MFD_ABX500_H
|
||||
#define MFD_ABX500_H
|
||||
|
||||
#define ABUNKNOWN 0
|
||||
#define AB3000 1
|
||||
#define AB3100 2
|
||||
#define AB3100_P1A 0xc0
|
||||
#define AB3100_P1B 0xc1
|
||||
#define AB3100_P1C 0xc2
|
||||
#define AB3100_P1D 0xc3
|
||||
#define AB3100_P1E 0xc4
|
||||
#define AB3100_P1F 0xc5
|
||||
#define AB3100_P1G 0xc6
|
||||
#define AB3100_R2A 0xc7
|
||||
#define AB3100_R2B 0xc8
|
||||
#define AB3550_P1A 0x10
|
||||
#define AB5500_1_0 0x20
|
||||
#define AB5500_2_0 0x21
|
||||
#define AB5500_2_1 0x22
|
||||
|
||||
/*
|
||||
* AB3100, EVENTA1, A2 and A3 event register flags
|
||||
@ -89,7 +109,7 @@ struct ab3100 {
|
||||
char chip_name[32];
|
||||
u8 chip_id;
|
||||
struct blocking_notifier_head event_subscribers;
|
||||
u32 startup_events;
|
||||
u8 startup_events[3];
|
||||
bool startup_events_read;
|
||||
};
|
||||
|
||||
@ -112,18 +132,102 @@ struct ab3100_platform_data {
|
||||
int external_voltage;
|
||||
};
|
||||
|
||||
int ab3100_set_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 regval);
|
||||
int ab3100_get_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 *regval);
|
||||
int ab3100_get_register_page_interruptible(struct ab3100 *ab3100,
|
||||
u8 first_reg, u8 *regvals, u8 numregs);
|
||||
int ab3100_mask_and_set_register_interruptible(struct ab3100 *ab3100,
|
||||
u8 reg, u8 andmask, u8 ormask);
|
||||
u8 ab3100_get_chip_type(struct ab3100 *ab3100);
|
||||
int ab3100_event_register(struct ab3100 *ab3100,
|
||||
struct notifier_block *nb);
|
||||
int ab3100_event_unregister(struct ab3100 *ab3100,
|
||||
struct notifier_block *nb);
|
||||
int ab3100_event_registers_startup_state_get(struct ab3100 *ab3100,
|
||||
u32 *fatevent);
|
||||
|
||||
/* AB3550, STR register flags */
|
||||
#define AB3550_STR_ONSWA (0x01)
|
||||
#define AB3550_STR_ONSWB (0x02)
|
||||
#define AB3550_STR_ONSWC (0x04)
|
||||
#define AB3550_STR_DCIO (0x08)
|
||||
#define AB3550_STR_BOOT_MODE (0x10)
|
||||
#define AB3550_STR_SIM_OFF (0x20)
|
||||
#define AB3550_STR_BATT_REMOVAL (0x40)
|
||||
#define AB3550_STR_VBUS (0x80)
|
||||
|
||||
/* Interrupt mask registers */
|
||||
#define AB3550_IMR1 0x29
|
||||
#define AB3550_IMR2 0x2a
|
||||
#define AB3550_IMR3 0x2b
|
||||
#define AB3550_IMR4 0x2c
|
||||
#define AB3550_IMR5 0x2d
|
||||
|
||||
enum ab3550_devid {
|
||||
AB3550_DEVID_ADC,
|
||||
AB3550_DEVID_DAC,
|
||||
AB3550_DEVID_LEDS,
|
||||
AB3550_DEVID_POWER,
|
||||
AB3550_DEVID_REGULATORS,
|
||||
AB3550_DEVID_SIM,
|
||||
AB3550_DEVID_UART,
|
||||
AB3550_DEVID_RTC,
|
||||
AB3550_DEVID_CHARGER,
|
||||
AB3550_DEVID_FUELGAUGE,
|
||||
AB3550_DEVID_VIBRATOR,
|
||||
AB3550_DEVID_CODEC,
|
||||
AB3550_NUM_DEVICES,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct abx500_init_setting
|
||||
* Initial value of the registers for driver to use during setup.
|
||||
*/
|
||||
struct abx500_init_settings {
|
||||
u8 bank;
|
||||
u8 reg;
|
||||
u8 setting;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ab3550_platform_data
|
||||
* Data supplied to initialize board connections to the AB3550
|
||||
*/
|
||||
struct ab3550_platform_data {
|
||||
struct {unsigned int base; unsigned int count; } irq;
|
||||
void *dev_data[AB3550_NUM_DEVICES];
|
||||
size_t dev_data_sz[AB3550_NUM_DEVICES];
|
||||
struct abx500_init_settings *init_settings;
|
||||
unsigned int init_settings_sz;
|
||||
};
|
||||
|
||||
int abx500_set_register_interruptible(struct device *dev, u8 bank, u8 reg,
|
||||
u8 value);
|
||||
int abx500_get_register_interruptible(struct device *dev, u8 bank, u8 reg,
|
||||
u8 *value);
|
||||
int abx500_get_register_page_interruptible(struct device *dev, u8 bank,
|
||||
u8 first_reg, u8 *regvals, u8 numregs);
|
||||
int abx500_set_register_page_interruptible(struct device *dev, u8 bank,
|
||||
u8 first_reg, u8 *regvals, u8 numregs);
|
||||
/**
|
||||
* abx500_mask_and_set_register_inerruptible() - Modifies selected bits of a
|
||||
* target register
|
||||
*
|
||||
* @dev: The AB sub device.
|
||||
* @bank: The i2c bank number.
|
||||
* @bitmask: The bit mask to use.
|
||||
* @bitvalues: The new bit values.
|
||||
*
|
||||
* Updates the value of an AB register:
|
||||
* value -> ((value & ~bitmask) | (bitvalues & bitmask))
|
||||
*/
|
||||
int abx500_mask_and_set_register_interruptible(struct device *dev, u8 bank,
|
||||
u8 reg, u8 bitmask, u8 bitvalues);
|
||||
int abx500_get_chip_id(struct device *dev);
|
||||
int abx500_event_registers_startup_state_get(struct device *dev, u8 *event);
|
||||
int abx500_startup_irq_enabled(struct device *dev, unsigned int irq);
|
||||
|
||||
struct abx500_ops {
|
||||
int (*get_chip_id) (struct device *);
|
||||
int (*get_register) (struct device *, u8, u8, u8 *);
|
||||
int (*set_register) (struct device *, u8, u8, u8);
|
||||
int (*get_register_page) (struct device *, u8, u8, u8 *, u8);
|
||||
int (*set_register_page) (struct device *, u8, u8, u8 *, u8);
|
||||
int (*mask_and_set_register) (struct device *, u8, u8, u8, u8);
|
||||
int (*event_registers_startup_state_get) (struct device *, u8 *);
|
||||
int (*startup_irq_enabled) (struct device *, unsigned int);
|
||||
};
|
||||
|
||||
int abx500_register_ops(struct device *core_dev, struct abx500_ops *ops);
|
||||
#endif
|
54
include/linux/mfd/janz.h
Normal file
54
include/linux/mfd/janz.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Common Definitions for Janz MODULbus devices
|
||||
*
|
||||
* Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef JANZ_H
|
||||
#define JANZ_H
|
||||
|
||||
struct janz_platform_data {
|
||||
/* MODULbus Module Number */
|
||||
unsigned int modno;
|
||||
};
|
||||
|
||||
/* PLX bridge chip onboard registers */
|
||||
struct janz_cmodio_onboard_regs {
|
||||
u8 unused1;
|
||||
|
||||
/*
|
||||
* Read access: interrupt status
|
||||
* Write access: interrupt disable
|
||||
*/
|
||||
u8 int_disable;
|
||||
u8 unused2;
|
||||
|
||||
/*
|
||||
* Read access: MODULbus number (hex switch)
|
||||
* Write access: interrupt enable
|
||||
*/
|
||||
u8 int_enable;
|
||||
u8 unused3;
|
||||
|
||||
/* write-only */
|
||||
u8 reset_assert;
|
||||
u8 unused4;
|
||||
|
||||
/* write-only */
|
||||
u8 reset_deassert;
|
||||
u8 unused5;
|
||||
|
||||
/* read-write access to serial EEPROM */
|
||||
u8 eep;
|
||||
u8 unused6;
|
||||
|
||||
/* write-only access to EEPROM chip select */
|
||||
u8 enid;
|
||||
};
|
||||
|
||||
#endif /* JANZ_H */
|
26
include/linux/mfd/rdc321x.h
Normal file
26
include/linux/mfd/rdc321x.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef __RDC321X_MFD_H
|
||||
#define __RDC321X_MFD_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
/* Offsets to be accessed in the southbridge PCI
|
||||
* device configuration register */
|
||||
#define RDC321X_WDT_CTRL 0x44
|
||||
#define RDC321X_GPIO_CTRL_REG1 0x48
|
||||
#define RDC321X_GPIO_DATA_REG1 0x4c
|
||||
#define RDC321X_GPIO_CTRL_REG2 0x84
|
||||
#define RDC321X_GPIO_DATA_REG2 0x88
|
||||
|
||||
#define RDC321X_MAX_GPIO 58
|
||||
|
||||
struct rdc321x_gpio_pdata {
|
||||
struct pci_dev *sb_pdev;
|
||||
unsigned max_gpios;
|
||||
};
|
||||
|
||||
struct rdc321x_wdt_pdata {
|
||||
struct pci_dev *sb_pdev;
|
||||
};
|
||||
|
||||
#endif /* __RDC321X_MFD_H */
|
132
include/linux/mfd/tc35892.h
Normal file
132
include/linux/mfd/tc35892.h
Normal file
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* License Terms: GNU General Public License, version 2
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_MFD_TC35892_H
|
||||
#define __LINUX_MFD_TC35892_H
|
||||
|
||||
#include <linux/device.h>
|
||||
|
||||
#define TC35892_RSTCTRL_IRQRST (1 << 4)
|
||||
#define TC35892_RSTCTRL_TIMRST (1 << 3)
|
||||
#define TC35892_RSTCTRL_ROTRST (1 << 2)
|
||||
#define TC35892_RSTCTRL_KBDRST (1 << 1)
|
||||
#define TC35892_RSTCTRL_GPIRST (1 << 0)
|
||||
|
||||
#define TC35892_IRQST 0x91
|
||||
|
||||
#define TC35892_MANFCODE_MAGIC 0x03
|
||||
#define TC35892_MANFCODE 0x80
|
||||
#define TC35892_VERSION 0x81
|
||||
#define TC35892_IOCFG 0xA7
|
||||
|
||||
#define TC35892_CLKMODE 0x88
|
||||
#define TC35892_CLKCFG 0x89
|
||||
#define TC35892_CLKEN 0x8A
|
||||
|
||||
#define TC35892_RSTCTRL 0x82
|
||||
#define TC35892_EXTRSTN 0x83
|
||||
#define TC35892_RSTINTCLR 0x84
|
||||
|
||||
#define TC35892_GPIOIS0 0xC9
|
||||
#define TC35892_GPIOIS1 0xCA
|
||||
#define TC35892_GPIOIS2 0xCB
|
||||
#define TC35892_GPIOIBE0 0xCC
|
||||
#define TC35892_GPIOIBE1 0xCD
|
||||
#define TC35892_GPIOIBE2 0xCE
|
||||
#define TC35892_GPIOIEV0 0xCF
|
||||
#define TC35892_GPIOIEV1 0xD0
|
||||
#define TC35892_GPIOIEV2 0xD1
|
||||
#define TC35892_GPIOIE0 0xD2
|
||||
#define TC35892_GPIOIE1 0xD3
|
||||
#define TC35892_GPIOIE2 0xD4
|
||||
#define TC35892_GPIORIS0 0xD6
|
||||
#define TC35892_GPIORIS1 0xD7
|
||||
#define TC35892_GPIORIS2 0xD8
|
||||
#define TC35892_GPIOMIS0 0xD9
|
||||
#define TC35892_GPIOMIS1 0xDA
|
||||
#define TC35892_GPIOMIS2 0xDB
|
||||
#define TC35892_GPIOIC0 0xDC
|
||||
#define TC35892_GPIOIC1 0xDD
|
||||
#define TC35892_GPIOIC2 0xDE
|
||||
|
||||
#define TC35892_GPIODATA0 0xC0
|
||||
#define TC35892_GPIOMASK0 0xc1
|
||||
#define TC35892_GPIODATA1 0xC2
|
||||
#define TC35892_GPIOMASK1 0xc3
|
||||
#define TC35892_GPIODATA2 0xC4
|
||||
#define TC35892_GPIOMASK2 0xC5
|
||||
|
||||
#define TC35892_GPIODIR0 0xC6
|
||||
#define TC35892_GPIODIR1 0xC7
|
||||
#define TC35892_GPIODIR2 0xC8
|
||||
|
||||
#define TC35892_GPIOSYNC0 0xE6
|
||||
#define TC35892_GPIOSYNC1 0xE7
|
||||
#define TC35892_GPIOSYNC2 0xE8
|
||||
|
||||
#define TC35892_GPIOWAKE0 0xE9
|
||||
#define TC35892_GPIOWAKE1 0xEA
|
||||
#define TC35892_GPIOWAKE2 0xEB
|
||||
|
||||
#define TC35892_GPIOODM0 0xE0
|
||||
#define TC35892_GPIOODE0 0xE1
|
||||
#define TC35892_GPIOODM1 0xE2
|
||||
#define TC35892_GPIOODE1 0xE3
|
||||
#define TC35892_GPIOODM2 0xE4
|
||||
#define TC35892_GPIOODE2 0xE5
|
||||
|
||||
#define TC35892_INT_GPIIRQ 0
|
||||
#define TC35892_INT_TI0IRQ 1
|
||||
#define TC35892_INT_TI1IRQ 2
|
||||
#define TC35892_INT_TI2IRQ 3
|
||||
#define TC35892_INT_ROTIRQ 5
|
||||
#define TC35892_INT_KBDIRQ 6
|
||||
#define TC35892_INT_PORIRQ 7
|
||||
|
||||
#define TC35892_NR_INTERNAL_IRQS 8
|
||||
#define TC35892_INT_GPIO(x) (TC35892_NR_INTERNAL_IRQS + (x))
|
||||
|
||||
struct tc35892 {
|
||||
struct mutex lock;
|
||||
struct device *dev;
|
||||
struct i2c_client *i2c;
|
||||
|
||||
int irq_base;
|
||||
int num_gpio;
|
||||
struct tc35892_platform_data *pdata;
|
||||
};
|
||||
|
||||
extern int tc35892_reg_write(struct tc35892 *tc35892, u8 reg, u8 data);
|
||||
extern int tc35892_reg_read(struct tc35892 *tc35892, u8 reg);
|
||||
extern int tc35892_block_read(struct tc35892 *tc35892, u8 reg, u8 length,
|
||||
u8 *values);
|
||||
extern int tc35892_block_write(struct tc35892 *tc35892, u8 reg, u8 length,
|
||||
const u8 *values);
|
||||
extern int tc35892_set_bits(struct tc35892 *tc35892, u8 reg, u8 mask, u8 val);
|
||||
|
||||
/**
|
||||
* struct tc35892_gpio_platform_data - TC35892 GPIO platform data
|
||||
* @gpio_base: first gpio number assigned to TC35892. A maximum of
|
||||
* %TC35892_NR_GPIOS GPIOs will be allocated.
|
||||
*/
|
||||
struct tc35892_gpio_platform_data {
|
||||
int gpio_base;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tc35892_platform_data - TC35892 platform data
|
||||
* @irq_base: base IRQ number. %TC35892_NR_IRQS irqs will be used.
|
||||
* @gpio: GPIO-specific platform data
|
||||
*/
|
||||
struct tc35892_platform_data {
|
||||
int irq_base;
|
||||
struct tc35892_gpio_platform_data *gpio;
|
||||
};
|
||||
|
||||
#define TC35892_NR_GPIOS 24
|
||||
#define TC35892_NR_IRQS TC35892_INT_GPIO(TC35892_NR_GPIOS)
|
||||
|
||||
#endif
|
169
include/linux/mfd/tps6507x.h
Normal file
169
include/linux/mfd/tps6507x.h
Normal file
@ -0,0 +1,169 @@
|
||||
/* linux/mfd/tps6507x.h
|
||||
*
|
||||
* Functions to access TPS65070 power management chip.
|
||||
*
|
||||
* Copyright (c) 2009 RidgeRun (todd.fischer@ridgerun.com)
|
||||
*
|
||||
*
|
||||
* For licencing details see kernel-base/COPYING
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_MFD_TPS6507X_H
|
||||
#define __LINUX_MFD_TPS6507X_H
|
||||
|
||||
/*
|
||||
* ----------------------------------------------------------------------------
|
||||
* Registers, all 8 bits
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/* Register definitions */
|
||||
#define TPS6507X_REG_PPATH1 0X01
|
||||
#define TPS6507X_CHG_USB BIT(7)
|
||||
#define TPS6507X_CHG_AC BIT(6)
|
||||
#define TPS6507X_CHG_USB_PW_ENABLE BIT(5)
|
||||
#define TPS6507X_CHG_AC_PW_ENABLE BIT(4)
|
||||
#define TPS6507X_CHG_AC_CURRENT BIT(2)
|
||||
#define TPS6507X_CHG_USB_CURRENT BIT(0)
|
||||
|
||||
#define TPS6507X_REG_INT 0X02
|
||||
#define TPS6507X_REG_MASK_AC_USB BIT(7)
|
||||
#define TPS6507X_REG_MASK_TSC BIT(6)
|
||||
#define TPS6507X_REG_MASK_PB_IN BIT(5)
|
||||
#define TPS6507X_REG_TSC_INT BIT(3)
|
||||
#define TPS6507X_REG_PB_IN_INT BIT(2)
|
||||
#define TPS6507X_REG_AC_USB_APPLIED BIT(1)
|
||||
#define TPS6507X_REG_AC_USB_REMOVED BIT(0)
|
||||
|
||||
#define TPS6507X_REG_CHGCONFIG0 0X03
|
||||
|
||||
#define TPS6507X_REG_CHGCONFIG1 0X04
|
||||
#define TPS6507X_CON_CTRL1_DCDC1_ENABLE BIT(4)
|
||||
#define TPS6507X_CON_CTRL1_DCDC2_ENABLE BIT(3)
|
||||
#define TPS6507X_CON_CTRL1_DCDC3_ENABLE BIT(2)
|
||||
#define TPS6507X_CON_CTRL1_LDO1_ENABLE BIT(1)
|
||||
#define TPS6507X_CON_CTRL1_LDO2_ENABLE BIT(0)
|
||||
|
||||
#define TPS6507X_REG_CHGCONFIG2 0X05
|
||||
|
||||
#define TPS6507X_REG_CHGCONFIG3 0X06
|
||||
|
||||
#define TPS6507X_REG_ADCONFIG 0X07
|
||||
#define TPS6507X_ADCONFIG_AD_ENABLE BIT(7)
|
||||
#define TPS6507X_ADCONFIG_START_CONVERSION BIT(6)
|
||||
#define TPS6507X_ADCONFIG_CONVERSION_DONE BIT(5)
|
||||
#define TPS6507X_ADCONFIG_VREF_ENABLE BIT(4)
|
||||
#define TPS6507X_ADCONFIG_INPUT_AD_IN1 0
|
||||
#define TPS6507X_ADCONFIG_INPUT_AD_IN2 1
|
||||
#define TPS6507X_ADCONFIG_INPUT_AD_IN3 2
|
||||
#define TPS6507X_ADCONFIG_INPUT_AD_IN4 3
|
||||
#define TPS6507X_ADCONFIG_INPUT_TS_PIN 4
|
||||
#define TPS6507X_ADCONFIG_INPUT_BAT_CURRENT 5
|
||||
#define TPS6507X_ADCONFIG_INPUT_AC_VOLTAGE 6
|
||||
#define TPS6507X_ADCONFIG_INPUT_SYS_VOLTAGE 7
|
||||
#define TPS6507X_ADCONFIG_INPUT_CHARGER_VOLTAGE 8
|
||||
#define TPS6507X_ADCONFIG_INPUT_BAT_VOLTAGE 9
|
||||
#define TPS6507X_ADCONFIG_INPUT_THRESHOLD_VOLTAGE 10
|
||||
#define TPS6507X_ADCONFIG_INPUT_ISET1_VOLTAGE 11
|
||||
#define TPS6507X_ADCONFIG_INPUT_ISET2_VOLTAGE 12
|
||||
#define TPS6507X_ADCONFIG_INPUT_REAL_TSC 14
|
||||
#define TPS6507X_ADCONFIG_INPUT_TSC 15
|
||||
|
||||
#define TPS6507X_REG_TSCMODE 0X08
|
||||
#define TPS6507X_TSCMODE_X_POSITION 0
|
||||
#define TPS6507X_TSCMODE_Y_POSITION 1
|
||||
#define TPS6507X_TSCMODE_PRESSURE 2
|
||||
#define TPS6507X_TSCMODE_X_PLATE 3
|
||||
#define TPS6507X_TSCMODE_Y_PLATE 4
|
||||
#define TPS6507X_TSCMODE_STANDBY 5
|
||||
#define TPS6507X_TSCMODE_ADC_INPUT 6
|
||||
#define TPS6507X_TSCMODE_DISABLE 7
|
||||
|
||||
#define TPS6507X_REG_ADRESULT_1 0X09
|
||||
|
||||
#define TPS6507X_REG_ADRESULT_2 0X0A
|
||||
#define TPS6507X_REG_ADRESULT_2_MASK (BIT(1) | BIT(0))
|
||||
|
||||
#define TPS6507X_REG_PGOOD 0X0B
|
||||
|
||||
#define TPS6507X_REG_PGOODMASK 0X0C
|
||||
|
||||
#define TPS6507X_REG_CON_CTRL1 0X0D
|
||||
#define TPS6507X_CON_CTRL1_DCDC1_ENABLE BIT(4)
|
||||
#define TPS6507X_CON_CTRL1_DCDC2_ENABLE BIT(3)
|
||||
#define TPS6507X_CON_CTRL1_DCDC3_ENABLE BIT(2)
|
||||
#define TPS6507X_CON_CTRL1_LDO1_ENABLE BIT(1)
|
||||
#define TPS6507X_CON_CTRL1_LDO2_ENABLE BIT(0)
|
||||
|
||||
#define TPS6507X_REG_CON_CTRL2 0X0E
|
||||
|
||||
#define TPS6507X_REG_CON_CTRL3 0X0F
|
||||
|
||||
#define TPS6507X_REG_DEFDCDC1 0X10
|
||||
#define TPS6507X_DEFDCDC1_DCDC1_EXT_ADJ_EN BIT(7)
|
||||
#define TPS6507X_DEFDCDC1_DCDC1_MASK 0X3F
|
||||
|
||||
#define TPS6507X_REG_DEFDCDC2_LOW 0X11
|
||||
#define TPS6507X_DEFDCDC2_LOW_DCDC2_MASK 0X3F
|
||||
|
||||
#define TPS6507X_REG_DEFDCDC2_HIGH 0X12
|
||||
#define TPS6507X_DEFDCDC2_HIGH_DCDC2_MASK 0X3F
|
||||
|
||||
#define TPS6507X_REG_DEFDCDC3_LOW 0X13
|
||||
#define TPS6507X_DEFDCDC3_LOW_DCDC3_MASK 0X3F
|
||||
|
||||
#define TPS6507X_REG_DEFDCDC3_HIGH 0X14
|
||||
#define TPS6507X_DEFDCDC3_HIGH_DCDC3_MASK 0X3F
|
||||
|
||||
#define TPS6507X_REG_DEFSLEW 0X15
|
||||
|
||||
#define TPS6507X_REG_LDO_CTRL1 0X16
|
||||
#define TPS6507X_REG_LDO_CTRL1_LDO1_MASK 0X0F
|
||||
|
||||
#define TPS6507X_REG_DEFLDO2 0X17
|
||||
#define TPS6507X_REG_DEFLDO2_LDO2_MASK 0X3F
|
||||
|
||||
#define TPS6507X_REG_WLED_CTRL1 0X18
|
||||
|
||||
#define TPS6507X_REG_WLED_CTRL2 0X19
|
||||
|
||||
/* VDCDC MASK */
|
||||
#define TPS6507X_DEFDCDCX_DCDC_MASK 0X3F
|
||||
|
||||
#define TPS6507X_MAX_REGISTER 0X19
|
||||
|
||||
/**
|
||||
* struct tps6507x_board - packages regulator and touchscreen init data
|
||||
* @tps6507x_regulator_data: regulator initialization values
|
||||
*
|
||||
* Board data may be used to initialize regulator and touchscreen.
|
||||
*/
|
||||
|
||||
struct tps6507x_board {
|
||||
struct regulator_init_data *tps6507x_pmic_init_data;
|
||||
struct touchscreen_init_data *tps6507x_ts_init_data;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tps6507x_dev - tps6507x sub-driver chip access routines
|
||||
* @read_dev() - I2C register read function
|
||||
* @write_dev() - I2C register write function
|
||||
*
|
||||
* Device data may be used to access the TPS6507x chip
|
||||
*/
|
||||
|
||||
struct tps6507x_dev {
|
||||
struct device *dev;
|
||||
struct i2c_client *i2c_client;
|
||||
int (*read_dev)(struct tps6507x_dev *tps6507x, char reg, int size,
|
||||
void *dest);
|
||||
int (*write_dev)(struct tps6507x_dev *tps6507x, char reg, int size,
|
||||
void *src);
|
||||
|
||||
/* Client devices */
|
||||
struct tps6507x_pmic *pmic;
|
||||
struct tps6507x_ts *ts;
|
||||
};
|
||||
|
||||
#endif /* __LINUX_MFD_TPS6507X_H */
|
@ -256,8 +256,9 @@ struct wm831x {
|
||||
int irq_masks_cache[WM831X_NUM_IRQ_REGS]; /* Cached hardware value */
|
||||
|
||||
/* Chip revision based flags */
|
||||
unsigned has_gpio_ena:1; /* Has GPIO enable bit */
|
||||
unsigned has_cs_sts:1; /* Has current sink status bit */
|
||||
unsigned has_gpio_ena:1; /* Has GPIO enable bit */
|
||||
unsigned has_cs_sts:1; /* Has current sink status bit */
|
||||
unsigned charger_irq_wake:1; /* Are charger IRQs a wake source? */
|
||||
|
||||
int num_gpio;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user