Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1758815Ab3EWMRh (ORCPT ); Thu, 23 May 2013 08:17:37 -0400 Received: from mail1.ams.com ([212.166.112.31]:48049 "EHLO mail1.ams.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1758776Ab3EWMR0 convert rfc822-to-8bit (ORCPT ); Thu, 23 May 2013 08:17:26 -0400 X-IronPort-AV: E=Sophos;i="4.87,728,1363129200"; d="scan'208";a="2797471" From: Florian Lobmaier To: "sameo@linux.intel.com" CC: "linux-kernel@vger.kernel.org" Date: Thu, 23 May 2013 14:07:45 +0200 Subject: [PATCH 05/07] gpio patch of ams AS3722 PMIC against linux_3.8.8 Thread-Topic: [PATCH 05/07] gpio patch of ams AS3722 PMIC against linux_3.8.8 Thread-Index: Ac5XrTXDjVknu+yGRFuTJS22kp1UGg== Message-ID: Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: acceptlanguage: en-US Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 8BIT MIME-Version: 1.0 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 11111 Lines: 367 From: Florian Lobmaier Added multi-function device driver support for ams AS3722 PMIC Includes modules gpio, regulator, rtc, and watchdog Signed-off-by: Florian Lobmaier --- diff -uprN -X Documentation/dontdiff ../kernel_3.8.8/linux-kernel/drivers/gpio/gpio-as3722.c ./drivers/gpio/gpio-as3722.c --- ../kernel_3.8.8/linux-kernel/drivers/gpio/gpio-as3722.c 1970-01-01 01:00:00.000000000 +0100 +++ ./drivers/gpio/gpio-as3722.c 2013-05-23 13:12:36.000000000 +0200 @@ -0,0 +1,320 @@ +/* + * as3722-gpio.c - gpiolib support for ams AS3722 PMICs + * + * Copyright (C) 2013 ams AG + * + * Author: Florian Lobmaier + * + * 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 + +struct as3722_gpio { + struct as3722 *as3722; + struct gpio_chip gpio_chip; +}; + +static inline struct as3722_gpio *to_as3722_gpio(struct gpio_chip *chip) +{ + return container_of(chip, struct as3722_gpio, gpio_chip); +} + +static int as3722_gpio_direction_in(struct gpio_chip *chip, unsigned + offset) +{ + struct as3722_gpio *as3722_gpio = to_as3722_gpio(chip); + struct as3722 *as3722 = as3722_gpio->as3722; + + return as3722_set_bits(as3722, AS3722_GPIO0_CONTROL_REG + offset, + AS3722_GPIO_MODE_MASK, + AS3722_GPIO_MODE_INPUT); +} + +static int as3722_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct as3722_gpio *as3722_gpio = to_as3722_gpio(chip); + struct as3722 *as3722 = as3722_gpio->as3722; + int ret; + u32 val; + + ret = as3722_reg_read(as3722, AS3722_GPIO_SIGNAL_IN_REG, &val); + if (ret < 0) + return ret; + + if (val & (AS3722_GPIO1_SIGNAL_MASK << offset)) + return 1; + else + return 0; +} + +static int as3722_gpio_direction_out(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct as3722_gpio *as3722_gpio = to_as3722_gpio(chip); + struct as3722 *as3722 = as3722_gpio->as3722; + + return as3722_set_bits(as3722, AS3722_GPIO0_CONTROL_REG + offset, + AS3722_GPIO_MODE_MASK, + AS3722_GPIO_MODE_OUTPUT_VDDH); +} + +static void as3722_gpio_set(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct as3722_gpio *as3722_gpio = to_as3722_gpio(chip); + struct as3722 *as3722 = as3722_gpio->as3722; + + as3722_set_bits(as3722, AS3722_GPIO_SIGNAL_OUT_REG, 1 << offset, + value << offset); +} + +static int as3722_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct as3722_gpio *as3722_gpio = to_as3722_gpio(chip); + struct as3722 *as3722 = as3722_gpio->as3722; + + return regmap_irq_get_virq(as3722->irq_data, AS3722_IRQ_GPIO1 + offset); +} + +static int as3722_gpio_set_config(struct as3722_gpio *as3722_gpio, + struct as3722_gpio_config *gpio_cfg) +{ + int ret = 0; + u8 val = 0; + int gpio = gpio_cfg->gpio; + struct as3722 *as3722 = as3722_gpio->as3722; + if ((gpio < AS3722_GPIO0) || (gpio > AS3722_GPIO7)) + return -EINVAL; + + /* .invert + .iosf + .mode */ + /* set up write of the GPIOX control register */ + val = (gpio_cfg->iosf & AS3722_GPIO_IOSF_MASK) + + (gpio_cfg->mode & AS3722_GPIO_MODE_MASK); + if (gpio_cfg->invert) + val += (AS3722_GPIO_INV & AS3722_GPIO_INV_MASK); + + ret = as3722_reg_write(as3722, AS3722_GPIO0_CONTROL_REG + gpio, val); + if (ret != 0) { + dev_err(as3722->dev, "AS3722_GPIO%d_CTRL_REG write err, + ret: %d\n", + gpio, ret); + return ret; + } + + /* if GPIO is configured as an output, set initial output state */ + if ((gpio_cfg->mode == AS3722_GPIO_MODE_OUTPUT_VDDH) || + (gpio_cfg->mode == AS3722_GPIO_MODE_OUTPUT_VDDL)) { + /*GPIO0 -> bit 0, ..., GPIO7 -> bit 7, output_state = 0 or 1*/ + val = (gpio_cfg->output_state ^ gpio_cfg->invert) << gpio; + ret = as3722_set_bits(as3722, AS3722_GPIO_SIGNAL_OUT_REG, + 1 << gpio, val); + } + + return ret; +} + +static int as3722_gpio_init_regs(struct as3722_gpio *as3722_gpio, + struct as3722_platform_data *pdata) +{ + int ret; + int i; + for (i = 0; i < pdata->num_gpio_cfgs; i++) { + ret = as3722_gpio_set_config(as3722_gpio, + &pdata->gpio_cfgs[i]); + if (ret < 0) { + dev_err(as3722_gpio->as3722->dev, + "Failed to set gpio config\n"); + return ret; + } + } + + return 0; +} + +#ifdef CONFIG_DEBUG_FS +static void as3722_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + struct as3722_gpio *as3722_gpio = to_as3722_gpio(chip); + struct as3722 *as3722 = as3722_gpio->as3722; + int i; + + for (i = 0; i < chip->ngpio; i++) { + int gpio = i + chip->base; + u32 reg; + int ret; + const char *label, *pull, *direction; + + /* 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); + + ret = as3722_reg_read(as3722, + AS3722_GPIO0_CONTROL_REG + i, ®); + if (ret < 0) { + dev_err(as3722->dev, + "GPIO control %d read failed: %d\n", + gpio, ret); + seq_printf(s, "\n"); + continue; + } + + switch (reg & AS3722_GPIO_MODE_MASK) { + case AS3722_GPIO_MODE_INPUT: + direction = "in"; + pull = "nopull"; + break; + case AS3722_GPIO_MODE_OUTPUT_VDDH: + direction = "out"; + pull = "push and pull"; + break; + case AS3722_GPIO_MODE_IO_OPEN_DRAIN: + direction = "io"; + pull = "nopull"; + break; + case AS3722_GPIO_MODE_INPUT_W_PULLUP: + direction = "in"; + pull = "pullup"; + break; + case AS3722_GPIO_MODE_INPUT_W_PULLDOWN: + direction = "in"; + pull = "pulldown"; + break; + case AS3722_GPIO_MODE_IO_OPEN_DRAIN_PULLUP: + direction = "io"; + pull = "pullup"; + break; + case AS3722_GPIO_MODE_OUTPUT_VDDL: + direction = "out"; + pull = "push and pull"; + break; + default: + direction = "INVALID DIRECTION/MODE"; + pull = "INVALID PULL"; + break; + } + + seq_printf(s, " %s %s %s\n" + " %s (0x%4x)\n", + direction, + as3722_gpio_get(chip, i) ? "high" : "low", + pull, + reg & AS3722_GPIO_INV_MASK ? " inverted" : "", + reg); + } +} +#else +#define as3722_gpio_dbg_show NULL +#endif + +static struct gpio_chip as3722_gpio_chip = { + .label = "as3722", + .owner = THIS_MODULE, + .direction_input = as3722_gpio_direction_in, + .get = as3722_gpio_get, + .direction_output = as3722_gpio_direction_out, + .set = as3722_gpio_set, + .to_irq = as3722_gpio_to_irq, + .dbg_show = as3722_gpio_dbg_show, + .can_sleep = 1, +}; + +static int as3722_gpio_probe(struct platform_device *pdev) +{ + struct as3722 *as3722 = dev_get_drvdata(pdev->dev.parent); + struct as3722_platform_data *pdata = dev_get_platdata(pdev->dev.parent); + struct as3722_gpio *as3722_gpio; + int ret; + + as3722_gpio = devm_kzalloc(&pdev->dev, + sizeof(*as3722_gpio), GFP_KERNEL); + if (as3722_gpio == NULL) { + dev_err(&pdev->dev, "Memory allocaiton failure\n"); + return -ENOMEM; + } + + as3722_gpio->as3722 = as3722; + as3722_gpio->gpio_chip = as3722_gpio_chip; + as3722_gpio->gpio_chip.ngpio = AS3722_NUM_GPIO; + as3722_gpio->gpio_chip.dev = &pdev->dev; + if (pdata && pdata->gpio_base) + as3722_gpio->gpio_chip.base = pdata->gpio_base; + else + as3722_gpio->gpio_chip.base = -1; + + platform_set_drvdata(pdev, as3722_gpio); + + ret = as3722_gpio_init_regs(as3722_gpio, pdata); + if (ret < 0) { + dev_err(&pdev->dev, "gpio_init_regs failed\n"); + return ret; + } + + ret = gpiochip_add(&as3722_gpio->gpio_chip); + if (ret < 0) { + dev_err(&pdev->dev, "Could not register gpiochip, %d\n", + ret); + return ret; + } + return 0; +} + +static int as3722_gpio_remove(struct platform_device *pdev) +{ + struct as3722_gpio *as3722_gpio = platform_get_drvdata(pdev); + + return gpiochip_remove(&as3722_gpio->gpio_chip); +} + +static struct platform_driver as3722_gpio_driver = { + .driver.name = "as3722-gpio", + .driver.owner = THIS_MODULE, + .probe = as3722_gpio_probe, + .remove = as3722_gpio_remove, +}; + +static int __init as3722_gpio_init(void) +{ + return platform_driver_register(&as3722_gpio_driver); +} +subsys_initcall(as3722_gpio_init); + +static void __exit as3722_gpio_exit(void) +{ + platform_driver_unregister(&as3722_gpio_driver); +} +module_exit(as3722_gpio_exit); + +MODULE_AUTHOR("Florian Lobmaier "); +MODULE_DESCRIPTION("GPIO interface for AS3722 PMICs"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:as3722-gpio"); diff -uprN -X Documentation/dontdiff ../kernel_3.8.8/linux-kernel/drivers/gpio/Kconfig ./drivers/gpio/Kconfig --- ../kernel_3.8.8/linux-kernel/drivers/gpio/Kconfig 2013-05-15 14:55:53.000000000 +0200 +++ ./drivers/gpio/Kconfig 2013-05-23 13:12:36.000000000 +0200 @@ -306,6 +306,15 @@ config GPIO_ARIZONA help Support for GPIOs on Wolfson Arizona class devices. +config GPIO_AS3722 + tristate "ams AS3722 GPIO support" + depends on MFD_AS3722 + help + This option enables support for on-chip GPIOs found on + AS3722 PMIC from ams. + To compile this driver as a module, choose M here: the module will + be called as3722-gpio. + config GPIO_MAX7300 tristate "Maxim MAX7300 GPIO expander" depends on I2C diff -uprN -X Documentation/dontdiff ../kernel_3.8.8/linux-kernel/drivers/gpio/Makefile ./drivers/gpio/Makefile --- ../kernel_3.8.8/linux-kernel/drivers/gpio/Makefile 2013-05-15 14:55:53.000000000 +0200 +++ ./drivers/gpio/Makefile 2013-05-23 13:12:36.000000000 +0200 @@ -16,6 +16,7 @@ obj-$(CONFIG_GPIO_ADP5520) += gpio-adp55 obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o +onj-$(CONFIG_GPIO_AS3722) += gpio-as3722.o obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o -- 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/