linux-stable/drivers/pinctrl/pinctrl-samsung.c
Linus Torvalds 42daabf62b ARM SoC late changes
These are changes that arrived a little late before the merge
 window or that have multiple dependencies on previous branches
 so they did not fit into one of the earlier ones. There
 are 10 branches merged here, a total of 39 non-merge commits.
 Contents are a mixed bag for the above reasons:
 
 * Two new SoC platforms: ST microelectronics stixxxx and
   the TI 'Nspire' graphing calculator. These should have
   been in the 'soc' branch but were a little late
 * Support for the Exynos 5420 variant in mach-exynos,
   which is based on the other exynos branches to avoid
   conflicts.
 * Various small changes for sh-mobile, ux500 and davinci
 * Common clk support for MSM
 
 Conflicts:
 * In Kconfig.debug, various additions trivially conflict,
   the list should be kept in alphabetical order when
   resolving.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.12 (GNU/Linux)
 
 iQIVAwUAUdLnl2CrR//JCVInAQIrKhAAwvtsGNe6j9nDuLEitWtQAmhHYZQyUJ8k
 o9j/1j1CqhE8C0bLRud8D4m1GxfxbGeRm2d0HoNbxda3FmntUufqBDi6neMiQiLO
 VltC5rOYL8Mday0Asc3SBfjBj8SZC2bypicKy5zUfzsObCBt343g1WvYffMDNmwH
 FveOQK6q2BKmO67cazc9tk5xmxjVwP/LB8r5mQtiXmMguw0R+ZIDDIP6xaURFkxX
 SAElleD2wtvpVHP1d6AKHpXN99u3xV3uoJjKljECEXdBzW/ZX8m7FG2tKY5xy368
 ta0Nhh2MSRnBhUYOH9uah4PQWYEsbZ+M/W+3J9tKRu6q9D/c/AAxILyXUY2tcHNC
 o1UwcUn1druirx3X1AW8HYAGNwW7BD3HANzIiUkQZG7ByfM4qCtUEo2SAFNIGBoR
 v1FMLhMPgMWotZnKrDQQd0anxkKIOFaSMRVgpQLW2jQt/B7sHLmEH2yDffkbSD76
 PQDThnW/dfm9dgeK+X4fPrveIMKbjQlbFz0okN+LPsUf8e1045HBgCi2A0lTIGWM
 kVVgXHKKXi8G8HBa4VyDlORVHXk1bJEheF+zlDvdk4fHkcf+H/OfvFG2O9TdIdpb
 ITXRyyteaRM4YIZpnJbzeeZDZXT89c2ah7xq36iM+L1ScidyntPquViXeasSc8r6
 pKu9ZDc0Mow=
 =cRu2
 -----END PGP SIGNATURE-----

Merge tag 'late-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc

Pull ARM SoC late changes from Arnd Bergmann:
 "These are changes that arrived a little late before the merge window
  or that have multiple dependencies on previous branches so they did
  not fit into one of the earlier ones.  There are 10 branches merged
  here, a total of 39 non-merge commits.  Contents are a mixed bag for
  the above reasons:

   * Two new SoC platforms: ST microelectronics stixxxx and the TI
     'Nspire' graphing calculator.  These should have been in the 'soc'
     branch but were a little late
   * Support for the Exynos 5420 variant in mach-exynos, which is based
     on the other exynos branches to avoid conflicts.
   * Various small changes for sh-mobile, ux500 and davinci
   * Common clk support for MSM"

* tag 'late-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (39 commits)
  ARM: ux500: bail out on alien cpus
  ARM: davinci: da850: adopt to pinctrl-single change for configuring multiple pins
  serial: sh-sci: Initialise variables before access in sci_set_termios()
  ARM: stih41x: Add B2020 board support
  ARM: stih41x: Add B2000 board support
  ARM: sti: Add DEBUG_LL console support
  ARM: sti: Add STiH416 SOC support
  ARM: sti: Add STiH415 SOC support
  ARM: msm: Migrate to common clock framework
  ARM: msm: Make proc_comm clock control into a platform driver
  ARM: msm: Prepare clk_get() users in mach-msm for clock-pcom driver
  ARM: msm: Remove clock-7x30.h include file
  ARM: msm: Remove custom clk_set_{max,min}_rate() API
  ARM: msm: Remove custom clk_set_flags() API
  msm: iommu: Use clk_set_rate() instead of clk_set_min_rate()
  msm: iommu: Convert to clk_prepare/unprepare
  msm_sdcc: Convert to clk_prepare/unprepare
  usb: otg: msm: Convert to clk_prepare/unprepare
  msm_serial: Use devm_clk_get() and properly return errors
  msm_serial: Convert to clk_prepare/unprepare
  ...
2013-07-02 14:42:51 -07:00

1168 lines
32 KiB
C

