cregit-Linux how code gets into the kernel

Release 4.7 drivers/pinctrl/nomadik/pinctrl-abx500.c

/*
 * Copyright (C) ST-Ericsson SA 2013
 *
 * Author: Patrice Chotard <patrice.chotard@st.com>
 * License terms: GNU General Public License (GPL) version 2
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/interrupt.h>
#include <linux/bitops.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/machine.h>

#include "pinctrl-abx500.h"
#include "../core.h"
#include "../pinconf.h"
#include "../pinctrl-utils.h"

/*
 * The AB9540 and AB8540 GPIO support are extended versions
 * of the AB8500 GPIO support.
 * The AB9540 supports an additional (7th) register so that
 * more GPIO may be configured and used.
 * The AB8540 supports 4 new gpios (GPIOx_VBAT) that have
 * internal pull-up and pull-down capabilities.
 */

/*
 * GPIO registers offset
 * Bank: 0x10
 */

#define AB8500_GPIO_SEL1_REG	0x00

#define AB8500_GPIO_SEL2_REG	0x01

#define AB8500_GPIO_SEL3_REG	0x02

#define AB8500_GPIO_SEL4_REG	0x03

#define AB8500_GPIO_SEL5_REG	0x04

#define AB8500_GPIO_SEL6_REG	0x05

#define AB9540_GPIO_SEL7_REG	0x06


#define AB8500_GPIO_DIR1_REG	0x10

#define AB8500_GPIO_DIR2_REG	0x11

#define AB8500_GPIO_DIR3_REG	0x12

#define AB8500_GPIO_DIR4_REG	0x13

#define AB8500_GPIO_DIR5_REG	0x14

#define AB8500_GPIO_DIR6_REG	0x15

#define AB9540_GPIO_DIR7_REG	0x16


#define AB8500_GPIO_OUT1_REG	0x20

#define AB8500_GPIO_OUT2_REG	0x21

#define AB8500_GPIO_OUT3_REG	0x22

#define AB8500_GPIO_OUT4_REG	0x23

#define AB8500_GPIO_OUT5_REG	0x24

#define AB8500_GPIO_OUT6_REG	0x25

#define AB9540_GPIO_OUT7_REG	0x26


#define AB8500_GPIO_PUD1_REG	0x30

#define AB8500_GPIO_PUD2_REG	0x31

#define AB8500_GPIO_PUD3_REG	0x32

#define AB8500_GPIO_PUD4_REG	0x33

#define AB8500_GPIO_PUD5_REG	0x34

#define AB8500_GPIO_PUD6_REG	0x35

#define AB9540_GPIO_PUD7_REG	0x36


#define AB8500_GPIO_IN1_REG	0x40

#define AB8500_GPIO_IN2_REG	0x41

#define AB8500_GPIO_IN3_REG	0x42

#define AB8500_GPIO_IN4_REG	0x43

#define AB8500_GPIO_IN5_REG	0x44

#define AB8500_GPIO_IN6_REG	0x45

#define AB9540_GPIO_IN7_REG	0x46

#define AB8540_GPIO_VINSEL_REG	0x47

#define AB8540_GPIO_PULL_UPDOWN_REG	0x48

#define AB8500_GPIO_ALTFUN_REG	0x50

#define AB8540_GPIO_PULL_UPDOWN_MASK	0x03

#define AB8540_GPIO_VINSEL_MASK	0x03

#define AB8540_GPIOX_VBAT_START	51

#define AB8540_GPIOX_VBAT_END	54


#define ABX500_GPIO_INPUT	0

#define ABX500_GPIO_OUTPUT	1


struct abx500_pinctrl {
	
struct device *dev;
	
struct pinctrl_dev *pctldev;
	
struct abx500_pinctrl_soc_data *soc;
	
struct gpio_chip chip;
	
struct ab8500 *parent;
	
struct abx500_gpio_irq_cluster *irq_cluster;
	
int irq_cluster_size;
};


