diff options
Diffstat (limited to 'drivers/gpio')
-rw-r--r-- | drivers/gpio/Kconfig | 258 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 24 | ||||
-rw-r--r-- | drivers/gpio/adp5520-gpio.c | 210 | ||||
-rw-r--r-- | drivers/gpio/adp5588-gpio.c | 266 | ||||
-rw-r--r-- | drivers/gpio/bt8xxgpio.c | 347 | ||||
-rw-r--r-- | drivers/gpio/cs5535-gpio.c | 357 | ||||
-rw-r--r-- | drivers/gpio/gpiolib.c | 1641 | ||||
-rw-r--r-- | drivers/gpio/langwell_gpio.c | 288 | ||||
-rw-r--r-- | drivers/gpio/max7301.c | 342 | ||||
-rw-r--r-- | drivers/gpio/max732x.c | 390 | ||||
-rw-r--r-- | drivers/gpio/mc33880.c | 196 | ||||
-rw-r--r-- | drivers/gpio/mcp23s08.c | 434 | ||||
-rw-r--r-- | drivers/gpio/pca953x.c | 379 | ||||
-rw-r--r-- | drivers/gpio/pcf857x.c | 370 | ||||
-rw-r--r-- | drivers/gpio/pl061.c | 353 | ||||
-rw-r--r-- | drivers/gpio/timbgpio.c | 342 | ||||
-rw-r--r-- | drivers/gpio/twl4030-gpio.c | 514 | ||||
-rw-r--r-- | drivers/gpio/ucb1400_gpio.c | 125 | ||||
-rw-r--r-- | drivers/gpio/vr41xx_giu.c | 585 | ||||
-rw-r--r-- | drivers/gpio/wm831x-gpio.c | 271 | ||||
-rw-r--r-- | drivers/gpio/xilinx_gpio.c | 235 |
21 files changed, 7927 insertions, 0 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig new file mode 100644 index 00000000000..1f1d88ae68d --- /dev/null +++ b/drivers/gpio/Kconfig @@ -0,0 +1,258 @@ +# +# GPIO infrastructure and expanders +# + +config ARCH_WANT_OPTIONAL_GPIOLIB + bool + help + Select this config option from the architecture Kconfig, if + it is possible to use gpiolib on the architecture, but let the + user decide whether to actually build it or not. + Select this instead of ARCH_REQUIRE_GPIOLIB, if your architecture does + not depend on GPIOs being available, but rather let the user + decide whether he needs it or not. + +config ARCH_REQUIRE_GPIOLIB + bool + select GPIOLIB + help + Platforms select gpiolib if they use this infrastructure + for all their GPIOs, usually starting with ones integrated + into SOC processors. + Selecting this from the architecture code will cause the gpiolib + code to always get built in. + + + +menuconfig GPIOLIB + bool "GPIO Support" + depends on ARCH_WANT_OPTIONAL_GPIOLIB || ARCH_REQUIRE_GPIOLIB + select GENERIC_GPIO + help + This enables GPIO support through the generic GPIO library. + You only need to enable this, if you also want to enable + one or more of the GPIO expansion card drivers below. + + If unsure, say N. + +if GPIOLIB + +config DEBUG_GPIO + bool "Debug GPIO calls" + depends on DEBUG_KERNEL + help + Say Y here to add some extra checks and diagnostics to GPIO calls. + These checks help ensure that GPIOs have been properly initialized + before they are used, and that sleeping calls are not made from + non-sleeping contexts. They can make bitbanged serial protocols + slower. The diagnostics help catch the type of setup errors + that are most common when setting up new platforms or boards. + +config GPIO_SYSFS + bool "/sys/class/gpio/... (sysfs interface)" + depends on SYSFS && EXPERIMENTAL + help + Say Y here to add a sysfs interface for GPIOs. + + This is mostly useful to work around omissions in a system's + kernel support. Those are common in custom and semicustom + hardware assembled using standard kernels with a minimum of + custom patches. In those cases, userspace code may import + a given GPIO from the kernel, if no kernel driver requested it. + + Kernel drivers may also request that a particular GPIO be + exported to userspace; this can be useful when debugging. + +# put expanders in the right section, in alphabetical order + +comment "Memory mapped GPIO expanders:" + +config GPIO_PL061 + bool "PrimeCell PL061 GPIO support" + depends on ARM_AMBA + help + Say yes here to support the PrimeCell PL061 GPIO device + +config GPIO_XILINX + bool "Xilinx GPIO support" + depends on PPC_OF || MICROBLAZE + help + Say yes here to support the Xilinx FPGA GPIO device + +config GPIO_VR41XX + tristate "NEC VR4100 series General-purpose I/O Uint support" + depends on CPU_VR41XX + help + Say yes here to support the NEC VR4100 series General-purpose I/O Uint + +comment "I2C GPIO expanders:" + +config GPIO_MAX732X + tristate "MAX7319, MAX7320-7327 I2C Port Expanders" + depends on I2C + help + Say yes here to support the MAX7319, MAX7320-7327 series of I2C + Port Expanders. Each IO port on these chips has a fixed role of + Input (designated by 'I'), Push-Pull Output ('O'), or Open-Drain + Input and Output (designed by 'P'). The combinations are listed + below: + + 8 bits: max7319 (8I), max7320 (8O), max7321 (8P), + max7322 (4I4O), max7323 (4P4O) + + 16 bits: max7324 (8I8O), max7325 (8P8O), + max7326 (4I12O), max7327 (4P12O) + + Board setup code must specify the model to use, and the start + number for these GPIOs. + +config GPIO_PCA953X + tristate "PCA953x, PCA955x, TCA64xx, and MAX7310 I/O ports" + depends on I2C + help + Say yes here to provide access to several register-oriented + SMBus I/O expanders, made mostly by NXP or TI. Compatible + models include: + + 4 bits: pca9536, pca9537 + + 8 bits: max7310, pca9534, pca9538, pca9554, pca9557, + tca6408 + + 16 bits: pca9535, pca9539, pca9555, tca6416 + + This driver can also be built as a module. If so, the module + will be called pca953x. + +config GPIO_PCF857X + tristate "PCF857x, PCA{85,96}7x, and MAX732[89] I2C GPIO expanders" + depends on I2C + help + Say yes here to provide access to most "quasi-bidirectional" I2C + GPIO expanders used for additional digital outputs or inputs. + Most of these parts are from NXP, though TI is a second source for + some of them. Compatible models include: + + 8 bits: pcf8574, pcf8574a, pca8574, pca8574a, + pca9670, pca9672, pca9674, pca9674a, + max7328, max7329 + + 16 bits: pcf8575, pcf8575c, pca8575, + pca9671, pca9673, pca9675 + + Your board setup code will need to declare the expanders in + use, and assign numbers to the GPIOs they expose. Those GPIOs + can then be used from drivers and other kernel code, just like + other GPIOs, but only accessible from task contexts. + + This driver provides an in-kernel interface to those GPIOs using + platform-neutral GPIO calls. + +config GPIO_TWL4030 + tristate "TWL4030, TWL5030, and TPS659x0 GPIOs" + depends on TWL4030_CORE + help + Say yes here to access the GPIO signals of various multi-function + power management chips from Texas Instruments. + +config GPIO_WM831X + tristate "WM831x GPIOs" + depends on MFD_WM831X + help + Say yes here to access the GPIO signals of WM831x power management + chips from Wolfson Microelectronics. + +config GPIO_ADP5520 + tristate "GPIO Support for ADP5520 PMIC" + depends on PMIC_ADP5520 + help + This option enables support for on-chip GPIO found + on Analog Devices ADP5520 PMICs. + + To compile this driver as a module, choose M here: the module will + be called adp5520-gpio. + +config GPIO_ADP5588 + tristate "ADP5588 I2C GPIO expander" + depends on I2C + help + This option enables support for 18 GPIOs found + on Analog Devices ADP5588 GPIO Expanders. + To compile this driver as a module, choose M here: the module will be + called adp5588-gpio. + +comment "PCI GPIO expanders:" + +config GPIO_CS5535 + tristate "AMD CS5535/CS5536 GPIO support" + depends on PCI && !CS5535_GPIO + help + The AMD CS5535 and CS5536 southbridges support 28 GPIO pins that + can be used for quite a number of things. The CS5535/6 is found on + AMD Geode and Lemote Yeeloong devices. + + If unsure, say N. + +config GPIO_BT8XX + tristate "BT8XX GPIO abuser" + depends on PCI && VIDEO_BT848=n + help + The BT8xx frame grabber chip has 24 GPIO pins than can be abused + as a cheap PCI GPIO card. + + This chip can be found on Miro, Hauppauge and STB TV-cards. + + The card needs to be physically altered for using it as a + GPIO card. For more information on how to build a GPIO card + from a BT8xx TV card, see the documentation file at + Documentation/bt8xxgpio.txt + + If unsure, say N. + +config GPIO_LANGWELL + bool "Intel Moorestown Platform Langwell GPIO support" + depends on PCI + help + Say Y here to support Intel Moorestown platform GPIO. + +config GPIO_TIMBERDALE + bool "Support for timberdale GPIO IP" + depends on MFD_TIMBERDALE && GPIOLIB && HAS_IOMEM + ---help--- + Add support for the GPIO IP in the timberdale FPGA. + +comment "SPI GPIO expanders:" + +config GPIO_MAX7301 + tristate "Maxim MAX7301 GPIO expander" + depends on SPI_MASTER + help + gpio driver for Maxim MAX7301 SPI GPIO expander. + +config GPIO_MCP23S08 + tristate "Microchip MCP23S08 I/O expander" + depends on SPI_MASTER + help + SPI driver for Microchip MCP23S08 I/O expander. This provides + a GPIO interface supporting inputs and outputs. + +config GPIO_MC33880 + tristate "Freescale MC33880 high-side/low-side switch" + depends on SPI_MASTER + help + SPI driver for Freescale MC33880 high-side/low-side switch. + This provides GPIO interface supporting inputs and outputs. + +comment "AC97 GPIO expanders:" + +config GPIO_UCB1400 + bool "Philips UCB1400 GPIO" + depends on UCB1400_CORE + help + This enables support for the Philips UCB1400 GPIO pins. + The UCB1400 is an AC97 audio codec. + + To compile this driver as a module, choose M here: the + module will be called ucb1400_gpio. + +endif diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile new file mode 100644 index 00000000000..48687238edb --- /dev/null +++ b/drivers/gpio/Makefile @@ -0,0 +1,24 @@ +# gpio support: dedicated expander chips, etc + +ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG + +obj-$(CONFIG_GPIOLIB) += gpiolib.o + +obj-$(CONFIG_GPIO_ADP5520) += adp5520-gpio.o +obj-$(CONFIG_GPIO_ADP5588) += adp5588-gpio.o +obj-$(CONFIG_GPIO_LANGWELL) += langwell_gpio.o +obj-$(CONFIG_GPIO_MAX7301) += max7301.o +obj-$(CONFIG_GPIO_MAX732X) += max732x.o +obj-$(CONFIG_GPIO_MC33880) += mc33880.o +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_TIMBERDALE) += timbgpio.o +obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o +obj-$(CONFIG_GPIO_UCB1400) += ucb1400_gpio.o +obj-$(CONFIG_GPIO_XILINX) += xilinx_gpio.o +obj-$(CONFIG_GPIO_CS5535) += cs5535-gpio.o +obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o +obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o +obj-$(CONFIG_GPIO_WM831X) += wm831x-gpio.o diff --git a/drivers/gpio/adp5520-gpio.c b/drivers/gpio/adp5520-gpio.c new file mode 100644 index 00000000000..0f93105873c --- /dev/null +++ b/drivers/gpio/adp5520-gpio.c @@ -0,0 +1,210 @@ +/* + * GPIO driver for Analog Devices ADP5520 MFD PMICs + * + * Copyright 2009 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/mfd/adp5520.h> + +#include <linux/gpio.h> + +struct adp5520_gpio { + struct device *master; + struct gpio_chip gpio_chip; + unsigned char lut[ADP5520_MAXGPIOS]; + unsigned long output; +}; + +static int adp5520_gpio_get_value(struct gpio_chip *chip, unsigned off) +{ + struct adp5520_gpio *dev; + uint8_t reg_val; + + dev = container_of(chip, struct adp5520_gpio, gpio_chip); + + /* + * There are dedicated registers for GPIO IN/OUT. + * Make sure we return the right value, even when configured as output + */ + + if (test_bit(off, &dev->output)) + adp5520_read(dev->master, ADP5520_GPIO_OUT, ®_val); + else + adp5520_read(dev->master, ADP5520_GPIO_IN, ®_val); + + return !!(reg_val & dev->lut[off]); +} + +static void adp5520_gpio_set_value(struct gpio_chip *chip, + unsigned off, int val) +{ + struct adp5520_gpio *dev; + dev = container_of(chip, struct adp5520_gpio, gpio_chip); + + if (val) + adp5520_set_bits(dev->master, ADP5520_GPIO_OUT, dev->lut[off]); + else + adp5520_clr_bits(dev->master, ADP5520_GPIO_OUT, dev->lut[off]); +} + +static int adp5520_gpio_direction_input(struct gpio_chip *chip, unsigned off) +{ + struct adp5520_gpio *dev; + dev = container_of(chip, struct adp5520_gpio, gpio_chip); + + clear_bit(off, &dev->output); + + return adp5520_clr_bits(dev->master, ADP5520_GPIO_CFG_2, + dev->lut[off]); +} + +static int adp5520_gpio_direction_output(struct gpio_chip *chip, + unsigned off, int val) +{ + struct adp5520_gpio *dev; + int ret = 0; + dev = container_of(chip, struct adp5520_gpio, gpio_chip); + + set_bit(off, &dev->output); + + if (val) + ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_OUT, + dev->lut[off]); + else + ret |= adp5520_clr_bits(dev->master, ADP5520_GPIO_OUT, + dev->lut[off]); + + ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_CFG_2, + dev->lut[off]); + + return ret; +} + +static int __devinit adp5520_gpio_probe(struct platform_device *pdev) +{ + struct adp5520_gpio_platform_data *pdata = pdev->dev.platform_data; + struct adp5520_gpio *dev; + struct gpio_chip *gc; + int ret, i, gpios; + unsigned char ctl_mask = 0; + + if (pdata == NULL) { + dev_err(&pdev->dev, "missing platform data\n"); + return -ENODEV; + } + + if (pdev->id != ID_ADP5520) { + dev_err(&pdev->dev, "only ADP5520 supports GPIO\n"); + return -ENODEV; + } + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + dev_err(&pdev->dev, "failed to alloc memory\n"); + return -ENOMEM; + } + + dev->master = pdev->dev.parent; + + for (gpios = 0, i = 0; i < ADP5520_MAXGPIOS; i++) + if (pdata->gpio_en_mask & (1 << i)) + dev->lut[gpios++] = 1 << i; + + if (gpios < 1) { + ret = -EINVAL; + goto err; + } + + gc = &dev->gpio_chip; + gc->direction_input = adp5520_gpio_direction_input; + gc->direction_output = adp5520_gpio_direction_output; + gc->get = adp5520_gpio_get_value; + gc->set = adp5520_gpio_set_value; + gc->can_sleep = 1; + + gc->base = pdata->gpio_start; + gc->ngpio = gpios; + gc->label = pdev->name; + gc->owner = THIS_MODULE; + + ret = adp5520_clr_bits(dev->master, ADP5520_GPIO_CFG_1, + pdata->gpio_en_mask); + + if (pdata->gpio_en_mask & ADP5520_GPIO_C3) + ctl_mask |= ADP5520_C3_MODE; + + if (pdata->gpio_en_mask & ADP5520_GPIO_R3) + ctl_mask |= ADP5520_R3_MODE; + + if (ctl_mask) + ret = adp5520_set_bits(dev->master, ADP5520_LED_CONTROL, + ctl_mask); + + ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_PULLUP, + pdata->gpio_pullup_mask); + + if (ret) { + dev_err(&pdev->dev, "failed to write\n"); + goto err; + } + + ret = gpiochip_add(&dev->gpio_chip); + if (ret) + goto err; + + platform_set_drvdata(pdev, dev); + return 0; + +err: + kfree(dev); + return ret; +} + +static int __devexit adp5520_gpio_remove(struct platform_device *pdev) +{ + struct adp5520_gpio *dev; + int ret; + + dev = platform_get_drvdata(pdev); + ret = gpiochip_remove(&dev->gpio_chip); + if (ret) { + dev_err(&pdev->dev, "%s failed, %d\n", + "gpiochip_remove()", ret); + return ret; + } + + kfree(dev); + return 0; +} + +static struct platform_driver adp5520_gpio_driver = { + .driver = { + .name = "adp5520-gpio", + .owner = THIS_MODULE, + }, + .probe = adp5520_gpio_probe, + .remove = __devexit_p(adp5520_gpio_remove), +}; + +static int __init adp5520_gpio_init(void) +{ + return platform_driver_register(&adp5520_gpio_driver); +} +module_init(adp5520_gpio_init); + +static void __exit adp5520_gpio_exit(void) +{ + platform_driver_unregister(&adp5520_gpio_driver); +} +module_exit(adp5520_gpio_exit); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("GPIO ADP5520 Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:adp5520-gpio"); diff --git a/drivers/gpio/adp5588-gpio.c b/drivers/gpio/adp5588-gpio.c new file mode 100644 index 00000000000..afc097a16b3 --- /dev/null +++ b/drivers/gpio/adp5588-gpio.c @@ -0,0 +1,266 @@ +/* + * GPIO Chip driver for Analog Devices + * ADP5588 I/O Expander and QWERTY Keypad Controller + * + * Copyright 2009 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/gpio.h> + +#include <linux/i2c/adp5588.h> + +#define DRV_NAME "adp5588-gpio" +#define MAXGPIO 18 +#define ADP_BANK(offs) ((offs) >> 3) +#define ADP_BIT(offs) (1u << ((offs) & 0x7)) + +struct adp5588_gpio { + struct i2c_client *client; + struct gpio_chip gpio_chip; + struct mutex lock; /* protect cached dir, dat_out */ + unsigned gpio_start; + uint8_t dat_out[3]; + uint8_t dir[3]; +}; + +static int adp5588_gpio_read(struct i2c_client *client, u8 reg) +{ + int ret = i2c_smbus_read_byte_data(client, reg); + + if (ret < 0) + dev_err(&client->dev, "Read Error\n"); + + return ret; +} + +static int adp5588_gpio_write(struct i2c_client *client, u8 reg, u8 val) +{ + int ret = i2c_smbus_write_byte_data(client, reg, val); + + if (ret < 0) + dev_err(&client->dev, "Write Error\n"); + + return ret; +} + +static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned off) +{ + struct adp5588_gpio *dev = + container_of(chip, struct adp5588_gpio, gpio_chip); + + return !!(adp5588_gpio_read(dev->client, GPIO_DAT_STAT1 + ADP_BANK(off)) + & ADP_BIT(off)); +} + +static void adp5588_gpio_set_value(struct gpio_chip *chip, + unsigned off, int val) +{ + unsigned bank, bit; + struct adp5588_gpio *dev = + container_of(chip, struct adp5588_gpio, gpio_chip); + + bank = ADP_BANK(off); + bit = ADP_BIT(off); + + mutex_lock(&dev->lock); + if (val) + dev->dat_out[bank] |= bit; + else + dev->dat_out[bank] &= ~bit; + + adp5588_gpio_write(dev->client, GPIO_DAT_OUT1 + bank, + dev->dat_out[bank]); + mutex_unlock(&dev->lock); +} + +static int adp5588_gpio_direction_input(struct gpio_chip *chip, unsigned off) +{ + int ret; + unsigned bank; + struct adp5588_gpio *dev = + container_of(chip, struct adp5588_gpio, gpio_chip); + + bank = ADP_BANK(off); + + mutex_lock(&dev->lock); + dev->dir[bank] &= ~ADP_BIT(off); + ret = adp5588_gpio_write(dev->client, GPIO_DIR1 + bank, dev->dir[bank]); + mutex_unlock(&dev->lock); + + return ret; +} + +static int adp5588_gpio_direction_output(struct gpio_chip *chip, + unsigned off, int val) +{ + int ret; + unsigned bank, bit; + struct adp5588_gpio *dev = + container_of(chip, struct adp5588_gpio, gpio_chip); + + bank = ADP_BANK(off); + bit = ADP_BIT(off); + + mutex_lock(&dev->lock); + dev->dir[bank] |= bit; + + if (val) + dev->dat_out[bank] |= bit; + else + dev->dat_out[bank] &= ~bit; + + ret = adp5588_gpio_write(dev->client, GPIO_DAT_OUT1 + bank, + dev->dat_out[bank]); + ret |= adp5588_gpio_write(dev->client, GPIO_DIR1 + bank, + dev->dir[bank]); + mutex_unlock(&dev->lock); + + return ret; +} + +static int __devinit adp5588_gpio_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adp5588_gpio_platform_data *pdata = client->dev.platform_data; + struct adp5588_gpio *dev; + struct gpio_chip *gc; + int ret, i, revid; + + if (pdata == NULL) { + dev_err(&client->dev, "missing platform data\n"); + return -ENODEV; + } + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); + return -EIO; + } + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + dev_err(&client->dev, "failed to alloc memory\n"); + return -ENOMEM; + } + + dev->client = client; + + gc = &dev->gpio_chip; + gc->direction_input = adp5588_gpio_direction_input; + gc->direction_output = adp5588_gpio_direction_output; + gc->get = adp5588_gpio_get_value; + gc->set = adp5588_gpio_set_value; + gc->can_sleep = 1; + + gc->base = pdata->gpio_start; + gc->ngpio = MAXGPIO; + gc->label = client->name; + gc->owner = THIS_MODULE; + + mutex_init(&dev->lock); + + + ret = adp5588_gpio_read(dev->client, DEV_ID); + if (ret < 0) + goto err; + + revid = ret & ADP5588_DEVICE_ID_MASK; + + for (i = 0, ret = 0; i <= ADP_BANK(MAXGPIO); i++) { + dev->dat_out[i] = adp5588_gpio_read(client, GPIO_DAT_OUT1 + i); + dev->dir[i] = adp5588_gpio_read(client, GPIO_DIR1 + i); + ret |= adp5588_gpio_write(client, KP_GPIO1 + i, 0); + ret |= adp5588_gpio_write(client, GPIO_PULL1 + i, + (pdata->pullup_dis_mask >> (8 * i)) & 0xFF); + + if (ret) + goto err; + } + + ret = gpiochip_add(&dev->gpio_chip); + if (ret) + goto err; + + dev_info(&client->dev, "gpios %d..%d on a %s Rev. %d\n", + gc->base, gc->base + gc->ngpio - 1, + client->name, revid); + + if (pdata->setup) { + ret = pdata->setup(client, gc->base, gc->ngpio, pdata->context); + if (ret < 0) + dev_warn(&client->dev, "setup failed, %d\n", ret); + } + + i2c_set_clientdata(client, dev); + return 0; + +err: + kfree(dev); + return ret; +} + +static int __devexit adp5588_gpio_remove(struct i2c_client *client) +{ + struct adp5588_gpio_platform_data *pdata = client->dev.platform_data; + struct adp5588_gpio *dev = i2c_get_clientdata(client); + int ret; + + if (pdata->teardown) { + ret = pdata->teardown(client, + dev->gpio_chip.base, dev->gpio_chip.ngpio, + pdata->context); + if (ret < 0) { + dev_err(&client->dev, "teardown failed %d\n", ret); + return ret; + } + } + + ret = gpiochip_remove(&dev->gpio_chip); + if (ret) { + dev_err(&client->dev, "gpiochip_remove failed %d\n", ret); + return ret; + } + + kfree(dev); + return 0; +} + +static const struct i2c_device_id adp5588_gpio_id[] = { + {DRV_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, adp5588_gpio_id); + +static struct i2c_driver adp5588_gpio_driver = { + .driver = { + .name = DRV_NAME, + }, + .probe = adp5588_gpio_probe, + .remove = __devexit_p(adp5588_gpio_remove), + .id_table = adp5588_gpio_id, +}; + +static int __init adp5588_gpio_init(void) +{ + return i2c_add_driver(&adp5588_gpio_driver); +} + +module_init(adp5588_gpio_init); + +static void __exit adp5588_gpio_exit(void) +{ + i2c_del_driver(&adp5588_gpio_driver); +} + +module_exit(adp5588_gpio_exit); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("GPIO ADP5588 Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/bt8xxgpio.c b/drivers/gpio/bt8xxgpio.c new file mode 100644 index 00000000000..2559f228940 --- /dev/null +++ b/drivers/gpio/bt8xxgpio.c @@ -0,0 +1,347 @@ +/* + + bt8xx GPIO abuser + + Copyright (C) 2008 Michael Buesch <mb@bu3sch.de> + + Please do _only_ contact the people listed _above_ with issues related to this driver. + All the other people listed below are not related to this driver. Their names + are only here, because this driver is derived from the bt848 driver. + + + Derived from the bt848 driver: + + Copyright (C) 1996,97,98 Ralph Metzler + & Marcus Metzler + (c) 1999-2002 Gerd Knorr + + some v4l2 code lines are taken from Justin's bttv2 driver which is + (c) 2000 Justin Schoeman + + V4L1 removal from: + (c) 2005-2006 Nickolay V. Shmyrev + + Fixes to be fully V4L2 compliant by + (c) 2006 Mauro Carvalho Chehab + + Cropping and overscan support + Copyright (C) 2005, 2006 Michael H. Schimek + Sponsored by OPQ Systems AB + + 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/pci.h> +#include <linux/spinlock.h> +#include <linux/gpio.h> + +/* Steal the hardware definitions from the bttv driver. */ +#include "../media/video/bt8xx/bt848.h" + + +#define BT8XXGPIO_NR_GPIOS 24 /* We have 24 GPIO pins */ + + +struct bt8xxgpio { + spinlock_t lock; + + void __iomem *mmio; + struct pci_dev *pdev; + struct gpio_chip gpio; + +#ifdef CONFIG_PM + u32 saved_outen; + u32 saved_data; +#endif +}; + +#define bgwrite(dat, adr) writel((dat), bg->mmio+(adr)) +#define bgread(adr) readl(bg->mmio+(adr)) + + +static int modparam_gpiobase = -1/* dynamic */; +module_param_named(gpiobase, modparam_gpiobase, int, 0444); +MODULE_PARM_DESC(gpiobase, "The GPIO number base. -1 means dynamic, which is the default."); + + +static int bt8xxgpio_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) +{ + struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio); + unsigned long flags; + u32 outen, data; + + spin_lock_irqsave(&bg->lock, flags); + + data = bgread(BT848_GPIO_DATA); + data &= ~(1 << nr); + bgwrite(data, BT848_GPIO_DATA); + + outen = bgread(BT848_GPIO_OUT_EN); + outen &= ~(1 << nr); + bgwrite(outen, BT848_GPIO_OUT_EN); + + spin_unlock_irqrestore(&bg->lock, flags); + + return 0; +} + +static int bt8xxgpio_gpio_get(struct gpio_chip *gpio, unsigned nr) +{ + struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&bg->lock, flags); + val = bgread(BT848_GPIO_DATA); + spin_unlock_irqrestore(&bg->lock, flags); + + return !!(val & (1 << nr)); +} + +static int bt8xxgpio_gpio_direction_output(struct gpio_chip *gpio, + unsigned nr, int val) +{ + struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio); + unsigned long flags; + u32 outen, data; + + spin_lock_irqsave(&bg->lock, flags); + + outen = bgread(BT848_GPIO_OUT_EN); + outen |= (1 << nr); + bgwrite(outen, BT848_GPIO_OUT_EN); + + data = bgread(BT848_GPIO_DATA); + if (val) + data |= (1 << nr); + else + data &= ~(1 << nr); + bgwrite(data, BT848_GPIO_DATA); + + spin_unlock_irqrestore(&bg->lock, flags); + + return 0; +} + +static void bt8xxgpio_gpio_set(struct gpio_chip *gpio, + unsigned nr, int val) +{ + struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio); + unsigned long flags; + u32 data; + + spin_lock_irqsave(&bg->lock, flags); + + data = bgread(BT848_GPIO_DATA); + if (val) + data |= (1 << nr); + else + data &= ~(1 << nr); + bgwrite(data, BT848_GPIO_DATA); + + spin_unlock_irqrestore(&bg->lock, flags); +} + +static void bt8xxgpio_gpio_setup(struct bt8xxgpio *bg) +{ + struct gpio_chip *c = &bg->gpio; + + c->label = dev_name(&bg->pdev->dev); + c->owner = THIS_MODULE; + c->direction_input = bt8xxgpio_gpio_direction_input; + c->get = bt8xxgpio_gpio_get; + c->direction_output = bt8xxgpio_gpio_direction_output; + c->set = bt8xxgpio_gpio_set; + c->dbg_show = NULL; + c->base = modparam_gpiobase; + c->ngpio = BT8XXGPIO_NR_GPIOS; + c->can_sleep = 0; +} + +static int bt8xxgpio_probe(struct pci_dev *dev, + const struct pci_device_id *pci_id) +{ + struct bt8xxgpio *bg; + int err; + + bg = kzalloc(sizeof(*bg), GFP_KERNEL); + if (!bg) + return -ENOMEM; + + bg->pdev = dev; + spin_lock_init(&bg->lock); + + err = pci_enable_device(dev); + if (err) { + printk(KERN_ERR "bt8xxgpio: Can't enable device.\n"); + goto err_freebg; + } + if (!request_mem_region(pci_resource_start(dev, 0), + pci_resource_len(dev, 0), + "bt8xxgpio")) { + printk(KERN_WARNING "bt8xxgpio: Can't request iomem (0x%llx).\n", + (unsigned long long)pci_resource_start(dev, 0)); + err = -EBUSY; + goto err_disable; + } + pci_set_master(dev); + pci_set_drvdata(dev, bg); + + bg->mmio = ioremap(pci_resource_start(dev, 0), 0x1000); + if (!bg->mmio) { + printk(KERN_ERR "bt8xxgpio: ioremap() failed\n"); + err = -EIO; + goto err_release_mem; + } + + /* Disable interrupts */ + bgwrite(0, BT848_INT_MASK); + + /* gpio init */ + bgwrite(0, BT848_GPIO_DMA_CTL); + bgwrite(0, BT848_GPIO_REG_INP); + bgwrite(0, BT848_GPIO_OUT_EN); + + bt8xxgpio_gpio_setup(bg); + err = gpiochip_add(&bg->gpio); + if (err) { + printk(KERN_ERR "bt8xxgpio: Failed to register GPIOs\n"); + goto err_release_mem; + } + + printk(KERN_INFO "bt8xxgpio: Abusing BT8xx card for GPIOs %d to %d\n", + bg->gpio.base, bg->gpio.base + BT8XXGPIO_NR_GPIOS - 1); + + return 0; + +err_release_mem: + release_mem_region(pci_resource_start(dev, 0), + pci_resource_len(dev, 0)); + pci_set_drvdata(dev, NULL); +err_disable: + pci_disable_device(dev); +err_freebg: + kfree(bg); + + return err; +} + +static void bt8xxgpio_remove(struct pci_dev *pdev) +{ + struct bt8xxgpio *bg = pci_get_drvdata(pdev); + + gpiochip_remove(&bg->gpio); + + bgwrite(0, BT848_INT_MASK); + bgwrite(~0x0, BT848_INT_STAT); + bgwrite(0x0, BT848_GPIO_OUT_EN); + + iounmap(bg->mmio); + release_mem_region(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + pci_disable_device(pdev); + + pci_set_drvdata(pdev, NULL); + kfree(bg); +} + +#ifdef CONFIG_PM +static int bt8xxgpio_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct bt8xxgpio *bg = pci_get_drvdata(pdev); + unsigned long flags; + + spin_lock_irqsave(&bg->lock, flags); + + bg->saved_outen = bgread(BT848_GPIO_OUT_EN); + bg->saved_data = bgread(BT848_GPIO_DATA); + + bgwrite(0, BT848_INT_MASK); + bgwrite(~0x0, BT848_INT_STAT); + bgwrite(0x0, BT848_GPIO_OUT_EN); + + spin_unlock_irqrestore(&bg->lock, flags); + + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + return 0; +} + +static int bt8xxgpio_resume(struct pci_dev *pdev) +{ + struct bt8xxgpio *bg = pci_get_drvdata(pdev); + unsigned long flags; + int err; + + pci_set_power_state(pdev, 0); + err = pci_enable_device(pdev); + if (err) + return err; + pci_restore_state(pdev); + + spin_lock_irqsave(&bg->lock, flags); + + bgwrite(0, BT848_INT_MASK); + bgwrite(0, BT848_GPIO_DMA_CTL); + bgwrite(0, BT848_GPIO_REG_INP); + bgwrite(bg->saved_outen, BT848_GPIO_OUT_EN); + bgwrite(bg->saved_data & bg->saved_outen, + BT848_GPIO_DATA); + + spin_unlock_irqrestore(&bg->lock, flags); + + return 0; +} +#else +#define bt8xxgpio_suspend NULL +#define bt8xxgpio_resume NULL +#endif /* CONFIG_PM */ + +static struct pci_device_id bt8xxgpio_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT849) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT878) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT879) }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, bt8xxgpio_pci_tbl); + +static struct pci_driver bt8xxgpio_pci_driver = { + .name = "bt8xxgpio", + .id_table = bt8xxgpio_pci_tbl, + .probe = bt8xxgpio_probe, + .remove = bt8xxgpio_remove, + .suspend = bt8xxgpio_suspend, + .resume = bt8xxgpio_resume, +}; + +static int __init bt8xxgpio_init(void) +{ + return pci_register_driver(&bt8xxgpio_pci_driver); +} +module_init(bt8xxgpio_init) + +static void __exit bt8xxgpio_exit(void) +{ + pci_unregister_driver(&bt8xxgpio_pci_driver); +} +module_exit(bt8xxgpio_exit) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michael Buesch"); +MODULE_DESCRIPTION("Abuse a BT8xx framegrabber card as generic GPIO card"); diff --git a/drivers/gpio/cs5535-gpio.c b/drivers/gpio/cs5535-gpio.c new file mode 100644 index 00000000000..0c3c498f226 --- /dev/null +++ b/drivers/gpio/cs5535-gpio.c @@ -0,0 +1,357 @@ +/* + * AMD CS5535/CS5536 GPIO driver + * Copyright (C) 2006 Advanced Micro Devices, Inc. + * Copyright (C) 2007-2009 Andres Salomon <dilinger@collabora.co.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/gpio.h> +#include <linux/io.h> +#include <linux/cs5535.h> + +#define DRV_NAME "cs5535-gpio" +#define GPIO_BAR 1 + +/* + * Some GPIO pins + * 31-29,23 : reserved (always mask out) + * 28 : Power Button + * 26 : PME# + * 22-16 : LPC + * 14,15 : SMBus + * 9,8 : UART1 + * 7 : PCI INTB + * 3,4 : UART2/DDC + * 2 : IDE_IRQ0 + * 1 : AC_BEEP + * 0 : PCI INTA + * + * If a mask was not specified, allow all except + * reserved and Power Button + */ +#define GPIO_DEFAULT_MASK 0x0F7FFFFF + +static ulong mask = GPIO_DEFAULT_MASK; +module_param_named(mask, mask, ulong, 0444); +MODULE_PARM_DESC(mask, "GPIO channel mask."); + +static struct cs5535_gpio_chip { + struct gpio_chip chip; + resource_size_t base; + + struct pci_dev *pdev; + spinlock_t lock; +} cs5535_gpio_chip; + +/* + * The CS5535/CS5536 GPIOs support a number of extra features not defined + * by the gpio_chip API, so these are exported. For a full list of the + * registers, see include/linux/cs5535.h. + */ + +static void __cs5535_gpio_set(struct cs5535_gpio_chip *chip, unsigned offset, + unsigned int reg) +{ + if (offset < 16) + /* low bank register */ + outl(1 << offset, chip->base + reg); + else + /* high bank register */ + outl(1 << (offset - 16), chip->base + 0x80 + reg); +} + +void cs5535_gpio_set(unsigned offset, unsigned int reg) +{ + struct cs5535_gpio_chip *chip = &cs5535_gpio_chip; + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + __cs5535_gpio_set(chip, offset, reg); + spin_unlock_irqrestore(&chip->lock, flags); +} +EXPORT_SYMBOL_GPL(cs5535_gpio_set); + +static void __cs5535_gpio_clear(struct cs5535_gpio_chip *chip, unsigned offset, + unsigned int reg) +{ + if (offset < 16) + /* low bank register */ + outl(1 << (offset + 16), chip->base + reg); + else + /* high bank register */ + outl(1 << offset, chip->base + 0x80 + reg); +} + +void cs5535_gpio_clear(unsigned offset, unsigned int reg) +{ + struct cs5535_gpio_chip *chip = &cs5535_gpio_chip; + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + __cs5535_gpio_clear(chip, offset, reg); + spin_unlock_irqrestore(&chip->lock, flags); +} +EXPORT_SYMBOL_GPL(cs5535_gpio_clear); + +int cs5535_gpio_isset(unsigned offset, unsigned int reg) +{ + struct cs5535_gpio_chip *chip = &cs5535_gpio_chip; + unsigned long flags; + long val; + + spin_lock_irqsave(&chip->lock, flags); + if (offset < 16) + /* low bank register */ + val = inl(chip->base + reg); + else { + /* high bank register */ + val = inl(chip->base + 0x80 + reg); + offset -= 16; + } + spin_unlock_irqrestore(&chip->lock, flags); + + return (val & (1 << offset)) ? 1 : 0; +} +EXPORT_SYMBOL_GPL(cs5535_gpio_isset); + +/* + * Generic gpio_chip API support. + */ + +static int chip_gpio_request(struct gpio_chip *c, unsigned offset) +{ + struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c; + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + /* check if this pin is available */ + if ((mask & (1 << offset)) == 0) { + dev_info(&chip->pdev->dev, + "pin %u is not available (check mask)\n", offset); + spin_unlock_irqrestore(&chip->lock, flags); + return -EINVAL; + } + + /* disable output aux 1 & 2 on this pin */ + __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_AUX1); + __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_AUX2); + + /* disable input aux 1 on this pin */ + __cs5535_gpio_clear(chip, offset, GPIO_INPUT_AUX1); + + spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static int chip_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + return cs5535_gpio_isset(offset, GPIO_READ_BACK); +} + +static void chip_gpio_set(struct gpio_chip *chip, unsigned offset, int val) +{ + if (val) + cs5535_gpio_set(offset, GPIO_OUTPUT_VAL); + else + cs5535_gpio_clear(offset, GPIO_OUTPUT_VAL); +} + +static int chip_direction_input(struct gpio_chip *c, unsigned offset) +{ + struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c; + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + __cs5535_gpio_set(chip, offset, GPIO_INPUT_ENABLE); + __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_ENABLE); + spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static int chip_direction_output(struct gpio_chip *c, unsigned offset, int val) +{ + struct cs5535_gpio_chip *chip = (struct cs5535_gpio_chip *) c; + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + __cs5535_gpio_set(chip, offset, GPIO_INPUT_ENABLE); + __cs5535_gpio_set(chip, offset, GPIO_OUTPUT_ENABLE); + if (val) + __cs5535_gpio_set(chip, offset, GPIO_OUTPUT_VAL); + else + __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_VAL); + + spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static char *cs5535_gpio_names[] = { + "GPIO0", "GPIO1", "GPIO2", "GPIO3", + "GPIO4", "GPIO5", "GPIO6", "GPIO7", + "GPIO8", "GPIO9", "GPIO10", "GPIO11", + "GPIO12", "GPIO13", "GPIO14", "GPIO15", + "GPIO16", "GPIO17", "GPIO18", "GPIO19", + "GPIO20", "GPIO21", "GPIO22", NULL, + "GPIO24", "GPIO25", "GPIO26", "GPIO27", + "GPIO28", NULL, NULL, NULL, +}; + +static struct cs5535_gpio_chip cs5535_gpio_chip = { + .chip = { + .owner = THIS_MODULE, + .label = DRV_NAME, + + .base = 0, + .ngpio = 32, + .names = cs5535_gpio_names, + .request = chip_gpio_request, + + .get = chip_gpio_get, + .set = chip_gpio_set, + + .direction_input = chip_direction_input, + .direction_output = chip_direction_output, + }, +}; + +static int __init cs5535_gpio_probe(struct pci_dev *pdev, + const struct pci_device_id *pci_id) +{ + int err; + ulong mask_orig = mask; + + /* There are two ways to get the GPIO base address; one is by + * fetching it from MSR_LBAR_GPIO, the other is by reading the + * PCI BAR info. The latter method is easier (especially across + * different architectures), so we'll stick with that for now. If + * it turns out to be unreliable in the face of crappy BIOSes, we + * can always go back to using MSRs.. */ + + err = pci_enable_device_io(pdev); + if (err) { + dev_err(&pdev->dev, "can't enable device IO\n"); + goto done; + } + + err = pci_request_region(pdev, GPIO_BAR, DRV_NAME); + if (err) { + dev_err(&pdev->dev, "can't alloc PCI BAR #%d\n", GPIO_BAR); + goto done; + } + + /* set up the driver-specific struct */ + cs5535_gpio_chip.base = pci_resource_start(pdev, GPIO_BAR); + cs5535_gpio_chip.pdev = pdev; + spin_lock_init(&cs5535_gpio_chip.lock); + + dev_info(&pdev->dev, "allocated PCI BAR #%d: base 0x%llx\n", GPIO_BAR, + (unsigned long long) cs5535_gpio_chip.base); + + /* mask out reserved pins */ + mask &= 0x1F7FFFFF; + + /* do not allow pin 28, Power Button, as there's special handling + * in the PMC needed. (note 12, p. 48) */ + mask &= ~(1 << 28); + + if (mask_orig != mask) + dev_info(&pdev->dev, "mask changed from 0x%08lX to 0x%08lX\n", + mask_orig, mask); + + /* finally, register with the generic GPIO API */ + err = gpiochip_add(&cs5535_gpio_chip.chip); + if (err) + goto release_region; + + dev_info(&pdev->dev, DRV_NAME ": GPIO support successfully loaded.\n"); + return 0; + +release_region: + pci_release_region(pdev, GPIO_BAR); +done: + return err; +} + +static void __exit cs5535_gpio_remove(struct pci_dev *pdev) +{ + int err; + + err = gpiochip_remove(&cs5535_gpio_chip.chip); + if (err) { + /* uhh? */ + dev_err(&pdev->dev, "unable to remove gpio_chip?\n"); + } + pci_release_region(pdev, GPIO_BAR); +} + +static struct pci_device_id cs5535_gpio_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, cs5535_gpio_pci_tbl); + +/* + * We can't use the standard PCI driver registration stuff here, since + * that allows only one driver to bind to each PCI device (and we want + * multiple drivers to be able to bind to the device). Instead, manually + * scan for the PCI device, request a single region, and keep track of the + * devices that we're using. + */ + +static int __init cs5535_gpio_scan_pci(void) +{ + struct pci_dev *pdev; + int err = -ENODEV; + int i; + + for (i = 0; i < ARRAY_SIZE(cs5535_gpio_pci_tbl); i++) { + pdev = pci_get_device(cs5535_gpio_pci_tbl[i].vendor, + cs5535_gpio_pci_tbl[i].device, NULL); + if (pdev) { + err = cs5535_gpio_probe(pdev, &cs5535_gpio_pci_tbl[i]); + if (err) + pci_dev_put(pdev); + + /* we only support a single CS5535/6 southbridge */ + break; + } + } + + return err; +} + +static void __exit cs5535_gpio_free_pci(void) +{ + cs5535_gpio_remove(cs5535_gpio_chip.pdev); + pci_dev_put(cs5535_gpio_chip.pdev); +} + +static int __init cs5535_gpio_init(void) +{ + return cs5535_gpio_scan_pci(); +} + +static void __exit cs5535_gpio_exit(void) +{ + cs5535_gpio_free_pci(); +} + +module_init(cs5535_gpio_init); +module_exit(cs5535_gpio_exit); + +MODULE_AUTHOR("Andres Salomon <dilinger@collabora.co.uk>"); +MODULE_DESCRIPTION("AMD CS5535/CS5536 GPIO driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c new file mode 100644 index 00000000000..350842ad363 --- /dev/null +++ b/drivers/gpio/gpiolib.c @@ -0,0 +1,1641 @@ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/spinlock.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/gpio.h> +#include <linux/idr.h> + + +/* Optional implementation infrastructure for GPIO interfaces. + * + * Platforms may want to use this if they tend to use very many GPIOs + * that aren't part of a System-On-Chip core; or across I2C/SPI/etc. + * + * When kernel footprint or instruction count is an issue, simpler + * implementations may be preferred. The GPIO programming interface + * allows for inlining speed-critical get/set operations for common + * cases, so that access to SOC-integrated GPIOs can sometimes cost + * only an instruction or two per bit. + */ + + +/* When debugging, extend minimal trust to callers and platform code. + * Also emit diagnostic messages that may help initial bringup, when + * board setup or driver bugs are most common. + * + * Otherwise, minimize overhead in what may be bitbanging codepaths. + */ +#ifdef DEBUG +#define extra_checks 1 +#else +#define extra_checks 0 +#endif + +/* gpio_lock prevents conflicts during gpio_desc[] table updates. + * While any GPIO is requested, its gpio_chip is not removable; + * each GPIO's "requested" flag serves as a lock and refcount. + */ +static DEFINE_SPINLOCK(gpio_lock); + +struct gpio_desc { + struct gpio_chip *chip; + unsigned long flags; +/* flag symbols are bit numbers */ +#define FLAG_REQUESTED 0 +#define FLAG_IS_OUT 1 +#define FLAG_RESERVED 2 +#define FLAG_EXPORT 3 /* protected by sysfs_lock */ +#define FLAG_SYSFS 4 /* exported via /sys/class/gpio/control */ +#define FLAG_TRIG_FALL 5 /* trigger on falling edge */ +#define FLAG_TRIG_RISE 6 /* trigger on rising edge */ +#define FLAG_ACTIVE_LOW 7 /* sysfs value has active low */ + +#define PDESC_ID_SHIFT 16 /* add new flags before this one */ + +#define GPIO_FLAGS_MASK ((1 << PDESC_ID_SHIFT) - 1) +#define GPIO_TRIGGER_MASK (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE)) + +#ifdef CONFIG_DEBUG_FS + const char *label; +#endif +}; +static struct gpio_desc gpio_desc[ARCH_NR_GPIOS]; + +#ifdef CONFIG_GPIO_SYSFS +struct poll_desc { + struct work_struct work; + struct sysfs_dirent *value_sd; +}; + +static struct idr pdesc_idr; +#endif + +static inline void desc_set_label(struct gpio_desc *d, const char *label) +{ +#ifdef CONFIG_DEBUG_FS + d->label = label; +#endif +} + +/* Warn when drivers omit gpio_request() calls -- legal but ill-advised + * when setting direction, and otherwise illegal. Until board setup code + * and drivers use explicit requests everywhere (which won't happen when + * those calls have no teeth) we can't avoid autorequesting. This nag + * message should motivate switching to explicit requests... so should + * the weaker cleanup after faults, compared to gpio_request(). + * + * NOTE: the autorequest mechanism is going away; at this point it's + * only "legal" in the sense that (old) code using it won't break yet, + * but instead only triggers a WARN() stack dump. + */ +static int gpio_ensure_requested(struct gpio_desc *desc, unsigned offset) +{ + const struct gpio_chip *chip = desc->chip; + const int gpio = chip->base + offset; + + if (WARN(test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0, + "autorequest GPIO-%d\n", gpio)) { + if (!try_module_get(chip->owner)) { + pr_err("GPIO-%d: module can't be gotten \n", gpio); + clear_bit(FLAG_REQUESTED, &desc->flags); + /* lose */ + return -EIO; + } + desc_set_label(desc, "[auto]"); + /* caller must chip->request() w/o spinlock */ + if (chip->request) + return 1; + } + return 0; +} + +/* caller holds gpio_lock *OR* gpio is marked as requested */ +static inline struct gpio_chip *gpio_to_chip(unsigned gpio) +{ + return gpio_desc[gpio].chip; +} + +/* dynamic allocation of GPIOs, e.g. on a hotplugged device */ +static int gpiochip_find_base(int ngpio) +{ + int i; + int spare = 0; + int base = -ENOSPC; + + for (i = ARCH_NR_GPIOS - 1; i >= 0 ; i--) { + struct gpio_desc *desc = &gpio_desc[i]; + struct gpio_chip *chip = desc->chip; + + if (!chip && !test_bit(FLAG_RESERVED, &desc->flags)) { + spare++; + if (spare == ngpio) { + base = i; + break; + } + } else { + spare = 0; + if (chip) + i -= chip->ngpio - 1; + } + } + + if (gpio_is_valid(base)) + pr_debug("%s: found new base at %d\n", __func__, base); + return base; +} + +/** + * gpiochip_reserve() - reserve range of gpios to use with platform code only + * @start: starting gpio number + * @ngpio: number of gpios to reserve + * Context: platform init, potentially before irqs or kmalloc will work + * + * Returns a negative errno if any gpio within the range is already reserved + * or registered, else returns zero as a success code. Use this function + * to mark a range of gpios as unavailable for dynamic gpio number allocation, + * for example because its driver support is not yet loaded. + */ +int __init gpiochip_reserve(int start, int ngpio) +{ + int ret = 0; + unsigned long flags; + int i; + + if (!gpio_is_valid(start) || !gpio_is_valid(start + ngpio - 1)) + return -EINVAL; + + spin_lock_irqsave(&gpio_lock, flags); + + for (i = start; i < start + ngpio; i++) { + struct gpio_desc *desc = &gpio_desc[i]; + + if (desc->chip || test_bit(FLAG_RESERVED, &desc->flags)) { + ret = -EBUSY; + goto err; + } + + set_bit(FLAG_RESERVED, &desc->flags); + } + + pr_debug("%s: reserved gpios from %d to %d\n", + __func__, start, start + ngpio - 1); +err: + spin_unlock_irqrestore(&gpio_lock, flags); + + return ret; +} + +#ifdef CONFIG_GPIO_SYSFS + +/* lock protects against unexport_gpio() being called while + * sysfs files are active. + */ +static DEFINE_MUTEX(sysfs_lock); + +/* + * /sys/class/gpio/gpioN... only for GPIOs that are exported + * /direction + * * MAY BE OMITTED if kernel won't allow direction changes + * * is read/write as "in" or "out" + * * may also be written as "high" or "low", initializing + * output value as specified ("out" implies "low") + * /value + * * always readable, subject to hardware behavior + * * may be writable, as zero/nonzero + * /edge + * * configures behavior of poll(2) on /value + * * available only if pin can generate IRQs on input + * * is read/write as "none", "falling", "rising", or "both" + * /active_low + * * configures polarity of /value + * * is read/write as zero/nonzero + * * also affects existing and subsequent "falling" and "rising" + * /edge configuration + */ + +static ssize_t gpio_direction_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct gpio_desc *desc = dev_get_drvdata(dev); + ssize_t status; + + mutex_lock(&sysfs_lock); + + if (!test_bit(FLAG_EXPORT, &desc->flags)) + status = -EIO; + else + status = sprintf(buf, "%s\n", + test_bit(FLAG_IS_OUT, &desc->flags) + ? "out" : "in"); + + mutex_unlock(&sysfs_lock); + return status; +} + +static ssize_t gpio_direction_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + const struct gpio_desc *desc = dev_get_drvdata(dev); + unsigned gpio = desc - gpio_desc; + ssize_t status; + + mutex_lock(&sysfs_lock); + + if (!test_bit(FLAG_EXPORT, &desc->flags)) + status = -EIO; + else if (sysfs_streq(buf, "high")) + status = gpio_direction_output(gpio, 1); + else if (sysfs_streq(buf, "out") || sysfs_streq(buf, "low")) + status = gpio_direction_output(gpio, 0); + else if (sysfs_streq(buf, "in")) + status = gpio_direction_input(gpio); + else + status = -EINVAL; + + mutex_unlock(&sysfs_lock); + return status ? : size; +} + +static /* const */ DEVICE_ATTR(direction, 0644, + gpio_direction_show, gpio_direction_store); + +static ssize_t gpio_value_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct gpio_desc *desc = dev_get_drvdata(dev); + unsigned gpio = desc - gpio_desc; + ssize_t status; + + mutex_lock(&sysfs_lock); + + if (!test_bit(FLAG_EXPORT, &desc->flags)) { + status = -EIO; + } else { + int value; + + value = !!gpio_get_value_cansleep(gpio); + if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) + value = !value; + + status = sprintf(buf, "%d\n", value); + } + + mutex_unlock(&sysfs_lock); + return status; +} + +static ssize_t gpio_value_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + const struct gpio_desc *desc = dev_get_drvdata(dev); + unsigned gpio = desc - gpio_desc; + ssize_t status; + + mutex_lock(&sysfs_lock); + + if (!test_bit(FLAG_EXPORT, &desc->flags)) + status = -EIO; + else if (!test_bit(FLAG_IS_OUT, &desc->flags)) + status = -EPERM; + else { + long value; + + status = strict_strtol(buf, 0, &value); + if (status == 0) { + if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) + value = !value; + gpio_set_value_cansleep(gpio, value != 0); + status = size; + } + } + + mutex_unlock(&sysfs_lock); + return status; +} + +static const DEVICE_ATTR(value, 0644, + gpio_value_show, gpio_value_store); + +static irqreturn_t gpio_sysfs_irq(int irq, void *priv) +{ + struct work_struct *work = priv; + + schedule_work(work); + return IRQ_HANDLED; +} + +static void gpio_notify_sysfs(struct work_struct *work) +{ + struct poll_desc *pdesc; + + pdesc = container_of(work, struct poll_desc, work); + sysfs_notify_dirent(pdesc->value_sd); +} + +static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev, + unsigned long gpio_flags) +{ + struct poll_desc *pdesc; + unsigned long irq_flags; + int ret, irq, id; + + if ((desc->flags & GPIO_TRIGGER_MASK) == gpio_flags) + return 0; + + irq = gpio_to_irq(desc - gpio_desc); + if (irq < 0) + return -EIO; + + id = desc->flags >> PDESC_ID_SHIFT; + pdesc = idr_find(&pdesc_idr, id); + if (pdesc) { + free_irq(irq, &pdesc->work); + cancel_work_sync(&pdesc->work); + } + + desc->flags &= ~GPIO_TRIGGER_MASK; + + if (!gpio_flags) { + ret = 0; + goto free_sd; + } + + irq_flags = IRQF_SHARED; + if (test_bit(FLAG_TRIG_FALL, &gpio_flags)) + irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? + IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING; + if (test_bit(FLAG_TRIG_RISE, &gpio_flags)) + irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? + IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING; + + if (!pdesc) { + pdesc = kmalloc(sizeof(*pdesc), GFP_KERNEL); + if (!pdesc) { + ret = -ENOMEM; + goto err_out; + } + + do { + ret = -ENOMEM; + if (idr_pre_get(&pdesc_idr, GFP_KERNEL)) + ret = idr_get_new_above(&pdesc_idr, + pdesc, 1, &id); + } while (ret == -EAGAIN); + + if (ret) + goto free_mem; + + desc->flags &= GPIO_FLAGS_MASK; + desc->flags |= (unsigned long)id << PDESC_ID_SHIFT; + + if (desc->flags >> PDESC_ID_SHIFT != id) { + ret = -ERANGE; + goto free_id; + } + + pdesc->value_sd = sysfs_get_dirent(dev->kobj.sd, "value"); + if (!pdesc->value_sd) { + ret = -ENODEV; + goto free_id; + } + INIT_WORK(&pdesc->work, gpio_notify_sysfs); + } + + ret = request_irq(irq, gpio_sysfs_irq, irq_flags, + "gpiolib", &pdesc->work); + if (ret) + goto free_sd; + + desc->flags |= gpio_flags; + return 0; + +free_sd: + sysfs_put(pdesc->value_sd); +free_id: + idr_remove(&pdesc_idr, id); + desc->flags &= GPIO_FLAGS_MASK; +free_mem: + kfree(pdesc); +err_out: + return ret; +} + +static const struct { + const char *name; + unsigned long flags; +} trigger_types[] = { + { "none", 0 }, + { "falling", BIT(FLAG_TRIG_FALL) }, + { "rising", BIT(FLAG_TRIG_RISE) }, + { "both", BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE) }, +}; + +static ssize_t gpio_edge_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct gpio_desc *desc = dev_get_drvdata(dev); + ssize_t status; + + mutex_lock(&sysfs_lock); + + if (!test_bit(FLAG_EXPORT, &desc->flags)) + status = -EIO; + else { + int i; + + status = 0; + for (i = 0; i < ARRAY_SIZE(trigger_types); i++) + if ((desc->flags & GPIO_TRIGGER_MASK) + == trigger_types[i].flags) { + status = sprintf(buf, "%s\n", + trigger_types[i].name); + break; + } + } + + mutex_unlock(&sysfs_lock); + return status; +} + +static ssize_t gpio_edge_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct gpio_desc *desc = dev_get_drvdata(dev); + ssize_t status; + int i; + + for (i = 0; i < ARRAY_SIZE(trigger_types); i++) + if (sysfs_streq(trigger_types[i].name, buf)) + goto found; + return -EINVAL; + +found: + mutex_lock(&sysfs_lock); + + if (!test_bit(FLAG_EXPORT, &desc->flags)) + status = -EIO; + else { + status = gpio_setup_irq(desc, dev, trigger_types[i].flags); + if (!status) + status = size; + } + + mutex_unlock(&sysfs_lock); + + return status; +} + +static DEVICE_ATTR(edge, 0644, gpio_edge_show, gpio_edge_store); + +static int sysfs_set_active_low(struct gpio_desc *desc, struct device *dev, + int value) +{ + int status = 0; + + if (!!test_bit(FLAG_ACTIVE_LOW, &desc->flags) == !!value) + return 0; + + if (value) + set_bit(FLAG_ACTIVE_LOW, &desc->flags); + else + clear_bit(FLAG_ACTIVE_LOW, &desc->flags); + + /* reconfigure poll(2) support if enabled on one edge only */ + if (dev != NULL && (!!test_bit(FLAG_TRIG_RISE, &desc->flags) ^ + !!test_bit(FLAG_TRIG_FALL, &desc->flags))) { + unsigned long trigger_flags = desc->flags & GPIO_TRIGGER_MASK; + + gpio_setup_irq(desc, dev, 0); + status = gpio_setup_irq(desc, dev, trigger_flags); + } + + return status; +} + +static ssize_t gpio_active_low_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct gpio_desc *desc = dev_get_drvdata(dev); + ssize_t status; + + mutex_lock(&sysfs_lock); + + if (!test_bit(FLAG_EXPORT, &desc->flags)) + status = -EIO; + else + status = sprintf(buf, "%d\n", + !!test_bit(FLAG_ACTIVE_LOW, &desc->flags)); + + mutex_unlock(&sysfs_lock); + + return status; +} + +static ssize_t gpio_active_low_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct gpio_desc *desc = dev_get_drvdata(dev); + ssize_t status; + + mutex_lock(&sysfs_lock); + + if (!test_bit(FLAG_EXPORT, &desc->flags)) { + status = -EIO; + } else { + long value; + + status = strict_strtol(buf, 0, &value); + if (status == 0) + status = sysfs_set_active_low(desc, dev, value != 0); + } + + mutex_unlock(&sysfs_lock); + + return status ? : size; +} + +static const DEVICE_ATTR(active_low, 0644, + gpio_active_low_show, gpio_active_low_store); + +static const struct attribute *gpio_attrs[] = { + &dev_attr_value.attr, + &dev_attr_active_low.attr, + NULL, +}; + +static const struct attribute_group gpio_attr_group = { + .attrs = (struct attribute **) gpio_attrs, +}; + +/* + * /sys/class/gpio/gpiochipN/ + * /base ... matching gpio_chip.base (N) + * /label ... matching gpio_chip.label + * /ngpio ... matching gpio_chip.ngpio + */ + +static ssize_t chip_base_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct gpio_chip *chip = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", chip->base); +} +static DEVICE_ATTR(base, 0444, chip_base_show, NULL); + +static ssize_t chip_label_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct gpio_chip *chip = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", chip->label ? : ""); +} +static DEVICE_ATTR(label, 0444, chip_label_show, NULL); + +static ssize_t chip_ngpio_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct gpio_chip *chip = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", chip->ngpio); +} +static DEVICE_ATTR(ngpio, 0444, chip_ngpio_show, NULL); + +static const struct attribute *gpiochip_attrs[] = { + &dev_attr_base.attr, + &dev_attr_label.attr, + &dev_attr_ngpio.attr, + NULL, +}; + +static const struct attribute_group gpiochip_attr_group = { + .attrs = (struct attribute **) gpiochip_attrs, +}; + +/* + * /sys/class/gpio/export ... write-only + * integer N ... number of GPIO to export (full access) + * /sys/class/gpio/unexport ... write-only + * integer N ... number of GPIO to unexport + */ +static ssize_t export_store(struct class *class, const char *buf, size_t len) +{ + long gpio; + int status; + + status = strict_strtol(buf, 0, &gpio); + if (status < 0) + goto done; + + /* No extra locking here; FLAG_SYSFS just signifies that the + * request and export were done by on behalf of userspace, so + * they may be undone on its behalf too. + */ + + status = gpio_request(gpio, "sysfs"); + if (status < 0) + goto done; + + status = gpio_export(gpio, true); + if (status < 0) + gpio_free(gpio); + else + set_bit(FLAG_SYSFS, &gpio_desc[gpio].flags); + +done: + if (status) + pr_debug("%s: status %d\n", __func__, status); + return status ? : len; +} + +static ssize_t unexport_store(struct class *class, const char *buf, size_t len) +{ + long gpio; + int status; + + status = strict_strtol(buf, 0, &gpio); + if (status < 0) + goto done; + + status = -EINVAL; + + /* reject bogus commands (gpio_unexport ignores them) */ + if (!gpio_is_valid(gpio)) + goto done; + + /* No extra locking here; FLAG_SYSFS just signifies that the + * request and export were done by on behalf of userspace, so + * they may be undone on its behalf too. + */ + if (test_and_clear_bit(FLAG_SYSFS, &gpio_desc[gpio].flags)) { + status = 0; + gpio_free(gpio); + } +done: + if (status) + pr_debug("%s: status %d\n", __func__, status); + return status ? : len; +} + +static struct class_attribute gpio_class_attrs[] = { + __ATTR(export, 0200, NULL, export_store), + __ATTR(unexport, 0200, NULL, unexport_store), + __ATTR_NULL, +}; + +static struct class gpio_class = { + .name = "gpio", + .owner = THIS_MODULE, + + .class_attrs = gpio_class_attrs, +}; + + +/** + * gpio_export - export a GPIO through sysfs + * @gpio: gpio to make available, already requested + * @direction_may_change: true if userspace may change gpio direction + * Context: arch_initcall or later + * + * When drivers want to make a GPIO accessible to userspace after they + * have requested it -- perhaps while debugging, or as part of their + * public interface -- they may use this routine. If the GPIO can + * change direction (some can't) and the caller allows it, userspace + * will see "direction" sysfs attribute which may be used to change + * the gpio's direction. A "value" attribute will always be provided. + * + * Returns zero on success, else an error. + */ +int gpio_export(unsigned gpio, bool direction_may_change) +{ + unsigned long flags; + struct gpio_desc *desc; + int status = -EINVAL; + char *ioname = NULL; + + /* can't export until sysfs is available ... */ + if (!gpio_class.p) { + pr_debug("%s: called too early!\n", __func__); + return -ENOENT; + } + + if (!gpio_is_valid(gpio)) + goto done; + + mutex_lock(&sysfs_lock); + + spin_lock_irqsave(&gpio_lock, flags); + desc = &gpio_desc[gpio]; + if (test_bit(FLAG_REQUESTED, &desc->flags) + && !test_bit(FLAG_EXPORT, &desc->flags)) { + status = 0; + if (!desc->chip->direction_input + || !desc->chip->direction_output) + direction_may_change = false; + } + spin_unlock_irqrestore(&gpio_lock, flags); + + if (desc->chip->names && desc->chip->names[gpio - desc->chip->base]) + ioname = desc->chip->names[gpio - desc->chip->base]; + + if (status == 0) { + struct device *dev; + + dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0), + desc, ioname ? ioname : "gpio%d", gpio); + if (!IS_ERR(dev)) { + status = sysfs_create_group(&dev->kobj, + &gpio_attr_group); + + if (!status && direction_may_change) + status = device_create_file(dev, + &dev_attr_direction); + + if (!status && gpio_to_irq(gpio) >= 0 + && (direction_may_change + || !test_bit(FLAG_IS_OUT, + &desc->flags))) + status = device_create_file(dev, + &dev_attr_edge); + + if (status != 0) + device_unregister(dev); + } else + status = PTR_ERR(dev); + if (status == 0) + set_bit(FLAG_EXPORT, &desc->flags); + } + + mutex_unlock(&sysfs_lock); + +done: + if (status) + pr_debug("%s: gpio%d status %d\n", __func__, gpio, status); + + return status; +} +EXPORT_SYMBOL_GPL(gpio_export); + +static int match_export(struct device *dev, void *data) +{ + return dev_get_drvdata(dev) == data; +} + +/** + * gpio_export_link - create a sysfs link to an exported GPIO node + * @dev: device under which to create symlink + * @name: name of the symlink + * @gpio: gpio to create symlink to, already exported + * + * Set up a symlink from /sys/.../dev/name to /sys/class/gpio/gpioN + * node. Caller is responsible for unlinking. + * + * Returns zero on success, else an error. + */ +int gpio_export_link(struct device *dev, const char *name, unsigned gpio) +{ + struct gpio_desc *desc; + int status = -EINVAL; + + if (!gpio_is_valid(gpio)) + goto done; + + mutex_lock(&sysfs_lock); + + desc = &gpio_desc[gpio]; + + if (test_bit(FLAG_EXPORT, &desc->flags)) { + struct device *tdev; + + tdev = class_find_device(&gpio_class, NULL, desc, match_export); + if (tdev != NULL) { + status = sysfs_create_link(&dev->kobj, &tdev->kobj, + name); + } else { + status = -ENODEV; + } + } + + mutex_unlock(&sysfs_lock); + +done: + if (status) + pr_debug("%s: gpio%d status %d\n", __func__, gpio, status); + + return status; +} +EXPORT_SYMBOL_GPL(gpio_export_link); + + +/** + * gpio_sysfs_set_active_low - set the polarity of gpio sysfs value + * @gpio: gpio to change + * @value: non-zero to use active low, i.e. inverted values + * + * Set the polarity of /sys/class/gpio/gpioN/value sysfs attribute. + * The GPIO does not have to be exported yet. If poll(2) support has + * been enabled for either rising or falling edge, it will be + * reconfigured to follow the new polarity. + * + * Returns zero on success, else an error. + */ +int gpio_sysfs_set_active_low(unsigned gpio, int value) +{ + struct gpio_desc *desc; + struct device *dev = NULL; + int status = -EINVAL; + + if (!gpio_is_valid(gpio)) + goto done; + + mutex_lock(&sysfs_lock); + + desc = &gpio_desc[gpio]; + + if (test_bit(FLAG_EXPORT, &desc->flags)) { + dev = class_find_device(&gpio_class, NULL, desc, match_export); + if (dev == NULL) { + status = -ENODEV; + goto unlock; + } + } + + status = sysfs_set_active_low(desc, dev, value); + +unlock: + mutex_unlock(&sysfs_lock); + +done: + if (status) + pr_debug("%s: gpio%d status %d\n", __func__, gpio, status); + + return status; +} +EXPORT_SYMBOL_GPL(gpio_sysfs_set_active_low); + +/** + * gpio_unexport - reverse effect of gpio_export() + * @gpio: gpio to make unavailable + * + * This is implicit on gpio_free(). + */ +void gpio_unexport(unsigned gpio) +{ + struct gpio_desc *desc; + int status = -EINVAL; + + if (!gpio_is_valid(gpio)) + goto done; + + mutex_lock(&sysfs_lock); + + desc = &gpio_desc[gpio]; + + if (test_bit(FLAG_EXPORT, &desc->flags)) { + struct device *dev = NULL; + + dev = class_find_device(&gpio_class, NULL, desc, match_export); + if (dev) { + gpio_setup_irq(desc, dev, 0); + clear_bit(FLAG_EXPORT, &desc->flags); + put_device(dev); + device_unregister(dev); + status = 0; + } else + status = -ENODEV; + } + + mutex_unlock(&sysfs_lock); +done: + if (status) + pr_debug("%s: gpio%d status %d\n", __func__, gpio, status); +} +EXPORT_SYMBOL_GPL(gpio_unexport); + +static int gpiochip_export(struct gpio_chip *chip) +{ + int status; + struct device *dev; + + /* Many systems register gpio chips for SOC support very early, + * before driver model support is available. In those cases we + * export this later, in gpiolib_sysfs_init() ... here we just + * verify that _some_ field of gpio_class got initialized. + */ + if (!gpio_class.p) + return 0; + + /* use chip->base for the ID; it's already known to be unique */ + mutex_lock(&sysfs_lock); + dev = device_create(&gpio_class, chip->dev, MKDEV(0, 0), chip, + "gpiochip%d", chip->base); + if (!IS_ERR(dev)) { + status = sysfs_create_group(&dev->kobj, + &gpiochip_attr_group); + } else + status = PTR_ERR(dev); + chip->exported = (status == 0); + mutex_unlock(&sysfs_lock); + + if (status) { + unsigned long flags; + unsigned gpio; + + spin_lock_irqsave(&gpio_lock, flags); + gpio = chip->base; + while (gpio_desc[gpio].chip == chip) + gpio_desc[gpio++].chip = NULL; + spin_unlock_irqrestore(&gpio_lock, flags); + + pr_debug("%s: chip %s status %d\n", __func__, + chip->label, status); + } + + return status; +} + +static void gpiochip_unexport(struct gpio_chip *chip) +{ + int status; + struct device *dev; + + mutex_lock(&sysfs_lock); + dev = class_find_device(&gpio_class, NULL, chip, match_export); + if (dev) { + put_device(dev); + device_unregister(dev); + chip->exported = 0; + status = 0; + } else + status = -ENODEV; + mutex_unlock(&sysfs_lock); + + if (status) + pr_debug("%s: chip %s status %d\n", __func__, + chip->label, status); +} + +static int __init gpiolib_sysfs_init(void) +{ + int status; + unsigned long flags; + unsigned gpio; + + idr_init(&pdesc_idr); + + status = class_register(&gpio_class); + if (status < 0) + return status; + + /* Scan and register the gpio_chips which registered very + * early (e.g. before the class_register above was called). + * + * We run before arch_initcall() so chip->dev nodes can have + * registered, and so arch_initcall() can always gpio_export(). + */ + spin_lock_irqsave(&gpio_lock, flags); + for (gpio = 0; gpio < ARCH_NR_GPIOS; gpio++) { + struct gpio_chip *chip; + + chip = gpio_desc[gpio].chip; + if (!chip || chip->exported) + continue; + + spin_unlock_irqrestore(&gpio_lock, flags); + status = gpiochip_export(chip); + spin_lock_irqsave(&gpio_lock, flags); + } + spin_unlock_irqrestore(&gpio_lock, flags); + + + return status; +} +postcore_initcall(gpiolib_sysfs_init); + +#else +static inline int gpiochip_export(struct gpio_chip *chip) +{ + return 0; +} + +static inline void gpiochip_unexport(struct gpio_chip *chip) +{ +} + +#endif /* CONFIG_GPIO_SYSFS */ + +/** + * gpiochip_add() - register a gpio_chip + * @chip: the chip to register, with chip->base initialized + * Context: potentially before irqs or kmalloc will work + * + * Returns a negative errno if the chip can't be registered, such as + * because the chip->base is invalid or already associated with a + * different chip. Otherwise it returns zero as a success code. + * + * When gpiochip_add() is called very early during boot, so that GPIOs + * can be freely used, the chip->dev device must be registered before + * the gpio framework's arch_initcall(). Otherwise sysfs initialization + * for GPIOs will fail rudely. + * + * If chip->base is negative, this requests dynamic assignment of + * a range of valid GPIOs. + */ +int gpiochip_add(struct gpio_chip *chip) +{ + unsigned long flags; + int status = 0; + unsigned id; + int base = chip->base; + + if ((!gpio_is_valid(base) || !gpio_is_valid(base + chip->ngpio - 1)) + && base >= 0) { + status = -EINVAL; + goto fail; + } + + spin_lock_irqsave(&gpio_lock, flags); + + if (base < 0) { + base = gpiochip_find_base(chip->ngpio); + if (base < 0) { + status = base; + goto unlock; + } + chip->base = base; + } + + /* these GPIO numbers must not be managed by another gpio_chip */ + for (id = base; id < base + chip->ngpio; id++) { + if (gpio_desc[id].chip != NULL) { + status = -EBUSY; + break; + } + } + if (status == 0) { + for (id = base; id < base + chip->ngpio; id++) { + gpio_desc[id].chip = chip; + + /* REVISIT: most hardware initializes GPIOs as + * inputs (often with pullups enabled) so power + * usage is minimized. Linux code should set the + * gpio direction first thing; but until it does, + * we may expose the wrong direction in sysfs. + */ + gpio_desc[id].flags = !chip->direction_input + ? (1 << FLAG_IS_OUT) + : 0; + } + } + +unlock: + spin_unlock_irqrestore(&gpio_lock, flags); + if (status == 0) + status = gpiochip_export(chip); +fail: + /* failures here can mean systems won't boot... */ + if (status) + pr_err("gpiochip_add: gpios %d..%d (%s) not registered\n", + chip->base, chip->base + chip->ngpio - 1, + chip->label ? : "generic"); + return status; +} +EXPORT_SYMBOL_GPL(gpiochip_add); + +/** + * gpiochip_remove() - unregister a gpio_chip + * @chip: the chip to unregister + * + * A gpio_chip with any GPIOs still requested may not be removed. + */ +int gpiochip_remove(struct gpio_chip *chip) +{ + unsigned long flags; + int status = 0; + unsigned id; + + spin_lock_irqsave(&gpio_lock, flags); + + for (id = chip->base; id < chip->base + chip->ngpio; id++) { + if (test_bit(FLAG_REQUESTED, &gpio_desc[id].flags)) { + status = -EBUSY; + break; + } + } + if (status == 0) { + for (id = chip->base; id < chip->base + chip->ngpio; id++) + gpio_desc[id].chip = NULL; + } + + spin_unlock_irqrestore(&gpio_lock, flags); + + if (status == 0) + gpiochip_unexport(chip); + + return status; +} +EXPORT_SYMBOL_GPL(gpiochip_remove); + + +/* These "optional" allocation calls help prevent drivers from stomping + * on each other, and help provide better diagnostics in debugfs. + * They're called even less than the "set direction" calls. + */ +int gpio_request(unsigned gpio, const char *label) +{ + struct gpio_desc *desc; + struct gpio_chip *chip; + int status = -EINVAL; + unsigned long flags; + + spin_lock_irqsave(&gpio_lock, flags); + + if (!gpio_is_valid(gpio)) + goto done; + desc = &gpio_desc[gpio]; + chip = desc->chip; + if (chip == NULL) + goto done; + + if (!try_module_get(chip->owner)) + goto done; + + /* NOTE: gpio_request() can be called in early boot, + * before IRQs are enabled, for non-sleeping (SOC) GPIOs. + */ + + if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) { + desc_set_label(desc, label ? : "?"); + status = 0; + } else { + status = -EBUSY; + module_put(chip->owner); + goto done; + } + + if (chip->request) { + /* chip->request may sleep */ + spin_unlock_irqrestore(&gpio_lock, flags); + status = chip->request(chip, gpio - chip->base); + spin_lock_irqsave(&gpio_lock, flags); + + if (status < 0) { + desc_set_label(desc, NULL); + module_put(chip->owner); + clear_bit(FLAG_REQUESTED, &desc->flags); + } + } + +done: + if (status) + pr_debug("gpio_request: gpio-%d (%s) status %d\n", + gpio, label ? : "?", status); + spin_unlock_irqrestore(&gpio_lock, flags); + return status; +} +EXPORT_SYMBOL_GPL(gpio_request); + +void gpio_free(unsigned gpio) +{ + unsigned long flags; + struct gpio_desc *desc; + struct gpio_chip *chip; + + might_sleep(); + + if (!gpio_is_valid(gpio)) { + WARN_ON(extra_checks); + return; + } + + gpio_unexport(gpio); + + spin_lock_irqsave(&gpio_lock, flags); + + desc = &gpio_desc[gpio]; + chip = desc->chip; + if (chip && test_bit(FLAG_REQUESTED, &desc->flags)) { + if (chip->free) { + spin_unlock_irqrestore(&gpio_lock, flags); + might_sleep_if(extra_checks && chip->can_sleep); + chip->free(chip, gpio - chip->base); + spin_lock_irqsave(&gpio_lock, flags); + } + desc_set_label(desc, NULL); + module_put(desc->chip->owner); + clear_bit(FLAG_ACTIVE_LOW, &desc->flags); + clear_bit(FLAG_REQUESTED, &desc->flags); + } else + WARN_ON(extra_checks); + + spin_unlock_irqrestore(&gpio_lock, flags); +} +EXPORT_SYMBOL_GPL(gpio_free); + + +/** + * gpiochip_is_requested - return string iff signal was requested + * @chip: controller managing the signal + * @offset: of signal within controller's 0..(ngpio - 1) range + * + * Returns NULL if the GPIO is not currently requested, else a string. + * If debugfs support is enabled, the string returned is the label passed + * to gpio_request(); otherwise it is a meaningless constant. + * + * This function is for use by GPIO controller drivers. The label can + * help with diagnostics, and knowing that the signal is used as a GPIO + * can help avoid accidentally multiplexing it to another controller. + */ +const char *gpiochip_is_requested(struct gpio_chip *chip, unsigned offset) +{ + unsigned gpio = chip->base + offset; + + if (!gpio_is_valid(gpio) || gpio_desc[gpio].chip != chip) + return NULL; + if (test_bit(FLAG_REQUESTED, &gpio_desc[gpio].flags) == 0) + return NULL; +#ifdef CONFIG_DEBUG_FS + return gpio_desc[gpio].label; +#else + return "?"; +#endif +} +EXPORT_SYMBOL_GPL(gpiochip_is_requested); + + +/* Drivers MUST set GPIO direction before making get/set calls. In + * some cases this is done in early boot, before IRQs are enabled. + * + * As a rule these aren't called more than once (except for drivers + * using the open-drain emulation idiom) so these are natural places + * to accumulate extra debugging checks. Note that we can't (yet) + * rely on gpio_request() having been called beforehand. + */ + +int gpio_direction_input(unsigned gpio) +{ + unsigned long flags; + struct gpio_chip *chip; + struct gpio_desc *desc = &gpio_desc[gpio]; + int status = -EINVAL; + + spin_lock_irqsave(&gpio_lock, flags); + + if (!gpio_is_valid(gpio)) + goto fail; + chip = desc->chip; + if (!chip || !chip->get || !chip->direction_input) + goto fail; + gpio -= chip->base; + if (gpio >= chip->ngpio) + goto fail; + status = gpio_ensure_requested(desc, gpio); + if (status < 0) + goto fail; + + /* now we know the gpio is valid and chip won't vanish */ + + spin_unlock_irqrestore(&gpio_lock, flags); + + might_sleep_if(extra_checks && chip->can_sleep); + + if (status) { + status = chip->request(chip, gpio); + if (status < 0) { + pr_debug("GPIO-%d: chip request fail, %d\n", + chip->base + gpio, status); + /* and it's not available to anyone else ... + * gpio_request() is the fully clean solution. + */ + goto lose; + } + } + + status = chip->direction_input(chip, gpio); + if (status == 0) + clear_bit(FLAG_IS_OUT, &desc->flags); +lose: + return status; +fail: + spin_unlock_irqrestore(&gpio_lock, flags); + if (status) + pr_debug("%s: gpio-%d status %d\n", + __func__, gpio, status); + return status; +} +EXPORT_SYMBOL_GPL(gpio_direction_input); + +int gpio_direction_output(unsigned gpio, int value) +{ + unsigned long flags; + struct gpio_chip *chip; + struct gpio_desc *desc = &gpio_desc[gpio]; + int status = -EINVAL; + + spin_lock_irqsave(&gpio_lock, flags); + + if (!gpio_is_valid(gpio)) + goto fail; + chip = desc->chip; + if (!chip || !chip->set || !chip->direction_output) + goto fail; + gpio -= chip->base; + if (gpio >= chip->ngpio) + goto fail; + status = gpio_ensure_requested(desc, gpio); + if (status < 0) + goto fail; + + /* now we know the gpio is valid and chip won't vanish */ + + spin_unlock_irqrestore(&gpio_lock, flags); + + might_sleep_if(extra_checks && chip->can_sleep); + + if (status) { + status = chip->request(chip, gpio); + if (status < 0) { + pr_debug("GPIO-%d: chip request fail, %d\n", + chip->base + gpio, status); + /* and it's not available to anyone else ... + * gpio_request() is the fully clean solution. + */ + goto lose; + } + } + + status = chip->direction_output(chip, gpio, value); + if (status == 0) + set_bit(FLAG_IS_OUT, &desc->flags); +lose: + return status; +fail: + spin_unlock_irqrestore(&gpio_lock, flags); + if (status) + pr_debug("%s: gpio-%d status %d\n", + __func__, gpio, status); + return status; +} +EXPORT_SYMBOL_GPL(gpio_direction_output); + + +/* I/O calls are only valid after configuration completed; the relevant + * "is this a valid GPIO" error checks should already have been done. + * + * "Get" operations are often inlinable as reading a pin value register, + * and masking the relevant bit in that register. + * + * When "set" operations are inlinable, they involve writing that mask to + * one register to set a low value, or a different register to set it high. + * Otherwise locking is needed, so there may be little value to inlining. + * + *------------------------------------------------------------------------ + * + * IMPORTANT!!! The hot paths -- get/set value -- assume that callers + * have requested the GPIO. That can include implicit requesting by + * a direction setting call. Marking a gpio as requested locks its chip + * in memory, guaranteeing that these table lookups need no more locking + * and that gpiochip_remove() will fail. + * + * REVISIT when debugging, consider adding some instrumentation to ensure + * that the GPIO was actually requested. + */ + +/** + * __gpio_get_value() - return a gpio's value + * @gpio: gpio whose value will be returned + * Context: any + * + * This is used directly or indirectly to implement gpio_get_value(). + * It returns the zero or nonzero value provided by the associated + * gpio_chip.get() method; or zero if no such method is provided. + */ +int __gpio_get_value(unsigned gpio) +{ + struct gpio_chip *chip; + + chip = gpio_to_chip(gpio); + WARN_ON(extra_checks && chip->can_sleep); + return chip->get ? chip->get(chip, gpio - chip->base) : 0; +} +EXPORT_SYMBOL_GPL(__gpio_get_value); + +/** + * __gpio_set_value() - assign a gpio's value + * @gpio: gpio whose value will be assigned + * @value: value to assign + * Context: any + * + * This is used directly or indirectly to implement gpio_set_value(). + * It invokes the associated gpio_chip.set() method. + */ +void __gpio_set_value(unsigned gpio, int value) +{ + struct gpio_chip *chip; + + chip = gpio_to_chip(gpio); + WARN_ON(extra_checks && chip->can_sleep); + chip->set(chip, gpio - chip->base, value); +} +EXPORT_SYMBOL_GPL(__gpio_set_value); + +/** + * __gpio_cansleep() - report whether gpio value access will sleep + * @gpio: gpio in question + * Context: any + * + * This is used directly or indirectly to implement gpio_cansleep(). It + * returns nonzero if access reading or writing the GPIO value can sleep. + */ +int __gpio_cansleep(unsigned gpio) +{ + struct gpio_chip *chip; + + /* only call this on GPIOs that are valid! */ + chip = gpio_to_chip(gpio); + + return chip->can_sleep; +} +EXPORT_SYMBOL_GPL(__gpio_cansleep); + +/** + * __gpio_to_irq() - return the IRQ corresponding to a GPIO + * @gpio: gpio whose IRQ will be returned (already requested) + * Context: any + * + * This is used directly or indirectly to implement gpio_to_irq(). + * It returns the number of the IRQ signaled by this (input) GPIO, + * or a negative errno. + */ +int __gpio_to_irq(unsigned gpio) +{ + struct gpio_chip *chip; + + chip = gpio_to_chip(gpio); + return chip->to_irq ? chip->to_irq(chip, gpio - chip->base) : -ENXIO; +} +EXPORT_SYMBOL_GPL(__gpio_to_irq); + + + +/* There's no value in making it easy to inline GPIO calls that may sleep. + * Common examples include ones connected to I2C or SPI chips. + */ + +int gpio_get_value_cansleep(unsigned gpio) +{ + struct gpio_chip *chip; + + might_sleep_if(extra_checks); + chip = gpio_to_chip(gpio); + return chip->get ? chip->get(chip, gpio - chip->base) : 0; +} +EXPORT_SYMBOL_GPL(gpio_get_value_cansleep); + +void gpio_set_value_cansleep(unsigned gpio, int value) +{ + struct gpio_chip *chip; + + might_sleep_if(extra_checks); + chip = gpio_to_chip(gpio); + chip->set(chip, gpio - chip->base, value); +} +EXPORT_SYMBOL_GPL(gpio_set_value_cansleep); + + +#ifdef CONFIG_DEBUG_FS + +static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + unsigned i; + unsigned gpio = chip->base; + struct gpio_desc *gdesc = &gpio_desc[gpio]; + int is_out; + + for (i = 0; i < chip->ngpio; i++, gpio++, gdesc++) { + if (!test_bit(FLAG_REQUESTED, &gdesc->flags)) + continue; + + is_out = test_bit(FLAG_IS_OUT, &gdesc->flags); + seq_printf(s, " gpio-%-3d (%-20.20s) %s %s", + gpio, gdesc->label, + is_out ? "out" : "in ", + chip->get + ? (chip->get(chip, i) ? "hi" : "lo") + : "? "); + + if (!is_out) { + int irq = gpio_to_irq(gpio); + struct irq_desc *desc = irq_to_desc(irq); + + /* This races with request_irq(), set_irq_type(), + * and set_irq_wake() ... but those are "rare". + * + * More significantly, trigger type flags aren't + * currently maintained by genirq. + */ + if (irq >= 0 && desc->action) { + char *trigger; + + switch (desc->status & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_NONE: + trigger = "(default)"; + break; + case IRQ_TYPE_EDGE_FALLING: + trigger = "edge-falling"; + break; + case IRQ_TYPE_EDGE_RISING: + trigger = "edge-rising"; + break; + case IRQ_TYPE_EDGE_BOTH: + trigger = "edge-both"; + break; + case IRQ_TYPE_LEVEL_HIGH: + trigger = "level-high"; + break; + case IRQ_TYPE_LEVEL_LOW: + trigger = "level-low"; + break; + default: + trigger = "?trigger?"; + break; + } + + seq_printf(s, " irq-%d %s%s", + irq, trigger, + (desc->status & IRQ_WAKEUP) + ? " wakeup" : ""); + } + } + + seq_printf(s, "\n"); + } +} + +static int gpiolib_show(struct seq_file *s, void *unused) +{ + struct gpio_chip *chip = NULL; + unsigned gpio; + int started = 0; + + /* REVISIT this isn't locked against gpio_chip removal ... */ + + for (gpio = 0; gpio_is_valid(gpio); gpio++) { + struct device *dev; + + if (chip == gpio_desc[gpio].chip) + continue; + chip = gpio_desc[gpio].chip; + if (!chip) + continue; + + seq_printf(s, "%sGPIOs %d-%d", + started ? "\n" : "", + chip->base, chip->base + chip->ngpio - 1); + dev = chip->dev; + if (dev) + seq_printf(s, ", %s/%s", + dev->bus ? dev->bus->name : "no-bus", + dev_name(dev)); + if (chip->label) + seq_printf(s, ", %s", chip->label); + if (chip->can_sleep) + seq_printf(s, ", can sleep"); + seq_printf(s, ":\n"); + + started = 1; + if (chip->dbg_show) + chip->dbg_show(s, chip); + else + gpiolib_dbg_show(s, chip); + } + return 0; +} + +static int gpiolib_open(struct inode *inode, struct file *file) +{ + return single_open(file, gpiolib_show, NULL); +} + +static const struct file_operations gpiolib_operations = { + .open = gpiolib_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __init gpiolib_debugfs_init(void) +{ + /* /sys/kernel/debug/gpio */ + (void) debugfs_create_file("gpio", S_IFREG | S_IRUGO, + NULL, NULL, &gpiolib_operations); + return 0; +} +subsys_initcall(gpiolib_debugfs_init); + +#endif /* DEBUG_FS */ diff --git a/drivers/gpio/langwell_gpio.c b/drivers/gpio/langwell_gpio.c new file mode 100644 index 00000000000..6c0ebbdc659 --- /dev/null +++ b/drivers/gpio/langwell_gpio.c @@ -0,0 +1,288 @@ +/* langwell_gpio.c Moorestown platform Langwell chip GPIO driver + * Copyright (c) 2008 - 2009, Intel Corporation. + * + * 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. + * + * 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. + */ + +/* Supports: + * Moorestown platform Langwell chip. + */ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/stddef.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <linux/gpio.h> + +struct lnw_gpio_register { + u32 GPLR[2]; + u32 GPDR[2]; + u32 GPSR[2]; + u32 GPCR[2]; + u32 GRER[2]; + u32 GFER[2]; + u32 GEDR[2]; +}; + +struct lnw_gpio { + struct gpio_chip chip; + struct lnw_gpio_register *reg_base; + spinlock_t lock; + unsigned irq_base; +}; + +static int lnw_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); + u8 reg = offset / 32; + void __iomem *gplr; + + gplr = (void __iomem *)(&lnw->reg_base->GPLR[reg]); + return readl(gplr) & BIT(offset % 32); +} + +static void lnw_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); + u8 reg = offset / 32; + void __iomem *gpsr, *gpcr; + + if (value) { + gpsr = (void __iomem *)(&lnw->reg_base->GPSR[reg]); + writel(BIT(offset % 32), gpsr); + } else { + gpcr = (void __iomem *)(&lnw->reg_base->GPCR[reg]); + writel(BIT(offset % 32), gpcr); + } +} + +static int lnw_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); + u8 reg = offset / 32; + u32 value; + unsigned long flags; + void __iomem *gpdr; + + gpdr = (void __iomem *)(&lnw->reg_base->GPDR[reg]); + spin_lock_irqsave(&lnw->lock, flags); + value = readl(gpdr); + value &= ~BIT(offset % 32); + writel(value, gpdr); + spin_unlock_irqrestore(&lnw->lock, flags); + return 0; +} + +static int lnw_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); + u8 reg = offset / 32; + unsigned long flags; + void __iomem *gpdr; + + lnw_gpio_set(chip, offset, value); + gpdr = (void __iomem *)(&lnw->reg_base->GPDR[reg]); + spin_lock_irqsave(&lnw->lock, flags); + value = readl(gpdr); + value |= BIT(offset % 32);; + writel(value, gpdr); + spin_unlock_irqrestore(&lnw->lock, flags); + return 0; +} + +static int lnw_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct lnw_gpio *lnw = container_of(chip, struct lnw_gpio, chip); + return lnw->irq_base + offset; +} + +static int lnw_irq_type(unsigned irq, unsigned type) +{ + struct lnw_gpio *lnw = get_irq_chip_data(irq); + u32 gpio = irq - lnw->irq_base; + u8 reg = gpio / 32; + unsigned long flags; + u32 value; + void __iomem *grer = (void __iomem *)(&lnw->reg_base->GRER[reg]); + void __iomem *gfer = (void __iomem *)(&lnw->reg_base->GFER[reg]); + + if (gpio >= lnw->chip.ngpio) + return -EINVAL; + spin_lock_irqsave(&lnw->lock, flags); + if (type & IRQ_TYPE_EDGE_RISING) + value = readl(grer) | BIT(gpio % 32); + else + value = readl(grer) & (~BIT(gpio % 32)); + writel(value, grer); + + if (type & IRQ_TYPE_EDGE_FALLING) + value = readl(gfer) | BIT(gpio % 32); + else + value = readl(gfer) & (~BIT(gpio % 32)); + writel(value, gfer); + spin_unlock_irqrestore(&lnw->lock, flags); + + return 0; +}; + +static void lnw_irq_unmask(unsigned irq) +{ +}; + +static void lnw_irq_mask(unsigned irq) +{ +}; + +static struct irq_chip lnw_irqchip = { + .name = "LNW-GPIO", + .mask = lnw_irq_mask, + .unmask = lnw_irq_unmask, + .set_type = lnw_irq_type, +}; + +static struct pci_device_id lnw_gpio_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080f) }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, lnw_gpio_ids); + +static void lnw_irq_handler(unsigned irq, struct irq_desc *desc) +{ + struct lnw_gpio *lnw = (struct lnw_gpio *)get_irq_data(irq); + u32 reg, gpio; + void __iomem *gedr; + u32 gedr_v; + + /* check GPIO controller to check which pin triggered the interrupt */ + for (reg = 0; reg < lnw->chip.ngpio / 32; reg++) { + gedr = (void __iomem *)(&lnw->reg_base->GEDR[reg]); + gedr_v = readl(gedr); + if (!gedr_v) + continue; + for (gpio = reg*32; gpio < reg*32+32; gpio++) + if (gedr_v & BIT(gpio % 32)) { + pr_debug("pin %d triggered\n", gpio); + generic_handle_irq(lnw->irq_base + gpio); + } + /* clear the edge detect status bit */ + writel(gedr_v, gedr); + } + desc->chip->eoi(irq); +} + +static int __devinit lnw_gpio_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + void *base; + int i; + resource_size_t start, len; + struct lnw_gpio *lnw; + u32 irq_base; + u32 gpio_base; + int retval = 0; + + retval = pci_enable_device(pdev); + if (retval) + goto done; + + retval = pci_request_regions(pdev, "langwell_gpio"); + if (retval) { + dev_err(&pdev->dev, "error requesting resources\n"); + goto err2; + } + /* get the irq_base from bar1 */ + start = pci_resource_start(pdev, 1); + len = pci_resource_len(pdev, 1); + base = ioremap_nocache(start, len); + if (!base) { + dev_err(&pdev->dev, "error mapping bar1\n"); + goto err3; + } + irq_base = *(u32 *)base; + gpio_base = *((u32 *)base + 1); + /* release the IO mapping, since we already get the info from bar1 */ + iounmap(base); + /* get the register base from bar0 */ + start = pci_resource_start(pdev, 0); + len = pci_resource_len(pdev, 0); + base = ioremap_nocache(start, len); + if (!base) { + dev_err(&pdev->dev, "error mapping bar0\n"); + retval = -EFAULT; + goto err3; + } + + lnw = kzalloc(sizeof(struct lnw_gpio), GFP_KERNEL); + if (!lnw) { + dev_err(&pdev->dev, "can't allocate langwell_gpio chip data\n"); + retval = -ENOMEM; + goto err4; + } + lnw->reg_base = base; + lnw->irq_base = irq_base; + lnw->chip.label = dev_name(&pdev->dev); + lnw->chip.direction_input = lnw_gpio_direction_input; + lnw->chip.direction_output = lnw_gpio_direction_output; + lnw->chip.get = lnw_gpio_get; + lnw->chip.set = lnw_gpio_set; + lnw->chip.to_irq = lnw_gpio_to_irq; + lnw->chip.base = gpio_base; + lnw->chip.ngpio = 64; + lnw->chip.can_sleep = 0; + pci_set_drvdata(pdev, lnw); + retval = gpiochip_add(&lnw->chip); + if (retval) { + dev_err(&pdev->dev, "langwell gpiochip_add error %d\n", retval); + goto err5; + } + set_irq_data(pdev->irq, lnw); + set_irq_chained_handler(pdev->irq, lnw_irq_handler); + for (i = 0; i < lnw->chip.ngpio; i++) { + set_irq_chip_and_handler_name(i + lnw->irq_base, &lnw_irqchip, + handle_simple_irq, "demux"); + set_irq_chip_data(i + lnw->irq_base, lnw); + } + + spin_lock_init(&lnw->lock); + goto done; +err5: + kfree(lnw); +err4: + iounmap(base); +err3: + pci_release_regions(pdev); +err2: + pci_disable_device(pdev); +done: + return retval; +} + +static struct pci_driver lnw_gpio_driver = { + .name = "langwell_gpio", + .id_table = lnw_gpio_ids, + .probe = lnw_gpio_probe, +}; + +static int __init lnw_gpio_init(void) +{ + return pci_register_driver(&lnw_gpio_driver); +} + +device_initcall(lnw_gpio_init); diff --git a/drivers/gpio/max7301.c b/drivers/gpio/max7301.c new file mode 100644 index 00000000000..480956f1ca5 --- /dev/null +++ b/drivers/gpio/max7301.c @@ -0,0 +1,342 @@ +/** + * drivers/gpio/max7301.c + * + * Copyright (C) 2006 Juergen Beisert, Pengutronix + * Copyright (C) 2008 Guennadi Liakhovetski, Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * The Maxim's MAX7301 device is an SPI driven GPIO expander. There are + * 28 GPIOs. 8 of them can trigger an interrupt. See datasheet for more + * details + * Note: + * - DIN must be stable at the rising edge of clock. + * - when writing: + * - always clock in 16 clocks at once + * - at DIN: D15 first, D0 last + * - D0..D7 = databyte, D8..D14 = commandbyte + * - D15 = low -> write command + * - when reading + * - always clock in 16 clocks at once + * - at DIN: D15 first, D0 last + * - D0..D7 = dummy, D8..D14 = register address + * - D15 = high -> read command + * - raise CS and assert it again + * - always clock in 16 clocks at once + * - at DOUT: D15 first, D0 last + * - D0..D7 contains the data from the first cycle + * + * The driver exports a standard gpiochip interface + */ + +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/spi/spi.h> +#include <linux/spi/max7301.h> +#include <linux/gpio.h> + +#define DRIVER_NAME "max7301" + +/* + * Pin configurations, see MAX7301 datasheet page 6 + */ +#define PIN_CONFIG_MASK 0x03 +#define PIN_CONFIG_IN_PULLUP 0x03 +#define PIN_CONFIG_IN_WO_PULLUP 0x02 +#define PIN_CONFIG_OUT 0x01 + +#define PIN_NUMBER 28 + + +/* + * Some registers must be read back to modify. + * To save time we cache them here in memory + */ +struct max7301 { + struct mutex lock; + u8 port_config[8]; /* field 0 is unused */ + u32 out_level; /* cached output levels */ + struct gpio_chip chip; + struct spi_device *spi; +}; + +/** + * max7301_write - Write a new register content + * @spi: The SPI device + * @reg: Register offset + * @val: Value to write + * + * A write to the MAX7301 means one message with one transfer + * + * Returns 0 if successful or a negative value on error + */ +static int max7301_write(struct spi_device *spi, unsigned int reg, unsigned int val) +{ + u16 word = ((reg & 0x7F) << 8) | (val & 0xFF); + return spi_write(spi, (const u8 *)&word, sizeof(word)); +} + +/** + * max7301_read - Read back register content + * @spi: The SPI device + * @reg: Register offset + * + * A read from the MAX7301 means two transfers; here, one message each + * + * Returns positive 8 bit value from device if successful or a + * negative value on error + */ +static int max7301_read(struct spi_device *spi, unsigned int reg) +{ + int ret; + u16 word; + + word = 0x8000 | (reg << 8); + ret = spi_write(spi, (const u8 *)&word, sizeof(word)); + if (ret) + return ret; + /* + * This relies on the fact, that a transfer with NULL tx_buf shifts out + * zero bytes (=NOOP for MAX7301) + */ + ret = spi_read(spi, (u8 *)&word, sizeof(word)); + if (ret) + return ret; + return word & 0xff; +} + +static int max7301_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct max7301 *ts = container_of(chip, struct max7301, chip); + u8 *config; + int ret; + + /* First 4 pins are unused in the controller */ + offset += 4; + + config = &ts->port_config[offset >> 2]; + + mutex_lock(&ts->lock); + + /* Standard GPIO API doesn't support pull-ups, has to be extended. + * Hard-coding no pollup for now. */ + *config = (*config & ~(3 << (offset & 3))) | (1 << (offset & 3)); + + ret = max7301_write(ts->spi, 0x08 + (offset >> 2), *config); + + mutex_unlock(&ts->lock); + + return ret; +} + +static int __max7301_set(struct max7301 *ts, unsigned offset, int value) +{ + if (value) { + ts->out_level |= 1 << offset; + return max7301_write(ts->spi, 0x20 + offset, 0x01); + } else { + ts->out_level &= ~(1 << offset); + return max7301_write(ts->spi, 0x20 + offset, 0x00); + } +} + +static int max7301_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct max7301 *ts = container_of(chip, struct max7301, chip); + u8 *config; + int ret; + + /* First 4 pins are unused in the controller */ + offset += 4; + + config = &ts->port_config[offset >> 2]; + + mutex_lock(&ts->lock); + + *config = (*config & ~(3 << (offset & 3))) | (1 << (offset & 3)); + + ret = __max7301_set(ts, offset, value); + + if (!ret) + ret = max7301_write(ts->spi, 0x08 + (offset >> 2), *config); + + mutex_unlock(&ts->lock); + + return ret; +} + +static int max7301_get(struct gpio_chip *chip, unsigned offset) +{ + struct max7301 *ts = container_of(chip, struct max7301, chip); + int config, level = -EINVAL; + + /* First 4 pins are unused in the controller */ + offset += 4; + + mutex_lock(&ts->lock); + + config = (ts->port_config[offset >> 2] >> ((offset & 3) * 2)) & 3; + + switch (config) { + case 1: + /* Output: return cached level */ + level = !!(ts->out_level & (1 << offset)); + break; + case 2: + case 3: + /* Input: read out */ + level = max7301_read(ts->spi, 0x20 + offset) & 0x01; + } + mutex_unlock(&ts->lock); + + return level; +} + +static void max7301_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct max7301 *ts = container_of(chip, struct max7301, chip); + + /* First 4 pins are unused in the controller */ + offset += 4; + + mutex_lock(&ts->lock); + + __max7301_set(ts, offset, value); + + mutex_unlock(&ts->lock); +} + +static int __devinit max7301_probe(struct spi_device *spi) +{ + struct max7301 *ts; + struct max7301_platform_data *pdata; + int i, ret; + + pdata = spi->dev.platform_data; + if (!pdata || !pdata->base) { + dev_dbg(&spi->dev, "incorrect or missing platform data\n"); + return -EINVAL; + } + + /* + * bits_per_word cannot be configured in platform data + */ + spi->bits_per_word = 16; + + ret = spi_setup(spi); + if (ret < 0) + return ret; + + ts = kzalloc(sizeof(struct max7301), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + mutex_init(&ts->lock); + + dev_set_drvdata(&spi->dev, ts); + + /* Power up the chip and disable IRQ output */ + max7301_write(spi, 0x04, 0x01); + + ts->spi = spi; + + ts->chip.label = DRIVER_NAME, + + ts->chip.direction_input = max7301_direction_input; + ts->chip.get = max7301_get; + ts->chip.direction_output = max7301_direction_output; + ts->chip.set = max7301_set; + + ts->chip.base = pdata->base; + ts->chip.ngpio = PIN_NUMBER; + ts->chip.can_sleep = 1; + ts->chip.dev = &spi->dev; + ts->chip.owner = THIS_MODULE; + + /* + * tristate all pins in hardware and cache the + * register values for later use. + */ + for (i = 1; i < 8; i++) { + int j; + /* 0xAA means input with internal pullup disabled */ + max7301_write(spi, 0x08 + i, 0xAA); + ts->port_config[i] = 0xAA; + for (j = 0; j < 4; j++) { + int offset = (i - 1) * 4 + j; + ret = max7301_direction_input(&ts->chip, offset); + if (ret) + goto exit_destroy; + } + } + + ret = gpiochip_add(&ts->chip); + if (ret) + goto exit_destroy; + + return ret; + +exit_destroy: + dev_set_drvdata(&spi->dev, NULL); + mutex_destroy(&ts->lock); + kfree(ts); + return ret; +} + +static int __devexit max7301_remove(struct spi_device *spi) +{ + struct max7301 *ts; + int ret; + + ts = dev_get_drvdata(&spi->dev); + if (ts == NULL) + return -ENODEV; + + dev_set_drvdata(&spi->dev, NULL); + + /* Power down the chip and disable IRQ output */ + max7301_write(spi, 0x04, 0x00); + + ret = gpiochip_remove(&ts->chip); + if (!ret) { + mutex_destroy(&ts->lock); + kfree(ts); + } else + dev_err(&spi->dev, "Failed to remove the GPIO controller: %d\n", + ret); + + return ret; +} + +static struct spi_driver max7301_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = max7301_probe, + .remove = __devexit_p(max7301_remove), +}; + +static int __init max7301_init(void) +{ + return spi_register_driver(&max7301_driver); +} +/* register after spi postcore initcall and before + * subsys initcalls that may rely on these GPIOs + */ +subsys_initcall(max7301_init); + +static void __exit max7301_exit(void) +{ + spi_unregister_driver(&max7301_driver); +} +module_exit(max7301_exit); + +MODULE_AUTHOR("Juergen Beisert"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MAX7301 SPI based GPIO-Expander"); +MODULE_ALIAS("spi:" DRIVER_NAME); diff --git a/drivers/gpio/max732x.c b/drivers/gpio/max732x.c new file mode 100644 index 00000000000..f7868243af8 --- /dev/null +++ b/drivers/gpio/max732x.c @@ -0,0 +1,390 @@ +/* + * max732x.c - I2C Port Expander with 8/16 I/O + * + * Copyright (C) 2007 Marvell International Ltd. + * Copyright (C) 2008 Jack Ren <jack.ren@marvell.com> + * Copyright (C) 2008 Eric Miao <eric.miao@marvell.com> + * + * Derived from drivers/gpio/pca953x.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; version 2 of the License. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/gpio.h> + +#include <linux/i2c.h> +#include <linux/i2c/max732x.h> + + +/* + * Each port of MAX732x (including MAX7319) falls into one of the + * following three types: + * + * - Push Pull Output + * - Input + * - Open Drain I/O + * + * designated by 'O', 'I' and 'P' individually according to MAXIM's + * datasheets. + * + * There are two groups of I/O ports, each group usually includes + * up to 8 I/O ports, and is accessed by a specific I2C address: + * + * - Group A : by I2C address 0b'110xxxx + * - Group B : by I2C address 0b'101xxxx + * + * where 'xxxx' is decided by the connections of pin AD2/AD0. The + * address used also affects the initial state of output signals. + * + * Within each group of ports, there are five known combinations of + * I/O ports: 4I4O, 4P4O, 8I, 8P, 8O, see the definitions below for + * the detailed organization of these ports. + * + * GPIO numbers start from 'gpio_base + 0' to 'gpio_base + 8/16', + * and GPIOs from GROUP_A are numbered before those from GROUP_B + * (if there are two groups). + * + * NOTE: MAX7328/MAX7329 are drop-in replacements for PCF8574/a, so + * they are not supported by this driver. + */ + +#define PORT_NONE 0x0 /* '/' No Port */ +#define PORT_OUTPUT 0x1 /* 'O' Push-Pull, Output Only */ +#define PORT_INPUT 0x2 /* 'I' Input Only */ +#define PORT_OPENDRAIN 0x3 /* 'P' Open-Drain, I/O */ + +#define IO_4I4O 0x5AA5 /* O7 O6 I5 I4 I3 I2 O1 O0 */ +#define IO_4P4O 0x5FF5 /* O7 O6 P5 P4 P3 P2 O1 O0 */ +#define IO_8I 0xAAAA /* I7 I6 I5 I4 I3 I2 I1 I0 */ +#define IO_8P 0xFFFF /* P7 P6 P5 P4 P3 P2 P1 P0 */ +#define IO_8O 0x5555 /* O7 O6 O5 O4 O3 O2 O1 O0 */ + +#define GROUP_A(x) ((x) & 0xffff) /* I2C Addr: 0b'110xxxx */ +#define GROUP_B(x) ((x) << 16) /* I2C Addr: 0b'101xxxx */ + +static const struct i2c_device_id max732x_id[] = { + { "max7319", GROUP_A(IO_8I) }, + { "max7320", GROUP_B(IO_8O) }, + { "max7321", GROUP_A(IO_8P) }, + { "max7322", GROUP_A(IO_4I4O) }, + { "max7323", GROUP_A(IO_4P4O) }, + { "max7324", GROUP_A(IO_8I) | GROUP_B(IO_8O) }, + { "max7325", GROUP_A(IO_8P) | GROUP_B(IO_8O) }, + { "max7326", GROUP_A(IO_4I4O) | GROUP_B(IO_8O) }, + { "max7327", GROUP_A(IO_4P4O) | GROUP_B(IO_8O) }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, max732x_id); + +struct max732x_chip { + struct gpio_chip gpio_chip; + + struct i2c_client *client; /* "main" client */ + struct i2c_client *client_dummy; + struct i2c_client *client_group_a; + struct i2c_client *client_group_b; + + unsigned int mask_group_a; + unsigned int dir_input; + unsigned int dir_output; + + struct mutex lock; + uint8_t reg_out[2]; +}; + +static int max732x_write(struct max732x_chip *chip, int group_a, uint8_t val) +{ + struct i2c_client *client; + int ret; + + client = group_a ? chip->client_group_a : chip->client_group_b; + ret = i2c_smbus_write_byte(client, val); + if (ret < 0) { + dev_err(&client->dev, "failed writing\n"); + return ret; + } + + return 0; +} + +static int max732x_read(struct max732x_chip *chip, int group_a, uint8_t *val) +{ + struct i2c_client *client; + int ret; + + client = group_a ? chip->client_group_a : chip->client_group_b; + ret = i2c_smbus_read_byte(client); + if (ret < 0) { + dev_err(&client->dev, "failed reading\n"); + return ret; + } + + *val = (uint8_t)ret; + return 0; +} + +static inline int is_group_a(struct max732x_chip *chip, unsigned off) +{ + return (1u << off) & chip->mask_group_a; +} + +static int max732x_gpio_get_value(struct gpio_chip *gc, unsigned off) +{ + struct max732x_chip *chip; + uint8_t reg_val; + int ret; + + chip = container_of(gc, struct max732x_chip, gpio_chip); + + ret = max732x_read(chip, is_group_a(chip, off), ®_val); + if (ret < 0) + return 0; + + return reg_val & (1u << (off & 0x7)); +} + +static void max732x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val) +{ + struct max732x_chip *chip; + uint8_t reg_out, mask = 1u << (off & 0x7); + int ret; + + chip = container_of(gc, struct max732x_chip, gpio_chip); + + mutex_lock(&chip->lock); + + reg_out = (off > 7) ? chip->reg_out[1] : chip->reg_out[0]; + reg_out = (val) ? reg_out | mask : reg_out & ~mask; + + ret = max732x_write(chip, is_group_a(chip, off), reg_out); + if (ret < 0) + goto out; + + /* update the shadow register then */ + if (off > 7) + chip->reg_out[1] = reg_out; + else + chip->reg_out[0] = reg_out; +out: + mutex_unlock(&chip->lock); +} + +static int max732x_gpio_direction_input(struct gpio_chip *gc, unsigned off) +{ + struct max732x_chip *chip; + unsigned int mask = 1u << off; + + chip = container_of(gc, struct max732x_chip, gpio_chip); + + if ((mask & chip->dir_input) == 0) { + dev_dbg(&chip->client->dev, "%s port %d is output only\n", + chip->client->name, off); + return -EACCES; + } + + return 0; +} + +static int max732x_gpio_direction_output(struct gpio_chip *gc, + unsigned off, int val) +{ + struct max732x_chip *chip; + unsigned int mask = 1u << off; + + chip = container_of(gc, struct max732x_chip, gpio_chip); + + if ((mask & chip->dir_output) == 0) { + dev_dbg(&chip->client->dev, "%s port %d is input only\n", + chip->client->name, off); + return -EACCES; + } + + max732x_gpio_set_value(gc, off, val); + return 0; +} + +static int __devinit max732x_setup_gpio(struct max732x_chip *chip, + const struct i2c_device_id *id, + unsigned gpio_start) +{ + struct gpio_chip *gc = &chip->gpio_chip; + uint32_t id_data = id->driver_data; + int i, port = 0; + + for (i = 0; i < 16; i++, id_data >>= 2) { + unsigned int mask = 1 << port; + + switch (id_data & 0x3) { + case PORT_OUTPUT: + chip->dir_output |= mask; + break; + case PORT_INPUT: + chip->dir_input |= mask; + break; + case PORT_OPENDRAIN: + chip->dir_output |= mask; + chip->dir_input |= mask; + break; + default: + continue; + } + + if (i < 8) + chip->mask_group_a |= mask; + port++; + } + + if (chip->dir_input) + gc->direction_input = max732x_gpio_direction_input; + if (chip->dir_output) { + gc->direction_output = max732x_gpio_direction_output; + gc->set = max732x_gpio_set_value; + } + gc->get = max732x_gpio_get_value; + gc->can_sleep = 1; + + gc->base = gpio_start; + gc->ngpio = port; + gc->label = chip->client->name; + gc->owner = THIS_MODULE; + + return port; +} + +static int __devinit max732x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct max732x_platform_data *pdata; + struct max732x_chip *chip; + struct i2c_client *c; + uint16_t addr_a, addr_b; + int ret, nr_port; + + pdata = client->dev.platform_data; + if (pdata == NULL) { + dev_dbg(&client->dev, "no platform data\n"); + return -EINVAL; + } + + chip = kzalloc(sizeof(struct max732x_chip), GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + chip->client = client; + + nr_port = max732x_setup_gpio(chip, id, pdata->gpio_base); + + addr_a = (client->addr & 0x0f) | 0x60; + addr_b = (client->addr & 0x0f) | 0x50; + + switch (client->addr & 0x70) { + case 0x60: + chip->client_group_a = client; + if (nr_port > 7) { + c = i2c_new_dummy(client->adapter, addr_b); + chip->client_group_b = chip->client_dummy = c; + } + break; + case 0x50: + chip->client_group_b = client; + if (nr_port > 7) { + c = i2c_new_dummy(client->adapter, addr_a); + chip->client_group_a = chip->client_dummy = c; + } + break; + default: + dev_err(&client->dev, "invalid I2C address specified %02x\n", + client->addr); + ret = -EINVAL; + goto out_failed; + } + + mutex_init(&chip->lock); + + max732x_read(chip, is_group_a(chip, 0), &chip->reg_out[0]); + if (nr_port > 7) + max732x_read(chip, is_group_a(chip, 8), &chip->reg_out[1]); + + ret = gpiochip_add(&chip->gpio_chip); + if (ret) + goto out_failed; + + if (pdata->setup) { + ret = pdata->setup(client, chip->gpio_chip.base, + chip->gpio_chip.ngpio, pdata->context); + if (ret < 0) + dev_warn(&client->dev, "setup failed, %d\n", ret); + } + + i2c_set_clientdata(client, chip); + return 0; + +out_failed: + kfree(chip); + return ret; +} + +static int __devexit max732x_remove(struct i2c_client *client) +{ + struct max732x_platform_data *pdata = client->dev.platform_data; + struct max732x_chip *chip = i2c_get_clientdata(client); + int ret; + + if (pdata->teardown) { + ret = pdata->teardown(client, chip->gpio_chip.base, + chip->gpio_chip.ngpio, pdata->context); + if (ret < 0) { + dev_err(&client->dev, "%s failed, %d\n", + "teardown", ret); + return ret; + } + } + + ret = gpiochip_remove(&chip->gpio_chip); + if (ret) { + dev_err(&client->dev, "%s failed, %d\n", + "gpiochip_remove()", ret); + return ret; + } + + /* unregister any dummy i2c_client */ + if (chip->client_dummy) + i2c_unregister_device(chip->client_dummy); + + kfree(chip); + return 0; +} + +static struct i2c_driver max732x_driver = { + .driver = { + .name = "max732x", + .owner = THIS_MODULE, + }, + .probe = max732x_probe, + .remove = __devexit_p(max732x_remove), + .id_table = max732x_id, +}; + +static int __init max732x_init(void) +{ + return i2c_add_driver(&max732x_driver); +} +/* register after i2c postcore initcall and before + * subsys initcalls that may rely on these GPIOs + */ +subsys_initcall(max732x_init); + +static void __exit max732x_exit(void) +{ + i2c_del_driver(&max732x_driver); +} +module_exit(max732x_exit); + +MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"); +MODULE_DESCRIPTION("GPIO expander driver for MAX732X"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/mc33880.c b/drivers/gpio/mc33880.c new file mode 100644 index 00000000000..e7d01bd8fdb --- /dev/null +++ b/drivers/gpio/mc33880.c @@ -0,0 +1,196 @@ +/* + * mc33880.c MC33880 high-side/low-side switch GPIO driver + * Copyright (c) 2009 Intel Corporation + * + * 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. + * + * 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. + */ + +/* Supports: + * Freescale MC33880 high-side/low-side switch + */ + +#include <linux/init.h> +#include <linux/mutex.h> +#include <linux/spi/spi.h> +#include <linux/spi/mc33880.h> +#include <linux/gpio.h> + +#define DRIVER_NAME "mc33880" + +/* + * Pin configurations, see MAX7301 datasheet page 6 + */ +#define PIN_CONFIG_MASK 0x03 +#define PIN_CONFIG_IN_PULLUP 0x03 +#define PIN_CONFIG_IN_WO_PULLUP 0x02 +#define PIN_CONFIG_OUT 0x01 + +#define PIN_NUMBER 8 + + +/* + * Some registers must be read back to modify. + * To save time we cache them here in memory + */ +struct mc33880 { + struct mutex lock; /* protect from simultanous accesses */ + u8 port_config; + struct gpio_chip chip; + struct spi_device *spi; +}; + +static int mc33880_write_config(struct mc33880 *mc) +{ + return spi_write(mc->spi, &mc->port_config, sizeof(mc->port_config)); +} + + +static int __mc33880_set(struct mc33880 *mc, unsigned offset, int value) +{ + if (value) + mc->port_config |= 1 << offset; + else + mc->port_config &= ~(1 << offset); + + return mc33880_write_config(mc); +} + + +static void mc33880_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct mc33880 *mc = container_of(chip, struct mc33880, chip); + + mutex_lock(&mc->lock); + + __mc33880_set(mc, offset, value); + + mutex_unlock(&mc->lock); +} + +static int __devinit mc33880_probe(struct spi_device *spi) +{ + struct mc33880 *mc; + struct mc33880_platform_data *pdata; + int ret; + + pdata = spi->dev.platform_data; + if (!pdata || !pdata->base) { + dev_dbg(&spi->dev, "incorrect or missing platform data\n"); + return -EINVAL; + } + + /* + * bits_per_word cannot be configured in platform data + */ + spi->bits_per_word = 8; + + ret = spi_setup(spi); + if (ret < 0) + return ret; + + mc = kzalloc(sizeof(struct mc33880), GFP_KERNEL); + if (!mc) + return -ENOMEM; + + mutex_init(&mc->lock); + + dev_set_drvdata(&spi->dev, mc); + + mc->spi = spi; + + mc->chip.label = DRIVER_NAME, + mc->chip.set = mc33880_set; + mc->chip.base = pdata->base; + mc->chip.ngpio = PIN_NUMBER; + mc->chip.can_sleep = 1; + mc->chip.dev = &spi->dev; + mc->chip.owner = THIS_MODULE; + + mc->port_config = 0x00; + /* write twice, because during initialisation the first setting + * is just for testing SPI communication, and the second is the + * "real" configuration + */ + ret = mc33880_write_config(mc); + mc->port_config = 0x00; + if (!ret) + ret = mc33880_write_config(mc); + + if (ret) { + printk(KERN_ERR "Failed writing to " DRIVER_NAME ": %d\n", ret); + goto exit_destroy; + } + + ret = gpiochip_add(&mc->chip); + if (ret) + goto exit_destroy; + + return ret; + +exit_destroy: + dev_set_drvdata(&spi->dev, NULL); + mutex_destroy(&mc->lock); + kfree(mc); + return ret; +} + +static int mc33880_remove(struct spi_device *spi) +{ + struct mc33880 *mc; + int ret; + + mc = dev_get_drvdata(&spi->dev); + if (mc == NULL) + return -ENODEV; + + dev_set_drvdata(&spi->dev, NULL); + + ret = gpiochip_remove(&mc->chip); + if (!ret) { + mutex_destroy(&mc->lock); + kfree(mc); + } else + dev_err(&spi->dev, "Failed to remove the GPIO controller: %d\n", + ret); + + return ret; +} + +static struct spi_driver mc33880_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = mc33880_probe, + .remove = __devexit_p(mc33880_remove), +}; + +static int __init mc33880_init(void) +{ + return spi_register_driver(&mc33880_driver); +} +/* register after spi postcore initcall and before + * subsys initcalls that may rely on these GPIOs + */ +subsys_initcall(mc33880_init); + +static void __exit mc33880_exit(void) +{ + spi_unregister_driver(&mc33880_driver); +} +module_exit(mc33880_exit); + +MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/gpio/mcp23s08.c b/drivers/gpio/mcp23s08.c new file mode 100644 index 00000000000..cd651ec8d03 --- /dev/null +++ b/drivers/gpio/mcp23s08.c @@ -0,0 +1,434 @@ +/* + * mcp23s08.c - SPI gpio expander driver + */ + +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/workqueue.h> +#include <linux/mutex.h> +#include <linux/gpio.h> +#include <linux/spi/spi.h> +#include <linux/spi/mcp23s08.h> + + +/* Registers are all 8 bits wide. + * + * The mcp23s17 has twice as many bits, and can be configured to work + * with either 16 bit registers or with two adjacent 8 bit banks. + * + * Also, there are I2C versions of both chips. + */ +#define MCP_IODIR 0x00 /* init/reset: all ones */ +#define MCP_IPOL 0x01 +#define MCP_GPINTEN 0x02 +#define MCP_DEFVAL 0x03 +#define MCP_INTCON 0x04 +#define MCP_IOCON 0x05 +# define IOCON_SEQOP (1 << 5) +# define IOCON_HAEN (1 << 3) +# define IOCON_ODR (1 << 2) +# define IOCON_INTPOL (1 << 1) +#define MCP_GPPU 0x06 +#define MCP_INTF 0x07 +#define MCP_INTCAP 0x08 +#define MCP_GPIO 0x09 +#define MCP_OLAT 0x0a + +struct mcp23s08 { + struct spi_device *spi; + u8 addr; + + u8 cache[11]; + /* lock protects the cached values */ + struct mutex lock; + + struct gpio_chip chip; + + struct work_struct work; +}; + +/* A given spi_device can represent up to four mcp23s08 chips + * sharing the same chipselect but using different addresses + * (e.g. chips #0 and #3 might be populated, but not #1 or $2). + * Driver data holds all the per-chip data. + */ +struct mcp23s08_driver_data { + unsigned ngpio; + struct mcp23s08 *mcp[4]; + struct mcp23s08 chip[]; +}; + +static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg) +{ + u8 tx[2], rx[1]; + int status; + + tx[0] = mcp->addr | 0x01; + tx[1] = reg; + status = spi_write_then_read(mcp->spi, tx, sizeof tx, rx, sizeof rx); + return (status < 0) ? status : rx[0]; +} + +static int mcp23s08_write(struct mcp23s08 *mcp, unsigned reg, u8 val) +{ + u8 tx[3]; + + tx[0] = mcp->addr; + tx[1] = reg; + tx[2] = val; + return spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0); +} + +static int +mcp23s08_read_regs(struct mcp23s08 *mcp, unsigned reg, u8 *vals, unsigned n) +{ + u8 tx[2]; + + if ((n + reg) > sizeof mcp->cache) + return -EINVAL; + tx[0] = mcp->addr | 0x01; + tx[1] = reg; + return spi_write_then_read(mcp->spi, tx, sizeof tx, vals, n); +} + +/*----------------------------------------------------------------------*/ + +static int mcp23s08_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); + int status; + + mutex_lock(&mcp->lock); + mcp->cache[MCP_IODIR] |= (1 << offset); + status = mcp23s08_write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); + mutex_unlock(&mcp->lock); + return status; +} + +static int mcp23s08_get(struct gpio_chip *chip, unsigned offset) +{ + struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); + int status; + + mutex_lock(&mcp->lock); + + /* REVISIT reading this clears any IRQ ... */ + status = mcp23s08_read(mcp, MCP_GPIO); + if (status < 0) + status = 0; + else { + mcp->cache[MCP_GPIO] = status; + status = !!(status & (1 << offset)); + } + mutex_unlock(&mcp->lock); + return status; +} + +static int __mcp23s08_set(struct mcp23s08 *mcp, unsigned mask, int value) +{ + u8 olat = mcp->cache[MCP_OLAT]; + + if (value) + olat |= mask; + else + olat &= ~mask; + mcp->cache[MCP_OLAT] = olat; + return mcp23s08_write(mcp, MCP_OLAT, olat); +} + +static void mcp23s08_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); + u8 mask = 1 << offset; + + mutex_lock(&mcp->lock); + __mcp23s08_set(mcp, mask, value); + mutex_unlock(&mcp->lock); +} + +static int +mcp23s08_direction_output(struct gpio_chip *chip, unsigned offset, int value) +{ + struct mcp23s08 *mcp = container_of(chip, struct mcp23s08, chip); + u8 mask = 1 << offset; + int status; + + mutex_lock(&mcp->lock); + status = __mcp23s08_set(mcp, mask, value); + if (status == 0) { + mcp->cache[MCP_IODIR] &= ~mask; + status = mcp23s08_write(mcp, MCP_IODIR, mcp->cache[MCP_IODIR]); + } + mutex_unlock(&mcp->lock); + return status; +} + +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_DEBUG_FS + +#include <linux/seq_file.h> + +/* + * This shows more info than the generic gpio dump code: + * pullups, deglitching, open drain drive. + */ +static void mcp23s08_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + struct mcp23s08 *mcp; + char bank; + int t; + unsigned mask; + + mcp = container_of(chip, struct mcp23s08, chip); + + /* NOTE: we only handle one bank for now ... */ + bank = '0' + ((mcp->addr >> 1) & 0x3); + + mutex_lock(&mcp->lock); + t = mcp23s08_read_regs(mcp, 0, mcp->cache, sizeof mcp->cache); + if (t < 0) { + seq_printf(s, " I/O ERROR %d\n", t); + goto done; + } + + for (t = 0, mask = 1; t < 8; t++, mask <<= 1) { + const char *label; + + label = gpiochip_is_requested(chip, t); + if (!label) + continue; + + seq_printf(s, " gpio-%-3d P%c.%d (%-12s) %s %s %s", + chip->base + t, bank, t, label, + (mcp->cache[MCP_IODIR] & mask) ? "in " : "out", + (mcp->cache[MCP_GPIO] & mask) ? "hi" : "lo", + (mcp->cache[MCP_GPPU] & mask) ? " " : "up"); + /* NOTE: ignoring the irq-related registers */ + seq_printf(s, "\n"); + } +done: + mutex_unlock(&mcp->lock); +} + +#else +#define mcp23s08_dbg_show NULL +#endif + +/*----------------------------------------------------------------------*/ + +static int mcp23s08_probe_one(struct spi_device *spi, unsigned addr, + unsigned base, unsigned pullups) +{ + struct mcp23s08_driver_data *data = spi_get_drvdata(spi); + struct mcp23s08 *mcp = data->mcp[addr]; + int status; + int do_update = 0; + + mutex_init(&mcp->lock); + + mcp->spi = spi; + mcp->addr = 0x40 | (addr << 1); + + mcp->chip.label = "mcp23s08", + + mcp->chip.direction_input = mcp23s08_direction_input; + mcp->chip.get = mcp23s08_get; + mcp->chip.direction_output = mcp23s08_direction_output; + mcp->chip.set = mcp23s08_set; + mcp->chip.dbg_show = mcp23s08_dbg_show; + + mcp->chip.base = base; + mcp->chip.ngpio = 8; + mcp->chip.can_sleep = 1; + mcp->chip.dev = &spi->dev; + mcp->chip.owner = THIS_MODULE; + + /* verify MCP_IOCON.SEQOP = 0, so sequential reads work, + * and MCP_IOCON.HAEN = 1, so we work with all chips. + */ + status = mcp23s08_read(mcp, MCP_IOCON); + if (status < 0) + goto fail; + if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN)) { + status &= ~IOCON_SEQOP; + status |= IOCON_HAEN; + status = mcp23s08_write(mcp, MCP_IOCON, (u8) status); + if (status < 0) + goto fail; + } + + /* configure ~100K pullups */ + status = mcp23s08_write(mcp, MCP_GPPU, pullups); + if (status < 0) + goto fail; + + status = mcp23s08_read_regs(mcp, 0, mcp->cache, sizeof mcp->cache); + if (status < 0) + goto fail; + + /* disable inverter on input */ + if (mcp->cache[MCP_IPOL] != 0) { + mcp->cache[MCP_IPOL] = 0; + do_update = 1; + } + + /* disable irqs */ + if (mcp->cache[MCP_GPINTEN] != 0) { + mcp->cache[MCP_GPINTEN] = 0; + do_update = 1; + } + + if (do_update) { + u8 tx[4]; + + tx[0] = mcp->addr; + tx[1] = MCP_IPOL; + memcpy(&tx[2], &mcp->cache[MCP_IPOL], sizeof(tx) - 2); + status = spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0); + if (status < 0) + goto fail; + } + + status = gpiochip_add(&mcp->chip); +fail: + if (status < 0) + dev_dbg(&spi->dev, "can't setup chip %d, --> %d\n", + addr, status); + return status; +} + +static int mcp23s08_probe(struct spi_device *spi) +{ + struct mcp23s08_platform_data *pdata; + unsigned addr; + unsigned chips = 0; + struct mcp23s08_driver_data *data; + int status; + unsigned base; + + pdata = spi->dev.platform_data; + if (!pdata || !gpio_is_valid(pdata->base)) { + dev_dbg(&spi->dev, "invalid or missing platform data\n"); + return -EINVAL; + } + + for (addr = 0; addr < 4; addr++) { + if (!pdata->chip[addr].is_present) + continue; + chips++; + } + if (!chips) + return -ENODEV; + + data = kzalloc(sizeof *data + chips * sizeof(struct mcp23s08), + GFP_KERNEL); + if (!data) + return -ENOMEM; + spi_set_drvdata(spi, data); + + base = pdata->base; + for (addr = 0; addr < 4; addr++) { + if (!pdata->chip[addr].is_present) + continue; + chips--; + data->mcp[addr] = &data->chip[chips]; + status = mcp23s08_probe_one(spi, addr, base, + pdata->chip[addr].pullups); + if (status < 0) + goto fail; + base += 8; + } + data->ngpio = base - pdata->base; + + /* NOTE: these chips have a relatively sane IRQ framework, with + * per-signal masking and level/edge triggering. It's not yet + * handled here... + */ + + if (pdata->setup) { + status = pdata->setup(spi, + pdata->base, data->ngpio, + pdata->context); + if (status < 0) + dev_dbg(&spi->dev, "setup --> %d\n", status); + } + + return 0; + +fail: + for (addr = 0; addr < 4; addr++) { + int tmp; + + if (!data->mcp[addr]) + continue; + tmp = gpiochip_remove(&data->mcp[addr]->chip); + if (tmp < 0) + dev_err(&spi->dev, "%s --> %d\n", "remove", tmp); + } + kfree(data); + return status; +} + +static int mcp23s08_remove(struct spi_device *spi) +{ + struct mcp23s08_driver_data *data = spi_get_drvdata(spi); + struct mcp23s08_platform_data *pdata = spi->dev.platform_data; + unsigned addr; + int status = 0; + + if (pdata->teardown) { + status = pdata->teardown(spi, + pdata->base, data->ngpio, + pdata->context); + if (status < 0) { + dev_err(&spi->dev, "%s --> %d\n", "teardown", status); + return status; + } + } + + for (addr = 0; addr < 4; addr++) { + int tmp; + + if (!data->mcp[addr]) + continue; + + tmp = gpiochip_remove(&data->mcp[addr]->chip); + if (tmp < 0) { + dev_err(&spi->dev, "%s --> %d\n", "remove", tmp); + status = tmp; + } + } + if (status == 0) + kfree(data); + return status; +} + +static struct spi_driver mcp23s08_driver = { + .probe = mcp23s08_probe, + .remove = mcp23s08_remove, + .driver = { + .name = "mcp23s08", + .owner = THIS_MODULE, + }, +}; + +/*----------------------------------------------------------------------*/ + +static int __init mcp23s08_init(void) +{ + return spi_register_driver(&mcp23s08_driver); +} +/* register after spi postcore initcall and before + * subsys initcalls that may rely on these GPIOs + */ +subsys_initcall(mcp23s08_init); + +static void __exit mcp23s08_exit(void) +{ + spi_unregister_driver(&mcp23s08_driver); +} +module_exit(mcp23s08_exit); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:mcp23s08"); diff --git a/drivers/gpio/pca953x.c b/drivers/gpio/pca953x.c new file mode 100644 index 00000000000..6a2fb3fbb3d --- /dev/null +++ b/drivers/gpio/pca953x.c @@ -0,0 +1,379 @@ +/* + * pca953x.c - 4/8/16 bit I/O ports + * + * Copyright (C) 2005 Ben Gardner <bgardner@wabtec.com> + * Copyright (C) 2007 Marvell International Ltd. + * + * Derived from drivers/i2c/chips/pca9539.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; version 2 of the License. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/i2c/pca953x.h> +#ifdef CONFIG_OF_GPIO +#include <linux/of_platform.h> +#include <linux/of_gpio.h> +#endif + +#define PCA953X_INPUT 0 +#define PCA953X_OUTPUT 1 +#define PCA953X_INVERT 2 +#define PCA953X_DIRECTION 3 + +static const struct i2c_device_id pca953x_id[] = { + { "pca9534", 8, }, + { "pca9535", 16, }, + { "pca9536", 4, }, + { "pca9537", 4, }, + { "pca9538", 8, }, + { "pca9539", 16, }, + { "pca9554", 8, }, + { "pca9555", 16, }, + { "pca9556", 8, }, + { "pca9557", 8, }, + + { "max7310", 8, }, + { "max7315", 8, }, + { "pca6107", 8, }, + { "tca6408", 8, }, + { "tca6416", 16, }, + /* NYET: { "tca6424", 24, }, */ + { } +}; +MODULE_DEVICE_TABLE(i2c, pca953x_id); + +struct pca953x_chip { + unsigned gpio_start; + uint16_t reg_output; + uint16_t reg_direction; + + struct i2c_client *client; + struct pca953x_platform_data *dyn_pdata; + struct gpio_chip gpio_chip; + char **names; +}; + +static int pca953x_write_reg(struct pca953x_chip *chip, int reg, uint16_t val) +{ + int ret; + + if (chip->gpio_chip.ngpio <= 8) + ret = i2c_smbus_write_byte_data(chip->client, reg, val); + else + ret = i2c_smbus_write_word_data(chip->client, reg << 1, val); + + if (ret < 0) { + dev_err(&chip->client->dev, "failed writing register\n"); + return ret; + } + + return 0; +} + +static int pca953x_read_reg(struct pca953x_chip *chip, int reg, uint16_t *val) +{ + int ret; + + if (chip->gpio_chip.ngpio <= 8) + ret = i2c_smbus_read_byte_data(chip->client, reg); + else + ret = i2c_smbus_read_word_data(chip->client, reg << 1); + + if (ret < 0) { + dev_err(&chip->client->dev, "failed reading register\n"); + return ret; + } + + *val = (uint16_t)ret; + return 0; +} + +static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off) +{ + struct pca953x_chip *chip; + uint16_t reg_val; + int ret; + + chip = container_of(gc, struct pca953x_chip, gpio_chip); + + reg_val = chip->reg_direction | (1u << off); + ret = pca953x_write_reg(chip, PCA953X_DIRECTION, reg_val); + if (ret) + return ret; + + chip->reg_direction = reg_val; + return 0; +} + +static int pca953x_gpio_direction_output(struct gpio_chip *gc, + unsigned off, int val) +{ + struct pca953x_chip *chip; + uint16_t reg_val; + int ret; + + chip = container_of(gc, struct pca953x_chip, gpio_chip); + + /* set output level */ + if (val) + reg_val = chip->reg_output | (1u << off); + else + reg_val = chip->reg_output & ~(1u << off); + + ret = pca953x_write_reg(chip, PCA953X_OUTPUT, reg_val); + if (ret) + return ret; + + chip->reg_output = reg_val; + + /* then direction */ + reg_val = chip->reg_direction & ~(1u << off); + ret = pca953x_write_reg(chip, PCA953X_DIRECTION, reg_val); + if (ret) + return ret; + + chip->reg_direction = reg_val; + return 0; +} + +static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off) +{ + struct pca953x_chip *chip; + uint16_t reg_val; + int ret; + + chip = container_of(gc, struct pca953x_chip, gpio_chip); + + ret = pca953x_read_reg(chip, PCA953X_INPUT, ®_val); + if (ret < 0) { + /* NOTE: diagnostic already emitted; that's all we should + * do unless gpio_*_value_cansleep() calls become different + * from their nonsleeping siblings (and report faults). + */ + return 0; + } + + return (reg_val & (1u << off)) ? 1 : 0; +} + +static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val) +{ + struct pca953x_chip *chip; + uint16_t reg_val; + int ret; + + chip = container_of(gc, struct pca953x_chip, gpio_chip); + + if (val) + reg_val = chip->reg_output | (1u << off); + else + reg_val = chip->reg_output & ~(1u << off); + + ret = pca953x_write_reg(chip, PCA953X_OUTPUT, reg_val); + if (ret) + return; + + chip->reg_output = reg_val; +} + +static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios) +{ + struct gpio_chip *gc; + + gc = &chip->gpio_chip; + + gc->direction_input = pca953x_gpio_direction_input; + gc->direction_output = pca953x_gpio_direction_output; + gc->get = pca953x_gpio_get_value; + gc->set = pca953x_gpio_set_value; + gc->can_sleep = 1; + + gc->base = chip->gpio_start; + gc->ngpio = gpios; + gc->label = chip->client->name; + gc->dev = &chip->client->dev; + gc->owner = THIS_MODULE; + gc->names = chip->names; +} + +/* + * Handlers for alternative sources of platform_data + */ +#ifdef CONFIG_OF_GPIO +/* + * Translate OpenFirmware node properties into platform_data + */ +static struct pca953x_platform_data * +pca953x_get_alt_pdata(struct i2c_client *client) +{ + struct pca953x_platform_data *pdata; + struct device_node *node; + const uint16_t *val; + + node = dev_archdata_get_node(&client->dev.archdata); + if (node == NULL) + return NULL; + + pdata = kzalloc(sizeof(struct pca953x_platform_data), GFP_KERNEL); + if (pdata == NULL) { + dev_err(&client->dev, "Unable to allocate platform_data\n"); + return NULL; + } + + pdata->gpio_base = -1; + val = of_get_property(node, "linux,gpio-base", NULL); + if (val) { + if (*val < 0) + dev_warn(&client->dev, + "invalid gpio-base in device tree\n"); + else + pdata->gpio_base = *val; + } + + val = of_get_property(node, "polarity", NULL); + if (val) + pdata->invert = *val; + + return pdata; +} +#else +static struct pca953x_platform_data * +pca953x_get_alt_pdata(struct i2c_client *client) +{ + return NULL; +} +#endif + +static int __devinit pca953x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pca953x_platform_data *pdata; + struct pca953x_chip *chip; + int ret; + + chip = kzalloc(sizeof(struct pca953x_chip), GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + pdata = client->dev.platform_data; + if (pdata == NULL) { + pdata = pca953x_get_alt_pdata(client); + /* + * Unlike normal platform_data, this is allocated + * dynamically and must be freed in the driver + */ + chip->dyn_pdata = pdata; + } + + if (pdata == NULL) { + dev_dbg(&client->dev, "no platform data\n"); + ret = -EINVAL; + goto out_failed; + } + + chip->client = client; + + chip->gpio_start = pdata->gpio_base; + + chip->names = pdata->names; + + /* initialize cached registers from their original values. + * we can't share this chip with another i2c master. + */ + pca953x_setup_gpio(chip, id->driver_data); + + ret = pca953x_read_reg(chip, PCA953X_OUTPUT, &chip->reg_output); + if (ret) + goto out_failed; + + ret = pca953x_read_reg(chip, PCA953X_DIRECTION, &chip->reg_direction); + if (ret) + goto out_failed; + + /* set platform specific polarity inversion */ + ret = pca953x_write_reg(chip, PCA953X_INVERT, pdata->invert); + if (ret) + goto out_failed; + + + ret = gpiochip_add(&chip->gpio_chip); + if (ret) + goto out_failed; + + if (pdata->setup) { + ret = pdata->setup(client, chip->gpio_chip.base, + chip->gpio_chip.ngpio, pdata->context); + if (ret < 0) + dev_warn(&client->dev, "setup failed, %d\n", ret); + } + + i2c_set_clientdata(client, chip); + return 0; + +out_failed: + kfree(chip->dyn_pdata); + kfree(chip); + return ret; +} + +static int pca953x_remove(struct i2c_client *client) +{ + struct pca953x_platform_data *pdata = client->dev.platform_data; + struct pca953x_chip *chip = i2c_get_clientdata(client); + int ret = 0; + + if (pdata->teardown) { + ret = pdata->teardown(client, chip->gpio_chip.base, + chip->gpio_chip.ngpio, pdata->context); + if (ret < 0) { + dev_err(&client->dev, "%s failed, %d\n", + "teardown", ret); + return ret; + } + } + + ret = gpiochip_remove(&chip->gpio_chip); + if (ret) { + dev_err(&client->dev, "%s failed, %d\n", + "gpiochip_remove()", ret); + return ret; + } + + kfree(chip->dyn_pdata); + kfree(chip); + return 0; +} + +static struct i2c_driver pca953x_driver = { + .driver = { + .name = "pca953x", + }, + .probe = pca953x_probe, + .remove = pca953x_remove, + .id_table = pca953x_id, +}; + +static int __init pca953x_init(void) +{ + return i2c_add_driver(&pca953x_driver); +} +/* register after i2c postcore initcall and before + * subsys initcalls that may rely on these GPIOs + */ +subsys_initcall(pca953x_init); + +static void __exit pca953x_exit(void) +{ + i2c_del_driver(&pca953x_driver); +} +module_exit(pca953x_exit); + +MODULE_AUTHOR("eric miao <eric.miao@marvell.com>"); +MODULE_DESCRIPTION("GPIO expander driver for PCA953x"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/pcf857x.c b/drivers/gpio/pcf857x.c new file mode 100644 index 00000000000..29f19ce3e80 --- /dev/null +++ b/drivers/gpio/pcf857x.c @@ -0,0 +1,370 @@ +/* + * pcf857x - driver for pcf857x, pca857x, and pca967x I2C GPIO expanders + * + * Copyright (C) 2007 David Brownell + * + * 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/kernel.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/i2c/pcf857x.h> + + +static const struct i2c_device_id pcf857x_id[] = { + { "pcf8574", 8 }, + { "pcf8574a", 8 }, + { "pca8574", 8 }, + { "pca9670", 8 }, + { "pca9672", 8 }, + { "pca9674", 8 }, + { "pcf8575", 16 }, + { "pca8575", 16 }, + { "pca9671", 16 }, + { "pca9673", 16 }, + { "pca9675", 16 }, + { "max7328", 8 }, + { "max7329", 8 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcf857x_id); + +/* + * The pcf857x, pca857x, and pca967x chips only expose one read and one + * write register. Writing a "one" bit (to match the reset state) lets + * that pin be used as an input; it's not an open-drain model, but acts + * a bit like one. This is described as "quasi-bidirectional"; read the + * chip documentation for details. + * + * Many other I2C GPIO expander chips (like the pca953x models) have + * more complex register models and more conventional circuitry using + * push/pull drivers. They often use the same 0x20..0x27 addresses as + * pcf857x parts, making the "legacy" I2C driver model problematic. + */ +struct pcf857x { + struct gpio_chip chip; + struct i2c_client *client; + struct mutex lock; /* protect 'out' */ + unsigned out; /* software latch */ +}; + +/*-------------------------------------------------------------------------*/ + +/* Talk to 8-bit I/O expander */ + +static int pcf857x_input8(struct gpio_chip *chip, unsigned offset) +{ + struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); + int status; + + mutex_lock(&gpio->lock); + gpio->out |= (1 << offset); + status = i2c_smbus_write_byte(gpio->client, gpio->out); + mutex_unlock(&gpio->lock); + + return status; +} + +static int pcf857x_get8(struct gpio_chip *chip, unsigned offset) +{ + struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); + s32 value; + + value = i2c_smbus_read_byte(gpio->client); + return (value < 0) ? 0 : (value & (1 << offset)); +} + +static int pcf857x_output8(struct gpio_chip *chip, unsigned offset, int value) +{ + struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); + unsigned bit = 1 << offset; + int status; + + mutex_lock(&gpio->lock); + if (value) + gpio->out |= bit; + else + gpio->out &= ~bit; + status = i2c_smbus_write_byte(gpio->client, gpio->out); + mutex_unlock(&gpio->lock); + + return status; +} + +static void pcf857x_set8(struct gpio_chip *chip, unsigned offset, int value) +{ + pcf857x_output8(chip, offset, value); +} + +/*-------------------------------------------------------------------------*/ + +/* Talk to 16-bit I/O expander */ + +static int i2c_write_le16(struct i2c_client *client, u16 word) +{ + u8 buf[2] = { word & 0xff, word >> 8, }; + int status; + + status = i2c_master_send(client, buf, 2); + return (status < 0) ? status : 0; +} + +static int i2c_read_le16(struct i2c_client *client) +{ + u8 buf[2]; + int status; + + status = i2c_master_recv(client, buf, 2); + if (status < 0) + return status; + return (buf[1] << 8) | buf[0]; +} + +static int pcf857x_input16(struct gpio_chip *chip, unsigned offset) +{ + struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); + int status; + + mutex_lock(&gpio->lock); + gpio->out |= (1 << offset); + status = i2c_write_le16(gpio->client, gpio->out); + mutex_unlock(&gpio->lock); + + return status; +} + +static int pcf857x_get16(struct gpio_chip *chip, unsigned offset) +{ + struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); + int value; + + value = i2c_read_le16(gpio->client); + return (value < 0) ? 0 : (value & (1 << offset)); +} + +static int pcf857x_output16(struct gpio_chip *chip, unsigned offset, int value) +{ + struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); + unsigned bit = 1 << offset; + int status; + + mutex_lock(&gpio->lock); + if (value) + gpio->out |= bit; + else + gpio->out &= ~bit; + status = i2c_write_le16(gpio->client, gpio->out); + mutex_unlock(&gpio->lock); + + return status; +} + +static void pcf857x_set16(struct gpio_chip *chip, unsigned offset, int value) +{ + pcf857x_output16(chip, offset, value); +} + +/*-------------------------------------------------------------------------*/ + +static int pcf857x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pcf857x_platform_data *pdata; + struct pcf857x *gpio; + int status; + + pdata = client->dev.platform_data; + if (!pdata) { + dev_dbg(&client->dev, "no platform data\n"); + return -EINVAL; + } + + /* Allocate, initialize, and register this gpio_chip. */ + gpio = kzalloc(sizeof *gpio, GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + mutex_init(&gpio->lock); + + gpio->chip.base = pdata->gpio_base; + gpio->chip.can_sleep = 1; + gpio->chip.dev = &client->dev; + gpio->chip.owner = THIS_MODULE; + + /* NOTE: the OnSemi jlc1562b is also largely compatible with + * these parts, notably for output. It has a low-resolution + * DAC instead of pin change IRQs; and its inputs can be the + * result of comparators. + */ + + /* 8574 addresses are 0x20..0x27; 8574a uses 0x38..0x3f; + * 9670, 9672, 9764, and 9764a use quite a variety. + * + * NOTE: we don't distinguish here between *4 and *4a parts. + */ + gpio->chip.ngpio = id->driver_data; + if (gpio->chip.ngpio == 8) { + gpio->chip.direction_input = pcf857x_input8; + gpio->chip.get = pcf857x_get8; + gpio->chip.direction_output = pcf857x_output8; + gpio->chip.set = pcf857x_set8; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE)) + status = -EIO; + + /* fail if there's no chip present */ + else + status = i2c_smbus_read_byte(client); + + /* '75/'75c addresses are 0x20..0x27, just like the '74; + * the '75c doesn't have a current source pulling high. + * 9671, 9673, and 9765 use quite a variety of addresses. + * + * NOTE: we don't distinguish here between '75 and '75c parts. + */ + } else if (gpio->chip.ngpio == 16) { + gpio->chip.direction_input = pcf857x_input16; + gpio->chip.get = pcf857x_get16; + gpio->chip.direction_output = pcf857x_output16; + gpio->chip.set = pcf857x_set16; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + status = -EIO; + + /* fail if there's no chip present */ + else + status = i2c_read_le16(client); + + } else { + dev_dbg(&client->dev, "unsupported number of gpios\n"); + status = -EINVAL; + } + + if (status < 0) + goto fail; + + gpio->chip.label = client->name; + + gpio->client = client; + i2c_set_clientdata(client, gpio); + + /* NOTE: these chips have strange "quasi-bidirectional" I/O pins. + * We can't actually know whether a pin is configured (a) as output + * and driving the signal low, or (b) as input and reporting a low + * value ... without knowing the last value written since the chip + * came out of reset (if any). We can't read the latched output. + * + * In short, the only reliable solution for setting up pin direction + * is to do it explicitly. The setup() method can do that, but it + * may cause transient glitching since it can't know the last value + * written (some pins may need to be driven low). + * + * Using pdata->n_latch avoids that trouble. When left initialized + * to zero, our software copy of the "latch" then matches the chip's + * all-ones reset state. Otherwise it flags pins to be driven low. + */ + gpio->out = ~pdata->n_latch; + + status = gpiochip_add(&gpio->chip); + if (status < 0) + goto fail; + + /* NOTE: these chips can issue "some pin-changed" IRQs, which we + * don't yet even try to use. Among other issues, the relevant + * genirq state isn't available to modular drivers; and most irq + * methods can't be called from sleeping contexts. + */ + + dev_info(&client->dev, "gpios %d..%d on a %s%s\n", + gpio->chip.base, + gpio->chip.base + gpio->chip.ngpio - 1, + client->name, + client->irq ? " (irq ignored)" : ""); + + /* Let platform code set up the GPIOs and their users. + * Now is the first time anyone could use them. + */ + if (pdata->setup) { + status = pdata->setup(client, + gpio->chip.base, gpio->chip.ngpio, + pdata->context); + if (status < 0) + dev_warn(&client->dev, "setup --> %d\n", status); + } + + return 0; + +fail: + dev_dbg(&client->dev, "probe error %d for '%s'\n", + status, client->name); + kfree(gpio); + return status; +} + +static int pcf857x_remove(struct i2c_client *client) +{ + struct pcf857x_platform_data *pdata = client->dev.platform_data; + struct pcf857x *gpio = i2c_get_clientdata(client); + int status = 0; + + if (pdata->teardown) { + status = pdata->teardown(client, + gpio->chip.base, gpio->chip.ngpio, + pdata->context); + if (status < 0) { + dev_err(&client->dev, "%s --> %d\n", + "teardown", status); + return status; + } + } + + status = gpiochip_remove(&gpio->chip); + if (status == 0) + kfree(gpio); + else + dev_err(&client->dev, "%s --> %d\n", "remove", status); + return status; +} + +static struct i2c_driver pcf857x_driver = { + .driver = { + .name = "pcf857x", + .owner = THIS_MODULE, + }, + .probe = pcf857x_probe, + .remove = pcf857x_remove, + .id_table = pcf857x_id, +}; + +static int __init pcf857x_init(void) +{ + return i2c_add_driver(&pcf857x_driver); +} +/* register after i2c postcore initcall and before + * subsys initcalls that may rely on these GPIOs + */ +subsys_initcall(pcf857x_init); + +static void __exit pcf857x_exit(void) +{ + i2c_del_driver(&pcf857x_driver); +} +module_exit(pcf857x_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Brownell"); diff --git a/drivers/gpio/pl061.c b/drivers/gpio/pl061.c new file mode 100644 index 00000000000..4ee4c8367a3 --- /dev/null +++ b/drivers/gpio/pl061.c @@ -0,0 +1,353 @@ +/* + * linux/drivers/gpio/pl061.c + * + * Copyright (C) 2008, 2009 Provigent Ltd. + * + * 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. + * + * Driver for the ARM PrimeCell(tm) General Purpose Input/Output (PL061) + * + * Data sheet: ARM DDI 0190B, September 2000 + */ +#include <linux/spinlock.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/list.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/irq.h> +#include <linux/bitops.h> +#include <linux/workqueue.h> +#include <linux/gpio.h> +#include <linux/device.h> +#include <linux/amba/bus.h> +#include <linux/amba/pl061.h> + +#define GPIODIR 0x400 +#define GPIOIS 0x404 +#define GPIOIBE 0x408 +#define GPIOIEV 0x40C +#define GPIOIE 0x410 +#define GPIORIS 0x414 +#define GPIOMIS 0x418 +#define GPIOIC 0x41C + +#define PL061_GPIO_NR 8 + +struct pl061_gpio { + /* We use a list of pl061_gpio structs for each trigger IRQ in the main + * interrupts controller of the system. We need this to support systems + * in which more that one PL061s are connected to the same IRQ. The ISR + * interates through this list to find the source of the interrupt. + */ + struct list_head list; + + /* Each of the two spinlocks protects a different set of hardware + * regiters and data structurs. This decouples the code of the IRQ from + * the GPIO code. This also makes the case of a GPIO routine call from + * the IRQ code simpler. + */ + spinlock_t lock; /* GPIO registers */ + spinlock_t irq_lock; /* IRQ registers */ + + void __iomem *base; + unsigned irq_base; + struct gpio_chip gc; +}; + +static int pl061_direction_input(struct gpio_chip *gc, unsigned offset) +{ + struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); + unsigned long flags; + unsigned char gpiodir; + + if (offset >= gc->ngpio) + return -EINVAL; + + spin_lock_irqsave(&chip->lock, flags); + gpiodir = readb(chip->base + GPIODIR); + gpiodir &= ~(1 << offset); + writeb(gpiodir, chip->base + GPIODIR); + spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static int pl061_direction_output(struct gpio_chip *gc, unsigned offset, + int value) +{ + struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); + unsigned long flags; + unsigned char gpiodir; + + if (offset >= gc->ngpio) + return -EINVAL; + + spin_lock_irqsave(&chip->lock, flags); + writeb(!!value << offset, chip->base + (1 << (offset + 2))); + gpiodir = readb(chip->base + GPIODIR); + gpiodir |= 1 << offset; + writeb(gpiodir, chip->base + GPIODIR); + spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static int pl061_get_value(struct gpio_chip *gc, unsigned offset) +{ + struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); + + return !!readb(chip->base + (1 << (offset + 2))); +} + +static void pl061_set_value(struct gpio_chip *gc, unsigned offset, int value) +{ + struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); + + writeb(!!value << offset, chip->base + (1 << (offset + 2))); +} + +static int pl061_to_irq(struct gpio_chip *gc, unsigned offset) +{ + struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); + + if (chip->irq_base == (unsigned) -1) + return -EINVAL; + + return chip->irq_base + offset; +} + +/* + * PL061 GPIO IRQ + */ +static void pl061_irq_disable(unsigned irq) +{ + struct pl061_gpio *chip = get_irq_chip_data(irq); + int offset = irq - chip->irq_base; + unsigned long flags; + u8 gpioie; + + spin_lock_irqsave(&chip->irq_lock, flags); + gpioie = readb(chip->base + GPIOIE); + gpioie &= ~(1 << offset); + writeb(gpioie, chip->base + GPIOIE); + spin_unlock_irqrestore(&chip->irq_lock, flags); +} + +static void pl061_irq_enable(unsigned irq) +{ + struct pl061_gpio *chip = get_irq_chip_data(irq); + int offset = irq - chip->irq_base; + unsigned long flags; + u8 gpioie; + + spin_lock_irqsave(&chip->irq_lock, flags); + gpioie = readb(chip->base + GPIOIE); + gpioie |= 1 << offset; + writeb(gpioie, chip->base + GPIOIE); + spin_unlock_irqrestore(&chip->irq_lock, flags); +} + +static int pl061_irq_type(unsigned irq, unsigned trigger) +{ + struct pl061_gpio *chip = get_irq_chip_data(irq); + int offset = irq - chip->irq_base; + unsigned long flags; + u8 gpiois, gpioibe, gpioiev; + + if (offset < 0 || offset > PL061_GPIO_NR) + return -EINVAL; + + spin_lock_irqsave(&chip->irq_lock, flags); + + gpioiev = readb(chip->base + GPIOIEV); + + gpiois = readb(chip->base + GPIOIS); + if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) { + gpiois |= 1 << offset; + if (trigger & IRQ_TYPE_LEVEL_HIGH) + gpioiev |= 1 << offset; + else + gpioiev &= ~(1 << offset); + } else + gpiois &= ~(1 << offset); + writeb(gpiois, chip->base + GPIOIS); + + gpioibe = readb(chip->base + GPIOIBE); + if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) + gpioibe |= 1 << offset; + else { + gpioibe &= ~(1 << offset); + if (trigger & IRQ_TYPE_EDGE_RISING) + gpioiev |= 1 << offset; + else + gpioiev &= ~(1 << offset); + } + writeb(gpioibe, chip->base + GPIOIBE); + + writeb(gpioiev, chip->base + GPIOIEV); + + spin_unlock_irqrestore(&chip->irq_lock, flags); + + return 0; +} + +static struct irq_chip pl061_irqchip = { + .name = "GPIO", + .enable = pl061_irq_enable, + .disable = pl061_irq_disable, + .set_type = pl061_irq_type, +}; + +static void pl061_irq_handler(unsigned irq, struct irq_desc *desc) +{ + struct list_head *chip_list = get_irq_chip_data(irq); + struct list_head *ptr; + struct pl061_gpio *chip; + + desc->chip->ack(irq); + list_for_each(ptr, chip_list) { + unsigned long pending; + int offset; + + chip = list_entry(ptr, struct pl061_gpio, list); + pending = readb(chip->base + GPIOMIS); + writeb(pending, chip->base + GPIOIC); + + if (pending == 0) + continue; + + for_each_bit(offset, &pending, PL061_GPIO_NR) + generic_handle_irq(pl061_to_irq(&chip->gc, offset)); + } + desc->chip->unmask(irq); +} + +static int __init pl061_probe(struct amba_device *dev, struct amba_id *id) +{ + struct pl061_platform_data *pdata; + struct pl061_gpio *chip; + struct list_head *chip_list; + int ret, irq, i; + static DECLARE_BITMAP(init_irq, NR_IRQS); + + pdata = dev->dev.platform_data; + if (pdata == NULL) + return -ENODEV; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + if (!request_mem_region(dev->res.start, + resource_size(&dev->res), "pl061")) { + ret = -EBUSY; + goto free_mem; + } + + chip->base = ioremap(dev->res.start, resource_size(&dev->res)); + if (chip->base == NULL) { + ret = -ENOMEM; + goto release_region; + } + + spin_lock_init(&chip->lock); + spin_lock_init(&chip->irq_lock); + INIT_LIST_HEAD(&chip->list); + + chip->gc.direction_input = pl061_direction_input; + chip->gc.direction_output = pl061_direction_output; + chip->gc.get = pl061_get_value; + chip->gc.set = pl061_set_value; + chip->gc.to_irq = pl061_to_irq; + chip->gc.base = pdata->gpio_base; + chip->gc.ngpio = PL061_GPIO_NR; + chip->gc.label = dev_name(&dev->dev); + chip->gc.dev = &dev->dev; + chip->gc.owner = THIS_MODULE; + + chip->irq_base = pdata->irq_base; + + ret = gpiochip_add(&chip->gc); + if (ret) + goto iounmap; + + /* + * irq_chip support + */ + + if (chip->irq_base == (unsigned) -1) + return 0; + + writeb(0, chip->base + GPIOIE); /* disable irqs */ + irq = dev->irq[0]; + if (irq < 0) { + ret = -ENODEV; + goto iounmap; + } + set_irq_chained_handler(irq, pl061_irq_handler); + if (!test_and_set_bit(irq, init_irq)) { /* list initialized? */ + chip_list = kmalloc(sizeof(*chip_list), GFP_KERNEL); + if (chip_list == NULL) { + clear_bit(irq, init_irq); + ret = -ENOMEM; + goto iounmap; + } + INIT_LIST_HEAD(chip_list); + set_irq_chip_data(irq, chip_list); + } else + chip_list = get_irq_chip_data(irq); + list_add(&chip->list, chip_list); + + for (i = 0; i < PL061_GPIO_NR; i++) { + if (pdata->directions & (1 << i)) + pl061_direction_output(&chip->gc, i, + pdata->values & (1 << i)); + else + pl061_direction_input(&chip->gc, i); + + set_irq_chip(i+chip->irq_base, &pl061_irqchip); + set_irq_handler(i+chip->irq_base, handle_simple_irq); + set_irq_flags(i+chip->irq_base, IRQF_VALID); + set_irq_chip_data(i+chip->irq_base, chip); + } + + return 0; + +iounmap: + iounmap(chip->base); +release_region: + release_mem_region(dev->res.start, resource_size(&dev->res)); +free_mem: + kfree(chip); + + return ret; +} + +static struct amba_id pl061_ids[] __initdata = { + { + .id = 0x00041061, + .mask = 0x000fffff, + }, + { 0, 0 }, +}; + +static struct amba_driver pl061_gpio_driver = { + .drv = { + .name = "pl061_gpio", + }, + .id_table = pl061_ids, + .probe = pl061_probe, +}; + +static int __init pl061_gpio_init(void) +{ + return amba_driver_register(&pl061_gpio_driver); +} +subsys_initcall(pl061_gpio_init); + +MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>"); +MODULE_DESCRIPTION("PL061 GPIO driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/timbgpio.c b/drivers/gpio/timbgpio.c new file mode 100644 index 00000000000..a4d344ba8e5 --- /dev/null +++ b/drivers/gpio/timbgpio.c @@ -0,0 +1,342 @@ +/* + * timbgpio.c timberdale FPGA GPIO driver + * Copyright (c) 2009 Intel Corporation + * + * 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. + * + * 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. + */ + +/* Supports: + * Timberdale FPGA GPIO + */ + +#include <linux/module.h> +#include <linux/gpio.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/timb_gpio.h> +#include <linux/interrupt.h> + +#define DRIVER_NAME "timb-gpio" + +#define TGPIOVAL 0x00 +#define TGPIODIR 0x04 +#define TGPIO_IER 0x08 +#define TGPIO_ISR 0x0c +#define TGPIO_IPR 0x10 +#define TGPIO_ICR 0x14 +#define TGPIO_FLR 0x18 +#define TGPIO_LVR 0x1c + +struct timbgpio { + void __iomem *membase; + spinlock_t lock; /* mutual exclusion */ + struct gpio_chip gpio; + int irq_base; +}; + +static int timbgpio_update_bit(struct gpio_chip *gpio, unsigned index, + unsigned offset, bool enabled) +{ + struct timbgpio *tgpio = container_of(gpio, struct timbgpio, gpio); + u32 reg; + + spin_lock(&tgpio->lock); + reg = ioread32(tgpio->membase + offset); + + if (enabled) + reg |= (1 << index); + else + reg &= ~(1 << index); + + iowrite32(reg, tgpio->membase + offset); + spin_unlock(&tgpio->lock); + + return 0; +} + +static int timbgpio_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) +{ + return timbgpio_update_bit(gpio, nr, TGPIODIR, true); +} + +static int timbgpio_gpio_get(struct gpio_chip *gpio, unsigned nr) +{ + struct timbgpio *tgpio = container_of(gpio, struct timbgpio, gpio); + u32 value; + + value = ioread32(tgpio->membase + TGPIOVAL); + return (value & (1 << nr)) ? 1 : 0; +} + +static int timbgpio_gpio_direction_output(struct gpio_chip *gpio, + unsigned nr, int val) +{ + return timbgpio_update_bit(gpio, nr, TGPIODIR, false); +} + +static void timbgpio_gpio_set(struct gpio_chip *gpio, + unsigned nr, int val) +{ + timbgpio_update_bit(gpio, nr, TGPIOVAL, val != 0); +} + +static int timbgpio_to_irq(struct gpio_chip *gpio, unsigned offset) +{ + struct timbgpio *tgpio = container_of(gpio, struct timbgpio, gpio); + + if (tgpio->irq_base <= 0) + return -EINVAL; + + return tgpio->irq_base + offset; +} + +/* + * GPIO IRQ + */ +static void timbgpio_irq_disable(unsigned irq) +{ + struct timbgpio *tgpio = get_irq_chip_data(irq); + int offset = irq - tgpio->irq_base; + + timbgpio_update_bit(&tgpio->gpio, offset, TGPIO_IER, 0); +} + +static void timbgpio_irq_enable(unsigned irq) +{ + struct timbgpio *tgpio = get_irq_chip_data(irq); + int offset = irq - tgpio->irq_base; + + timbgpio_update_bit(&tgpio->gpio, offset, TGPIO_IER, 1); +} + +static int timbgpio_irq_type(unsigned irq, unsigned trigger) +{ + struct timbgpio *tgpio = get_irq_chip_data(irq); + int offset = irq - tgpio->irq_base; + unsigned long flags; + u32 lvr, flr; + + if (offset < 0 || offset > tgpio->gpio.ngpio) + return -EINVAL; + + spin_lock_irqsave(&tgpio->lock, flags); + + lvr = ioread32(tgpio->membase + TGPIO_LVR); + flr = ioread32(tgpio->membase + TGPIO_FLR); + + if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) { + flr &= ~(1 << offset); + if (trigger & IRQ_TYPE_LEVEL_HIGH) + lvr |= 1 << offset; + else + lvr &= ~(1 << offset); + } + + if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) + return -EINVAL; + else { + flr |= 1 << offset; + /* opposite compared to the datasheet, but it mirrors the + * reality + */ + if (trigger & IRQ_TYPE_EDGE_FALLING) + lvr |= 1 << offset; + else + lvr &= ~(1 << offset); + } + + iowrite32(lvr, tgpio->membase + TGPIO_LVR); + iowrite32(flr, tgpio->membase + TGPIO_FLR); + iowrite32(1 << offset, tgpio->membase + TGPIO_ICR); + spin_unlock_irqrestore(&tgpio->lock, flags); + + return 0; +} + +static void timbgpio_irq(unsigned int irq, struct irq_desc *desc) +{ + struct timbgpio *tgpio = get_irq_data(irq); + unsigned long ipr; + int offset; + + desc->chip->ack(irq); + ipr = ioread32(tgpio->membase + TGPIO_IPR); + iowrite32(ipr, tgpio->membase + TGPIO_ICR); + + for_each_bit(offset, &ipr, tgpio->gpio.ngpio) + generic_handle_irq(timbgpio_to_irq(&tgpio->gpio, offset)); +} + +static struct irq_chip timbgpio_irqchip = { + .name = "GPIO", + .enable = timbgpio_irq_enable, + .disable = timbgpio_irq_disable, + .set_type = timbgpio_irq_type, +}; + +static int __devinit timbgpio_probe(struct platform_device *pdev) +{ + int err, i; + struct gpio_chip *gc; + struct timbgpio *tgpio; + struct resource *iomem; + struct timbgpio_platform_data *pdata = pdev->dev.platform_data; + int irq = platform_get_irq(pdev, 0); + + if (!pdata || pdata->nr_pins > 32) { + err = -EINVAL; + goto err_mem; + } + + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!iomem) { + err = -EINVAL; + goto err_mem; + } + + tgpio = kzalloc(sizeof(*tgpio), GFP_KERNEL); + if (!tgpio) { + err = -EINVAL; + goto err_mem; + } + tgpio->irq_base = pdata->irq_base; + + spin_lock_init(&tgpio->lock); + + if (!request_mem_region(iomem->start, resource_size(iomem), + DRIVER_NAME)) { + err = -EBUSY; + goto err_request; + } + + tgpio->membase = ioremap(iomem->start, resource_size(iomem)); + if (!tgpio->membase) { + err = -ENOMEM; + goto err_ioremap; + } + + gc = &tgpio->gpio; + + gc->label = dev_name(&pdev->dev); + gc->owner = THIS_MODULE; + gc->dev = &pdev->dev; + gc->direction_input = timbgpio_gpio_direction_input; + gc->get = timbgpio_gpio_get; + gc->direction_output = timbgpio_gpio_direction_output; + gc->set = timbgpio_gpio_set; + gc->to_irq = (irq >= 0 && tgpio->irq_base > 0) ? timbgpio_to_irq : NULL; + gc->dbg_show = NULL; + gc->base = pdata->gpio_base; + gc->ngpio = pdata->nr_pins; + gc->can_sleep = 0; + + err = gpiochip_add(gc); + if (err) + goto err_chipadd; + + platform_set_drvdata(pdev, tgpio); + + /* make sure to disable interrupts */ + iowrite32(0x0, tgpio->membase + TGPIO_IER); + + if (irq < 0 || tgpio->irq_base <= 0) + return 0; + + for (i = 0; i < pdata->nr_pins; i++) { + set_irq_chip_and_handler_name(tgpio->irq_base + i, + &timbgpio_irqchip, handle_simple_irq, "mux"); + set_irq_chip_data(tgpio->irq_base + i, tgpio); +#ifdef CONFIG_ARM + set_irq_flags(tgpio->irq_base + i, IRQF_VALID | IRQF_PROBE); +#endif + } + + set_irq_data(irq, tgpio); + set_irq_chained_handler(irq, timbgpio_irq); + + return 0; + +err_chipadd: + iounmap(tgpio->membase); +err_ioremap: + release_mem_region(iomem->start, resource_size(iomem)); +err_request: + kfree(tgpio); +err_mem: + printk(KERN_ERR DRIVER_NAME": Failed to register GPIOs: %d\n", err); + + return err; +} + +static int __devexit timbgpio_remove(struct platform_device *pdev) +{ + int err; + struct timbgpio_platform_data *pdata = pdev->dev.platform_data; + struct timbgpio *tgpio = platform_get_drvdata(pdev); + struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + int irq = platform_get_irq(pdev, 0); + + if (irq >= 0 && tgpio->irq_base > 0) { + int i; + for (i = 0; i < pdata->nr_pins; i++) { + set_irq_chip(tgpio->irq_base + i, NULL); + set_irq_chip_data(tgpio->irq_base + i, NULL); + } + + set_irq_handler(irq, NULL); + set_irq_data(irq, NULL); + } + + err = gpiochip_remove(&tgpio->gpio); + if (err) + printk(KERN_ERR DRIVER_NAME": failed to remove gpio_chip\n"); + + iounmap(tgpio->membase); + release_mem_region(iomem->start, resource_size(iomem)); + kfree(tgpio); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver timbgpio_platform_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = timbgpio_probe, + .remove = timbgpio_remove, +}; + +/*--------------------------------------------------------------------------*/ + +static int __init timbgpio_init(void) +{ + return platform_driver_register(&timbgpio_platform_driver); +} + +static void __exit timbgpio_exit(void) +{ + platform_driver_unregister(&timbgpio_platform_driver); +} + +module_init(timbgpio_init); +module_exit(timbgpio_exit); + +MODULE_DESCRIPTION("Timberdale GPIO driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Mocean Laboratories"); +MODULE_ALIAS("platform:"DRIVER_NAME); + diff --git a/drivers/gpio/twl4030-gpio.c b/drivers/gpio/twl4030-gpio.c new file mode 100644 index 00000000000..7fe881e2bdf --- /dev/null +++ b/drivers/gpio/twl4030-gpio.c @@ -0,0 +1,514 @@ +/* + * twl4030_gpio.c -- access to GPIOs on TWL4030/TPS659x0 chips + * + * Copyright (C) 2006-2007 Texas Instruments, Inc. + * Copyright (C) 2006 MontaVista Software, Inc. + * + * Code re-arranged and cleaned up by: + * Syed Mohammed Khasim <x0khasim@ti.com> + * + * Initial Code: + * Andy Lowe / Nishanth Menon + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kthread.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <linux/i2c/twl.h> + + +/* + * The GPIO "subchip" supports 18 GPIOs which can be configured as + * inputs or outputs, with pullups or pulldowns on each pin. Each + * GPIO can trigger interrupts on either or both edges. + * + * GPIO interrupts can be fed to either of two IRQ lines; this is + * intended to support multiple hosts. + * + * There are also two LED pins used sometimes as output-only GPIOs. + */ + + +static struct gpio_chip twl_gpiochip; +static int twl4030_gpio_irq_base; + +/* genirq interfaces are not available to modules */ +#ifdef MODULE +#define is_module() true +#else +#define is_module() false +#endif + +/* GPIO_CTRL Fields */ +#define MASK_GPIO_CTRL_GPIO0CD1 BIT(0) +#define MASK_GPIO_CTRL_GPIO1CD2 BIT(1) +#define MASK_GPIO_CTRL_GPIO_ON BIT(2) + +/* Mask for GPIO registers when aggregated into a 32-bit integer */ +#define GPIO_32_MASK 0x0003ffff + +/* Data structures */ +static DEFINE_MUTEX(gpio_lock); + +/* store usage of each GPIO. - each bit represents one GPIO */ +static unsigned int gpio_usage_count; + +/*----------------------------------------------------------------------*/ + +/* + * To configure TWL4030 GPIO module registers + */ +static inline int gpio_twl4030_write(u8 address, u8 data) +{ + return twl_i2c_write_u8(TWL4030_MODULE_GPIO, data, address); +} + +/*----------------------------------------------------------------------*/ + +/* + * LED register offsets (use TWL4030_MODULE_{LED,PWMA,PWMB})) + * PWMs A and B are dedicated to LEDs A and B, respectively. + */ + +#define TWL4030_LED_LEDEN 0x0 + +/* LEDEN bits */ +#define LEDEN_LEDAON BIT(0) +#define LEDEN_LEDBON BIT(1) +#define LEDEN_LEDAEXT BIT(2) +#define LEDEN_LEDBEXT BIT(3) +#define LEDEN_LEDAPWM BIT(4) +#define LEDEN_LEDBPWM BIT(5) +#define LEDEN_PWM_LENGTHA BIT(6) +#define LEDEN_PWM_LENGTHB BIT(7) + +#define TWL4030_PWMx_PWMxON 0x0 +#define TWL4030_PWMx_PWMxOFF 0x1 + +#define PWMxON_LENGTH BIT(7) + +/*----------------------------------------------------------------------*/ + +/* + * To read a TWL4030 GPIO module register + */ +static inline int gpio_twl4030_read(u8 address) +{ + u8 data; + int ret = 0; + + ret = twl_i2c_read_u8(TWL4030_MODULE_GPIO, &data, address); + return (ret < 0) ? ret : data; +} + +/*----------------------------------------------------------------------*/ + +static u8 cached_leden; /* protected by gpio_lock */ + +/* The LED lines are open drain outputs ... a FET pulls to GND, so an + * external pullup is needed. We could also expose the integrated PWM + * as a LED brightness control; we initialize it as "always on". + */ +static void twl4030_led_set_value(int led, int value) +{ + u8 mask = LEDEN_LEDAON | LEDEN_LEDAPWM; + int status; + + if (led) + mask <<= 1; + + mutex_lock(&gpio_lock); + if (value) + cached_leden &= ~mask; + else + cached_leden |= mask; + status = twl_i2c_write_u8(TWL4030_MODULE_LED, cached_leden, + TWL4030_LED_LEDEN); + mutex_unlock(&gpio_lock); +} + +static int twl4030_set_gpio_direction(int gpio, int is_input) +{ + u8 d_bnk = gpio >> 3; + u8 d_msk = BIT(gpio & 0x7); + u8 reg = 0; + u8 base = REG_GPIODATADIR1 + d_bnk; + int ret = 0; + + mutex_lock(&gpio_lock); + ret = gpio_twl4030_read(base); + if (ret >= 0) { + if (is_input) + reg = ret & ~d_msk; + else + reg = ret | d_msk; + + ret = gpio_twl4030_write(base, reg); + } + mutex_unlock(&gpio_lock); + return ret; +} + +static int twl4030_set_gpio_dataout(int gpio, int enable) +{ + u8 d_bnk = gpio >> 3; + u8 d_msk = BIT(gpio & 0x7); + u8 base = 0; + + if (enable) + base = REG_SETGPIODATAOUT1 + d_bnk; + else + base = REG_CLEARGPIODATAOUT1 + d_bnk; + + return gpio_twl4030_write(base, d_msk); +} + +static int twl4030_get_gpio_datain(int gpio) +{ + u8 d_bnk = gpio >> 3; + u8 d_off = gpio & 0x7; + u8 base = 0; + int ret = 0; + + if (unlikely((gpio >= TWL4030_GPIO_MAX) + || !(gpio_usage_count & BIT(gpio)))) + return -EPERM; + + base = REG_GPIODATAIN1 + d_bnk; + ret = gpio_twl4030_read(base); + if (ret > 0) + ret = (ret >> d_off) & 0x1; + + return ret; +} + +/*----------------------------------------------------------------------*/ + +static int twl_request(struct gpio_chip *chip, unsigned offset) +{ + int status = 0; + + mutex_lock(&gpio_lock); + + /* Support the two LED outputs as output-only GPIOs. */ + if (offset >= TWL4030_GPIO_MAX) { + u8 ledclr_mask = LEDEN_LEDAON | LEDEN_LEDAEXT + | LEDEN_LEDAPWM | LEDEN_PWM_LENGTHA; + u8 module = TWL4030_MODULE_PWMA; + + offset -= TWL4030_GPIO_MAX; + if (offset) { + ledclr_mask <<= 1; + module = TWL4030_MODULE_PWMB; + } + + /* initialize PWM to always-drive */ + status = twl_i2c_write_u8(module, 0x7f, + TWL4030_PWMx_PWMxOFF); + if (status < 0) + goto done; + status = twl_i2c_write_u8(module, 0x7f, + TWL4030_PWMx_PWMxON); + if (status < 0) + goto done; + + /* init LED to not-driven (high) */ + module = TWL4030_MODULE_LED; + status = twl_i2c_read_u8(module, &cached_leden, + TWL4030_LED_LEDEN); + if (status < 0) + goto done; + cached_leden &= ~ledclr_mask; + status = twl_i2c_write_u8(module, cached_leden, + TWL4030_LED_LEDEN); + if (status < 0) + goto done; + + status = 0; + goto done; + } + + /* on first use, turn GPIO module "on" */ + if (!gpio_usage_count) { + struct twl4030_gpio_platform_data *pdata; + u8 value = MASK_GPIO_CTRL_GPIO_ON; + + /* optionally have the first two GPIOs switch vMMC1 + * and vMMC2 power supplies based on card presence. + */ + pdata = chip->dev->platform_data; + value |= pdata->mmc_cd & 0x03; + + status = gpio_twl4030_write(REG_GPIO_CTRL, value); + } + + if (!status) + gpio_usage_count |= (0x1 << offset); + +done: + mutex_unlock(&gpio_lock); + return status; +} + +static void twl_free(struct gpio_chip *chip, unsigned offset) +{ + if (offset >= TWL4030_GPIO_MAX) { + twl4030_led_set_value(offset - TWL4030_GPIO_MAX, 1); + return; + } + + mutex_lock(&gpio_lock); + + gpio_usage_count &= ~BIT(offset); + + /* on last use, switch off GPIO module */ + if (!gpio_usage_count) + gpio_twl4030_write(REG_GPIO_CTRL, 0x0); + + mutex_unlock(&gpio_lock); +} + +static int twl_direction_in(struct gpio_chip *chip, unsigned offset) +{ + return (offset < TWL4030_GPIO_MAX) + ? twl4030_set_gpio_direction(offset, 1) + : -EINVAL; +} + +static int twl_get(struct gpio_chip *chip, unsigned offset) +{ + int status = 0; + + if (offset < TWL4030_GPIO_MAX) + status = twl4030_get_gpio_datain(offset); + else if (offset == TWL4030_GPIO_MAX) + status = cached_leden & LEDEN_LEDAON; + else + status = cached_leden & LEDEN_LEDBON; + return (status < 0) ? 0 : status; +} + +static int twl_direction_out(struct gpio_chip *chip, unsigned offset, int value) +{ + if (offset < TWL4030_GPIO_MAX) { + twl4030_set_gpio_dataout(offset, value); + return twl4030_set_gpio_direction(offset, 0); + } else { + twl4030_led_set_value(offset - TWL4030_GPIO_MAX, value); + return 0; + } +} + +static void twl_set(struct gpio_chip *chip, unsigned offset, int value) +{ + if (offset < TWL4030_GPIO_MAX) + twl4030_set_gpio_dataout(offset, value); + else + twl4030_led_set_value(offset - TWL4030_GPIO_MAX, value); +} + +static int twl_to_irq(struct gpio_chip *chip, unsigned offset) +{ + return (twl4030_gpio_irq_base && (offset < TWL4030_GPIO_MAX)) + ? (twl4030_gpio_irq_base + offset) + : -EINVAL; +} + +static struct gpio_chip twl_gpiochip = { + .label = "twl4030", + .owner = THIS_MODULE, + .request = twl_request, + .free = twl_free, + .direction_input = twl_direction_in, + .get = twl_get, + .direction_output = twl_direction_out, + .set = twl_set, + .to_irq = twl_to_irq, + .can_sleep = 1, +}; + +/*----------------------------------------------------------------------*/ + +static int __devinit gpio_twl4030_pulls(u32 ups, u32 downs) +{ + u8 message[6]; + unsigned i, gpio_bit; + + /* For most pins, a pulldown was enabled by default. + * We should have data that's specific to this board. + */ + for (gpio_bit = 1, i = 1; i < 6; i++) { + u8 bit_mask; + unsigned j; + + for (bit_mask = 0, j = 0; j < 8; j += 2, gpio_bit <<= 1) { + if (ups & gpio_bit) + bit_mask |= 1 << (j + 1); + else if (downs & gpio_bit) + bit_mask |= 1 << (j + 0); + } + message[i] = bit_mask; + } + + return twl_i2c_write(TWL4030_MODULE_GPIO, message, + REG_GPIOPUPDCTR1, 5); +} + +static int __devinit gpio_twl4030_debounce(u32 debounce, u8 mmc_cd) +{ + u8 message[4]; + + /* 30 msec of debouncing is always used for MMC card detect, + * and is optional for everything else. + */ + message[1] = (debounce & 0xff) | (mmc_cd & 0x03); + debounce >>= 8; + message[2] = (debounce & 0xff); + debounce >>= 8; + message[3] = (debounce & 0x03); + + return twl_i2c_write(TWL4030_MODULE_GPIO, message, + REG_GPIO_DEBEN1, 3); +} + +static int gpio_twl4030_remove(struct platform_device *pdev); + +static int __devinit gpio_twl4030_probe(struct platform_device *pdev) +{ + struct twl4030_gpio_platform_data *pdata = pdev->dev.platform_data; + int ret; + + /* maybe setup IRQs */ + if (pdata->irq_base) { + if (is_module()) { + dev_err(&pdev->dev, + "can't dispatch IRQs from modules\n"); + goto no_irqs; + } + ret = twl4030_sih_setup(TWL4030_MODULE_GPIO); + if (ret < 0) + return ret; + WARN_ON(ret != pdata->irq_base); + twl4030_gpio_irq_base = ret; + } + +no_irqs: + /* + * NOTE: boards may waste power if they don't set pullups + * and pulldowns correctly ... default for non-ULPI pins is + * pulldown, and some other pins may have external pullups + * or pulldowns. Careful! + */ + ret = gpio_twl4030_pulls(pdata->pullups, pdata->pulldowns); + if (ret) + dev_dbg(&pdev->dev, "pullups %.05x %.05x --> %d\n", + pdata->pullups, pdata->pulldowns, + ret); + + ret = gpio_twl4030_debounce(pdata->debounce, pdata->mmc_cd); + if (ret) + dev_dbg(&pdev->dev, "debounce %.03x %.01x --> %d\n", + pdata->debounce, pdata->mmc_cd, + ret); + + twl_gpiochip.base = pdata->gpio_base; + twl_gpiochip.ngpio = TWL4030_GPIO_MAX; + twl_gpiochip.dev = &pdev->dev; + + /* NOTE: we assume VIBRA_CTL.VIBRA_EN, in MODULE_AUDIO_VOICE, + * is (still) clear if use_leds is set. + */ + if (pdata->use_leds) + twl_gpiochip.ngpio += 2; + + ret = gpiochip_add(&twl_gpiochip); + if (ret < 0) { + dev_err(&pdev->dev, + "could not register gpiochip, %d\n", + ret); + twl_gpiochip.ngpio = 0; + gpio_twl4030_remove(pdev); + } else if (pdata->setup) { + int status; + + status = pdata->setup(&pdev->dev, + pdata->gpio_base, TWL4030_GPIO_MAX); + if (status) + dev_dbg(&pdev->dev, "setup --> %d\n", status); + } + + return ret; +} + +/* Cannot use __devexit as gpio_twl4030_probe() calls us */ +static int gpio_twl4030_remove(struct platform_device *pdev) +{ + struct twl4030_gpio_platform_data *pdata = pdev->dev.platform_data; + int status; + + if (pdata->teardown) { + status = pdata->teardown(&pdev->dev, + pdata->gpio_base, TWL4030_GPIO_MAX); + if (status) { + dev_dbg(&pdev->dev, "teardown --> %d\n", status); + return status; + } + } + + status = gpiochip_remove(&twl_gpiochip); + if (status < 0) + return status; + + if (is_module()) + return 0; + + /* REVISIT no support yet for deregistering all the IRQs */ + WARN_ON(1); + return -EIO; +} + +/* Note: this hardware lives inside an I2C-based multi-function device. */ +MODULE_ALIAS("platform:twl4030_gpio"); + +static struct platform_driver gpio_twl4030_driver = { + .driver.name = "twl4030_gpio", + .driver.owner = THIS_MODULE, + .probe = gpio_twl4030_probe, + .remove = gpio_twl4030_remove, +}; + +static int __init gpio_twl4030_init(void) +{ + return platform_driver_register(&gpio_twl4030_driver); +} +subsys_initcall(gpio_twl4030_init); + +static void __exit gpio_twl4030_exit(void) +{ + platform_driver_unregister(&gpio_twl4030_driver); +} +module_exit(gpio_twl4030_exit); + +MODULE_AUTHOR("Texas Instruments, Inc."); +MODULE_DESCRIPTION("GPIO interface for TWL4030"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/ucb1400_gpio.c b/drivers/gpio/ucb1400_gpio.c new file mode 100644 index 00000000000..50e6bd1392c --- /dev/null +++ b/drivers/gpio/ucb1400_gpio.c @@ -0,0 +1,125 @@ +/* + * Philips UCB1400 GPIO driver + * + * Author: Marek Vasut <marek.vasut@gmail.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. + * + */ + +#include <linux/module.h> +#include <linux/ucb1400.h> + +struct ucb1400_gpio_data *ucbdata; + +static int ucb1400_gpio_dir_in(struct gpio_chip *gc, unsigned off) +{ + struct ucb1400_gpio *gpio; + gpio = container_of(gc, struct ucb1400_gpio, gc); + ucb1400_gpio_set_direction(gpio->ac97, off, 0); + return 0; +} + +static int ucb1400_gpio_dir_out(struct gpio_chip *gc, unsigned off, int val) +{ + struct ucb1400_gpio *gpio; + gpio = container_of(gc, struct ucb1400_gpio, gc); + ucb1400_gpio_set_direction(gpio->ac97, off, 1); + ucb1400_gpio_set_value(gpio->ac97, off, val); + return 0; +} + +static int ucb1400_gpio_get(struct gpio_chip *gc, unsigned off) +{ + struct ucb1400_gpio *gpio; + gpio = container_of(gc, struct ucb1400_gpio, gc); + return ucb1400_gpio_get_value(gpio->ac97, off); +} + +static void ucb1400_gpio_set(struct gpio_chip *gc, unsigned off, int val) +{ + struct ucb1400_gpio *gpio; + gpio = container_of(gc, struct ucb1400_gpio, gc); + ucb1400_gpio_set_value(gpio->ac97, off, val); +} + +static int ucb1400_gpio_probe(struct platform_device *dev) +{ + struct ucb1400_gpio *ucb = dev->dev.platform_data; + int err = 0; + + if (!(ucbdata && ucbdata->gpio_offset)) { + err = -EINVAL; + goto err; + } + + platform_set_drvdata(dev, ucb); + + ucb->gc.label = "ucb1400_gpio"; + ucb->gc.base = ucbdata->gpio_offset; + ucb->gc.ngpio = 10; + ucb->gc.owner = THIS_MODULE; + + ucb->gc.direction_input = ucb1400_gpio_dir_in; + ucb->gc.direction_output = ucb1400_gpio_dir_out; + ucb->gc.get = ucb1400_gpio_get; + ucb->gc.set = ucb1400_gpio_set; + ucb->gc.can_sleep = 1; + + err = gpiochip_add(&ucb->gc); + if (err) + goto err; + + if (ucbdata && ucbdata->gpio_setup) + err = ucbdata->gpio_setup(&dev->dev, ucb->gc.ngpio); + +err: + return err; + +} + +static int ucb1400_gpio_remove(struct platform_device *dev) +{ + int err = 0; + struct ucb1400_gpio *ucb = platform_get_drvdata(dev); + + if (ucbdata && ucbdata->gpio_teardown) { + err = ucbdata->gpio_teardown(&dev->dev, ucb->gc.ngpio); + if (err) + return err; + } + + err = gpiochip_remove(&ucb->gc); + return err; +} + +static struct platform_driver ucb1400_gpio_driver = { + .probe = ucb1400_gpio_probe, + .remove = ucb1400_gpio_remove, + .driver = { + .name = "ucb1400_gpio" + }, +}; + +static int __init ucb1400_gpio_init(void) +{ + return platform_driver_register(&ucb1400_gpio_driver); +} + +static void __exit ucb1400_gpio_exit(void) +{ + platform_driver_unregister(&ucb1400_gpio_driver); +} + +void __init ucb1400_gpio_set_data(struct ucb1400_gpio_data *data) +{ + ucbdata = data; +} + +module_init(ucb1400_gpio_init); +module_exit(ucb1400_gpio_exit); + +MODULE_DESCRIPTION("Philips UCB1400 GPIO driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/vr41xx_giu.c b/drivers/gpio/vr41xx_giu.c new file mode 100644 index 00000000000..b16c9a8c03f --- /dev/null +++ b/drivers/gpio/vr41xx_giu.c @@ -0,0 +1,585 @@ +/* + * Driver for NEC VR4100 series General-purpose I/O Unit. + * + * Copyright (C) 2002 MontaVista Software Inc. + * Author: Yoichi Yuasa <source@mvista.com> + * Copyright (C) 2003-2009 Yoichi Yuasa <yuasa@linux-mips.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/gpio.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/types.h> + +#include <asm/vr41xx/giu.h> +#include <asm/vr41xx/irq.h> +#include <asm/vr41xx/vr41xx.h> + +MODULE_AUTHOR("Yoichi Yuasa <yuasa@linux-mips.org>"); +MODULE_DESCRIPTION("NEC VR4100 series General-purpose I/O Unit driver"); +MODULE_LICENSE("GPL"); + +#define GIUIOSELL 0x00 +#define GIUIOSELH 0x02 +#define GIUPIODL 0x04 +#define GIUPIODH 0x06 +#define GIUINTSTATL 0x08 +#define GIUINTSTATH 0x0a +#define GIUINTENL 0x0c +#define GIUINTENH 0x0e +#define GIUINTTYPL 0x10 +#define GIUINTTYPH 0x12 +#define GIUINTALSELL 0x14 +#define GIUINTALSELH 0x16 +#define GIUINTHTSELL 0x18 +#define GIUINTHTSELH 0x1a +#define GIUPODATL 0x1c +#define GIUPODATEN 0x1c +#define GIUPODATH 0x1e + #define PIOEN0 0x0100 + #define PIOEN1 0x0200 +#define GIUPODAT 0x1e +#define GIUFEDGEINHL 0x20 +#define GIUFEDGEINHH 0x22 +#define GIUREDGEINHL 0x24 +#define GIUREDGEINHH 0x26 + +#define GIUUSEUPDN 0x1e0 +#define GIUTERMUPDN 0x1e2 + +#define GPIO_HAS_PULLUPDOWN_IO 0x0001 +#define GPIO_HAS_OUTPUT_ENABLE 0x0002 +#define GPIO_HAS_INTERRUPT_EDGE_SELECT 0x0100 + +enum { + GPIO_INPUT, + GPIO_OUTPUT, +}; + +static DEFINE_SPINLOCK(giu_lock); +static unsigned long giu_flags; + +static void __iomem *giu_base; + +#define giu_read(offset) readw(giu_base + (offset)) +#define giu_write(offset, value) writew((value), giu_base + (offset)) + +#define GPIO_PIN_OF_IRQ(irq) ((irq) - GIU_IRQ_BASE) +#define GIUINT_HIGH_OFFSET 16 +#define GIUINT_HIGH_MAX 32 + +static inline u16 giu_set(u16 offset, u16 set) +{ + u16 data; + + data = giu_read(offset); + data |= set; + giu_write(offset, data); + + return data; +} + +static inline u16 giu_clear(u16 offset, u16 clear) +{ + u16 data; + + data = giu_read(offset); + data &= ~clear; + giu_write(offset, data); + + return data; +} + +static void ack_giuint_low(unsigned int irq) +{ + giu_write(GIUINTSTATL, 1 << GPIO_PIN_OF_IRQ(irq)); +} + +static void mask_giuint_low(unsigned int irq) +{ + giu_clear(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(irq)); +} + +static void mask_ack_giuint_low(unsigned int irq) +{ + unsigned int pin; + + pin = GPIO_PIN_OF_IRQ(irq); + giu_clear(GIUINTENL, 1 << pin); + giu_write(GIUINTSTATL, 1 << pin); +} + +static void unmask_giuint_low(unsigned int irq) +{ + giu_set(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(irq)); +} + +static struct irq_chip giuint_low_irq_chip = { + .name = "GIUINTL", + .ack = ack_giuint_low, + .mask = mask_giuint_low, + .mask_ack = mask_ack_giuint_low, + .unmask = unmask_giuint_low, +}; + +static void ack_giuint_high(unsigned int irq) +{ + giu_write(GIUINTSTATH, + 1 << (GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET)); +} + +static void mask_giuint_high(unsigned int irq) +{ + giu_clear(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET)); +} + +static void mask_ack_giuint_high(unsigned int irq) +{ + unsigned int pin; + + pin = GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET; + giu_clear(GIUINTENH, 1 << pin); + giu_write(GIUINTSTATH, 1 << pin); +} + +static void unmask_giuint_high(unsigned int irq) +{ + giu_set(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET)); +} + +static struct irq_chip giuint_high_irq_chip = { + .name = "GIUINTH", + .ack = ack_giuint_high, + .mask = mask_giuint_high, + .mask_ack = mask_ack_giuint_high, + .unmask = unmask_giuint_high, +}; + +static int giu_get_irq(unsigned int irq) +{ + u16 pendl, pendh, maskl, maskh; + int i; + + pendl = giu_read(GIUINTSTATL); + pendh = giu_read(GIUINTSTATH); + maskl = giu_read(GIUINTENL); + maskh = giu_read(GIUINTENH); + + maskl &= pendl; + maskh &= pendh; + + if (maskl) { + for (i = 0; i < 16; i++) { + if (maskl & (1 << i)) + return GIU_IRQ(i); + } + } else if (maskh) { + for (i = 0; i < 16; i++) { + if (maskh & (1 << i)) + return GIU_IRQ(i + GIUINT_HIGH_OFFSET); + } + } + + printk(KERN_ERR "spurious GIU interrupt: %04x(%04x),%04x(%04x)\n", + maskl, pendl, maskh, pendh); + + atomic_inc(&irq_err_count); + + return -EINVAL; +} + +void vr41xx_set_irq_trigger(unsigned int pin, irq_trigger_t trigger, + irq_signal_t signal) +{ + u16 mask; + + if (pin < GIUINT_HIGH_OFFSET) { + mask = 1 << pin; + if (trigger != IRQ_TRIGGER_LEVEL) { + giu_set(GIUINTTYPL, mask); + if (signal == IRQ_SIGNAL_HOLD) + giu_set(GIUINTHTSELL, mask); + else + giu_clear(GIUINTHTSELL, mask); + if (giu_flags & GPIO_HAS_INTERRUPT_EDGE_SELECT) { + switch (trigger) { + case IRQ_TRIGGER_EDGE_FALLING: + giu_set(GIUFEDGEINHL, mask); + giu_clear(GIUREDGEINHL, mask); + break; + case IRQ_TRIGGER_EDGE_RISING: + giu_clear(GIUFEDGEINHL, mask); + giu_set(GIUREDGEINHL, mask); + break; + default: + giu_set(GIUFEDGEINHL, mask); + giu_set(GIUREDGEINHL, mask); + break; + } + } + set_irq_chip_and_handler(GIU_IRQ(pin), + &giuint_low_irq_chip, + handle_edge_irq); + } else { + giu_clear(GIUINTTYPL, mask); + giu_clear(GIUINTHTSELL, mask); + set_irq_chip_and_handler(GIU_IRQ(pin), + &giuint_low_irq_chip, + handle_level_irq); + } + giu_write(GIUINTSTATL, mask); + } else if (pin < GIUINT_HIGH_MAX) { + mask = 1 << (pin - GIUINT_HIGH_OFFSET); + if (trigger != IRQ_TRIGGER_LEVEL) { + giu_set(GIUINTTYPH, mask); + if (signal == IRQ_SIGNAL_HOLD) + giu_set(GIUINTHTSELH, mask); + else + giu_clear(GIUINTHTSELH, mask); + if (giu_flags & GPIO_HAS_INTERRUPT_EDGE_SELECT) { + switch (trigger) { + case IRQ_TRIGGER_EDGE_FALLING: + giu_set(GIUFEDGEINHH, mask); + giu_clear(GIUREDGEINHH, mask); + break; + case IRQ_TRIGGER_EDGE_RISING: + giu_clear(GIUFEDGEINHH, mask); + giu_set(GIUREDGEINHH, mask); + break; + default: + giu_set(GIUFEDGEINHH, mask); + giu_set(GIUREDGEINHH, mask); + break; + } + } + set_irq_chip_and_handler(GIU_IRQ(pin), + &giuint_high_irq_chip, + handle_edge_irq); + } else { + giu_clear(GIUINTTYPH, mask); + giu_clear(GIUINTHTSELH, mask); + set_irq_chip_and_handler(GIU_IRQ(pin), + &giuint_high_irq_chip, + handle_level_irq); + } + giu_write(GIUINTSTATH, mask); + } +} +EXPORT_SYMBOL_GPL(vr41xx_set_irq_trigger); + +void vr41xx_set_irq_level(unsigned int pin, irq_level_t level) +{ + u16 mask; + + if (pin < GIUINT_HIGH_OFFSET) { + mask = 1 << pin; + if (level == IRQ_LEVEL_HIGH) + giu_set(GIUINTALSELL, mask); + else + giu_clear(GIUINTALSELL, mask); + giu_write(GIUINTSTATL, mask); + } else if (pin < GIUINT_HIGH_MAX) { + mask = 1 << (pin - GIUINT_HIGH_OFFSET); + if (level == IRQ_LEVEL_HIGH) + giu_set(GIUINTALSELH, mask); + else + giu_clear(GIUINTALSELH, mask); + giu_write(GIUINTSTATH, mask); + } +} +EXPORT_SYMBOL_GPL(vr41xx_set_irq_level); + +static int giu_set_direction(struct gpio_chip *chip, unsigned pin, int dir) +{ + u16 offset, mask, reg; + unsigned long flags; + + if (pin >= chip->ngpio) + return -EINVAL; + + if (pin < 16) { + offset = GIUIOSELL; + mask = 1 << pin; + } else if (pin < 32) { + offset = GIUIOSELH; + mask = 1 << (pin - 16); + } else { + if (giu_flags & GPIO_HAS_OUTPUT_ENABLE) { + offset = GIUPODATEN; + mask = 1 << (pin - 32); + } else { + switch (pin) { + case 48: + offset = GIUPODATH; + mask = PIOEN0; + break; + case 49: + offset = GIUPODATH; + mask = PIOEN1; + break; + default: + return -EINVAL; + } + } + } + + spin_lock_irqsave(&giu_lock, flags); + + reg = giu_read(offset); + if (dir == GPIO_OUTPUT) + reg |= mask; + else + reg &= ~mask; + giu_write(offset, reg); + + spin_unlock_irqrestore(&giu_lock, flags); + + return 0; +} + +int vr41xx_gpio_pullupdown(unsigned int pin, gpio_pull_t pull) +{ + u16 reg, mask; + unsigned long flags; + + if ((giu_flags & GPIO_HAS_PULLUPDOWN_IO) != GPIO_HAS_PULLUPDOWN_IO) + return -EPERM; + + if (pin >= 15) + return -EINVAL; + + mask = 1 << pin; + + spin_lock_irqsave(&giu_lock, flags); + + if (pull == GPIO_PULL_UP || pull == GPIO_PULL_DOWN) { + reg = giu_read(GIUTERMUPDN); + if (pull == GPIO_PULL_UP) + reg |= mask; + else + reg &= ~mask; + giu_write(GIUTERMUPDN, reg); + + reg = giu_read(GIUUSEUPDN); + reg |= mask; + giu_write(GIUUSEUPDN, reg); + } else { + reg = giu_read(GIUUSEUPDN); + reg &= ~mask; + giu_write(GIUUSEUPDN, reg); + } + + spin_unlock_irqrestore(&giu_lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(vr41xx_gpio_pullupdown); + +static int vr41xx_gpio_get(struct gpio_chip *chip, unsigned pin) +{ + u16 reg, mask; + + if (pin >= chip->ngpio) + return -EINVAL; + + if (pin < 16) { + reg = giu_read(GIUPIODL); + mask = 1 << pin; + } else if (pin < 32) { + reg = giu_read(GIUPIODH); + mask = 1 << (pin - 16); + } else if (pin < 48) { + reg = giu_read(GIUPODATL); + mask = 1 << (pin - 32); + } else { + reg = giu_read(GIUPODATH); + mask = 1 << (pin - 48); + } + + if (reg & mask) + return 1; + + return 0; +} + +static void vr41xx_gpio_set(struct gpio_chip *chip, unsigned pin, + int value) +{ + u16 offset, mask, reg; + unsigned long flags; + + if (pin >= chip->ngpio) + return; + + if (pin < 16) { + offset = GIUPIODL; + mask = 1 << pin; + } else if (pin < 32) { + offset = GIUPIODH; + mask = 1 << (pin - 16); + } else if (pin < 48) { + offset = GIUPODATL; + mask = 1 << (pin - 32); + } else { + offset = GIUPODATH; + mask = 1 << (pin - 48); + } + + spin_lock_irqsave(&giu_lock, flags); + + reg = giu_read(offset); + if (value) + reg |= mask; + else + reg &= ~mask; + giu_write(offset, reg); + + spin_unlock_irqrestore(&giu_lock, flags); +} + + +static int vr41xx_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + return giu_set_direction(chip, offset, GPIO_INPUT); +} + +static int vr41xx_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + vr41xx_gpio_set(chip, offset, value); + + return giu_set_direction(chip, offset, GPIO_OUTPUT); +} + +static int vr41xx_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + if (offset >= chip->ngpio) + return -EINVAL; + + return GIU_IRQ_BASE + offset; +} + +static struct gpio_chip vr41xx_gpio_chip = { + .label = "vr41xx", + .owner = THIS_MODULE, + .direction_input = vr41xx_gpio_direction_input, + .get = vr41xx_gpio_get, + .direction_output = vr41xx_gpio_direction_output, + .set = vr41xx_gpio_set, + .to_irq = vr41xx_gpio_to_irq, +}; + +static int __devinit giu_probe(struct platform_device *pdev) +{ + struct resource *res; + unsigned int trigger, i, pin; + struct irq_chip *chip; + int irq, retval; + + switch (pdev->id) { + case GPIO_50PINS_PULLUPDOWN: + giu_flags = GPIO_HAS_PULLUPDOWN_IO; + vr41xx_gpio_chip.ngpio = 50; + break; + case GPIO_36PINS: + vr41xx_gpio_chip.ngpio = 36; + break; + case GPIO_48PINS_EDGE_SELECT: + giu_flags = GPIO_HAS_INTERRUPT_EDGE_SELECT; + vr41xx_gpio_chip.ngpio = 48; + break; + default: + dev_err(&pdev->dev, "GIU: unknown ID %d\n", pdev->id); + return -ENODEV; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EBUSY; + + giu_base = ioremap(res->start, res->end - res->start + 1); + if (!giu_base) + return -ENOMEM; + + vr41xx_gpio_chip.dev = &pdev->dev; + + retval = gpiochip_add(&vr41xx_gpio_chip); + + giu_write(GIUINTENL, 0); + giu_write(GIUINTENH, 0); + + trigger = giu_read(GIUINTTYPH) << 16; + trigger |= giu_read(GIUINTTYPL); + for (i = GIU_IRQ_BASE; i <= GIU_IRQ_LAST; i++) { + pin = GPIO_PIN_OF_IRQ(i); + if (pin < GIUINT_HIGH_OFFSET) + chip = &giuint_low_irq_chip; + else + chip = &giuint_high_irq_chip; + + if (trigger & (1 << pin)) + set_irq_chip_and_handler(i, chip, handle_edge_irq); + else + set_irq_chip_and_handler(i, chip, handle_level_irq); + + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0 || irq >= nr_irqs) + return -EBUSY; + + return cascade_irq(irq, giu_get_irq); +} + +static int __devexit giu_remove(struct platform_device *pdev) +{ + if (giu_base) { + iounmap(giu_base); + giu_base = NULL; + } + + return 0; +} + +static struct platform_driver giu_device_driver = { + .probe = giu_probe, + .remove = __devexit_p(giu_remove), + .driver = { + .name = "GIU", + .owner = THIS_MODULE, + }, +}; + +static int __init vr41xx_giu_init(void) +{ + return platform_driver_register(&giu_device_driver); +} + +static void __exit vr41xx_giu_exit(void) +{ + platform_driver_unregister(&giu_device_driver); +} + +module_init(vr41xx_giu_init); +module_exit(vr41xx_giu_exit); diff --git a/drivers/gpio/wm831x-gpio.c b/drivers/gpio/wm831x-gpio.c new file mode 100644 index 00000000000..c5a00f7a7bf --- /dev/null +++ b/drivers/gpio/wm831x-gpio.c @@ -0,0 +1,271 @@ +/* + * wm831x-gpio.c -- gpiolib support for Wolfson WM831x PMICs + * + * Copyright 2009 Wolfson Microelectronics PLC. + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/gpio.h> +#include <linux/mfd/core.h> +#include <linux/platform_device.h> +#include <linux/seq_file.h> + +#include <linux/mfd/wm831x/core.h> +#include <linux/mfd/wm831x/pdata.h> +#include <linux/mfd/wm831x/gpio.h> +#include <linux/mfd/wm831x/irq.h> + +struct wm831x_gpio { + struct wm831x *wm831x; + struct gpio_chip gpio_chip; +}; + +static inline struct wm831x_gpio *to_wm831x_gpio(struct gpio_chip *chip) +{ + return container_of(chip, struct wm831x_gpio, gpio_chip); +} + +static int wm831x_gpio_direction_in(struct gpio_chip *chip, unsigned offset) +{ + struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); + struct wm831x *wm831x = wm831x_gpio->wm831x; + + return wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + offset, + WM831X_GPN_DIR | WM831X_GPN_TRI, + WM831X_GPN_DIR); +} + +static int wm831x_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); + struct wm831x *wm831x = wm831x_gpio->wm831x; + int ret; + + ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL); + if (ret < 0) + return ret; + + if (ret & 1 << offset) + return 1; + else + return 0; +} + +static void wm831x_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); + struct wm831x *wm831x = wm831x_gpio->wm831x; + + wm831x_set_bits(wm831x, WM831X_GPIO_LEVEL, 1 << offset, + value << offset); +} + +static int wm831x_gpio_direction_out(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); + struct wm831x *wm831x = wm831x_gpio->wm831x; + int ret; + + ret = wm831x_set_bits(wm831x, WM831X_GPIO1_CONTROL + offset, + WM831X_GPN_DIR | WM831X_GPN_TRI, 0); + if (ret < 0) + return ret; + + /* Can only set GPIO state once it's in output mode */ + wm831x_gpio_set(chip, offset, value); + + return 0; +} + +static int wm831x_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); + struct wm831x *wm831x = wm831x_gpio->wm831x; + + if (!wm831x->irq_base) + return -EINVAL; + + return wm831x->irq_base + WM831X_IRQ_GPIO_1 + offset; +} + +#ifdef CONFIG_DEBUG_FS +static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip); + struct wm831x *wm831x = wm831x_gpio->wm831x; + int i; + + for (i = 0; i < chip->ngpio; i++) { + int gpio = i + chip->base; + int reg; + const char *label, *pull, *powerdomain; + + /* We report the GPIO even if it's not requested since + * we're also reporting things like alternate + * functions which apply even when the GPIO is not in + * use as a GPIO. + */ + label = gpiochip_is_requested(chip, i); + if (!label) + label = "Unrequested"; + + seq_printf(s, " gpio-%-3d (%-20.20s) ", gpio, label); + + reg = wm831x_reg_read(wm831x, WM831X_GPIO1_CONTROL + i); + if (reg < 0) { + dev_err(wm831x->dev, + "GPIO control %d read failed: %d\n", + gpio, reg); + seq_printf(s, "\n"); + continue; + } + + switch (reg & WM831X_GPN_PULL_MASK) { + case WM831X_GPIO_PULL_NONE: + pull = "nopull"; + break; + case WM831X_GPIO_PULL_DOWN: + pull = "pulldown"; + break; + case WM831X_GPIO_PULL_UP: + pull = "pullup"; + default: + pull = "INVALID PULL"; + break; + } + + switch (i + 1) { + case 1 ... 3: + case 7 ... 9: + if (reg & WM831X_GPN_PWR_DOM) + powerdomain = "VPMIC"; + else + powerdomain = "DBVDD"; + break; + + case 4 ... 6: + case 10 ... 12: + if (reg & WM831X_GPN_PWR_DOM) + powerdomain = "SYSVDD"; + else + powerdomain = "DBVDD"; + break; + + case 13 ... 16: + powerdomain = "TPVDD"; + break; + + default: + BUG(); + break; + } + + seq_printf(s, " %s %s %s %s%s\n" + " %s%s (0x%4x)\n", + reg & WM831X_GPN_DIR ? "in" : "out", + wm831x_gpio_get(chip, i) ? "high" : "low", + pull, + powerdomain, + reg & WM831X_GPN_POL ? " inverted" : "", + reg & WM831X_GPN_OD ? "open-drain" : "CMOS", + reg & WM831X_GPN_TRI ? " tristated" : "", + reg); + } +} +#else +#define wm831x_gpio_dbg_show NULL +#endif + +static struct gpio_chip template_chip = { + .label = "wm831x", + .owner = THIS_MODULE, + .direction_input = wm831x_gpio_direction_in, + .get = wm831x_gpio_get, + .direction_output = wm831x_gpio_direction_out, + .set = wm831x_gpio_set, + .to_irq = wm831x_gpio_to_irq, + .dbg_show = wm831x_gpio_dbg_show, + .can_sleep = 1, +}; + +static int __devinit wm831x_gpio_probe(struct platform_device *pdev) +{ + struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); + struct wm831x_pdata *pdata = wm831x->dev->platform_data; + struct wm831x_gpio *wm831x_gpio; + int ret; + + wm831x_gpio = kzalloc(sizeof(*wm831x_gpio), GFP_KERNEL); + if (wm831x_gpio == NULL) + return -ENOMEM; + + wm831x_gpio->wm831x = wm831x; + wm831x_gpio->gpio_chip = template_chip; + wm831x_gpio->gpio_chip.ngpio = wm831x->num_gpio; + wm831x_gpio->gpio_chip.dev = &pdev->dev; + if (pdata && pdata->gpio_base) + wm831x_gpio->gpio_chip.base = pdata->gpio_base; + else + wm831x_gpio->gpio_chip.base = -1; + + ret = gpiochip_add(&wm831x_gpio->gpio_chip); + if (ret < 0) { + dev_err(&pdev->dev, "Could not register gpiochip, %d\n", + ret); + goto err; + } + + platform_set_drvdata(pdev, wm831x_gpio); + + return ret; + +err: + kfree(wm831x_gpio); + return ret; +} + +static int __devexit wm831x_gpio_remove(struct platform_device *pdev) +{ + struct wm831x_gpio *wm831x_gpio = platform_get_drvdata(pdev); + int ret; + + ret = gpiochip_remove(&wm831x_gpio->gpio_chip); + if (ret == 0) + kfree(wm831x_gpio); + + return ret; +} + +static struct platform_driver wm831x_gpio_driver = { + .driver.name = "wm831x-gpio", + .driver.owner = THIS_MODULE, + .probe = wm831x_gpio_probe, + .remove = __devexit_p(wm831x_gpio_remove), +}; + +static int __init wm831x_gpio_init(void) +{ + return platform_driver_register(&wm831x_gpio_driver); +} +subsys_initcall(wm831x_gpio_init); + +static void __exit wm831x_gpio_exit(void) +{ + platform_driver_unregister(&wm831x_gpio_driver); +} +module_exit(wm831x_gpio_exit); + +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); +MODULE_DESCRIPTION("GPIO interface for WM831x PMICs"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm831x-gpio"); diff --git a/drivers/gpio/xilinx_gpio.c b/drivers/gpio/xilinx_gpio.c new file mode 100644 index 00000000000..3c1177abebd --- /dev/null +++ b/drivers/gpio/xilinx_gpio.c @@ -0,0 +1,235 @@ +/* + * Xilinx gpio driver + * + * Copyright 2008 Xilinx, Inc. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/of_device.h> +#include <linux/of_platform.h> +#include <linux/of_gpio.h> +#include <linux/io.h> +#include <linux/gpio.h> + +/* Register Offset Definitions */ +#define XGPIO_DATA_OFFSET (0x0) /* Data register */ +#define XGPIO_TRI_OFFSET (0x4) /* I/O direction register */ + +struct xgpio_instance { + struct of_mm_gpio_chip mmchip; + u32 gpio_state; /* GPIO state shadow register */ + u32 gpio_dir; /* GPIO direction shadow register */ + spinlock_t gpio_lock; /* Lock used for synchronization */ +}; + +/** + * xgpio_get - Read the specified signal of the GPIO device. + * @gc: Pointer to gpio_chip device structure. + * @gpio: GPIO signal number. + * + * This function reads the specified signal of the GPIO device. It returns 0 if + * the signal clear, 1 if signal is set or negative value on error. + */ +static int xgpio_get(struct gpio_chip *gc, unsigned int gpio) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + + return (in_be32(mm_gc->regs + XGPIO_DATA_OFFSET) >> gpio) & 1; +} + +/** + * xgpio_set - Write the specified signal of the GPIO device. + * @gc: Pointer to gpio_chip device structure. + * @gpio: GPIO signal number. + * @val: Value to be written to specified signal. + * + * This function writes the specified value in to the specified signal of the + * GPIO device. + */ +static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +{ + unsigned long flags; + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct xgpio_instance *chip = + container_of(mm_gc, struct xgpio_instance, mmchip); + + spin_lock_irqsave(&chip->gpio_lock, flags); + + /* Write to GPIO signal and set its direction to output */ + if (val) + chip->gpio_state |= 1 << gpio; + else + chip->gpio_state &= ~(1 << gpio); + out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state); + + spin_unlock_irqrestore(&chip->gpio_lock, flags); +} + +/** + * xgpio_dir_in - Set the direction of the specified GPIO signal as input. + * @gc: Pointer to gpio_chip device structure. + * @gpio: GPIO signal number. + * + * This function sets the direction of specified GPIO signal as input. + * It returns 0 if direction of GPIO signals is set as input otherwise it + * returns negative error value. + */ +static int xgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) +{ + unsigned long flags; + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct xgpio_instance *chip = + container_of(mm_gc, struct xgpio_instance, mmchip); + + spin_lock_irqsave(&chip->gpio_lock, flags); + + /* Set the GPIO bit in shadow register and set direction as input */ + chip->gpio_dir |= (1 << gpio); + out_be32(mm_gc->regs + XGPIO_TRI_OFFSET, chip->gpio_dir); + + spin_unlock_irqrestore(&chip->gpio_lock, flags); + + return 0; +} + +/** + * xgpio_dir_out - Set the direction of the specified GPIO signal as output. + * @gc: Pointer to gpio_chip device structure. + * @gpio: GPIO signal number. + * @val: Value to be written to specified signal. + * + * This function sets the direction of specified GPIO signal as output. If all + * GPIO signals of GPIO chip is configured as input then it returns + * error otherwise it returns 0. + */ +static int xgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +{ + unsigned long flags; + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct xgpio_instance *chip = + container_of(mm_gc, struct xgpio_instance, mmchip); + + spin_lock_irqsave(&chip->gpio_lock, flags); + + /* Write state of GPIO signal */ + if (val) + chip->gpio_state |= 1 << gpio; + else + chip->gpio_state &= ~(1 << gpio); + out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state); + + /* Clear the GPIO bit in shadow register and set direction as output */ + chip->gpio_dir &= (~(1 << gpio)); + out_be32(mm_gc->regs + XGPIO_TRI_OFFSET, chip->gpio_dir); + + spin_unlock_irqrestore(&chip->gpio_lock, flags); + + return 0; +} + +/** + * xgpio_save_regs - Set initial values of GPIO pins + * @mm_gc: pointer to memory mapped GPIO chip structure + */ +static void xgpio_save_regs(struct of_mm_gpio_chip *mm_gc) +{ + struct xgpio_instance *chip = + container_of(mm_gc, struct xgpio_instance, mmchip); + + out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state); + out_be32(mm_gc->regs + XGPIO_TRI_OFFSET, chip->gpio_dir); +} + +/** + * xgpio_of_probe - Probe method for the GPIO device. + * @np: pointer to device tree node + * + * This function probes the GPIO device in the device tree. It initializes the + * driver data structure. It returns 0, if the driver is bound to the GPIO + * device, or a negative value if there is an error. + */ +static int __devinit xgpio_of_probe(struct device_node *np) +{ + struct xgpio_instance *chip; + struct of_gpio_chip *ofchip; + int status = 0; + const u32 *tree_info; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + ofchip = &chip->mmchip.of_gc; + + /* Update GPIO state shadow register with default value */ + tree_info = of_get_property(np, "xlnx,dout-default", NULL); + if (tree_info) + chip->gpio_state = *tree_info; + + /* Update GPIO direction shadow register with default value */ + chip->gpio_dir = 0xFFFFFFFF; /* By default, all pins are inputs */ + tree_info = of_get_property(np, "xlnx,tri-default", NULL); + if (tree_info) + chip->gpio_dir = *tree_info; + + /* Check device node and parent device node for device width */ + ofchip->gc.ngpio = 32; /* By default assume full GPIO controller */ + tree_info = of_get_property(np, "xlnx,gpio-width", NULL); + if (!tree_info) + tree_info = of_get_property(np->parent, + "xlnx,gpio-width", NULL); + if (tree_info) + ofchip->gc.ngpio = *tree_info; + + spin_lock_init(&chip->gpio_lock); + + ofchip->gpio_cells = 2; + ofchip->gc.direction_input = xgpio_dir_in; + ofchip->gc.direction_output = xgpio_dir_out; + ofchip->gc.get = xgpio_get; + ofchip->gc.set = xgpio_set; + + chip->mmchip.save_regs = xgpio_save_regs; + + /* Call the OF gpio helper to setup and register the GPIO device */ + status = of_mm_gpiochip_add(np, &chip->mmchip); + if (status) { + kfree(chip); + pr_err("%s: error in probe function with status %d\n", + np->full_name, status); + return status; + } + pr_info("XGpio: %s: registered\n", np->full_name); + return 0; +} + +static struct of_device_id xgpio_of_match[] __devinitdata = { + { .compatible = "xlnx,xps-gpio-1.00.a", }, + { /* end of list */ }, +}; + +static int __init xgpio_init(void) +{ + struct device_node *np; + + for_each_matching_node(np, xgpio_of_match) + xgpio_of_probe(np); + + return 0; +} + +/* Make sure we get initialized before anyone else tries to use us */ +subsys_initcall(xgpio_init); +/* No exit call at the moment as we cannot unregister of GPIO chips */ + +MODULE_AUTHOR("Xilinx, Inc."); +MODULE_DESCRIPTION("Xilinx GPIO driver"); +MODULE_LICENSE("GPL"); |