/*
* pin-controller/pin-mux/pin-config/gpio-driver for Samsung's SoC's.
*
* Copyright (c) 2012 Samsung Electronics Co., Ltd.
* http://www.samsung.com
* Copyright (c) 2012 Linaro Ltd
* http://www.linaro.org
*
* Author: Thomas Abraham <thomas.ab@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.
*
* This driver implements the Samsung pinctrl driver. It supports setting up of
* pinmux and pinconf configurations. The gpiolib interface is also included.
* External interrupt (gpio and wakeup) support are not included in this driver
* but provides extensions to which platform specific implementation of the gpio
* and wakeup interrupts can be hooked to.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/irqdomain.h>
#include <linux/spinlock.h>
#include <linux/syscore_ops.h>
#include "core.h"
#include "pinctrl-samsung.h"
#define GROUP_SUFFIX "-grp"
#define GSUFFIX_LEN sizeof(GROUP_SUFFIX)
#define FUNCTION_SUFFIX "-mux"
#define FSUFFIX_LEN sizeof(FUNCTION_SUFFIX)
/* list of all possible config options supported */
static struct pin_config {
char *prop_cfg;
unsigned int cfg_type;
} pcfgs[] = {
{ "samsung,pin-pud", PINCFG_TYPE_PUD },
{ "samsung,pin-drv", PINCFG_TYPE_DRV },
{ "samsung,pin-con-pdn", PINCFG_TYPE_CON_PDN },
{ "samsung,pin-pud-pdn", PINCFG_TYPE_PUD_PDN },
};
/* Global list of devices (struct samsung_pinctrl_drv_data) */
LIST_HEAD(drvdata_list);
static unsigned int pin_base;
static inline struct samsung_pin_bank *gc_to_pin_bank(struct gpio_chip *gc)
{
return container_of(gc, struct samsung_pin_bank, gpio_chip);
}
/* check if the selector is a valid pin group selector */
static int samsung_get_group_count(struct pinctrl_dev *pctldev)
{
struct samsung_pinctrl_drv_data *drvdata;
drvdata = pinctrl_dev_get_drvdata(pctldev);
return drvdata->nr_groups;
}
/* return the name of the group selected by the group selector */
static const char *samsung_get_group_name(struct pinctrl_dev *pctldev,
unsigned selector)
{
struct samsung_pinctrl_drv_data *drvdata;
drvdata = pinctrl_dev_get_drvdata(pctldev);
return drvdata->pin_groups[selector].name;
}
/* return the pin numbers associated with the specified group */
static int samsung_get_group_pins(struct pinctrl_dev *pctldev,
unsigned selector, const unsigned **pins, unsigned *num_pins)
{
struct samsung_pinctrl_drv_data *drvdata;
drvdata = pinctrl_dev_get_drvdata(pctldev);
*pins = drvdata->pin_groups[selector].pins;
*num_pins = drvdata->pin_groups[selector].num_pins;
return 0;
}
/* create pinctrl_map entries by parsing device tree nodes */
static int samsung_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,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) {
dev_err(dev, "could not alloc memory for pin-maps\n");
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 = kzalloc(strlen(np->name) + GSUFFIX_LEN, GFP_KERNEL);
if (!gname) {
dev_err(dev, "failed to alloc memory for group name\n");
goto free_map;
}
sprintf(gname, "%s%s", np->name, GROUP_SUFFIX);
/*
* 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) {
dev_err(dev, "failed to alloc memory for configs\n");
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,pin-function", NULL)) {
fname = kzalloc(strlen(np->name) + FSUFFIX_LEN, GFP_KERNEL);
if (!fname) {
dev_err(dev, "failed to alloc memory for func name\n");
goto free_cfg;
}
sprintf(fname, "%s%s", np->name, FUNCTION_SUFFIX);
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 samsung_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 samsung_pctrl_ops = {
.get_groups_count = samsung_get_group_count,
.get_group_name = samsung_get_group_name,
.get_group_pins = samsung_get_group_pins,
.dt_node_to_map = samsung_dt_node_to_map,
.dt_free_map = samsung_dt_free_map,
};
/* check if the selector is a valid pin function selector */
static int samsung_get_functions_count(struct pinctrl_dev *pctldev)
{
struct samsung_pinctrl_drv_data *drvdata;
drvdata = pinctrl_dev_get_drvdata(pctldev);
return drvdata->nr_functions;
}
/* return the name of the pin function specified */
static const char *samsung_pinmux_get_fname(struct pinctrl_dev *pctldev,
unsigned selector)
{
struct samsung_pinctrl_drv_data *drvdata;
drvdata = pinctrl_dev_get_drvdata(pctldev);
return drvdata->pmx_functions[selector].name;
}
/* return the groups associated for the specified function selector */
static int samsung_pinmux_get_groups(struct pinctrl_dev *pctldev,
unsigned selector, const char * const **groups,
unsigned * const num_groups)
{
struct samsung_pinctrl_drv_data *drvdata;
drvdata = pinctrl_dev_get_drvdata(pctldev);
*groups = drvdata->pmx_functions[selector].groups;
*num_groups = drvdata->pmx_functions[selector].num_groups;
return 0;
}
/*
* given a pin number that is local to a pin controller, find out the pin bank
* and the register base of the pin bank.
*/
static void pin_to_reg_bank(struct samsung_pinctrl_drv_data *drvdata,
unsigned pin, void __iomem **reg, u32 *offset,
struct samsung_pin_bank **bank)
{
struct samsung_pin_bank *b;
b = drvdata->ctrl->pin_banks;
while ((pin >= b->pin_base) &&
((b->pin_base + b->nr_pins - 1) < pin))
b++;
*reg = drvdata->virt_base + b->pctl_offset;
*offset = pin - b->pin_base;
if (bank)
*bank = b;
}
/* enable or disable a pinmux function */
static void samsung_pinmux_setup(struct pinctrl_dev *pctldev, unsigned selector,
unsigned group, bool enable)
{
struct samsung_pinctrl_drv_data *drvdata;
const unsigned int *pins;
struct samsung_pin_bank *bank;
void __iomem *reg;
u32 mask, shift, data, pin_offset, cnt;
unsigned long flags;
drvdata = pinctrl_dev_get_drvdata(pctldev);
pins = drvdata->pin_groups[group].pins;
/*
* for each pin in the pin group selected, program the correspoding pin
* pin function number in the config register.
*/
for (cnt = 0; cnt < drvdata->pin_groups[group].num_pins; cnt++) {
struct samsung_pin_bank_type *type;
pin_to_reg_bank(drvdata, pins[cnt] - drvdata->ctrl->base,
&reg, &pin_offset, &bank);
type = bank->type;
mask = (1 << type->fld_width[PINCFG_TYPE_FUNC]) - 1;
shift = pin_offset * type->fld_width[PINCFG_TYPE_FUNC];
if (shift >= 32) {
/* Some banks have two config registers */
shift -= 32;
reg += 4;
}
spin_lock_irqsave(&bank->slock, flags);
data = readl(reg + type->reg_offset[PINCFG_TYPE_FUNC]);
data &= ~(mask << shift);
if (enable)
data |= drvdata->pin_groups[group].func << shift;
writel(data, reg + type->reg_offset[PINCFG_TYPE_FUNC]);
spin_unlock_irqrestore(&bank->slock, flags);
}
}
/* enable a specified pinmux by writing to registers */
static int samsung_pinmux_enable(struct pinctrl_dev *pctldev, unsigned selector,
unsigned group)
{
samsung_pinmux_setup(pctldev, selector, group, true);
return 0;
}
/* disable a specified pinmux by writing to registers */
static void samsung_pinmux_disable(struct pinctrl_dev *pctldev,
unsigned selector, unsigned group)
{
samsung_pinmux_setup(pctldev, selector, group, false);
}
/*
* 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 samsung_pinmux_gpio_set_direction(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range, unsigned offset, bool input)
{
struct samsung_pin_bank_type *type;
struct samsung_pin_bank *bank;
struct samsung_pinctrl_drv_data *drvdata;
void __iomem *reg;
u32 data, pin_offset, mask, shift;
unsigned long flags;
bank = gc_to_pin_bank(range->gc);
type = bank->type;
drvdata = pinctrl_dev_get_drvdata(pctldev);
pin_offset = offset - bank->pin_base;
reg = drvdata->virt_base + bank->pctl_offset +
type->reg_offset[PINCFG_TYPE_FUNC];
mask = (1 << type->fld_width[PINCFG_TYPE_FUNC]) - 1;
shift = pin_offset * type->fld_width[PINCFG_TYPE_FUNC];
if (shift >= 32) {
/* Some banks have two config registers */
shift -= 32;
reg += 4;
}
spin_lock_irqsave(&bank->slock, flags);
data = readl(reg);
data &= ~(mask << shift);
if (!input)
data |= FUNC_OUTPUT << shift;
writel(data, reg);
spin_unlock_irqrestore(&bank->slock, flags);
return 0;
}
/* list of pinmux callbacks for the pinmux vertical in pinctrl core */
static const struct pinmux_ops samsung_pinmux_ops = {
.get_functions_count = samsung_get_functions_count,
.get_function_name = samsung_pinmux_get_fname,
.get_function_groups = samsung_pinmux_get_groups,
.enable = samsung_pinmux_enable,
.disable = samsung_pinmux_disable,
.gpio_set_direction = samsung_pinmux_gpio_set_direction,
};
/* set or get the pin config settings for a specified pin */
static int samsung_pinconf_rw(struct pinctrl_dev *pctldev, unsigned int pin,
unsigned long *config, bool set)
{
struct samsung_pinctrl_drv_data *drvdata;
struct samsung_pin_bank_type *type;
struct samsung_pin_bank *bank;
void __iomem *reg_base;
enum pincfg_type cfg_type = PINCFG_UNPACK_TYPE(*config);
u32 data, width, pin_offset, mask, shift;
u32 cfg_value, cfg_reg;
unsigned long flags;
drvdata = pinctrl_dev_get_drvdata(pctldev);
pin_to_reg_bank(drvdata, pin - drvdata->ctrl->base, &reg_base,
&pin_offset, &bank);
type = bank->type;
if (cfg_type >= PINCFG_TYPE_NUM || !type->fld_width[cfg_type])
return -EINVAL;
width = type->fld_width[cfg_type];
cfg_reg = type->reg_offset[cfg_type];
spin_lock_irqsave(&bank->slock, flags);
mask = (1 << width) - 1;
shift = pin_offset * width;
data = readl(reg_base + cfg_reg);
if (set) {
cfg_value = PINCFG_UNPACK_VALUE(*config);
data &= ~(mask << shift);
data |= (cfg_value << shift);
writel(data, reg_base + cfg_reg);
} else {
data >>= shift;
data &= mask;
*config = PINCFG_PACK(cfg_type, data);
}
spin_unlock_irqrestore(&bank->slock, flags);
return 0;
}
/* set the pin config settings for a specified pin */
static int samsung_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
unsigned long config)
{
return samsung_pinconf_rw(pctldev, pin, &config, true);
}
/* get the pin config settings for a specified pin */
static int samsung_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin,
unsigned long *config)
{
return samsung_pinconf_rw(pctldev, pin, config, false);
}
/* set the pin config settings for a specified pin group */
static int samsung_pinconf_group_set(struct pinctrl_dev *pctldev,
unsigned group, unsigned long config)
{
struct samsung_pinctrl_drv_data *drvdata;
const unsigned int *pins;
unsigned int cnt;
drvdata = pinctrl_dev_get_drvdata(pctldev);
pins = drvdata->pin_groups[group].pins;
for (cnt = 0; cnt < drvdata->pin_groups[group].num_pins; cnt++)
samsung_pinconf_set(pctldev, pins[cnt], config);
return 0;
}
/* get the pin config settings for a specified pin group */
static int samsung_pinconf_group_get(struct pinctrl_dev *pctldev,
unsigned int group, unsigned long *config)
{
struct samsung_pinctrl_drv_data *drvdata;
const unsigned int *pins;
drvdata = pinctrl_dev_get_drvdata(pctldev);
pins = drvdata->pin_groups[group].pins;
samsung_pinconf_get(pctldev, pins[0], config);
return 0;
}
/* list of pinconfig callbacks for pinconfig vertical in the pinctrl code */
static const struct pinconf_ops samsung_pinconf_ops = {
.pin_config_get = samsung_pinconf_get,
.pin_config_set = samsung_pinconf_set,
.pin_config_group_get = samsung_pinconf_group_get,
.pin_config_group_set = samsung_pinconf_group_set,
};
/* gpiolib gpio_set callback function */
static void samsung_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
{
struct samsung_pin_bank *bank = gc_to_pin_bank(gc);
struct samsung_pin_bank_type *type = bank->type;
unsigned long flags;
void __iomem *reg;
u32 data;
reg = bank->drvdata->virt_base + bank->pctl_offset;
spin_lock_irqsave(&bank->slock, flags);
data = readl(reg + type->reg_offset[PINCFG_TYPE_DAT]);
data &= ~(1 << offset);
if (value)
data |= 1 << offset;
writel(data, reg + type->reg_offset[PINCFG_TYPE_DAT]);
spin_unlock_irqrestore(&bank->slock, flags);
}
/* gpiolib gpio_get callback function */
static int samsung_gpio_get(struct gpio_chip *gc, unsigned offset)
{
void __iomem *reg;
u32 data;
struct samsung_pin_bank *bank = gc_to_pin_bank(gc);
struct samsung_pin_bank_type *type = bank->type;
reg = bank->drvdata->virt_base + bank->pctl_offset;
data = readl(reg + type->reg_offset[PINCFG_TYPE_DAT]);
data >>= offset;
data &= 1;
return data;
}
/*
* gpiolib gpio_direction_input callback function. The setting of the pin
* mux function as 'gpio input' will be handled by the pinctrl susbsystem
* interface.
*/
static int samsung_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
{
return pinctrl_gpio_direction_input(gc->base + offset);
}
/*
* gpiolib gpio_direction_output callback function. The setting of the pin
* mux function as 'gpio output' will be handled by the pinctrl susbsystem
* interface.
*/
static int samsung_gpio_direction_output(struct gpio_chip *gc, unsigned offset,
int value)
{
samsung_gpio_set(gc, offset, value);
return pinctrl_gpio_direction_output(gc->base + offset);
}
/*
* gpiolib gpio_to_irq callback function. Creates a mapping between a GPIO pin
* and a virtual IRQ, if not already present.
*/
static int samsung_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
{
struct samsung_pin_bank *bank = gc_to_pin_bank(gc);
unsigned int virq;
if (!bank->irq_domain)
return -ENXIO;
virq = irq_create_mapping(bank->irq_domain, offset);
return (virq) ? : -ENXIO;
}
/*
* Parse the pin names listed in the 'samsung,pins' property and convert it
* into a list of gpio numbers are create a pin group from it.
*/
static int samsung_pinctrl_parse_dt_pins(struct platform_device *pdev,
struct device_node *cfg_np,
struct pinctrl_desc *pctl,
unsigned int **pin_list,
unsigned int *npins)
{
struct device *dev = &pdev->dev;
struct property *prop;
struct pinctrl_pin_desc const *pdesc = pctl->pins;
unsigned int idx = 0, cnt;
const char *pin_name;
*npins = of_property_count_strings(cfg_np, "samsung,pins");
if (IS_ERR_VALUE(*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) {
dev_err(dev, "failed to allocate memory for pin list\n");
return -ENOMEM;
}
of_property_for_each_string(cfg_np, "samsung,pins", prop, pin_name) {
for (cnt = 0; cnt < pctl->npins; cnt++) {
if (pdesc[cnt].name) {
if (!strcmp(pin_name, pdesc[cnt].name)) {
(*pin_list)[idx++] = pdesc[cnt].number;
break;
}
}
}
if (cnt == pctl->npins) {
dev_err(dev, "pin %s not valid in %s node\n",
pin_name, cfg_np->name);
devm_kfree(dev, *pin_list);
return -EINVAL;
}
}
return 0;
}
/*
* Parse the information about all the available pin groups and pin functions
* from device node of the pin-controller. A pin group is formed with all
* the pins listed in the "samsung,pins" property.
*/
static int samsung_pinctrl_parse_dt(struct platform_device *pdev,
struct samsung_pinctrl_drv_data *drvdata)
{
struct device *dev = &pdev->dev;
struct device_node *dev_np = dev->of_node;
struct device_node *cfg_np;
struct samsung_pin_group *groups, *grp;
struct samsung_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) {
dev_err(dev, "failed allocate memory for ping group list\n");
return -EINVAL;
}
grp = groups;
functions = devm_kzalloc(dev, grp_cnt * sizeof(*functions), GFP_KERNEL);
if (!functions) {
dev_err(dev, "failed to allocate memory for function list\n");
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;
if (!of_find_property(cfg_np, "samsung,pins", NULL))
continue;
ret = samsung_pinctrl_parse_dt_pins(pdev, cfg_np,
&drvdata->pctl, &pin_list, &npins);
if (ret)
return ret;
/* derive pin group name from the node name */
gname = devm_kzalloc(dev, strlen(cfg_np->name) + GSUFFIX_LEN,
GFP_KERNEL);
if (!gname) {
dev_err(dev, "failed to alloc memory for group name\n");
return -ENOMEM;
}
sprintf(gname, "%s%s", cfg_np->name, GROUP_SUFFIX);
grp->name = gname;
grp->pins = pin_list;
grp->num_pins = npins;
of_property_read_u32(cfg_np, "samsung,pin-function", &function);
grp->func = function;
grp++;
if (!of_find_property(cfg_np, "samsung,pin-function", NULL))
continue;
/* derive function name from the node name */
fname = devm_kzalloc(dev, strlen(cfg_np->name) + FSUFFIX_LEN,
GFP_KERNEL);
if (!fname) {
dev_err(dev, "failed to alloc memory for func name\n");
return -ENOMEM;
}
sprintf(fname, "%s%s", cfg_np->name, FUNCTION_SUFFIX);
func->name = fname;
func->groups = devm_kzalloc(dev, sizeof(char *), GFP_KERNEL);
if (!func->groups) {
dev_err(dev, "failed to alloc memory for group list "
"in pin function");
return -ENOMEM;
}
func->groups[0] = gname;
func->num_groups = 1;
func++;
func_idx++;
}
drvdata->pin_groups = groups;
drvdata->nr_groups = grp_cnt;
drvdata->pmx_functions = functions;
drvdata->nr_functions = func_idx;
return 0;
}
/* register the pinctrl interface with the pinctrl subsystem */
static int samsung_pinctrl_register(struct platform_device *pdev,
struct samsung_pinctrl_drv_data *drvdata)
{
struct pinctrl_desc *ctrldesc = &drvdata->pctl;
struct pinctrl_pin_desc *pindesc, *pdesc;
struct samsung_pin_bank *pin_bank;
char *pin_names;
int pin, bank, ret;
ctrldesc->name = "samsung-pinctrl";
ctrldesc->owner = THIS_MODULE;
ctrldesc->pctlops = &samsung_pctrl_ops;
ctrldesc->pmxops = &samsung_pinmux_ops;
ctrldesc->confops = &samsung_pinconf_ops;
pindesc = devm_kzalloc(&pdev->dev, sizeof(*pindesc) *
drvdata->ctrl->nr_pins, GFP_KERNEL);
if (!pindesc) {
dev_err(&pdev->dev, "mem alloc for pin descriptors failed\n");
return -ENOMEM;
}
ctrldesc->pins = pindesc;
ctrldesc->npins = drvdata->ctrl->nr_pins;
/* dynamically populate the pin number and pin name for pindesc */
for (pin = 0, pdesc = pindesc; pin < ctrldesc->npins; pin++, pdesc++)
pdesc->number = pin + drvdata->ctrl->base;
/*
* 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 *
drvdata->ctrl->nr_pins, GFP_KERNEL);
if (!pin_names) {
dev_err(&pdev->dev, "mem alloc for pin names failed\n");
return -ENOMEM;
}
/* for each pin, the name of the pin is pin-bank name + pin number */
for (bank = 0; bank < drvdata->ctrl->nr_banks; bank++) {
pin_bank = &drvdata->ctrl->pin_banks[bank];
for (pin = 0; pin < pin_bank->nr_pins; pin++) {
sprintf(pin_names, "%s-%d", pin_bank->name, pin);
pdesc = pindesc + pin_bank->pin_base + pin;
pdesc->name = pin_names;
pin_names += PIN_NAME_LENGTH;
}
}
drvdata->pctl_dev = pinctrl_register(ctrldesc, &pdev->dev, drvdata);
if (!drvdata->pctl_dev) {
dev_err(&pdev->dev, "could not register pinctrl driver\n");
return -EINVAL;
}
for (bank = 0; bank < drvdata->ctrl->nr_banks; ++bank) {
pin_bank = &drvdata->ctrl->pin_banks[bank];
pin_bank->grange.name = pin_bank->name;
pin_bank->grange.id = bank;
pin_bank->grange.pin_base = pin_bank->pin_base;
pin_bank->grange.base = pin_bank->gpio_chip.base;
pin_bank->grange.npins = pin_bank->gpio_chip.ngpio;
pin_bank->grange.gc = &pin_bank->gpio_chip;
pinctrl_add_gpio_range(drvdata->pctl_dev, &pin_bank->grange);
}
ret = samsung_pinctrl_parse_dt(pdev, drvdata);
if (ret) {
pinctrl_unregister(drvdata->pctl_dev);
return ret;
}
return 0;
}
static const struct gpio_chip samsung_gpiolib_chip = {
.set = samsung_gpio_set,
.get = samsung_gpio_get,
.direction_input = samsung_gpio_direction_input,
.direction_output = samsung_gpio_direction_output,
.to_irq = samsung_gpio_to_irq,
.owner = THIS_MODULE,
};
/* register the gpiolib interface with the gpiolib subsystem */
static int samsung_gpiolib_register(struct platform_device *pdev,
struct samsung_pinctrl_drv_data *drvdata)
{
struct samsung_pin_ctrl *ctrl = drvdata->ctrl;
struct samsung_pin_bank *bank = ctrl->pin_banks;
struct gpio_chip *gc;
int ret;
int i;
for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
bank->gpio_chip = samsung_gpiolib_chip;
gc = &bank->gpio_chip;
gc->base = ctrl->base + bank->pin_base;
gc->ngpio = bank->nr_pins;
gc->dev = &pdev->dev;
gc->of_node = bank->of_node;
gc->label = bank->name;
ret = gpiochip_add(gc);
if (ret) {
dev_err(&pdev->dev, "failed to register gpio_chip %s, error code: %d\n",
gc->label, ret);
goto fail;
}
}
return 0;
fail:
for (--i, --bank; i >= 0; --i, --bank)
if (gpiochip_remove(&bank->gpio_chip))
dev_err(&pdev->dev, "gpio chip %s remove failed\n",
bank->gpio_chip.label);
return ret;
}
/* unregister the gpiolib interface with the gpiolib subsystem */
static int samsung_gpiolib_unregister(struct platform_device *pdev,
struct samsung_pinctrl_drv_data *drvdata)
{
struct samsung_pin_ctrl *ctrl = drvdata->ctrl;
struct samsung_pin_bank *bank = ctrl->pin_banks;
int ret = 0;
int i;
for (i = 0; !ret && i < ctrl->nr_banks; ++i, ++bank)
ret = gpiochip_remove(&bank->gpio_chip);
if (ret)
dev_err(&pdev->dev, "gpio chip remove failed\n");
return ret;
}
static const struct of_device_id samsung_pinctrl_dt_match[];
/* retrieve the soc specific data */
static struct samsung_pin_ctrl *samsung_pinctrl_get_soc_data(
struct samsung_pinctrl_drv_data *d,
struct platform_device *pdev)
{
int id;
const struct of_device_id *match;
struct device_node *node = pdev->dev.of_node;
struct device_node *np;
struct samsung_pin_ctrl *ctrl;
struct samsung_pin_bank *bank;
int i;
id = of_alias_get_id(node, "pinctrl");
if (id < 0) {
dev_err(&pdev->dev, "failed to get alias id\n");
return NULL;
}
match = of_match_node(samsung_pinctrl_dt_match, node);
ctrl = (struct samsung_pin_ctrl *)match->data + id;
bank = ctrl->pin_banks;
for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
spin_lock_init(&bank->slock);
bank->drvdata = d;
bank->pin_base = ctrl->nr_pins;
ctrl->nr_pins += bank->nr_pins;
}
for_each_child_of_node(node, np) {
if (!of_find_property(np, "gpio-controller", NULL))
continue;
bank = ctrl->pin_banks;
for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
if (!strcmp(bank->name, np->name)) {
bank->of_node = np;
break;
}
}
}
ctrl->base = pin_base;
pin_base += ctrl->nr_pins;
return ctrl;
}
static int samsung_pinctrl_probe(struct platform_device *pdev)
{
struct samsung_pinctrl_drv_data *drvdata;
struct device *dev = &pdev->dev;
struct samsung_pin_ctrl *ctrl;
struct resource *res;
int ret;
if (!dev->of_node) {
dev_err(dev, "device tree node not found\n");
return -ENODEV;
}
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata) {
dev_err(dev, "failed to allocate memory for driver's "
"private data\n");
return -ENOMEM;
}
ctrl = samsung_pinctrl_get_soc_data(drvdata, pdev);
if (!ctrl) {
dev_err(&pdev->dev, "driver data not available\n");
return -EINVAL;
}
drvdata->ctrl = ctrl;
drvdata->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
drvdata->virt_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(drvdata->virt_base))
return PTR_ERR(drvdata->virt_base);
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res)
drvdata->irq = res->start;
ret = samsung_gpiolib_register(pdev, drvdata);
if (ret)
return ret;
ret = samsung_pinctrl_register(pdev, drvdata);
if (ret) {
samsung_gpiolib_unregister(pdev, drvdata);
return ret;
}
if (ctrl->eint_gpio_init)
ctrl->eint_gpio_init(drvdata);
if (ctrl->eint_wkup_init)
ctrl->eint_wkup_init(drvdata);
platform_set_drvdata(pdev, drvdata);
/* Add to the global list */
list_add_tail(&drvdata->node, &drvdata_list);
return 0;
}
#ifdef CONFIG_PM
/**
* samsung_pinctrl_suspend_dev - save pinctrl state for suspend for a device
*
* Save data for all banks handled by this device.
*/
static void samsung_pinctrl_suspend_dev(
struct samsung_pinctrl_drv_data *drvdata)
{
struct samsung_pin_ctrl *ctrl = drvdata->ctrl;
void __iomem *virt_base = drvdata->virt_base;
int i;
for (i = 0; i < ctrl->nr_banks; i++) {
struct samsung_pin_bank *bank = &ctrl->pin_banks[i];
void __iomem *reg = virt_base + bank->pctl_offset;
u8 *offs = bank->type->reg_offset;
u8 *widths = bank->type->fld_width;
enum pincfg_type type;
/* Registers without a powerdown config aren't lost */
if (!widths[PINCFG_TYPE_CON_PDN])
continue;
for (type = 0; type < PINCFG_TYPE_NUM; type++)
if (widths[type])
bank->pm_save[type] = readl(reg + offs[type]);
if (widths[PINCFG_TYPE_FUNC] * bank->nr_pins > 32) {
/* Some banks have two config registers */
bank->pm_save[PINCFG_TYPE_NUM] =
readl(reg + offs[PINCFG_TYPE_FUNC] + 4);
pr_debug("Save %s @ %p (con %#010x %08x)\n",
bank->name, reg,
bank->pm_save[PINCFG_TYPE_FUNC],
bank->pm_save[PINCFG_TYPE_NUM]);
} else {
pr_debug("Save %s @ %p (con %#010x)\n", bank->name,
reg, bank->pm_save[PINCFG_TYPE_FUNC]);
}
}
if (ctrl->suspend)
ctrl->suspend(drvdata);
}
/**
* samsung_pinctrl_resume_dev - restore pinctrl state from suspend for a device
*
* Restore one of the banks that was saved during suspend.
*
* We don't bother doing anything complicated to avoid glitching lines since
* we're called before pad retention is turned off.
*/
static void samsung_pinctrl_resume_dev(struct samsung_pinctrl_drv_data *drvdata)
{
struct samsung_pin_ctrl *ctrl = drvdata->ctrl;
void __iomem *virt_base = drvdata->virt_base;
int i;
if (ctrl->resume)
ctrl->resume(drvdata);
for (i = 0; i < ctrl->nr_banks; i++) {
struct samsung_pin_bank *bank = &ctrl->pin_banks[i];
void __iomem *reg = virt_base + bank->pctl_offset;
u8 *offs = bank->type->reg_offset;
u8 *widths = bank->type->fld_width;
enum pincfg_type type;
/* Registers without a powerdown config aren't lost */
if (!widths[PINCFG_TYPE_CON_PDN])
continue;
if (widths[PINCFG_TYPE_FUNC] * bank->nr_pins > 32) {
/* Some banks have two config registers */
pr_debug("%s @ %p (con %#010x %08x => %#010x %08x)\n",
bank->name, reg,
readl(reg + offs[PINCFG_TYPE_FUNC]),
readl(reg + offs[PINCFG_TYPE_FUNC] + 4),
bank->pm_save[PINCFG_TYPE_FUNC],
bank->pm_save[PINCFG_TYPE_NUM]);
writel(bank->pm_save[PINCFG_TYPE_NUM],
reg + offs[PINCFG_TYPE_FUNC] + 4);
} else {
pr_debug("%s @ %p (con %#010x => %#010x)\n", bank->name,
reg, readl(reg + offs[PINCFG_TYPE_FUNC]),
bank->pm_save[PINCFG_TYPE_FUNC]);
}
for (type = 0; type < PINCFG_TYPE_NUM; type++)
if (widths[type])
writel(bank->pm_save[type], reg + offs[type]);
}
}
/**
* samsung_pinctrl_suspend - save pinctrl state for suspend
*
* Save data for all banks across all devices.
*/
static int samsung_pinctrl_suspend(void)
{
struct samsung_pinctrl_drv_data *drvdata;
list_for_each_entry(drvdata, &drvdata_list, node) {
samsung_pinctrl_suspend_dev(drvdata);
}
return 0;
}
/**
* samsung_pinctrl_resume - restore pinctrl state for suspend
*
* Restore data for all banks across all devices.
*/
static void samsung_pinctrl_resume(void)
{
struct samsung_pinctrl_drv_data *drvdata;
list_for_each_entry_reverse(drvdata, &drvdata_list, node) {
samsung_pinctrl_resume_dev(drvdata);
}
}
#else
#define samsung_pinctrl_suspend NULL
#define samsung_pinctrl_resume NULL
#endif
static struct syscore_ops samsung_pinctrl_syscore_ops = {
.suspend = samsung_pinctrl_suspend,
.resume = samsung_pinctrl_resume,
};
static const struct of_device_id samsung_pinctrl_dt_match[] = {
#ifdef CONFIG_PINCTRL_EXYNOS
{ .compatible = "samsung,exynos4210-pinctrl",
.data = (void *)exynos4210_pin_ctrl },
{ .compatible = "samsung,exynos4x12-pinctrl",
.data = (void *)exynos4x12_pin_ctrl },
{ .compatible = "samsung,exynos5250-pinctrl",
.data = (void *)exynos5250_pin_ctrl },
{ .compatible = "samsung,exynos5420-pinctrl",
.data = (void *)exynos5420_pin_ctrl },
#endif
#ifdef CONFIG_PINCTRL_S3C64XX
{ .compatible = "samsung,s3c64xx-pinctrl",
.data = s3c64xx_pin_ctrl },
#endif
#ifdef CONFIG_PINCTRL_S3C24XX
{ .compatible = "samsung,s3c2412-pinctrl",
.data = s3c2412_pin_ctrl },
{ .compatible = "samsung,s3c2416-pinctrl",
.data = s3c2416_pin_ctrl },
{ .compatible = "samsung,s3c2440-pinctrl",
.data = s3c2440_pin_ctrl },
{ .compatible = "samsung,s3c2450-pinctrl",
.data = s3c2450_pin_ctrl },
#endif
{},
};
MODULE_DEVICE_TABLE(of, samsung_pinctrl_dt_match);
static struct platform_driver samsung_pinctrl_driver = {
.probe = samsung_pinctrl_probe,
.driver = {
.name = "samsung-pinctrl",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(samsung_pinctrl_dt_match),
},
};
static int __init samsung_pinctrl_drv_register(void)
{
/*
* Register syscore ops for save/restore of registers across suspend.
* It's important to ensure that this driver is running at an earlier
* initcall level than any arch-specific init calls that install syscore
* ops that turn off pad retention (like exynos_pm_resume).
*/
register_syscore_ops(&samsung_pinctrl_syscore_ops);
return platform_driver_register(&samsung_pinctrl_driver);
}
postcore_initcall(samsung_pinctrl_drv_register);
static void __exit samsung_pinctrl_drv_unregister(void)
{
platform_driver_unregister(&samsung_pinctrl_driver);
}
module_exit(samsung_pinctrl_drv_unregister);
MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com>");
MODULE_DESCRIPTION("Samsung pinctrl driver");
MODULE_LICENSE("GPL v2");