diff options
Diffstat (limited to 'drivers/gpio/gpiolib-of.c')
-rw-r--r-- | drivers/gpio/gpiolib-of.c | 128 |
1 files changed, 104 insertions, 24 deletions
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 2e537ee979f3..6e0eb67dcbf0 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -929,7 +929,7 @@ struct notifier_block gpio_of_notifier = { #endif /* CONFIG_OF_DYNAMIC */ /** - * of_gpio_simple_xlate - translate gpiospec to the GPIO number and flags + * of_gpio_twocell_xlate - translate twocell gpiospec to the GPIO number and flags * @gc: pointer to the gpio_chip structure * @gpiospec: GPIO specifier as found in the device tree * @flags: a flags pointer to fill in @@ -941,9 +941,9 @@ struct notifier_block gpio_of_notifier = { * Returns: * GPIO number (>= 0) on success, negative errno on failure. */ -static int of_gpio_simple_xlate(struct gpio_chip *gc, - const struct of_phandle_args *gpiospec, - u32 *flags) +static int of_gpio_twocell_xlate(struct gpio_chip *gc, + const struct of_phandle_args *gpiospec, + u32 *flags) { /* * We're discouraging gpio_cells < 2, since that way you'll have to @@ -951,7 +951,7 @@ static int of_gpio_simple_xlate(struct gpio_chip *gc, * number and the flags from a single gpio cell -- this is possible, * but not recommended). */ - if (gc->of_gpio_n_cells < 2) { + if (gc->of_gpio_n_cells != 2) { WARN_ON(1); return -EINVAL; } @@ -968,6 +968,49 @@ static int of_gpio_simple_xlate(struct gpio_chip *gc, return gpiospec->args[0]; } +/** + * of_gpio_threecell_xlate - translate threecell gpiospec to the GPIO number and flags + * @gc: pointer to the gpio_chip structure + * @gpiospec: GPIO specifier as found in the device tree + * @flags: a flags pointer to fill in + * + * This is simple translation function, suitable for the most 1:n mapped + * GPIO chips, i.e. several GPIO chip instances from one device tree node. + * In this case the following binding is implied: + * + * foo-gpios = <&gpio instance offset flags>; + * + * Returns: + * GPIO number (>= 0) on success, negative errno on failure. + */ +static int of_gpio_threecell_xlate(struct gpio_chip *gc, + const struct of_phandle_args *gpiospec, + u32 *flags) +{ + if (gc->of_gpio_n_cells != 3) { + WARN_ON(1); + return -EINVAL; + } + + if (WARN_ON(gpiospec->args_count != 3)) + return -EINVAL; + + /* + * Check chip instance number, the driver responds with true if + * this is the chip we are looking for. + */ + if (!gc->of_node_instance_match(gc, gpiospec->args[0])) + return -EINVAL; + + if (gpiospec->args[1] >= gc->ngpio) + return -EINVAL; + + if (flags) + *flags = gpiospec->args[2]; + + return gpiospec->args[1]; +} + #if IS_ENABLED(CONFIG_OF_GPIO_MM_GPIOCHIP) #include <linux/gpio/legacy-of-mm-gpiochip.h> /** @@ -1057,6 +1100,9 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) const char *name; static const char group_names_propname[] = "gpio-ranges-group-names"; bool has_group_names; + int offset; /* Offset of the first GPIO line on the chip */ + int pin; /* Pin base number in the range */ + int count; /* Number of pins/GPIO lines to map */ np = dev_of_node(&chip->gpiodev->dev); if (!np) @@ -1065,7 +1111,15 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) has_group_names = of_property_present(np, group_names_propname); for (;; index++) { - ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, + /* + * Ordinary phandles contain 2-3 cells: + * gpios = <&gpio [instance] offset flags>; + * Ranges always contain one more cell: + * gpio-ranges <&pinctrl [gpio_instance] gpio_offet pin_offet count>; + * This is why we parse chip->of_gpio_n_cells + 1 cells + */ + ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", + chip->of_gpio_n_cells + 1, index, &pinspec); if (ret) break; @@ -1075,13 +1129,33 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) if (!pctldev) return -EPROBE_DEFER; + if (chip->of_gpio_n_cells == 3) { + /* First cell is the gpiochip instance number */ + offset = pinspec.args[1]; + pin = pinspec.args[2]; + count = pinspec.args[3]; + } else { + offset = pinspec.args[0]; + pin = pinspec.args[1]; + count = pinspec.args[2]; + } + + /* + * With multiple GPIO chips per node, check that this chip is the + * right instance. + */ + if (chip->of_node_instance_match && + (chip->of_gpio_n_cells == 3) && + !chip->of_node_instance_match(chip, pinspec.args[0])) + continue; + /* Ignore ranges outside of this GPIO chip */ - if (pinspec.args[0] >= (chip->offset + chip->ngpio)) + if (offset >= (chip->offset + chip->ngpio)) continue; - if (pinspec.args[0] + pinspec.args[2] <= chip->offset) + if (offset + count <= chip->offset) continue; - if (pinspec.args[2]) { + if (count) { /* npins != 0: linear range */ if (has_group_names) { of_property_read_string_index(np, @@ -1095,27 +1169,27 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) } /* Trim the range to fit this GPIO chip */ - if (chip->offset > pinspec.args[0]) { - trim = chip->offset - pinspec.args[0]; - pinspec.args[2] -= trim; - pinspec.args[1] += trim; - pinspec.args[0] = 0; + if (chip->offset > offset) { + trim = chip->offset - offset; + count -= trim; + pin += trim; + offset = 0; } else { - pinspec.args[0] -= chip->offset; + offset -= chip->offset; } - if ((pinspec.args[0] + pinspec.args[2]) > chip->ngpio) - pinspec.args[2] = chip->ngpio - pinspec.args[0]; + if ((offset + count) > chip->ngpio) + count = chip->ngpio - offset; ret = gpiochip_add_pin_range(chip, pinctrl_dev_get_devname(pctldev), - pinspec.args[0], - pinspec.args[1], - pinspec.args[2]); + offset, + pin, + count); if (ret) return ret; } else { /* npins == 0: special range */ - if (pinspec.args[1]) { + if (pin) { pr_err("%pOF: Illegal gpio-range format.\n", np); break; @@ -1140,7 +1214,7 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) } ret = gpiochip_add_pingroup_range(chip, pctldev, - pinspec.args[0], name); + offset, name); if (ret) return ret; } @@ -1163,8 +1237,14 @@ int of_gpiochip_add(struct gpio_chip *chip) return 0; if (!chip->of_xlate) { - chip->of_gpio_n_cells = 2; - chip->of_xlate = of_gpio_simple_xlate; + if (chip->of_gpio_n_cells == 3) { + if (!chip->of_node_instance_match) + return -EINVAL; + chip->of_xlate = of_gpio_threecell_xlate; + } else { + chip->of_gpio_n_cells = 2; + chip->of_xlate = of_gpio_twocell_xlate; + } } if (chip->of_gpio_n_cells > MAX_PHANDLE_ARGS) |