Skip to content

Commit

Permalink
Merge branch 'gpio-reserved-ranges' into devel
Browse files Browse the repository at this point in the history
  • Loading branch information
Linus Walleij committed Mar 27, 2018
2 parents 6cb9215 + 691bf5d commit 89b0b4e
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 11 deletions.
7 changes: 4 additions & 3 deletions Documentation/devicetree/bindings/gpio/gpio.txt
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,9 @@ in a lot of designs, some using all 32 bits, some using 18 and some using
first 18 GPIOs, at local offset 0 .. 17, are in use.

If these GPIOs do not happen to be the first N GPIOs at offset 0...N-1, an
additional bitmask is needed to specify which GPIOs are actually in use,
and which are dummies. The bindings for this case has not yet been
specified, but should be specified if/when such hardware appears.
additional set of tuples is needed to specify which GPIOs are unusable, with
the gpio-reserved-ranges binding. This property indicates the start and size
of the GPIOs that can't be used.

Optionally, a GPIO controller may have a "gpio-line-names" property. This is
an array of strings defining the names of the GPIO lines going out of the
Expand All @@ -178,6 +178,7 @@ gpio-controller@00000000 {
gpio-controller;
#gpio-cells = <2>;
ngpios = <18>;
gpio-reserved-ranges = <0 4>, <12 2>;
gpio-line-names = "MMC-CD", "MMC-WP", "VDD eth", "RST eth", "LED R",
"LED G", "LED B", "Col A", "Col B", "Col C", "Col D",
"Row A", "Row B", "Row C", "Row D", "NMI button",
Expand Down
24 changes: 24 additions & 0 deletions drivers/gpio/gpiolib-of.c
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,28 @@ void of_mm_gpiochip_remove(struct of_mm_gpio_chip *mm_gc)
}
EXPORT_SYMBOL(of_mm_gpiochip_remove);

static void of_gpiochip_init_valid_mask(struct gpio_chip *chip)
{
int len, i;
u32 start, count;
struct device_node *np = chip->of_node;

len = of_property_count_u32_elems(np, "gpio-reserved-ranges");
if (len < 0 || len % 2 != 0)
return;

for (i = 0; i < len; i += 2) {
of_property_read_u32_index(np, "gpio-reserved-ranges",
i, &start);
of_property_read_u32_index(np, "gpio-reserved-ranges",
i + 1, &count);
if (start >= chip->ngpio || start + count >= chip->ngpio)
continue;

bitmap_clear(chip->valid_mask, start, count);
}
};

#ifdef CONFIG_PINCTRL
static int of_gpiochip_add_pin_range(struct gpio_chip *chip)
{
Expand Down Expand Up @@ -615,6 +637,8 @@ int of_gpiochip_add(struct gpio_chip *chip)
if (chip->of_gpio_n_cells > MAX_PHANDLE_ARGS)
return -EINVAL;

of_gpiochip_init_valid_mask(chip);

status = of_gpiochip_add_pin_range(chip);
if (status)
return status;
Expand Down
66 changes: 61 additions & 5 deletions drivers/gpio/gpiolib.c
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,57 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc)
return 0;
}

static unsigned long *gpiochip_allocate_mask(struct gpio_chip *chip)
{
unsigned long *p;

p = kmalloc_array(BITS_TO_LONGS(chip->ngpio), sizeof(*p), GFP_KERNEL);
if (!p)
return NULL;

/* Assume by default all GPIOs are valid */
bitmap_fill(p, chip->ngpio);

return p;
}

static int gpiochip_init_valid_mask(struct gpio_chip *gpiochip)
{
#ifdef CONFIG_OF_GPIO
int size;
struct device_node *np = gpiochip->of_node;

size = of_property_count_u32_elems(np, "gpio-reserved-ranges");
if (size > 0 && size % 2 == 0)
gpiochip->need_valid_mask = true;
#endif

if (!gpiochip->need_valid_mask)
return 0;

gpiochip->valid_mask = gpiochip_allocate_mask(gpiochip);
if (!gpiochip->valid_mask)
return -ENOMEM;

return 0;
}