static int abx500_gpio_get_bit(struct gpio_chip *chip, u8 reg, unsigned offset, bool *bit) { struct abx500_pinctrl *pct = gpiochip_get_data(chip); u8 pos = offset % 8; u8 val; int ret; reg += offset / 8; ret = abx500_get_register_interruptible(pct->dev, AB8500_MISC, reg, &val); *bit = !!(val & BIT(pos)); if (ret < 0) dev_err(pct->dev, "%s read reg =%x, offset=%x failed (%d)\n", __func__, reg, offset, ret); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard10599.06%266.67%
linus walleijlinus walleij10.94%133.33%
Total106100.00%3100.00%


static int abx500_gpio_set_bits(struct gpio_chip *chip, u8 reg, unsigned offset, int val) { struct abx500_pinctrl *pct = gpiochip_get_data(chip); u8 pos = offset % 8; int ret; reg += offset / 8; ret = abx500_mask_and_set_register_interruptible(pct->dev, AB8500_MISC, reg, BIT(pos), val << pos); if (ret < 0) dev_err(pct->dev, "%s write reg, %x offset %x failed (%d)\n", __func__, reg, offset, ret); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard9095.74%250.00%
lee joneslee jones33.19%125.00%
linus walleijlinus walleij11.06%125.00%
Total94100.00%4100.00%

/** * abx500_gpio_get() - Get the particular GPIO value * @chip: Gpio device * @offset: GPIO number to read */
static int abx500_gpio_get(struct gpio_chip *chip, unsigned offset) { struct abx500_pinctrl *pct = gpiochip_get_data(chip); bool bit; bool is_out; u8 gpio_offset = offset - 1; int ret; ret = abx500_gpio_get_bit(chip, AB8500_GPIO_DIR1_REG, gpio_offset, &is_out); if (ret < 0) goto out; if (is_out) ret = abx500_gpio_get_bit(chip, AB8500_GPIO_OUT1_REG, gpio_offset, &bit); else ret = abx500_gpio_get_bit(chip, AB8500_GPIO_IN1_REG, gpio_offset, &bit); out: if (ret < 0) { dev_err(pct->dev, "%s failed (%d)\n", __func__, ret); return ret; } return bit; }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard12499.20%375.00%
linus walleijlinus walleij10.80%125.00%
Total125100.00%4100.00%


static void abx500_gpio_set(struct gpio_chip *chip, unsigned offset, int val) { struct abx500_pinctrl *pct = gpiochip_get_data(chip); int ret; ret = abx500_gpio_set_bits(chip, AB8500_GPIO_OUT1_REG, offset, val); if (ret < 0) dev_err(pct->dev, "%s write failed (%d)\n", __func__, ret); }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard6198.39%266.67%
linus walleijlinus walleij11.61%133.33%
Total62100.00%3100.00%

#ifdef CONFIG_DEBUG_FS
static int abx500_get_pull_updown(struct abx500_pinctrl *pct, int offset, enum abx500_gpio_pull_updown *pull_updown) { u8 pos; u8 val; int ret; struct pullud *pullud; if (!pct->soc->pullud) { dev_err(pct->dev, "%s AB chip doesn't support pull up/down feature", __func__); ret = -EPERM; goto out; } pullud = pct->soc->pullud; if ((offset < pullud->first_pin) || (offset > pullud->last_pin)) { ret = -EINVAL; goto out; } ret = abx500_get_register_interruptible(pct->dev, AB8500_MISC, AB8540_GPIO_PULL_UPDOWN_REG, &val); pos = (offset - pullud->first_pin) << 1; *pull_updown = (val >> pos) & AB8540_GPIO_PULL_UPDOWN_MASK; out: if (ret < 0) dev_err(pct->dev, "%s failed (%d)\n", __func__, ret); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard162100.00%2100.00%
Total162100.00%2100.00%

#endif
static int abx500_set_pull_updown(struct abx500_pinctrl *pct, int offset, enum abx500_gpio_pull_updown val) { u8 pos; int ret; struct pullud *pullud; if (!pct->soc->pullud) { dev_err(pct->dev, "%s AB chip doesn't support pull up/down feature", __func__); ret = -EPERM; goto out; } pullud = pct->soc->pullud; if ((offset < pullud->first_pin) || (offset > pullud->last_pin)) { ret = -EINVAL; goto out; } pos = (offset - pullud->first_pin) << 1; ret = abx500_mask_and_set_register_interruptible(pct->dev, AB8500_MISC, AB8540_GPIO_PULL_UPDOWN_REG, AB8540_GPIO_PULL_UPDOWN_MASK << pos, val << pos); out: if (ret < 0) dev_err(pct->dev, "%s failed (%d)\n", __func__, ret); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard152100.00%3100.00%
Total152100.00%3100.00%


static bool abx500_pullud_supported(struct gpio_chip *chip, unsigned gpio) { struct abx500_pinctrl *pct = gpiochip_get_data(chip); struct pullud *pullud = pct->soc->pullud; return (pullud && gpio >= pullud->first_pin && gpio <= pullud->last_pin); }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard5198.08%266.67%
linus walleijlinus walleij11.92%133.33%
Total52100.00%3100.00%


static int abx500_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int val) { struct abx500_pinctrl *pct = gpiochip_get_data(chip); unsigned gpio; int ret; /* set direction as output */ ret = abx500_gpio_set_bits(chip, AB8500_GPIO_DIR1_REG, offset, ABX500_GPIO_OUTPUT); if (ret < 0) goto out; /* disable pull down */ ret = abx500_gpio_set_bits(chip, AB8500_GPIO_PUD1_REG, offset, ABX500_GPIO_PULL_NONE); if (ret < 0) goto out; /* if supported, disable both pull down and pull up */ gpio = offset + 1; if (abx500_pullud_supported(chip, gpio)) { ret = abx500_set_pull_updown(pct, gpio, ABX500_GPIO_PULL_NONE); } out: if (ret < 0) { dev_err(pct->dev, "%s failed (%d)\n", __func__, ret); return ret; } /* set the output as 1 or 0 */ return abx500_gpio_set_bits(chip, AB8500_GPIO_OUT1_REG, offset, val); }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard14699.32%583.33%
linus walleijlinus walleij10.68%116.67%
Total147100.00%6100.00%


static int abx500_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { /* set the register as input */ return abx500_gpio_set_bits(chip, AB8500_GPIO_DIR1_REG, offset, ABX500_GPIO_INPUT); }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard27100.00%2100.00%
Total27100.00%2100.00%


static int abx500_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { struct abx500_pinctrl *pct = gpiochip_get_data(chip); /* The AB8500 GPIO numbers are off by one */ int gpio = offset + 1; int hwirq; int i; for (i = 0; i < pct->irq_cluster_size; i++) { struct abx500_gpio_irq_cluster *cluster = &pct->irq_cluster[i]; if (gpio >= cluster->start && gpio <= cluster->end) { /* * The ABx500 GPIO's associated IRQs are clustered together * throughout the interrupt numbers at irregular intervals. * To solve this quandry, we have placed the read-in values * into the cluster information table. */ hwirq = gpio - cluster->start + cluster->to_irq; return irq_create_mapping(pct->parent->domain, hwirq); } } return -EINVAL; }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard7869.03%120.00%
lee joneslee jones3026.55%240.00%
linus walleijlinus walleij54.42%240.00%
Total113100.00%5100.00%


static int abx500_set_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip, unsigned gpio, int alt_setting) { struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev); struct alternate_functions af = pct->soc->alternate_functions[gpio]; int ret; int val; unsigned offset; const char *modes[] = { [ABX500_DEFAULT] = "default", [ABX500_ALT_A] = "altA", [ABX500_ALT_B] = "altB", [ABX500_ALT_C] = "altC", }; /* sanity check */ if (((alt_setting == ABX500_ALT_A) && (af.gpiosel_bit == UNUSED)) || ((alt_setting == ABX500_ALT_B) && (af.alt_bit1 == UNUSED)) || ((alt_setting == ABX500_ALT_C) && (af.alt_bit2 == UNUSED))) { dev_dbg(pct->dev, "pin %d doesn't support %s mode\n", gpio, modes[alt_setting]); return -EINVAL; } /* on ABx5xx, there is no GPIO0, so adjust the offset */ offset = gpio - 1; switch (alt_setting) { case ABX500_DEFAULT: /* * for ABx5xx family, default mode is always selected by * writing 0 to GPIOSELx register, except for pins which * support at least ALT_B mode, default mode is selected * by writing 1 to GPIOSELx register */ val = 0; if (af.alt_bit1 != UNUSED) val++; ret = abx500_gpio_set_bits(chip, AB8500_GPIO_SEL1_REG, offset, val); break; case ABX500_ALT_A: /* * for ABx5xx family, alt_a mode is always selected by * writing 1 to GPIOSELx register, except for pins which * support at least ALT_B mode, alt_a mode is selected * by writing 0 to GPIOSELx register and 0 in ALTFUNC * register */ if (af.alt_bit1 != UNUSED) { ret = abx500_gpio_set_bits(chip, AB8500_GPIO_SEL1_REG, offset, 0); if (ret < 0) goto out; ret = abx500_gpio_set_bits(chip, AB8500_GPIO_ALTFUN_REG, af.alt_bit1, !!(af.alta_val & BIT(0))); if (ret < 0) goto out; if (af.alt_bit2 != UNUSED) ret = abx500_gpio_set_bits(chip, AB8500_GPIO_ALTFUN_REG, af.alt_bit2, !!(af.alta_val & BIT(1))); } else ret = abx500_gpio_set_bits(chip, AB8500_GPIO_SEL1_REG, offset, 1); break; case ABX500_ALT_B: ret = abx500_gpio_set_bits(chip, AB8500_GPIO_SEL1_REG, offset, 0); if (ret < 0) goto out; ret = abx500_gpio_set_bits(chip, AB8500_GPIO_ALTFUN_REG, af.alt_bit1, !!(af.altb_val & BIT(0))); if (ret < 0) goto out; if (af.alt_bit2 != UNUSED) ret = abx500_gpio_set_bits(chip, AB8500_GPIO_ALTFUN_REG, af.alt_bit2, !!(af.altb_val & BIT(1))); break; case ABX500_ALT_C: ret = abx500_gpio_set_bits(chip, AB8500_GPIO_SEL1_REG, offset, 0); if (ret < 0) goto out; ret = abx500_gpio_set_bits(chip, AB8500_GPIO_ALTFUN_REG, af.alt_bit2, !!(af.altc_val & BIT(0))); if (ret < 0) goto out; ret = abx500_gpio_set_bits(chip, AB8500_GPIO_ALTFUN_REG, af.alt_bit2, !!(af.altc_val & BIT(1))); break; default: dev_dbg(pct->dev, "unknown alt_setting %d\n", alt_setting); return -EINVAL; } out: if (ret < 0) dev_err(pct->dev, "%s failed (%d)\n", __func__, ret); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard54098.72%240.00%
dan carpenterdan carpenter61.10%240.00%
masanari iidamasanari iida10.18%120.00%
Total547100.00%5100.00%

#ifdef CONFIG_DEBUG_FS
static int abx500_get_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip, unsigned gpio) { u8 mode; bool bit_mode; bool alt_bit1; bool alt_bit2; struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev); struct alternate_functions af = pct->soc->alternate_functions[gpio]; /* on ABx5xx, there is no GPIO0, so adjust the offset */ unsigned offset = gpio - 1; int ret; /* * if gpiosel_bit is set to unused, * it means no GPIO or special case */ if (af.gpiosel_bit == UNUSED) return ABX500_DEFAULT; /* read GpioSelx register */ ret = abx500_gpio_get_bit(chip, AB8500_GPIO_SEL1_REG + (offset / 8), af.gpiosel_bit, &bit_mode); if (ret < 0) goto out; mode = bit_mode; /* sanity check */ if ((af.alt_bit1 < UNUSED) || (af.alt_bit1 > 7) || (af.alt_bit2 < UNUSED) || (af.alt_bit2 > 7)) { dev_err(pct->dev, "alt_bitX value not in correct range (-1 to 7)\n"); return -EINVAL; } /* if alt_bit2 is used, alt_bit1 must be used too */ if ((af.alt_bit2 != UNUSED) && (af.alt_bit1 == UNUSED)) { dev_err(pct->dev, "if alt_bit2 is used, alt_bit1 can't be unused\n"); return -EINVAL; } /* check if pin use AlternateFunction register */ if ((af.alt_bit1 == UNUSED) && (af.alt_bit2 == UNUSED)) return mode; /* * if pin GPIOSEL bit is set and pin supports alternate function, * it means DEFAULT mode */ if (mode) return ABX500_DEFAULT; /* * pin use the AlternatFunction register * read alt_bit1 value */ ret = abx500_gpio_get_bit(chip, AB8500_GPIO_ALTFUN_REG, af.alt_bit1, &alt_bit1); if (ret < 0) goto out; if (af.alt_bit2 != UNUSED) { /* read alt_bit2 value */ ret = abx500_gpio_get_bit(chip, AB8500_GPIO_ALTFUN_REG, af.alt_bit2, &alt_bit2); if (ret < 0) goto out; } else alt_bit2 = 0; mode = (alt_bit2 << 1) + alt_bit1; if (mode == af.alta_val) return ABX500_ALT_A; else if (mode == af.altb_val) return ABX500_ALT_B; else return ABX500_ALT_C; out: dev_err(pct->dev, "%s failed (%d)\n", __func__, ret); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard33997.13%250.00%
linus walleijlinus walleij92.58%125.00%
axel linaxel lin10.29%125.00%
Total349100.00%4100.00%

#include <linux/seq_file.h>
static void abx500_gpio_dbg_show_one(struct seq_file *s, struct pinctrl_dev *pctldev, struct gpio_chip *chip, unsigned offset, unsigned gpio) { struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev); const char *label = gpiochip_is_requested(chip, offset - 1); u8 gpio_offset = offset - 1; int mode = -1; bool is_out; bool pd; enum abx500_gpio_pull_updown pud = 0; int ret; const char *modes[] = { [ABX500_DEFAULT] = "default", [ABX500_ALT_A] = "altA", [ABX500_ALT_B] = "altB", [ABX500_ALT_C] = "altC", }; const char *pull_up_down[] = { [ABX500_GPIO_PULL_DOWN] = "pull down", [ABX500_GPIO_PULL_NONE] = "pull none", [ABX500_GPIO_PULL_NONE + 1] = "pull none", [ABX500_GPIO_PULL_UP] = "pull up", }; ret = abx500_gpio_get_bit(chip, AB8500_GPIO_DIR1_REG, gpio_offset, &is_out); if (ret < 0) goto out; seq_printf(s, " gpio-%-3d (%-20.20s) %-3s", gpio, label ?: "(none)", is_out ? "out" : "in "); if (!is_out) { if (abx500_pullud_supported(chip, offset)) { ret = abx500_get_pull_updown(pct, offset, &pud); if (ret < 0) goto out; seq_printf(s, " %-9s", pull_up_down[pud]); } else { ret = abx500_gpio_get_bit(chip, AB8500_GPIO_PUD1_REG, gpio_offset, &pd); if (ret < 0) goto out; seq_printf(s, " %-9s", pull_up_down[pd]); } } else seq_printf(s, " %-9s", chip->get(chip, offset) ? "hi" : "lo"); mode = abx500_get_mode(pctldev, chip, offset); seq_printf(s, " %s", (mode < 0) ? "unknown" : modes[mode]); out: if (ret < 0) dev_err(pct->dev, "%s failed (%d)\n", __func__, ret); }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard350100.00%5100.00%
Total350100.00%5100.00%


