cregit-Linux how code gets into the kernel

Release 4.7 drivers/pinctrl/samsung/pinctrl-exynos5440.c

/*
 * pin-controller/pin-mux/pin-config/gpio-driver for Samsung's EXYNOS5440 SoC.
 *
 * Copyright (c) 2012 Samsung Electronics Co., Ltd.
 *              http://www.samsung.com
 *
 * 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.
 */

#include <linux/module.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; }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham28100.00%1100.00%
Total28100.00%1100.00%

/* 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; }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham38100.00%1100.00%
Total38100.00%1100.00%

/* 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; }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham63100.00%1100.00%
Total63100.00%1100.00%

/* 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; }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham45997.45%150.00%
dan carpenterdan carpenter122.55%150.00%
Total471100.00%2100.00%

/* 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); }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham132100.00%1100.00%
Total132100.00%1100.00%

/* 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; }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham28100.00%1100.00%
Total28100.00%1100.00%

/* 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; }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham38100.00%1100.00%
Total38100.00%1100.00%

/* 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; }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham66100.00%1100.00%
Total66100.00%1100.00%

/* 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); }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham100100.00%1100.00%
Total100100.00%1100.00%

/* 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; }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham3096.77%150.00%
linus walleijlinus walleij13.23%150.00%
Total31100.00%2100.00%

/* * 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; }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham25100.00%1100.00%
Total25100.00%1100.00%

/* 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; }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham33088.24%150.00%
sherman yinsherman yin4411.76%150.00%
Total374100.00%2100.00%

/* 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; }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham233100.00%1100.00%
Total233100.00%1100.00%

/* 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; }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham8491.30%150.00%
sherman yinsherman yin88.70%150.00%
Total92100.00%2100.00%

/* 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; }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham64100.00%1100.00%
Total64100.00%1100.00%

/* 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); }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham7598.68%150.00%
linus walleijlinus walleij11.32%150.00%
Total76100.00%2100.00%

/* 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; }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham5598.21%150.00%
linus walleijlinus walleij11.79%150.00%
Total56100.00%2100.00%

/* 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; }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham9198.91%150.00%
linus walleijlinus walleij11.09%150.00%
Total92100.00%2100.00%

/* 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; }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham10399.04%150.00%
linus walleijlinus walleij10.96%150.00%
Total104100.00%2100.00%

/* 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; }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham7298.63%150.00%
linus walleijlinus walleij11.37%150.00%
Total73100.00%2100.00%

/* 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); }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham142100.00%1100.00%
Total142100.00%1100.00%

/* * 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; }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham37096.86%266.67%
dan carpenterdan carpenter123.14%133.33%
Total382100.00%3100.00%

/* 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; }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham34092.39%240.00%
andrew jefferyandrew jeffery184.89%120.00%
masahiro yamadamasahiro yamada71.90%120.00%
laxman dewanganlaxman dewangan30.82%120.00%
Total368100.00%5100.00%

/* 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; }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham15497.47%250.00%
linus walleijlinus walleij42.53%250.00%
Total158100.00%4100.00%

/* 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; }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham26100.00%1100.00%
Total26100.00%1100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham57100.00%1100.00%
Total57100.00%1100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham60100.00%1100.00%
Total60100.00%1100.00%

/* 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; }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham61100.00%1100.00%
Total61100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham47100.00%1100.00%
Total47100.00%1100.00%

/* 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; }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham200100.00%1100.00%
Total200100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham19395.07%250.00%
thierry redingthierry reding94.43%125.00%
wei yongjunwei yongjun10.49%125.00%
Total203100.00%4100.00%

static const struct of_device_id exynos5440_pinctrl_dt_match[] = { { .compatible = "samsung,exynos5440-pinctrl" }, {}, }; MODULE_DEVICE_TABLE(of, exynos5440_pinctrl_dt_match); static struct platform_driver exynos5440_pinctrl_driver = { .probe = exynos5440_pinctrl_probe, .driver = { .name = "exynos5440-pinctrl", .of_match_table = exynos5440_pinctrl_dt_match, }, };
static int __init exynos5440_pinctrl_drv_register(void) { return platform_driver_register(&exynos5440_pinctrl_driver); }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham16100.00%1100.00%
Total16100.00%1100.00%

postcore_initcall(exynos5440_pinctrl_drv_register);
static void __exit exynos5440_pinctrl_drv_unregister(void) { platform_driver_unregister(&exynos5440_pinctrl_driver); }

Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham15100.00%1100.00%
Total15100.00%1100.00%

module_exit(exynos5440_pinctrl_drv_unregister); MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com>"); MODULE_DESCRIPTION("Samsung EXYNOS5440 SoC pinctrl driver"); MODULE_LICENSE("GPL v2");

Overall Contributors

PersonTokensPropCommitsCommitProp
thomas abrahamthomas abraham433396.94%423.53%
sherman yinsherman yin521.16%15.88%
dan carpenterdan carpenter240.54%15.88%
andrew jefferyandrew jeffery230.51%15.88%
linus walleijlinus walleij130.29%317.65%
thierry redingthierry reding90.20%15.88%
masahiro yamadamasahiro yamada70.16%15.88%
laxman dewanganlaxman dewangan30.07%15.88%
laurent pinchartlaurent pinchart30.07%15.88%
sachin kamatsachin kamat10.02%15.88%
axel linaxel lin10.02%15.88%
wei yongjunwei yongjun10.02%15.88%
Total4470100.00%17100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}