diff options
Diffstat (limited to 'drivers')
27 files changed, 969 insertions, 248 deletions
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index 05ff8c77649..d52ce118832 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -76,6 +76,7 @@ #include <linux/device.h> #include <linux/platform_device.h> #include <linux/ata_platform.h> +#include <linux/mbus.h> #include <scsi/scsi_host.h> #include <scsi/scsi_cmnd.h> #include <scsi/scsi_device.h> @@ -370,6 +371,9 @@ enum { #define IS_GEN_IIE(hpriv) ((hpriv)->hp_flags & MV_HP_GEN_IIE) #define HAS_PCI(host) (!((host)->ports[0]->flags & MV_FLAG_SOC)) +#define WINDOW_CTRL(i) (0x20030 + ((i) << 4)) +#define WINDOW_BASE(i) (0x20034 + ((i) << 4)) + enum { /* DMA boundary 0xffff is required by the s/g splitting * we need on /length/ in mv_fill-sg(). @@ -2769,6 +2773,27 @@ static int mv_create_dma_pools(struct mv_host_priv *hpriv, struct device *dev) return 0; } +static void mv_conf_mbus_windows(struct mv_host_priv *hpriv, + struct mbus_dram_target_info *dram) +{ + int i; + + for (i = 0; i < 4; i++) { + writel(0, hpriv->base + WINDOW_CTRL(i)); + writel(0, hpriv->base + WINDOW_BASE(i)); + } + + for (i = 0; i < dram->num_cs; i++) { + struct mbus_dram_window *cs = dram->cs + i; + + writel(((cs->size - 1) & 0xffff0000) | + (cs->mbus_attr << 8) | + (dram->mbus_dram_target_id << 4) | 1, + hpriv->base + WINDOW_CTRL(i)); + writel(cs->base, hpriv->base + WINDOW_BASE(i)); + } +} + /** * mv_platform_probe - handle a positive probe of an soc Marvell * host @@ -2823,6 +2848,12 @@ static int mv_platform_probe(struct platform_device *pdev) res->end - res->start + 1); hpriv->base -= MV_SATAHC0_REG_BASE; + /* + * (Re-)program MBUS remapping windows if we are asked to. + */ + if (mv_platform_data->dram != NULL) + mv_conf_mbus_windows(hpriv, mv_platform_data->dram); + rc = mv_create_dma_pools(hpriv, &pdev->dev); if (rc) return rc; diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 5fa9c3c67e0..b04c99580d0 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -645,7 +645,7 @@ config I2C_PCA_ISA config I2C_MV64XXX tristate "Marvell mv64xxx I2C Controller" - depends on (MV64X60 || ARCH_ORION) && EXPERIMENTAL + depends on (MV64X60 || PLAT_ORION) && EXPERIMENTAL help If you say yes to this option, support will be included for the built-in I2C interface on the Marvell 64xxx line of host bridges. diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index 2d2087ad708..6fd2d6a84ef 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -39,6 +39,7 @@ #include <asm/io.h> #include <asm/arch/i2c.h> #include <asm/arch/pxa-regs.h> +#include <asm/arch/pxa2xx-gpio.h> struct pxa_i2c { spinlock_t lock; diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index b21593f9358..2da2edfa68e 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -93,6 +93,7 @@ config ISP1301_OMAP config TPS65010 tristate "TPS6501x Power Management chips" + depends on HAVE_GPIO_LIB default y if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_OSK help If you say yes here you get support for the TPS6501x series of diff --git a/drivers/i2c/chips/tps65010.c b/drivers/i2c/chips/tps65010.c index 4154a910885..b67f69c2e7f 100644 --- a/drivers/i2c/chips/tps65010.c +++ b/drivers/i2c/chips/tps65010.c @@ -30,9 +30,13 @@ #include <linux/debugfs.h> #include <linux/seq_file.h> #include <linux/mutex.h> +#include <linux/platform_device.h> #include <linux/i2c/tps65010.h> +#include <asm/gpio.h> + + /*-------------------------------------------------------------------------*/ #define DRIVER_VERSION "2 May 2005" @@ -84,7 +88,9 @@ struct tps65010 { u8 chgstatus, regstatus, chgconf; u8 nmask1, nmask2; - /* not currently tracking GPIO state */ + u8 outmask; + struct gpio_chip chip; + struct platform_device *leds; }; #define POWER_POLL_DELAY msecs_to_jiffies(5000) @@ -449,12 +455,72 @@ static irqreturn_t tps65010_irq(int irq, void *_tps) /*-------------------------------------------------------------------------*/ +/* offsets 0..3 == GPIO1..GPIO4 + * offsets 4..5 == LED1/nPG, LED2 (we set one of the non-BLINK modes) + */ +static void +tps65010_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + if (offset < 4) + tps65010_set_gpio_out_value(offset + 1, value); + else + tps65010_set_led(offset - 3, value ? ON : OFF); +} + +static int +tps65010_output(struct gpio_chip *chip, unsigned offset, int value) +{ + /* GPIOs may be input-only */ + if (offset < 4) { + struct tps65010 *tps; + + tps = container_of(chip, struct tps65010, chip); + if (!(tps->outmask & (1 << offset))) + return -EINVAL; + tps65010_set_gpio_out_value(offset + 1, value); + } else + tps65010_set_led(offset - 3, value ? ON : OFF); + + return 0; +} + +static int tps65010_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + int value; + struct tps65010 *tps; + + tps = container_of(chip, struct tps65010, chip); + + if (offset < 4) { + value = i2c_smbus_read_byte_data(tps->client, TPS_DEFGPIO); + if (value < 0) + return 0; + if (value & (1 << (offset + 4))) /* output */ + return !(value & (1 << offset)); + else /* input */ + return (value & (1 << offset)); + } + + /* REVISIT we *could* report LED1/nPG and LED2 state ... */ + return 0; +} + + +/*-------------------------------------------------------------------------*/ + static struct tps65010 *the_tps; static int __exit tps65010_remove(struct i2c_client *client) { struct tps65010 *tps = i2c_get_clientdata(client); + struct tps65010_board *board = client->dev.platform_data; + if (board && board->teardown) { + int status = board->teardown(client, board->context); + if (status < 0) + dev_dbg(&client->dev, "board %s %s err %d\n", + "teardown", client->name, status); + } if (client->irq > 0) free_irq(client->irq, tps); cancel_delayed_work(&tps->work); @@ -469,6 +535,7 @@ static int tps65010_probe(struct i2c_client *client) { struct tps65010 *tps; int status; + struct tps65010_board *board = client->dev.platform_data; if (the_tps) { dev_dbg(&client->dev, "only one tps6501x chip allowed\n"); @@ -577,6 +644,38 @@ static int tps65010_probe(struct i2c_client *client) tps->file = debugfs_create_file(DRIVER_NAME, S_IRUGO, NULL, tps, DEBUG_FOPS); + + /* optionally register GPIOs */ + if (board && board->base > 0) { + tps->outmask = board->outmask; + + tps->chip.label = client->name; + + tps->chip.set = tps65010_gpio_set; + tps->chip.direction_output = tps65010_output; + + /* NOTE: only partial support for inputs; nyet IRQs */ + tps->chip.get = tps65010_gpio_get; + + tps->chip.base = board->base; + tps->chip.ngpio = 6; + tps->chip.can_sleep = 1; + + status = gpiochip_add(&tps->chip); + if (status < 0) + dev_err(&client->dev, "can't add gpiochip, err %d\n", + status); + else if (board->setup) { + status = board->setup(client, board->context); + if (status < 0) { + dev_dbg(&client->dev, + "board %s %s err %d\n", + "setup", client->name, status); + status = 0; + } + } + } + return 0; fail1: kfree(tps); diff --git a/drivers/input/keyboard/corgikbd.c b/drivers/input/keyboard/corgikbd.c index 790fed368aa..5d6cc7f1dc9 100644 --- a/drivers/input/keyboard/corgikbd.c +++ b/drivers/input/keyboard/corgikbd.c @@ -23,6 +23,7 @@ #include <asm/arch/corgi.h> #include <asm/arch/hardware.h> #include <asm/arch/pxa-regs.h> +#include <asm/arch/pxa2xx-gpio.h> #include <asm/hardware/scoop.h> #define KB_ROWS 8 diff --git a/drivers/input/keyboard/spitzkbd.c b/drivers/input/keyboard/spitzkbd.c index 1d59a2dc3c1..0be74bfc58f 100644 --- a/drivers/input/keyboard/spitzkbd.c +++ b/drivers/input/keyboard/spitzkbd.c @@ -23,6 +23,7 @@ #include <asm/arch/spitz.h> #include <asm/arch/hardware.h> #include <asm/arch/pxa-regs.h> +#include <asm/arch/pxa2xx-gpio.h> #define KB_ROWS 7 #define KB_COLS 11 diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 57a1c28bf12..39573b91c8d 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -28,13 +28,6 @@ #include <linux/spi/ads7846.h> #include <asm/irq.h> -#ifdef CONFIG_ARM -#include <asm/mach-types.h> -#ifdef CONFIG_ARCH_OMAP -#include <asm/arch/gpio.h> -#endif -#endif - /* * This code has been heavily tested on a Nokia 770, and lightly @@ -1174,31 +1167,6 @@ static struct spi_driver ads7846_driver = { static int __init ads7846_init(void) { - /* grr, board-specific init should stay out of drivers!! */ - -#ifdef CONFIG_ARCH_OMAP - if (machine_is_omap_osk()) { - /* GPIO4 = PENIRQ; GPIO6 = BUSY */ - omap_request_gpio(4); - omap_set_gpio_direction(4, 1); - omap_request_gpio(6); - omap_set_gpio_direction(6, 1); - } - // also TI 1510 Innovator, bitbanging through FPGA - // also Nokia 770 - // also Palm Tungsten T2 -#endif - - // PXA: - // also Dell Axim X50 - // also HP iPaq H191x/H192x/H415x/H435x - // also Intel Lubbock (additional to UCB1400; as temperature sensor) - // also Sharp Zaurus C7xx, C8xx (corgi/sheperd/husky) - - // Atmel at91sam9261-EK uses ads7843 - - // also various AMD Au1x00 devel boards - return spi_register_driver(&ads7846_driver); } module_init(ads7846_init); @@ -1206,14 +1174,6 @@ module_init(ads7846_init); static void __exit ads7846_exit(void) { spi_unregister_driver(&ads7846_driver); - -#ifdef CONFIG_ARCH_OMAP - if (machine_is_omap_osk()) { - omap_free_gpio(4); - omap_free_gpio(6); - } -#endif - } module_exit(ads7846_exit); diff --git a/drivers/input/touchscreen/corgi_ts.c b/drivers/input/touchscreen/corgi_ts.c index 99d92f5c93d..a22576779ac 100644 --- a/drivers/input/touchscreen/corgi_ts.c +++ b/drivers/input/touchscreen/corgi_ts.c @@ -22,6 +22,7 @@ #include <asm/arch/sharpsl.h> #include <asm/arch/hardware.h> #include <asm/arch/pxa-regs.h> +#include <asm/arch/pxa2xx-gpio.h> #define PWR_MODE_ACTIVE 0 diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 859814f62cb..a3a6199639f 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -46,13 +46,6 @@ config LEDS_SPITZ This option enables support for the LEDs on Sharp Zaurus SL-Cxx00 series (C1000, C3000, C3100). -config LEDS_TOSA - tristate "LED Support for the Sharp SL-6000 series" - depends on LEDS_CLASS && PXA_SHARPSL - help - This option enables support for the LEDs on Sharp Zaurus - SL-6000 series. - config LEDS_S3C24XX tristate "LED Support for Samsung S3C24XX GPIO LEDs" depends on LEDS_CLASS && ARCH_S3C2410 diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 84ced3b1a13..e54f42da21a 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -9,7 +9,6 @@ obj-$(CONFIG_LEDS_ATMEL_PWM) += leds-atmel-pwm.o obj-$(CONFIG_LEDS_CORGI) += leds-corgi.o obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o obj-$(CONFIG_LEDS_SPITZ) += leds-spitz.o -obj-$(CONFIG_LEDS_TOSA) += leds-tosa.o obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o diff --git a/drivers/leds/leds-tosa.c b/drivers/leds/leds-tosa.c deleted file mode 100644 index 7ebecc41a9b..00000000000 --- a/drivers/leds/leds-tosa.c +++ /dev/null @@ -1,132 +0,0 @@ -/* - * LED Triggers Core - * - * Copyright 2005 Dirk Opfer - * - * Author: Dirk Opfer <Dirk@Opfer-Online.de> - * based on spitz.c - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/platform_device.h> -#include <linux/leds.h> -#include <asm/hardware/scoop.h> -#include <asm/mach-types.h> -#include <asm/arch/hardware.h> -#include <asm/arch/pxa-regs.h> -#include <asm/arch/tosa.h> - -static void tosaled_amber_set(struct led_classdev *led_cdev, - enum led_brightness value) -{ - if (value) - set_scoop_gpio(&tosascoop_jc_device.dev, - TOSA_SCOOP_JC_CHRG_ERR_LED); - else - reset_scoop_gpio(&tosascoop_jc_device.dev, - TOSA_SCOOP_JC_CHRG_ERR_LED); -} - -static void tosaled_green_set(struct led_classdev *led_cdev, - enum led_brightness value) -{ - if (value) - set_scoop_gpio(&tosascoop_jc_device.dev, - TOSA_SCOOP_JC_NOTE_LED); - else - reset_scoop_gpio(&tosascoop_jc_device.dev, - TOSA_SCOOP_JC_NOTE_LED); -} - -static struct led_classdev tosa_amber_led = { - .name = "tosa:amber:charge", - .default_trigger = "sharpsl-charge", - .brightness_set = tosaled_amber_set, -}; - -static struct led_classdev tosa_green_led = { - .name = "tosa:green:mail", - .default_trigger = "nand-disk", - .brightness_set = tosaled_green_set, -}; - -#ifdef CONFIG_PM -static int tosaled_suspend(struct platform_device *dev, pm_message_t state) -{ -#ifdef CONFIG_LEDS_TRIGGERS - if (tosa_amber_led.trigger && strcmp(tosa_amber_led.trigger->name, - "sharpsl-charge")) -#endif - led_classdev_suspend(&tosa_amber_led); - led_classdev_suspend(&tosa_green_led); - return 0; -} - -static int tosaled_resume(struct platform_device *dev) -{ - led_classdev_resume(&tosa_amber_led); - led_classdev_resume(&tosa_green_led); - return 0; -} -#else -#define tosaled_suspend NULL -#define tosaled_resume NULL -#endif - -static int tosaled_probe(struct platform_device *pdev) -{ - int ret; - - ret = led_classdev_register(&pdev->dev, &tosa_amber_led); - if (ret < 0) - return ret; - - ret = led_classdev_register(&pdev->dev, &tosa_green_led); - if (ret < 0) - led_classdev_unregister(&tosa_amber_led); - - return ret; -} - -static int tosaled_remove(struct platform_device *pdev) -{ - led_classdev_unregister(&tosa_amber_led); - led_classdev_unregister(&tosa_green_led); - - return 0; -} - -static struct platform_driver tosaled_driver = { - .probe = tosaled_probe, - .remove = tosaled_remove, - .suspend = tosaled_suspend, - .resume = tosaled_resume, - .driver = { - .name = "tosa-led", - .owner = THIS_MODULE, - }, -}; - -static int __init tosaled_init(void) -{ - return platform_driver_register(&tosaled_driver); -} - -static void __exit tosaled_exit(void) -{ - platform_driver_unregister(&tosaled_driver); -} - -module_init(tosaled_init); -module_exit(tosaled_exit); - -MODULE_AUTHOR("Dirk Opfer <Dirk@Opfer-Online.de>"); -MODULE_DESCRIPTION("Tosa LED driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:tosa-led"); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 0c886c88238..2566479937c 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -22,6 +22,22 @@ config MFD_ASIC3 This driver supports the ASIC3 multifunction chip found on many PDAs (mainly iPAQ and HTC based ones) +config HTC_EGPIO + bool "HTC EGPIO support" + depends on GENERIC_HARDIRQS && HAVE_GPIO_LIB + help + This driver supports the CPLD egpio chip present on + several HTC phones. It provides basic support for input + pins, output pins, and irqs. + +config HTC_PASIC3 + tristate "HTC PASIC3 LED/DS1WM chip support" + help + This core driver provides register access for the LED/DS1WM + chips labeled "AIC2" and "AIC3", found on HTC Blueangel and + HTC Magician devices, respectively. Actual functionality is + handled by the leds-pasic3 and ds1wm drivers. + endmenu menu "Multimedia Capabilities Port drivers" diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 521cd5cb68a..eef4e26807d 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -5,6 +5,9 @@ obj-$(CONFIG_MFD_SM501) += sm501.o obj-$(CONFIG_MFD_ASIC3) += asic3.o +obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o +obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o + obj-$(CONFIG_MCP) += mcp-core.o obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o diff --git a/drivers/mfd/htc-egpio.c b/drivers/mfd/htc-egpio.c new file mode 100644 index 00000000000..8872cc07751 --- /dev/null +++ b/drivers/mfd/htc-egpio.c @@ -0,0 +1,440 @@ +/* + * Support for the GPIO/IRQ expander chips present on several HTC phones. + * These are implemented in CPLD chips present on the board. + * + * Copyright (c) 2007 Kevin O'Connor <kevin@koconnor.net> + * Copyright (c) 2007 Philipp Zabel <philipp.zabel@gmail.com> + * + * This file may be distributed under the terms of the GNU GPL license. + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <linux/spinlock.h> +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/mfd/htc-egpio.h> + +struct egpio_chip { + int reg_start; + int cached_values; + unsigned long is_out; + struct device *dev; + struct gpio_chip chip; +}; + +struct egpio_info { + spinlock_t lock; + + /* iomem info */ + void __iomem *base_addr; + int bus_shift; /* byte shift */ + int reg_shift; /* bit shift */ + int reg_mask; + + /* irq info */ + int ack_register; + int ack_write; + u16 irqs_enabled; + uint irq_start; + int nirqs; + uint chained_irq; + + /* egpio info */ + struct egpio_chip *chip; + int nchips; +}; + +static inline void egpio_writew(u16 value, struct egpio_info *ei, int reg) +{ + writew(value, ei->base_addr + (reg << ei->bus_shift)); +} + +static inline u16 egpio_readw(struct egpio_info *ei, int reg) +{ + return readw(ei->base_addr + (reg << ei->bus_shift)); +} + +/* + * IRQs + */ + +static inline void ack_irqs(struct egpio_info *ei) +{ + egpio_writew(ei->ack_write, ei, ei->ack_register); + pr_debug("EGPIO ack - write %x to base+%x\n", + ei->ack_write, ei->ack_register << ei->bus_shift); +} + +static void egpio_ack(unsigned int irq) +{ +} + +/* There does not appear to be a way to proactively mask interrupts + * on the egpio chip itself. So, we simply ignore interrupts that + * aren't desired. */ +static void egpio_mask(unsigned int irq) +{ + struct egpio_info *ei = get_irq_chip_data(irq); + ei->irqs_enabled &= ~(1 << (irq - ei->irq_start)); + pr_debug("EGPIO mask %d %04x\n", irq, ei->irqs_enabled); +} +static void egpio_unmask(unsigned int irq) +{ + struct egpio_info *ei = get_irq_chip_data(irq); + ei->irqs_enabled |= 1 << (irq - ei->irq_start); + pr_debug("EGPIO unmask %d %04x\n", irq, ei->irqs_enabled); +} + +static struct irq_chip egpio_muxed_chip = { + .name = "htc-egpio", + .ack = egpio_ack, + .mask = egpio_mask, + .unmask = egpio_unmask, +}; + +static void egpio_handler(unsigned int irq, struct irq_desc *desc) +{ + struct egpio_info *ei = get_irq_data(irq); + int irqpin; + + /* Read current pins. */ + unsigned long readval = egpio_readw(ei, ei->ack_register); + pr_debug("IRQ reg: %x\n", (unsigned int)readval); + /* Ack/unmask interrupts. */ + ack_irqs(ei); + /* Process all set pins. */ + readval &= ei->irqs_enabled; + for_each_bit(irqpin, &readval, ei->nirqs) { + /* Run irq handler */ + pr_debug("got IRQ %d\n", irqpin); + irq = ei->irq_start + irqpin; + desc = &irq_desc[irq]; + desc->handle_irq(irq, desc); + } +} + +int htc_egpio_get_wakeup_irq(struct device *dev) +{ + struct egpio_info *ei = dev_get_drvdata(dev); + + /* Read current pins. */ + u16 readval = egpio_readw(ei, ei->ack_register); + /* Ack/unmask interrupts. */ + ack_irqs(ei); + /* Return first set pin. */ + readval &= ei->irqs_enabled; + return ei->irq_start + ffs(readval) - 1; +} +EXPORT_SYMBOL(htc_egpio_get_wakeup_irq); + +static inline int egpio_pos(struct egpio_info *ei, int bit) +{ + return bit >> ei->reg_shift; +} + +static inline int egpio_bit(struct egpio_info *ei, int bit) +{ + return 1 << (bit & ((1 << ei->reg_shift)-1)); +} + +/* + * Input pins + */ + +static int egpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct egpio_chip *egpio; + struct egpio_info *ei; + unsigned bit; + int reg; + int value; + + pr_debug("egpio_get_value(%d)\n", chip->base + offset); + + egpio = container_of(chip, struct egpio_chip, chip); + ei = dev_get_drvdata(egpio->dev); + bit = egpio_bit(ei, offset); + reg = egpio->reg_start + egpio_pos(ei, offset); + + value = egpio_readw(ei, reg); + pr_debug("readw(%p + %x) = %x\n", + ei->base_addr, reg << ei->bus_shift, value); + return value & bit; +} + +static int egpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct egpio_chip *egpio; + + egpio = container_of(chip, struct egpio_chip, chip); + return test_bit(offset, &egpio->is_out) ? -EINVAL : 0; +} + + +/* + * Output pins + */ + +static void egpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + unsigned long flag; + struct egpio_chip *egpio; + struct egpio_info *ei; + unsigned bit; + int pos; + int reg; + int shift; + + pr_debug("egpio_set(%s, %d(%d), %d)\n", + chip->label, offset, offset+chip->base, value); + + egpio = container_of(chip, struct egpio_chip, chip); + ei = dev_get_drvdata(egpio->dev); + bit = egpio_bit(ei, offset); + pos = egpio_pos(ei, offset); + reg = egpio->reg_start + pos; + shift = pos << ei->reg_shift; + + pr_debug("egpio %s: reg %d = 0x%04x\n", value ? "set" : "clear", + reg, (egpio->cached_values >> shift) & ei->reg_mask); + + spin_lock_irqsave(&ei->lock, flag); + if (value) + egpio->cached_values |= (1 << offset); + else + egpio->cached_values &= ~(1 << offset); + egpio_writew((egpio->cached_values >> shift) & ei->reg_mask, ei, reg); + spin_unlock_irqrestore(&ei->lock, flag); +} + +static int egpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct egpio_chip *egpio; + + egpio = container_of(chip, struct egpio_chip, chip); + if (test_bit(offset, &egpio->is_out)) { + egpio_set(chip, offset, value); + return 0; + } else { + return -EINVAL; + } +} + +static void egpio_write_cache(struct egpio_info *ei) +{ + int i; + struct egpio_chip *egpio; + int shift; + + for (i = 0; i < ei->nchips; i++) { + egpio = &(ei->chip[i]); + if (!egpio->is_out) + continue; + + for (shift = 0; shift < egpio->chip.ngpio; + shift += (1<<ei->reg_shift)) { + + int reg = egpio->reg_start + egpio_pos(ei, shift); + + if (!((egpio->is_out >> shift) & ei->reg_mask)) + continue; + + pr_debug("EGPIO: setting %x to %x, was %x\n", reg, + (egpio->cached_values >> shift) & ei->reg_mask, + egpio_readw(ei, reg)); + + egpio_writew((egpio->cached_values >> shift) + & ei->reg_mask, ei, reg); + } + } +} + + +/* + * Setup + */ + +static int __init egpio_probe(struct platform_device *pdev) +{ + struct htc_egpio_platform_data *pdata = pdev->dev.platform_data; + struct resource *res; + struct egpio_info *ei; + struct gpio_chip *chip; + unsigned int irq, irq_end; + int i; + int ret; + + /* Initialize ei data structure. */ + ei = kzalloc(sizeof(*ei), GFP_KERNEL); + if (!ei) + return -ENOMEM; + + spin_lock_init(&ei->lock); + + /* Find chained irq */ + ret = -EINVAL; + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (res) + ei->chained_irq = res->start; + + /* Map egpio chip into virtual address space. */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + goto fail; + ei->base_addr = ioremap_nocache(res->start, res->end - res->start); + if (!ei->base_addr) + goto fail; + pr_debug("EGPIO phys=%08x virt=%p\n", res->start, ei->base_addr); + + if ((pdata->bus_width != 16) && (pdata->bus_width != 32)) + goto fail; + ei->bus_shift = fls(pdata->bus_width - 1) - 3; + pr_debug("bus_shift = %d\n", ei->bus_shift); + + if ((pdata->reg_width != 8) && (pdata->reg_width != 16)) + goto fail; + ei->reg_shift = fls(pdata->reg_width - 1); + pr_debug("reg_shift = %d\n", ei->reg_shift); + + ei->reg_mask = (1 << pdata->reg_width) - 1; + + platform_set_drvdata(pdev, ei); + + ei->nchips = pdata->num_chips; + ei->chip = kzalloc(sizeof(struct egpio_chip) * ei->nchips, GFP_KERNEL); + if (!ei) { + ret = -ENOMEM; + goto fail; + } + for (i = 0; i < ei->nchips; i++) { + ei->chip[i].reg_start = pdata->chip[i].reg_start; + ei->chip[i].cached_values = pdata->chip[i].initial_values; + ei->chip[i].is_out = pdata->chip[i].direction; + ei->chip[i].dev = &(pdev->dev); + chip = &(ei->chip[i].chip); + chip->label = "htc-egpio"; + chip->get = egpio_get; + chip->set = egpio_set; + chip->direction_input = egpio_direction_input; + chip->direction_output = egpio_direction_output; + chip->base = pdata->chip[i].gpio_base; + chip->ngpio = pdata->chip[i].num_gpios; + + gpiochip_add(chip); + } + + /* Set initial pin values */ + egpio_write_cache(ei); + + ei->irq_start = pdata->irq_base; + ei->nirqs = pdata->num_irqs; + ei->ack_register = pdata->ack_register; + + if (ei->chained_irq) { + /* Setup irq handlers */ + ei->ack_write = 0xFFFF; + if (pdata->invert_acks) + ei->ack_write = 0; + irq_end = ei->irq_start + ei->nirqs; + for (irq = ei->irq_start; irq < irq_end; irq++) { + set_irq_chip(irq, &egpio_muxed_chip); + set_irq_chip_data(irq, ei); + set_irq_handler(irq, handle_simple_irq); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + } + set_irq_type(ei->chained_irq, IRQ_TYPE_EDGE_RISING); + set_irq_data(ei->chained_irq, ei); + set_irq_chained_handler(ei->chained_irq, egpio_handler); + ack_irqs(ei); + |