static void gpiochip_free_valid_mask(struct gpio_chip *gpiochip)
{
kfree(gpiochip->valid_mask);
gpiochip->valid_mask = NULL;
}

bool gpiochip_line_is_valid(const struct gpio_chip *gpiochip,
unsigned int offset)
{
/* No mask means all valid */
if (likely(!gpiochip->valid_mask))
return true;
return test_bit(offset, gpiochip->valid_mask);
}
EXPORT_SYMBOL_GPL(gpiochip_line_is_valid);

/*
* GPIO line handle management
*/
Expand Down Expand Up @@ -1261,6 +1312,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
if (status)
goto err_remove_from_list;

status = gpiochip_init_valid_mask(chip);
if (status)
goto err_remove_irqchip_mask;

status = gpiochip_add_irqchip(chip, lock_key, request_key);
if (status)
goto err_remove_chip;
Expand Down Expand Up @@ -1290,6 +1345,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
acpi_gpiochip_remove(chip);
gpiochip_free_hogs(chip);
of_gpiochip_remove(chip);
gpiochip_free_valid_mask(chip);
err_remove_irqchip_mask:
gpiochip_irqchip_free_valid_mask(chip);
err_remove_from_list:
spin_lock_irqsave(&gpio_lock, flags);
Expand Down Expand Up @@ -1346,6 +1403,7 @@ void gpiochip_remove(struct gpio_chip *chip)
acpi_gpiochip_remove(chip);
gpiochip_remove_pin_ranges(chip);
of_gpiochip_remove(chip);
gpiochip_free_valid_mask(chip);
/*
* We accept no more calls into the driver from this point, so
* NULL the driver data pointer
Expand Down Expand Up @@ -1506,14 +1564,10 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip)
if (!gpiochip->irq.need_valid_mask)
return 0;

gpiochip->irq.valid_mask = kcalloc(BITS_TO_LONGS(gpiochip->ngpio),
sizeof(long), GFP_KERNEL);
gpiochip->irq.valid_mask = gpiochip_allocate_mask(gpiochip);
if (!gpiochip->irq.valid_mask)
return -ENOMEM;

/* Assume by default all GPIOs are valid */
bitmap_fill(gpiochip->irq.valid_mask, gpiochip->ngpio);

return 0;
}