static void abx500_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) { unsigned i; unsigned gpio = chip->base; struct abx500_pinctrl *pct = gpiochip_get_data(chip); struct pinctrl_dev *pctldev = pct->pctldev; for (i = 0; i < chip->ngpio; i++, gpio++) { /* On AB8500, there is no GPIO0, the first is the GPIO 1 */ abx500_gpio_dbg_show_one(s, pctldev, chip, i + 1, gpio); seq_printf(s, "\n"); } }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard8798.86%150.00%
linus walleijlinus walleij11.14%150.00%
Total88100.00%2100.00%

#else
static inline void abx500_gpio_dbg_show_one(struct seq_file *s, struct pinctrl_dev *pctldev, struct gpio_chip *chip, unsigned offset, unsigned gpio) { }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard27100.00%1100.00%
Total27100.00%1100.00%

#define abx500_gpio_dbg_show NULL #endif static struct gpio_chip abx500gpio_chip = { .label = "abx500-gpio", .owner = THIS_MODULE, .request = gpiochip_generic_request, .free = gpiochip_generic_free, .direction_input = abx500_gpio_direction_input, .get = abx500_gpio_get, .direction_output = abx500_gpio_direction_output, .set = abx500_gpio_set, .to_irq = abx500_gpio_to_irq, .dbg_show = abx500_gpio_dbg_show, };
static int abx500_pmx_get_funcs_cnt(struct pinctrl_dev *pctldev) { struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev); return pct->soc->nfunctions; }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard1864.29%150.00%
lee joneslee jones1035.71%150.00%
Total28100.00%2100.00%


