Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

gpiolib: Identify arrays matching GPIO hardware

Certain GPIO array lookup results may map directly to GPIO pins of a
single GPIO chip in hardware order. If that condition is recognized
and handled efficiently, significant performance gain of get/set array
functions may be possible.

While processing a request for an array of GPIO descriptors, identify
those which represent corresponding pins of a single GPIO chip. Skip
over pins which require open source or open drain special processing.
Moreover, identify pins which require inversion. Pass a pointer to
that information with the array to the caller so it can benefit from
enhanced performance as soon as get/set array functions can accept and
make efficient use of it.

Cc: Jonathan Corbet <corbet@lwn.net>
Signed-off-by: Janusz Krzysztofik <jmkrzyszt@gmail.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>

authored by

Janusz Krzysztofik and committed by
Linus Walleij
bf9346f5 b9762beb

+92 -2
+3 -1
Documentation/driver-api/gpio/consumer.rst
··· 109 109 enum gpiod_flags flags) 110 110 111 111 This function returns a struct gpio_descs which contains an array of 112 - descriptors:: 112 + descriptors. It also contains a pointer to a gpiolib private structure which, 113 + if passed back to get/set array functions, may speed up I/O proocessing:: 113 114 114 115 struct gpio_descs { 116 + struct gpio_array *info; 115 117 unsigned int ndescs; 116 118 struct gpio_desc *desc[]; 117 119 }
+71 -1
drivers/gpio/gpiolib.c
··· 4174 4174 { 4175 4175 struct gpio_desc *desc; 4176 4176 struct gpio_descs *descs; 4177 - int count; 4177 + struct gpio_array *array_info = NULL; 4178 + struct gpio_chip *chip; 4179 + int count, bitmap_size; 4178 4180 4179 4181 count = gpiod_count(dev, con_id); 4180 4182 if (count < 0) ··· 4192 4190 gpiod_put_array(descs); 4193 4191 return ERR_CAST(desc); 4194 4192 } 4193 + 4195 4194 descs->desc[descs->ndescs] = desc; 4195 + 4196 + chip = gpiod_to_chip(desc); 4197 + /* 4198 + * Select a chip of first array member 4199 + * whose index matches its pin hardware number 4200 + * as a candidate for fast bitmap processing. 4201 + */ 4202 + if (!array_info && gpio_chip_hwgpio(desc) == descs->ndescs) { 4203 + struct gpio_descs *array; 4204 + 4205 + bitmap_size = BITS_TO_LONGS(chip->ngpio > count ? 4206 + chip->ngpio : count); 4207 + 4208 + array = kzalloc(struct_size(descs, desc, count) + 4209 + struct_size(array_info, invert_mask, 4210 + 3 * bitmap_size), GFP_KERNEL); 4211 + if (!array) { 4212 + gpiod_put_array(descs); 4213 + return ERR_PTR(-ENOMEM); 4214 + } 4215 + 4216 + memcpy(array, descs, 4217 + struct_size(descs, desc, descs->ndescs + 1)); 4218 + kfree(descs); 4219 + 4220 + descs = array; 4221 + array_info = (void *)(descs->desc + count); 4222 + array_info->get_mask = array_info->invert_mask + 4223 + bitmap_size; 4224 + array_info->set_mask = array_info->get_mask + 4225 + bitmap_size; 4226 + 4227 + array_info->desc = descs->desc; 4228 + array_info->size = count; 4229 + array_info->chip = chip; 4230 + bitmap_set(array_info->get_mask, descs->ndescs, 4231 + count - descs->ndescs); 4232 + bitmap_set(array_info->set_mask, descs->ndescs, 4233 + count - descs->ndescs); 4234 + descs->info = array_info; 4235 + } 4236 + /* 4237 + * Unmark members which don't qualify for fast bitmap 4238 + * processing (different chip, not in hardware order) 4239 + */ 4240 + if (array_info && (chip != array_info->chip || 4241 + gpio_chip_hwgpio(desc) != descs->ndescs)) { 4242 + __clear_bit(descs->ndescs, array_info->get_mask); 4243 + __clear_bit(descs->ndescs, array_info->set_mask); 4244 + } else if (array_info) { 4245 + /* Exclude open drain or open source from fast output */ 4246 + if (gpiochip_line_is_open_drain(chip, descs->ndescs) || 4247 + gpiochip_line_is_open_source(chip, descs->ndescs)) 4248 + __clear_bit(descs->ndescs, 4249 + array_info->set_mask); 4250 + /* Identify 'fast' pins which require invertion */ 4251 + if (gpiod_is_active_low(desc)) 4252 + __set_bit(descs->ndescs, 4253 + array_info->invert_mask); 4254 + } 4255 + 4196 4256 descs->ndescs++; 4197 4257 } 4258 + if (array_info) 4259 + dev_dbg(dev, 4260 + "GPIO array info: chip=%s, size=%d, get_mask=%lx, set_mask=%lx, invert_mask=%lx\n", 4261 + array_info->chip->label, array_info->size, 4262 + *array_info->get_mask, *array_info->set_mask, 4263 + *array_info->invert_mask); 4198 4264 return descs; 4199 4265 } 4200 4266 EXPORT_SYMBOL_GPL(gpiod_get_array);
+9
drivers/gpio/gpiolib.h
··· 183 183 } 184 184 #endif 185 185 186 + struct gpio_array { 187 + struct gpio_desc **desc; 188 + unsigned int size; 189 + struct gpio_chip *chip; 190 + unsigned long *get_mask; 191 + unsigned long *set_mask; 192 + unsigned long invert_mask[]; 193 + }; 194 + 186 195 struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum); 187 196 int gpiod_get_array_value_complex(bool raw, bool can_sleep, 188 197 unsigned int array_size,
+9
include/linux/gpio/consumer.h
··· 18 18 struct gpio_desc; 19 19 20 20 /** 21 + * Opaque descriptor for a structure of GPIO array attributes. This structure 22 + * is attached to struct gpiod_descs obtained from gpiod_get_array() and can be 23 + * passed back to get/set array functions in order to activate fast processing 24 + * path if applicable. 25 + */ 26 + struct gpio_array; 27 + 28 + /** 21 29 * Struct containing an array of descriptors that can be obtained using 22 30 * gpiod_get_array(). 23 31 */ 24 32 struct gpio_descs { 33 + struct gpio_array *info; 25 34 unsigned int ndescs; 26 35 struct gpio_desc *desc[]; 27 36 };