Expand All @@ -1526,6 +1580,8 @@ static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gpiochip)
bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gpiochip,
unsigned int offset)
{
if (!gpiochip_line_is_valid(gpiochip, offset))
return false;
/* No mask means all valid */
if (likely(!gpiochip->irq.valid_mask))
return true;
Expand Down
65 changes: 62 additions & 3 deletions drivers/pinctrl/qcom/pinctrl-msm.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,14 @@ static const struct pinctrl_ops msm_pinctrl_ops = {
.dt_free_map = pinctrl_utils_free_map,
};

static int msm_pinmux_request(struct pinctrl_dev *pctldev, unsigned offset)
{
struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
struct gpio_chip *chip = &pctrl->chip;

return gpiochip_line_is_valid(chip, offset) ? 0 : -EINVAL;
}

static int msm_get_functions_count(struct pinctrl_dev *pctldev)
{
struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
Expand Down Expand Up @@ -166,6 +174,7 @@ static int msm_pinmux_set_mux(struct pinctrl_dev *pctldev,
}

static const struct pinmux_ops msm_pinmux_ops = {
.request = msm_pinmux_request,
.get_functions_count = msm_get_functions_count,
.get_function_name = msm_get_function_name,
.get_function_groups = msm_get_function_groups,
Expand Down Expand Up @@ -506,6 +515,9 @@ static void msm_gpio_dbg_show_one(struct seq_file *s,
"pull up"
};

if (!gpiochip_line_is_valid(chip, offset))
return;

g = &pctrl->soc->groups[offset];
ctl_reg = readl(pctrl->regs + g->ctl_reg);

Expand All @@ -517,17 +529,16 @@ static void msm_gpio_dbg_show_one(struct seq_file *s,
seq_printf(s, " %-8s: %-3s %d", g->name, is_out ? "out" : "in", func);
seq_printf(s, " %dmA", msm_regval_to_drive(drive));
seq_printf(s, " %s", pulls[pull]);
seq_puts(s, "\n");
}

static void msm_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
{
unsigned gpio = chip->base;
unsigned i;

for (i = 0; i < chip->ngpio; i++, gpio++) {
for (i = 0; i < chip->ngpio; i++, gpio++)
msm_gpio_dbg_show_one(s, NULL, chip, i, gpio);
seq_puts(s, "\n");
}
}

#else
Expand Down Expand Up @@ -808,6 +819,46 @@ static void msm_gpio_irq_handler(struct irq_desc *desc)
chained_irq_exit(chip, desc);
}

static int msm_gpio_init_valid_mask(struct gpio_chip *chip,
struct msm_pinctrl *pctrl)
{
int ret;
unsigned int len, i;
unsigned int max_gpios = pctrl->soc->ngpios;
u16 *tmp;

/* The number of GPIOs in the ACPI tables */
len = ret = device_property_read_u16_array(pctrl->dev, "gpios", NULL, 0);
if (ret < 0)
return 0;

if (ret > max_gpios)
return -EINVAL;

tmp = kmalloc_array(len, sizeof(*tmp), GFP_KERNEL);
if (!tmp)
return -ENOMEM;

ret = device_property_read_u16_array(pctrl->dev, "gpios", tmp, len);
if (ret < 0) {
dev_err(pctrl->dev, "could not read list of GPIOs\n");
goto out;
}

bitmap_zero(chip->valid_mask, max_gpios);
for (i = 0; i < len; i++)
set_bit(tmp[i], chip->valid_mask);

out:
kfree(tmp);
return ret;
}

static bool msm_gpio_needs_valid_mask(struct msm_pinctrl *pctrl)
{
return device_property_read_u16_array(pctrl->dev, "gpios", NULL, 0) > 0;
}

static int msm_gpio_init(struct msm_pinctrl *pctrl)
{
struct gpio_chip *chip;
Expand All @@ -824,13 +875,21 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl)
chip->parent = pctrl->dev;
chip->owner = THIS_MODULE;
chip->of_node = pctrl->dev->of_node;
chip->need_valid_mask = msm_gpio_needs_valid_mask(pctrl);

ret = gpiochip_add_data(&pctrl->chip, pctrl);
if (ret) {
dev_err(pctrl->dev, "Failed register gpiochip\n");
return ret;
}

ret = msm_gpio_init_valid_mask(chip, pctrl);
if (ret) {
dev_err(pctrl->dev, "Failed to setup irq valid bits\n");
gpiochip_remove(&pctrl->chip);
return ret;
}

ret = gpiochip_add_pin_range(&pctrl->chip, dev_name(pctrl->dev), 0, 0, chip->ngpio);
if (ret) {
dev_err(pctrl->dev, "Failed to add pin range\n");
Expand Down
16 changes: 16 additions & 0 deletions include/linux/gpio/driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,21 @@ struct gpio_chip {
struct gpio_irq_chip irq;
#endif

/**
* @need_valid_mask:
*
* If set core allocates @valid_mask with all bits set to one.
*/
bool need_valid_mask;

/**
* @valid_mask:
*
* If not %NULL holds bitmask of GPIOs which are valid to be used
* from the chip.
*/
unsigned long *valid_mask;

#if defined(CONFIG_OF_GPIO)
/*
* If CONFIG_OF is enabled, then all GPIO controllers described in the
Expand Down Expand Up @@ -384,6 +399,7 @@ bool gpiochip_line_is_open_source(struct gpio_chip *chip, unsigned int offset);

/* Sleep persistence inquiry for drivers */
bool gpiochip_line_is_persistent(struct gpio_chip *chip, unsigned int offset);
bool gpiochip_line_is_valid(const struct gpio_chip *chip, unsigned int offset);

/* get driver data */
void *gpiochip_get_data(struct gpio_chip *chip);
Expand Down

0 comments on commit 89b0b4e

Please sign in to comment.