static const char *abx500_pmx_get_func_name(struct pinctrl_dev *pctldev, unsigned function) { struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev); return pct->soc->functions[function].name; }

Contributors

PersonTokensPropCommitsCommitProp
lee joneslee jones2052.63%150.00%
patrice chotardpatrice chotard1847.37%150.00%
Total38100.00%2100.00%


static int abx500_pmx_get_func_groups(struct pinctrl_dev *pctldev, unsigned function, const char * const **groups, unsigned * const num_groups) { struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev); *groups = pct->soc->functions[function].groups; *num_groups = pct->soc->functions[function].ngroups; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
lee joneslee jones4058.82%150.00%
patrice chotardpatrice chotard2841.18%150.00%
Total68100.00%2100.00%


static int abx500_pmx_set(struct pinctrl_dev *pctldev, unsigned function, unsigned group) { struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev); struct gpio_chip *chip = &pct->chip; const struct abx500_pingroup *g; int i; int ret = 0; g = &pct->soc->groups[group]; if (g->altsetting < 0) return -EINVAL; dev_dbg(pct->dev, "enable group %s, %u pins\n", g->name, g->npins); for (i = 0; i < g->npins; i++) { dev_dbg(pct->dev, "setting pin %d to altsetting %d\n", g->pins[i], g->altsetting); ret = abx500_set_mode(pctldev, chip, g->pins[i], g->altsetting); } if (ret < 0) dev_err(pct->dev, "%s failed (%d)\n", __func__, ret); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard17099.42%266.67%
linus walleijlinus walleij10.58%133.33%
Total171100.00%3100.00%


