Commit 17d30ac0 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6

* 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6: (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
  ...
parents e38c1e54 191211f5
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/i2c/at24.h> #include <linux/i2c/at24.h>
#include <linux/i2c/pca953x.h> #include <linux/i2c/pca953x.h>
#include <linux/mfd/tps6507x.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
...@@ -24,6 +25,8 @@ ...@@ -24,6 +25,8 @@
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
#include <linux/mtd/physmap.h> #include <linux/mtd/physmap.h>
#include <linux/regulator/machine.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-types.h>
#include <asm/mach/arch.h> #include <asm/mach/arch.h>
...@@ -533,10 +536,24 @@ struct regulator_init_data tps65070_regulator_data[] = { ...@@ -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[] = { static struct i2c_board_info __initdata da850evm_tps65070_info[] = {
{ {
I2C_BOARD_INFO("tps6507x", 0x48), I2C_BOARD_INFO("tps6507x", 0x48),
.platform_data = &tps65070_regulator_data[0], .platform_data = &tps_board,
}, },
}; };
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/mfd/ab3100.h> #include <linux/mfd/abx500.h>
#include <linux/regulator/machine.h> #include <linux/regulator/machine.h>
#include <linux/amba/bus.h> #include <linux/amba/bus.h>
#include <mach/irqs.h> #include <mach/irqs.h>
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
/* BUCK SLEEP 0xAC: 1.05V, Not used, SLEEP_A and B, Not used */ /* BUCK SLEEP 0xAC: 1.05V, Not used, SLEEP_A and B, Not used */
#define BUCK_SLEEP_SETTING 0xAC #define BUCK_SLEEP_SETTING 0xAC
#ifdef CONFIG_AB3100_CORE
static struct regulator_consumer_supply supply_ldo_c[] = { static struct regulator_consumer_supply supply_ldo_c[] = {
{ {
.dev_name = "ab3100-codec", .dev_name = "ab3100-codec",
...@@ -253,14 +254,68 @@ static struct ab3100_platform_data ab3100_plf_data = { ...@@ -253,14 +254,68 @@ static struct ab3100_platform_data ab3100_plf_data = {
LDO_D_SETTING, 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[] = { 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", .type = "ab3100",
.addr = 0x48, .addr = 0x48,
.irq = IRQ_U300_IRQ0_EXT, .irq = IRQ_U300_IRQ0_EXT,
.platform_data = &ab3100_plf_data, .platform_data = &ab3100_plf_data,
}, },
#else
{ },
#endif
}; };
static struct i2c_board_info __initdata bus1_i2c_board_info[] = { static struct i2c_board_info __initdata bus1_i2c_board_info[] = {
......
...@@ -109,6 +109,13 @@ ...@@ -109,6 +109,13 @@
#define U300_NR_IRQS 48 #define U300_NR_IRQS 48
#endif #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 #define NR_IRQS U300_NR_IRQS
#endif
#endif #endif
...@@ -50,7 +50,7 @@ struct pl022_config_chip ab4500_chip_info = { ...@@ -50,7 +50,7 @@ struct pl022_config_chip ab4500_chip_info = {
static struct spi_board_info u8500_spi_devices[] = { static struct spi_board_info u8500_spi_devices[] = {
{ {
.modalias = "ab4500", .modalias = "ab8500",
.controller_data = &ab4500_chip_info, .controller_data = &ab4500_chip_info,
.max_speed_hz = 12000000, .max_speed_hz = 12000000,
.bus_num = 0, .bus_num = 0,
......
...@@ -195,6 +195,13 @@ config GPIO_PCF857X ...@@ -195,6 +195,13 @@ config GPIO_PCF857X
This driver provides an in-kernel interface to those GPIOs using This driver provides an in-kernel interface to those GPIOs using
platform-neutral GPIO calls. 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 config GPIO_TWL4030
tristate "TWL4030, TWL5030, and TPS659x0 GPIOs" tristate "TWL4030, TWL5030, and TPS659x0 GPIOs"
depends on TWL4030_CORE depends on TWL4030_CORE
...@@ -282,6 +289,15 @@ config GPIO_TIMBERDALE ...@@ -282,6 +289,15 @@ config GPIO_TIMBERDALE
---help--- ---help---
Add support for the GPIO IP in the timberdale FPGA. 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:" comment "SPI GPIO expanders:"
config GPIO_MAX7301 config GPIO_MAX7301
...@@ -317,4 +333,14 @@ config GPIO_UCB1400 ...@@ -317,4 +333,14 @@ config GPIO_UCB1400
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called ucb1400_gpio. 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 endif
...@@ -16,6 +16,7 @@ obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o ...@@ -16,6 +16,7 @@ obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o
obj-$(CONFIG_GPIO_PCA953X) += pca953x.o obj-$(CONFIG_GPIO_PCA953X) += pca953x.o
obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o
obj-$(CONFIG_GPIO_PL061) += pl061.o obj-$(CONFIG_GPIO_PL061) += pl061.o
obj-$(CONFIG_GPIO_TC35892) += tc35892-gpio.o
obj-$(CONFIG_GPIO_TIMBERDALE) += timbgpio.o obj-$(CONFIG_GPIO_TIMBERDALE) += timbgpio.o
obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o
obj-$(CONFIG_GPIO_UCB1400) += ucb1400_gpio.o obj-$(CONFIG_GPIO_UCB1400) += ucb1400_gpio.o
...@@ -27,4 +28,6 @@ obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o ...@@ -27,4 +28,6 @@ obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o
obj-$(CONFIG_GPIO_WM831X) += wm831x-gpio.o obj-$(CONFIG_GPIO_WM831X) += wm831x-gpio.o
obj-$(CONFIG_GPIO_WM8350) += wm8350-gpiolib.o obj-$(CONFIG_GPIO_WM8350) += wm8350-gpiolib.o
obj-$(CONFIG_GPIO_WM8994) += wm8994-gpio.o obj-$(CONFIG_GPIO_WM8994) += wm8994-gpio.o
obj-$(CONFIG_GPIO_SCH) += sch_gpio.o obj-$(CONFIG_GPIO_SCH) += sch_gpio.o
\ No newline at end of file obj-$(CONFIG_GPIO_RDC321X) += rdc321x-gpio.o
obj-$(CONFIG_GPIO_JANZ_TTL) += janz-ttl.o
/*
* 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);
/*
* 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, &reg);
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");
/*
* 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 ...@@ -590,4 +590,17 @@ config TOUCHSCREEN_PCAP
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called pcap_ts. 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 endif
...@@ -46,3 +46,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL) += atmel-wm97xx.o ...@@ -46,3 +46,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL) += atmel-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o
obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o
/*
* 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 @@ static int __devinit device_irq_init(struct pm860x_chip *chip, ...@@ -566,7 +566,7 @@ static int __devinit device_irq_init(struct pm860x_chip *chip,
return ret; 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) if (chip->core_irq)
free_irq(chip->core_irq, chip); free_irq(chip->core_irq, chip);
...@@ -703,7 +703,7 @@ static void __devinit device_8607_init(struct pm860x_chip *chip, ...@@ -703,7 +703,7 @@ static void __devinit device_8607_init(struct pm860x_chip *chip,
return; return;
} }
int pm860x_device_init(struct pm860x_chip *chip, int __devinit pm860x_device_init(struct pm860x_chip *chip,
struct pm860x_platform_data *pdata) struct pm860x_platform_data *pdata)
{ {
chip->core_irq = 0; chip->core_irq = 0;
...@@ -731,7 +731,7 @@ int pm860x_device_init(struct pm860x_chip *chip, ...@@ -731,7 +731,7 @@ int pm860x_device_init(struct pm860x_chip *chip,
return 0; return 0;
} }
void pm860x_device_exit(struct pm860x_chip *chip) void __devexit pm860x_device_exit(struct pm860x_chip *chip)
{ {
device_irq_exit(chip); device_irq_exit(chip);
mfd_remove_devices(chip->dev); mfd_remove_devices(chip->dev);
......
...@@ -200,8 +200,8 @@ static int __devexit pm860x_remove(struct i2c_client *client) ...@@ -200,8 +200,8 @@ static int __devexit pm860x_remove(struct i2c_client *client)
pm860x_device_exit(chip); pm860x_device_exit(chip);
i2c_unregister_device(chip->companion); i2c_unregister_device(chip->companion);
i2c_set_clientdata(chip->companion, NULL);
i2c_set_clientdata(chip->client, NULL); i2c_set_clientdata(chip->client, NULL);
i2c_set_clientdata(client, NULL);
kfree(chip); kfree(chip);
return 0; return 0;
} }
......
...@@ -2,8 +2,14 @@ ...@@ -2,8 +2,14 @@
# Multifunction miscellaneous devices # Multifunction miscellaneous devices
# #
menu "Multifunction device drivers" menuconfig MFD_SUPPORT
bool "Multifunction device drivers"
depends on HAS_IOMEM depends on HAS_IOMEM
default y
help
Configure MFD device drivers.
if MFD_SUPPORT
config MFD_CORE config MFD_CORE
tristate tristate
...@@ -116,6 +122,18 @@ config TPS65010 ...@@ -116,6 +122,18 @@ config TPS65010
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called tps65010. 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 config MENELAUS
bool "Texas Instruments TWL92330/Menelaus PM chip" bool "Texas Instruments TWL92330/Menelaus PM chip"
depends on I2C=y && ARCH_OMAP2 depends on I2C=y && ARCH_OMAP2
...@@ -159,6 +177,17 @@ config TWL4030_CODEC ...@@ -159,6 +177,17 @@ config TWL4030_CODEC
select MFD_CORE select MFD_CORE
default n 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 config MFD_TMIO
bool bool
default n default n
...@@ -351,9 +380,19 @@ config PCF50633_GPIO ...@@ -351,9 +380,19 @@ config PCF50633_GPIO
Say yes here if you want to include support GPIO for pins on Say yes here if you want to include support GPIO for pins on
the PCF50633 chip. 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 config AB3100_CORE
bool "ST-Ericsson AB3100 Mixed Signal Circuit core functions" bool "ST-Ericsson AB3100 Mixed Signal Circuit core functions"
depends on I2C=y depends on I2C=y && ABX500_CORE
default y if ARCH_U300 default y if ARCH_U300
help help
Select this to enable the AB3100 Mixed Signal IC core Select this to enable the AB3100 Mixed Signal IC core
...@@ -381,15 +420,30 @@ config EZX_PCAP ...@@ -381,15 +420,30 @@ config EZX_PCAP
This enables the PCAP ASIC present on EZX Phones. This is This enables the PCAP ASIC present on EZX Phones. This is
needed for MMC, TouchScreen, Sound, USB, etc.. needed for MMC, TouchScreen, Sound, USB, etc..
config AB4500_CORE config AB8500_CORE
tristate "ST-Ericsson's AB4500 Mixed Signal Power management chip" bool "ST-Ericsson AB8500 Mixed Signal Power Management chip"
depends on SPI depends on SPI=y && GENERIC_HARDIRQS
select MFD_CORE
help 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 chip. This connects to U8500 on the SSP/SPI bus and exports
read/write functions for the devices to get access to this chip. read/write functions for the devices to get access to this chip.
This chip embeds various other multimedia funtionalities as well. 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 config MFD_TIMBERDALE
tristate "Support for the Timberdale FPGA" tristate "Support for the Timberdale FPGA"
select MFD_CORE select MFD_CORE
...@@ -409,7 +463,26 @@ config LPC_SCH ...@@ -409,7 +463,26 @@ config LPC_SCH
LPC bridge function of the Intel SCH provides support for LPC bridge function of the Intel SCH provides support for
System Management Bus and General Purpose I/O. 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" menu "Multimedia Capabilities Port drivers"
depends on ARCH_SA1100 depends on ARCH_SA1100
......
...@@ -15,6 +15,7 @@ obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o ...@@ -15,6 +15,7 @@ obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o
obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o
obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.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_T7L66XB) += t7l66xb.o tmio_core.o
obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o tmio_core.o obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o tmio_core.o
obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.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 ...@@ -29,6 +30,7 @@ obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o
obj-$(CONFIG_MFD_WM8994) += wm8994-core.o wm8994-irq.o obj-$(CONFIG_MFD_WM8994) += wm8994-core.o wm8994-irq.o
obj-$(CONFIG_TPS65010) += tps65010.o obj-$(CONFIG_TPS65010) += tps65010.o
obj-$(CONFIG_TPS6507X) += tps6507x.o
obj-$(CONFIG_MENELAUS) += menelaus.o obj-$(CONFIG_MENELAUS) += menelaus.o
obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o
...@@ -55,12 +57,17 @@ obj-$(CONFIG_PMIC_DA903X) += da903x.o ...@@ -55,12 +57,17 @@ obj-$(CONFIG_PMIC_DA903X) += da903x.o
max8925-objs := max8925-core.o max8925-i2c.o max8925-objs := max8925-core.o max8925-i2c.o
obj-$(CONFIG_MFD_MAX8925) += max8925.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_ADC) += pcf50633-adc.o
obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.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_CORE) += ab3100-core.o
obj-$(CONFIG_AB3100_OTP) += ab3100-otp.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_MFD_TIMBERDALE) += timberdale.o
obj-$(CONFIG_PMIC_ADP5520) += adp5520.o obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
obj-$(CONFIG_LPC_SCH) += lpc_sch.o obj-$(CONFIG_LPC_SCH) += lpc_sch.o
\ No newline at end of file obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o
obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/uaccess.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 */ /* These are the only registers inside AB3100 used in this main file */
...@@ -59,24 +59,15 @@ ...@@ -59,24 +59,15 @@
* The AB3100 is usually assigned address 0x48 (7-bit) * The AB3100 is usually assigned address 0x48 (7-bit)
* The chip is defined in the platform i2c_board_data section. * The chip is defined in the platform i2c_board_data section.
*/ */
static int ab3100_get_chip_id(struct device *dev)
u8 ab3100_get_chip_type(struct ab3100 *ab3100)
{ {
u8 chip = ABUNKNOWN; struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
switch (ab3100->chip_id & 0xf0) { return (int)ab3100->chip_id;
case 0xa0:
chip = AB3000;
break;
case 0xc0:
chip = AB3100;
break;
}
return chip;
} }
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}; u8 regandval[2] = {reg, regval};
int err; int err;
...@@ -108,8 +99,14 @@ int ab3100_set_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 regval) ...@@ -108,8 +99,14 @@ int ab3100_set_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 regval)
mutex_unlock(&ab3100->access_mutex); mutex_unlock(&ab3100->access_mutex);
return err; 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 * 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, ...@@ -148,8 +145,8 @@ static int ab3100_set_test_register_interruptible(struct ab3100 *ab3100,
return err; return err;
} }
static int ab3100_get_register_interruptible(struct ab3100 *ab3100,
int ab3100_get_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 *regval) u8 reg, u8 *regval)
{ {
int err; int err;
...@@ -203,10 +200,16 @@ int ab3100_get_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 *regval) ...@@ -203,10 +200,16 @@ int ab3100_get_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 *regval)
mutex_unlock(&ab3100->access_mutex); mutex_unlock(&ab3100->access_mutex);
return err; 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);
return ab3100_get_register_interruptible(ab3100, reg, value);
}
int ab3100_get_register_page_interruptible(struct ab3100 *ab3100, static int ab3100_get_register_page_interruptible(struct ab3100 *ab3100,
u8 first_reg, u8 *regvals, u8 numregs) u8 first_reg, u8 *regvals, u8 numregs)
{ {
int err; int err;
...@@ -260,10 +263,17 @@ int ab3100_get_register_page_interruptible(struct ab3100 *ab3100, ...@@ -260,10 +263,17 @@ int ab3100_get_register_page_interruptible(struct ab3100 *ab3100,
mutex_unlock(&ab3100->access_mutex); mutex_unlock(&ab3100->access_mutex);
return err; 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);
return ab3100_get_register_page_interruptible(ab3100,
first_reg, regvals, numregs);
}
int ab3100_mask_and_set_register_interruptible(struct ab3100 *ab3100, static int ab3100_mask_and_set_register_interruptible(struct ab3100 *ab3100,
u8 reg, u8 andmask, u8 ormask) u8 reg, u8 andmask, u8 ormask)
{ {
u8 regandval[2] = {reg, 0}; u8 regandval[2] = {reg, 0};
...@@ -331,8 +341,15 @@ int ab3100_mask_and_set_register_interruptible(struct ab3100 *ab3100, ...@@ -331,8 +341,15 @@ int ab3100_mask_and_set_register_interruptible(struct ab3100 *ab3100,
mutex_unlock(&ab3100->access_mutex); mutex_unlock(&ab3100->access_mutex);
return err; 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. * Register a simple callback for handling any AB3100 events.
...@@ -357,15 +374,27 @@ int ab3100_event_unregister(struct ab3100 *ab3100, ...@@ -357,15 +374,27 @@ int ab3100_event_unregister(struct ab3100 *ab3100,
EXPORT_SYMBOL(ab3100_event_unregister); EXPORT_SYMBOL(ab3100_event_unregister);
int ab3100_event_registers_startup_state_get(struct ab3100 *ab3100, static int ab3100_event_registers_startup_state_get(struct device *dev,
u32 *fatevent) u8 *event)
{ {
struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
if (!ab3100->startup_events_read) if (!ab3100->startup_events_read)
return -EAGAIN; /* Try again later */ return -EAGAIN; /* Try again later */
*fatevent = ab3100->startup_events; memcpy(event, ab3100->startup_events, 3);
return 0; 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 * 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) ...@@ -390,7 +419,9 @@ static irqreturn_t ab3100_irq_handler(int irq, void *data)
event_regs[2]; event_regs[2];
if (!ab3100->startup_events_read) { 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; ab3100->startup_events_read = true;
} }
/* /*
...@@ -703,7 +734,8 @@ static int __init ab3100_setup(struct ab3100 *ab3100) ...@@ -703,7 +734,8 @@ static int __init ab3100_setup(struct ab3100 *ab3100)
dev_warn(ab3100->dev, dev_warn(ab3100->dev,
"AB3100 P1E variant detected, " "AB3100 P1E variant detected, "
"forcing chip to 32KHz\n"); "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: exit_no_setup:
...@@ -898,6 +930,10 @@ static int __init ab3100_probe(struct i2c_client *client, ...@@ -898,6 +930,10 @@ static int __init ab3100_probe(struct i2c_client *client,
if (err) if (err)
goto exit_no_irq; 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 */ /* Set parent and a pointer back to the container in device data */
for (i = 0; i < ARRAY_SIZE(ab3100_platform_devs); i++) { for (i = 0; i < ARRAY_SIZE(ab3100_platform_devs); i++) {
ab3100_platform_devs[i]->dev.parent = ab3100_platform_devs[i]->dev.parent =
...@@ -915,11 +951,13 @@ static int __init ab3100_probe(struct i2c_client *client, ...@@ -915,11 +951,13 @@ static int __init ab3100_probe(struct i2c_client *client,
return 0; return 0;
exit_no_ops:
exit_no_irq: exit_no_irq:
exit_no_setup: exit_no_setup:
i2c_unregister_device(ab3100->testreg_client); i2c_unregister_device(ab3100->testreg_client);
exit_no_testreg_client: exit_no_testreg_client:
exit_no_detect: exit_no_detect:
i2c_set_clientdata(client, NULL);
kfree(ab3100); kfree(ab3100);
return err; return err;
} }
...@@ -941,6 +979,7 @@ static int __exit ab3100_remove(struct i2c_client *client) ...@@ -941,6 +979,7 @@ static int __exit ab3100_remove(struct i2c_client *client)
* their notifiers so deactivate IRQ * their notifiers so deactivate IRQ
*/ */
free_irq(client->irq, ab3100); free_irq(client->irq, ab3100);
i2c_set_clientdata(client, NULL);
kfree(ab3100); kfree(ab3100);
return 0; return 0;
} }
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/mfd/ab3100.h> #include <linux/mfd/abx500.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
...@@ -30,7 +30,6 @@ ...@@ -30,7 +30,6 @@
/** /**
* struct ab3100_otp * struct ab3100_otp
* @dev containing device * @dev containing device
* @ab3100 a pointer to the parent ab3100 device struct
* @locked whether the OTP is locked, after locking, no more bits * @locked whether the OTP is locked, after locking, no more bits
* can be changed but before locking it is still possible * can be changed but before locking it is still possible
* to change bits from 1->0. * to change bits from 1->0.
...@@ -49,7 +48,6 @@ ...@@ -49,7 +48,6 @@
*/ */
struct ab3100_otp { struct ab3100_otp {
struct device *dev; struct device *dev;
struct ab3100 *ab3100;
bool locked; bool locked;
u32 freq; u32 freq;
bool paf; bool paf;
...@@ -63,19 +61,19 @@ struct ab3100_otp { ...@@ -63,19 +61,19 @@ struct ab3100_otp {
static int __init ab3100_otp_read(struct ab3100_otp *otp) static int __init ab3100_otp_read(struct ab3100_otp *otp)
{ {
struct ab3100 *ab = otp->ab3100;
u8 otpval[8]; u8 otpval[8];
u8 otpp; u8 otpp;
int err; int err;
err = ab3100_get_register_interruptible(ab, AB3100_OTPP, &otpp); err = abx500_get_register_interruptible(otp->dev, 0,
AB3100_OTPP, &otpp);
if (err) { if (err) {
dev_err(otp->dev, "unable to read OTPP register\n"); dev_err(otp->dev, "unable to read OTPP register\n");
return err; return err;
} }
err = ab3100_get_register_page_interruptible(ab, AB3100_OTP0, err = abx500_get_register_page_interruptible(otp->dev, 0,
otpval, 8); AB3100_OTP0, otpval, 8);
if (err) { if (err) {
dev_err(otp->dev, "unable to read OTP register page\n"); dev_err(otp->dev, "unable to read OTP register page\n");
return err; return err;
...@@ -197,7 +195,6 @@ static int __init ab3100_otp_probe(struct platform_device *pdev) ...@@ -197,7 +195,6 @@ static int __init ab3100_otp_probe(struct platform_device *pdev)
otp->dev = &pdev->dev; otp->dev = &pdev->dev;
/* Replace platform data coming in with a local struct */ /* Replace platform data coming in with a local struct */
otp->ab3100 = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, otp); platform_set_drvdata(pdev, otp);
err = ab3100_otp_read(otp); err = ab3100_otp_read(otp);
......
This diff is collapsed.
/*
* 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");
This diff is collapsed.
/*
* 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");
/*
* 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) ...@@ -544,6 +544,7 @@ static int __devexit da903x_remove(struct i2c_client *client)
struct da903x_chip *chip = i2c_get_clientdata(client); struct da903x_chip *chip = i2c_get_clientdata(client);
da903x_remove_subdevs(chip); da903x_remove_subdevs(chip);
i2c_set_clientdata(client, NULL);
kfree(chip); kfree(chip);
return 0; return 0;
} }
......
/*
* 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, ...@@ -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->i2c, MAX8925_ON_OFF_IRQ2);
max8925_reg_read(chip->rtc, MAX8925_RTC_IRQ); max8925_reg_read(chip->rtc, MAX8925_RTC_IRQ);
max8925_reg_read(chip->adc, MAX8925_TSC_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_ALARM0_CNTL, 0);
max8925_reg_write(chip->rtc, MAX8925_ALARM1_CNTL, 0); max8925_reg_write(chip->rtc, MAX8925_ALARM1_CNTL, 0);
max8925_reg_write(chip->i2c, MAX8925_CHG_IRQ1_MASK, 0xff); 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, ...@@ -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_IRQ1_MASK, 0xff);
max8925_reg_write(chip->i2c, MAX8925_ON_OFF_IRQ2_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->rtc, MAX8925_RTC_IRQ_MASK, 0xff);
max8925_reg_write(chip->adc, MAX8925_TSC_IRQ_MASK, 0xff);
mutex_init(&chip->irq_lock); mutex_init(&chip->irq_lock);
chip->core_irq = irq; chip->core_irq = irq;
...@@ -547,7 +546,11 @@ static int max8925_irq_init(struct max8925_chip *chip, int 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); dev_err(chip->dev, "Failed to request core IRQ: %d\n", ret);
chip->core_irq = 0; chip->core_irq = 0;
} }
tsc_irq: tsc_irq:
/* mask TSC interrupt */
max8925_reg_write(chip->adc, MAX8925_TSC_IRQ_MASK, 0x0f);
if (!pdata->tsc_irq) { if (!pdata->tsc_irq) {
dev_warn(chip->dev, "No interrupt support on TSC IRQ\n"); dev_warn(chip->dev, "No interrupt support on TSC IRQ\n");
return 0; return 0;
......
...@@ -173,8 +173,6 @@ static int __devexit max8925_remove(struct i2c_client *client) ...@@ -173,8 +173,6 @@ static int __devexit max8925_remove(struct i2c_client *client)
max8925_device_exit(chip); max8925_device_exit(chip);
i2c_unregister_device(chip->adc); i2c_unregister_device(chip->adc);
i2c_unregister_device(chip->rtc); i2c_unregister_device(chip->rtc);
i2c_set_clientdata(chip->adc, NULL);
i2c_set_clientdata(chip->rtc, NULL);
i2c_set_clientdata(chip->i2c, NULL); i2c_set_clientdata(chip->i2c, NULL);
kfree(chip); kfree(chip);
return 0; return 0;
......
...@@ -1228,6 +1228,7 @@ static int menelaus_probe(struct i2c_client *client, ...@@ -1228,6 +1228,7 @@ static int menelaus_probe(struct i2c_client *client,
free_irq(client->irq, menelaus); free_irq(client->irq, menelaus);
flush_scheduled_work(); flush_scheduled_work();
fail1: fail1:
i2c_set_clientdata(client, NULL);
kfree(menelaus); kfree(menelaus);
return err; return err;
} }
...@@ -1237,8 +1238,8 @@ static int __exit menelaus_remove(struct i2c_client *client) ...@@ -1237,8 +1238,8 @@ static int __exit menelaus_remove(struct i2c_client *client)
struct menelaus_chip *menelaus = i2c_get_clientdata(client); struct menelaus_chip *menelaus = i2c_get_clientdata(client);
free_irq(client->irq, menelaus); free_irq(client->irq, menelaus);
kfree(menelaus);
i2c_set_clientdata(client, NULL); i2c_set_clientdata(client, NULL);
kfree(menelaus);
the_menelaus = NULL; the_menelaus = NULL;
return 0; return 0;
} }
......
...@@ -48,7 +48,7 @@ static int mfd_add_device(struct device *parent, int id, ...@@ -48,7 +48,7 @@ static int mfd_add_device(struct device *parent, int id,
res[r].flags = cell->resources[r].flags; res[r].flags = cell->resources[r].flags;
/* Find out base to use */ /* 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].parent = mem_base;
res[r].start = mem_base->start + res[r].start = mem_base->start +
cell->resources[r].start; cell->resources[r].start;
......
...@@ -30,13 +30,13 @@ ...@@ -30,13 +30,13 @@
struct pcf50633_adc_request { struct pcf50633_adc_request {
int mux; int mux;
int avg; int avg;
int result;
void (*callback)(struct pcf50633 *, void *, int); void (*callback)(struct pcf50633 *, void *, int);
void *callback_param; void *callback_param;
};
/* Used in case of sync requests */ struct pcf50633_adc_sync_request {
int result;
struct completion completion; struct completion completion;
}; };
#define PCF50633_MAX_ADC_FIFO_DEPTH 8 #define PCF50633_MAX_ADC_FIFO_DEPTH 8
...@@ -109,10 +109,10 @@ adc_enqueue_request(struct pcf50633 *pcf, struct pcf50633_adc_request *req) ...@@ -109,10 +109,10 @@ adc_enqueue_request(struct pcf50633 *pcf, struct pcf50633_adc_request *req)
return 0; return 0;
} }
static void static void pcf50633_adc_sync_read_callback(struct pcf50633 *pcf, void *param,
pcf50633_adc_sync_read_callback(struct pcf50633 *pcf, void *param, int result) int result)
{ {
struct pcf50633_adc_request *req = param; struct pcf50633_adc_sync_request *req = param;
req->result = result; req->result = result;
complete(&req->completion); complete(&req->completion);
...@@ -120,28 +120,19 @@ pcf50633_adc_sync_read_callback(struct pcf50633 *pcf, void *param, int result) ...@@ -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) int pcf50633_adc_sync_read(struct pcf50633 *pcf, int mux, int avg)
{ {
struct pcf50633_adc_request *req; struct pcf50633_adc_sync_request req;
int err; int ret;
/* req is freed when the result is ready, in interrupt handler */ init_completion(&req.completion);
req = kzalloc(sizeof(*req), GFP_KERNEL);
if (!req)
return -ENOMEM;
req->mux = mux;
req->avg = avg;
req->callback = pcf50633_adc_sync_read_callback;
req->callback_param = req;
init_completion(&req->completion); ret = pcf50633_adc_async_read(pcf, mux, avg,
err = adc_enqueue_request(pcf, req); pcf50633_adc_sync_read_callback, &req);
if (err) if (ret)
return err; return ret;
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); EXPORT_SYMBOL_GPL(pcf50633_adc_sync_read);
......
This diff is collapsed.
This diff is collapsed.
/*
* 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) ...@@ -318,6 +318,9 @@ static int t7l66xb_probe(struct platform_device *dev)
struct resource *iomem, *rscr; struct resource *iomem, *rscr;
int ret; int ret;
if (pdata == NULL)
return -EINVAL;
iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); iomem = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (!iomem) if (!iomem)
return -EINVAL; return -EINVAL;
......
This diff is collapsed.
This diff is collapsed.
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
#ifndef MFD_TIMBERDALE_H #ifndef MFD_TIMBERDALE_H
#define 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 */ /* This driver only support versions >= 3.8 and < 4.0 */
#define TIMB_SUPPORTED_MAJOR 3 #define TIMB_SUPPORTED_MAJOR 3
...@@ -66,7 +66,7 @@ ...@@ -66,7 +66,7 @@
#define CHIPCTLOFFSET 0x800 #define CHIPCTLOFFSET 0x800
#define CHIPCTLEND 0x8ff #define CHIPCTLEND 0x8ff
#define CHIPCTLSIZE (CHIPCTLEND - CHIPCTLOFFSET) #define CHIPCTLSIZE (CHIPCTLEND - CHIPCTLOFFSET + 1)
#define INTCOFFSET 0xc00 #define INTCOFFSET 0xc00
#define INTCEND 0xfff #define INTCEND 0xfff
...@@ -127,4 +127,16 @@ ...@@ -127,4 +127,16 @@
#define GPIO_PIN_BT_RST 15 #define GPIO_PIN_BT_RST 15
#define GPIO_NR_PINS 16 #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 #endif
...@@ -530,8 +530,8 @@ static int __exit tps65010_remove(struct i2c_client *client) ...@@ -530,8 +530,8 @@ static int __exit tps65010_remove(struct i2c_client *client)
cancel_delayed_work(&tps->work); cancel_delayed_work(&tps->work);
flush_scheduled_work(); flush_scheduled_work();
debugfs_remove(tps->file); debugfs_remove(tps->file);
kfree(tps);
i2c_set_clientdata(client, NULL); i2c_set_clientdata(client, NULL);
kfree(tps);
the_tps = NULL; the_tps = NULL;
return 0; return 0;
} }
......
This diff is collapsed.
...@@ -232,10 +232,11 @@ static const struct sih sih_modules_twl5031[8] = { ...@@ -232,10 +232,11 @@ static const struct sih sih_modules_twl5031[8] = {
}, },
[6] = { [6] = {
/* /*
* ACI doesn't use the same SIH organization. * ECI/DBI doesn't use the same SIH organization.
* For example, it supports only one interrupt line * 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, .module = TWL5031_MODULE_ACCESSORY,
.bits = 9, .bits = 9,
.bytes_ixr = 2, .bytes_ixr = 2,
...@@ -247,8 +248,8 @@ static const struct sih sih_modules_twl5031[8] = { ...@@ -247,8 +248,8 @@ static const struct sih sih_modules_twl5031[8] = {
}, },
[7] = { [7] = {
/* Accessory */ /* Audio accessory */
.name = "acc", .name = "audio",
.module = TWL5031_MODULE_ACCESSORY, .module = TWL5031_MODULE_ACCESSORY,
.control_offset = TWL5031_ACCSIHCTRL, .control_offset = TWL5031_ACCSIHCTRL,
.bits = 2, .bits = 2,
......
This diff is collapsed.
...@@ -39,8 +39,6 @@ struct wm831x_irq_data { ...@@ -39,8 +39,6 @@ struct wm831x_irq_data {
int primary; int primary;
int reg; int reg;
int mask; int mask;
irq_handler_t handler;
void *handler_data;
}; };
static struct wm831x_irq_data wm831x_irqs[] = { static struct wm831x_irq_data wm831x_irqs[] = {
...@@ -492,6 +490,14 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq) ...@@ -492,6 +490,14 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
mutex_init(&wm831x->irq_lock); 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) { if (!irq) {
dev_warn(wm831x->dev, dev_warn(wm831x->dev,
"No interrupt specified - functionality limited\n"); "No interrupt specified - functionality limited\n");
...@@ -507,14 +513,6 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq) ...@@ -507,14 +513,6 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
wm831x->irq = irq; wm831x->irq = irq;
wm831x->irq_base = pdata->irq_base; 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 */ /* Register them with genirq */
for (cur_irq = wm831x->irq_base; for (cur_irq = wm831x->irq_base;
cur_irq < ARRAY_SIZE(wm831x_irqs) + 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, ...@@ -64,10 +64,8 @@ static int wm8350_i2c_probe(struct i2c_client *i2c,
int ret = 0; int ret = 0;
wm8350 = kzalloc(sizeof(struct wm8350), GFP_KERNEL); wm8350 = kzalloc(sizeof(struct wm8350), GFP_KERNEL);
if (wm8350 == NULL) { if (wm8350 == NULL)
kfree(i2c);
return -ENOMEM; return -ENOMEM;
}
i2c_set_clientdata(i2c, wm8350); i2c_set_clientdata(i2c, wm8350);
wm8350->dev = &i2c->dev; wm8350->dev = &i2c->dev;
...@@ -82,6 +80,7 @@ static int wm8350_i2c_probe(struct i2c_client *i2c, ...@@ -82,6 +80,7 @@ static int wm8350_i2c_probe(struct i2c_client *i2c,
return ret; return ret;
err: err:
i2c_set_clientdata(i2c, NULL);
kfree(wm8350); kfree(wm8350);
return ret; return ret;
} }
...@@ -91,6 +90,7 @@ static int wm8350_i2c_remove(struct i2c_client *i2c) ...@@ -91,6 +90,7 @@ static int wm8350_i2c_remove(struct i2c_client *i2c)
struct wm8350 *wm8350 = i2c_get_clientdata(i2c); struct wm8350 *wm8350 = i2c_get_clientdata(i2c);
wm8350_device_exit(wm8350); wm8350_device_exit(wm8350);
i2c_set_clientdata(i2c, NULL);
kfree(wm8350); kfree(wm8350);
return 0; return 0;
......
This diff is collapsed.
This diff is collapsed.
...@@ -15,5 +15,6 @@ obj-$(CONFIG_CAN_AT91) += at91_can.o ...@@ -15,5 +15,6 @@ obj-$(CONFIG_CAN_AT91) += at91_can.o
obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o
obj-$(CONFIG_CAN_MCP251X) += mcp251x.o obj-$(CONFIG_CAN_MCP251X) += mcp251x.o
obj-$(CONFIG_CAN_BFIN) += bfin_can.o obj-$(CONFIG_CAN_BFIN) += bfin_can.o
obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o
ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment