Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755571Ab0GGNVI (ORCPT ); Wed, 7 Jul 2010 09:21:08 -0400 Received: from fb1.tech.numericable.fr ([82.216.111.51]:46449 "EHLO fb1.tech.numericable.fr" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754966Ab0GGNVF (ORCPT ); Wed, 7 Jul 2010 09:21:05 -0400 X-Greylist: delayed 462 seconds by postgrey-1.27 at vger.kernel.org; Wed, 07 Jul 2010 09:21:05 EDT From: Thibaut Girka To: Samuel Ortiz Cc: linux-kernel@vger.kernel.org, Thibaut Girka Subject: [PATCH 3/4] Smedia Glamo 3362 GPIO interface driver Date: Wed, 7 Jul 2010 15:12:05 +0200 Message-Id: <1278508326-7109-4-git-send-email-thib@sitedethib.com> X-Mailer: git-send-email 1.7.1 In-Reply-To: <1278508326-7109-1-git-send-email-thib@sitedethib.com> References: <1278508326-7109-1-git-send-email-thib@sitedethib.com> To: Samuel Ortiz Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 9381 Lines: 335 Signed-off-by: Thibaut Girka --- drivers/gpio/Kconfig | 6 + drivers/gpio/Makefile | 1 + drivers/gpio/glamo-gpio.c | 284 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 291 insertions(+), 0 deletions(-) create mode 100644 drivers/gpio/glamo-gpio.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 724038d..cc82f42 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -70,6 +70,12 @@ config GPIO_MAX730X comment "Memory mapped GPIO expanders:" +config GPIO_GLAMO + tristate "Glamo GPIO support" + depends on MFD_GLAMO + help + Say yes here to support GPIO functionality of the Smedia Glamo. + config GPIO_IT8761E tristate "IT8761E GPIO support" depends on GPIOLIB diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 51c3cdd..0de1cf9 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_GPIOLIB) += gpiolib.o obj-$(CONFIG_GPIO_ADP5520) += adp5520-gpio.o obj-$(CONFIG_GPIO_ADP5588) += adp5588-gpio.o +obj-$(CONFIG_GPIO_GLAMO) += glamo-gpio.o obj-$(CONFIG_GPIO_LANGWELL) += langwell_gpio.o obj-$(CONFIG_GPIO_MAX730X) += max730x.o obj-$(CONFIG_GPIO_MAX7300) += max7300.o diff --git a/drivers/gpio/glamo-gpio.c b/drivers/gpio/glamo-gpio.c new file mode 100644 index 0000000..dff60eb --- /dev/null +++ b/drivers/gpio/glamo-gpio.c @@ -0,0 +1,284 @@ +/* Smedia Glamo 336x/337x gpio driver + * + * (C) 2009 Lars-Peter Clausen + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define GLAMO_NR_GPIO 21 +#define GLAMO_NR_GPIO_REGS DIV_ROUND_UP(GLAMO_NR_GPIO, 4) + +#define GLAMO_GPIO_REG(x) (((x) * 2) + GLAMO_REG_GPIO_GEN1) + +struct glamo_gpio { + struct glamo_core *glamo; + struct gpio_chip chip; + uint16_t saved_regs[GLAMO_NR_GPIO_REGS]; +}; + +#define GLAMO_GPIO_REG_GPIO(gpio) GLAMO_GPIO_REG(gpio >> 2) +#define GLAMO_GPIO_BIT(gpio, offset) BIT(((gpio) & 0x3) + (offset)) +#define GLAMO_GPIO_DIRECTION_BIT(gpio) GLAMO_GPIO_BIT(gpio, 0) +#define GLAMO_GPIO_OUTPUT_BIT(gpio) GLAMO_GPIO_BIT(gpio, 4) +#define GLAMO_GPIO_INPUT_BIT(gpio) GLAMO_GPIO_BIT(gpio, 8) +#define GLAMO_GPIO_FUNC_BIT(gpio) GLAMO_GPIO_BIT(gpio, 12) + + +static inline struct glamo_core *chip_to_glamo(struct gpio_chip *chip) +{ + return container_of(chip, struct glamo_gpio, chip)->glamo; +} + +static void glamo_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct glamo_core *glamo = chip_to_glamo(chip); + unsigned int reg = GLAMO_GPIO_REG_GPIO(offset); + uint16_t tmp; + + spin_lock(&glamo->lock); + tmp = readw(glamo->base + reg); + if (value) + tmp |= GLAMO_GPIO_OUTPUT_BIT(offset); + else + tmp &= ~GLAMO_GPIO_OUTPUT_BIT(offset); + writew(tmp, glamo->base + reg); + spin_unlock(&glamo->lock); +} + +static int glamo_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct glamo_core *glamo = chip_to_glamo(chip); + return !!(readw(glamo->base + GLAMO_GPIO_REG_GPIO(offset)) & GLAMO_GPIO_INPUT_BIT(offset)); +} + +static int glamo_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + struct glamo_core *glamo = chip_to_glamo(chip); + unsigned int reg = GLAMO_GPIO_REG_GPIO(offset); + uint16_t tmp; + + spin_lock(&glamo->lock); + tmp = readw(glamo->base + reg); + if ((tmp & GLAMO_GPIO_FUNC_BIT(offset)) == 0) { + tmp |= GLAMO_GPIO_FUNC_BIT(offset); + writew(tmp, glamo->base + reg); + } + spin_unlock(&glamo->lock); + + return 0; +} + +static void glamo_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + struct glamo_core *glamo = chip_to_glamo(chip); + unsigned int reg = GLAMO_GPIO_REG_GPIO(offset); + uint16_t tmp; + + spin_lock(&glamo->lock); + tmp = readw(glamo->base + reg); + if ((tmp & GLAMO_GPIO_FUNC_BIT(offset)) == 1) { + tmp &= ~GLAMO_GPIO_FUNC_BIT(offset); + writew(tmp, glamo->base + reg); + } + spin_unlock(&glamo->lock); +} + +static int glamo_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct glamo_core *glamo = chip_to_glamo(chip); + unsigned int reg = GLAMO_GPIO_REG_GPIO(offset); + uint16_t tmp; + + spin_lock(&glamo->lock); + tmp = readw(glamo->base + reg); + tmp &= ~GLAMO_GPIO_DIRECTION_BIT(offset); + + if (value) + tmp |= GLAMO_GPIO_OUTPUT_BIT(offset); + else + tmp &= ~GLAMO_GPIO_OUTPUT_BIT(offset); + + writew(tmp, glamo->base + reg); + spin_unlock(&glamo->lock); + + return 0; +} + +static int glamo_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct glamo_core *glamo = chip_to_glamo(chip); + unsigned int reg = GLAMO_GPIO_REG_GPIO(offset); + uint16_t tmp; + + spin_lock(&glamo->lock); + tmp = readw(glamo->base + reg); + if ((tmp & GLAMO_GPIO_DIRECTION_BIT(offset)) == 0) { + tmp |= GLAMO_GPIO_DIRECTION_BIT(offset); + writew(tmp, glamo->base + reg); + } + spin_unlock(&glamo->lock); + + return 0; +} + +static const struct __devinit gpio_chip glamo_gpio_chip = { + .label = "glamo", + .request = glamo_gpio_request, + .free = glamo_gpio_free, + .direction_input = glamo_gpio_direction_input, + .get = glamo_gpio_get, + .direction_output = glamo_gpio_direction_output, + .set = glamo_gpio_set, + .base = -1, + .ngpio = GLAMO_NR_GPIO, + .can_sleep = 0, + .owner = THIS_MODULE, +}; + +static int __devinit glamo_gpio_probe(struct platform_device *pdev) +{ + struct glamo_platform_data *pdata = pdev->dev.parent->platform_data; + struct glamo_gpio *glamo_gpio; + int ret; + + glamo_gpio = kzalloc(sizeof(*glamo_gpio), GFP_KERNEL); + if (!glamo_gpio) + return -ENOMEM; + + glamo_gpio->glamo = dev_get_drvdata(pdev->dev.parent); + glamo_gpio->chip = glamo_gpio_chip; + glamo_gpio->chip.dev = &pdev->dev; + if (pdata && pdata->gpio_data) + glamo_gpio->chip.base = pdata->gpio_data->base; + + ret = gpiochip_add(&glamo_gpio->chip); + + if (ret) { + dev_err(&pdev->dev, "Could not register gpio chip: %d\n", ret); + goto err; + } + + platform_set_drvdata(pdev, glamo_gpio); + + if (pdata && pdata->gpio_data && pdata->gpio_data->registered) + pdata->gpio_data->registered(&pdev->dev); + + return 0; +err: + kfree(glamo_gpio); + return ret; +} + +static int __devexit glamo_gpio_remove(struct platform_device *pdev) +{ + struct glamo_gpio *glamo_gpio = platform_get_drvdata(pdev); + int ret; + + ret = gpiochip_remove(&glamo_gpio->chip); + if (!ret) + goto done; + + platform_set_drvdata(pdev, NULL); + kfree(glamo_gpio); + +done: + return ret; +} + +#ifdef CONFIG_PM + +static int glamo_gpio_suspend(struct device *dev) +{ + struct glamo_gpio *glamo_gpio = dev_get_drvdata(dev); + struct glamo_core *glamo = glamo_gpio->glamo; + uint16_t *saved_regs = glamo_gpio->saved_regs; + int i; + + spin_lock(&glamo->lock); + for (i = 0; i < GLAMO_NR_GPIO / 4; ++i) + saved_regs[i] = readw(glamo->base + GLAMO_GPIO_REG(i)); + spin_unlock(&glamo->lock); + + return 0; +} + +static int glamo_gpio_resume(struct device *dev) +{ + struct glamo_gpio *glamo_gpio = dev_get_drvdata(dev); + struct glamo_core *glamo = glamo_gpio->glamo; + uint16_t *saved_regs = glamo_gpio->saved_regs; + int i; + + spin_lock(&glamo->lock); + for (i = 0; i < GLAMO_NR_GPIO_REGS; ++i) + writew(saved_regs[i], glamo->base + GLAMO_GPIO_REG(i)); + spin_unlock(&glamo->lock); + return 0; +} + +static const struct dev_pm_ops glamo_pm_ops = { + .suspend = glamo_gpio_suspend, + .resume = glamo_gpio_resume, + .freeze = glamo_gpio_suspend, + .thaw = glamo_gpio_resume, +}; + +#define GLAMO_GPIO_PM_OPS (&glamo_pm_ops) + +#else +#define GLAMO_GPIO_PM_OPS NULL +#endif + +static struct platform_driver glamo_gpio_driver = { + .driver = { + .name = "glamo-gpio", + .owner = THIS_MODULE, + .pm = GLAMO_GPIO_PM_OPS, + }, + .probe = glamo_gpio_probe, + .remove = __devexit_p(glamo_gpio_remove), +}; + +static int __devinit glamo_gpio_init(void) +{ + return platform_driver_register(&glamo_gpio_driver); +} +module_init(glamo_gpio_init); + +static void __exit glamo_gpio_exit(void) +{ + platform_driver_unregister(&glamo_gpio_driver); +} +module_exit(glamo_gpio_exit); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("GPIO interface for the Glamo multimedia device"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:glamo-gpio"); -- 1.7.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/