static int abx500_gpio_request_enable(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset) { struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev); const struct abx500_pinrange *p; int ret; int i; /* * Different ranges have different ways to enable GPIO function on a * pin, so refer back to our local range type, where we handily define * what altfunc enables GPIO for a certain pin. */ for (i = 0; i < pct->soc->gpio_num_ranges; i++) { p = &pct->soc->gpio_ranges[i]; if ((offset >= p->offset) && (offset < (p->offset + p->npins))) break; } if (i == pct->soc->gpio_num_ranges) { dev_err(pct->dev, "%s failed to locate range\n", __func__); return -ENODEV; } dev_dbg(pct->dev, "enable GPIO by altfunc %d at gpio %d\n", p->altfunc, offset); ret = abx500_set_mode(pct->pctldev, &pct->chip, offset, p->altfunc); if (ret < 0) dev_err(pct->dev, "%s setting altfunc failed\n", __func__); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard17999.44%150.00%
sachin kamatsachin kamat10.56%150.00%
Total180100.00%2100.00%


static void abx500_gpio_disable_free(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset) { }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard18100.00%1100.00%
Total18100.00%1100.00%

static const struct pinmux_ops abx500_pinmux_ops = { .get_functions_count = abx500_pmx_get_funcs_cnt, .get_function_name = abx500_pmx_get_func_name, .get_function_groups = abx500_pmx_get_func_groups, .set_mux = abx500_pmx_set, .gpio_request_enable = abx500_gpio_request_enable, .gpio_disable_free = abx500_gpio_disable_free, };
static int abx500_get_groups_cnt(struct pinctrl_dev *pctldev) { struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev); return pct->soc->ngroups; }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard28100.00%1100.00%
Total28100.00%1100.00%


static const char *abx500_get_group_name(struct pinctrl_dev *pctldev, unsigned selector) { struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev); return pct->soc->groups[selector].name; }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard38100.00%1100.00%
Total38100.00%1100.00%


static int abx500_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector, const unsigned **pins, unsigned *num_pins) { struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev); *pins = pct->soc->groups[selector].pins; *num_pins = pct->soc->groups[selector].npins; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard65100.00%1100.00%
Total65100.00%1100.00%


static void abx500_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned offset) { struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev); struct gpio_chip *chip = &pct->chip; abx500_gpio_dbg_show_one(s, pctldev, chip, offset, chip->base + offset - 1); }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard58100.00%1100.00%
Total58100.00%1100.00%


