diff options
author | Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> | 2012-12-15 23:51:19 +0100 |
---|---|---|
committer | Simon Horman <horms+renesas@verge.net.au> | 2013-01-25 01:24:25 +0100 |
commit | 6e54d8d252ed09ae148af6565971974af9a96e10 (patch) | |
tree | ac4e0c344ec0e371decf95663d75f7918bd33bbc /drivers/pinctrl/sh-pfc/gpio.c | |
parent | sh-pfc: Remove unused resource and num_resources platform data fields (diff) | |
download | linux-6e54d8d252ed09ae148af6565971974af9a96e10.tar.xz linux-6e54d8d252ed09ae148af6565971974af9a96e10.zip |
sh-pfc: Move driver from drivers/sh/ to drivers/pinctrl/
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Acked-by: Paul Mundt <lethal@linux-sh.org>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
Diffstat (limited to 'drivers/pinctrl/sh-pfc/gpio.c')
-rw-r--r-- | drivers/pinctrl/sh-pfc/gpio.c | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/drivers/pinctrl/sh-pfc/gpio.c b/drivers/pinctrl/sh-pfc/gpio.c new file mode 100644 index 000000000000..a0454f321710 --- /dev/null +++ b/drivers/pinctrl/sh-pfc/gpio.c @@ -0,0 +1,179 @@ +/* + * SuperH Pin Function Controller GPIO driver. + * + * Copyright (C) 2008 Magnus Damm + * Copyright (C) 2009 - 2012 Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME " gpio: " fmt + +#include <linux/device.h> +#include <linux/gpio.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/pinctrl/consumer.h> +#include <linux/sh_pfc.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#include "core.h" + +struct sh_pfc_chip { + struct sh_pfc *pfc; + struct gpio_chip gpio_chip; +}; + +static struct sh_pfc_chip *gpio_to_pfc_chip(struct gpio_chip *gc) +{ + return container_of(gc, struct sh_pfc_chip, gpio_chip); +} + +static struct sh_pfc *gpio_to_pfc(struct gpio_chip *gc) +{ + return gpio_to_pfc_chip(gc)->pfc; +} + +static int sh_gpio_request(struct gpio_chip *gc, unsigned offset) +{ + return pinctrl_request_gpio(offset); +} + +static void sh_gpio_free(struct gpio_chip *gc, unsigned offset) +{ + pinctrl_free_gpio(offset); +} + +static void sh_gpio_set_value(struct sh_pfc *pfc, unsigned gpio, int value) +{ + struct pinmux_data_reg *dr = NULL; + int bit = 0; + + if (sh_pfc_get_data_reg(pfc, gpio, &dr, &bit) != 0) + BUG(); + else + sh_pfc_write_bit(dr, bit, value); +} + +static int sh_gpio_get_value(struct sh_pfc *pfc, unsigned gpio) +{ + struct pinmux_data_reg *dr = NULL; + int bit = 0; + + if (sh_pfc_get_data_reg(pfc, gpio, &dr, &bit) != 0) + return -EINVAL; + + return sh_pfc_read_bit(dr, bit); +} + +static int sh_gpio_direction_input(struct gpio_chip *gc, unsigned offset) +{ + return pinctrl_gpio_direction_input(offset); +} + +static int sh_gpio_direction_output(struct gpio_chip *gc, unsigned offset, + int value) +{ + sh_gpio_set_value(gpio_to_pfc(gc), offset, value); + + return pinctrl_gpio_direction_output(offset); +} + +static int sh_gpio_get(struct gpio_chip *gc, unsigned offset) +{ + return sh_gpio_get_value(gpio_to_pfc(gc), offset); +} + +static void sh_gpio_set(struct gpio_chip *gc, unsigned offset, int value) +{ + sh_gpio_set_value(gpio_to_pfc(gc), offset, value); +} + +static int sh_gpio_to_irq(struct gpio_chip *gc, unsigned offset) +{ + struct sh_pfc *pfc = gpio_to_pfc(gc); + pinmux_enum_t enum_id; + pinmux_enum_t *enum_ids; + int i, k, pos; + + pos = 0; + enum_id = 0; + while (1) { + pos = sh_pfc_gpio_to_enum(pfc, offset, pos, &enum_id); + if (pos <= 0 || !enum_id) + break; + + for (i = 0; i < pfc->pdata->gpio_irq_size; i++) { + enum_ids = pfc->pdata->gpio_irq[i].enum_ids; + for (k = 0; enum_ids[k]; k++) { + if (enum_ids[k] == enum_id) + return pfc->pdata->gpio_irq[i].irq; + } + } + } + + return -ENOSYS; +} + +static void sh_pfc_gpio_setup(struct sh_pfc_chip *chip) +{ + struct sh_pfc *pfc = chip->pfc; + struct gpio_chip *gc = &chip->gpio_chip; + + gc->request = sh_gpio_request; + gc->free = sh_gpio_free; + gc->direction_input = sh_gpio_direction_input; + gc->get = sh_gpio_get; + gc->direction_output = sh_gpio_direction_output; + gc->set = sh_gpio_set; + gc->to_irq = sh_gpio_to_irq; + + WARN_ON(pfc->pdata->first_gpio != 0); /* needs testing */ + + gc->label = pfc->pdata->name; + gc->owner = THIS_MODULE; + gc->base = pfc->pdata->first_gpio; + gc->ngpio = (pfc->pdata->last_gpio - pfc->pdata->first_gpio) + 1; +} + +int sh_pfc_register_gpiochip(struct sh_pfc *pfc) +{ + struct sh_pfc_chip *chip; + int ret; + + chip = devm_kzalloc(pfc->dev, sizeof(*chip), GFP_KERNEL); + if (unlikely(!chip)) + return -ENOMEM; + + chip->pfc = pfc; + + sh_pfc_gpio_setup(chip); + + ret = gpiochip_add(&chip->gpio_chip); + if (unlikely(ret < 0)) + return ret; + + pfc->gpio = chip; + + pr_info("%s handling gpio %d -> %d\n", + pfc->pdata->name, pfc->pdata->first_gpio, + pfc->pdata->last_gpio); + + return 0; +} + +int sh_pfc_unregister_gpiochip(struct sh_pfc *pfc) +{ + struct sh_pfc_chip *chip = pfc->gpio; + int ret; + + ret = gpiochip_remove(&chip->gpio_chip); + if (unlikely(ret < 0)) + return ret; + + pfc->gpio = NULL; + return 0; +} |