diff options
Diffstat (limited to 'drivers')
47 files changed, 7260 insertions, 955 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 4fd0f27..724038d 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -195,6 +195,13 @@ config GPIO_PCF857X This driver provides an in-kernel interface to those GPIOs using platform-neutral GPIO calls. +config GPIO_TC35892 + bool "TC35892 GPIOs" + depends on MFD_TC35892 + help + This enables support for the GPIOs found on the TC35892 + I/O Expander. + config GPIO_TWL4030 tristate "TWL4030, TWL5030, and TPS659x0 GPIOs" depends on TWL4030_CORE @@ -282,6 +289,15 @@ config GPIO_TIMBERDALE ---help--- Add support for the GPIO IP in the timberdale FPGA. +config GPIO_RDC321X + tristate "RDC R-321x GPIO support" + depends on PCI && GPIOLIB + select MFD_CORE + select MFD_RDC321X + help + Support for the RDC R321x SoC GPIOs over southbridge + PCI configuration space. + comment "SPI GPIO expanders:" config GPIO_MAX7301 @@ -317,4 +333,14 @@ config GPIO_UCB1400 To compile this driver as a module, choose M here: the module will be called ucb1400_gpio. +comment "MODULbus GPIO expanders:" + +config GPIO_JANZ_TTL + tristate "Janz VMOD-TTL Digital IO Module" + depends on MFD_JANZ_CMODIO + help + This enables support for the Janz VMOD-TTL Digital IO module. + This driver provides support for driving the pins in output + mode only. Input mode is not supported. + endif diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 10f3f8d..51c3cdd 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o obj-$(CONFIG_GPIO_PCA953X) += pca953x.o obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o obj-$(CONFIG_GPIO_PL061) += pl061.o +obj-$(CONFIG_GPIO_TC35892) += tc35892-gpio.o obj-$(CONFIG_GPIO_TIMBERDALE) += timbgpio.o obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o obj-$(CONFIG_GPIO_UCB1400) += ucb1400_gpio.o @@ -27,4 +28,6 @@ obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o obj-$(CONFIG_GPIO_WM831X) += wm831x-gpio.o obj-$(CONFIG_GPIO_WM8350) += wm8350-gpiolib.o obj-$(CONFIG_GPIO_WM8994) += wm8994-gpio.o -obj-$(CONFIG_GPIO_SCH) += sch_gpio.o
\ No newline at end of file +obj-$(CONFIG_GPIO_SCH) += sch_gpio.o +obj-$(CONFIG_GPIO_RDC321X) += rdc321x-gpio.o +obj-$(CONFIG_GPIO_JANZ_TTL) += janz-ttl.o diff --git a/drivers/gpio/janz-ttl.c b/drivers/gpio/janz-ttl.c new file mode 100644 index 0000000..813ac07 --- /dev/null +++ b/drivers/gpio/janz-ttl.c @@ -0,0 +1,258 @@ +/* + * Janz MODULbus VMOD-TTL GPIO Driver + * + * Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/gpio.h> +#include <linux/slab.h> + +#include <linux/mfd/janz.h> + +#define DRV_NAME "janz-ttl" + +#define PORTA_DIRECTION 0x23 +#define PORTB_DIRECTION 0x2B +#define PORTC_DIRECTION 0x06 +#define PORTA_IOCTL 0x24 +#define PORTB_IOCTL 0x2C +#define PORTC_IOCTL 0x07 + +#define MASTER_INT_CTL 0x00 +#define MASTER_CONF_CTL 0x01 + +#define CONF_PAE (1 << 2) +#define CONF_PBE (1 << 7) +#define CONF_PCE (1 << 4) + +struct ttl_control_regs { + __be16 portc; + __be16 portb; + __be16 porta; + __be16 control; +}; + +struct ttl_module { + struct gpio_chip gpio; + + /* base address of registers */ + struct ttl_control_regs __iomem *regs; + + u8 portc_shadow; + u8 portb_shadow; + u8 porta_shadow; + + spinlock_t lock; +}; + +static int ttl_get_value(struct gpio_chip *gpio, unsigned offset) +{ + struct ttl_module *mod = dev_get_drvdata(gpio->dev); + u8 *shadow; + int ret; + + if (offset < 8) { + shadow = &mod->porta_shadow; + } else if (offset < 16) { + shadow = &mod->portb_shadow; + offset -= 8; + } else { + shadow = &mod->portc_shadow; + offset -= 16; + } + + spin_lock(&mod->lock); + ret = *shadow & (1 << offset); + spin_unlock(&mod->lock); + return ret; +} + +static void ttl_set_value(struct gpio_chip *gpio, unsigned offset, int value) +{ + struct ttl_module *mod = dev_get_drvdata(gpio->dev); + void __iomem *port; + u8 *shadow; + + if (offset < 8) { + port = &mod->regs->porta; + shadow = &mod->porta_shadow; + } else if (offset < 16) { + port = &mod->regs->portb; + shadow = &mod->portb_shadow; + offset -= 8; + } else { + port = &mod->regs->portc; + shadow = &mod->portc_shadow; + offset -= 16; + } + + spin_lock(&mod->lock); + if (value) + *shadow |= (1 << offset); + else + *shadow &= ~(1 << offset); + + iowrite16be(*shadow, port); + spin_unlock(&mod->lock); +} + +static void __devinit ttl_write_reg(struct ttl_module *mod, u8 reg, u16 val) +{ + iowrite16be(reg, &mod->regs->control); + iowrite16be(val, &mod->regs->control); +} + +static void __devinit ttl_setup_device(struct ttl_module *mod) +{ + /* reset the device to a known state */ + iowrite16be(0x0000, &mod->regs->control); + iowrite16be(0x0001, &mod->regs->control); + iowrite16be(0x0000, &mod->regs->control); + + /* put all ports in open-drain mode */ + ttl_write_reg(mod, PORTA_IOCTL, 0x00ff); + ttl_write_reg(mod, PORTB_IOCTL, 0x00ff); + ttl_write_reg(mod, PORTC_IOCTL, 0x000f); + + /* set all ports as outputs */ + ttl_write_reg(mod, PORTA_DIRECTION, 0x0000); + ttl_write_reg(mod, PORTB_DIRECTION, 0x0000); + ttl_write_reg(mod, PORTC_DIRECTION, 0x0000); + + /* set all ports to drive zeroes */ + iowrite16be(0x0000, &mod->regs->porta); + iowrite16be(0x0000, &mod->regs->portb); + iowrite16be(0x0000, &mod->regs->portc); + + /* enable all ports */ + ttl_write_reg(mod, MASTER_CONF_CTL, CONF_PAE | CONF_PBE | CONF_PCE); +} + +static int __devinit ttl_probe(struct platform_device *pdev) +{ + struct janz_platform_data *pdata; + struct device *dev = &pdev->dev; + struct ttl_module *mod; + struct gpio_chip *gpio; + struct resource *res; + int ret; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(dev, "no platform data\n"); + ret = -ENXIO; + goto out_return; + } + + mod = kzalloc(sizeof(*mod), GFP_KERNEL); + if (!mod) { + dev_err(dev, "unable to allocate private data\n"); + ret = -ENOMEM; + goto out_return; + } + + platform_set_drvdata(pdev, mod); + spin_lock_init(&mod->lock); + + /* get access to the MODULbus registers for this module */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "MODULbus registers not found\n"); + ret = -ENODEV; + goto out_free_mod; + } + + mod->regs = ioremap(res->start, resource_size(res)); + if (!mod->regs) { + dev_err(dev, "MODULbus registers not ioremap\n"); + ret = -ENOMEM; + goto out_free_mod; + } + + ttl_setup_device(mod); + + /* Initialize the GPIO data structures */ + gpio = &mod->gpio; + gpio->dev = &pdev->dev; + gpio->label = pdev->name; + gpio->get = ttl_get_value; + gpio->set = ttl_set_value; + gpio->owner = THIS_MODULE; + + /* request dynamic allocation */ + gpio->base = -1; + gpio->ngpio = 20; + + ret = gpiochip_add(gpio); + if (ret) { + dev_err(dev, "unable to add GPIO chip\n"); + goto out_iounmap_regs; + } + + dev_info(&pdev->dev, "module %d: registered GPIO device\n", + pdata->modno); + return 0; + +out_iounmap_regs: + iounmap(mod->regs); +out_free_mod: + kfree(mod); +out_return: + return ret; +} + +static int __devexit ttl_remove(struct platform_device *pdev) +{ + struct ttl_module *mod = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int ret; + + ret = gpiochip_remove(&mod->gpio); + if (ret) { + dev_err(dev, "unable to remove GPIO chip\n"); + return ret; + } + + iounmap(mod->regs); + kfree(mod); + return 0; +} + +static struct platform_driver ttl_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = ttl_probe, + .remove = __devexit_p(ttl_remove), +}; + +static int __init ttl_init(void) +{ + return platform_driver_register(&ttl_driver); +} + +static void __exit ttl_exit(void) +{ + platform_driver_unregister(&ttl_driver); +} + +MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>"); +MODULE_DESCRIPTION("Janz MODULbus VMOD-TTL Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:janz-ttl"); + +module_init(ttl_init); +module_exit(ttl_exit); diff --git a/drivers/gpio/rdc321x-gpio.c b/drivers/gpio/rdc321x-gpio.c new file mode 100644 index 0000000..2762698 --- /dev/null +++ b/drivers/gpio/rdc321x-gpio.c @@ -0,0 +1,246 @@ +/* + * RDC321x GPIO driver + * + * Copyright (C) 2008, Volker Weiss <dev@tintuc.de> + * Copyright (C) 2007-2010 Florian Fainelli <florian@openwrt.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/platform_device.h> +#include <linux/pci.h> +#include <linux/gpio.h> +#include <linux/mfd/rdc321x.h> +#include <linux/slab.h> + +struct rdc321x_gpio { + spinlock_t lock; + struct pci_dev *sb_pdev; + u32 data_reg[2]; + int reg1_ctrl_base; + int reg1_data_base; + int reg2_ctrl_base; + int reg2_data_base; + struct gpio_chip chip; +}; + +/* read GPIO pin */ +static int rdc_gpio_get_value(struct gpio_chip *chip, unsigned gpio) +{ + struct rdc321x_gpio *gpch; + u32 value = 0; + int reg; + + gpch = container_of(chip, struct rdc321x_gpio, chip); + reg = gpio < 32 ? gpch->reg1_data_base : gpch->reg2_data_base; + + spin_lock(&gpch->lock); + pci_write_config_dword(gpch->sb_pdev, reg, + gpch->data_reg[gpio < 32 ? 0 : 1]); + pci_read_config_dword(gpch->sb_pdev, reg, &value); + spin_unlock(&gpch->lock); + + return (1 << (gpio & 0x1f)) & value ? 1 : 0; +} + +static void rdc_gpio_set_value_impl(struct gpio_chip *chip, + unsigned gpio, int value) +{ + struct rdc321x_gpio *gpch; + int reg = (gpio < 32) ? 0 : 1; + + gpch = container_of(chip, struct rdc321x_gpio, chip); + + if (value) + gpch->data_reg[reg] |= 1 << (gpio & 0x1f); + else + gpch->data_reg[reg] &= ~(1 << (gpio & 0x1f)); + + pci_write_config_dword(gpch->sb_pdev, + reg ? gpch->reg2_data_base : gpch->reg1_data_base, + gpch->data_reg[reg]); +} + +/* set GPIO pin to value */ +static void rdc_gpio_set_value(struct gpio_chip *chip, + unsigned gpio, int value) +{ + struct rdc321x_gpio *gpch; + + gpch = container_of(chip, struct rdc321x_gpio, chip); + spin_lock(&gpch->lock); + rdc_gpio_set_value_impl(chip, gpio, value); + spin_unlock(&gpch->lock); +} + +static int rdc_gpio_config(struct gpio_chip *chip, + unsigned gpio, int value) +{ + struct rdc321x_gpio *gpch; + int err; + u32 reg; + + gpch = container_of(chip, struct rdc321x_gpio, chip); + + spin_lock(&gpch->lock); + err = pci_read_config_dword(gpch->sb_pdev, gpio < 32 ? + gpch->reg1_ctrl_base : gpch->reg2_ctrl_base, ®); + if (err) + goto unlock; + + reg |= 1 << (gpio & 0x1f); + + err = pci_write_config_dword(gpch->sb_pdev, gpio < 32 ? + gpch->reg1_ctrl_base : gpch->reg2_ctrl_base, reg); + if (err) + goto unlock; + + rdc_gpio_set_value_impl(chip, gpio, value); + +unlock: + spin_unlock(&gpch->lock); + + return err; +} + +/* configure GPIO pin as input */ +static int rdc_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) +{ + return rdc_gpio_config(chip, gpio, 1); +} + +/* + * Cache the initial value of both GPIO data registers + */ +static int __devinit rdc321x_gpio_probe(struct platform_device *pdev) +{ + int err; + struct resource *r; + struct rdc321x_gpio *rdc321x_gpio_dev; + struct rdc321x_gpio_pdata *pdata; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "no platform data supplied\n"); + return -ENODEV; + } + + rdc321x_gpio_dev = kzalloc(sizeof(struct rdc321x_gpio), GFP_KERNEL); + if (!rdc321x_gpio_dev) { + dev_err(&pdev->dev, "failed to allocate private data\n"); + return -ENOMEM; + } + + r = platform_get_resource_byname(pdev, IORESOURCE_IO, "gpio-reg1"); + if (!r) { + dev_err(&pdev->dev, "failed to get gpio-reg1 resource\n"); + err = -ENODEV; + goto out_free; + } + + spin_lock_init(&rdc321x_gpio_dev->lock); + rdc321x_gpio_dev->sb_pdev = pdata->sb_pdev; + rdc321x_gpio_dev->reg1_ctrl_base = r->start; + rdc321x_gpio_dev->reg1_data_base = r->start + 0x4; + + r = platform_get_resource_byname(pdev, IORESOURCE_IO, "gpio-reg2"); + if (!r) { + dev_err(&pdev->dev, "failed to get gpio-reg2 resource\n"); + err = -ENODEV; + goto out_free; + } + + rdc321x_gpio_dev->reg2_ctrl_base = r->start; + rdc321x_gpio_dev->reg2_data_base = r->start + 0x4; + + rdc321x_gpio_dev->chip.label = "rdc321x-gpio"; + rdc321x_gpio_dev->chip.direction_input = rdc_gpio_direction_input; + rdc321x_gpio_dev->chip.direction_output = rdc_gpio_config; + rdc321x_gpio_dev->chip.get = rdc_gpio_get_value; + rdc321x_gpio_dev->chip.set = rdc_gpio_set_value; + rdc321x_gpio_dev->chip.base = 0; + rdc321x_gpio_dev->chip.ngpio = pdata->max_gpios; + + platform_set_drvdata(pdev, rdc321x_gpio_dev); + + /* This might not be, what others (BIOS, bootloader, etc.) + wrote to these registers before, but it's a good guess. Still + better than just using 0xffffffff. */ + err = pci_read_config_dword(rdc321x_gpio_dev->sb_pdev, + rdc321x_gpio_dev->reg1_data_base, + &rdc321x_gpio_dev->data_reg[0]); + if (err) + goto out_drvdata; + + err = pci_read_config_dword(rdc321x_gpio_dev->sb_pdev, + rdc321x_gpio_dev->reg2_data_base, + &rdc321x_gpio_dev->data_reg[1]); + if (err) + goto out_drvdata; + + dev_info(&pdev->dev, "registering %d GPIOs\n", + rdc321x_gpio_dev->chip.ngpio); + return gpiochip_add(&rdc321x_gpio_dev->chip); + +out_drvdata: + platform_set_drvdata(pdev, NULL); +out_free: + kfree(rdc321x_gpio_dev); + return err; +} + +static int __devexit rdc321x_gpio_remove(struct platform_device *pdev) +{ + int ret; + struct rdc321x_gpio *rdc321x_gpio_dev = platform_get_drvdata(pdev); + + ret = gpiochip_remove(&rdc321x_gpio_dev->chip); + if (ret) + dev_err(&pdev->dev, "failed to unregister chip\n"); + + kfree(rdc321x_gpio_dev); + platform_set_drvdata(pdev, NULL); + + return ret; +} + +static struct platform_driver rdc321x_gpio_driver = { + .driver.name = "rdc321x-gpio", + .driver.owner = THIS_MODULE, + .probe = rdc321x_gpio_probe, + .remove = __devexit_p(rdc321x_gpio_remove), +}; + +static int __init rdc321x_gpio_init(void) +{ + return platform_driver_register(&rdc321x_gpio_driver); +} + +static void __exit rdc321x_gpio_exit(void) +{ + platform_driver_unregister(&rdc321x_gpio_driver); +} + +module_init(rdc321x_gpio_init); +module_exit(rdc321x_gpio_exit); + +MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>"); +MODULE_DESCRIPTION("RDC321x GPIO driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rdc321x-gpio"); diff --git a/drivers/gpio/tc35892-gpio.c b/drivers/gpio/tc35892-gpio.c new file mode 100644 index 0000000..1be6288 --- /dev/null +++ b/drivers/gpio/tc35892-gpio.c @@ -0,0 +1,381 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License, version 2 + * Author: Hanumath Prasad <hanumath.prasad@stericsson.com> for ST-Ericsson + * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/mfd/tc35892.h> + +/* + * These registers are modified under the irq bus lock and cached to avoid + * unnecessary writes in bus_sync_unlock. + */ +enum { REG_IBE, REG_IEV, REG_IS, REG_IE }; + +#define CACHE_NR_REGS 4 +#define CACHE_NR_BANKS 3 + +struct tc35892_gpio { + struct gpio_chip chip; + struct tc35892 *tc35892; + struct device *dev; + struct mutex irq_lock; + + int irq_base; + + /* Caches of interrupt control registers for bus_lock */ + u8 regs[CACHE_NR_REGS][CACHE_NR_BANKS]; + u8 oldregs[CACHE_NR_REGS][CACHE_NR_BANKS]; +}; + +static inline struct tc35892_gpio *to_tc35892_gpio(struct gpio_chip *chip) +{ + return container_of(chip, struct tc35892_gpio, chip); +} + +static int tc35892_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip); + struct tc35892 *tc35892 = tc35892_gpio->tc35892; + u8 reg = TC35892_GPIODATA0 + (offset / 8) * 2; + u8 mask = 1 << (offset % 8); + int ret; + + ret = tc35892_reg_read(tc35892, reg); + if (ret < 0) + return ret; + + return ret & mask; +} + +static void tc35892_gpio_set(struct gpio_chip *chip, unsigned offset, int val) +{ + struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip); + struct tc35892 *tc35892 = tc35892_gpio->tc35892; + u8 reg = TC35892_GPIODATA0 + (offset / 8) * 2; + unsigned pos = offset % 8; + u8 data[] = {!!val << pos, 1 << pos}; + + tc35892_block_write(tc35892, reg, ARRAY_SIZE(data), data); +} + +static int tc35892_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int val) +{ + struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip); + struct tc35892 *tc35892 = tc35892_gpio->tc35892; + u8 reg = TC35892_GPIODIR0 + offset / 8; + unsigned pos = offset % 8; + + tc35892_gpio_set(chip, offset, val); + + return tc35892_set_bits(tc35892, reg, 1 << pos, 1 << pos); +} + +static int tc35892_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip); + struct tc35892 *tc35892 = tc35892_gpio->tc35892; + u8 reg = TC35892_GPIODIR0 + offset / 8; + unsigned pos = offset % 8; + + return tc35892_set_bits(tc35892, reg, 1 << pos, 0); +} + +static int tc35892_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip); + + return tc35892_gpio->irq_base + offset; +} + +static struct gpio_chip template_chip = { + .label = "tc35892", + .owner = THIS_MODULE, + .direction_input = tc35892_gpio_direction_input, + .get = tc35892_gpio_get, + .direction_output = tc35892_gpio_direction_output, + .set = tc35892_gpio_set, + .to_irq = tc35892_gpio_to_irq, + .can_sleep = 1, +}; + +static int tc35892_gpio_irq_set_type(unsigned int irq, unsigned int type) +{ + struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq); + int offset = irq - tc35892_gpio->irq_base; + int regoffset = offset / 8; + int mask = 1 << (offset % 8); + + if (type == IRQ_TYPE_EDGE_BOTH) { + tc35892_gpio->regs[REG_IBE][regoffset] |= mask; + return 0; + } + + tc35892_gpio->regs[REG_IBE][regoffset] &= ~mask; + + if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH) + tc35892_gpio->regs[REG_IS][regoffset] |= mask; + else + tc35892_gpio->regs[REG_IS][regoffset] &= ~mask; + + if (type == IRQ_TYPE_EDGE_RISING || type == IRQ_TYPE_LEVEL_HIGH) + tc35892_gpio->regs[REG_IEV][regoffset] |= mask; + else + tc35892_gpio->regs[REG_IEV][regoffset] &= ~mask; + + return 0; +} + +static void tc35892_gpio_irq_lock(unsigned int irq) +{ + struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq); + + mutex_lock(&tc35892_gpio->irq_lock); +} + +static void tc35892_gpio_irq_sync_unlock(unsigned int irq) +{ + struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq); + struct tc35892 *tc35892 = tc35892_gpio->tc35892; + static const u8 regmap[] = { + [REG_IBE] = TC35892_GPIOIBE0, + [REG_IEV] = TC35892_GPIOIEV0, + [REG_IS] = TC35892_GPIOIS0, + [REG_IE] = TC35892_GPIOIE0, + }; + int i, j; + + for (i = 0; i < CACHE_NR_REGS; i++) { + for (j = 0; j < CACHE_NR_BANKS; j++) { + u8 old = tc35892_gpio->oldregs[i][j]; + u8 new = tc35892_gpio->regs[i][j]; + + if (new == old) + continue; + + tc35892_gpio->oldregs[i][j] = new; + tc35892_reg_write(tc35892, regmap[i] + j * 8, new); + } + } + + mutex_unlock(&tc35892_gpio->irq_lock); +} + +static void tc35892_gpio_irq_mask(unsigned int irq) +{ + struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq); + int offset = irq - tc35892_gpio->irq_base; + int regoffset = offset / 8; + int mask = 1 << (offset % 8); + + tc35892_gpio->regs[REG_IE][regoffset] &= ~mask; +} + +static void tc35892_gpio_irq_unmask(unsigned int irq) +{ + struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq); + int offset = irq - tc35892_gpio->irq_base; + int regoffset = offset / 8; + int mask = 1 << (offset % 8); + + tc35892_gpio->regs[REG_IE][regoffset] |= mask; +} + +static struct irq_chip tc35892_gpio_irq_chip = { + .name = "tc35892-gpio", + .bus_lock = tc35892_gpio_irq_lock, + .bus_sync_unlock = tc35892_gpio_irq_sync_unlock, + .mask = tc35892_gpio_irq_mask, + .unmask = tc35892_gpio_irq_unmask, + .set_type = tc35892_gpio_irq_set_type, +}; + +static irqreturn_t tc35892_gpio_irq(int irq, void *dev) +{ + struct tc35892_gpio *tc35892_gpio = dev; + struct tc35892 *tc35892 = tc35892_gpio->tc35892; + u8 status[CACHE_NR_BANKS]; + int ret; + int i; + + ret = tc35892_block_read(tc35892, TC35892_GPIOMIS0, + ARRAY_SIZE(status), status); + if (ret < 0) + return IRQ_NONE; + + for (i = 0; i < ARRAY_SIZE(status); i++) { + unsigned int stat = status[i]; + if (!stat) + continue; + + while (stat) { + int bit = __ffs(stat); + int line = i * 8 + bit; + + handle_nested_irq(tc35892_gpio->irq_base + line); + stat &= ~(1 << bit); + } + + tc35892_reg_write(tc35892, TC35892_GPIOIC0 + i, status[i]); + } + + return IRQ_HANDLED; +} + +static int tc35892_gpio_irq_init(struct tc35892_gpio *tc35892_gpio) +{ + int base = tc35892_gpio->irq_base; + int irq; + + for (irq = base; irq < base + tc35892_gpio->chip.ngpio; irq++) { + set_irq_chip_data(irq, tc35892_gpio); + set_irq_chip_and_handler(irq, &tc35892_gpio_irq_chip, + handle_simple_irq); + set_irq_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + set_irq_noprobe(irq); +#endif + } + + return 0; +} + +static void tc35892_gpio_irq_remove(struct tc35892_gpio *tc35892_gpio) +{ + int base = tc35892_gpio->irq_base; + int irq; + + for (irq = base; irq < base + tc35892_gpio->chip.ngpio; irq++) { +#ifdef CONFIG_ARM + set_irq_flags(irq, 0); +#endif + set_irq_chip_and_handler(irq, NULL, NULL); + set_irq_chip_data(irq, NULL); + } +} + +static int __devinit tc35892_gpio_probe(struct platform_device *pdev) +{ + struct tc35892 *tc35892 = dev_get_drvdata(pdev->dev.parent); + struct tc35892_gpio_platform_data *pdata; + struct tc35892_gpio *tc35892_gpio; + int ret; + int irq; + + pdata = tc35892->pdata->gpio; + if (!pdata) + return -ENODEV; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + tc35892_gpio = kzalloc(sizeof(struct tc35892_gpio), GFP_KERNEL); + if (!tc35892_gpio) + return -ENOMEM; + + mutex_init(&tc35892_gpio->irq_lock); + + tc35892_gpio->dev = &pdev->dev; + tc35892_gpio->tc35892 = tc35892; + + tc35892_gpio->chip = template_chip; + tc35892_gpio->chip.ngpio = tc35892->num_gpio; + tc35892_gpio->chip.dev = &pdev->dev; + tc35892_gpio->chip.base = pdata->gpio_base; + + tc35892_gpio->irq_base = tc35892->irq_base + TC35892_INT_GPIO(0); + + /* Bring the GPIO module out of reset */ + ret = tc35892_set_bits(tc35892, TC35892_RSTCTRL, + TC35892_RSTCTRL_GPIRST, 0); + if (ret < 0) + goto out_free; + + ret = tc35892_gpio_irq_init(tc35892_gpio); + if (ret) + goto out_free; + + ret = request_threaded_irq(irq, NULL, tc35892_gpio_irq, IRQF_ONESHOT, + "tc35892-gpio", tc35892_gpio); + if (ret) { + dev_err(&pdev->dev, "unable to get irq: %d\n", ret); + goto out_removeirq; + } + + ret = gpiochip_add(&tc35892_gpio->chip); + if (ret) { + dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret); + goto out_freeirq; + } + + platform_set_drvdata(pdev, tc35892_gpio); + + return 0; + +out_freeirq: + free_irq(irq, tc35892_gpio); +out_removeirq: + tc35892_gpio_irq_remove(tc35892_gpio); +out_free: + kfree(tc35892_gpio); + return ret; +} + +static int __devexit tc35892_gpio_remove(struct platform_device *pdev) +{ + struct tc35892_gpio *tc35892_gpio = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + int ret; + + ret = gpiochip_remove(&tc35892_gpio->chip); + if (ret < 0) { + dev_err(tc35892_gpio->dev, + "unable to remove gpiochip: %d\n", ret); + return ret; + } + + free_irq(irq, tc35892_gpio); + tc35892_gpio_irq_remove(tc35892_gpio); + + platform_set_drvdata(pdev, NULL); + kfree(tc35892_gpio); + + return 0; +} + +static struct platform_driver tc35892_gpio_driver = { + .driver.name = "tc35892-gpio", + .driver.owner = THIS_MODULE, + .probe = tc35892_gpio_probe, + .remove = __devexit_p(tc35892_gpio_remove), +}; + +static int __init tc35892_gpio_init(void) +{ + return platform_driver_register(&tc35892_gpio_driver); +} +subsys_initcall(tc35892_gpio_init); + +static void __exit tc35892_gpio_exit(void) +{ + platform_driver_unregister(&tc35892_gpio_driver); +} +module_exit(tc35892_gpio_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TC35892 GPIO driver"); +MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent"); diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index b9f58ca..6703c6b 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -590,4 +590,17 @@ config TOUCHSCREEN_PCAP To compile this driver as a module, choose M here: the module will be called pcap_ts. + +config TOUCHSCREEN_TPS6507X + tristate "TPS6507x based touchscreens" + depends on I2C + help + Say Y here if you have a TPS6507x based touchscreen + controller. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called tps6507x_ts. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 8ad36ee..497964a 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -46,3 +46,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL) += atmel-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o +obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o diff --git a/drivers/input/touchscreen/tps6507x-ts.c b/drivers/input/touchscreen/tps6507x-ts.c new file mode 100644 index 0000000..5de80a1 --- /dev/null +++ b/drivers/input/touchscreen/tps6507x-ts.c @@ -0,0 +1,400 @@ +/* + * drivers/input/touchscreen/tps6507x_ts.c + * + * Touchscreen driver for the tps6507x chip. + * + * Copyright (c) 2009 RidgeRun (todd.fischer@ridgerun.com) + * + * Credits: + * + * Using code from tsc2007, MtekVision Co., Ltd. + * + * For licencing details see kernel-base/COPYING + * + * TPS65070, TPS65073, TPS650731, and TPS650732 support + * 10 bit touch screen interface. + */ + +#include <linux/module.h> +#include <linux/workqueue.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/platform_device.h> +#include <linux/mfd/tps6507x.h> +#include <linux/input/tps6507x-ts.h> +#include <linux/delay.h> + +#define TSC_DEFAULT_POLL_PERIOD 30 /* ms */ +#define TPS_DEFAULT_MIN_PRESSURE 0x30 +#define MAX_10BIT ((1 << 10) - 1) + +#define TPS6507X_ADCONFIG_CONVERT_TS (TPS6507X_ADCONFIG_AD_ENABLE | \ + TPS6507X_ADCONFIG_START_CONVERSION | \ + TPS6507X_ADCONFIG_INPUT_REAL_TSC) +#define TPS6507X_ADCONFIG_POWER_DOWN_TS (TPS6507X_ADCONFIG_INPUT_REAL_TSC) + +struct ts_event { + u16 x; + u16 y; + u16 pressure; +}; + +struct tps6507x_ts { + struct input_dev *input_dev; + struct device *dev; + char phys[32]; + struct workqueue_struct *wq; + struct delayed_work work; + unsigned polling; /* polling is active */ + struct ts_event tc; + struct tps6507x_dev *mfd; + u16 model; + unsigned pendown; + int irq; + void (*clear_penirq)(void); + unsigned long poll_period; /* ms */ + u16 min_pressure; + int vref; /* non-zero to leave vref on */ +}; + +static int tps6507x_read_u8(struct tps6507x_ts *tsc, u8 reg, u8 *data) +{ + int err; + + err = tsc->mfd->read_dev(tsc->mfd, reg, 1, data); + + if (err) + return err; + + return 0; +} + +static int tps6507x_write_u8(struct tps6507x_ts *tsc, u8 reg, u8 data) +{ + return tsc->mfd->write_dev(tsc->mfd, reg, 1, &data); +} + +static s32 tps6507x_adc_conversion(struct tps6507x_ts *tsc, + u8 tsc_mode, u16 *value) +{ + s32 ret; + u8 adc_status; + u8 result; + + /* Route input signal to A/D converter */ + + ret = tps6507x_write_u8(tsc, TPS6507X_REG_TSCMODE, tsc_mode); + if (ret) { + dev_err(tsc->dev, "TSC mode read failed\n"); + goto err; + } + + /* Start A/D conversion */ + + ret = tps6507x_write_u8(tsc, TPS6507X_REG_ADCONFIG, + TPS6507X_ADCONFIG_CONVERT_TS); + if (ret) { + dev_err(tsc->dev, "ADC config write failed\n"); + return ret; + } + + do { + ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADCONFIG, + &adc_status); + if (ret) { + dev_err(tsc->dev, "ADC config read failed\n"); + goto err; + } + } while (adc_status & TPS6507X_ADCONFIG_START_CONVERSION); + + ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADRESULT_2, &result); + if (ret) { + dev_err(tsc->dev, "ADC result 2 read failed\n"); + goto err; + } + + *value = (result & TPS6507X_REG_ADRESULT_2_MASK) << 8; + + ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADRESULT_1, &result); + if (ret) { + dev_err(tsc->dev, "ADC result 1 read failed\n"); + goto err; + } + + *value |= result; + + dev_dbg(tsc->dev, "TSC channel %d = 0x%X\n", tsc_mode, *value); + +err: + return ret; +} + +/* Need to call tps6507x_adc_standby() after using A/D converter for the + * touch screen interrupt to work properly. + */ + +static s32 tps6507x_adc_standby(struct tps6507x_ts *tsc) +{ + s32 ret; + s32 loops = 0; + u8 val; + + ret = tps6507x_write_u8(tsc, TPS6507X_REG_ADCONFIG, + TPS6507X_ADCONFIG_INPUT_TSC); + if (ret) + return ret; + + ret = tps6507x_write_u8(tsc, TPS6507X_REG_TSCMODE, + TPS6507X_TSCMODE_STANDBY); + if (ret) + return ret; + + ret = tps6507x_read_u8(tsc, TPS6507X_REG_INT, &val); + if (ret) + return ret; + + while (val & TPS6507X_REG_TSC_INT) { + mdelay(10); + ret = tps6507x_read_u8(tsc, TPS6507X_REG_INT, &val); + if (ret) + return ret; + loops++; + } + + return ret; +} + +static void tps6507x_ts_handler(struct work_struct *work) +{ + struct tps6507x_ts *tsc = container_of(work, + struct tps6507x_ts, work.work); + struct input_dev *input_dev = tsc->input_dev; + int pendown; + int schd; + int poll = 0; + s32 ret; + + ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_PRESSURE, + &tsc->tc.pressure); + if (ret) + goto done; + + pendown = tsc->tc.pressure > tsc->min_pressure; + + if (unlikely(!pendown && tsc->pendown)) { + dev_dbg(tsc->dev, "UP\n"); + input_report_key(input_dev, BTN_TOUCH, 0); + input_report_abs(input_dev, ABS_PRESSURE, 0); + input_sync(input_dev); + tsc->pendown = 0; + } + + if (pendown) { + + if (!tsc->pendown) { + dev_dbg(tsc->dev, "DOWN\n"); + input_report_key(input_dev, BTN_TOUCH, 1); + } else + dev_dbg(tsc->dev, "still down\n"); + + ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_X_POSITION, + &tsc->tc.x); + if (ret) + goto done; + + ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_Y_POSITION, + &tsc->tc.y); + if (ret) + goto done; + + input_report_abs(input_dev, ABS_X, tsc->tc.x); + input_report_abs(input_dev, ABS_Y, tsc->tc.y); + input_report_abs(input_dev, ABS_PRESSURE, tsc->tc.pressure); + input_sync(input_dev); + tsc->pendown = 1; + poll = 1; + } + +done: + /* always poll if not using interrupts */ + poll = 1; + + if (poll) { + schd = queue_delayed_work(tsc->wq, &tsc->work, + tsc->poll_period * HZ / 1000); + if (schd) + tsc->polling = 1; + else { + tsc->polling = 0; + dev_err(tsc->dev, "re-schedule failed"); + } + } else + tsc->polling = 0; + + ret = tps6507x_adc_standby(tsc); +} + +static int tps6507x_ts_probe(struct platform_device *pdev) +{ + int error; + struct tps6507x_ts *tsc; + struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent); + struct touchscreen_init_data *init_data; + struct input_dev *input_dev; + struct tps6507x_board *tps_board; + int schd; + + /** + * tps_board points to pmic related constants + * coming from the board-evm file. + */ + + tps_board = (struct tps6507x_board *)tps6507x_dev->dev->platform_data; + + if (!tps_board) { + dev_err(tps6507x_dev->dev, + "Could not find tps6507x platform data\n"); + return -EIO; + } + + /** + * init_data points to array of regulator_init structures + * coming from the board-evm file. + */ + + init_data = tps_board->tps6507x_ts_init_data; + + tsc = kzalloc(sizeof(struct tps6507x_ts), GFP_KERNEL); + if (!tsc) { + dev_err(tps6507x_dev->dev, "failed to allocate driver data\n"); + error = -ENOMEM; + goto err0; + } + + tps6507x_dev->ts = tsc; + tsc->mfd = tps6507x_dev; + tsc->dev = tps6507x_dev->dev; + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(tsc->dev, "Failed to allocate input device.\n"); + error = -ENOMEM; + goto err1; + } + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(input_dev, ABS_X, 0, MAX_10BIT, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, MAX_10BIT, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_10BIT, 0, 0); + + input_dev->name = "TPS6507x Touchscreen"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = tsc->dev; + + snprintf(tsc->phys, sizeof(tsc->phys), + "%s/input0", dev_name(tsc->dev)); + input_dev->phys = tsc->phys; + + dev_dbg(tsc->dev, "device: %s\n", input_dev->phys); + + input_set_drvdata(input_dev, tsc); + + tsc->input_dev = input_dev; + + INIT_DELAYED_WORK(&tsc->work, tps6507x_ts_handler); + tsc->wq = create_workqueue("TPS6507x Touchscreen"); + + if (init_data) { + tsc->poll_period = init_data->poll_period; + tsc->vref = init_data->vref; + tsc->min_pressure = init_data->min_pressure; + input_dev->id.vendor = init_data->vendor; + input_dev->id.product = init_data->product; + input_dev->id.version = init_data->version; + } else { + tsc->poll_period = TSC_DEFAULT_POLL_PERIOD; + tsc->min_pressure = TPS_DEFAULT_MIN_PRESSURE; + } + + error = tps6507x_adc_standby(tsc); + if (error) + goto err2; + + error = input_register_device(input_dev); + if (error) + goto err2; + + schd = queue_delayed_work(tsc->wq, &tsc->work, + tsc->poll_period * HZ / 1000); + + if (schd) + tsc->polling = 1; + else { + tsc->polling = 0; + dev_err(tsc->dev, "schedule failed"); + goto err2; + } + + return 0; + +err2: + cancel_delayed_work(&tsc->work); + flush_workqueue(tsc->wq); + destroy_workqueue(tsc->wq); + tsc->wq = 0; + input_free_device(input_dev); +err1: + kfree(tsc); + tps6507x_dev->ts = NULL; +err0: + return error; +} + +static int __devexit tps6507x_ts_remove(struct platform_device *pdev) +{ + struct tps6507x_dev *tps6507x_dev = platform_get_drvdata(pdev); + struct tps6507x_ts *tsc = tps6507x_dev->ts; + struct input_dev *input_dev = tsc->input_dev; + + if (!tsc) + return 0; + + cancel_delayed_work(&tsc->work); + flush_workqueue(tsc->wq); + destroy_workqueue(tsc->wq); + tsc->wq = 0; + + input_free_device(input_dev); + + tps6507x_dev->ts = NULL; + kfree(tsc); + + return 0; +} + +static struct platform_driver tps6507x_ts_driver = { + .driver = { + .name = "tps6507x-ts", + .owner = THIS_MODULE, + }, + .probe = tps6507x_ts_probe, + .remove = __devexit_p(tps6507x_ts_remove), +}; + +static int __init tps6507x_ts_init(void) +{ + return platform_driver_register(&tps6507x_ts_driver); +} +module_init(tps6507x_ts_init); + +static void __exit tps6507x_ts_exit(void) +{ + platform_driver_unregister(&tps6507x_ts_driver); +} +module_exit(tps6507x_ts_exit); + +MODULE_AUTHOR("Todd Fischer <todd.fischer@ridgerun.com>"); +MODULE_DESCRIPTION("TPS6507x - TouchScreen driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:tps6507x-tsc"); diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c index 405d2d5..2c65a2c57 100644 --- a/drivers/mfd/88pm860x-core.c +++ b/drivers/mfd/88pm860x-core.c @@ -566,7 +566,7 @@ out: return ret; } -static void __devexit device_irq_exit(struct pm860x_chip *chip) +static void device_irq_exit(struct pm860x_chip *chip) { if (chip->core_irq) free_irq(chip->core_irq, chip); @@ -703,7 +703,7 @@ out: return; } -int pm860x_device_init(struct pm860x_chip *chip, +int __devinit pm860x_device_init(struct pm860x_chip *chip, struct pm860x_platform_data *pdata) { chip->core_irq = 0; @@ -731,7 +731,7 @@ int pm860x_device_init(struct pm860x_chip *chip, return 0; } -void pm860x_device_exit(struct pm860x_chip *chip) +void __devexit pm860x_device_exit(struct pm860x_chip *chip) { device_irq_exit(chip); mfd_remove_devices(chip->dev); diff --git a/drivers/mfd/88pm860x-i2c.c b/drivers/mfd/88pm860x-i2c.c index 4a6e718..c933b64 100644 --- a/drivers/mfd/88pm860x-i2c.c +++ b/drivers/mfd/88pm860x-i2c.c @@ -200,8 +200,8 @@ static int __devexit pm860x_remove(struct i2c_client *client) pm860x_device_exit(chip); i2c_unregister_device(chip->companion); - i2c_set_clientdata(chip->companion, NULL); i2c_set_clientdata(chip->client, NULL); + i2c_set_clientdata(client, NULL); kfree(chip); return 0; } diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 3c6a986..9da0e50 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -2,8 +2,14 @@ # Multifunction miscellaneous devices # -menu "Multifunction device drivers" +menuconfig MFD_SUPPORT + bool "Multifunction device drivers" depends on HAS_IOMEM + default y + help + Configure MFD device drivers. + +if MFD_SUPPORT config MFD_CORE tristate @@ -116,6 +122,18 @@ config TPS65010 This driver can also be built as a module. If so, the module will be called tps65010. +config TPS6507X + tristate "TPS6507x Power Management / Touch Screen chips" + select MFD_CORE + depends on I2C + help + If you say yes here you get support for the TPS6507x series of + Power Management / Touch Screen chips. These include voltage + regulators, lithium ion/polymer battery charging, touch screen + and other features that are often used in portable devices. + This driver can also be built as a module. If so, the module + will be called tps6507x. + config MENELAUS bool "Texas Instruments TWL92330/Menelaus PM chip" depends on I2C=y && ARCH_OMAP2 @@ -159,6 +177,17 @@ config TWL4030_CODEC select MFD_CORE default n +config MFD_TC35892 + bool "Support Toshiba TC35892" + depends on I2C=y && GENERIC_HARDIRQS + select MFD_CORE + help + Support for the Toshiba TC35892 I/O Expander. + + This driver provides common support for accessing the device, + additional drivers must be enabled in order to use the + functionality of the device. + config MFD_TMIO bool default n @@ -351,9 +380,19 @@ config PCF50633_GPIO Say yes here if you want to include support GPIO for pins on the PCF50633 chip. +config ABX500_CORE + bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions" + default y if ARCH_U300 + help + Say yes here if you have the ABX500 Mixed Signal IC family + chips. This core driver expose register access functions. + Functionality specific drivers using these functions can + remain unchanged when IC changes. Binding of the functions to + actual register access is done by the IC core driver. + config AB3100_CORE bool "ST-Ericsson AB3100 Mixed Signal Circuit core functions" - depends on I2C=y + depends on I2C=y && ABX500_CORE default y if ARCH_U300 help Select this to enable the AB3100 Mixed Signal IC core @@ -381,15 +420,30 @@ config EZX_PCAP This enables the PCAP ASIC present on EZX Phones. This is needed for MMC, TouchScreen, Sound, USB, etc.. -config AB4500_CORE - tristate "ST-Ericsson's AB4500 Mixed Signal Power management chip" - depends on SPI +config AB8500_CORE + bool "ST-Ericsson AB8500 Mixed Signal Power Management chip" + depends on SPI=y && GENERIC_HARDIRQS + select MFD_CORE help - Select this option to enable access to AB4500 power management + Select this option to enable access to AB8500 power management chip. This connects to U8500 on the SSP/SPI bus and exports read/write functions for the devices to get access to this chip. This chip embeds various other multimedia funtionalities as well. +config AB3550_CORE + bool "ST-Ericsson AB3550 Mixed Signal Circuit core functions" + select MFD_CORE + depends on I2C=y && GENERIC_HARDIRQS && ABX500_CORE + help + Select this to enable the AB3550 Mixed Signal IC core + functionality. This connects to a AB3550 on the I2C bus + and expose a number of symbols needed for dependent devices + to read and write registers and subscribe to events from + this multi-functional IC. This is needed to use other features + of the AB3550 such as battery-backed RTC, charging control, + LEDs, vibrator, system power and temperature, power management + and ALSA sound. + config MFD_TIMBERDALE tristate "Support for the Timberdale FPGA" select MFD_CORE @@ -409,7 +463,26 @@ config LPC_SCH LPC bridge function of the Intel SCH provides support for System Management Bus and General Purpose I/O. -endmenu +config MFD_RDC321X + tristate "Support for RDC-R321x southbridge" + select MFD_CORE + depends on PCI + help + Say yes here if you want to have support for the RDC R-321x SoC + southbridge which provides access to GPIOs and Watchdog using the + southbridge PCI device configuration space. + +config MFD_JANZ_CMODIO + tristate "Support for Janz CMOD-IO PCI MODULbus Carrier Board" + select MFD_CORE + depends on PCI + help + This is the core driver for the Janz CMOD-IO PCI MODULbus + carrier board. This device is a PCI to MODULbus bridge which may + host many different types of MODULbus daughterboards, including + CAN and GPIO controllers. + +endif # MFD_SUPPORT menu "Multimedia Capabilities Port drivers" depends on ARCH_SA1100 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 87935f9..fb503e7 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o +obj-$(CONFIG_MFD_TC35892) += tc35892.o obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o tmio_core.o obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o tmio_core.o @@ -29,6 +30,7 @@ obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o obj-$(CONFIG_MFD_WM8994) += wm8994-core.o wm8994-irq.o obj-$(CONFIG_TPS65010) += tps65010.o +obj-$(CONFIG_TPS6507X) += tps6507x.o obj-$(CONFIG_MENELAUS) += menelaus.o obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o @@ -55,12 +57,17 @@ obj-$(CONFIG_PMIC_DA903X) += da903x.o max8925-objs := max8925-core.o max8925-i2c.o obj-$(CONFIG_MFD_MAX8925) += max8925.o -obj-$(CONFIG_MFD_PCF50633) += pcf50633-core.o +pcf50633-objs := pcf50633-core.o pcf50633-irq.o +obj-$(CONFIG_MFD_PCF50633) += pcf50633.o obj-$(CONFIG_PCF50633_ADC) += pcf50633-adc.o obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o +obj-$(CONFIG_ABX500_CORE) += abx500-core.o obj-$(CONFIG_AB3100_CORE) += ab3100-core.o obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o -obj-$(CONFIG_AB4500_CORE) += ab4500-core.o +obj-$(CONFIG_AB3550_CORE) += ab3550-core.o +obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-spi.o obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o obj-$(CONFIG_PMIC_ADP5520) += adp5520.o -obj-$(CONFIG_LPC_SCH) += lpc_sch.o
\ No newline at end of file +obj-$(CONFIG_LPC_SCH) += lpc_sch.o +obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o +obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o diff --git a/drivers/mfd/ab3100-core.c b/drivers/mfd/ab3100-core.c index e4ca590..53ebfee 100644 --- a/drivers/mfd/ab3100-core.c +++ b/drivers/mfd/ab3100-core.c @@ -19,7 +19,7 @@ #include <linux/debugfs.h> #include <linux/seq_file.h> #include <linux/uaccess.h> -#include <linux/mfd/ab3100.h> +#include <linux/mfd/abx500.h> /* These are the only registers inside AB3100 used in this main file */ @@ -59,24 +59,15 @@ * The AB3100 is usually assigned address 0x48 (7-bit) * The chip is defined in the platform i2c_board_data section. */ - -u8 ab3100_get_chip_type(struct ab3100 *ab3100) +static int ab3100_get_chip_id(struct device *dev) { - u8 chip = ABUNKNOWN; - - switch (ab3100->chip_id & 0xf0) { - case 0xa0: - chip = AB3000; - break; - case 0xc0: - chip = AB3100; - break; - } - return chip; + struct ab3100 *ab3100 = dev_get_drvdata(dev->parent); + + return (int)ab3100->chip_id; } -EXPORT_SYMBOL(ab3100_get_chip_type); -int ab3100_set_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 regval) +static int ab3100_set_register_interruptible(struct ab3100 *ab3100, + u8 reg, u8 regval) { u8 regandval[2] = {reg, regval}; int err; @@ -108,8 +99,14 @@ int ab3100_set_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 regval) mutex_unlock(&ab3100->access_mutex); return err; } -EXPORT_SYMBOL(ab3100_set_register_interruptible); +static int set_register_interruptible(struct device *dev, + u8 bank, u8 reg, u8 value) +{ + struct ab3100 *ab3100 = dev_get_drvdata(dev->parent); + + return ab3100_set_register_interruptible(ab3100, reg, value); +} /* * The test registers exist at an I2C bus address up one @@ -148,8 +145,8 @@ static int ab3100_set_test_register_interruptible(struct ab3100 *ab3100, return err; } - -int ab3100_get_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 *regval) +static int ab3100_get_register_interruptible(struct ab3100 *ab3100, + u8 reg, u8 *regval) { int err; @@ -203,10 +200,16 @@ int ab3100_get_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 *regval) mutex_unlock(&ab3100->access_mutex); return err; } -EXPORT_SYMBOL(ab3100_get_register_interruptible); +static int get_register_interruptible(struct device *dev, u8 bank, u8 reg, + u8 *value) +{ + struct ab3100 *ab3100 = dev_get_drvdata(dev->parent); + + 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) { int err; @@ -260,10 +263,17 @@ int ab3100_get_register_page_interruptible(struct ab3100 *ab3100, mutex_unlock(&ab3100->access_mutex); return err; } -EXPORT_SYMBOL(ab3100_get_register_page_interruptible); +static int get_register_page_interruptible(struct device *dev, u8 bank, + u8 first_reg, u8 *regvals, u8 numregs) +{ + struct ab3100 *ab3100 = dev_get_drvdata(dev->parent); + + 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 regandval[2] = {reg, 0}; @@ -331,8 +341,15 @@ int ab3100_mask_and_set_register_interruptible(struct ab3100 *ab3100, mutex_unlock(&ab3100->access_mutex); return err; } -EXPORT_SYMBOL(ab3100_mask_and_set_register_interruptible); +static int mask_and_set_register_interruptible(struct device *dev, u8 bank, + u8 reg, u8 bitmask, u8 bitvalues) +{ + struct ab3100 *ab3100 = dev_get_drvdata(dev->parent); + + return ab3100_mask_and_set_register_interruptible(ab3100, + reg, bitmask, (bitmask & bitvalues)); +} /* * Register a simple callback for handling any AB3100 events. @@ -357,15 +374,27 @@ int ab3100_event_unregister(struct ab3100 *ab3100, EXPORT_SYMBOL(ab3100_event_unregister); -int ab3100_event_registers_startup_state_get(struct ab3100 *ab3100, - u32 *fatevent) +static int ab3100_event_registers_startup_state_get(struct device *dev, + u8 *event) { + struct ab3100 *ab3100 = dev_get_drvdata(dev->parent); if (!ab3100->startup_events_read) return -EAGAIN; /* Try again later */ - *fatevent = ab3100->startup_events; + memcpy(event, ab3100->startup_events, 3); return 0; } -EXPORT_SYMBOL(ab3100_event_registers_startup_state_get); + +static struct abx500_ops ab3100_ops = { + .get_chip_id = ab3100_get_chip_id, + .set_register = set_register_interruptible, + .get_register = get_register_interruptible, + .get_register_page = get_register_page_interruptible, + .set_register_page = NULL, + .mask_and_set_register = mask_and_set_register_interruptible, + .event_registers_startup_state_get = + ab3100_event_registers_startup_state_get, + .startup_irq_enabled = NULL, +}; /* * This is a threaded interrupt handler so we can make some @@ -390,7 +419,9 @@ static irqreturn_t ab3100_irq_handler(int irq, void *data) event_regs[2]; if (!ab3100->startup_events_read) { - ab3100->startup_events = fatevent; + ab3100->startup_events[0] = event_regs[0]; + ab3100->startup_events[1] = event_regs[1]; + ab3100->startup_events[2] = event_regs[2]; ab3100->startup_events_read = true; } /* @@ -703,7 +734,8 @@ static int __init ab3100_setup(struct ab3100 *ab3100) dev_warn(ab3100->dev, "AB3100 P1E variant detected, " "forcing chip to 32KHz\n"); - err = ab3100_set_test_register_interruptible(ab3100, 0x02, 0x08); + err = ab3100_set_test_register_interruptible(ab3100, + 0x02, 0x08); } exit_no_setup: @@ -898,6 +930,10 @@ static int __init ab3100_probe(struct i2c_client *client, if (err) goto exit_no_irq; + err = abx500_register_ops(&client->dev, &ab3100_ops); + if (err) + goto exit_no_ops; + /* Set parent and a pointer back to the container in device data */ for (i = 0; i < ARRAY_SIZE(ab3100_platform_devs); i++) { ab3100_platform_devs[i]->dev.parent = @@ -915,11 +951,13 @@ static int __init ab3100_probe(struct i2c_client *client, return 0; + exit_no_ops: exit_no_irq: exit_no_setup: i2c_unregister_device(ab3100->testreg_client); exit_no_testreg_client: exit_no_detect: + i2c_set_clientdata(client, NULL); kfree(ab3100); return err; } @@ -941,6 +979,7 @@ static int __exit ab3100_remove(struct i2c_client *client) * their notifiers so deactivate IRQ */ free_irq(client->irq, ab3100); + i2c_set_clientdata(client, NULL); kfree(ab3100); return 0; } diff --git a/drivers/mfd/ab3100-otp.c b/drivers/mfd/ab3100-otp.c index 2d14655..63d2b72 100644 --- a/drivers/mfd/ab3100-otp.c +++ b/drivers/mfd/ab3100-otp.c @@ -12,7 +12,7 @@ #include <linux/slab.h> #include <linux/init.h> #include <linux/platform_device.h> -#include <linux/mfd/ab3100.h> +#include <linux/mfd/abx500.h> #include <linux/debugfs.h> #include <linux/seq_file.h> @@ -30,7 +30,6 @@ /** * struct ab3100_otp * @dev containing device - * @ab3100 a pointer to the parent ab3100 device struct * @locked whether the OTP is locked, after locking, no more bits * can be changed but before locking it is still possible * to change bits from 1->0. @@ -49,7 +48,6 @@ */ struct ab3100_otp { struct device *dev; - struct ab3100 *ab3100; bool locked; u32 freq; bool paf; @@ -63,19 +61,19 @@ struct ab3100_otp { static int __init ab3100_otp_read(struct ab3100_otp *otp) { - struct ab3100 *ab = otp->ab3100; u8 otpval[8]; u8 otpp; int err; - err = ab3100_get_register_interruptible(ab, AB3100_OTPP, &otpp); + err = abx500_get_register_interruptible(otp->dev, 0, + AB3100_OTPP, &otpp); if (err) { dev_err(otp->dev, "unable to read OTPP register\n"); return err; } - err = ab3100_get_register_page_interruptible(ab, AB3100_OTP0, - otpval, 8); + err = abx500_get_register_page_interruptible(otp->dev, 0, + AB3100_OTP0, otpval, 8); if (err) { dev_err(otp->dev, "unable to read OTP register page\n"); return err; @@ -197,7 +195,6 @@ static int __init ab3100_otp_probe(struct platform_device *pdev) otp->dev = &pdev->dev; /* Replace platform data coming in with a local struct */ - otp->ab3100 = platform_get_drvdata(pdev); platform_set_drvdata(pdev, otp); err = ab3100_otp_read(otp); diff --git a/drivers/mfd/ab3550-core.c b/drivers/mfd/ab3550-core.c new file mode 100644 index 0000000..1060f8e --- /dev/null +++ b/drivers/mfd/ab3550-core.c @@ -0,0 +1,1401 @@ +/* + * Copyright (C) 2007-2010 ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + * Low-level core for exclusive access to the AB3550 IC on the I2C bus + * and some basic chip-configuration. + * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com> + * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com> + * Author: Mattias Wallin <mattias.wallin@stericsson.com> + * Author: Rickard Andersson <rickard.andersson@stericsson.com> + */ + +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/random.h> +#include <linux/workqueue.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/uaccess.h> +#include <linux/mfd/abx500.h> +#include <linux/list.h> +#include <linux/bitops.h> +#include <linux/spinlock.h> +#include <linux/mfd/core.h> + +#define AB3550_NAME_STRING "ab3550" +#define AB3550_ID_FORMAT_STRING "AB3550 %s" +#define AB3550_NUM_BANKS 2 +#define AB3550_NUM_EVENT_REG 5 + +/* These are the only registers inside AB3550 used in this main file */ + +/* Chip ID register */ +#define AB3550_CID_REG 0x20 + +/* Interrupt event registers */ +#define AB3550_EVENT_BANK 0 +#define AB3550_EVENT_REG 0x22 + +/* Read/write operation values. */ +#define AB3550_PERM_RD (0x01) +#define AB3550_PERM_WR (0x02) + +/* Read/write permissions. */ +#define AB3550_PERM_RO (AB3550_PERM_RD) +#define AB3550_PERM_RW (AB3550_PERM_RD | AB3550_PERM_WR) + +/** + * struct ab3550 + * @access_mutex: lock out concurrent accesses to the AB registers + * @i2c_client: I2C client for this chip + * @chip_name: name of this chip variant + * @chip_id: 8 bit chip ID for this chip variant + * @mask_work: a worker for writing to mask registers + * @event_lock: a lock to protect the event_mask + * @event_mask: a local copy of the mask event registers + * @startup_events: a copy of the first reading of the event registers + * @startup_events_read: whether the first events have been read + */ +struct ab3550 { + struct mutex access_mutex; + struct i2c_client *i2c_client[AB3550_NUM_BANKS]; + char chip_name[32]; + u8 chip_id; + struct work_struct mask_work; + spinlock_t event_lock; + u8 event_mask[AB3550_NUM_EVENT_REG]; + u8 startup_events[AB3550_NUM_EVENT_REG]; + bool startup_events_read; +#ifdef CONFIG_DEBUG_FS + unsigned int debug_bank; + unsigned int debug_address; +#endif +}; + +/** + * struct ab3550_reg_range + * @first: the first address of the range + * @last: the last address of the range + * @perm: access permissions for the range + */ +struct ab3550_reg_range { + u8 first; + u8 last; + u8 perm; +}; + +/** + * struct ab3550_reg_ranges + * @count: the number of ranges in the list + * @range: the list of register ranges + */ +struct ab3550_reg_ranges { + u8 count; + const struct ab3550_reg_range *range; +}; + +/* + * Permissible register ranges for reading and writing per device and bank. + * + * The ranges must be listed in increasing address order, and no overlaps are + * allowed. It is assumed that write permission implies read permission + * (i.e. only RO and RW permissions should be used). Ranges with write + * permission must not be split up. + */ + +#define NO_RANGE {.count = 0, .range = NULL,} + +static struct +ab3550_reg_ranges ab3550_reg_ranges[AB3550_NUM_DEVICES][AB3550_NUM_BANKS] = { + [AB3550_DEVID_DAC] = { + NO_RANGE, + { + .count = 2, + .range = (struct ab3550_reg_range[]) { + { + .first = 0xb0, + .last = 0xba, + .perm = AB3550_PERM_RW, + }, + { + .first = 0xbc, + .last = 0xc3, + .perm = AB3550_PERM_RW, + }, + }, + }, + }, + [AB3550_DEVID_LEDS] = { + NO_RANGE, + { + .count = 2, + .range = (struct ab3550_reg_range[]) { + { + .first = 0x5a, + .last = 0x88, + .perm = AB3550_PERM_RW, + }, + { + .first = 0x8a, + .last = 0xad, + .perm = AB3550_PERM_RW, + }, + } + }, + }, + [AB3550_DEVID_POWER] = { + { + .count = 1, + .range = (struct ab3550_reg_range[]) { + { + .first = 0x21, + .last = 0x21, + .perm = AB3550_PERM_RO, + }, + } + }, + NO_RANGE, + }, + [AB3550_DEVID_REGULATORS] = { + { + .count = 1, + .range = (struct ab3550_reg_range[]) { + { + .first = 0x69, + .last = 0xa3, + .perm = AB3550_PERM_RW, + }, + } + }, + { + .count = 1, + .range = (struct ab3550_reg_range[]) { + { + .first = 0x14, + .last = 0x16, + .perm = AB3550_PERM_RW, + }, + } + }, + }, + [AB3550_DEVID_SIM] = { + { + .count = 1, + .range = (struct ab3550_reg_range[]) { + { + .first = 0x21, + .last = 0x21, + .perm = AB3550_PERM_RO, + }, + } + }, + { + .count = 1, + .range = (struct ab3550_reg_range[]) { + { + .first = 0x14, + .last = 0x17, + .perm = AB3550_PERM_RW, + }, + } + + }, + }, + [AB3550_DEVID_UART] = { + NO_RANGE, + NO_RANGE, + }, + [AB3550_DEVID_RTC] = { + { + .count = 1, + .range = (struct ab3550_reg_range[]) { + { + .first = 0x00, + .last = 0x0c, + .perm = AB3550_PERM_RW, + }, + } + }, + NO_RANGE, + }, + [AB3550_DEVID_CHARGER] = { + { + .count = 2, + .range = (struct ab3550_reg_range[]) { + { + .first = 0x10, + .last = 0x1a, + .perm = AB3550_PERM_RW, + }, + { + .first = 0x21, + .last = 0x21, + .perm = AB3550_PERM_RO, + }, + } + }, + NO_RANGE, + }, + [AB3550_DEVID_ADC] = { + NO_RANGE, + { + .count = 1, + .range = (struct ab3550_reg_range[]) { + { + .first = 0x20, + .last = 0x56, + .perm = AB3550_PERM_RW, + }, + + } + }, + }, + [AB3550_DEVID_FUELGAUGE] = { + { + .count = 1, + .range = (struct ab3550_reg_range[]) { + { + .first = 0x21, + .last = 0x21, + .perm = AB3550_PERM_RO, + }, + } + }, + { + .count = 1, + .range = (struct ab3550_reg_range[]) { + { + .first = 0x00, + .last = 0x0e, + .perm = AB3550_PERM_RW, + }, + } + }, + }, + [AB3550_DEVID_VIBRATOR] = { + NO_RANGE, + { + .count = 1, + .range = (struct ab3550_reg_range[]) { + { + .first = 0x10, + .last = 0x13, + .perm = AB3550_PERM_RW, + }, + + } + }, + }, + [AB3550_DEVID_CODEC] = { + { + .count = 2, + .range = (struct ab3550_reg_range[]) { + { + .first = 0x31, + .last = 0x63, + .perm = AB3550_PERM_RW, + }, + { + .first = 0x65, + .last = 0x68, + .perm = AB3550_PERM_RW, + }, + } + }, + NO_RANGE, + }, +}; + +static struct mfd_cell ab3550_devs[AB3550_NUM_DEVICES] = { + [AB3550_DEVID_DAC] = { + .name = "ab3550-dac", + .id = AB3550_DEVID_DAC, + .num_resources = 0, + }, + [AB3550_DEVID_LEDS] = { + .name = "ab3550-leds", + .id = AB3550_DEVID_LEDS, + }, + [AB3550_DEVID_POWER] = { + .name = "ab3550-power", + .id = AB3550_DEVID_POWER, + }, + [AB3550_DEVID_REGULATORS] = { + .name = "ab3550-regulators", + .id = AB3550_DEVID_REGULATORS, + }, + [AB3550_DEVID_SIM] = { + .name = "ab3550-sim", + .id = AB3550_DEVID_SIM, + }, + [AB3550_DEVID_UART] = { + .name = "ab3550-uart", + .id = AB3550_DEVID_UART, + }, + [AB3550_DEVID_RTC] = { + .name = "ab3550-rtc", + .id = AB3550_DEVID_RTC, + }, + [AB3550_DEVID_CHARGER] = { + .name = "ab3550-charger", + .id = AB3550_DEVID_CHARGER, + }, + [AB3550_DEVID_ADC] = { + .name = "ab3550-adc", + .id = AB3550_DEVID_ADC, + .num_resources = 10, + .resources = (struct resource[]) { + { + .name = "TRIGGER-0", + .flags = IORESOURCE_IRQ, + .start = 16, + .end = 16, + }, + { + .name = "TRIGGER-1", + .flags = IORESOURCE_IRQ, + .start = 17, + .end = 17, + }, + { + .name = "TRIGGER-2", + .flags = IORESOURCE_IRQ, + .start = 18, + .end = 18, + }, + { + .name = "TRIGGER-3", + .flags = IORESOURCE_IRQ, + .start = 19, + .end = 19, + }, + { + .name = "TRIGGER-4", + .flags = IORESOURCE_IRQ, + .start = 20, + .end = 20, + }, + { + .name = "TRIGGER-5", + .flags = IORESOURCE_IRQ, + .start = 21, + .end = 21, + }, + { + .name = "TRIGGER-6", + .flags = IORESOURCE_IRQ, + .start = 22, + .end = 22, + }, + { + .name = "TRIGGER-7", + .flags = IORESOURCE_IRQ, + .start = 23, + .end = 23, + }, + { + .name = "TRIGGER-VBAT-TXON", + .flags = IORESOURCE_IRQ, + .start = 13, + .end = 13, + }, + { + .name = "TRIGGER-VBAT", + .flags = IORESOURCE_IRQ, + .start = 12, + .end = 12, + }, + }, + }, + [AB3550_DEVID_FUELGAUGE] = { + .name = "ab3550-fuelgauge", + .id = AB3550_DEVID_FUELGAUGE, + }, + [AB3550_DEVID_VIBRATOR] = { + .name = "ab3550-vibrator", + .id = AB3550_DEVID_VIBRATOR, + }, + [AB3550_DEVID_CODEC] = { + .name = "ab3550-codec", + .id = AB3550_DEVID_CODEC, + }, +}; + +/* + * I2C transactions with error messages. + */ +static int ab3550_i2c_master_send(struct ab3550 *ab, u8 bank, u8 *data, + u8 count) +{ + int err; + + err = i2c_master_send(ab->i2c_client[bank], data, count); + if (err < 0) { + dev_err(&ab->i2c_client[0]->dev, "send error: %d\n", err); + return err; + } + return 0; +} + +static int ab3550_i2c_master_recv(struct ab3550 *ab, u8 bank, u8 *data, + u8 count) +{ + int err; + + err = i2c_master_recv(ab->i2c_client[bank], data, count); + if (err < 0) { + dev_err(&ab->i2c_client[0]->dev, "receive error: %d\n", err); + return err; + } + return 0; +} + +/* + * Functionality for getting/setting register values. + */ +static int get_register_interruptible(struct ab3550 *ab, u8 bank, u8 reg, + u8 *value) +{ + int err; + + err = mutex_lock_interruptible(&ab->access_mutex); + if (err) + return err; + + err = ab3550_i2c_master_send(ab, bank, ®, 1); + if (!err) + err = ab3550_i2c_master_recv(ab, bank, value, 1); + + mutex_unlock(&ab->access_mutex); + return err; +} + +static int get_register_page_interruptible(struct ab3550 *ab, u8 bank, + u8 first_reg, u8 *regvals, u8 numregs) +{ + int err; + + err = mutex_lock_interruptible(&ab->access_mutex); + if (err) + return err; + + err = ab3550_i2c_master_send(ab, bank, &first_reg, 1); + if (!err) + err = ab3550_i2c_master_recv(ab, bank, regvals, numregs); + + mutex_unlock(&ab->access_mutex); + return err; +} + +static int mask_and_set_register_interruptible(struct ab3550 *ab, u8 bank, + u8 reg, u8 bitmask, u8 bitvalues) +{ + int err = 0; + + if (likely(bitmask)) { + u8 reg_bits[2] = {reg, 0}; + + err = mutex_lock_interruptible(&ab->access_mutex); + if (err) + return err; + + if (bitmask == 0xFF) /* No need to read in this case. */ + reg_bits[1] = bitvalues; + else { /* Read and modify the register value. */ + u8 bits; + + err = ab3550_i2c_master_send(ab, bank, ®, 1); + if (err) + goto unlock_and_return; + err = ab3550_i2c_master_recv(ab, bank, &bits, 1); + if (err) + goto unlock_and_return; + reg_bits[1] = ((~bitmask & bits) | + (bitmask & bitvalues)); + } + /* Write the new value. */ + err = ab3550_i2c_master_send(ab, bank, reg_bits, 2); +unlock_and_return: + mutex_unlock(&ab->access_mutex); + } + return err; +} + +/* + * Read/write permission checking functions. + */ +static bool page_write_allowed(const struct ab3550_reg_ranges *ranges, + u8 first_reg, u8 last_reg) +{ + u8 i; + + if (last_reg < first_reg) + return false; + + for (i = 0; i < ranges->count; i++) { + if (first_reg < ranges->range[i].first) + break; + if ((last_reg <= ranges->range[i].last) && + (ranges->range[i].perm & AB3550_PERM_WR)) + return true; + } + return false; +} + +static bool reg_write_allowed(const struct ab3550_reg_ranges *ranges, u8 reg) +{ + return page_write_allowed(ranges, reg, reg); +} + +static bool page_read_allowed(const struct ab3550_reg_ranges *ranges, + u8 first_reg, u8 last_reg) +{ + u8 i; + + if (last_reg < first_reg) + return false; + /* Find the range (if it exists in the list) that includes first_reg. */ + for (i = 0; i < ranges->count; i++) { + if (first_reg < ranges->range[i].first) + return false; + if (first_reg <= ranges->range[i].last) + break; + } + /* Make sure that the entire range up to and including last_reg is + * readable. This may span several of the ranges in the list. + */ + while ((i < ranges->count) && + (ranges->range[i].perm & AB3550_PERM_RD)) { + if (last_reg <= ranges->range[i].last) + return true; + if ((++i >= ranges->count) || + (ranges->range[i].first != + (ranges->range[i - 1].last + 1))) { + break; + } + } + return false; +} + +static bool reg_read_allowed(const struct ab3550_reg_ranges *ranges, u8 reg) +{ + return page_read_allowed(ranges, reg, reg); +} + +/* + * The exported register access functionality. + */ +int ab3550_get_chip_id(struct device *dev) +{ + struct ab3550 *ab = dev_get_drvdata(dev->parent); + return (int)ab->chip_id; +} + +int ab3550_mask_and_set_register_interruptible(struct device *dev, u8 bank, + u8 reg, u8 bitmask, u8 bitvalues) +{ + struct ab3550 *ab; + struct platform_device *pdev = to_platform_device(dev); + + if ((AB3550_NUM_BANKS <= bank) || + !reg_write_allowed(&ab3550_reg_ranges[pdev->id][bank], reg)) + return -EINVAL; + + ab = dev_get_drvdata(dev->parent); + return mask_and_set_register_interruptible(ab, bank, reg, + bitmask, bitvalues); +} + +int ab3550_set_register_interruptible(struct device *dev, u8 bank, u8 reg, + u8 value) +{ + return ab3550_mask_and_set_register_interruptible(dev, bank, reg, 0xFF, + value); +} + +int ab3550_get_register_interruptible(struct device *dev, u8 bank, u8 reg, + u8 *value) +{ + struct ab3550 *ab; + struct platform_device *pdev = to_platform_device(dev); + + if ((AB3550_NUM_BANKS <= bank) || + !reg_read_allowed(&ab3550_reg_ranges[pdev->id][bank], reg)) + return -EINVAL; + + ab = dev_get_drvdata(dev->parent); + return get_register_interruptible(ab, bank, reg, value); +} + +int ab3550_get_register_page_interruptible(struct device *dev, u8 bank, + u8 first_reg, u8 *regvals, u8 numregs) +{ + struct ab3550 *ab; + struct platform_device *pdev = to_platform_device(dev); + + if ((AB3550_NUM_BANKS <= bank) || + !page_read_allowed(&ab3550_reg_ranges[pdev->id][bank], + first_reg, (first_reg + numregs - 1))) + return -EINVAL; + + ab = dev_get_drvdata(dev->parent); + return get_register_page_interruptible(ab, bank, first_reg, regvals, + numregs); +} + +int ab3550_event_registers_startup_state_get(struct device *dev, u8 *event) +{ + struct ab3550 *ab; + + ab = dev_get_drvdata(dev->parent); + if (!ab->startup_events_read) + return -EAGAIN; /* Try again later */ + + memcpy(event, ab->startup_events, AB3550_NUM_EVENT_REG); + return 0; +} + +int ab3550_startup_irq_enabled(struct device *dev, unsigned int irq) +{ + struct ab3550 *ab; + struct ab3550_platform_data *plf_data; + bool val; + + ab = get_irq_chip_data(irq); + plf_data = ab->i2c_client[0]->dev.platform_data; + irq -= plf_data->irq.base; + val = ((ab->startup_events[irq / 8] & BIT(irq % 8)) != 0); + + return val; +} + +static struct abx500_ops ab3550_ops = { + .get_chip_id = ab3550_get_chip_id, + .get_register = ab3550_get_register_interruptible, + .set_register = ab3550_set_register_interruptible, + .get_register_page = ab3550_get_register_page_interruptible, + .set_register_page = NULL, + .mask_and_set_register = ab3550_mask_and_set_register_interruptible, + .event_registers_startup_state_get = + ab3550_event_registers_startup_state_get, + .startup_irq_enabled = ab3550_startup_irq_enabled, +}; + +static irqreturn_t ab3550_irq_handler(int irq, void *data) +{ + struct ab3550 *ab = data; + int err; + unsigned int i; + u8 e[AB3550_NUM_EVENT_REG]; + u8 *events; + unsigned long flags; + + events = (ab->startup_events_read ? e : ab->startup_events); + + err = get_register_page_interruptible(ab, AB3550_EVENT_BANK, + AB3550_EVENT_REG, events, AB3550_NUM_EVENT_REG); + if (err) + goto err_event_rd; + + if (!ab->startup_events_read) { + dev_info(&ab->i2c_client[0]->dev, + "startup events 0x%x,0x%x,0x%x,0x%x,0x%x\n", + ab->startup_events[0], ab->startup_events[1], + ab->startup_events[2], ab->startup_events[3], + ab->startup_events[4]); + ab->startup_events_read = true; + goto out; + } + + /* The two highest bits in event[4] are not used. */ + events[4] &= 0x3f; + + spin_lock_irqsave(&ab->event_lock, flags); + for (i = 0; i < AB3550_NUM_EVENT_REG; i++) + events[i] &= ~ab->event_mask[i]; + spin_unlock_irqrestore(&ab->event_lock, flags); + + for (i = 0; i < AB3550_NUM_EVENT_REG; i++) { + u8 bit; + u8 event_reg; + + dev_dbg(&ab->i2c_client[0]->dev, "IRQ Event[%d]: 0x%2x\n", + i, events[i]); + + event_reg = events[i]; + for (bit = 0; event_reg; bit++, event_reg /= 2) { + if (event_reg % 2) { + unsigned int irq; + struct ab3550_platform_data *plf_data; + + plf_data = ab->i2c_client[0]->dev.platform_data; + irq = plf_data->irq.base + (i * 8) + bit; + handle_nested_irq(irq); + } + } + } +out: + return IRQ_HANDLED; + +err_event_rd: + dev_dbg(&ab->i2c_client[0]->dev, "error reading event registers\n"); + return IRQ_HANDLED; +} + +#ifdef CONFIG_DEBUG_FS +static struct ab3550_reg_ranges debug_ranges[AB3550_NUM_BANKS] = { + { + .count = 6, + .range = (struct ab3550_reg_range[]) { + { + .first = 0x00, + .last = 0x0e, + }, + { + .first = 0x10, + .last = 0x1a, + }, + { + .first = 0x1e, + .last = 0x4f, + }, + { + .first = 0x51, + .last = 0x63, + }, + { + .first = 0x65, + .last = 0xa3, + }, + { + .first = 0xa5, + .last = 0xa8, + }, + } + }, + { + .count = 8, + .range = (struct ab3550_reg_range[]) { + { + .first = 0x00, + .last = 0x0e, + }, + { + .first = 0x10, + .last = 0x17, + }, + { + .first = 0x1a, + .last = 0x1c, + }, + { + .first = 0x20, + .last = 0x56, + }, + { + .first = 0x5a, + .last = 0x88, + }, + { + .first = 0x8a, + .last = 0xad, + }, + { + .first = 0xb0, + .last = 0xba, + }, + { + .first = 0xbc, + .last = 0xc3, + }, + } + }, +}; + +static int ab3550_registers_print(struct seq_file *s, void *p) +{ + struct ab3550 *ab = s->private; + int bank; + + seq_printf(s, AB3550_NAME_STRING " register values:\n"); + + for (bank = 0; bank < AB3550_NUM_BANKS; bank++) { + unsigned int i; + + seq_printf(s, " bank %d:\n", bank); + for (i = 0; i < debug_ranges[bank].count; i++) { + u8 reg; + + for (reg = debug_ranges[bank].range[i].first; + reg <= debug_ranges[bank].range[i].last; + reg++) { + u8 value; + + get_register_interruptible(ab, bank, reg, + &value); + seq_printf(s, " [%d/0x%02X]: 0x%02X\n", bank, + reg, value); + } + } + } + return 0; +} + +static int ab3550_registers_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab3550_registers_print, inode->i_private); +} + +static const struct file_operations ab3550_registers_fops = { + .open = ab3550_registers_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int ab3550_bank_print(struct seq_file *s, void *p) +{ + struct ab3550 *ab = s->private; + + seq_printf(s, "%d\n", ab->debug_bank); + return 0; +} + +static int ab3550_bank_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab3550_bank_print, inode->i_private); +} + +static ssize_t ab3550_bank_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private; + char buf[32]; + int buf_size; + unsigned long user_bank; + int err; + + /* Get userspace string and assure termination */ + buf_size = min(count, (sizeof(buf) - 1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + err = strict_strtoul(buf, 0, &user_bank); + if (err) + return -EINVAL; + + if (user_bank >= AB3550_NUM_BANKS) { + dev_err(&ab->i2c_client[0]->dev, + "debugfs error input > number of banks\n"); + return -EINVAL; + } + + ab->debug_bank = user_bank; + + return buf_size; +} + +static int ab3550_address_print(struct seq_file *s, void *p) +{ + struct ab3550 *ab = s->private; + + seq_printf(s, "0x%02X\n", ab->debug_address); + return 0; +} + +static int ab3550_address_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab3550_address_print, inode->i_private); +} + +static ssize_t ab3550_address_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private; + char buf[32]; + int buf_size; + unsigned long user_address; + int err; + + /* Get userspace string and assure termination */ + buf_size = min(count, (sizeof(buf) - 1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + err = strict_strtoul(buf, 0, &user_address); + if (err) + return -EINVAL; + if (user_address > 0xff) { + dev_err(&ab->i2c_client[0]->dev, + "debugfs error input > 0xff\n"); + return -EINVAL; + } + ab->debug_address = user_address; + return buf_size; +} + +static int ab3550_val_print(struct seq_file *s, void *p) +{ + struct ab3550 *ab = s->private; + int err; + u8 regvalue; + + err = get_register_interruptible(ab, (u8)ab->debug_bank, + (u8)ab->debug_address, ®value); + if (err) + return -EINVAL; + seq_printf(s, "0x%02X\n", regvalue); + + return 0; +} + +static int ab3550_val_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab3550_val_print, inode->i_private); +} + +static ssize_t ab3550_val_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private; + char buf[32]; + int buf_size; + unsigned long user_val; + int err; + u8 regvalue; + + /* Get userspace string and assure termination */ + buf_size = min(count, (sizeof(buf)-1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + err = strict_strtoul(buf, 0, &user_val); + if (err) + return -EINVAL; + if (user_val > 0xff) { + dev_err(&ab->i2c_client[0]->dev, + "debugfs error input > 0xff\n"); + return -EINVAL; + } + err = mask_and_set_register_interruptible( + ab, (u8)ab->debug_bank, + (u8)ab->debug_address, 0xFF, (u8)user_val); + if (err) + return -EINVAL; + + get_register_interruptible(ab, (u8)ab->debug_bank, + (u8)ab->debug_address, ®value); + if (err) + return -EINVAL; + + return buf_size; +} + +static const struct file_operations ab3550_bank_fops = { + .open = ab3550_bank_open, + .write = ab3550_bank_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static const struct file_operations ab3550_address_fops = { + .open = ab3550_address_open, + .write = ab3550_address_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static const struct file_operations ab3550_val_fops = { + .open = ab3550_val_open, + .write = ab3550_val_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static struct dentry *ab3550_dir; +static struct dentry *ab3550_reg_file; +static struct dentry *ab3550_bank_file; +static struct dentry *ab3550_address_file; +static struct dentry *ab3550_val_file; + +static inline void ab3550_setup_debugfs(struct ab3550 *ab) +{ + ab->debug_bank = 0; + ab->debug_address = 0x00; + + ab3550_dir = debugfs_create_dir(AB3550_NAME_STRING, NULL); + if (!ab3550_dir) + goto exit_no_debugfs; + + ab3550_reg_file = debugfs_create_file("all-registers", + S_IRUGO, ab3550_dir, ab, &ab3550_registers_fops); + if (!ab3550_reg_file) + goto exit_destroy_dir; + + ab3550_bank_file = debugfs_create_file("register-bank", + (S_IRUGO | S_IWUGO), ab3550_dir, ab, &ab3550_bank_fops); + if (!ab3550_bank_file) + goto exit_destroy_reg; + + ab3550_address_file = debugfs_create_file("register-address", + (S_IRUGO | S_IWUGO), ab3550_dir, ab, &ab3550_address_fops); + if (!ab3550_address_file) + goto exit_destroy_bank; + + ab3550_val_file = debugfs_create_file("register-value", + (S_IRUGO | S_IWUGO), ab3550_dir, ab, &ab3550_val_fops); + if (!ab3550_val_file) + goto exit_destroy_address; + + return; + +exit_destroy_address: + debugfs_remove(ab3550_address_file); +exit_destroy_bank: + debugfs_remove(ab3550_bank_file); +exit_destroy_reg: + debugfs_remove(ab3550_reg_file); +exit_destroy_dir: + debugfs_remove(ab3550_dir); +exit_no_debugfs: + dev_err(&ab->i2c_client[0]->dev, "failed to create debugfs entries.\n"); + return; +} + +static inline void ab3550_remove_debugfs(void) +{ + debugfs_remove(ab3550_val_file); + debugfs_remove(ab3550_address_file); + debugfs_remove(ab3550_bank_file); + debugfs_remove(ab3550_reg_file); + debugfs_remove(ab3550_dir); +} + +#else /* !CONFIG_DEBUG_FS */ +static inline void ab3550_setup_debugfs(struct ab3550 *ab) +{ +} +static inline void ab3550_remove_debugfs(void) +{ +} +#endif + +/* + * Basic set-up, datastructure creation/destruction and I2C interface. + * This sets up a default config in the AB3550 chip so that it + * will work as expected. + */ +static int __init ab3550_setup(struct ab3550 *ab) +{ + int err = 0; + int i; + struct ab3550_platform_data *plf_data; + struct abx500_init_settings *settings; + + plf_data = ab->i2c_client[0]->dev.platform_data; + settings = plf_data->init_settings; + + for (i = 0; i < plf_data->init_settings_sz; i++) { + err = mask_and_set_register_interruptible(ab, + settings[i].bank, + settings[i].reg, + 0xFF, settings[i].setting); + if (err) + goto exit_no_setup; + + /* If event mask register update the event mask in ab3550 */ + if ((settings[i].bank == 0) && + (AB3550_IMR1 <= settings[i].reg) && + (settings[i].reg <= AB3550_IMR5)) { + ab->event_mask[settings[i].reg - AB3550_IMR1] = + settings[i].setting; + } + } +exit_no_setup: + return err; +} + +static void ab3550_mask_work(struct work_struct *work) +{ + struct ab3550 *ab = container_of(work, struct ab3550, mask_work); + int i; + unsigned long flags; + u8 mask[AB3550_NUM_EVENT_REG]; + + spin_lock_irqsave(&ab->event_lock, flags); + for (i = 0; i < AB3550_NUM_EVENT_REG; i++) + mask[i] = ab->event_mask[i]; + spin_unlock_irqrestore(&ab->event_lock, flags); + + for (i = 0; i < AB3550_NUM_EVENT_REG; i++) { + int err; + + err = mask_and_set_register_interruptible(ab, 0, + (AB3550_IMR1 + i), ~0, mask[i]); + if (err) + dev_err(&ab->i2c_client[0]->dev, + "ab3550_mask_work failed 0x%x,0x%x\n", + (AB3550_IMR1 + i), mask[i]); + } +} + +static void ab3550_mask(unsigned int irq) +{ + unsigned long flags; + struct ab3550 *ab; + struct ab3550_platform_data *plf_data; + + ab = get_irq_chip_data(irq); + plf_data = ab->i2c_client[0]->dev.platform_data; + irq -= plf_data->irq.base; + + spin_lock_irqsave(&ab->event_lock, flags); + ab->event_mask[irq / 8] |= BIT(irq % 8); + spin_unlock_irqrestore(&ab->event_lock, flags); + + schedule_work(&ab->mask_work); +} + +static void ab3550_unmask(unsigned int irq) +{ + unsigned long flags; + struct ab3550 *ab; + struct ab3550_platform_data *plf_data; + + ab = get_irq_chip_data(irq); + plf_data = ab->i2c_client[0]->dev.platform_data; + irq -= plf_data->irq.base; + + spin_lock_irqsave(&ab->event_lock, flags); + ab->event_mask[irq / 8] &= ~BIT(irq % 8); + spin_unlock_irqrestore(&ab->event_lock, flags); + + schedule_work(&ab->mask_work); +} + +static void noop(unsigned int irq) +{ +} + +static struct irq_chip ab3550_irq_chip = { + .name = "ab3550-core", /* Keep the same name as the request */ + .startup = NULL, /* defaults to enable */ + .shutdown = NULL, /* defaults to disable */ + .enable = NULL, /* defaults to unmask */ + .disable = ab3550_mask, /* No default to mask in chip.c */ + .ack = noop, + .mask = ab3550_mask, + .unmask = ab3550_unmask, + .end = NULL, +}; + +struct ab_family_id { + u8 id; + char *name; +}; + +static const struct ab_family_id ids[] __initdata = { + /* AB3550 */ + { + .id = AB3550_P1A, + .name = "P1A" + }, + /* Terminator */ + { + .id = 0x00, + } +}; + +static int __init ab3550_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ab3550 *ab; + struct ab3550_platform_data *ab3550_plf_data = + client->dev.platform_data; + int err; + int i; + int num_i2c_clients = 0; + + ab = kzalloc(sizeof(struct ab3550), GFP_KERNEL); + if (!ab) { + dev_err(&client->dev, + "could not allocate " AB3550_NAME_STRING " device\n"); + return -ENOMEM; + } + + /* Initialize data structure */ + mutex_init(&ab->access_mutex); + spin_lock_init(&ab->event_lock); + ab->i2c_client[0] = client; + + i2c_set_clientdata(client, ab); + + /* Read chip ID register */ + err = get_register_interruptible(ab, 0, AB3550_CID_REG, &ab->chip_id); + if (err) { + dev_err(&client->dev, "could not communicate with the analog " + "baseband chip\n"); + goto exit_no_detect; + } + + for (i = 0; ids[i].id != 0x0; i++) { + if (ids[i].id == ab->chip_id) { + snprintf(&ab->chip_name[0], sizeof(ab->chip_name) - 1, + AB3550_ID_FORMAT_STRING, ids[i].name); + break; + } + } + + if (ids[i].id == 0x0) { + dev_err(&client->dev, "unknown analog baseband chip id: 0x%x\n", + ab->chip_id); + dev_err(&client->dev, "driver not started!\n"); + goto exit_no_detect; + } + + dev_info(&client->dev, "detected AB chip: %s\n", &ab->chip_name[0]); + + /* Attach other dummy I2C clients. */ + while (++num_i2c_clients < AB3550_NUM_BANKS) { + ab->i2c_client[num_i2c_clients] = + i2c_new_dummy(client->adapter, + (client->addr + num_i2c_clients)); + if (!ab->i2c_client[num_i2c_clients]) { + err = -ENOMEM; + goto exit_no_dummy_client; + } + strlcpy(ab->i2c_client[num_i2c_clients]->name, id->name, + sizeof(ab->i2c_client[num_i2c_clients]->name)); + } + + err = ab3550_setup(ab); + if (err) + goto exit_no_setup; + + INIT_WORK(&ab->mask_work, ab3550_mask_work); + + for (i = 0; i < ab3550_plf_data->irq.count; i++) { + unsigned int irq; + + irq = ab3550_plf_data->irq.base + i; + set_irq_chip_data(irq, ab); + set_irq_chip_and_handler(irq, &ab3550_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 + } + + err = request_threaded_irq(client->irq, NULL, ab3550_irq_handler, + IRQF_ONESHOT, "ab3550-core", ab); + /* This real unpredictable IRQ is of course sampled for entropy */ + rand_initialize_irq(client->irq); + + if (err) + goto exit_no_irq; + + err = abx500_register_ops(&client->dev, &ab3550_ops); + if (err) + goto exit_no_ops; + + /* Set up and register the platform devices. */ + for (i = 0; i < AB3550_NUM_DEVICES; i++) { + ab3550_devs[i].platform_data = ab3550_plf_data->dev_data[i]; + ab3550_devs[i].data_size = ab3550_plf_data->dev_data_sz[i]; + } + + err = mfd_add_devices(&client->dev, 0, ab3550_devs, + ARRAY_SIZE(ab3550_devs), NULL, + ab3550_plf_data->irq.base); + + ab3550_setup_debugfs(ab); + + return 0; + +exit_no_ops: +exit_no_irq: +exit_no_setup: +exit_no_dummy_client: + /* Unregister the dummy i2c clients. */ + while (--num_i2c_clients) + i2c_unregister_device(ab->i2c_client[num_i2c_clients]); +exit_no_detect: + kfree(ab); + return err; +} + +static int __exit ab3550_remove(struct i2c_client *client) +{ + struct ab3550 *ab = i2c_get_clientdata(client); + int num_i2c_clients = AB3550_NUM_BANKS; + + mfd_remove_devices(&client->dev); + ab3550_remove_debugfs(); + + while (--num_i2c_clients) + i2c_unregister_device(ab->i2c_client[num_i2c_clients]); + + /* + * At this point, all subscribers should have unregistered + * their notifiers so deactivate IRQ + */ + free_irq(client->irq, ab); + i2c_set_clientdata(client, NULL); + kfree(ab); + return 0; +} + +static const struct i2c_device_id ab3550_id[] = { + {AB3550_NAME_STRING, 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, ab3550_id); + +static struct i2c_driver ab3550_driver = { + .driver = { + .name = AB3550_NAME_STRING, + .owner = THIS_MODULE, + }, + .id_table = ab3550_id, + .probe = ab3550_probe, + .remove = __exit_p(ab3550_remove), +}; + +static int __init ab3550_i2c_init(void) +{ + return i2c_add_driver(&ab3550_driver); +} + +static void __exit ab3550_i2c_exit(void) +{ + i2c_del_driver(&ab3550_driver); +} + +subsys_initcall(ab3550_i2c_init); +module_exit(ab3550_i2c_exit); + +MODULE_AUTHOR("Mattias Wallin <mattias.wallin@stericsson.com>"); +MODULE_DESCRIPTION("AB3550 core driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/ab4500-core.c b/drivers/mfd/ab4500-core.c deleted file mode 100644 index c275daa..0000000 --- a/drivers/mfd/ab4500-core.c +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (C) 2009 ST-Ericsson - * - * Author: Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com> - * - * This program is free software; you can redistribute it - * and/or modify it under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation. - * - * AB4500 is a companion power management chip used with U8500. - * On this platform, this is interfaced with SSP0 controller - * which is a ARM primecell pl022. - * - * At the moment the module just exports read/write features. - * Interrupt management to be added - TODO. - */ -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/spi/spi.h> -#include <linux/mfd/ab4500.h> - -/* just required if probe fails, we need to - * unregister the device - */ -static struct spi_driver ab4500_driver; - -/* - * This funtion writes to any AB4500 registers using - * SPI protocol & before it writes it packs the data - * in the below 24 bit frame format - * - * *|------------------------------------| - * *| 23|22...18|17.......10|9|8|7......0| - * *| r/w bank adr data | - * * ------------------------------------ - * - * This function shouldn't be called from interrupt - * context - */ -int ab4500_write(struct ab4500 *ab4500, unsigned char block, - unsigned long addr, unsigned char data) -{ - struct spi_transfer xfer; - struct spi_message msg; - int err; - unsigned long spi_data = - block << 18 | addr << 10 | data; - - mutex_lock(&ab4500->lock); - ab4500->tx_buf[0] = spi_data; - ab4500->rx_buf[0] = 0; - - xfer.tx_buf = ab4500->tx_buf; - xfer.rx_buf = NULL; - xfer.len = sizeof(unsigned long); - - spi_message_init(&msg); - spi_message_add_tail(&xfer, &msg); - - err = spi_sync(ab4500->spi, &msg); - mutex_unlock(&ab4500->lock); - - return err; -} -EXPORT_SYMBOL(ab4500_write); - -int ab4500_read(struct ab4500 *ab4500, unsigned char block, - unsigned long addr) -{ - struct spi_transfer xfer; - struct spi_message msg; - unsigned long spi_data = - 1 << 23 | block << 18 | addr << 10; - - mutex_lock(&ab4500->lock); - ab4500->tx_buf[0] = spi_data; - ab4500->rx_buf[0] = 0; - - xfer.tx_buf = ab4500->tx_buf; - xfer.rx_buf = ab4500->rx_buf; - xfer.len = sizeof(unsigned long); - - spi_message_init(&msg); - spi_message_add_tail(&xfer, &msg); - - spi_sync(ab4500->spi, &msg); - mutex_unlock(&ab4500->lock); - - return ab4500->rx_buf[0]; -} -EXPORT_SYMBOL(ab4500_read); - -/* ref: ab3100 core */ -#define AB4500_DEVICE(devname, devid) \ -static struct platform_device ab4500_##devname##_device = { \ - .name = devid, \ - .id = -1, \ -} - -/* list of childern devices of ab4500 - all are - * not populated here - TODO - */ -AB4500_DEVICE(charger, "ab4500-charger"); -AB4500_DEVICE(audio, "ab4500-audio"); -AB4500_DEVICE(usb, "ab4500-usb"); -AB4500_DEVICE(tvout, "ab4500-tvout"); -AB4500_DEVICE(sim, "ab4500-sim"); -AB4500_DEVICE(gpadc, "ab4500-gpadc"); -AB4500_DEVICE(clkmgt, "ab4500-clkmgt"); -AB4500_DEVICE(misc, "ab4500-misc"); - -static struct platform_device *ab4500_platform_devs[] = { - &ab4500_charger_device, - &ab4500_audio_device, - &ab4500_usb_device, - &ab4500_tvout_device, - &ab4500_sim_device, - &ab4500_gpadc_device, - &ab4500_clkmgt_device, - &ab4500_misc_device, -}; - -static int __init ab4500_probe(struct spi_device *spi) -{ - struct ab4500 *ab4500; - unsigned char revision; - int err = 0; - int i; - - ab4500 = kzalloc(sizeof *ab4500, GFP_KERNEL); - if (!ab4500) { - dev_err(&spi->dev, "could not allocate AB4500\n"); - err = -ENOMEM; - goto not_detect; - } - - ab4500->spi = spi; - spi_set_drvdata(spi, ab4500); - - mutex_init(&ab4500->lock); - - /* read the revision register */ - revision = ab4500_read(ab4500, AB4500_MISC, AB4500_REV_REG); - - /* revision id 0x0 is for early drop, 0x10 is for cut1.0 */ - if (revision == 0x0 || revision == 0x10) - dev_info(&spi->dev, "Detected chip: %s, revision = %x\n", - ab4500_driver.driver.name, revision); - else { - dev_err(&spi->dev, "unknown chip: 0x%x\n", revision); - goto not_detect; - } - - for (i = 0; i < ARRAY_SIZE(ab4500_platform_devs); i++) { - ab4500_platform_devs[i]->dev.parent = - &spi->dev; - platform_set_drvdata(ab4500_platform_devs[i], ab4500); - } - - /* register the ab4500 platform devices */ - platform_add_devices(ab4500_platform_devs, - ARRAY_SIZE(ab4500_platform_devs)); - - return err; - - not_detect: - spi_unregister_driver(&ab4500_driver); - kfree(ab4500); - return err; -} - -static int __devexit ab4500_remove(struct spi_device *spi) -{ - struct ab4500 *ab4500 = - spi_get_drvdata(spi); - - kfree(ab4500); - - return 0; -} - -static struct spi_driver ab4500_driver = { - .driver = { - .name = "ab4500", - .owner = THIS_MODULE, - }, - .probe = ab4500_probe, - .remove = __devexit_p(ab4500_remove) -}; - -static int __devinit ab4500_init(void) -{ - return spi_register_driver(&ab4500_driver); -} - -static void __exit ab4500_exit(void) -{ - spi_unregister_driver(&ab4500_driver); -} - -subsys_initcall(ab4500_init); -module_exit(ab4500_exit); - -MODULE_AUTHOR("Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com"); -MODULE_DESCRIPTION("AB4500 core driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c new file mode 100644 index 0000000..f3d26fa --- /dev/null +++ b/drivers/mfd/ab8500-core.c @@ -0,0 +1,444 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License v2 + * Author: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com> + * Author: Rabin Vincent <rabin.vincent@stericsson.com> + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/mfd/core.h> +#include <linux/mfd/ab8500.h> + +/* + * Interrupt register offsets + * Bank : 0x0E + */ +#define AB8500_IT_SOURCE1_REG 0x0E00 +#define AB8500_IT_SOURCE2_REG 0x0E01 +#define AB8500_IT_SOURCE3_REG 0x0E02 +#define AB8500_IT_SOURCE4_REG 0x0E03 +#define AB8500_IT_SOURCE5_REG 0x0E04 +#define AB8500_IT_SOURCE6_REG 0x0E05 +#define AB8500_IT_SOURCE7_REG 0x0E06 +#define AB8500_IT_SOURCE8_REG 0x0E07 +#define AB8500_IT_SOURCE19_REG 0x0E12 +#define AB8500_IT_SOURCE20_REG 0x0E13 +#define AB8500_IT_SOURCE21_REG 0x0E14 +#define AB8500_IT_SOURCE22_REG 0x0E15 +#define AB8500_IT_SOURCE23_REG 0x0E16 +#define AB8500_IT_SOURCE24_REG 0x0E17 + +/* + * latch registers + */ +#define AB8500_IT_LATCH1_REG 0x0E20 +#define AB8500_IT_LATCH2_REG 0x0E21 +#define AB8500_IT_LATCH3_REG 0x0E22 +#define AB8500_IT_LATCH4_REG 0x0E23 +#define AB8500_IT_LATCH5_REG 0x0E24 +#define AB8500_IT_LATCH6_REG 0x0E25 +#define AB8500_IT_LATCH7_REG 0x0E26 +#define AB8500_IT_LATCH8_REG 0x0E27 +#define AB8500_IT_LATCH9_REG 0x0E28 +#define AB8500_IT_LATCH10_REG 0x0E29 +#define AB8500_IT_LATCH19_REG 0x0E32 +#define AB8500_IT_LATCH20_REG 0x0E33 +#define AB8500_IT_LATCH21_REG 0x0E34 +#define AB8500_IT_LATCH22_REG 0x0E35 +#define AB8500_IT_LATCH23_REG 0x0E36 +#define AB8500_IT_LATCH24_REG 0x0E37 + +/* + * mask registers + */ + +#define AB8500_IT_MASK1_REG 0x0E40 +#define AB8500_IT_MASK2_REG 0x0E41 +#define AB8500_IT_MASK3_REG 0x0E42 +#define AB8500_IT_MASK4_REG 0x0E43 +#define AB8500_IT_MASK5_REG 0x0E44 +#define AB8500_IT_MASK6_REG 0x0E45 +#define AB8500_IT_MASK7_REG 0x0E46 +#define AB8500_IT_MASK8_REG 0x0E47 +#define AB8500_IT_MASK9_REG 0x0E48 +#define AB8500_IT_MASK10_REG 0x0E49 +#define AB8500_IT_MASK11_REG 0x0E4A +#define AB8500_IT_MASK12_REG 0x0E4B +#define AB8500_IT_MASK13_REG 0x0E4C +#define AB8500_IT_MASK14_REG 0x0E4D +#define AB8500_IT_MASK15_REG 0x0E4E +#define AB8500_IT_MASK16_REG 0x0E4F +#define AB8500_IT_MASK17_REG 0x0E50 +#define AB8500_IT_MASK18_REG 0x0E51 +#define AB8500_IT_MASK19_REG 0x0E52 +#define AB8500_IT_MASK20_REG 0x0E53 +#define AB8500_IT_MASK21_REG 0x0E54 +#define AB8500_IT_MASK22_REG 0x0E55 +#define AB8500_IT_MASK23_REG 0x0E56 +#define AB8500_IT_MASK24_REG 0x0E57 + +#define AB8500_REV_REG 0x1080 + +/* + * Map interrupt numbers to the LATCH and MASK register offsets, Interrupt + * numbers are indexed into this array with (num / 8). + * + * This is one off from the register names, i.e. AB8500_IT_MASK1_REG is at + * offset 0. + */ +static const int ab8500_irq_regoffset[AB8500_NUM_IRQ_REGS] = { + 0, 1, 2, 3, 4, 6, 7, 8, 9, 18, 19, 20, 21, +}; + +static int __ab8500_write(struct ab8500 *ab8500, u16 addr, u8 data) +{ + int ret; + + dev_vdbg(ab8500->dev, "wr: addr %#x <= %#x\n", addr, data); + + ret = ab8500->write(ab8500, addr, data); + if (ret < 0) + dev_err(ab8500->dev, "failed to write reg %#x: %d\n", + addr, ret); + + return ret; +} + +/** + * ab8500_write() - write an AB8500 register + * @ab8500: device to write to + * @addr: address of the register + * @data: value to write + */ +int ab8500_write(struct ab8500 *ab8500, u16 addr, u8 data) +{ + int ret; + + mutex_lock(&ab8500->lock); + ret = __ab8500_write(ab8500, addr, data); + mutex_unlock(&ab8500->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(ab8500_write); + +static int __ab8500_read(struct ab8500 *ab8500, u16 addr) +{ + int ret; + + ret = ab8500->read(ab8500, addr); + if (ret < 0) + dev_err(ab8500->dev, "failed to read reg %#x: %d\n", + addr, ret); + + dev_vdbg(ab8500->dev, "rd: addr %#x => data %#x\n", addr, ret); + + return ret; +} + +/** + * ab8500_read() - read an AB8500 register + * @ab8500: device to read from + * @addr: address of the register + */ +int ab8500_read(struct ab8500 *ab8500, u16 addr) +{ + int ret; + + mutex_lock(&ab8500->lock); + ret = __ab8500_read(ab8500, addr); + mutex_unlock(&ab8500->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(ab8500_read); + +/** + * ab8500_set_bits() - set a bitfield in an AB8500 register + * @ab8500: device to read from + * @addr: address of the register + * @mask: mask of the bitfield to modify + * @data: value to set to the bitfield + */ +int ab8500_set_bits(struct ab8500 *ab8500, u16 addr, u8 mask, u8 data) +{ + int ret; + + mutex_lock(&ab8500->lock); + + ret = __ab8500_read(ab8500, addr); + if (ret < 0) + goto out; + + ret &= ~mask; + ret |= data; + + ret = __ab8500_write(ab8500, addr, ret); + +out: + mutex_unlock(&ab8500->lock); + return ret; +} +EXPORT_SYMBOL_GPL(ab8500_set_bits); + +static void ab8500_irq_lock(unsigned int irq) +{ + struct ab8500 *ab8500 = get_irq_chip_data(irq); + + mutex_lock(&ab8500->irq_lock); +} + +static void ab8500_irq_sync_unlock(unsigned int irq) +{ + struct ab8500 *ab8500 = get_irq_chip_data(irq); + int i; + + for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) { + u8 old = ab8500->oldmask[i]; + u8 new = ab8500->mask[i]; + int reg; + + if (new == old) + continue; + + ab8500->oldmask[i] = new; + + reg = AB8500_IT_MASK1_REG + ab8500_irq_regoffset[i]; + ab8500_write(ab8500, reg, new); + } + + mutex_unlock(&ab8500->irq_lock); +} + +static void ab8500_irq_mask(unsigned int irq) +{ + struct ab8500 *ab8500 = get_irq_chip_data(irq); + int offset = irq - ab8500->irq_base; + int index = offset / 8; + int mask = 1 << (offset % 8); + + ab8500->mask[index] |= mask; +} + +static void ab8500_irq_unmask(unsigned int irq) +{ + struct ab8500 *ab8500 = get_irq_chip_data(irq); + int offset = irq - ab8500->irq_base; + int index = offset / 8; + int mask = 1 << (offset % 8); + + ab8500->mask[index] &= ~mask; +} + +static struct irq_chip ab8500_irq_chip = { + .name = "ab8500", + .bus_lock = ab8500_irq_lock, + .bus_sync_unlock = ab8500_irq_sync_unlock, + .mask = ab8500_irq_mask, + .unmask = ab8500_irq_unmask, +}; + +static irqreturn_t ab8500_irq(int irq, void *dev) +{ + struct ab8500 *ab8500 = dev; + int i; + + dev_vdbg(ab8500->dev, "interrupt\n"); + + for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) { + int regoffset = ab8500_irq_regoffset[i]; + int status; + + status = ab8500_read(ab8500, AB8500_IT_LATCH1_REG + regoffset); + if (status <= 0) + continue; + + do { + int bit = __ffs(status); + int line = i * 8 + bit; + + handle_nested_irq(ab8500->irq_base + line); + status &= ~(1 << bit); + } while (status); + } + + return IRQ_HANDLED; +} + +static int ab8500_irq_init(struct ab8500 *ab8500) +{ + int base = ab8500->irq_base; + int irq; + + for (irq = base; irq < base + AB8500_NR_IRQS; irq++) { + set_irq_chip_data(irq, ab8500); + set_irq_chip_and_handler(irq, &ab8500_irq_chip, + handle_simple_irq); + set_irq_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + set_irq_noprobe(irq); +#endif + } + + return 0; +} + +static void ab8500_irq_remove(struct ab8500 *ab8500) +{ + int base = ab8500->irq_base; + int irq; + + for (irq = base; irq < base + AB8500_NR_IRQS; irq++) { +#ifdef CONFIG_ARM + set_irq_flags(irq, 0); +#endif + set_irq_chip_and_handler(irq, NULL, NULL); + set_irq_chip_data(irq, NULL); + } +} + +static struct resource ab8500_gpadc_resources[] = { + { + .name = "HW_CONV_END", + .start = AB8500_INT_GP_HW_ADC_CONV_END, + .end = AB8500_INT_GP_HW_ADC_CONV_END, + .flags = IORESOURCE_IRQ, + }, + { + .name = "SW_CONV_END", + .start = AB8500_INT_GP_SW_ADC_CONV_END, + .end = AB8500_INT_GP_SW_ADC_CONV_END, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource ab8500_rtc_resources[] = { + { + .name = "60S", + .start = AB8500_INT_RTC_60S, + .end = AB8500_INT_RTC_60S, + .flags = IORESOURCE_IRQ, + }, + { + .name = "ALARM", + .start = AB8500_INT_RTC_ALARM, + .end = AB8500_INT_RTC_ALARM, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell ab8500_devs[] = { + { + .name = "ab8500-gpadc", + .num_resources = ARRAY_SIZE(ab8500_gpadc_resources), + .resources = ab8500_gpadc_resources, + }, + { + .name = "ab8500-rtc", + .num_resources = ARRAY_SIZE(ab8500_rtc_resources), + .resources = ab8500_rtc_resources, + }, + { .name = "ab8500-charger", }, + { .name = "ab8500-audio", }, + { .name = "ab8500-usb", }, + { .name = "ab8500-pwm", }, +}; + +int __devinit ab8500_init(struct ab8500 *ab8500) +{ + struct ab8500_platform_data *plat = dev_get_platdata(ab8500->dev); + int ret; + int i; + + if (plat) + ab8500->irq_base = plat->irq_base; + + mutex_init(&ab8500->lock); + mutex_init(&ab8500->irq_lock); + + ret = ab8500_read(ab8500, AB8500_REV_REG); + if (ret < 0) + return ret; + + /* + * 0x0 - Early Drop + * 0x10 - Cut 1.0 + * 0x11 - Cut 1.1 + */ + if (ret == 0x0 || ret == 0x10 || ret == 0x11) { + ab8500->revision = ret; + dev_info(ab8500->dev, "detected chip, revision: %#x\n", ret); + } else { + dev_err(ab8500->dev, "unknown chip, revision: %#x\n", ret); + return -EINVAL; + } + + if (plat && plat->init) + plat->init(ab8500); + + /* Clear and mask all interrupts */ + for (i = 0; i < 10; i++) { + ab8500_read(ab8500, AB8500_IT_LATCH1_REG + i); + ab8500_write(ab8500, AB8500_IT_MASK1_REG + i, 0xff); + } + + for (i = 18; i < 24; i++) { + ab8500_read(ab8500, AB8500_IT_LATCH1_REG + i); + ab8500_write(ab8500, AB8500_IT_MASK1_REG + i, 0xff); + } + + for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) + ab8500->mask[i] = ab8500->oldmask[i] = 0xff; + + if (ab8500->irq_base) { + ret = ab8500_irq_init(ab8500); + if (ret) + return ret; + + ret = request_threaded_irq(ab8500->irq, NULL, ab8500_irq, + IRQF_ONESHOT, "ab8500", ab8500); + if (ret) + goto out_removeirq; + } + + ret = mfd_add_devices(ab8500->dev, -1, ab8500_devs, + ARRAY_SIZE(ab8500_devs), NULL, + ab8500->irq_base); + if (ret) + goto out_freeirq; + + return ret; + +out_freeirq: + if (ab8500->irq_base) { + free_irq(ab8500->irq, ab8500); +out_removeirq: + ab8500_irq_remove(ab8500); + } + return ret; +} + +int __devexit ab8500_exit(struct ab8500 *ab8500) +{ + mfd_remove_devices(ab8500->dev); + if (ab8500->irq_base) { + free_irq(ab8500->irq, ab8500); + ab8500_irq_remove(ab8500); + } + + return 0; +} + +MODULE_AUTHOR("Srinidhi Kasagar, Rabin Vincent"); +MODULE_DESCRIPTION("AB8500 MFD core"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/ab8500-spi.c b/drivers/mfd/ab8500-spi.c new file mode 100644 index 0000000..b81d4f7 --- /dev/null +++ b/drivers/mfd/ab8500-spi.c @@ -0,0 +1,133 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License v2 + * Author: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com> + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> +#include <linux/mfd/ab8500.h> + +/* + * This funtion writes to any AB8500 registers using + * SPI protocol & before it writes it packs the data + * in the below 24 bit frame format + * + * *|------------------------------------| + * *| 23|22...18|17.......10|9|8|7......0| + * *| r/w bank adr data | + * * ------------------------------------ + * + * This function shouldn't be called from interrupt + * context + */ +static int ab8500_spi_write(struct ab8500 *ab8500, u16 addr, u8 data) +{ + struct spi_device *spi = container_of(ab8500->dev, struct spi_device, + dev); + unsigned long spi_data = addr << 10 | data; + struct spi_transfer xfer; + struct spi_message msg; + + ab8500->tx_buf[0] = spi_data; + ab8500->rx_buf[0] = 0; + + xfer.tx_buf = ab8500->tx_buf; + xfer.rx_buf = NULL; + xfer.len = sizeof(unsigned long); + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + return spi_sync(spi, &msg); +} + +static int ab8500_spi_read(struct ab8500 *ab8500, u16 addr) +{ + struct spi_device *spi = container_of(ab8500->dev, struct spi_device, + dev); + unsigned long spi_data = 1 << 23 | addr << 10; + struct spi_transfer xfer; + struct spi_message msg; + int ret; + + ab8500->tx_buf[0] = spi_data; + ab8500->rx_buf[0] = 0; + + xfer.tx_buf = ab8500->tx_buf; + xfer.rx_buf = ab8500->rx_buf; + xfer.len = sizeof(unsigned long); + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + ret = spi_sync(spi, &msg); + if (!ret) + ret = ab8500->rx_buf[0]; + + return ret; +} + +static int __devinit ab8500_spi_probe(struct spi_device *spi) +{ + struct ab8500 *ab8500; + int ret; + + ab8500 = kzalloc(sizeof *ab8500, GFP_KERNEL); + if (!ab8500) + return -ENOMEM; + + ab8500->dev = &spi->dev; + ab8500->irq = spi->irq; + + ab8500->read = ab8500_spi_read; + ab8500->write = ab8500_spi_write; + + spi_set_drvdata(spi, ab8500); + + ret = ab8500_init(ab8500); + if (ret) + kfree(ab8500); + + return ret; +} + +static int __devexit ab8500_spi_remove(struct spi_device *spi) +{ + struct ab8500 *ab8500 = spi_get_drvdata(spi); + + ab8500_exit(ab8500); + kfree(ab8500); + + return 0; +} + +static struct spi_driver ab8500_spi_driver = { + .driver = { + .name = "ab8500", + .owner = THIS_MODULE, + }, + .probe = ab8500_spi_probe, + .remove = __devexit_p(ab8500_spi_remove) +}; + +static int __init ab8500_spi_init(void) +{ + return spi_register_driver(&ab8500_spi_driver); +} +subsys_initcall(ab8500_spi_init); + +static void __exit ab8500_spi_exit(void) +{ + spi_unregister_driver(&ab8500_spi_driver); +} +module_exit(ab8500_spi_exit); + +MODULE_AUTHOR("Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com"); +MODULE_DESCRIPTION("AB8500 SPI"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/abx500-core.c b/drivers/mfd/abx500-core.c new file mode 100644 index 0000000..3b3b97e --- /dev/null +++ b/drivers/mfd/abx500-core.c @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2007-2010 ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + * Register access functions for the ABX500 Mixed Signal IC family. + * Author: Mattias Wallin <mattias.wallin@stericsson.com> + */ + +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/mfd/abx500.h> + +static LIST_HEAD(abx500_list); + +struct abx500_device_entry { + struct list_head list; + struct abx500_ops ops; + struct device *dev; +}; + +static void lookup_ops(struct device *dev, struct abx500_ops **ops) +{ + struct abx500_device_entry *dev_entry; + + *ops = NULL; + list_for_each_entry(dev_entry, &abx500_list, list) { + if (dev_entry->dev == dev) { + *ops = &dev_entry->ops; + return; + } + } +} + +int abx500_register_ops(struct device *dev, struct abx500_ops *ops) +{ + struct abx500_device_entry *dev_entry; + + dev_entry = kzalloc(sizeof(struct abx500_device_entry), GFP_KERNEL); + if (IS_ERR(dev_entry)) { + dev_err(dev, "register_ops kzalloc failed"); + return -ENOMEM; + } + dev_entry->dev = dev; + memcpy(&dev_entry->ops, ops, sizeof(struct abx500_ops)); + + list_add_tail(&dev_entry->list, &abx500_list); + return 0; +} +EXPORT_SYMBOL(abx500_register_ops); + +void abx500_remove_ops(struct device *dev) +{ + struct abx500_device_entry *dev_entry, *tmp; + + list_for_each_entry_safe(dev_entry, tmp, &abx500_list, list) + { + if (dev_entry->dev == dev) { + list_del(&dev_entry->list); + kfree(dev_entry); + } + } +} +EXPORT_SYMBOL(abx500_remove_ops); + +int abx500_set_register_interruptible(struct device *dev, u8 bank, u8 reg, + u8 value) +{ + struct abx500_ops *ops; + + lookup_ops(dev->parent, &ops); + if ((ops != NULL) && (ops->set_register != NULL)) + return ops->set_register(dev, bank, reg, value); + else + return -ENOTSUPP; +} +EXPORT_SYMBOL(abx500_set_register_interruptible); + +int abx500_get_register_interruptible(struct device *dev, u8 bank, u8 reg, + u8 *value) +{ + struct abx500_ops *ops; + + lookup_ops(dev->parent, &ops); + if ((ops != NULL) && (ops->get_register != NULL)) + return ops->get_register(dev, bank, reg, value); + else + return -ENOTSUPP; +} +EXPORT_SYMBOL(abx500_get_register_interruptible); + +int abx500_get_register_page_interruptible(struct device *dev, u8 bank, + u8 first_reg, u8 *regvals, u8 numregs) +{ + struct abx500_ops *ops; + + lookup_ops(dev->parent, &ops); + if ((ops != NULL) && (ops->get_register_page != NULL)) + return ops->get_register_page(dev, bank, + first_reg, regvals, numregs); + else + return -ENOTSUPP; +} +EXPORT_SYMBOL(abx500_get_register_page_interruptible); + +int abx500_mask_and_set_register_interruptible(struct device *dev, u8 bank, + u8 reg, u8 bitmask, u8 bitvalues) +{ + struct abx500_ops *ops; + + lookup_ops(dev->parent, &ops); + if ((ops != NULL) && (ops->mask_and_set_register != NULL)) + return ops->mask_and_set_register(dev, bank, + reg, bitmask, bitvalues); + else + return -ENOTSUPP; +} +EXPORT_SYMBOL(abx500_mask_and_set_register_interruptible); + +int abx500_get_chip_id(struct device *dev) +{ + struct abx500_ops *ops; + + lookup_ops(dev->parent, &ops); + if ((ops != NULL) && (ops->get_chip_id != NULL)) + return ops->get_chip_id(dev); + else + return -ENOTSUPP; +} +EXPORT_SYMBOL(abx500_get_chip_id); + +int abx500_event_registers_startup_state_get(struct device *dev, u8 *event) +{ + struct abx500_ops *ops; + + lookup_ops(dev->parent, &ops); + if ((ops != NULL) && (ops->event_registers_startup_state_get != NULL)) + return ops->event_registers_startup_state_get(dev, event); + else + return -ENOTSUPP; +} +EXPORT_SYMBOL(abx500_event_registers_startup_state_get); + +int abx500_startup_irq_enabled(struct device *dev, unsigned int irq) +{ + struct abx500_ops *ops; + + lookup_ops(dev->parent, &ops); + if ((ops != NULL) && (ops->startup_irq_enabled != NULL)) + return ops->startup_irq_enabled(dev, irq); + else + return -ENOTSUPP; +} +EXPORT_SYMBOL(abx500_startup_irq_enabled); + +MODULE_AUTHOR("Mattias Wallin <mattias.wallin@stericsson.com>"); +MODULE_DESCRIPTION("ABX500 core driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/da903x.c b/drivers/mfd/da903x.c index 67181b1..3ad915d 100644 --- a/drivers/mfd/da903x.c +++ b/drivers/mfd/da903x.c @@ -544,6 +544,7 @@ static int __devexit da903x_remove(struct i2c_client *client) struct da903x_chip *chip = i2c_get_clientdata(client); da903x_remove_subdevs(chip); + i2c_set_clientdata(client, NULL); kfree(chip); return 0; } diff --git a/drivers/mfd/janz-cmodio.c b/drivers/mfd/janz-cmodio.c new file mode 100644 index 0000000..9ed6307 --- /dev/null +++ b/drivers/mfd/janz-cmodio.c @@ -0,0 +1,304 @@ +/* + * Janz CMOD-IO MODULbus Carrier Board PCI Driver + * + * Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu> + * + * Lots of inspiration and code was copied from drivers/mfd/sm501.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/mfd/core.h> + +#include <linux/mfd/janz.h> + +#define DRV_NAME "janz-cmodio" + +/* Size of each MODULbus module in PCI BAR4 */ +#define CMODIO_MODULBUS_SIZE 0x200 + +/* Maximum number of MODULbus modules on a CMOD-IO carrier board */ +#define CMODIO_MAX_MODULES 4 + +/* Module Parameters */ +static unsigned int num_modules = CMODIO_MAX_MODULES; +static unsigned char *modules[CMODIO_MAX_MODULES] = { + "empty", "empty", "empty", "empty", +}; + +module_param_array(modules, charp, &num_modules, S_IRUGO); +MODULE_PARM_DESC(modules, "MODULbus modules attached to the carrier board"); + +/* Unique Device Id */ +static unsigned int cmodio_id; + +struct cmodio_device { + /* Parent PCI device */ + struct pci_dev *pdev; + + /* PLX control registers */ + struct janz_cmodio_onboard_regs __iomem *ctrl; + + /* hex switch position */ + u8 hex; + + /* mfd-core API */ + struct mfd_cell cells[CMODIO_MAX_MODULES]; + struct resource resources[3 * CMODIO_MAX_MODULES]; + struct janz_platform_data pdata[CMODIO_MAX_MODULES]; +}; + +/* + * Subdevices using the mfd-core API + */ + +static int __devinit cmodio_setup_subdevice(struct cmodio_device *priv, + char *name, unsigned int devno, + unsigned int modno) +{ + struct janz_platform_data *pdata; + struct mfd_cell *cell; + struct resource *res; + struct pci_dev *pci; + + pci = priv->pdev; + cell = &priv->cells[devno]; + res = &priv->resources[devno * 3]; + pdata = &priv->pdata[devno]; + + cell->name = name; + cell->resources = res; + cell->num_resources = 3; + + /* Setup the subdevice ID -- must be unique */ + cell->id = cmodio_id++; + + /* Add platform data */ + pdata->modno = modno; + cell->platform_data = pdata; + cell->data_size = sizeof(*pdata); + + /* MODULbus registers -- PCI BAR3 is big-endian MODULbus access */ + res->flags = IORESOURCE_MEM; + res->parent = &pci->resource[3]; + res->start = pci->resource[3].start + (CMODIO_MODULBUS_SIZE * modno); + res->end = res->start + CMODIO_MODULBUS_SIZE - 1; + res++; + + /* PLX Control Registers -- PCI BAR4 is interrupt and other registers */ + res->flags = IORESOURCE_MEM; + res->parent = &pci->resource[4]; + res->start = pci->resource[4].start; + res->end = pci->resource[4].end; + res++; + + /* + * IRQ + * + * The start and end fields are used as an offset to the irq_base + * parameter passed into the mfd_add_devices() function call. All + * devices share the same IRQ. + */ + res->flags = IORESOURCE_IRQ; + res->parent = NULL; + res->start = 0; + res->end = 0; + res++; + + return 0; +} + +/* Probe each submodule using kernel parameters */ +static int __devinit cmodio_probe_submodules(struct cmodio_device *priv) +{ + struct pci_dev *pdev = priv->pdev; + unsigned int num_probed = 0; + char *name; + int i; + + for (i = 0; i < num_modules; i++) { + name = modules[i]; + if (!strcmp(name, "") || !strcmp(name, "empty")) + continue; + + dev_dbg(&priv->pdev->dev, "MODULbus %d: name %s\n", i, name); + cmodio_setup_subdevice(priv, name, num_probed, i); + num_probed++; + } + + /* print an error message if no modules were probed */ + if (num_probed == 0) { + dev_err(&priv->pdev->dev, "no MODULbus modules specified, " + "please set the ``modules'' kernel " + "parameter according to your " + "hardware configuration\n"); + return -ENODEV; + } + + return mfd_add_devices(&pdev->dev, 0, priv->cells, + num_probed, NULL, pdev->irq); +} + +/* + * SYSFS Attributes + */ + +static ssize_t mbus_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct cmodio_device *priv = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%x\n", priv->hex); +} + +static DEVICE_ATTR(modulbus_number, S_IRUGO, mbus_show, NULL); + +static struct attribute *cmodio_sysfs_attrs[] = { + &dev_attr_modulbus_number.attr, + NULL, +}; + +static const struct attribute_group cmodio_sysfs_attr_group = { + .attrs = cmodio_sysfs_attrs, +}; + +/* + * PCI Driver + */ + +static int __devinit cmodio_pci_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + struct cmodio_device *priv; + int ret; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(&dev->dev, "unable to allocate private data\n"); + ret = -ENOMEM; + goto out_return; + } + + pci_set_drvdata(dev, priv); + priv->pdev = dev; + + /* Hardware Initialization */ + ret = pci_enable_device(dev); + if (ret) { + dev_err(&dev->dev, "unable to enable device\n"); + goto out_free_priv; + } + + pci_set_master(dev); + ret = pci_request_regions(dev, DRV_NAME); + if (ret) { + dev_err(&dev->dev, "unable to request regions\n"); + goto out_pci_disable_device; + } + + /* Onboard configuration registers */ + priv->ctrl = pci_ioremap_bar(dev, 4); + if (!priv->ctrl) { + dev_err(&dev->dev, "unable to remap onboard regs\n"); + ret = -ENOMEM; + goto out_pci_release_regions; + } + + /* Read the hex switch on the carrier board */ + priv->hex = ioread8(&priv->ctrl->int_enable); + + /* Add the MODULbus number (hex switch value) to the device's sysfs */ + ret = sysfs_create_group(&dev->dev.kobj, &cmodio_sysfs_attr_group); + if (ret) { + dev_err(&dev->dev, "unable to create sysfs attributes\n"); + goto out_unmap_ctrl; + } + + /* + * Disable all interrupt lines, each submodule will enable its + * own interrupt line if needed + */ + iowrite8(0xf, &priv->ctrl->int_disable); + + /* Register drivers for all submodules */ + ret = cmodio_probe_submodules(priv); + if (ret) { + dev_err(&dev->dev, "unable to probe submodules\n"); + goto out_sysfs_remove_group; + } + + return 0; + +out_sysfs_remove_group: + sysfs_remove_group(&dev->dev.kobj, &cmodio_sysfs_attr_group); +out_unmap_ctrl: + iounmap(priv->ctrl); +out_pci_release_regions: + pci_release_regions(dev); +out_pci_disable_device: + pci_disable_device(dev); +out_free_priv: + kfree(priv); +out_return: + return ret; +} + +static void __devexit cmodio_pci_remove(struct pci_dev *dev) +{ + struct cmodio_device *priv = pci_get_drvdata(dev); + + mfd_remove_devices(&dev->dev); + sysfs_remove_group(&dev->dev.kobj, &cmodio_sysfs_attr_group); + iounmap(priv->ctrl); + pci_release_regions(dev); + pci_disable_device(dev); + kfree(priv); +} + +#define PCI_VENDOR_ID_JANZ 0x13c3 + +/* The list of devices that this module will support */ +static DEFINE_PCI_DEVICE_TABLE(cmodio_pci_ids) = { + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_JANZ, 0x0101 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_JANZ, 0x0100 }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, cmodio_pci_ids); + +static struct pci_driver cmodio_pci_driver = { + .name = DRV_NAME, + .id_table = cmodio_pci_ids, + .probe = cmodio_pci_probe, + .remove = __devexit_p(cmodio_pci_remove), +}; + +/* + * Module Init / Exit + */ + +static int __init cmodio_init(void) +{ + return pci_register_driver(&cmodio_pci_driver); +} + +static void __exit cmodio_exit(void) +{ + pci_unregister_driver(&cmodio_pci_driver); +} + +MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>"); +MODULE_DESCRIPTION("Janz CMOD-IO PCI MODULbus Carrier Board Driver"); +MODULE_LICENSE("GPL"); + +module_init(cmodio_init); +module_exit(cmodio_exit); diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c index 85d63c0..f621bce 100644 --- a/drivers/mfd/max8925-core.c +++ b/drivers/mfd/max8925-core.c @@ -508,7 +508,7 @@ static int max8925_irq_init(struct max8925_chip *chip, int irq, max8925_reg_read(chip->i2c, MAX8925_ON_OFF_IRQ2); max8925_reg_read(chip->rtc, MAX8925_RTC_IRQ); max8925_reg_read(chip->adc, MAX8925_TSC_IRQ); - /* mask all interrupts */ + /* mask all interrupts except for TSC */ max8925_reg_write(chip->rtc, MAX8925_ALARM0_CNTL, 0); max8925_reg_write(chip->rtc, MAX8925_ALARM1_CNTL, 0); max8925_reg_write(chip->i2c, MAX8925_CHG_IRQ1_MASK, 0xff); @@ -516,7 +516,6 @@ static int max8925_irq_init(struct max8925_chip *chip, int irq, max8925_reg_write(chip->i2c, MAX8925_ON_OFF_IRQ1_MASK, 0xff); max8925_reg_write(chip->i2c, MAX8925_ON_OFF_IRQ2_MASK, 0xff); max8925_reg_write(chip->rtc, MAX8925_RTC_IRQ_MASK, 0xff); - max8925_reg_write(chip->adc, MAX8925_TSC_IRQ_MASK, 0xff); mutex_init(&chip->irq_lock); chip->core_irq = irq; @@ -547,7 +546,11 @@ static int max8925_irq_init(struct max8925_chip *chip, int irq, dev_err(chip->dev, "Failed to request core IRQ: %d\n", ret); chip->core_irq = 0; } + tsc_irq: + /* mask TSC interrupt */ + max8925_reg_write(chip->adc, MAX8925_TSC_IRQ_MASK, 0x0f); + if (!pdata->tsc_irq) { dev_warn(chip->dev, "No interrupt support on TSC IRQ\n"); return 0; diff --git a/drivers/mfd/max8925-i2c.c b/drivers/mfd/max8925-i2c.c index d9fd878..e73f3f5 100644 --- a/drivers/mfd/max8925-i2c.c +++ b/drivers/mfd/max8925-i2c.c @@ -173,8 +173,6 @@ static int __devexit max8925_remove(struct i2c_client *client) max8925_device_exit(chip); i2c_unregister_device(chip->adc); i2c_unregister_device(chip->rtc); - i2c_set_clientdata(chip->adc, NULL); - i2c_set_clientdata(chip->rtc, NULL); i2c_set_clientdata(chip->i2c, NULL); kfree(chip); return 0; diff --git a/drivers/mfd/menelaus.c b/drivers/mfd/menelaus.c index a94b131..721948b 100644 --- a/drivers/mfd/menelaus.c +++ b/drivers/mfd/menelaus.c @@ -1228,6 +1228,7 @@ fail2: free_irq(client->irq, menelaus); flush_scheduled_work(); fail1: + i2c_set_clientdata(client, NULL); kfree(menelaus); return err; } @@ -1237,8 +1238,8 @@ static int __exit menelaus_remove(struct i2c_client *client) struct menelaus_chip *menelaus = i2c_get_clientdata(client); free_irq(client->irq, menelaus); - kfree(menelaus); i2c_set_clientdata(client, NULL); + kfree(menelaus); the_menelaus = NULL; return 0; } diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index 8ffbb7a..7dd76bc 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -48,7 +48,7 @@ static int mfd_add_device(struct device *parent, int id, res[r].flags = cell->resources[r].flags; /* Find out base to use */ - if (cell->resources[r].flags & IORESOURCE_MEM) { + if ((cell->resources[r].flags & IORESOURCE_MEM) && mem_base) { res[r].parent = mem_base; res[r].start = mem_base->start + cell->resources[r].start; diff --git a/drivers/mfd/pcf50633-adc.c b/drivers/mfd/pcf50633-adc.c index fe8f922..aed0d2a 100644 --- a/drivers/mfd/pcf50633-adc.c +++ b/drivers/mfd/pcf50633-adc.c @@ -30,13 +30,13 @@ struct pcf50633_adc_request { int mux; int avg; - int result; void (*callback)(struct pcf50633 *, void *, int); void *callback_param; +}; - /* Used in case of sync requests */ +struct pcf50633_adc_sync_request { + int result; struct completion completion; - }; #define PCF50633_MAX_ADC_FIFO_DEPTH 8 @@ -109,10 +109,10 @@ adc_enqueue_request(struct pcf50633 *pcf, struct pcf50633_adc_request *req) return 0; } -static void -pcf50633_adc_sync_read_callback(struct pcf50633 *pcf, void *param, int result) +static void pcf50633_adc_sync_read_callback(struct pcf50633 *pcf, void *param, + int result) { - struct pcf50633_adc_request *req = param; + struct pcf50633_adc_sync_request *req = param; req->result = result; complete(&req->completion); @@ -120,28 +120,19 @@ pcf50633_adc_sync_read_callback(struct pcf50633 *pcf, void *param, int result) int pcf50633_adc_sync_read(struct pcf50633 *pcf, int mux, int avg) { - struct pcf50633_adc_request *req; - int err; + struct pcf50633_adc_sync_request req; + int ret; - /* req is freed when the result is ready, in interrupt handler */ - req = kzalloc(sizeof(*req), GFP_KERNEL); - if (!req) - return -ENOMEM; - - req->mux = mux; - req->avg = avg; - req->callback = pcf50633_adc_sync_read_callback; - req->callback_param = req; + init_completion(&req.completion); - init_completion(&req->completion); - err = adc_enqueue_request(pcf, req); - if (err) - return err; + ret = pcf50633_adc_async_read(pcf, mux, avg, + pcf50633_adc_sync_read_callback, &req); + if (ret) + 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); diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c index dc95ddb..704736e 100644 --- a/drivers/mfd/pcf50633-core.c +++ b/drivers/mfd/pcf50633-core.c @@ -21,16 +21,16 @@ #include <linux/workqueue.h> #include <linux/platform_device.h> #include <linux/i2c.h> -#include <linux/irq.h> #include <linux/slab.h> #include <linux/mfd/pcf50633/core.h> -/* Two MBCS registers used during cold start */ -#define PCF50633_REG_MBCS1 0x4b -#define PCF50633_REG_MBCS2 0x4c -#define PCF50633_MBCS1_USBPRES 0x01 -#define PCF50633_MBCS1_ADAPTPRES 0x01 +int pcf50633_irq_init(struct pcf50633 *pcf, int irq); +void pcf50633_irq_free(struct pcf50633 *pcf); +#ifdef CONFIG_PM +int pcf50633_irq_suspend(struct pcf50633 *pcf); +int pcf50633_irq_resume(struct pcf50633 *pcf); +#endif static int __pcf50633_read(struct pcf50633 *pcf, u8 reg, int num, u8 *data) { @@ -215,244 +215,6 @@ static struct attribute_group pcf_attr_group = { .attrs = pcf_sysfs_entries, }; -int pcf50633_register_irq(struct pcf50633 *pcf, int irq, - void (*handler) (int, void *), void *data) -{ - if (irq < 0 || irq > PCF50633_NUM_IRQ || !handler) - return -EINVAL; - - if (WARN_ON(pcf->irq_handler[irq].handler)) - return -EBUSY; - - mutex_lock(&pcf->lock); - pcf->irq_handler[irq].handler = handler; - pcf->irq_handler[irq].data = data; - mutex_unlock(&pcf->lock); - - return 0; -} -EXPORT_SYMBOL_GPL(pcf50633_register_irq); - -int pcf50633_free_irq(struct pcf50633 *pcf, int irq) -{ - if (irq < 0 || irq > PCF50633_NUM_IRQ) - return -EINVAL; - - mutex_lock(&pcf->lock); - pcf->irq_handler[irq].handler = NULL; - mutex_unlock(&pcf->lock); - - return 0; -} -EXPORT_SYMBOL_GPL(pcf50633_free_irq); - -static int __pcf50633_irq_mask_set(struct pcf50633 *pcf, int irq, u8 mask) -{ - u8 reg, bits, tmp; - int ret = 0, idx; - - idx = irq >> 3; - reg = PCF50633_REG_INT1M + idx; - bits = 1 << (irq & 0x07); - - mutex_lock(&pcf->lock); - - if (mask) { - ret = __pcf50633_read(pcf, reg, 1, &tmp); - if (ret < 0) - goto out; - - tmp |= bits; - - ret = __pcf50633_write(pcf, reg, 1, &tmp); - if (ret < 0) - goto out; - - pcf->mask_regs[idx] &= ~bits; - pcf->mask_regs[idx] |= bits; - } else { - ret = __pcf50633_read(pcf, reg, 1, &tmp); - if (ret < 0) - goto out; - - tmp &= ~bits; - - ret = __pcf50633_write(pcf, reg, 1, &tmp); - if (ret < 0) - goto out; - - pcf->mask_regs[idx] &= ~bits; - } -out: - mutex_unlock(&pcf->lock); - - return ret; -} - -int pcf50633_irq_mask(struct pcf50633 *pcf, int irq) -{ - dev_dbg(pcf->dev, "Masking IRQ %d\n", irq); - - return __pcf50633_irq_mask_set(pcf, irq, 1); -} -EXPORT_SYMBOL_GPL(pcf50633_irq_mask); - -int pcf50633_irq_unmask(struct pcf50633 *pcf, int irq) -{ - dev_dbg(pcf->dev, "Unmasking IRQ %d\n", irq); - - return __pcf50633_irq_mask_set(pcf, irq, 0); -} -EXPORT_SYMBOL_GPL(pcf50633_irq_unmask); - -int pcf50633_irq_mask_get(struct pcf50633 *pcf, int irq) -{ - u8 reg, bits; - - reg = irq >> 3; - bits = 1 << (irq & 0x07); - - return pcf->mask_regs[reg] & bits; -} -EXPORT_SYMBOL_GPL(pcf50633_irq_mask_get); - -static void pcf50633_irq_call_handler(struct pcf50633 *pcf, int irq) -{ - if (pcf->irq_handler[irq].handler) - pcf->irq_handler[irq].handler(irq, pcf->irq_handler[irq].data); -} - -/* Maximum amount of time ONKEY is held before emergency action is taken */ -#define PCF50633_ONKEY1S_TIMEOUT 8 - -static void pcf50633_irq_worker(struct work_struct *work) -{ - struct pcf50633 *pcf; - int ret, i, j; - u8 pcf_int[5], chgstat; - - pcf = container_of(work, struct pcf50633, irq_work); - - /* Read the 5 INT regs in one transaction */ - ret = pcf50633_read_block(pcf, PCF50633_REG_INT1, - ARRAY_SIZE(pcf_int), pcf_int); - if (ret != ARRAY_SIZE(pcf_int)) { - dev_err(pcf->dev, "Error reading INT registers\n"); - - /* - * If this doesn't ACK the interrupt to the chip, we'll be - * called once again as we're level triggered. - */ - goto out; - } - - /* defeat 8s death from lowsys on A5 */ - pcf50633_reg_write(pcf, PCF50633_REG_OOCSHDWN, 0x04); - - /* We immediately read the usb and adapter status. We thus make sure - * only of USBINS/USBREM IRQ handlers are called */ - if (pcf_int[0] & (PCF50633_INT1_USBINS | PCF50633_INT1_USBREM)) { - chgstat = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2); - if (chgstat & (0x3 << 4)) - pcf_int[0] &= ~(1 << PCF50633_INT1_USBREM); - else - pcf_int[0] &= ~(1 << PCF50633_INT1_USBINS); - } - - /* Make sure only one of ADPINS or ADPREM is set */ - if (pcf_int[0] & (PCF50633_INT1_ADPINS | PCF50633_INT1_ADPREM)) { - chgstat = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2); - if (chgstat & (0x3 << 4)) - pcf_int[0] &= ~(1 << PCF50633_INT1_ADPREM); - else - pcf_int[0] &= ~(1 << PCF50633_INT1_ADPINS); - } - - dev_dbg(pcf->dev, "INT1=0x%02x INT2=0x%02x INT3=0x%02x " - "INT4=0x%02x INT5=0x%02x\n", pcf_int[0], - pcf_int[1], pcf_int[2], pcf_int[3], pcf_int[4]); - - /* Some revisions of the chip don't have a 8s standby mode on - * ONKEY1S press. We try to manually do it in such cases. */ - if ((pcf_int[0] & PCF50633_INT1_SECOND) && pcf->onkey1s_held) { - dev_info(pcf->dev, "ONKEY1S held for %d secs\n", - pcf->onkey1s_held); - if (pcf->onkey1s_held++ == PCF50633_ONKEY1S_TIMEOUT) - if (pcf->pdata->force_shutdown) - pcf->pdata->force_shutdown(pcf); - } - - if (pcf_int[2] & PCF50633_INT3_ONKEY1S) { - dev_info(pcf->dev, "ONKEY1S held\n"); - pcf->onkey1s_held = 1 ; - - /* Unmask IRQ_SECOND */ - pcf50633_reg_clear_bits(pcf, PCF50633_REG_INT1M, - PCF50633_INT1_SECOND); - - /* Unmask IRQ_ONKEYR */ - pcf50633_reg_clear_bits(pcf, PCF50633_REG_INT2M, - PCF50633_INT2_ONKEYR); - } - - if ((pcf_int[1] & PCF50633_INT2_ONKEYR) && pcf->onkey1s_held) { - pcf->onkey1s_held = 0; - - /* Mask SECOND and ONKEYR interrupts */ - if (pcf->mask_regs[0] & PCF50633_INT1_SECOND) - pcf50633_reg_set_bit_mask(pcf, - PCF50633_REG_INT1M, - PCF50633_INT1_SECOND, - PCF50633_INT1_SECOND); - - if (pcf->mask_regs[1] & PCF50633_INT2_ONKEYR) - pcf50633_reg_set_bit_mask(pcf, - PCF50633_REG_INT2M, - PCF50633_INT2_ONKEYR, - PCF50633_INT2_ONKEYR); - } - - /* Have we just resumed ? */ - if (pcf->is_suspended) { - pcf->is_suspended = 0; - - /* Set the resume reason filtering out non resumers */ - for (i = 0; i < ARRAY_SIZE(pcf_int); i++) - pcf->resume_reason[i] = pcf_int[i] & - pcf->pdata->resumers[i]; - - /* Make sure we don't pass on any ONKEY events to - * userspace now */ - pcf_int[1] &= ~(PCF50633_INT2_ONKEYR | PCF50633_INT2_ONKEYF); - } - - for (i = 0; i < ARRAY_SIZE(pcf_int); i++) { - /* Unset masked interrupts */ - pcf_int[i] &= ~pcf->mask_regs[i]; - - for (j = 0; j < 8 ; j++) - if (pcf_int[i] & (1 << j)) - pcf50633_irq_call_handler(pcf, (i * 8) + j); - } - -out: - put_device(pcf->dev); - enable_irq(pcf->irq); -} - -static irqreturn_t pcf50633_irq(int irq, void *data) -{ - struct pcf50633 *pcf = data; - - dev_dbg(pcf->dev, "pcf50633_irq\n"); - - get_device(pcf->dev); - disable_irq_nosync(pcf->irq); - queue_work(pcf->work_queue, &pcf->irq_work); - - return IRQ_HANDLED; -} - static void pcf50633_client_dev_register(struct pcf50633 *pcf, const char *name, struct platform_device **pdev) @@ -479,70 +241,17 @@ pcf50633_client_dev_register(struct pcf50633 *pcf, const char *name, static int pcf50633_suspend(struct i2c_client *client, pm_message_t state) { struct pcf50633 *pcf; - int ret = 0, i; - u8 res[5]; - pcf = i2c_get_clientdata(client); - /* Make sure our interrupt handlers are not called - * henceforth */ - disable_irq(pcf->irq); - - /* Make sure that any running IRQ worker has quit */ - cancel_work_sync(&pcf->irq_work); - - /* Save the masks */ - ret = pcf50633_read_block(pcf, PCF50633_REG_INT1M, - ARRAY_SIZE(pcf->suspend_irq_masks), - pcf->suspend_irq_masks); - if (ret < 0) { - dev_err(pcf->dev, "error saving irq masks\n"); - goto out; - } - - /* Write wakeup irq masks */ - for (i = 0; i < ARRAY_SIZE(res); i++) - res[i] = ~pcf->pdata->resumers[i]; - - ret = pcf50633_write_block(pcf, PCF50633_REG_INT1M, - ARRAY_SIZE(res), &res[0]); - if (ret < 0) { - dev_err(pcf->dev, "error writing wakeup irq masks\n"); - goto out; - } - - pcf->is_suspended = 1; - -out: - return ret; + return pcf50633_irq_suspend(pcf); } static int pcf50633_resume(struct i2c_client *client) { struct pcf50633 *pcf; - int ret; - pcf = i2c_get_clientdata(client); - /* Write the saved mask registers */ - ret = pcf50633_write_block(pcf, PCF50633_REG_INT1M, - ARRAY_SIZE(pcf->suspend_irq_masks), - pcf->suspend_irq_masks); - if (ret < 0) - dev_err(pcf->dev, "Error restoring saved suspend masks\n"); - - /* Restore regulators' state */ - - - get_device(pcf->dev); - - /* - * Clear any pending interrupts and set resume reason if any. - * This will leave with enable_irq() - */ - pcf50633_irq_worker(&pcf->irq_work); - - return 0; + return pcf50633_irq_resume(pcf); } #else #define pcf50633_suspend NULL @@ -573,43 +282,19 @@ static int __devinit pcf50633_probe(struct i2c_client *client, i2c_set_clientdata(client, pcf); pcf->dev = &client->dev; pcf->i2c_client = client; - pcf->irq = client->irq; - pcf->work_queue = create_singlethread_workqueue("pcf50633"); - - if (!pcf->work_queue) { - dev_err(&client->dev, "Failed to alloc workqueue\n"); - ret = -ENOMEM; - goto err_free; - } - - INIT_WORK(&pcf->irq_work, pcf50633_irq_worker); version = pcf50633_reg_read(pcf, 0); variant = pcf50633_reg_read(pcf, 1); if (version < 0 || variant < 0) { dev_err(pcf->dev, "Unable to probe pcf50633\n"); ret = -ENODEV; - goto err_destroy_workqueue; + goto err_free; } dev_info(pcf->dev, "Probed device version %d variant %d\n", version, variant); - /* Enable all interrupts except RTC SECOND */ - pcf->mask_regs[0] = 0x80; - pcf50633_reg_write(pcf, PCF50633_REG_INT1M, pcf->mask_regs[0]); - pcf50633_reg_write(pcf, PCF50633_REG_INT2M, 0x00); - pcf50633_reg_write(pcf, PCF50633_REG_INT3M, 0x00); - pcf50633_reg_write(pcf, PCF50633_REG_INT4M, 0x00); - pcf50633_reg_write(pcf, PCF50633_REG_INT5M, 0x00); - - ret = request_irq(client->irq, pcf50633_irq, - IRQF_TRIGGER_LOW, "pcf50633", pcf); - - if (ret) { - dev_err(pcf->dev, "Failed to request IRQ %d\n", ret); - goto err_destroy_workqueue; - } + pcf50633_irq_init(pcf, client->irq); /* Create sub devices */ pcf50633_client_dev_register(pcf, "pcf50633-input", @@ -641,10 +326,6 @@ static int __devinit pcf50633_probe(struct i2c_client *client, platform_device_add(pdev); } - if (enable_irq_wake(client->irq) < 0) - dev_err(pcf->dev, "IRQ %u cannot be enabled as wake-up source" - "in this hardware revision", client->irq); - ret = sysfs_create_group(&client->dev.kobj, &pcf_attr_group); if (ret) dev_err(pcf->dev, "error creating sysfs entries\n"); @@ -654,8 +335,6 @@ static int __devinit pcf50633_probe(struct i2c_client *client, return 0; -err_destroy_workqueue: - destroy_workqueue(pcf->work_queue); err_free: i2c_set_clientdata(client, NULL); kfree(pcf); @@ -668,8 +347,7 @@ static int __devexit pcf50633_remove(struct i2c_client *client) struct pcf50633 *pcf = i2c_get_clientdata(client); int i; - free_irq(pcf->irq, pcf); - destroy_workqueue(pcf->work_queue); + pcf50633_irq_free(pcf); platform_device_unregister(pcf->input_pdev); platform_device_unregister(pcf->rtc_pdev); @@ -679,6 +357,7 @@ static int __devexit pcf50633_remove(struct i2c_client *client) for (i = 0; i < PCF50633_NUM_REGULATORS; i++) platform_device_unregister(pcf->regulator_pdev[i]); + i2c_set_clientdata(client, NULL); kfree(pcf); return 0; diff --git a/drivers/mfd/pcf50633-irq.c b/drivers/mfd/pcf50633-irq.c new file mode 100644 index 0000000..1b0192f --- /dev/null +++ b/drivers/mfd/pcf50633-irq.c @@ -0,0 +1,318 @@ +/* NXP PCF50633 Power Management Unit (PMU) driver + * + * (C) 2006-2008 by Openmoko, Inc. + * Author: Harald Welte <laforge@openmoko.org> + * Balaji Rao <balajirrao@openmoko.org> + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mutex.h> +#include <linux/slab.h> + +#include <linux/mfd/pcf50633/core.h> + +/* Two MBCS registers used during cold start */ +#define PCF50633_REG_MBCS1 0x4b +#define PCF50633_REG_MBCS2 0x4c +#define PCF50633_MBCS1_USBPRES 0x01 +#define PCF50633_MBCS1_ADAPTPRES 0x01 + +int pcf50633_register_irq(struct pcf50633 *pcf, int irq, + void (*handler) (int, void *), void *data) +{ + if (irq < 0 || irq >= PCF50633_NUM_IRQ || !handler) + return -EINVAL; + + if (WARN_ON(pcf->irq_handler[irq].handler)) + return -EBUSY; + + mutex_lock(&pcf->lock); + pcf->irq_handler[irq].handler = handler; + pcf->irq_handler[irq].data = data; + mutex_unlock(&pcf->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(pcf50633_register_irq); + +int pcf50633_free_irq(struct pcf50633 *pcf, int irq) +{ + if (irq < 0 || irq >= PCF50633_NUM_IRQ) + return -EINVAL; + + mutex_lock(&pcf->lock); + pcf->irq_handler[irq].handler = NULL; + mutex_unlock(&pcf->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(pcf50633_free_irq); + +static int __pcf50633_irq_mask_set(struct pcf50633 *pcf, int irq, u8 mask) +{ + u8 reg, bit; + int ret = 0, idx; + + idx = irq >> 3; + reg = PCF50633_REG_INT1M + idx; + bit = 1 << (irq & 0x07); + + pcf50633_reg_set_bit_mask(pcf, reg, bit, mask ? bit : 0); + + mutex_lock(&pcf->lock); + + if (mask) + pcf->mask_regs[idx] |= bit; + else + pcf->mask_regs[idx] &= ~bit; + + mutex_unlock(&pcf->lock); + + return ret; +} + +int pcf50633_irq_mask(struct pcf50633 *pcf, int irq) +{ + dev_dbg(pcf->dev, "Masking IRQ %d\n", irq); + + return __pcf50633_irq_mask_set(pcf, irq, 1); +} +EXPORT_SYMBOL_GPL(pcf50633_irq_mask); + +int pcf50633_irq_unmask(struct pcf50633 *pcf, int irq) +{ + dev_dbg(pcf->dev, "Unmasking IRQ %d\n", irq); + + return __pcf50633_irq_mask_set(pcf, irq, 0); +} +EXPORT_SYMBOL_GPL(pcf50633_irq_unmask); + +int pcf50633_irq_mask_get(struct pcf50633 *pcf, int irq) +{ + u8 reg, bits; + + reg = irq >> 3; + bits = 1 << (irq & 0x07); + + return pcf->mask_regs[reg] & bits; +} +EXPORT_SYMBOL_GPL(pcf50633_irq_mask_get); + +static void pcf50633_irq_call_handler(struct pcf50633 *pcf, int irq) +{ + if (pcf->irq_handler[irq].handler) + pcf->irq_handler[irq].handler(irq, pcf->irq_handler[irq].data); +} + +/* Maximum amount of time ONKEY is held before emergency action is taken */ +#define PCF50633_ONKEY1S_TIMEOUT 8 + +static irqreturn_t pcf50633_irq(int irq, void *data) +{ + struct pcf50633 *pcf = data; + int ret, i, j; + u8 pcf_int[5], chgstat; + + /* Read the 5 INT regs in one transaction */ + ret = pcf50633_read_block(pcf, PCF50633_REG_INT1, + ARRAY_SIZE(pcf_int), pcf_int); + if (ret != ARRAY_SIZE(pcf_int)) { + dev_err(pcf->dev, "Error reading INT registers\n"); + + /* + * If this doesn't ACK the interrupt to the chip, we'll be + * called once again as we're level triggered. + */ + goto out; + } + + /* defeat 8s death from lowsys on A5 */ + pcf50633_reg_write(pcf, PCF50633_REG_OOCSHDWN, 0x04); + + /* We immediately read the usb and adapter status. We thus make sure + * only of USBINS/USBREM IRQ handlers are called */ + if (pcf_int[0] & (PCF50633_INT1_USBINS | PCF50633_INT1_USBREM)) { + chgstat = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2); + if (chgstat & (0x3 << 4)) + pcf_int[0] &= ~PCF50633_INT1_USBREM; + else + pcf_int[0] &= ~PCF50633_INT1_USBINS; + } + + /* Make sure only one of ADPINS or ADPREM is set */ + if (pcf_int[0] & (PCF50633_INT1_ADPINS | PCF50633_INT1_ADPREM)) { + chgstat = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2); + if (chgstat & (0x3 << 4)) + pcf_int[0] &= ~PCF50633_INT1_ADPREM; + else + pcf_int[0] &= ~PCF50633_INT1_ADPINS; + } + + dev_dbg(pcf->dev, "INT1=0x%02x INT2=0x%02x INT3=0x%02x " + "INT4=0x%02x INT5=0x%02x\n", pcf_int[0], + pcf_int[1], pcf_int[2], pcf_int[3], pcf_int[4]); + + /* Some revisions of the chip don't have a 8s standby mode on + * ONKEY1S press. We try to manually do it in such cases. */ + if ((pcf_int[0] & PCF50633_INT1_SECOND) && pcf->onkey1s_held) { + dev_info(pcf->dev, "ONKEY1S held for %d secs\n", + pcf->onkey1s_held); + if (pcf->onkey1s_held++ == PCF50633_ONKEY1S_TIMEOUT) + if (pcf->pdata->force_shutdown) + pcf->pdata->force_shutdown(pcf); + } + + if (pcf_int[2] & PCF50633_INT3_ONKEY1S) { + dev_info(pcf->dev, "ONKEY1S held\n"); + pcf->onkey1s_held = 1 ; + + /* Unmask IRQ_SECOND */ + pcf50633_reg_clear_bits(pcf, PCF50633_REG_INT1M, + PCF50633_INT1_SECOND); + + /* Unmask IRQ_ONKEYR */ + pcf50633_reg_clear_bits(pcf, PCF50633_REG_INT2M, + PCF50633_INT2_ONKEYR); + } + + if ((pcf_int[1] & PCF50633_INT2_ONKEYR) && pcf->onkey1s_held) { + pcf->onkey1s_held = 0; + + /* Mask SECOND and ONKEYR interrupts */ + if (pcf->mask_regs[0] & PCF50633_INT1_SECOND) + pcf50633_reg_set_bit_mask(pcf, + PCF50633_REG_INT1M, + PCF50633_INT1_SECOND, + PCF50633_INT1_SECOND); + + if (pcf->mask_regs[1] & PCF50633_INT2_ONKEYR) + pcf50633_reg_set_bit_mask(pcf, + PCF50633_REG_INT2M, + PCF50633_INT2_ONKEYR, + PCF50633_INT2_ONKEYR); + } + + /* Have we just resumed ? */ + if (pcf->is_suspended) { + pcf->is_suspended = 0; + + /* Set the resume reason filtering out non resumers */ + for (i = 0; i < ARRAY_SIZE(pcf_int); i++) + pcf->resume_reason[i] = pcf_int[i] & + pcf->pdata->resumers[i]; + + /* Make sure we don't pass on any ONKEY events to + * userspace now */ + pcf_int[1] &= ~(PCF50633_INT2_ONKEYR | PCF50633_INT2_ONKEYF); + } + + for (i = 0; i < ARRAY_SIZE(pcf_int); i++) { + /* Unset masked interrupts */ + pcf_int[i] &= ~pcf->mask_regs[i]; + + for (j = 0; j < 8 ; j++) + if (pcf_int[i] & (1 << j)) + pcf50633_irq_call_handler(pcf, (i * 8) + j); + } + +out: + return IRQ_HANDLED; +} + +#ifdef CONFIG_PM + +int pcf50633_irq_suspend(struct pcf50633 *pcf) +{ + int ret; + int i; + u8 res[5]; + + + /* Make sure our interrupt handlers are not called + * henceforth */ + disable_irq(pcf->irq); + + /* Save the masks */ + ret = pcf50633_read_block(pcf, PCF50633_REG_INT1M, + ARRAY_SIZE(pcf->suspend_irq_masks), + pcf->suspend_irq_masks); + if (ret < 0) { + dev_err(pcf->dev, "error saving irq masks\n"); + goto out; + } + + /* Write wakeup irq masks */ + for (i = 0; i < ARRAY_SIZE(res); i++) + res[i] = ~pcf->pdata->resumers[i]; + + ret = pcf50633_write_block(pcf, PCF50633_REG_INT1M, + ARRAY_SIZE(res), &res[0]); + if (ret < 0) { + dev_err(pcf->dev, "error writing wakeup irq masks\n"); + goto out; + } + + pcf->is_suspended = 1; + +out: + return ret; +} + +int pcf50633_irq_resume(struct pcf50633 *pcf) +{ + int ret; + + /* Write the saved mask registers */ + ret = pcf50633_write_block(pcf, PCF50633_REG_INT1M, + ARRAY_SIZE(pcf->suspend_irq_masks), + pcf->suspend_irq_masks); + if (ret < 0) + dev_err(pcf->dev, "Error restoring saved suspend masks\n"); + + enable_irq(pcf->irq); + + return ret; +} + +#endif + +int pcf50633_irq_init(struct pcf50633 *pcf, int irq) +{ + int ret; + + pcf->irq = irq; + + /* Enable all interrupts except RTC SECOND */ + pcf->mask_regs[0] = 0x80; + pcf50633_reg_write(pcf, PCF50633_REG_INT1M, pcf->mask_regs[0]); + pcf50633_reg_write(pcf, PCF50633_REG_INT2M, 0x00); + pcf50633_reg_write(pcf, PCF50633_REG_INT3M, 0x00); + pcf50633_reg_write(pcf, PCF50633_REG_INT4M, 0x00); + pcf50633_reg_write(pcf, PCF50633_REG_INT5M, 0x00); + + ret = request_threaded_irq(irq, NULL, pcf50633_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "pcf50633", pcf); + + if (ret) + dev_err(pcf->dev, "Failed to request IRQ %d\n", ret); + + if (enable_irq_wake(irq) < 0) + dev_err(pcf->dev, "IRQ %u cannot be enabled as wake-up source" + "in this hardware revision", irq); + + return ret; +} + +void pcf50633_irq_free(struct pcf50633 *pcf) +{ + free_irq(pcf->irq, pcf); +} diff --git a/drivers/mfd/rdc321x-southbridge.c b/drivers/mfd/rdc321x-southbridge.c new file mode 100644 index 0000000..5092297 --- /dev/null +++ b/drivers/mfd/rdc321x-southbridge.c @@ -0,0 +1,123 @@ +/* + * RDC321x MFD southbrige driver + * + * Copyright (C) 2007-2010 Florian Fainelli <florian@openwrt.org> + * Copyright (C) 2010 Bernhard Loos <bernhardloos@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/pci.h> +#include <linux/mfd/core.h> +#include <linux/mfd/rdc321x.h> + +static struct rdc321x_wdt_pdata rdc321x_wdt_pdata; + +static struct resource rdc321x_wdt_resource[] = { + { + .name = "wdt-reg", + .start = RDC321X_WDT_CTRL, + .end = RDC321X_WDT_CTRL + 0x3, + .flags = IORESOURCE_IO, + } +}; + +static struct rdc321x_gpio_pdata rdc321x_gpio_pdata = { + .max_gpios = RDC321X_MAX_GPIO, +}; + +static struct resource rdc321x_gpio_resources[] = { + { + .name = "gpio-reg1", + .start = RDC321X_GPIO_CTRL_REG1, + .end = RDC321X_GPIO_CTRL_REG1 + 0x7, + .flags = IORESOURCE_IO, + }, { + .name = "gpio-reg2", + .start = RDC321X_GPIO_CTRL_REG2, + .end = RDC321X_GPIO_CTRL_REG2 + 0x7, + .flags = IORESOURCE_IO, + } +}; + +static struct mfd_cell rdc321x_sb_cells[] = { + { + .name = "rdc321x-wdt", + .resources = rdc321x_wdt_resource, + .num_resources = ARRAY_SIZE(rdc321x_wdt_resource), + .driver_data = &rdc321x_wdt_pdata, + }, { + .name = "rdc321x-gpio", + .resources = rdc321x_gpio_resources, + .num_resources = ARRAY_SIZE(rdc321x_gpio_resources), + .driver_data = &rdc321x_gpio_pdata, + }, +}; + +static int __devinit rdc321x_sb_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int err; + + err = pci_enable_device(pdev); + if (err) { + dev_err(&pdev->dev, "failed to enable device\n"); + return err; + } + + rdc321x_gpio_pdata.sb_pdev = pdev; + rdc321x_wdt_pdata.sb_pdev = pdev; + + return mfd_add_devices(&pdev->dev, -1, + rdc321x_sb_cells, ARRAY_SIZE(rdc321x_sb_cells), NULL, 0); +} + +static void __devexit rdc321x_sb_remove(struct pci_dev *pdev) +{ + mfd_remove_devices(&pdev->dev); +} + +static DEFINE_PCI_DEVICE_TABLE(rdc321x_sb_table) = { + { PCI_DEVICE(PCI_VENDOR_ID_RDC, PCI_DEVICE_ID_RDC_R6030) }, + {} +}; + +static struct pci_driver rdc321x_sb_driver = { + .name = "RDC321x Southbridge", + .id_table = rdc321x_sb_table, + .probe = rdc321x_sb_probe, + .remove = __devexit_p(rdc321x_sb_remove), +}; + +static int __init rdc321x_sb_init(void) +{ + return pci_register_driver(&rdc321x_sb_driver); +} + +static void __exit rdc321x_sb_exit(void) +{ + pci_unregister_driver(&rdc321x_sb_driver); +} + +module_init(rdc321x_sb_init); +module_exit(rdc321x_sb_exit); + +MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("RDC R-321x MFD southbridge driver"); diff --git a/drivers/mfd/t7l66xb.c b/drivers/mfd/t7l66xb.c index da6383a..5041d33 100644 --- a/drivers/mfd/t7l66xb.c +++ b/drivers/mfd/t7l66xb.c @@ -318,6 +318,9 @@ static int t7l66xb_probe(struct platform_device *dev) struct resource *iomem, *rscr; int ret; + if (pdata == NULL) + return -EINVAL; + iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); if (!iomem) return -EINVAL; diff --git a/drivers/mfd/tc35892.c b/drivers/mfd/tc35892.c new file mode 100644 index 0000000..715f095 --- /dev/null +++ b/drivers/mfd/tc35892.c @@ -0,0 +1,347 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License, version 2 + * Author: Hanumath Prasad <hanumath.prasad@stericsson.com> for ST-Ericsson + * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson + */ + +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/mfd/core.h> +#include <linux/mfd/tc35892.h> + +/** + * tc35892_reg_read() - read a single TC35892 register + * @tc35892: Device to read from + * @reg: Register to read + */ +int tc35892_reg_read(struct tc35892 *tc35892, u8 reg) +{ + int ret; + + ret = i2c_smbus_read_byte_data(tc35892->i2c, reg); + if (ret < 0) + dev_err(tc35892->dev, "failed to read reg %#x: %d\n", + reg, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(tc35892_reg_read); + +/** + * tc35892_reg_read() - write a single TC35892 register + * @tc35892: Device to write to + * @reg: Register to read + * @data: Value to write + */ +int tc35892_reg_write(struct tc35892 *tc35892, u8 reg, u8 data) +{ + int ret; + + ret = i2c_smbus_write_byte_data(tc35892->i2c, reg, data); + if (ret < 0) + dev_err(tc35892->dev, "failed to write reg %#x: %d\n", + reg, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(tc35892_reg_write); + +/** + * tc35892_block_read() - read multiple TC35892 registers + * @tc35892: Device to read from + * @reg: First register + * @length: Number of registers + * @values: Buffer to write to + */ +int tc35892_block_read(struct tc35892 *tc35892, u8 reg, u8 length, u8 *values) +{ + int ret; + + ret = i2c_smbus_read_i2c_block_data(tc35892->i2c, reg, length, values); + if (ret < 0) + dev_err(tc35892->dev, "failed to read regs %#x: %d\n", + reg, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(tc35892_block_read); + +/** + * tc35892_block_write() - write multiple TC35892 registers + * @tc35892: Device to write to + * @reg: First register + * @length: Number of registers + * @values: Values to write + */ +int tc35892_block_write(struct tc35892 *tc35892, u8 reg, u8 length, + const u8 *values) +{ + int ret; + + ret = i2c_smbus_write_i2c_block_data(tc35892->i2c, reg, length, + values); + if (ret < 0) + dev_err(tc35892->dev, "failed to write regs %#x: %d\n", + reg, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(tc35892_block_write); + +/** + * tc35892_set_bits() - set the value of a bitfield in a TC35892 register + * @tc35892: Device to write to + * @reg: Register to write + * @mask: Mask of bits to set + * @values: Value to set + */ +int tc35892_set_bits(struct tc35892 *tc35892, u8 reg, u8 mask, u8 val) +{ + int ret; + + mutex_lock(&tc35892->lock); + + ret = tc35892_reg_read(tc35892, reg); + if (ret < 0) + goto out; + + ret &= ~mask; + ret |= val; + + ret = tc35892_reg_write(tc35892, reg, ret); + +out: + mutex_unlock(&tc35892->lock); + return ret; +} +EXPORT_SYMBOL_GPL(tc35892_set_bits); + +static struct resource gpio_resources[] = { + { + .start = TC35892_INT_GPIIRQ, + .end = TC35892_INT_GPIIRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell tc35892_devs[] = { + { + .name = "tc35892-gpio", + .num_resources = ARRAY_SIZE(gpio_resources), + .resources = &gpio_resources[0], + }, +}; + +static irqreturn_t tc35892_irq(int irq, void *data) +{ + struct tc35892 *tc35892 = data; + int status; + + status = tc35892_reg_read(tc35892, TC35892_IRQST); + if (status < 0) + return IRQ_NONE; + + while (status) { + int bit = __ffs(status); + + handle_nested_irq(tc35892->irq_base + bit); + status &= ~(1 << bit); + } + + /* + * A dummy read or write (to any register) appears to be necessary to + * have the last interrupt clear (for example, GPIO IC write) take + * effect. + */ + tc35892_reg_read(tc35892, TC35892_IRQST); + + return IRQ_HANDLED; +} + +static void tc35892_irq_dummy(unsigned int irq) +{ + /* No mask/unmask at this level */ +} + +static struct irq_chip tc35892_irq_chip = { + .name = "tc35892", + .mask = tc35892_irq_dummy, + .unmask = tc35892_irq_dummy, +}; + +static int tc35892_irq_init(struct tc35892 *tc35892) +{ + int base = tc35892->irq_base; + int irq; + + for (irq = base; irq < base + TC35892_NR_INTERNAL_IRQS; irq++) { + set_irq_chip_data(irq, tc35892); + set_irq_chip_and_handler(irq, &tc35892_irq_chip, + handle_edge_irq); + set_irq_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + set_irq_noprobe(irq); +#endif + } + + return 0; +} + +static void tc35892_irq_remove(struct tc35892 *tc35892) +{ + int base = tc35892->irq_base; + int irq; + + for (irq = base; irq < base + TC35892_NR_INTERNAL_IRQS; irq++) { +#ifdef CONFIG_ARM + set_irq_flags(irq, 0); +#endif + set_irq_chip_and_handler(irq, NULL, NULL); + set_irq_chip_data(irq, NULL); + } +} + +static int tc35892_chip_init(struct tc35892 *tc35892) +{ + int manf, ver, ret; + + manf = tc35892_reg_read(tc35892, TC35892_MANFCODE); + if (manf < 0) + return manf; + + ver = tc35892_reg_read(tc35892, TC35892_VERSION); + if (ver < 0) + return ver; + + if (manf != TC35892_MANFCODE_MAGIC) { + dev_err(tc35892->dev, "unknown manufacturer: %#x\n", manf); + return -EINVAL; + } + + dev_info(tc35892->dev, "manufacturer: %#x, version: %#x\n", manf, ver); + + /* Put everything except the IRQ module into reset */ + ret = tc35892_reg_write(tc35892, TC35892_RSTCTRL, + TC35892_RSTCTRL_TIMRST + | TC35892_RSTCTRL_ROTRST + | TC35892_RSTCTRL_KBDRST + | TC35892_RSTCTRL_GPIRST); + if (ret < 0) + return ret; + + /* Clear the reset interrupt. */ + return tc35892_reg_write(tc35892, TC35892_RSTINTCLR, 0x1); +} + +static int __devinit tc35892_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct tc35892_platform_data *pdata = i2c->dev.platform_data; + struct tc35892 *tc35892; + int ret; + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA + | I2C_FUNC_SMBUS_I2C_BLOCK)) + return -EIO; + + tc35892 = kzalloc(sizeof(struct tc35892), GFP_KERNEL); + if (!tc35892) + return -ENOMEM; + + mutex_init(&tc35892->lock); + + tc35892->dev = &i2c->dev; + tc35892->i2c = i2c; + tc35892->pdata = pdata; + tc35892->irq_base = pdata->irq_base; + tc35892->num_gpio = id->driver_data; + + i2c_set_clientdata(i2c, tc35892); + + ret = tc35892_chip_init(tc35892); + if (ret) + goto out_free; + + ret = tc35892_irq_init(tc35892); + if (ret) + goto out_free; + + ret = request_threaded_irq(tc35892->i2c->irq, NULL, tc35892_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "tc35892", tc35892); + if (ret) { + dev_err(tc35892->dev, "failed to request IRQ: %d\n", ret); + goto out_removeirq; + } + + ret = mfd_add_devices(tc35892->dev, -1, tc35892_devs, + ARRAY_SIZE(tc35892_devs), NULL, + tc35892->irq_base); + if (ret) { + dev_err(tc35892->dev, "failed to add children\n"); + goto out_freeirq; + } + + return 0; + +out_freeirq: + free_irq(tc35892->i2c->irq, tc35892); +out_removeirq: + tc35892_irq_remove(tc35892); +out_free: + i2c_set_clientdata(i2c, NULL); + kfree(tc35892); + return ret; +} + +static int __devexit tc35892_remove(struct i2c_client *client) +{ + struct tc35892 *tc35892 = i2c_get_clientdata(client); + + mfd_remove_devices(tc35892->dev); + + free_irq(tc35892->i2c->irq, tc35892); + tc35892_irq_remove(tc35892); + + i2c_set_clientdata(client, NULL); + kfree(tc35892); + + return 0; +} + +static const struct i2c_device_id tc35892_id[] = { + { "tc35892", 24 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tc35892_id); + +static struct i2c_driver tc35892_driver = { + .driver.name = "tc35892", + .driver.owner = THIS_MODULE, + .probe = tc35892_probe, + .remove = __devexit_p(tc35892_remove), + .id_table = tc35892_id, +}; + +static int __init tc35892_init(void) +{ + return i2c_add_driver(&tc35892_driver); +} +subsys_initcall(tc35892_init); + +static void __exit tc35892_exit(void) +{ + i2c_del_driver(&tc35892_driver); +} +module_exit(tc35892_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TC35892 MFD core driver"); +MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent"); diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c index 7f478ec..ac59950 100644 --- a/drivers/mfd/timberdale.c +++ b/drivers/mfd/timberdale.c @@ -31,6 +31,7 @@ #include <linux/i2c.h> #include <linux/i2c-ocores.h> +#include <linux/i2c-xiic.h> #include <linux/i2c/tsc2007.h> #include <linux/spi/spi.h> @@ -40,6 +41,8 @@ #include <media/timb_radio.h> +#include <linux/timb_dma.h> + #include "timberdale.h" #define DRIVER_NAME "timberdale" @@ -69,6 +72,12 @@ static struct i2c_board_info timberdale_i2c_board_info[] = { }, }; +static __devinitdata struct xiic_i2c_platform_data +timberdale_xiic_platform_data = { + .devices = timberdale_i2c_board_info, + .num_devices = ARRAY_SIZE(timberdale_i2c_board_info) +}; + static __devinitdata struct ocores_i2c_platform_data timberdale_ocores_platform_data = { .regstep = 4, @@ -77,7 +86,20 @@ timberdale_ocores_platform_data = { .num_devices = ARRAY_SIZE(timberdale_i2c_board_info) }; -const static __devinitconst struct resource timberdale_ocores_resources[] = { +static const __devinitconst struct resource timberdale_xiic_resources[] = { + { + .start = XIICOFFSET, + .end = XIICEND, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_TIMBERDALE_I2C, + .end = IRQ_TIMBERDALE_I2C, + .flags = IORESOURCE_IRQ, + }, +}; + +static const __devinitconst struct resource timberdale_ocores_resources[] = { { .start = OCORESOFFSET, .end = OCORESEND, @@ -126,7 +148,7 @@ static __devinitdata struct xspi_platform_data timberdale_xspi_platform_data = { */ }; -const static __devinitconst struct resource timberdale_spi_resources[] = { +static const __devinitconst struct resource timberdale_spi_resources[] = { { .start = SPIOFFSET, .end = SPIEND, @@ -139,7 +161,7 @@ const static __devinitconst struct resource timberdale_spi_resources[] = { }, }; -const static __devinitconst struct resource timberdale_eth_resources[] = { +static const __devinitconst struct resource timberdale_eth_resources[] = { { .start = ETHOFFSET, .end = ETHEND, @@ -159,7 +181,7 @@ static __devinitdata struct timbgpio_platform_data .irq_base = 200, }; -const static __devinitconst struct resource timberdale_gpio_resources[] = { +static const __devinitconst struct resource timberdale_gpio_resources[] = { { .start = GPIOOFFSET, .end = GPIOEND, @@ -172,7 +194,7 @@ const static __devinitconst struct resource timberdale_gpio_resources[] = { }, }; -const static __devinitconst struct resource timberdale_mlogicore_resources[] = { +static const __devinitconst struct resource timberdale_mlogicore_resources[] = { { .start = MLCOREOFFSET, .end = MLCOREEND, @@ -190,7 +212,7 @@ const static __devinitconst struct resource timberdale_mlogicore_resources[] = { }, }; -const static __devinitconst struct resource timberdale_uart_resources[] = { +static const __devinitconst struct resource timberdale_uart_resources[] = { { .start = UARTOFFSET, .end = UARTEND, @@ -203,7 +225,7 @@ const static __devinitconst struct resource timberdale_uart_resources[] = { }, }; -const static __devinitconst struct resource timberdale_uartlite_resources[] = { +static const __devinitconst struct resource timberdale_uartlite_resources[] = { { .start = UARTLITEOFFSET, .end = UARTLITEEND, @@ -216,7 +238,7 @@ const static __devinitconst struct resource timberdale_uartlite_resources[] = { }, }; -const static __devinitconst struct resource timberdale_radio_resources[] = { +static const __devinitconst struct resource timberdale_radio_resources[] = { { .start = RDSOFFSET, .end = RDSEND, @@ -250,7 +272,66 @@ static __devinitdata struct timb_radio_platform_data } }; -const static __devinitconst struct resource timberdale_dma_resources[] = { +static __devinitdata struct timb_dma_platform_data timb_dma_platform_data = { + .nr_channels = 10, + .channels = { + { + /* UART RX */ + .rx = true, + .descriptors = 2, + .descriptor_elements = 1 + }, + { + /* UART TX */ + .rx = false, + .descriptors = 2, + .descriptor_elements = 1 + }, + { + /* MLB RX */ + .rx = true, + .descriptors = 2, + .descriptor_elements = 1 + }, + { + /* MLB TX */ + .rx = false, + .descriptors = 2, + .descriptor_elements = 1 + }, + { + /* Video RX */ + .rx = true, + .bytes_per_line = 1440, + .descriptors = 2, + .descriptor_elements = 16 + }, + { + /* Video framedrop */ + }, + { + /* SDHCI RX */ + .rx = true, + }, + { + /* SDHCI TX */ + }, + { + /* ETH RX */ + .rx = true, + .descriptors = 2, + .descriptor_elements = 1 + }, + { + /* ETH TX */ + .rx = false, + .descriptors = 2, + .descriptor_elements = 1 + }, + } +}; + +static const __devinitconst struct resource timberdale_dma_resources[] = { { .start = DMAOFFSET, .end = DMAEND, @@ -265,11 +346,25 @@ const static __devinitconst struct resource timberdale_dma_resources[] = { static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg0[] = { { + .name = "timb-dma", + .num_resources = ARRAY_SIZE(timberdale_dma_resources), + .resources = timberdale_dma_resources, + .platform_data = &timb_dma_platform_data, + .data_size = sizeof(timb_dma_platform_data), + }, + { .name = "timb-uart", .num_resources = ARRAY_SIZE(timberdale_uart_resources), .resources = timberdale_uart_resources, }, { + .name = "xiic-i2c", + .num_resources = ARRAY_SIZE(timberdale_xiic_resources), + .resources = timberdale_xiic_resources, + .platform_data = &timberdale_xiic_platform_data, + .data_size = sizeof(timberdale_xiic_platform_data), + }, + { .name = "timb-gpio", .num_resources = ARRAY_SIZE(timberdale_gpio_resources), .resources = timberdale_gpio_resources, @@ -295,14 +390,16 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg0[] = { .num_resources = ARRAY_SIZE(timberdale_eth_resources), .resources = timberdale_eth_resources, }, +}; + +static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = { { .name = "timb-dma", .num_resources = ARRAY_SIZE(timberdale_dma_resources), .resources = timberdale_dma_resources, + .platform_data = &timb_dma_platform_data, + .data_size = sizeof(timb_dma_platform_data), }, -}; - -static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = { { .name = "timb-uart", .num_resources = ARRAY_SIZE(timberdale_uart_resources), @@ -314,6 +411,13 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = { .resources = timberdale_uartlite_resources, }, { + .name = "xiic-i2c", + .num_resources = ARRAY_SIZE(timberdale_xiic_resources), + .resources = timberdale_xiic_resources, + .platform_data = &timberdale_xiic_platform_data, + .data_size = sizeof(timberdale_xiic_platform_data), + }, + { .name = "timb-gpio", .num_resources = ARRAY_SIZE(timberdale_gpio_resources), .resources = timberdale_gpio_resources, @@ -344,20 +448,29 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = { .num_resources = ARRAY_SIZE(timberdale_eth_resources), .resources = timberdale_eth_resources, }, +}; + +static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg2[] = { { .name = "timb-dma", .num_resources = ARRAY_SIZE(timberdale_dma_resources), .resources = timberdale_dma_resources, + .platform_data = &timb_dma_platform_data, + .data_size = sizeof(timb_dma_platform_data), }, -}; - -static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg2[] = { { .name = "timb-uart", .num_resources = ARRAY_SIZE(timberdale_uart_resources), .resources = timberdale_uart_resources, }, { + .name = "xiic-i2c", + .num_resources = ARRAY_SIZE(timberdale_xiic_resources), + .resources = timberdale_xiic_resources, + .platform_data = &timberdale_xiic_platform_data, + .data_size = sizeof(timberdale_xiic_platform_data), + }, + { .name = "timb-gpio", .num_resources = ARRAY_SIZE(timberdale_gpio_resources), .resources = timberdale_gpio_resources, @@ -378,14 +491,16 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg2[] = { .platform_data = &timberdale_xspi_platform_data, .data_size = sizeof(timberdale_xspi_platform_data), }, +}; + +static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = { { .name = "timb-dma", .num_resources = ARRAY_SIZE(timberdale_dma_resources), .resources = timberdale_dma_resources, + .platform_data = &timb_dma_platform_data, + .data_size = sizeof(timb_dma_platform_data), }, -}; - -static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = { { .name = "timb-uart", .num_resources = ARRAY_SIZE(timberdale_uart_resources), @@ -424,11 +539,6 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = { .num_resources = ARRAY_SIZE(timberdale_eth_resources), .resources = timberdale_eth_resources, }, - { - .name = "timb-dma", - .num_resources = ARRAY_SIZE(timberdale_dma_resources), - .resources = timberdale_dma_resources, - }, }; static const __devinitconst struct resource timberdale_sdhc_resources[] = { diff --git a/drivers/mfd/timberdale.h b/drivers/mfd/timberdale.h index 8d27ffa..c11bf6e 100644 --- a/drivers/mfd/timberdale.h +++ b/drivers/mfd/timberdale.h @@ -23,7 +23,7 @@ #ifndef MFD_TIMBERDALE_H #define MFD_TIMBERDALE_H -#define DRV_VERSION "0.1" +#define DRV_VERSION "0.2" /* This driver only support versions >= 3.8 and < 4.0 */ #define TIMB_SUPPORTED_MAJOR 3 @@ -66,7 +66,7 @@ #define CHIPCTLOFFSET 0x800 #define CHIPCTLEND 0x8ff -#define CHIPCTLSIZE (CHIPCTLEND - CHIPCTLOFFSET) +#define CHIPCTLSIZE (CHIPCTLEND - CHIPCTLOFFSET + 1) #define INTCOFFSET 0xc00 #define INTCEND 0xfff @@ -127,4 +127,16 @@ #define GPIO_PIN_BT_RST 15 #define GPIO_NR_PINS 16 +/* DMA Channels */ +#define DMA_UART_RX 0 +#define DMA_UART_TX 1 +#define DMA_MLB_RX 2 +#define DMA_MLB_TX 3 +#define DMA_VIDEO_RX 4 +#define DMA_VIDEO_DROP 5 +#define DMA_SDHCI_RX 6 +#define DMA_SDHCI_TX 7 +#define DMA_ETH_RX 8 +#define DMA_ETH_TX 9 + #endif diff --git a/drivers/mfd/tps65010.c b/drivers/mfd/tps65010.c index e595530..9b22a77 100644 --- a/drivers/mfd/tps65010.c +++ b/drivers/mfd/tps65010.c @@ -530,8 +530,8 @@ static int __exit tps65010_remove(struct i2c_client *client) cancel_delayed_work(&tps->work); flush_scheduled_work(); debugfs_remove(tps->file); - kfree(tps); i2c_set_clientdata(client, NULL); + kfree(tps); the_tps = NULL; return 0; } diff --git a/drivers/mfd/tps6507x.c b/drivers/mfd/tps6507x.c new file mode 100644 index 0000000..d859dff --- /dev/null +++ b/drivers/mfd/tps6507x.c @@ -0,0 +1,159 @@ +/* + * tps6507x.c -- TPS6507x chip family multi-function driver + * + * Copyright (c) 2010 RidgeRun (todd.fischer@ridgerun.com) + * + * Author: Todd Fischer + * todd.fischer@ridgerun.com + * + * Credits: + * + * Using code from wm831x-*.c, wm8400-core, Wolfson Microelectronics PLC. + * + * For licencing details see kernel-base/COPYING + * + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/mfd/core.h> +#include <linux/mfd/tps6507x.h> + +static struct mfd_cell tps6507x_devs[] = { + { + .name = "tps6507x-pmic", + }, + { + .name = "tps6507x-ts", + }, +}; + + +static int tps6507x_i2c_read_device(struct tps6507x_dev *tps6507x, char reg, + int bytes, void *dest) +{ + struct i2c_client *i2c = tps6507x->i2c_client; + struct i2c_msg xfer[2]; + int ret; + + /* Write register */ + xfer[0].addr = i2c->addr; + xfer[0].flags = 0; + xfer[0].len = 1; + xfer[0].buf = ® + + /* Read data */ + xfer[1].addr = i2c->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = bytes; + xfer[1].buf = dest; + + ret = i2c_transfer(i2c->adapter, xfer, 2); + if (ret == 2) + ret = 0; + else if (ret >= 0) + ret = -EIO; + + return ret; +} + +static int tps6507x_i2c_write_device(struct tps6507x_dev *tps6507x, char reg, + int bytes, void *src) +{ + struct i2c_client *i2c = tps6507x->i2c_client; + /* we add 1 byte for device register */ + u8 msg[TPS6507X_MAX_REGISTER + 1]; + int ret; + + if (bytes > (TPS6507X_MAX_REGISTER + 1)) + return -EINVAL; + + msg[0] = reg; + memcpy(&msg[1], src, bytes); + + ret = i2c_master_send(i2c, msg, bytes + 1); + if (ret < 0) + return ret; + if (ret != bytes + 1) + return -EIO; + return 0; +} + +static int tps6507x_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct tps6507x_dev *tps6507x; + int ret = 0; + + tps6507x = kzalloc(sizeof(struct tps6507x_dev), GFP_KERNEL); + if (tps6507x == NULL) { + kfree(i2c); + return -ENOMEM; + } + + i2c_set_clientdata(i2c, tps6507x); + tps6507x->dev = &i2c->dev; + tps6507x->i2c_client = i2c; + tps6507x->read_dev = tps6507x_i2c_read_device; + tps6507x->write_dev = tps6507x_i2c_write_device; + + ret = mfd_add_devices(tps6507x->dev, -1, + tps6507x_devs, ARRAY_SIZE(tps6507x_devs), + NULL, 0); + + if (ret < 0) + goto err; + + return ret; + +err: + mfd_remove_devices(tps6507x->dev); + kfree(tps6507x); + return ret; +} + +static int tps6507x_i2c_remove(struct i2c_client *i2c) +{ + struct tps6507x_dev *tps6507x = i2c_get_clientdata(i2c); + + mfd_remove_devices(tps6507x->dev); + kfree(tps6507x); + + return 0; +} + +static const struct i2c_device_id tps6507x_i2c_id[] = { + { "tps6507x", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tps6507x_i2c_id); + + +static struct i2c_driver tps6507x_i2c_driver = { + .driver = { + .name = "tps6507x", + .owner = THIS_MODULE, + }, + .probe = tps6507x_i2c_probe, + .remove = tps6507x_i2c_remove, + .id_table = tps6507x_i2c_id, +}; + +static int __init tps6507x_i2c_init(void) +{ + return i2c_add_driver(&tps6507x_i2c_driver); +} +/* init early so consumer devices can complete system boot */ +subsys_initcall(tps6507x_i2c_init); + +static void __exit tps6507x_i2c_exit(void) +{ + i2c_del_driver(&tps6507x_i2c_driver); +} +module_exit(tps6507x_i2c_exit); + +MODULE_DESCRIPTION("TPS6507x chip family multi-function driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index 202bdd5..097f24d 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c @@ -232,10 +232,11 @@ static const struct sih sih_modules_twl5031[8] = { }, [6] = { /* - * ACI doesn't use the same SIH organization. - * For example, it supports only one interrupt line + * ECI/DBI doesn't use the same SIH organization. + * For example, it supports only one interrupt output line. + * That is, the interrupts are seen on both INT1 and INT2 lines. */ - .name = "aci", + .name = "eci_dbi", .module = TWL5031_MODULE_ACCESSORY, .bits = 9, .bytes_ixr = 2, @@ -247,8 +248,8 @@ static const struct sih sih_modules_twl5031[8] = { }, [7] = { - /* Accessory */ - .name = "acc", + /* Audio accessory */ + .name = "audio", .module = TWL5031_MODULE_ACCESSORY, .control_offset = TWL5031_ACCSIHCTRL, .bits = 2, diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c index f2ab025..1a968f3 100644 --- a/drivers/mfd/wm831x-core.c +++ b/drivers/mfd/wm831x-core.c @@ -322,7 +322,11 @@ EXPORT_SYMBOL_GPL(wm831x_set_bits); */ int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input) { - int ret, src; + int ret, src, irq_masked, timeout; + + /* Are we using the interrupt? */ + irq_masked = wm831x_reg_read(wm831x, WM831X_INTERRUPT_STATUS_1_MASK); + irq_masked &= WM831X_AUXADC_DATA_EINT; mutex_lock(&wm831x->auxadc_lock); @@ -342,6 +346,9 @@ int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input) goto out; } + /* Clear any notification from a very late arriving interrupt */ + try_wait_for_completion(&wm831x->auxadc_done); + ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, WM831X_AUX_CVT_ENA, WM831X_AUX_CVT_ENA); if (ret < 0) { @@ -349,22 +356,46 @@ int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input) goto disable; } - /* If an interrupt arrived late clean up after it */ - try_wait_for_completion(&wm831x->auxadc_done); - - /* Ignore the result to allow us to soldier on without IRQ hookup */ - wait_for_completion_timeout(&wm831x->auxadc_done, msecs_to_jiffies(5)); - - ret = wm831x_reg_read(wm831x, WM831X_AUXADC_CONTROL); - if (ret < 0) { - dev_err(wm831x->dev, "AUXADC status read failed: %d\n", ret); - goto disable; - } - - if (ret & WM831X_AUX_CVT_ENA) { - dev_err(wm831x->dev, "Timed out reading AUXADC\n"); - ret = -EBUSY; - goto disable; + if (irq_masked) { + /* If we're not using interrupts then poll the + * interrupt status register */ + timeout = 5; + while (timeout) { + msleep(1); + + ret = wm831x_reg_read(wm831x, + WM831X_INTERRUPT_STATUS_1); + if (ret < 0) { + dev_err(wm831x->dev, + "ISR 1 read failed: %d\n", ret); + goto disable; + } + + /* Did it complete? */ + if (ret & WM831X_AUXADC_DATA_EINT) { + wm831x_reg_write(wm831x, + WM831X_INTERRUPT_STATUS_1, + WM831X_AUXADC_DATA_EINT); + break; + } else { + dev_err(wm831x->dev, + "AUXADC conversion timeout\n"); + ret = -EBUSY; + goto disable; + } + } + } else { + /* If we are using interrupts then wait for the + * interrupt to complete. Use an extremely long + * timeout to handle situations with heavy load where + * the notification of the interrupt may be delayed by + * threaded IRQ handling. */ + if (!wait_for_completion_timeout(&wm831x->auxadc_done, + msecs_to_jiffies(500))) { + dev_err(wm831x->dev, "Timed out waiting for AUXADC\n"); + ret = -EBUSY; + goto disable; + } } ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA); @@ -1463,6 +1494,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) case WM8310: parent = WM8310; wm831x->num_gpio = 16; + wm831x->charger_irq_wake = 1; if (rev > 0) { wm831x->has_gpio_ena = 1; wm831x->has_cs_sts = 1; @@ -1474,6 +1506,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) case WM8311: parent = WM8311; wm831x->num_gpio = 16; + wm831x->charger_irq_wake = 1; if (rev > 0) { wm831x->has_gpio_ena = 1; wm831x->has_cs_sts = 1; @@ -1485,6 +1518,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) case WM8312: parent = WM8312; wm831x->num_gpio = 16; + wm831x->charger_irq_wake = 1; if (rev > 0) { wm831x->has_gpio_ena = 1; wm831x->has_cs_sts = 1; @@ -1623,6 +1657,42 @@ static void wm831x_device_exit(struct wm831x *wm831x) kfree(wm831x); } +static int wm831x_device_suspend(struct wm831x *wm831x) +{ + int reg, mask; + + /* If the charger IRQs are a wake source then make sure we ack + * them even if they're not actively being used (eg, no power + * driver or no IRQ line wired up) then acknowledge the + * interrupts otherwise suspend won't last very long. + */ + if (wm831x->charger_irq_wake) { + reg = wm831x_reg_read(wm831x, WM831X_INTERRUPT_STATUS_2_MASK); + + mask = WM831X_CHG_BATT_HOT_EINT | + WM831X_CHG_BATT_COLD_EINT | + WM831X_CHG_BATT_FAIL_EINT | + WM831X_CHG_OV_EINT | WM831X_CHG_END_EINT | + WM831X_CHG_TO_EINT | WM831X_CHG_MODE_EINT | + WM831X_CHG_START_EINT; + + /* If any of the interrupts are masked read the statuses */ + if (reg & mask) + reg = wm831x_reg_read(wm831x, + WM831X_INTERRUPT_STATUS_2); + + if (reg & mask) { + dev_info(wm831x->dev, + "Acknowledging masked charger IRQs: %x\n", + reg & mask); + wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_2, + reg & mask); + } + } + + return 0; +} + static int wm831x_i2c_read_device(struct wm831x *wm831x, unsigned short reg, int bytes, void *dest) { @@ -1697,6 +1767,13 @@ static int wm831x_i2c_remove(struct i2c_client *i2c) return 0; } +static int wm831x_i2c_suspend(struct i2c_client *i2c, pm_message_t mesg) +{ + struct wm831x *wm831x = i2c_get_clientdata(i2c); + + return wm831x_device_suspend(wm831x); +} + static const struct i2c_device_id wm831x_i2c_id[] = { { "wm8310", WM8310 }, { "wm8311", WM8311 }, @@ -1714,6 +1791,7 @@ static struct i2c_driver wm831x_i2c_driver = { }, .probe = wm831x_i2c_probe, .remove = wm831x_i2c_remove, + .suspend = wm831x_i2c_suspend, .id_table = wm831x_i2c_id, }; diff --git a/drivers/mfd/wm831x-irq.c b/drivers/mfd/wm831x-irq.c index 4c1122c..7dabe4d 100644 --- a/drivers/mfd/wm831x-irq.c +++ b/drivers/mfd/wm831x-irq.c @@ -39,8 +39,6 @@ struct wm831x_irq_data { int primary; int reg; int mask; - irq_handler_t handler; - void *handler_data; }; static struct wm831x_irq_data wm831x_irqs[] = { @@ -492,6 +490,14 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq) mutex_init(&wm831x->irq_lock); + /* Mask the individual interrupt sources */ + for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) { + wm831x->irq_masks_cur[i] = 0xffff; + wm831x->irq_masks_cache[i] = 0xffff; + wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1_MASK + i, + 0xffff); + } + if (!irq) { dev_warn(wm831x->dev, "No interrupt specified - functionality limited\n"); @@ -507,14 +513,6 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq) wm831x->irq = irq; wm831x->irq_base = pdata->irq_base; - /* Mask the individual interrupt sources */ - for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) { - wm831x->irq_masks_cur[i] = 0xffff; - wm831x->irq_masks_cache[i] = 0xffff; - wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1_MASK + i, - 0xffff); - } - /* Register them with genirq */ for (cur_irq = wm831x->irq_base; cur_irq < ARRAY_SIZE(wm831x_irqs) + wm831x->irq_base; diff --git a/drivers/mfd/wm8350-i2c.c b/drivers/mfd/wm8350-i2c.c index 65830f5..7795af4 100644 --- a/drivers/mfd/wm8350-i2c.c +++ b/drivers/mfd/wm8350-i2c.c @@ -64,10 +64,8 @@ static int wm8350_i2c_probe(struct i2c_client *i2c, int ret = 0; wm8350 = kzalloc(sizeof(struct wm8350), GFP_KERNEL); - if (wm8350 == NULL) { - kfree(i2c); + if (wm8350 == NULL) return -ENOMEM; - } i2c_set_clientdata(i2c, wm8350); wm8350->dev = &i2c->dev; @@ -82,6 +80,7 @@ static int wm8350_i2c_probe(struct i2c_client *i2c, return ret; err: + i2c_set_clientdata(i2c, NULL); kfree(wm8350); return ret; } @@ -91,6 +90,7 @@ static int wm8350_i2c_remove(struct i2c_client *i2c) struct wm8350 *wm8350 = i2c_get_clientdata(i2c); wm8350_device_exit(wm8350); + i2c_set_clientdata(i2c, NULL); kfree(wm8350); return 0; diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c index 865ce01..e08aafa 100644 --- a/drivers/mfd/wm8400-core.c +++ b/drivers/mfd/wm8400-core.c @@ -118,7 +118,7 @@ static int wm8400_read(struct wm8400 *wm8400, u8 reg, int num_regs, u16 *dest) { int i, ret = 0; - BUG_ON(reg + num_regs - 1 > ARRAY_SIZE(wm8400->reg_cache)); + BUG_ON(reg + num_regs > ARRAY_SIZE(wm8400->reg_cache)); /* If there are any volatile reads then read back the entire block */ for (i = reg; i < reg + num_regs; i++) @@ -144,7 +144,7 @@ static int wm8400_write(struct wm8400 *wm8400, u8 reg, int num_regs, { int ret, i; - BUG_ON(reg + num_regs - 1 > ARRAY_SIZE(wm8400->reg_cache)); + BUG_ON(reg + num_regs > ARRAY_SIZE(wm8400->reg_cache)); for (i = 0; i < num_regs; i++) { BUG_ON(!reg_data[reg + i].writable); diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index 05b7517..2c5227c 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -63,6 +63,16 @@ config CAN_BFIN To compile this driver as a module, choose M here: the module will be called bfin_can. +config CAN_JANZ_ICAN3 + tristate "Janz VMOD-ICAN3 Intelligent CAN controller" + depends on CAN_DEV && MFD_JANZ_CMODIO + ---help--- + Driver for Janz VMOD-ICAN3 Intelligent CAN controller module, which + connects to a MODULbus carrier board. + + This driver can also be built as a module. If so, the module will be + called janz-ican3.ko. + source "drivers/net/can/mscan/Kconfig" source "drivers/net/can/sja1000/Kconfig" diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index 7a702f2..9047cd0 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -15,5 +15,6 @@ obj-$(CONFIG_CAN_AT91) += at91_can.o obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o obj-$(CONFIG_CAN_MCP251X) += mcp251x.o obj-$(CONFIG_CAN_BFIN) += bfin_can.o +obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c new file mode 100644 index 0000000..6e533dc --- /dev/null +++ b/drivers/net/can/janz-ican3.c @@ -0,0 +1,1830 @@ +/* + * Janz MODULbus VMOD-ICAN3 CAN Interface 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/netdevice.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> + +#include <linux/mfd/janz.h> + +/* the DPM has 64k of memory, organized into 256x 256 byte pages */ +#define DPM_NUM_PAGES 256 +#define DPM_PAGE_SIZE 256 +#define DPM_PAGE_ADDR(p) ((p) * DPM_PAGE_SIZE) + +/* JANZ ICAN3 "old-style" host interface queue page numbers */ +#define QUEUE_OLD_CONTROL 0 +#define QUEUE_OLD_RB0 1 +#define QUEUE_OLD_RB1 2 +#define QUEUE_OLD_WB0 3 +#define QUEUE_OLD_WB1 4 + +/* Janz ICAN3 "old-style" host interface control registers */ +#define MSYNC_PEER 0x00 /* ICAN only */ +#define MSYNC_LOCL 0x01 /* host only */ +#define TARGET_RUNNING 0x02 + +#define MSYNC_RB0 0x01 +#define MSYNC_RB1 0x02 +#define MSYNC_RBLW 0x04 +#define MSYNC_RB_MASK (MSYNC_RB0 | MSYNC_RB1) + +#define MSYNC_WB0 0x10 +#define MSYNC_WB1 0x20 +#define MSYNC_WBLW 0x40 +#define MSYNC_WB_MASK (MSYNC_WB0 | MSYNC_WB1) + +/* Janz ICAN3 "new-style" host interface queue page numbers */ +#define QUEUE_TOHOST 5 +#define QUEUE_FROMHOST_MID 6 +#define QUEUE_FROMHOST_HIGH 7 +#define QUEUE_FROMHOST_LOW 8 + +/* The first free page in the DPM is #9 */ +#define DPM_FREE_START 9 + +/* Janz ICAN3 "new-style" and "fast" host interface descriptor flags */ +#define DESC_VALID 0x80 +#define DESC_WRAP 0x40 +#define DESC_INTERRUPT 0x20 +#define DESC_IVALID 0x10 +#define DESC_LEN(len) (len) + +/* Janz ICAN3 Firmware Messages */ +#define MSG_CONNECTI 0x02 +#define MSG_DISCONNECT 0x03 +#define MSG_IDVERS 0x04 +#define MSG_MSGLOST 0x05 +#define MSG_NEWHOSTIF 0x08 +#define MSG_INQUIRY 0x0a +#define MSG_SETAFILMASK 0x10 +#define MSG_INITFDPMQUEUE 0x11 +#define MSG_HWCONF 0x12 +#define MSG_FMSGLOST 0x15 +#define MSG_CEVTIND 0x37 +#define MSG_CBTRREQ 0x41 +#define MSG_COFFREQ 0x42 +#define MSG_CONREQ 0x43 +#define MSG_CCONFREQ 0x47 + +/* + * Janz ICAN3 CAN Inquiry Message Types + * + * NOTE: there appears to be a firmware bug here. You must send + * NOTE: INQUIRY_STATUS and expect to receive an INQUIRY_EXTENDED + * NOTE: response. The controller never responds to a message with + * NOTE: the INQUIRY_EXTENDED subspec :( + */ +#define INQUIRY_STATUS 0x00 +#define INQUIRY_TERMINATION 0x01 +#define INQUIRY_EXTENDED 0x04 + +/* Janz ICAN3 CAN Set Acceptance Filter Mask Message Types */ +#define SETAFILMASK_REJECT 0x00 +#define SETAFILMASK_FASTIF 0x02 + +/* Janz ICAN3 CAN Hardware Configuration Message Types */ +#define HWCONF_TERMINATE_ON 0x01 +#define HWCONF_TERMINATE_OFF 0x00 + +/* Janz ICAN3 CAN Event Indication Message Types */ +#define CEVTIND_EI 0x01 +#define CEVTIND_DOI 0x02 +#define CEVTIND_LOST 0x04 +#define CEVTIND_FULL 0x08 +#define CEVTIND_BEI 0x10 + +#define CEVTIND_CHIP_SJA1000 0x02 + +#define ICAN3_BUSERR_QUOTA_MAX 255 + +/* Janz ICAN3 CAN Frame Conversion */ +#define ICAN3_ECHO 0x10 +#define ICAN3_EFF_RTR 0x40 +#define ICAN3_SFF_RTR 0x10 +#define ICAN3_EFF 0x80 + +#define ICAN3_CAN_TYPE_MASK 0x0f +#define ICAN3_CAN_TYPE_SFF 0x00 +#define ICAN3_CAN_TYPE_EFF 0x01 + +#define ICAN3_CAN_DLC_MASK 0x0f + +/* + * SJA1000 Status and Error Register Definitions + * + * Copied from drivers/net/can/sja1000/sja1000.h + */ + +/* status register content */ +#define SR_BS 0x80 +#define SR_ES 0x40 +#define SR_TS 0x20 +#define SR_RS 0x10 +#define SR_TCS 0x08 +#define SR_TBS 0x04 +#define SR_DOS 0x02 +#define SR_RBS 0x01 + +#define SR_CRIT (SR_BS|SR_ES) + +/* ECC register */ +#define ECC_SEG 0x1F +#define ECC_DIR 0x20 +#define ECC_ERR 6 +#define ECC_BIT 0x00 +#define ECC_FORM 0x40 +#define ECC_STUFF 0x80 +#define ECC_MASK 0xc0 + +/* Number of buffers for use in the "new-style" host interface */ +#define ICAN3_NEW_BUFFERS 16 + +/* Number of buffers for use in the "fast" host interface */ +#define ICAN3_TX_BUFFERS 512 +#define ICAN3_RX_BUFFERS 1024 + +/* SJA1000 Clock Input */ +#define ICAN3_CAN_CLOCK 8000000 + +/* Driver Name */ +#define DRV_NAME "janz-ican3" + +/* DPM Control Registers -- starts at offset 0x100 in the MODULbus registers */ +struct ican3_dpm_control { + /* window address register */ + u8 window_address; + u8 unused1; + + /* + * Read access: clear interrupt from microcontroller + * Write access: send interrupt to microcontroller + */ + u8 interrupt; + u8 unused2; + + /* write-only: reset all hardware on the module */ + u8 hwreset; + u8 unused3; + + /* write-only: generate an interrupt to the TPU */ + u8 tpuinterrupt; +}; + +struct ican3_dev { + + /* must be the first member */ + struct can_priv can; + + /* CAN network device */ + struct net_device *ndev; + struct napi_struct napi; + + /* Device for printing */ + struct device *dev; + + /* module number */ + unsigned int num; + + /* base address of registers and IRQ */ + struct janz_cmodio_onboard_regs __iomem *ctrl; + struct ican3_dpm_control __iomem *dpmctrl; + void __iomem *dpm; + int irq; + + /* CAN bus termination status */ + struct completion termination_comp; + bool termination_enabled; + + /* CAN bus error status registers */ + struct completion buserror_comp; + struct can_berr_counter bec; + + /* old and new style host interface */ + unsigned int iftype; + + /* + * Any function which changes the current DPM page must hold this + * lock while it is performing data accesses. This ensures that the + * function will not be preempted and end up reading data from a + * different DPM page than it expects. + */ + spinlock_t lock; + + /* new host interface */ + unsigned int rx_int; + unsigned int rx_num; + unsigned int tx_num; + + /* fast host interface */ + unsigned int fastrx_start; + unsigned int fastrx_int; + unsigned int fastrx_num; + unsigned int fasttx_start; + unsigned int fasttx_num; + + /* first free DPM page */ + unsigned int free_page; +}; + +struct ican3_msg { + u8 control; + u8 spec; + __le16 len; + u8 data[252]; +}; + +struct ican3_new_desc { + u8 control; + u8 pointer; +}; + +struct ican3_fast_desc { + u8 control; + u8 command; + u8 data[14]; +}; + +/* write to the window basic address register */ +static inline void ican3_set_page(struct ican3_dev *mod, unsigned int page) +{ + BUG_ON(page >= DPM_NUM_PAGES); + iowrite8(page, &mod->dpmctrl->window_address); +} + +/* + * ICAN3 "old-style" host interface + */ + +/* + * Recieve a message from the ICAN3 "old-style" firmware interface + * + * LOCKING: must hold mod->lock + * + * returns 0 on success, -ENOMEM when no message exists + */ +static int ican3_old_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg) +{ + unsigned int mbox, mbox_page; + u8 locl, peer, xord; + + /* get the MSYNC registers */ + ican3_set_page(mod, QUEUE_OLD_CONTROL); + peer = ioread8(mod->dpm + MSYNC_PEER); + locl = ioread8(mod->dpm + MSYNC_LOCL); + xord = locl ^ peer; + + if ((xord & MSYNC_RB_MASK) == 0x00) { + dev_dbg(mod->dev, "no mbox for reading\n"); + return -ENOMEM; + } + + /* find the first free mbox to read */ + if ((xord & MSYNC_RB_MASK) == MSYNC_RB_MASK) + mbox = (xord & MSYNC_RBLW) ? MSYNC_RB0 : MSYNC_RB1; + else + mbox = (xord & MSYNC_RB0) ? MSYNC_RB0 : MSYNC_RB1; + + /* copy the message */ + mbox_page = (mbox == MSYNC_RB0) ? QUEUE_OLD_RB0 : QUEUE_OLD_RB1; + ican3_set_page(mod, mbox_page); + memcpy_fromio(msg, mod->dpm, sizeof(*msg)); + + /* + * notify the firmware that the read buffer is available + * for it to fill again + */ + locl ^= mbox; + + ican3_set_page(mod, QUEUE_OLD_CONTROL); + iowrite8(locl, mod->dpm + MSYNC_LOCL); + return 0; +} + +/* + * Send a message through the "old-style" firmware interface + * + * LOCKING: must hold mod->lock + * + * returns 0 on success, -ENOMEM when no free space exists + */ +static int ican3_old_send_msg(struct ican3_dev *mod, struct ican3_msg *msg) +{ + unsigned int mbox, mbox_page; + u8 locl, peer, xord; + + /* get the MSYNC registers */ + ican3_set_page(mod, QUEUE_OLD_CONTROL); + peer = ioread8(mod->dpm + MSYNC_PEER); + locl = ioread8(mod->dpm + MSYNC_LOCL); + xord = locl ^ peer; + + if ((xord & MSYNC_WB_MASK) == MSYNC_WB_MASK) { + dev_err(mod->dev, "no mbox for writing\n"); + return -ENOMEM; + } + + /* calculate a free mbox to use */ + mbox = (xord & MSYNC_WB0) ? MSYNC_WB1 : MSYNC_WB0; + + /* copy the message to the DPM */ + mbox_page = (mbox == MSYNC_WB0) ? QUEUE_OLD_WB0 : QUEUE_OLD_WB1; + ican3_set_page(mod, mbox_page); + memcpy_toio(mod->dpm, msg, sizeof(*msg)); + + locl ^= mbox; + if (mbox == MSYNC_WB1) + locl |= MSYNC_WBLW; + + ican3_set_page(mod, QUEUE_OLD_CONTROL); + iowrite8(locl, mod->dpm + MSYNC_LOCL); + return 0; +} + +/* + * ICAN3 "new-style" Host Interface Setup + */ + +static void __devinit ican3_init_new_host_interface(struct ican3_dev *mod) +{ + struct ican3_new_desc desc; + unsigned long flags; + void __iomem *dst; + int i; + + spin_lock_irqsave(&mod->lock, flags); + + /* setup the internal datastructures for RX */ + mod->rx_num = 0; + mod->rx_int = 0; + + /* tohost queue descriptors are in page 5 */ + ican3_set_page(mod, QUEUE_TOHOST); + dst = mod->dpm; + + /* initialize the tohost (rx) queue descriptors: pages 9-24 */ + for (i = 0; i < ICAN3_NEW_BUFFERS; i++) { + desc.control = DESC_INTERRUPT | DESC_LEN(1); /* I L=1 */ + desc.pointer = mod->free_page; + + /* set wrap flag on last buffer */ + if (i == ICAN3_NEW_BUFFERS - 1) + desc.control |= DESC_WRAP; + + memcpy_toio(dst, &desc, sizeof(desc)); + dst += sizeof(desc); + mod->free_page++; + } + + /* fromhost (tx) mid queue descriptors are in page 6 */ + ican3_set_page(mod, QUEUE_FROMHOST_MID); + dst = mod->dpm; + + /* setup the internal datastructures for TX */ + mod->tx_num = 0; + + /* initialize the fromhost mid queue descriptors: pages 25-40 */ + for (i = 0; i < ICAN3_NEW_BUFFERS; i++) { + desc.control = DESC_VALID | DESC_LEN(1); /* V L=1 */ + desc.pointer = mod->free_page; + + /* set wrap flag on last buffer */ + if (i == ICAN3_NEW_BUFFERS - 1) + desc.control |= DESC_WRAP; + + memcpy_toio(dst, &desc, sizeof(desc)); + dst += sizeof(desc); + mod->free_page++; + } + + /* fromhost hi queue descriptors are in page 7 */ + ican3_set_page(mod, QUEUE_FROMHOST_HIGH); + dst = mod->dpm; + + /* initialize only a single buffer in the fromhost hi queue (unused) */ + desc.control = DESC_VALID | DESC_WRAP | DESC_LEN(1); /* VW L=1 */ + desc.pointer = mod->free_page; + memcpy_toio(dst, &desc, sizeof(desc)); + mod->free_page++; + + /* fromhost low queue descriptors are in page 8 */ + ican3_set_page(mod, QUEUE_FROMHOST_LOW); + dst = mod->dpm; + + /* initialize only a single buffer in the fromhost low queue (unused) */ + desc.control = DESC_VALID | DESC_WRAP | DESC_LEN(1); /* VW L=1 */ + desc.pointer = mod->free_page; + memcpy_toio(dst, &desc, sizeof(desc)); + mod->free_page++; + + spin_unlock_irqrestore(&mod->lock, flags); +} + +/* + * ICAN3 Fast Host Interface Setup + */ + +static void __devinit ican3_init_fast_host_interface(struct ican3_dev *mod) +{ + struct ican3_fast_desc desc; + unsigned long flags; + unsigned int addr; + void __iomem *dst; + int i; + + spin_lock_irqsave(&mod->lock, flags); + + /* save the start recv page */ + mod->fastrx_start = mod->free_page; + mod->fastrx_num = 0; + mod->fastrx_int = 0; + + /* build a single fast tohost queue descriptor */ + memset(&desc, 0, sizeof(desc)); + desc.control = 0x00; + desc.command = 1; + + /* build the tohost queue descriptor ring in memory */ + addr = 0; + for (i = 0; i < ICAN3_RX_BUFFERS; i++) { + + /* set the wrap bit on the last buffer */ + if (i == ICAN3_RX_BUFFERS - 1) + desc.control |= DESC_WRAP; + + /* switch to the correct page */ + ican3_set_page(mod, mod->free_page); + + /* copy the descriptor to the DPM */ + dst = mod->dpm + addr; + memcpy_toio(dst, &desc, sizeof(desc)); + addr += sizeof(desc); + + /* move to the next page if necessary */ + if (addr >= DPM_PAGE_SIZE) { + addr = 0; + mod->free_page++; + } + } + + /* make sure we page-align the next queue */ + if (addr != 0) + mod->free_page++; + + /* save the start xmit page */ + mod->fasttx_start = mod->free_page; + mod->fasttx_num = 0; + + /* build a single fast fromhost queue descriptor */ + memset(&desc, 0, sizeof(desc)); + desc.control = DESC_VALID; + desc.command = 1; + + /* build the fromhost queue descriptor ring in memory */ + addr = 0; + for (i = 0; i < ICAN3_TX_BUFFERS; i++) { + + /* set the wrap bit on the last buffer */ + if (i == ICAN3_TX_BUFFERS - 1) + desc.control |= DESC_WRAP; + + /* switch to the correct page */ + ican3_set_page(mod, mod->free_page); + + /* copy the descriptor to the DPM */ + dst = mod->dpm + addr; + memcpy_toio(dst, &desc, sizeof(desc)); + addr += sizeof(desc); + + /* move to the next page if necessary */ + if (addr >= DPM_PAGE_SIZE) { + addr = 0; + mod->free_page++; + } + } + + spin_unlock_irqrestore(&mod->lock, flags); +} + +/* + * ICAN3 "new-style" Host Interface Message Helpers + */ + +/* + * LOCKING: must hold mod->lock + */ +static int ican3_new_send_msg(struct ican3_dev *mod, struct ican3_msg *msg) +{ + struct ican3_new_desc desc; + void __iomem *desc_addr = mod->dpm + (mod->tx_num * sizeof(desc)); + + /* switch to the fromhost mid queue, and read the buffer descriptor */ + ican3_set_page(mod, QUEUE_FROMHOST_MID); + memcpy_fromio(&desc, desc_addr, sizeof(desc)); + + if (!(desc.control & DESC_VALID)) { + dev_dbg(mod->dev, "%s: no free buffers\n", __func__); + return -ENOMEM; + } + + /* switch to the data page, copy the data */ + ican3_set_page(mod, desc.pointer); + memcpy_toio(mod->dpm, msg, sizeof(*msg)); + + /* switch back to the descriptor, set the valid bit, write it back */ + ican3_set_page(mod, QUEUE_FROMHOST_MID); + desc.control ^= DESC_VALID; + memcpy_toio(desc_addr, &desc, sizeof(desc)); + + /* update the tx number */ + mod->tx_num = (desc.control & DESC_WRAP) ? 0 : (mod->tx_num + 1); + return 0; +} + +/* + * LOCKING: must hold mod->lock + */ +static int ican3_new_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg) +{ + struct ican3_new_desc desc; + void __iomem *desc_addr = mod->dpm + (mod->rx_num * sizeof(desc)); + + /* switch to the tohost queue, and read the buffer descriptor */ + ican3_set_page(mod, QUEUE_TOHOST); + memcpy_fromio(&desc, desc_addr, sizeof(desc)); + + if (!(desc.control & DESC_VALID)) { + dev_dbg(mod->dev, "%s: no buffers to recv\n", __func__); + return -ENOMEM; + } + + /* switch to the data page, copy the data */ + ican3_set_page(mod, desc.pointer); + memcpy_fromio(msg, mod->dpm, sizeof(*msg)); + + /* switch back to the descriptor, toggle the valid bit, write it back */ + ican3_set_page(mod, QUEUE_TOHOST); + desc.control ^= DESC_VALID; + memcpy_toio(desc_addr, &desc, sizeof(desc)); + + /* update the rx number */ + mod->rx_num = (desc.control & DESC_WRAP) ? 0 : (mod->rx_num + 1); + return 0; +} + +/* + * Message Send / Recv Helpers + */ + +static int ican3_send_msg(struct ican3_dev *mod, struct ican3_msg *msg) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&mod->lock, flags); + + if (mod->iftype == 0) + ret = ican3_old_send_msg(mod, msg); + else + ret = ican3_new_send_msg(mod, msg); + + spin_unlock_irqrestore(&mod->lock, flags); + return ret; +} + +static int ican3_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&mod->lock, flags); + + if (mod->iftype == 0) + ret = ican3_old_recv_msg(mod, msg); + else + ret = ican3_new_recv_msg(mod, msg); + + spin_unlock_irqrestore(&mod->lock, flags); + return ret; +} + +/* + * Quick Pre-constructed Messages + */ + +static int __devinit ican3_msg_connect(struct ican3_dev *mod) +{ + struct ican3_msg msg; + + memset(&msg, 0, sizeof(msg)); + msg.spec = MSG_CONNECTI; + msg.len = cpu_to_le16(0); + + return ican3_send_msg(mod, &msg); +} + +static int __devexit ican3_msg_disconnect(struct ican3_dev *mod) +{ + struct ican3_msg msg; + + memset(&msg, 0, sizeof(msg)); + msg.spec = MSG_DISCONNECT; + msg.len = cpu_to_le16(0); + + return ican3_send_msg(mod, &msg); +} + +static int __devinit ican3_msg_newhostif(struct ican3_dev *mod) +{ + struct ican3_msg msg; + int ret; + + memset(&msg, 0, sizeof(msg)); + msg.spec = MSG_NEWHOSTIF; + msg.len = cpu_to_le16(0); + + /* If we're not using the old interface, switching seems bogus */ + WARN_ON(mod->iftype != 0); + + ret = ican3_send_msg(mod, &msg); + if (ret) + return ret; + + /* mark the module as using the new host interface */ + mod->iftype = 1; + return 0; +} + +static int __devinit ican3_msg_fasthostif(struct ican3_dev *mod) +{ + struct ican3_msg msg; + unsigned int addr; + + memset(&msg, 0, sizeof(msg)); + msg.spec = MSG_INITFDPMQUEUE; + msg.len = cpu_to_le16(8); + + /* write the tohost queue start address */ + addr = DPM_PAGE_ADDR(mod->fastrx_start); + msg.data[0] = addr & 0xff; + msg.data[1] = (addr >> 8) & 0xff; + msg.data[2] = (addr >> 16) & 0xff; + msg.data[3] = (addr >> 24) & 0xff; + + /* write the fromhost queue start address */ + addr = DPM_PAGE_ADDR(mod->fasttx_start); + msg.data[4] = addr & 0xff; + msg.data[5] = (addr >> 8) & 0xff; + msg.data[6] = (addr >> 16) & 0xff; + msg.data[7] = (addr >> 24) & 0xff; + + /* If we're not using the new interface yet, we cannot do this */ + WARN_ON(mod->iftype != 1); + + return ican3_send_msg(mod, &msg); +} + +/* + * Setup the CAN filter to either accept or reject all + * messages from the CAN bus. + */ +static int __devinit ican3_set_id_filter(struct ican3_dev *mod, bool accept) +{ + struct ican3_msg msg; + int ret; + + /* Standard Frame Format */ + memset(&msg, 0, sizeof(msg)); + msg.spec = MSG_SETAFILMASK; + msg.len = cpu_to_le16(5); + msg.data[0] = 0x00; /* IDLo LSB */ + msg.data[1] = 0x00; /* IDLo MSB */ + msg.data[2] = 0xff; /* IDHi LSB */ + msg.data[3] = 0x07; /* IDHi MSB */ + + /* accept all frames for fast host if, or reject all frames */ + msg.data[4] = accept ? SETAFILMASK_FASTIF : SETAFILMASK_REJECT; + + ret = ican3_send_msg(mod, &msg); + if (ret) + return ret; + + /* Extended Frame Format */ + memset(&msg, 0, sizeof(msg)); + msg.spec = MSG_SETAFILMASK; + msg.len = cpu_to_le16(13); + msg.data[0] = 0; /* MUX = 0 */ + msg.data[1] = 0x00; /* IDLo LSB */ + msg.data[2] = 0x00; + msg.data[3] = 0x00; + msg.data[4] = 0x20; /* IDLo MSB */ + msg.data[5] = 0xff; /* IDHi LSB */ + msg.data[6] = 0xff; + msg.data[7] = 0xff; + msg.data[8] = 0x3f; /* IDHi MSB */ + + /* accept all frames for fast host if, or reject all frames */ + msg.data[9] = accept ? SETAFILMASK_FASTIF : SETAFILMASK_REJECT; + + return ican3_send_msg(mod, &msg); +} + +/* + * Bring the CAN bus online or offline + */ +static int ican3_set_bus_state(struct ican3_dev *mod, bool on) +{ + struct ican3_msg msg; + + memset(&msg, 0, sizeof(msg)); + msg.spec = on ? MSG_CONREQ : MSG_COFFREQ; + msg.len = cpu_to_le16(0); + + return ican3_send_msg(mod, &msg); +} + +static int ican3_set_termination(struct ican3_dev *mod, bool on) +{ + struct ican3_msg msg; + + memset(&msg, 0, sizeof(msg)); + msg.spec = MSG_HWCONF; + msg.len = cpu_to_le16(2); + msg.data[0] = 0x00; + msg.data[1] = on ? HWCONF_TERMINATE_ON : HWCONF_TERMINATE_OFF; + + return ican3_send_msg(mod, &msg); +} + +static int ican3_send_inquiry(struct ican3_dev *mod, u8 subspec) +{ + struct ican3_msg msg; + + memset(&msg, 0, sizeof(msg)); + msg.spec = MSG_INQUIRY; + msg.len = cpu_to_le16(2); + msg.data[0] = subspec; + msg.data[1] = 0x00; + + return ican3_send_msg(mod, &msg); +} + +static int ican3_set_buserror(struct ican3_dev *mod, u8 quota) +{ + struct ican3_msg msg; + + memset(&msg, 0, sizeof(msg)); + msg.spec = MSG_CCONFREQ; + msg.len = cpu_to_le16(2); + msg.data[0] = 0x00; + msg.data[1] = quota; + + return ican3_send_msg(mod, &msg); +} + +/* + * ICAN3 to Linux CAN Frame Conversion + */ + +static void ican3_to_can_frame(struct ican3_dev *mod, + struct ican3_fast_desc *desc, + struct can_frame *cf) +{ + if ((desc->command & ICAN3_CAN_TYPE_MASK) == ICAN3_CAN_TYPE_SFF) { + if (desc->data[1] & ICAN3_SFF_RTR) + cf->can_id |= CAN_RTR_FLAG; + + cf->can_id |= desc->data[0] << 3; + cf->can_id |= (desc->data[1] & 0xe0) >> 5; + cf->can_dlc = desc->data[1] & ICAN3_CAN_DLC_MASK; + memcpy(cf->data, &desc->data[2], sizeof(cf->data)); + } else { + cf->can_dlc = desc->data[0] & ICAN3_CAN_DLC_MASK; + if (desc->data[0] & ICAN3_EFF_RTR) + cf->can_id |= CAN_RTR_FLAG; + + if (desc->data[0] & ICAN3_EFF) { + cf->can_id |= CAN_EFF_FLAG; + cf->can_id |= desc->data[2] << 21; /* 28-21 */ + cf->can_id |= desc->data[3] << 13; /* 20-13 */ + cf->can_id |= desc->data[4] << 5; /* 12-5 */ + cf->can_id |= (desc->data[5] & 0xf8) >> 3; + } else { + cf->can_id |= desc->data[2] << 3; /* 10-3 */ + cf->can_id |= desc->data[3] >> 5; /* 2-0 */ + } + + memcpy(cf->data, &desc->data[6], sizeof(cf->data)); + } +} + +static void can_frame_to_ican3(struct ican3_dev *mod, + struct can_frame *cf, + struct ican3_fast_desc *desc) +{ + /* clear out any stale data in the descriptor */ + memset(desc->data, 0, sizeof(desc->data)); + + /* we always use the extended format, with the ECHO flag set */ + desc->command = ICAN3_CAN_TYPE_EFF; + desc->data[0] |= cf->can_dlc; + desc->data[1] |= ICAN3_ECHO; + + if (cf->can_id & CAN_RTR_FLAG) + desc->data[0] |= ICAN3_EFF_RTR; + + /* pack the id into the correct places */ + if (cf->can_id & CAN_EFF_FLAG) { + desc->data[0] |= ICAN3_EFF; + desc->data[2] = (cf->can_id & 0x1fe00000) >> 21; /* 28-21 */ + desc->data[3] = (cf->can_id & 0x001fe000) >> 13; /* 20-13 */ + desc->data[4] = (cf->can_id & 0x00001fe0) >> 5; /* 12-5 */ + desc->data[5] = (cf->can_id & 0x0000001f) << 3; /* 4-0 */ + } else { + desc->data[2] = (cf->can_id & 0x7F8) >> 3; /* bits 10-3 */ + desc->data[3] = (cf->can_id & 0x007) << 5; /* bits 2-0 */ + } + + /* copy the data bits into the descriptor */ + memcpy(&desc->data[6], cf->data, sizeof(cf->data)); +} + +/* + * Interrupt Handling + */ + +/* + * Handle an ID + Version message response from the firmware. We never generate + * this message in production code, but it is very useful when debugging to be + * able to display this message. + */ +static void ican3_handle_idvers(struct ican3_dev *mod, struct ican3_msg *msg) +{ + dev_dbg(mod->dev, "IDVERS response: %s\n", msg->data); +} + +static void ican3_handle_msglost(struct ican3_dev *mod, struct ican3_msg *msg) +{ + struct net_device *dev = mod->ndev; + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + + /* + * Report that communication messages with the microcontroller firmware + * are being lost. These are never CAN frames, so we do not generate an + * error frame for userspace + */ + if (msg->spec == MSG_MSGLOST) { + dev_err(mod->dev, "lost %d control messages\n", msg->data[0]); + return; + } + + /* + * Oops, this indicates that we have lost messages in the fast queue, + * which are exclusively CAN messages. Our driver isn't reading CAN + * frames fast enough. + * + * We'll pretend that the SJA1000 told us that it ran out of buffer + * space, because there is not a better message for this. + */ + skb = alloc_can_err_skb(dev, &cf); + if (skb) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + stats->rx_errors++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + } +} + +/* + * Handle CAN Event Indication Messages from the firmware + * + * The ICAN3 firmware provides the values of some SJA1000 registers when it + * generates this message. The code below is largely copied from the + * drivers/net/can/sja1000/sja1000.c file, and adapted as necessary + */ +static int ican3_handle_cevtind(struct ican3_dev *mod, struct ican3_msg *msg) +{ + struct net_device *dev = mod->ndev; + struct net_device_stats *stats = &dev->stats; + enum can_state state = mod->can.state; + u8 status, isrc, rxerr, txerr; + struct can_frame *cf; + struct sk_buff *skb; + + /* we can only handle the SJA1000 part */ + if (msg->data[1] != CEVTIND_CHIP_SJA1000) { + dev_err(mod->dev, "unable to handle errors on non-SJA1000\n"); + return -ENODEV; + } + + /* check the message length for sanity */ + if (le16_to_cpu(msg->len) < 6) { + dev_err(mod->dev, "error message too short\n"); + return -EINVAL; + } + + skb = alloc_can_err_skb(dev, &cf); + if (skb == NULL) + return -ENOMEM; + + isrc = msg->data[0]; + status = msg->data[3]; + rxerr = msg->data[4]; + txerr = msg->data[5]; + + /* data overrun interrupt */ + if (isrc == CEVTIND_DOI || isrc == CEVTIND_LOST) { + dev_dbg(mod->dev, "data overrun interrupt\n"); + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + stats->rx_over_errors++; + stats->rx_errors++; + } + + /* error warning + passive interrupt */ + if (isrc == CEVTIND_EI) { + dev_dbg(mod->dev, "error warning + passive interrupt\n"); + if (status & SR_BS) { + state = CAN_STATE_BUS_OFF; + cf->can_id |= CAN_ERR_BUSOFF; + can_bus_off(dev); + } else if (status & SR_ES) { + if (rxerr >= 128 || txerr >= 128) + state = CAN_STATE_ERROR_PASSIVE; + else + state = CAN_STATE_ERROR_WARNING; + } else { + state = CAN_STATE_ERROR_ACTIVE; + } + } + + /* bus error interrupt */ + if (isrc == CEVTIND_BEI) { + u8 ecc = msg->data[2]; + + dev_dbg(mod->dev, "bus error interrupt\n"); + mod->can.can_stats.bus_error++; + stats->rx_errors++; + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + + switch (ecc & ECC_MASK) { + case ECC_BIT: + cf->data[2] |= CAN_ERR_PROT_BIT; + break; + case ECC_FORM: + cf->data[2] |= CAN_ERR_PROT_FORM; + break; + case ECC_STUFF: + cf->data[2] |= CAN_ERR_PROT_STUFF; + break; + default: + cf->data[2] |= CAN_ERR_PROT_UNSPEC; + cf->data[3] = ecc & ECC_SEG; + break; + } + + if ((ecc & ECC_DIR) == 0) + cf->data[2] |= CAN_ERR_PROT_TX; + + cf->data[6] = txerr; + cf->data[7] = rxerr; + } + + if (state != mod->can.state && (state == CAN_STATE_ERROR_WARNING || + state == CAN_STATE_ERROR_PASSIVE)) { + cf->can_id |= CAN_ERR_CRTL; + if (state == CAN_STATE_ERROR_WARNING) { + mod->can.can_stats.error_warning++; + cf->data[1] = (txerr > rxerr) ? + CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + } else { + mod->can.can_stats.error_passive++; + cf->data[1] = (txerr > rxerr) ? + CAN_ERR_CRTL_TX_PASSIVE : + CAN_ERR_CRTL_RX_PASSIVE; + } + + cf->data[6] = txerr; + cf->data[7] = rxerr; + } + + mod->can.state = state; + stats->rx_errors++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + return 0; +} + +static void ican3_handle_inquiry(struct ican3_dev *mod, struct ican3_msg *msg) +{ + switch (msg->data[0]) { + case INQUIRY_STATUS: + case INQUIRY_EXTENDED: + mod->bec.rxerr = msg->data[5]; + mod->bec.txerr = msg->data[6]; + complete(&mod->buserror_comp); + break; + case INQUIRY_TERMINATION: + mod->termination_enabled = msg->data[6] & HWCONF_TERMINATE_ON; + complete(&mod->termination_comp); + break; + default: + dev_err(mod->dev, "recieved an unknown inquiry response\n"); + break; + } +} + +static void ican3_handle_unknown_message(struct ican3_dev *mod, + struct ican3_msg *msg) +{ + dev_warn(mod->dev, "recieved unknown message: spec 0x%.2x length %d\n", + msg->spec, le16_to_cpu(msg->len)); +} + +/* + * Handle a control message from the firmware + */ +static void ican3_handle_message(struct ican3_dev *mod, struct ican3_msg *msg) +{ + dev_dbg(mod->dev, "%s: modno %d spec 0x%.2x len %d bytes\n", __func__, + mod->num, msg->spec, le16_to_cpu(msg->len)); + + switch (msg->spec) { + case MSG_IDVERS: + ican3_handle_idvers(mod, msg); + break; + case MSG_MSGLOST: + case MSG_FMSGLOST: + ican3_handle_msglost(mod, msg); + break; + case MSG_CEVTIND: + ican3_handle_cevtind(mod, msg); + break; + case MSG_INQUIRY: + ican3_handle_inquiry(mod, msg); + break; + default: + ican3_handle_unknown_message(mod, msg); + break; + } +} + +/* + * Check that there is room in the TX ring to transmit another skb + * + * LOCKING: must hold mod->lock + */ +static bool ican3_txok(struct ican3_dev *mod) +{ + struct ican3_fast_desc __iomem *desc; + u8 control; + + /* copy the control bits of the descriptor */ + ican3_set_page(mod, mod->fasttx_start + (mod->fasttx_num / 16)); + desc = mod->dpm + ((mod->fasttx_num % 16) * sizeof(*desc)); + control = ioread8(&desc->control); + + /* if the control bits are not valid, then we have no more space */ + if (!(control & DESC_VALID)) + return false; + + return true; +} + +/* + * Recieve one CAN frame from the hardware + * + * This works like the core of a NAPI function, but is intended to be called + * from workqueue context instead. This driver already needs a workqueue to + * process control messages, so we use the workqueue instead of using NAPI. + * This was done to simplify locking. + * + * CONTEXT: must be called from user context + */ +static int ican3_recv_skb(struct ican3_dev *mod) +{ + struct net_device *ndev = mod->ndev; + struct net_device_stats *stats = &ndev->stats; + struct ican3_fast_desc desc; + void __iomem *desc_addr; + struct can_frame *cf; + struct sk_buff *skb; + unsigned long flags; + + spin_lock_irqsave(&mod->lock, flags); + + /* copy the whole descriptor */ + ican3_set_page(mod, mod->fastrx_start + (mod->fastrx_num / 16)); + desc_addr = mod->dpm + ((mod->fastrx_num % 16) * sizeof(desc)); + memcpy_fromio(&desc, desc_addr, sizeof(desc)); + + spin_unlock_irqrestore(&mod->lock, flags); + + /* check that we actually have a CAN frame */ + if (!(desc.control & DESC_VALID)) + return -ENOBUFS; + + /* allocate an skb */ + skb = alloc_can_skb(ndev, &cf); + if (unlikely(skb == NULL)) { + stats->rx_dropped++; + goto err_noalloc; + } + + /* convert the ICAN3 frame into Linux CAN format */ + ican3_to_can_frame(mod, &desc, cf); + + /* receive the skb, update statistics */ + netif_receive_skb(skb); + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + +err_noalloc: + /* toggle the valid bit and return the descriptor to the ring */ + desc.control ^= DESC_VALID; + + spin_lock_irqsave(&mod->lock, flags); + + ican3_set_page(mod, mod->fastrx_start + (mod->fastrx_num / 16)); + memcpy_toio(desc_addr, &desc, 1); + + /* update the next buffer pointer */ + mod->fastrx_num = (desc.control & DESC_WRAP) ? 0 + : (mod->fastrx_num + 1); + + /* there are still more buffers to process */ + spin_unlock_irqrestore(&mod->lock, flags); + return 0; +} + +static int ican3_napi(struct napi_struct *napi, int budget) +{ + struct ican3_dev *mod = container_of(napi, struct ican3_dev, napi); + struct ican3_msg msg; + unsigned long flags; + int received = 0; + int ret; + + /* process all communication messages */ + while (true) { + ret = ican3_recv_msg(mod, &msg); + if (ret) + break; + + ican3_handle_message(mod, &msg); + } + + /* process all CAN frames from the fast interface */ + while (received < budget) { + ret = ican3_recv_skb(mod); + if (ret) + break; + + received++; + } + + /* We have processed all packets that the adapter had, but it + * was less than our budget, stop polling */ + if (received < budget) + napi_complete(napi); + + spin_lock_irqsave(&mod->lock, flags); + + /* Wake up the transmit queue if necessary */ + if (netif_queue_stopped(mod->ndev) && ican3_txok(mod)) + netif_wake_queue(mod->ndev); + + spin_unlock_irqrestore(&mod->lock, flags); + + /* re-enable interrupt generation */ + iowrite8(1 << mod->num, &mod->ctrl->int_enable); + return received; +} + +static irqreturn_t ican3_irq(int irq, void *dev_id) +{ + struct ican3_dev *mod = dev_id; + u8 stat; + + /* + * The interrupt status register on this device reports interrupts + * as zeroes instead of using ones like most other devices + */ + stat = ioread8(&mod->ctrl->int_disable) & (1 << mod->num); + if (stat == (1 << mod->num)) + return IRQ_NONE; + + /* clear the MODULbus interrupt from the microcontroller */ + ioread8(&mod->dpmctrl->interrupt); + + /* disable interrupt generation, schedule the NAPI poller */ + iowrite8(1 << mod->num, &mod->ctrl->int_disable); + napi_schedule(&mod->napi); + return IRQ_HANDLED; +} + +/* + * Firmware reset, startup, and shutdown + */ + +/* + * Reset an ICAN module to its power-on state + * + * CONTEXT: no network device registered + * LOCKING: work function disabled + */ +static int ican3_reset_module(struct ican3_dev *mod) +{ + u8 val = 1 << mod->num; + unsigned long start; + u8 runold, runnew; + + /* disable interrupts so no more work is scheduled */ + iowrite8(1 << mod->num, &mod->ctrl->int_disable); + + /* flush any pending work */ + flush_scheduled_work(); + + /* the first unallocated page in the DPM is #9 */ + mod->free_page = DPM_FREE_START; + + ican3_set_page(mod, QUEUE_OLD_CONTROL); + runold = ioread8(mod->dpm + TARGET_RUNNING); + + /* reset the module */ + iowrite8(val, &mod->ctrl->reset_assert); + iowrite8(val, &mod->ctrl->reset_deassert); + + /* wait until the module has finished resetting and is running */ + start = jiffies; + do { + ican3_set_page(mod, QUEUE_OLD_CONTROL); + runnew = ioread8(mod->dpm + TARGET_RUNNING); + if (runnew == (runold ^ 0xff)) + return 0; + + msleep(10); + } while (time_before(jiffies, start + HZ / 4)); + + dev_err(mod->dev, "failed to reset CAN module\n"); + return -ETIMEDOUT; +} + +static void __devexit ican3_shutdown_module(struct ican3_dev *mod) +{ + ican3_msg_disconnect(mod); + ican3_reset_module(mod); +} + +/* + * Startup an ICAN module, bringing it into fast mode + */ +static int __devinit ican3_startup_module(struct ican3_dev *mod) +{ + int ret; + + ret = ican3_reset_module(mod); + if (ret) { + dev_err(mod->dev, "unable to reset module\n"); + return ret; + } + + /* re-enable interrupts so we can send messages */ + iowrite8(1 << mod->num, &mod->ctrl->int_enable); + + ret = ican3_msg_connect(mod); + if (ret) { + dev_err(mod->dev, "unable to connect to module\n"); + return ret; + } + + ican3_init_new_host_interface(mod); + ret = ican3_msg_newhostif(mod); + if (ret) { + dev_err(mod->dev, "unable to switch to new-style interface\n"); + return ret; + } + + /* default to "termination on" */ + ret = ican3_set_termination(mod, true); + if (ret) { + dev_err(mod->dev, "unable to enable termination\n"); + return ret; + } + + /* default to "bus errors enabled" */ + ret = ican3_set_buserror(mod, ICAN3_BUSERR_QUOTA_MAX); + if (ret) { + dev_err(mod->dev, "unable to set bus-error\n"); + return ret; + } + + ican3_init_fast_host_interface(mod); + ret = ican3_msg_fasthostif(mod); + if (ret) { + dev_err(mod->dev, "unable to switch to fast host interface\n"); + return ret; + } + + ret = ican3_set_id_filter(mod, true); + if (ret) { + dev_err(mod->dev, "unable to set acceptance filter\n"); + return ret; + } + + return 0; +} + +/* + * CAN Network Device + */ + +static int ican3_open(struct net_device *ndev) +{ + struct ican3_dev *mod = netdev_priv(ndev); + u8 quota; + int ret; + + /* open the CAN layer */ + ret = open_candev(ndev); + if (ret) { + dev_err(mod->dev, "unable to start CAN layer\n"); + return ret; + } + + /* set the bus error generation state appropriately */ + if (mod->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) + quota = ICAN3_BUSERR_QUOTA_MAX; + else + quota = 0; + + ret = ican3_set_buserror(mod, quota); + if (ret) { + dev_err(mod->dev, "unable to set bus-error\n"); + close_candev(ndev); + return ret; + } + + /* bring the bus online */ + ret = ican3_set_bus_state(mod, true); + if (ret) { + dev_err(mod->dev, "unable to set bus-on\n"); + close_candev(ndev); + return ret; + } + + /* start up the network device */ + mod->can.state = CAN_STATE_ERROR_ACTIVE; + netif_start_queue(ndev); + + return 0; +} + +static int ican3_stop(struct net_device *ndev) +{ + struct ican3_dev *mod = netdev_priv(ndev); + int ret; + + /* stop the network device xmit routine */ + netif_stop_queue(ndev); + mod->can.state = CAN_STATE_STOPPED; + + /* bring the bus offline, stop receiving packets */ + ret = ican3_set_bus_state(mod, false); + if (ret) { + dev_err(mod->dev, "unable to set bus-off\n"); + return ret; + } + + /* close the CAN layer */ + close_candev(ndev); + return 0; +} + +static int ican3_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct ican3_dev *mod = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf = (struct can_frame *)skb->data; + struct ican3_fast_desc desc; + void __iomem *desc_addr; + unsigned long flags; + + spin_lock_irqsave(&mod->lock, flags); + + /* check that we can actually transmit */ + if (!ican3_txok(mod)) { + dev_err(mod->dev, "no free descriptors, stopping queue\n"); + netif_stop_queue(ndev); + spin_unlock_irqrestore(&mod->lock, flags); + return NETDEV_TX_BUSY; + } + + /* copy the control bits of the descriptor */ + ican3_set_page(mod, mod->fasttx_start + (mod->fasttx_num / 16)); + desc_addr = mod->dpm + ((mod->fasttx_num % 16) * sizeof(desc)); + memset(&desc, 0, sizeof(desc)); + memcpy_fromio(&desc, desc_addr, 1); + + /* convert the Linux CAN frame into ICAN3 format */ + can_frame_to_ican3(mod, cf, &desc); + + /* + * the programming manual says that you must set the IVALID bit, then + * interrupt, then set the valid bit. Quite weird, but it seems to be + * required for this to work + */ + desc.control |= DESC_IVALID; + memcpy_toio(desc_addr, &desc, sizeof(desc)); + + /* generate a MODULbus interrupt to the microcontroller */ + iowrite8(0x01, &mod->dpmctrl->interrupt); + + desc.control ^= DESC_VALID; + memcpy_toio(desc_addr, &desc, sizeof(desc)); + + /* update the next buffer pointer */ + mod->fasttx_num = (desc.control & DESC_WRAP) ? 0 + : (mod->fasttx_num + 1); + + /* update statistics */ + stats->tx_packets++; + stats->tx_bytes += cf->can_dlc; + kfree_skb(skb); + + /* + * This hardware doesn't have TX-done notifications, so we'll try and + * emulate it the best we can using ECHO skbs. Get the next TX + * descriptor, and see if we have room to send. If not, stop the queue. + * It will be woken when the ECHO skb for the current packet is recv'd. + */ + + /* copy the control bits of the descriptor */ + if (!ican3_txok(mod)) + netif_stop_queue(ndev); + + spin_unlock_irqrestore(&mod->lock, flags); + return NETDEV_TX_OK; +} + +static const struct net_device_ops ican3_netdev_ops = { + .ndo_open = ican3_open, + .ndo_stop = ican3_stop, + .ndo_start_xmit = ican3_xmit, +}; + +/* + * Low-level CAN Device + */ + +/* This structure was stolen from drivers/net/can/sja1000/sja1000.c */ +static struct can_bittiming_const ican3_bittiming_const = { + .name = DRV_NAME, + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 64, + .brp_inc = 1, +}; + +/* + * This routine was stolen from drivers/net/can/sja1000/sja1000.c + * + * The bittiming register command for the ICAN3 just sets the bit timing + * registers on the SJA1000 chip directly + */ +static int ican3_set_bittiming(struct net_device *ndev) +{ + struct ican3_dev *mod = netdev_priv(ndev); + struct can_bittiming *bt = &mod->can.bittiming; + struct ican3_msg msg; + u8 btr0, btr1; + + btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6); + btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) | + (((bt->phase_seg2 - 1) & 0x7) << 4); + if (mod->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + btr1 |= 0x80; + + memset(&msg, 0, sizeof(msg)); + msg.spec = MSG_CBTRREQ; + msg.len = cpu_to_le16(4); + msg.data[0] = 0x00; + msg.data[1] = 0x00; + msg.data[2] = btr0; + msg.data[3] = btr1; + + return ican3_send_msg(mod, &msg); +} + +static int ican3_set_mode(struct net_device *ndev, enum can_mode mode) +{ + struct ican3_dev *mod = netdev_priv(ndev); + int ret; + + if (mode != CAN_MODE_START) + return -ENOTSUPP; + + /* bring the bus online */ + ret = ican3_set_bus_state(mod, true); + if (ret) { + dev_err(mod->dev, "unable to set bus-on\n"); + return ret; + } + + /* start up the network device */ + mod->can.state = CAN_STATE_ERROR_ACTIVE; + + if (netif_queue_stopped(ndev)) + netif_wake_queue(ndev); + + return 0; +} + +static int ican3_get_berr_counter(const struct net_device *ndev, + struct can_berr_counter *bec) +{ + struct ican3_dev *mod = netdev_priv(ndev); + int ret; + + ret = ican3_send_inquiry(mod, INQUIRY_STATUS); + if (ret) + return ret; + + ret = wait_for_completion_timeout(&mod->buserror_comp, HZ); + if (ret <= 0) { + dev_info(mod->dev, "%s timed out\n", __func__); + return -ETIMEDOUT; + } + + bec->rxerr = mod->bec.rxerr; + bec->txerr = mod->bec.txerr; + return 0; +} + +/* + * Sysfs Attributes + */ + +static ssize_t ican3_sysfs_show_term(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ican3_dev *mod = netdev_priv(to_net_dev(dev)); + int ret; + + ret = ican3_send_inquiry(mod, INQUIRY_TERMINATION); + if (ret) + return ret; + + ret = wait_for_completion_timeout(&mod->termination_comp, HZ); + if (ret <= 0) { + dev_info(mod->dev, "%s timed out\n", __func__); + return -ETIMEDOUT; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", mod->termination_enabled); +} + +static ssize_t ican3_sysfs_set_term(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ican3_dev *mod = netdev_priv(to_net_dev(dev)); + unsigned long enable; + int ret; + + if (strict_strtoul(buf, 0, &enable)) + return -EINVAL; + + ret = ican3_set_termination(mod, enable); + if (ret) + return ret; + + return count; +} + +static DEVICE_ATTR(termination, S_IWUGO | S_IRUGO, ican3_sysfs_show_term, + ican3_sysfs_set_term); + +static struct attribute *ican3_sysfs_attrs[] = { + &dev_attr_termination.attr, + NULL, +}; + +static struct attribute_group ican3_sysfs_attr_group = { + .attrs = ican3_sysfs_attrs, +}; + +/* + * PCI Subsystem + */ + +static int __devinit ican3_probe(struct platform_device *pdev) +{ + struct janz_platform_data *pdata; + struct net_device *ndev; + struct ican3_dev *mod; + struct resource *res; + struct device *dev; + int ret; + + pdata = pdev->dev.platform_data; + if (!pdata) + return -ENXIO; + + dev_dbg(&pdev->dev, "probe: module number %d\n", pdata->modno); + + /* save the struct device for printing */ + dev = &pdev->dev; + + /* allocate the CAN device and private data */ + ndev = alloc_candev(sizeof(*mod), 0); + if (!ndev) { + dev_err(dev, "unable to allocate CANdev\n"); + ret = -ENOMEM; + goto out_return; + } + + platform_set_drvdata(pdev, ndev); + mod = netdev_priv(ndev); + mod->ndev = ndev; + mod->dev = &pdev->dev; + mod->num = pdata->modno; + netif_napi_add(ndev, &mod->napi, ican3_napi, ICAN3_RX_BUFFERS); + spin_lock_init(&mod->lock); + init_completion(&mod->termination_comp); + init_completion(&mod->buserror_comp); + + /* setup device-specific sysfs attributes */ + ndev->sysfs_groups[0] = &ican3_sysfs_attr_group; + + /* the first unallocated page in the DPM is 9 */ + mod->free_page = DPM_FREE_START; + + ndev->netdev_ops = &ican3_netdev_ops; + ndev->flags |= IFF_ECHO; + SET_NETDEV_DEV(ndev, &pdev->dev); + + mod->can.clock.freq = ICAN3_CAN_CLOCK; + mod->can.bittiming_const = &ican3_bittiming_const; + mod->can.do_set_bittiming = ican3_set_bittiming; + mod->can.do_set_mode = ican3_set_mode; + mod->can.do_get_berr_counter = ican3_get_berr_counter; + mod->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES + | CAN_CTRLMODE_BERR_REPORTING; + + /* find our IRQ number */ + mod->irq = platform_get_irq(pdev, 0); + if (mod->irq < 0) { + dev_err(dev, "IRQ line not found\n"); + ret = -ENODEV; + goto out_free_ndev; + } + + ndev->irq = mod->irq; + + /* 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_ndev; + } + + mod->dpm = ioremap(res->start, resource_size(res)); + if (!mod->dpm) { + dev_err(dev, "MODULbus registers not ioremap\n"); + ret = -ENOMEM; + goto out_free_ndev; + } + + mod->dpmctrl = mod->dpm + DPM_PAGE_SIZE; + + /* get access to the control registers for this module */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(dev, "CONTROL registers not found\n"); + ret = -ENODEV; + goto out_iounmap_dpm; + } + + mod->ctrl = ioremap(res->start, resource_size(res)); + if (!mod->ctrl) { + dev_err(dev, "CONTROL registers not ioremap\n"); + ret = -ENOMEM; + goto out_iounmap_dpm; + } + + /* disable our IRQ, then hookup the IRQ handler */ + iowrite8(1 << mod->num, &mod->ctrl->int_disable); + ret = request_irq(mod->irq, ican3_irq, IRQF_SHARED, DRV_NAME, mod); + if (ret) { + dev_err(dev, "unable to request IRQ\n"); + goto out_iounmap_ctrl; + } + + /* reset and initialize the CAN controller into fast mode */ + napi_enable(&mod->napi); + ret = ican3_startup_module(mod); + if (ret) { + dev_err(dev, "%s: unable to start CANdev\n", __func__); + goto out_free_irq; + } + + /* register with the Linux CAN layer */ + ret = register_candev(ndev); + if (ret) { + dev_err(dev, "%s: unable to register CANdev\n", __func__); + goto out_free_irq; + } + + dev_info(dev, "module %d: registered CAN device\n", pdata->modno); + return 0; + +out_free_irq: + napi_disable(&mod->napi); + iowrite8(1 << mod->num, &mod->ctrl->int_disable); + free_irq(mod->irq, mod); +out_iounmap_ctrl: + iounmap(mod->ctrl); +out_iounmap_dpm: + iounmap(mod->dpm); +out_free_ndev: + free_candev(ndev); +out_return: + return ret; +} + +static int __devexit ican3_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct ican3_dev *mod = netdev_priv(ndev); + + /* unregister the netdevice, stop interrupts */ + unregister_netdev(ndev); + napi_disable(&mod->napi); + iowrite8(1 << mod->num, &mod->ctrl->int_disable); + free_irq(mod->irq, mod); + + /* put the module into reset */ + ican3_shutdown_module(mod); + + /* unmap all registers */ + iounmap(mod->ctrl); + iounmap(mod->dpm); + + free_candev(ndev); + + return 0; +} + +static struct platform_driver ican3_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = ican3_probe, + .remove = __devexit_p(ican3_remove), +}; + +static int __init ican3_init(void) +{ + return platform_driver_register(&ican3_driver); +} + +static void __exit ican3_exit(void) +{ + platform_driver_unregister(&ican3_driver); +} + +MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>"); +MODULE_DESCRIPTION("Janz MODULbus VMOD-ICAN3 Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:janz-ican3"); + +module_init(ican3_init); +module_exit(ican3_exit); diff --git a/drivers/regulator/ab3100.c b/drivers/regulator/ab3100.c index 1afd008..7b14a67 100644 --- a/drivers/regulator/ab3100.c +++ b/drivers/regulator/ab3100.c @@ -16,7 +16,7 @@ #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/regulator/driver.h> -#include <linux/mfd/ab3100.h> +#include <linux/mfd/abx500.h> /* LDO registers and some handy masking definitions for AB3100 */ #define AB3100_LDO_A 0x40 @@ -41,7 +41,7 @@ * struct ab3100_regulator * A struct passed around the individual regulator functions * @platform_device: platform device holding this regulator - * @ab3100: handle to the AB3100 parent chip + * @dev: handle to the device * @plfdata: AB3100 platform data passed in at probe time * @regreg: regulator register number in the AB3100 * @fixed_voltage: a fixed voltage for this regulator, if this @@ -52,7 +52,7 @@ */ struct ab3100_regulator { struct regulator_dev *rdev; - struct ab3100 *ab3100; + struct device *dev; struct ab3100_platform_data *plfdata; u8 regreg; int fixed_voltage; @@ -183,7 +183,7 @@ static int ab3100_enable_regulator(struct regulator_dev *reg) int err; u8 regval; - err = ab3100_get_register_interruptible(abreg->ab3100, abreg->regreg, + err = abx500_get_register_interruptible(abreg->dev, 0, abreg->regreg, ®val); if (err) { dev_warn(®->dev, "failed to get regid %d value\n", @@ -197,7 +197,7 @@ static int ab3100_enable_regulator(struct regulator_dev *reg) regval |= AB3100_REG_ON_MASK; - err = ab3100_set_register_interruptible(abreg->ab3100, abreg->regreg, + err = abx500_set_register_interruptible(abreg->dev, 0, abreg->regreg, regval); if (err) { dev_warn(®->dev, "failed to set regid %d value\n", @@ -245,14 +245,14 @@ static int ab3100_disable_regulator(struct regulator_dev *reg) if (abreg->regreg == AB3100_LDO_D) { dev_info(®->dev, "disabling LDO D - shut down system\n"); /* Setting LDO D to 0x00 cuts the power to the SoC */ - return ab3100_set_register_interruptible(abreg->ab3100, + return abx500_set_register_interruptible(abreg->dev, 0, AB3100_LDO_D, 0x00U); } /* * All other regulators are handled here */ - err = ab3100_get_register_interruptible(abreg->ab3100, abreg->regreg, + err = abx500_get_register_interruptible(abreg->dev, 0, abreg->regreg, ®val); if (err) { dev_err(®->dev, "unable to get register 0x%x\n", @@ -260,7 +260,7 @@ static int ab3100_disable_regulator(struct regulator_dev *reg) return err; } regval &= ~AB3100_REG_ON_MASK; - return ab3100_set_register_interruptible(abreg->ab3100, abreg->regreg, + return abx500_set_register_interruptible(abreg->dev, 0, abreg->regreg, regval); } @@ -270,7 +270,7 @@ static int ab3100_is_enabled_regulator(struct regulator_dev *reg) u8 regval; int err; - err = ab3100_get_register_interruptible(abreg->ab3100, abreg->regreg, + err = abx500_get_register_interruptible(abreg->dev, 0, abreg->regreg, ®val); if (err) { dev_err(®->dev, "unable to get register 0x%x\n", @@ -305,7 +305,7 @@ static int ab3100_get_voltage_regulator(struct regulator_dev *reg) * For variable types, read out setting and index into * supplied voltage list. */ - err = ab3100_get_register_interruptible(abreg->ab3100, + err = abx500_get_register_interruptible(abreg->dev, 0, abreg->regreg, ®val); if (err) { dev_warn(®->dev, @@ -373,7 +373,7 @@ static int ab3100_set_voltage_regulator(struct regulator_dev *reg, if (bestindex < 0) return bestindex; - err = ab3100_get_register_interruptible(abreg->ab3100, + err = abx500_get_register_interruptible(abreg->dev, 0, abreg->regreg, ®val); if (err) { dev_warn(®->dev, @@ -386,7 +386,7 @@ static int ab3100_set_voltage_regulator(struct regulator_dev *reg, regval &= ~0xE0; regval |= (bestindex << 5); - err = ab3100_set_register_interruptible(abreg->ab3100, + err = abx500_set_register_interruptible(abreg->dev, 0, abreg->regreg, regval); if (err) dev_warn(®->dev, "failed to set regulator register %02x\n", @@ -414,7 +414,7 @@ static int ab3100_set_suspend_voltage_regulator(struct regulator_dev *reg, /* LDO E and BUCK have special suspend voltages you can set */ bestindex = ab3100_get_best_voltage_index(reg, uV, uV); - err = ab3100_get_register_interruptible(abreg->ab3100, + err = abx500_get_register_interruptible(abreg->dev, 0, targetreg, ®val); if (err) { dev_warn(®->dev, @@ -427,7 +427,7 @@ static int ab3100_set_suspend_voltage_regulator(struct regulator_dev *reg, regval &= ~0xE0; regval |= (bestindex << 5); - err = ab3100_set_register_interruptible(abreg->ab3100, + err = abx500_set_register_interruptible(abreg->dev, 0, targetreg, regval); if (err) dev_warn(®->dev, "failed to set regulator register %02x\n", @@ -574,13 +574,12 @@ ab3100_regulator_desc[AB3100_NUM_REGULATORS] = { static int __devinit ab3100_regulators_probe(struct platform_device *pdev) { struct ab3100_platform_data *plfdata = pdev->dev.platform_data; - struct ab3100 *ab3100 = platform_get_drvdata(pdev); int err = 0; u8 data; int i; /* Check chip state */ - err = ab3100_get_register_interruptible(ab3100, + err = abx500_get_register_interruptible(&pdev->dev, 0, AB3100_LDO_D, &data); if (err) { dev_err(&pdev->dev, "could not read initial status of LDO_D\n"); @@ -595,7 +594,7 @@ static int __devinit ab3100_regulators_probe(struct platform_device *pdev) /* Set up regulators */ for (i = 0; i < ARRAY_SIZE(ab3100_reg_init_order); i++) { - err = ab3100_set_register_interruptible(ab3100, + err = abx500_set_register_interruptible(&pdev->dev, 0, ab3100_reg_init_order[i], plfdata->reg_initvals[i]); if (err) { @@ -617,7 +616,7 @@ static int __devinit ab3100_regulators_probe(struct platform_device *pdev) * see what it looks like for a certain machine, go * into the machine I2C setup. */ - reg->ab3100 = ab3100; + reg->dev = &pdev->dev; reg->plfdata = plfdata; /* diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c index 74841ab..14b45762 100644 --- a/drivers/regulator/tps6507x-regulator.c +++ b/drivers/regulator/tps6507x-regulator.c @@ -22,68 +22,9 @@ #include <linux/platform_device.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> -#include <linux/i2c.h> #include <linux/delay.h> #include <linux/slab.h> - -/* Register definitions */ -#define TPS6507X_REG_PPATH1 0X01 -#define TPS6507X_REG_INT 0X02 -#define TPS6507X_REG_CHGCONFIG0 0X03 -#define TPS6507X_REG_CHGCONFIG1 0X04 -#define TPS6507X_REG_CHGCONFIG2 0X05 -#define TPS6507X_REG_CHGCONFIG3 0X06 -#define TPS6507X_REG_REG_ADCONFIG 0X07 -#define TPS6507X_REG_TSCMODE 0X08 -#define TPS6507X_REG_ADRESULT_1 0X09 -#define TPS6507X_REG_ADRESULT_2 0X0A -#define TPS6507X_REG_PGOOD 0X0B -#define TPS6507X_REG_PGOODMASK 0X0C -#define TPS6507X_REG_CON_CTRL1 0X0D -#define TPS6507X_REG_CON_CTRL2 0X0E -#define TPS6507X_REG_CON_CTRL3 0X0F -#define TPS6507X_REG_DEFDCDC1 0X10 -#define TPS6507X_REG_DEFDCDC2_LOW 0X11 -#define TPS6507X_REG_DEFDCDC2_HIGH 0X12 -#define TPS6507X_REG_DEFDCDC3_LOW 0X13 -#define TPS6507X_REG_DEFDCDC3_HIGH 0X14 -#define TPS6507X_REG_DEFSLEW 0X15 -#define TPS6507X_REG_LDO_CTRL1 0X16 -#define TPS6507X_REG_DEFLDO2 0X17 -#define TPS6507X_REG_WLED_CTRL1 0X18 -#define TPS6507X_REG_WLED_CTRL2 0X19 - -/* CON_CTRL1 bitfields */ -#define TPS6507X_CON_CTRL1_DCDC1_ENABLE BIT(4) -#define TPS6507X_CON_CTRL1_DCDC2_ENABLE BIT(3) -#define TPS6507X_CON_CTRL1_DCDC3_ENABLE BIT(2) -#define TPS6507X_CON_CTRL1_LDO1_ENABLE BIT(1) -#define TPS6507X_CON_CTRL1_LDO2_ENABLE BIT(0) - -/* DEFDCDC1 bitfields */ -#define TPS6507X_DEFDCDC1_DCDC1_EXT_ADJ_EN BIT(7) -#define TPS6507X_DEFDCDC1_DCDC1_MASK 0X3F - -/* DEFDCDC2_LOW bitfields */ -#define TPS6507X_DEFDCDC2_LOW_DCDC2_MASK 0X3F - -/* DEFDCDC2_HIGH bitfields */ -#define TPS6507X_DEFDCDC2_HIGH_DCDC2_MASK 0X3F - -/* DEFDCDC3_LOW bitfields */ -#define TPS6507X_DEFDCDC3_LOW_DCDC3_MASK 0X3F - -/* DEFDCDC3_HIGH bitfields */ -#define TPS6507X_DEFDCDC3_HIGH_DCDC3_MASK 0X3F - -/* TPS6507X_REG_LDO_CTRL1 bitfields */ -#define TPS6507X_REG_LDO_CTRL1_LDO1_MASK 0X0F - -/* TPS6507X_REG_DEFLDO2 bitfields */ -#define TPS6507X_REG_DEFLDO2_LDO2_MASK 0X3F - -/* VDCDC MASK */ -#define TPS6507X_DEFDCDCX_DCDC_MASK 0X3F +#include <linux/mfd/tps6507x.h> /* DCDC's */ #define TPS6507X_DCDC_1 0 @@ -162,101 +103,146 @@ struct tps_info { const u16 *table; }; -struct tps_pmic { +static const struct tps_info tps6507x_pmic_regs[] = { + { + .name = "VDCDC1", + .min_uV = 725000, + .max_uV = 3300000, + .table_len = ARRAY_SIZE(VDCDCx_VSEL_table), + .table = VDCDCx_VSEL_table, + }, + { + .name = "VDCDC2", + .min_uV = 725000, + .max_uV = 3300000, + .table_len = ARRAY_SIZE(VDCDCx_VSEL_table), + .table = VDCDCx_VSEL_table, + }, + { + .name = "VDCDC3", + .min_uV = 725000, + .max_uV = 3300000, + .table_len = ARRAY_SIZE(VDCDCx_VSEL_table), + .table = VDCDCx_VSEL_table, + }, + { + .name = "LDO1", + .min_uV = 1000000, + .max_uV = 3300000, + .table_len = ARRAY_SIZE(LDO1_VSEL_table), + .table = LDO1_VSEL_table, + }, + { + .name = "LDO2", + .min_uV = 725000, + .max_uV = 3300000, + .table_len = ARRAY_SIZE(LDO2_VSEL_table), + .table = LDO2_VSEL_table, + }, +}; + +struct tps6507x_pmic { struct regulator_desc desc[TPS6507X_NUM_REGULATOR]; - struct i2c_client *client; + struct tps6507x_dev *mfd; struct regulator_dev *rdev[TPS6507X_NUM_REGULATOR]; const struct tps_info *info[TPS6507X_NUM_REGULATOR]; struct mutex io_lock; }; - -static inline int tps_6507x_read(struct tps_pmic *tps, u8 reg) +static inline int tps6507x_pmic_read(struct tps6507x_pmic *tps, u8 reg) { - return i2c_smbus_read_byte_data(tps->client, reg); + u8 val; + int err; + + err = tps->mfd->read_dev(tps->mfd, reg, 1, &val); + + if (err) + return err; + + return val; } -static inline int tps_6507x_write(struct tps_pmic *tps, u8 reg, u8 val) +static inline int tps6507x_pmic_write(struct tps6507x_pmic *tps, u8 reg, u8 val) { - return i2c_smbus_write_byte_data(tps->client, reg, val); + return tps->mfd->write_dev(tps->mfd, reg, 1, &val); } -static int tps_6507x_set_bits(struct tps_pmic *tps, u8 reg, u8 mask) +static int tps6507x_pmic_set_bits(struct tps6507x_pmic *tps, u8 reg, u8 mask) { int err, data; mutex_lock(&tps->io_lock); - data = tps_6507x_read(tps, reg); + data = tps6507x_pmic_read(tps, reg); if (data < 0) { - dev_err(&tps->client->dev, "Read from reg 0x%x failed\n", reg); + dev_err(tps->mfd->dev, "Read from reg 0x%x failed\n", reg); err = data; goto out; } data |= mask; - err = tps_6507x_write(tps, reg, data); + err = tps6507x_pmic_write(tps, reg, data); if (err) - dev_err(&tps->client->dev, "Write for reg 0x%x failed\n", reg); + dev_err(tps->mfd->dev, "Write for reg 0x%x failed\n", reg); out: mutex_unlock(&tps->io_lock); return err; } -static int tps_6507x_clear_bits(struct tps_pmic *tps, u8 reg, u8 mask) +static int tps6507x_pmic_clear_bits(struct tps6507x_pmic *tps, u8 reg, u8 mask) { int err, data; mutex_lock(&tps->io_lock); - data = tps_6507x_read(tps, reg); + data = tps6507x_pmic_read(tps, reg); if (data < 0) { - dev_err(&tps->client->dev, "Read from reg 0x%x failed\n", reg); + dev_err(tps->mfd->dev, "Read from reg 0x%x failed\n", reg); err = data; goto out; } data &= ~mask; - err = tps_6507x_write(tps, reg, data); + err = tps6507x_pmic_write(tps, reg, data); if (err) - dev_err(&tps->client->dev, "Write for reg 0x%x failed\n", reg); + dev_err(tps->mfd->dev, "Write for reg 0x%x failed\n", reg); out: mutex_unlock(&tps->io_lock); return err; } -static int tps_6507x_reg_read(struct tps_pmic *tps, u8 reg) +static int tps6507x_pmic_reg_read(struct tps6507x_pmic *tps, u8 reg) { int data; mutex_lock(&tps->io_lock); - data = tps_6507x_read(tps, reg); + data = tps6507x_pmic_read(tps, reg); if (data < 0) - dev_err(&tps->client->dev, "Read from reg 0x%x failed\n", reg); + dev_err(tps->mfd->dev, "Read from reg 0x%x failed\n", reg); mutex_unlock(&tps->io_lock); return data; } -static int tps_6507x_reg_write(struct tps_pmic *tps, u8 reg, u8 val) +static int tps6507x_pmic_reg_write(struct tps6507x_pmic *tps, u8 reg, u8 val) { int err; mutex_lock(&tps->io_lock); - err = tps_6507x_write(tps, reg, val); + err = tps6507x_pmic_write(tps, reg, val); if (err < 0) - dev_err(&tps->client->dev, "Write for reg 0x%x failed\n", reg); + dev_err(tps->mfd->dev, "Write for reg 0x%x failed\n", reg); mutex_unlock(&tps->io_lock); return err; } -static int tps6507x_dcdc_is_enabled(struct regulator_dev *dev) +static int tps6507x_pmic_dcdc_is_enabled(struct regulator_dev *dev) { - struct tps_pmic *tps = rdev_get_drvdata(dev); + struct tps6507x_pmic *tps = rdev_get_drvdata(dev); int data, dcdc = rdev_get_id(dev); u8 shift; @@ -264,7 +250,7 @@ static int tps6507x_dcdc_is_enabled(struct regulator_dev *dev) return -EINVAL; shift = TPS6507X_MAX_REG_ID - dcdc; - data = tps_6507x_reg_read(tps, TPS6507X_REG_CON_CTRL1); + data = tps6507x_pmic_reg_read(tps, TPS6507X_REG_CON_CTRL1); if (data < 0) return data; @@ -272,9 +258,9 @@ static int tps6507x_dcdc_is_enabled(struct regulator_dev *dev) return (data & 1<<shift) ? 1 : 0; } -static int tps6507x_ldo_is_enabled(struct regulator_dev *dev) +static int tps6507x_pmic_ldo_is_enabled(struct regulator_dev *dev) { - struct tps_pmic *tps = rdev_get_drvdata(dev); + struct tps6507x_pmic *tps = rdev_get_drvdata(dev); int data, ldo = rdev_get_id(dev); u8 shift; @@ -282,7 +268,7 @@ static int tps6507x_ldo_is_enabled(struct regulator_dev *dev) return -EINVAL; shift = TPS6507X_MAX_REG_ID - ldo; - data = tps_6507x_reg_read(tps, TPS6507X_REG_CON_CTRL1); + data = tps6507x_pmic_reg_read(tps, TPS6507X_REG_CON_CTRL1); if (data < 0) return data; @@ -290,9 +276,9 @@ static int tps6507x_ldo_is_enabled(struct regulator_dev *dev) return (data & 1<<shift) ? 1 : 0; } -static int tps6507x_dcdc_enable(struct regulator_dev *dev) +static int tps6507x_pmic_dcdc_enable(struct regulator_dev *dev) { - struct tps_pmic *tps = rdev_get_drvdata(dev); + struct tps6507x_pmic *tps = rdev_get_drvdata(dev); int dcdc = rdev_get_id(dev); u8 shift; @@ -300,12 +286,12 @@ static int tps6507x_dcdc_enable(struct regulator_dev *dev) return -EINVAL; shift = TPS6507X_MAX_REG_ID - dcdc; - return tps_6507x_set_bits(tps, TPS6507X_REG_CON_CTRL1, 1 << shift); + return tps6507x_pmic_set_bits(tps, TPS6507X_REG_CON_CTRL1, 1 << shift); } -static int tps6507x_dcdc_disable(struct regulator_dev *dev) +static int tps6507x_pmic_dcdc_disable(struct regulator_dev *dev) { - struct tps_pmic *tps = rdev_get_drvdata(dev); + struct tps6507x_pmic *tps = rdev_get_drvdata(dev); int dcdc = rdev_get_id(dev); u8 shift; @@ -313,12 +299,13 @@ static int tps6507x_dcdc_disable(struct regulator_dev *dev) return -EINVAL; shift = TPS6507X_MAX_REG_ID - dcdc; - return tps_6507x_clear_bits(tps, TPS6507X_REG_CON_CTRL1, 1 << shift); + return tps6507x_pmic_clear_bits(tps, TPS6507X_REG_CON_CTRL1, + 1 << shift); } -static int tps6507x_ldo_enable(struct regulator_dev *dev) +static int tps6507x_pmic_ldo_enable(struct regulator_dev *dev) { - struct tps_pmic *tps = rdev_get_drvdata(dev); + struct tps6507x_pmic *tps = rdev_get_drvdata(dev); int ldo = rdev_get_id(dev); u8 shift; @@ -326,12 +313,12 @@ static int tps6507x_ldo_enable(struct regulator_dev *dev) return -EINVAL; shift = TPS6507X_MAX_REG_ID - ldo; - return tps_6507x_set_bits(tps, TPS6507X_REG_CON_CTRL1, 1 << shift); + return tps6507x_pmic_set_bits(tps, TPS6507X_REG_CON_CTRL1, 1 << shift); } -static int tps6507x_ldo_disable(struct regulator_dev *dev) +static int tps6507x_pmic_ldo_disable(struct regulator_dev *dev) { - struct tps_pmic *tps = rdev_get_drvdata(dev); + struct tps6507x_pmic *tps = rdev_get_drvdata(dev); int ldo = rdev_get_id(dev); u8 shift; @@ -339,12 +326,13 @@ static int tps6507x_ldo_disable(struct regulator_dev *dev) return -EINVAL; shift = TPS6507X_MAX_REG_ID - ldo; - return tps_6507x_clear_bits(tps, TPS6507X_REG_CON_CTRL1, 1 << shift); + return tps6507x_pmic_clear_bits(tps, TPS6507X_REG_CON_CTRL1, + 1 << shift); } -static int tps6507x_dcdc_get_voltage(struct regulator_dev *dev) +static int tps6507x_pmic_dcdc_get_voltage(struct regulator_dev *dev) { - struct tps_pmic *tps = rdev_get_drvdata(dev); + struct tps6507x_pmic *tps = rdev_get_drvdata(dev); int data, dcdc = rdev_get_id(dev); u8 reg; @@ -362,7 +350,7 @@ static int tps6507x_dcdc_get_voltage(struct regulator_dev *dev) return -EINVAL; } - data = tps_6507x_reg_read(tps, reg); + data = tps6507x_pmic_reg_read(tps, reg); if (data < 0) return data; @@ -370,10 +358,10 @@ static int tps6507x_dcdc_get_voltage(struct regulator_dev *dev) return tps->info[dcdc]->table[data] * 1000; } -static int tps6507x_dcdc_set_voltage(struct regulator_dev *dev, +static int tps6507x_pmic_dcdc_set_voltage(struct regulator_dev *dev, int min_uV, int max_uV) { - struct tps_pmic *tps = rdev_get_drvdata(dev); + struct tps6507x_pmic *tps = rdev_get_drvdata(dev); int data, vsel, dcdc = rdev_get_id(dev); u8 reg; @@ -411,19 +399,19 @@ static int tps6507x_dcdc_set_voltage(struct regulator_dev *dev, if (vsel == tps->info[dcdc]->table_len) return -EINVAL; - data = tps_6507x_reg_read(tps, reg); + data = tps6507x_pmic_reg_read(tps, reg); if (data < 0) return data; data &= ~TPS6507X_DEFDCDCX_DCDC_MASK; data |= vsel; - return tps_6507x_reg_write(tps, reg, data); + return tps6507x_pmic_reg_write(tps, reg, data); } -static int tps6507x_ldo_get_voltage(struct regulator_dev *dev) +static int tps6507x_pmic_ldo_get_voltage(struct regulator_dev *dev) { - struct tps_pmic *tps = rdev_get_drvdata(dev); + struct tps6507x_pmic *tps = rdev_get_drvdata(dev); int data, ldo = rdev_get_id(dev); u8 reg, mask; @@ -437,7 +425,7 @@ static int tps6507x_ldo_get_voltage(struct regulator_dev *dev) TPS6507X_REG_DEFLDO2_LDO2_MASK); } - data = tps_6507x_reg_read(tps, reg); + data = tps6507x_pmic_reg_read(tps, reg); if (data < 0) return data; @@ -445,10 +433,10 @@ static int tps6507x_ldo_get_voltage(struct regulator_dev *dev) return tps->info[ldo]->table[data] * 1000; } -static int tps6507x_ldo_set_voltage(struct regulator_dev *dev, +static int tps6507x_pmic_ldo_set_voltage(struct regulator_dev *dev, int min_uV, int max_uV) { - struct tps_pmic *tps = rdev_get_drvdata(dev); + struct tps6507x_pmic *tps = rdev_get_drvdata(dev); int data, vsel, ldo = rdev_get_id(dev); u8 reg, mask; @@ -479,20 +467,20 @@ static int tps6507x_ldo_set_voltage(struct regulator_dev *dev, if (vsel == tps->info[ldo]->table_len) return -EINVAL; - data = tps_6507x_reg_read(tps, reg); + data = tps6507x_pmic_reg_read(tps, reg); if (data < 0) return data; data &= ~mask; data |= vsel; - return tps_6507x_reg_write(tps, reg, data); + return tps6507x_pmic_reg_write(tps, reg, data); } -static int tps6507x_dcdc_list_voltage(struct regulator_dev *dev, +static int tps6507x_pmic_dcdc_list_voltage(struct regulator_dev *dev, unsigned selector) { - struct tps_pmic *tps = rdev_get_drvdata(dev); + struct tps6507x_pmic *tps = rdev_get_drvdata(dev); int dcdc = rdev_get_id(dev); if (dcdc < TPS6507X_DCDC_1 || dcdc > TPS6507X_DCDC_3) @@ -504,10 +492,10 @@ static int tps6507x_dcdc_list_voltage(struct regulator_dev *dev, return tps->info[dcdc]->table[selector] * 1000; } -static int tps6507x_ldo_list_voltage(struct regulator_dev *dev, +static int tps6507x_pmic_ldo_list_voltage(struct regulator_dev *dev, unsigned selector) { - struct tps_pmic *tps = rdev_get_drvdata(dev); + struct tps6507x_pmic *tps = rdev_get_drvdata(dev); int ldo = rdev_get_id(dev); if (ldo < TPS6507X_LDO_1 || ldo > TPS6507X_LDO_2) @@ -520,47 +508,54 @@ static int tps6507x_ldo_list_voltage(struct regulator_dev *dev, } /* Operations permitted on VDCDCx */ -static struct regulator_ops tps6507x_dcdc_ops = { - .is_enabled = tps6507x_dcdc_is_enabled, - .enable = tps6507x_dcdc_enable, - .disable = tps6507x_dcdc_disable, - .get_voltage = tps6507x_dcdc_get_voltage, - .set_voltage = tps6507x_dcdc_set_voltage, - .list_voltage = tps6507x_dcdc_list_voltage, +static struct regulator_ops tps6507x_pmic_dcdc_ops = { + .is_enabled = tps6507x_pmic_dcdc_is_enabled, + .enable = tps6507x_pmic_dcdc_enable, + .disable = tps6507x_pmic_dcdc_disable, + .get_voltage = tps6507x_pmic_dcdc_get_voltage, + .set_voltage = tps6507x_pmic_dcdc_set_voltage, + .list_voltage = tps6507x_pmic_dcdc_list_voltage, }; /* Operations permitted on LDOx */ -static struct regulator_ops tps6507x_ldo_ops = { - .is_enabled = tps6507x_ldo_is_enabled, - .enable = tps6507x_ldo_enable, - .disable = tps6507x_ldo_disable, - .get_voltage = tps6507x_ldo_get_voltage, - .set_voltage = tps6507x_ldo_set_voltage, - .list_voltage = tps6507x_ldo_list_voltage, +static struct regulator_ops tps6507x_pmic_ldo_ops = { + .is_enabled = tps6507x_pmic_ldo_is_enabled, + .enable = tps6507x_pmic_ldo_enable, + .disable = tps6507x_pmic_ldo_disable, + .get_voltage = tps6507x_pmic_ldo_get_voltage, + .set_voltage = tps6507x_pmic_ldo_set_voltage, + .list_voltage = tps6507x_pmic_ldo_list_voltage, }; -static int __devinit tps_6507x_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static __devinit +int tps6507x_pmic_probe(struct platform_device *pdev) { + struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent); static int desc_id; - const struct tps_info *info = (void *)id->driver_data; + const struct tps_info *info = &tps6507x_pmic_regs[0]; struct regulator_init_data *init_data; struct regulator_dev *rdev; - struct tps_pmic *tps; + struct tps6507x_pmic *tps; + struct tps6507x_board *tps_board; int i; int error; - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; + /** + * tps_board points to pmic related constants + * coming from the board-evm file. + */ + + tps_board = dev_get_platdata(tps6507x_dev->dev); + if (!tps_board) + return -EINVAL; /** * init_data points to array of regulator_init structures * coming from the board-evm file. */ - init_data = client->dev.platform_data; + init_data = tps_board->tps6507x_pmic_init_data; if (!init_data) - return -EIO; + return -EINVAL; tps = kzalloc(sizeof(*tps), GFP_KERNEL); if (!tps) @@ -569,7 +564,7 @@ static int __devinit tps_6507x_probe(struct i2c_client *client, mutex_init(&tps->io_lock); /* common for all regulators */ - tps->client = client; + tps->mfd = tps6507x_dev; for (i = 0; i < TPS6507X_NUM_REGULATOR; i++, info++, init_data++) { /* Register the regulators */ @@ -578,15 +573,16 @@ static int __devinit tps_6507x_probe(struct i2c_client *client, tps->desc[i].id = desc_id++; tps->desc[i].n_voltages = num_voltages[i]; tps->desc[i].ops = (i > TPS6507X_DCDC_3 ? - &tps6507x_ldo_ops : &tps6507x_dcdc_ops); + &tps6507x_pmic_ldo_ops : &tps6507x_pmic_dcdc_ops); tps->desc[i].type = REGULATOR_VOLTAGE; tps->desc[i].owner = THIS_MODULE; rdev = regulator_register(&tps->desc[i], - &client->dev, init_data, tps); + tps6507x_dev->dev, init_data, tps); if (IS_ERR(rdev)) { - dev_err(&client->dev, "failed to register %s\n", - id->name); + dev_err(tps6507x_dev->dev, + "failed to register %s regulator\n", + pdev->name); error = PTR_ERR(rdev); goto fail; } @@ -595,7 +591,7 @@ static int __devinit tps_6507x_probe(struct i2c_client *client, tps->rdev[i] = rdev; } - i2c_set_clientdata(client, tps); + tps6507x_dev->pmic = tps; return 0; @@ -608,19 +604,17 @@ fail: } /** - * tps_6507x_remove - TPS6507x driver i2c remove handler + * tps6507x_remove - TPS6507x driver i2c remove handler * @client: i2c driver client device structure * * Unregister TPS driver as an i2c client device driver */ -static int __devexit tps_6507x_remove(struct i2c_client *client) +static int __devexit tps6507x_pmic_remove(struct platform_device *pdev) { - struct tps_pmic *tps = i2c_get_clientdata(client); + struct tps6507x_dev *tps6507x_dev = platform_get_drvdata(pdev); + struct tps6507x_pmic *tps = tps6507x_dev->pmic; int i; - /* clear the client data in i2c */ - i2c_set_clientdata(client, NULL); - for (i = 0; i < TPS6507X_NUM_REGULATOR; i++) regulator_unregister(tps->rdev[i]); @@ -629,83 +623,38 @@ static int __devexit tps_6507x_remove(struct i2c_client *client) return 0; } -static const struct tps_info tps6507x_regs[] = { - { - .name = "VDCDC1", - .min_uV = 725000, - .max_uV = 3300000, - .table_len = ARRAY_SIZE(VDCDCx_VSEL_table), - .table = VDCDCx_VSEL_table, - }, - { - .name = "VDCDC2", - .min_uV = 725000, - .max_uV = 3300000, - .table_len = ARRAY_SIZE(VDCDCx_VSEL_table), - .table = VDCDCx_VSEL_table, - }, - { - .name = "VDCDC3", - .min_uV = 725000, - .max_uV = 3300000, - .table_len = ARRAY_SIZE(VDCDCx_VSEL_table), - .table = VDCDCx_VSEL_table, - }, - { - .name = "LDO1", - .min_uV = 1000000, - .max_uV = 3300000, - .table_len = ARRAY_SIZE(LDO1_VSEL_table), - .table = LDO1_VSEL_table, - }, - { - .name = "LDO2", - .min_uV = 725000, - .max_uV = 3300000, - .table_len = ARRAY_SIZE(LDO2_VSEL_table), - .table = LDO2_VSEL_table, - }, -}; - -static const struct i2c_device_id tps_6507x_id[] = { - {.name = "tps6507x", - .driver_data = (unsigned long) tps6507x_regs,}, - { }, -}; -MODULE_DEVICE_TABLE(i2c, tps_6507x_id); - -static struct i2c_driver tps_6507x_i2c_driver = { +static struct platform_driver tps6507x_pmic_driver = { .driver = { - .name = "tps6507x", + .name = "tps6507x-pmic", .owner = THIS_MODULE, }, - .probe = tps_6507x_probe, - .remove = __devexit_p(tps_6507x_remove), - .id_table = tps_6507x_id, + .probe = tps6507x_pmic_probe, + .remove = __devexit_p(tps6507x_pmic_remove), }; /** - * tps_6507x_init + * tps6507x_pmic_init * * Module init function */ -static int __init tps_6507x_init(void) +static int __init tps6507x_pmic_init(void) { - return i2c_add_driver(&tps_6507x_i2c_driver); + return platform_driver_register(&tps6507x_pmic_driver); } -subsys_initcall(tps_6507x_init); +subsys_initcall(tps6507x_pmic_init); /** - * tps_6507x_cleanup + * tps6507x_pmic_cleanup * * Module exit function */ -static void __exit tps_6507x_cleanup(void) +static void __exit tps6507x_pmic_cleanup(void) { - i2c_del_driver(&tps_6507x_i2c_driver); + platform_driver_unregister(&tps6507x_pmic_driver); } -module_exit(tps_6507x_cleanup); +module_exit(tps6507x_pmic_cleanup); MODULE_AUTHOR("Texas Instruments"); MODULE_DESCRIPTION("TPS6507x voltage regulator driver"); MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:tps6507x-pmic"); diff --git a/drivers/rtc/rtc-ab3100.c b/drivers/rtc/rtc-ab3100.c index 4704aac..d26780e 100644 --- a/drivers/rtc/rtc-ab3100.c +++ b/drivers/rtc/rtc-ab3100.c @@ -9,7 +9,7 @@ #include <linux/init.h> #include <linux/platform_device.h> #include <linux/rtc.h> -#include <linux/mfd/ab3100.h> +#include <linux/mfd/abx500.h> /* Clock rate in Hz */ #define AB3100_RTC_CLOCK_RATE 32768 @@ -45,7 +45,6 @@ */ static int ab3100_rtc_set_mmss(struct device *dev, unsigned long secs) { - struct ab3100 *ab3100_data = dev_get_drvdata(dev); u8 regs[] = {AB3100_TI0, AB3100_TI1, AB3100_TI2, AB3100_TI3, AB3100_TI4, AB3100_TI5}; unsigned char buf[6]; @@ -61,27 +60,26 @@ static int ab3100_rtc_set_mmss(struct device *dev, unsigned long secs) buf[5] = (fat_time >> 40) & 0xFF; for (i = 0; i < 6; i++) { - err = ab3100_set_register_interruptible(ab3100_data, + err = abx500_set_register_interruptible(dev, 0, regs[i], buf[i]); if (err) return err; } /* Set the flag to mark that the clock is now set */ - return ab3100_mask_and_set_register_interruptible(ab3100_data, + return abx500_mask_and_set_register_interruptible(dev, 0, AB3100_RTC, - 0xFE, 0x01); + 0x01, 0x01); } static int ab3100_rtc_read_time(struct device *dev, struct rtc_time *tm) { - struct ab3100 *ab3100_data = dev_get_drvdata(dev); unsigned long time; u8 rtcval; int err; - err = ab3100_get_register_interruptible(ab3100_data, + err = abx500_get_register_interruptible(dev, 0, AB3100_RTC, &rtcval); if (err) return err; @@ -94,7 +92,7 @@ static int ab3100_rtc_read_time(struct device *dev, struct rtc_time *tm) u8 buf[6]; /* Read out time registers */ - err = ab3100_get_register_page_interruptible(ab3100_data, + err = abx500_get_register_page_interruptible(dev, 0, AB3100_TI0, buf, 6); if (err != 0) @@ -114,7 +112,6 @@ static int ab3100_rtc_read_time(struct device *dev, struct rtc_time *tm) static int ab3100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) { - struct ab3100 *ab3100_data = dev_get_drvdata(dev); unsigned long time; u64 fat_time; u8 buf[6]; @@ -122,7 +119,7 @@ static int ab3100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) int err; /* Figure out if alarm is enabled or not */ - err = ab3100_get_register_interruptible(ab3100_data, + err = abx500_get_register_interruptible(dev, 0, AB3100_RTC, &rtcval); if (err) return err; @@ -133,7 +130,7 @@ static int ab3100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) /* No idea how this could be represented */ alarm->pending = 0; /* Read out alarm registers, only 4 bytes */ - err = ab3100_get_register_page_interruptible(ab3100_data, + err = abx500_get_register_page_interruptible(dev, 0, AB3100_AL0, buf, 4); if (err) return err; @@ -148,7 +145,6 @@ static int ab3100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) static int ab3100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) { - struct ab3100 *ab3100_data = dev_get_drvdata(dev); u8 regs[] = {AB3100_AL0, AB3100_AL1, AB3100_AL2, AB3100_AL3}; unsigned char buf[4]; unsigned long secs; @@ -165,21 +161,19 @@ static int ab3100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) /* Set the alarm */ for (i = 0; i < 4; i++) { - err = ab3100_set_register_interruptible(ab3100_data, + err = abx500_set_register_interruptible(dev, 0, regs[i], buf[i]); if (err) return err; } /* Then enable the alarm */ - return ab3100_mask_and_set_register_interruptible(ab3100_data, - AB3100_RTC, ~(1 << 2), + return abx500_mask_and_set_register_interruptible(dev, 0, + AB3100_RTC, (1 << 2), alarm->enabled << 2); } static int ab3100_rtc_irq_enable(struct device *dev, unsigned int enabled) { - struct ab3100 *ab3100_data = dev_get_drvdata(dev); - /* * It's not possible to enable/disable the alarm IRQ for this RTC. * It does not actually trigger any IRQ: instead its only function is @@ -188,12 +182,12 @@ static int ab3100_rtc_irq_enable(struct device *dev, unsigned int enabled) * and need to be handled there instead. */ if (enabled) - return ab3100_mask_and_set_register_interruptible(ab3100_data, - AB3100_RTC, ~(1 << 2), + return abx500_mask_and_set_register_interruptible(dev, 0, + AB3100_RTC, (1 << 2), 1 << 2); else - return ab3100_mask_and_set_register_interruptible(ab3100_data, - AB3100_RTC, ~(1 << 2), + return abx500_mask_and_set_register_interruptible(dev, 0, + AB3100_RTC, (1 << 2), 0); } @@ -210,10 +204,9 @@ static int __init ab3100_rtc_probe(struct platform_device *pdev) int err; u8 regval; struct rtc_device *rtc; - struct ab3100 *ab3100_data = platform_get_drvdata(pdev); /* The first RTC register needs special treatment */ - err = ab3100_get_register_interruptible(ab3100_data, + err = abx500_get_register_interruptible(&pdev->dev, 0, AB3100_RTC, ®val); if (err) { dev_err(&pdev->dev, "unable to read RTC register\n"); @@ -231,7 +224,7 @@ static int __init ab3100_rtc_probe(struct platform_device *pdev) * This bit remains until RTC power is lost. */ regval = 1 | RTC_SETTING; - err = ab3100_set_register_interruptible(ab3100_data, + err = abx500_set_register_interruptible(&pdev->dev, 0, AB3100_RTC, regval); /* Ignore any error on this write */ } diff --git a/drivers/watchdog/rdc321x_wdt.c b/drivers/watchdog/rdc321x_wdt.c index 69c6adb..428f8a1 100644 --- a/drivers/watchdog/rdc321x_wdt.c +++ b/drivers/watchdog/rdc321x_wdt.c @@ -1,7 +1,7 @@ /* * RDC321x watchdog driver * - * Copyright (C) 2007 Florian Fainelli <florian@openwrt.org> + * Copyright (C) 2007-2010 Florian Fainelli <florian@openwrt.org> * * This driver is highly inspired from the cpu5_wdt driver * @@ -36,8 +36,7 @@ #include <linux/watchdog.h> #include <linux/io.h> #include <linux/uaccess.h> - -#include <asm/rdc321x_defs.h> +#include <linux/mfd/rdc321x.h> #define RDC_WDT_MASK 0x80000000 /* Mask */ #define RDC_WDT_EN 0x00800000 /* Enable bit */ @@ -63,6 +62,8 @@ static struct { int default_ticks; unsigned long inuse; spinlock_t lock; + struct pci_dev *sb_pdev; + int base_reg; } rdc321x_wdt_device; /* generic helper functions */ @@ -70,14 +71,18 @@ static struct { static void rdc321x_wdt_trigger(unsigned long unused) { unsigned long flags; + u32 val; if (rdc321x_wdt_device.running) ticks--; /* keep watchdog alive */ spin_lock_irqsave(&rdc321x_wdt_device.lock, flags); - outl(RDC_WDT_EN | inl(RDC3210_CFGREG_DATA), - RDC3210_CFGREG_DATA); + pci_read_config_dword(rdc321x_wdt_device.sb_pdev, + rdc321x_wdt_device.base_reg, &val); + val |= RDC_WDT_EN; + pci_write_config_dword(rdc321x_wdt_device.sb_pdev, + rdc321x_wdt_device.base_reg, val); spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags); /* requeue?? */ @@ -105,10 +110,13 @@ static void rdc321x_wdt_start(void) /* Clear the timer */ spin_lock_irqsave(&rdc321x_wdt_device.lock, flags); - outl(RDC_CLS_TMR, RDC3210_CFGREG_ADDR); + pci_write_config_dword(rdc321x_wdt_device.sb_pdev, + rdc321x_wdt_device.base_reg, RDC_CLS_TMR); /* Enable watchdog and set the timeout to 81.92 us */ - outl(RDC_WDT_EN | RDC_WDT_CNT, RDC3210_CFGREG_DATA); + pci_write_config_dword(rdc321x_wdt_device.sb_pdev, + rdc321x_wdt_device.base_reg, + RDC_WDT_EN | RDC_WDT_CNT); spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags); mod_timer(&rdc321x_wdt_device.timer, @@ -148,7 +156,7 @@ static long rdc321x_wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; - unsigned int value; + u32 value; static const struct watchdog_info ident = { .options = WDIOF_CARDRESET, .identity = "RDC321x WDT", @@ -162,9 +170,10 @@ static long rdc321x_wdt_ioctl(struct file *file, unsigned int cmd, case WDIOC_GETSTATUS: /* Read the value from the DATA register */ spin_lock_irqsave(&rdc321x_wdt_device.lock, flags); - value = inl(RDC3210_CFGREG_DATA); + pci_read_config_dword(rdc321x_wdt_device.sb_pdev, + rdc321x_wdt_device.base_reg, &value); spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags); - if (copy_to_user(argp, &value, sizeof(int))) + if (copy_to_user(argp, &value, sizeof(u32))) return -EFAULT; break; case WDIOC_GETSUPPORT: @@ -219,17 +228,35 @@ static struct miscdevice rdc321x_wdt_misc = { static int __devinit rdc321x_wdt_probe(struct platform_device *pdev) { int err; + struct resource *r; + struct rdc321x_wdt_pdata *pdata; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "no platform data supplied\n"); + return -ENODEV; + } + + r = platform_get_resource_byname(pdev, IORESOURCE_IO, "wdt-reg"); + if (!r) { + dev_err(&pdev->dev, "failed to get wdt-reg resource\n"); + return -ENODEV; + } + + rdc321x_wdt_device.sb_pdev = pdata->sb_pdev; + rdc321x_wdt_device.base_reg = r->start; err = misc_register(&rdc321x_wdt_misc); if (err < 0) { - printk(KERN_ERR PFX "watchdog misc_register failed\n"); + dev_err(&pdev->dev, "misc_register failed\n"); return err; } spin_lock_init(&rdc321x_wdt_device.lock); /* Reset the watchdog */ - outl(RDC_WDT_RST, RDC3210_CFGREG_DATA); + pci_write_config_dword(rdc321x_wdt_device.sb_pdev, + rdc321x_wdt_device.base_reg, RDC_WDT_RST); init_completion(&rdc321x_wdt_device.stop); rdc321x_wdt_device.queue = 0; @@ -240,7 +267,7 @@ static int __devinit rdc321x_wdt_probe(struct platform_device *pdev) rdc321x_wdt_device.default_ticks = ticks; - printk(KERN_INFO PFX "watchdog init success\n"); + dev_info(&pdev->dev, "watchdog init success\n"); return 0; } |