static int abx500_dt_add_map_mux(struct pinctrl_map **map, unsigned *reserved_maps, unsigned *num_maps, const char *group, const char *function) { if (*num_maps == *reserved_maps) return -ENOSPC; (*map)[*num_maps].type = PIN_MAP_TYPE_MUX_GROUP; (*map)[*num_maps].data.mux.group = group; (*map)[*num_maps].data.mux.function = function; (*num_maps)++; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard98100.00%2100.00%
Total98100.00%2100.00%


static int abx500_dt_add_map_configs(struct pinctrl_map **map, unsigned *reserved_maps, unsigned *num_maps, const char *group, unsigned long *configs, unsigned num_configs) { unsigned long *dup_configs; if (*num_maps == *reserved_maps) return -ENOSPC; dup_configs = kmemdup(configs, num_configs * sizeof(*dup_configs), GFP_KERNEL); if (!dup_configs) return -ENOMEM; (*map)[*num_maps].type = PIN_MAP_TYPE_CONFIGS_PIN; (*map)[*num_maps].data.configs.group_or_pin = group; (*map)[*num_maps].data.configs.configs = dup_configs; (*map)[*num_maps].data.configs.num_configs = num_configs; (*num_maps)++; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard149100.00%1100.00%
Total149100.00%1100.00%


static const char *abx500_find_pin_name(struct pinctrl_dev *pctldev, const char *pin_name) { int i, pin_number; struct abx500_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev); if (sscanf((char *)pin_name, "GPIO%d", &pin_number) == 1) for (i = 0; i < npct->soc->npins; i++) if (npct->soc->pins[i].number == pin_number) return npct->soc->pins[i].name; return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard98100.00%1100.00%
Total98100.00%1100.00%


static int abx500_dt_subnode_to_map(struct pinctrl_dev *pctldev, struct device_node *np, struct pinctrl_map **map, unsigned *reserved_maps, unsigned *num_maps) { int ret; const char *function = NULL; unsigned long *configs; unsigned int nconfigs = 0; struct property *prop; ret = of_property_read_string(np, "function", &function); if (ret >= 0) { const char *group; ret = of_property_count_strings(np, "groups"); if (ret < 0) goto exit; ret = pinctrl_utils_reserve_map(pctldev, map, reserved_maps, num_maps, ret); if (ret < 0) goto exit; of_property_for_each_string(np, "groups", prop, group) { ret = abx500_dt_add_map_mux(map, reserved_maps, num_maps, group, function); if (ret < 0) goto exit; } } ret = pinconf_generic_parse_dt_config(np, pctldev, &configs, &nconfigs); if (nconfigs) { const char *gpio_name; const char *pin; ret = of_property_count_strings(np, "pins"); if (ret < 0) goto exit; ret = pinctrl_utils_reserve_map(pctldev, map, reserved_maps, num_maps, ret); if (ret < 0) goto exit; of_property_for_each_string(np, "pins", prop, pin) { gpio_name = abx500_find_pin_name(pctldev, pin); ret = abx500_dt_add_map_configs(map, reserved_maps, num_maps, gpio_name, configs, 1); if (ret < 0) goto exit; } } exit: return ret; }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard18063.38%116.67%
linus walleijlinus walleij10235.92%466.67%
soren brinkmannsoren brinkmann20.70%116.67%
Total284100.00%6100.00%


static int abx500_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node *np_config, struct pinctrl_map **map, unsigned *num_maps) { unsigned reserved_maps; struct device_node *np; int ret; reserved_maps = 0; *map = NULL; *num_maps = 0; for_each_child_of_node(np_config, np) { ret = abx500_dt_subnode_to_map(pctldev, np, map, &reserved_maps, num_maps); if (ret < 0) { pinctrl_utils_free_map(pctldev, *map, *num_maps); return ret; } } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard9999.00%150.00%
irina tirdeairina tirdea11.00%150.00%
Total100100.00%2100.00%

static const struct pinctrl_ops abx500_pinctrl_ops = { .get_groups_count = abx500_get_groups_cnt, .get_group_name = abx500_get_group_name, .get_group_pins = abx500_get_group_pins, .pin_dbg_show = abx500_pin_dbg_show, .dt_node_to_map = abx500_dt_node_to_map, .dt_free_map = pinctrl_utils_free_map, };
static int abx500_pin_config_get(struct pinctrl_dev *pctldev, unsigned pin, unsigned long *config) { return -ENOSYS; }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard2191.30%133.33%
sachin kamatsachin kamat14.35%133.33%
lee joneslee jones14.35%133.33%
Total23100.00%3100.00%


static int abx500_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin, unsigned long *configs, unsigned num_configs) { struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev); struct gpio_chip *chip = &pct->chip; unsigned offset; int ret = -EINVAL; int i; enum pin_config_param param; enum pin_config_param argument; for (i = 0; i < num_configs; i++) { param = pinconf_to_config_param(configs[i]); argument = pinconf_to_config_argument(configs[i]); dev_dbg(chip->parent, "pin %d [%#lx]: %s %s\n", pin, configs[i], (param == PIN_CONFIG_OUTPUT) ? "output " : "input", (param == PIN_CONFIG_OUTPUT) ? (argument ? "high" : "low") : (argument ? "pull up" : "pull down")); /* on ABx500, there is no GPIO0, so adjust the offset */ offset = pin - 1; switch (param) { case PIN_CONFIG_BIAS_DISABLE: ret = abx500_gpio_direction_input(chip, offset); if (ret < 0) goto out; /* * Some chips only support pull down, while some * actually support both pull up and pull down. Such * chips have a "pullud" range specified for the pins * that support both features. If the pin is not * within that range, we fall back to the old bit set * that only support pull down. */ if (abx500_pullud_supported(chip, pin)) ret = abx500_set_pull_updown(pct, pin, ABX500_GPIO_PULL_NONE); else /* Chip only supports pull down */ ret = abx500_gpio_set_bits(chip, AB8500_GPIO_PUD1_REG, offset, ABX500_GPIO_PULL_NONE); break; case PIN_CONFIG_BIAS_PULL_DOWN: ret = abx500_gpio_direction_input(chip, offset); if (ret < 0) goto out; /* * if argument = 1 set the pull down * else clear the pull down * Some chips only support pull down, while some * actually support both pull up and pull down. Such * chips have a "pullud" range specified for the pins * that support both features. If the pin is not * within that range, we fall back to the old bit set * that only support pull down. */ if (abx500_pullud_supported(chip, pin)) ret = abx500_set_pull_updown(pct, pin, argument ? ABX500_GPIO_PULL_DOWN : ABX500_GPIO_PULL_NONE); else /* Chip only supports pull down */ ret = abx500_gpio_set_bits(chip, AB8500_GPIO_PUD1_REG, offset, argument ? ABX500_GPIO_PULL_DOWN : ABX500_GPIO_PULL_NONE); break; case PIN_CONFIG_BIAS_PULL_UP: ret = abx500_gpio_direction_input(chip, offset); if (ret < 0) goto out; /* * if argument = 1 set the pull up * else clear the pull up */ ret = abx500_gpio_direction_input(chip, offset); /* * Some chips only support pull down, while some * actually support both pull up and pull down. Such * chips have a "pullud" range specified for the pins * that support both features. If the pin is not * within that range, do nothing */ if (abx500_pullud_supported(chip, pin)) ret = abx500_set_pull_updown(pct, pin, argument ? ABX500_GPIO_PULL_UP : ABX500_GPIO_PULL_NONE); break; case PIN_CONFIG_OUTPUT: ret = abx500_gpio_direction_output(chip, offset, argument); break; default: dev_err(chip->parent, "illegal configuration requested\n"); } } /* for each config */ out: if (ret < 0) dev_err(pct->dev, "%s failed (%d)\n", __func__, ret); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard33686.38%770.00%
sherman yinsherman yin5012.85%110.00%
linus walleijlinus walleij20.51%110.00%
sachin kamatsachin kamat10.26%110.00%
Total389100.00%10100.00%

static const struct pinconf_ops abx500_pinconf_ops = { .pin_config_get = abx500_pin_config_get, .pin_config_set = abx500_pin_config_set, .is_generic = true, }; static struct pinctrl_desc abx500_pinctrl_desc = { .name = "pinctrl-abx500", .pctlops = &abx500_pinctrl_ops, .pmxops = &abx500_pinmux_ops, .confops = &abx500_pinconf_ops, .owner = THIS_MODULE, };
static int abx500_get_gpio_num(struct abx500_pinctrl_soc_data *soc) { unsigned int lowest = 0; unsigned int highest = 0; unsigned int npins = 0; int i; /* * Compute number of GPIOs from the last SoC gpio range descriptors * These ranges may include "holes" but the GPIO number space shall * still be homogeneous, so we need to detect and account for any * such holes so that these are included in the number of GPIO pins. */ for (i = 0; i < soc->gpio_num_ranges; i++) { unsigned gstart; unsigned gend; const struct abx500_pinrange *p; p = &soc->gpio_ranges[i]; gstart = p->offset; gend = p->offset + p->npins - 1; if (i == 0) { /* First iteration, set start values */ lowest = gstart; highest = gend; } else { if (gstart < lowest) lowest = gstart; if (gend > highest) highest = gend; } } /* this gives the absolute number of pins */ npins = highest - lowest + 1; return npins; }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard142100.00%1100.00%
Total142100.00%1100.00%

static const struct of_device_id abx500_gpio_match[] = { { .compatible = "stericsson,ab8500-gpio", .data = (void *)PINCTRL_AB8500, }, { .compatible = "stericsson,ab8505-gpio", .data = (void *)PINCTRL_AB8505, }, { .compatible = "stericsson,ab8540-gpio", .data = (void *)PINCTRL_AB8540, }, { .compatible = "stericsson,ab9540-gpio", .data = (void *)PINCTRL_AB9540, }, { } };
static int abx500_gpio_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; const struct of_device_id *match; struct abx500_pinctrl *pct; unsigned int id = -1; int ret; int i; if (!np) { dev_err(&pdev->dev, "gpio dt node missing\n"); return -ENODEV; } pct = devm_kzalloc(&pdev->dev, sizeof(struct abx500_pinctrl), GFP_KERNEL); if (pct == NULL) { dev_err(&pdev->dev, "failed to allocate memory for pct\n"); return -ENOMEM; } pct->dev = &pdev->dev; pct->parent = dev_get_drvdata(pdev->dev.parent); pct->chip = abx500gpio_chip; pct->chip.parent = &pdev->dev; pct->chip.base = -1; /* Dynamic allocation */ match = of_match_device(abx500_gpio_match, &pdev->dev); if (!match) { dev_err(&pdev->dev, "gpio dt not matching\n"); return -ENODEV; } id = (unsigned long)match->data; /* Poke in other ASIC variants here */ switch (id) { case PINCTRL_AB8500: abx500_pinctrl_ab8500_init(&pct->soc); break; case PINCTRL_AB8540: abx500_pinctrl_ab8540_init(&pct->soc); break; case PINCTRL_AB9540: abx500_pinctrl_ab9540_init(&pct->soc); break; case PINCTRL_AB8505: abx500_pinctrl_ab8505_init(&pct->soc); break; default: dev_err(&pdev->dev, "Unsupported pinctrl sub driver (%d)\n", id); return -EINVAL; } if (!pct->soc) { dev_err(&pdev->dev, "Invalid SOC data\n"); return -EINVAL; } pct->chip.ngpio = abx500_get_gpio_num(pct->soc); pct->irq_cluster = pct->soc->gpio_irq_cluster; pct->irq_cluster_size = pct->soc->ngpio_irq_cluster; ret = gpiochip_add_data(&pct->chip, pct); if (ret) { dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret); return ret; } dev_info(&pdev->dev, "added gpiochip\n"); abx500_pinctrl_desc.pins = pct->soc->pins; abx500_pinctrl_desc.npins = pct->soc->npins; pct->pctldev = devm_pinctrl_register(&pdev->dev, &abx500_pinctrl_desc, pct); if (IS_ERR(pct->pctldev)) { dev_err(&pdev->dev, "could not register abx500 pinctrl driver\n"); ret = PTR_ERR(pct->pctldev); goto out_rem_chip; } dev_info(&pdev->dev, "registered pin controller\n"); /* We will handle a range of GPIO pins */ for (i = 0; i < pct->soc->gpio_num_ranges; i++) { const struct abx500_pinrange *p = &pct->soc->gpio_ranges[i]; ret = gpiochip_add_pin_range(&pct->chip, dev_name(&pdev->dev), p->offset - 1, p->offset, p->npins); if (ret < 0) goto out_rem_chip; } platform_set_drvdata(pdev, pct); dev_info(&pdev->dev, "initialized abx500 pinctrl driver\n"); return 0; out_rem_chip: gpiochip_remove(&pct->chip); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard45881.79%533.33%
lee joneslee jones6110.89%533.33%
linus walleijlinus walleij285.00%320.00%
masahiro yamadamasahiro yamada91.61%16.67%
laxman dewanganlaxman dewangan40.71%16.67%
Total560100.00%15100.00%

