diff options
-rw-r--r-- | drivers/pinctrl/samsung/Kconfig | 10 | ||||
-rw-r--r-- | drivers/pinctrl/samsung/Makefile | 1 | ||||
-rw-r--r-- | drivers/pinctrl/samsung/pinctrl-exynos5440.c | 1005 |
3 files changed, 2 insertions, 1014 deletions
diff --git a/drivers/pinctrl/samsung/Kconfig b/drivers/pinctrl/samsung/Kconfig index 11b5eeb14c4a..425fadd6c346 100644 --- a/drivers/pinctrl/samsung/Kconfig +++ b/drivers/pinctrl/samsung/Kconfig @@ -8,26 +8,20 @@ config PINCTRL_SAMSUNG select PINCONF config PINCTRL_EXYNOS - bool "Pinctrl driver data for Samsung EXYNOS SoCs other than 5440" + bool "Pinctrl driver data for Samsung EXYNOS SoCs" depends on OF && GPIOLIB && (ARCH_EXYNOS || ARCH_S5PV210) select PINCTRL_SAMSUNG select PINCTRL_EXYNOS_ARM if ARM && (ARCH_EXYNOS || ARCH_S5PV210) select PINCTRL_EXYNOS_ARM64 if ARM64 && ARCH_EXYNOS config PINCTRL_EXYNOS_ARM - bool "ARMv7-specific pinctrl driver data for Exynos (except Exynos5440)" if COMPILE_TEST + bool "ARMv7-specific pinctrl driver data for Exynos" if COMPILE_TEST depends on PINCTRL_EXYNOS config PINCTRL_EXYNOS_ARM64 bool "ARMv8-specific pinctrl driver data for Exynos" if COMPILE_TEST depends on PINCTRL_EXYNOS -config PINCTRL_EXYNOS5440 - bool "Samsung EXYNOS5440 SoC pinctrl driver" - depends on SOC_EXYNOS5440 - select PINMUX - select PINCONF - config PINCTRL_S3C24XX bool "Samsung S3C24XX SoC pinctrl driver" depends on ARCH_S3C24XX && OF diff --git a/drivers/pinctrl/samsung/Makefile b/drivers/pinctrl/samsung/Makefile index df426561d067..ed951df6a112 100644 --- a/drivers/pinctrl/samsung/Makefile +++ b/drivers/pinctrl/samsung/Makefile @@ -5,6 +5,5 @@ obj-$(CONFIG_PINCTRL_SAMSUNG) += pinctrl-samsung.o obj-$(CONFIG_PINCTRL_EXYNOS) += pinctrl-exynos.o obj-$(CONFIG_PINCTRL_EXYNOS_ARM) += pinctrl-exynos-arm.o obj-$(CONFIG_PINCTRL_EXYNOS_ARM64) += pinctrl-exynos-arm64.o -obj-$(CONFIG_PINCTRL_EXYNOS5440) += pinctrl-exynos5440.o obj-$(CONFIG_PINCTRL_S3C24XX) += pinctrl-s3c24xx.o obj-$(CONFIG_PINCTRL_S3C64XX) += pinctrl-s3c64xx.o diff --git a/drivers/pinctrl/samsung/pinctrl-exynos5440.c b/drivers/pinctrl/samsung/pinctrl-exynos5440.c deleted file mode 100644 index 3d8d5e812839..000000000000 --- a/drivers/pinctrl/samsung/pinctrl-exynos5440.c +++ /dev/null @@ -1,1005 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -// -// pin-controller/pin-mux/pin-config/gpio-driver for Samsung's EXYNOS5440 SoC. -// -// Author: Thomas Abraham <thomas.ab@samsung.com> -// -// Copyright (c) 2012 Samsung Electronics Co., Ltd. -// http://www.samsung.com - -#include <linux/init.h> -#include <linux/platform_device.h> -#include <linux/io.h> -#include <linux/slab.h> -#include <linux/err.h> -#include <linux/gpio/driver.h> -#include <linux/device.h> -#include <linux/pinctrl/pinctrl.h> -#include <linux/pinctrl/pinmux.h> -#include <linux/pinctrl/pinconf.h> -#include <linux/interrupt.h> -#include <linux/irqdomain.h> -#include <linux/of_irq.h> -#include "../core.h" - -/* EXYNOS5440 GPIO and Pinctrl register offsets */ -#define GPIO_MUX 0x00 -#define GPIO_IE 0x04 -#define GPIO_INT 0x08 -#define GPIO_TYPE 0x0C -#define GPIO_VAL 0x10 -#define GPIO_OE 0x14 -#define GPIO_IN 0x18 -#define GPIO_PE 0x1C -#define GPIO_PS 0x20 -#define GPIO_SR 0x24 -#define GPIO_DS0 0x28 -#define GPIO_DS1 0x2C - -#define EXYNOS5440_MAX_PINS 23 -#define EXYNOS5440_MAX_GPIO_INT 8 -#define PIN_NAME_LENGTH 10 - -#define GROUP_SUFFIX "-grp" -#define FUNCTION_SUFFIX "-mux" - -/* - * pin configuration type and its value are packed together into a 16-bits. - * The upper 8-bits represent the configuration type and the lower 8-bits - * hold the value of the configuration type. - */ -#define PINCFG_TYPE_MASK 0xFF -#define PINCFG_VALUE_SHIFT 8 -#define PINCFG_VALUE_MASK (0xFF << PINCFG_VALUE_SHIFT) -#define PINCFG_PACK(type, value) (((value) << PINCFG_VALUE_SHIFT) | type) -#define PINCFG_UNPACK_TYPE(cfg) ((cfg) & PINCFG_TYPE_MASK) -#define PINCFG_UNPACK_VALUE(cfg) (((cfg) & PINCFG_VALUE_MASK) >> \ - PINCFG_VALUE_SHIFT) - -/** - * enum pincfg_type - possible pin configuration types supported. - * @PINCFG_TYPE_PUD: Pull up/down configuration. - * @PINCFG_TYPE_DRV: Drive strength configuration. - * @PINCFG_TYPE_SKEW_RATE: Skew rate configuration. - * @PINCFG_TYPE_INPUT_TYPE: Pin input type configuration. - */ -enum pincfg_type { - PINCFG_TYPE_PUD, - PINCFG_TYPE_DRV, - PINCFG_TYPE_SKEW_RATE, - PINCFG_TYPE_INPUT_TYPE -}; - -/** - * struct exynos5440_pin_group: represent group of pins for pincfg setting. - * @name: name of the pin group, used to lookup the group. - * @pins: the pins included in this group. - * @num_pins: number of pins included in this group. - */ -struct exynos5440_pin_group { - const char *name; - const unsigned int *pins; - u8 num_pins; -}; - -/** - * struct exynos5440_pmx_func: represent a pin function. - * @name: name of the pin function, used to lookup the function. - * @groups: one or more names of pin groups that provide this function. - * @num_groups: number of groups included in @groups. - * @function: the function number to be programmed when selected. - */ -struct exynos5440_pmx_func { - const char *name; - const char **groups; - u8 num_groups; - unsigned long function; -}; - -/** - * struct exynos5440_pinctrl_priv_data: driver's private runtime data. - * @reg_base: ioremapped based address of the register space. - * @gc: gpio chip registered with gpiolib. - * @pin_groups: list of pin groups parsed from device tree. - * @nr_groups: number of pin groups available. - * @pmx_functions: list of pin functions parsed from device tree. - * @nr_functions: number of pin functions available. - * @range: gpio range to register with pinctrl - */ -struct exynos5440_pinctrl_priv_data { - void __iomem *reg_base; - struct gpio_chip *gc; - struct irq_domain *irq_domain; - - const struct exynos5440_pin_group *pin_groups; - unsigned int nr_groups; - const struct exynos5440_pmx_func *pmx_functions; - unsigned int nr_functions; - struct pinctrl_gpio_range range; -}; - -/** - * struct exynos5440_gpio_intr_data: private data for gpio interrupts. - * @priv: driver's private runtime data. - * @gpio_int: gpio interrupt number. - */ -struct exynos5440_gpio_intr_data { - struct exynos5440_pinctrl_priv_data *priv; - unsigned int gpio_int; -}; - -/* list of all possible config options supported */ -static struct pin_config { - char *prop_cfg; - unsigned int cfg_type; -} pcfgs[] = { - { "samsung,exynos5440-pin-pud", PINCFG_TYPE_PUD }, - { "samsung,exynos5440-pin-drv", PINCFG_TYPE_DRV }, - { "samsung,exynos5440-pin-skew-rate", PINCFG_TYPE_SKEW_RATE }, - { "samsung,exynos5440-pin-input-type", PINCFG_TYPE_INPUT_TYPE }, -}; - -/* check if the selector is a valid pin group selector */ -static int exynos5440_get_group_count(struct pinctrl_dev *pctldev) -{ - struct exynos5440_pinctrl_priv_data *priv; - - priv = pinctrl_dev_get_drvdata(pctldev); - return priv->nr_groups; -} - -/* return the name of the group selected by the group selector */ -static const char *exynos5440_get_group_name(struct pinctrl_dev *pctldev, - unsigned selector) -{ - struct exynos5440_pinctrl_priv_data *priv; - - priv = pinctrl_dev_get_drvdata(pctldev); - return priv->pin_groups[selector].name; -} - -/* return the pin numbers associated with the specified group */ -static int exynos5440_get_group_pins(struct pinctrl_dev *pctldev, - unsigned selector, const unsigned **pins, unsigned *num_pins) -{ - struct exynos5440_pinctrl_priv_data *priv; - - priv = pinctrl_dev_get_drvdata(pctldev); - *pins = priv->pin_groups[selector].pins; - *num_pins = priv->pin_groups[selector].num_pins; - return 0; -} - -/* create pinctrl_map entries by parsing device tree nodes */ -static int exynos5440_dt_node_to_map(struct pinctrl_dev *pctldev, - struct device_node *np, struct pinctrl_map **maps, - unsigned *nmaps) -{ - struct device *dev = pctldev->dev; - struct pinctrl_map *map; - unsigned long *cfg = NULL; - char *gname, *fname; - int cfg_cnt = 0, map_cnt = 0, idx = 0; - - /* count the number of config options specfied in the node */ - for (idx = 0; idx < ARRAY_SIZE(pcfgs); idx++) - if (of_find_property(np, pcfgs[idx].prop_cfg, NULL)) - cfg_cnt++; - - /* - * Find out the number of map entries to create. All the config options - * can be accomadated into a single config map entry. - */ - if (cfg_cnt) - map_cnt = 1; - if (of_find_property(np, "samsung,exynos5440-pin-function", NULL)) - map_cnt++; - if (!map_cnt) { - dev_err(dev, "node %s does not have either config or function " - "configurations\n", np->name); - return -EINVAL; - } - - /* Allocate memory for pin-map entries */ - map = kzalloc(sizeof(*map) * map_cnt, GFP_KERNEL); - if (!map) - return -ENOMEM; - *nmaps = 0; - - /* - * Allocate memory for pin group name. The pin group name is derived - * from the node name from which these map entries are be created. - */ - gname = kasprintf(GFP_KERNEL, "%s%s", np->name, GROUP_SUFFIX); - if (!gname) - goto free_map; - - /* - * don't have config options? then skip over to creating function - * map entries. - */ - if (!cfg_cnt) - goto skip_cfgs; - - /* Allocate memory for config entries */ - cfg = kzalloc(sizeof(*cfg) * cfg_cnt, GFP_KERNEL); - if (!cfg) - goto free_gname; - - /* Prepare a list of config settings */ - for (idx = 0, cfg_cnt = 0; idx < ARRAY_SIZE(pcfgs); idx++) { - u32 value; - if (!of_property_read_u32(np, pcfgs[idx].prop_cfg, &value)) - cfg[cfg_cnt++] = - PINCFG_PACK(pcfgs[idx].cfg_type, value); - } - - /* create the config map entry */ - map[*nmaps].data.configs.group_or_pin = gname; - map[*nmaps].data.configs.configs = cfg; - map[*nmaps].data.configs.num_configs = cfg_cnt; - map[*nmaps].type = PIN_MAP_TYPE_CONFIGS_GROUP; - *nmaps += 1; - -skip_cfgs: - /* create the function map entry */ - if (of_find_property(np, "samsung,exynos5440-pin-function", NULL)) { - fname = kasprintf(GFP_KERNEL, - "%s%s", np->name, FUNCTION_SUFFIX); - if (!fname) - goto free_cfg; - - map[*nmaps].data.mux.group = gname; - map[*nmaps].data.mux.function = fname; - map[*nmaps].type = PIN_MAP_TYPE_MUX_GROUP; - *nmaps += 1; - } - - *maps = map; - return 0; - -free_cfg: - kfree(cfg); -free_gname: - kfree(gname); -free_map: - kfree(map); - return -ENOMEM; -} - -/* free the memory allocated to hold the pin-map table */ -static void exynos5440_dt_free_map(struct pinctrl_dev *pctldev, - struct pinctrl_map *map, unsigned num_maps) -{ - int idx; - - for (idx = 0; idx < num_maps; idx++) { - if (map[idx].type == PIN_MAP_TYPE_MUX_GROUP) { - kfree(map[idx].data.mux.function); - if (!idx) - kfree(map[idx].data.mux.group); - } else if (map->type == PIN_MAP_TYPE_CONFIGS_GROUP) { - kfree(map[idx].data.configs.configs); - if (!idx) - kfree(map[idx].data.configs.group_or_pin); - } - } - - kfree(map); -} - -/* list of pinctrl callbacks for the pinctrl core */ -static const struct pinctrl_ops exynos5440_pctrl_ops = { - .get_groups_count = exynos5440_get_group_count, - .get_group_name = exynos5440_get_group_name, - .get_group_pins = exynos5440_get_group_pins, - .dt_node_to_map = exynos5440_dt_node_to_map, - .dt_free_map = exynos5440_dt_free_map, -}; - -/* check if the selector is a valid pin function selector */ -static int exynos5440_get_functions_count(struct pinctrl_dev *pctldev) -{ - struct exynos5440_pinctrl_priv_data *priv; - - priv = pinctrl_dev_get_drvdata(pctldev); - return priv->nr_functions; -} - -/* return the name of the pin function specified */ -static const char *exynos5440_pinmux_get_fname(struct pinctrl_dev *pctldev, - unsigned selector) -{ - struct exynos5440_pinctrl_priv_data *priv; - - priv = pinctrl_dev_get_drvdata(pctldev); - return priv->pmx_functions[selector].name; -} - -/* return the groups associated for the specified function selector */ -static int exynos5440_pinmux_get_groups(struct pinctrl_dev *pctldev, - unsigned selector, const char * const **groups, - unsigned * const num_groups) -{ - struct exynos5440_pinctrl_priv_data *priv; - - priv = pinctrl_dev_get_drvdata(pctldev); - *groups = priv->pmx_functions[selector].groups; - *num_groups = priv->pmx_functions[selector].num_groups; - return 0; -} - -/* enable or disable a pinmux function */ -static void exynos5440_pinmux_setup(struct pinctrl_dev *pctldev, unsigned selector, - unsigned group, bool enable) -{ - struct exynos5440_pinctrl_priv_data *priv; - void __iomem *base; - u32 function; - u32 data; - - priv = pinctrl_dev_get_drvdata(pctldev); - base = priv->reg_base; - function = priv->pmx_functions[selector].function; - - data = readl(base + GPIO_MUX); - if (enable) - data |= (1 << function); - else - data &= ~(1 << function); - writel(data, base + GPIO_MUX); -} - -/* enable a specified pinmux by writing to registers */ -static int exynos5440_pinmux_set_mux(struct pinctrl_dev *pctldev, - unsigned selector, - unsigned group) -{ - exynos5440_pinmux_setup(pctldev, selector, group, true); - return 0; -} - -/* - * The calls to gpio_direction_output() and gpio_direction_input() - * leads to this function call (via the pinctrl_gpio_direction_{input|output}() - * function called from the gpiolib interface). - */ -static int exynos5440_pinmux_gpio_set_direction(struct pinctrl_dev *pctldev, - struct pinctrl_gpio_range *range, unsigned offset, bool input) -{ - return 0; -} - -/* list of pinmux callbacks for the pinmux vertical in pinctrl core */ -static const struct pinmux_ops exynos5440_pinmux_ops = { - .get_functions_count = exynos5440_get_functions_count, - .get_function_name = exynos5440_pinmux_get_fname, - .get_function_groups = exynos5440_pinmux_get_groups, - .set_mux = exynos5440_pinmux_set_mux, - .gpio_set_direction = exynos5440_pinmux_gpio_set_direction, -}; - -/* set the pin config settings for a specified pin */ -static int exynos5440_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, - unsigned long *configs, - unsigned num_configs) -{ - struct exynos5440_pinctrl_priv_data *priv; - void __iomem *base; - enum pincfg_type cfg_type; - u32 cfg_value; - u32 data; - int i; - - priv = pinctrl_dev_get_drvdata(pctldev); - base = priv->reg_base; - - for (i = 0; i < num_configs; i++) { - cfg_type = PINCFG_UNPACK_TYPE(configs[i]); - cfg_value = PINCFG_UNPACK_VALUE(configs[i]); - - switch (cfg_type) { - case PINCFG_TYPE_PUD: - /* first set pull enable/disable bit */ - data = readl(base + GPIO_PE); - data &= ~(1 << pin); - if (cfg_value) - data |= (1 << pin); - writel(data, base + GPIO_PE); - - /* then set pull up/down bit */ - data = readl(base + GPIO_PS); - data &= ~(1 << pin); - if (cfg_value == 2) - data |= (1 << pin); - writel(data, base + GPIO_PS); - break; - - case PINCFG_TYPE_DRV: - /* set the first bit of the drive strength */ - data = readl(base + GPIO_DS0); - data &= ~(1 << pin); - data |= ((cfg_value & 1) << pin); - writel(data, base + GPIO_DS0); - cfg_value >>= 1; - - /* set the second bit of the driver strength */ - data = readl(base + GPIO_DS1); - data &= ~(1 << pin); - data |= ((cfg_value & 1) << pin); - writel(data, base + GPIO_DS1); - break; - case PINCFG_TYPE_SKEW_RATE: - data = readl(base + GPIO_SR); - data &= ~(1 << pin); - data |= ((cfg_value & 1) << pin); - writel(data, base + GPIO_SR); - break; - case PINCFG_TYPE_INPUT_TYPE: - data = readl(base + GPIO_TYPE); - data &= ~(1 << pin); - data |= ((cfg_value & 1) << pin); - writel(data, base + GPIO_TYPE); - break; - default: - WARN_ON(1); - return -EINVAL; - } - } /* for each config */ - - return 0; -} - -/* get the pin config settings for a specified pin */ -static int exynos5440_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, - unsigned long *config) -{ - struct exynos5440_pinctrl_priv_data *priv; - void __iomem *base; - enum pincfg_type cfg_type = PINCFG_UNPACK_TYPE(*config); - u32 data; - - priv = pinctrl_dev_get_drvdata(pctldev); - base = priv->reg_base; - - switch (cfg_type) { - case PINCFG_TYPE_PUD: - data = readl(base + GPIO_PE); - data = (data >> pin) & 1; - if (!data) - *config = 0; - else - *config = ((readl(base + GPIO_PS) >> pin) & 1) + 1; - break; - case PINCFG_TYPE_DRV: - data = readl(base + GPIO_DS0); - data = (data >> pin) & 1; - *config = data; - data = readl(base + GPIO_DS1); - data = (data >> pin) & 1; - *config |= (data << 1); - break; - case PINCFG_TYPE_SKEW_RATE: - data = readl(base + GPIO_SR); - *config = (data >> pin) & 1; - break; - case PINCFG_TYPE_INPUT_TYPE: - data = readl(base + GPIO_TYPE); - *config = (data >> pin) & 1; - break; - default: - WARN_ON(1); - return -EINVAL; - } - - return 0; -} - -/* set the pin config settings for a specified pin group */ -static int exynos5440_pinconf_group_set(struct pinctrl_dev *pctldev, - unsigned group, unsigned long *configs, - unsigned num_configs) -{ - struct exynos5440_pinctrl_priv_data *priv; - const unsigned int *pins; - unsigned int cnt; - - priv = pinctrl_dev_get_drvdata(pctldev); - pins = priv->pin_groups[group].pins; - - for (cnt = 0; cnt < priv->pin_groups[group].num_pins; cnt++) - exynos5440_pinconf_set(pctldev, pins[cnt], configs, - num_configs); - - return 0; -} - -/* get the pin config settings for a specified pin group */ -static int exynos5440_pinconf_group_get(struct pinctrl_dev *pctldev, - unsigned int group, unsigned long *config) -{ - struct exynos5440_pinctrl_priv_data *priv; - const unsigned int *pins; - - priv = pinctrl_dev_get_drvdata(pctldev); - pins = priv->pin_groups[group].pins; - exynos5440_pinconf_get(pctldev, pins[0], config); - return 0; -} - -/* list of pinconfig callbacks for pinconfig vertical in the pinctrl code */ -static const struct pinconf_ops exynos5440_pinconf_ops = { - .pin_config_get = exynos5440_pinconf_get, - .pin_config_set = exynos5440_pinconf_set, - .pin_config_group_get = exynos5440_pinconf_group_get, - .pin_config_group_set = exynos5440_pinconf_group_set, -}; - -/* gpiolib gpio_set callback function */ -static void exynos5440_gpio_set(struct gpio_chip *gc, unsigned offset, int value) -{ - struct exynos5440_pinctrl_priv_data *priv = gpiochip_get_data(gc); - void __iomem *base = priv->reg_base; - u32 data; - - data = readl(base + GPIO_VAL); - data &= ~(1 << offset); - if (value) - data |= 1 << offset; - writel(data, base + GPIO_VAL); -} - -/* gpiolib gpio_get callback function */ -static int exynos5440_gpio_get(struct gpio_chip *gc, unsigned offset) -{ - struct exynos5440_pinctrl_priv_data *priv = gpiochip_get_data(gc); - void __iomem *base = priv->reg_base; - u32 data; - - data = readl(base + GPIO_IN); - data >>= offset; - data &= 1; - return data; -} - -/* gpiolib gpio_direction_input callback function */ -static int exynos5440_gpio_direction_input(struct gpio_chip *gc, unsigned offset) -{ - struct exynos5440_pinctrl_priv_data *priv = gpiochip_get_data(gc); - void __iomem *base = priv->reg_base; - u32 data; - - /* first disable the data output enable on this pin */ - data = readl(base + GPIO_OE); - data &= ~(1 << offset); - writel(data, base + GPIO_OE); - - /* now enable input on this pin */ - data = readl(base + GPIO_IE); - data |= 1 << offset; - writel(data, base + GPIO_IE); - return 0; -} - -/* gpiolib gpio_direction_output callback function */ -static int exynos5440_gpio_direction_output(struct gpio_chip *gc, unsigned offset, - int value) -{ - struct exynos5440_pinctrl_priv_data *priv = gpiochip_get_data(gc); - void __iomem *base = priv->reg_base; - u32 data; - - exynos5440_gpio_set(gc, offset, value); - - /* first disable the data input enable on this pin */ - data = readl(base + GPIO_IE); - data &= ~(1 << offset); - writel(data, base + GPIO_IE); - - /* now enable output on this pin */ - data = readl(base + GPIO_OE); - data |= 1 << offset; - writel(data, base + GPIO_OE); - return 0; -} - -/* gpiolib gpio_to_irq callback function */ -static int exynos5440_gpio_to_irq(struct gpio_chip *gc, unsigned offset) -{ - struct exynos5440_pinctrl_priv_data *priv = gpiochip_get_data(gc); - unsigned int virq; - - if (offset < 16 || offset > 23) - return -ENXIO; - - if (!priv->irq_domain) - return -ENXIO; - - virq = irq_create_mapping(priv->irq_domain, offset - 16); - return virq ? : -ENXIO; -} - -/* parse the pin numbers listed in the 'samsung,exynos5440-pins' property */ -static int exynos5440_pinctrl_parse_dt_pins(struct platform_device *pdev, - struct device_node *cfg_np, unsigned int **pin_list, - unsigned int *npins) -{ - struct device *dev = &pdev->dev; - struct property *prop; - - prop = of_find_property(cfg_np, "samsung,exynos5440-pins", NULL); - if (!prop) - return -ENOENT; - - *npins = prop->length / sizeof(unsigned long); - if (!*npins) { - dev_err(dev, "invalid pin list in %s node", cfg_np->name); - return -EINVAL; - } - - *pin_list = devm_kzalloc(dev, *npins * sizeof(**pin_list), GFP_KERNEL); - if (!*pin_list) - return -ENOMEM; - - return of_property_read_u32_array(cfg_np, "samsung,exynos5440-pins", - *pin_list, *npins); -} - -/* - * Parse the information about all the available pin groups and pin functions - * from device node of the pin-controller. - */ -static int exynos5440_pinctrl_parse_dt(struct platform_device *pdev, - struct exynos5440_pinctrl_priv_data *priv) -{ - struct device *dev = &pdev->dev; - struct device_node *dev_np = dev->of_node; - struct device_node *cfg_np; - struct exynos5440_pin_group *groups, *grp; - struct exynos5440_pmx_func *functions, *func; - unsigned *pin_list; - unsigned int npins, grp_cnt, func_idx = 0; - char *gname, *fname; - int ret; - - grp_cnt = of_get_child_count(dev_np); - if (!grp_cnt) - return -EINVAL; - - groups = devm_kzalloc(dev, grp_cnt * sizeof(*groups), GFP_KERNEL); - if (!groups) - return -EINVAL; - - grp = groups; - - functions = devm_kzalloc(dev, grp_cnt * sizeof(*functions), GFP_KERNEL); - if (!functions) - return -EINVAL; - - func = functions; - - /* - * Iterate over all the child nodes of the pin controller node - * and create pin groups and pin function lists. - */ - for_each_child_of_node(dev_np, cfg_np) { - u32 function; - - ret = exynos5440_pinctrl_parse_dt_pins(pdev, cfg_np, - &pin_list, &npins); - if (ret) { - gname = NULL; - goto skip_to_pin_function; - } - - /* derive pin group name from the node name */ - gname = devm_kasprintf(dev, GFP_KERNEL, - "%s%s", cfg_np->name, GROUP_SUFFIX); - if (!gname) - return -ENOMEM; - - grp->name = gname; - grp->pins = pin_list; - grp->num_pins = npins; - grp++; - -skip_to_pin_function: - ret = of_property_read_u32(cfg_np, "samsung,exynos5440-pin-function", - &function); - if (ret) - continue; - - /* derive function name from the node name */ - fname = devm_kasprintf(dev, GFP_KERNEL, - "%s%s", cfg_np->name, FUNCTION_SUFFIX); - if (!fname) - return -ENOMEM; - - func->name = fname; - func->groups = devm_kzalloc(dev, sizeof(char *), GFP_KERNEL); - if (!func->groups) - return -ENOMEM; - func->groups[0] = gname; - func->num_groups = gname ? 1 : 0; - func->function = function; - func++; - func_idx++; - } - - priv->pin_groups = groups; - priv->nr_groups = grp_cnt; - priv->pmx_functions = functions; - priv->nr_functions = func_idx; - return 0; -} - -/* register the pinctrl interface with the pinctrl subsystem */ -static int exynos5440_pinctrl_register(struct platform_device *pdev, - struct exynos5440_pinctrl_priv_data *priv) -{ - struct device *dev = &pdev->dev; - struct pinctrl_desc *ctrldesc; - struct pinctrl_dev *pctl_dev; - struct pinctrl_pin_desc *pindesc, *pdesc; - char *pin_names; - int pin, ret; - - ctrldesc = devm_kzalloc(dev, sizeof(*ctrldesc), GFP_KERNEL); - if (!ctrldesc) - return -ENOMEM; - - ctrldesc->name = "exynos5440-pinctrl"; - ctrldesc->owner = THIS_MODULE; - ctrldesc->pctlops = &exynos5440_pctrl_ops; - ctrldesc->pmxops = &exynos5440_pinmux_ops; - ctrldesc->confops = &exynos5440_pinconf_ops; - - pindesc = devm_kzalloc(&pdev->dev, sizeof(*pindesc) * - EXYNOS5440_MAX_PINS, GFP_KERNEL); - if (!pindesc) - return -ENOMEM; - ctrldesc->pins = pindesc; - ctrldesc->npins = EXYNOS5440_MAX_PINS; - - /* dynamically populate the pin number and pin name for pindesc */ - for (pin = 0, pdesc = pindesc; pin < ctrldesc->npins; pin++, pdesc++) - pdesc->number = pin; - - /* - * allocate space for storing the dynamically generated names for all - * the pins which belong to this pin-controller. - */ - pin_names = devm_kzalloc(&pdev->dev, sizeof(char) * PIN_NAME_LENGTH * - ctrldesc->npins, GFP_KERNEL); - if (!pin_names) - return -ENOMEM; - - /* for each pin, set the name of the pin */ - for (pin = 0; pin < ctrldesc->npins; pin++) { - snprintf(pin_names, 6, "gpio%02d", pin); - pdesc = pindesc + pin; - pdesc->name = pin_names; - pin_names += PIN_NAME_LENGTH; - } - - ret = exynos5440_pinctrl_parse_dt(pdev, priv); - if (ret) - return ret; - - pctl_dev = devm_pinctrl_register(&pdev->dev, ctrldesc, priv); - if (IS_ERR(pctl_dev)) { - dev_err(&pdev->dev, "could not register pinctrl driver\n"); - return PTR_ERR(pctl_dev); - } - - priv->range.name = "exynos5440-pctrl-gpio-range"; - priv->range.id = 0; - priv->range.base = 0; - priv->range.npins = EXYNOS5440_MAX_PINS; - priv->range.gc = priv->gc; - pinctrl_add_gpio_range(pctl_dev, &priv->range); - return 0; -} - -/* register the gpiolib interface with the gpiolib subsystem */ -static int exynos5440_gpiolib_register(struct platform_device *pdev, - struct exynos5440_pinctrl_priv_data *priv) -{ - struct gpio_chip *gc; - int ret; - - gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL); - if (!gc) - return -ENOMEM; - - priv->gc = gc; - gc->base = 0; - gc->ngpio = EXYNOS5440_MAX_PINS; - gc->parent = &pdev->dev; - gc->set = exynos5440_gpio_set; - gc->get = exynos5440_gpio_get; - gc->direction_input = exynos5440_gpio_direction_input; - gc->direction_output = exynos5440_gpio_direction_output; - gc->to_irq = exynos5440_gpio_to_irq; - gc->label = "gpiolib-exynos5440"; - gc->owner = THIS_MODULE; - ret = gpiochip_add_data(gc, priv); - if (ret) { - dev_err(&pdev->dev, "failed to register gpio_chip %s, error " - "code: %d\n", gc->label, ret); - return ret; - } - - return 0; -} - -/* unregister the gpiolib interface with the gpiolib subsystem */ -static int exynos5440_gpiolib_unregister(struct platform_device *pdev, - struct exynos5440_pinctrl_priv_data *priv) -{ - gpiochip_remove(priv->gc); - return 0; -} - -static void exynos5440_gpio_irq_unmask(struct irq_data *irqd) -{ - struct exynos5440_pinctrl_priv_data *d; - unsigned long gpio_int; - - d = irq_data_get_irq_chip_data(irqd); - gpio_int = readl(d->reg_base + GPIO_INT); - gpio_int |= 1 << irqd->hwirq; - writel(gpio_int, d->reg_base + GPIO_INT); -} - -static void exynos5440_gpio_irq_mask(struct irq_data *irqd) -{ - struct exynos5440_pinctrl_priv_data *d; - unsigned long gpio_int; - - d = irq_data_get_irq_chip_data(irqd); - gpio_int = readl(d->reg_base + GPIO_INT); - gpio_int &= ~(1 << irqd->hwirq); - writel(gpio_int, d->reg_base + GPIO_INT); -} - -/* irq_chip for gpio interrupts */ -static struct irq_chip exynos5440_gpio_irq_chip = { - .name = "exynos5440_gpio_irq_chip", - .irq_unmask = exynos5440_gpio_irq_unmask, - .irq_mask = exynos5440_gpio_irq_mask, -}; - -/* interrupt handler for GPIO interrupts 0..7 */ -static irqreturn_t exynos5440_gpio_irq(int irq, void *data) -{ - struct exynos5440_gpio_intr_data *intd = data; - struct exynos5440_pinctrl_priv_data *d = intd->priv; - int virq; - - virq = irq_linear_revmap(d->irq_domain, intd->gpio_int); - if (!virq) - return IRQ_NONE; - generic_handle_irq(virq); - return IRQ_HANDLED; -} - -static int exynos5440_gpio_irq_map(struct irq_domain *h, unsigned int virq, - irq_hw_number_t hw) -{ - struct exynos5440_pinctrl_priv_data *d = h->host_data; - - irq_set_chip_data(virq, d); - irq_set_chip_and_handler(virq, &exynos5440_gpio_irq_chip, - handle_level_irq); - return 0; -} - -/* irq domain callbacks for gpio interrupt controller */ -static const struct irq_domain_ops exynos5440_gpio_irqd_ops = { - .map = exynos5440_gpio_irq_map, - .xlate = irq_domain_xlate_twocell, -}; - -/* setup handling of gpio interrupts */ -static int exynos5440_gpio_irq_init(struct platform_device *pdev, - struct exynos5440_pinctrl_priv_data *priv) -{ - struct device *dev = &pdev->dev; - struct exynos5440_gpio_intr_data *intd; - int i, irq, ret; - - intd = devm_kzalloc(dev, sizeof(*intd) * EXYNOS5440_MAX_GPIO_INT, - GFP_KERNEL); - if (!intd) - return -ENOMEM; - - for (i = 0; i < EXYNOS5440_MAX_GPIO_INT; i++) { - irq = irq_of_parse_and_map(dev->of_node, i); - if (irq <= 0) { - dev_err(dev, "irq parsing failed\n"); - return -EINVAL; - } - - intd->gpio_int = i; - intd->priv = priv; - ret = devm_request_irq(dev, irq, exynos5440_gpio_irq, - 0, dev_name(dev), intd++); - if (ret) { - dev_err(dev, "irq request failed\n"); - return -ENXIO; - } - } - - priv->irq_domain = irq_domain_add_linear(dev->of_node, - EXYNOS5440_MAX_GPIO_INT, - &exynos5440_gpio_irqd_ops, priv); - if (!priv->irq_domain) { - dev_err(dev, "failed to create irq domain\n"); - return -ENXIO; - } - - return 0; -} - -static int exynos5440_pinctrl_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct exynos5440_pinctrl_priv_data *priv; - struct resource *res; - int ret; - - if (!dev->of_node) { - dev_err(dev, "device tree node not found\n"); - return -ENODEV; - } - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->reg_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(priv->reg_base)) - return PTR_ERR(priv->reg_base); - - ret = exynos5440_gpiolib_register(pdev, priv); - if (ret) - return ret; - - ret = exynos5440_pinctrl_register(pdev, priv); - if (ret) { - exynos5440_gpiolib_unregister(pdev, priv); - return ret; - } - - ret = exynos5440_gpio_irq_init(pdev, priv); - if (ret) { - dev_err(dev, "failed to setup gpio interrupts\n"); - return ret; - } - - platform_set_drvdata(pdev, priv); - dev_info(dev, "EXYNOS5440 pinctrl driver registered\n"); - return 0; -} - -static const struct of_device_id exynos5440_pinctrl_dt_match[] = { - { .compatible = "samsung,exynos5440-pinctrl" }, - {}, -}; - -static struct platform_driver exynos5440_pinctrl_driver = { - .probe = exynos5440_pinctrl_probe, - .driver = { - .name = "exynos5440-pinctrl", - .of_match_table = exynos5440_pinctrl_dt_match, - .suppress_bind_attrs = true, - }, -}; - -static int __init exynos5440_pinctrl_drv_register(void) -{ - return platform_driver_register(&exynos5440_pinctrl_driver); -} -postcore_initcall(exynos5440_pinctrl_drv_register); |