diff options
-rw-r--r-- | drivers/gpio/Kconfig | 7 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/pcf50633-gpio.c | 228 | ||||
-rw-r--r-- | drivers/mfd/Kconfig | 7 | ||||
-rw-r--r-- | drivers/mfd/Makefile | 1 | ||||
-rw-r--r-- | drivers/mfd/pcf50633-core.c | 12 | ||||
-rw-r--r-- | drivers/mfd/pcf50633-gpio.c | 121 | ||||
-rw-r--r-- | include/linux/mfd/pcf50633/core.h | 4 | ||||
-rw-r--r-- | include/linux/mfd/pcf50633/gpio.h | 59 |
9 files changed, 278 insertions, 162 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 2ad0128c63c..c1a4e822344 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -172,6 +172,13 @@ config GPIO_ADP5520 To compile this driver as a module, choose M here: the module will be called adp5520-gpio. +config GPIO_PCF50633 + tristate "GPIO support for NXP PCF50633" + depends on MFD_PCF50633 + help + Say yes here if you want to include support GPIO for pins on + the PCF50633 chip. + comment "PCI GPIO expanders:" config GPIO_BT8XX diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 00a532c9a1e..25d66f19fb2 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -19,3 +19,4 @@ obj-$(CONFIG_GPIO_XILINX) += xilinx_gpio.o obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o obj-$(CONFIG_GPIO_WM831X) += wm831x-gpio.o +obj-$(CONFIG_GPIO_PCF50633) += pcf50633-gpio.o diff --git a/drivers/gpio/pcf50633-gpio.c b/drivers/gpio/pcf50633-gpio.c new file mode 100644 index 00000000000..dc49defed23 --- /dev/null +++ b/drivers/gpio/pcf50633-gpio.c @@ -0,0 +1,228 @@ +/* NXP PCF50633 GPIO Driver + * + * (C) 2006-2008 by Openmoko, Inc. + * Author: Balaji Rao <balajirrao@openmoko.org> + * Copyright 2010, Lars-Peter Clausen <lars@metafoo.de> + * All rights reserved. + * + * Broken down from monstrous PCF50633 driver mainly by + * Harald Welte, Andy Green and Werner Almesberger + * + * 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/platform_device.h> + +#include <linux/mfd/pcf50633/core.h> +#include <linux/mfd/pcf50633/gpio.h> +#include <linux/gpio.h> + +#define PCF50633_REG_GPIOCTL 0x13 +#define PCF50633_REG_GPIOCFG(x) (0x14 + (x)) + +enum pcf50633_regulator_id { + PCF50633_REGULATOR_AUTO, + PCF50633_REGULATOR_DOWN1, + PCF50633_REGULATOR_DOWN2, + PCF50633_REGULATOR_LDO1, + PCF50633_REGULATOR_LDO2, + PCF50633_REGULATOR_LDO3, + PCF50633_REGULATOR_LDO4, + PCF50633_REGULATOR_LDO5, + PCF50633_REGULATOR_LDO6, + PCF50633_REGULATOR_HCLDO, + PCF50633_REGULATOR_MEMLDO, +}; + +#define PCF50633_REG_AUTOOUT 0x1a +#define PCF50633_REG_DOWN1OUT 0x1e +#define PCF50633_REG_DOWN2OUT 0x22 +#define PCF50633_REG_MEMLDOOUT 0x26 +#define PCF50633_REG_LDO1OUT 0x2d +#define PCF50633_REG_LDO2OUT 0x2f +#define PCF50633_REG_LDO3OUT 0x31 +#define PCF50633_REG_LDO4OUT 0x33 +#define PCF50633_REG_LDO5OUT 0x35 +#define PCF50633_REG_LDO6OUT 0x37 +#define PCF50633_REG_HCLDOOUT 0x39 + +static const u8 pcf50633_regulator_registers[PCF50633_NUM_REGULATORS] = { + [PCF50633_REGULATOR_AUTO] = PCF50633_REG_AUTOOUT, + [PCF50633_REGULATOR_DOWN1] = PCF50633_REG_DOWN1OUT, + [PCF50633_REGULATOR_DOWN2] = PCF50633_REG_DOWN2OUT, + [PCF50633_REGULATOR_MEMLDO] = PCF50633_REG_MEMLDOOUT, + [PCF50633_REGULATOR_LDO1] = PCF50633_REG_LDO1OUT, + [PCF50633_REGULATOR_LDO2] = PCF50633_REG_LDO2OUT, + [PCF50633_REGULATOR_LDO3] = PCF50633_REG_LDO3OUT, + [PCF50633_REGULATOR_LDO4] = PCF50633_REG_LDO4OUT, + [PCF50633_REGULATOR_LDO5] = PCF50633_REG_LDO5OUT, + [PCF50633_REGULATOR_LDO6] = PCF50633_REG_LDO6OUT, + [PCF50633_REGULATOR_HCLDO] = PCF50633_REG_HCLDOOUT, +}; + +struct pcf50633_gpio { + struct pcf50633 *pcf; + struct gpio_chip chip; +}; + +static inline struct pcf50633 *gpio_chip_to_pcf50633(struct gpio_chip *chip) +{ + struct pcf50633 *pcf = dev_to_pcf50633(chip->dev->parent); + return pcf; +} + +static void pcf50633_gpio_set_value(struct gpio_chip *chip, unsigned gpio, int value) +{ + struct pcf50633 *pcf = gpio_chip_to_pcf50633(chip); + u8 reg; + + reg = PCF50633_REG_GPIOCFG(gpio); + + pcf50633_reg_set_bit_mask(pcf, reg, 0x07, value ? 0x0 : 0x7); +} + +static int pcf50633_gpio_get_value(struct gpio_chip *chip, unsigned gpio) +{ + struct pcf50633 *pcf = gpio_chip_to_pcf50633(chip); + return pcf50633_reg_read(pcf, PCF50633_REG_GPIOCFG(gpio)) >> 3; +} + + +static int pcf50633_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, + int value) +{ + struct pcf50633 *pcf = gpio_chip_to_pcf50633(chip); + int ret; + + ret = pcf50633_gpio_set_config(pcf, pcf->pdata->gpio_base + gpio, + PCF50633_GPIO_CONFIG_OUTPUT); + if (!ret) + pcf50633_gpio_set_value(chip, gpio, value); + + return ret; +} + +static int pcf50633_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) +{ + return -ENOSYS; +} + +int pcf50633_gpio_set_config(struct pcf50633 *pcf, unsigned gpio, + enum pcf50633_gpio_config config) +{ + u8 reg; + u8 direction; + int ret; + + gpio -= pcf->pdata->gpio_base; + + if (gpio < 3) { + direction = (config == PCF50633_GPIO_CONFIG_INPUT) ? (1 << gpio) : 0; + ret = pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_GPIOCTL, (1 << gpio), + direction); + if (ret) { + return ret; + } + } else if (gpio > 3 || config == PCF50633_GPIO_CONFIG_INPUT) { + return -EINVAL; + } + + if (config != PCF50633_GPIO_CONFIG_INPUT) { + reg = PCF50633_REG_GPIOCFG(gpio); + ret = pcf50633_reg_set_bit_mask(pcf, reg, 0x0f, config); + } + + return ret; +} +EXPORT_SYMBOL_GPL(pcf50633_gpio_set_config); + +int pcf50633_gpio_power_supply_set(struct pcf50633 *pcf, + int gpio, int regulator, int on) +{ + u8 reg, val, mask; + + gpio -= pcf->pdata->gpio_base; + + /* the *ENA register is always one after the *OUT register */ + reg = pcf50633_regulator_registers[regulator] + 1; + + val = !!on << (gpio - PCF50633_GPIO1); + mask = 1 << (gpio - PCF50633_GPIO1); + + return pcf50633_reg_set_bit_mask(pcf, reg, mask, val); +} +EXPORT_SYMBOL_GPL(pcf50633_gpio_power_supply_set); + + +static int __devinit pcf50633_gpio_probe(struct platform_device *pdev) +{ + struct pcf50633 *pcf = dev_to_pcf50633(pdev->dev.parent); + struct pcf50633_gpio *pcf_gpio; + + pcf_gpio = kzalloc(sizeof(*pcf_gpio), GFP_KERNEL); + + if (!pcf_gpio) + return -ENOMEM; + + pcf_gpio->pcf = pcf; + + pcf_gpio->chip.direction_input = pcf50633_gpio_direction_input; + pcf_gpio->chip.direction_output = pcf50633_gpio_direction_output; + pcf_gpio->chip.get = pcf50633_gpio_get_value; + pcf_gpio->chip.set = pcf50633_gpio_set_value; + + pcf_gpio->chip.base = pcf->pdata->gpio_base; + pcf_gpio->chip.ngpio = 4; + pcf_gpio->chip.label = dev_name(pcf->dev); + pcf_gpio->chip.can_sleep = 1; + pcf_gpio->chip.owner = THIS_MODULE; + pcf_gpio->chip.dev = &pdev->dev; + + platform_set_drvdata(pdev, pcf_gpio); + + return gpiochip_add(&pcf_gpio->chip); +} + +static int __devexit pcf50633_gpio_remove(struct platform_device *pdev) +{ + struct pcf50633_gpio *pcf_gpio = platform_get_drvdata(pdev); + + gpiochip_remove(&pcf_gpio->chip); + + platform_set_drvdata(pdev, NULL); + kfree(pcf_gpio); + + return 0; +} + +static struct platform_driver pcf50633_gpio_driver = { + .probe = pcf50633_gpio_probe, + .remove = __devexit_p(pcf50633_gpio_remove), + .driver = { + .name = "pcf50633-gpio", + .owner = THIS_MODULE, + }, +}; + +int __init pcf50633_gpio_init(void) +{ + return platform_driver_register(&pcf50633_gpio_driver); +} +module_init(pcf50633_gpio_init); + +void __exit pcf50633_gpio_exit(void) +{ + platform_driver_unregister(&pcf50633_gpio_driver); +} +module_exit(pcf50633_gpio_exit); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("GPIO driver for the PCF50633"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index f5a60b49dfb..0c2126fe16c 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -268,13 +268,6 @@ config PCF50633_ADC Say yes here if you want to include support for ADC in the NXP PCF50633 chip. -config PCF50633_GPIO - tristate "Support for NXP PCF50633 GPIO" - depends on MFD_PCF50633 - help - Say yes here if you want to include support GPIO for pins on - the PCF50633 chip. - config AB3100_CORE tristate "ST-Ericsson AB3100 Mixed Signal Circuit core functions" depends on I2C diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 5272b0451f0..f30cede74c2 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -48,6 +48,5 @@ obj-$(CONFIG_PMIC_DA903X) += da903x.o obj-$(CONFIG_MFD_PCF50633) += pcf50633-core.o obj-$(CONFIG_PCF50633_ADC) += pcf50633-adc.o -obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o obj-$(CONFIG_AB3100_CORE) += ab3100-core.o obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c index 41ac3a52df1..f41c1a38935 100644 --- a/drivers/mfd/pcf50633-core.c +++ b/drivers/mfd/pcf50633-core.c @@ -454,7 +454,7 @@ static irqreturn_t pcf50633_irq(int irq, void *data) static void pcf50633_client_dev_register(struct pcf50633 *pcf, const char *name, - struct platform_device **pdev) + struct platform_device **pdev) { int ret; @@ -621,7 +621,8 @@ static int __devinit pcf50633_probe(struct i2c_client *client, &pcf->adc_pdev); pcf50633_client_dev_register(pcf, "pcf50633-backlight", &pcf->bl_pdev); - + pcf50633_client_dev_register(pcf, "pcf50633-gpio", + &pcf->gpio_pdev); for (i = 0; i < PCF50633_NUM_REGULATORS; i++) { struct platform_device *pdev; @@ -647,9 +648,9 @@ static int __devinit pcf50633_probe(struct i2c_client *client, ret = sysfs_create_group(&client->dev.kobj, &pcf_attr_group); if (ret) dev_info(pcf->dev, "Failed to create sysfs entries\n"); - - if (pdata->probe_done) - pdata->probe_done(pcf); + + if (pcf->pdata->probe_done) + pcf->pdata->probe_done(pcf); return 0; @@ -670,6 +671,7 @@ static int __devexit pcf50633_remove(struct i2c_client *client) free_irq(pcf->irq, pcf); destroy_workqueue(pcf->work_queue); + platform_device_unregister(pcf->gpio_pdev); platform_device_unregister(pcf->input_pdev); platform_device_unregister(pcf->rtc_pdev); platform_device_unregister(pcf->mbc_pdev); diff --git a/drivers/mfd/pcf50633-gpio.c b/drivers/mfd/pcf50633-gpio.c deleted file mode 100644 index 9ab19a8f669..00000000000 --- a/drivers/mfd/pcf50633-gpio.c +++ /dev/null @@ -1,121 +0,0 @@ -/* NXP PCF50633 GPIO Driver - * - * (C) 2006-2008 by Openmoko, Inc. - * Author: Balaji Rao <balajirrao@openmoko.org> - * All rights reserved. - * - * Broken down from monstrous PCF50633 driver mainly by - * Harald Welte, Andy Green and Werner Almesberger - * - * 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/mfd/pcf50633/core.h> -#include <linux/mfd/pcf50633/gpio.h> - -enum pcf50633_regulator_id { - PCF50633_REGULATOR_AUTO, - PCF50633_REGULATOR_DOWN1, - PCF50633_REGULATOR_DOWN2, - PCF50633_REGULATOR_LDO1, - PCF50633_REGULATOR_LDO2, - PCF50633_REGULATOR_LDO3, - PCF50633_REGULATOR_LDO4, - PCF50633_REGULATOR_LDO5, - PCF50633_REGULATOR_LDO6, - PCF50633_REGULATOR_HCLDO, - PCF50633_REGULATOR_MEMLDO, -}; - -#define PCF50633_REG_AUTOOUT 0x1a -#define PCF50633_REG_DOWN1OUT 0x1e -#define PCF50633_REG_DOWN2OUT 0x22 -#define PCF50633_REG_MEMLDOOUT 0x26 -#define PCF50633_REG_LDO1OUT 0x2d -#define PCF50633_REG_LDO2OUT 0x2f -#define PCF50633_REG_LDO3OUT 0x31 -#define PCF50633_REG_LDO4OUT 0x33 -#define PCF50633_REG_LDO5OUT 0x35 -#define PCF50633_REG_LDO6OUT 0x37 -#define PCF50633_REG_HCLDOOUT 0x39 - -static const u8 pcf50633_regulator_registers[PCF50633_NUM_REGULATORS] = { - [PCF50633_REGULATOR_AUTO] = PCF50633_REG_AUTOOUT, - [PCF50633_REGULATOR_DOWN1] = PCF50633_REG_DOWN1OUT, - [PCF50633_REGULATOR_DOWN2] = PCF50633_REG_DOWN2OUT, - [PCF50633_REGULATOR_MEMLDO] = PCF50633_REG_MEMLDOOUT, - [PCF50633_REGULATOR_LDO1] = PCF50633_REG_LDO1OUT, - [PCF50633_REGULATOR_LDO2] = PCF50633_REG_LDO2OUT, - [PCF50633_REGULATOR_LDO3] = PCF50633_REG_LDO3OUT, - [PCF50633_REGULATOR_LDO4] = PCF50633_REG_LDO4OUT, - [PCF50633_REGULATOR_LDO5] = PCF50633_REG_LDO5OUT, - [PCF50633_REGULATOR_LDO6] = PCF50633_REG_LDO6OUT, - [PCF50633_REGULATOR_HCLDO] = PCF50633_REG_HCLDOOUT, -}; - -int pcf50633_gpio_set(struct pcf50633 *pcf, int gpio, u8 val) -{ - u8 reg; - - reg = gpio - PCF50633_GPIO1 + PCF50633_REG_GPIO1CFG; - - return pcf50633_reg_set_bit_mask(pcf, reg, 0x07, val); -} -EXPORT_SYMBOL_GPL(pcf50633_gpio_set); - -u8 pcf50633_gpio_get(struct pcf50633 *pcf, int gpio) -{ - u8 reg, val; - - reg = gpio - PCF50633_GPIO1 + PCF50633_REG_GPIO1CFG; - val = pcf50633_reg_read(pcf, reg) & 0x07; - - return val; -} -EXPORT_SYMBOL_GPL(pcf50633_gpio_get); - -int pcf50633_gpio_invert_set(struct pcf50633 *pcf, int gpio, int invert) -{ - u8 val, reg; - - reg = gpio - PCF50633_GPIO1 + PCF50633_REG_GPIO1CFG; - val = !!invert << 3; - - return pcf50633_reg_set_bit_mask(pcf, reg, 1 << 3, val); -} -EXPORT_SYMBOL_GPL(pcf50633_gpio_invert_set); - -int pcf50633_gpio_invert_get(struct pcf50633 *pcf, int gpio) -{ - u8 reg, val; - - reg = gpio - PCF50633_GPIO1 + PCF50633_REG_GPIO1CFG; - val = pcf50633_reg_read(pcf, reg); - - return val & (1 << 3); -} -EXPORT_SYMBOL_GPL(pcf50633_gpio_invert_get); - -int pcf50633_gpio_power_supply_set(struct pcf50633 *pcf, - int gpio, int regulator, int on) -{ - u8 reg, val, mask; - - /* the *ENA register is always one after the *OUT register */ - reg = pcf50633_regulator_registers[regulator] + 1; - - val = !!on << (gpio - PCF50633_GPIO1); - mask = 1 << (gpio - PCF50633_GPIO1); - - return pcf50633_reg_set_bit_mask(pcf, reg, mask, val); -} -EXPORT_SYMBOL_GPL(pcf50633_gpio_power_supply_set); - -MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/pcf50633/core.h b/include/linux/mfd/pcf50633/core.h index 42f9af553ed..e69829b6e90 100644 --- a/include/linux/mfd/pcf50633/core.h +++ b/include/linux/mfd/pcf50633/core.h @@ -19,6 +19,7 @@ #include <linux/regulator/machine.h> #include <linux/power_supply.h> #include <linux/mfd/pcf50633/backlight.h> +#include <linux/gpio.h> struct pcf50633; @@ -41,6 +42,8 @@ struct pcf50633_platform_data { u8 resumers[5]; struct pcf50633_bl_platform_data *backlight_data; + + int gpio_base; }; struct pcf50633_irq { @@ -146,6 +149,7 @@ struct pcf50633 { int onkey1s_held; + struct platform_device *gpio_pdev; struct platform_device *rtc_pdev; struct platform_device *mbc_pdev; struct platform_device *adc_pdev; diff --git a/include/linux/mfd/pcf50633/gpio.h b/include/linux/mfd/pcf50633/gpio.h index a42b845efc5..af2c341d5f6 100644 --- a/include/linux/mfd/pcf50633/gpio.h +++ b/include/linux/mfd/pcf50633/gpio.h @@ -15,37 +15,40 @@ #include <linux/mfd/pcf50633/core.h> -#define PCF50633_GPIO1 1 -#define PCF50633_GPIO2 2 -#define PCF50633_GPIO3 3 -#define PCF50633_GPO 4 - -#define PCF50633_REG_GPIO1CFG 0x14 -#define PCF50633_REG_GPIO2CFG 0x15 -#define PCF50633_REG_GPIO3CFG 0x16 -#define PCF50633_REG_GPOCFG 0x17 - -#define PCF50633_GPOCFG_GPOSEL_MASK 0x07 - -enum pcf50633_reg_gpocfg { - PCF50633_GPOCFG_GPOSEL_0 = 0x00, - PCF50633_GPOCFG_GPOSEL_LED_NFET = 0x01, - PCF50633_GPOCFG_GPOSEL_SYSxOK = 0x02, - PCF50633_GPOCFG_GPOSEL_CLK32K = 0x03, - PCF50633_GPOCFG_GPOSEL_ADAPUSB = 0x04, - PCF50633_GPOCFG_GPOSEL_USBxOK = 0x05, - PCF50633_GPOCFG_GPOSEL_ACTPH4 = 0x06, - PCF50633_GPOCFG_GPOSEL_1 = 0x07, - PCF50633_GPOCFG_GPOSEL_INVERSE = 0x08, +#define PCF50633_GPIO1 0 +#define PCF50633_GPIO2 1 +#define PCF50633_GPIO3 2 +#define PCF50633_GPO 3 + +#define PCF50633_REG_GPIOCFG(x) (0x14 + (x)) + +enum pcf50633_gpio_config { + PCF50633_GPIO_CONFIG_OUTPUT = 0x0, + PCF50633_GPIO_CONFIG_SYSxOK = 0x2, + PCF50633_GPIO_CONFIG_CHARGING = 0x3, + PCF50633_GPIO_CONFIG_MOBILE_MODE = 0x4, + PCF50633_GPIO_CONFIG_USBxOK = 0x5, + PCF50633_GPIO_CONFIG_ACTPH = 0x6, + PCF50633_GPIO_CONFIG_INPUT = 0x7, + + PCF50633_GPIO_CONFIG_INVERT = 0x8, + + PCF50633_GPO_CONFIG_OUTPUT = 0x0, + PCF50633_GPO_CONFIG_LED_NFET = 0x1, + PCF50633_GPO_CONFIG_SYSxOK = 0x2, + PCF50633_GPO_CONFIG_CLK32K = 0x3, + PCF50633_GPO_CONFIG_MOBILE_MODE = 0x4, + PCF50633_GPO_CONFIG_USBxOK = 0x5, + PCF50633_GPO_CONFIG_ACTPH = 0x6, + PCF50633_GPO_CONFIG_INPUT = 0x7, + + PCF50633_GPO_CONFIG_INVERT = 0x8, }; -int pcf50633_gpio_set(struct pcf50633 *pcf, int gpio, u8 val); -u8 pcf50633_gpio_get(struct pcf50633 *pcf, int gpio); +int pcf50633_gpio_set_config(struct pcf50633 *pcf, unsigned gpio, + enum pcf50633_gpio_config config); -int pcf50633_gpio_invert_set(struct pcf50633 *, int gpio, int invert); -int pcf50633_gpio_invert_get(struct pcf50633 *pcf, int gpio); - -int pcf50633_gpio_power_supply_set(struct pcf50633 *, +int pcf50633_gpio_power_supply_set(struct pcf50633 *pcf, int gpio, int regulator, int on); #endif /* __LINUX_MFD_PCF50633_GPIO_H */ |