/** * abx500_gpio_remove() - remove Ab8500-gpio driver * @pdev: Platform device registered */
static int abx500_gpio_remove(struct platform_device *pdev) { struct abx500_pinctrl *pct = platform_get_drvdata(pdev); gpiochip_remove(&pct->chip); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard32100.00%1100.00%
Total32100.00%1100.00%

static struct platform_driver abx500_gpio_driver = { .driver = { .name = "abx500-gpio", .of_match_table = abx500_gpio_match, }, .probe = abx500_gpio_probe, .remove = abx500_gpio_remove, };
static int __init abx500_gpio_init(void) { return platform_driver_register(&abx500_gpio_driver); }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard16100.00%1100.00%
Total16100.00%1100.00%

core_initcall(abx500_gpio_init); MODULE_AUTHOR("Patrice Chotard <patrice.chotard@st.com>"); MODULE_DESCRIPTION("Driver allows to use AxB5xx unused pins to be used as GPIO"); MODULE_ALIAS("platform:abx500-gpio"); MODULE_LICENSE("GPL v2");

Overall Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard511290.93%1529.41%
lee joneslee jones2514.46%1019.61%
linus walleijlinus walleij1662.95%1223.53%
sherman yinsherman yin500.89%11.96%
masahiro yamadamasahiro yamada90.16%11.96%
arnd bergmannarnd bergmann80.14%11.96%
dan carpenterdan carpenter60.11%23.92%
axel linaxel lin40.07%23.92%
laxman dewanganlaxman dewangan40.07%11.96%
sachin kamatsachin kamat30.05%11.96%
irina tirdeairina tirdea20.04%11.96%
soren brinkmannsoren brinkmann20.04%11.96%
laurent pinchartlaurent pinchart20.04%11.96%
jonas gorskijonas gorski20.04%11.96%
masanari iidamasanari iida10.02%11.96%
Total5622100.00%51100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}