diff options
author | Paul Mundt <lethal@linux-sh.org> | 2012-07-20 11:42:59 +0400 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2012-07-20 11:42:59 +0400 |
commit | 9ff561fdf73493d757bbc74aa58627e1381650fb (patch) | |
tree | 4484d230662126b1ac94ea545ca5429c91e0a68b /drivers/sh | |
parent | 7b98cf0cf4e8798b9e7435f966ed0d90a2a925de (diff) | |
parent | fdd85ec3eb8cc1b663678a3efa16ee59a32e0277 (diff) | |
download | linux-9ff561fdf73493d757bbc74aa58627e1381650fb.tar.xz |
Merge branch 'common/pinctrl' into sh-latest
Diffstat (limited to 'drivers/sh')
-rw-r--r-- | drivers/sh/Kconfig | 16 | ||||
-rw-r--r-- | drivers/sh/Makefile | 4 | ||||
-rw-r--r-- | drivers/sh/pfc/Kconfig | 26 | ||||
-rw-r--r-- | drivers/sh/pfc/Makefile | 3 | ||||
-rw-r--r-- | drivers/sh/pfc/core.c (renamed from drivers/sh/pfc.c) | 86 | ||||
-rw-r--r-- | drivers/sh/pfc/gpio.c (renamed from drivers/sh/pfc-gpio.c) | 104 | ||||
-rw-r--r-- | drivers/sh/pfc/pinctrl.c | 530 |
7 files changed, 619 insertions, 150 deletions
diff --git a/drivers/sh/Kconfig b/drivers/sh/Kconfig index d7dbfee1bc70..d860ef743568 100644 --- a/drivers/sh/Kconfig +++ b/drivers/sh/Kconfig @@ -1,20 +1,6 @@ menu "SuperH / SH-Mobile Driver Options" source "drivers/sh/intc/Kconfig" - -comment "Pin function controller options" - -config SH_PFC - # XXX move off the gpio dependency - depends on GENERIC_GPIO - select GPIO_SH_PFC if ARCH_REQUIRE_GPIOLIB - def_bool y - -config GPIO_SH_PFC - tristate "SuperH PFC GPIO support" - depends on SH_PFC && GPIOLIB - help - This enables support for GPIOs within the SoC's pin function - controller. +source "drivers/sh/pfc/Kconfig" endmenu diff --git a/drivers/sh/Makefile b/drivers/sh/Makefile index f5d93e8de090..e57895b1a425 100644 --- a/drivers/sh/Makefile +++ b/drivers/sh/Makefile @@ -5,7 +5,7 @@ obj-y := intc/ obj-$(CONFIG_HAVE_CLK) += clk/ obj-$(CONFIG_MAPLE) += maple/ +obj-$(CONFIG_SH_PFC) += pfc/ obj-$(CONFIG_SUPERHYWAY) += superhyway/ -obj-$(CONFIG_SH_PFC) += pfc.o -obj-$(CONFIG_GPIO_SH_PFC) += pfc-gpio.o + obj-y += pm_runtime.o diff --git a/drivers/sh/pfc/Kconfig b/drivers/sh/pfc/Kconfig new file mode 100644 index 000000000000..804f9ad1bf4a --- /dev/null +++ b/drivers/sh/pfc/Kconfig @@ -0,0 +1,26 @@ +comment "Pin function controller options" + +config SH_PFC + # XXX move off the gpio dependency + depends on GENERIC_GPIO + select GPIO_SH_PFC if ARCH_REQUIRE_GPIOLIB + select PINCTRL_SH_PFC + def_bool y + +# +# Placeholder for now, rehome to drivers/pinctrl once the PFC APIs +# have settled. +# +config PINCTRL_SH_PFC + tristate "SuperH PFC pin controller driver" + depends on SH_PFC + select PINCTRL + select PINMUX + select PINCONF + +config GPIO_SH_PFC + tristate "SuperH PFC GPIO support" + depends on SH_PFC && GPIOLIB + help + This enables support for GPIOs within the SoC's pin function + controller. diff --git a/drivers/sh/pfc/Makefile b/drivers/sh/pfc/Makefile new file mode 100644 index 000000000000..7916027cce37 --- /dev/null +++ b/drivers/sh/pfc/Makefile @@ -0,0 +1,3 @@ +obj-y += core.o +obj-$(CONFIG_PINCTRL_SH_PFC) += pinctrl.o +obj-$(CONFIG_GPIO_SH_PFC) += gpio.o diff --git a/drivers/sh/pfc.c b/drivers/sh/pfc/core.c index ce4579ebd602..68169373c98b 100644 --- a/drivers/sh/pfc.c +++ b/drivers/sh/pfc/core.c @@ -8,7 +8,7 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define pr_fmt(fmt) "sh_pfc " KBUILD_MODNAME ": " fmt #include <linux/errno.h> #include <linux/kernel.h> @@ -19,6 +19,7 @@ #include <linux/bitops.h> #include <linux/slab.h> #include <linux/ioport.h> +#include <linux/pinctrl/machine.h> static struct sh_pfc *sh_pfc __read_mostly; @@ -501,49 +502,6 @@ int sh_pfc_config_gpio(struct sh_pfc *pfc, unsigned gpio, int pinmux_type, } EXPORT_SYMBOL_GPL(sh_pfc_config_gpio); -int sh_pfc_set_direction(struct sh_pfc *pfc, unsigned gpio, - int new_pinmux_type) -{ - int pinmux_type; - int ret = -EINVAL; - - if (!pfc) - goto err_out; - - pinmux_type = pfc->gpios[gpio].flags & PINMUX_FLAG_TYPE; - - switch (pinmux_type) { - case PINMUX_TYPE_GPIO: - break; - case PINMUX_TYPE_OUTPUT: - case PINMUX_TYPE_INPUT: - case PINMUX_TYPE_INPUT_PULLUP: - case PINMUX_TYPE_INPUT_PULLDOWN: - sh_pfc_config_gpio(pfc, gpio, pinmux_type, GPIO_CFG_FREE); - break; - default: - goto err_out; - } - - if (sh_pfc_config_gpio(pfc, gpio, - new_pinmux_type, - GPIO_CFG_DRYRUN) != 0) - goto err_out; - - if (sh_pfc_config_gpio(pfc, gpio, - new_pinmux_type, - GPIO_CFG_REQ) != 0) - BUG(); - - pfc->gpios[gpio].flags &= ~PINMUX_FLAG_TYPE; - pfc->gpios[gpio].flags |= new_pinmux_type; - - ret = 0; - err_out: - return ret; -} -EXPORT_SYMBOL_GPL(sh_pfc_set_direction); - int register_sh_pfc(struct sh_pfc *pfc) { int (*initroutine)(struct sh_pfc *) = NULL; @@ -563,16 +521,52 @@ int register_sh_pfc(struct sh_pfc *pfc) spin_lock_init(&pfc->lock); + pinctrl_provide_dummies(); setup_data_regs(pfc); sh_pfc = pfc; - pr_info("%s support registered\n", pfc->name); + /* + * Initialize pinctrl bindings first + */ + initroutine = symbol_request(sh_pfc_register_pinctrl); + if (initroutine) { + ret = (*initroutine)(pfc); + symbol_put_addr(initroutine); + + if (unlikely(ret != 0)) + goto err; + } else { + pr_err("failed to initialize pinctrl bindings\n"); + goto err; + } + + /* + * Then the GPIO chip + */ initroutine = symbol_request(sh_pfc_register_gpiochip); if (initroutine) { - (*initroutine)(pfc); + ret = (*initroutine)(pfc); symbol_put_addr(initroutine); + + /* + * If the GPIO chip fails to come up we still leave the + * PFC state as it is, given that there are already + * extant users of it that have succeeded by this point. + */ + if (unlikely(ret != 0)) { + pr_notice("failed to init GPIO chip, ignoring...\n"); + ret = 0; + } } + pr_info("%s support registered\n", pfc->name); + return 0; + +err: + pfc_iounmap(pfc); + sh_pfc = NULL; + + return ret; } diff --git a/drivers/sh/pfc-gpio.c b/drivers/sh/pfc/gpio.c index d74e5a96024b..62bca98474a9 100644 --- a/drivers/sh/pfc-gpio.c +++ b/drivers/sh/pfc/gpio.c @@ -8,7 +8,7 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define pr_fmt(fmt) "sh_pfc " KBUILD_MODNAME ": " fmt #include <linux/init.h> #include <linux/gpio.h> @@ -16,6 +16,7 @@ #include <linux/spinlock.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/pinctrl/consumer.h> struct sh_pfc_chip { struct sh_pfc *pfc; @@ -34,80 +35,12 @@ static struct sh_pfc *gpio_to_pfc(struct gpio_chip *gc) static int sh_gpio_request(struct gpio_chip *gc, unsigned offset) { - struct sh_pfc *pfc = gpio_to_pfc(gc); - struct pinmux_data_reg *dummy; - unsigned long flags; - int i, ret, pinmux_type; - - ret = -EINVAL; - - if (!pfc) - goto err_out; - - spin_lock_irqsave(&pfc->lock, flags); - - if ((pfc->gpios[offset].flags & PINMUX_FLAG_TYPE) != PINMUX_TYPE_NONE) - goto err_unlock; - - /* setup pin function here if no data is associated with pin */ - - if (sh_pfc_get_data_reg(pfc, offset, &dummy, &i) != 0) - pinmux_type = PINMUX_TYPE_FUNCTION; - else - pinmux_type = PINMUX_TYPE_GPIO; - - if (pinmux_type == PINMUX_TYPE_FUNCTION) { - if (sh_pfc_config_gpio(pfc, offset, - pinmux_type, - GPIO_CFG_DRYRUN) != 0) - goto err_unlock; - - if (sh_pfc_config_gpio(pfc, offset, - pinmux_type, - GPIO_CFG_REQ) != 0) - BUG(); - } - - pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; - pfc->gpios[offset].flags |= pinmux_type; - - ret = 0; - err_unlock: - spin_unlock_irqrestore(&pfc->lock, flags); - err_out: - return ret; + return pinctrl_request_gpio(offset); } static void sh_gpio_free(struct gpio_chip *gc, unsigned offset) { - struct sh_pfc *pfc = gpio_to_pfc(gc); - unsigned long flags; - int pinmux_type; - - if (!pfc) - return; - - spin_lock_irqsave(&pfc->lock, flags); - - pinmux_type = pfc->gpios[offset].flags & PINMUX_FLAG_TYPE; - sh_pfc_config_gpio(pfc, offset, pinmux_type, GPIO_CFG_FREE); - pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; - pfc->gpios[offset].flags |= PINMUX_TYPE_NONE; - - spin_unlock_irqrestore(&pfc->lock, flags); -} - -static int sh_gpio_direction_input(struct gpio_chip *gc, unsigned offset) -{ - struct sh_pfc *pfc = gpio_to_pfc(gc); - unsigned long flags; - int ret; - - spin_lock_irqsave(&pfc->lock, flags); - ret = sh_pfc_set_direction(pfc, offset, PINMUX_TYPE_INPUT); - spin_unlock_irqrestore(&pfc->lock, flags); - - return ret; + pinctrl_free_gpio(offset); } static void sh_gpio_set_value(struct sh_pfc *pfc, unsigned gpio, int value) @@ -121,22 +54,6 @@ static void sh_gpio_set_value(struct sh_pfc *pfc, unsigned gpio, int value) sh_pfc_write_bit(dr, bit, value); } -static int sh_gpio_direction_output(struct gpio_chip *gc, unsigned offset, - int value) -{ - struct sh_pfc *pfc = gpio_to_pfc(gc); - unsigned long flags; - int ret; - - sh_gpio_set_value(pfc, offset, value); - - spin_lock_irqsave(&pfc->lock, flags); - ret = sh_pfc_set_direction(pfc, offset, PINMUX_TYPE_OUTPUT); - spin_unlock_irqrestore(&pfc->lock, flags); - - return ret; -} - static int sh_gpio_get_value(struct sh_pfc *pfc, unsigned gpio) { struct pinmux_data_reg *dr = NULL; @@ -148,6 +65,19 @@ static int sh_gpio_get_value(struct sh_pfc *pfc, unsigned gpio) 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); diff --git a/drivers/sh/pfc/pinctrl.c b/drivers/sh/pfc/pinctrl.c new file mode 100644 index 000000000000..0802b6c0d653 --- /dev/null +++ b/drivers/sh/pfc/pinctrl.c @@ -0,0 +1,530 @@ +/* + * SuperH Pin Function Controller pinmux support. + * + * Copyright (C) 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 DRV_NAME "pinctrl-sh_pfc" + +#define pr_fmt(fmt) DRV_NAME " " KBUILD_MODNAME ": " fmt + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/sh_pfc.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/platform_device.h> +#include <linux/pinctrl/consumer.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinmux.h> +#include <linux/pinctrl/pinconf-generic.h> + +struct sh_pfc_pinctrl { + struct pinctrl_dev *pctl; + struct sh_pfc *pfc; + + struct pinmux_gpio **functions; + unsigned int nr_functions; + + struct pinctrl_pin_desc *pads; + unsigned int nr_pads; + + spinlock_t lock; +}; + +static struct sh_pfc_pinctrl *sh_pfc_pmx; + +static int sh_pfc_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); + + return pmx->nr_pads; +} + +static const char *sh_pfc_get_group_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); + + return pmx->pads[selector].name; +} + +static int sh_pfc_get_group_pins(struct pinctrl_dev *pctldev, unsigned group, + const unsigned **pins, unsigned *num_pins) +{ + struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); + + *pins = &pmx->pads[group].number; + *num_pins = 1; + + return 0; +} + +static void sh_pfc_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, + unsigned offset) +{ + seq_printf(s, "%s", DRV_NAME); +} + +static struct pinctrl_ops sh_pfc_pinctrl_ops = { + .get_groups_count = sh_pfc_get_groups_count, + .get_group_name = sh_pfc_get_group_name, + .get_group_pins = sh_pfc_get_group_pins, + .pin_dbg_show = sh_pfc_pin_dbg_show, +}; + +static int sh_pfc_get_functions_count(struct pinctrl_dev *pctldev) +{ + struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); + + return pmx->nr_functions; +} + +static const char *sh_pfc_get_function_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); + + return pmx->functions[selector]->name; +} + +static int sh_pfc_get_function_groups(struct pinctrl_dev *pctldev, unsigned func, + const char * const **groups, + unsigned * const num_groups) +{ + struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); + + *groups = &pmx->functions[func]->name; + *num_groups = 1; + + return 0; +} + +static int sh_pfc_noop_enable(struct pinctrl_dev *pctldev, unsigned func, + unsigned group) +{ + return 0; +} + +static void sh_pfc_noop_disable(struct pinctrl_dev *pctldev, unsigned func, + unsigned group) +{ +} + +static inline int sh_pfc_config_function(struct sh_pfc *pfc, unsigned offset) +{ + if (sh_pfc_config_gpio(pfc, offset, + PINMUX_TYPE_FUNCTION, + GPIO_CFG_DRYRUN) != 0) + return -EINVAL; + + if (sh_pfc_config_gpio(pfc, offset, + PINMUX_TYPE_FUNCTION, + GPIO_CFG_REQ) != 0) + return -EINVAL; + + return 0; +} + +static int sh_pfc_reconfig_pin(struct sh_pfc *pfc, unsigned offset, + int new_type) +{ + unsigned long flags; + int pinmux_type; + int ret = -EINVAL; + + spin_lock_irqsave(&pfc->lock, flags); + + pinmux_type = pfc->gpios[offset].flags & PINMUX_FLAG_TYPE; + + /* + * See if the present config needs to first be de-configured. + */ + switch (pinmux_type) { + case PINMUX_TYPE_GPIO: + break; + case PINMUX_TYPE_OUTPUT: + case PINMUX_TYPE_INPUT: + case PINMUX_TYPE_INPUT_PULLUP: + case PINMUX_TYPE_INPUT_PULLDOWN: + sh_pfc_config_gpio(pfc, offset, pinmux_type, GPIO_CFG_FREE); + break; + default: + goto err; + } + + /* + * Dry run + */ + if (sh_pfc_config_gpio(pfc, offset, new_type, + GPIO_CFG_DRYRUN) != 0) + goto err; + + /* + * Request + */ + if (sh_pfc_config_gpio(pfc, offset, new_type, + GPIO_CFG_REQ) != 0) + goto err; + + pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; + pfc->gpios[offset].flags |= new_type; + + ret = 0; + +err: + spin_unlock_irqrestore(&pfc->lock, flags); + + return ret; +} + + +static int sh_pfc_gpio_request_enable(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned offset) +{ + struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); + struct sh_pfc *pfc = pmx->pfc; + unsigned long flags; + int ret, pinmux_type; + + spin_lock_irqsave(&pfc->lock, flags); + + pinmux_type = pfc->gpios[offset].flags & PINMUX_FLAG_TYPE; + + switch (pinmux_type) { + case PINMUX_TYPE_FUNCTION: + pr_notice_once("Use of GPIO API for function requests is " + "deprecated, convert to pinctrl\n"); + /* handle for now */ + ret = sh_pfc_config_function(pfc, offset); + if (unlikely(ret < 0)) + goto err; + + break; + case PINMUX_TYPE_GPIO: + break; + default: + pr_err("Unsupported mux type (%d), bailing...\n", pinmux_type); + return -ENOTSUPP; + } + + ret = 0; + +err: + spin_unlock_irqrestore(&pfc->lock, flags); + + return ret; +} + +static void sh_pfc_gpio_disable_free(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned offset) +{ + struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); + struct sh_pfc *pfc = pmx->pfc; + unsigned long flags; + int pinmux_type; + + spin_lock_irqsave(&pfc->lock, flags); + + pinmux_type = pfc->gpios[offset].flags & PINMUX_FLAG_TYPE; + + sh_pfc_config_gpio(pfc, offset, pinmux_type, GPIO_CFG_FREE); + + spin_unlock_irqrestore(&pfc->lock, flags); +} + +static int sh_pfc_gpio_set_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned offset, bool input) +{ + struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); + int type = input ? PINMUX_TYPE_INPUT : PINMUX_TYPE_OUTPUT; + + return sh_pfc_reconfig_pin(pmx->pfc, offset, type); +} + +static struct pinmux_ops sh_pfc_pinmux_ops = { + .get_functions_count = sh_pfc_get_functions_count, + .get_function_name = sh_pfc_get_function_name, + .get_function_groups = sh_pfc_get_function_groups, + .enable = sh_pfc_noop_enable, + .disable = sh_pfc_noop_disable, + .gpio_request_enable = sh_pfc_gpio_request_enable, + .gpio_disable_free = sh_pfc_gpio_disable_free, + .gpio_set_direction = sh_pfc_gpio_set_direction, +}; + +static int sh_pfc_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long *config) +{ + struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); + struct sh_pfc *pfc = pmx->pfc; + + *config = pfc->gpios[pin].flags & PINMUX_FLAG_TYPE; + + return 0; +} + +static int sh_pfc_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long config) +{ + struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); + struct sh_pfc *pfc = pmx->pfc; + + /* Validate the new type */ + if (config >= PINMUX_FLAG_TYPE) + return -EINVAL; + + return sh_pfc_reconfig_pin(pmx->pfc, pin, config); +} + +static void sh_pfc_pinconf_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned pin) +{ + const char *pinmux_type_str[] = { + [PINMUX_TYPE_NONE] = "none", + [PINMUX_TYPE_FUNCTION] = "function", + [PINMUX_TYPE_GPIO] = "gpio", + [PINMUX_TYPE_OUTPUT] = "output", + [PINMUX_TYPE_INPUT] = "input", + [PINMUX_TYPE_INPUT_PULLUP] = "input bias pull up", + [PINMUX_TYPE_INPUT_PULLDOWN] = "input bias pull down", + }; + unsigned long config; + int rc; + + rc = sh_pfc_pinconf_get(pctldev, pin, &config); + if (unlikely(rc != 0)) + return; + + seq_printf(s, " %s", pinmux_type_str[config]); +} + +static struct pinconf_ops sh_pfc_pinconf_ops = { + .pin_config_get = sh_pfc_pinconf_get, + .pin_config_set = sh_pfc_pinconf_set, + .pin_config_dbg_show = sh_pfc_pinconf_dbg_show, +}; + +static struct pinctrl_gpio_range sh_pfc_gpio_range = { + .name = DRV_NAME, + .id = 0, +}; + +static struct pinctrl_desc sh_pfc_pinctrl_desc = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pctlops = &sh_pfc_pinctrl_ops, + .pmxops = &sh_pfc_pinmux_ops, + .confops = &sh_pfc_pinconf_ops, +}; + +int sh_pfc_register_pinctrl(struct sh_pfc *pfc) +{ + sh_pfc_pmx = kzalloc(sizeof(struct sh_pfc_pinctrl), GFP_KERNEL); + if (unlikely(!sh_pfc_pmx)) + return -ENOMEM; + + spin_lock_init(&sh_pfc_pmx->lock); + + sh_pfc_pmx->pfc = pfc; + + return 0; +} +EXPORT_SYMBOL_GPL(sh_pfc_register_pinctrl); + +static inline void __devinit sh_pfc_map_one_gpio(struct sh_pfc *pfc, + struct sh_pfc_pinctrl *pmx, + struct pinmux_gpio *gpio, + unsigned offset) +{ + struct pinmux_data_reg *dummy; + unsigned long flags; + int bit; + + gpio->flags &= ~PINMUX_FLAG_TYPE; + + if (sh_pfc_get_data_reg(pfc, offset, &dummy, &bit) == 0) + gpio->flags |= PINMUX_TYPE_GPIO; + else { + gpio->flags |= PINMUX_TYPE_FUNCTION; + + spin_lock_irqsave(&pmx->lock, flags); + pmx->nr_functions++; + spin_unlock_irqrestore(&pmx->lock, flags); + } +} + +/* pinmux ranges -> pinctrl pin descs */ +static int __devinit sh_pfc_map_gpios(struct sh_pfc *pfc, + struct sh_pfc_pinctrl *pmx) +{ + unsigned long flags; + int i; + + pmx->nr_pads = pfc->last_gpio - pfc->first_gpio + 1; + + pmx->pads = kmalloc(sizeof(struct pinctrl_pin_desc) * pmx->nr_pads, + GFP_KERNEL); + if (unlikely(!pmx->pads)) { + pmx->nr_pads = 0; + return -ENOMEM; + } + + spin_lock_irqsave(&pfc->lock, flags); + + /* + * We don't necessarily have a 1:1 mapping between pin and linux + * GPIO number, as the latter maps to the associated enum_id. + * Care needs to be taken to translate back to pin space when + * dealing with any pin configurations. + */ + for (i = 0; i < pmx->nr_pads; i++) { + struct pinctrl_pin_desc *pin = pmx->pads + i; + struct pinmux_gpio *gpio = pfc->gpios + i; + + pin->number = pfc->first_gpio + i; + pin->name = gpio->name; + + /* XXX */ + if (unlikely(!gpio->enum_id)) + continue; + + sh_pfc_map_one_gpio(pfc, pmx, gpio, i); + } + + spin_unlock_irqrestore(&pfc->lock, flags); + + sh_pfc_pinctrl_desc.pins = pmx->pads; + sh_pfc_pinctrl_desc.npins = pmx->nr_pads; + + return 0; +} + +static int __devinit sh_pfc_map_functions(struct sh_pfc *pfc, + struct sh_pfc_pinctrl *pmx) +{ + unsigned long flags; + int i, fn; + + pmx->functions = kzalloc(pmx->nr_functions * sizeof(void *), + GFP_KERNEL); + if (unlikely(!pmx->functions)) + return -ENOMEM; + + spin_lock_irqsave(&pmx->lock, flags); + + for (i = fn = 0; i < pmx->nr_pads; i++) { + struct pinmux_gpio *gpio = pfc->gpios + i; + + if ((gpio->flags & PINMUX_FLAG_TYPE) == PINMUX_TYPE_FUNCTION) + pmx->functions[fn++] = gpio; + } + + spin_unlock_irqrestore(&pmx->lock, flags); + + return 0; +} + +static int __devinit sh_pfc_pinctrl_probe(struct platform_device *pdev) +{ + struct sh_pfc *pfc; + int ret; + + if (unlikely(!sh_pfc_pmx)) + return -ENODEV; + + pfc = sh_pfc_pmx->pfc; + + ret = sh_pfc_map_gpios(pfc, sh_pfc_pmx); + if (unlikely(ret != 0)) + return ret; + + ret = sh_pfc_map_functions(pfc, sh_pfc_pmx); + if (unlikely(ret != 0)) + goto free_pads; + + sh_pfc_pmx->pctl = pinctrl_register(&sh_pfc_pinctrl_desc, &pdev->dev, + sh_pfc_pmx); + if (IS_ERR(sh_pfc_pmx->pctl)) { + ret = PTR_ERR(sh_pfc_pmx->pctl); + goto free_functions; + } + + sh_pfc_gpio_range.npins = pfc->last_gpio - pfc->first_gpio + 1; + sh_pfc_gpio_range.base = pfc->first_gpio; + sh_pfc_gpio_range.pin_base = pfc->first_gpio; + + pinctrl_add_gpio_range(sh_pfc_pmx->pctl, &sh_pfc_gpio_range); + + platform_set_drvdata(pdev, sh_pfc_pmx); + + return 0; + +free_functions: + kfree(sh_pfc_pmx->functions); +free_pads: + kfree(sh_pfc_pmx->pads); + kfree(sh_pfc_pmx); + + return ret; +} + +static int __devexit sh_pfc_pinctrl_remove(struct platform_device *pdev) +{ + struct sh_pfc_pinctrl *pmx = platform_get_drvdata(pdev); + + pinctrl_remove_gpio_range(pmx->pctl, &sh_pfc_gpio_range); + pinctrl_unregister(pmx->pctl); + + platform_set_drvdata(pdev, NULL); + + kfree(sh_pfc_pmx->functions); + kfree(sh_pfc_pmx->pads); + kfree(sh_pfc_pmx); + + return 0; +} + +static struct platform_driver sh_pfc_pinctrl_driver = { + .probe = sh_pfc_pinctrl_probe, + .remove = __devexit_p(sh_pfc_pinctrl_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static struct platform_device sh_pfc_pinctrl_device = { + .name = DRV_NAME, + .id = -1, +}; + +static int __init sh_pfc_pinctrl_init(void) +{ + int rc; + + rc = platform_driver_register(&sh_pfc_pinctrl_driver); + if (likely(!rc)) { + rc = platform_device_register(&sh_pfc_pinctrl_device); + if (unlikely(rc)) + platform_driver_unregister(&sh_pfc_pinctrl_driver); + } + + return rc; +} + +static void __exit sh_pfc_pinctrl_exit(void) +{ + platform_driver_unregister(&sh_pfc_pinctrl_driver); +} + +subsys_initcall(sh_pfc_pinctrl_init); +module_exit(sh_pfc_pinctrl_exit); |